React の多言語化対応を簡単に:react-i18next 導入
react-i18next は、多言語化のためのライブラリである i18next の React 用ライブラリで、React アプリケーションを簡単に多言語化対応できます。
この記事では、react-i18next の基本的な使い方を紹介します。
インストール
まず、必要なパッケージをインストールしましょう。
npm install react-i18next i18next i18next-http-backend i18next-browser-languagedetector
各パッケージの説明
- i18next : 多言語化のためのライブラリ
- react-i18next : i18next の React 向け公式拡張機能
- i18next-http-backend : 翻訳ファイルを非同期に読み込むためのライブラリ
- i18next-browser-languagedetector : ブラウザの言語を検出するためのライブラリ
セットアップ
まず、設定ファイルを作成します。
// src/i18n/config.ts
import i18n from "i18next";
import LanguageDetector from "i18next-browser-languagedetector";
import HttpApi from "i18next-http-backend";
import { initReactI18next } from "react-i18next";
// サポートする言語をオブジェクトで定義しています。
// ユーザーが言語を手動で切り替える場合に使用するためのものです。
export const supportedLngs = {
en: "English",
ja: "日本語",
};
i18n
.use(HttpApi) // 翻訳ファイルを非同期に読み込むため
.use(LanguageDetector) // ユーザーの言語設定を検知するため
.use(initReactI18next) // i18next インスタンスを初期化
.init({
fallbackLng: "ja", // フォールバック言語。指定された言語ファイルがない場合などにこの言語が使用される
returnEmptyString: false, // 空文字での定義を許可に
supportedLngs: Object.keys(supportedLngs),
debug: true, // true にすると開発コンソールに i18next が正しく初期化されたことを示す出力が表示される
// デフォルトは`escapeValue: true`
// 18next が翻訳メッセージ内のコードをエスケープし、XSS 攻撃から保護するためのもの
// React がこのエスケープを行ってくれるので、今回はこれをオフにする
interpolation: {
escapeValue: false,
},
});
export default i18n;
その他の設定は公式ドキュメントに記載されています。
翻訳ファイルの作成
設定ファイルで使用したi18next-http-backend
は、デフォルトでは Web サイトのルートを基準としたパブリック URL で翻訳ファイルを探し、非同期で翻訳ファイルを読み込んでくれます。
例:http://example.com/locales/ja/translation.json
i18next-http-backend
が期待する場所に翻訳ファイルを追加しましょう。
// public/locales/ja/translation.json
{
"welcome": "ようこそ、私たちのアプリに!",
"home": {
"title": "ホーム"
}
}
// public/locales/en/translation.json
{
"welcome": "Welcome to our app",
"home": {
"title": "Home"
}
}
ちなみに、翻訳ファイルのパスを変更することもできます。
方法は i18next-http-backend
のREADMEを参照してください。
設定ファイルをインポート
設定ファイルをアプリのエントリポイント(main.tsx)にインポートします。
// src/main.tsx
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.tsx";
+ import "./i18n/config.ts";
import "./index.css";
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.Suspense fallback={<div>Loading...</div>}>
<App />
</React.StrictMode>,
);
Suspense を使用して、翻訳ファイルのダウンロード中にLoading...
が表示されるようにしています。
ブラウザのコンソールに以下の画像のような出力が表示されていれば、i18n のセットアップが成功しています。この出力は、config ファイルでdebug: true
に設定しているため、出力されています。
実際に翻訳
翻訳ファイルを使用するには、useTranslation
フックを使用します。
翻訳ファイルのキーをt
関数に渡します。これでブラウザの言語に応じてテキストが切り替わるはずです。
// src/App.tsx
import { useTranslation } from "react-i18next";
function App() {
const { t } = useTranslation();
return (
<div className="...">
<h2>{t("welcome")}</h2>
</div>
);
}
export default App;
言語の自動検出
i18next-browser-languagedetector
を使用すると、ブラウザの言語設定を検出できます。
設定ファイルでuse(LanguageDetector)
を設定しているので、ユーザーのブラウザの言語設定を検出できています。
言語の手動で切り替え
ユーザーが言語を切り替えるための Select コンポーネントを作成します。
設定ファイルで定義した supportedLngs
をインポートして、対応言語の選択肢として使用します。
なお、UI には shadcn/ui を使用しています。
作成するパスはどこでも良いかと思います。
// src/i18n/LocaleSwitcher.tsx
import { useTranslation } from "react-i18next";
import { supportedLngs } from "./config";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
export default function LocaleSwitcher() {
const { i18n } = useTranslation();
return (
<Select
value={i18n.resolvedLanguage}
onValueChange={(value) => i18n.changeLanguage(value)}
>
<SelectTrigger className="w-40">
<SelectValue />
</SelectTrigger>
<SelectContent>
{Object.entries(supportedLngs).map(([code, name]) => (
<SelectItem value={code} key={code}>
{name}
</SelectItem>
))}
</SelectContent>
</Select>
);
}
-
i18n.resolvedLanguage
- 実際に使用されている言語を表します。ブラウザの言語検出や
i18n.changeLanguage()
で設定された言語を考慮し、利用可能な翻訳ファイルに基づいて解決された言語コードを返します。例えば、「en」が選択されても、その翻訳ファイルがない場合「ja」にフォールバックすることがあります
- 実際に使用されている言語を表します。ブラウザの言語検出や
-
i18n.changeLanguage()
- 言語を手動で変更するためのメソッドです。言語コード(例:'en'、'ja')を引数として受け取り、指定された言語に切り替えます。アプリケーション全体の翻訳が更新され、指定言語の翻訳ファイルがない場合はフォールバック言語が使用されます
作成したコンポーネントを追加すると、以下のように言語切り替えができるようになります。
翻訳で動的な値を扱う
実行時に動的に翻訳メッセージに挿入したい場合も多いと思います。その場合は以下のようにします。
// public/locales/ja/translation.json
{
"welcome": "ようこそ、私たちのアプリに!",
"home": {
"title": "ホーム"
},
+ "greeting": "こんにちは、{{name}}さん!"
}
// public/locales/en/translation.json
{
"welcome": "Welcome to our app",
"home": {
"title": "Home"
},
+ "greeting": "Hello, {{name}}!"
}
// src/App.tsx
import { useTranslation } from "react-i18next";
// ユーザーを取得する仮の関数
import { getLoggedInUser } from "...";
function App() {
const { t } = useTranslation();
const user = getLoggedInUser();
return (
<div className="...">
<h2>{t("welcome")}</h2>
<p>{t("greeting", { name: user.name })}</p>
</div>
);
}
export default App;
終わりに
導入自体は簡単にできました。翻訳ファイルの作成が少し面倒です。
本記事では、手っ取り早く多言語化したかったので最低限で進めています。翻訳ファイルの管理や作成、運用などはやり方がいろいろあるようなので、そちらも調べてみるといいと思います。
参考
Discussion