一、國際化 / 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>"