我认为您可以通过结合 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*?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);
}