javascript - ngx - Uso correcto para angular-translate en controladores
ngx-translate angular 6 (5)
Recomendado: no traduzca en el controlador, traduzca en su vista
Recomiendo mantener su controlador libre de la lógica de traducción y traducir las cadenas directamente dentro de su vista de esta manera:
<h1>{{ ''TITLE.HELLO_WORLD'' | translate }}</h1>
Usando el servicio proporcionado
Angular Translate proporciona el servicio $translate
que puede usar en sus Controladores.
Un ejemplo de uso del servicio $translate
puede ser:
.controller(''TranslateMe'', [''$scope'', ''$translate'', function ($scope, $translate) {
$translate(''PAGE.TITLE'')
.then(function (translatedValue) {
$scope.pageTitle = translatedValue;
});
});
El servicio de traducción también tiene un método para traducir cadenas directamente sin la necesidad de manejar una promesa, utilizando $translate.instant()
:
.controller(''TranslateMe'', [''$scope'', ''$translate'', function ($scope, $translate) {
$scope.pageTitle = $translate.instant(''TITLE.DASHBOARD''); // Assuming TITLE.DASHBOARD is defined
});
La desventaja de utilizar $translate.instant()
podría ser que el archivo de idioma aún no está cargado si lo está cargando como sincronización.
Usando el filtro provisto
Esta es mi forma preferida, ya que no tengo que manejar las promesas de esta manera. La salida del filtro se puede establecer directamente en una variable de ámbito.
.controller(''TranslateMe'', [''$scope'', ''$filter'', function ($scope, $filter) {
var $translate = $filter(''translate'');
$scope.pageTitle = $translate(''TITLE.DASHBOARD''); // Assuming TITLE.DASHBOARD is defined
});
Usando la directiva provista
Dado que @PascalPrecht es el creador de esta increíble biblioteca, recomendaría ir con su asesoramiento (ver su respuesta a continuación) y utilizar la directiva provista que parece manejar traducciones muy inteligentes.
La directiva se ocupa de la ejecución asincrónica y también es lo suficientemente inteligente como para desarmar identificaciones de traducción en el alcance si la traducción no tiene valores dinámicos.
Estoy usando angular-translate para i18n en una aplicación AngularJS.
Para cada vista de aplicación, hay un controlador dedicado. En los controladores a continuación, configuro el valor para que se muestre como el título de la página.
Código
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'';
}])
Estoy cargando los archivos de traducción usando la extensión angular-translate-loader-url .
Problema
En la carga de la página inicial, se muestra la clave de traducción en lugar de la traducción para esa clave. La traducción es Hello, World!
, pero estoy viendo HELLO_WORLD
.
La segunda vez que voy a la página, todo está bien y se muestra la versión traducida.
Supongo que el problema tiene que ver con el hecho de que tal vez el archivo de traducción aún no se haya cargado cuando el controlador está asignando el valor a $scope.pageTitle
.
Observación
Al usar <h1>{{ pageTitle | translate }}</h1>
<h1>{{ pageTitle | translate }}</h1>
y $scope.pageTitle = ''HELLO_WORLD'';
, la traducción funciona perfecta desde el primer momento. El problema con esto es que no siempre quiero usar traducciones (por ejemplo, para el segundo controlador solo quiero pasar una cadena en bruto).
Pregunta
¿Es este un problema / limitación conocida? ¿Cómo se puede resolver esto?
En realidad, debería usar la directiva de traducción para tales cosas.
<h1 translate="{{pageTitle}}"></h1>
La directiva se ocupa de la ejecución asincrónica y también es lo suficientemente inteligente como para desarmar identificaciones de traducción en el alcance si la traducción no tiene valores dinámicos.
Sin embargo, si no hay forma de evitarlo y realmente tiene que usar el servicio $translate
en el controlador, debe envolver la llamada en un evento $translateChangeSuccess
usando $rootScope
en combinación con $translate.instant()
esta manera:
.controller(''foo'', function ($rootScope, $scope, $translate) {
$rootScope.$on(''$translateChangeSuccess'', function () {
$scope.pageTitle = $translate.instant(''PAGE.TITLE'');
});
})
Entonces, ¿por $rootScope
y no $scope
? La razón de esto es que en los eventos de traducción angular se $emit
en $rootScope
lugar de en $broadcast
ed en $scope
porque no necesitamos transmitir a través de toda la jerarquía del alcance.
¿Por qué $translate.instant()
y no solo async $translate()
? Cuando se $translateChangeSuccess
evento $translateChangeSuccess
, es seguro que los datos de traducción necesarios están allí y no está ocurriendo una ejecución asíncrona (por ejemplo, la ejecución del cargador asíncrono), por lo tanto, solo podemos usar $translate.instant()
que es sincrónico y simplemente supone que las traducciones están disponibles.
Desde la versión 2.8.0 también hay $translate.onReady()
, que devuelve una promesa que se resuelve tan pronto como las traducciones estén listas. Ver el registro de cambios
Lo que está sucediendo es que Angular-translate está viendo la expresión con un sistema basado en eventos, y al igual que en cualquier otro caso de vinculación o vinculación bidireccional, se dispara un evento cuando se recuperan los datos y se cambia el valor, lo cual obviamente no funciona para la traducción. Los datos de traducción, a diferencia de otros datos dinámicos en la página, deben, por supuesto, mostrarse inmediatamente al usuario. No puede aparecer después de que se carga la página.
Incluso si puede depurar con éxito este problema, el mayor problema es que el trabajo de desarrollo involucrado es enorme. Un desarrollador tiene que extraer manualmente cada cadena en el sitio, ponerlo en un archivo .json, consultarlo manualmente por código de cadena (es decir, ''pageTitle'' en este caso). La mayoría de los sitios comerciales tienen miles de cadenas para las que esto debe suceder. Y eso es sólo el comienzo. Ahora necesita un sistema para mantener las traducciones sincronizadas cuando el texto subyacente cambia en algunos de ellos, un sistema para enviar los archivos de traducción a los diversos traductores, para reintegrarlos en la compilación, para volver a implementar el sitio para que los traductores puedan ver sus cambios en el contexto, y así sucesivamente.
Además, como se trata de un sistema "vinculante" basado en eventos, se dispara un evento por cada cadena de la página, lo que no solo es una forma más lenta de transformar la página, sino que puede ralentizar todas las acciones en la página, si comienza a agregarle un gran número de eventos.
De todos modos, usar una plataforma de traducción de post-procesamiento tiene más sentido para mí. Usando GlobalizeIt, por ejemplo, un traductor puede ir a una página en el sitio y comenzar a editar el texto directamente en la página para su idioma, y eso es todo: https://www.globalizeit.com/HowItWorks . No se necesita programación (aunque puede ser extensible mediante programación), se integra fácilmente con Angular: https://www.globalizeit.com/Translate/Angular , la transformación de la página ocurre de una vez, y siempre muestra el texto traducido con la representación inicial de la página.
Descripción completa: soy cofundador :)
Para realizar una traducción en el controlador, puede usar el servicio $translate
:
$translate([''COMMON.SI'', ''COMMON.NO'']).then(function (translations) {
vm.si = translations[''COMMON.SI''];
vm.no = translations[''COMMON.NO''];
});
Esa afirmación solo hace la traducción en la activación del controlador, pero no detecta el cambio de tiempo de ejecución en el lenguaje. Para lograr ese comportamiento, puede escuchar el evento $rootScope
: $translateChangeSuccess
y hacer la misma traducción allí:
$rootScope.$on(''$translateChangeSuccess'', function () {
$translate([''COMMON.SI'', ''COMMON.NO'']).then(function (translations) {
vm.si = translations[''COMMON.SI''];
vm.no = translations[''COMMON.NO''];
});
});
Por supuesto, puede encapsular el servicio $translate
en un método y llamarlo en el controlador y en el oyente $translateChangeSucess
.
EDITAR : Consulte la respuesta de PascalPrecht (el autor de angular-translate) para una mejor solución.
La naturaleza asíncrona de la carga causa el problema. Usted ve, con {{ pageTitle | translate }}
{{ pageTitle | translate }}
, Angular observará la expresión; cuando se cargan los datos de localización, el valor de la expresión cambia y la pantalla se actualiza.
Entonces, puedes hacer eso tú mismo:
.controller(''FirstPageCtrl'', [''$scope'', ''$filter'', function ($scope, $filter) {
$scope.$watch(
function() { return $filter(''translate'')(''HELLO_WORLD''); },
function(newval) { $scope.pageTitle = newval; }
);
});
Sin embargo, esto ejecutará la expresión observada en cada ciclo de resumen. Esto no es óptimo y puede o no causar una degradación visible del rendimiento. De todos modos, es lo que hace Angular, así que no puede ser tan malo ...