【问题标题】:React-intl multi language app: changing languages and translations storageReact-intl 多语言应用程序:更改语言和翻译存储
【发布时间】:2016-06-17 01:03:09
【问题描述】:

我有 react-router 应用程序并想添加 i18n。在 react-intl example 根组件中包裹在 IntlProvider 中:

ReactDOM.render(
<IntlProvider locale="en">
    <App />
</IntlProvider>,
document.getElementById('container')

);

但是只有一种语言环境。如何更新应用程序以添加其他语言以及存储翻译的最佳方式是什么?

【问题讨论】:

    标签: reactjs internationalization formatjs react-intl


    【解决方案1】:

    我遇到了同样的问题,这就是我发现的:

    为了更改语言,我使用了here 提供的解决方案,它基本上是通过 Connect 函数将 IntlProvider 绑定到 ReduxStore。另外不要忘记在语言更改时包含重新渲染组件的键。这基本上是所有代码:

    这是ConnectedIntlProvider.js,只是绑定了默认的IntlProvider(注意github原始评论中缺少的key prop)

    import { connect } from 'react-redux';
    import { IntlProvider } from 'react-intl';
    
    // This function will map the current redux state to the props for the component that it is "connected" to.
    // When the state of the redux store changes, this function will be called, if the props that come out of
    // this function are different, then the component that is wrapped is re-rendered.
    function mapStateToProps(state) {
      const { lang, messages } = state.locales;
      return { locale: lang, key: lang, messages };
    }
    
    export default connect(mapStateToProps)(IntlProvider);
    

    然后在你的入口点文件中:

    // index.js (your top-level file)
    
    import ConnectedIntlProvider from 'ConnectedIntlProvider';
    
    const store = applyMiddleware(thunkMiddleware)(createStore)(reducers);
    
    ReactDOM.render((
      <Provider store={store}>
        <ConnectedIntlProvider>
          <Router history={createHistory()}>{routes}</Router>
        </ConnectedIntlProvider>
      </Provider>
    ), document.getElementById( APP_DOM_CONTAINER ));
    

    接下来要做的就是实现 reducer 来管理语言环境,并让动作创建者按需更改语言。

    至于存储翻译的最佳方式 - 我发现很多关于这个主题的讨论和情况似乎很混乱,老实说,我很困惑 react-intl 的制造商如此关注日期和数字格式而忘记了翻译.所以,我不知道绝对正确的处理方法,但这就是我所做的:

    创建文件夹“locales”并在其中创建一堆文件,如“en.js”、“fi.js”、“ru.js”等。基本上所有您使用的语言。
    在每个文件中导出 json 对象,翻译如下:

    export const ENGLISH_STATE = {
      lang: 'en',
      messages: {
          'app.header.title': 'Awesome site',
          'app.header.subtitle': 'check it out',
          'app.header.about': 'About',
          'app.header.services': 'services',
          'app.header.shipping': 'Shipping & Payment',
      }
    }
    

    其他文件具有完全相同的结构,但内部包含已翻译的字符串。
    然后在负责语言更改的 reducer 中,从这些文件中导入所有状态,并在调度更改语言的操作后立即将它们加载到 redux 存储中。您在上一步中创建的组件会将更改传播到 IntlProvider 并且将发生新的语言环境。使用 &lt;FormattedMessage&gt;intl.formatMessage({id: 'app.header.title'})} 在页面上输出它,在他们的 github wiki 上阅读更多信息。
    他们那里有一些DefineMessages功能,但老实说我找不到任何好的信息如何使用它,基本上你可以忘记它并没问题。

    【讨论】:

    • 它与 redux 完美配合。我用阿波罗尝试了这种方法,但它不起作用。我很绝望。
    【解决方案2】:

    有了新的 Context API,我相信现在不需要使用 redux:

    IntlContext.jsx

    import React from "react";
    import { IntlProvider, addLocaleData } from "react-intl";
    import en from "react-intl/locale-data/en";
    import de from "react-intl/locale-data/de";
    
    const deTranslation = { 
      //... 
    };
    const enTranslation = { 
      //... 
    };
    
    addLocaleData([...en, ...de]);
    
    const Context = React.createContext();
    
    class IntlProviderWrapper extends React.Component {
      constructor(...args) {
        super(...args);
    
        this.switchToEnglish = () =>
          this.setState({ locale: "en", messages: enTranslation });
    
        this.switchToDeutsch = () =>
          this.setState({ locale: "de", messages: deTranslation });
    
        // pass everything in state to avoid creating object inside render method (like explained in the documentation)
        this.state = {
          locale: "en",
          messages: enTranslation,
          switchToEnglish: this.switchToEnglish,
          switchToDeutsch: this.switchToDeutsch
        };
      }
    
      render() {
        const { children } = this.props;
        const { locale, messages } = this.state;
        return (
          <Context.Provider value={this.state}>
            <IntlProvider
              key={locale}
              locale={locale}
              messages={messages}
              defaultLocale="en"
            >
              {children}
            </IntlProvider>
          </Context.Provider>
        );
      }
    }
    
    export { IntlProviderWrapper, Context as IntlContext };
    

    App.jsx组件:

    import { Provider } from "react-redux";
    import {  IntlProviderWrapper } from "./IntlContext";
    
    class App extends Component {
      render() {
        return (
          <Provider store={store}>
            <IntlProviderWrapper>
              ...
            </IntlProviderWrapper>
          </Provider>
        );
      }
    }
    

    LanguageSwitch.jsx

    import React from "react";
    import { IntlContext } from "./IntlContext";
    
    const LanguageSwitch = () => (
      <IntlContext.Consumer>
        {({ switchToEnglish, switchToDeutsch }) => (
          <React.Fragment>
            <button onClick={switchToEnglish}>
              English
            </button>
            <button onClick={switchToDeutsch}>
              Deutsch
            </button>
          </React.Fragment>
        )}
      </IntlContext.Consumer>
    );
    
    // with hooks:
    const LanguageSwitch2 = () => {
      const { switchToEnglish, switchToDeutsch } = React.useContext(IntlContext);
      return (
        <>
          <button onClick={switchToEnglish}>English</button>
          <button onClick={switchToDeutsch}>Deutsch</button>
        </>
      );
    };
    
    export default LanguageSwitch;
    

    我创建了一个 repository 来展示这个想法。 还有codesandbox example

    【讨论】:

    • 效果很好。我只是好奇是否可以用 useContext 来代替。所以你可以使用上下文(IntlContext)并将switchToEnglish等附加到上下文?
    • @Dac0d3r 当然,只需要导出整个上下文,检查编辑并链接到代码框。
    • 非常感谢。看起来很棒,正是我需要的!!这值得 imo。成为公认的答案。 :-)
    • Tomasz 我确实在这里创建了一个相关问题:stackoverflow.com/questions/55541089/… 所以我可以删除它,或者您可以简单地链接到您的答案,我会接受它 - 由您决定 :-)
    • @Dac0d3r 很高兴它有帮助!
    猜你喜欢
    • 2020-07-06
    • 1970-01-01
    • 2015-04-03
    • 1970-01-01
    • 2015-05-09
    • 2013-07-10
    • 2013-06-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多