# 機能設計書 67-多言語対応

## 概要

本ドキュメントは、InvenTreeモバイルアプリにおける多言語対応機能の設計を記述するものである。複数の言語でアプリを使用できる国際化（i18n）機能の仕様を定義する。

### 本機能の処理概要

本機能は、アプリのUI文字列を複数の言語で表示する国際化機能を提供する。Flutterの標準的なlocalizationフレームワークを使用し、flutter_localizationsパッケージおよびアプリ固有の翻訳ファイル（ARBファイル）から多言語テキストを読み込む。ユーザーはアプリ設定画面から言語を選択でき、選択した言語はローカルに保存されて次回起動時にも適用される。

**業務上の目的・背景**：InvenTreeは国際的なオープンソースプロジェクトであり、世界中の企業・個人が在庫管理に使用している。母国語でアプリを使用できることは、ユーザビリティと業務効率の向上に直結する。特に倉庫作業者など、英語に不慣れなユーザーにとって多言語対応は必須の機能である。

**機能の利用シーン**：
- 非英語圏のユーザーが母国語でアプリを使用したい場合
- 多国籍チームで共通のInvenTreeインスタンスを使用する場合
- システムのデフォルト言語とは異なる言語でアプリを使用したい場合

**主要な処理内容**：
1. サポートされている言語一覧の定義
2. 言語設定のローカルデータベースへの保存・読み込み
3. L10()ヘルパー関数による翻訳テキストの取得
4. MaterialAppのlocaleプロパティによる言語切り替え

**関連システム・外部連携**：
- Crowdin：翻訳管理プラットフォーム（翻訳協力者向け）
- InvenTreeサーバー：API応答は選択された言語のAccept-Languageヘッダーで送信される

**権限による制御**：本機能に特別な権限制御はなく、すべてのユーザーが利用可能である。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 10 | アプリ設定画面 | 主画面 | 言語設定の変更時に多言語対応機能と連携 |

## 機能種別

設定管理 / UI国際化

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| locale | Locale | No | 選択された言語ロケール | サポート言語リストに含まれること |

### 入力データソース

- ローカルDB：`InvenTreeSettings.db`のsettingsストアからcustomLocaleキーで言語設定を読み込み
- システム設定：デフォルトはシステムのロケール設定を使用

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| localizedString | String | 現在のロケールに対応する翻訳テキスト |
| currentLocale | Locale | 現在適用されているロケール |

### 出力先

- 画面表示：すべてのUI文字列
- API通信：Accept-Languageヘッダー

## 処理フロー

### 処理シーケンス

```
1. アプリ起動
   └─ runInitTasks()で保存された言語設定を読み込み
   └─ setLocale()でアプリロケールを設定
2. UI構築時
   └─ L10().{プロパティ名}で翻訳テキストを取得
   └─ I18N.of(context)から現在のロケールの翻訳を取得
3. 言語変更時（アプリ設定画面）
   └─ InvenTreeSettingsManager.setSelectedLocale()で保存
   └─ InvenTreeApp.of(context).setLocale()でUIに反映
```

### フローチャート

```mermaid
flowchart TD
    A[アプリ起動] --> B[保存された言語設定読み込み]
    B --> C{言語設定あり?}
    C -->|Yes| D[指定ロケール適用]
    C -->|No| E[システムロケール使用]
    D --> F[MaterialApp構築]
    E --> F
    F --> G[UI表示]
    G --> H{L10呼び出し}
    H --> I[I18N.of context から翻訳取得]
    I --> J[翻訳テキスト返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-67-01 | デフォルト言語 | 言語未設定時はシステムロケールを使用 | 初回起動時 |
| BR-67-02 | フォールバック | 翻訳が存在しない場合は英語にフォールバック | 翻訳なしの場合 |
| BR-67-03 | 永続化 | 選択された言語はローカルDBに保存 | 言語変更時 |
| BR-67-04 | サーバー連携 | API通信時は選択言語をAccept-Languageヘッダーで送信 | API呼び出し時 |

### 計算ロジック

該当なし

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 言語設定読み込み | settings（sembast store） | SELECT | customLocaleキーで言語設定を取得 |
| 言語設定保存 | settings（sembast store） | UPDATE | customLocaleキーで言語設定を保存 |

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

#### settings（sembast store）

| 操作 | 項目（キー） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT/UPDATE | customLocale | ロケール文字列（例: "ja", "de"） | 空文字列はシステムロケール |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 翻訳未対応 | 選択言語に翻訳がない文字列 | 英語にフォールバック |
| - | ロケール無効 | サポートされていないロケール | システムロケールを使用 |

### リトライ仕様

リトライは不要。フォールバック機構で対応。

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

sembastデータベースの単一キー操作のため、明示的なトランザクション管理は不要。

## パフォーマンス要件

- 翻訳テキスト取得：1ms以内（メモリキャッシュから）
- 言語切り替え：200ms以内（UIリビルド含む）

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

- 言語設定はローカルデバイスにのみ保存
- 翻訳テキストに機密情報は含まれない

## 備考

- 翻訳はCrowdinプラットフォームで管理されている
- サポート言語はsupported_locales.dartで定義
- L10()関数は簡潔な呼び出しのためのショートカット

---

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

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

### 推奨読解順序

#### Step 1: 翻訳テキスト取得の仕組みを理解する

L10()ヘルパー関数の実装を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | l10.dart | `lib/l10.dart` | L10()関数の実装 |

**読解のコツ**:
- **1-2行目**: `I18N`クラス（app_localizations.dart）と英語デフォルト（app_localizations_en.dart）をインポート
- **10-28行目**: `L10()`関数の実装
- **12-14行目**: テスト環境ではコンテキストが不要なため英語を返す
- **16-23行目**: OneContextからコンテキストを取得し、I18N.of()で翻訳を取得
- **27行目**: コンテキストがnullの場合は英語にフォールバック

#### Step 2: アプリ初期化と言語設定を理解する

main.dartでのロケール設定を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | main.dart | `lib/main.dart` | InvenTreeAppStateクラス |

**主要処理フロー**:
- **107-108行目**: `_locale`状態変数（nullはシステムロケール）
- **121-124行目**: `runInitTasks`でロケール設定を読み込み、setLocaleを呼び出し
- **147-152行目**: `setLocale`メソッドでロケールをsetStateで更新
- **154行目**: `locale`ゲッターで現在のロケールを返す
- **178-186行目**: MaterialAppのlocalizationDelegatesとsupportedLocales設定
- **186行目**: `locale: _locale`で現在のロケールを適用

#### Step 3: 言語設定の永続化を理解する

preferences.dartでの言語設定保存を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | preferences.dart | `lib/preferences.dart` | 言語設定関連メソッド |

**主要処理フロー**:
- **121-136行目**: `getSelectedLocale`メソッドでcustomLocaleキーから言語を読み込み
- **138-140行目**: `setSelectedLocale`メソッドでcustomLocaleキーに言語を保存

#### Step 4: API通信での言語ヘッダーを理解する

api.dartでのAccept-Languageヘッダー設定を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | api.dart | `lib/api.dart` | currentLocaleゲッターとdefaultHeaders |

**主要処理フロー**:
- **1492-1508行目**: `currentLocale`ゲッターで現在のロケールコードを取得
- **1510-1523行目**: `defaultHeaders`メソッドでAccept-Languageヘッダーを設定

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

```
InvenTreeApp (main.dart)
    │
    ├─ InvenTreeAppState
    │      │
    │      ├─ initState()
    │      │      └─ runInitTasks()
    │      │             └─ InvenTreeSettingsManager.getSelectedLocale()
    │      │                    └─ setLocale()
    │      │
    │      ├─ setLocale(Locale? locale)
    │      │      └─ setState() → _locale更新
    │      │
    │      └─ build()
    │             └─ MaterialApp(
    │                    locale: _locale,
    │                    localizationsDelegates: [...],
    │                    supportedLocales: supported_locales
    │                )
    │
L10() (l10.dart)
    │
    └─ I18N.of(context)
           └─ 翻訳テキスト返却

InvenTreeAPI (api.dart)
    │
    ├─ currentLocale (getter)
    │      └─ InvenTreeApp.of(context)?.locale?.languageCode
    │
    └─ defaultHeaders()
           └─ HttpHeaders.acceptLanguageHeader: currentLocale
```

### データフロー図

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

                      ┌─────────────────────┐
ローカルDB ──────────▶│ getSelectedLocale() │
(customLocale)        └──────────┬──────────┘
                                 │
                                 ▼
                      ┌─────────────────────┐
                      │ InvenTreeAppState   │
                      │ - _locale           │──────▶ MaterialApp.locale
                      │ - setLocale()       │
                      └──────────┬──────────┘
                                 │
                                 ▼
                      ┌─────────────────────┐
UI文字列要求 ────────▶│ L10().{property}    │
                      │ - I18N.of(context)  │──────▶ 翻訳テキスト
                      └─────────────────────┘

                      ┌─────────────────────┐
API通信 ─────────────▶│ defaultHeaders()    │
                      │ - currentLocale     │──────▶ Accept-Language
                      └─────────────────────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| l10.dart | `lib/l10.dart` | ソース | L10()ヘルパー関数 |
| main.dart | `lib/main.dart` | ソース | アプリ初期化・ロケール設定 |
| preferences.dart | `lib/preferences.dart` | ソース | 言語設定の永続化 |
| api.dart | `lib/api.dart` | ソース | API通信時のAccept-Languageヘッダー |
| app_localizations.dart | `lib/l10n/collected/app_localizations.dart` | 生成ソース | 翻訳テキストクラス（I18N） |
| supported_locales.dart | `lib/l10n/supported_locales.dart` | ソース | サポート言語一覧 |
