Интернационализация проекта. Михаил Шилов
TRANSCRIPT
Интернационализация проекта
i18n
i18ninternationalization
интернационализациякомплекс мер, обеспечивающих возможность
локализации проекта
l10nlocalization
локализацияадаптация содержания проекта под
культурно-языковые предпочтения целевой аудитории
- Wow, this startup can be worldwide!
- Молодцы, позаботились о людях!
Что локализуем?
Что локализуем?1. Интерфейс
a. текстыb. форматыc. направлениеd. сообщения системы
2. Данныеa. справочныеb. пользовательские
Что локализуем?1. Интерфейс
a. текстыb. форматы (даты, валюта, числа)c. направлениеd. сообщения системы
2. Данныеa. справочныеb. пользовательские
Что локализуем?1. Интерфейс
a. текстыb. форматыc. направление (ltr vs rtl)d. сообщения системы
2. Данныеa. справочныеb. пользовательские
Что локализуем?1. Интерфейс
a. текстыb. форматыc. направлениеd. сообщения системы
2. Данныеa. справочныеb. пользовательские
Что локализуем?1. Интерфейс
a. текстыb. форматыc. направлениеd. сообщения системы
2. Данныеa. справочныеb. пользовательские
Что локализуем?1. Интерфейс
a. текстыb. форматыc. направление d. сообщения системы
2. Данныеa. справочныеb. пользовательские
FAIL
FAIL
FAIL
FAIL
Что локализуем?1. Интерфейс
a. текстыb. форматыc. направление d. сообщения системы
2. Данныеa. справочныеb. пользовательские (админка)
Как локализуем?
А знаете ли Вы, что ...
Рассуждать “в Германии 83 млн. человек, в Швейцарии 7 млн. (на немецком говорят 63%), а в Австрии 8 млн. - бинго!, всегда отображаем деньги в Евро” - нормально!
Только 4 млн. или 4.6% немецко-говорящих пользователей могут быть недовольны, зато не мучаем пользователей дополнительными вопросами (а с английским и испанским всё еще сложнее будет).
Если требуется максимальная гибкость локализации, то кроме языка (de) нужно будет учесть и регионы (de-DE, de-CH или de-AT - для Германии, Швейцарии или Австрии).
Как локализуем?
locale - идентификатор локали
“en” <- default“ru”“fr”“es”…
detectUserLocale()saveUserLocale(locale)getUserLocale()
loadTranslations(locale)
Accept-Language: locale
Как локализуем?Переводы текстов
Ожидания от формата:- произвольная группировка - NAMESPACE - подстановка значений - {{object.variable}} - человеческий формат (для переводчика)
JSON, YAML ✔
/src/translations/en.json ← defaultru.json
fr.jsones.json
key - translation
Как локализуем?Переводы текстов
Реальность (js runtime):- JSON требует конвертации в объект - YAML не поддерживается- обработка исходника затратна
Автоматика ✔translations/*.json => l10n/*.js
/l10n/en.js ← defaultru.js
fr.jses.js
<script src=”/l10n/{{locale}}.js”> </script>
key - translation
l10n
Как локализуем?Переводы текстов
Автоматика решает:
● нормализация исходников○ идентичность ключей в
локализациях○ “маркировка” непереведенных “”○ сортировка ключей
● fallback локализации ○ не переведенные значения
заполняются из default языка○ позволяет использовать один
файл локализации
ru src
ru l10n
Как локализуем?Код
Тексты в коде и шаблонах это:a. тяжелое наследиеb. так несовременно
Найти и заменить КЛЮЧАМИ_ПЕРЕВОДОВ ✔
<a href=”/”>Home</a><a href=”/” translate=”NAV.HOME”></a>
alert(‘Wrong way’)alert(i18n.translate(‘WAYS.WARN’))
Ctrl-F, Ctrl-C, Ctrl-V... new school
Как локализуем?Справочные данные
1. статика/data/{{locale}}/library.json
2. база данныхSELECT * FROM libraryWHERE locale = {{locale}}
или
SELECT * FROM library tLEFT OUTER JOIN
library_translations trON t.id = tr.recId AND tr.locale = {{locale}}
Как локализуем?Пользовательские данные
Запрос: GET /questionsЗаголовок: Accept-Language: en
SELECT s.id, CASE WHEN t.value IS NULL THEN s.label ELSE t.value END AS “label”FROM questions AS s LEFT JOIN translations AS t ON t.table = “questions” AND t.field = “label” AND t.rec_id = s.id AND t.locale = “en”
Ответ: { id:1, label: “How old are you?”}
i18n на примереFoodLogiQ
FoodLogiQ
Connect - Single Page Web AppHTML, JS, Go
ID - OAuth identity providerHTML, Go
API - RESTfull servicesGo
redirect if not authorized
token, locale_id
Accept-Language: locale_id
Localized data
FoodLogiQ - объем работ
1. Интерфейсa. текстыb. форматы (даты, валюта, числа)c. направлениеd. сообщения системы
2. Данныеa. справочные - опционально,
каждый конкретный случай обсуждаем отдельно
b. пользовательские
Connect● Любые строки, "захардкоженные" в
HTML и JS● Форматы
API● Результаты выполнения операции
(подробности, сообщения об ошибках)● E-mail● Нотификации
все это должно возвращаться на нужном языке и с данными в нужном формате
ID● Шаблоны HTML страниц● E-mail
FoodLogiQ - инструменты Connect● Любые строки, "захардкоженные" в
HTML и JS● Форматы
API● Результаты выполнения операции
(подробности, сообщения об ошибках)● E-mail● Нотификации
все это должно возвращаться на нужном языке и с данными в нужном формате
ID● Шаблоны HTML страниц● E-mail
Connectпереводы: Angular-Translateформат: angular native support
ID, APIпереводы: nicksnyder/go-i18nформат: vube/i18n
Для ID и API можно использовать общий i18n движок
FoodLogiQ - ID, API engine
1.i18n● init(path_to_config) - выполняется при
старте приложения● getI18n(locale_id) - выполняется в
начале реквеста1.I18n● T(TranslationID, args...) - перевод● Formatter - форматирование1.I18nTemplate● ParseFile(filename) - загружает файл
шаблона● Execute(data, I18n) - подставляет в
шаблон актуальные данные и интернационализирует его
i18n+void init(path_to_config)+I18n getI18n(locale_id)
I18n+ string T(TranslationID, args...)+ Formatter
I18nTemplate+ void ParseFile(filename)+ void Execute(data, I18n)
Formatter+ formatDateTime(date, format)+ formatCurrency(amount, currency)+ formatNumber(number)
FoodLogiQ - ID, API engineTemplates
FoodLogiQ - ID i18n
Register
en
Registro
es
locale_id in cookie
Mongo
Connect
token, locale_id name,pwd,locale_id
not authenticated?
FoodLogiQ - API i18n
API
Accept-Language HTTP Header
localized data
FoodLogiQ - Connect
On App Start (server side):1. Преобразовывает файлы с
переводами из JSON в JS
On Page Requested (server side) 2. Вставляет locale_id в JS-код страницы3. Добавляет на HTML-страницу js-файл
с переводами на нужный язык4. Добавляет на страницу файл с
правилами форматирования в нужной локали
Теперь Client Side имеет доступ ко всем инструментам локализации
FoodLogiQ - Connect
Пользователь имеет возможность поменять язык в настройках.
При смене языка новая locale_id сохраняется в БД, а веб-страница целиком перегружается.
Без перезагрузки (на лету) - красивее, с перезагрузкой - проще поместить на страницу скрипты для новой локали. И надежность выше.
FoodLogiQ - Connect
JS:$translate.instant(‘STRING_ID’,{object:object});$filter("date")(input, format);
HTML:<div translate-namespace=”COMMON”>
<span translate=”.STRING_ID”></span></div>{{‘COMMON.STRING_ID’|translate}}
Жизненный опыт
● Чем раньше приступить к интернационализации - тем проще.
● Использовать готовые решения и фреймворки
● Автоматизировать рутинные процедуры (скрипты, hotkeys)
● Применять юнит-тесты
Вопросы?
Алексей Бочкарев[email protected]@ntrlab.com
Михаил Шилов[email protected]@ntrlab.com
copyright: { symbol: “©” ru: “НТР Лаб, 14.04.2016”, en: “NTR Lab, 04/14/2016”}