
Reactサンプルアプリ開発 プラニングポーカー (16) react-i18nextによる多言語対応
今回は、react-i18next
というライブラリを使ってプラニングポーカーアプリを多言語化してみる。
こちらのブログを参考にさせて頂いた。

まずはパッケージのインストール。
$ yarn add i18next react-i18next
言語選択用のコンポーネント。
import React, { FunctionComponent } from "react";
export type LanguageSelectorProps = {
languages: { code: string, name: string }[],
selectedLanguage: string;
onLanguageSelectionChange: (lang: string) => void;
}
export const LanugageSelector: FunctionComponent<LanguageSelectorProps> = (props) => {
const langs = props.languages.map((lang) => {
return (lang.code === props.selectedLanguage) ?
(
<span className="i18n-selected">{lang.name}</span>
) :
(
<span onClick={() => props.onLanguageSelectionChange(lang.code)}>{lang.name}</span>
)
});
return (
<p className="i18n">
{langs}
</p>
);
}
- 使用できる言語の一覧、選択中の言語、選択言語変更時のコールバックをプロパティとして定義(3-7行目)
- 選択中の言語とそれ以外で設定するクラスやイベントが異なるため、三項演算子を利用してJSXの配列を生成(10-18行目)
次にルートのtsxに多言語の初期化を記述。
// i18n
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import jaJson from './i18n/ja/top.json';
import enJson from './i18n/en/top.json';
i18n.use(initReactI18next).init({
debug: true,
resources: {
en: { translation: enJson },
ja: { translation: jaJson },
},
lng: 'ja',
fallbackLng: 'ja',
keySeparator: false,
interpolation: { escapeValue: false },
});
- 多言語のリソースはTypeScript上に直接記述も可能だが、JSONファイルに外出しする方法を選んだ。そのJSONのインポートが4-5行目
- 読み込んだJSONを9-12行目で
resources
に指定 - リソースのキーに半角ピリオド
.
を使用したいため、15行目でkeySeparator: false
を指定している。
JSONファルの中身はこんな感じ。
{
"title": "Planning Poker",
"message.desc1": "Let's play planning poker with your team!",
"message.desc2": "Enter table name and nickname to start.",
"placeholder.tablename": "Table name",
"placeholder.nickname": "Your nickname",
"option.decktype.default": "--- Select deck type ---",
"option.decktype.int": "integer",
"option.decktype.half": "0.5 steps",
"option.decktype.fibo": "fibonacci",
"button.opentable": "Open a table"
}
多言語化の準備が整ったので、適用する。
言語選択コンポーネントと、react-i18next
からuseTranslation
関数をインポート。
import { LanugageSelector } from "./LanguageSelector";
import { useTranslation } from 'react-i18next'
言語切替の対応。
export const TopPage: FunctionComponent = () => {
const [t, i18n] = useTranslation();
const languages = [{ code: "ja", name: "Japanese" }, { code: "en", name: "English" }];
const [currentLang, setCurrentLang] = useState("ja");
// Change language of i18n
useEffect(() => {
i18n.changeLanguage(currentLang);
}, [currentLang, i18n])
useTranslation
をコールし、t
とi18n
という関数を変数に代入しておく(2行目)- 3行目は、選択可能な言語のリスト(日本語と英語)
- 4行目は、現在の言語を表すステート変数とその更新関数の取得
- 現在の言語(
currentLang
)が変更された場合は副作用を発生させ、i18n.changeLanguage
を呼び出して言語の切り替えを行う(6-8行目)
以下のコードは、言語選択コンポーネントの変更イベントをハンドリングするコールバック関数。
// Handler
const handleLanguageSelectionChange = (lang: string) => {
setCurrentLang(lang);
}
JSX。
return (
<>
<header className="header">
<h1 className="title">{t('title')}</h1>
<p className="description">
{t('message.desc1')}<br />
{t('message.desc2')}
</p>
<LanugageSelector languages={languages} selectedLanguage={currentLang} onLanguageSelectionChange={handleLanguageSelectionChange} />
</header>
- 9行目に、言語選択コンポーネントを配置
react-i18next
のt
関数を使って取得した翻訳済みテキストを表示するようにしている(4行目、6行目、7行目)
一箇所はまったのは、サブミットボタンのvalue
にt
関数の結果をセットしようとするとTypeScriptのエラーが出てしまう。
以下のようにtoString
で文字列化することで回避した。
<input type="submit" value={t('button.opentable').toString()} />
なお、メッセージに値を埋め込みたい場合は以下のようにする。
リソースの定義:
"message.desc1": "Join {{table}} and play planning poker!"
{{table}}
が引数。
コンポーネント側では、t
関数の第2引数に埋め込む引数を格納したオブジェクトを渡す(以下のコードの2行目)。
<p className="description">
{t('message.desc1', { table: props.tableName })}<br />
{t('message.desc2')}
</p>
ソースコードはGitHubにアップしている。