Angular 翻译 $translateProvider.useStaticFilesLoader 的异步计时问题
Angular Translate async timing issue with $translateProvider.useStaticFilesLoader
我正在使用出色的 Angular 翻译 ($translate
) directive/service 来处理多种语言环境,因为我有多个语言环境文件,所以我使用方便的 $translateProvider.useStaticFilesLoader
通过 localeAbbr.json 的结构加载我的翻译文件,例如 en.json
、es.json
等...我构建了一个 Plunker 来显示我的开源项目,该项目使用语言环境Git 原始文件(指向实际的 Github 存储库,这意味着不是 plunker 演示的本地文件)。我的项目是作为指令和服务构建的,我制作了一个小的 Plunker 来显示 JSON 文件加载的时间问题。
所有这一切都表明它似乎 $translateProvider.useStaticFilesLoader
有效 asynchronous
而我真的需要它是 synchronous
因为当 plunker 运行时, JSON文件尚未解析,而我已经在消息中调用了 $translate.instant()
。
我有 Plunker 显示问题。
这是我的快速服务演示的一部分:
app.factory('validationService', ['$filter', '$translate', function ($filter, $translate) {
var service = this;
var validationSummary = [];
var errorMessages = [
'INVALID_ALPHA',
'INVALID_ALPHA_SPACE',
'INVALID_ALPHA_NUM',
'INVALID_BOOLEAN'
];
//var $translate = $filter('translate');
for(var i=0, ln=errorMessages.length; i < ln; i++) {
validationSummary.push({
field: i,
message: $translate.instant(errorMessages[i])
});
}
// attach public functions
service.getValidationSummary = getValidationSummary;
return service;
// function declaration
function getValidationSummary() {
return validationSummary;
}
}]);
$translateProvider 配置
app.config(['$translateProvider', function ($translateProvider) {
$translateProvider.useStaticFilesLoader({
prefix: 'https://rawgit.com/ghiscoding/angular-validation/master/locales/validation/',
suffix: '.json'
});
// load English ('en') table on startup
$translateProvider.preferredLanguage('en').fallbackLanguage('en');
}]);
通过控制器调用我的服务:
app.controller("TestController", function($scope, validationService) {
var vm = this;
vm.displayValidationSummary = true;
vm.validationSummary = validationService.getValidationSummary();
});
最后 HTML 使用控制器:
<div class="alert alert-danger alert-dismissable" ng-show="vm.displayValidationSummary">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true" ng-click="displayValidationSummary = false">×</button>
<h4><strong>{{ 'ERRORS' | translate }}!</strong></h4>
<ul>
<li ng-repeat="item in vm.validationSummary">{{item.field }}: {{item.message}}</li>
</ul>
</div>
由于我使用的是 AngularJS 1.3+,我还发现 $translate
只被翻译了一次,所以作者建议使用 translateFilter.$stateful = true;
我试过了但是不行' 似乎有帮助。
这里又是 Plunker
我已经花了数周时间来寻找和编写各种解决方案,但我从来没有让它工作,看到我的原始翻译代码我真的很难过:(
请帮忙!!!
编辑
我意识到我的问题并没有涵盖与我的问题相关的所有内容。除了翻译延迟问题之外,我还必须传递额外的参数,这是将它们传递给翻译匿名函数的一个大问题。到承诺完成时,我的论证状态已经改变。例如:
$translate(validator.message).then(function(translation) {
// only log the invalid message in the $validationSummary
addToValidationSummary(formElmObj, translation);
// error Display
if(!isValid) {
updateErrorMsg(translation, isValid);
}else if(!!formElmObj && formElmObj.isValid) {
addToValidationSummary(formElmObj, '');
}
}, function(data) {
throw 'Failed to translate' + data;
});
在使用 AngularJS 或 JavaScript 时,您确实需要接受异步范式。为了减少处理异步代码的麻烦,您可以使用 Promises。 Angular 为您提供名为 $q 的服务,它可以为您完成繁重的工作
https://docs.angularjs.org/api/ng/service/$q
让人们了解 Promises 可能需要时间,但在长期 运行.
中付出努力是值得的
本质上,您需要对 validationService 执行的操作是利用 $translate 的承诺 api,它将根据提供的密钥为您提供所需的翻译。这归结为你向 $translate 请求所有你希望获得翻译的 translationId,当所有的都被获取后,你用你的消息填充 validationSummary 数组。
app.factory('validationService', ['$q', '$translate', function ($q, $translate) {
var translationsPromises = [],
validationSummary = [],
errorMessages = [
'INVALID_ALPHA',
'INVALID_ALPHA_SPACE',
'INVALID_ALPHA_NUM',
'INVALID_BOOLEAN'
];
angular.forEach(errorMessages, function(val, key) {
translationsPromises.push($translate(val));
});
$q.all(translationsPromises)
.then(function(translations) {
angular.forEach(translations, function(val, key) {
validationSummary.push({
filed: key,
message: val
});
});
})
.catch(function (err) {
console.error('Failed to translate error messages for validation summary', err);
});
// function declaration
function getValidationSummary() {
return validationSummary;
}
return {
getValidationSummary: getValidationSummary
};
}]);
我已经 fork 你的 plunker 并修改它以包含上面的示例
http://plnkr.co/edit/7DCwvY9jloXwfetKtcDA?p=preview
另一个观察结果是您在 HTML 中使用了翻译过滤器。请注意,如果您有一个很大的 DOM,这可能会很昂贵,因为 Angular 将调用翻译每个摘要上的每个键。一种可以考虑的方法是为您的 vm 提供一个标签对象,并使用 $filter 服务在控制器实例化时填充它们。
我找到了将额外参数传递给承诺的匿名函数的问题的答案是使用 Closures,这样变量在承诺之前和内部也是相同的。所以我基本上必须将我的 $translate
调用包装到闭包中,如下所示:
(function(formElmObj, isValid, validator) {
$translate(validator.message).then(function(translation) {
message = message.trim();
// only log the invalid message in the $validationSummary
addToValidationSummary(formElmObj, message);
// error Display
if(!isValid) {
updateErrorMsg(message, isValid);
}else if(!!formElmObj && formElmObj.isValid) {
addToValidationSummary(formElmObj, '');
}
}, function(data) {
throw 'Failed to translate' + data;
});
})(formElmObj, isValid, validator);
现在终于,我的变量是正确的并保持那个时间点的值:)
虽然 $translateProvider.useStaticFilesLoader
确实没有 return 承诺,但我查看了 $translate 服务内部,发现它提供了一个方便的回调 onReady()
return 一个承诺。当 $translate
服务完成加载当前选择的语言时调用此回调,并且有助于确保即时翻译在页面初始化后按预期工作:
$translate.onReady(function () {
// perform your instant translations here
var translatedMsg = $translate.instant('INVALID_ALPHA');
});
我正在使用出色的 Angular 翻译 ($translate
) directive/service 来处理多种语言环境,因为我有多个语言环境文件,所以我使用方便的 $translateProvider.useStaticFilesLoader
通过 localeAbbr.json 的结构加载我的翻译文件,例如 en.json
、es.json
等...我构建了一个 Plunker 来显示我的开源项目,该项目使用语言环境Git 原始文件(指向实际的 Github 存储库,这意味着不是 plunker 演示的本地文件)。我的项目是作为指令和服务构建的,我制作了一个小的 Plunker 来显示 JSON 文件加载的时间问题。
所有这一切都表明它似乎 $translateProvider.useStaticFilesLoader
有效 asynchronous
而我真的需要它是 synchronous
因为当 plunker 运行时, JSON文件尚未解析,而我已经在消息中调用了 $translate.instant()
。
我有 Plunker 显示问题。
这是我的快速服务演示的一部分:
app.factory('validationService', ['$filter', '$translate', function ($filter, $translate) {
var service = this;
var validationSummary = [];
var errorMessages = [
'INVALID_ALPHA',
'INVALID_ALPHA_SPACE',
'INVALID_ALPHA_NUM',
'INVALID_BOOLEAN'
];
//var $translate = $filter('translate');
for(var i=0, ln=errorMessages.length; i < ln; i++) {
validationSummary.push({
field: i,
message: $translate.instant(errorMessages[i])
});
}
// attach public functions
service.getValidationSummary = getValidationSummary;
return service;
// function declaration
function getValidationSummary() {
return validationSummary;
}
}]);
$translateProvider 配置
app.config(['$translateProvider', function ($translateProvider) {
$translateProvider.useStaticFilesLoader({
prefix: 'https://rawgit.com/ghiscoding/angular-validation/master/locales/validation/',
suffix: '.json'
});
// load English ('en') table on startup
$translateProvider.preferredLanguage('en').fallbackLanguage('en');
}]);
通过控制器调用我的服务:
app.controller("TestController", function($scope, validationService) {
var vm = this;
vm.displayValidationSummary = true;
vm.validationSummary = validationService.getValidationSummary();
});
最后 HTML 使用控制器:
<div class="alert alert-danger alert-dismissable" ng-show="vm.displayValidationSummary">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true" ng-click="displayValidationSummary = false">×</button>
<h4><strong>{{ 'ERRORS' | translate }}!</strong></h4>
<ul>
<li ng-repeat="item in vm.validationSummary">{{item.field }}: {{item.message}}</li>
</ul>
</div>
由于我使用的是 AngularJS 1.3+,我还发现 $translate
只被翻译了一次,所以作者建议使用 translateFilter.$stateful = true;
我试过了但是不行' 似乎有帮助。
这里又是 Plunker
我已经花了数周时间来寻找和编写各种解决方案,但我从来没有让它工作,看到我的原始翻译代码我真的很难过:(
请帮忙!!!
编辑
我意识到我的问题并没有涵盖与我的问题相关的所有内容。除了翻译延迟问题之外,我还必须传递额外的参数,这是将它们传递给翻译匿名函数的一个大问题。到承诺完成时,我的论证状态已经改变。例如:
$translate(validator.message).then(function(translation) {
// only log the invalid message in the $validationSummary
addToValidationSummary(formElmObj, translation);
// error Display
if(!isValid) {
updateErrorMsg(translation, isValid);
}else if(!!formElmObj && formElmObj.isValid) {
addToValidationSummary(formElmObj, '');
}
}, function(data) {
throw 'Failed to translate' + data;
});
在使用 AngularJS 或 JavaScript 时,您确实需要接受异步范式。为了减少处理异步代码的麻烦,您可以使用 Promises。 Angular 为您提供名为 $q 的服务,它可以为您完成繁重的工作
https://docs.angularjs.org/api/ng/service/$q
让人们了解 Promises 可能需要时间,但在长期 运行.
中付出努力是值得的本质上,您需要对 validationService 执行的操作是利用 $translate 的承诺 api,它将根据提供的密钥为您提供所需的翻译。这归结为你向 $translate 请求所有你希望获得翻译的 translationId,当所有的都被获取后,你用你的消息填充 validationSummary 数组。
app.factory('validationService', ['$q', '$translate', function ($q, $translate) {
var translationsPromises = [],
validationSummary = [],
errorMessages = [
'INVALID_ALPHA',
'INVALID_ALPHA_SPACE',
'INVALID_ALPHA_NUM',
'INVALID_BOOLEAN'
];
angular.forEach(errorMessages, function(val, key) {
translationsPromises.push($translate(val));
});
$q.all(translationsPromises)
.then(function(translations) {
angular.forEach(translations, function(val, key) {
validationSummary.push({
filed: key,
message: val
});
});
})
.catch(function (err) {
console.error('Failed to translate error messages for validation summary', err);
});
// function declaration
function getValidationSummary() {
return validationSummary;
}
return {
getValidationSummary: getValidationSummary
};
}]);
我已经 fork 你的 plunker 并修改它以包含上面的示例
http://plnkr.co/edit/7DCwvY9jloXwfetKtcDA?p=preview
另一个观察结果是您在 HTML 中使用了翻译过滤器。请注意,如果您有一个很大的 DOM,这可能会很昂贵,因为 Angular 将调用翻译每个摘要上的每个键。一种可以考虑的方法是为您的 vm 提供一个标签对象,并使用 $filter 服务在控制器实例化时填充它们。
我找到了将额外参数传递给承诺的匿名函数的问题的答案是使用 Closures,这样变量在承诺之前和内部也是相同的。所以我基本上必须将我的 $translate
调用包装到闭包中,如下所示:
(function(formElmObj, isValid, validator) {
$translate(validator.message).then(function(translation) {
message = message.trim();
// only log the invalid message in the $validationSummary
addToValidationSummary(formElmObj, message);
// error Display
if(!isValid) {
updateErrorMsg(message, isValid);
}else if(!!formElmObj && formElmObj.isValid) {
addToValidationSummary(formElmObj, '');
}
}, function(data) {
throw 'Failed to translate' + data;
});
})(formElmObj, isValid, validator);
现在终于,我的变量是正确的并保持那个时间点的值:)
虽然 $translateProvider.useStaticFilesLoader
确实没有 return 承诺,但我查看了 $translate 服务内部,发现它提供了一个方便的回调 onReady()
return 一个承诺。当 $translate
服务完成加载当前选择的语言时调用此回调,并且有助于确保即时翻译在页面初始化后按预期工作:
$translate.onReady(function () {
// perform your instant translations here
var translatedMsg = $translate.instant('INVALID_ALPHA');
});