react-intl 實現 React 國際化多語言

效果預覽

React Intl 國際化步驟

  1. 建立國際化資源文件
  2. 根據語言獲取國際化資源
  3. 引入 react-intl 的 local data
  4. 建立 LocaleProvider 國際化上下文組件
  5. 建立 react-intl 國際化上下文組件
  6. 使用 react-intl's components & apis,進行國際化開發

1. 建立國際化資源文件

目前咱們管理資源文件的方式是在 src/locales 文件夾下:javascript

.
├── en-US.js
├── en-US.messages.js
├── zh-Hans.js
└── zh-Hans.messages.js複製代碼

*.messages.js 是咱們的資源文件(這裏咱們採用了 js 格式,你也能夠使用 json 等等),返回的是一個對象,key 爲咱們翻譯用的 id,value 爲具體語言的翻譯,內容是:html

export default {
  'page.localeProvider.react': '{ name }, a JavaScript library for building user interfaces.',
  'page.localeProvider.react.html': '<p>{ name } makes it painless to create interactive UIs. Design simple views for each state in your application, and { name } will efficiently update and render just the right components when your data changes.</p><p>Declarative views make your code more predictable and easier to debug.</p>',
  'page.localeProvider.unreadCount': 'You have {unreadCount} new {notifications}',
  'page.localeProvider.title.date': 'Current date: ',
  'page.localeProvider.title.time': 'Current time: ',
  'page.localeProvider.title.relative': 'Relative current time: ',
  'page.localeProvider.title.number': 'Comma number: ',
  'page.localeProvider.title.price': 'Price: ',
  'page.localeProvider.title.percent': 'Percent: ',
};複製代碼

en-US.js 文件封裝了 messages、locale 等國際化上下文組件須要的內容:java

import appLocaleData from 'react-intl/locale-data/en';
// 引入組件多語言
import paginationLocale from '@/components/pagination/locales/en-US';
import messages from './en-US.messages';

window.appLocale = {
  // 合併全部 messages, 加入組件的 messages
  messages: Object.assign({}, messages, {
    Pagination: paginationLocale,
  }),

  // 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',
      },
    },
    // 貨幣
    money: {
      currency: 'USD',
    },
  },
};

export default window.appLocale;複製代碼

有了這些資源文件以及相關的封裝以後,咱們就能夠在 LocaleProviderInltProvider 中使用了。react

2. 根據語言加載國際化資源

上一步咱們建立了不一樣語言版本的國際化資源文件,咱們還須要一個函數可以根據語言,加載對應的資源文件:git

/** * 獲取國際化資源文件 * * @param {any} lang * @returns */
function getLocale(lang) {
  let result = {};
  switch (lang) {
    case 'zh-CN':
      result = require('./locales/zh-Hans');
      break;
    case 'en-US':
      result = require('./locales/en-US');
      break;
    default:
      result = require('./locales/zh-Hans');
  }

  return result.default || result;
}複製代碼

3. 引入 react-intl 的 local data

import { addLocaleData } from 'react-intl';
...

render() {
  const appLocale = getLocale('en-US');
  addLocaleData(...appLocale.data);
  ...
}複製代碼

react-intl 在作國際化的時候須要一些特有的 local data,主要是進行相對時間翻譯時,好比昨天、今天、明天、幾分鐘前、幾個月前之類的。
咱們經過 addLocaleData 這個方法加載相關內容,你們能夠根據實際狀況加載須要的 locale-data。github

4. 建立 LocaleProvider 國際化上下文組件

爲了組件可以國際化資源信息,咱們須要一個 LocaleProvider 組件,用它來提供國際化的上下文,具體用法:json

export default class LocaleProvider extends React.Component {
  static propTypes = {
    children: PropTypes.any,
    locale: PropTypes.object,
  };

  static childContextTypes = {
    // 語言信息
    locale: PropTypes.object,
  };

  getChildContext() {
    return {
      locale: {
        ...this.props.locale,
      },
    };
  }

  render() {
    return React.Children.only(this.props.children);
  }
}複製代碼

5. 建立 react-intl 國際化上下文組件

爲了可以使用 react-intl 進行國際化,跟 redux 這些框架同樣,咱們須要一個 Provider Component,用它來提供國際化的上下文,具體用法:redux

...
import { addLocaleData, IntlProvider } from 'react-intl';
import LocaleProvider from '@/components/locale-provider';
import Home from '@/views/home';
...

render() {
  // 根據語言獲取國際化資源
  const appLocale = getLocale('en-US');
  addLocaleData(...appLocale.data);

  return (
    <LocaleProvider locale={appLocale}> <IntlProvider locale={appLocale.locale} messages={appLocale.messages} formats={appLocale.formats} > <Home /> </IntlProvider> </LocaleProvider>
  );
}複製代碼
LocaleProvider 有三個配置參數:
  locale, <object>, 國際化資源.

IntlProvider 有三個配置參數:
  locale, <string>, 語言標記,例如 'zh-CN' 'en-US'
  messages, <object>, 國際化所需的 key-value 對象
  formats, <object>, 自定義 format,好比日期格式、貨幣等複製代碼

在定義好 IntlProvider 以後,咱們就能夠在頁面使用它提供的 api 或者組件來進行國際化了。segmentfault

6. 使用 react-intl's components & apis

react-intl 提供了豐富的組件和 api 來完成頁面部分的國際化。api

字符串的格式化

a. <FormattedMessage /> 這個組件用於格式化字符串,是全部的組件中使用頻率最高的組件。除了能夠根據配置輸出不一樣語言的簡單字符串以外,還能夠輸出包含動態變化的參數的複雜字符串,具體的用法在後面的例子中會慢慢敘述。

好比咱們在 *.message.js 配置文件中寫了以下內容:

export default {
  'page.localeProvider.react': '{ name }, a JavaScript library for building user interfaces.',
};複製代碼

使用這個組件的時候,咱們這麼寫:

<FormattedMessage
  tagName="p"
  id="page.localeProvider.react"
  values={{
    name: 'React',
  }}
  defaultMessage="{name} 是一個用於構建用戶界面的 JAVASCRIPT 庫。"
  description="{name} 是什麼?"
/>複製代碼
  • id 指代的是這個字符串在配置文件中的屬性名
  • description 指的是對於這個位置替代的字符串的描述,便於維護代碼,不寫的話也不會影響輸出的結果
  • defaultMessage 當在locale配置文件中沒有找到這個id的時候,輸出的默認值
  • tagName 實際生成的標籤,默認是 span
  • values 動態參數. 格式爲對象

輸出的結果:

<p>React, a JavaScript library for building user interfaces.</p>複製代碼

b. <FormattedHTMLMessage /> 這個組件的用法和徹底相同,惟一的不一樣就是輸出的字符串能夠包含HTML標籤。

日期時間

a. <FormattedDate /> 用於格式化日期,可以將一個時間戳格式化成不一樣語言中的日期格式。

傳入時間戳做爲參數:

<FormattedDate 
  value={new Date(1459832991883)}
/>複製代碼

輸出結果:

<!-- 英文 -->
<span>4/5/2016</span>

<!-- 中文 -->
<span>2016/5/4</span>複製代碼

b. <FormattedTime> 用於格式化時間,效果與<FormattedDate />類似。

傳入時間戳做爲參數:

<FormattedTime 
  value={new Date(1459832991883)}
/>複製代碼

輸出結果:

<!-- 英文 -->
<span>1:09 AM</span>

<!-- 中文 -->
<span>上午1:09</span>複製代碼

c. <FormattedRelative /> 經過這個組件能夠顯示傳入組件的某個時間戳和當前時間的關係,好比「10 minutes ago」。

傳入時間戳做爲參數:

<FormattedRelative 
  value={Date.now()}
/>複製代碼

輸出結果:

<!-- 英文 =>> 運行時的輸出結果: -->
<span>now</span>

<!-- 英文 =>> 10秒以後的輸出結果: -->
<span>10 seconds ago</span>

<!-- 英文 =>> 1分鐘以後的輸出結果: -->
<span>1 minute ago</span>

<!-- 中文 =>> 運行時的輸出結果: -->
<span>如今</span>

<!-- 中文 =>> 10秒以後的輸出結果: -->
<span>10秒前</span>

<!-- 中文 =>> 1分鐘以後的輸出結果: -->
<span>1分鐘前</span>複製代碼

數字量詞

a. <FormattedPlural /> 這個組件可用於格式化量詞,在中文的語境中,其實不太會用獲得,好比咱們說一個雞腿,那麼量詞就是‘個’,咱們說兩個雞腿,量詞仍是‘個’,不會發生變化。可是在英文的語言環境中,描述一個蘋果的時候,量詞是apple,當蘋果數量爲兩個時,就會變成apples,這個組件的做用就在於此。

傳入組件的參數中,value爲數量,其餘的爲不一樣數量時對應的量詞,在下面的例子中,一個的時候量詞爲message,兩個的時候量詞爲messages。實際上能夠傳入組件的量詞包括 zero, one, two, few, many, other 已經涵蓋了全部的狀況。

結合 <FormattedMessage />運用:

const unreadCount = 10;
const unreadCount2 = 1;
... 

<p>
  <FormattedMessage
    id="page.localeProvider.unreadCount"
    defaultMessage={'你有{ unreadCount }條新信息'}
    values={{
      unreadCount: (
        <strong
          style={{
            color: '#f30',
            fontWeight: 'normal',
          }}
        >
          <FormattedNumber
            value={unreadCount}
          />
        </strong>
      ),
      notifications: (
        <FormattedPlural
          value={unreadCount}
          one="notification"
          other="notifications"
        />
      ),
    }}
  />
</p>
<p>
  <FormattedMessage
    id="page.localeProvider.unreadCount"
    defaultMessage={'你有{ unreadCount2 }條新信息'}
    values={{
      unreadCount: (
        <strong
          style={{
            color: '#f30',
            fontWeight: 'normal',
          }}
        >
          <FormattedNumber
            value={unreadCount2}
          />
        </strong>
      ),
      notifications: (
        <FormattedPlural
          value={unreadCount2}
          one="notification"
          other="notifications"
        />
      ),
    }}
  />
</p>複製代碼

輸出結果:

<!-- 英文 -->
<p>You have 10 new notifications</p>
<p>You have 1 notification</p>

<!-- 中文 -->
<p>你有10條新信息</p>
<p>你有1條新信息</p>複製代碼

b. <FormattedNumber /> 這個組件最主要的用途是用來給一串數字標逗號,好比10000這個數字,在中文的語言環境中應該是1,0000,是每隔4位加一個逗號,而在英語的環境中是10,000,每隔3位加一個逗號。

傳入數字做爲參數:

<FormattedNumber 
  value={1000}
/>複製代碼

輸出結果:

<span>1,000</span>複製代碼

<FormattedNumber /> 輸出百分比

傳入小數做爲參數:

<FormattedNumber
  value={0.5}
  style="percent"
/>複製代碼

輸出結果:

<span>50%</span>複製代碼

<FormattedNumber /> 輸出貨幣

傳入數字做爲參數:

// locale.formats.money.currency 是 /locales/*.js 國際化資源配置的貨幣信息。中文: 'CNY'; 英文: 'USD'

<FormattedNumber
  value={123456.78}
  style="currency"
  currency={locale.formats.money.currency}
/>複製代碼

輸出結果:

<!-- 英文 -->
<span>$123,456.78</span>

<!-- 中文 -->
<span>¥123,456.78</span>複製代碼

注:項目在中文狀況下也是每隔3位加一個逗號,具體緣由詳,若是有知道緣由的請告知。

組件國際化

1. 建立獲取上下文國際化資源函數

/** * 獲取 組件的語言配置 * * @param {any} props 屬性 * @param {any} context 上下文 * @param {any} componentName 組件名. 對應 context.locale.messages 中的 key 值 * @param {any} getDefaultLocale */
function getComponentLocale(props, context, componentName, getDefaultLocale) {
  let locale = {};

  // 若是 context 上下文中有多語言配置. 則取 context 上下文中的多語言值.
  // 不然, 取默認值的多語言值.
  if (context && context.locale && context.locale.messages[componentName]) {
    locale = context.locale.messages[componentName];
  } else {
    const defaultLocale = getDefaultLocale();
    locale = defaultLocale.default || defaultLocale;
  }

  let result = {
    ...locale,
  };

  // 若是屬性有語言配置項, 則合併.
  if (props.locale) {
    result = {
      ...result,
      ...props.locale,
    };

    if (props.locale.lang) {
      result.lang = {
        ...locale.lang,
        ...props.locale.lang,
      };
    }
  }

  return result;
}複製代碼

2. 建立國際化的組件

...
import { getComponentLocale } from '../_utils/getLocale';
...

export default class Pagination extends React.Component {
  // context 上下文
  static contextTypes = {
    locale: PropTypes.object,
  };

  render() {
    const currentlocale = getComponentLocale(this.props, this.context, 'Pagination', () => {
      require('./locales/zh-CN');
    });

    return (
      <div className="pagination"> <div className="pagination__wrapper"> <div className="pagination__button__prev"> <a>{currentlocale.prevText}</a> </div> <div className="pagination__button__next"> <a>{currentlocale.nextText}</a> </div> </div> </div>
    );
  }
}複製代碼

國際化規範附錄

React Intl 編寫規範

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

源碼

整個項目源碼

資料

相關文章
相關標籤/搜索