知乎專欄:zhuanlan.zhihu.com/c_215040065,我的博客:blog.caichengnan.com/css
國際化是一個很常見的需求,以前沒有這方面的相關經驗,因此決定練一下手。正好最近在寫一個react骨架(新項目可直接移植的骨架),上網查了一下,經常使用的解決方案是yahoo的react-intl
庫,大體效果以下。react
首先解決靜態國際化,即根據瀏覽器的語言,自動加載對應的語言模板。這裏只需判斷navigator.language
類型便可,而後經過react-intl提供了IntlProvider
組件,加載組件屬性的locale和messages,最後在須要用到國際化的組件裏,引入FormattedMessage
組件(react-intl內置),經過id映射到對應的國際化文件裏的屬性(例以下面的en_US.js的hello)。便可實現靜態國際化。git
動態國際化,即用戶能夠經過按鈕切換,實現語言的切換。最容易想到的方案就是,在語言模板放在redux的store裏,提供一個切換語言的action,改變store裏的國家和語言模板,再觸發對應的FormattedMessage
組件渲染。let's do it!github
en_US.jsredux
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
。ide
IntlProvider
組件相似redux的Provider
組件,須要在全局引入。因此咱們封裝一下Intl.jsx
組件,將redux和IntlProvider
相結合。Intl.jsxsvg
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,就能解決這個問題ui
FormattedMessage
,固然react-intl還支持其餘類型的轉換組件,好比時間類型FormattedDate
等等。可從官網上查詢API。githubApp.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,一個FormattedMessage
id與數據的綁定。
Intl.jsx
便可// ... 省略前面的引入
ReactDOM.render(
<Provider store={store}> <Intl> <App /> </Intl> </Provider>,
document.getElementById('root'));
複製代碼
總體實現下來,動態的國際化切換也沒有多難,可是咱們要有思考。把國際化的數據放在redux中是否有些浪費,能否不引入FormattedMessage
也能解決文字的切換,在IntlProvider
上綁定key是否會形成其餘無關組件的從新渲染。這些都是咱們須要考慮的問題。
若是錯誤請指出,若是對您有幫助,麻煩點個贊