Правильное использование для angular -транслята в контроллерах

Я использую angular-translate для i18n в приложении AngularJS.

Для каждого вида приложения есть выделенный контроллер. В приведенных ниже контроллерах я устанавливаю значение, которое будет отображаться как заголовок страницы.

код

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';, перевод работает отлично с первого раза. Проблема в том, что я не всегда хочу использовать переводы (например, для второго контроллера я просто хочу передать необработанную строку).

Вопрос

Является ли это известной проблемой/ограничением? Как это можно решить?

Ответ 1

РЕДАКТИРОВАТЬ. Пожалуйста, см. ответ от 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, поэтому он не может быть так плохо...

Ответ 2

Рекомендуется: не переводить в контроллер, переводить на ваш взгляд

Я бы рекомендовал оставить ваш контроллер свободным от логики перевода и перевести строки прямо в ваше представление следующим образом:

<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() может быть то, что языковой файл еще не загружен, если вы загружаете его async.

Используя предоставленный фильтр

Это мой предпочтительный способ, так как мне не нужно обрабатывать promises таким образом. Вывод фильтра может быть напрямую установлен на переменную области видимости.

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

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

Используя предоставленную директиву

Так как @PascalPrecht является создателем этой удивительной библиотеки, я бы рекомендовал вам с его советом (см. его ответ ниже) и использовать предоставленную директиву, которая, как представляется, обрабатывать переводы очень умные.

Директива выполняет асинхронное выполнение, а также достаточно умна, чтобы вывести идентификаторы перевода в область видимости, если перевод не имеет динамических значений.

Ответ 3

Собственно, вы должны использовать директиву translate для таких материалов.

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

Директива выполняет асинхронное выполнение, а также достаточно умна, чтобы вывести идентификаторы перевода в область видимости, если перевод не имеет динамических значений.

Однако, если нет пути, и вы действительно должны использовать $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 $emit ed на $rootScope, а не $broadcast ed на $scope, потому что нам не нужно транслировать всю иерархию областей.

Почему $translate.instant(), а не только async $translate()? Когда выполняется событие $translateChangeSuccess, он уверен, что необходимы необходимые данные для перевода и не выполняется асинхронное выполнение (например, выполнение асинхронного загрузчика), поэтому мы можем просто использовать $translate.instant(), который является синхронным, и просто предполагает, что переводы доступны.

Начиная с версии 2.8.0 также есть $translate.onReady(), который возвращает обещание, которое разрешается, как только перевод будет готов. Посмотрите журнал изменений.

Ответ 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'];
        });
    });

Конечно, вы можете инкапсулировать службу $translate в методе и вызвать ее в контроллере и в прослушивателе $translateChangeSucess.

Ответ 5

Что происходит, так это то, что Angular -translate наблюдает за выражением с помощью системы на основе событий и так же, как и в любом другом случае привязки или двусторонней привязки, событие запускается при извлечении данных и значение изменилось, что явно не работает для перевода. Данные перевода, в отличие от других динамических данных на странице, должны, конечно, сразу же появляться у пользователя. Он не может появиться после загрузки страницы.

Даже если вы можете успешно отладить эту проблему, большая проблема заключается в том, что работа по разработке огромна. Разработчик должен вручную извлечь каждую строку на сайте, поместить ее в файл .json, вручную ссылаться на нее по строковому (в данном случае, "pageTitle" ). На большинстве коммерческих сайтов есть тысячи строк, для которых это должно произойти. И это только начало. Теперь вам нужна система хранения синхронных переводов, когда в некоторых из них изменяется исходный текст, система отправки файлов перевода различным переводчикам, реинтеграция их в сборку, перераспределение сайта, чтобы переводчики могли видеть их изменения в контексте, и так далее.

Кроме того, поскольку это "привязка", система на основе событий, событие запускается для каждой отдельной строки на странице, что не только является более медленным способом преобразования страницы, но и может замедлять все действия на страницу, если вы начнете добавлять к ней большое количество событий.

В любом случае, использование платформы обработки после обработки имеет больше смысла для меня. Например, используя GlobalizeIt, переводчик может просто перейти на страницу на сайте и начать редактирование текста непосредственно на странице для своего языка и что он: https://www.globalizeit.com/HowItWorks. Не требуется программирования (хотя он может быть программно расширяемым), он легко интегрируется с Angular: https://www.globalizeit.com/Translate/Angular, происходит преобразование страницы за один раз, и он всегда отображает переведенный текст с первоначальной визуализацией страницы.

Полное раскрытие: я являюсь соучредителем:)