# 画面設計書 189-翻訳編集画面

## 概要

本ドキュメントは、Medusa Admin Dashboard における翻訳編集画面の設計仕様を定義する。

### 本画面の処理概要

翻訳編集画面は、特定のエンティティタイプの翻訳データを編集するための画面である。DataGridを使用して、複数のエンティティの翻訳を一括で編集できる。

**業務上の目的・背景**：
多言語対応において、商品名や説明文などを各言語に翻訳する作業は大量のデータを扱う。本画面では、スプレッドシートのようなグリッドUIで複数エンティティの翻訳を効率的に編集できる。ロケールを切り替えながら、各言語の翻訳を一元管理できる。

**画面へのアクセス方法**：
翻訳一覧画面 > エンティティの「Edit」ボタンをクリックすることでアクセスする。

**主要な操作・処理内容**：
1. エンティティ一覧の表示 - DataGridでツリー構造表示
2. 翻訳の編集 - 各セルをクリックして編集
3. ロケールの切り替え - セレクトボックスで言語変更
4. ページネーション - 無限スクロールで追加読み込み
5. 翻訳の保存 - 変更内容をバッチで一括保存
6. 未保存変更の確認 - ロケール切替時に確認ダイアログ

**画面遷移**：
- 遷移元：翻訳一覧画面
- 遷移先：なし（モーダルを閉じて翻訳一覧画面に戻る）

**権限による表示制御**：
管理者権限を持つユーザーのみがアクセス可能。Feature Flagで翻訳機能が有効な場合のみアクセス可能。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 85 | 翻訳CRUD | 主機能 | 翻訳データの取得・更新 |

## 画面種別

フルスクリーンモーダル（RouteFocusModal）

## URL/ルーティング

`/settings/translations/edit?reference={entityType}&reference_id={id}`

- `reference`: エンティティタイプ（例：product, product_category）
- `reference_id`: 特定エンティティのID（複数指定可能、省略時は全件表示）

## 入出力項目

### 入力項目

| 項目名 | 必須 | 型 | 入力形式 | 最大長 | 制約・補足 |
|--------|------|-----|---------|--------|-----------|
| entities | Yes | Record | DataGridセル | - | reference_idごとのフィールド翻訳値 |
| entities[ref_id].id | No | string | 自動 | - | 翻訳レコードID（新規時はnull） |
| entities[ref_id].fields | Yes | Record | テキスト | - | フィールド名と翻訳値のマップ |

### バリデーション仕様

特別なバリデーションはない。空文字の場合は翻訳なしとして扱う。

## 表示項目

### ヘッダー

| 項目名 | 表示形式 | 説明 |
|--------|----------|------|
| ロケールセレクト | Select | 編集対象のロケールを選択 |

### DataGrid

| 項目名 | 表示形式 | 説明 |
|--------|----------|------|
| フィールド名列 | 固定列 | 翻訳可能フィールドの名前 |
| オリジナル列 | 読み取り専用 | 元の値（デフォルト言語） |
| 翻訳列 | 編集可能 | 選択したロケールの翻訳値 |

### フッター

| 項目名 | 表示形式 | 説明 |
|--------|----------|------|
| キャンセルボタン | ボタン | モーダルを閉じる |
| 変更を保存ボタン | ボタン | 変更を保存（閉じない） |
| 保存して閉じるボタン | ボタン | 変更を保存してモーダルを閉じる |

## イベント仕様

### 1-画面初期表示

1. URLパラメータから `reference` と `reference_id` を取得
2. Feature Flagで翻訳機能が有効か確認（無効な場合は戻る）
3. 並行してデータを取得：
   - `useTranslationSettings` で翻訳設定を取得
   - `useReferenceTranslations` で翻訳データを無限スクロールで取得
   - `useStore` でサポートされているロケールを取得
4. 初期ロケール（最初のロケール）でスナップショットを作成
5. フォームに初期値を設定

### 2-セル編集

1. DataGridのセルをクリック
2. MultilineCellで編集可能状態になる
3. 編集完了時にフォーム状態が更新される
4. 編集中はEscapeキーでモーダルが閉じないよう制御

### 3-ロケール切り替え

1. ヘッダーのSelectでロケールを変更
2. 現在の変更を検出（computeChanges）
3. 変更がある場合：未保存確認ダイアログを表示
4. 変更がない場合：直接ロケールを切り替え
5. ロケール切り替え時：
   - クエリを無効化して最新データを取得
   - 新しいスナップショットを作成
   - フォームをリセット

### 4-無限スクロール

1. DataGridがスクロール末尾に達する
2. `fetchNextPage` を呼び出し
3. 新しいエンティティをスナップショットに追加
4. フォームを更新（既存の編集内容を保持）

### 5-変更を保存ボタン押下

1. 現在の変更を計算（computeChanges）
2. 変更がある場合：
   - バッチサイズ（150件）で分割
   - `useBatchTranslations` でAPIを呼び出し
   - 新規作成された翻訳のIDをフォームに反映
   - スナップショットを更新
3. 成功時：トースト表示

### 6-保存して閉じるボタン押下

1. 保存処理を実行
2. 成功時：モーダルを閉じる

### 7-未保存確認ダイアログ

1. 「閉じる」ボタン：ダイアログを閉じる（切り替えキャンセル）
2. 「変更を保存」ボタン：保存してからロケール切り替え

## データベース更新仕様

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 画面初期表示 | translation | SELECT | 翻訳データの取得 |
| 画面初期表示 | translation_setting | SELECT | 翻訳設定の取得 |
| 画面初期表示 | store | SELECT | ストア情報（ロケール）の取得 |
| 保存ボタン押下 | translation | INSERT/UPDATE/DELETE | 翻訳のバッチ更新 |

### テーブル別更新項目詳細

#### translation

| 列名 | INSERT | UPDATE | DELETE | 備考 |
|------|--------|--------|--------|------|
| id | 自動生成 | 更新対象のキー | 削除対象のキー | UUIDを使用 |
| reference_id | 必須 | - | - | 参照先エンティティのID |
| reference | 必須 | - | - | エンティティタイプ |
| locale_code | 必須 | - | - | ロケールコード |
| translations | 必須 | 更新可 | - | フィールドと翻訳値のJSON |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|---------------|----------|
| translations.edit.successToast | 成功 | 翻訳が保存されました | 保存成功時 |
| translations.edit.unsavedChanges.title | 確認 | 未保存の変更があります | ロケール切替時に変更あり |
| translations.edit.unsavedChanges.description | 確認 | 保存しますか？ | ロケール切替時に変更あり |
| - | エラー | APIエラーメッセージ | 保存失敗時 |

## 例外処理

| 例外ケース | 処理内容 |
|-----------|----------|
| referenceパラメータがない | navigate(-1)で戻る |
| 翻訳機能が無効 | navigate(-1)で戻る |
| API通信エラー | エラーをスローし、エラーバウンダリでキャッチ |
| データ取得中 | ready状態になるまでフォーム非表示 |
| ロケール切替中 | ボタンにローディング表示 |

## 備考

- RouteFocusModalレイアウトを使用
- DataGridコンポーネントで複数エンティティを一括編集
- 無限スクロールでページネーション（デフォルト20件ずつ）
- keepPreviousDataでデータ取得中も前のデータを表示
- バッチサイズは150件ずつ
- 翻訳が空の場合、既存レコードは削除される
- 編集中はEscapeキーでモーダルが閉じないよう制御
- ロケール切替時はクエリを無効化して最新データを取得

---

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

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

### 推奨読解順序

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

まず、翻訳編集のデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | translations-edit-form.tsx | `packages/admin/dashboard/src/routes/translations/translations-edit/components/translations-edit-form/translations-edit-form.tsx` | 21-52行目: スキーマと行タイプの定義 |

**読解のコツ**: TranslationRowはEntityRow（親）とFieldRow（子）の2タイプがあり、ツリー構造を形成する。

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

画面のメインコンポーネントを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | translations-edit.tsx | `packages/admin/dashboard/src/routes/translations/translations-edit/translations-edit.tsx` | 全体構造とデータ取得 |

**主要処理フロー**:
1. **14行目**: useFeatureFlagで翻訳機能の有効確認
2. **16-18行目**: URLパラメータからreferenceとreference_idを取得
3. **20-25行目**: 無効な場合はnavigate(-1)で戻る
4. **27-47行目**: 3つのAPIからデータを取得
5. **55-62行目**: ready状態を判定
6. **68-83行目**: RouteFocusModal内にフォームを表示

#### Step 3: フォームコンポーネントを理解する

メインのフォームコンポーネントを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | translations-edit-form.tsx | `.../components/translations-edit-form/translations-edit-form.tsx` | フォーム処理全体 |

**主要処理フロー**:
- **59-88行目**: buildLocaleSnapshotでロケールごとのスナップショット作成
- **90-123行目**: extendSnapshotで無限スクロール時にスナップショット拡張
- **136-186行目**: computeChangesで変更検出
- **195-208行目**: buildTranslationRowsでDataGrid用の行データ作成
- **210-328行目**: useTranslationsGridColumnsでカラム定義
- **471-547行目**: saveCurrentLocaleで保存処理
- **549-578行目**: switchToLocaleでロケール切り替え
- **580-602行目**: handleLocaleChangeで変更確認

#### Step 4: API連携を理解する

APIフックの実装を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | translations.tsx | `packages/admin/dashboard/src/hooks/api/translations.tsx` | 翻訳関連のAPIフック |

**主要処理フロー**:
- **45-100行目**: useReferenceTranslationsで翻訳データ取得（無限スクロール対応）
- **123-155行目**: useBatchTranslationsでバッチ更新（create/update/delete）
- **140-149行目**: invalidateQueriesでクエリ無効化

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

```
TranslationsEdit (translations-edit.tsx)
    │
    ├─ useFeatureFlag("translation") - 機能有効確認
    │
    ├─ useTranslationSettings() - 翻訳設定取得
    │      └─ sdk.admin.translation.settings()
    │
    ├─ useReferenceTranslations() - 翻訳データ取得（無限スクロール）
    │      └─ sdk.admin.translation.entities()
    │
    ├─ useStore() - ストア情報取得
    │      └─ sdk.admin.store.retrieve()
    │
    └─ TranslationsEditForm
           │
           ├─ buildLocaleSnapshot() - スナップショット作成
           │
           ├─ React Hook Form
           │      └─ TranslationsFormSchema
           │
           ├─ DataGrid
           │      ├─ useTranslationsGridColumns() - カラム定義
           │      └─ buildTranslationRows() - 行データ作成
           │
           ├─ saveCurrentLocale()
           │      ├─ computeChanges() - 変更検出
           │      └─ useBatchTranslations()
           │             └─ sdk.admin.translation.batch()
           │
           └─ handleLocaleChange()
                  └─ switchToLocale()
                         └─ invalidateQueries()
```

### データフロー図

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

URLパラメータ ───▶ useReferenceTranslations ───▶ references, translations
    (reference)        (無限スクロール)

ストア情報 ───▶ availableLocales ───▶ ロケールセレクト

翻訳設定 ───▶ translatableFields ───▶ DataGridカラム

セル編集 ───▶ フォーム状態更新 ───▶ computeChanges
                                         │
                                         ▼
                                   create/update/delete
                                         │
                                         ▼
                                   バッチAPI呼び出し
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| translations-edit.tsx | `packages/admin/dashboard/src/routes/translations/translations-edit/translations-edit.tsx` | ソース | 編集画面のエントリーポイント |
| translations-edit-form.tsx | `.../components/translations-edit-form/translations-edit-form.tsx` | ソース | 編集フォーム |
| translations.tsx | `packages/admin/dashboard/src/hooks/api/translations.tsx` | ソース | 翻訳APIフック |
| DataGrid | `packages/admin/dashboard/src/components/data-grid` | ソース | グリッドコンポーネント |
| RouteFocusModal | `packages/admin/dashboard/src/components/modals/route-focus-modal` | ソース | フルスクリーンモーダル |
