web-dev-qa-db-ja.com

React-intl多言語アプリ:言語と翻訳ストレージの変更

私はreact-routerアプリを持っていて、i18nを追加したいと思います。 react-intl IntlProviderでラップされたルートコンポーネント:

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

);

ただし、ロケールは1つだけです。他の言語を追加するためにアプリを更新する方法と翻訳を保存する最良の方法は?

12
qwe asd

私は同じ問題に直面しました、そしてこれは私が見つけたものです:

言語を変更するために、提供されたソリューション here を使用しました。これは基本的に、IntlProviderをConnect関数でReduxStoreにバインドしています。また、言語の変更時にコンポーネントを再レンダリングするためのキーを含めることを忘れないでください。これは基本的にすべてのコードです:

これはConnectedIntlProvider.jsで、デフォルトのIntlProviderをバインドするだけです(githubの元のコメントに欠落しているキープロップに注意してください)

_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 ));
_

次に行うことは、ロケールを管理するためのリデューサーを実装し、アクションクリエーターにオンデマンドで言語を変更させることです。

翻訳を保存する最良の方法については、このトピックについて多くの議論があり、状況はかなり混乱しているようです。正直なところ、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',
  }
}
_

他のファイルはまったく同じ構造ですが、翻訳された文字列が内部にあります。
次に、言語変更を担当するレデューサーで、これらのファイルからすべての状態をインポートし、言語を変更するアクションがディスパッチされるとすぐに、それらをreduxストアにロードします。前の手順で作成したコンポーネントは、変更をIntlProviderに伝達し、新しいロケールが実行されます。 _<FormattedMessage>_またはintl.formatMessage({id: 'app.header.title'})}を使用してページに出力します。詳細については、github wikiを参照してください。
そこにはいくつかのDefineMessages関数がありますが、正直なところ、その使用方法に関する適切な情報を見つけることができませんでした。基本的に、それを忘れて問題はありません。

18
ScienceSamovar

新しい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 でもあります。

9