引子
我之前也介绍过我们的团队 草系码猿🌿 用一年经历带你了解BBFE是个怎样的前端团队|Be Better Front End, 未来我也会为大家带来更多我们团队的实践探索,敬请期待~
下面由我们团队为大家分享BBFE在国际化方向的探索与思考
。
背景
一个软件产品走向国际市场,在不同的国家和地区使用,必然要在设计软件时考虑国际化。那么国际化是啥呢?维基百科中有如下定义:
国际化 是指在设计软件时,将软件与特定语言及地区脱钩的过程。当软件被移植到不同的语言及地区时,软件本身不用做内部工程上的改变或修正。
这就要求我们进行软件开发时,不应该为每个语言环境都开发单独版本,浪费大量的开发成本,而应该将需要翻译和替换的文本、图片等资源从源代码中提取出来,以配置文件的形式进行管理,即多语言国际化配置文件。
提取后的软件项目称为国际化通用版项目,配置文件称为语言包,国际化通用版项目与不同的语言包就能使软件支持不同的语言环境,实现国际化。
现状
我们看看使用 Vue 的软件项目是怎么做到多语言国际化的。
通常,我们会选用 Vue 官方推荐的 Vue-i18n 方案,使用 Vue-i18n 实现国际化需要 6 个步骤:
- 开发者使用源语言(如中文)开发项目;
- 开发者手动从源代码中提取源语言包配置文件;
- 开发者将源代码中源语言(如中文)改写为使用语言包的标记方法;
- 开发者将提取的语言包转换为约定的格式提供给翻译供应商;
- 翻译供应商完成翻译,按约定的格式交付目标语言(如英文)包;
- 开发者生成可使用的目标语言包,软件项目按需使用;
多语言国际化配置文件随着项目规模扩大而增加,开发者需要不断重复上述过程。
面对小型的软件项目,手动维护还比较轻松,然而面对大型规模的软件项目,手动维护过程就显得极其繁琐,且容易出错导致问题不断。
以奇安信的大型管控系统为例,前端团队采用上述多语言国际化方案后,便带来痛苦的迭代及维护工作。整个软件项目的国际化开发过程中,共计手动产出 120+ 个英文语言包,因国际化手动工作而导致的相关 BUG 超过 180 个。
反思
采用上述多语言国际化方案后,我们经历了一段痛苦的开发过程。
我们总结该方案在大型中后台系统的应用场景存在 3 个缺陷:
- 提取语言包的过程需要开发者手动参与,过程繁琐效率低下且极易出错;
- 代码中语言包标记方法使用"文本 Key" ,无法有效阅读理解,维护成本高;
- 系统启动过程中全量加载目标语言包,延迟内容呈现,降低用户体验;
我们分析缺陷方案出现的根本原因,是没有事先思考与设计多语言国际化的目标与用例,采纳的社区方案并不能保障上来就是干的靠谱预期。我们将项目磕磕绊绊的勉强交付,受伤后才思考多语言国际化究竟在做什么事,达成哪些目标。
针对旧版方案的上述问题,我们决定探索符合 BBFE 实际需要的多语言国际化新方案,并制定 3 个目标:
- 用自动化工具代替手动提取语言包的工作,降低成本保障正确性;
- 能使用源语言(中文)编写代码,增强代码可读性与可维护性;
- 奇安信前端框架QP内置按需加载目标语言包的能力,提升系统运行性能;
调研
建立目标后,我们调研学习国际化领域的相关知识与协议,深入理解多个成熟的国际化开源方案,发现大致分为 2 种思路:
- 软件项目配合不同的目标语言编译输出不同的项目制品,部署于不同的服务环境,代表方案:Angular-i18n;
- 软件项目编译输出为一个标准制品,系统在运行过程中根据语言环境加载对应的目标语言资源,代表方案:Vue-i18n , i18next , kiwi 等;
其中,我们对 Angular的国际化方案( Angular-i18n ) 分析如下:
- 开发者使用源语言编写代码,标记出需要翻译的文本。对于比较复杂的文本信息(不同语言有不同的复数描述规则和语法结构)使用 ICU 消息格式编写;
- 使用命令行工具将标记过的文本提取为 XLIFF 格式的语言包模板;
- 为每种目标语言生成独立的 XLIFF 语言包文件,把语言包提供给翻译供应商;
- 为一个或多个语言环境构建项目制品时,使用命令行工具合并目标语言包;
方案的使用过程比较方便,且部分满足我们对新方案的目标所需。如使用命令行工具提取语言包,及使用源语言标记文本翻译。
XLIFF\ICU 是国际化领域的协议与方案,以下为简介:
XLIFF
XLIFF(XML Localisation Interchange File Format,即XML本地化交换文件格式)是一种基于XML协议的交换格式,旨在标准化国际化、本地化过程中在工具之间传递可本地化数据的方式。
可以包含一些给翻译人员提供的附加信息,使翻译人员能更好地翻译。
同时,也是因为 XLIFF 是标准的解决方案,各应用市场也存在给翻译人员使用的 XLIFF 翻译工具,如 XLIFF editor、Poeditor 等,下图所示是 XLIFF editor 的使用界面:
ICU
为在国际化过程中需要根据各地的风俗和语言习惯,实现对数字、货币、时间、日期、和消息的格式化、解析,对字符串进行大小写转换、整理、搜索和排序等功能,ICU (International Components for Unicode,Unicode 国际化标准组件)应运而生,它是一套成熟、广泛使用的C/C++、Java和.NET 类库集。我们可以利用 ICU 消息格式 定义解决翻译中常见的复数、选择占位等复杂形式,下图所示是 ICU消息格式 表示复数的一种情况:
问题
然而,我们发现 Angular-i18n 方案也存在一些实际问题:
- 只提供模版内的标记方法,对于脚本逻辑的文本,没有提供官方解决方案;
- 编译为不同语言的项目制品,会提升项目制品部署及路由配置的复杂度;
思考
我们经过调研,并进行充分的讨论与思考,逐步理清围绕多语言国际化配置文件的两个重要模式,即 生产语言包 和 消费语言包 。
当下前端的国际化开源方案,以生产语言包和消费语言包的角度整理如下图:
结合奇安信产品的国际化需求,我们对国际化的新方案期望如下:
一、生产语言包:自动化工具代替大量手动提取语言包的操作。Angular-i18n 使用命令行工具提取生成 XLIFF 标准的语言包模板,是可以借鉴的部分。
二、消费语言包:按需加载目标语言环境的配置文件,系统运行过程中可切换目标语言。对于文本描述复杂的信息(如复数、时间等),需要标准解决方案。Vue-i18n 在浏览器运行时加载语言包,Angular-i18n 采用 ICU 格式编写复杂文本,是可以借鉴的部分。
综合 Angular-i18n 与 Vue-i18n 方案中可借鉴的部分,再加上BBFE团队的整体方案设计与优化,便形成奇安信的多语言国际化的新方案(即千星平台内置的国际化标准方案)。
方案
接下来,我们捋捋新版多语言国际化方案,并阐述其中的设计思路:
1.标记
首先,我们标记出需要翻译的文本内容。
对于普通文本,使用 tx
方法来标记,而 tx
方法在不同的使用场景又有不一样的使用变形。
在 Vue 模板中,我们使用 $tx
来标记:
$tx('你好,世界!')
而在 js 文件中,我们使用QP框架的全局方法 qp.i18n.tx
来标记:
export const sayHello = (name) => { return qp.i18n.tx('你好,{name}', {name}); }
如果需要给标记的文本提供更多描述信息(通常是方便翻译人员工作参考),可以使用标记方法提供的第 2 个参数,如:
<span> {{ $tx('你好,世界!', '首页|用户进入网站的欢迎界面') }} </span>
如上代码编写,首页|用户进入网站的欢迎界面
这部分补充信息也被提取至语言包模板(XLIFF格式),提供给翻译人员使用。
对于复数、时间等复杂信息的文本,须使标记方法的第 1 个参数使用 ICU message format
:
<span> {{ $tx('{minutes, plural, =0 {刚刚} =1 {一分钟前} other {# 分钟}}更新', {minutes}, '更新时间|使用 ICU 格式') }} </span>
实现上,会使用 intl-messageformat
包转译上述 ICU
特殊标记。
如果,需要翻译的文本嵌于复杂的 DOM 结构,则使用QP框架的全局组件 Translate
,以避免文本被 DOM 结构截断上下文信息,如:
<Translate i18n="i18n"> <b>国际化</b> <span>(i18n)是一个过程,用于对你的应用进行设计和准备以便在全球不同地区使用。</span> </Translate>
Translate
组件包裹的 DOM 结构会以翻译单元的形式提供给翻译人员(符合XLIFF 的定义)。
对于需要从服务端获取的目标语言文本,如字典等,会提供 t
方法用于标记,方法同 Vue-i18n
,需要定义文本 key 在浏览器运行时显示目标文本。
值得注意的是,使用 t
方法标记的信息不再被提取工具所提取至语言包模板。
2.提取语言包模板
其次,将需要翻译的文本标记之后,便可以抽取源语言包模板。
我们提供命令行工具,将需要翻译的文本提取为 XLIFF
格式的语言包模板。
按需生成目标语言的 XLIFF 配置文件(即语言包),把语言包提供给翻译供应商,供应商翻译完毕后也将返还目标语言的 XLIFF 配置文件(即目标语言包)。
3.项目配置目标语言包
然后,软件项目需要配置的语言包分为 2 类,即软件项目源码内提取并翻译的目标语言包
及 软件项目依赖的 NPM Package 的语言包
。
- 对于软件项目源码内提取并翻译的目标语言包(XLIFF 格式)
提取并翻译的 XLIFF
目标语言包包含提供给翻译人员的附加信息,这些信息是为翻译人员做更好的工作参考。
以项目制品的系统运行过程而言,这些附加信息是冗余的,包含冗余信息的目标语言包对于系统运行带来不必要的负担,且在浏览器解析 XLIFF 文件也会耗费运行时性能。
于是,我们对已翻译好的目标语言包再次提取关键信息,生成体积小且容易在浏览器运行时使用的 json 文件。这便是命令行工具的第二个功能:将 XLIFF 语言包转换为 json 语言包。转换后的 json 语言包只包含翻译单元的 key 及对应的目标语言文本信息:
- 对于依赖的 NPM Package 中的语言包
每个语言环境会对应独立的目标语言 js 文件,系统启动时会按照语言环境引入目标语言包。目标语言 js 文件名遵循 BCP47
的命名规则,如匹配美式英语的语言环境,就需要创建名为 en-US.js
的目标语言文件,该文件需导入( import )项目所有依赖的 NPM Package 目标语言环境的语言包,以数组方式导出( export) 。
// src/configs/dependency-locales/en-US.js import somePackageEn from 'some-package/dist/locales/en-US.js'; import anotherPackageEn from 'another-package/dist/locales/en-US.json'; export default [somePackageEn, anotherPackageEn]; // 多个第三方 package 的语言包以数组的形式导出
4.按需加载目标语言包
最后,QP框架内置目标语言包切换的策略,如下图:
QP框架根据不同的语言环境加载对应的目标语言包,此处是框架内置规则,不再赘述。
总结
本文讲述 BBFE 在奇安信的大型管控系统开发多语言国际化方案的心路历程。
尽管我们以基于Vue的项目为案例,甚至数次提到内置于QP框架的多语言国际化方案(QP框架是奇安信前端统一研发平台--千星平台的基础设施之一)。仍然希望BBFE 在国际化方案研发过程中的思考可以为各位阅读者提供一点帮助。
本文所述的多语言国际化方案是汲取优秀案例 Angular-i18n 及 Vue-i18n 的设计思路,使用 XLIFF / ICU / BCP47 等国际化通用标准。能够「站在巨人的肩膀上」做出的每一点改进,都应归功于前端开源社区文化的繁盛,BBFE 感谢前辈们
未来,我们应该需要一个更加完备的多语言国际化工作平台(如下图)去维护语言包的工作流(抽取、转换、集成等),国际化工作远不止于多语言处理,国际化的探索之路值得我们共同期待与挑战。
参考资料
XLIFF 维基百科:en.wikipedia.org/wiki/XLIFF
ICU : unicode-org.github.io/icu/
CLDR : cldr.unicode.org/
BCP47 : tools.ietf.org/search/bcp4…
Angular-i18n:angular.cn/guide/i18n
Vue-i18n : kazupon.github.io/vue-i18n
i18nNext : www.i18next.com/
react-intl-universal: github.com/alibaba/rea…
kiwi : github.com/alibaba/kiw…