國際化基礎(chǔ)知識
- 國際化與本地化
國際化與本地化,或者說全球化壳嚎,其目的是讓你的站點支持多個國家和區(qū)域。其中國際化是指功能和代碼設(shè)計能處理多種語言和文化習(xí)俗末早,能夠在創(chuàng)建不同語言版本時烟馅,不需要重新設(shè)計源程序代碼。國際化的英文單詞是 Internationalization 荐吉,簡稱 I18N。 本地化是將站點按照特定國家口渔、地區(qū)或語言市場的需要進(jìn)行加工样屠,使之滿足特定用戶對語言和文化的特殊要求。本地化的英文是 Localization缺脉,縮寫為 L10N痪欲。以 moment.js 為例,其支持 setLocale 方法切換語言相當(dāng)于國際化攻礼,每一個 locale 的配置文件定義了具體區(qū)域時間格式相當(dāng)于本地化业踢。
Unicode Common Locale Data Repository (CLDR) 是軟件國際化的基石,它作為一個國際標(biāo)準(zhǔn)提供了構(gòu)建國際化軟件所需要的定義和數(shù)據(jù)礁扮≈伲可參考其 json 形式示例 形式
ECMA 提供的國際化 API(底層使用了 CLDR)瞬沦,包括
- Intl.Collator, 排序
- Intl.NumberFormat, 數(shù)字的格式化
- Intl.DateTimeFormat, 日期的格式化
由于兼容性原因,你還需要 polyfill
一套定義國際化語法的標(biāo)準(zhǔn)雇锡,僅僅是一種規(guī)范逛钻,不同語言有不同的語法,不同語法需要對應(yīng)一種相應(yīng)的解析方法锰提,例如 javascript 里的一種 icu 語法曙痘,和其解析器:
https://github.com/messageformat/messageformat 、https://github.com/messageformat/parser
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 點)立肘,包括以下一些核心庫:
- 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.
react-intl 提供 API 和組件兩種形式方式使用:
加載 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.
- 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’})
- 總結(jié)
- 提供了國際化前端轉(zhuǎn)換的完整方案(即字符串为障、時間晦闰、日期的轉(zhuǎn)換)
- 使用麻煩,需要指定 id鳍怨,而不是默認(rèn)語言作為 id呻右,比如使用 formatMessage(‘你好’)
- 沒有提供完整解決方案,即只解決了如何從翻譯文件解析轉(zhuǎn)換鞋喇,而沒有解決如何自動生成翻譯文件(babel-plugin-react-intl 不完善)声滥,整合使用現(xiàn)有的翻譯工具
- 文檔和示例不完整、不清晰
方案二:使用 node-gettext + narp 構(gòu)建一套完整的翻譯工具鏈
- 使用 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 文件
- 使用 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
- 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
- 總結(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,他更活躍、更友好晾匠、更可靠一點匾旭。