Uso correto para tradução angular em controladores

Eu estou usando angular-translate para i18n em um aplicativo AngularJS.

Para cada visualização de aplicativo, existe um controlador dedicado. Nos controladores abaixo, eu configurei o valor a ser mostrado como o título da página.

Código

HTML

{{ pageTitle }}

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

Estou carregando os arquivos de tradução usando a extensão angular-translate-loader-url .

Problema

No carregamento de página inicial, a chave de conversão é mostrada em vez da tradução dessa chave. A tradução é Hello, World! mas estou vendo HELLO_WORLD .

A segunda vez que eu vou para a página, tudo está bem e a versão traduzida é mostrada.

Presumo que o problema tenha a ver com o fato de que talvez o arquivo de tradução ainda não esteja carregado quando o controlador está atribuindo o valor a $scope.pageTitle .

Observação

Ao usar

{{ pageTitle | translate }}

{{ pageTitle | translate }}

e $scope.pageTitle = 'HELLO_WORLD'; , a tradução funciona perfeita desde a primeira vez. O problema com isso é que eu nem sempre quero usar traduções (por exemplo, para o segundo controlador eu só quero passar uma string bruta).

Questão

Esta é uma questão / limitação conhecida? como isso pode ser resolvido?

EDIT : Por favor, veja a resposta de PascalPrecht (o autor de angular-translate) para uma melhor solução.


A natureza assíncrona do carregamento causa o problema. Você vê, com {{ pageTitle | translate }} {{ pageTitle | translate }} , Angular vai assistir a expressão; Quando os dados de localização são carregados, o valor da expressão é alterado e a canvas é atualizada.

Então, você pode fazer isso sozinho:

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

No entanto, isso executará a expressão observada em todos os ciclos de digestão. Isso é sub-ótimo e pode ou não causar uma degradação visível do desempenho. Enfim, é o que Angular faz, então não pode ser tão ruim assim …

Recomendado: não traduza no controlador, traduza em sua visão

Eu recomendaria manter seu controlador livre de lógica de tradução e traduzir suas strings diretamente dentro de sua visão da seguinte forma:

 

{{ 'TITLE.HELLO_WORLD' | translate }}

Usando o serviço fornecido

O Angular Translate fornece o serviço $translate que você pode usar em seus Controllers.

Um exemplo de uso do serviço $translate pode ser:

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

O serviço translate também tem um método para traduzir diretamente strings sem a necessidade de lidar com uma promise, usando $translate.instant() :

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

A desvantagem de usar $translate.instant() pode ser que o arquivo de idioma ainda não esteja carregado se você estiver carregando-o como asynchronous.

Usando o filtro fornecido

Este é o meu caminho preferido desde que eu não tenho que lidar com promises dessa maneira. A saída do filtro pode ser definida diretamente para uma variável de escopo.

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

Usando a diretiva fornecida

Como o @PascalPrecht é o criador desta incrível biblioteca, eu recomendo ir com o seu conselho (veja a sua resposta abaixo) e usar a diretiva fornecida que parece lidar com traduções muito inteligentes.

A diretiva cuida da execução assíncrona e também é inteligente o suficiente para desvendar IDs de tradução no escopo se a tradução não tiver valores dynamics.

Na verdade, você deve usar a diretiva translate para essas coisas.

 

A diretiva cuida da execução assíncrona e também é inteligente o suficiente para desvendar IDs de tradução no escopo se a tradução não tiver valores dynamics.

No entanto, se não houver nenhuma maneira de contornar e você realmente precisar usar o serviço $translate no controlador, você deve encapsular a chamada em um evento $translateChangeSuccess usando $rootScope em combinação com $translate.instant() desta forma:

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

Então, por que $rootScope e não $scope ? A razão para isso é que, em events de tradução angular, $emit são $emit em $rootScope vez de $broadcast em $scope porque não precisamos transmitir por toda a hierarquia de escopo.

Por que $translate.instant() e não apenas $translate() asynchronous? Quando o evento $translateChangeSuccess é acionado, é certo que os dados de tradução necessários estão lá e nenhuma execução assíncrona está acontecendo (por exemplo, execução do carregador asynchronous), portanto podemos usar $translate.instant() que é síncrono e apenas assume que as traduções Estão disponíveis.

Desde a versão 2.8.0 há também $translate.onReady() , que retorna uma promise que é resolvida assim que as traduções estiverem prontas. Veja o changelog .

Para fazer uma tradução no controller você poderia usar o serviço $translate :

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

Essa declaração só faz a tradução na ativação do controlador, mas não detecta a mudança de tempo de execução na linguagem. Para conseguir esse comportamento, você poderia ouvir o evento $rootScope : $translateChangeSuccess e fazer a mesma tradução lá:

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

É claro, você poderia encapsular o serviço $translate em um método e chamá-lo no controller e no listener $translateChangeSucess .

O que está acontecendo é que Angular-translate está observando a expressão com um sistema baseado em events e, assim como em qualquer outro caso de binding ou de binding bidirecional, um evento é triggersdo quando os dados são recuperados e o valor alterado, o que obviamente não funciona para tradução. Dados de tradução, ao contrário de outros dados dynamics na página, devem, é claro, aparecer imediatamente para o usuário. Não pode aparecer depois que a página é carregada.

Mesmo que você consiga depurar com sucesso esse problema, o maior problema é que o trabalho de desenvolvimento envolvido é enorme. Um desenvolvedor precisa extrair manualmente cada string do site, colocá-la em um arquivo .json, referenciá-la manualmente pelo código da string (ou seja, ‘pageTitle’ neste caso). A maioria dos sites comerciais tem milhares de strings para as quais isso precisa acontecer. E isso é apenas o começo. Agora você precisa de um sistema para manter as traduções sincronizadas quando o texto subjacente for alterado em alguns deles, um sistema para enviar os arquivos de tradução aos vários tradutores, reintegrá-los à compilation, reimplementar o site para que os tradutores possam ver suas mudanças no contexto, e assim por diante.

Além disso, como esse é um sistema baseado em events ‘vinculativo’, um evento está sendo acionado para cada sequência na página, que não apenas é uma maneira mais lenta de transformar a página, mas também pode retardar todas as ações na página. se você começar a adicionar um grande número de events a ele.

De qualquer forma, usar uma plataforma de tradução pós-processamento faz mais sentido para mim. Usando o GlobalizeIt, por exemplo, um tradutor pode simplesmente ir a uma página no site e começar a editar o texto diretamente na página para o seu idioma, e é isso: https://www.globalizeit.com/HowItWorks . Não é necessária programação (embora possa ser programaticamente extensível), integra-se facilmente ao Angular: https://www.globalizeit.com/Translate/Angular , a transformação da página acontece de uma só vez e exibe sempre o texto traduzido com a renderização inicial da página.

Divulgação completa: sou co-fundador 🙂

Intereting Posts