|
| 1 | +# i18n 仕様書(Moore's Command Editor) |
| 2 | + |
| 3 | +本仕様はエディタ UI とプロジェクト(コマンド定義)で利用する翻訳データの構造、配置パス、読み込み規約を定義します。既存実装(frontend/src/i18n 配下)に準拠しています。 |
| 4 | + |
| 5 | +## 目的と適用範囲 |
| 6 | +- エディタ UI 固定文言(アプリ内に同梱。必要に応じて上書き可能) |
| 7 | +- プロジェクト側のコマンド/プロパティ名・説明・プレースホルダー・列挙値 |
| 8 | +- 実行時の読み込み元と優先順位、ファイル命名規則 |
| 9 | + |
| 10 | +## 実行時の挙動(概要) |
| 11 | +- 初期化: `frontend/src/i18n/config.ts` で i18next を初期化。 |
| 12 | + - 既定言語: 日本語(`lng: 'ja'`)/フォールバック: 英語(`fallbackLng: 'en'`) |
| 13 | + - React: Suspense 無効(`useSuspense: false`) |
| 14 | +- 動的ロード: `frontend/src/i18n/translationLoader.ts` の `loadTranslations()` がプロジェクト翻訳を追加ロード。 |
| 15 | + - Tauri 以外(開発/テスト): `frontend/src/sample/i18n/*.json` をフェッチ |
| 16 | + - Tauri 実行時: `projectPath/i18n/*.json` をファイル I/O で読み込み |
| 17 | +- マージ優先度: アプリ内固定リソース → プロジェクト翻訳で上書き(同一キー) |
| 18 | + |
| 19 | +## 置き場所(JSON パス) |
| 20 | +- 開発・テスト用サンプル |
| 21 | + - `frontend/src/sample/i18n/english.json` |
| 22 | + - `frontend/src/sample/i18n/japanese.json` |
| 23 | +- プロダクション(Tauri 実行時のプロジェクト) |
| 24 | + - `<projectPath>/i18n/english.json` |
| 25 | + - `<projectPath>/i18n/japanese.json` |
| 26 | + - `<projectPath>/i18n/chinese.json` |
| 27 | + - `<projectPath>/i18n/spanish.json` |
| 28 | + |
| 29 | +備考: |
| 30 | +- 実際に言語コードとして使用されるのは各 JSON の `locale` フィールドです(ファイル名は検出対象の候補)。 |
| 31 | +- `projectPath` はアプリのストアで選択されたプロジェクトルートです(`useSkitStore.getState().projectPath`)。 |
| 32 | + |
| 33 | +## JSON フォーマット(厳密定義) |
| 34 | +TypeScript による型定義(参考): |
| 35 | + |
| 36 | +```ts |
| 37 | +// 言語コードは BCP 47 推奨(例: 'en', 'ja', 'zh', 'es' など) |
| 38 | +export type LocaleCode = string; |
| 39 | + |
| 40 | +export interface TranslationFile { |
| 41 | + locale: LocaleCode; // 必須: 使用する言語コード |
| 42 | + name: string; // 必須: 言語表示名(例: "English", "日本語") |
| 43 | + translations: Record<string, string>; // 必須: 翻訳キー → 文字列 |
| 44 | +} |
| 45 | +``` |
| 46 | + |
| 47 | +必須キー: |
| 48 | +- `locale`: 例 `"en"`, `"ja"` |
| 49 | +- `name`: 例 `"English"`, `"Japanese"` |
| 50 | +- `translations`: 平坦(フラット)なキーの辞書。値はすべて文字列。 |
| 51 | + |
| 52 | +## 翻訳キー設計(命名規則) |
| 53 | +- UI 固定文言(アプリ同梱; プロジェクトで上書き可) |
| 54 | + - `editor.*`(メニュー、ツールバー、パネル、ダイアログ、バリデーション、共通ラベル) |
| 55 | + - `language.*`(言語 UI 用のラベル群) |
| 56 | + - `skitList.*` |
| 57 | +- コマンド関連(プロジェクト側が提供) |
| 58 | + - 基本: |
| 59 | + - `command.{commandId}.name` |
| 60 | + - `command.{commandId}.description` |
| 61 | + - プロパティ: |
| 62 | + - `command.{commandId}.property.{propKey}.name` |
| 63 | + - `command.{commandId}.property.{propKey}.description` |
| 64 | + - `command.{commandId}.property.{propKey}.placeholder` |
| 65 | + - 列挙値(enum がある場合): |
| 66 | + - `command.{commandId}.property.{propKey}.enum.{EnumValue}` |
| 67 | + |
| 68 | +生成ヘルパー: `generateCommandTranslationKeys(command)` が上記パターンのキー一覧を生成。 |
| 69 | + |
| 70 | +注意: |
| 71 | +- キーはドット区切りのフラット文字列。入れ子 JSON は使用しません。 |
| 72 | +- `{EnumValue}` は元定義の値と完全一致(大文字小文字・スペース含む)。 |
| 73 | + |
| 74 | +## 文字列の書式と補間 |
| 75 | +- 補間(i18next 互換 Mustache 形式): `{{name}}`, `{{count}}` など |
| 76 | + - 例: `editor.toolbar.commandAdded: "{{command}} added"` |
| 77 | +- HTML は原則含めない(エスケープに依存しない安全な表現を推奨) |
| 78 | +- 改行が必要な場合は `\n` を利用 |
| 79 | + |
| 80 | +## 言語の検出・選択 |
| 81 | +- 既定: 日本語(`ja`) |
| 82 | +- フォールバック: 英語(`en`) |
| 83 | +- 開発モードでは `localStorage.i18nextLng` を `ja` に設定(`src/main.tsx`) |
| 84 | +- 利用可能言語の一覧はロード済みリソースから算出(`getAvailableLanguages()`) |
| 85 | + |
| 86 | +## マージと優先度 |
| 87 | +1. アプリ内固定翻訳(`config.ts` の `editorTranslations`)が初期ロード |
| 88 | +2. プロジェクト翻訳(`translationLoader.ts`)を同一 `namespace: 'translation'` に追加入力 |
| 89 | +3. 同一キーが存在する場合、プロジェクト側で上書き |
| 90 | + |
| 91 | +## バリデーション・品質チェック |
| 92 | +- JSON が妥当(コメント不可・UTF-8 推奨) |
| 93 | +- `locale` が正しい(BCP 47 準拠を推奨、例: `en`, `ja`) |
| 94 | +- `translations` の値はすべて非空文字列 |
| 95 | +- 重複キーなし(同一ファイル内) |
| 96 | +- 列挙値キーの末尾 `{EnumValue}` は元データ定義と完全一致 |
| 97 | +- 必要キー(`name`, `description` 等)が網羅されていること(`generateCommandTranslationKeys` で検出可) |
| 98 | + |
| 99 | +## 拡張と追加言語 |
| 100 | +- 新しい言語を追加するには `<projectPath>/i18n/{languageName}.json` を作成し、`locale` に適切なコードを設定 |
| 101 | +- ローダーはファイル名 `english|japanese|chinese|spanish` を探索しますが、実際の適用言語は JSON 内の `locale` に従います |
| 102 | +- 追加の言語ファイル拡張を行う場合は、ローダー側の探索リスト拡張が必要です |
| 103 | + |
| 104 | +## 例(最小) |
| 105 | +`english.json` |
| 106 | +```json |
| 107 | +{ |
| 108 | + "locale": "en", |
| 109 | + "name": "English", |
| 110 | + "translations": { |
| 111 | + "command.text.name": "Text", |
| 112 | + "command.text.description": "Display dialogue", |
| 113 | + "command.text.property.body.name": "Body", |
| 114 | + "command.text.property.body.description": "Dialogue content", |
| 115 | + "command.text.property.body.placeholder": "Enter dialogue text" |
| 116 | + } |
| 117 | +} |
| 118 | +``` |
| 119 | + |
| 120 | +`japanese.json` |
| 121 | +```json |
| 122 | +{ |
| 123 | + "locale": "ja", |
| 124 | + "name": "日本語", |
| 125 | + "translations": { |
| 126 | + "command.text.name": "テキスト", |
| 127 | + "command.text.description": "セリフを表示", |
| 128 | + "command.text.property.body.name": "本文", |
| 129 | + "command.text.property.body.description": "セリフ内容", |
| 130 | + "command.text.property.body.placeholder": "セリフを入力" |
| 131 | + } |
| 132 | +} |
| 133 | +``` |
| 134 | + |
| 135 | +## 関連ソース |
| 136 | +- 設定: `frontend/src/i18n/config.ts` |
| 137 | +- ローダー: `frontend/src/i18n/translationLoader.ts` |
| 138 | +- 初期化: `frontend/src/main.tsx` |
| 139 | +- サンプル: `frontend/src/sample/i18n/*.json` |
| 140 | + |
| 141 | +## 運用メモ |
| 142 | +- プロジェクト翻訳で UI 固定キー(`editor.*` 等)を定義すると同梱文言を上書き可能。ただし、推奨はコマンド関連キーの提供。 |
| 143 | +- JSON 変更の反映にはアプリの再読み込みが必要。 |
| 144 | +- 型の観点から、`translations` は `Record<string, string>` を維持し、曖昧な `any` の使用は避けること。 |
| 145 | + |
0 commit comments