下の記事に感化されて、i18n対応をやってみました。
日本語で開発しつつ、いつでも多言語対応できる環境を react-i18next で維持する
i18n対応の部分は上の記事を見ていただく(辞書ファイルの自動生成とかもありますが、この記事ではスキップしてるので是非読んでください)として、useContextを利用してグローバルstateに言語情報を持たせて、パッと切り替えられる様にしました。
トップコンポーネント
まずは大元のApp.tsxにstateを持たせます。
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は下記の様な感じです。
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の書き方はこちらの記事を参考にさせていただきました。
言語切り替えコンポーネント
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を変更してください。
言語が切り替わるコンポーネント
下記は実際に言語が切り替わるコンポーネントです。
import React from 'react';
import { useTranslation } from 'react-i18next'
export default const AnotherSubSub = () => {
const { t } = useTranslation();
return (
<p>
{t('ここの言語が切り替わります')}
</p>
);
}
こちらもApp以下の子孫コンポーネントでしたら、どこでも構いません。
ここの言語が切り替わります
というのをキーとしてstateにセットしたlocaleに合わせて日本語、英語が切り替えます。
辞書設定
{
"ここの言語が切り替わります": "ここの言語が切り替わります"
}
{
"ここの言語が切り替わります": "The language will change right here"
}
これで言語切り替え設定が完了しました。
まとめ
あとは言語の切り替えが発生するコンポーネントに上記の様な設定をすれば、言語切り替えボタンを押した時に全てのコンポーネントに言語切り替えが設定されます。
ただ、initReactI18nextを初期化する時に全ての辞書ファイルを読み込むので、辞書ファイルが非常に大きい場合はパフォーマンスが落ちるかもしれません(この辺は未検証)。
また、初期化時に常に日本語で表示しているため、そこは適宜初期の言語設定を加えてください。(アメリカからアクセスあった場合は初期設定で英語にする等)
(日本語をキーにするのは便利だなぁ)