知乎专栏: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是否会形成其余无关组件的从新渲染。这些都是咱们须要考虑的问题。
若是错误请指出,若是对您有帮助,麻烦点个赞