Zhihu column: zhuanlan.zhihu.com/c_215040065, personal blog: blog.caichengnan.com/
preface
Internationalization is a very common requirement and I have no previous experience in this area, so I decided to practice. Yahoo’s React-Intl library is the most common solution to this problem.
Implementation approach
-
The first solution is static internationalization, which means automatically loading the corresponding language template based on the browser language. The IntlProvider component is used to load locale and messages of component properties via react-Intl. Introduce the FormattedMessage component (built-in in React-Intl) that maps by ID to properties in the corresponding internationalization file (such as the hello of en_us.js below). Static internationalization can be achieved.
-
Dynamic internationalization, that is, users can switch between languages by pressing buttons. The most obvious solution would be to provide an action to switch languages in redux’s store, change the country and language templates in the store, and trigger the corresponding FormattedMessage component to render. Let ‘s do it!
Code implementation
- Create a locale file under SRC to store internationalized language files. Here we create en_us.js and zh_cn.js.
en_US.js
const en_US = {
hello: 'Hello, world! '.name: 'my name is {name}'
}
export default en_US;
Copy the code
zh_CN.js
const zh_CN = {
hello: 'Hello world! '.name: 'My name is {name}'
}
export default zh_CN;
Copy the code
One is the regular variable Hello, and the other is the field name with the variable {name}.
- The react – intl
IntlProvider
Components similar to Redux’sProvider
Component that needs to be imported globally. So let’s encapsulate itIntl.jsx
Component, redux andIntlProvider
Combined.
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:
returnen_US; }}const mapStateToProps = (state, ownProps) = > ({
locale: state.root.language,
localeMessage: chooseLocale(state.root.language)
});
let Intl = connect(mapStateToProps)(Inter);
export default Intl;
Copy the code
The component binds the data in redux to the IntlProvider component. The addLocaleData function adds the language to be localized, which needs to be declared. In redux, two props are passed, locale representing the current language and localeMessage representing the content of the language file in locale.
There’s one key thing here, the key property. Property changes in IntlProvider are not triggeredFormattedMessage
Rerender, initially wanted to forceUpdate the component, then went online to find a solution to this problem by adding a key to the component
- Introduced in components that actually use the language
FormattedMessage
Of course, react-Intl also supports conversion components of other types, such as time typesFormattedDate
And so on. You can check the API on the official website.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' ? </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);Copy the code
App.js mainly implements two functions, one is to implement the dynamic switching action, one is to bind the FormattedMessageid with the data.
- Finally introduced in the root file
Intl.jsx
Can be
/ /... Omit the previous introduction
ReactDOM.render(
<Provider store={store}>
<Intl>
<App />
</Intl>
</Provider>.document.getElementById('root'));
Copy the code
- Here is the source code on Github
Making the source code
conclusion
The whole implementation, dynamic internationalization switch is not too difficult, but we have to think about it. Is it wasteful to put internationalized data in redux, can text switching be handled without importing FormattedMessage, and does binding keys to IntlProvider cause rerendering of other unrelated components? These are the questions we need to consider.
Please point out any errors and give a thumbs up if they help you