1. 程式人生 > >前端國際化(react-intl)

前端國際化(react-intl)

antd/antd-mobile 國際化方案

  • 國際化方案概述
  • 前端國際化詳解、舉例
  • 國際化資原始檔管理
  • 專案之間、開發者與翻譯者之間的協作
  • 國際化規範附錄
  • 擴充套件閱讀

國際化方案概述

國際化是一個看似簡單,實則非常複雜的領域,實際進行國際化工作時,大家會發現它往往會涉及很多內容:

  • 前端國際化
  • 服務端國際化
  • 國際化資原始檔管理
  • 專案之間、開發者與翻譯者之間如何協作

而且,國際化方案往往與具體的技術棧是繫結的。 本國際化方案僅針對 React 技術棧,且不會涉及服務端國際化內容。

前端國際化詳解、舉例

國際化的核心步驟有兩步:

  1. 建立資原始檔,以 key-value 方式儲存
  2. 載入資原始檔,將頁面上 key 的內容替換為相關 value

在這裡我們重點看下如何將頁面上的 “key” 替換為相關 “value”。 首先,我需要跟大家介紹一個類庫 React Intl,我們的國際化方案主要是基於它展開的。 React Intl 是由 yahoo 開發的,針對 React 的國際化類庫,基於 Format.js,支援語言、時間、貨幣等等國際化。

React Intl 國際化步驟

  1. 判斷是否需要引入 polyfill 檔案
  2. 引入 react-intl 的 local data
  3. 建立 react-intl 國際化上下文元件
  4. 使用 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 的方式,具體過程是這樣的:

  1. 約定 defaultMessage 必填,約定其內容就是中文翻譯,這樣對開發者來說了整個開發過程跟普通寫程式碼並沒有太多區別,只是需要使用特定元件來顯示文案
  2. 頁面開發完畢後,執行 npm run build:i18n-js,這個時候我們的指令碼會做幾件事件:
    • 自動生成 zh-Hans-CN.messages.js,將所有的 defaultMessage 作為中文翻譯 value
    • 自動生成 en-US.messages.js,預設 value 為空值,如果 value 你修改過,那 merge 的時候會保留使用者修改的值
  3. 如果你刪除了部分國際化程式碼,執行指令碼後,相關的 key-value 會從所有的 messages.js 中刪除

資原始檔的載入

大家可以看到我們根目錄下有兩個 html 檔案:

  • index.html
  • index-en.html

這麼做的主要目的是分開載入資原始檔,index.html 載入中文資原始檔,index-en.html 載入英文資原始檔 這樣做的好處不會載入多餘的資原始檔,效能上也不錯,當然麻煩之處在於需要服務端判斷顯示中文還是英文頁面 除此之外,也可以通過 ajax 請求獲取資原始檔,動態給 IntlProvider 傳相應的 local data

專案之間、開發者與翻譯者之間的協作

聊完了具體的國際化,我們來聊一聊國際化相關的協作,這裡我就不提供具體的方案了,來說下思路吧。

Todo

國際化規範附錄

React Intl 編寫規範

  1. 必須填寫 defaultMessage,並將 defaultMessage 作為中文翻譯
  2. id 不得重複
  3. 在使用 intl.formatMessage() 時,必須使用 defineMessages,預定義訊息

Todo

擴充套件閱讀