【Github:vue-viewer-editor/vve-i18n-cli】

这是我同事开发的国际化自动处理脚本,我进行过一次扩展,让其也支持我们一个 jQuery 老项目的国际化日常维护

至此,我们团队内的国际化均是使用该脚本来进行日常维护

该自动化脚本极大的为我们提效,基本将国际化的词条相关工作降低到 0 了,这意味着我们基本上不用特意留出太多时间来处理国际化方面的工作

但是,国际化其实不只有词条相关的工作,至于还有哪些工作,我之前发表过一篇《项目国际化的难点痛点是什么》里面吐槽得很清晰了

不可否认的是,如果没有这个自动化脚本,根本就没法在领导期望的时间内完成国际化的工作

自从有了这个脚本后,从以前的跟领导评估说要 4 天的国际化工作量到现在只评估了 1 天工作量,实际上跑下脚本分分钟就解决了,我还可以愉快的滑一天水,领导开心,我也开心~

自动化脚本能力

检查项目里是否存在不合理的编程方式,如中文做 key 值等

上述示例命令运行结果呈现:

{"type":"script-pre","text":"这里也可能有中文,还用不了this上下文"}

{"type":"props","text":"这里的中文也用不了this上下文"}

{"type":"zh-key","text":"中文"}

script-pre 场景是说发现有中文存在于 script 标签内,这部分代码运行在 js 模块作用域内,this 指向不是 vue 组件,包裹 this.$t 的话会导致程序异常,所以要先手动处理下,能下沉就下沉,否则就先手动用全局函数包裹,然后忽略这个处理

props 场景是说发现有中文存在于 vue 的 props 字段里,这里也无法访问 this,会报异常,建议这块更改成 computed 用法

zh-key 场景是说发现中文做 key 值,需要用户确认是否能被翻译处理

这个命令可以提前主动的发现代码里的国际化处理问题,避免将问题遗留到测试或线上阶段

标记无需处理的词条

总有些场景,你希望这个中文词条不要被国际化处理,这时候可以类似 es-lint 的忽略配置一样,既可以忽略整个文件,也可以忽略文件中的某个词条

vve-i18n-cli.config.js 里增加下忽略配置规则:

// vve-i18n-cli.config.js
module.exports = {
// 国际化文本包裹相关
zhWrap: {
// 需要过滤的文件
ignoreI18nFileRules: [],
// 需要处理的文件
i18nFileRules: ["!(node_modules|config|statsvnTmp)/**/*.+(js|vue)"],
// 当词条前缀出现以下正则时,该词条过滤不处理
ignorePreReg: [
/t\s*\([\s\n]*$/, // 词条在 t( 方法内的不处理,$t() 符合该规则
/console\.(?:log|error|warn|info|debug)\s*\(\s*$/, // 词条在 console.xxx 方法内的不处理,过滤掉日志内的中文处理
new RegExp("//.+"), // 注释中的词条不处理
new RegExp("i18n-disabled.+"), // 词条前面出现 i18n-disabled 关键词的不处理
],
},
};

然后代码中这些场景就不被处理了:

const ZH_KEY = /*i18n-disabled*/ "出现忽略处理的关键词的词条也不会被处理";
// 注释里中文不会被处理
console.error("日志里的中文也不会被处理");

自动对各种场景的中文词条包裹翻译函数

当检查完代码基本没问题,也配置了需要忽略处理的词条文件后,就可以通过命令自动对中文词条进行包裹翻译函数处理了:

不管你项目文件有成百上千个,都是一个命令就自动完成国际化翻译函数包裹词条处理

包裹哪些文件、包含的翻译函数名,不同场景(js 里,vue 里)用什么函数包裹,js 里是否需要加入 import 引入包含函数的代码,哪些文件不处理,哪些词条不处理,哪些前缀标记的不处理等等

以上场景都是通过 vve-i18n-cli.config.js 配置文件处理,详情查看下面章节,有默认的配置,你也可以根据你项目需要进行自己诉求的配置

脚本不是写死的行为,通过不同配置,可以适应到各种项目里去使用,目前我们团队的老项目,新项目,各种项目就通过各自项目的配置来接入了这个国际化自动处理脚本

自动将所有词条提取到 json 文件中(按模块维护)

当项目完成的国际化包裹词条处理后,就可以接着下一步,把词条提取到 json 文件里了:

想机翻,可以,默认不翻译,只做提取

想按模块提取到不同 json 文件里,可以,配置下模块规则

想生成多份语言的 json,可以,默认只有 zh.json, en.json


国际化的词条工作无外乎就是词条包裹处理,词条提取,词条翻译

这些工作难度不大,但工作量大,借助这类国际化自动处理脚本,就可以极大的提高效率,开心的滑水了

如何使用

安装

npm install vve-i18n-cli -D

package 里添加脚本命令,简化命令使用

{
"scripts": {
"i18n": "vve-i18n-cli",
"i18n-wrap": "vve-i18n-zh-wrap-cli",
"i18n-check": "vve-i18n-zh-check-cli"
}
}

根目录下创建配置文件 vve-i18n-cli.config.js

// vve-i18n-cli.config.js
module.exports = {
// 工作目录
cwd: ".",
// 根目录,国际文本所在的根目录
rootDir: "src",
// 默认所有模块,如果有传module参数,就只处理某个模块
// '**/module-**/**/index.js'
moduleIndexRules: ["."],
// 忽略模块
ignoreModuleIndexRules: [],
// 匹配含有国际化文本的文件规则
i18nFileRules: ["**/*.+(vue|js)"],
// 不匹配含有国际化文本的文件规则
ignoreI18nFileRules: [],
// 国际化文本的正则表达式,正则中第一个捕获对象当做国际化文本
i18nTextRules: [/(?:[\$.])t\([\s\n]*['"](.+?)['"]/g],
// 模块的国际化的json文件需要被保留下的key,即使这些组件在项目中没有被引用
// 规则可以是一个字符串,正则,或者是函数
keepKeyRules: [
/^G\/+/, // G/开头的会被保留
],
// 忽略国际化KEY的规则
// 规则可以是一个字符串,正则,或者是函数
ignoreKeyRules: [],
// 生成的国际化资源包的输出目录
outDir: "lang",
// 生成的国际化的语言
i18nLanguages: [
"zh", // 中文
"en", // 英文
],
// 配置文件的路径,没有配置,默认路径是在${cwd}/vve-i18n-cli.config.js
config: undefined,
// 是否取配置文件
disableConfigFile: false,
// 是否翻译
translate: false,
// 翻译的基础语言,默认是用中文翻译
translateFromLang: "zh",
// 是否强制翻译,即已翻译修改的内容,也重新用翻译生成
forceTranslate: false,
// 翻译的语言
translateLanguage: ["zh", "en"],
// 非中文使用拼音来来翻译
translateUsePinYin: false,
// 模块下${outDir}/index.js文件不存在才拷贝index.js
copyIndex: false,
// 是否强制拷贝最新index.js
forceCopyIndex: false, // 国际化文本包裹相关
zhWrap: {
cwd: ".",
// 根目录,国际文本所在的根目录
rootDir: ".",
ignoreI18nFileRules: [],
i18nFileRules: ["!(node_modules|config|statsvnTmp)/**/*.+(js|vue)"],
ignorePreReg: [
/t\s*\([\s\n]*$/,
/tl\s*\([\s\n]*$/,
/console\.(?:log|error|warn|info|debug)\s*\(\s*$/,
/\/\/\s*$/,
new RegExp("//.+"),
new RegExp("i18n-disabled.+"),
],
ignoreText: ["^[\\u4e00-\\u9fa5a-zA-Z0-9“._=,':;*#!”-]+$"],
// js相关文件需要引入的国际化文件
i18nImportForJs: "",
// js相关文件需要使用国际化方法
jsI18nFuncName: "$i18n.$t",
// vue相关文件需要使用的国际化方法
vueI18nFuncName: "$t",
},
zhCheck: {
cwd: ".",
// 根目录,国际文本所在的根目录
rootDir: ".",
ignoreI18nFileRules: [],
i18nFileRules: ["!(node_modules|config|statsvnTmp)/**/*.+(vue|js)"],
// 反引号中需要忽略的文本规则,可以是正则或者字符串
ignoreTextInQuoteRules: [/t\(/],
},
};

先检查是否存在不合理的代码实现

npm run i18n-check

再执行自动对词条包裹翻译函数的命令

npm run i18n-wrap

最后把这些被翻译函数包裹的词条提取到 json 文件里

npm run i18n


这份脚本很通用化,根据各自配置规则,可以适应到各种项目里面,实在不行,代码也是开源的,拉下来修修改改得了

扩展

我们团队的翻译不是机翻,而是有专门的翻译团队进行翻译,因此提取完 json 词条后,还需要用 excel 跟翻译团队打交道

所以可以来扩展下几个脚本

提取未翻译词条到 excel 文件中

/**
* 抽取未翻译的词条到excel文件中
*/
const map = require("map-stream");
const path = require("path");
const vfs = require("vinyl-fs");
const XLSX = require("xlsx"); const ROOT_DIR = path.resolve("./");
const fileRules = [
"**/*/i18n/en.json",
// "**/eweb-setting-planningDeployment/i18n/en.json",
]; const writeExcel = (arr, name = "未翻译词条") => {
const sheet_data = arr.map((v) => {
return {
中文: v,
English: "",
};
});
const new_sheet = XLSX.utils.json_to_sheet(sheet_data);
// // 创新一个新的excel对象,就是workbook
const new_workbook = XLSX.utils.book_new();
// // 将表的内容写入workbook
XLSX.utils.book_append_sheet(new_workbook, new_sheet, "sheet1");
XLSX.writeFile(new_workbook, `${name}.xlsx`);
}; function run() {
const zhList = [];
console.log("================================>start", ROOT_DIR);
vfs
.src(
fileRules.map((item) => path.resolve(ROOT_DIR, item)),
{
ignore: ["node_modules/**/*", "statsvnTmp/**/*"],
}
)
.pipe(
map((file, cb) => {
console.log("处理文件 =========================>", file.path); let fileContent = file.contents.toString();
fileContent = JSON.parse(fileContent);
Object.keys(fileContent).map((zh) => {
if (zh.match(/[\u4E00-\u9FFF]/)) {
if (zh === fileContent[zh]) {
// 未翻译
zhList.push(zh);
}
}
});
cb();
})
)
.on("end", () => {
const uniZh = Array.from(new Set(zhList));
writeExcel(uniZh);
console.log("未翻译词条数量:", uniZh.length);
console.log(
"================================>end",
"根目录下生成 excle 文件"
);
});
} run();

将 excel 中的词条回填到 json 文件中

/**
* 将翻译后的内容替换到en.json文件中
*/
const map = require("map-stream");
const path = require("path");
const vfs = require("vinyl-fs");
const fs = require("fs");
const XLSX = require("xlsx"); const ROOT_DIR = path.resolve("./");
const fileRules = ["**/*/i18n/en.json"];
// 文件名称 默认名称 resource.json
const fileName = "resource";
const fileJsonName = fileName + ".json";
const fileXlsName = fileName + ".xlsx"; const excelReader = (exlcePathArray = []) => {
if (!Array.isArray(exlcePathArray)) {
exlcePathArray = [exlcePathArray];
}
const obj = {};
for (const i in exlcePathArray) {
if (Object.hasOwnProperty.call(exlcePathArray, i)) {
const excleFilePath = exlcePathArray[i];
console.log("读取excle " + excleFilePath);
const workbook = XLSX.readFileSync(excleFilePath, {
type: "binary",
});
for (const sheet in workbook.Sheets) {
const dataArray = XLSX.utils.sheet_to_json(workbook.Sheets[sheet]);
obj[sheet] = dataArray;
}
}
}
return obj;
}; // 如果未找到 resource.json 查找 excel文件
if (!fs.existsSync(path.resolve(ROOT_DIR, fileJsonName))) {
// 判断resource.excel是否存在
if (fs.existsSync(path.resolve(ROOT_DIR, fileXlsName))) {
let fileObj = excelReader(path.resolve(ROOT_DIR, fileXlsName));
let dataObj = {};
if (fileObj.sheet1) {
// 中文列存在 例子: {"中文":"下载中...","English":"down…"}
for (let i = 0; i < fileObj.sheet1.length; i++) {
let itemZhKey = fileObj.sheet1[i]["中文"];
let itemEnKey = fileObj.sheet1[i]["English"];
if (itemZhKey && itemEnKey) {
dataObj[itemZhKey] = itemEnKey;
}
}
}
// 获取sheet1的内容
const data = JSON.stringify(dataObj);
try {
fs.writeFileSync(path.resolve(ROOT_DIR, fileJsonName), data);
} catch (error) {
console.log("生成文件 resource.xlsx 异常");
throw error;
}
} else {
console.log("不存在文件 resource.xlsx");
throw new Error("不存在文件 resource.xlsx");
}
} const originResource = require(path.resolve(ROOT_DIR, fileJsonName));
let resource = Object.assign({}, originResource);
Object.keys(originResource).map((key) => {
resource[`${key}:`] = `${originResource[key]}:`;
resource[`${key}:`] = `${originResource[key]}:`;
resource[`${key})`] = `${originResource[key]})`;
resource[`${key})`] = `${originResource[key]})`;
resource[`${key} `] = `${originResource[key]} `;
resource[` ${key}`] = ` ${originResource[key]}`;
resource[`(${key}`] = `(${originResource[key]}`;
resource[`(${key}`] = `(${originResource[key]}`;
resource[` ${key} `] = ` ${originResource[key]} `;
}); function run() {
console.log("================================>start", ROOT_DIR);
let failedCount = 0;
let successCount = 0;
let failedZhs = [];
vfs
.src(
fileRules.map((item) => path.resolve(ROOT_DIR, item)),
{
ignore: ["node_modules/**/*", "statsvnTmp/**/*"],
}
)
.pipe(
map((file, cb) => {
console.log("处理文件 =========================>", file.path); let fileContent = file.contents.toString();
fileContent = JSON.parse(fileContent);
let hasChange = false;
Object.keys(fileContent).map((zh) => {
if (zh.match(/[\u4E00-\u9FFF]/)) {
if (zh === fileContent[zh]) {
// 未翻译
if (resource[zh] && resource[zh] !== zh) {
hasChange = true;
fileContent[zh] = resource[zh];
successCount++;
} else {
failedCount++;
failedZhs.push(zh);
}
}
}
});
if (hasChange) {
fs.writeFileSync(
file.path,
JSON.stringify(fileContent, " ", 2) + "\n"
);
}
cb();
})
)
.on("end", () => {
fs.writeFileSync(
"unHandle.json",
JSON.stringify(failedZhs, " ", 2) + "\n"
);
console.log("本次翻译成功词条数量:", successCount);
console.log("还剩余未翻译词条数量:", failedCount);
console.log("================================>end");
});
} run();

使用 vve-i18n-cli 来一键式自动化实现国际化的更多相关文章

  1. 【EasyCi】持续集成交付,一键式自动化部署系统,开箱即用

    前言 本人是一家互联网公司的java开发,由于公司初期公司未招运维人员,恰好我对linux比较熟悉,便在公司服务器搭建了一套Jenkins.Gitlab.Maven私服.Docker私服.Sonarq ...

  2. azure 云上MySQL最新版本 MySQL5.7.11 批量自动化一键式安装 (转)

    --背景云端 以前都喜欢了源码安装mysql,总觉得源码是高大上的事情,不过源码也需要时间,特别是make的时候,如果磁盘和cpu差的话,时间很长很长,在虚拟机上安装mysql尤其甚慢了. 现在业务发 ...

  3. MySQL最新版本 MySQL5.7.11 批量自动化一键式安装(转)

    --背景云端 以前都喜欢了源码安装MySQL,总觉得源码是高大上的事情,不过源码也需要时间,特别是make的时候,如果磁盘和cpu差的话,时间很长很长,在虚拟机上安装mysql尤其甚慢了. 现在业务发 ...

  4. 自动化运维(2)之一键式单实例安装MySQL

    ZMySQLAutoTools文档 目标:自动化构建部署MySQL数据库,一键式单实例mysql安装,备份,监控,主从集群部署等.以及jdk,tomcat,nginx等基础中间件的自动化部署安装及运维 ...

  5. 一键式Spring集成工具 Spring Boot

    最近公司使用Spring boot进行开发,稍微了解一下,不过自我感觉把集中式配置applicate.properties搞明白,注解用过Spring MVC的boot绝对没问题的 比如拦截器:@As ...

  6. Azure Site Recovery 通过一键式流程将虚拟机故障转移至 Azure虚拟机

    Ruturaj Dhekane 云 + Enterprise 项目经理  现在,AzureSite Recovery可以通过其"灾难恢复至 Azure"功能保护您的工作负荷,并 ...

  7. 如何:使用 Visual Studio 中的一键式发布来部署 Web 应用程序项目

    原文: 如何:使用 Visual Studio 中的一键式发布来部署 Web 应用程序项目 本主题介绍如何在以下产品中使用 一键式发布 发布(部署)Web 应用程序项目: Visual Studio ...

  8. Visual Studio 2012 Web一键式发布

    按照保哥的介绍,尝试“ Web一键式发布”,但总是出错,主要就是404错误,不知道是什么原因.默认的 Web一键式发布是在C:\inetpub\wwwroot目录下,难道是权限问题?折腾N久无果.好吧 ...

  9. 无线网破解软件|一键式破解无线网|BT17软件包下载[笔记本+软件就行]

    从新版BT17发布到现在已经有一段时间,谢谢大家的一直来的关注.现在给大家讲解一下无线网破解问题,告诉 大家如何一键式破解WPA,WPA2,AES.Tkip等加密方式以及新版BT17软件包的下载地址. ...

  10. C# 制作Java +Mysql+Tomcat 环境安装程序,一键式安装

    原文:C# 制作Java +Mysql+Tomcat 环境安装程序,一键式安装 要求: JDK.Mysql.Tomcat三者制作成一个安装包, 不能单独安装,安装过程不显示三者的界面, 安装完成要配置 ...

随机推荐

  1. Solution -「CF 1073G」Yet Another LCP Problem

    Description Link. 给定字符串,正整数集合 \(A,B\),满足 \(\forall u\in A,v\in B,1\le u,v\le n\). 求 \(\sum_{i\in A}\ ...

  2. oracle:ORA-14765建索引阻塞创建分区及处理步骤

    在生产库建立一个索引,报ORA-14765创建索引时不能创建分区,也就是索引的创建阻塞分区的建立. 处理步骤: 1.与开发人员沟通昨天下午在Tbl_Waste表上建索引,一直未返回成功,定位问题SQL ...

  3. C++的extern关键字在HotSpot VM中的重要应用

    extern关键字有两个用处: (1)extern在C/C++语言中表示函数和全局变量作用范围(可见性)的关键字,这个关键字会告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用. (2)在C+ ...

  4. DevOps平台建设的关键点是什么?

    关键还是在人 找到一个「吃过猪肉,见过猪跑的」,你问他什么是猪,他自然比「没吃过猪肉,没见过猪跑的人」更了解猪.海豚海豚,你知道猪是什么样么?它都没上过陆地,这辈子都没见过猪,它哪知道猪是什么样. 有 ...

  5. DBeaver Ultimate 22.1.0 连接数据库(MySQL+Mongo+Clickhouse)

    前言 继续书接上文 Docker Compose V2 安装常用数据库MySQL+Mongo,部署安装好之后我本来是找了一个web端的在线连接数据库的工具,但是使用过程中并不丝滑,最终还是选择了使用 ...

  6. crontab guru

    https://crontab.guru/every-5-minutes Cron Job Monitoring crontab guru The quick and simple editor fo ...

  7. the solution of Mining Your Own Business

    the description of problem (我看的是 PDF 里面的原题所以这里描述会和题目不一样,但是大意一致) 给定一个未必连通的无向图,问最少在几个点设置出口,可以保证任意一个点坍塌 ...

  8. Jmeter-变量的嵌套使用

    场景: 有存在获取到多个登录账号,循环获取单个变量的情况. 常用方法: ${__BeanShell(vars.get("变量字段_${变量字段}"))} 取值示例: 思维扩展: 一 ...

  9. 普冉PY32系列(八) GPIO模拟和硬件SPI方式驱动无线收发芯片XN297LBW

    目录 普冉PY32系列(一) PY32F0系列32位Cortex M0+ MCU简介 普冉PY32系列(二) Ubuntu GCC Toolchain和VSCode开发环境 普冉PY32系列(三) P ...

  10. ELK+ filebeat

    ELK 企业级日志分析系统 ELK 概述 1.ELK 简介 ELK平台是一套完整的日志集中处理解决方案,将 ElasticSearch.Logstash 和 Kiabana 三个开源工具配合使用, 完 ...