【问题标题】:React-Intl how to switch locale and messages from variableReact-Intl 如何从变量切换语言环境和消息
【发布时间】:2017-11-21 23:11:26
【问题描述】:

我正在尝试弄清楚如何使用 React-Intl 更改语言。这是我的第一个 React 应用程序,它是使用 create-react-app 制作的,我没有使用 Redux 或 Flux。

在我的 index.js 中,我有以下代码:

import React from 'react';
import ReactDOM from 'react-dom';
import TodoApp from './components/TodoApp';
import registerServiceWorker from './registerServiceWorker';
import './index.css';

// Bootstrap CSS libraries
import 'bootstrap/dist/css/bootstrap.css';
import 'bootstrap/dist/css/bootstrap-theme.css';

import { IntlProvider, addLocaleData } from 'react-intl';
import intlEN from 'react-intl/locale-data/en';
import intlES from 'react-intl/locale-data/es';
import intlMessagesES from './i18n/locales/es.json';
import intlMessagesEN from './i18n/locales/en.json';

addLocaleData([...intlEN, ...intlES]);

/* Define your translations */
let i18nConfig = {
    locale: 'es',
    messages: intlMessagesES
};

let changeLanguage = (lang) => {
    i18nConfig = { locale: lang, messages: intlMessagesEN };
    return i18nConfig;
}

ReactDOM.render(
    <IntlProvider locale={ i18nConfig.locale } key={ i18nConfig.locale } messages={ i18nConfig.messages }>
        <TodoApp onChangeLanguage={changeLanguage} />
    </IntlProvider>,
    document.getElementById('root'));
registerServiceWorker();

TodoApp 正在通过 props 发送一个关于“lang”参数的字符串(即:“es”或“en”),当我更改 i18nConfig 时,IntlProvider 似乎没有任何变化。我的想法是更改我的 i18nConfig 变量,然后我的所有应用程序也会更改语言。

我在 TodoApp 中有 FormattedMessages,我的两条 JSON 消息是这样填充的:

// i18n/locales/en.json
{
  "footer.add.placeholder": "Enter a name ...",
  "footer.add.priority0.text": "No priority",
  "footer.add.priority1.text": "Priority 1",
   ...
}

你知道我的代码缺少什么吗?也许我对 React-Intl 有一些不明白的地方。任何建议都会有所帮助,谢谢。

【问题讨论】:

标签: javascript reactjs ecmascript-6 create-react-app react-intl


【解决方案1】:

如果您从根目录中删除所有内容,它会起作用:

ReactDOM.render(<TodoApp />, document.getElementById('root'));

但是现在我们像这样改变 TodoApp 组件:

1) 我们添加 'locale' 作为组件状态并导入 React-Intl:

import { IntlProvider, addLocaleData } from 'react-intl';
import intlEN from 'react-intl/locale-data/en';
import intlES from 'react-intl/locale-data/es';
import intlMessagesES from './../i18n/locales/es.json';
import intlMessagesEN from './../i18n/locales/en.json';

addLocaleData([...intlEN, ...intlES]);

/* Define your default translations */
let i18nConfig = {
    locale: 'en',
    messages: intlMessagesEN
};

2) 更改我们的changeLanguage 函数(这次称为'onChangeLanguage'),该函数从子组件语言选择器接收'lang':

onChangeLanguage(lang) {
        switch (lang) {
            case 'ES': i18nConfig.messages = intlMessagesES; break;
            case 'EN': i18nConfig.messages = intlMessagesEN; break;
            default: i18nConfig.messages = intlMessagesEN; break;
        }
        this.setState({ locale: lang });
        i18nConfig.locale = lang;
}

最后渲染:

render() {
        return (
            <IntlProvider key={ i18nConfig.locale } locale={ i18nConfig.locale }  messages={ i18nConfig.messages }>
                <div>
                    <Header onChangeLanguage={this.onChangeLanguage} />
                    // Other components ...
                </div>
            </IntlProvider>
        );
    }

如果有人完全不明白,请在 cmets 中提问!感谢@TomásEhrich

【讨论】:

  • 就像一个魅力,但在渲染方法中的一个小修改:{this.onChangeLanguage.bind(this)}
  • 我知道这个问题很老,但我遇到了同样的问题。它几乎可以工作了,调用了 onChangeLanguage 方法,但翻译没有更新。
【解决方案2】:

你可以使用 redux 管理你的 locale 和 localeMessage。只需在 IntlProvider 中添加一个键。

import React, { Component } from 'react';
import { IntlProvider } from 'react-intl';

class Inter extends Component {
    render() {
      let { locale, localeMessage, children } = this.props;
      return (
          <IntlProvider key={locale} locale={locale} messages={localeMessage}>
              {children}
          </IntlProvider>
      )
    }
};

export default Inter;

【讨论】:

  • 谢谢,但不幸的是,这个问题背后的想法是不要使用 Redux。
【解决方案3】:

使用新的 React Context API 很容易做到。创建一个包装器:

import React from "react";
import Types from "prop-types";
import { IntlProvider, addLocaleData } from "react-intl";
import en from "react-intl/locale-data/en";
import de from "react-intl/locale-data/de";
import deTranslation from "../../lang/de";
import enTranslation from "../../lang/en";

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 };

然后使用那个 Provider 和 Consumer:

import { Provider } from "react-redux";
import {  IntlProviderWrapper } from "./IntlContext";

class App extends Component {
  render() {
    return (
      <Provider store={store}>
        <IntlProviderWrapper>
          ...
        </IntlProviderWrapper>
      </Provider>
    );
  }
}

应用中的某处:

import React from "react";
import { Text, Button } from "native-base";
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;

CodeSandbox 上的示例

这里是相关的repository,有一个更通用的解决方案。

注意:目前 react-intl 仍在使用旧的上下文 API,但在未来这样的解决方案可能会开箱即用。

【讨论】:

  • 嗨,@tomaszmularczyk 我尝试使用上面的代码和 repo,但是在添加 时我收到一条错误消息,提示“类型无效:--expected ... 但得到:未定义。”请问有什么可以帮忙的吗?
  • @jae.phoenix 你使用 react 版本 >= 16.3 吗?感觉你忘了从文件中导出它。
  • 嗨,@tomaszmularczyk,是的,16.4.2。我按原样使用了您的存储库,它是从那里导出的。
  • 在状态下传递更新程序函数switchToEnglish 一开始似乎很糟糕,阅读文档后它变得更加清晰。链接reactjs.org/docs/…
  • 但是如何在上下文本身上附加switchToEnglish,这样就可以在useEffect中通过useContext(intlContext).switchToEnglish()来访问呢?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-05-08
  • 1970-01-01
  • 1970-01-01
  • 2012-04-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多