[ 一塊兒學React系列 -- 10 ] i18n

今天來介紹一個很是international的東西。html

i18n
國際化( internationalization)的簡稱。之因此叫i18n,是由於字母 i和n之間有18個字母,因此才叫i18n。不要認爲這是一個高大上的名詞,其實就是由於懶才簡寫的。hiahiahia...

由於本系列是以React爲中心,因此只介紹React項目中的國際化解決方案。固然還有不少不少...不少別的國際化解決方案,可是不是全部的輪子都適合React這輛開往幼兒園的車。
實際上國際化在平常項目中用的沒那麼頻繁,除非有業務需求,好比要作一個很是international的項目。目前在React中比較熱門的兩個包就是react-intl-universalreact-intl。由於本文重點介紹對象是前者,因此咱們先簡單介紹下後者。固然在這裏不會把它的使用方法列出來,而是把它的缺點列出來,爲何呢?由於筆者懶啊!
react-intl不足的地方主要是兩個:前端

它只能用於視圖層。舉個例子,好比React.Comoponent對象,可是對於Vanilla JS就會顯得很無力了,由於它沒法在Vanilla JS中實例化。(這裏會有人感到奇怪,Vanilla JS是什麼鬼?哈哈...百gu度ge吧,不會發現新大陸)!

其次react

想使用國際化方法,咱們必需要利用它的一個方法將本身的組件轉化成另一個class。這就比較蛋疼了,例子以下:
import { injectIntl } from 'react-intl';
class MyComponent extends Component {
  render() {
    const intl = this.props;
    const title = intl.formatMessage({ id: 'title' });
    return (<div>{title}</div>);
  }
};
export default injectIntl(MyComponent);

看我筆者第一篇文章的朋友應該有印象:全部被包裹過的組件,若是你想得到本來的組件的對象,那得調用相應的方法。這裏也不例外,若是咱們想獲取組件的原對象,那就得這麼作:git

class MyComponent {...}
export default injectIntl(MyComponent, {withRef: true});
 
class App {
  render() {
    <MyComponent ref="my"/>
  }
  getMyInstance() {
    console.log('getMyInstance', this.refs.my.getWrappedInstance());
  }
}

這樣寫會不會以爲太麻煩了...
因此Alibaba前端組就按捺不住了,而後就搞出了本身的react-intl-universal。看名字不就是在react-intl後面加個universal嗎?的確是這樣,不過筆者不清楚這個框架的核心邏輯是否是參考的react-intl,可是單從名字來看就有點"可疑"了,翻譯就是react-intl的通用版(固然,純屬意淫,一笑而過!)。github

react-intl-universal

做爲一個國際化解決方案,首先實現國際化是它的基本功能。其次它還有一些別的功能,好比文本格式化、貨幣格式化、時間格式化等等,我相信這些都是咱們頁面開發常用到的功能。json

i18n

首先來看一下它的技術功能:國際化
react-intl-universal採用了與組件無關的方法來實現國際化。國際化的本質其實就是將咱們預先設置好的不一樣語言的句子按照語言環境顯示在頁面上。瀏覽器

import intl from 'react-intl-universal';

經過intl這個對象來實現初始化和國際化處理。咱們能夠認爲這個intl是一個單例對象。咱們在App啓動的時候對其進行初始化,爾後在別的地方再次導入的時候仍然是一個已經初始化過的對象。在這種狀況下,國際化處理就會變得異常簡單。其次就是準備多語言句子了,傳統的在前端處理這個問題是將不一樣語言的句子放在不一樣的json文件中再導出,文件結構以下:
clipboard.png
這樣咱們就能夠在App啓動或者切換語言的時候導入相應的json對象了。cookie

首先是API介紹

intl對象主要有三個經常使用的用於國際化處理的API,determineLocale、init、getapp

  • determineLocale
    看到方法名就應該知道它是用來幹什麼了。它用來肯定在整個體系中使用的是哪一種語言。看代碼:
let currentLocale = intl.determineLocale({
    urlLocaleKey: "lang",
    cookieLocaleKey: "lang"
});

react-intl-universal肯定語言的方式有三種,一個是經過urlLocaleKey,即lang關鍵字從url中獲取是哪一種語言。好比:http://localhost?lang=en-US,由於lang對應的值是en_US,因此語言爲英文。其次是從Cookie獲取,由於Cookie也是以鍵值對形式存儲的,因此會檢查當前域下的Cookie是否有對應的lang。若是上述兩種都沒有,那麼會默認使用瀏覽器當前的語言類型。固然上述的urlLocaleKey和cookieLocaleKey是能夠自定義的,不是固定的lang.框架

  • init

init方法即用來初始化intl對象。初始化參數主要是兩個,一個是currentLocale即當前的語言,另外一個是locales即當前語言對應的json對象,好比{"en-US":{"key1":"value1"} 或者 {"zh-CN":{"key1":"值1"}}

  • get
    get方法就相對簡單,就是根據鍵去intl中獲取對應的值,這裏不作過多解釋。

完整的初始化過程以下:

class App extends Component{
    ....
    
    componentDidMount() {
        this.loadLocales();
    }

    loadLocales() {
        const _self = this;
        let currentLocale = intl.determineLocale({  //若是cookie和url中均沒有相關參數,那麼以瀏覽器語言爲準
            urlLocaleKey: "lang",
            cookieLocaleKey: "lang"
        });

        http
            .get(`locales/${currentLocale}.json`)   //理解爲按需加載而且locales文件夾須要放在public文件下供http訪問
            .then(res => {
                return intl.init({
                    currentLocale,
                    locales: {
                        [currentLocale]: res.data //若是key是變量,那麼須要用[]包一下
                    }
                });
            })
            .then(() => {
                _self.setState({initDone: true});
            });
    }
    
    ....
}

而後在須要國際化的地方這麼使用

import intl from 'react-intl-universal';
<p>{intl.get('name')}</p>

是否是很簡單? 並且徹底避免了react-intl的兩個缺點。

格式化工具

前面說了react-intl-universal不只僅能夠用來作國際化處理,還能夠用來作簡單的文本格式化處理。下面咱們列舉幾個經常使用的。

Html Snippet

假如咱們的json文件中有這麼一段

...
"red": "<p style='color:red'>紅色</p>",
...

若是咱們直接用get方法獲取的話,那麼會直接把<p style='color:red'>紅色</p>給打印出來。若是咱們想將它以html片斷的形式打印出來的話,就使用getHTML方法,它在獲取到句子的時候會進行解析並生成最終的Html Snippet。

Default Message

缺省值其實就是默認值,是對於json鍵值對的默認值。將入咱們去獲取一個json中沒有的鍵值對那麼系統就會報錯。如何去規避這個問題呢?react-intl-universal給咱們提供了這樣一個方法:

intl.get('not-exist-key').defaultMessage('default message')

這是一個鏈式調用。若是json中沒有not-exist-key這個鍵,那就會默認返回defaultMessage的參數。簡寫是intl.get('not-exist-key').d('default message')

Message With Variables

假如某個句子包含了一個變量怎麼辦?好比一個用戶名,咱們只有在用戶登陸的時候才知道他的用戶名。

{
    "me": "你好,我是{me}"
}

此時就用到了get放的第二個參數。對於上面的例子,咱們能夠這樣處理:

<p>{intl.get('me', {'me': '皮卡丘'})}</p>

get方法會找出句子中被{}包住的變量me,而後在第二個參數(json對象)找出me對應的值皮卡丘並將{me}整個用皮卡丘替換。另外須要注意的是,json對象只能爲一層,不可嵌套

Display Currency

它還能夠用來格式化貨幣。假若有這麼一段句子

{
  "price": "這件衣服是 {price,number,CNY} 人民幣",
}

若是咱們想將一個數字以人民幣的形式寫進去的話能夠這麼作:

{intl.get('price', {'price': 1000})}

最終顯示結果是:這件衣服是 ¥1,000 人民幣
其實它作了兩件事:一個是加符號,另外一個是加分隔符。同時CNY表示人民幣,USD表示美圓

Display Dates

而後是日期的處理。假若有這麼一段話:

{
    "date": "今天是{date,date,full}"
}

而後咱們這麼使用它的話:

<p>{intl.get('date',{'date':new Date()})}</p>

顯示結果是今天是2018年12月3日星期一。其實{date,date,full}這段指令就是將date變量替換成對應日期(new Date())並以long形式展現。

同時日期展現形式有四種

  • short: shows date as shortest as possible
  • medium: shows short textual representation of the month
  • long: shows long textual representation of the month
  • full: shows dates with the most detail

他們之間有什麼不一樣呢?咱們用剛剛的例子作個展現:

  • short: 今天是18/12/3
  • medium: 今天是2018年12月3日
  • long: 今天是2018年12月3日
  • full: 今天是2018年12月3日星期一

Display Times

最後是時間。咱們循序漸進來。假若有這麼一段話:

{
    "time": "如今時間是{time,time,short}"
}

而後咱們這麼使用它的話:

<p>{intl.get('time',{'time':new Date()})}</p>

顯示結果是如今時間是下午5:54。其實{time,time,short}這段指令就是將time變量替換成對應日期(new Date())並以short形式展現。

可是時間展現形式只有三種,它沒有full

  • short: shows date as shortest as possible
  • medium: shows short textual representation of the month
  • long: shows long textual representation of the month

他們之間有什麼不一樣呢?咱們用剛剛的例子作個展現:

  • short: 如今時間是下午5:54
  • medium: 如今時間是下午5:58:22
  • long: 如今時間是GMT+8 下午5:58:50

上述貼出來的示例都是在中文環境下。若是有興趣的朋友能夠把整個例子download下來本地運行下,邊看邊寫,受益不淺。好了,收拾下班咯...

相關文章
相關標籤/搜索