# 機能設計書 101-多言語テキスト管理

## 概要

本ドキュメントは、K-Shoot MANIA(ゲーム本体)および K-Shoot Editor(譜面エディタ)における多言語テキスト管理機能の設計を記述する。Japanese / English / Korean / Simplified Chinese / Traditional Chinese の5言語テキストファイルを読み込み、UIラベルやメッセージを動的に切り替える機能である。

### 本機能の処理概要

本機能は、`lang/` ディレクトリ配下の言語テキストファイル(`{言語名}.txt`)を読み込み、アプリケーション全体のUIテキストを多言語化する。テキストファイルはキーID(例: `m01-000`, `e02-010`)と表示文字列を `|` 区切りで定義し、2次元配列 `lang` に格納される。

**業務上の目的・背景**: K-Shoot MANIAは国際的なリズムゲームコミュニティで使用されるため、複数言語での操作を可能にすることが不可欠である。日本語を既定言語としつつ、英語・韓国語・中国語(簡体字/繁体字)のユーザーにも対応する必要がある。

**機能の利用シーン**: アプリケーション起動時の初期化フェーズで言語ファイルが読み込まれ、タイトル画面・選曲画面・オプション設定画面・リザルト画面・譜面エディタのメニュー等、すべてのUI要素に適用される。オプション設定画面で言語を切り替えた場合は、設定値が `config.ini` の `currentlang` に保存され、次回起動時に反映される。

**主要な処理内容**:
1. `lang/` ディレクトリ内の `*.txt` ファイル一覧を取得し、利用可能な言語リストを構築
2. `config.ini` の `currentlang` 設定値に基づき、該当する言語テキストファイルを選択
3. テキストファイルを行単位で解析し、キーID(プレフィックス `m` はゲーム本体用、`e` はエディタ用)をインデックスに変換して `lang` 配列に格納
4. エスケープシーケンス(`\\n` を改行、`\\t` をタブに変換)を処理
5. 言語ファイルが見つからない場合のフォールバック処理(Japanese → 先頭言語 → エラー終了)

**関連システム・外部連携**: 外部システムとの連携はなし。ファイルシステム上の `lang/` ディレクトリのみを参照する。

**権限による制御**: 特になし。すべてのユーザーが同一の言語切替機能を利用可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 1 | タイトル画面 | 参照画面 | lang変数による多言語テキスト表示(メニュー項目ラベル等) |
| 3 | 曲選択画面 | 参照画面 | lang変数による各種UIラベルの多言語表示 |
| 4 | プレイ前処理画面 | 参照画面 | lang.2.11/lang.2.12/lang.2.13によるエラーダイアログの多言語メッセージ表示 |
| 6 | リザルト画面 | 参照画面 | lang変数によるリザルト画面UIラベルの多言語表示 |
| 7 | コースリザルト画面 | 参照画面 | lang変数によるコースリザルト画面UIラベルの多言語表示 |
| 8 | オプション設定画面 | 主画面 | lang.3.*変数による設定項目ラベル・値説明の多言語表示、言語切替操作 |

## 機能種別

データ読み込み / テキスト変換

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| clang | 文字列 | Yes | 現在選択されている言語名(例: "Japanese") | lang/ディレクトリに対応する.txtファイルが存在すること |
| 言語テキストファイル | テキストファイル | Yes | `{言語名}.txt` 形式の言語定義ファイル | 各行が `{キーID}|{テキスト}` 形式であること |

### 入力データソース

- `lang/` ディレクトリ配下の `*.txt` ファイル群(Japanese.txt, English.txt, Korean.txt, Simplified Chinese.txt, Traditional Chinese.txt)
- `config.ini` の `currentlang` 設定項目

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| lang | 文字列型2次元配列 | ゲーム本体: `lang(カテゴリ, インデックス)` 最大5x96。エディタ: `lang(カテゴリ, インデックス)` 最大11x96 |
| curlang | 整数 | 現在の言語インデックス(langlist配列内の位置) |
| langlist | 文字列配列 | 利用可能な言語名一覧 |

### 出力先

- メモリ上の `lang` 配列(アプリケーション全体から参照)
- `config.ini` への言語設定保存(オプション画面経由での変更時)

## 処理フロー

### 処理シーケンス

```
1. lang/ディレクトリに移動
   └─ chdir命令でlang/ディレクトリへ移動
2. 利用可能な言語ファイル一覧を取得
   └─ dirlist命令で*.txtファイルを列挙し、langlist配列に格納
3. 選択言語のファイル存在確認
   └─ exist命令でclang+".txt"の存在を確認
4. フォールバック処理（ファイルが存在しない場合）
   └─ Japanese → langlist先頭 → エラー終了の順で試行
5. 言語テキストファイルの読み込み
   └─ notesel/noteloadで全文を読み込み、conv_crlfで改行コードを統一
6. 行単位の解析ループ
   └─ 各行をnotegetで取得し、"|"でsplit、キーIDからカテゴリとインデックスを算出
7. lang配列への格納
   └─ キーIDのプレフィックス(m/e)とカテゴリ番号・インデックス番号でlang配列にテキストを格納
8. エスケープシーケンス変換
   └─ \\nを改行、\\tをタブに変換
9. フォント名の解析（lang.0.0にセミコロン区切りで含まれる場合）
   └─ fxfont変数にフォント名を設定
10. 元ディレクトリに復帰
    └─ chdir ".."で元のディレクトリに戻る
```

### フローチャート

```mermaid
flowchart TD
    A[開始: lang/ディレクトリに移動] --> B[dirlistで*.txt一覧取得]
    B --> C{clang+'.txt'が存在?}
    C -->|Yes| F[noteloadでファイル読み込み]
    C -->|No| D{Japanese.txt存在?}
    D -->|Yes| E[clang='Japanese']
    D -->|No| D2{langlist先頭が存在?}
    D2 -->|Yes| E2[clang=langlist.0]
    D2 -->|No| ERR[エラーダイアログ表示 → 終了]
    E --> F
    E2 --> F
    F --> G[conv_crlfで改行コード統一]
    G --> H[行単位ループ開始]
    H --> I{'|'でsplit → キーID + テキスト}
    I --> J{プレフィックスが'm'か'e'か?}
    J -->|m: ゲーム本体| K[lang.カテゴリ.インデックスに格納]
    J -->|e: エディタ| K
    J -->|その他| H2[次の行へ]
    K --> L[エスケープシーケンス変換]
    L --> H2
    H2 --> M{全行処理完了?}
    M -->|No| H
    M -->|Yes| N[元ディレクトリに復帰]
    N --> O[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-101-01 | 言語ファイル命名規則 | 言語ファイルは `{言語名}.txt` の形式でlang/ディレクトリに配置される | 常時 |
| BR-101-02 | キーIDフォーマット(ゲーム本体) | `mXX-YYY` 形式。XXはカテゴリ(00-04)、YYYはインデックス(000-095) | ゲーム本体(kshootmania.hsp)での使用時 |
| BR-101-03 | キーIDフォーマット(エディタ) | `eXX-YYY` 形式。XXはカテゴリ(00-10)、YYYはインデックス(000-095) | エディタ(kshooteditor.hsp)での使用時 |
| BR-101-04 | フォールバック順序 | 指定言語 → Japanese → langlist先頭 → エラー終了 | 言語ファイルが見つからない場合 |
| BR-101-05 | デフォルト言語 | 初期値は "Japanese" | config.iniにcurrentlang設定が存在しない場合 |

### 計算ロジック

キーIDからlang配列インデックスへの変換:
- カテゴリ = `int(strmid(キーID, 1, 2))` (キーIDの2-3文字目を整数変換)
- インデックス = `int(strmid(キーID, 4, 3))` (キーIDの5-7文字目を整数変換)
- ゲーム本体: カテゴリ < 5 かつ >= 0、インデックス < 96 かつ >= 0
- エディタ: カテゴリ < 11 かつ >= 0、インデックス < 96 かつ >= 0

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

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

該当なし(ファイルベースのテキスト読み込みのみ)

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 致命的エラー | lang/ディレクトリに言語ファイルが1つも存在しない | "Language file does not exist."ダイアログを表示しアプリケーション終了 |
| - | フォールバック | 指定言語のファイルが存在しない | Japaneseにフォールバック、それも存在しなければlanglist先頭を使用 |

### リトライ仕様

リトライ処理はなし。フォールバック処理により代替言語を使用する。

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

該当なし(読み取り専用処理)

## パフォーマンス要件

起動時に1回のみ言語ファイルを読み込むため、特段のパフォーマンス要件はなし。

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

言語ファイルは外部テキストファイルであるため、不正な文字列が挿入された場合にUIが崩れる可能性がある。ただし、言語ファイルはアプリケーション同梱のため、ユーザーが意図的に編集しない限りリスクは低い。

## 備考

- ゲーム本体では `m` プレフィックスのキーのみ読み込み(カテゴリ0-4)、エディタでは `e` プレフィックスのキーのみ読み込み(カテゴリ0-10)と、アプリケーションごとに異なるプレフィックスで管理されている
- 同一の言語テキストファイルにゲーム本体用(`m`)とエディタ用(`e`)の両方のテキストが含まれている
- 言語テキストファイルはShift-JISエンコーディングで格納されている(Japanese.txt等)

---

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

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

### 推奨読解順序

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

言語テキストファイルのフォーマットと、格納先のlang配列の構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | English.txt | `lang/English.txt` | キーIDの命名規則(`mXX-YYY`/`eXX-YYY`)と `\|` 区切りのテキスト定義形式を確認する |
| 1-2 | Japanese.txt | `lang/Japanese.txt` | 日本語テキストの定義。Shift-JISエンコーディングであることに注意 |

**読解のコツ**: キーIDの先頭文字 `m` はゲーム本体(MANIA)用、`e` はエディタ(Editor)用を意味する。カテゴリ番号は画面/機能グループに対応する(例: m00=システム、m01=選曲画面、m02=プレイ画面、m03=オプション、m04=ネットワーク)。

#### Step 2: エントリーポイントを理解する(ゲーム本体)

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | kshootmania.hsp | `kshootmania.hsp` | `*refreshlang` サブルーチン(1246-1297行目)が言語読み込みの主処理 |

**主要処理フロー**:
1. **1246行目**: `*refreshlang` サブルーチン開始
2. **1248行目**: `chdir dir_default+"\\lang"` で言語ファイルディレクトリへ移動
3. **1249行目**: `dirlist langliststr,"*.txt",1` で利用可能な言語ファイル一覧を取得
4. **1259行目**: `exist clang+".txt"` で指定言語ファイルの存在確認
5. **1260-1273行目**: フォールバック処理(Japanese → langlist先頭 → エラー終了)
6. **1275-1278行目**: `curlang` の算出(langlist配列内の現在言語の位置)
7. **1279行目**: `sdim lang,256,5,96` でlang配列を初期化(5カテゴリ x 96エントリ)
8. **1281-1282行目**: `notesel`/`noteload` で言語ファイルを読み込み
9. **1283行目**: `conv_crlf langstr,TYPE_CRLF` で改行コード統一
10. **1284-1295行目**: 行単位ループで `m` プレフィックスのキーを解析しlang配列に格納

#### Step 3: エントリーポイントを理解する(エディタ)

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | kshooteditor.hsp | `kshooteditor.hsp` | 初期化時の言語読み込み処理(588-632行目) |

**主要処理フロー**:
1. **588行目**: `chdir "./lang"` で言語ファイルディレクトリへ移動
2. **589行目**: `dirlist langliststr,"*.txt",1` でファイル一覧取得
3. **591-606行目**: clangの検証とフォールバック処理
4. **607行目**: `sdim lang,256,11,96` でlang配列初期化(11カテゴリ x 96エントリ)
5. **609-610行目**: 言語ファイル読み込み
6. **612-624行目**: 行単位ループで `e` プレフィックスのキーを解析しlang配列に格納
7. **620-621行目**: エスケープシーケンス変換(`\\n` → 改行、`\\t` → タブ)
8. **627-632行目**: フォント名の解析(lang.0.0にセミコロン区切りで含まれる場合)

#### Step 4: 言語設定の保存と読み込み

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | kshootmania.hsp | `kshootmania.hsp` | config.iniからの`currentlang`読み込み(583-584行目付近) |
| 4-2 | kshooteditor.hsp | `kshooteditor.hsp` | config.iniからの`currentlang`読み込み(541-542行目) |

**主要処理フロー**:
- **kshootmania.hsp 345行目**: `clang="Japanese"` でデフォルト値設定
- **kshootmania.hsp 583-584行目**: `if(StartsWith(confstr.cnt, "currentlang=")):clang=strmid(confstr.cnt,12,64)` でconfig.iniから読み込み
- **kshooteditor.hsp 522行目**: `clang="Japanese"` でデフォルト値設定
- **kshooteditor.hsp 541-542行目**: config.iniからのcurrentlang読み込み

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

```
kshootmania.hsp (メインループ)
    │
    ├─ *refreshlang (言語読み込みサブルーチン)
    │      ├─ chdir → lang/ ディレクトリ移動
    │      ├─ dirlist → 言語ファイル一覧取得
    │      ├─ exist → 言語ファイル存在確認
    │      ├─ notesel/noteload → ファイル読み込み
    │      ├─ conv_crlf → 改行コード統一
    │      ├─ split → 行の分割・解析
    │      └─ replace → エスケープシーケンス変換
    │
    └─ lang配列 → 各画面(title/select/play/result/option等)が参照

kshooteditor.hsp (エディタ初期化)
    │
    ├─ chdir "./lang" → 言語ディレクトリ移動
    ├─ dirlist → 言語ファイル一覧取得
    ├─ notesel/noteload → ファイル読み込み
    ├─ split → 行の分割・解析
    ├─ replace → エスケープシーケンス変換
    └─ lang配列 → メニュー・ダイアログ・ステータスバー等が参照
```

### データフロー図

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

lang/*.txt        ───────▶  *refreshlang / 初期化処理   ───▶  lang配列(メモリ)
(言語テキストファイル)         (ファイル解析・配列格納)           (UIテキスト参照元)

config.ini        ───────▶  currentlang解析             ───▶  clang変数
(currentlang設定)             (言語名取得)                      (現在の言語名)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Japanese.txt | `lang/Japanese.txt` | 言語テキスト | 日本語テキスト定義ファイル |
| English.txt | `lang/English.txt` | 言語テキスト | 英語テキスト定義ファイル |
| Korean.txt | `lang/Korean.txt` | 言語テキスト | 韓国語テキスト定義ファイル |
| Simplified Chinese.txt | `lang/Simplified Chinese.txt` | 言語テキスト | 簡体字中国語テキスト定義ファイル |
| Traditional Chinese.txt | `lang/Traditional Chinese.txt` | 言語テキスト | 繁体字中国語テキスト定義ファイル |
| kshootmania.hsp | `kshootmania.hsp` | ソース | ゲーム本体の言語読み込み処理(*refreshlang) |
| kshooteditor.hsp | `kshooteditor.hsp` | ソース | エディタの言語読み込み処理(初期化部分) |
