- PVSM.RU - https://www.pvsm.ru -
“Я хочу, чтобы после того, как программист добавил новую строчку в интерфейс, она сама перевелась на 19 языков и сама положила себя в SVN и была готова к релизу утром” — это мечта любого разработчика, вкусившего запретный плод локализации продукта на иностранные языки. В Alconost Translations [1] мы помогаем если не исполнить, то хотя бы приблизиться к этой мечте. Да, решение, похожее на описанное в статье существует не только для разработчиков LinkedIn, но и для простых смертных.
О том, как процесс построен в LinkedIn — в этой статье (внимание — Java).

Интернационализация оказывает критическое влияние на деятельность и развитие сети LinkedIn, которая сегодня доступна на 19 разных языках. Чтобы ускорить работу с локализованными текстами, международная инженерная команда разработала систему динамического внедрения переведенных строк контента в работающие сервисы.
Новая система позволяет нам оперативно модифицировать контент: вносить быстрые правки и изменения без привлечения разработчиков, пересборки и перезапуска сервиса.
Весь контент изначально пишется на английском нашими разработчиками и продакт-менеджерами. Обычно текст, нуждающийся в переводе, содержится в properties-файле:
add_to_network__send_invitation=Send invitation
add_to_network__cancel=Cancel
add_to_network__add_message=Add a personal message
add_to_network__user_message=I'd like to add you to my professional network.nn- {0}
Затем наша штатная команда локализации переводит контент на разные языки. Вот, например, тот же properties-файл, переведенный на итальянский:
add_to_network__send_invitation=Invia un invito
add_to_network__cancel=Annulla
add_to_network__add_message=Aggiungi un messaggio personale
add_to_network__user_message=Vorrei aggiungerti alla mia rete professionale.nnnn-{0}
Чтобы текст отобразился на странице, мы используем в шаблонах функции интернационализации, которые обращаются по заданному ключу к properties-файлу для пользовательской локали.
<form>
<label for="message">${i18n('add_to_network__add_message')}</label>
<textarea id="message">${i18n('add_to_network__user_message', fullname)}</textarea>
<input type="submit" value="${i18n('add_to_network__send_invitation')}"/>
<input type="button" value="${i18n('add_to_network__cancel')}"/>
</form>
До внедрения динамической подгрузки языков система собирала все properties-файлы в один артефакт вместе с кодом приложения (WAR). Это приводило к определенным проблемам:
Новая система собирает и выкладывает properties-файлы отдельно от кода приложения. Мы ввели концепцию языкового пакета — это JAR-файл, содержащий весь переведенный контент для конкретного языка. Обновленные версии таких языковых пакетов могут выкладываться на веб-сервер в любой момент. Их также можно в любой момент откатить назад, если будут обнаружены ошибки.
Мы добавили новую библиотеку загрузки ресурсов, которая определяет доступность новых языковых пакетов и начинает использовать обновленные переводы — все это без перезапуска сервиса. Если библиотека не находит перевод, она использует исходные англоязычные строки.
Внедрение новых переводов — лишь часть большой картины: нам также нужно было найти способ быстро находить новые и свежеизмененные строки, доставлять их переводчикам и включать результат перевода в языковой пакет. Вот так выглядит наш процесс полностью:

Благодаря тому, что система динамической подгрузки языков отделяет локализуемые тексты от кода, появилась возможность выкладывать в работающий сервис новые строки контента раньше, чем код приложения, который их использует. Поэтому теперь все наши интернационализационные ресурсы должны быть обратно совместимы с предыдущими версиями, чтобы обеспечить уверенность в том, что ничего не сломается при внедрении новых строк.
В этом контексте обратная совместимость означает, что добавление новых строк всегда безопасно, но если вы изменяете существующую, то должны сохранить неизменным количество и тип переменных. Например, изначально у нас была такая строка:
accept_invite__hello_connect=Hello {0}, would you like to connect with {1}?
Мы спокойно можем поменять некоторые слова:
# This change is backwards compatible
accept_invite__hello_connect=Hi {0}, would you like to add {1} to your professional network?
Но удаление, добавление или изменение типа переменных нарушит обратную совместимость, так как код приложения будет предоставлять значения только для прежних переменных:
# This change is NOT backwards compatible!
accept_invite__hello_connect=Hello {0}, would you like to connect with {1}, a coworker at {2}?
Мы добиваемся обратной совместимости с помощью исполняемого перед коммитом кода, который предупреждает удаление ресурсов и заодно проверяет, чтобы обновления существующих текстовых ресурсов были совместимы с набором переменных. Фрагмент кода ниже демонстрирует часть нашей логики валидации:
/**
* Verify that translation isn't missing indexes used with the
* original as well as does not specify additional indexes not
* present in the original.
*/
private boolean verifyPlaceholderIndexMatch(String originalMessageTemplate,
PlaceholderInfo originalPlaceholderInfo,
PlaceholderInfo placeholderInfo)
{
if(originalPlaceholderInfo.keySet().equals(placeholderInfo.keySet()) == false)
{
// remove all index numbers in the translation from the index
// set in the original to determine what's missing in the
// translation
Set<String> missingIndexSet = new HashSet<String>(originalPlaceholderInfo.keySet());
missingIndexSet.removeAll(placeholderInfo.keySet());
// remove all index numbers in the original from the set of
// indexes in the translation to determine what extra index
// we have in the translation
Set<String> extraIndexSet = new HashSet<String>(placeholderInfo.keySet());
extraIndexSet.removeAll(originalPlaceholderInfo.keySet());
return false;
}
return true;
}
Новая система ускорила процесс перевода в LinkedIn: попадание новых строк в работающий сервис стало значительно быстрее и легче, а мы получили возможность постепенно выкатывать переводы и откатывать их назад в случае необходимости. И самое важное: наша система интернационализации теперь может масштабироваться с учетом растущего количества приложений, языков и участников.
Переведено в Alconost Translations [1].
Автор: alconost
Источник [2]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/upravlenie-proektami/29873
Ссылки в тексте:
[1] Alconost Translations: http://alconost.com/
[2] Источник: http://habrahabr.ru/post/173467/
Нажмите здесь для печати.