主干和把手的国际化
我正在研究一个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模型中 - 并且使用演示数据混淆Backbone模型似乎也是错误的。
就清理数据和干净的API而言,我认为我想要做的是编写另一个RESTful服务,它接收消息键和类似列表,并返回包含所有本地化消息的JSON数据结构。 但是,问题依然存在:
我们使用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助手和一些正则表达式来创建一个相当优雅的解决方案。
这是我会建议的:
{{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