Angular 翻译 $translateProvider.useStaticFilesLoader 的异步计时问题

Angular Translate async timing issue with $translateProvider.useStaticFilesLoader

我正在使用出色的 Angular 翻译 ($translate) directive/service 来处理多种语言环境,因为我有多个语言环境文件,所以我使用方便的 $translateProvider.useStaticFilesLoader通过 localeAbbr.json 的结构加载我的翻译文件,例如 en.jsones.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">&times;</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');
});