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

IT技术 javascript angularjs angular-translate
2021-03-04 03:46:55

在 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.

评论

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

问题

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

5个回答

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

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

<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()可能是如果您异步加载语言文件,则尚未加载它。

使用提供的过滤器

这是我的首选方式,因为我不必以这种方式处理Promise。过滤器的输出可以直接设置为作用域变量。

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

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

使用提供的指令

由于@PascalPrecht 是这个很棒的库的创建者,我建议听从他的建议(请参阅下面的回答)并使用提供的指令,该指令似乎可以非常智能地处理翻译。

该指令负责异步执行,并且如果翻译没有动态值,它也足够聪明以取消观察作用域上的翻译 ID。

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

实际上,您应该对此类内容使用 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(),它返回一个Promise,一旦翻译准备好就解决。请参阅变更日志

实际上,尽可能避免使用过滤器总是更好,因为它们会降低您的应用程序的速度,因为它们总是会设置新的手表。然而,该指令更进一步。它检查是否必须监视翻译 ID 的值。这样可以更好地执行您的应用程序。你可以做一个plunk并将我链接到它,以便我可以进一步查看吗?
2021-04-15 03:46:55
Plunk:plnkr.co/edit/j53xL1EdJ6bT20ldlhxr可能在我的例子中,指令决定不看值。同样作为一个单独的问题,如果找不到键,我的自定义错误处理程序会被调用,但它不显示返回的字符串。我会再做一次。
2021-04-25 03:46:55
如果我使用 translate 指令而不是 filter 会不会有任何性能问题?我也相信在内部,它会观察 Instant() 的返回值。那么当当前作用域被销毁时它会删除手表吗?
2021-04-26 03:46:55
我尝试使用您的建议,但是当范围变量的值动态更改时它不起作用。
2021-05-07 03:46:55
@PascalPrecht 只是一个问题,在翻译中使用一次绑定是一个好习惯吗?像这样{{::'HELLO_WORLD | translate}}'
2021-05-09 03:46:55

编辑:请参阅 PascalPrecht(angular-translate 的作者)的答案以获得更好的解决方案。


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

所以,你可以自己做:

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

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

我同意,使用 $watch 是矫枉过正。下面的答案是更正确的用法。
2021-04-16 03:46:55
我会说使用 a$scope.$watch是相当矫枉过正,因为 Angular Translate 提供了要在控制器中使用的服务。请看我下面的回答。
2021-04-17 03:46:55
Angular Translate 过滤器不是必需的,因为它$translate.instant()提供与服务相同的功能。除此之外,请注意Pascal的回答。
2021-04-18 03:46:55
谢谢!我希望在视图或控制器中使用过滤器的行为完全相同。这里似乎并非如此。
2021-05-14 03:46:55

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

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

该语句仅在控制器激活时进行翻译,但不会检测语言的运行时更改。为了实现该行为,您可以收听$rootScopeevent:$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侦听器中调用它

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

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

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

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

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