前言
做了很多国际化项目、感觉每次都要维护几份翻译文件真的很头疼。每次还要在不同文件之间进行 CV 操作反复横跳,再加上还要找网站翻译,翻译了业务方觉得不专业,还要求改。整套流程下来真的是身心疲惫,有没有什么办法可以让我只维护一份翻译文件,一条命令自动生成其他翻译文件呢?于是我做了一个工具来帮我完成这个事情。
功能演示
接下来介绍今天的主角 auto-command 中的 translate 功能,先上功能演示图。
支持提示功能的配置化文件
只需要引入 defineConfig 这个方法就能获取全部配置,并知道哪儿些是必填属性。
快速生成对应的翻译文件
4.5s自动生成1358 条数据,因为这个工具是基于语种进行并发请求理论上语种越多相对效率会越高。以我一分钟 3 条数据的翻译速度(可能加上网速也太理想)效能提升5600 倍,若你的手速快一点,网速快一点能够达到一分钟 10 条数据的翻译速度,效能也可以提升1680 倍。某个同学不服,展现出单生十年来年练就的手速,达到一秒一条数据,效能也能提升280 倍。所以你再考虑什么呢?有国际化需求用就完事了。

快速试用基于 ant-design-pro
流程如下(可参考 gif 图):
- 进入仓库地址复制连接
- 把代码克隆到本地
- 使用 pnpm 安装
- 进入例子目录运行
npx tcmd

实际应用场景
场景一
若是你公司要求不高,机翻就能满足要求,那么这个工具太适合你了。恭喜你,你可以快乐的摸鱼了。按照你的手速和数据量估计工时,然后自动化翻译。嘿嘿嘿。
场景二
这也是我应用到的场景,因为我做的是金融行业,对词汇要求比较专业,领域里的一些专业名词会由业务方提供,页面其他地方的文本可以用机翻。那么可以配置一个 keep 属性,这个属性如果开启,工具就不会对已经翻译过的词汇进行翻译了。我是先用 auto-command 跑一遍生成翻译,再使用业务方提供的文档进行替换。后续考虑加一个识别文档自动替换的功能。
如果你的需求和我不谋而合,欢迎来提 pr,如果有其他需求也欢迎来提 issues。项目地址
使用方法
step1 安装到项目
$ npm i auto-command -D
# or
$ yarn add auto-command -D
# or
$ pnpm i auto-command -D
step2 在根目录增加配置文件.autocmd.ts
,详细配置说明
import { defineConfig } from 'auto-command/lib';
export default defineConfig({
translate: {
outDir: '你locales文件的绝对路径',
},
});
step3 使用 tcmd 命令选择 translate 选项(如上面我演示的一样),也可以在 package.json 里配置命令,直接使用 translate 功能。
{
"scripts": {
"trans": "ac --type=translate"
}
}
目前的功能
格式支持
auto-command 目前支持 ant-design/pro-components(单文件层格式)和 ant-design/ant-design-pro(目录层)两种格式,并用这个工具查出这两个项目国际化的一些缺陷,详情见 PRfeat: 国际化补充模式生成和feat: 国际化基于 en_US 全量补充 。~真棒,好像又解锁了一个新用法:混 pr 神器。~如果这两种格式不能支持到你欢迎来提 issues 和 pr,为开源世界的翻译事业添砖加瓦。
翻译器种类
目前支持谷歌翻译(国外)和有道翻译(国内),谷歌翻译没有翻译上限,完全免费,但是需要科学上网。有道翻译需要注册一个开发者账号拿到 key(auto-command 内置了 key,仅供测试使用),每个月有词汇上限,用完得付费。理论上支持翻译器上所有的语言(118 种),基于对 ant-design-pro 的支持,目前开放 54 种语言支持,如果你有需求欢迎提 issues。
关于
命令行执行基于@txpjs/translate仓库来进行翻译的,实际用了 i18n 这个 api,你如果有定制化翻译的需求也可以使用这个仓库的基础 api - translate 进行翻译(目前支持有道、谷歌)。
源码解读
这个翻译器分为三个模块:
- 核心翻译模块:对接 api 只负责翻译词汇。
- 业务翻译模块:负责对不同翻译器整合,拦截外部的参数进行优化,起到承上启下的作用。
- 主模块:负责生成用户可用的国际化文件。
核心翻译模块
这个模块的主要提供了翻译 api 和翻译器语言映射。api 接收 4 个参数:翻译的内容、翻译的语言、翻译器类型、额外参数(主要是 key、代理配置这些东西)。拿到这些东西去调 api。模块地址
业务翻译模块
这个模块主要是对接具体翻译业务,例如目前我的翻译业务是项目内国际化业务。后续可能增加文档翻译业务等等。
目前这个模块做了两个事情:
- 是统一 code,这个模块会基于核心翻译模块提供的翻译器语言映射+umi 的国旗组件(SelectLang)语言支持映射自动生成一个以 SelectLang 的 code 为主的新映射来实现 code 统一
- 处理文件命名的连接符号。国际化文件命名规范是
语种-地区
,中间的连接符号不是很严格,推荐-
,不过也有项目用的_
所以需要进行特殊处理配置。
主模块
这个模块主要涉及到文件读写文件(fs-extra)、文件解析(babel 系列)、数据处理、命令行日志输出。模块主要分为 3 个阶段:开始阶段、数据处理阶段、输出文件阶段
开始阶段
这个阶段会递归用户配置的目录,把目录文件转换成一个树状结构数据,每一层长这样
{
name, //名称
type, //类型:目录 or 文件
path, //绝对路径
content, // 内容:目录是节点,文件是字符串
}
这个阶段还会对文件进行处理,通过 babel 解析 ts 文件得到一个 JSON 格式的数据(下面称为文件树)。
这个阶段还提供一个 filter 钩子用来排除不需要处理的文件,目录型的第一层文件就不需要翻译,可以通过这个钩子排除掉。
数据处理阶段
这个阶段整个就是个自定义的钩子函数,会接收到开始阶段处理的文档树结构的数据和配置文档的参数。
因为目录树的数据格式都是规范的,所以在递归的时候可以通过判断当前节点有且仅有上面的 4 个属性来区分当前节点是在目录树里还是在文件树里。
如果是文件树节点且节点是字符串,就取出来组装成一个长字符串拿给翻译器翻译(文件里的数据用换行符进行隔断,文件与文件之间用两个换行符进行隔断,这样就可以实现一个语种翻译只需要请求一次,大大提高了性能),翻译完成后的字符串再进行逆处理形成一份翻译过的节点数据。这里不同语种请求基于 promise.all 进行并发请求,这样可以提升翻译速度。
翻译完了会得到一份和配置里 language.to 对应的翻译数据。用这份新数据和之前文件的数据进行对比把之前文件里的数据重新赋值到这份新数据里面,就实现了 keep 属性的功能。如果 keep 设置为 false 就直接输出这份翻译数据
输出文件阶段
文件输出就是开始阶段的逆向操作,从数据处理阶段拿到最终数据,递归数据,生成与数据结构对应的文件结构。
源码解读
这个翻译器分为三个模块:
- 核心翻译模块:对接 api 只负责翻译词汇。
- 业务翻译模块:负责对不同翻译器整合,拦截外部的参数进行优化,起到承上启下的作用。
- 主模块:负责生成用户可用的国际化文件。
核心翻译模块
这个模块的主要提供了翻译 api 和翻译器语言映射。api 接收 4 个参数:翻译的内容、翻译的语言、翻译器类型、额外参数(主要是 key、代理配置这些东西)。拿到这些东西去调 api。模块地址
业务翻译模块
这个模块主要是对接具体翻译业务,例如目前我的翻译业务是项目内国际化业务。后续可能增加文档翻译业务等等。
目前这个模块做了两个事情:
- 是统一 code,这个模块会基于核心翻译模块提供的翻译器语言映射+umi 的国旗组件(SelectLang)语言支持映射自动生成一个以 SelectLang 的 code 为主的新映射来实现 code 统一
- 处理文件命名的连接符号。国际化文件命名规范是
语种-地区
,中间的连接符号不是很严格,推荐-
,不过也有项目用的_
所以需要进行特殊处理配置。
主模块
这个模块主要涉及到文件读写文件(fs-extra)、文件解析(babel 系列)、数据处理、命令行日志输出。模块主要分为 3 个阶段:开始阶段、数据处理阶段、输出文件阶段
开始阶段
这个阶段会递归用户配置的目录,把目录文件转换成一个树状结构数据,每一层长这样
{
name, //名称
type, //类型:目录 or 文件
path, //绝对路径
content, // 内容:目录是节点,文件是字符串
}
这个阶段还会对文件进行处理,通过 babel 解析 ts 文件得到一个 JSON 格式的数据(下面称为文件树)。
这个阶段还提供一个 filter 钩子用来排除不需要处理的文件,目录型的第一层文件就不需要翻译,可以通过这个钩子排除掉。
数据处理阶段
这个阶段整个就是个自定义的钩子函数,会接收到开始阶段处理的文档树结构的数据和配置文档的参数。
因为目录树的数据格式都是规范的,所以在递归的时候可以通过判断当前节点有且仅有上面的 4 个属性来区分当前节点是在目录树里还是在文件树里。
如果是文件树节点且节点是字符串,就取出来组装成一个长字符串拿给翻译器翻译(文件里的数据用换行符进行隔断,文件与文件之间用两个换行符进行隔断,这样就可以实现一个语种翻译只需要请求一次,大大提高了性能),翻译完成后的字符串再进行逆处理形成一份翻译过的节点数据。这里不同语种请求基于 promise.all 进行并发请求,这样可以提升翻译速度。
翻译完了会得到一份和配置里 language.to 对应的翻译数据。用这份新数据和之前文件的数据进行对比把之前文件里的数据重新赋值到这份新数据里面,就实现了 keep 属性的功能。如果 keep 设置为 false 就直接输出这份翻译数据
输出文件阶段
文件输出就是开始阶段的逆向操作,从数据处理阶段拿到最终数据,递归数据,生成与数据结构对应的文件结构。
结语
最后,欢迎提需求、提 pr。如果你也对效能提升感兴趣欢迎联系我一起交流。