【问题标题】:Correct use for angular-translate in controllers在控制器中正确使用角度平移
【发布时间】:2013-12-30 17:01:21
【问题描述】:

我在 AngularJS 应用程序中为 i18n 使用 angular-translate

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

代码

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

我正在使用 angular-translate-loader-url 扩展名加载翻译文件。

问题

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

第二次进入页面,一切正常,显示翻译版本。

我认为问题与控制器将值分配给$scope.pageTitle 时可能尚未加载翻译文件有关。

备注

使用&lt;h1&gt;{{ pageTitle | translate }}&lt;/h1&gt;$scope.pageTitle = 'HELLO_WORLD'; 时,翻译从第一次开始就完美无缺。这样做的问题是我并不总是想使用翻译(例如,对于第二个控制器,我只想传递一个原始字符串)。

问题

这是一个已知问题/限制吗?怎么解决?

【问题讨论】:

    标签: javascript angularjs angular-translate


    【解决方案1】:

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

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

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

    翻译服务也有一个直接翻译字符串的方法,不需要处理promise,使用$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 是这个很棒的库的创建者,我建议使用his advise (see his answer below) 并使用提供的指令,它似乎可以非常智能地处理翻译。

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

    【讨论】:

    • 如果你尝试过而不是写那些不相关的评论,你现在就知道答案了。简短的回答:是的。这是可能的。
    • 在您的示例中使用控制器中的过滤器:与 Instant() 一样,如果未加载语言文件,这将无法正常工作吗?在那种情况下我们不应该使用手表吗?或者你的意思是说'只有在你知道翻译已加载时才使用过滤器?
    • @Bombinosh 如果您知道已加载翻译,我会说使用过滤方法。就个人而言,我什至建议您不要动态加载翻译。它是应用程序的必需部分,因此您最好不要让用户等待它。但这是个人意见。
    • 翻译的重点是它们可以根据用户偏好甚至用户操作进行更改。因此,您通常需要动态加载它们。至少如果要翻译的字符串数量很重要,和/或如果您有很多翻译。
    • 当在 HTML 中完成翻译时,摘要循环运行两次,但在控制器中只运行一次。在 99% 的情况下,这可能无关紧要,但我遇到了一个问题,即在有角度的 ui 网格中性能很差,在许多单元格中都有翻译。肯定是一个边缘案例,只是需要注意的事情
    【解决方案2】:

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

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

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

    但是,如果没有办法,并且您真的必须在控制器中使用 $translate 服务,您应该使用 $rootScope 结合 @ 将调用包装在 $translateChangeSuccess 事件中987654327@这样的:

    .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() 而不仅仅是异步$translate()?当$translateChangeSuccess 事件被触发时,确定需要的翻​​译数据在那里并且没有异步执行发生(例如异步加载器执行),因此我们可以只使用同步的$translate.instant() 并假设翻译可用.

    从 2.8.0 版开始,还有$translate.onReady(),它返回一个承诺,一旦翻译准备好就解决。 See the changelog.

    【讨论】:

    • 如果我使用翻译指令而不是过滤器,会不会有任何性能问题?我也相信在内部,它会观察 Instant() 的返回值。那么当当前作用域被销毁时它会移除手表吗?
    • 我尝试使用您的建议,但是当范围变量的值动态变化时它不起作用。
    • 实际上,尽可能避免使用过滤器总是更好,因为它们总是会设置新的手表,从而减慢您的应用程序的速度。然而,该指令走得更远。它检查是否必须观察翻译 id 的值。这样可以更好地执行您的应用程序。你能打个电话给我链接,让我再看看吗?
    • Plunk: plnkr.co/edit/j53xL1EdJ6bT20ldlhxr 可能在我的例子中,指令决定不关注价值。同样作为一个单独的问题,如果找不到密钥,则会调用我的自定义错误处理程序,但它不会显示返回的字符串。我会再做一次。
    • @PascalPrecht 只是一个问题,在翻译中使用一次绑定是一个好习惯吗?喜欢这个{{::'HELLO_WORLD | translate}}'
    【解决方案3】:

    编辑:请参阅 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 所做的,所以它不会那么糟糕......

    【讨论】:

    • 谢谢!我希望在视图或控制器中使用过滤器的行为完全相同。这似乎不是这里的情况。
    • 我会说使用$scope.$watch 有点过头了,因为 Angular Translate 提供了在控制器中使用的服务。请参阅下面的答案。
    • 不需要 Angular Translate 过滤器,因为 $translate.instant() 提供与服务相同的功能。除此之外,请注意帕斯卡的回答。
    • 我同意,使用 $watch 太过分了。下面的答案是更正确的用法。
    【解决方案4】:

    要在控制器中进行翻译,您可以使用$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'];
            });
        });
    

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

    【讨论】:

      【解决方案5】:

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

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

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

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-05-28
        • 2018-08-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多