基於react-intl實現手動國際化切換

carro發表於2018-07-14

知乎專欄:zhuanlan.zhihu.com/c_215040065,個人部落格:blog.caichengnan.com/

前言

國際化是一個很常見的需求,之前沒有這方面的相關經驗,所以決定練一下手。正好最近在寫一個react骨架(新專案可直接移植的骨架),上網查了一下,常用的解決方案是yahoo的react-intl庫,大致效果如下。

基於react-intl實現手動國際化切換

實現思路

  • 首先解決靜態國際化,即根據瀏覽器的語言,自動載入對應的語言模板。這裡只需判斷navigator.language型別即可,然後通過react-intl提供了IntlProvider元件,載入元件屬性的locale和messages,最後在需要用到國際化的元件裡,引入FormattedMessage元件(react-intl內建),通過id對映到對應的國際化檔案裡的屬性(例如下面的en_US.js的hello)。即可實現靜態國際化。

  • 動態國際化,即使用者可以通過按鈕切換,實現語言的切換。最容易想到的方案就是,在語言模板放在redux的store裡,提供一個切換語言的action,改變store裡的國家和語言模板,再觸發對應的FormattedMessage元件渲染。let's do it!

程式碼實現

  • 在src下新建locale檔案存放國際化語言的檔案,這裡我們新建了en_US.js和zh_CN.js。

en_US.js

const en_US = {
  hello: 'Hello, world!',
  name: 'my name is {name}'
}    
export default en_US;
複製程式碼

zh_CN.js

const zh_CN = {
  hello: '你好,世界!',
  name: '我的名字是 {name}'
}
export default zh_CN; 
複製程式碼

一個是常規的變數hello,一個是帶有變數{name}的欄位name

  • react-intl的IntlProvider元件類似redux的Provider元件,需要在全域性引入。所以我們封裝一下Intl.jsx元件,將redux和IntlProvider相結合。

Intl.jsx

import React, { Component } from 'react';
import { addLocaleData, IntlProvider } from 'react-intl';
import { connect } from 'react-redux';
import zh_CN from './locale/lang/zh_CN';
import en_US from './locale/lang/en_US.js';
import zh from 'react-intl/locale-data/zh';
import en from 'react-intl/locale-data/en';

addLocaleData([...zh,...en]);

class Inter extends Component {
  render() {
    let { locale, localeMessage, children } = this.props;
    return (
      <IntlProvider key={locale} locale={locale} messages={localeMessage}>
        {children}
      </IntlProvider>
    )
  }
};

function chooseLocale(val) {
  let _val = val || navigator.language.split('_')[0];
  switch (_val) {
    case 'en':
      return en_US;
    case 'zh':
      return zh_CN;
    default:
      return en_US;
  }
}

const mapStateToProps = (state, ownProps) => ({
  locale: state.root.language,
  localeMessage: chooseLocale(state.root.language)
});

let Intl = connect(mapStateToProps)(Inter);

export default Intl;
複製程式碼

解釋一下這個元件,元件是將redux裡的資料繫結到IntlProvider元件上,addLocaleData函式新增需要本地化的語言,這個需要宣告。redux中傳遞兩個props,locale代表當前語言,localeMessage代表locale裡的語言檔案內容。

這裡有一個很關鍵的地方,即key屬性。IntlProvider中的屬性變更並不會觸發FormattedMessage重新渲染,剛開始想要forceUpdate強制更新元件,後來上網查了一個解決方案,在元件中加入key,就能解決這個問題

  • 在實際使用語言的元件中引入FormattedMessage,當然react-intl還支援其他型別的轉換元件,比如時間型別FormattedDate等等。可從官網上查詢API。github

App.js

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import { FormattedMessage } from 'react-intl';
import actions from '../actions/index.js';
import { connect } from 'react-redux';

class App extends Component {
  changeLanguage() {
    let lang = this.props.locale;
    lang = lang === 'zh' ? 'en' : 'zh';
    this.props.changeLanguage(lang);
  }
  render() {
    const { locale } = this.props;
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h1 className="App-title">
            <FormattedMessage
              id="hello"
            />
          </h1>
        </header>
        <p className="App-intro">
          <FormattedMessage
            id="name"
            values={{ name: <b>{'carroll'}</b> }}
          />
        </p>
        <button onClick={() => this.changeLanguage()}>{locale === 'zh' ? '切換英文' : 'change chinese'}</button>

      </div>
    );
  }
}
const mapStateToProps = (state, ownProps) => ({
  locale: state.root.language,
});
const mapDispatchToProps = (dispatch, ownProps) => ({
  changeLanguage: (val) => dispatch(actions.changeLanguage(val))
});
export default connect(
  mapStateToProps,
  mapDispatchToProps
)(App);
複製程式碼

App.js主要實現了兩個功能,一個實現動態切換的action,一個FormattedMessageid與資料的繫結。

  • 最後在根檔案引入Intl.jsx即可
// ... 省略前面的引入
ReactDOM.render(
  <Provider store={store}>
    <Intl>
      <App />
    </Intl>
  </Provider>,
  document.getElementById('root'));
複製程式碼
  • 下面是github上的原始碼

github原始碼

總結

整體實現下來,動態的國際化切換也沒有多難,但是我們要有思考。把國際化的資料放在redux中是否有些浪費,可否不引入FormattedMessage也能解決文字的切換,在IntlProvider上繫結key是否會造成其他無關元件的重新渲染。這些都是我們需要考慮的問題。

如果錯誤請指出,如果對您有幫助,麻煩點個贊

相關文章