谈谈国际化的使用姿势(i18n)
国际化(Internationalization,简称 i18n)的核心目标是让产品能够适应不同语言、文化和地区的用户需求。其必要性体现在以下几个方面:
- 用户覆盖广度:全球用户语言环境多样,支持多语言可提升用户体验,扩大市场范围
- 代码可维护性:通过统一管理多语言资源,避免硬编码文本分散在代码中,减少重复修改成本
- 动态适配能力:结合浏览器语言检测或用户手动切换,实现内容实时更新,无需重新部署代码
通常一个中大型的项目都会实现国际化,开发人员只需要将对应语言的翻译根据映射到页面中就可以完成。在现代web框架以及国际化框架的加持下,整体功能的实现也越来越简单。如vue中通常使用 Vue-I18n、React可以用 React-I18next、Angular提供了 localize,这些都可以加速国际化开发
开发流程
在真实的项目开发中,常见的开发流程即:开发人员在编码的过程中,通常会将待翻译的文案提取到对应语言map中,最后通知翻译团队进行翻译,翻译完成后再更新到代码中,然后在测试过程中验证,有问题继续重复以上步骤
i18n几种方案
i18n是现代国际化实现必不可缺的帮手,它提供了相对灵活的方案,开发者可以根据使用场景选择合适的方案快速实现国际化
下面就来看看项目中i18n的多种使用方式,本文按照由简到繁的顺序一一介绍,以vue-i18n为演示框架
小贴士
本文只做国际化方案的演示介绍,不包括详细的配置步骤,读者可根据方案进行调整实现
代码集成实现
代码集成是i18n最常用的一种方案,它将所有需要翻译的文案统一到代码中,在编译时进行替换,从而实现国际化
首先创建i18n实例
// i18n实例
const i18n = createI18n({
// 默认语音
locale: 'zh',
// 备选语音(默认语音不存在时使用)
fallbackLocale: 'en',
// 对应语言的翻译映射,以语言值为key,对应语言的翻译为value
messages: {
en: {
message: {
hello: 'hello world'
}
},
zh: {
message: {
hello: '你好世界'
}
}
}
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
在组件中使用
<h1>{{ $t('message.hello') }}</h1>
当语言环境改变时,修改i18n的语言值就完成了翻译文案的切换
i18n.value = 'en';
当翻译文件体量越来越大时,通常都会将翻译内容以语言划分文件夹,然后根据语义或者模块信息将内容分组后存储
i18n
└── en
├── common.js # 英文公共文件
└── login.js # 登录页面英文翻译
└── zh
├── common.js # 中文公共文件
└── common.js # 可能为重复或备用中文文件
2
3
4
5
6
7
📌 代码集成方式缺点:
- 翻译同步维护难度大
- 翻译体积文件过大时,加载性能问题
开发时通常都会用母语作为默认语言,然后其他的语言同步好对应的字段,交给翻译后进行翻译,当翻译内容好后再手动同步到对应的字段代码中,在后期多次需求开发时,如果翻译同步不及时会变得异常困难,而且手动同步也会变得很繁琐,成本太高开发体验很差
基于JSON实现
基于JSON的方案顾名思义就是将翻译内容保存到JSON文件中,然后根据语言加载对应的JSON文件,完成国际化工作
const i18n = createI18n({
locale: 'zh',
globalInjection: true,
missingWarn: false,
silentFallbackWarn: true,
silentTranslationWarn: true,
});
export async function loadLangResource(i18n, locale) {
const messages = await import(/* webpackChunkName: "locale-[request]" */ `/static/${locale}.json`);
// 或者不参与打包
// const messages = await fetch(`/static/${locale}.json?v=${app.version}`)
i18n.global.setLocaleMessage(locale, messages.default);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
从上述代码中可以看到在对应的语言环境下,加载对应的JSON翻译文件,此种方式可以很好的将翻译与代码解耦,实现翻译共享与复用,并且翻译资源文件拆分异步加载可以提高加载性能
📌 基于JSON方式缺点:
- 翻译同步维护难度同样大
- JSON中无法注释,需采用JSON5等方案
JSON方式虽然与代码进行了解耦,但仍然存在翻译同步维护的困难,如JSON字段的维护怎么维护?没有注释就像天书。当然,这并不是JSON的最佳使用方案,这里也是简单介绍下此种方案,但简单的翻译工作也可以使用这种简单的JSON方案
头疼的是JSON怎么维护问题,接下来看看如何解决这个问题
基于JSON与CMS实现
JSON方案虽然与代码解耦了,但开发者去修改JSON文件是万万不能接受的!!
如果可以将JSON字段用某个工具与翻译人员关联起来,那么就可以省不少事,并且翻译人员可以随时修改翻译内容,并通知更新。这里介绍2种方案:
- 基于在线表格
- 基于数据库(CMS)
来看看在线表格方案,什么意思呢:开发人员将待翻译内容呈现在在线表格中,翻译人员直接在表格中同步翻译内容,最后通知开发者更新即可。那么如何与JSON关联起来呢?要知道代码中i18n还是会以key
为索引显示对应的翻译内容,所以表格中需要有对应的key字段,开发者需要创建key字段,最后将表格数据转换成json文件即可
这里关键的步骤在于如何将在线表格数据转换成JSON,其实很简单,市面上的文档如:Google、Tencent等等都支持API操作,读者可前往对应的开发者文档查看
本文用腾讯在线表格演示,如下图所示,在线表格中包含了:翻译表标题、表头、翻译内容,开发者提供对应的翻译字段key,翻译人员将翻译内容填入即可
有了API数据后就可以转换成JSON了,可以用Node实现API请求然后生成对应的JSON文件
// 伪代码
async function getData() {
const data = await fetch("https://domain.com/apii/sheet/uidlpsdfierersefadf")
data.json().then(res => {
// 转换逻辑
// 根据不同的语言生成对应的JSON文件
fs.writeFileSync(`${lang}.json`, JSON.stringify(res))`.)
})
}
2
3
4
5
6
7
8
9
10
将对应的i18n生成脚本写入到项目的package.json中
{
"scripts": {
"i18n": "node i18n.js"
}
}
2
3
4
5
这样每次在翻译表格更新后,直接走脚本就行了,开发者无需去关心JSON文件长啥样了。同样的再走完上文中JSON中i18n的json加载逻辑,即可完成多语言的切换
接着看看CMS(Content Management System)与JSON的结合使用,其实在线表格的方式已经减少了很大一部分工作,但对于企业来说维护自己的翻译系统还是必要的
设计这一套系统也非常简单,服务端只需要使用以下条件可快速实现:
- 数据库:使用mongodb飞关系型数据库无需关注字段的数量,可以满足多语言的多样性数据
- 数据表:项目语言表、具体语言表
总体来说使用mongodb创建一个不同项目的语言表和具体语言表就基本满足了项目使用了
CMS当然还需要实现在线表格的功能,或者说客户端页面使用功能,基本要满足:语言表创建、修改、查询等等,市面上有很多方式可以帮助到,这里就不细讲了
来看这是CMS中AI Chat项目的语言表
开发团队可以定制具体的规则来标准化开发流程,如:不同的颜色代表不同的含义
除此之外,可以根据RBAC权限来控制语言表的访问
那么在项目中如何拉取语言表呢?通常在项目中新增env配置:
I18N_TABLE_KEY=xxxx
根据项目语言表的key就可以拉取数据了,同样的重复上文中的脚本操作完成json文件的生成
你是否在想如果有了CMS是不是可以直接跳过JSON文件载体,直接走API?其实也不是不可以,只不过JSON方式更稳妥,至于为什么读者可以多考虑考虑
📌 基于CMS与JSON方式缺点:
- 架构复杂
基于插件自动化实现
话说回来国际化的实现过程确实很痛苦,要维护密密麻麻的翻译,何尝不相信自动化翻译呢?
我们可以借助翻译服务商提供翻译接口自动将文案翻译成目标语言,然后通过自动化插件将翻译后的文案自动填充到项目文件中,从而实现国际化
这种方式的维护成本很低,开发者根本不需要管翻译内容,一般实现步骤如下:
- 项目中直接用默认语言写入文案,然后标记当前位置需要翻译
<h1 m-translate>你好</h1>
- 编写翻译转换脚本,可以使用Babel自定义插件编译AST,找到标记的待翻译节点,然后为当前文案生成唯一的key,将文案替换为key后,再将文案翻译成多种目标语言
const translateAPI = require("google-translate-api");
export const AutoTranslatePlugin = declare(
({ assertVersion, types, template }) => {
assertVersion(7);
return {
name: "AutoTranslatePlugin",
visitor: {
Expression(path) {
// 走服务商翻译 translateAPI
},
// 补充...
},
};
}
);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- 生成对应的最终的翻译JSON文件 在插件遍历AST以及翻译过程中将生成的文案key整理后生成到对应的JSON文件中,这样就实现了文案自动化处理
📌 基于插件自动化方式缺点:
- 翻译质量不可控
方案对比
方案 | 适用场景 | 核心优势 | 主要缺点 |
---|---|---|---|
代码集成 | 中小型项目 | 实现简单,社区支持完善 | 维护成本高,翻译同步困难 |
CMS 动态管理 | 非技术团队协作 | 内容可实时更新,灵活性高 | 强依赖网络,架构复杂 |
自动化工具 | 多语言快速迭代 | 减少人工干预,提升效率 | 翻译质量依赖 API,成本不可控 |
总结
本文通过不同角度介绍了国际化的实现方案,以及它们各自的优缺点,读者可根据项目要求或开发标准选择适合的方案。透露下本人工作中常使用CMS+JSON
方式,如果你还有更好的方案,可留言分享