Современное веб-приложение должно уметь подстраиваться под пользователя, обеспечивая понятный и дружелюбный интерфейс. Динамическая локализация является одним из таких умений.
В декларативном фреймворке Evado локализация поддерживается на двух уровнях. Первый уровень - это когда перевод осуществляется на сервере, при подготовке контента. Второй - это перевод непосредственно в браузере клиента.
Серверная локализация
За локализацию на стороне сервера отвечает компонент i18n. Для его конфигурации используются следующие параметры:
- language - кодовое название языка приложения.
- sources - конфигурация источников сообщений (см. ниже).
- sourceLanguage - кодовое название языка, который является ключом для сообщений (по умолчанию для всех источников, но может быть переопределен в конкретном источнике).
Данные для перевода хранятся в источниках сообщений (по умолчанию - это файлы в папке message). Источник объединяет сообщения в отдельную категорию. Например, тексты для рассылок или уведомлений.
Ключи сообщений
Обычно ключом (идентификатором) сообщения является фраза на исходном языке источника. В коде указывается ключевая фраза, а при переводе подставляется соответствующая ей фраза на целевом языке. Если перевод отсутствует, то будет отображен ключ.
В некоторых случаях, лучшим вариантом будет использование специальных ключей (например, app.shop.notify.success) вместо обычных фраз. Для этого необходимо для источника указать параметр forceTranslation: true. В этом случае, сначала производится поиск перевода по ключу для целевого языка, если он отсутствует, то ищется перевод для исходного языка, а если и он отсутствует, то тогда отобразится сам ключ.
Методы перевода
Для удобства использования методы перевода вынесены в разные части приложения, но фактически они являются обертками для основного метода translate из компонента i18.
- i18.translate (message, params, source, language) - переводит сообщение с указанными параметрами (см. ниже), категорией сообщений (источником) и целевым языком.
- [module].translate (message, params, source = 'app') - переводит сообщение, используя по умолчания основную категорию и целевой язык приложения.
- [controller].translate (message, params, source = 'app') - переводит сообщение, используя по умолчания основную категорию приложения и целевой язык из текущего запроса. В шаблонах представления метод [controller].translate доступен через псевдоним _t.
Динамические значения сообщения
Часто бывает необходимо не просто найти соответствие фразы на другом языке, но и добавить в неё некоторые значения. Для этого в сообщении указываются специальные метки. Например, 'Value must be no greater than {max}': 'Значение должно быть не больше {max}'. Сами значения передаются через параметры во время перевода:
translate('Value must be no greater than {max}', { max: 12 }).
Каждое динамическое значение может быть автоматически отформатировано в соответствии с целевым языком. Типичный пример - это отображение даты:
- Источник: 'Current date: {now}': 'Текущая дата: {now}'.
- Метод: translate('Current date: {now}', {now: [new Date, 'dateLongFormat']}).
- Результат на английском: Current date: May 11, 2023.
- Результат на русском: Текущая дата: 11 мая 2023.
Явное форматирование значений (в том числе с учетом языка) может осуществляться перед переводом с помощью компонента formatter.
Тип Message
Если над сообщением необходимо произвести какие-либо дополнительные действия (передавать его между функциями), то лучшим решением будет создание объекта класса Message, который содержит в себе как ключевую фразу, так и значения динамических параметров.
Перевести объект Message можно через его собственный метод translate, передав ему соответствующий компонент i18n.
Клиентская локализация
Другой способ локализации, это подмена исходных сообщений непосредственно в браузере клиента. Этот способ оптимален с точки зрения производительности. Клиент один раз выкачивает файл со всеми сообщениями для выбранного языка, и затем использует его для перевода отображаемой информации.
Файлы источников сообщений создаются в директории доступной для внешних запросов (например, web/app/message/ru.js). Данные оформлены в объекты ключ - значение (исходное сообщение - целевое сообщение).
Изначально в приложении существует единственная категория сообщений - Jam.i18n.defaults. Доступ к ней осуществляется по умолчанию - без указания категории. Если вы захотите добавить собственные сообщения в эту категорию, то обязательно сохраните существующие, расширив объект своими данными.
Иерархия категорий
Новые категории сообщений подключаются напрямую к классу Jam.I18n. Например, Jam.I18n.myCategory = { 'My text': 'Мой текст' }.
Если в название категории имеется точка, то формируется иерархия, которая определяет последовательность поиска перевода в категориях. Например, созданы следующие категории:
Jam.I18n['meta'] = { 'Name': 'Название' },
Jam.I18n['meta.myClass'] = { 'File': 'Файл' },
Jam.I18n['meta.myClass.myAttr'] = { 'Edit': 'Редактировать' }.
При вызове Jam.t('Name', 'meta.myClass.myAttr') поиск перевода будет происходить последовательно в категориях meta.myClass.myAttr, meta.myClass и meta. Первое найденное значение и будет возвращено.
Перевод с помощью HTML-разметки
Перевод на клиенте может быть сделан, как непосредственно с помощью вызова метода Jam.i18n.translate (псевдоним - Jam.t), так и автоматически - по данным HTML-разметки. Если указан атрибут data-t, то содержимое HTML-тэга будет переведено. Например, <span data-t="">Birth date</span> превратится в <span data-t="">Дата рождения</span>. Можно указать конкретную категорию сообщений - <span data-t="profile">Age</span>.
Кроме содержимого HTML-тэга переводятся и некоторые его атрибуты (по умолчанию - title и placeholder). Для них так же используется категория указанная в data-t, но ее можно изменить на любую другую с помощью атрибутов data-t-title и data-t-placeholder. Задать собственный список атрибутов для перевода можно в data-t-attrs.
Заключение
Хорошая локализация значительно расширяет круг пользователей, упрощая взаимодействие с вашим приложением. А фреймворк Evado предоставляет надежные инструменты, упрощая разработку и поддержку хорошей локализации.
Пример демо-приложения доступен в открытом репозитории.