多言語の言語定義を設定ファイルで管理する(改修案)
本体の標準機能ではありません
このページは本体を改修する場合の設計メモです。前提にした現行の実装は 1.5.8.1 です。
プリザンターの 7 言語(en・zh・ja・de・ko・es・vn)は、表示文字列の Display JSON こそ Languages 配列で言語を増やせる形ですが、それ以外はソースのあちこちに言語コードが固定で書かれています。新しい言語を足すにはコードの変更と CodeDefiner の実行が要ります。ここでは言語の定義を 1 つの JSON にまとめ、ファイルの追加だけで言語を足せるようにする改修を、段階を分けて整理します。
現行の仕組み(Display JSON・DisplayHash・言語の決まり方)は 多言語対応の実装 を参照してください。
現行で言語コードが固定されている箇所
図を読み込み中…
根本は、カラム定義の言語別ラベルが LabelText_en・LabelText_zh のように言語コードをプロパティ名に含む形になっていることです。言語を足すことがスキーマ(プロパティ)の追加になり、Def.cs の再生成と Initializer.cs の手修正が連鎖します。各箇所のファイルと行は 多言語対応の実装の「実践的なポイント」 にまとめています。
改修の方針
| 方針 | 内容 |
|---|---|
| 言語の定義を 1 か所に | 対応言語の一覧を 1 つの JSON で持ち、すべてのコードがそれを見る |
| プロパティ名から言語を外す | LabelText_xx の固定プロパティをやめ、Dictionary<string, string> にする |
| フォールバックをそろえる | 見つからなければ英語 → DefaultLanguage の順 |
| Display JSON はそのまま | App_Data/Displays/*.json の Languages 配列は今の形を使う |
Languages.json
App_Data/Parameters/Languages.json を新設します。
{
"Languages": [
{
"Code": "en",
"Name": "English",
"CultureInfo": "en-US",
"AcceptLanguageMapping": ["en"],
"CurrencySymbol": "$",
"CurrencyPattern": "prefix",
"DateFormat": "M/d/Y",
"DatePickerLocale": null,
"Default": true
},
{
"Code": "ja",
"Name": "Japanese",
"CultureInfo": "ja-JP",
"AcceptLanguageMapping": ["ja"],
"CurrencySymbol": "¥",
"CurrencyPattern": "prefix",
"DateFormat": "Y/m/d",
"DatePickerLocale": "ja"
},
{
"Code": "vn",
"Name": "Vietnamese",
"CultureInfo": "vi-VN",
"AcceptLanguageMapping": ["vi", "vn"],
"CurrencySymbol": "₫",
"CurrencyPattern": "suffix",
"DateFormat": "d/m/Y"
}
]
}(zh・de・ko・es も同じ形で並べます。)AcceptLanguageMapping に vi を入れておけば、今 switch で書いている vi → vn の変換も設定で表せます。
段階ごとの改修
Phase 1: サーバー側の言語設定をまとめる
| 対象 | 今 | 改修後 |
|---|---|---|
ParameterAccessor/Parts/Languages.cs(新規) | なし | Languages.json を読むクラス |
ParameterAccessor/Parameters.cs | なし | Parameters.Languages を足す |
Context.SessionLanguage() | switch で 6 言語と vi → vn | AcceptLanguageMapping から作った辞書で引く |
Context.CultureInfoCurrency() | switch で 7 言語 | CultureInfo フィールドから作る |
MultilingualLabelExportImport | SupportedLanguages の固定リスト | Parameters.Languages から作る |
Initializer.SetLanguage() | フォールバックの "en" が固定 | Default: true の言語を使う |
// SessionLanguage() の Accept-Language 判定の置き換えイメージ
private static Dictionary<string, string> acceptLanguageMap;
private static Dictionary<string, string> AcceptLanguageMap
=> acceptLanguageMap ??= Parameters.Languages.Languages
.SelectMany(l => l.AcceptLanguageMapping
.Select(m => new { Key = m, Value = l.Code }))
.ToDictionary(x => x.Key, x => x.Value);
// ...
var lang = HttpAcceptLanguage()?.Split_1st('-');
language = lang != null && AcceptLanguageMap.TryGetValue(lang, out var mapped)
? mapped
: Parameters.Service?.DefaultLanguage;Phase 1 だけでも、Context と CSV の言語一覧の固定は外れます。ただしカラムのラベルは LabelText_xx のままなので、表示文字列(Display JSON)は新しい言語にできても、テーブルの項目名の新しい言語にはまだコードの変更が要ります。
Phase 2: カラム定義を辞書にする
| 対象 | 今 | 改修後 |
|---|---|---|
__ColumnSettings.json | LabelText_en 〜 LabelText_vn を個別に定義 | LabelTexts(Dictionary<string, string>) |
Def.cs の ColumnDefinition | LabelText_xx フィールド | LabelTexts にまとめる |
Initializer.SetDisplayAccessor() | 7 言語の匿名型を手で組む | LabelTexts を回して DisplayElement を作る |
Definition_Column/*.json | LabelText_xx プロパティ | "LabelTexts": { "ja": "言語", "en": "Language" } に移行 |
Displays_Body.txt | "_ja" を固定 | Parameters.Languages から生成 |
| CodeDefiner のテンプレート | 固定フィールド前提 | 辞書前提の生成に変える |
{
"Id": "Users_Language",
"LabelTexts": {
"ja": "言語",
"en": "Language",
"zh": "语言",
"de": "Sprache",
"ko": "언어",
"es": "Idioma",
"vn": "Ngôn ngữ"
}
}影響範囲がとても大きいので、LabelText_xx → LabelTexts の移行ツールを用意し、しばらくは両方を読めるようにします。
Phase 3: フロントエンド
| 対象 | 今 | 改修後 |
|---|---|---|
validator.js | 通貨の書式が switch | ページに埋め込んだ言語設定から組み立てる |
jqueryui.js | case 'ja' だけ日時ピッカーを日本語化 | DatePickerLocale を使う |
| ページ | 言語は hidden の #Language だけ | Languages.json の通貨・日付の設定を JSON で埋め込む |
改修後に言語を足す手順
図を読み込み中…
Display JSON を言語単位のファイルにするか
1.5.8.1 の App_Data/Displays/ は 1 表示文字列 1 ファイルで、1,353 ファイルあります。言語を足すたびに全ファイルを書き換えることになるため、言語ごとのファイルにする案も比べます。
App_Data/Displays/
├── _meta.json ← Id ごとの Type・ClientScript
├── en.json ← { "Add": "Add", "Cancel": "Cancel", ... }
├── ja.json ← { "Add": "追加", "Cancel": "キャンセル", ... }
└── ...| 観点 | ID 単位(現行) | 言語単位 |
|---|---|---|
| ファイル数 | 約 1,350 | 言語数 + 1 |
| 新しい言語の追加 | 全ファイルに要素を足す | ファイルを 1 つ足す |
| 新しい表示文字列の追加 | ファイルを 1 つ足す | 全言語のファイルとメタを直す |
| Git での競合 | 起きにくい(ID ごとに別ファイル) | 起きやすい(同じ言語ファイルに集中) |
| 訳の抜け漏れ確認 | 全ファイルを走査 | en.json と対象言語のキーを比べる |
| 翻訳ツールとの連携 | しにくい | しやすい |
| 起動時の読み込み | ファイルを約 1,350 回開く | 言語数 + 1 回 |
| 改修の要否 | 不要 | Initializer.DisplayHash() の読み込みを変える |
両方の良さを取るなら、ベースの訳は言語単位(languages/{言語コード}.json)で持ち、ID 単位のファイル(overrides/*.json)で個別に上書きできるハイブリッドにします。読み込みは「言語ファイル → 上書きファイル → _meta.json で Type・ClientScript を付ける」の順です。Phase 1〜3 の Languages.json と組み合わせれば、言語の追加は Languages.json への追記と言語ファイル 1 つの追加で済みます。
どれを選ぶかの目安は次のとおりです。
| 重視すること | 向く方式 |
|---|---|
| 言語の追加・翻訳者との作業 | 言語単位かハイブリッド |
| 表示文字列の追加・削除が多い、CodeDefiner の改修を減らしたい | ID 単位(現行のまま) |
| 起動の速さ | 言語単位 |
| 既存の運用との互換 | ハイブリッド |