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 – intlIntlProviderComponents similar to Redux’sProviderComponent that needs to be imported globally. So let’s encapsulate itIntl.jsxComponent, redux andIntlProviderCombined.

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 triggeredFormattedMessageRerender, 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 languageFormattedMessageOf course, react-Intl also supports conversion components of other types, such as time typesFormattedDateAnd 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 fileIntl.jsxCan 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