# 機能設計書 85-翻訳CRUD

## 概要

本ドキュメントは、Medusaコマースプラットフォームにおける翻訳CRUD機能の設計について記述する。翻訳モジュールの中核機能として、多言語翻訳データの作成・取得・更新・削除を行う。

### 本機能の処理概要

**業務上の目的・背景**：グローバルなECサイト運営では、商品名、説明文、カテゴリ名などのコンテンツを複数言語で提供する必要がある。翻訳CRUD機能は、各エンティティの翻訳データを管理し、ユーザーの言語設定に応じた適切なコンテンツ表示を実現する。国際展開、ユーザー体験の向上、売上拡大を支援する。

**機能の利用シーン**：商品の多言語コンテンツを登録する場面、注文確認メールの多言語テンプレートを管理する場面、管理画面のローカライズ対応などで利用される。

**主要な処理内容**：
1. 翻訳の作成（createTranslations）：エンティティID、ロケールコード、翻訳データを指定して新規作成
2. 翻訳の一覧取得（listTranslations）：フィルタ条件を指定して翻訳を検索・取得
3. 翻訳の詳細取得（retrieveTranslation）：IDを指定して特定の翻訳を取得
4. 翻訳の更新（updateTranslations）：翻訳データを更新
5. 翻訳の削除（deleteTranslations）：不要な翻訳を削除
6. ロケールの管理（createLocales / listLocales）：対応言語の管理
7. 翻訳統計の取得（getStatistics）：翻訳進捗状況の集計

**関連システム・外部連携**：商品モジュール、注文モジュール、カテゴリモジュールなど、翻訳対象エンティティを持つすべてのモジュールと連携する。

**権限による制御**：翻訳管理操作にはRBACポリシーによる認可が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 187 | 翻訳一覧画面 | 主機能 | 翻訳一覧の取得・表示 |
| 189 | 翻訳編集画面 | 主機能 | 翻訳の編集 |

## 機能種別

CRUD操作 / 多言語対応

## 入力仕様

### 入力パラメータ

#### 翻訳作成（createTranslations）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| reference_id | string | Yes | 翻訳対象エンティティID | 空文字不可 |
| reference | string | Yes | エンティティタイプ（product, categoryなど） | 翻訳可能エンティティのみ |
| locale_code | string | Yes | BCP 47言語コード（en-US, ja-JPなど） | 正規化処理あり |
| translations | object | Yes | 翻訳データ（フィールド名: 翻訳値） | JSON形式 |

#### 翻訳一覧取得（listTranslations）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| reference_id | string/string[] | No | エンティティID | - |
| reference | string/string[] | No | エンティティタイプ | - |
| locale_code | string/string[] | No | ロケールコード | - |
| q | string | No | 翻訳内容の検索クエリ | - |

#### ロケール作成（createLocales）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| code | string | Yes | BCP 47言語コード | 一意制約、正規化処理 |
| name | string | Yes | ロケール表示名 | 空文字不可 |

### 入力データソース

API経由でのJSON形式リクエストボディ、管理画面からの操作

## 出力仕様

### 出力データ

#### TranslationDTO

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | string | 翻訳ID（プレフィックス: trans_） |
| reference_id | string | 翻訳対象エンティティID |
| reference | string | エンティティタイプ |
| locale_code | string | ロケールコード |
| translations | object | 翻訳データ |
| translated_field_count | number | 翻訳済みフィールド数 |
| created_at | Date | 作成日時 |
| updated_at | Date | 更新日時 |

#### LocaleDTO

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | string | ロケールID（プレフィックス: loc_） |
| code | string | ロケールコード |
| name | string | ロケール表示名 |

### 出力先

APIレスポンス（JSON形式）

## 処理フロー

### 処理シーケンス

```
1. 翻訳作成
   └─ ロケールコードの正規化（normalizeLocale）
   └─ 翻訳可能フィールド設定の取得
   └─ 翻訳済みフィールド数の計算
   └─ DBにレコード挿入

2. 翻訳取得
   └─ フィルタ条件の適用
   └─ 翻訳可能フィールドでのフィルタリング
   └─ シリアライズして返却

3. 翻訳更新
   └─ 既存翻訳のreferenceを取得（必要時）
   └─ 翻訳済みフィールド数の再計算
   └─ DBレコード更新
```

### フローチャート

```mermaid
flowchart TD
    A[翻訳作成要求] --> B[ロケールコード正規化]
    B --> C[翻訳可能フィールド取得]
    C --> D[translated_field_count計算]
    D --> E[DB INSERT]
    E --> F[TranslationDTO返却]

    G[翻訳取得要求] --> H[フィルタ適用]
    H --> I[翻訳可能フィールド取得]
    I --> J[フィールドフィルタリング]
    J --> K[結果返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-85-1 | reference_id + locale_code一意 | 同一エンティティ・ロケールの翻訳は1つのみ | 翻訳作成時 |
| BR-85-2 | ロケールコード正規化 | ロケールコードはBCP 47形式に正規化 | 翻訳/ロケール作成時 |
| BR-85-3 | 翻訳フィールドフィルタ | 翻訳可能フィールドのみ返却 | 翻訳取得時 |
| BR-85-4 | フィールド数自動計算 | translated_field_countは自動計算 | 翻訳作成/更新時 |
| BR-85-5 | ロケールコード一意 | 同一ロケールコードは登録不可 | ロケール作成時 |

### 計算ロジック

翻訳済みフィールド数の計算（computeTranslatedFieldCount）：
- translations オブジェクトのキーと翻訳可能フィールド設定を比較
- 翻訳可能フィールドかつ非空の値を持つフィールドをカウント

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| createTranslations | translation | INSERT | 翻訳レコード作成 |
| listTranslations | translation | SELECT | 翻訳一覧取得 |
| retrieveTranslation | translation | SELECT | 単一翻訳取得 |
| updateTranslations | translation | UPDATE | 翻訳更新 |
| deleteTranslations | translation | DELETE | 翻訳削除 |
| createLocales | locale | INSERT | ロケール作成 |
| listLocales | locale | SELECT | ロケール一覧取得 |

### テーブル別操作詳細

#### translation

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | id | trans_プレフィックス + UUID | 自動生成 |
| INSERT | reference_id | エンティティID | 必須 |
| INSERT | reference | エンティティタイプ | 必須 |
| INSERT | locale_code | 正規化されたコード | 必須 |
| INSERT | translations | JSON翻訳データ | 必須 |
| INSERT | translated_field_count | 計算値 | 自動計算 |

#### locale

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | id | loc_プレフィックス + UUID | 自動生成 |
| INSERT | code | 正規化されたコード | 一意 |
| INSERT | name | 表示名 | 必須 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| DUPLICATE_ERROR | 重複エラー | 同一reference_id + locale_codeの翻訳が存在 | 既存翻訳を更新 |
| DUPLICATE_ERROR | 重複エラー | 同一codeのロケールが存在 | 別コードを使用 |
| NOT_FOUND | 検索エラー | 指定IDの翻訳が存在しない | 正しいIDを指定 |

### リトライ仕様

データベースデッドロック発生時は、トランザクションマネージャーにより自動リトライされる。

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

- 各操作はMedusaServiceの基底メソッドによりトランザクション管理される
- 複数翻訳の一括作成/更新は同一トランザクション内で処理

## パフォーマンス要件

- 翻訳一覧取得：1000件以下で500ms以内
- 翻訳統計取得：1000ms以内
- reference_id + locale_code、reference + locale_codeインデックスによる検索高速化

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

- 翻訳管理操作にはRBACポリシーによる認可が必要
- XSS対策として翻訳データの出力時はエスケープ処理が必要
- 機密情報を翻訳データに含めない

## 備考

- ロケールコードはBCP 47形式（en-US、ja-JPなど）で管理
- translated_field_countは最適化のための事前計算フィールド
- 翻訳可能フィールドはDmlEntity.getTranslatableEntities()で取得される設定に基づく

---

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

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

### 推奨読解順序

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

翻訳およびロケールのエンティティ構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | translation.ts | `packages/modules/translation/src/models/translation.ts` | reference_id/reference/locale_code/translations/translated_field_countフィールド |
| 1-2 | locale.ts | `packages/modules/translation/src/models/locale.ts` | code/nameフィールド、code一意制約 |

**読解のコツ**: translationモデルは`reference_id + locale_code`の複合一意制約を持つ。`translated_field_count`は最適化のための事前計算フィールド。

#### Step 2: 翻訳作成処理を理解する

ロケールコード正規化とフィールド数計算。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | translation-module.ts | `packages/modules/translation/src/services/translation-module.ts` | createTranslationsメソッド |
| 2-2 | compute-translated-field-count.ts | `packages/modules/translation/src/utils/compute-translated-field-count.ts` | フィールド数計算ロジック |

**主要処理フロー**:
- **349-382行目**: `createTranslations`メソッド
  - **358-361行目**: 翻訳可能フィールド設定を取得
  - **363-370行目**: ロケールコード正規化、translated_field_count計算
  - **372-375行目**: translationService_.create()でDB挿入

#### Step 3: 翻訳取得とフィルタリングを理解する

翻訳可能フィールドでのフィルタリング処理。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | translation-module.ts | `packages/modules/translation/src/services/translation-module.ts` | listTranslations, retrieveTranslationメソッド |
| 3-2 | filter-translation-fields.ts | `packages/modules/translation/src/utils/filter-translation-fields.ts` | フィールドフィルタリング |

**主要処理フロー**:
- **241-268行目**: `listTranslations`メソッド
  - **248-250行目**: prepareFiltersでqパラメータをILIKE検索に変換
  - **262-265行目**: 翻訳可能フィールド設定を取得
  - **267行目**: filterTranslationFieldsでフィールドフィルタリング

#### Step 4: 翻訳更新処理を理解する

更新時のフィールド数再計算。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | translation-module.ts | `packages/modules/translation/src/services/translation-module.ts` | updateTranslationsメソッド |

**主要処理フロー**:
- **395-458行目**: `updateTranslations`メソッド
  - **408-414行目**: 更新対象のうちtranslationsを含むものを抽出
  - **417-426行目**: referenceが未指定の場合、既存翻訳から取得
  - **428-445行目**: translated_field_countを再計算

#### Step 5: 翻訳可能フィールド設定の取得を理解する

エンティティごとの翻訳可能フィールド設定。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | translation-module.ts | `packages/modules/translation/src/services/translation-module.ts` | getTranslatableFieldsメソッド |

**主要処理フロー**:
- **134-151行目**: `getTranslatableFields`メソッド
  - settingsService_.list()で翻訳設定を取得
  - is_active=trueのエンティティのフィールドのみ返却

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

```
API Route / 管理画面
    │
    └─ TranslationModuleService
           │
           ├─ MedusaService (継承)
           │      └─ 基本CRUD操作
           │
           ├─ translationService_
           │      ├─ create()
           │      ├─ list()
           │      ├─ retrieve()
           │      └─ update()
           │
           ├─ localeService_
           │      ├─ create()
           │      └─ list()
           │
           ├─ settingsService_
           │      └─ list() (翻訳可能フィールド取得)
           │
           └─ utils
                  ├─ normalizeLocale()
                  ├─ computeTranslatedFieldCount()
                  └─ filterTranslationFields()
```

### データフロー図

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

翻訳作成要求 ───▶ TranslationModuleService.createTranslations() ───▶ TranslationDTO
     │                      │
     ├─ reference_id        ├─▶ normalizeLocale()
     ├─ reference           │
     ├─ locale_code         ├─▶ getTranslatableFields()
     │                      │
     └─ translations        ├─▶ computeTranslatedFieldCount()
                            │
                            └─▶ translationService_.create()

翻訳取得要求 ───▶ TranslationModuleService.listTranslations() ───▶ TranslationDTO[]
     │                      │
     └─ filters             ├─▶ prepareFilters()
                            │
                            ├─▶ translationService_.list()
                            │
                            └─▶ filterTranslationFields()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| translation-module.ts | `packages/modules/translation/src/services/translation-module.ts` | サービス | 翻訳CRUD、ロケール管理 |
| translation.ts | `packages/modules/translation/src/models/translation.ts` | モデル | 翻訳エンティティ定義 |
| locale.ts | `packages/modules/translation/src/models/locale.ts` | モデル | ロケールエンティティ定義 |
| settings.ts | `packages/modules/translation/src/models/settings.ts` | モデル | 翻訳設定エンティティ定義 |
| compute-translated-field-count.ts | `packages/modules/translation/src/utils/compute-translated-field-count.ts` | ユーティリティ | フィールド数計算 |
| filter-translation-fields.ts | `packages/modules/translation/src/utils/filter-translation-fields.ts` | ユーティリティ | フィールドフィルタリング |
