前端國際化方案選擇

國際化基礎(chǔ)知識

  1. 國際化與本地化

國際化與本地化,或者說全球化壳嚎,其目的是讓你的站點支持多個國家和區(qū)域。其中國際化是指功能和代碼設(shè)計能處理多種語言和文化習(xí)俗末早,能夠在創(chuàng)建不同語言版本時烟馅,不需要重新設(shè)計源程序代碼。國際化的英文單詞是 Internationalization 荐吉,簡稱 I18N。 本地化是將站點按照特定國家口渔、地區(qū)或語言市場的需要進(jìn)行加工样屠,使之滿足特定用戶對語言和文化的特殊要求。本地化的英文是 Localization缺脉,縮寫為 L10N痪欲。以 moment.js 為例,其支持 setLocale 方法切換語言相當(dāng)于國際化攻礼,每一個 locale 的配置文件定義了具體區(qū)域時間格式相當(dāng)于本地化业踢。

  1. CLDR

Unicode Common Locale Data Repository (CLDR) 是軟件國際化的基石,它作為一個國際標(biāo)準(zhǔn)提供了構(gòu)建國際化軟件所需要的定義和數(shù)據(jù)礁扮≈伲可參考其 json 形式示例 形式

  1. ECMAScript Internationalization API

ECMA 提供的國際化 API(底層使用了 CLDR)瞬沦,包括

  • Intl.Collator, 排序
  • Intl.NumberFormat, 數(shù)字的格式化
  • Intl.DateTimeFormat, 日期的格式化

由于兼容性原因,你還需要 polyfill

  1. ICU

一套定義國際化語法的標(biāo)準(zhǔn)雇锡,僅僅是一種規(guī)范逛钻,不同語言有不同的語法,不同語法需要對應(yīng)一種相應(yīng)的解析方法锰提,例如 javascript 里的一種 icu 語法曙痘,和其解析器:

https://github.com/messageformat/messageformathttps://github.com/messageformat/parser

  1. Format.js

FormatJS is a modular collection of JavaScript libraries for internationalization that are focused on formatting numbers, dates, and strings for displaying to people. It includes a set of core libraries that build on the JavaScript Intl built-ins and industry-wide i18n standards, plus a set of integrations for common template and component libraries.

format.js 是一套國際化的方案(實現(xiàn)了上面的 3 和 4 點)立肘,包括以下一些核心庫:

核心庫
  1. po边坤、pot、mo 三種翻譯文件格式
  • po(Portable Object)文件谅年,是面向翻譯人員的茧痒、提取于源代碼的一種資源文件。當(dāng)軟件升級的時候踢故,通過使用 gettext 軟件包處理 po 文件文黎,可以在一定程度上使翻譯成果得以繼承,減輕翻譯人員的負(fù)擔(dān)殿较。文件格式示例如下:
#: ./src/js/components/PaymentManagement/OrderFilter/index.js:69
#: ./src/js/components/PaymentManagement/OrderFilter/index.js:67
msgid "請?zhí)顚懕?ID"
msgstr "Please fill in the table ID"

#: ./src/js/utils/helper.js:382
msgctxt "數(shù)字金額"
msgid "{count} 千萬"
msgstr "{count}0 million"

#: ./src/js/components/Dashboard/StatisticsPanel/API/index.js:33
msgid "{count} 次"
msgid_plural "{count} 次"
msgstr[0] "{count} time"
msgstr[1] "{count} times"
  • pot 文件耸峭,性質(zhì)和 po 一樣,只不過它主要用作模板

  • mo( Machine Object)文件是面向計算機的淋纲、由 PO 文件通過 gettext 軟件包編譯而成的二進(jìn)制文件劳闹。程序通過讀取 MO 文件使自身的界面轉(zhuǎn)換成用戶使用的語言。|

國際化方案

方案一:react-intl

react-intl 是基于 react 整合了 format.js 的 一套 react 國際化方案洽瞬。

This library provides React components and an API to format dates, numbers, and strings, including pluralization and handling translations.

  1. react-intl 提供 API 和組件兩種形式方式使用:

  2. 加載 localeData

import {addLocaleData} from 'react-intl';
import frLocaleData from 'react-intl/locale-data/fr';
addLocaleData(frLocaleData);

The locale data added with this function supports plural and relative-time formatting features as described in Loading Locale Data.

  1. Creating an I18n Context
ReactDOM.render( 
  <IntlProvider
    locale={usersLocale}
    messages={translationsForUsersLocale}
  >
    <App/>
  </IntlProvider>, 
  document.getElementById('container')
);

根據(jù) locale 加載不同的 messages 文件本涕,messages 格式如下:

{
  "greeting": "Hello, {name}"
}

4.格式化接口

日期:

  • formatDate
  • formatTime
  • formatRelative

數(shù)字:

  • formatNumber
  • formatPlural(This function should only be used in apps that only need to support one language. If your app supports multiple languages use formatMessage instead.)
  • formatNumber(1000); // "1,000" formatNumber(0.5, {style: 'percent'}); // "50%" - formatNumber(1000, {style: 'currency', currency: 'USD'}); // $1,000

字符串:

  • formatMessage
  • formatHTMLMessage
  • formatMessage(messages.greeting, {name: 'Eric'})

復(fù)數(shù):

"Hello, {name}, you have {itemCount, plural, =0 {no items} one {# item} other {# items} }."

5.格式化組件

如果句子中有 html 元素,推薦使用 FormattedMessage 組件

<FormattedMessage 
  id='app.greeting' 
   description='Greeting to welcome the user to the app'  defaultMessage='Hello, {name}!'
   values={{  name: <b>Eric</b>  }}
/>

This allows messages to still be defined as a plain string without HTML — making it easier for it to be translated. At runtime, React will also optimize this by only re-rendering the variable parts of the message when they change. In the above example, if the user changed their name, React would only need to update the contents of the <b> element.

6.提供 babel-plugin-react-intl 插件伙窃,自動提取待翻譯文本菩颖,前提需要先用 defineMessages 進(jìn)行定義,同時生成的 json 無法和現(xiàn)有的翻譯工具(需要 po 文件格式)整合(react-intl-gettext 工具可以將 json 轉(zhuǎn)為 po 文件)

const messages = defineMessages({ 
   greeting: {
     id: 'app.home.greeting',  
     description: 'Message to greet the user.',  
     defaultMessage: 'Hello, {name}!', 
   }, 
});

formatMessage('app.home.greeting', {name: ‘xxx’})
  1. 總結(jié)
  • 提供了國際化前端轉(zhuǎn)換的完整方案(即字符串为障、時間晦闰、日期的轉(zhuǎn)換)
  • 使用麻煩,需要指定 id鳍怨,而不是默認(rèn)語言作為 id呻右,比如使用 formatMessage(‘你好’)
  • 沒有提供完整解決方案,即只解決了如何從翻譯文件解析轉(zhuǎn)換鞋喇,而沒有解決如何自動生成翻譯文件(babel-plugin-react-intl 不完善)声滥,整合使用現(xiàn)有的翻譯工具
  • 文檔和示例不完整、不清晰

方案二:使用 node-gettext + narp 構(gòu)建一套完整的翻譯工具鏈

  1. 使用 node-gettext 做為轉(zhuǎn)換工具
import  Gettext  from  'node-gettext' 
import swedishTranslations from  './translations/sv-SE.json'  
const gt = new  Gettext()  gt.addTranslations('sv-SE', 'messages', swedishTranslations)  
gt.setLocale('sv-SE') 
gt.gettext('The world is a funny place')

node-gettext 提供了字符串的轉(zhuǎn)換侦香,沒有提供日期落塑、數(shù)字的轉(zhuǎn)換:

GNU gettext features categories such as LC_MESSAGES, LC_NUMERIC and LC_MONETARY, but since there already is a plethora of great JavaScript libraries to deal with numbers, currencies, dates etc, node-gettext is simply targeted towards strings/phrases. You could say it just assumes the LC_MESSAGES category at all times.

另外纽疟,node-gettext 接受的是 json 文件

  1. 使用 narp 完成翻譯文本自動提取,向 poeditor 或 transifex 上傳待翻譯文件和拉取翻譯后的文件芜赌,和格式轉(zhuǎn)換等功能
  • . narp push

Extraction of strings from source code (react-gettext-parser),
Merging upstream and local translations in order to support translations in branches (pot-merge)
Uploading POT resources to the translation service you use (Transifex or POEditor)

  • narp pull

Download PO translations from the translation service(丟棄掉舊的仰挣,全部用最新拉取的)
Converting PO to JSON (gettext-parser)
Writing the JSON translations to where you want it

  1. The JSON translations are formatted for node-gettext.(使用 node gettext 轉(zhuǎn)換)
  • 使用 react-gettext-parser 提取翻譯字符串,生成 pot 文件
  • 調(diào)用 poeditor 或 transifex 平臺 API 上傳下載翻譯文件
  • 使用 gettext-parser Parse and compile gettext po and mo files to/from json
  1. 總結(jié):
  • node-gettext 僅提供字符串的轉(zhuǎn)換缠沈,數(shù)字和時間等膘壶,需要自己使用第三方庫來實現(xiàn),這個特點見仁見智洲愤,對于想一步到位的開發(fā)者而言颓芭,需要自己找第三方庫,同時每個庫都要單獨進(jìn)行 locale 配置柬赐,稍顯麻煩亡问;而對于需要定制化,或?qū)π阅芤蟾叩拈_發(fā)者肛宋,則提供了一個擴(kuò)展性很強的環(huán)境州藕。
  • node-gettext 不支持插值,需要借助第三方插件
  • node-gettext 的語法簡單通用
  • narp 封裝了 poeditor 和 transifex 兩個平臺的 API酝陈,通過命令行可以實現(xiàn)快速地上傳和下載翻譯文件床玻,同時實現(xiàn)了文件自動合并功能,省去在多分支情況下沉帮,遇到?jīng)_突時要手動合并的麻煩
  • 目前 narp 會將不同的翻譯文件合成一個 json 文件锈死,但支持的語種和待翻譯語句較多時,產(chǎn)生的文件會很大穆壕,嚴(yán)重影響性能待牵。最好的做法還是,不同語言生成到不同文件中喇勋,初始加載默認(rèn)語言缨该,在根據(jù)當(dāng)前區(qū)域,動態(tài)加載所需語言(這種情況下切換語言的話川背,需要刷新才能得到更新贰拿,這種需求也比較合理)。

該方案的操作實例渗常,可以參考這里 https://github.com/YaoKaiLun/i18n-example

方案三:i18n-next

前面的方案二壮不,已經(jīng)基本滿足了我們的業(yè)務(wù)需求汗盘,而且其相對簡單皱碘,容易理解。但如果你有更多的需求隐孽,或者你對國際化要求更高癌椿,那你可以嘗試使用 i18n-next健蕊。

I18next is an internationalization-framework written in and for JavaScript. But it's much more than that.
i18next goes beyond just providing the standard i18n features such as (plurals, context, interpolation, format). It provides you with a complete solution to localize your product from web to mobile and desktop.

它支持以下特性:

  • Splitting translations into multiple files. Only load translations needed.
  • There are plugins to detect languages for most environments (browser, native, server). This enables to set priority of where to detect and even enables to cache set languages over requests / visits.
  • There are endless plugins to load translation from server, filesystem, ... this backends also assert that loading gets retried on failure, or that a file does not get loaded twice and callbacks of success are only called once. Those backends can even provide an additional layer for local caching eg. in localStorage.
  • Options what to load and how to fallback depending on language.
  • Support for objects and arrays?
  • Full control over management of the translations stored.
  • Rich system of events to react on changes important to your application.
  • Freedom of i18n formats - prefer ICU? Just use i18next-icu plugin.

同時,可以通過一些自動提取翻譯語句和解析 PO 文件插件來實現(xiàn)方案二中 narp 的部分功能踢俄。

事實上缩功,i18n-next 在 2011 年的時候就已經(jīng)創(chuàng)建了。i18n-next 可以說提供了一個解決國際化問題的完整生態(tài)都办,除了基本的需求外嫡锌,你還可以通過各種擴(kuò)展,實現(xiàn)更多功能琳钉。而且相比 react-intl,他更活躍、更友好晾匠、更可靠一點匾旭。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市及皂,隨后出現(xiàn)的幾起案子甫男,更是在濱河造成了極大的恐慌,老刑警劉巖验烧,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件板驳,死亡現(xiàn)場離奇詭異,居然都是意外死亡噪窘,警方通過查閱死者的電腦和手機笋庄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來倔监,“玉大人直砂,你說我怎么就攤上這事『葡埃” “怎么了静暂?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長谱秽。 經(jīng)常有香客問我洽蛀,道長,這世上最難降的妖魔是什么疟赊? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任郊供,我火速辦了婚禮,結(jié)果婚禮上近哟,老公的妹妹穿的比我還像新娘驮审。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布疯淫。 她就那樣靜靜地躺著地来,像睡著了一般。 火紅的嫁衣襯著肌膚如雪熙掺。 梳的紋絲不亂的頭發(fā)上未斑,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天,我揣著相機與錄音币绩,去河邊找鬼蜡秽。 笑死,一個胖子當(dāng)著我的面吹牛缆镣,可吹牛的內(nèi)容都是我干的载城。 我是一名探鬼主播,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼费就,長吁一口氣:“原來是場噩夢啊……” “哼诉瓦!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起力细,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤睬澡,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后眠蚂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體煞聪,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年逝慧,在試婚紗的時候發(fā)現(xiàn)自己被綠了昔脯。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡笛臣,死狀恐怖云稚,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情沈堡,我是刑警寧澤静陈,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站诞丽,受9級特大地震影響鲸拥,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜僧免,卻給世界環(huán)境...
    茶點故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一刑赶、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧懂衩,春花似錦撞叨、人聲如沸呛伴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至沛申,卻和暖如春劣领,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背铁材。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工尖淘, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人著觉。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓村生,卻偏偏與公主長得像,于是被迫代替她去往敵國和親饼丘。 傳聞我的和親對象是個殘疾皇子趁桃,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,490評論 2 348

推薦閱讀更多精彩內(nèi)容