# 機能設計書 51-翻訳システム

## 概要

本ドキュメントは、Payload CMSにおける翻訳システム（i18n: Internationalization）の機能設計を定義する。翻訳システムは、管理画面UIおよびAPIレスポンスの多言語対応を実現するためのコアパッケージである。

### 本機能の処理概要

翻訳システムは、Payload CMS全体における多言語対応を統括し、40以上の言語をサポートするローカライゼーション機能を提供する。

**業務上の目的・背景**：グローバルに展開するWebアプリケーションやCMSでは、多様な言語・地域のユーザーに対応する必要がある。翻訳システムは、管理画面のUI文言、バリデーションメッセージ、エラーメッセージ等を各ユーザーの言語設定に応じて動的に切り替えることで、国際的なチームでのコンテンツ管理を可能にする。また、日付・時刻フォーマットのローカライズ（date-fns連携）により、地域に適した表示形式を実現する。

**機能の利用シーン**：
- 管理画面ログイン時に、ユーザーのブラウザ言語設定またはアカウント設定に基づいてUIを自動翻訳
- ドキュメント編集時のバリデーションエラーメッセージの多言語表示
- 日付フィールドにおけるローカルフォーマットでの日付表示
- カスタムフィールドラベルの多言語定義

**主要な処理内容**：
1. i18nインスタンスの初期化（`initI18n`）：言語設定に基づく翻訳オブジェクトの構築
2. 翻訳文字列の取得（`t`関数）：キーに基づく翻訳テキストの取得と変数置換
3. 複数形対応：カウント値に応じた適切な翻訳形式（one/few/many/other）の選択
4. ラベル翻訳（`getTranslation`）：フィールドラベル等の動的翻訳
5. date-fnsロケールの動的インポート：言語に対応した日付フォーマットロケールの読み込み
6. コンテキスト別翻訳抽出：クライアント用/API用に必要な翻訳キーのみを抽出

**関連システム・外部連携**：
- date-fns: 日付フォーマットのローカライズに使用
- Admin UI（packages/ui）: 管理画面の翻訳テキスト表示
- Payload Core: バリデーションメッセージ、エラーメッセージの翻訳

**権限による制御**：翻訳システム自体には権限制御はない。ユーザーの言語設定はアカウント設定画面から変更可能で、変更にはアカウント更新権限が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 15 | バージョン詳細画面 | 補助機能 | ローカライズされたバージョン比較（多言語環境時） |
| 16 | アカウント設定画面 | 補助機能 | 言語設定の変更 |

## 機能種別

データ変換処理 / ローカライゼーション / ユーティリティ

## 入力仕様

### 入力パラメータ

#### initI18n関数

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| config | I18nOptions | Yes | i18n設定オブジェクト | - |
| config.fallbackLanguage | AcceptedLanguages | No | フォールバック言語 | acceptedLanguagesに含まれること |
| config.supportedLanguages | SupportedLanguages | No | サポートする言語マップ | - |
| config.translations | object | No | カスタム翻訳オブジェクト | - |
| context | 'api' \| 'client' | Yes | 翻訳コンテキスト | 'api'または'client' |
| language | AcceptedLanguages | No | 使用言語 | acceptedLanguagesに含まれること |

#### t関数

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| key | TTranslationKeys | Yes | 翻訳キー | 'namespace:key'形式 |
| vars | Record<string, any> | No | 置換変数オブジェクト | - |
| vars.count | number | No | 複数形判定用カウント値 | - |

#### getTranslation関数

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| label | LabelType | Yes | 翻訳対象ラベル | string/Record/Function/JSX.Element |
| i18n | I18n | Yes | i18nインスタンス | - |

### 入力データソース

- 言語ファイル: `packages/translations/src/languages/*.ts`
- ユーザー設定: アカウント設定画面からの言語選択
- HTTPヘッダー: `Accept-Language`ヘッダーからの言語抽出

## 出力仕様

### 出力データ

#### initI18n戻り値（I18nオブジェクト）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| dateFNS | Locale | date-fnsロケールオブジェクト |
| dateFNSKey | DateFNSKeys | date-fnsロケールキー |
| fallbackLanguage | string | フォールバック言語コード |
| language | string | 現在の言語コード |
| t | TFunction | 翻訳関数 |
| translations | object | マージ済み翻訳オブジェクト |

#### t関数戻り値

| 項目名 | 型 | 説明 |
|--------|-----|------|
| (return) | string | 翻訳済み文字列 |

### 出力先

- 管理画面UI: 翻訳済みテキストとして各コンポーネントで表示
- APIレスポンス: エラーメッセージ、バリデーションメッセージとして返却

## 処理フロー

### 処理シーケンス

```
1. i18n初期化（initI18n）
   └─ 言語設定とコンテキストに基づくi18nインスタンス生成
2. 言語ファイル読み込み
   └─ 指定言語の翻訳オブジェクトを取得
3. コンテキスト別フィルタリング
   └─ client/apiに応じて必要な翻訳キーのみを抽出
4. カスタム翻訳のマージ
   └─ config.translationsで指定されたカスタム翻訳を深くマージ
5. date-fnsロケール読み込み
   └─ 動的インポートで該当言語のロケールを取得
6. 翻訳関数（t）の生成
   └─ クロージャとしてtranslationsを保持する関数を返却
7. 翻訳実行（t関数呼び出し）
   └─ キーに基づく翻訳取得、変数置換、複数形対応
```

### フローチャート

```mermaid
flowchart TD
    A[initI18n呼び出し] --> B{言語がサポートされているか}
    B -->|No| C[Error: Language not supported]
    B -->|Yes| D[言語ファイルから翻訳取得]
    D --> E{コンテキストがclientか}
    E -->|Yes| F[クライアント用キーのみフィルタ]
    E -->|No| G[全翻訳を使用]
    F --> H[カスタム翻訳をマージ]
    G --> H
    H --> I[date-fnsロケールを動的インポート]
    I --> J[I18nオブジェクト生成]
    J --> K[t関数呼び出し]
    K --> L[キーで翻訳文字列を取得]
    L --> M{countが指定されているか}
    M -->|Yes| N[複数形サフィックス決定]
    M -->|No| O[基本キーを使用]
    N --> P[翻訳文字列取得]
    O --> P
    P --> Q{varsが指定されているか}
    Q -->|Yes| R[変数を置換]
    Q -->|No| S[翻訳結果を返却]
    R --> S
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-51-01 | 言語フォールバック | 指定言語の翻訳が存在しない場合、fallbackLanguageを使用 | 翻訳キーが見つからない場合 |
| BR-51-02 | 複数形選択 | count値に応じて_zero/_one/_two/_few/_many/_otherサフィックスを選択 | count変数が数値で指定された場合 |
| BR-51-03 | RTL言語対応 | ar/fa/heはRTL（右から左）言語として識別 | rtlLanguages配列で定義 |
| BR-51-04 | クライアント翻訳最適化 | クライアント側では必要最小限のキーのみを送信 | context='client'の場合 |
| BR-51-05 | キャッシュ | 同一言語・コンテキストの組み合わせはメモ化 | initI18n呼び出し時 |

### 計算ロジック

**複数形サフィックス決定ロジック**:
```typescript
if (count === 0 && `${key}_zero` in translations) return '_zero'
else if (count === 1 && `${key}_one` in translations) return '_one'
else if (count === 2 && `${key}_two` in translations) return '_two'
else if (count > 5 && `${key}_many` in translations) return '_many'
else if (count > 2 && count <= 5 && `${key}_few` in translations) return '_few'
else if (`${key}_other` in translations) return '_other'
```

**変数置換ロジック**:
- `{{variableName}}` 形式のプレースホルダを検索
- varsオブジェクトから対応する値を取得し置換
- 値が存在しない場合はプレースホルダをそのまま保持

## データベース操作仕様

翻訳システムはデータベースを直接操作しない。翻訳データは静的なTypeScriptファイルとして管理される。

### 操作別データベース影響一覧

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| - | - | - | データベース操作なし |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| LANG_NOT_SUPPORTED | 設定エラー | 指定言語がサポート言語リストにない | サポート言語を使用するか、fallbackLanguageを設定 |
| KEY_NOT_FOUND | 警告 | 翻訳キーが見つからない | console.logで警告、キー文字列をそのまま返却 |

### リトライ仕様

翻訳処理は同期的で失敗時のリトライは行わない。翻訳が見つからない場合はキー文字列がフォールバック値として使用される。

## トランザクション仕様

翻訳システムはステートレスな処理であり、トランザクション管理は不要。

## パフォーマンス要件

- initI18nの結果はメモ化され、同一の言語・コンテキストの組み合わせでは再計算されない
- date-fnsロケールは動的インポートにより、必要な言語のみがバンドルに含まれる
- クライアント向け翻訳はclientTranslationKeysで定義されたキーのみに絞り込まれ、バンドルサイズを最適化

## セキュリティ考慮事項

- 翻訳文字列内の変数置換は単純な文字列置換であり、XSS対策が必要な場合は呼び出し側で対応
- ユーザー入力を翻訳キーとして直接使用しないこと（存在しないキーはそのまま表示される）

## 備考

- サポート言語は`acceptedLanguages`配列で定義（40言語以上）
- 新しい翻訳キーの追加時は、英語ファイル（en.ts）に追加後、`pnpm run translateNewKeys`で他言語に自動翻訳可能
- Lexicalエディタ用の翻訳は別途`packages/richtext-lexical`で管理

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

まず、翻訳システムで使用される型定義を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | types.ts | `packages/translations/src/types.ts` | I18n、TFunction、Language等の主要型定義 |

**読解のコツ**:
- **52-55行目**: `Language`型は`dateFNSKey`と`translations`を持つ
- **134-137行目**: `TFunction`は翻訳キーとオプションを受け取り文字列を返す関数型
- **139-153行目**: `I18n`インターフェースがi18nインスタンスの全体構造を定義
- TypeScriptのConditional Typesを使用した`NestedKeysStripped`等の型ユーティリティに注目

#### Step 2: エントリーポイントを理解する

翻訳システムの初期化とエクスポートの流れを把握。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | index.ts | `packages/translations/src/exports/index.ts` | パッケージのパブリックAPIエクスポート |
| 2-2 | init.ts | `packages/translations/src/utilities/init.ts` | i18n初期化ロジック |

**主要処理フロー**:
1. **188-218行目（init.ts）**: `initI18n`関数がメモ化された非同期関数として定義
2. **189-191行目**: 言語サポートチェック
3. **194行目**: コンテキストに応じた翻訳抽出
4. **196-200行目**: t関数の初期化
5. **202-204行目**: date-fnsロケールの動的インポート

#### Step 3: 翻訳取得ロジックを理解する

翻訳文字列の取得と変数置換の処理を把握。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | init.ts | `packages/translations/src/utilities/init.ts` | getTranslationString、t関数、replaceVars関数 |

**主要処理フロー**:
- **21-73行目**: `getTranslationString`関数で翻訳キーを分解し、複数形対応を処理
- **36-66行目**: キーをコロンで分割し、ネストされた翻訳オブジェクトを走査
- **42-54行目**: count値に基づく複数形サフィックスの決定ロジック
- **82-104行目**: `replaceVars`関数で`{{variable}}`形式のプレースホルダを置換
- **114-144行目**: `t`関数がgetTranslationStringとreplaceVarsを組み合わせ

#### Step 4: コンテキスト別翻訳フィルタリングを理解する

クライアント用に必要な翻訳のみを抽出する処理を把握。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | getTranslationsByContext.ts | `packages/translations/src/utilities/getTranslationsByContext.ts` | コンテキスト別フィルタリング |
| 4-2 | clientKeys.ts | `packages/translations/src/clientKeys.ts` | クライアント用翻訳キー定義 |

**主要処理フロー**:
- **57-63行目（getTranslationsByContext.ts）**: contextが'client'の場合のみフィルタリング
- **5-41行目**: `filterKeys`関数で再帰的にキーをフィルタ
- **7-540行目（clientKeys.ts）**: クライアントで必要な翻訳キーの完全リスト

#### Step 5: 言語ファイルの構造を理解する

実際の翻訳データがどのように構造化されているかを把握。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | en.ts | `packages/translations/src/languages/en.ts` | 英語翻訳（基準言語） |
| 5-2 | ja.ts | `packages/translations/src/languages/ja.ts` | 日本語翻訳の例 |

**主要処理フロー**:
- **3-643行目（en.ts）**: `enTranslations`オブジェクトがネームスペース別に翻訳を定義
- **645-648行目**: `en`オブジェクトが`dateFNSKey`と`translations`をエクスポート
- 各ネームスペース（authentication、error、fields、general等）の構造に注目

#### Step 6: date-fnsロケール統合を理解する

日付フォーマットのローカライズ処理を把握。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 6-1 | importDateFNSLocale.ts | `packages/translations/src/importDateFNSLocale.ts` | date-fnsロケールの動的インポート |

**主要処理フロー**:
- **3-187行目**: switch文で言語コードに応じたdate-fnsロケールを動的インポート
- 動的インポートによりコード分割が実現され、必要な言語のみがロードされる

### プログラム呼び出し階層図

```
initI18n (init.ts)
    │
    ├─ getTranslationsByContext (getTranslationsByContext.ts)
    │      └─ filterKeys (再帰的キーフィルタリング)
    │             └─ clientTranslationKeys (clientKeys.ts)
    │
    ├─ initTFunction (init.ts)
    │      └─ deepMergeSimple (deepMergeSimple.ts)
    │             └─ translations マージ
    │
    └─ importDateFNSLocale (importDateFNSLocale.ts)
           └─ date-fns/locale/* (動的インポート)

t関数呼び出し
    │
    └─ getTranslationString (init.ts)
           └─ replaceVars (init.ts)

getTranslation (getTranslation.ts)
    │
    └─ i18n.t (翻訳関数)
```

### データフロー図

```
[入力]                    [処理]                         [出力]

言語設定 ─────────┐
                  │
コンテキスト ────┼──▶ initI18n ──▶ I18nオブジェクト
                  │        │
翻訳ファイル ────┘        │
                          ▼
                   getTranslationsByContext
                          │
                          ▼
                   initTFunction ──▶ t関数
                          │
                          ▼
                   importDateFNSLocale ──▶ dateFNSロケール

翻訳キー ─────────┐
                  │
変数オブジェクト ─┼──▶ t関数 ──▶ 翻訳済み文字列
                  │
翻訳オブジェクト ─┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| types.ts | `packages/translations/src/types.ts` | 型定義 | I18n関連の全型定義 |
| index.ts | `packages/translations/src/exports/index.ts` | エクスポート | パブリックAPI定義 |
| init.ts | `packages/translations/src/utilities/init.ts` | ソース | i18n初期化、t関数実装 |
| getTranslation.ts | `packages/translations/src/utilities/getTranslation.ts` | ソース | ラベル翻訳ヘルパー |
| getTranslationsByContext.ts | `packages/translations/src/utilities/getTranslationsByContext.ts` | ソース | コンテキスト別翻訳抽出 |
| languages.ts | `packages/translations/src/utilities/languages.ts` | ソース | 言語設定、ヘッダー解析 |
| deepMergeSimple.ts | `packages/translations/src/utilities/deepMergeSimple.ts` | ソース | オブジェクト深マージ |
| clientKeys.ts | `packages/translations/src/clientKeys.ts` | 設定 | クライアント用翻訳キー定義 |
| importDateFNSLocale.ts | `packages/translations/src/importDateFNSLocale.ts` | ソース | date-fnsロケール動的読み込み |
| en.ts | `packages/translations/src/languages/en.ts` | 翻訳データ | 英語翻訳（基準言語） |
| ja.ts | `packages/translations/src/languages/ja.ts` | 翻訳データ | 日本語翻訳 |
| package.json | `packages/translations/package.json` | 設定 | パッケージ定義、依存関係 |
