前端國際化(react-intl)
antd/antd-mobile 國際化方案
- 國際化方案概述
- 前端國際化詳解、舉例
- 國際化資原始檔管理
- 專案之間、開發者與翻譯者之間的協作
- 國際化規範附錄
- 擴充套件閱讀
國際化方案概述
國際化是一個看似簡單,實則非常複雜的領域,實際進行國際化工作時,大家會發現它往往會涉及很多內容:
- 前端國際化
- 服務端國際化
- 國際化資原始檔管理
- 專案之間、開發者與翻譯者之間如何協作
而且,國際化方案往往與具體的技術棧是繫結的。 本國際化方案僅針對 React 技術棧,且不會涉及服務端國際化內容。
前端國際化詳解、舉例
國際化的核心步驟有兩步:
- 建立資原始檔,以 key-value 方式儲存
- 載入資原始檔,將頁面上 key 的內容替換為相關 value
在這裡我們重點看下如何將頁面上的 “key” 替換為相關 “value”。 首先,我需要跟大家介紹一個類庫 React Intl,我們的國際化方案主要是基於它展開的。 React Intl 是由 yahoo 開發的,針對 React 的國際化類庫,基於 Format.js,支援語言、時間、貨幣等等國際化。
React Intl 國際化步驟
- 判斷是否需要引入 polyfill 檔案
- 引入 react-intl 的 local data
- 建立 react-intl 國際化上下文元件
- 使用 react-intl’s components & apis,進行國際化開發
polyfill
React Intl uses and builds on the Internationalization API built-in to JavaScript.
如官方文件提到的那樣,JavaScript 有一套國際化標準 API,React Intl 也是基於它的,但是由於 Safari 或者一些舊版本的瀏覽器不支援,於是我們需要在這些瀏覽器下引入 polyfill 檔案(方式有很多種,參見官方文件):
<!-- index.en.html -->
<script>
// 我們這裡採用的做法是直接判斷 window.Intl 是否存在,從而確定是否要引入 polyfill 檔案
// 下面的 cdn 地址大家可以修改成本地資原始檔,或者參考官方文件其他引入方式
if (!window.Intl) {
document.writeln('<script src="https://as.alipayobjects.com/g/component/intl/1.0.1/Intl.js">' + '<' + '/script>');
document.writeln('<script src="https://as.alipayobjects.com/g/component/intl/1.0.1/locale-data/jsonp/en.js">' + '<' + '/script>');
}
</script>
引入 react-intl 的 local data
import { addLocaleData } from 'react-intl';
import en from 'react-intl/locale-data/en';
addLocaleData(en);
react-intl 在做國際化的時候需要一些特有的 local data,主要是進行相對時間翻譯時,比如昨天、今天、明天、幾分鐘前、幾個月前之類的。
我們通過 addLocaleData
這個方法載入相關內容,大家可以根據實際情況載入需要的 locale-data。
建立 react-intl 國際化上下文元件
為了能夠使用 react-intl 進行國際化,跟 redux 這些框架一樣,我們需要一個 Provider Component,用它來提供國際化的上下文,具體用法:
ReactDOM.render(
<IntlProvider
locale={appLocale.locale}
messages={appLocale.messages}
formats={appLocale.formats}
>
<Provider store={store}>
<Routes history={window.appHistory} />
</Provider>
</IntlProvider>,
document.getElementById('__react-content')
);
通常一個單頁專案只有一個 IntlProvider,當然,IntlProvider 是支援巢狀的。 IntlProvider 有三個入參:
- locale, , 例如 ‘zh-CN’ ‘en-US’
- messages, , 翻譯所需的 key-value 物件
- formats, , 自定義 format,比如日期格式等自定義
在定義好 IntlProvider
之後,我們就可以在頁面使用它提供的 api 或者元件來進行國際化了。
use react-intl’s components & apis
react-intl 提供了豐富的元件和 api 來完成頁面部分的國際化,比如:
import './NotFound.less';
import React from 'react';
import Button from 'antd/lib/button';
import { FormattedMessage } from 'react-intl';
const NotFound = () =>
<div className="page-404">
<div className="page-404-cnt">
<h1>404</h1>
<p>
<FormattedMessage id="page404.message" defaultMessage="未找到該頁面" />
</p>
<a href="/">
<Button type="primary" style={{ marginTop: 5 }}>
<FormattedMessage id="page404.return home" defaultMessage="返回首頁" />
</Button>
</a>
</div>
</div>;
export default NotFound;
<FormattedMessage />
是我們最常用的一個元件,屬性包括 id
defaultMessage
等。
React Intl 國際化基本步驟就是這樣,詳情請檢視官方文件。
國際化資原始檔管理
上面的文件主要講了在引入資原始檔之後,如何進行國際化的步驟,接下來我們來聊下國際化資原始檔的管理。
國際化資原始檔內容
目前我們管理資原始檔的方式是在 src/locales 資料夾下(當然,你放在哪裡都可以):
.
├── en-US.js
├── en-US.messages.js
├── zh-Hans-CN.js
└── zh-Hans-CN.messages.js
*.messages.js 是我們的資原始檔(這裡我們採用了 js 格式,你也可以使用 json 等等),返回的是一個物件,key 為我們翻譯用的 id,value 為具體語言的翻譯,內容是:
module.exports = {
"sendVerifyCode": "Resend verify code after {count} seconds",
"resendVerifyCode": "Resend",
"page404.message": "Not Found",
"page404.return home": "Return To Home",
"page500.message": "Server error, please try again."
}
en-US.js 檔案封裝了 messages、locale 等國際化上下文元件需要的內容:
import datePickerLocale from 'antd/lib/date-picker/locale/en_US';
import appLocaleData from 'react-intl/locale-data/en';
import messages from './en-US.messages.js';
window.appLocale = {
// 合併所有 messages,加入 antd 元件的 messages
messages: Object.assign({}, messages, {
datePickerLocale,
}),
// locale
locale: 'en-US',
// react-intl locale-data
data: appLocaleData,
// 自定義 formates
formats: {
date: {
normal: {
hour12: false,
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
},
},
},
};
有了這些資原始檔以及相關的封裝之後,我們就可以在 InltProvider
中使用了。
國際化資原始檔自動化生成
上面提到了 *.messages.js,我們手動維護它們其實是比較麻煩的,對此,我們提供了一種自動化生成 messages 的方式,具體過程是這樣的:
- 約定 defaultMessage 必填,約定其內容就是中文翻譯,這樣對開發者來說了整個開發過程跟普通寫程式碼並沒有太多區別,只是需要使用特定元件來顯示文案
- 頁面開發完畢後,執行
npm run build:i18n-js
,這個時候我們的指令碼會做幾件事件:- 自動生成 zh-Hans-CN.messages.js,將所有的 defaultMessage 作為中文翻譯 value
- 自動生成 en-US.messages.js,預設 value 為空值,如果 value 你修改過,那 merge 的時候會保留使用者修改的值
- 如果你刪除了部分國際化程式碼,執行指令碼後,相關的 key-value 會從所有的 messages.js 中刪除
資原始檔的載入
大家可以看到我們根目錄下有兩個 html 檔案:
- index.html
- index-en.html
這麼做的主要目的是分開載入資原始檔,index.html 載入中文資原始檔,index-en.html 載入英文資原始檔
這樣做的好處不會載入多餘的資原始檔,效能上也不錯,當然麻煩之處在於需要服務端判斷顯示中文還是英文頁面
除此之外,也可以通過 ajax 請求獲取資原始檔,動態給 IntlProvider
傳相應的 local data
專案之間、開發者與翻譯者之間的協作
聊完了具體的國際化,我們來聊一聊國際化相關的協作,這裡我就不提供具體的方案了,來說下思路吧。
Todo
國際化規範附錄
React Intl 編寫規範
- 必須填寫 defaultMessage,並將 defaultMessage 作為中文翻譯
- id 不得重複
- 在使用 intl.formatMessage() 時,必須使用 defineMessages,預定義訊息
Todo