快速前端国际化方案(react-intl-universal)

1,645 阅读8分钟

1.介绍

1.1 背景

项目考虑支持国际化多语言版本,在原有的框架基础上,需要针对性地修改中文文本,兼容切换多语言的功能,并在之后的日常开发中,都需要更好地适配多语言环境。

1.2 问题说明

目前考虑可能会遇到的问题总结如下:

  1. 组件代码中的文本都使用了中文纯文本,需要改成支持切换多语言的代码,并且默认使用中文文本占位。
  2. 面向项目这么多代码,如何快速提取项目中所有的中文文本,减少人力成本,并根据翻译对照生成不同语言的文本后,自动存回代码中,在切换语言时载入。
  3. 如何根据不同的语义语境,将同一个中文词语(但不同语义)翻译成不同文本。
  4. 支持不同地区的数字、货币、日期和时间;支持变量语言切换。
  5. 不同语言切换下样式兼容。

1.3 解决方案

1.3.1 引入react-intl-universal

配合多语言的开发,引入react社区里比较热门的国际化框架react-intl-universal,使用简单但功能强大,支持多种语言,支持不同地区的数字、货币、日期和时间,支持变量语言切换,支持嵌套的JSON格式等等。

1.3.2 使用i18n-pick扫描、替换文本

在添加了react-intl-universal框架之后,需要一个工具提取原项目代码里的中文文本,i18n-pick可以支持快速扫描项目中所有中文文本并去重,并且在扫描后可以借助atool-l10n生成的语言翻译json文件,传给react-intl-universal直接使用,和react-intl-universal框架相得益彰,再根据扫描结果用i18n-pick对项目中的中文文本自动提取替换为react-intl-universal支持的代码格式。

2.react-intl-universal

react-intl-universal是前端国际化设计方案中的基础框架,它负责载入各种国际化文本和提供封装好的方法来获取对应语言的文本。我们可以通过它提供的一些API来将我们项目的纯文本进行替换,并在以后的开发中使用。

2.1 react-intl vs react-intl-universal

react-intl和react-intl-universal都是比较热门的国际化框架,经过调研,决定选用react-intl-universal来实现国际化。

react-intl的缺点

  • 国际化只能应用于视图层,例如 React.Component。对于 Vanilla JS 文件,无法将其国际化。
  • 因为需要使用injectIntl包装组件,要获取React.Component的实例,react-intl不能使用常规方法this.refs.comname

而react-intl-universal在有以下特征的基础上,同时使用更加简单方便,无需用组件包裹中文文本,也没有以上两个问题。

  • 比较简单,只有三个主要的 API 和一个可选的工具方法。
  • 支持显示不同地区的数字、货币、日期和时间。
  • 支持变量语言切换。
  • 支持 HTML切换。
  • 支持 150 多种语言。
  • 支持在浏览器和 Node.js 中运行。
  • 格式严格按照ICU 标准执行。
  • 支持嵌套 JSON 格式的语言文件设置数据。

2.2 引入

//安装react-intl-universal
npm install react-intl-universal

初始化语言(参考代码),切换语言的时候再重新调用初始化方法即可切换成功。

//全局初始化 
const locales = {
    "en": require('./locales/en.json'),
    "zh": require('./locales/zh.json')
 }
intl.init({ currentLocale: 'en-US', locales, })

2.3 应用示例

部分应用示例如下,具体请参考react-intl-universal

//语言环境数据en-US.js
{ 
  "PROMOTION": "项目""HELLO": "Hello, {name}. Welcome to {where}!" ,
}

//组件
import React from 'react'
import intl from 'react-intl-universal';

const BasicComponent: React.FC<any> = () => {
  return (
    <div>
      <div className="title">Basic Examples:</div>
      {/* 基础调用, 结果:"项目"*/}
      <div>{intl.get('PROMOTION')}</div>
      
       {/* 默认值支持, 结果:"欢迎"*/}
      <div>{intl.get('WELCOME').defaultMessage('欢迎')}</div>
      
      {/* 变量支持, 结果: "Hello, Tony. Welcome to NetEase!"*/}
      <div>{intl.get('HELLO', { name: 'Tony', where: 'NetEase' })}</div>
    </div>
  );
}

export default BasicComponent;

3.i18n-pick

i18n-pick是我们用来配合react-intl-universal开发的一个工具,它的作用是用来做文本的提取和编译的,我们需要通过i18n-pick提供的扫描命令将项目中所有文本提取到一个json文件中,开发人员对该json文件进行处理后给到翻译同事进行翻译,完成翻译工作后,我们会生成多语言的json文件,再通过i18n-pick提供的提取命令,替换原代码中对应地方的中文文本为react-intl-universal所需的代码格式,供给react-intl-universal消费,完成我们的一整套国际化的逻辑。

i18n-pick优点

  • 同时支持 JavaScript or TypeScript 的解析。
  • 使用简洁,只需要几个命令即可完成代码替换修改。

i18n-pick意义

  • 节省在项目所有文件中检索中文文本并去重的时间
  • 节省将项目中有中文文本的地方改为支持国际化代码的时间

3.1 安装配置

安装

npm install i18n-pick --save-dev

配置,根目录下添加i18n.config.json文件,并添加以下配置

{
  // 引用语句
 "importStatement": "import intl from 'react-intl-universal'",
  // 调用语句
  "callStatement": "intl.get",
  // 语言文件目标目录
  "targetDir": "i18n-messages",
  // 不予扫描的文件,遵循 glob
  "exclude": [
    "**/demo.{js,jsx}"
  ],
  // 是否统计函数参数中的中文
  "callExpression": false,
  // 自动中文做key
  "autoZhKey": true,
}

3.2 扫描

扫描项目中所有中文文本,进行去重,得到中文文本以及对应所在的所有文件路径数据。

//添加命令语句,path为被扫描目录
"i18n-scan": "i18n-pick scan [path]",

执行命令语句npm run i18n-scan, 运行完成后会在项目根目录生成i18n-messages文件夹,包含sourcemap.txt和zh-CH.json两个文件。

对测试项目中某个文件夹下的扫描结果如下:

image.png

得到的zn-CH.json如下

image.png

扫描完成后,需要开发人员遍历一次该文件,根据项目习惯对扫描后的结果进行修改、分类、归纳,检查生成文件中的id值,根据自己的命名规则对id进行修改,对中文文本可以根据公共文本和模块内文本进行归纳区分,支持根据模块分层级模式(xx.xx.xx)进行调用,对应翻译的en-US.js等语言文件中也支持嵌套层级。这个步骤归纳区分好,可以方便后续翻译文件的管理和更新。

3.3 翻译

根据上面得到的zh-CH.json,获取对应的key-value数据格式,key为id,value为defaultMessage,得到react-intl-universal可使用的中文语言文件zh-CN.js,再把该文件翻译,得到多语言对应的文件供react-intl-universal使用。

这个步骤也可以使用atool-l10n等自动翻译工具来代替,自动化翻译并生成react-intl-universal所需格式的多语言文件。

最终得到被翻译后的语言文件格式期望如下:

{
  //公共部分文本
  'systemName':'Influencer Marketing System'
  ...
  ...
  //分模块文本
  'brandManager':{
    'title': 'Brand Manager',
    'promotion':{
      ...
    },
    'cooperation':{
      ...
    }
  },
  'kolManager':{
    ...
  }
  ...
}

3.4 自动替换

i18n-pick提供的pick提取命令,就是对i18n-messages文件夹下的内容进行分析,根据文件中记录的路径来源和文本等信息,自动替换原代码中所有对应文件中的中文文本,改为react-intl-universal所需的代码格式,供给react-intl-universal使用。

//添加命令语句
"i18n-pick": "i18n-pick pick",

执行命令npm run i18n-pick,得到提取结果。提取前后代码对比如下:

image.png

image.png

到这一步为止,项目国际化算初步完成。可以在页面中查看国际化初步效果了。

4.样式兼容

项目长期在中文的语境环境下进行开发,因此许多样式都是按照中文情况进行设计的,因此在国际化背景下,需要修改部分框架的样式来兼容多语言的情况。

4.1 词语换行

由于在中文语境下并没有单词的概念,一个字就是一个词,所有部分设置样式overflow-wrap: break-word,允许可以在同一个单词之间断行,但在英文语境下,如果允许在一个单词间断行,则会让语句看起来很奇怪,因此该样式默认规则改成 overflow-wrap: normal。

4.2 行高设置

行高影响多行文字下的布局,由于英文有四线格的概念,行高太小时可能无法显示渲染在三四格的英文,而对中文并没有影响,因此当同时font-size和line-height时,应该注意line-height需要大于等于font-size(并且当font-size太小时,line-height不能等于font-size),否则英文可能显示不全。

4.3 英文的首字母大小写问题

对于英文的名词通常有首字母大写的需求,因此在组件中需要注意是否需要转换大小写,并且在翻译后的文本中,名词时常都以首字母大写的方式显示,因此凑成段落后也需要注意将部分词语转成首字母小写,以免出现语法错误。