关于
javascript
国际化方案很多,比较有名的有fbt
、i18next
、react-i18next
、vue-i18n
、react-intl
等等,每一种解决方案均有大量的用户。为什么还要再造一个轮子?好吧,再造轮子的理由不外乎不满足于现有方案,总想着现有方案的种种不足之处,然后就撸起袖子开始干。
那么到底是对现有解决方案有什么不满?最主要有三点:
-
大部份均为要翻译的文本信息指定一个
key
,然后在源码文件中使用形如$t("message.login")
之类的方式,然后在翻译时将之转换成最终的文本信息。此方式最大的问题是,在源码中必须人为地指定每一个key
,在中文语境中,想为每一句中文均配套想一句符合语义的英文key
是比较麻烦的,也很不直观不符合直觉。我希望在源文件中就直接使用中文,如t("xxx万岁")
,然后国际化框架应该能自动处理后续的一系列麻烦。 -
要能够比较友好地支持多库多包
monorepo
场景下的国际化协作,当主程序切换语言时,其他包或库也可以自动切换,并且在开发上每个包或库均可以独立地进行开发,集成到主程序时能无缝集成。这点在现有方案上没有找到比较理想的解决方案。
基于此就开始造出VoerkaI18n
这个全新的国际化多语言解决方案,主要特性包括:
-
全流程支持
从文本提取/自动翻译/编译/动态切换的全流程工程化支持,适用于大型项目
-
集成自动翻译
调用在线翻译服务API支持对提取的文本进行自动翻译,大幅度提高工程效率
- 符合直觉
在源码中直接使用符合直觉的翻译形式,不需要绞尽脑汁想种种key
- 自动提取文本
提供扫描提取工具对源码文件中需要翻译的文本进行提取
-
TypeScript支持
内置支持TypeScript类型以及生成TypeScript源码
-
适用性
支持任意Javascript应用,包括
Nodejs/Vue/React/ReactNative
等。 -
多库联动
支持多包工程下多库进行语言切换的联动
-
工具链
提供Vue/React/Babel等扩展插件,简化各种应用开发
-
插值变量
强大的插值变量机制,能扩展支持复数、日期、货币等灵活强大的多语言特性
-
语言补丁
在应用上线后发现错误时可以在线修复
-
动态增加语种
可以在应用上线后动态增加语种支持
-
90%+测试覆盖率
核心运行时超过90%的测试覆盖率
快速入门
本节以标准的Nodejs
应用程序为例,简要介绍VoerkaI18n
国际化框架的基本使用。
vue
或react
应用的使用流程也基本相同,可以参考Vue集成和React集成。
shell
myapp
|--package.json
|--index.js
在本项目的所有支持的源码文件中均可以使用t
函数对要翻译的文本进行包装,简单而粗暴。
javascript
// index.js
console.log(t("xxx万岁"))
console.log(t("xxx成立于{}",1949))
t
翻译函数是从myapp/languages/index.js
文件导出的翻译函数,但是现在myapp/languages
还不存在,后续会使用工具自动生成。voerkai18n
后续会使用正则表达式对提取要翻译的文本。
第一步:安装命令行工具
安装@voerkai18n/cli
到全局。
shell
> npm install -g @voerkai18n/cli
> yarn global add @voerkai18n/cli
> pnpm add -g @voerkai18n/cli
第二步:初始化工程
在工程目录中运行voerkai18n init
命令进行初始化。
javascript
> voerkai18n init
上述命令会在当前工程目录下创建languages/settings.json
文件。如果您的源代码在src
子文件夹中,则会创建在src/languages/settings.json
settings.json
内容如下:
json
{
"languages": [
{
"name": "zh",
"title": "zh",
"default": true, // 默认语言
},
{
"name": "en",
"title": "en"
}
],
"namespaces": {}
}
上述命令代表了:
- 本项目拟支持
中文
和英文
两种语言。 - 默认语言是
中文
(即在源代码中直接使用中文) - 激活语言是
中文
(代表当前生效的语言)
注意:
- 可以修改该文件来配置支持的语言、默认语言、激活语言等。可支持的语言可参阅语言代码列表。
voerkai18n init
是可选的,voerkai18n extract
也可以实现相同的功能。- 一般情况下,您可以手工修改
settings.json
,如定义名称空间。 voerkai18n init
仅仅是创建languages
文件,并且生成settings.json
,因此您也可以自己手工创建。- 针对
js/typescript
或react/vue
等不同的应用,voerkai18n init
可以通过不同的参数来配置生成ts
文件或js
文件。 - 更多的
voerkai18n init
命令的使用请查阅这里
第三步:标识翻译内容
接下来在源码文件中,将所有需要翻译的内容使用t
翻译函数进行包装,例如下:
javascript
import { t } from "./languages"
// 不含插值变量
t("xxx")
// 位置插值变量
t("xxx{}","万岁")
t("xxx成立于{}年,首都{}",1949,"北京")
t
翻译函数只是一个普通函数,您需要为之提供执行环境,关于t
翻译函数的更多用法见这里
第四步:提取文本
接下来我们使用voerkai18n extract
命令来自动扫描工程源码文件中的需要的翻译的文本信息。 voerkai18n extract
命令会使用正则表达式来提取t("提取文本")
包装的文本。
shell
myapp>voerkai18n extract
执行voerkai18n extract
命令后,就会在myapp/languages
通过生成translates/default.json
、settings.json
等相关文件。
- translates/default.json : 该文件就是从当前工程扫描提取出来的需要进行翻译的文本信息。所有需要翻译的文本内容均会收集到该文件中。
- settings.json: 语言环境的基本配置信息,包含支持的语言、默认语言、激活语言等信息。
最后文件结构如下:
shell
myapp
|-- languages
|-- settings.json // 语言配置文件
|-- translates // 此文件夹是所有需要翻译的内容
|-- default.json // 默认名称空间内容
|-- package.json
|-- index.js
如果略过第一步中的voerkai18n init
,也可以使用以下命令来为创建和更新settings.json
javascript
myapp>voerkai18n extract -D -lngs zh en de jp -d zh -a zh
以上命令代表:
- 扫描当前文件夹下所有源码文件,默认是
js
、jsx
、html
、vue
文件类型。 - 支持
zh
、en
、de
、jp
四种语言 - 默认语言是中文。(指在源码文件中我们直接使用中文即可)
- 激活语言是中文(即默认切换到中文)
-D
代表显示扫描调试信息,可以显示从哪些文件提供哪些文本
第五步:人工翻译
接下来就可以分别对language/translates
文件夹下的所有JSON
文件进行翻译了。每个JSON
文件大概如下:
json
{
"xxx万岁":{
"en":"<在此编写对应的英文翻译内容>",
"de":"<在此编写对应的德文翻译内容>",
"jp":"<在此编写对应的日文翻译内容>",
"$files":["index.js"] // 记录了该信息是从哪几个文件中提取的
},
"xxx成立于{}":{
"en":"<在此编写对应的英文翻译内容>",
"de":"<在此编写对应的德文翻译内容>",
"jp":"<在此编写对应的日文翻译内容>",
"$files":["index.js"]
}
}
我们只需要修改该文件翻译对应的语言即可。
重点:如果翻译期间对源文件进行了修改,则只需要重新执行一下voerkai18n extract
命令,该命令会进行以下操作:
- 如果文本内容在源代码中已经删除了,则会自动从翻译清单中删除。
- 如果文本内容在源代码中已修改了,则会视为新增加的内容。
- 如果文本内容已经翻译了一部份了,则会保留已翻译的内容。
总之,反复执行voerkai18n extract
命令是安全的,不会导致进行了一半的翻译内容丢失,可以放心执行。
第六步:自动翻译
voerkai18n
支持通过voerkai18n translate
命令来实现调用在线翻译服务进行自动翻译。
javascript
>voerkai18n translate --appkey <在百度翻译上申请的密钥> --appid <在百度翻译上申请的appid>
在项目文件夹下执行上面的语句,将会自动调用百度的在线翻译API
进行翻译,以现在的翻译水平而言,您只需要进行少量的微调即可。关于voerkai18n translate
命令的使用请查阅后续介绍。
第七步:编译语言包
当我们完成myapp/languages/translates
下的所有JSON语言文件
的翻译后(如果配置了名称空间后,每一个名称空间会对应生成一个文件,详见后续名称空间
介绍),接下来需要对翻译后的文件进行编译。
shell
myapp> voerkai18n compile
compile
命令根据myapp/languages/translates/*.json
和myapp/languages/settings.json
文件编译生成以下文件:
javascript
|-- languages
|-- settings.json // 语言配置文件
|-- idMap.js // 文本信息id映射表
|-- index.js // 包含该应用作用域下的翻译函数等
|-- storage.js
|-- zh.js // 语言包
|-- en.js
|-- jp.js
|-- de.js
|-- formatters // 自定义扩展格式化器
|-- zh.js
|-- en.js
|-- jp.js
|-- de.js
|-- translates // 此文件夹包含了所有需要翻译的内容
|-- default.json
|-- package.json
|-- index.js
第八步:导入翻译函数
第一步中我们在源文件中直接使用了t
翻译函数包装要翻译的文本信息,该t
翻译函数就是在编译环节自动生成并声明在myapp/languages/index.js
中的。
javascript
import { t } from "./languages"
因此,我们需要在需要进行翻译时导入该函数即可。
但是如果源码文件很多,重次重复导入t
函数也是比较麻烦的,所以我们也提供了一个babel/vite
等插件来自动导入t
函数,可以根据使用场景进行选择。
第九步:切换语言
当需要切换语言时,可以通过调用change
方法来切换语言。
javascript
import { i18nScope } from "./languages"
// 切换到英文
await i18nScope.change("en")
// 或者VoerkaI18n是一个全局单例,可以直接访问
await VoerkaI18n.change("en")
i18nScope.change
与VoerkaI18n.change
两者是等价的。
一般可能也需要在语言切换后进行界面更新渲染,可以订阅事件来响应语言切换。
javascript
import { i18nScope } from "./languages"
// 切换到英文
i18nScope.on("change",(newLanguage)=>{
// 在此重新渲染界面
...
})
//
VoerkaI18n.on("change",(newLanguage)=>{
// 在此重新渲染界面
...
})
@voerkai18n/vue和@voerkai18n/react提供了相对应的插件和库来简化重新界面更新渲染。
第十步:语言包补丁
一般情况下,多语言的工程化过程就结束了,voerkai18n
在多语言实践考虑得更加人性化。有没有经常发现这样的情况,当项目上线后,才发现:
- 翻译有误
- 客户对某些用语有个人喜好,要求你更改。
- 临时要增加支持一种语言
一般碰到这种情况,只好重新打包构建工程,重新发布,整个过程繁琐而麻烦。 现在voerkai18n
针对此问题提供了完美的解决方案,可以通过服务器来为应用打语言包补丁
和动态增加语言
支持,而不需要重新打包应用和修改应用。
方法如下:
- 注册一个默认的语言包加载器函数,用来从服务器加载语言包文件。
javascript
import { i18nScope } from "./languages"
i18nScope.registerDefaultLoader(async (language,scope)=>{
return await (await fetch(`/languages/${scope.id}/${language}.json`)).json()
})
- 将语言包补丁文件保存在Web服务器上指定的位置
/languages/<应用名称>/<语言名称>.json
即可。 - 当应用启动后会自动从服务器上加载语言补丁包合并,从而实现动为语言包打补丁的功能。
- 利用该特性也可以实现动态增加临时支持一种语言的功能