# 機能設計書 51-ロケール設定

## 概要

本ドキュメントは、Fat Free CRMにおけるユーザーロケール（言語）設定機能の設計書である。ユーザーが画面表示言語を切り替える機能について、処理仕様・データベース操作・コード構造を定義する。

### 本機能の処理概要

ユーザーが自分のアカウントで使用する画面表示言語（ロケール）を変更する機能である。ヘッダーメニューから言語を選択すると、即座に選択した言語で画面が再描画される。

**業務上の目的・背景**：Fat Free CRMは多言語対応システムであり、世界各国のユーザーが利用する可能性がある。ユーザーごとに母国語または使い慣れた言語で操作できることで、業務効率向上とユーザビリティ改善を実現する。グローバル企業や多国籍チームでの利用シーンを想定している。

**機能の利用シーン**：
- 新規ユーザーが初回ログイン後に自分の言語を設定する場面
- 多言語環境のオフィスで、異なる言語話者が同じシステムを利用する場面
- 出張や異動により言語設定を変更したい場面

**主要な処理内容**：
1. ヘッダーメニューの言語選択ドロップダウンからロケールを選択
2. JavaScript経由でサーバーにPOSTリクエストを送信
3. UsersController#redrawアクションでpreference[:locale]を更新
4. クライアント側でJavaScriptによりページをリロード
5. 新しいロケールで画面が再描画される

**関連システム・外部連携**：特になし。Ruby on RailsのI18n（国際化）機能を利用して言語切替を実現している。

**権限による制御**：ログイン済みユーザーのみが自分のロケール設定を変更可能。他のユーザーのロケール設定は変更できない。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 32 | ユーザー詳細画面（プロフィール） | 主画面 | ロケール設定変更 |

## 機能種別

設定変更処理 / UPDATE操作

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| locale | String | Yes | 選択された言語のロケールコード（例: "en-US", "ja"） | システムで定義された言語コードのみ許可 |
| id | Integer | Yes | ユーザーID（current_userと一致） | ログインユーザー自身のIDのみ許可 |

### 入力データソース

- 画面入力（ヘッダーメニューの言語選択ドロップダウン）
- JavaScriptメニューコンポーネント（crm.Menu）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| JavaScriptリダイレクト | String | `window.location.href = "/users/{id}";` 形式のリダイレクトJS |

### 出力先

- ブラウザ（JavaScriptによるページリロード）
- preferencesテーブル（ロケール設定の永続化）

## 処理フロー

### 処理シーケンス

```
1. ユーザーがヘッダーメニューの言語選択をクリック
   └─ crm.Menuコンポーネントがドロップダウンを表示
2. 言語を選択すると、on_selectコールバックが実行
   └─ POST /users/:id/redraw?locale=xx へリクエスト送信
3. UsersController#redrawが呼び出される
   └─ CanCan認可チェック（load_and_authorize_resource）
4. current_user.preference[:locale]を更新
   └─ Preferenceモデルが値をBase64エンコードしてDBに保存
5. JavaScriptレスポンスを返却
   └─ `window.location.href = "/users/{id}";` を返す
6. ブラウザがJavaScriptを実行
   └─ ユーザープロファイル画面にリダイレクト
7. 新しいロケールでページが再描画される
   └─ I18n.localeがpreference[:locale]に設定される
```

### フローチャート

```mermaid
flowchart TD
    A[言語メニュークリック] --> B[crm.Menuがドロップダウン表示]
    B --> C[言語を選択]
    C --> D[POST /users/:id/redraw]
    D --> E{CanCan認可チェック}
    E -->|認可成功| F[preference[:locale]を更新]
    E -->|認可失敗| G[403エラー]
    F --> H[preferencesテーブルに保存]
    H --> I[JavaScriptレスポンス返却]
    I --> J[ブラウザがリダイレクト実行]
    J --> K[新ロケールで画面再描画]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-51-01 | 自己設定のみ | ユーザーは自分自身のロケール設定のみ変更可能 | 常時 |
| BR-51-02 | 即時反映 | ロケール変更は即座に画面に反映される | 変更保存成功時 |
| BR-51-03 | 永続化 | ロケール設定はログアウト後も保持される | 常時 |

### 計算ロジック

特になし

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| ロケール保存 | preferences | INSERT/UPDATE | ユーザーのロケール設定を保存 |

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

#### preferences

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT/UPDATE | user_id | current_user.id | ユーザーID |
| INSERT/UPDATE | name | "locale" | プリファレンス名 |
| INSERT/UPDATE | value | Base64.encode64(locale.to_json) | ロケール値（Base64エンコード） |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 403 | 認可エラー | 他ユーザーのロケールを変更しようとした場合 | アクセス拒否画面を表示 |
| 401 | 認証エラー | 未ログイン状態でアクセス | ログイン画面にリダイレクト |

### リトライ仕様

特になし（同期処理のため）

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

単一レコードの更新のため、特別なトランザクション制御は不要。ActiveRecordの自動トランザクション管理に依存する。

## パフォーマンス要件

- レスポンス時間: 500ms以内
- 単純なPreference更新のみのため、特別なインデックスは不要

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

- CanCanによる認可制御でユーザー自身のプロファイルのみ更新可能
- CSRFトークン検証によりクロスサイトリクエストフォージェリを防止
- ロケールパラメータは直接I18nに渡されるため、許可された言語コードのみを受け付ける

## 備考

- 対応言語はconfig/localesディレクトリに定義されたYAMLファイルに依存する
- 新しい言語を追加するには、対応するYAMLファイルと翻訳データの追加が必要

---

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

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

### 推奨読解順序

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

ユーザープリファレンスの保存方法を理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | preference.rb | `app/models/users/preference.rb` | プリファレンスの保存・取得ロジック |
| 1-2 | user.rb | `app/models/users/user.rb` | ユーザーとプリファレンスの関連 |

**読解のコツ**: Preferenceモデルは`[]`と`[]=`メソッドをオーバーライドしており、ハッシュライクなアクセスを提供している。値はBase64エンコードされてDBに保存される点に注意。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | users_controller.rb | `app/controllers/users_controller.rb` | redrawアクションの実装 |

**主要処理フロー**:
1. **100-103行目**: redrawアクションでpreference[:locale]を更新し、JavaScriptでリダイレクト

#### Step 3: ビュー層を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | _languages.html.haml | `app/views/users/_languages.html.haml` | 言語メニューのJavaScript生成 |
| 3-2 | users_helper.rb | `app/helpers/users_helper.rb` | sort_by_languageヘルパーメソッド |

**主要処理フロー**:
- **14-18行目（users_helper.rb）**: 言語リストをJavaScriptメニュー項目形式に変換

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

```
ヘッダーメニュー (_languages.html.haml)
    │
    ├─ crm.Menu JavaScript
    │      └─ on_select: redraw呼び出し
    │
    └─ UsersController#redraw
           ├─ current_user.preference[:locale]=
           │      └─ Preference#[]=
           │             └─ Preference.create/update
           └─ render js: リダイレクトJS
```

### データフロー図

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

言語選択 ───▶ UsersController#redraw ───▶ preference更新
  │                    │                         │
  │                    │                         ▼
  │                    │                   preferencesテーブル
  │                    │
  │                    └─────────────────▶ JSリダイレクト
  │                                              │
  └──────────────────────────────────────────────┘
                      ページリロード（新ロケール適用）
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| users_controller.rb | `app/controllers/users_controller.rb` | コントローラー | redrawアクション |
| preference.rb | `app/models/users/preference.rb` | モデル | プリファレンス保存/取得 |
| user.rb | `app/models/users/user.rb` | モデル | ユーザーモデル、preference関連 |
| users_helper.rb | `app/helpers/users_helper.rb` | ヘルパー | 言語リスト生成 |
| _languages.html.haml | `app/views/users/_languages.html.haml` | ビュー | 言語メニューUI |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義（155-163行目） |
