1
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?

プリザンターの多言語対応の実装を詳しくみてみる[第1回:多言語アーキテクチャ概要編]

1
Posted at

はじめに

プリザンターは 7 つの言語(英語・日本語・中国語・ドイツ語・韓国語・スペイン語・ベトナム語)をサポートしています。ユーザーが言語を切り替えると、画面上のラベルやメッセージが即座にその言語で表示されます。

この多言語対応はどのような仕組みで実現されているのでしょうか。本連載では、プリザンターの多言語対応(i18n)の内部実装を 5 回に分けて詳しくみていきます。

テーマ
第 1 回(本記事) 多言語アーキテクチャ概要編 — 全体設計と構成要素
第 2 回 Display 定義と DisplayAccessor 編 — JSON 定義ファイルの構造とアクセサクラス
第 3 回 CodeDefiner によるコード生成編 — テンプレートから C# / TypeScript を自動生成する仕組み
第 4 回 言語解決メカニズム編 — ユーザー言語の決定ロジックとフォールバック
第 5 回 クライアントサイド多言語対応編 — TypeScript での言語切り替えと $p.display()

バージョン 1.5.1.0 を対象にしています

対応言語一覧

プリザンターが標準でサポートしている言語は以下の 7 つです。

言語コード 言語名 言語名(現地語)
en 英語 English
ja 日本語 日本語
zh 中国語 中文
de ドイツ語 Deutsch
ko 韓国語 한국어
es スペイン語 Español
vn ベトナム語 Tiếng Việt

この一覧は Users_Language カラムの ChoicesText で定義されています。

Implem.Pleasanter/App_Data/Definitions/Definition_Column/Users_Language.json
{
    "Id": "Users_Language",
    "ColumnName": "Language",
    "ChoicesText": "en,English\nzh,Chinese\nja,Japanese\nde,German\nko,Korean\nes,Spanish\nvn,Vietnamese",
    "Default": "ja"
}

ベトナム語のコードは HTTP の Accept-Language ヘッダーでは vi ですが、プリザンター内部では vn として扱われます。この変換は Context.SessionLanguage() メソッドで行われています。

多言語アーキテクチャの全体像

プリザンターの多言語対応は 3 つの層で構成されています。

それぞれの層の役割を見てみましょう。

定義層

多言語テキストの原データを管理する層です。

コンポーネント パス 役割
Display JSON ファイル App_Data/Displays/*.json UI に表示する文字列を 7 言語分定義
カラム定義 App_Data/Definitions/Definition_Column/*.json テーブルカラムのラベルを多言語定義
コード定義テンプレート App_Data/Definitions/Definition_Code/*.json CodeDefiner が生成するコードのテンプレート

生成層

CodeDefiner ツールが定義層のデータを読み取り、サーバーサイドとクライアントサイドのコードを自動生成する層です。

  • 入力: Display JSON + カラム定義 + コード定義テンプレート
  • 出力: Libraries/Responses/Displays.cs(約 28,000 行)や display.ts などの自動生成コード

実行層

アプリケーション起動時に Display JSON を読み込んで DisplayHash を構築し、リクエストごとにユーザーの言語設定に基づいて表示テキストを解決する層です。

Display JSON の概要

多言語テキストの最小単位は、App_Data/Displays/ ディレクトリに配置された JSON ファイルです。1 ファイルにつき 1 つの表示文字列を定義し、全部で約 1,255 ファイルが存在します。

たとえば「確認」ボタンのテキストは以下のように定義されています。

Implem.Pleasanter/App_Data/Displays/Confirm.json
{
    "Id": "Confirm",
    "Type": 310,
    "Languages": [
        { "Body": "Confirm" },
        { "Language": "zh", "Body": "确认" },
        { "Language": "ja", "Body": "確認" },
        { "Language": "de", "Body": "Bestätigen" },
        { "Language": "ko", "Body": "확인" },
        { "Language": "es", "Body": "Confirmar" },
        { "Language": "vn", "Body": "Xác nhận" }
    ]
}

ここで注目すべきポイントが 2 つあります。

  1. デフォルト言語(英語)には Language プロパティがないLanguages 配列の先頭要素は Language を持たず、これがフォールバック(既定値)として扱われます
  2. Type フィールドで用途を分類している — メッセージの種別に応じて Type が設定されています

Display Type(メッセージ種別)

Type フィールドの値と意味は以下のとおりです。

名称 用途 CSS クラス
110 Normal 通常のラベル・テキスト
120 Date 日付関連テキスト
130 DateFormat 日付フォーマット
210 Success 成功メッセージ alert-success
220 Information 情報メッセージ alert-information
230 Warning 警告メッセージ alert-warning
240 Error エラーメッセージ alert-error
310 Confirmation 確認ダイアログ alert-confirm
410 Validation バリデーション

カラム定義の多言語ラベル

テーブルカラムのラベルは Definition_Column/*.json で多言語定義されています。

App_Data/Definitions/Definition_Column/Users_Language.json
{
    "Id": "Users_Language",
    "ModelName": "User",
    "TableName": "Users",
    "ColumnName": "Language",
    "LabelText": "言語",
    "LabelText_en": "Language",
    "LabelText_zh": "语言",
    "LabelText_de": "Sprache",
    "LabelText_ko": "언어",
    "LabelText_es": "Idioma",
    "LabelText_vn": "Ngôn ngữ"
}

LabelText が日本語(主キー)で、LabelText_en / LabelText_zh / LabelText_de / LabelText_ko / LabelText_es / LabelText_vn が各言語のラベルです。これらのラベルは初期化時に Display JSON と同じ DisplayHash に統合されます。

言語解決の概要

ユーザーの言語は Context.Language プロパティに保持されます。解決の優先順位は以下のとおりです。

優先度 条件 取得元
1(最高) 認証済みユーザー UserModel.Language(DB の Users.Language カラム)
2 クエリ文字列指定 URL パラメータ ?Language=ja
3 セッション保存 前回のリクエストで保存された言語
4 ブラウザ設定 HTTP Accept-Language ヘッダーを解析
5(最低) システムデフォルト Parameters.Service.DefaultLanguage

表示テキスト取得の流れ

言語が決定されると、Displays.Get() メソッドで表示テキストを取得します。

Implem.Pleasanter/Libraries/Responses/Displays.cs
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;
}

キーの構造はシンプルです。

キー形式 意味
{DisplayId} Confirm デフォルト(英語)
{DisplayId}_{Language} Confirm_ja 言語固有テキスト

たとえば context.Language"ja" の場合、まず Confirm_ja を探し、見つかれば "確認" を返します。見つからなければ Confirm のデフォルト値 "Confirm" を返します。

まとめ

プリザンターの多言語対応は、以下の 3 層構造で実現されています。

  1. 定義層 — Display JSON(1,255 ファイル)とカラム定義で多言語テキストを管理
  2. 生成層 — CodeDefiner が定義データからサーバー/クライアント両方のコードを自動生成
  3. 実行層 — 起動時に DisplayHash を構築し、Context.Language に基づいて表示テキストを解決

次回は、Display JSON とそれを扱う DisplayAccessor クラス群の詳細を見ていきます。

1
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
1
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?