# 機能設計書 44-言語設定

## 概要

本ドキュメントは、Actualの「言語設定」機能の設計内容を記載する。アプリケーションの表示言語を切り替える国際化（i18n）機能について、処理内容とUIコンポーネントの仕様を定義する。

### 本機能の処理概要

言語設定機能は、アプリケーション全体のUIテキストを選択した言語で表示する機能である。i18next（React i18n）ライブラリを使用し、動的な言語切り替えをサポートする。

**業務上の目的・背景**：グローバルなユーザー基盤をサポートするため、多言語対応を提供する。ユーザーの母語での操作を可能にし、アクセシビリティとユーザーエクスペリエンスを向上させる。また、コミュニティによる翻訳貢献を容易にするためWeblateとの連携も行う。

**機能の利用シーン**：初期セットアップ時に使用言語を選択する場合、英語以外の言語でアプリケーションを使用したい場合、システムデフォルト言語で利用したい場合に使用される。

**主要な処理内容**：
1. 利用可能な言語一覧の表示（翻訳ファイルが存在する言語）
2. 言語選択とi18nextへの反映
3. システムデフォルト言語への自動追従オプション
4. グローバル設定への保存
5. 言語ファイルの動的読み込み

**関連システム・外部連携**：Weblate（翻訳管理プラットフォーム）

**権限による制御**：特になし（全ユーザーが利用可能）

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 40 | 設定画面 | 主機能 | UI言語の切り替え設定 |

## 機能種別

設定・国際化処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| language | string | No | 言語コード（BCP 47形式） | 空文字（システムデフォルト）または利用可能言語のいずれか |

### 入力データソース

- 画面入力（ドロップダウン選択）
- システム設定（navigator.language）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| language | string | 選択された言語コード |

### 出力先

- グローバル設定（global-store.json）
- i18next言語設定

## 処理フロー

### 処理シーケンス

```
1. 設定画面のLanguageSettingsコンポーネント表示
   ├─ useGlobalPrefフックで現在の言語設定を取得
   └─ availableLanguagesから利用可能言語一覧を取得

2. 言語選択
   ├─ Selectコンポーネントで言語を選択
   ├─ setLanguage呼び出し（グローバル設定に保存）
   └─ setI18NextLanguage呼び出し

3. setI18NextLanguage処理
   ├─ 空文字の場合はnavigator.languageを使用
   ├─ 言語が利用可能か確認（isLanguageAvailable）
   ├─ 利用不可の場合はフォールバック処理
   │    ├─ ハイフン前の言語コードで再試行（en-US → en）
   │    ├─ 小文字変換で再試行
   │    └─ 最終的に'en'にフォールバック
   └─ i18n.changeLanguage呼び出し

4. 言語ファイル読み込み
   └─ resourcesToBackendで動的に翻訳ファイルを読み込み
```

### フローチャート

```mermaid
flowchart TD
    A[言語設定画面表示] --> B[現在の言語設定読み込み]
    B --> C{言語選択}

    C -->|System default| D[setLanguage 空文字]
    C -->|特定言語| E[setLanguage 言語コード]

    D --> F[setI18NextLanguage 空文字]
    E --> G[setI18NextLanguage 言語コード]

    F --> H[navigator.language取得]
    H --> I{言語利用可能?}

    G --> I

    I -->|Yes| J[i18n.changeLanguage]
    I -->|No| K{フォールバック処理}

    K -->|ハイフン分割| L[基本言語で再試行]
    K -->|小文字変換| M[小文字で再試行]
    K -->|最終| N[en にフォールバック]

    L --> I
    M --> I
    N --> J

    J --> O[言語ファイル動的読み込み]
    O --> P[UI再レンダリング]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-44-01 | デフォルト言語 | 初期状態では空文字（システムデフォルト） | 未設定時 |
| BR-44-02 | フォールバック言語 | 英語（en）は常に利用可能（自然言語キー使用） | 言語ファイル未存在時 |
| BR-44-03 | 言語コードフォールバック | 'en-US'→'en'、'PT-BR'→'pt-br'の順でフォールバック | 完全一致する言語ファイルがない場合 |
| BR-44-04 | 言語表示名 | 各言語は自身の言語で表示される（例：Deutsch） | ドロップダウン表示時 |
| BR-44-05 | 翻訳品質免責 | 英語以外の翻訳は品質保証なし、コミュニティ翻訳 | 常時 |

### 言語表示名の例外

| 言語コード | オーバーライド表示名 |
|-----------|-------------------|
| pt-BR | Portugues (Brasil) |

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

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

本機能はデータベースを直接操作しない。グローバル設定ファイル（global-store.json）に保存される。

| 操作 | 対象 | 操作種別 | 概要 |
|-----|------|---------|------|
| 言語変更 | global-store.json | UPDATE | languageを更新 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| LANGUAGE_NOT_AVAILABLE | 警告 | 翻訳ファイルが存在しない言語を指定 | 自動でフォールバック言語を使用 |

### フォールバック処理詳細

1. 指定言語が利用不可の場合、コンソールにinfo出力
2. ハイフン付き言語コードは基本部分で再試行
3. 大文字を含む場合は小文字変換で再試行
4. 最終的に'en'（英語）にフォールバック

### リトライ仕様

言語ファイルの読み込みに失敗した場合は、i18nextのデフォルト動作により英語のキー（自然言語）がそのまま表示される。

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

なし（設定変更は即座に反映）

## パフォーマンス要件

- 言語切替: 翻訳ファイル読み込み含め1秒以内
- 翻訳ファイルは遅延読み込み（resourcesToBackend）

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

特になし（表示設定のみで、センシティブなデータを扱わない）

## 備考

- 翻訳はWeblate（hosted.weblate.org）で管理され、コミュニティが貢献可能
- Playwrightテスト環境では言語機能が無効化される（availableLanguages = []）
- 翻訳ファイルは`/locale/{lang}.json`形式で格納
- 翻訳キーは英語の自然言語テキストをそのまま使用（nsSeparator, keySeparatorを無効化）

---

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

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

### 推奨読解順序

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

言語設定で使用されるデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | i18n.ts | `packages/desktop-client/src/i18n.ts` | 利用可能言語の取得方法（10-12行目） |
| 1-2 | languages.ts | `packages/desktop-client/src/languages.ts` | 言語ファイルのインポートマップ |

**読解のコツ**: availableLanguagesはlanguagesオブジェクトのキーから抽出され、パス形式`/locale/{lang}.json`から言語コードを取り出している。

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

言語設定UIのエントリーポイントを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | LanguageSettings.tsx | `packages/desktop-client/src/components/settings/LanguageSettings.tsx` | LanguageSettingsコンポーネント全体の構造（34-92行目） |

**主要処理フロー**:
1. **15-17行目**: languageDisplayNameOverrideで言語表示名のオーバーライド定義
2. **19-32行目**: languageOptions関数でドロップダウンオプションを構築
3. **36行目**: useGlobalPref('language')で現在の言語設定を取得
4. **37行目**: isEnabled変数で言語機能の有効性を判定
5. **49-52行目**: onChange時にsetLanguageとsetI18NextLanguageを呼び出し

#### Step 3: i18n初期化と言語切り替えを理解する

i18nextの初期化と言語切り替え処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | i18n.ts | `packages/desktop-client/src/i18n.ts` | i18n初期化とsetI18NextLanguage関数（24-83行目） |

**主要処理フロー**:
- **24-41行目**: i18n.init()でi18next初期化、fallbackLng: false設定
- **43-83行目**: setI18NextLanguage関数で言語切り替え処理
- **44-48行目**: 空文字の場合はnavigator.languageを使用
- **50-76行目**: フォールバック処理（ハイフン分割、小文字変換、最終的にen）
- **82行目**: i18n.changeLanguage()でi18nextの言語を変更

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

```
LanguageSettings (LanguageSettings.tsx)
    │
    ├─ useGlobalPref('language')
    │      └─ useSelector(state.prefs.global?.language)
    │
    ├─ availableLanguages (i18n.ts)
    │      └─ Object.keys(languages) から言語コード抽出
    │
    ├─ languageOptions(t)
    │      ├─ ['', t('System default')]
    │      ├─ Menu.line
    │      └─ availableLanguages.map() で各言語オプション構築
    │           └─ Intl.DisplayNames() で言語表示名取得
    │
    ├─ setLanguage()
    │      └─ dispatch(saveGlobalPrefs({prefs: {language: value}}))
    │
    └─ setI18NextLanguage()
           ├─ 空文字 → navigator.language 使用
           ├─ isLanguageAvailable() チェック
           │      └─ languages[`/locale/${language}.json`] 存在確認
           ├─ フォールバック処理
           │      ├─ ハイフン分割で再試行
           │      ├─ 小文字変換で再試行
           │      └─ 'en' にフォールバック
           └─ i18n.changeLanguage()
                  └─ resourcesToBackend でファイル読み込み
                         └─ loadLanguage() → languages[path]()
```

### データフロー図

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

言語選択 ────────────────▶ setLanguage()
ドロップダウン                    │
                                ▼
                          saveGlobalPrefs() ────────▶ グローバル設定
                                │                     (language)
                                ▼
                          setI18NextLanguage()
                                │
                    ┌───────────┴───────────┐
                    ▼                       ▼
              空文字の場合            言語コード指定
                    │                       │
                    ▼                       ▼
            navigator.language        isLanguageAvailable()
                    │                       │
                    └───────────┬───────────┘
                                ▼
                    ┌─── 利用可能? ───┐
                    │                │
                   Yes              No
                    │                │
                    ▼                ▼
              i18n.changeLanguage() フォールバック処理
                    │                │
                    ▼                ▼
              言語ファイル読み込み   再帰呼び出し
                    │
                    ▼
              UI再レンダリング
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| LanguageSettings.tsx | `packages/desktop-client/src/components/settings/LanguageSettings.tsx` | ソース | 言語設定UIコンポーネント |
| i18n.ts | `packages/desktop-client/src/i18n.ts` | ソース | i18next初期化と言語切り替えロジック |
| languages.ts | `packages/desktop-client/src/languages.ts` | ソース | 言語ファイルのインポートマップ |
| useGlobalPref.ts | `packages/desktop-client/src/hooks/useGlobalPref.ts` | ソース | グローバル設定読み書きフック |
| UI.tsx | `packages/desktop-client/src/components/settings/UI.tsx` | ソース | 設定画面共通UIコンポーネント |
| Link.tsx | `packages/desktop-client/src/components/common/Link.tsx` | ソース | 外部リンクコンポーネント（Weblate連携用） |
| locale/*.json | `packages/desktop-client/locale/` | 設定 | 各言語の翻訳ファイル |
