$translateProvider.useStaticFilesLoader的Angular Translate异步定

Angular Translate async timing issue with $translateProvider.useStaticFilesLoader

本文关键字:Translate 异步 Angular translateProvider useStaticFilesLoader      更新时间:2023-09-26

我使用出色的Angular Translate($translate)指令/服务来处理多种语言环境,由于我有多个语言环境文件,我使用方便的$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

实现承诺可能需要时间,但从长远来看,这是值得的。

从本质上讲,您需要使用validationService来使用$translate的promise api,当它能够做到这一点时,它将根据提供的密钥为您提供所需的翻译。归根结底,您需要$translates来获得您希望获得翻译的所有translationId,当所有的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
  };
}]);

我已经分叉了你的plunker,并对其进行了修改,以包括上面的样品

http://plnkr.co/edit/7DCwvY9jloXwfetKtcDA?p=preview

另一个观察结果是,您在HTML中使用了translate过滤器。请注意,如果您有一个大型DOM,这可能会被证明是昂贵的,因为Angular将调用来翻译每个摘要上的每个键。一种需要考虑的方法是为vm提供一个labels对象,并在控制器实例化时使用$filter服务来填充它们。

我发现,将额外参数传递给promise的匿名函数的问题的答案是使用闭包,这样一来,promise之前和内部的变量都是相同的。因此,我基本上必须将我的$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确实不返回promise,但我查看了$translate服务内部,发现它提供了一个方便的回调onReady(),它确实返回了promise。当$translate服务完成加载当前选定的语言时,会调用此回调,它有助于确保即时翻译在页面初始化后按预期工作:

$translate.onReady(function () {
    // perform your instant translations here
    var translatedMsg = $translate.instant('INVALID_ALPHA');
});