国际化方案调研与应用
背景
国际化(internationalization, 简称i18n)是一个能够让产品适应多地区和多语言的能力。对于国际化公司而言,产品的使用者往往是来自不同国家、不同地区的用户人群,对平台使用语言的要求不尽相同。因此,有必要实现一套前端国际化通用方案,旨在提供语言环境适配和切换的能力,降低用户阅读门槛,从而提升平台使用体验和工作效率。
三、目标
针对目前各平台项目没有提供国际化功能,将制定一套前端通用国际化方案,帮助平台开发者快速接入国际化功能而无需手动实现。
结合目前技术选型和业务情况,国际化方案需要支持的能力包括以下几点:
支持多语言展示与切换(核心)
默认语言、多类型语言选择与切换
支持翻译资源管理
非硬编码,业务方可以通过线上平台维护翻译资源,支持线上修改资源、扩展多语言,从而降低维护成本。
支持cli初始化国际化配置
cli初始化项目时支持添加国际化功能(可选)
支持自动识别/切换语言
识别地区,自动切换到当前地区对应的语言环境
兼容UI库国际化
UI库内置组件的国际化同步
四、理想技术模型
五、开源方案(运行时)
1. react-intl
react-intl是formatjs(JavaScript国际化库)的一部分,其主要针对基于react框架开发的项目来实现国际化, 通过使用其提供的API来实现对文本、日期、货币、数字的翻译。
全局配置
1)自定义配置语言字典。语言字典就是各类语言所对应的数据映射集,以文件的形式存储,必须保持相同的格式。如:
en_US.js:
1 | const en_US = { |
zh_CN.js:
1 | const zh_CN = { |
2)生成全局配置
1 |
|
2)在项目入口文件中引入react-intl提供的国际化容器组件IntlProvider,包裹在根组件外面, 并导入默认全局配置:
1 | import ReactDOM from 'react-dom' |
使用方法
导入全局配置后,我们就能使用react-intl提供的组件/API来展示当前语言匹配的文案了。react-intl支持component、hoc、hook两种使用方法:
Component写法:
使用react-intl提供的**
1 | import { FormattedMessage } from 'react-intl'; |
如果文本中包含变量,则需要通过values字段传入文本模板中需要解析的数据。比如:
1 | import { FormattedMessage } from 'react-intl'; |
HOC写法:
使用react-intl提供的injectIntl可以对class组件进行包装,通过props向组件注入intl对象,然后通过调用intl.formatMessage获取翻译文案:
1 | import React from 'react'; |
Hook写法:
支持React Function Component的Hook写法,可以使用react-intl提供的useIntl来实现:
1 | import { useIntl } from 'react-intl'; |
umi支持
umi框架内置国际化功能,其底层也是基于react-intl来实现的,我们可以通过其提供的api来实现国际化能力:
1)通过umi提供的useIntl来获取formatMessage等api来进行具体的值绑定:
1 | import React, { useState } from 'react'; |
- 使用setLocale来设置切换语言,默认会刷新页面
1 | import { setLocale } from 'umi'; |
优缺点
优点:
- 支持多种方式来实现语言翻译,兼容Class组件和Function组件,用法丰富;
- 通过props的方式来注入语言包,可以在不刷新页面的情况下切换语言
缺点:
- 只能应用于视图层,例如React.Component。对于Vanilla JS文件(原生JS),无法对其进行国际化
- 对于injectIntl包裹的组件,要获取Component的实例,react-intl不能使用常规方法如:this.refs.xxx
2. react-intl-universal
基于react-intl的一些缺陷,react-intl-universal是阿里巴巴团队提供的一个国际化方案。它也是基于react-intl开发,提供了一个可以用来加载当前地区配置的单例对象,并允许非react组件(原生JS)使用这个库。
初始化配置
通过调用intl对象的init方法,并传入currentLocale和locales数据来初始化配置:
1 | import React, {Component} from "react"; |
使用方法
1) get
状态更新后,通过intl提供的get方法来获取数据
1 | ... |
2) getHtml
也可以使用intl.getHtml来返回html结构:
1 | // zh.json |
3) 切换语言
1 | ... |
优缺点
优点
- 使用简单,只包含三个主要的API和可选的帮助器
- 支持在浏览器和Node中运行
缺点
- 无法实现无感知切换语言,需要刷新页面
3. react-i18next
I18next是一个历史悠久且非常完整的国际化框架,不仅适用于浏览器,也能够在其他javascript环境(Node/Deno)上运行。react-i18next则是基于i18next开发的一个适用于react/react-native的国际化方案。它能够支持组件方案,囊括class组件、HOC和Hook。同时也提供了丰富的插件支持,比如可以用插件检测当前系统的语言环境,从服务器或者文件系统加载翻译资源等。
初始化配置
1)自定义配置语言字典。i8next规定使用JSON文件来存储翻译文案,如:
en.json:
1 | { |
zh.json:
1 | { |
2)导入资源,通过init方法初始化配置,并导出i18n实例:
index.js:
1 | import i18n from 'i18next' |
2)在项目入口文件中导入配置,即可支持初始化流程:
1 | import ReactDOM from 'react-dom' |
使用方法
1)组件中使用:
这里仅以Hook写法举例,其他用法参考官方文档:https://react.i18next.com/
1 | import React from 'react'; |
2)原生JS中使用:
1 | import i18n from 'i18next' |
切换语言
i18next提供了切换语言的API,可以直接使用:
1 | import i18n from 'i18next' |
优点
- 使用hook方式,简单易用。
- 提供切换语言,不用手动实现
- 方案完整性高,同时支持浏览器端和服务端
4. 方案对比
基础对比
总结:react-i18next在包大小、下载时间、下载量上具有绝对的优势
react-intl | react-intl-universal | react-i18next | |
---|---|---|---|
包大小(kB) https://bundlephobia.com/ | 56.7 | 86.5 | 20 ✅ |
下载时间(ms)4G | 19 | 32 | 7 ✅ |
下载量 | 900000+ | 10000+ | 1400000+ ✅ |
https://www.npmtrends.com/react-i18next-vs-react-intl-vs-react-intl-universal
特性对比
react-intl | react-intl-universal | react-i18next | |
---|---|---|---|
无感切换语言 | ✅ | ❌ | ✅ |
Vanilla JS支持 | ❌ | ✅ | ✅ |
Hook写法支持 | ✅ | ❌ | ✅ |
模板解析支持 | ✅ | ✅ | ✅ |
内置切换语言API | ❌ | ✅ | ✅ |
无破坏性 | ❌(装饰器的代码实现会改变ref) | ✅ | ✅ |
初始值获取 | 手动传入 | url / cookie / localStorage | localStorage/url |
是否需要Provider | ✅ | ❌ | ❌ |
服务端支持 | ❌ | ✅ | ✅ |