0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

プリザンターの多言語対応の実装を詳しくみてみる[第3回:CodeDefinerによるコード生成編]

0
Posted at

はじめに

前回(第 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 はコマンドライン引数によって動作モードを切り替えます。

Implem.CodeDefiner/Starter.cs
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)

App_Data/Definitions/Definition_Code/Displays_Parts.json
{
    "Id": "Displays_Parts",
    "Source": "Mvc",
    "OutputPath": "Libraries\\Responses\\Displays.cs",
    "DisplayLanguages": true
}
フィールド 説明
Id テンプレートの識別子。{Id}_Body.txt というテンプレートファイルと対応
Source 生成先のプロジェクト種別
OutputPath 生成されるファイルのパス
DisplayLanguages true の場合、全言語バリアントのコードを生成

テンプレートファイル(テキスト)

テンプレートファイルにはプレースホルダが含まれており、CodeDefiner がこれを実際の値に置換します。

App_Data/Definitions/Definition_Code/Displays_Parts_Body.txt
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 を走査し、各言語バリアントのコードを生成するメインメソッドです。

Implem.CodeDefiner/Functions/AspNetMvc/CSharp/Parts/Display.cs
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))));
}

このメソッドは以下の流れで動作します。

  1. DisplayAccessor.Displays.DisplayHash から全 Display を取得
  2. 各 Display の Languages 配列をイテレート
  3. CheckExclude() でフィルタリング
  4. ReplaceCode() でプレースホルダを実際の値に置換

CheckExclude メソッド(フィルタリング)

特定の条件に合致しない Display / 言語バリアントを除外するメソッドです。

Implem.CodeDefiner/Functions/AspNetMvc/CSharp/Parts/Display.cs
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 メソッド(プレースホルダ置換)

テンプレート内のプレースホルダを実際の値に置換するメソッドです。

Implem.CodeDefiner/Functions/AspNetMvc/CSharp/Parts/Display.cs
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&auml;tigen

#DisplayId# の置換ロジックに注目してください。言語が指定されている場合は _ + 言語コードをサフィックスとして付加します。つまり、1 つの Display に対して最大 8 つ(デフォルト + 7 言語)のメソッドが生成されます。

生成されるコードの例

サーバーサイド(Displays.cs)

CodeDefiner が生成する Libraries/Responses/Displays.cs は約 28,000 行のファイルです。

Implem.Pleasanter/Libraries/Responses/Displays.cs(自動生成)
// --- 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 クラスも自動生成されます。

Implem.CodeDefiner/Functions/AspNetMvc/CSharp/Parts/Display.cs
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 × 全言語のラッパーメソッドを含む

次回は、ユーザーの言語がどのように決定されるかを詳しく見ていきます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?