7

私はGrails/Backbone / Handlebarsアプリケーションに取り組んでいます。これは、はるかに大規模なレガシーJavaシステムのフロントエンドであり、(歴史的およびカスタマイズ性の理由から)国際化メッセージは、いくつかのSOAPサービスの背後に隠されたデータベースの奥深くにあります。さまざまな内部Javaライブラリの背後に隠されています。Grailsレイヤーからこれらのメッセージを取得するのは簡単で、正常に機能します。

しかし、私が疑問に思っているのは、(たとえば)国際化されたラベルをハンドルバーテンプレートに取り込む方法です。

現在、GSPフラグメントを使用してテンプレートを生成しています。これには、関心のあるメッセージを取得するカスタムタグが含まれています。

<li><myTags:message msgKey="title"/> {{title}}</li>

ただし、パフォーマンスとコードレイアウトの理由から、GSPテンプレートから離れて、ストレートHTMLに変換したいと思います。i18n.jsなどのクライアント側の国際化オプションを少し調べましたが、それらは私が持っていないメッセージファイルの存在に依存しているようです。(私はそれを生成することができたかもしれませんが、それは巨大で高価になるでしょう。)

これまでのところ、私が考えることができる最善のことは、ラベルをBackboneモデルにもくさびで留めることです。そのため、最終的には次のようになります。

<li>{{titleLabel}} {{title}}</li>

ただし、これは、クリーンなRESTful JSON APIの上にBackboneモデルを構築するという理想からはほど遠いものです。RESTfulサービスによって返されるJSONがプレゼンテーションデータ(つまり、ローカライズされたラベル)で雑然としている、または私がしなければならないラベルをBackboneモデルに挿入するために追加の作業を行います。また、Backboneモデルをプレゼンテーションデータで乱雑にすることも間違っているようです。

クリーンなデータとクリーンなAPIの観点から、私がやりたいのは、メッセージキーなどのリストを取得し、ローカライズされたすべてのメッセージを含むJSONデータ構造を返す別のRESTfulサービスを作成することだと思います。ただし、疑問が残ります。

  1. 特定のビューに必要なメッセージキーを(おそらくテンプレートで)示すための最良の方法は何ですか?
  2. データの正しい形式は何ですか?
  3. ローカライズされたメッセージをバックボーンビューに取り込むにはどうすればよいですか?
  4. 役立つ既存のJavascriptライブラリはありますか、それとも私は何かを作り始めるべきですか?
  5. より良い/より標準的な代替アプローチはありますか?
4

2 に答える 2

3

Handelbarsヘルパーといくつかの正規表現を組み合わせることで、非常にエレガントなソリューションを作成できると思います。

これが私が提案するものです:

  1. メッセージキーのJSON配列を受け取り、JSONオブジェクトを返すサービスを作成します。ここで、キーはメッセージキーであり、値はローカライズされたテキストです。
  2. メッセージキー(サーバー上のメッセージキーと一致する)を受け取り、翻訳されたテキストを出力するハンドルバーヘルパーを定義します。のようなもの{{localize "messageKey"}}。すべてのテンプレートのローカリゼーションにこのヘルパーを使用します。
  3. テンプレートからメッセージキーを取得し、サービスを要求するテンプレートプリプロセッサを作成します。プリプロセッサは、取得したすべてのメッセージキーをキャッシュし、まだ持っていないメッセージキーのみを要求します。
    • テンプレートをレンダリングする必要があるときにこのプリプロセッサをオンデマンドで呼び出すか、事前に呼び出してメッセージキーをキャッシュすることで、必要なときにすぐに使用できるようにすることができます。
    • さらに最適化するために、キャッシュをブラウザのローカルストレージに永続化できます。

これが概念実証です。ローカルストレージの永続性や、キャッシュ目的で複数のテンプレートのテキストを一度にフェッチするためのサポートはまだありませんが、ハッキングするのは簡単だったので、さらにいくつかの作業を行うとうまくいくと思います。

クライアントAPIは次のようになります。

var localizer = new HandlebarsLocalizer();

//compile a template
var html = $("#tmpl").html();
localizer.compile(html).done(function(template) { 
  //..template is now localized and ready to use 
});

怠惰な読者の情報源は次のとおりです。

var HandlebarsLocalizer = function() {

  var _templateCache = {};
  var _localizationCache = {};

  //fetches texts, adds them to cache, resolves deferred with template
  var _fetch = function(keys, template, deferred) {
    $.ajax({
      type:'POST',
      dataType:'json',
      url: '/echo/json',
      data: JSON.stringify({
        keys: keys 
      }),
      success: function(response) {
        //handle response here, this is just dummy
        _.each(keys, function(key) { _localizationCache[key] = "(" + key + ") localized by server";  });
        console.log(_localizationCache);
        deferred.resolve(template);
      },
      error: function() {
        deferred.reject();
      }
    });
  };

  //precompiles html into a Handlebars template function and fetches all required
  //localization keys. Returns a promise of template.
  this.compile = function(html) {

    var cacheObject = _templateCache[html],
        deferred = new $.Deferred();

    //cached -> return
    if(cacheObject && cacheObject.ready) { 
      deferred.resolve(cacheObject.template);
      return deferred.promise();
    }
    //grep all localization keys from template
    var regex = /{{\s*?localize\s*['"](.*)['"]\s*?}}/g, required = [], match;
    while((match = regex.exec(html))) {
      var key = match[1];
      //if we don't have this key yet, we need to fetch it
      if(!_localizationCache[key]) {
        required.push(key);
      }
    }

    //not cached -> create
    if(!cacheObject) {
      cacheObject = {
        template:Handlebars.compile(html),
        ready: (required.length === 0)
      };   
      _templateCache[html] = cacheObject;
    }

    //we have all the localization texts ->
    if(cacheObject.ready) {
      deferred.resolve(cacheObject.template);
    } 
    //we need some more texts ->
    else {
      deferred.done(function() { cacheObject.ready = true; });
      _fetch(required, cacheObject.template, deferred);    
    }

    return deferred.promise();
  };

  //translates given key
  this.localize = function(key) {
    return _localizationCache[key] || "TRANSLATION MISSING:"+key;
  };

  //make localize function available to templates
  Handlebars.registerHelper('localize', this.localize);
}
于 2013-01-08T02:52:20.540 に答える
2

Backbone / Handlebarsアプリでの国際化には、 http://i18next.comを使用します。(そして、プラグインを介してテンプレートをロードおよびコンパイルするRequire.jsもあります。)

i18nextは、リソースを動的にロードするように構成できます。gettext形式のJSONをサポートします(複数形およびコンテキストバリアントをサポートします)。リモートリソースをロードする方法に関する彼らのページからの例:

var option = { 
  resGetPath: 'resources.json?lng=__lng__&ns=__ns__',
  dynamicLoad: true 
};

i18n.init(option);

(もちろん、言語やフォールバック言語の設定など、より多くの構成が必要になります)

次に、提供された変数(最も単純なバージョン、複数形、コンテキストなし)でi18nextを呼び出すHandlebarsヘルパーを構成できます。

// namespace: "translation" (default)
Handlebars.registerHelper('_', function (i18n_key) {
    i18n_key = Handlebars.compile(i18n_key)(this);
    var result = i18n.t(i18n_key);
    if (!result) {
        console.log("ERROR : Handlebars-Helpers : no translation result for " + i18n_key);
    }
    return new Handlebars.SafeString(result);
});

また、テンプレートでは、キーに展開される動的変数を提供することができます。

<li>{{_ titleLabeli18nKey}} {{title}}</li>

または、キーを直接指定します。

<li>{{_ "page.fancy.title"}} {{title}}</li>

日時のローカリゼーションには、http://momentjs.com(現地時間への変換、フォーマット、翻訳など)を使用します。

于 2013-07-18T16:19:46.037 に答える