LoginSignup
28
21

More than 5 years have passed since last update.

reactの言語切替の単純な実装(国際化・他言語化)

Posted at

実装したいもの

ボタン押したら英語と日本語に切り替わるやつ

Reactの国際化/多言語化について少し探すとreact-intl, react-i18nextの2つが挙がり、結論としては

以下は書きながら感じたことを雑多に記す。

react-intl

言語切替のサンプルコードを見てみると、urlの?localeを見て生成するコードを変更するSPAに適さない手法が使われており、参考にならなかった。
代わりに、issues内の投稿で参考になるコードがあったので、そちらを真似した。

codesandbox.io/s/xp36r03qn4
import React from "react";
import { render } from "react-dom";
import { createStore } from "redux";
import { connect, Provider } from "react-redux";
import { addLocaleData, IntlProvider, FormattedMessage } from "react-intl";

///...

const initialState = {
  intl: locales.ja
};
const store = createStore((state, action) => {
  if (action.error) throw action.error;
  switch (action.type) {
    case "CHANGE_LOCALE":
      return Object.assign({}, state, { intl: locales[action.payload] });
    default:
      return state;
  }
}, initialState);

const ConnectedIntlProvider = connect(state => state.intl)(IntlProvider);

const App = () => (
  <Provider store={store}>
    <ConnectedIntlProvider>
      <div>
        <h1>
          <FormattedMessage id="greet" />
        </h1>
        {["en", "ja"].map(locale => (
          <button
            key={locale}
            onClick={() => {
              store.dispatch({ type: "CHANGE_LOCALE", payload: locale });
            }}
          >
            {locale}
          </button>
        ))}
      </div>
    </ConnectedIntlProvider>
  </Provider>
);

render(<App />, document.querySelector("#root"));

<IntlProvider>は言語切替機能を提供せず、単一の翻訳情報をpropsに求める。上記コードは現在の言語をreduxに持たせ、react-redux.connectで反映する形にした。

ただ、react-intlの初期化の手順が複雑で、以下のようなコードで動作した。

codesandbox.io/s/xp36r03qn4
///...
const locales = {
  en: {
    locale: "en",
    messages: {
      greet: "hello world"
    },
    // 数値整形関数。addLocaleData時に定義しないとconsoleで警告が出る
    pluralRuleFunction: a => a
  },
  ja: {
    locale: "ja",
    messages: {
      greet: "ハローワールド"
    },
    pluralRuleFunction: a => a
  }
};
addLocaleData(locales.en);
addLocaleData(locales.ja);
///...

addLocaleData時にpluralRuleFunctionが必須らしい。ドキュメントで確認できず、console上のエラーの意味を理解するのに時間が掛かった。

react-i18next

初期化が楽。i18nextインスタンス(以下i18nと呼ぶ)に、デフォルト言語と多言語情報を設定する。

codesandbox.io/s/zkn071oxql
// ...
const locales = {
  en: {
    greet: "hello world"
  },
  ja: {
    greet: "ハローワールド"
  }
};

const i18n = i18next.init({ fallbackLng: "en", fallbackNS: "translation" });
i18n.addResourceBundle("en", "translation", locales.en);
i18n.addResourceBundle("ja", "translation", locales.ja);
// ...

react-i18nextのTranslate HOCで梱包したMainpropsに翻訳関数である.tを持つ。

codesandbox.io/s/zkn071oxql
import React from "react";
import { render } from "react-dom";
import i18next from "i18next";
import { I18nextProvider, translate } from "react-i18next";

//...

const Main = translate()(props => (
  <div>
    <h1>{props.t("greet")}</h1>
    {["en", "ja"].map(locale => (
      <button
        key={locale}
        onClick={() => {
          i18n.changeLanguage(locale);
        }}
      >
        {locale}
      </button>
    ))}
  </div>
));

const App = () => (
  <I18nextProvider i18n={i18n}>
    <Main />
  </I18nextProvider>
);

render(<App />, document.querySelector("#root"));

i18n.changeLanguage('ja')I18nextProvider内のtranslateしたMainに即時反映されるため、下記のように言語がすぐに切り替わる。

結論

単純なkey-value置換と即時反映ならreact-i18nextで良い。

28
21
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
28
21