作者 | sky-,猫不是熊
背景
I18n = ,因为这个词是由首尾字符 i/n 和中间的 18 个字母组成,简称 i18n。对于程序来说,意味着在不修改内部代码的情况下,应该能够根据不同的语言、不同的地区,显示相应的界面,以支持不同语言的人顺利使用程序。
商业背景
互联网行业进入下半场,精细化运营是关键。多语言支持让产品可以更好地服务国内其他语言用户,也为产品出口奠定基础。全球化,你的小程序准备好了吗?
四月初,滴滴出行小程序团队接到支持英文版的请求,预计六月初上线。目前滴滴出行小程序集成了众多业务线和各类公共库,前端硬编码的静态文本和服务端发送的文案展示给用户,都需要同时对接多种语言。考虑到目前小程序的规模,文本采集、语料翻译、npm 支持、联调、测试、沟通成本等时间都相当紧张,前端开发仅投入了 1.5 人力。但我们顶住了压力,最终滴滴出行小程序英文版如期上线。目前运行稳定,用户反馈良好,取得了超出预期的效益。
当然,这一切得益于各个团队同学们的高效工作,各个团队的紧密配合,以及部门技术团队对Mpx框架优雅的多语言支持。下面是重点,俗话说工欲善其事,必先利其器。如果贵公司的业务需要开发小程序,还需要接入多种语言,那么请搬起凳子,我们一起来看看小程序框架Mpx是如何优雅地支持多语言能力的。相信看完本文,有助于大家了解Mpx,加深对框架的理解,最终利用Mpx框架高效迭代小程序。年终奖多出的部分可以打赏作者,买杯咖啡喝。
滴滴出行小程序中英文版本如下:
也欢迎大家在微信/支付宝中搜索滴滴出行小程序,亲身体验。PS:切换语言请打开小程序,点击左上角用户头像,进入侧边栏设置页,点击中英文切换即可体验。
技术背景
在上述业务背景下,滴滴自研的专注于提升小程序开发体验、内置i18n能力的增强型小程序框架Mpx框架被提上日程。
与WEB不同的是,小程序(本文以微信小程序为例)的运行环境采用双线程架构设计,渲染层界面使用线程渲染,逻辑层使用线程运行JS脚本。当逻辑层数据发生变化时,通过转发数据到(微信客户端),再转发数据到渲染层来更新页面。由于线程间通信成本较高,实际项目开发时需要控制频率和数量。另外小程序的渲染层不支持运行JS,有些操作比如事件处理等无法在渲染层实现。因此微信官方提供了脚本语言WXS,可以结合WXML构建页面的结构(不了解WXS?点击这里)。
基于小程序双线程的架构设计,实现 i18n 有一定的技术难点和挑战,得益于前期 Mpx 框架打下的坚实基础,最终能够优雅地支持多语言能力,并达到与 vue-i18n 基本一致的用户体验。
使用
使用方面,Mpx提供的支持i18n能力的API与vue-i18n大致一致,使用方式基本一致。
在模板中使用 i18n
在编译阶段,将用户配置的i18n词典通过wxs-i18n-与框架内置的翻译函数结合,形成可执行的WXS翻译函数,随翻译函数调用自动注入到模板中,具体调用方式如下图所示。
// mpx 文件
<view>
<view>{{ $t('message.hello', { msg: 'hello' })}}view>
<view>{{formattedDatetime}}view>
view>
</template>
在 JS 中使用 i18n
通过框架提供的能力,将WXS的翻译功能转换成JS模块,注入到JS运行时中,使得在运行环境中也能调用翻译功能。
// mpx 文件
<script>
import mpx, { createComponent } from '@mpxjs/core'
createComponent({
ready () {
// js 中使用
console.log(this.$t('message.hello', { msg: 'hello' }))
// 局部 locale 变更,生效范围为当前组件内
this.$i18n.locale = 'en-US'
setTimeout(() => {
// 全局 locale 变更,生效范围为项目全局
mpx.i18n.locale = 'zh-CN'
}, 10000)
},
computed: {
formattedDatetime () {
return this.$d(new Date(), 'long')
}
}
})
script>
定义 i18n 词典
i18n配置对象是在项目构建时传入的,主要包括语言词典和默认语言类型。
new MpxWebpackPlugin({
i18n: {
locale: 'en-US',
// messages 既可以通过对象字面量传入,也可以通过 messagesPath 指定一个 js 模块路径,在该模块中定义配置并导出,dateTimeFormats/dateTimeFormatsPath 和 numberFormats/numberFormatsPath 同理
messages: {
'en-US': {
message: {
hello: '{msg} world'
}
},
'zh-CN': {
message: {
hello: '{msg} 世界'
}
}
},
// messagesPath: path.resolve(__dirname, '../src/i18n.js')
}
})
如果项目是通过Mpx提供的cli工具生成的,那么这部分的配置会在mpx.conf.js文件中,不仅可以直接在文件中内联编写,还可以指定语言包的路径。
上文提到,mpx的i18n方案接入成本低,使用优雅,体验极佳,直观的体验可以参考如下mpx i18n demo:
计划
Mpx框架对i18n的支持几乎完全实现了vue-i18n的所有能力,下面我们来详细讲解一下Mpx框架对i18n能力的具体实现。
解决方案探索
基于小程序运行环境双线程架构,我们尝试了不同的解决方案,具体探索过程如下:
方案一:基于Mpx框架提供的数据增强能力,通过计算属性的方式支持i18n。该方案实现思路类似(后面会做对比分析),但是存在一定的不足,包括线程通信带来的性能开销,以及for循环场景下的复杂处理等,因此最终被放弃。方案二:基于WXS+JS支持i18n适配。将WXS注入到视图层,将WXS语法转换成JS后注入到逻辑层。这样,视图层和逻辑层都可以实现i18n的适配,一定程度上有效减少了两个线程之间的通信时间,提升了性能。
考虑到性能和合理性,我们最终采用方案二来实现Mpx的i18n解决方案。
![]()
Mpx跨平台i18n实现流程
实施难度
模板中运行的 WXS 跨平台处理
WXS 是运行在视图层的 JS,可以减少与逻辑层通信的时间,提高性能。因此,Mpx 框架在迭代初期就已经支持在模板和 JS 运行环境中使用 WXS 语言,并平滑小程序跨平台的 WXS 语法。在模板中,Mpx 定制了一个,使用微信 WXS 作为 DSL,将注入的 WXS 转化为 ast,然后遍历 ast 节点,平滑各大平台对 WXS 语法处理的差异,输出一个各平台都能识别的 WXS 文件。目前主要支持微信(WXS)、支付宝(sjs)、百度()、QQ(qs)、头条(sjs)等小程序平台。
WXS是一个运行在逻辑层面的跨平台处理引擎。
WXS 与 是不同的语言,有自己的语法,与 并不一致。并且 WXS 的运行环境与其他代码隔离,WXS 无法调用其他文件中定义的函数,也无法调用小程序提供的 API。因此在逻辑层,Mpx 将注入的 WXS 语法转换成 JS 注入到当前模块中。例如 WXS 全局方法 / 无法在 JS 中调用,Mpx 分别将其转换成 JS 模块,再将模块注入 .js 中。同样的,Mpx 会将编译时注入的 i18n wxs 翻译函数和 i18n 配置对象挂载到全局对象中,混入页面组件中,并监听 i18n 配置对象,这样就可以在 JS 和模板中直接调用 i18n 翻译函数,实现数据响应。
以上就是 Mpx 框架在小程序中支持 i18n 能力的技术细节,由于 WXS 是可以在视图层执行的类 JS 语法的语言,这样可以减少小程序逻辑层和视图层的沟通时间,提升性能。但由于实现上依赖 WXS 类能力,以及 WXS 执行环境的限制,目前模板中可以直接使用的翻译函数包括 $t/$tc/$te,如果需要对数字或者日期进行格式化,可以在 JS 中使用 Mpx 提供的计算属性中对应的翻译函数。
导出到 Web 时使用 i18n
Mpx 还支持 H5 的转换输出,并且 Mpx 提供的 i18n 能力在使用上与 vue-i18n 基本一致。输出 web 时,框架会自动引入 vue-i18n,并使用当前 Mpx i18n 配置信息进行初始化。用户无需做任何改动,即可输出与小程序性能完全相同的 i18n web 项目。
比较的
上面分析了Mpx框架的i18n方案的技术细节,我们再来看看和其他方案的对比,主要是基于Vue编写小程序的方案和微信官方的方案,看看这两者提供的i18n支持和Mpx相比如何。
解决方案
它通过直接引入vue-i18n来提供i18n能力的支持,但是在小程序的模板上无法调用JS方法,本质还是利用计算属性进行语言转换,然后通过模板插值在小程序模板中使用。
在模板中:
{{. }}
在 JS 中,你需要写:
computed: {
message () {
return { hello: this.$t('message.hello') }
}
}
因此这种方案存在性能问题,最终渲染层看到的文本依然是通过跨线程通信完成的,这会导致线程间通信增多,性能开销较大。
另外这种形式早期使用起来成本比较高,但后来进行了优化,可以在模板上写$t(),使用起来方便很多。
$t() 的实现是编译时识别到 $t 时自动替换,用单个数据替换,因此数据部分还是需要和以前一样维护两份。特别是模板上的 for 循环,即使 for 中只有一个数据需要转换,也要用计算属性替换整个列表,这进一步增加了线程间通信时的性能开销。
微信官方计划
微信小程序本身也提供了i18n解决方案,仓库地址为:
-/-i18n
该方案在i18n本身的实现上和Mpx框架的设计比较类似,同样是基于WXS(英雄所见略同)实现的,但由于周边配套设施方面没有形成完整的体系,因此整体用户体验比基于Mpx框架开发支持i18n的国际化小程序略逊一筹。
重点在于官方的解决方案需要在gulp工具基础上额外构建,而在JS中使用时也要额外引入,才能在JS中开启翻译功能。
Mpx 框架通过统一构建产生完整的内容,用户无需担心更新语言包后忘记重新构建,不仅在 JS 中使用更加方便,语言信息也具有响应性,任何组件都可以轻松监听语言值的变化来做其他事情。
最后,Mpx的i18n方案相较于微信官方方案有着巨大的优势,结合Mpx的跨平台能力,可以一套代码就支持微信/支付宝/百度/QQ/今日头条等多个平台的i18n小程序。
总结
Mpx框架专注于小程序开发,希望为开发者提供最舒适的开发体验,拥有众多优秀特性帮助开发者提升效率,本文介绍了其内置的i18n能力,通过对比分析发现其相比于其他框架方案在使用成本、性能等方面具有明显优势,欢迎有相关需求的同学尝试一下。
未来,Mpx 会不断迭代优化,提供更多更好的能力帮助小程序开发者提升效率。使用过程中遇到任何问题,欢迎在 Git 上提出,团队成员会及时响应。同时我们也鼓励大家为开源社区做贡献,参与 Mpx 共建,为小程序技术发展贡献力量。
Git地址:
Mpx 文档:
欢迎大家进行技术交流和反馈,顺便可以点赞鼓励开源项目贡献者,我们会继续为社区做贡献。