はじめに
前回(第 2 回)では、Display JSON ファイルと DisplayAccessor クラス群の詳細を見てきました。第 3 回では CodeDefiner が定義データからサーバーサイド・クライアントサイドのコードをどのように自動生成するのかを詳しくみていきます。
| 回 | テーマ |
|---|---|
| 第 1 回 | 多言語アーキテクチャ概要編 — 全体設計と構成要素 |
| 第 2 回 | Display 定義と DisplayAccessor 編 — JSON 定義ファイルの構造とアクセサクラス |
| 第 3 回(本記事) | CodeDefiner によるコード生成編 — テンプレートから C# / TypeScript を自動生成する仕組み |
| 第 4 回 | 言語解決メカニズム編 — ユーザー言語の決定ロジックとフォールバック |
| 第 5 回 |
クライアントサイド多言語対応編 — TypeScript での言語切り替えと $p.display()
|
バージョン 1.5.1.0 を対象にしています
CodeDefiner とは
CodeDefiner は、プリザンターのビルドプロセスで使われるコード生成ツールです。Display JSON やカラム定義などの定義データを入力として、C# や TypeScript のソースコードを自動生成します。
CodeDefiner の起動
CodeDefiner はコマンドライン引数によって動作モードを切り替えます。
public static void Main(string[] args)
{
Initializer.Initialize(
path,
assemblyVersion: Assembly.GetExecutingAssembly()
.GetName().Version.ToString(),
setLanguage: argHash.Get("l"),
setTimeZone: argHash.Get("z"),
codeDefiner: true,
setSaPassword: argHash.ContainsKey("s"),
setRandomPassword: argHash.ContainsKey("r"));
switch (action)
{
case "rds":
ConfigureDatabase(...);
CreateDefinitionAccessorCode();
CreateMvcCode(target); // ← ここで Display コード生成
break;
}
}
rds アクションを実行すると、データベース設定に加えて CreateMvcCode() が呼ばれ、MVC のコード生成が行われます。この中で Display 関連のコードも生成されます。
コード生成の全体像
コード生成は、コード定義テンプレートに従って行われます。
コード定義テンプレートの仕組み
コード定義は App_Data/Definitions/Definition_Code/ に配置された JSON ファイルとテキストテンプレートのペアで構成されています。
テンプレート定義ファイル(JSON)
{
"Id": "Displays_Parts",
"Source": "Mvc",
"OutputPath": "Libraries\\Responses\\Displays.cs",
"DisplayLanguages": true
}
| フィールド | 説明 |
|---|---|
Id |
テンプレートの識別子。{Id}_Body.txt というテンプレートファイルと対応 |
Source |
生成先のプロジェクト種別 |
OutputPath |
生成されるファイルのパス |
DisplayLanguages |
true の場合、全言語バリアントのコードを生成 |
テンプレートファイル(テキスト)
テンプレートファイルにはプレースホルダが含まれており、CodeDefiner がこれを実際の値に置換します。
public static string #DisplayId#(
Context context,
params string[] data)
{
return Get(
context: context,
id: "#DisplayId#",
data: data);
}
#DisplayId# がプレースホルダで、各 Display の Id(と言語サフィックス)に置換されます。
Display コード生成の詳細
コード生成の中核は Implem.CodeDefiner/Functions/AspNetMvc/CSharp/Parts/Display.cs にあります。
SetCodeCollection メソッド
全 Display を走査し、各言語バリアントのコードを生成するメインメソッドです。
internal static void SetCodeCollection(
CodeDefinition codeDefinition,
List<string> codeCollection,
DataContainer dataContainer)
{
var code = string.Empty;
DisplayAccessor.Displays.DisplayHash
.Select(o => o.Value)
.ForEach(display => display.Languages
.Where(element =>
!CheckExclude(codeDefinition, display, element))
.ForEach(element =>
Creators.SetCodeCollection(
ref code,
codeCollection,
codeDefinition,
dataContainer,
() => ReplaceCode(
ref code, display, element))));
}
このメソッドは以下の流れで動作します。
-
DisplayAccessor.Displays.DisplayHashから全 Display を取得 - 各 Display の
Languages配列をイテレート -
CheckExclude()でフィルタリング -
ReplaceCode()でプレースホルダを実際の値に置換
CheckExclude メソッド(フィルタリング)
特定の条件に合致しない Display / 言語バリアントを除外するメソッドです。
private static bool CheckExclude(
CodeDefinition codeDefinition,
DisplayAccessor.Display display,
DisplayAccessor.DisplayElement element)
{
if (!codeDefinition.DisplayLanguages
&& !element.Language.IsNullOrEmpty())
return true;
if (!codeDefinition.DisplayType.IsNullOrEmpty()
&& !codeDefinition.DisplayType.Split(',')
.Contains(display.Type.ToString()))
return true;
if (codeDefinition.ClientScript
&& display.ClientScript != true)
return true;
return false;
}
3 つのフィルタ条件があります。
| 条件 | 除外対象 | 説明 |
|---|---|---|
DisplayLanguages が false
|
デフォルト以外の言語 | 言語バリアントを生成しない場合 |
DisplayType 指定あり |
指定外の Type | 特定種別のみ生成する場合 |
ClientScript が true
|
ClientScript != true |
クライアントサイド専用の Display のみ |
ReplaceCode メソッド(プレースホルダ置換)
テンプレート内のプレースホルダを実際の値に置換するメソッドです。
private static void ReplaceCode(
ref string code,
DisplayAccessor.Display display,
DisplayAccessor.DisplayElement element)
{
foreach (var placeholder in code.RegexValues(
CodePatterns.ReplacementPlaceholder))
{
switch (placeholder)
{
case "DisplayId":
code = code.Replace(
"#DisplayId#",
display.Id + (!element.Language.IsNullOrEmpty()
? "_" + element.Language
: string.Empty));
break;
case "DisplayContent":
code = code.Replace(
"#DisplayContent#", element.Body);
break;
case "DisplayCssClass":
code = code.Replace(
"#DisplayCssClass#", CssClass(display.Type));
break;
case "DisplayContentEncoded":
code = code.Replace(
"#DisplayContentEncoded#",
HttpUtility.HtmlEncode(element.Body));
break;
}
}
}
プレースホルダの一覧
| プレースホルダ | 置換内容 | 例 |
|---|---|---|
#DisplayId# |
Display.Id + 言語サフィックス |
Confirm、Confirm_ja
|
#DisplayContent# |
DisplayElement.Body |
確認 |
#DisplayCssClass# |
Type に対応する CSS クラス | alert-confirm |
#DisplayContentEncoded# |
HTML エンコードされた Body | Bestätigen |
#DisplayId# の置換ロジックに注目してください。言語が指定されている場合は _ + 言語コードをサフィックスとして付加します。つまり、1 つの Display に対して最大 8 つ(デフォルト + 7 言語)のメソッドが生成されます。
生成されるコードの例
サーバーサイド(Displays.cs)
CodeDefiner が生成する Libraries/Responses/Displays.cs は約 28,000 行のファイルです。
// --- GetDisplayHash: フラット化された DisplayHash の構築 ---
public static Dictionary<string, string> DisplayHash = GetDisplayHash();
private static Dictionary<string, string> GetDisplayHash()
{
var data = new Dictionary<string, string>();
DisplayAccessor.Displays.DisplayHash.ForEach(display =>
display.Value.Languages.ForEach(element =>
data.Add(
display.Key + (!element.Language.IsNullOrEmpty()
? "_" + element.Language
: string.Empty),
element.Body)));
return data;
}
// --- 言語対応の Get メソッド ---
public static string Get(
Context context, string id, params string[] data)
{
return Get(
id: id,
language: context.Language,
data: data);
}
public static string Get(
string id, string language, params string[] data)
{
var screen = id;
var key = id + "_" + language;
if (DisplayHash.ContainsKey(key))
screen = DisplayHash[key];
else if (DisplayHash.ContainsKey(id))
screen = DisplayHash[id];
return data?.Any() == true
? screen.Params(data)
: screen;
}
// --- 自動生成されたラッパーメソッド(1,255 × 8 言語分) ---
public static string Confirm(
Context context, params string[] data)
{
return Get(
context: context,
id: "Confirm",
data: data);
}
public static string Confirm_ja(
Context context, params string[] data)
{
return Get(
context: context,
id: "Confirm_ja",
data: data);
}
// ... Confirm_zh, Confirm_de, Confirm_ko, Confirm_es, Confirm_vn
自動生成されるラッパーメソッドは、Displays.Confirm(context) のように使えます。内部では Get(context, "Confirm") を呼び出し、context.Language に基づいて適切な言語の文字列を返します。
CSS クラスのマッピング
Display Type に応じた CSS クラスも自動生成されます。
private static string CssClass(DisplayAccessor.Displays.Types type)
{
return type switch
{
DisplayAccessor.Displays.Types.Success => "alert-success",
DisplayAccessor.Displays.Types.Information => "alert-information",
DisplayAccessor.Displays.Types.Warning => "alert-warning",
DisplayAccessor.Displays.Types.Error => "alert-error",
DisplayAccessor.Displays.Types.Confirmation => "alert-confirm",
_ => null
};
}
コード生成の流れまとめ
1 つの Display(Confirm.json)のコード生成の流れを追ってみましょう。
まとめ
本記事では、CodeDefiner による多言語コード生成の仕組みを見てきました。
- CodeDefiner は
rdsアクションで Display 関連のコードを生成する - コード定義テンプレート(JSON + テキスト)のプレースホルダを Display データで置換してコードを生成する
-
#DisplayId#は言語サフィックス付きで展開される(例:Confirm、Confirm_ja) -
CheckExclude()によってDisplayLanguages/DisplayType/ClientScriptの条件で出力対象を制御する - 生成される
Displays.csは約 28,000 行に及び、全 Display × 全言語のラッパーメソッドを含む
次回は、ユーザーの言語がどのように決定されるかを詳しく見ていきます。