一、国际化 / i18n
目前国际化,就是开发者写对象,一个key关联若干语种的翻译。相比于浏览器自带的翻译功能,语义更加准确。
“国际化”的简称:i18n(其来源是英文单词 internationalization的首末字符i和n,18为中间的字符数)
二、react项目国际化
react-intl是业界最受欢迎的软件包之一:React-intl是雅虎的语言国际化开源项目FormatJS的一部分,通过其提供的组件和API可以与ReactJS绑定。这种方法引入了两个主要问题:
一:只能应用于视图层,例如React.Component。对于Vanilla JS文件(原生JS),无法对其进行国际化。
二:要获取React.Component的实例,react-intl不能使用常规方法如this.refs.comname
相比之下,react-intl-universal具有以下特征:
- react-intl-universal不仅可以在React.Component中使用,还可以在Vanilla JS中使用
- 简单。只有三个主要的API和一个可选的帮助器。
- 显示不同语言环境的数字,货币,日期和时间。
- 多元化字符串中的标签。
- 消息中的支持变量。
- 在消息中支持HTML。
- 支持150多种语言。
- 在浏览器和Node.js中运行。
- 消息格式由ICU标准严格执行。
- 支持嵌套JSON格式的语言环境数据。
三、具体实现
1. 安装
cnpm install react-intl-universal --save
2. App.js文件
本地语言文件
en-US.json
{"tableTitle": "Results","searchBoardTitle": "filter","ReportNumber": "Report Number"
}
zh-CN.json
{"tableTitle": "列表","searchBoardTitle":"查询","ReportNumber": "报案号"
}
引入ant组件、加载语言环境数据
import { ConfigProvider } from 'antd';
import { emit } from './emit.js'
import zh_CN from 'antd/es/locale/zh_CN';
import en_US from 'antd/es/locale/en_US';
import intl from 'react-intl-universal';
const locales = {'en-US': require('./locales/en-US.json'),'zh-CN': require('./locales/zh-CN.json'),
};
...
初始化语言
class App extends React.PureComponent {constructor(props) {super(props);this.state = {antdLang: zh_CN, // 修改antd 组件的国际化}}async componentWillMount(){const { userStore, history } = this.props;emit.on('change_language', lang => this.loadLocales(lang)); // 监听语言改变事件this.loadLocales(); // 初始化语言}loadLocales(lang = 'en-US') {intl.init({currentLocale: lang, // 设置初始语言locales,}).then(() => {this.setState({antdLang: lang === 'zh-CN' ? zh_CN : en_US});});}
...
加入antd的LocaleProvider 组件:该组件接受一个属性 locale,该属性为当前语言的文案。antd 会通过 react 的 context 将这些信息传递给被 LocaleProvider 包裹的子组件,
render() {return (<ConfigProvider locale={this.state.antdLang}> <div className={styles.App}><PolestarApp><Header /><Suspense fallback={<div></div>}><Switch><Route path='/' exact component={Home} /></Switch></Suspense></PolestarApp></div></ConfigProvider>)}
3. emit.js文件
通过events实现事件监听,即在header切换语言时(发送消息),把切换事件传递到App.js中(接收消息)
const EventEmitter = require('events').EventEmitter;
const emit = new EventEmitter();
export { emit };
4. header中增加语言切换
import React from 'react';
import styles from './header.module.css';
import { Select } from 'antd';
import { emit } from '../../emit.js'function Header() {const handleChange =(val) => {// 发送消息emit.emit('change_language', val);}return (<div className={styles.header_container}><p className={styles.header_name}></p><Select defaultValue="English" onChange={handleChange}><Option value="en-US">English</Option><Option value="zh-CN">中文</Option></Select></div>)
}export default Header;
5.国际化
1. 业务组件的国际化
以claimsManage.js为例:
import intl from 'react-intl-universal'; // 引入
原先写死的部分改成intl.get(key)
2.单独抽取出的js文件的国际化
如果直接在js文件中引入react-intl-universal并使用intl.get(label),传递给searchBoard组件的label为空,需要将原先导出对象,改写成一个function,这样就可以在locales的国际化初始化完成的后,再生成新的配置对象,改写后内容如下:
import intl from 'react-intl-universal';const SearchFormSetting = () => ({lineNum: 4,data: [{type: 'TextField',initialValue: '',label: intl.get('ReportNumber'),key: 'claimId',},{type: 'DatePickerField',initialValue: '',label: intl.get('ReportStartTime'),longLabel: true,key: 'reportStartTime'},{type: 'DatePickerField',initialValue: '',longLabel: true,label: intl.get('ReportEndTime'),key: 'reportEndTime'},]}
)
export default SearchFormSetting
3.其他
1)带变量的消息
get方法的第二个参数中的变量name、where将会被替换成字符串
组件:
<p>{intl.get('HELLO', { name: 'Tony',where:intl.get('where') }) }</p>
en-US.json
"where": "Hangzhou",
"HELLO": "Hello {name},welcome to {where}!"
zh-CN.json
"where": "杭州","HELLO": "你好 {name},欢迎来到{where}!"
2)数字:复数形式和千分符
组件:
<p>{intl.get('CHANCE', { num: 0 })}</p>
<p>{intl.get('CHANCE', { num: 1 })}</p>
<p>{intl.get('CHANCE', { num: 10000000 })}</p>
en-US.json
"CHANCE": "rest chances:{num, plural, =0 {none.} =1 {one chance.} other {# chances.}}"
zh-CN.json
"CHANCE": "剩余次数为{num, plural, =0 {零。} =1 {1次。} other {# 次.}}"
3)显示货币
语言环境数据采用ICU格式。
语法为{名称,类型,格式}。
在如下示例中:
price是消息中的变量名称
类型有number,date,和time。
format是可选的,如果format是货币代码之一,它将以相应的货币格式显示。
如果type为number和format省略,则结果为带千分符的格式化数字。
组件:
{intl.get('SALE_PRICE', { price: 123456.78 })}
en-US.json
"SALE_PRICE": "The price is {price, number, USD}"
zh-CN.json
"SALE_PRICE": "价格是 {price, number, CNY}"
货币代码对照表
4)显示日期
type为date,则format具short、medium、long、full四个值,不同type对日期描述的详尽程度也不同,不穿默认为short
组件:
{intl.get('SALE_START', { start: new Date() })}
{intl.get('SALE_END', { end: new Date() })}
en-US.json
"SALE_START": "Sale begins from {start, date}",
"SALE_END": " to {end, date, long}"
zh-CN.json
"SALE_START": "活动从{start, date}开始,",
"SALE_END": "{end, date, long}结束"
type - short
type - meduim(英:月份简写)
type - long(英:月份全称)
type - full(具体到星期)
5)设置默认值
组件:
class Locale extends React.Component {render() {const name = 'Tony';return (<div>{intl.get('HELLO', { name }).defaultMessage(`Hello, ${name}`)}</div>);}
}
en-US.json
"HELLO": "Hello, {name}"
zh-CN.json
"HELLO": "你好, {name}"
6)返回HTML
组件:
{intl.getHTML('TIP')}
en-US.json
"TIP": "This is <span style='color:red'>SPAN</span>"
zh-CN.json
"TIP": "<span style='color:red'>span元素</span>"