摘要:研究了一个创新方案,可以实现自动收录词条、自动转译代码以及自动翻译的工具包,可以帮你自动化完成国际化工作。还有个优点就是,它不会对你的代码和写代码习惯造成影响,可以潜移默化地为你完成国际化工作
喜欢的话,麻烦github上点个Star吧,谢谢大家
前言
国际化,老生常谈的技术,现行业内已经有许多成熟的技术方案。而我,自己也不止一次次的造轮子过。后来突然萌生了一个想法,行业内都是用的这套技术思路去实现的,我想来个不一样。 一开始仅是兴趣使然,后来越搞越复杂,曾经想放弃,再到好不容易折腾出个雏形,还给项目使用上了,欸,这时候发现,还挺好用的,起码,对比之前一个个项目搞个国际化,跟使用我现在的折腾出来的自动化国际化工具比起来,开发效率提高不下90%,大大提高了生产力。
文章开始前,想跟大家先说明白一个事,我实现的工具,跟现有网上查到大部分资料或我接触过的方案不太一样,但是没有意思说跟现在方案决一高下,也并没有想说我的技术方案比现在的好。只是说折腾了一个较为通用的工具出来,分享给大家,喜欢的人可以使用下,或者挺适合那种想要快速实现国际化的项目用等等。
本文篇幅较长,若你不想了解国际化的背景情况,以及我诞生工具的前因后果,那么可以直接跳转 i18n-auto-webpack 浏览工具的使用说明
国际化核心思路
不论是现有的技术方案还是我写的工具,要实现国际化,核心思路是一样的,只是具体实现每个部分的手段会不一样。
除这里介绍的思路外,当然现有方案中还有其他方式实现国际化,例如多个语言站点等等,我就介绍目前使用较广的这种,且跟我的工具实现思路是一样的。
我把实现国际化的手段分为四部分:
- 收集词条
- 翻译词条
- 国际化语言切换
- 源码替换
收集词条 & 翻译词条
实现国际化,首先需要知道我们的网站有什么东西需要进行翻译的。例如我们网站上所有的中文都要实现国际化,需要翻译成英文、法文、德文等。我们就需要提前准备一份中文、英文、法文、德文的词条配置文件,在用户需要展示什么语言的时候,就读取对应的配置文件。
所以实现国际化的第一步,就是要把你项目代码里的所有中文先收集起来生成一份中文配置表文件,然后把你所需要国际化的语言按照这份中文表进行翻译,分别生成各语言配置表文件。
词条表文件的格式一般为
// file zh.json
{
key: value
}
// value为词条,key为该词条对应的编号
// 如 { '001': '你好'},而在英文配置文件中就是{ '001': 'Hello'}
当然格式随你喜欢,主要是要配合下面说「国际化语言切换」这部分内容使用,怎么使用方便就用什么格式。
国际化语言切换
怎么理解这部分呢,例如你的代码里原本就是写着中文的词条的,你需要把这些中文实现国际化,你怎么实现呢?
基本都是写个国际化转换函数,然后把源码中的中文改写成使用这个函数去进行转换。举个最简单的例子,例如词条配置表的格式如上节所说,那么国际化转换函数可以这么写:
// i18n.js
import zhWord from 'zh.json'
export function changeI18n (key) {
return zhWord[key]
}
// index.js
import { changeI18n } from 'i18n.js'
alert(changeI18n('001'))
// 原本代码是写着 alert('你好')
当然上面的例子是十分简单的,仅仅是为了展示把源码替换切换函数,然后根据传入的词条编号选择对应展示的词条。上面的例子是固定写着读取的是中文的词条,实际上的切换函数要复杂得多,需要根据用户操作等选择正确的国际化语言词条配置文件进行读取然后返回对应编号的词条。
实际上这块切换的逻辑实现,现在针对各大语言框架都有各自较为成熟的类库了。如Vue
的vue-i18n
,React
的react-intl
和react-i18next
,angular
的ngx-translate
等等。
源码替换
这个实际上就是把代码中所有词条(如中文)都替换成上节所说的国际化语言切换函数。
代码少,都好说,顶多就是人工替换,一个个找出来一个个手动更换。但是我们是程序员,解放双手是我们的使命。有些人可能会自己写程序来批量替换项目中的所有词条。
方案差异
了解过核心的思路后,我们再来对比下现有的方案和我写工具的思路上有哪些差异。
如上所说,实现国际化的手段的四部分:
- 收录词条
- 翻译词条
- 国际化语言切换
- 源码替换
除了「国际化语言切换」这部份是一样的外,其余实现上会有些差异。
现有方案
现在的大多数方案,在收集翻译以及替换源码上,除一开始实现国际化时可能会采用程序进行一次性完成后,后续的维护(增删改词条),可能都是通过人工维护。例如大家写代码需要写到中文,就会在编写代码的时候主动以切换函数的形式写上,然后自己去中文词条表文件中新增一条记录,又去翻译一下然后在对应语言表中新增一下。当然上面这种说法并不是绝对呀,也会有不少人后面统一写好代码后再利用程序统一在收集翻译并替换一次。
那么大家用的程序是大概怎样的呢?
基本上收录词条和源码替换可能是一起实现的,他们会写个程序直接访问本地项目中的所有文件,然后找出文件内的词条收集起来生成配置文件,并在这个收集过程中可能顺带也把源码给替换了。程序完成之后就能看到项目的中代码都永久性更改了。
而至于翻译,有的可能就直接人工翻译,去翻译网站翻译,抑或自己利用翻译的api写个程序实现翻译后然后把翻译结果更新到各个语言表文件中。
我的方案
我的方案的最大差异就在于替换代码这一步,工具里的替换代码不是永久性的,即不会破坏源代码,而是在项目代码编译阶段,才进行词条的代码替换。现有的技术方案是直接对源代码着手的,一旦替换完成就会形成新的源代码。
我之所以把方案改革的着手点放在这里的原因,是我在使用现有技术方案的时候老是心中觉得有一些不便利性,于是尝试换成这种形式的替换看看。现在这种编译时替换带来的好处有:
- 不会破坏源代码,即维持了原有代码的可阅读性。假设使用现有技术方案进行永久性替换的话,阅读代码的时候只会看到一串词条编号,不容易理解。当然有些方案是直接采用中文作为key的,这样在减少对阅读性的影响(实际上中文为key会有一些问题)
- 搜索查找便利。假设使用现有的技术方案进行永久性替换的话,想在代码中寻找某些词条时,搜索的结果只会引导你到词条配置表文件中,然后再根据对应的key去代码中再次搜索。我觉得有点绕,不太方便。
- 保持原来的开发风格和习惯。若采用现有技术方案,那么开发者在编程时可能就需要注意提醒自己写到词条时就需要用国际化切换函数替代,也有存在遗忘的风险。用我提供的工具的话,对开发者现有的开发习惯不会造成影响,原来该怎么写代码就怎么写,也能弥补遗忘的情况。
因为代码替换工作在编译代码阶段进行,因此收集词条的工作也随着编译阶段进行,即在编译阶段,一边收录词条一边替换代码,收集一个替换一个同步进行。
而翻译工作,则是在收录完毕代码中的词条后,自动调用翻译api接口进行批量翻译。
与原有开发流程契合度高。
整体方案上是围绕着代码编译进行的,所以这就刚好跟我们的工程化相当于符合,不论是打包生产还是本地开发调试,都需要编译构建工程,那么我们刚好就利用这些工作流程,搭乘编译流程的顺风车,进行这部分的国际化工作。
这样,就不用像原来现有方案那样,国际化工作和开发流程独立开来,例如要单独跑脚本先进行国际化工作,然后启动工程构建运行进行调试,这些都是独立的两个命令进行。
现在就可以直接一个启动构建命令,完成国际化和开发调试/生产构建的工作了。
上面所说的所有这些我的方案的差异点,都为我折腾出来的工具所谓 「自动化」 奠定了基础。
工具优点与自动化
在介绍我的工具之前,先聊下工具的优点以及其自动化情况。
实际上自动化就是我的工具的最大优点,在自动化背后各处体现着它更细的各优先。
所谓自动化,就是让开发者在不改变其开发习惯下,在正常的开发过程中,悄悄帮你完成自动化操作。
即你原来是怎么写代码的,现在还是怎么写代码。在你启动构建命令时,自动开启了国际化的工作,不需要额外的命令辅助开启工作,也不需要写代码时特意写国际化切换函数。甚至并不会影响你对源代码的阅读与查询,丝毫不改变你的开发习惯。
自动收录词条
开发者并不需要关心收录词条这块工作。
没有收录词条的配置文件,自动创建生成;你边开发敲入中文词条代码,边帮你自动收录这个新输入的词条到配置文件中,自动编号。甚至你在代码中删除了词条,也自动帮你从配置文件中删除掉。
自动替换代码
能够自动根据收录的词条配置情况,自动匹配词条编号,自动转换源码,自动输出结果。自动追踪新收录的中文词条,自动替换。
自动翻译
可以自动根据收录的中文词条,自动翻译成所需的外国语言,并自动生成/更新到国际化语言配置文件中。自动追踪新收录的中文词条,自动追加新增翻译的词条。
不受语言限制
由于前端框架语言语法不一样,若采用传统方式编写程序提取本地文件里的内容,按照字符串来匹配找出中文,一般写一套程序只针对一套框架语言,如vue
文件,一开始写了一套程序提取vue
文件里的中文,后面转用angular
了,需要改造原来程序或者另外写一套逻辑来提取angular
文件的中文。
而采用本工具,若你的工程webpack
构建的工程,那么使用该工具可以不受语言限制,不论是vue
还是angular
,甚至是其他编程语言,也是有可能使用该工具实现自动国际化的。究其原因,可看下面的配置loader
部份介绍。
能力独立性使用
工具本身还具有分拆功能独立性的特点。当你整个完整工具进行使用时,就具有上述虽有的自动化特性。但可能你有某些需求,并不一定要求全部使用工具的全部能力时,你还能单独选择能力进行使用,能够根据你的所需所选择。
根据配置,用户能仅使用它的自动收录词条能力,也能单独使用它的自动翻译能力,注意它可是支持批量翻译的,关于翻译能力这块后续会单独介绍
使用过翻译api的经验的人可能会清楚这块,由于翻译api存在诸多限制,因此自己写程序实现翻译还是需要点时间的,而这里我的工具可单独提供该翻译能力,意味着它其实可以单独当个类库进行使用,已经合理规避了翻译api的各种限制了。
可支持生成映射文件
可以生成映射文件,记录哪个文件里有哪些中文词条,每个中文词条在该文件中出现多少次。有了这个映射文件的信息,你还能做更多事情。例如对于大量的词条,可拆分为多个配置文件,而不是一个大表等等。
i18n-auto-webpack
我的工具名叫i18n-auto-webpack
。顾名思义,这是一个专门webpack
使用的工具包。
它不单单可以对中文进行国际化,还可以按照你指定的语言来进行工作。
后续看情况看是否也增加对其他构建工具的支持(当然工具没啥人支持就算了)
这个工具包主要分为两个主要部分
- loader。这是一个自定义的
webpack
loader,需要指定引入该loader以完成自动收集与替换代码主要功能。 - plugin。这是一个自定义
webpack
plugin,需要使用该插件以完成生成词条配置文件的功能。
使用的方法也就是webpack
对loader和plugin的使用方法。
需要注意,该工具不包含实现国际化转换函数。每个项目采用的语言框架不一样,各自采用的转换函数调用方式也都不一样,该工具是要搭配国际化转换函数一起使用的。如你项目是
vue
,可使用vue-i18n
,React
的react-intl
和react-i18next
,angular
的ngx-translate
,或者自己实现转换函数等等。i18n-auto-webpack
只提供收录词条、替换代码为指定的国际化转化函数、以及翻译词条能力(除非你就是想单独使用这些能力)。
这里说的转换函数,如vue-i18n的
$t
之类的方法
Usage
安装
npm i i18n-auto-webpack
在工程项目根目录下创建全局配置文件i18nauto.config.js
const {resolve} = require('path')
const rootPath = process.cwd()
module.exports = {
// 中文词条的配置文件
entry: {
filename: 'zh.json', // 文件名(不含目录路径)
path: resolve(rootPath, './lang') // 文件所在绝对目录(不含文件名)
},
// 翻译配置
translate: {
on: true, // 是否开启翻译
lang: ['en'], // 要翻译成哪些语言
path: resolve(rootPath, './lang'), // 生成的翻译文件所在目录
secretId: 'your secretId', // 必填。翻译api所需的你用户信息secretId
secretKey: 'your secretKey' // 必填。翻译api所需的你用户信息secretKey
}
}
entry
是指定根据已有的中文词条配置表基础上更新收集的词条,没有的话直接创建。
例如你项目里已经存在一份中文词条配置文件,抑或是你之前就已经利用该工具生成过一份配置文件了,接下来的开发是需要基于这份已有的文件进行更新新增或被删除的词条。
默认情况下,收集完成后同样会更新到entry
指定路径的文件中。即这个文件既被用来作为收集词条的基础,又被作为最终生成的配置文件。若指定路径文件不存在,会自动创建该文件。
特殊情况下,假设你有需要生成配置文件到不同于
entry
的地方,那么可以指定output
字段,该字段默认值就是entry
,你也可以自己指定。但是值得注意的是,当你指定了一个不同于entry
的值,若entry
和output
的文件不能保持一样,就会每次收集词条就会把output
里比entry
多的词条视为新增的词条,就会触发重新生成配置文件,所以当你毅然选择指定output
,请保持手动同步更新到entry
文件中(当然你有另外用途需要区分开来除外)。 因此我的建议是,没啥特殊情况,就只使用一个entry
字段就好了
i18nauto.config.js
更多配置规则请查阅i18nauto.config.js
配置表
translate
翻译设置部份,需要有更多的说明,请查阅配置翻译
配置loader
在webpack的配置中进行如下设置
exports.export = {
module: {
rules: [
{
test: /\.js$/,
loader: 'i18n-auto-webpack/loader',
options: {
watch: true,
name: 'i18n.t',
dependency: {
name: 'i18n',
value: 'src/common/i18n.js'
}
}
}
]
}
}
对什么文件内的中文要进行收录,就对这些文件使用工具的loader
,引用的loader
路径为i18n-auto-webpack/loader
。
例子中options
选项的为基础常用的配置:
watch
是否监听更新,若设置true
,则开发者编写代码每触发一次热更新,就收集一次代码中新增的中文词条替换代码。若设置为false
,则只对第一次启动工程构建的文件进行收集词条替换代码,后续开发中新增的不会对新增的词条进行代码替换。默认为false
。 可多个loader
使用不同的watch
。
name
国际化语言切换函数的调用路径。例如你原本想要替换代码中的中文词条为国际化语言切换函数,怎么调用这个函数就传什么,例如i18n.t('001')
,虽然最终执行的是t
函数,但是你调用这个t
需要通过i18n
这个对象,那么完整的调用路径即为i18n.t
,所以name要传i18n.t
。
dependency
国际化语言切换函数所需的依赖。例如i18n
这个对象的内容是封装在这个src/common/i18n.js
文件中导出的,要进行切换,需要用到i18n
这个对象里的t
函数。那么dependency
就有两种写法:
// 写法一
options: {
name: 'i18n.t',
dependency: {
name: 'i18n',
value: 'src/common/i18n.js'
}
}
// 上述写法会最终生成代码 import i18n from 'src/common/i18n.js'
// 然后代码中将会使用i18n.t()进行切换语言
// 写法二
options: {
name: 't'
dependency: {
name: 't',
objectPattern: true, // 表示解构形式
value: 'src/common/i18n.js'
}
}
// 上述写法会最终生成代码 import { t } from 'src/common/i18n.js'
// 然后代码中将会使用t()进行切换语言
loader更多配置请查阅 loader配置表
如果你出现首次运行该工具收集的词条是正常的,但是后续再次收集发现少了,那么可能是受限于构建脚手架或者webpack某个版本的影响,对编译结果进行了缓存,导致无法收集。你可尝试禁用webpack的cache-loader缓存看看,如下所示
// 禁用缓存
// webpack-chain写法
config.module.rule('vue').uses.delete('cache-loader');
config.module.rule('js').uses.delete('cache-loader');
config.module.rule('ts').uses.delete('cache-loader');
注意事项
i18n-auto-webpack/loader
预期接收的代码是Javascript
内容,它的工作原理是对传递进来的是Javascript
代码解析成AST
,然后分析AST
查找提取中文等系列操作后再转回去Javascript
代码。
所以当你若要对一个非Javascript
内容的文件使用i18n-auto-webpack/loader
时,请在其他loader
将其转化为Javascript
后使用,请确保loader
的执行顺序。
例如.vue
文件,你想收录.vue
文件里template
和script
部份的中文词条,则要对.vue
文件使用i18n-auto-webpack/loader
。
// webpack config file
const i18nAutoLoaderOptions = {
watch: true,
name: 'i18n.t',
dependency: {
name: 'i18n',
value: 'src/common/i18n.js'
}
}
// 针对vue-loader v14版本
module.exports = {
module: {
rules: [
// 在原本使用vue-loader的那个规则上新增它的options配置,加上对'i18n-auto-webpack/loader'的处理
{
test: /\.vue$/,
use: [
{
loader: 'vue-loader',
options: {
postLoaders: {
// 这里是在template部份转化成render函数(js代码)后用'i18n-auto-webpack/loader'处理
html: {
loader: 'i18n-auto-webpack/loader',
options: {
watch: true,
name: '_vm'
}
},
js: {
loader: 'i18n-auto-webpack/loader',
options: i18nAutoLoaderOptions
}
}
}
}
]
}
]
}
}
// 针对vue-loader v15版本
module.exports = {
module: {
rules: [
// 这是原本对vue文件使用vue-loader的规则,一般来讲你是不需要自己加这个规则的,我这里写出来是为了让你更好的区分下面那个关于vue文件的loader
{
test: /\.vue$/,
loader: 'vue-loader'
},
// 针对vue文件里的template部分。这个要新起一个规则,不能用在原来使用`vue-loader`的那个规则里
{
test: /\.vue$/,
resourceQuery: /type=template/,
enforce: 'post',
loader: 'i18n-auto-webpack/loader',
options: i18nAutoLoaderOptions
},
// 同样会适用于vue文件里的script部份的js代码
// 注意这是对你项目原本处理js文件的rule基础上,加上我这个loader,注意loader得顺序。而不是直接新写这么一个rule,我这个只是示例。
{
test: /\.js$/,
use: [
{
loader: 'i18n-auto-webpack/loader',
options: i18nAutoLoaderOptions
},
{
loader: 'babel-loader'
}
]
}
]
}
}
// 针对vue-loader v16到目前最新版本
//在这个版本的vue-loader就很友好了,实际上你对js文件的规则配置也同样适用于vue文件里的script部份和template部份了。因此只需要在原来配置js文件的规则添加多一个i18n-auto-webpack/loader即可。
module.exports = {
module: {
rules: [
// 注意这是对你项目原本处理js文件的rule基础上,加上我这个loader,注意loader得顺序。而不是直接新写这么一个rule,我这个只是示例。
{
test: /\.js$/,
use: [
{
loader: 'i18n-auto-webpack/loader',
options: i18nAutoLoaderOptions
},
{
loader: 'babel-loader'
}
]
}
]
}
}
你可能会看到这么多版本的vue-loader
怎么使用我这个i18n-auto-webpack/loader
还不同的写法,觉得好像好复杂似的。但是这并不是我这个loader
的问题,我的loader
负责的事情是很简单单一的,就是把Javascript
的代码中找出中文替换而已。因此使用它的写法不同取决于它的上游loader
,这是上游loader
的责任,这里就是vue-loader
的责任了,谁让它改版没有向前兼容。
理论上i18n-auto-webpack/loader
是可以对任何前端框架或语言进行使用的,只要有合适的loader
将其转换成Javascript
后使用。
配置plugin
在webpack的配置中进行如下设置
import i18nAutoPlugin from 'i18n-auto-webpack/plugin'
module.exports = {
plugins: [
new i18nAutoPlugin({
watch: true
})
]
}
是否监听更新,若设置true
,则开发者编写代码每触发一次热更新,就收集一次代码中新增的中文词条更新到配置文件中。若设置为false
,则只对第一次启动工程构建的文件进行收录词条创建配置文件,后续开发中新增的不会更新到配置文件中。默认为false
。
若你需要手动更改中文词条配置文件(处于校验阶段,或第一次使用预料着可能需要手动调整更正),建议不要开启watch
,不然可能覆盖你的调整。
plugin更多配置请查阅 plugin配置表
配置翻译
要开启自动翻译功能,需要在i18nauto.config.js
中进行相应的设置。
const {resolve} = require('path')
const rootPath = process.cwd()
module.exports = {
// 翻译的常用配置
translate: {
on: true, // 是否开启翻译
lang: ['en'], // 要翻译成哪些语言
path: resolve(rootPath, './lang'), // 生成的翻译文件所在目录
secretId: 'your secretId' // 必填。翻译api所需的你用户信息secretId
secretKey: 'your secretKey' // 必填。翻译api所需的你用户信息secretKey
}
}
本工具采用的翻译api是腾讯翻译。至于为什么选择腾讯翻译,当然有做过各种翻译api的调研,最后发现还是腾讯翻译的api相对来讲使用起来会更加有优势(除去翻译结果外,谁更准我这英语业务水平不好判断)
当你项目有大量词条需要被翻译时,其实你更多的不会关心翻译出来的是否准确,基本差不了哪里去的。如果你觉得个别翻译不准,可自己手动在翻译出来生成的配置文件中修改就可以了。本身不论你是用哪个api,最终还是需要有一个人工的校正工作。
因为采用的是第三方的翻译api,现在所有的翻译api都是要求注册用户获取授权才能调用的,而且免费用户都是有使用额度的。所以要想使用这个工具的翻译能力,首先使用者需要去注册腾讯云的机器翻译,获取secretId
和secretKey
写在配置文件里。
可能有人就会问为啥我这个工具自身里面用注册过的用户身份去调取api,而要每个使用者自己提供用户身份。首先,大家可以去查一下,基本实现翻译的工具类库,都是会要求使用者提供用户身份Id的,不可能工具内部用一个帐号去帮大家调用api的,因为api是需要收费的,一个月内有免费的额度。如果大家都用一个帐号,那么这个费用就很高了,而且还要类库的开发者自己承担。其次,用户身份是很重要的,需要用户自己保管,类库开发者不能把用户身份信息暴露在源代码中。
出于安全考虑,当你的项目工程代码是公网公开的,请勿把你或你公司的帐号的
secretId
和secretKey
直接写在i18nauto.config.js
中,请使用环境变量进行替代,读取本地机器上的环境变量。或者是把i18nauto.config.js
文件设置在.gitignore
中进行git版本控制忽略。这样就不会存在别人盗用你的帐号的风险。
获取secretId
和secretKey
先访问腾讯云控制台,进行注册登陆。
进入用户-用户列表,找到对应用户(若无则新建用户)。
点击进入用户详情,选择进入API密钥导航
可以看到截图左下角有个密钥列。这里面就可以找到secretId
和secretKey
了。
翻译api的限制
腾讯机器翻译api有两个限制需要我们留意下的:
- 文本翻译的每月免费额度为5百万字符。
- 1秒内请求api不得超过5次。
其实一般来讲,每月免费额度为5百万字符是够用的,一个项目中中文大部份应该不会超过5百万字符,假设真的超过了,可以考虑分月分批翻译。
为了让使用者用该工具更安心,我这边提供了两个配置项用于设置当翻译超过你指定的字符数限制时,停止调用翻译api。具体可查看 startTotal和endTotal说明
注意这个数量,是按照翻译语言种类来统计的。例如“你好啊”这个要翻译成英文和德文,那么就是 3 * 2 = 6,这个就消耗了6个字符的额度了。 3是中文的字符数,2的翻译成两个语种。
而第二个限制,i18n-auto-webpack
这个工具内部已经实现了节流以满足这个条件要求。但是正因为此举,假设你的编码IDE是设置了自动保存的,而你边敲代码边自动保存触发了重新编译,就会触发收录中文词条,收录到了新的中文词条就会触发了翻译,若你保存次数很多很快,就会频繁触发翻译接口,而i18n-auto-webpack
因为做了节流处理,就会可能导致翻译没那么实时,这是无法避免的。加上我们敲代码可能会输错或者来回改动,触发翻译越多,翻译字符数量也会上升,而免费额度只有5百万字符。
因此,对于这两个限制条件来讲,要开启自动翻译功能,我的建议是
- 要么你的编码IDE设为手动保存减少频繁触发热更新;(最佳实践)
- 要么把loader和plugin部分的
watch
配置设置为false
,不要实时监听变化而更新; - 要么把自动翻译功能关闭,选择
i18n-auto-webpack
提供的单独翻译能力i18n-auto-webpack/translate
方法,编写命令,在你想要翻译的时候手动触发该命令进行翻译。(最稳妥方案)
关于使用单独的翻译能力,请查阅下面的独立翻译函数
介绍
至此,上述介绍的配置,是可以快速实现自动化国际化的简单配置。若想了解更多功能和定制化,可继续查阅下属的详细配置表
独立翻译函数
使用i18n-auto-webpack/translate
提供的能力,能够单独编写脚本实现翻译能力。
这样就能够依赖它来编写nodejs
脚本,然后写在npm script
里写个命令,专门在开发者基本编写好代码后,运行命令做最后的统一翻译。这样就可以关闭自动翻译,能规避翻译api的条件限制导致的一些问题(见上节内容【翻译api的限制】)
或者你已经做好国际化的方案了,已经实现了代码替换和收录中文了,剩下的就只有翻译工作还没做,还没生成翻译语言的配置文件,那么你也可以使用i18n-auto-webpack/translate
来帮你完成这一步工作。
如果想单独使用这个翻译能力,仍然需要配合
i18nauto.config.js
文件使用,需要设置翻译接口所需的你的secretId
和secretKey
使用示例
// 引入翻译方法
const { createTranslate } = require('i18n-auto-webpack/translate')
const path = require('path')
// 翻译的目标配置
const target = {
path: path.resolve(__dirname), // 翻译的结果放在哪个目录中
lang: ['en'] // 要翻译出来的语言
}
// 翻译的源内容配置
const source = {
path: path.resolve(__dirname, 'zh.json') // 根据哪个中文配置文件来生成对应的国际化语言配置文件
}
// 执行该方法后翻译结果会自动创建/修改到指定的目录中
createTranslate(target, source)
入参
方法的参数功能介绍:
一共三个入参,按顺序如下 target
, source
, needFile
关于<Obejct>target
:
属性名 | 类型 | 功能描述 | 默认值 | 是否必填 |
---|---|---|---|---|
path | String | 翻译文件的目录绝对路径,不包含文件名的 | 运行该方法的路径 + lang目录,没有则会自动创建该目录 | 否 |
lang | Array | 翻译语言列表 | ['en'] | 否 |
nameRule | Function | 设置翻译文件的名称格式 | lang + '.json' | 否 |
关于<Obejct>source
:
属性名 | 类型 | 功能描述 | 默认值 | 是否必填 |
---|---|---|---|---|
path | String | 翻译来源文件的目录绝对路径,包含文件名的 | - | 是 |
关于needFile
Booealn
类型,默认是true
。
代表是否需要把翻译的结果生成到指定的目录中
- 若存在同名文件,则会根据已存在的文件内容来判断哪些中文需要进行翻译,然后把翻译结果合并到该文件里。
- 若不存在同名文件,则会直接把所有中文翻译后创建出来。
返回值
返回的是Promise
。返回的回调结果是一个对象,以翻译的语言为key
,翻译结果为value
,如
en: {
1: 'hello',
2: 'Who am i'
}
单独的能力
使用i18n-auto-webpack
可以使用它的完整功能,但是可能有部分人仅仅是看中了该工具的其中某个能力,而不需要使用它的全部功能。
仅收录中文词条
开发者有需求仅仅是想收录项目代码中的中文词条,生成配置文件,而不需要转换代码中的中文词条,不想通过编译时转换代码这种方式。
适合场景: 不需要翻译,有专人翻译,并且你不想使用编译构建时转译代码,想直接写在源码中进行词条转换,而你不想人工收集本地词条,可用本工具完成收集工作
那么就可以在使用i18n-auto-webpack/loader
时设置transform: false
。
对转换的文件设置transform: false
,这里以js文件的配置为例子:
module.exports = {
module: {
rules: [
{
test: /\.js$/,
loader: 'i18n-auto-webpack/loader',
options: {
transform: false
}
}
]
}
}
plugin的设置还是需要的
仅使用翻译能力
若你已经实现了国际化方案,你已经做好了收录中文词条和转换代码的工作了,剩下的也只是想要把自己收录好的中文翻译成其他语言,还差这一步工作。
或者你正在寻找一个类库想实现翻译功能,去做别的需求。
适合场景: 你的项目里已有词条配置文件了,代码里也做好了转换,现在仅仅差各个语言的词条配置文件,即你只想实现翻译。
那么i18n-auto-webpack/translate
可以帮到你,我把该工具使用到的翻译能力单独抽离出来,可以当成独立的类库进行使用。
具体使用方法可参考独立翻译函数
因为翻译需要用腾讯机器翻译的用户信息,该部分信息可能不适合进行git版本的管理。考虑到这点,该工具支持项目本地创建local.i18nauto.config.js
来区分于i18nauto.config.js
,它的优先级更高,所以你的一些私人配置可写在local.i18nauto.config.js
上,再用.gitignore
屏蔽掉它即可。
仅使用编译转换代码能力
若你只想根据将代码中需要国际化的词条转换成国际化转换函数的依赖使用,其他能力不需要(自动翻译配置、自动生成收集源码词条配置、生成source map)
适合场景: 不需要翻译,有专人翻译,也有维护好的源码词条配置文件,后续的词条文件更新也是人工维护的,而你想自动根据已有的词条配置文件完成词条之间的代码转换。
对转换的文件设置fallback: true
以及watch: false
,这里以js文件的配置为例子:
module.exports = {
module: {
rules: [
{
test: /\.js$/,
loader: 'i18n-auto-webpack/loader',
options: {
watch: false,
fallback: true,
name: 'i18n.t',
dependency: {
name: 'i18n',
value: 'src/common/i18n.js'
}
}
}
]
}
}
仅需要设置loader,不需要plugin
生成映射文件
当你有需求想要知道项目里的哪些文件有中文词条,有什么中文词条,而且它在该文件中出现的次数是多少。
那么可以设置sourceMap: true
达到该目的。
const i18nAutoPlugin = require('i18n-auto-webpack/plugin')
module.exports = {
plugins: [
new i18nAutoPlugin({
sourceMap: true
})
]
}
特殊情况
个别不需要国际化
当代码中某些词条是不需要国际化的,那么可以在词条前添加行内块状注释的形式,单独指明该词条不需要国际化,如:
const word = /*no-i18n-auto*/ '你好';
new Error(/*no-i18n-auto*/ '报错了');
代码中仍需写转换函数时
当你有不得不需要在代码中直接使用国际化转换函数的时候,你仍然可以放心大胆使用。例如
// i18n.tc是国际化转换函数
const word = i18n.tc('1')
对应的词条表中是
// zh.json
{
"1": "你好"
}
i18n-auto-webpack
会自动根据你在loader
中设置的国际化转换依赖的name
值来判断你调用的方法是否为国际化转换函数,是的话,词条表中会为你保留对应的词条,而不会说因为代码中没有这个中文,会在词条表中删除对应的词条。当然,这时候你需要自己手动在词条表中设置对应的词条了。
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: [
{
loader: 'i18n-auto-webpack/loader',
options: {
watch: true,
name: 'i18n.tc', // 就是这个名字来判断
dependency: {
name: 'i18n',
value: 'src/common/i18n.js'
}
}
}
]
}
]
}
}
这种场景更多是的,你需要使用国际化转换函数传递更丰富的参数完成更丰富的能力、或者是带有html
标签的字符串需要直接渲染出来的(类似vue
的v-html
),如果你不自己处理成写成国际化转换函数的形式,那么就会把html
的标签也当成词条的一部分进行提取和翻译,可能会破坏你的逻辑。
若你使用国际化转换函数不仅仅是用name
指定的方式调用,还有其他方式调用, 你也想保留对应的key的词条,那么可以使用alias
配置,指定它的其他调用方式。例如使用了vue-i18n
,可以直接在组件中用this.$t()
来调用转换,此时你可设置alias: ['$t', '_vm.$t']
来保留调用它的key对应的词条:
{
loader: 'i18n-auto-webpack/loader',
options: {
watch: true,
name: 'i18n.tc',
alias: ['$t', '_vm.$t'],
dependency: {
name: 'i18n',
value: 'src/common/i18n.js'
}
}
}
此时代码中使用i18n.tc
和$t
的方法内key对应的词条都将保留。
此外,
使用i18n-auto-webpack
是一个提高工作效率的工具,也能相对成功找到代码中中文的词条进行替换,但是各种开发者写的各种代码,会存在各种可能性,我这边只能说把大部分常规场景都囊括进来,若你遇到特殊的写法或场景,使用该工具无法成功提取到中文,请告诉我,我将进行补充,或者你可能需要调整为直接在代码中使用国际化转换函数的写法。项目代码中存在直接使用国际化转换函数+中文(i18n-auto-webpack
帮忙收录国际化)的场景是无法避免的。
详细配置表
i18nauto.config.js
配置项 | 描述 | 类型 | 必填 | 默认值 |
---|---|---|---|---|
entry | 词条配置表的入口文件配置。因为编译转译代码时,需要知道已有配置表对应词条的key值,所以要先指定一个配置表文件先。注意这个文件是你要收集的语言的配置表,而不是翻译后的其他语言配置表,有以下属性: | Object | 是 | |
path :配置表文件的所属路径(不含文件名) | String | 否 | 项目根目录/lang | |
filename :配置表文件的文件名(不含路径) | String | 否 | zh.json | |
output | 生成代码中收录的词条配置表文件信息,有以下属性: | Object | 否 | 跟当设置了entry ,没有设置output ,那么跟随entry 设置 |
path :配置表文件的所属路径(不含文件名) | String | 否 | 项目根目录/lang | |
filename :配置表文件的文件名(不含路径) | String | 否 | zh.json | |
localePattern | 收录的语言正则表达式,默认是收录中文。所以你想收录其他语言,可根据实际传入可代表其他语言的正则表达式 | RegExp | 否 | /[\u4e00-\u9fa5]/ |
keyRule | 可自定义key的生成规则,接受两个入参,如nameRule (value, config) {} ,value 为收集到的词条,config 为当前的词条表对象,必须返回一个值表示新的key值 | Function | 否 | null |
translate | 设置自动翻译相关,有以下属性: | Object | 否 | false,不开启自动翻译 |
on :是否开启翻译 | Boolean | 否 | false | |
lang :要翻译成哪些语言 | Array | 否 | ['en'],英文。语言的标识可参考api | |
path :生成的翻译文件所在目录 | String | 否 | 项目根目录/lang | |
nameRule :生成的翻译文件名 | Function | 否 | nameRule (lang) {return lang + '.json' } | |
startTotal :表示你已经使用了多少字符额度了,本次启动服务触发的翻译字符数,将基于这个额度上进行计算 | Number | 否 | 0 | |
endTotal :当达到了指定的endTotal额度限制时,就不再触发翻译请求了。默认值就是腾讯翻译api的免费额度,不想限制传Infinity | Number | 否 | 5000000 | |
secretId :翻译api的用户身份secretId,请去腾讯云控制台查阅 | String | 是 | ||
secretKey :翻译api的用户身份secretKey,请去腾讯云控制台查阅 | String | 是 | ||
region :对哪个地区的语言进行翻译 | String | 否 | ap-beijing | |
endpoint :接口请求地址 | String | 否 | tmt.tencentcloudapi.com | |
source :要进行翻译的语言 | String | 否 | zh | |
projectId :项目ID,可以根据控制台-账号中心-项目管理中的配置填写,如无配置请填写默认项目ID:0 | Number | 否 | 0 |
关于startTotal
和endTotal
因为腾讯翻译api一个月有免费的翻译文本数量限制,最多5百万字符,若超出,则需要付费了。所以startTotal
和endTotal
的设置会让你使用得更安心些。注意,startTotal
只会从本次启动服务(如启动了dev-server)基于它进行累计计算。我们并不会知道之前的服务你使用了多少额度,所以你可能每次启动服务的时候都需要修改这个startTotal
可惜的是腾讯机器翻译api暂时还没有api可以查询用户使用额度
关于translate
下的子属性,从secretId
开始,都是遵循腾讯云翻译api的要求的配置。若想了解更多,可查阅 腾讯云翻译api文档
本地可同样新建一个
local.i18nauto.config.js
文件,如果本地项目中既存在i18nauto.config.js
又存在local.i18natuo.config.js
,那么以local.i18nauto.config.js
为准。该文件设立的初衷是,假设你有一些本地配置不想上传到git
,例如翻译的你个人用户secretId
和secretKey
,你不希望上传上去被共享,那么可以先用.gitignore
问价把local.i18nauto.config.js
屏蔽了,然后在该文件上写上你的私人配置
loader
配置项 | 描述 | 类型 | 必填 | 默认值 |
---|---|---|---|---|
includes | 支持实现国际化的文件(夹),元素值为文件(夹)的绝对路径 ,若为文件夹地址,请以/ 结尾,则文件夹下的文件都会实现国际化,可搭配excludes 使用,excludes 的优先级更高。 | Array | 否 | [] |
excludes | 排除实现国际化的文件(夹),元素值为文件(夹)的绝对路径 ,若为文件夹地址,请以/ 结尾,则文件夹下的文件都会被排除。可搭配includes 使用,excludes 的优先级更高。 | Array | 否 | [] |
dependency | 转译成国际化所需代码时,若你需要在这个文件中引入某些依赖,则可用该配置。目前只支持引入单个文件,后续需优化成支持多个,数组形式。有以下属性: | Object | 否 | |
name :引入的依赖所赋予的变量名,如import name from 'xxx' ,就是这里的name | String | 当设置了dependency,则必填 | ||
value :引入的依赖的路径,可以是任意格式的路径,实际上就是一个字符串,就跟你要写在代码里的import 或require 方法的路径是一样的即可。注意这个值会用来判断文档当前是否已经引入过该依赖的,判断的依据是直接根据这个路径字符串完全匹配判断,而不是跟实际引入文件判断,一个文件的引入路径写法不一样,会造成判断不准 | String | 当设置了dependency,则必填 | ||
objectPattern :引入的依赖的形式。若是解构格式,则需要设置为true。 | Boolean | 否 | ||
name | 替换代码中词条的实现国际化的函数调用完整路径名 | String | 是 | |
alias | 替换代码中词条的实现国际化的函数调用完整路径名的别称。支持正则表达式 | Array | 否 | |
watch | 是否实时监听文件变化,实时更新对应于配置表中的key值 | Boolean | 否 | false |
transform | 是否需要转换代码。若你仅仅想收录项目中的词条,而不转换代码,可设置为false | Boolean | 否 | true |
fallback | 当在词条配置文件中找不到,即新加的词条,是否保持原状,即不转译词条。true为开启不转译新词条。适合人工维护词条配置文件的场景 | Boolean | 否 | false |
plugin
配置项 | 描述 | 类型 | 必填 | 默认值 |
---|---|---|---|---|
output | 生成的配置表文件信息,优先级比全局配置文件i18nauto.config.js 高,有以下属性: | Object | 否 | |
path :配置表文件的所属路径(不含文件名) | String | 否 | ||
filename :配置表文件的文件名(不含路径) | String | 否 | ||
watch | 是否实时监听文件变化,实时更新配置文件。主要是针对开发环境启动后。当watch 为Object 时,有以下属性: | Object | Boolean | 否 | false |
on :是否开启监听 | Boolean | 否 | false | |
sourceMap | 是否生成映射表。当sourceMap 为Object 时,有以下属性: | Object | Boolean | 否 | false |
on :是否生成词条映射表,记录哪个文件有哪些词条 | Boolean | 否 | false | |
path :生成的映射文件存放路径(不含文件名) | String | 否 | 项目根目录/lang | |
filename :生成的映射文件名(不含路径) | String | 否 | zh.sourcemap.json |
不论怎样,第一次启动项目,如开发环境下启动项目,或打包生产环境,必然会根据实际情况需要看是否更新一次词条配置表。
其他
使用注释 /*no-i18n-auto*/
可单独指明哪句代码不需要转换
const word = /*no-i18n-auto*/ '你好';
new Error(/*no-i18n-auto*/ '报错了');
最后
关于我提供的i18n-auto-webpack
的前因后果都介绍清楚完毕了。如果大家感兴趣的话,使用它时遇到什么问题,欢迎提问题,我将尽最大能力为大家解答。
喜欢的话,麻烦github上点个Star吧,谢谢大家
本文正在参加「金石计划」