# 画面設計書 97-ロケール一覧画面

## 概要

本ドキュメントは、Chatwootのヘルプセンター機能における「ロケール一覧画面」の設計仕様を記述したものである。

### 本画面の処理概要

ロケール一覧画面は、ヘルプセンターポータルで利用可能な言語（ロケール）を一覧表示し、ロケールの追加・デフォルト設定・削除を行う画面である。多言語対応のヘルプセンターを構築するために、複数のロケールを管理できる。

**業務上の目的・背景**：グローバルなカスタマーサポートを提供するために、ヘルプセンターの記事を複数言語で提供する必要がある。ロケール管理機能により、サポート対象の言語を追加・削除し、デフォルト言語を設定することで、多言語ヘルプセンターの運用を効率化する。

**画面へのアクセス方法**：ヘルプセンターのサイドナビゲーションから「Locales」タブをクリック、または直接URL `/app/accounts/:accountId/portals/:portalSlug/locales` にアクセスする。

**主要な操作・処理内容**：
1. ポータルに設定されたロケール一覧を表示
2. 新規ロケールの追加
3. デフォルトロケールの変更
4. ロケールの削除

**画面遷移**：
- 遷移元：サイドナビゲーション、ポータル設定画面
- 遷移先：なし（本画面で操作完結）

**権限による表示制御**：administrator、agent、knowledge_base_manage権限を持つユーザーがアクセス可能。HELP_CENTERフィーチャーフラグが有効である必要がある。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 152 | ポータル管理 | 主機能 | ポータルのロケール設定管理 |

## 画面種別

一覧

## URL/ルーティング

| 項目 | 値 |
|------|-----|
| パス | `/app/accounts/:accountId/portals/:portalSlug/locales` |
| ルート名 | `portals_locales_index` |
| コンポーネント | `PortalsLocalesIndexPage.vue` |

## 入出力項目

### 入力項目（URLパラメータ）

| 項目名 | 型 | 必須 | 説明 |
|--------|-----|------|------|
| accountId | Number | Yes | アカウントID |
| portalSlug | String | Yes | ポータルのスラッグ |

### 出力項目

| 項目名 | 型 | 説明 |
|--------|-----|------|
| locales | Array | ロケール一覧 |
| portal | Object | ポータル情報 |

## 表示項目

### ヘッダーコントロール

| 項目名 | 型 | 説明 |
|--------|-----|------|
| ロケール数 | Text | "{n} locales" |
| 新規ロケールボタン | Button | "New locale" |

### ロケールカード（LocaleCard）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| locale | String | ロケール名（例: English） |
| localeCode | String | ロケールコード（例: en） |
| isDefault | Boolean | デフォルトロケールか |
| articlesCount | Number | 記事数 |
| categoriesCount | Number | カテゴリ数 |
| アクションメニュー | Dropdown | Make default / Delete |

## イベント仕様

### 1-画面初期表示

**トリガー**: コンポーネントのマウント時

**処理フロー**:
1. URLパラメータからportalSlugを取得
2. Vuexゲッター `portals/portalBySlug` でポータル情報を取得
3. ポータルの `config.allowed_locales` から許可ロケール一覧を取得
4. `allLocales` 定数からロケール名を取得してリスト構築
5. LocalesPageコンポーネントでロケール一覧を表示

### 2-新規ロケール追加

**トリガー**: 「New locale」ボタンクリック

**処理フロー**:
1. openAddLocaleDialog関数を実行
2. AddLocaleDialogを開く
3. 未追加のロケールをComboBoxで選択
4. 「確認」ボタンクリックでonCreate関数を実行
5. `portals/update` アクションをディスパッチ
6. PATCH `/api/v1/accounts/:accountId/portals/:portalSlug` を呼び出し
7. 成功時：成功メッセージを表示、ダイアログを閉じる
8. 失敗時：エラーメッセージを表示
9. イベントトラッキング（PORTALS_EVENTS.CREATE_LOCALE）

### 3-デフォルトロケール変更

**トリガー**: アクションメニューから「Make default」選択

**処理フロー**:
1. handleAction関数で action='change-default' を検出
2. changeDefaultLocale関数を実行
3. `portals/update` アクションをディスパッチ（default_localeを更新）
4. 成功時：成功メッセージを表示
5. 失敗時：エラーメッセージを表示
6. イベントトラッキング（PORTALS_EVENTS.SET_DEFAULT_LOCALE）

### 4-ロケール削除

**トリガー**: アクションメニューから「Delete」選択

**処理フロー**:
1. handleAction関数で action='delete' を検出
2. deletePortalLocale関数を実行
3. ロケール一覧から対象ロケールを除外
4. `portals/update` アクションをディスパッチ（allowed_localesを更新）
5. 削除したロケールが最後にアクティブだった場合、UISettings更新
6. 成功時：成功メッセージを表示
7. 失敗時：エラーメッセージを表示
8. イベントトラッキング（PORTALS_EVENTS.DELETE_LOCALE）

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| ロケール追加 | portals | UPDATE | config.allowed_localesに追加 |
| デフォルト変更 | portals | UPDATE | config.default_localeを更新 |
| ロケール削除 | portals | UPDATE | config.allowed_localesから削除 |

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

#### portals

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | config->allowed_locales | ロケールコード配列 | JSONB型 |
| UPDATE | config->default_locale | ロケールコード | デフォルト変更時 |

## メッセージ仕様

| 種別 | メッセージキー | メッセージ内容 | 表示条件 |
|------|--------------|--------------|----------|
| ロケール数 | HELP_CENTER.LOCALES_PAGE.LOCALES_COUNT | "No locales available \| {n} locale \| {n} locales" | 常時 |
| 新規ボタン | HELP_CENTER.LOCALES_PAGE.NEW_LOCALE_BUTTON_TEXT | "New locale" | 常時 |
| デフォルト表示 | HELP_CENTER.LOCALES_PAGE.LOCALE_CARD.DEFAULT | "Default" | デフォルトロケール |
| 記事数 | HELP_CENTER.LOCALES_PAGE.LOCALE_CARD.ARTICLES_COUNT | "{count} article \| {count} articles" | 常時 |
| カテゴリ数 | HELP_CENTER.LOCALES_PAGE.LOCALE_CARD.CATEGORIES_COUNT | "{count} category \| {count} categories" | 常時 |
| デフォルト設定 | HELP_CENTER.LOCALES_PAGE.LOCALE_CARD.DROPDOWN_MENU.MAKE_DEFAULT | "Make default" | メニュー |
| 削除 | HELP_CENTER.LOCALES_PAGE.LOCALE_CARD.DROPDOWN_MENU.DELETE | "Delete" | メニュー |
| ロケール追加成功 | HELP_CENTER.LOCALES_PAGE.ADD_LOCALE_DIALOG.API.SUCCESS_MESSAGE | "Locale added successfully" | 追加成功時 |
| ロケール追加失敗 | HELP_CENTER.LOCALES_PAGE.ADD_LOCALE_DIALOG.API.ERROR_MESSAGE | "Unable to add locale. Try again." | 追加失敗時 |
| デフォルト変更成功 | HELP_CENTER.PORTAL.CHANGE_DEFAULT_LOCALE.API.SUCCESS_MESSAGE | "Default locale updated successfully" | 変更成功時 |
| デフォルト変更失敗 | HELP_CENTER.PORTAL.CHANGE_DEFAULT_LOCALE.API.ERROR_MESSAGE | "Unable to update default locale. Try again." | 変更失敗時 |
| ロケール削除成功 | HELP_CENTER.PORTAL.DELETE_LOCALE.API.SUCCESS_MESSAGE | "Locale removed from portal successfully" | 削除成功時 |
| ロケール削除失敗 | HELP_CENTER.PORTAL.DELETE_LOCALE.API.ERROR_MESSAGE | "Unable to remove locale from portal. Try again." | 削除失敗時 |

## 例外処理

| 例外状況 | 処理内容 |
|---------|---------|
| ポータル未取得 | 空のロケールリストを表示 |
| ロケール追加失敗 | useAlertでエラーメッセージを表示 |
| デフォルト変更失敗 | useAlertでエラーメッセージを表示 |
| ロケール削除失敗 | useAlertでエラーメッセージを表示 |
| デフォルトロケール削除試行 | メニュー項目が無効化（disabled）される |

## 備考

- デフォルトロケールは削除できない（メニュー項目が無効化される）
- ロケール削除時、そのロケールの記事・カテゴリは削除されない
- ロケール情報はポータルのconfig（JSONB）フィールドに保存される
- UISettingsでlast_active_locale_codeが管理され、削除時に更新される
- フィーチャーフラグ `HELP_CENTER` が有効である必要がある

---

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

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

### 推奨読解順序

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

ポータルのロケール設定構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Portal (Model) | `app/models/portal.rb` | configフィールドの構造、CONFIG_JSON_KEYS |
| 1-2 | locales.js | `app/javascript/shared/constants/locales.js` | 利用可能なロケール定義 |

**読解のコツ**: Portal モデルの config JSONB フィールドに allowed_locales と default_locale が保存される点を確認する。

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

画面のエントリーポイントとデータ取得を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | PortalsLocalesIndexPage.vue | `app/javascript/dashboard/routes/dashboard/helpcenter/pages/PortalsLocalesIndexPage.vue` | 画面コンポーネントのメイン処理 |
| 2-2 | helpcenter.routes.js | `app/javascript/dashboard/routes/dashboard/helpcenter/helpcenter.routes.js` | ルート定義（74-78行目） |

**主要処理フロー**:
1. **11行目**: useMapGetterでportals/portalBySlugを取得
2. **13行目**: computedでportalを算出
3. **15-29行目**: allowedLocalesをcomputedで構築（allLocalesから名前をマッピング）

#### Step 3: 子コンポーネントを理解する

ロケール一覧表示のUIコンポーネントを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | LocalesPage.vue | `app/javascript/dashboard/components-next/HelpCenter/Pages/LocalePage/LocalesPage.vue` | レイアウト、ヘッダー、リスト表示 |
| 3-2 | LocaleList.vue | `app/javascript/dashboard/components-next/HelpCenter/Pages/LocalePage/LocaleList.vue` | ロケールカードリスト、アクション処理 |
| 3-3 | LocaleCard.vue | `app/javascript/dashboard/components-next/HelpCenter/LocaleCard/LocaleCard.vue` | 個別カード表示、ドロップダウンメニュー |
| 3-4 | AddLocaleDialog.vue | `app/javascript/dashboard/components-next/HelpCenter/Pages/LocalePage/AddLocaleDialog.vue` | ロケール追加ダイアログ |

**主要処理フロー**:
- **LocaleList.vue 30-52行目**: updatePortalLocales関数でポータル更新
- **LocaleList.vue 54-65行目**: changeDefaultLocale関数でデフォルト変更
- **LocaleList.vue 80-99行目**: deletePortalLocale関数でロケール削除
- **LocaleList.vue 101-107行目**: handleAction関数でアクション振り分け

#### Step 4: Vuex Actionsを理解する

ポータル更新のAPI呼び出し処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | actions.js (portals) | `app/javascript/dashboard/store/modules/helpCenterPortals/actions.js` | updateアクション（53-72行目） |
| 4-2 | portals.js (API) | `app/javascript/dashboard/api/helpCenter/portals.js` | updatePortal APIメソッド（13-15行目） |

**主要処理フロー**:
- **53-72行目**: updateアクション - ポータル情報更新
- **APIメソッド**: PATCH `/api/v1/accounts/:accountId/portals/:portalSlug`

#### Step 5: 定数・ヘルパーを理解する

ロケールメニュー項目とトラッキングイベントを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | portalHelper.js | `app/javascript/dashboard/helper/portalHelper.js` | LOCALE_MENU_ITEMS定義（136-149行目） |
| 5-2 | events.js | `app/javascript/dashboard/helper/AnalyticsHelper/events.js` | PORTALS_EVENTSの定義 |

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

```
PortalsLocalesIndexPage.vue
    │
    ├─ portalBySlug getter
    │      └─ portal (computed)
    │             └─ allowedLocales (computed)
    │
    └─ LocalesPage.vue
           │
           ├─ @click (New locale)
           │      └─ openAddLocaleDialog()
           │             └─ AddLocaleDialog.vue
           │                    └─ onCreate()
           │                           └─ store.dispatch('portals/update')
           │                                  └─ PortalsAPI.updatePortal()
           │
           └─ LocaleList.vue
                  │
                  ├─ LocaleCard.vue[forEach]
                  │      └─ @action → handleAction()
                  │             ├─ change-default → changeDefaultLocale()
                  │             │      └─ updatePortalLocales()
                  │             │             └─ store.dispatch('portals/update')
                  │             │
                  │             └─ delete → deletePortalLocale()
                  │                    ├─ updatePortalLocales()
                  │                    │      └─ store.dispatch('portals/update')
                  │                    └─ updateLastActivePortal()
                  │                           └─ updateUISettings()
                  │
                  └─ isLocaleDefault()
                         └─ portal.meta.default_locale
```

### データフロー図

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

URLパラメータ ────────▶ portalBySlug getter ───────────▶ portal
  - portalSlug              │
                            ▼
                       portal.config.allowed_locales
                            │
                            ▼
                       allLocales[code] → name mapping
                            │
                            ▼
                       allowedLocales[]
                            │
                            ▼
                       LocalesPage.vue
                            │
                            └─▶ LocaleList.vue
                                   └─▶ LocaleCard[forEach]
                                          │
                                          ├─▶ locale name + code
                                          ├─▶ isDefault badge
                                          ├─▶ articles count
                                          ├─▶ categories count
                                          └─▶ action dropdown
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| PortalsLocalesIndexPage.vue | `app/javascript/dashboard/routes/dashboard/helpcenter/pages/PortalsLocalesIndexPage.vue` | ソース | 画面コンポーネント |
| LocalesPage.vue | `app/javascript/dashboard/components-next/HelpCenter/Pages/LocalePage/LocalesPage.vue` | ソース | ロケール一覧UI |
| LocaleList.vue | `app/javascript/dashboard/components-next/HelpCenter/Pages/LocalePage/LocaleList.vue` | ソース | ロケールリスト・アクション処理 |
| LocaleCard.vue | `app/javascript/dashboard/components-next/HelpCenter/LocaleCard/LocaleCard.vue` | ソース | ロケールカード表示 |
| AddLocaleDialog.vue | `app/javascript/dashboard/components-next/HelpCenter/Pages/LocalePage/AddLocaleDialog.vue` | ソース | ロケール追加ダイアログ |
| helpcenter.routes.js | `app/javascript/dashboard/routes/dashboard/helpcenter/helpcenter.routes.js` | ソース | ルート定義 |
| actions.js | `app/javascript/dashboard/store/modules/helpCenterPortals/actions.js` | ソース | Vuexアクション |
| portals.js | `app/javascript/dashboard/api/helpCenter/portals.js` | ソース | APIクライアント |
| portal.rb | `app/models/portal.rb` | ソース | モデル定義 |
| portalHelper.js | `app/javascript/dashboard/helper/portalHelper.js` | ソース | ヘルパー関数・定数 |
| locales.js | `app/javascript/shared/constants/locales.js` | ソース | ロケール定数 |
