经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » 编程经验 » 查看文章
如何用 vscode 捞出还未国际化的中文词条
来源:cnblogs  作者:请叫我大苏  时间:2024/1/5 9:07:11  对本文有异议

做国际化一个很头疼的坑就是,你不知道项目里到底还有哪些中文词条没有国际化处理

纯靠人工去检查不现实,也不靠谱,而且浪费资源

所以还是得通过脚本工具来检查,思路是:

  1. 先保存好本地代码变更,准备好一个无文件变更的本地环境
  2. 再通过脚本把代码里的非展示性中文移除掉
    • 注释里的中文、console 里的中文,已经国际化处理过的中文
  3. 再用中文正则在 vscode 的全局搜索里匹配,捞出来的就是未国际化处理的中文词条
  4. 最后需要回退本地的更改,毕竟脚本是直接改动本地文件

脚本仅仅是检查用,用完记得回退代码

匹配中文词条的正则

  • 单个中文:
    • [\u4E00-\u9FFF]
  • 连续中文:
    • [\u4E00-\u9FFF]+
  • 掺杂了各种符号、字母的中文句子:
    • [a-zA-Z0-9、:]*[\u4E00-\u9FFF]+[\u4E00-\u9FFF\.\-\*。,,a-zA-Z0-9/()()::”“!?、%_【】《》>~~ ]*
    • (这里不建议把 : : - ' " 这几个特殊符号也列到正则里,因为这些符号比较特殊,有的语法层面也支持,列进来反而会引出新问题,所以宁愿这种场景的句子被截成多断)
  • 最好再加上文件的排除:
    • *.css,*.scss,*.less,*.json,*.bat,privacyProtocal.html,userProtocal.html,*.md,webpack**.js,*.txt,*.svg,*.properties,*.npmrc,vve-i18n-cli.config.js,baas,config,*.art,demo_index.html,*.sh,*.xml,*.java

脚本

移除非展示性中文的脚本

  1. // index.js
  2. #!/usr/bin/env node
  3. /**
  4. * 用来移除掉指定项目里的以下几类场景的中文:
  5. * - 注释里的中文
  6. * - 被国际化全局函数包裹的中文 $t
  7. *
  8. * 这样子方便借助 vs code 的全局正则搜索中文功能,来快速 review 未国际化的中文
  9. * 正则: [\u4E00-\u9FA5]+
  10. */
  11. "use strict";
  12. const program = require("commander");
  13. const { loadConfig } = require("../configuration");
  14. const core = require("./core");
  15. const vfs = require("vinyl-fs");
  16. const map = require("map-stream");
  17. const path = require("path");
  18. const fs = require("fs");
  19. function commaSeparatedList(value, split = ",") {
  20. return value.split(split).filter((item) => item);
  21. }
  22. program
  23. .version(require("../../package.json").version)
  24. .option("--cwd <path>", "工作目录")
  25. .option("--root-dir <path>", "国际文本所在的根目录")
  26. .option(
  27. "--config <path>",
  28. "配置文件的路径,没有配置,默认路径是在${cwd}/vve-i18n-cli.config.js"
  29. )
  30. .option("--no-config", "是否取配置文件")
  31. .option(
  32. "--i18n-file-rules <items>",
  33. "匹配含有国际化文本的文件规则",
  34. commaSeparatedList
  35. )
  36. .option(
  37. "--ignore-i18n-file-rules <items>",
  38. "不匹配含有国际化文本的文件规则",
  39. commaSeparatedList
  40. )
  41. .parse(process.argv);
  42. const config = {
  43. // 工作目录
  44. cwd: ".",
  45. // 根目录,国际文本所在的根目录
  46. rootDir: "src",
  47. // 配置文件的路径,没有配置,默认路径是在${cwd}/vve-i18n-cli.config.js
  48. config: undefined,
  49. // 是否取配置文件
  50. noConfig: false,
  51. // 匹配含有国际化文本的文件规则
  52. i18nFileRules: ["**/*.+(vue|js|html|htm)"],
  53. // 不匹配含有国际化文本的文件规则
  54. ignoreI18nFileRules: ["**/node_modules/**"],
  55. };
  56. Object.assign(config, program);
  57. const CONFIG_JS_FILENAME = "vve-i18n-cli.config.js";
  58. let absoluteCwd = path.resolve(config.cwd);
  59. // 优先判断是否需要读取文件
  60. if (!config.noConfig) {
  61. let configFilePath = path.join(absoluteCwd, CONFIG_JS_FILENAME);
  62. if (config.config) {
  63. configFilePath = path.resolve(config.config);
  64. }
  65. if (fs.existsSync(configFilePath)) {
  66. const conf = loadConfig(configFilePath);
  67. if (conf && conf.options && conf.options.zhCheck) {
  68. Object.assign(config, conf.options.zhCheck, program);
  69. }
  70. }
  71. }
  72. // 制定配置文件后,cwd在配置文件中定义,则cwd就需要重新获取
  73. if (!program.cwd) {
  74. absoluteCwd = path.resolve(config.cwd);
  75. }
  76. const absoluteRootDir = path.resolve(absoluteCwd, config.rootDir);
  77. function run() {
  78. console.log("================================>start");
  79. vfs
  80. .src(
  81. config.i18nFileRules.map((item) => path.resolve(absoluteRootDir, item)),
  82. {
  83. ignore: config.ignoreI18nFileRules.map((item) =>
  84. path.resolve(absoluteRootDir, item)
  85. ),
  86. dot: false,
  87. }
  88. )
  89. .pipe(
  90. map((file, cb) => {
  91. console.log("开始解析 =========================>", file.path);
  92. const extname = path.extname(file.path);
  93. let fileContent = file.contents.toString();
  94. let newFileContent = fileContent;
  95. if (extname.toLowerCase() === ".vue") {
  96. newFileContent = core.removeUnusedZhInVue(fileContent);
  97. } else if (extname.toLowerCase() === ".js") {
  98. newFileContent = core.removeUnusedZhInJs(fileContent);
  99. } else if ([".html", ".htm"].includes(extname.toLowerCase())) {
  100. newFileContent = core.removeUnusedZhInHtml(fileContent);
  101. }
  102. if (newFileContent !== fileContent) {
  103. console.log("发现无用的中文,正在移除中...");
  104. fs.writeFileSync(file.path, newFileContent);
  105. }
  106. console.log("解析结束 =========================>", file.path);
  107. cb();
  108. })
  109. )
  110. .on("end", () => {
  111. console.log("================================>end");
  112. });
  113. }
  114. run();
  1. // core.js
  2. // 包含中文
  3. const zhReg = new RegExp("[\\u4E00-\\u9FFF]+", "");
  4. // 处理 vue 文件
  5. function removeUnusedZhInVue(fileContent) {
  6. return removeUnusedZh(fileContent);
  7. }
  8. exports.removeUnusedZhInVue = removeUnusedZhInVue;
  9. // 处理 js 文件
  10. function removeUnusedZhInJs(fileContent) {
  11. return removeUnusedZh(fileContent);
  12. }
  13. exports.removeUnusedZhInJs = removeUnusedZhInJs;
  14. // 处理 html 文件
  15. // 处理 js 文件
  16. function removeUnusedZhInHtml(fileContent) {
  17. return removeUnusedZh(fileContent);
  18. }
  19. exports.removeUnusedZhInHtml = removeUnusedZhInHtml;
  20. function removeUnusedZh(fileContent) {
  21. const hasAnnotation = {
  22. "/*": false,
  23. "<!--": false,
  24. };
  25. // 逐行处理
  26. fileContent = fileContent
  27. .split("\n")
  28. .map((line) => {
  29. // 移除无用中文
  30. if (line.match(zhReg)) {
  31. const regs = [
  32. new RegExp("//(.*[\\u4E00-\\u9FFF]+)", ""), // 移除 // xx
  33. new RegExp("console.log\\(['\"](.*[\\u4E00-\\u9FFF]+)", ""), // 移除 console.log(xxx)
  34. new RegExp("console.info\\(['\"](.*[\\u4E00-\\u9FFF]+)", ""), // 移除 console.info(xxx)
  35. new RegExp(
  36. "\\$t\\([ ]*['\"`](.*?[\\u4E00-\\u9FFF]+.*?)['\"`]\\)",
  37. ""
  38. ), // 移除 $t("xxx")
  39. ];
  40. regs.forEach((reg) => {
  41. let match = line.match(reg);
  42. while (match && match[1]) {
  43. line = line.replace(match[1], "");
  44. match = line.match(reg);
  45. }
  46. });
  47. }
  48. if (!hasAnnotation["/*"] && line.indexOf("/*") > -1) {
  49. hasAnnotation["/*"] = true;
  50. }
  51. if (!hasAnnotation["<!--"] && line.indexOf("<!--") > -1) {
  52. hasAnnotation["<!--"] = true;
  53. }
  54. return line;
  55. })
  56. .join("\n");
  57. if (hasAnnotation["/*"]) {
  58. // 移除 /* xxx */
  59. const reg = new RegExp("/\\*([\\s\\S]*?)\\*/", "g");
  60. fileContent = fileContent.replace(reg, function (match, key, index) {
  61. // console.log("[/**/] ==1 >", { match, key, index });
  62. let newKey = key;
  63. while (newKey.match(zhReg)) {
  64. newKey = newKey.replace(zhReg, "");
  65. }
  66. return match.replace(key, newKey);
  67. });
  68. }
  69. // 移除 <!-- xxx -->
  70. if (hasAnnotation["<!--"]) {
  71. const reg = new RegExp("<!--([\\s\\S]*?)-->", "g");
  72. fileContent = fileContent.replace(reg, function (match, key, index) {
  73. let newKey = key;
  74. while (newKey.match(zhReg)) {
  75. newKey = newKey.replace(zhReg, "");
  76. }
  77. return match.replace(key, newKey);
  78. });
  79. }
  80. return fileContent;
  81. }
  1. // configuration.js
  2. const buildDebug = require("debug");
  3. const path = require("path");
  4. const debug = buildDebug("files:configuration");
  5. function loadConfig(filepath) {
  6. try {
  7. const conf = readConfig(filepath);
  8. return conf;
  9. } catch (e) {
  10. debug("error", e);
  11. return null;
  12. }
  13. }
  14. function readConfig(filepath) {
  15. let options;
  16. try {
  17. const configModule = require(filepath);
  18. options =
  19. configModule && configModule.__esModule
  20. ? configModule.default || undefined
  21. : configModule;
  22. } catch (err) {
  23. throw err;
  24. } finally {
  25. }
  26. return {
  27. filepath,
  28. dirname: path.dirname(filepath),
  29. options,
  30. };
  31. }
  32. module.exports = {
  33. loadConfig,
  34. readConfig,
  35. };
  1. {
  2. "dependencies": {
  3. "commander": "^3.0.2",
  4. "debug": "^4.1.1",
  5. "jsonfile": "^5.0.0",
  6. "lodash.uniq": "^4.5.0",
  7. "map-stream": "0.0.7",
  8. "pinyin-pro": "^3.11.0",
  9. "translation.js": "^0.7.9",
  10. "vinyl-fs": "^3.0.3",
  11. "xlsx": "^0.18.5"
  12. },
  13. "devDependencies": {
  14. "chai": "^4.2.0",
  15. "mocha": "^6.2.1",
  16. "nyc": "^14.1.1",
  17. "shelljs": "^0.8.3",
  18. "standard-version": "^7.0.0"
  19. },
  20. "version": "3.2.3"
  21. }
  1. // vve-i18n-cli.config.js
  2. module.exports = {
  3. // 工作目录
  4. cwd: ".",
  5. // 根目录,国际文本所在的根目录
  6. rootDir: "demo",
  7. // 默认所有模块,如果有传module参数,就只处理某个模块
  8. // '**/module-**/**/index.js'
  9. moduleIndexRules: ["*/pro.properties"],
  10. // 匹配含有国际化文本的文件规则
  11. i18nFileRules: ["**/*.+(vue|js)"],
  12. // 国际化文本的正则表达式,正则中第一个捕获对象当做国际化文本
  13. i18nTextRules: [/(?:[\$.])t\(['"](.+?)['"]/g],
  14. // 模块的国际化的json文件需要被保留下的key,即使这些组件在项目中没有被引用
  15. // key可以是一个字符串,正则,或者是函数
  16. keepKeyRules: [
  17. /^G\/+/, // G/开头的会被保留
  18. ],
  19. ignoreKeyRules: [/^el/],
  20. // 生成的国际化资源包的输出目录
  21. outDir: "i18n",
  22. // 生成的国际化的语言
  23. i18nLanguages: [
  24. "zh", // 中文
  25. "en", // 英文
  26. ],
  27. // 是否翻译
  28. translate: false,
  29. // 翻译的基础语言,默认是用中文翻译
  30. translateFromLang: "zh",
  31. // 是否强制翻译,即已翻译修改的内容,也重新用翻译生成
  32. forceTranslate: false,
  33. // 翻译的语言
  34. translateLanguage: ["zh", "en"],
  35. // 模块下${outDir}/index.js文件不存在才拷贝index.js
  36. copyIndex: true,
  37. // 是否强制拷贝最新index.js
  38. forceCopyIndex: false,
  39. // 国际化文本包裹相关
  40. zhWrap: {
  41. cwd: ".",
  42. // 根目录,国际文本所在的根目录
  43. rootDir: ".",
  44. i18nFileRules: [
  45. "!(node_modules|config)/**/*.+(vue)",
  46. // "base/components/login.vue",
  47. "base/common/js/httpHandle.js",
  48. ],
  49. ignorePreReg: [
  50. /t\s*\(\s*$/,
  51. /tl\s*\(\s*$/,
  52. /console\.(?:log|error|warn|info|debug)\s*\(\s*$/,
  53. new RegExp("//.+"),
  54. ],
  55. // js相关文件需要引入的国际化文件
  56. i18nImportForJs: "import i18n from '@inap_base/i18n/core'",
  57. // js相关文件需要使用国际化方法
  58. jsI18nFuncName: "i18n.t",
  59. // vue相关文件需要使用的国际化方法
  60. vueI18nFuncName: "$t",
  61. },
  62. };

硬替换脚本

具体查看 zh-i18n.zip

原文链接:https://www.cnblogs.com/dasusu/p/17945999

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号