126

I'm using angular-translate for i18n in an AngularJS application.

For every application view, there is a dedicated controller. In the controllers below, I set the value to be shown as the page title.

Code

HTML

<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';
    }])

I'm loading the translation files using the angular-translate-loader-url extension.

Problem

On the initial page load, the translation key is shown instead of the translation for that key. The translation is Hello, World!, but I'm seeing HELLO_WORLD.

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

I assume the issue has to do with the fact that maybe the translation file is not yet loaded when the controller is assigning the value to $scope.pageTitle.

Remark

When using <h1>{{ pageTitle | translate }}</h1> and $scope.pageTitle = 'HELLO_WORLD';, the translation works perfect from the first time. The problem with this is that I don't always want to use translations (eg. for the second controller I just want to pass a raw string).

Question

Is this a known issue / limitation? How can this be solved?

4

5 回答 5

144

推荐:不要在控制器中翻译,在你的视图中翻译

我建议让您的控制器不受翻译逻辑的影响,并直接在您的视图中翻译您的字符串,如下所示:

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

使用提供的服务

Angular Translate 提供了$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。

于 2014-03-13T12:40:28.167 回答
124

实际上,您应该对此类内容使用 translate 指令。

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

该指令负责异步执行,并且如果翻译没有动态值,它也足够聪明,可以在范围内取消观察翻译 id。

但是,如果没有办法,并且您真的必须在控制器中使用$translate服务,您应该将调用包装在一个$translateChangeSuccess事件中$rootScope,并结合使用$translate.instant()如下:

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

那么为什么$rootScope$scope呢?这样做的原因是,在 angular-translate 中,事件是$emited on$rootScope而不是$broadcasted on,$scope因为我们不需要通过整个作用域层次结构进行广播。

为什么$translate.instant()不只是 async $translate()?当$translateChangeSuccess事件被触发时,可以确定需要的翻​​译数据在那里并且没有异步执行发生(例如异步加载器执行),因此我们可以只使用$translate.instant()同步并假设翻译可用。

从 2.8.0 版本开始,还有$translate.onReady(),它返回一个承诺,一旦翻译准备好就解决。请参阅更改日志

于 2014-05-26T09:01:00.707 回答
71

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


加载的异步性质导致了问题。你看,有了{{ pageTitle | translate }},Angular 会观察表达式;加载本地化数据时,表达式的值会发生变化,屏幕也会更新。

所以,你可以自己做:

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

但是,这将在每个摘要循环上运行监视的表达式。这是次优的,可能会或可能不会导致明显的性能下降。无论如何,这就是 Angular 所做的,所以它不会那么糟糕......

于 2013-12-12T11:40:59.700 回答
5

要在控制器中进行翻译,您可以使用$translate服务:

$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'];
        });
    });

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

于 2015-02-28T22:40:49.500 回答
1

发生的事情是 Angular-translate 正在使用基于事件的系统监视表达式,并且就像在任何其他绑定或双向绑定的情况下一样,当检索数据并且值发生更改时会触发一个事件,这显然不适用于翻译。与页面上的其他动态数据不同,翻译数据当然必须立即显示给用户。页面加载后无法弹出。

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

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

无论如何,使用后处理翻译平台对我来说更有意义。例如,使用 GlobalizeIt,翻译人员可以直接转到网站上的页面,然后直接在页面上根据他们的语言开始编辑文本,就是这样: https ://www.globalizeit.com/HowItWorks 。无需编程(尽管它可以通过编程方式扩展),它很容易与 Angular 集成: https ://www.globalizeit.com/Translate/Angular ,页面的转换是一次性发生的,它总是显示翻译后的文本页面的初始渲染。

全面披露:我是联合创始人 :)

于 2017-10-10T04:11:19.697 回答