摘要:而就是產(chǎn)品具體實(shí)現(xiàn)某一種語言和文化的過程。貨幣的符號,以及數(shù)字分割方式各個國家都存在不同。那么有沒有其他的復(fù)數(shù)形式回答當(dāng)然是肯定的,比如波蘭語。但這個是自己的語法,并非標(biāo)準(zhǔn),同時這個語法還會破壞的測試,并不是一個很好的選擇。
記得我剛來我們公司的時候,接手現(xiàn)在負(fù)責(zé)的項(xiàng)目的時候,我就發(fā)覺了一個問題:所有的文本資源都是硬編碼在代碼里面。這當(dāng)然會帶來很多問題。但考慮到我負(fù)責(zé)的這個項(xiàng)目是公司內(nèi)部的管理工具,同時大部分用戶都是中國人,因此抽離文本資源,做i18n的需求并不是十分強(qiáng)烈。
這周公司招了一位外籍員工。我并不確定她是哪一國人,不過從口音上來判斷,以及言談間她曾經(jīng)提到的加利福尼亞州,我想應(yīng)該是一位美國女性。老大說她會和其他的PM一樣,居住在廈門,遠(yuǎn)程工作,偶爾來辦公室上班。并且她也會使用我負(fù)責(zé)的這個工具。
現(xiàn)在i18n就有比較強(qiáng)烈的需求了。有必要出一個合理的架構(gòu),一勞永逸的解決問題。
i18n的主要關(guān)注點(diǎn)i18n是Internationalization的縮寫,實(shí)際上i18n應(yīng)該是指創(chuàng)建或者調(diào)整產(chǎn)品,使得產(chǎn)品具有能輕松適配指定的語言和文化的能力。當(dāng)然,我們還有另外一個概念,叫做Localization(簡寫L10n),也就是本地化。L10n正確的說是指已經(jīng)全球化的產(chǎn)品,適配某一個具體語言和文化的這一個過程。
有點(diǎn)繞口,簡單說就是,i18n就是給產(chǎn)品添加新特性,使產(chǎn)品能夠支持對多種語言和文化(貨幣,時間等等)。而L10n就是產(chǎn)品具體實(shí)現(xiàn)某一種語言和文化的過程。
回過頭來,i18n有這么幾個主要的關(guān)注點(diǎn):
Date and times formatting
Number formatting
Language sensitive string comparison
Pluralization
Date and times formatting不同國家對應(yīng)的日期格式其實(shí)都是不同的,盡管我不覺得十分復(fù)雜,不過細(xì)節(jié)的處理上也是有很多選擇:
weekday,你可以設(shè)置成顯示全名字,比如zh-CN的星期四,en-US的Thursday等等
month,你可以設(shè)置成數(shù)字形式,全名,短名,類似于12,December,Dec
...
完整的例子:
var date = new Date(Date.UTC(2012, 11, 20, 3, 0, 0)); // Results below use the time zone of America/Los_Angeles (UTC-0800, Pacific Standard Time) // US English uses month-day-year order console.log(new Intl.DateTimeFormat("en-US").format(date)); // → "12/19/2012" // British English uses day-month-year order console.log(new Intl.DateTimeFormat("en-GB").format(date)); // → "19/12/2012" // Korean uses year-month-day order console.log(new Intl.DateTimeFormat("ko-KR").format(date)); // → "2012. 12. 19." // Arabic in most Arabic speaking countries uses real Arabic digits console.log(new Intl.DateTimeFormat("ar-EG").format(date)); // → "???/???/????" // for Japanese, applications may want to use the Japanese calendar, // where 2012 was the year 24 of the Heisei era console.log(new Intl.DateTimeFormat("ja-JP-u-ca-japanese").format(date)); // → "24/12/19" // when requesting a language that may not be supported, such as // Balinese, include a fallback language, in this case Indonesian console.log(new Intl.DateTimeFormat(["ban", "id"]).format(date)); // → "19/12/2012" var date = new Date(Date.UTC(2012, 11, 20, 3, 0, 0)); // request a weekday along with a long date var options = { weekday: "long", year: "numeric", month: "long", day: "numeric" }; // an application may want to use UTC and make that visible options.timeZone = "UTC"; options.timeZoneName = "short"; console.log(new Intl.DateTimeFormat("en-US", options).format(date)); // → "Thursday, December 20, 2012, GMT"Number formatting以及Pluralization
數(shù)字的格式化,這個比較有趣。這里說的數(shù)字,包含了貨幣,百分比,浮點(diǎn)數(shù)。其中貨幣的顯示應(yīng)該是相對比較復(fù)雜的。就以en-US來說,1000美元通常顯示成$1,000.00,而1000人民幣則會顯示成¥1,000.00。貨幣的符號,以及數(shù)字分割方式各個國家都存在不同。
var number = 123456.789; // German uses comma as decimal separator and period for thousands console.log(new Intl.NumberFormat("de-DE").format(number)); // → 123.456,789 // India uses thousands/lakh/crore separators console.log(new Intl.NumberFormat("en-IN").format(number)); // → 1,23,456.789 // the nu extension key requests a numbering system, e.g. Chinese decimal console.log(new Intl.NumberFormat("zh-Hans-CN-u-nu-hanidec").format(number)); // → 一二三,四五六.七八九 var number = 123456.789; // request a currency format console.log(new Intl.NumberFormat("de-DE", { style: "currency", currency: "EUR" }).format(number)); // → 123.456,79 € // the Japanese yen doesn"t use a minor unit console.log(new Intl.NumberFormat("ja-JP", { style: "currency", currency: "JPY" }).format(number)); // → ¥123,457
涉及到數(shù)字的,還有另外一個問題,那就是語言的復(fù)數(shù)形式。中文似乎是沒有復(fù)數(shù)形式的,比如我們經(jīng)常說,一只兔子,兩只兔子。但是如果你用英語,你就能明顯發(fā)覺不對。在英語里,應(yīng)該說one rabbit,two rabbits,many rabbits。是的,英語里主要有兩種復(fù)數(shù)形式。
那么有沒有其他的 復(fù)數(shù)形式?回答當(dāng)然是肯定的,比如波蘭語。在波蘭語,兔子一詞是królik,它的復(fù)數(shù)形式有這么幾種情況:
兔子的數(shù)量是1,那么應(yīng)該這么說,królik
如果兔子的數(shù)量在2-4之間,那么應(yīng)該說,królika
如果兔子的數(shù)量不是1,并且數(shù)量在0 - 1之間,或者5 - 9之間,或者12 - 14之間,都用królików
其他情況統(tǒng)一用króliki
解決方案的設(shè)計 項(xiàng)目背景使用facebook官方的create-react-app腳手架創(chuàng)建的react app
關(guān)注點(diǎn)目前我的解決方案有這么幾個關(guān)注點(diǎn):
文本資源要能夠輕易的導(dǎo)出
文本資源要孤立,避免和程序?qū)崿F(xiàn)的耦合
提供極簡的接口方法設(shè)計
處理語言復(fù)數(shù)形式的庫,應(yīng)該要能很好的拓展
技術(shù)選型FormatJS, a modular collection of JavaScript libraries for internationalization that are focused on formatting numbers, dates, and strings for displaying to people.
解決方案 項(xiàng)目的目錄結(jié)構(gòu)/-- |--node_modules |--public |--src |--app |--common |--components |--configs |--i18n |--app |--routes |--setting |--en-US.js |--zh-CN.js |--index_en-US.js |--common |--index_en-US.js |--components |--index_en-US.js |--index_en-US.js |--index_zh-CN.js |--index.js |--logo.svg |--setupTests.js
如上文所示,我將所有的文本資源都獨(dú)立出來,多帶帶存放在了i18n這個文件夾下。實(shí)際上,這些文本資源是有自己獨(dú)立的命名空間的,比如/src/app相關(guān)的文本資源,就會多帶帶放在這個文件夾下。其他的比如/src/common/和/src/components/就以此類推。
Intl類這個類很簡單,封裝了處理文本資源的相關(guān)方法。getText的參數(shù)key需要特別注意,這個參數(shù)應(yīng)該是絕對路徑,比如app.routes.setting.preferences這樣。那么,相關(guān)的資源應(yīng)該是要放在/src/i18n/app/routes/setting/en-US.js文件里。
class Intl { static COMP_COMMON_TEXT = "components.Common"; constructor(locale, resource) { this.locale = locale || "zh-CN"; this.resource = resource; } getCommonText(key, params = {}) { return this.getText(`${Intl.COMP_COMMON_TEXT}.${key}`, params); } getText(key, params = {}) { let textResource = ""; let source = this.resource; const locale = this.locale; const properties = key.split("."); const hasOwnProperty = Object.prototype.hasOwnProperty; properties.forEach((property, index) => { const stillNameSpace = index !== properties.length - 1; if (stillNameSpace) { source = source[property]; } else if (hasOwnProperty.call(source[property], "default")) { textResource = source[property].default; } else { textResource = source[property] || ""; } }); const msg = new IntlMessageFormat(textResource, locale); return msg.format(params); } }IntlProvider
這是一個React組件。這里我們要利用React提供的Context這一特性,讓整個React App范圍內(nèi),都會從上下文中得到getText的方法。
我們都知道,Web app初始化的時候加載的Javascript腳本是越小越好,并且我們應(yīng)該盡力保證按需加載所需要的資源。這也是我們?yōu)槭裁蠢肳ebPack提供的Code Splitting機(jī)制讓W(xué)ebPack在打包的時候,切分出多帶帶的chunk,減少包的體積。
在WebPack 1.x的時候,我們可以使用require.ensure()。但這個是WebPack自己的語法,并非標(biāo)準(zhǔn),同時這個語法還會破壞Jest的測試,并不是一個很好的選擇。WebPack 2.x以后就開始提供基于import()的Code Splitting機(jī)制。因此我們應(yīng)該利用起來。
具體的兩個文檔:
WebPack的Code Splitting with ES2015
Dynamic import() proposal
class IntlProvider extends React.Component { static DEFAULT_LOCALE = "zh-CN"; static propTypes = { locale: PropTypes.string, children: PropTypes.element, }; static defaultProps = { locale: "zh-CN", children: null, }; static childContextTypes = { getText: PropTypes.func, getCommonText: PropTypes.func, }; state = {}; constructor(props, context) { super(props, context); this.childContext = new Intl(props.locale); } async componentWillMount() { const { locale } = this.props; const lang = await import(`../i18n/index_${locale}.js`); this.childContext = new Intl(locale, lang); this.setState({ lang, }); } getChildContext() { if (!this.childContext) { return { getText: (key, params) => "", getCommonText: (key, params) => "", }; } return { getText: (key, params) => this.childContext.getText(key, params), getCommonText: (key, params) => this.childContext.getCommonText(key, params), }; } render() { const comp = (!this.state.lang) ? null : React.Children.only(this.props.children); return comp; } }App
使用的時候也是相當(dāng)簡單,不多說,直接上代碼。
class App extends React.PureComponent { render() { const { preferences } = this.props; return (參考文檔); } } {this.props.children}
Tags for Identifying Languages
ECMAScript Internationalization API
Pluralization for JavaScript
ICU User Guide
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/89829.html
摘要:是一個簡單且靈活易用的格式化和工具。它通過連接組件給組件一個默認(rèn)為的。是一個可以根據(jù)不同的顯示不同內(nèi)容的函數(shù)。和內(nèi)容之間的關(guān)系可以靈活地通過配置確定。在線互動演示最簡單的使用方式你好歡迎你好歡迎也可與相連 react-put 是一個簡單且靈活易用的格式化和 i18n 工具。 它通過連接組件給組件一個默認(rèn)為 put 的 props。put 是一個可以根據(jù)不同的 key 顯示不同內(nèi)容的函數(shù)...
摘要:先睹為快先看一下最后的成果來一發(fā)控制臺中對應(yīng)中的信息開始原理原理其實(shí)很簡單字符串替換。拉取遠(yuǎn)程的國際化文件到本地,再根據(jù)語言做一個映射就可以了。 背景 樓主最近新接了一個項(xiàng)目,從0開始做,需要做多語言的國際化,今天搞了一下,基本達(dá)到了想要的效果, 在這里簡單分享下: 一些探索 也說不上是探索吧,就Google了一波, GitHub 上找了一個比較成熟的庫 react-i18next,...
摘要:先睹為快先看一下最后的成果來一發(fā)控制臺中對應(yīng)中的信息開始原理原理其實(shí)很簡單字符串替換。拉取遠(yuǎn)程的國際化文件到本地,再根據(jù)語言做一個映射就可以了。 背景 樓主最近新接了一個項(xiàng)目,從0開始做,需要做多語言的國際化,今天搞了一下,基本達(dá)到了想要的效果, 在這里簡單分享下: 一些探索 也說不上是探索吧,就Google了一波, GitHub 上找了一個比較成熟的庫 react-i18next,...
摘要:假如有這么一段句子這件衣服是人民幣如果我們想將一個數(shù)字以人民幣的形式寫進(jìn)去的話可以這么做最終顯示結(jié)果是這件衣服是人民幣其實(shí)它做了兩件事一個是加符號,另一個是加分隔符。同時表示人民幣,表示美元。 今天來介紹一個非常international的東西。 i18n國際化(internationalization)的簡稱。之所以叫i18n,是因?yàn)樽帜竔和n之間有18個字母,所以才叫i18n。不...
摘要:本國際化方案僅針對技術(shù)棧,且不會涉及服務(wù)端國際化內(nèi)容。引入多語言環(huán)境的數(shù)據(jù)雖然我只用到了文本翻譯的功能,以為就不需要加載這些數(shù)據(jù),但后來發(fā)現(xiàn)這是必須的步驟。 前言 最近新接了一個項(xiàng)目,從0開始做,需要做多語言的國際化,今天搞了一下,基本達(dá)到了想要的效果, 在這里簡單分享下: showImg(https://segmentfault.com/img/bVbuiJB); 背景國際化方案國際...
閱讀 663·2023-04-26 02:03
閱讀 1037·2021-11-23 09:51
閱讀 1111·2021-10-14 09:42
閱讀 1738·2021-09-13 10:23
閱讀 927·2021-08-27 13:12
閱讀 839·2019-08-30 11:21
閱讀 1001·2019-08-30 11:14
閱讀 1041·2019-08-30 11:09