Использование angular-translate для локализации приложений ASP.NET MVC + AngularJS
Привет, Хабр! В этой статье речь пойдет о применении библиотеки angular-translate для локализации приложения. Расскажем о возможностях этой библиотеки, опишем проблемы, которые могут возникнуть, и дадим советы по их решению (основываясь, конечно, на собственном опыте).ВведениеДля локализации, то есть перевода приложения на другой язык, существует концепция языковых ресурсов. Суть её проста: вместо того, чтобы заводить константу с текстом, мы проставляем в коде некоторый идентификатор, обычно называемый ключом перевода [translation key], который может использоваться как на этапе сборки приложения, так и во время его исполнения. Встречая ключ перевода, компоновщик приложения (или runtime) заменяет его на нужный текст, и пользователь видит надпись уже на нужном ему языке. Конечно, полностью проблема перевода этим не решается. Например, строки текста вполне могут иметь разную длину на разных языках, и тогда перевод может испортить пользовательский интерфейс, выдав обрезанные надписи. Кошмаром для разработчика может стать перевод на арабские языки, где текст читается справа налево. Тем не менее, такая технология имеет место быть и успешно применяется в ряде случаев.В мире .Net стандартом работы с языковыми ресурсами являются файлы *.Resx. Специальная утилита resgen превращает их в файлы *.resources., после чего эти ресурсы встраиваются либо в основную сборку, либо во вспомогательные сборки, а затем могут быть использованы в приложении.
В веб-проектах изначально использовалась такая же технология, и такое решение было удачным, так как страницы генерировались в основном на сервере, где могли быть размещены языковые ресурсы. Однако со временем клиентская часть усложнялась, и проблем с ней становились всё больше. Решали их обычно двумя способами: либо делали альтернативные JS для разных языков (такой подход используется, например, в MS SharePoint), либо передавали локализованные данные в виде параметров в коде *.ASPX-страниц. Понятно, что второй подход больше подходит к небольшим блокам клиентского кода — таким, где сообщений и тестовых элементов относительно немного. Что же касается первого, то его недостаток в том, что локализация «размазывается» между файлами ресурсов и локальными версиями JavaScript.
В последнее время во многих приложениях UI стал генерироваться непосредственно на клиенте, необходимость в серверных ресурсах стала меньше. Вместе с тем, широкое распространение получили framework«и для разработки приложений, реализующие достаточно богатые модели программирования (такие как knockout.js, angular.js, backbone.js и т.д.). Каждая из моделей рекомендует собственную организацию проекта, поэтому, по-видимому, наиболее удобным будет, чтобы framework имел свою модель локализации.
Итак, у нас есть приложение, которое необходимо научить работать с другим языком. Конечно, мы понимаем, что в идеальном мире об этом стоит позаботиться ещё на стадии проектировании приложения, однако в реальности поддержка локализации или «локализуемость» — это то, чем часто жертвуют на первых этапах разработки в силу понятных причин.
Кстати говоря, MSDN рекомендует добавлять поддержку обеспечения работы в 3 шага:
Глобализация — т.е. поддержка систем записи, используемых календарей, соглашения о формате даты и времени, соглашения о представлении числовых и денежных величин, а также правила сортировки. Локализуемость — т.е. отделения логики приложение от локализуемых ресурсов. Локализация — непосредственно перевод приложения на целевой язык. При разработке приложения с использованием AngularJS есть несколько способов локализовать приложение. Самый простой — создать отдельные шаблоны страниц для всех поддерживаемых языков. Минусы подобного подхода очевидны: трудозатратность, дублирование кода, отсутствие модульности, несоответствие подходу SPA и т.д. Оптимальным решением будет некий универсальный способ для перевода страниц на основе ресурсов (словарей). Здесь можно пойти двумя путями: реализовать свой собственный сервис локализации или использовать готовые средства, такие как библиотека angular-translate. О ней расскажем подробнее.Основные возможности библиотеки angular-translate Библиотека angular-translate реализует солидный функционал для нужд локализации. Он представлен набором сервисов, фильтров и директив для локализации. Для работы библиотеки нужны JSON-файлы с ключами и переводами. /* file:»~/fruits/en.json» */ /* file »~/fruits/ru.json» */ { { «APPLE»: «Apple», «APPLE»: «Яблоко», «ORANGE»: «Orange» «ORANGE»: «Апельсин» } } Библиотека способна подгружать с сервера необходимые файлы по необходимости (так называемый lazy-loading).Подключить angular-translate в проект можно через менеджер пакетов Bower. К сожалению, на момент написания статьи в стандартном репозитории Nuget пакета для angular-translate не было, поэтому пришлось вручную добавить библиотеку angular-translate.js и ссылку на нее, a также добавить модуль «pascalprecht.translate» в angualr как зависимость.Основная служба angular-translate — это провайдер $translate. На этапе конфигурации (обратите внимание, что нужно использовать обращение $translateProvider, например) сервис позволяет регистрировать таблицы с встроенными в приложение переводами, асинхронные загрузчики (urlLoader, staticFilesLoader, partialLoader), а также выбирать хранилищe (cookie, local) для настроек локализации. На этапе выполнения сервис доступен как $translate, являясь одновременно и функцией, используемой для перевода, и «объектом», содержащим функции для настройки перевода.
Перевод В angular-translate перевод возможно осуществить тремя различными способами: C использованием сервиса »$translate»:
$translate ('APPLE').then (function (result) { $scope.fruitName = result; }); $translate не обеспечивает two-way binding по умолчанию, в результате чего, при смене языка в runtime, текст, переведённый с помощью $trnaslate, не изменится автоматически. Исправить это возможно, подписавшись на событие $translateChangeSucces в $rootScope: $rootScope.$on ('$translateChangeSuccess', function () { $translate ('HEADLINE').then (function (translation) { $scope.headline = translation; }); }); $trnaslate стандартно работает асинхронно, но поддерживает и синхронный вариант пере-вода с использованием встроенного метода «instant».С использованием директивы «translate»:
{{'APPLE' | translate}}
Загрузка словарей В angular-translate существует несколько способов добавить необходимые словари (обратите внимание, что в словарях можно использовать namespace«ы, реализованные посредством вложенных json-объектов): Встроенные в приложение словариРазмещение словарей непосредственно в коде и/или из json-файлов ресурсов в составе проекта. Подключается так:var fruits = { APPLE: 'Apple' CITRIC: { ORANGE: 'Orange' } }; $translateProvider .translations ('en', fruits) .preferredLanguage ('en'); Асинхронная загрузкаПозволяет загружать словари с сервера по необходимости. Реализуется стандартными загрузчиками (есть возможность определить свой загрузчик): urlLoader. Загружает словари по указанному url. Реализован как отдельный пакет в Bower «angular-translate-loader-url». Подключается так: $translateProvider.useUrlLoader ('foo/bar.json'); $translateProvider.preferredLanguage ('en'); В итоге, загрузчик из примера пошлёт следующий запрос: foo/bar.json? lang=en. staticFilesLoader. Используется для загрузки нескольких файлов для локализации, имеющих заданный формат (prefix и suffix). Реализован как отдельный пакет в Bower. Подключаются так: $translateProvider.useStaticFilesLoader ({ prefix: 'locale-', suffix: '.json' }); $translateProvider.preferredLanguage ('en'); partialLoader. Используется для частичной загрузки данных локализации, структурированных на сервере по шаблону (например,»/l10n/{part}/{lang}» или »/l10n/{lang}/{part}). Он асинхронно загружает только указанные файлы (части) после вызова соответствующей функции (addPart). Идёт в составе пакета angular-translate-loader-partial. Пример: $translatePartialLoaderProvider.addPart ('fruits'); $translateProvider.useLoader ('$translatePartialLoader', { urlTemplate: '/l10n/{part}/{lang}.json' }); $translateProvider.preferredLanguage ('en'); Интерполируемые переменные Angular-translate поддерживает наличие в json-словарях «интерполируемых переменных», например: { «DELICIOUS_FRUIT»:»{{fruit_name}} is delicious!» } Подобного вида строки могут в дальнейшем быть использованы следующим образом: Сервисом:
$translate ('DELICIOUS_FRUIT', { fruit_name: 'Apple' });
Фильтром:
{{ 'DELICIOUS_FRUIT' | translate:'{ fruit_name: «Apple» }' }}
или {{ 'DELICIOUS_FRUIT' | translate: fruitData }}
где
$scope.fruitData = {
o fruit_name: 'Apple'
o };
Директивой: