Правильне використання для кутового перекладу в контролерах


121

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

Питання

Це відоме питання / обмеження? Як це можна вирішити?

Відповіді:


69

EDIT : Будь ласка, дивіться відповідь PascalPrecht (автора углового перекладу) для кращого рішення.


Асинхронність завантаження викликає проблему. Розумієте, з {{ pageTitle | translate }}, Кутовий буде спостерігати за виразом; при завантаженні даних про локалізацію значення виразу змінюється і екран оновлюється.

Отже, ви можете зробити це самостійно:

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

Однак це запустить спостережуваний вираз на кожному дайджест-циклі. Це неоптимально і може спричинити видиме погіршення продуктивності. У будь-якому випадку це те, що робить Angular, тому це не може бути так погано ...


Дякую! Я б очікував, що використання фільтра в режимі перегляду або в контролері буде вести себе точно так само. Це, мабуть, не так.
ndequeker

Я б сказав, що використання a $scope.$watchє досить зайвим, оскільки Angular Translate пропонує послугу, яку слід використовувати в контролерах. Дивіться мою відповідь нижче.
Робін ван Баален

1
Фільтр Angular Translate не потрібно, оскільки $translate.instant()пропонує те саме, що і послуга. Окрім цього, зверніть увагу на відповідь Паскаля.
knalli

Я згоден, використання $ watch надмірно. Нижче відповіді більш правильне використання.
jpblancoder

141

Рекомендовано: не перекладайте в контролер, не перекладайте на ваш погляд

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

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

Використовуючи наданий фільтр

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

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

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

Використовуючи надану директиву

Оскільки @PascalPrecht є автором цієї дивовижної бібліотеки, я б рекомендував піти з його порадою (див. Його відповідь нижче) і використовувати надану директиву, яка, здається, обробляє переклади дуже розумно.

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


Якби ви спробували це замість того, щоб написати той незв'язаний коментар, ви б вже знали відповідь. Коротка відповідь: так. Це можливо.
Робін ван Бален

1
у вашому прикладі з фільтром у контролері: як у режимі instant (), якщо мовний файл не завантажений, це не працюватиме правильно? Чи не варто в такому випадку використовувати годинник? Або ви хочете сказати: "використовувати фільтр, лише якщо ви знаєте, що переклади завантажені?
Бомбінош

@Bombinosh Я б сказав, використовуйте метод фільтра, якщо ви знаєте, що переклади завантажуються. Особисто я б рекомендував не завантажувати переклади динамічно, якщо цього не потрібно. Це обов'язкова частина вашої програми, тому вам краще не хочеться, щоб користувач її чекав. Але це особиста думка.
Робін ван Бален

Сенс перекладів полягає в тому, що вони можуть змінюватися на налаштуваннях користувачів або навіть на дії користувачів. Тож вам потрібно загалом завантажувати їх динамічно. Принаймні, якщо кількість рядків для перекладу є важливою та / або якщо у вас є багато перекладів.
PhiLho

4
Коли переклад здійснюється в HTML, цикл дайвінгу виконується двічі, але лише один раз у контролері. У 99% випадків це, мабуть, не має значення, але у мене виникли проблеми із жахливою роботою в кутовій сітці інтерфейсу з перекладом у багатьох клітинках.
Крайній

123

Насправді вам слід скористатися директивою перекладу для таких матеріалів.

<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 ? Причиною тому є те, що події у кутовому перекладі $emitредагуються, $rootScopeа не $broadcastредагуються$scope оскільки нам не потрібно транслюватись через всю ієрархію діапазону.

Чому? $translate.instant() і не просто асинхронізацію $translate()? Коли $translateChangeSuccessподія запускається, то впевнене, що потрібні дані перекладу є, і не відбувається асинхронне виконання (наприклад, виконання асинхронного завантажувача), тому ми можемо просто використовувати$translate.instant() синхронний і просто припускати, що переклади доступні.

Оскільки версія 2.8.0 також є $translate.onReady(), яка повертає обіцянку, яка буде вирішена, як тільки переклади будуть готові. Дивіться журнал змін .


Чи можуть виникнути проблеми з продуктивністю, якщо я використовую директиву перекладу замість фільтра? Також я вірю внутрішньо, він спостерігає за поверненням значення instant (). То чи видаляє годинник, коли поточний обсяг руйнується?
Нілеш

Я спробував скористатись вашою пропозицією, але вона не працює, коли значення змінної діапазону динамічно змінюється.
Нілеш

10
Насправді завжди краще уникати фільтрів, де це можливо, оскільки вони сповільнюють ваш додаток, оскільки вони завжди встановлюють нові годинники. Однак директива йде трохи далі. Він перевіряє, чи має він спостерігати значення ідентифікатора перекладу чи ні. Це дозволяє краще виконувати ваш додаток. Не могли б ви зробити планку і зв’язати мене з нею, щоб я міг поглянути далі?
Паскаль Прехт

Plunk : plnkr.co/edit/j53xL1EdJ6bT20ldlhxr Напевно, у моєму прикладі директива вирішує не дивитися значення. Крім того, як окрема проблема, мій користувальницький обробник помилок викликається, якщо ключ не знайдено, але він не відображає повернутий рядок. Я зроблю ще одну планку за це.
Нілеш

2
@PascalPrecht Лише питання, чи є хорошою практикою використовувати bind-один раз з перекладом? Як це {{::'HELLO_WORLD | translate}}'.
Zunair Zubair

5

Для перекладу в контролер ви можете скористатися $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слухачі.


1

Що відбувається, так це те, що Angular-translate дивиться вираз із системою, що базується на подіях, і так само, як і в будь-якому іншому випадку прив'язки або двостороннього прив'язки, при отриманні даних знімається подія і змінюється значення, яке очевидно, не працює для перекладу. Дані перекладу, на відміну від інших динамічних даних на сторінці, повинні, звичайно, негайно відображатися користувачеві. Він не може з’являтися після завантаження сторінки.

Навіть якщо ви можете успішно налагодити цю проблему, більша проблема полягає в тому, що робота з розробки є величезною. Розробник повинен вручну витягти кожну рядок на сайті, помістити її у файл .json, вручну посилатися на нього за допомогою рядкового коду (тобто у цьому випадку "pageTitle"). Більшість комерційних сайтів мають тисячі рядків, для яких це має відбутися. І це лише початок. Тепер вам потрібна система зберігання перекладів синхронізовано, коли основний текст змінюється в деяких з них, система для надсилання файлів перекладу різним перекладачам, реінтеграції їх у збірку, передислокації сайту, щоб перекладачі могли бачити їх зміни в контексті, і далі, і далі.

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

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

Повне розкриття: Я є співзасновником :)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.