# 機能設計書 86-翻訳設定管理

## 概要

本ドキュメントは、Medusaコマースプラットフォームにおける翻訳設定管理機能の設計について記述する。翻訳モジュールの設定機能として、翻訳対象エンティティおよび翻訳可能フィールドの設定管理を行う。

### 本機能の処理概要

**業務上の目的・背景**：ECサイトのすべてのテキストフィールドを翻訳対象とするのは非効率である。翻訳設定管理機能は、エンティティごとにどのフィールドを翻訳可能とするかを設定し、翻訳作業の効率化と一貫性を確保する。また、エンティティの翻訳機能を有効/無効化することで、段階的な多言語対応を可能にする。

**機能の利用シーン**：新しいエンティティに翻訳機能を追加する場面、特定フィールドを翻訳対象から除外する場面、翻訳機能の有効/無効を切り替える場面などで利用される。

**主要な処理内容**：
1. 翻訳設定の作成（createTranslationSettings）：エンティティタイプと翻訳可能フィールドを設定
2. 翻訳設定の一覧取得（listTranslationSettings）：設定済みエンティティ一覧を取得
3. 翻訳設定の更新（updateTranslationSettings）：翻訳可能フィールドや有効/無効状態を更新
4. 翻訳可能フィールドの取得（getTranslatableFields）：エンティティ別の翻訳可能フィールドを取得
5. 無効フィールドの取得（getInactiveTranslatableFields）：翻訳対象外として設定されたフィールドを取得
6. 翻訳統計の取得（getStatistics）：エンティティ別・ロケール別の翻訳進捗を集計

**関連システム・外部連携**：翻訳CRUD機能（翻訳可能フィールドの参照）、DmlEntity（翻訳可能エンティティの定義）と連携する。

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 188 | 翻訳設定画面 | 主機能 | 翻訳設定の管理 |
| 190 | ロケール追加画面 | 主機能 | ロケールの追加 |

## 機能種別

CRUD操作 / 設定管理 / 統計集計

## 入力仕様

### 入力パラメータ

#### 翻訳設定作成（createTranslationSettings）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| entity_type | string | Yes | エンティティタイプ（product, categoryなど） | 翻訳可能エンティティのみ |
| fields | string[] | Yes | 翻訳可能フィールド一覧 | DML定義に存在するフィールドのみ |
| is_active | boolean | No | 翻訳機能の有効/無効 | デフォルト: true |

#### 翻訳設定更新（updateTranslationSettings）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| id | string | Yes | 翻訳設定ID | 存在確認 |
| fields | string[] | No | 翻訳可能フィールド一覧 | DML定義に存在するフィールドのみ |
| is_active | boolean | No | 翻訳機能の有効/無効 | - |

#### 翻訳統計取得（getStatistics）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| locales | string[] | Yes | 対象ロケール一覧 | 1件以上必須 |
| entities | object | Yes | エンティティごとのカウント | 1件以上必須 |

### 入力データソース

API経由でのJSON形式リクエストボディ、アプリケーション起動時のDML定義

## 出力仕様

### 出力データ

#### TranslationSettingsDTO

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | string | 翻訳設定ID（プレフィックス: trset_） |
| entity_type | string | エンティティタイプ |
| fields | string[] | 翻訳可能フィールド一覧 |
| is_active | boolean | 翻訳機能の有効/無効 |
| created_at | Date | 作成日時 |
| updated_at | Date | 更新日時 |

#### TranslationStatisticsOutput

| 項目名 | 型 | 説明 |
|--------|-----|------|
| [entity_type] | object | エンティティ別統計 |
| .expected | number | 期待翻訳数 |
| .translated | number | 翻訳済み数 |
| .missing | number | 未翻訳数 |
| .by_locale | object | ロケール別統計 |

### 出力先

APIレスポンス（JSON形式）

## 処理フロー

### 処理シーケンス

```
1. アプリケーション起動時
   └─ DmlEntity.getTranslatableEntities()で翻訳可能エンティティを取得
   └─ 既存設定と比較し、差分をupsert

2. 翻訳設定作成/更新
   └─ エンティティタイプの存在確認
   └─ フィールドの存在確認（DML定義と照合）
   └─ 設定をDB保存
   └─ translated_field_countの再計算

3. 翻訳統計取得
   └─ 各エンティティの翻訳可能フィールド数を取得
   └─ 期待翻訳数 = エンティティ数 × フィールド数 × ロケール数
   └─ DBから翻訳済みカウントを集計
   └─ 不足数を計算
```

### フローチャート

```mermaid
flowchart TD
    A[アプリ起動] --> B[DmlEntity.getTranslatableEntities]
    B --> C[既存設定取得]
    C --> D{差分あり?}
    D -->|新規エンティティ| E[設定作成]
    D -->|削除エンティティ| F[is_active=false設定]
    D -->|差分なし| G[完了]
    E --> G
    F --> G

    H[翻訳統計要求] --> I[翻訳可能フィールド取得]
    I --> J[期待翻訳数計算]
    J --> K[DB集計クエリ実行]
    K --> L[統計結果返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-86-1 | entity_type一意 | 同一エンティティタイプの設定は1つのみ | 設定作成時 |
| BR-86-2 | フィールド存在確認 | DML定義に存在するフィールドのみ設定可能 | 設定作成/更新時 |
| BR-86-3 | エンティティ存在確認 | 翻訳可能エンティティのみ設定可能 | 設定作成/更新時 |
| BR-86-4 | 自動同期 | アプリ起動時にDML定義と同期 | アプリ起動時 |
| BR-86-5 | 自動無効化 | DML定義から削除されたエンティティはis_active=falseに | アプリ起動時 |
| BR-86-6 | フィールド数再計算 | 設定更新時は関連翻訳のtranslated_field_countを再計算 | 設定更新時 |

### 計算ロジック

翻訳統計の計算：
- 期待翻訳数（expected）= エンティティ件数 × 翻訳可能フィールド数 × ロケール数
- 翻訳済み数（translated）= SUM(translated_field_count) from translation WHERE reference = entity_type
- 未翻訳数（missing）= expected - translated

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| createTranslationSettings | translation_settings | INSERT | 設定作成 |
| listTranslationSettings | translation_settings | SELECT | 設定一覧取得 |
| updateTranslationSettings | translation_settings, translation | UPDATE | 設定更新、翻訳のフィールド数再計算 |
| onApplicationStart | translation_settings | UPSERT | DML定義との同期 |
| getStatistics | translation, translation_settings | SELECT | 統計集計クエリ |

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

#### translation_settings

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | id | trset_プレフィックス + UUID | 自動生成 |
| INSERT | entity_type | エンティティタイプ | 一意 |
| INSERT | fields | フィールド配列（JSON） | 必須 |
| INSERT | is_active | true/false | デフォルト: true |
| UPDATE | is_active | false | DML定義削除時 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| INVALID_DATA | 検証エラー | 翻訳可能でないエンティティを指定 | 有効なエンティティを指定 |
| INVALID_DATA | 検証エラー | 存在しないフィールドを指定 | DML定義を確認 |
| INVALID_DATA | 検証エラー | 統計取得時にロケール未指定 | 1件以上のロケールを指定 |
| INVALID_DATA | 検証エラー | 統計取得時にエンティティ未指定 | 1件以上のエンティティを指定 |

### リトライ仕様

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

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

- 設定更新時はtranslated_field_countの再計算も同一トランザクションで処理
- アプリ起動時の同期処理はupsertで一括処理

## パフォーマンス要件

- 翻訳設定一覧取得：100件以下で100ms以内
- 翻訳統計取得：10エンティティ × 10ロケールで2秒以内
- entity_typeインデックスによる検索高速化

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

- 翻訳設定管理操作には管理者権限が必要
- 設定変更は翻訳データの表示に直接影響するため慎重に行う

## 備考

- DmlEntity.getTranslatableEntities()は各モジュールのモデル定義から翻訳可能エンティティを収集
- アプリ起動時の自動同期により、コードとDBの整合性が保たれる
- is_active=falseのエンティティは翻訳可能フィールドとして空配列を返す

---

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

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

### 推奨読解順序

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

翻訳設定のエンティティ構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | settings.ts | `packages/modules/translation/src/models/settings.ts` | entity_type/fields/is_activeフィールド |

**読解のコツ**: entity_typeに一意制約があり、fieldsはJSON配列として保存される。is_activeでエンティティの翻訳機能を有効/無効化できる。

#### Step 2: アプリ起動時の自動同期を理解する

DML定義とDBの同期処理。

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

**主要処理フロー**:
- **85-89行目**: `__hooks.onApplicationStart`でアプリ起動時に実行
- **91-132行目**: `onApplicationStart_`メソッド
  - **92-95行目**: DmlEntity.getTranslatableEntities()で翻訳可能エンティティを取得
  - **97-100行目**: 既存設定を取得
  - **107-117行目**: DML定義から削除されたエンティティをis_active=falseに
  - **119-129行目**: 新規エンティティの設定を作成
  - **131行目**: settingsService_.upsert()で一括保存

#### Step 3: 翻訳設定のバリデーションを理解する

エンティティとフィールドの存在確認。

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

**主要処理フロー**:
- **657-713行目**: `validateSettings`静的メソッド
  - **663-666行目**: DmlEntity.getTranslatableEntities()と照合
  - **674-693行目**: エンティティが存在しない、またはフィールドが無効な場合を検出
  - **695-711行目**: 検証エラー時は詳細メッセージを生成

#### Step 4: 翻訳統計取得を理解する

エンティティ別・ロケール別の翻訳進捗集計。

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

**主要処理フロー**:
- **537-651行目**: `getStatistics`メソッド
  - **543-556行目**: 入力バリデーション（locales, entitiesの存在確認）
  - **564-567行目**: 翻訳可能フィールド設定を取得
  - **572-589行目**: 翻訳可能フィールドがないエンティティは0を返却
  - **596-609行目**: SQLクエリでtranslated_field_countを集計
  - **611-648行目**: 期待数、翻訳済み数、未翻訳数を計算

#### Step 5: 設定更新時のフィールド数再計算を理解する

翻訳データのtranslated_field_count再計算。

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

**主要処理フロー**:
- **482-535行目**: `updateTranslationSettings`メソッド
  - **500-503行目**: settingsService_.update()で設定を更新
  - **505-507行目**: 対象エンティティタイプを抽出
  - **514-516行目**: 該当エンティティの翻訳を取得
  - **518-524行目**: translated_field_countを再計算
  - **526-528行目**: 翻訳レコードを更新

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

```
アプリケーション起動
    │
    └─ TranslationModuleService.__hooks.onApplicationStart
           │
           └─ onApplicationStart_()
                  │
                  ├─▶ DmlEntity.getTranslatableEntities()
                  │
                  ├─▶ settingsService_.list()
                  │
                  └─▶ settingsService_.upsert()

API Route
    │
    └─ TranslationModuleService
           │
           ├─ updateTranslationSettings()
           │      ├─▶ ensureEntityType_()
           │      ├─▶ validateSettings()
           │      ├─▶ settingsService_.update()
           │      ├─▶ translationService_.list()
           │      └─▶ translationService_.update() (再計算)
           │
           └─ getStatistics()
                  ├─▶ getTranslatableFields()
                  └─▶ knex.raw() (集計クエリ)
```

### データフロー図

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

DML定義 ───▶ onApplicationStart_() ───▶ translation_settings同期
     │              │
     └─▶ DmlEntity.getTranslatableEntities()

設定更新要求 ───▶ updateTranslationSettings() ───▶ TranslationSettingsDTO
     │                    │
     ├─ id                ├─▶ validateSettings()
     ├─ fields            │
     └─ is_active         ├─▶ settingsService_.update()
                          │
                          └─▶ translationService_.update() (フィールド数再計算)

統計取得要求 ───▶ getStatistics() ───▶ TranslationStatisticsOutput
     │                    │
     ├─ locales           ├─▶ getTranslatableFields()
     │                    │
     └─ entities          └─▶ SQLクエリ (SUM translated_field_count)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| translation-module.ts | `packages/modules/translation/src/services/translation-module.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` | ユーティリティ | フィールド数計算 |
| index.ts | `packages/modules/translation/src/index.ts` | エクスポート | モジュールエクスポート |
