8
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

(React + TypeScript)UseContextとi18nextを使ってi18n対応してみる

Last updated at Posted at 2020-02-04

下の記事に感化されて、i18n対応をやってみました。

日本語で開発しつつ、いつでも多言語対応できる環境を react-i18next で維持する

i18n対応の部分は上の記事を見ていただく(辞書ファイルの自動生成とかもありますが、この記事ではスキップしてるので是非読んでください)として、useContextを利用してグローバルstateに言語情報を持たせて、パッと切り替えられる様にしました。

トップコンポーネント

まずは大元のApp.tsxにstateを持たせます。

App.tsx
import React, { FC, useEffect, createContext, useReducer } from 'react';
import { appReducer, StateType, ActionType } from './reducer'
import { useTranslation, initReactI18next } from 'react-i18next';
import i18n from 'i18next';
import enJson from './locales/en.json';
import jaJson from './locales/ja.json';
import MainContents from './View/MainContents';

type ContextType = {
  state: StateType;
  dispatch: React.Dispatch<ActionType>;
};

// 初期ステートを作成
const initialState = { lang: 'ja' };

// コンテキストを作成する
export const AppContext = createContext({} as ContextType);

//i18nextの初期化
i18n.use(initReactI18next).init({
  debug: true,
  resources: {
    en: { translation: enJson },
    ja: { translation: jaJson },
  },
  lng: 'ja',
  fallbackLng: false,
  returnEmptyString: false, 
});

const App: FC = () => {
  const { i18n } = useTranslation();
  const [state, dispatch] = useReducer(appReducer, initialState);

  useEffect(() => {
    i18n.changeLanguage(state.lang);
  }, [i18n, state.lang]);

  return (
      <AppContext.Provider value={{ state, dispatch }}>
        <MainContents />
      </AppContext.Provider>
  );
};

Reducer

Reducerは下記の様な感じです。

reducer.ts
export const CHANGE_LANG = 'CHANGE_LANG' as const;

export type StateType = {
  lang: string;
};

export const changeLang = (lang: string) => ({
  type: CHANGE_LANG,
  payload: { lang },
});

export type ActionType = ReturnType<typeof changeLang>;

export const appReducer = (state: StateType, action: ActionType) => {
  switch (action.type) {
    case CHANGE_LANG:
      return { ...state, lang: action.payload.lang };
    default:
      return state;
  }
};

この辺のreducerの書き方はこちらの記事を参考にさせていただきました。

言語切り替えコンポーネント

SubSub.tsx
import React, { useContext } from 'react'
import { AppContext } from '../App';
import { CHANGE_LANG } from '../reducer';

export default const SubSub = () {
  const { state, dispatch } = useContext(AppContext);
  
  return (
    <button
      onClick={() =>
        dispatch({
          type: CHANGE_LANG,
          payload: { lang: state.lang == 'ja' ? 'en' : 'ja' },
        })
      }
    >
     言語切り替え
   </button>
  );
}

※SubSub.tsxはMain.tsxの孫くらいのコンポーネントだとしてください。Main.tsx配下の子孫コンポーネントであればなんでも良いです。

言語切り替えのところは3カ国以上なら適宜別のロジックでstateを変更してください。

言語が切り替わるコンポーネント

下記は実際に言語が切り替わるコンポーネントです。

AnotherSubSub.tsx
import React from 'react';
import { useTranslation } from 'react-i18next'

export default const AnotherSubSub = () => {
  const { t } = useTranslation();

  return (
    <p>
      {t('ここの言語が切り替わります')}
    </p>
  );
}

こちらもApp以下の子孫コンポーネントでしたら、どこでも構いません。
ここの言語が切り替わりますというのをキーとしてstateにセットしたlocaleに合わせて日本語、英語が切り替えます。

辞書設定

ja.json
{
  "ここの言語が切り替わります": "ここの言語が切り替わります"
}
en.json
{
  "ここの言語が切り替わります": "The language will change right here"
}

これで言語切り替え設定が完了しました。

まとめ

あとは言語の切り替えが発生するコンポーネントに上記の様な設定をすれば、言語切り替えボタンを押した時に全てのコンポーネントに言語切り替えが設定されます。

ただ、initReactI18nextを初期化する時に全ての辞書ファイルを読み込むので、辞書ファイルが非常に大きい場合はパフォーマンスが落ちるかもしれません(この辺は未検証)。

また、初期化時に常に日本語で表示しているため、そこは適宜初期の言語設定を加えてください。(アメリカからアクセスあった場合は初期設定で英語にする等)

(日本語をキーにするのは便利だなぁ)

8
5
0

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
8
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?