Skip to main content

React 国際化完全ガイド

ゼロから多言語対応へ。React アプリに i18n を設定し、AI で翻訳を自動化する方法を解説します。

1

パッケージをインストール

必要なパッケージは 3 つです。React バインディングの react-i18next、コアライブラリの i18next、そして必要に応じてロケールを自動検出する i18next-browser-languagedetector を使用します。

react-i18next は React のフックとコンポーネントを提供します。i18next は、翻訳の読み込み、補間、複数形処理を担うコアエンジンです。言語検出プラグインは、ブラウザの言語設定を自動的に読み取ります。
Terminal
npm install react-i18next i18next i18next-browser-languagedetector i18next-http-backend
2

i18n インスタンスを設定

デフォルト言語、翻訳リソース、プラグインチェーンを指定して i18next を初期化する i18n 設定ファイルを作成します。このファイルは、コンポーネントがレンダリングされる前に、アプリのエントリーポイントでインポートする必要があります。

src/i18n.ts
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import Backend from 'i18next-http-backend';

i18n
  .use(Backend)
  .use(LanguageDetector)
  .use(initReactI18next)  // Must come before .init()
  .init({
    fallbackLng: 'en',
    debug: process.env.NODE_ENV === 'development',
    interpolation: {
      escapeValue: false,  // React already escapes
    },
    backend: {
      loadPath: '/locales/{{lng}}/{{ns}}.json',
    },
  });

export default i18n;
"You will need to pass in an i18next instance by using initReactI18next" — このエラーは、i18n.init() より前に i18n.use(initReactI18next) を呼び出していないことを意味します。.use() は必ず .init() より先に呼び出してください。
3

アプリを I18nextProvider でラップ

アプリのルートで i18n 設定ファイルをインポートし、コンポーネントツリーを I18nextProvider でラップします。この設定がないと、useTranslation() は翻訳済みテキストではなく未処理のキーを返します。

src/main.tsx
import React, { Suspense } from 'react';
import ReactDOM from 'react-dom/client';
import { I18nextProvider } from 'react-i18next';
import i18n from './i18n';  // Import your config
import App from './App';

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <Suspense fallback={<div>Loading...</div>}>
      <I18nextProvider i18n={i18n}>
        <App />
      </I18nextProvider>
    </Suspense>
  </React.StrictMode>
);
翻訳に "Welcome to our app" ではなく "welcome" のような未処理のキーが表示される場合、最も一般的な原因は I18nextProvider がないこと、または i18n 設定ファイルがインポートされていないことです。
4

翻訳ファイルを作成

言語ごとに 1 つの JSON ファイルを作成します。機能またはページ別に文字列を整理するには、ネストされたキーを使用します。翻訳元の言語(通常は英語)を唯一の信頼できる情報源として管理してください。

public/locales/en/translation.json
// public/locales/en/translation.json
{
  "nav": {
    "home": "Home",
    "about": "About",
    "settings": "Settings"
  },
  "greeting": "Hello, {{name}}!",
  "cart": {
    "itemCount_one": "{{count}} item",
    "itemCount_other": "{{count}} items"
  }
}

// public/locales/de/translation.json
{
  "nav": {
    "home": "Startseite",
    "about": "Über uns",
    "settings": "Einstellungen"
  },
  "greeting": "Hallo, {{name}}!",
  "cart": {
    "itemCount_one": "{{count}} Artikel",
    "itemCount_other": "{{count}} Artikel"
  }
}
キーには表示位置ではなく、内容を表す名前を付けてください。"homepageCartLabel" よりも "cart.itemCount" が適切です。UI を再設計しても使い続けられるキーにします。
5

コンポーネントで翻訳を使用

任意のコンポーネントで useTranslation() を呼び出し、t() 関数を取得します。単純な文字列、変数を補間した文字列、Trans コンポーネントで JSX を埋め込んだ翻訳に使用できます。

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

function Greeting({ userName }: { userName: string }) {
  const { t } = useTranslation();

  return (
    <div>
      <h1>{t('greeting', { name: userName })}</h1>
      <nav>
        <a href="/">{t('nav.home')}</a>
        <a href="/about">{t('nav.about')}</a>
      </nav>
    </div>
  );
}
Trans component for JSX
import { Trans, useTranslation } from 'react-i18next';

// For JSX inside translations:
// "terms": "By signing up, you agree to our <link>Terms</link>."

function SignUp() {
  const { t } = useTranslation();
  return (
    <Trans i18nKey="terms" components={{
      link: <a href="/terms" className="underline" />
    }} />
  );
}
t(`error.$'{code}'`) のような動的キーは実行時には動作しますが、i18next-scanner などのツールでは静的に抽出できません。抽出ツールを使用する場合は、動的キーを明示的に列挙するか、コメントによるヒントを使用してください。
6

複数形と変数を処理

i18next は、単数形と複数形の 2 種類だけでなく、CLDR ルールに基づいて複数形を処理します。アラビア語には 6 形式(zero、one、two、few、many、other)、日本語には 1 形式(other)があります。翻訳ファイルに必要な形式をすべて定義すると、i18next が適切なものを自動的に選択します。

Plural forms by language
// English: 2 forms (one, other)
{
  "itemCount_one": "{{count}} item",
  "itemCount_other": "{{count}} items"
}

// Arabic: 6 forms (zero, one, two, few, many, other)
{
  "itemCount_zero": "لا عناصر",
  "itemCount_one": "عنصر واحد",
  "itemCount_two": "عنصران",
  "itemCount_few": "{{count}} عناصر",
  "itemCount_many": "{{count}} عنصرًا",
  "itemCount_other": "{{count}} عنصر"
}

// Japanese: 1 form (other)
{
  "itemCount_other": "{{count}}個のアイテム"
}
単数形の判定に count === 1 をハードコードしないでください。フランス語では 0 も単数形として扱います。ロシア語、アラビア語、ポーランド語には、英語にはない形式があります。複数形のルールは i18next に処理させてください。
7

言語の切り替えと検出を追加

i18n.changeLanguage() を呼び出す言語セレクターを作成します。ブラウザの言語検出機能と組み合わせ、初回アクセス時にユーザーの優先言語を自動検出し、その後は明示的に選択した言語を保存します。

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

const LANGUAGES = [
  { code: 'en', label: 'English' },
  { code: 'de', label: 'Deutsch' },
  { code: 'ja', label: '日本語' },
  { code: 'es', label: 'Español' },
];

function LanguageSwitcher() {
  const { i18n } = useTranslation();

  return (
    <select
      value={i18n.language}
      onChange={(e) => i18n.changeLanguage(e.target.value)}
    >
      {LANGUAGES.map(({ code, label }) => (
        <option key={code} value={code}>{label}</option>
      ))}
    </select>
  );
}
SSR(Next.js、Remix)を使用すると、サーバーとクライアントで異なる言語が検出される場合があります。サーバーにはブラウザの言語設定がないためです。その結果、ハイドレーションの不一致が発生します。対策として、検出したロケールをサーバーからクライアントへ prop または Cookie で渡し、両方で同じ言語をレンダリングしてください。
8

翻訳を自動化

i18n の設定が完了したら、AI を使用してロケールファイルを翻訳します。IDE で AI アシスタントに翻訳元ファイルの翻訳を依頼するか、CI/CD パイプラインで i18n Agent CLI を使用します。

Terminal
# In your IDE, ask your AI assistant:
> Translate public/locales/en/translation.json to German, Japanese, and Spanish

✓ de/translation.json created (1.2s)
✓ ja/translation.json created (1.5s)
✓ es/translation.json created (1.1s)

# Or use the CLI in CI/CD:
npx i18n-agent translate public/locales/en/translation.json --lang de,ja,es
翻訳は差分ごとに進めてください。翻訳元ファイルに新しいキーを追加したときは、すべてのファイルを再生成せず、その差分だけを翻訳します。これにより、人がレビューした翻訳を維持できます。

翻訳品質を自動管理

i18n-validate を使用し、キー不足や壊れたプレースホルダーをリリース前に検出します。実際の翻訳が完成する前に、i18n-pseudo の疑似翻訳で UI をテストできます。

よくある落とし穴

翻訳に未処理のキーが表示される

原因として、I18nextProvider がない、アプリのルートで i18n 設定がインポートされていない、名前空間が読み込まれていない、翻訳がまだ非同期で読み込まれている、などが考えられます。手掛かりを得るには、debug: true にしてブラウザコンソールを確認してください。

フォールバックのない Suspense エラー

"A component suspended while responding to synchronous input" — アプリを '&lt;Suspense&gt;' 境界で囲むか、i18next の init 設定で useSuspense: false を指定してください。

SSR のハイドレーション不一致

サーバーとクライアントが異なるロケールでレンダリングしています。両方が同じロケール情報源を使用するようにしてください。ブラウザの検出だけに依存せず、サーバーから prop として渡します。

翻訳キーが自動補完されない

リソース型で i18next モジュールを拡張します:declare module 'i18next' '{ interface CustomTypeOptions { resources: typeof resources } }'。これにより、型安全な t() 呼び出しを自動補完できます。

推奨ファイル構成

Project Structure
my-react-app/
├── public/
│   └── locales/
│       ├── en/
│       │   ├── translation.json    # Default namespace
│       │   ├── common.json         # Shared strings
│       │   └── dashboard.json      # Feature namespace
│       ├── de/
│       │   ├── translation.json
│       │   ├── common.json
│       │   └── dashboard.json
│       └── ja/
│           └── ...
├── src/
│   ├── i18n.ts                     # i18n configuration
│   ├── main.tsx                    # App entry with Provider
│   ├── App.tsx
│   └── components/
│       └── LanguageSwitcher.tsx
└── package.json

i18n Agent を今すぐ試す

翻訳ファイルをここにドロップ

JSON, YAML, PO, XML, CSV, Markdown, Properties

またはクリックしてファイルを選択

翻訳先言語

登録不要すぐに見積もり

よくある質問