主干和把手的国际化

我正在研究一个Grails / Backbone / Handlebars应用程序,这个应用程序是一个更大的遗留Java系统的前端,其中(为了历史和可定制的原因)国际化消息深藏在隐藏在几个SOAP服务之后的数据库中,隐藏在各种内部Java库后面。 从Grails层获取这些消息非常简单,并且工作正常。

我想知道的是,如何将(例如)国际化标签放入我的Handlebars模板中。

现在,我使用GSP片段来生成模板,其中包括一个自定义标签,用于获取我感兴趣的消息,如:

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

但是,出于性能和代码布局的原因,我希望摆脱GSP模板并将它们变为直接的HTML。 我已经研究了一些客户端的国际化选项,例如i18n.js,但它们似乎取决于我没有收到的消息文件的存在。 (我可能会生成它,但它会是巨大而昂贵的。)

到目前为止,我能想到的最好的事情就是将标签粘贴到Backbone模型中,所以我最终会得到类似

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

然而,这实际上远离了在干净的RESTful JSON API上构建Backbone模型的理想 - 要么由RESTful服务返回的JSON与演示数据(即本地化标签)混杂在一起,要么我必须做额外的工作来将标签注入到Backbone模型中 - 并且使用演示数据混淆Bac​​kbone模型似乎也是错误的。

就清理数据和干净的API而言,我认为我想要做的是编写另一个RESTful服务,它接收消息键和类似列表,并返回包含所有本地化消息的JSON数据结构。 但是,问题依然存在:

  • 指示(可能在模板中)给定视图需要什么消息键的最佳方式是什么?
  • 数据的正确格式是什么?
  • 如何将本地化消息放入Backbone视图?
  • 是否有任何现有的Javascript库可以帮助,或者我应该开始制作一些东西?
  • 有更好/更标准的替代方法吗?

  • 我们使用http://i18next.com在Backbone / Handlebars应用程序中进行国际化。 (还有Require.js,它也通过插件加载和编译模板。)

    i18next可以配置为动态加载资源。 它支持gettext格式的JSON(支持复数和上下文变体)。 从他们的页面上获取如何加载远程资源的示例:

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

    (你当然需要更多的配置,比如设置语言,备用语言等)

    然后,您可以配置一个Handlebars助手,它可以在提供的变量(最简单的版本,无复数,无上下文)上调用i18next:

    // 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(转换为本地时间,格式,翻译等)。


    我认为你可以通过结合Handelbars助手和一些正则表达式来创建一个相当优雅的解决方案。

    这是我会建议的:

  • 创建一个接受消息键JSON数组并返回JSON对象的服务,其中键是消息键,值是本地化文本。
  • 定义一个Handlebars助手,它接收一个消息键(它与服务器上的消息键相匹配)并输出一个已翻译的文本。 像{{localize "messageKey"}} 。 使用此帮助程序进行所有模板本地化。
  • 编写一个模板预处理器,该模板预处理器对来自模板的消息密钥进行查询并为您的服务发出请求。 预处理器缓存它获取的所有消息密钥,并只请求它不具有的消息密钥。
  • 您可以在需要渲染模板时按需调用此预处理器,也可以预先调用并缓存消息密钥,以便在需要时准备好。
  • 要进一步优化,您可以将缓存保存到浏览器本地存储。
  • 这是一个概念证明。 它还没有本地存储持久性或支持一次为了缓存目的而获取多个模板的文本,但很容易一起入侵,我认为在一些进一步的工作中它可以很好地工作。

    客户端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*?localizes*['"](.*)['"]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);
    }
    
    链接地址: http://www.djcxy.com/p/68157.html

    上一篇: side internationalization for Backbone and Handlebars

    下一篇: HTML is to HAML as XSLT is to ...?