控制器角度平移的正确使用

我在 AngularJS 应用程序中使用 角度平移表示 i18n。

对于每个应用程序视图,都有一个专用的控制器。在下面的控制器中,我将值设置为显示为页面标题。

密码

超文本标示语言

<h1>{{ pageTitle }}</h1>

JavaScript

.controller('FirstPageCtrl', ['$scope', '$filter', function ($scope, $filter) {
$scope.pageTitle = $filter('translate')('HELLO_WORLD');
}])


.controller('SecondPageCtrl', ['$scope', '$filter', function ($scope, $filter) {
$scope.pageTitle = 'Second page title';
}])

我正在使用 角度平移装载机网址扩展加载翻译文件。

Problem

在初始页面加载时,将显示翻译键,而不是该键的翻译。翻译是 Hello, World!但我看到的是 HELLO_WORLD

The second time I go to the page, all is well and the translated version is shown.

我假设这个问题与这样一个事实有关: 当控制器将值分配给 $scope.pageTitle时,可能还没有加载翻译文件。

注意

当使用 <h1>{{ pageTitle | translate }}</h1>$scope.pageTitle = 'HELLO_WORLD';时,翻译工作从第一次开始就很完美。这样做的问题在于,我并不总是希望使用转换(例如,对于第二个控制器,我只想传递一个原始字符串)。

提问

这是一个已知的问题/局限吗? 如何解决这个问题?

178587 次浏览

编辑 : 请参阅 PascalPrecht (角度翻译的作者)的答案以获得更好的解决方案。


加载的异步性质导致了这个问题。你看,使用 \{\{ pageTitle | translate }},Angular 会观察表达式; 当加载定位数据时,表达式的值会发生变化,屏幕也会更新。

所以,你可以自己做:

.controller('FirstPageCtrl', ['$scope', '$filter', function ($scope, $filter) {
$scope.$watch(
function() { return $filter('translate')('HELLO_WORLD'); },
function(newval) { $scope.pageTitle = newval; }
);
});

但是,这将在每个摘要周期上运行所观察的表达式。这是次优的,可能会也可能不会导致明显的性能下降。无论如何,这是什么角度做,所以它不能是那么糟糕..。

Recommended: don't translate in the controller, translate in your view

我建议让你的控制器远离转换逻辑,像这样直接在视图中转换字符串:

<h1>\{\{ 'TITLE.HELLO_WORLD' | translate }}</h1>

使用提供的服务

角度翻译提供的 $translate服务,您可以在您的控制器使用。

使用 $translate服务的例子可以是:

.controller('TranslateMe', ['$scope', '$translate', function ($scope, $translate) {
$translate('PAGE.TITLE')
.then(function (translatedValue) {
$scope.pageTitle = translatedValue;
});
});

翻译服务还有一种直接翻译字符串的方法,不需要处理承诺,使用 $translate.instant():

.controller('TranslateMe', ['$scope', '$translate', function ($scope, $translate) {
$scope.pageTitle = $translate.instant('TITLE.DASHBOARD'); // Assuming TITLE.DASHBOARD is defined
});

使用 $translate.instant()的缺点可能是,如果要异步加载语言文件,那么还没有加载它。

使用提供的过滤器

这是我最喜欢的方式,因为我不必这样处理承诺。过滤器的输出可以直接设置为范围变量。

.controller('TranslateMe', ['$scope', '$filter', function ($scope, $filter) {
var $translate = $filter('translate');


$scope.pageTitle = $translate('TITLE.DASHBOARD'); // Assuming TITLE.DASHBOARD is defined
});

使用提供的指令

由于@PascalPrecht 是这个令人敬畏的库的创建者,我建议使用 他的建议(见下面他的回答),并使用提供的指令,它似乎可以处理非常智能的翻译。

该指令负责异步执行,并且如果翻译没有动态值,它也足够聪明,可以在作用域上取消监视翻译 id。

实际上,对于这些内容,您应该使用翻译指令。

<h1 translate="\{\{pageTitle}}"></h1>

该指令负责异步执行,并且如果翻译没有动态值,它也足够聪明,可以在作用域上取消监视翻译 id。

但是,如果没有其他办法,而且您确实在控制器中使用了 $translate服务,那么您应该使用 $rootScope$translate.instant()组合使用 $translateChangeSuccess事件来包装调用,如下所示:

.controller('foo', function ($rootScope, $scope, $translate) {
$rootScope.$on('$translateChangeSuccess', function () {
$scope.pageTitle = $translate.instant('PAGE.TITLE');
});
})

那为什么是 $rootScope而不是 $scope呢?原因在于,在角度翻译中,事件在 $rootScope上是 $emited,而在 $scope上是 $broadcasted,因为我们不需要在整个范围层次结构中进行广播。

为什么是 $translate.instant()而不仅仅是异步 $translate()?当触发 $translateChangeSuccess事件时,可以确定所需的翻译数据已经存在,并且没有异步执行(例如异步加载程序执行) ,因此我们可以只使用同步的 $translate.instant(),并假设翻译是可用的。

自从版本2.8.0以来,还有 $translate.onReady(),它返回一个承诺,一旦翻译准备就绪,这个承诺就会得到解决。看看变更记录.

To make a translation in the controller you could use $translate service:

$translate(['COMMON.SI', 'COMMON.NO']).then(function (translations) {
vm.si = translations['COMMON.SI'];
vm.no = translations['COMMON.NO'];
});

该语句仅在控制器激活时进行转换,但不检测语言中的运行时更改。为了实现这种行为,您可以监听 $rootScope事件: $translateChangeSuccess并在那里执行相同的翻译:

    $rootScope.$on('$translateChangeSuccess', function () {
$translate(['COMMON.SI', 'COMMON.NO']).then(function (translations) {
vm.si = translations['COMMON.SI'];
vm.no = translations['COMMON.NO'];
});
});

当然,您可以将 $translateservice 封装在一个方法中,并在控制器和 $translateChangeSucess侦听器中调用它。

正在发生的是,角度翻译是通过一个基于事件的系统来观察表达式,就像其他任何绑定或双向绑定的情况一样,当数据被检索时,一个事件被触发,值发生了变化,这显然不适合翻译。与页面上的其他动态数据不同,翻译数据必须立即显示给用户。页面加载后不能弹出。

即使您能够成功地调试这个问题,更大的问题是所涉及的开发工作是巨大的。开发人员必须手动提取站点上的每个字符串,并将其放入。Json 文件,通过字符串代码手动引用它(即本例中的“ pageTitle”)。大多数商业站点都有数千个字符串需要这样做。而这仅仅是个开始。现在你需要一个系统来保持翻译的同步,当底层文本发生变化的时候,这个系统可以把翻译文件发送给不同的翻译人员,可以把它们重新整合到构建中,可以重新部署网站,这样翻译人员就可以在上下文中看到它们的变化,等等。

此外,由于这是一个“绑定”的、基于事件的系统,页面上的每个字符串都会触发一个事件,这不仅是一种缓慢的页面转换方式,而且如果你开始向页面添加大量事件,还会减慢页面上所有操作的速度。

Anyway, using a post-processing translation platform makes more sense to me. Using GlobalizeIt for example, a translator can just go to a page on the site and start editing the text directly on the page for their language, and that's it: https://www.globalizeit.com/HowItWorks. No programming needed (though it can be programmatically extensible), it integrates easily with Angular: https://www.globalizeit.com/Translate/Angular, the transformation of the page happens in one go, and it always displays the translated text with the initial render of the page.

Full disclosure: I'm a co-founder :)