如何用 vscode 捞出还未国际化的中文词条
生活随笔
收集整理的這篇文章主要介紹了
如何用 vscode 捞出还未国际化的中文词条
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
做國際化一個很頭疼的坑就是,你不知道項目里到底還有哪些中文詞條沒有國際化處理
純靠人工去檢查不現實,也不靠譜,而且浪費資源
所以還是得通過腳本工具來檢查,思路是:
- 先保存好本地代碼變更,準備好一個無文件變更的本地環境
- 再通過腳本把代碼里的非展示性中文移除掉
- 注釋里的中文、console 里的中文,已經國際化處理過的中文
- 再用中文正則在 vscode 的全局搜索里匹配,撈出來的就是未國際化處理的中文詞條
- 最后需要回退本地的更改,畢竟腳本是直接改動本地文件
腳本僅僅是檢查用,用完記得回退代碼
匹配中文詞條的正則
- 單個中文:
[\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
腳本
移除非展示性中文的腳本
// index.js
#!/usr/bin/env node
/**
* 用來移除掉指定項目里的以下幾類場景的中文:
* - 注釋里的中文
* - 被國際化全局函數包裹的中文 $t
*
* 這樣子方便借助 vs code 的全局正則搜索中文功能,來快速 review 未國際化的中文
* 正則: [\u4E00-\u9FA5]+
*/
"use strict";
const program = require("commander");
const { loadConfig } = require("../configuration");
const core = require("./core");
const vfs = require("vinyl-fs");
const map = require("map-stream");
const path = require("path");
const fs = require("fs");
function commaSeparatedList(value, split = ",") {
return value.split(split).filter((item) => item);
}
program
.version(require("../../package.json").version)
.option("--cwd <path>", "工作目錄")
.option("--root-dir <path>", "國際文本所在的根目錄")
.option(
"--config <path>",
"配置文件的路徑,沒有配置,默認路徑是在${cwd}/vve-i18n-cli.config.js"
)
.option("--no-config", "是否取配置文件")
.option(
"--i18n-file-rules <items>",
"匹配含有國際化文本的文件規則",
commaSeparatedList
)
.option(
"--ignore-i18n-file-rules <items>",
"不匹配含有國際化文本的文件規則",
commaSeparatedList
)
.parse(process.argv);
const config = {
// 工作目錄
cwd: ".",
// 根目錄,國際文本所在的根目錄
rootDir: "src",
// 配置文件的路徑,沒有配置,默認路徑是在${cwd}/vve-i18n-cli.config.js
config: undefined,
// 是否取配置文件
noConfig: false,
// 匹配含有國際化文本的文件規則
i18nFileRules: ["**/*.+(vue|js|html|htm)"],
// 不匹配含有國際化文本的文件規則
ignoreI18nFileRules: ["**/node_modules/**"],
};
Object.assign(config, program);
const CONFIG_JS_FILENAME = "vve-i18n-cli.config.js";
let absoluteCwd = path.resolve(config.cwd);
// 優先判斷是否需要讀取文件
if (!config.noConfig) {
let configFilePath = path.join(absoluteCwd, CONFIG_JS_FILENAME);
if (config.config) {
configFilePath = path.resolve(config.config);
}
if (fs.existsSync(configFilePath)) {
const conf = loadConfig(configFilePath);
if (conf && conf.options && conf.options.zhCheck) {
Object.assign(config, conf.options.zhCheck, program);
}
}
}
// 制定配置文件后,cwd在配置文件中定義,則cwd就需要重新獲取
if (!program.cwd) {
absoluteCwd = path.resolve(config.cwd);
}
const absoluteRootDir = path.resolve(absoluteCwd, config.rootDir);
function run() {
console.log("================================>start");
vfs
.src(
config.i18nFileRules.map((item) => path.resolve(absoluteRootDir, item)),
{
ignore: config.ignoreI18nFileRules.map((item) =>
path.resolve(absoluteRootDir, item)
),
dot: false,
}
)
.pipe(
map((file, cb) => {
console.log("開始解析 =========================>", file.path);
const extname = path.extname(file.path);
let fileContent = file.contents.toString();
let newFileContent = fileContent;
if (extname.toLowerCase() === ".vue") {
newFileContent = core.removeUnusedZhInVue(fileContent);
} else if (extname.toLowerCase() === ".js") {
newFileContent = core.removeUnusedZhInJs(fileContent);
} else if ([".html", ".htm"].includes(extname.toLowerCase())) {
newFileContent = core.removeUnusedZhInHtml(fileContent);
}
if (newFileContent !== fileContent) {
console.log("發現無用的中文,正在移除中...");
fs.writeFileSync(file.path, newFileContent);
}
console.log("解析結束 =========================>", file.path);
cb();
})
)
.on("end", () => {
console.log("================================>end");
});
}
run();
// core.js
// 包含中文
const zhReg = new RegExp("[\\u4E00-\\u9FFF]+", "");
// 處理 vue 文件
function removeUnusedZhInVue(fileContent) {
return removeUnusedZh(fileContent);
}
exports.removeUnusedZhInVue = removeUnusedZhInVue;
// 處理 js 文件
function removeUnusedZhInJs(fileContent) {
return removeUnusedZh(fileContent);
}
exports.removeUnusedZhInJs = removeUnusedZhInJs;
// 處理 html 文件
// 處理 js 文件
function removeUnusedZhInHtml(fileContent) {
return removeUnusedZh(fileContent);
}
exports.removeUnusedZhInHtml = removeUnusedZhInHtml;
function removeUnusedZh(fileContent) {
const hasAnnotation = {
"/*": false,
"<!--": false,
};
// 逐行處理
fileContent = fileContent
.split("\n")
.map((line) => {
// 移除無用中文
if (line.match(zhReg)) {
const regs = [
new RegExp("http://(.*[\\u4E00-\\u9FFF]+)", ""), // 移除 // xx
new RegExp("console.log\\(['\"](.*[\\u4E00-\\u9FFF]+)", ""), // 移除 console.log(xxx)
new RegExp("console.info\\(['\"](.*[\\u4E00-\\u9FFF]+)", ""), // 移除 console.info(xxx)
new RegExp(
"\\$t\\([ ]*['\"`](.*?[\\u4E00-\\u9FFF]+.*?)['\"`]\\)",
""
), // 移除 $t("xxx")
];
regs.forEach((reg) => {
let match = line.match(reg);
while (match && match[1]) {
line = line.replace(match[1], "");
match = line.match(reg);
}
});
}
if (!hasAnnotation["/*"] && line.indexOf("/*") > -1) {
hasAnnotation["/*"] = true;
}
if (!hasAnnotation["<!--"] && line.indexOf("<!--") > -1) {
hasAnnotation["<!--"] = true;
}
return line;
})
.join("\n");
if (hasAnnotation["/*"]) {
// 移除 /* xxx */
const reg = new RegExp("/\\*([\\s\\S]*?)\\*/", "g");
fileContent = fileContent.replace(reg, function (match, key, index) {
// console.log("[/**/] ==1 >", { match, key, index });
let newKey = key;
while (newKey.match(zhReg)) {
newKey = newKey.replace(zhReg, "");
}
return match.replace(key, newKey);
});
}
// 移除 <!-- xxx -->
if (hasAnnotation["<!--"]) {
const reg = new RegExp("<!--([\\s\\S]*?)-->", "g");
fileContent = fileContent.replace(reg, function (match, key, index) {
let newKey = key;
while (newKey.match(zhReg)) {
newKey = newKey.replace(zhReg, "");
}
return match.replace(key, newKey);
});
}
return fileContent;
}
// configuration.js
const buildDebug = require("debug");
const path = require("path");
const debug = buildDebug("files:configuration");
function loadConfig(filepath) {
try {
const conf = readConfig(filepath);
return conf;
} catch (e) {
debug("error", e);
return null;
}
}
function readConfig(filepath) {
let options;
try {
const configModule = require(filepath);
options =
configModule && configModule.__esModule
? configModule.default || undefined
: configModule;
} catch (err) {
throw err;
} finally {
}
return {
filepath,
dirname: path.dirname(filepath),
options,
};
}
module.exports = {
loadConfig,
readConfig,
};
{
"dependencies": {
"commander": "^3.0.2",
"debug": "^4.1.1",
"jsonfile": "^5.0.0",
"lodash.uniq": "^4.5.0",
"map-stream": "0.0.7",
"pinyin-pro": "^3.11.0",
"translation.js": "^0.7.9",
"vinyl-fs": "^3.0.3",
"xlsx": "^0.18.5"
},
"devDependencies": {
"chai": "^4.2.0",
"mocha": "^6.2.1",
"nyc": "^14.1.1",
"shelljs": "^0.8.3",
"standard-version": "^7.0.0"
},
"version": "3.2.3"
}
// vve-i18n-cli.config.js
module.exports = {
// 工作目錄
cwd: ".",
// 根目錄,國際文本所在的根目錄
rootDir: "demo",
// 默認所有模塊,如果有傳module參數,就只處理某個模塊
// '**/module-**/**/index.js'
moduleIndexRules: ["*/pro.properties"],
// 匹配含有國際化文本的文件規則
i18nFileRules: ["**/*.+(vue|js)"],
// 國際化文本的正則表達式,正則中第一個捕獲對象當做國際化文本
i18nTextRules: [/(?:[\$.])t\(['"](.+?)['"]/g],
// 模塊的國際化的json文件需要被保留下的key,即使這些組件在項目中沒有被引用
// key可以是一個字符串,正則,或者是函數
keepKeyRules: [
/^G\/+/, // G/開頭的會被保留
],
ignoreKeyRules: [/^el/],
// 生成的國際化資源包的輸出目錄
outDir: "i18n",
// 生成的國際化的語言
i18nLanguages: [
"zh", // 中文
"en", // 英文
],
// 是否翻譯
translate: false,
// 翻譯的基礎語言,默認是用中文翻譯
translateFromLang: "zh",
// 是否強制翻譯,即已翻譯修改的內容,也重新用翻譯生成
forceTranslate: false,
// 翻譯的語言
translateLanguage: ["zh", "en"],
// 模塊下${outDir}/index.js文件不存在才拷貝index.js
copyIndex: true,
// 是否強制拷貝最新index.js
forceCopyIndex: false,
// 國際化文本包裹相關
zhWrap: {
cwd: ".",
// 根目錄,國際文本所在的根目錄
rootDir: ".",
i18nFileRules: [
"!(node_modules|config)/**/*.+(vue)",
// "base/components/login.vue",
"base/common/js/httpHandle.js",
],
ignorePreReg: [
/t\s*\(\s*$/,
/tl\s*\(\s*$/,
/console\.(?:log|error|warn|info|debug)\s*\(\s*$/,
new RegExp("http://.+"),
],
// js相關文件需要引入的國際化文件
i18nImportForJs: "import i18n from '@inap_base/i18n/core'",
// js相關文件需要使用國際化方法
jsI18nFuncName: "i18n.t",
// vue相關文件需要使用的國際化方法
vueI18nFuncName: "$t",
},
};
硬替換腳本
具體查看 zh-i18n.zip
總結
以上是生活随笔為你收集整理的如何用 vscode 捞出还未国际化的中文词条的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【开源项目推荐】Great Expect
- 下一篇: 普通家庭且成绩一般的学生如何实现留学梦?