1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[React] Reactアプリの多言語対応 (i18n)_Docker, Storybookを使用

Last updated at Posted at 2025-07-11

概要

  • Reactコンポーネントに多言語対応(i18n)を導入する手順をまとめた
  • i18next, react-i18nextを活用し、Storybook上で翻訳内容を確認できる環境を構築した
  • Dockerを活用し、環境差異を抑えたローカル開発が可能な構成にした

実施条件

  • React + TypeScript プロジェクトがすでに構築されている
  • Storybook が導入済みで npm run storybook にて起動確認ができる
  • Docker / Docker Compose を使用した開発環境が用意されている

参考リンク
[Docker][React] DockerでReactプロジェクトを起動する
[Storybook][React][TypeScript] Storybookの導入手順
[Docker][Storybook] DockerでStorybookを起動する

環境

ツール バージョン 目的
Node.js 22.5.1 React や Storybook の実行環境
npm 10.8.2 パッケージ管理
React 19.1.0 UI コンポーネントの開発
TypeScript 5.x.x 型安全な開発を行う
Storybook 最新安定版(9.0) UI コンポーネントの確認とテスト
i18next 最新安定版(2025年時点) 多言語対応(翻訳処理の導入)
Docker 28.1.1 ローカル環境の構築と再現性の確保

手順

1. i18nライブラリをインストール

npm install i18next react-i18next

2. 翻訳ファイルを作成

以下のように翻訳ファイルを用意する
src/locales/en/translation.json:

translation.json
{
  "description_text": "This is a description"
}

src/locales/ja/translation.json:

translation.json
{
  "description_text": "これは説明文です"
}

3. i18n 初期化ファイルを作成

src/i18n.ts:

i18n.ts
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';

import enTranslation from './locales/en/translation.json';
import jaTranslation from './locales/ja/translation.json';

i18n.use(initReactI18next).init({
  resources: {
    en: { translation: enTranslation },
    ja: { translation: jaTranslation },
  },
  lng: 'ja', // 翻訳キーを解決する際の初期言語
  fallbackLng: 'en', // 翻訳キーが見つからない場合の代替言語
  interpolation: { escapeValue: false },
});

export default i18n;

4. エントリポイントで i18n を import

src/index.tsx or src/main.tsx or .storybook/preview.tsにて初期化ファイルを読み込む

src/index.tsx
import './i18n'; // i18nの初期化
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);
src/main.tsx
import './i18n'; // i18nの初期化
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(
    <App />,
    document.getElementById('root')
);
.storybook/preview.ts
// .storybook/preview.ts
import '../src/i18n';

5. コンポーネントで useTranslation を使用

Description.tsx を多言語対応にする

Description.tsx
import React from 'react';
+ import { useTranslation } from 'react-i18next';
import './description.css';

export type DescriptionProps = {
- text: string;
+ textKey: string;
  color?: string;
  align?: 'left' | 'center' | 'right';
  style?: React.CSSProperties;
};

export const Description: React.FC<DescriptionProps> = ({
- textKey,
+ textKey,
  color,
  align = 'left',
  style,
}) => {
+ const { t } = useTranslation();

  return (
    <p
      className="description"
      style={{ color, textAlign: align, ...style }}
      data-testid="description"
    >
-     {text}
+     {t(textKey)}
    </p>
  );
};

6. Storybookで動作確認

Description.stories.tsx:

Description.stories.tsx
import React from 'react';
import { Description } from './Description';

export default {
  title: 'Example/Description',
  component: Description,
};

export const Default = () => (
  <Description textKey="description_text" />
);

DockerでStorybook起動

起動コマンド:

docker-compose up --build

localhost:6006 にアクセスして、i18nが反映されているかを確認する

翻訳(変換)プロセス

1. storiesから翻訳キーが渡される

Description.stories.tsなどの親コンポーネントからPropsとして翻訳キーDescription.tsxへ渡す

Description.stories.ts
import React from 'react';
import { Description } from './Description';

export default {
  title: 'Example/Description',
  component: Description,
};

export const Default: Story = {
  args: {
    textKey: 'description_text' // ← 翻訳キー,
    color: '#666',
    align: 'left',
  },
};

2. Description.tsx内でt()関数が呼ばれる

Description.tsx内でt()関数が呼ばれる = useTranslationフックが呼ばれる

Description.tsx
import React from 'react';
import { useTranslation } from 'react-i18next'; // ←react-i18nextのフック
import './description.css';

export type DescriptionProps = {
  textKey: string;
  color?: string;
  align?: 'left' | 'center' | 'right';
  style?: React.CSSProperties;
};

export const Description: React.FC<DescriptionProps> = ({
  textKey,
  color,
  align = 'left',
  style,
}) => {
  const { t } = useTranslation(); // ←react-i18nextのフックを関数化

  return (
    <p
      className="description"
      style={{ color, textAlign: align, ...style }}
      data-testid="description"
    >
      {t(textKey)} //// ←t()(react-i18nextのフックの関数)を実行
    </p>
  );
};

3. i18n.tsが設定を提供

i18n.tsuseTranslationフックへ翻訳(変換)プロセスを提供

i18n.ts
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';

import enTranslation from './locales/en/translation.json';
import jaTranslation from './locales/ja/translation.json';

i18n.use(initReactI18next).init({
  resources: {
    en: { translation: enTranslation },
    ja: { translation: jaTranslation },
  },
  lng: 'ja', // 翻訳キーを解決する際の初期言語
  fallbackLng: 'en', // 翻訳キーが見つからない場合の代替言語
  interpolation: { escapeValue: false },
});

export default i18n;

4. t()関数がtranslation.jsonファイルを参照

i18n.tsの設定に基づき、t()react-i18nextフックの関数)がsrc/locales/ja/translation.jsonを参照

translation.json
{
  "description_text": "これは説明文です"
}

5. translation.jsonから変換後の文字列を出力

src/locales/ja/translation.jsonに基づき、description_textこれは説明文ですに変換される

おわりに

Reactでの多言語対応は、i18nextとStorybookを組み合わせることで、UIごとに簡単に確認できました。
特にチーム開発においては、Storybookによる翻訳確認が非常に有効だと思われます。

参考

1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?