Интернационализация (как окрестили I18n) и локализации (дублированные l10n) являются очень важными (хотя часто жесткими ) шагами для любого приложения, которое будет использоваться по всему миру. В одной из предыдущих статей мы увидели, как реализовать I18n на серверной части на основе Ruby on Rails, но сегодня пришло время поговорить о клиентской части. В этой статье мы обсудим, как локализовать приложения JavaScript, используя следующие решения:
- jQuery.I18n от Викимедиа
- Полиглот от Airbnb
- Глобализация командой jQuery
Все эти решения совершенно разные и имеют свои особенности, поэтому мы увидим их в действии.
Исходный код доступен на GitHub .
Препараты
Прежде чем перейти к основной части, давайте быстро подготовим базовую структуру для нашего простого демонстрационного проекта. Создайте отдельную папку с файлом index.html внутри. Мы сделаем копии этого файла, чтобы протестировать различные решения. Затем создайте вложенную папку с именем common и поместите внутрь файл jquery.js, который можно загрузить с веб-сайта jquery.com . Вы можете выбрать любую версию jQuery (1, 2 или 3) в зависимости от того, какие браузеры вы хотите поддерживать — для этой демонстрации это не имеет значения.
Теперь заполните index.html разметкой:
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="common/jquery.js"></script>
</head>
<body>
</body>
</html>
Этого достаточно для нас, чтобы начать!
jQuery.I18n
Давайте начнем с библиотеки интернационализации на основе jQuery, разработанной и поддерживаемой Викимедиа . Викимедиа — это глобальное движение, которое заботится о таких известных проектах, как Википедия, Викиновости, Викиучебники и другие. В свою очередь, jQuery.I18n использует формат файла локализации на основе JSON и поддерживает пол, грамматические формы, динамическое изменение языка, резервные цепочки и многое другое.
Переводы для jQuery.I18n могут быть разделены на несколько файлов ( en.json , de.json и т. Д.) Или сохранены вместе в одном файле. Вот пример en.json :
{
"@metadata": {
"authors": [
"Me"
],
"last-updated": "2016-09-21",
"locale": "en",
"message-documentation": "qqq"
},
"appname-title": "Example Application",
"appname-sub-title": "An example application with jquery.i18n"
}
Итак, как вы можете видеть, эти файлы поддерживают метаданные, в которых вы указываете авторов, дату последнего обновления, локаль и другую информацию. Далее вы предоставляете фактические переводы в формате ключ-значение. Рекомендуется добавлять ключи к имени приложения, чтобы сделать его уникальным, но это не обязательно.
Для небольших приложений вы можете предоставить все переводы в одном файле . В этом случае имя языка указывается в качестве родительского ключа:
{
"@metadata": { ... }
"en": {
"appname-title": "Example Application"
},
"ru": {
"appname-title": "Тестовое приложение"
}
}
Более того, вы даже можете указать путь к файлу переводов:
{
"@metadata": { ... }
"en": {
"appname-title": "Example Application"
},
"ru": "ru.yml"
}
Теперь, конечно, вам интересно, как загрузить эти переводы в ваше приложение. Есть несколько способов, и первый, вероятно, самый простой. Вместо создания отдельного файла JSON вы можете поместить все переводы непосредственно в скрипт, передав их load
функции:
$.i18n().load({
'en': {
'appname-title': 'Example Application'
},
'ru' : {
'appname-title': 'Тестовое приложение'
}
});
Я не могу сказать, что это рекомендуемая практика, но для действительно небольших проектов это хорошо.
Другим решением является загрузка сообщений с внешнего URL
$.i18n().load({
en: 'i18n/en.json'
})
При использовании этого подхода функция load
вернет обещание, поэтому вы можете поместить дальнейшие действия внутри done
функции:
$.i18n().load({
en: 'i18n/en.json'
}).done( function() { console.log('done!') } )
Вы можете установить локаль при инициализации библиотеки, используя любую из locale
опций
$.i18n( {
locale: 'en'
} );
или предоставив его внутри lang
атрибута для html
тега:
<html lang="en" dir="ltr">
Конечно, локаль может быть переключена позже, переопределив locale
опцию:
$.i18n({
locale: 'de'
});
// or
$.i18n().locale = 'de';
Чтобы перевести сообщение, вы должны предоставить ключ к $.i18n
функции
$.i18n('message-key1');
или просто использовать в data-
атрибут (без дополнительных JavaScript не требуется). Исходное содержимое является резервным текстом для отображения в случае, если что-то идет не так и перевод не может быть найден:
<li data-i18n="message-key">Fallback text</li>
Обратите внимание, что сообщения поддерживают интерполяцию, принимая параметры. Они написаны как $1
, и $2
т.д .:
var message = "Good day, $1";
$.i18n(message, 'Joe');
Множественные и пол обрабатываются с помощью следующего синтаксиса:
var message = "Found $1 {{PLURAL:$1|result|results}}";
$.i18n(message, 1);
var message = "$1 changed {{GENDER:$2|his|her}} profile picture";
$.i18n(message, 'Emma', 'female');
На практике
Для начала скопируйте наш базовый файл index.html как jquery_i18n.html . Создайте новый каталог jquery_i18n и поместите в него файл main-jquery_i18n.js . Следующий клон jQuery.I18n где-нибудь на вашем компьютере вместе с субмодулями:
$ git clone https://github.com/wikimedia/jquery.i18n.git
$ cd jquery.i18n
$ git submodule update --init
Нам потребуются все файлы из каталога src (без папки languages ), а также CLDRPluralRuleParser.js из libs \ CLDRPluralRuleParser \ src . Скопируйте все это в папку jquery_i18n и затем включите их в правильном порядке:
jquery_i18n.html
[...]
<script src="jquery_i18n/CLDRPluralRuleParser.js"></script>
<script src="jquery_i18n/jquery.i18n.js"></script>
<script src="jquery_i18n/jquery.i18n.messagestore.js"></script>
<script src="jquery_i18n/jquery.i18n.fallbacks.js"></script>
<script src="jquery_i18n/jquery.i18n.language.js"></script>
<script src="jquery_i18n/jquery.i18n.parser.js"></script>
<script src="jquery_i18n/jquery.i18n.emitter.js"></script>
<script src="jquery_i18n/jquery.i18n.emitter.bidi.js"></script>
<script src="jquery_i18n/main-jquery_i18n.js"></script>
[...]
Давайте также предоставим начальную локаль через lang
атрибут:
jquery_i18n.html
[...]
<html lang="en" dir="ltr">
[...]
Наконец, добавьте ссылки для переключения языка и пару пустых тегов, которые будут размещать наш переведенный контент. Я буду работать с английским и русским языками, но, конечно, вы можете выбрать любые другие языки — это не имеет большого значения, но для реальных приложений убедитесь, что ваши тексты правильно переведены , желательно человеком .
jquery_i18n.html
[...]
<body>
<a href="#" class="lang-switch" data-locale="en">English</a> |
<a href="#" class="lang-switch" data-locale="ru">Русский</a>
<h1 data-i18n="welcome"></h1>
<p id="messages"></p>
</body>
[...]
Теперь перейдите к сценарию. Нам нужно будет загрузить переводы, как только документ будет готов. Для простоты давайте сохраним все наши сообщения в скрипте:
Основные-jquery_i18n.js
[...]
jQuery(document).ready(function() {
$.i18n().load({
'en': {
'welcome': 'Welcome!',
}
'ru': {
'welcome': 'Добро пожаловать!',
}
});
});
[...]
Обратите внимание на welcome
ключ — то же имя используется в data-i18n
атрибуте для h1
тега. Таким образом, правильный перевод будет использоваться автоматически — все, что нам нужно сделать, это запустить этот процесс, вызвав i18n()
функцию. Я извлеку это внутри update_texts
:
Основные-jquery_i18n.js
[...]
jQuery(document).ready(function() {
var update_texts = function() {
$('body').i18n();
};
$.i18n().load({...});
update_texts();
});
[...]
Теперь давайте позаботимся о переключении языков. Это просто — просто прослушайте click
событие, извлеките значение data-locale
атрибута и установите соответствующий языковой стандарт:
Основные-jquery_i18n.js
[...]
$('.lang-switch').click(function(e) {
e.preventDefault();
$.i18n().locale = $(this).data('locale');
update_texts();
});
[...]
Наконец, мы собираемся добавить перевод для этого #messages
раздела. Это будет немного сложнее:
Основные-jquery_i18n.js
[...]
$.i18n().load({
'en': {
'welcome': 'Welcome!',
'message_from': '$1 has send you $2 {{plural:$2|message|messages}}. {{gender:$3|He|She}} is waiting for your response!'
},
'ru': {
'welcome': 'Добро пожаловать!',
'message_from': '$1 {{gender:$3|отправил|отправила}} вам $2 {{plural:$2|сообщение|сообщений|сообщения}}. {{gender:$3|Он|Она}} ждёт ответа!'
}
});
[...]
Здесь плюрализм и гендерная информация используются одновременно. Для русского языка мне пришлось добавить больше опций, потому что правила плюрализации более сложные. Чтобы это работало, настройте update_texts()
функцию:
Основные-jquery_i18n.js
[...]
var update_texts = function() {
$('body').i18n();
$('#messages').text($.i18n('message_from', 'Ann', 2, 'female'));
};
[...]
Теперь откройте страницу и попробуйте переключаться между языками — все должно работать просто великолепно!
Polyglot.js
Polyglot.js — это небольшое решение, созданное Airbnb (инжиниринговая компания), которое работает в браузере и в средах Common.js. Он поддерживает интерполяцию и множественное число, имея нулевые зависимости. Возьмите производственную версию здесь .
Чтобы начать работать с Polyglot, создайте его экземпляр :
var polyglot = new Polyglot();
Он основан на классе, поэтому вы можете работать с другим набором локалей одновременно.
Далее приведите список фраз :
polyglot.extend({
"hello": "Hello"
});
// or
var polyglot = new Polyglot({phrases: {"hello": "Hello"}});
Как правило, документация предлагает подготовить хэш фраз на серверной части, а затем вывести их в script
тег. Обратите внимание, что Polyglot не будет переводить — ваша работа — давать правильные фразы в зависимости от локали пользователя.
Фразы могут быть заменены или удалены полностью (например, чтобы освободить память) с помощью replace
или clear
методов соответственно .
Обратите внимание, что вложение также поддерживается:
polyglot.extend({
"nav": {
"sidebar": {
"welcome": "Welcome"
}
}
});
Если вы пришли из мира Rails, интерполяция должна показаться вам знакомой:
polyglot.extend({
"hello_name": "Hello, %{name}."
});
Чтобы выполнить фактический перевод, используйте t
метод:
polyglot.t("hello_name", {name: "John"});
Если ваш ключ вложенный, то используйте точку .
в качестве разделителя:
polyglot.t("nav.sidebar.welcome");
Обратите внимание, что название языка нигде не указывается — в настоящее время оно используется только при работе с множественным числом. Чтобы установить его, используйте locale
функцию
polyglot.locale("de")
или установите его при создании нового объекта, передав locale
опцию.
Сообщения с множественным числом должны быть разделены четырьмя каналами ( ||||
):
polyglot.extend({
"num_cars": "%{smart_count} car |||| %{smart_count} cars",
});
Правильное сообщение будет выбрано на основе smart_count
параметра.
На практике
Теперь давайте быстро увидим это решение в действии. Скопируйте файл index.html и назовите его polyglot.html . Затем создайте папку polyglot и поместите в нее рабочую версию сценария. Также создайте файл main-polyglot.js и подключите все:
polyglot.html
[...]
<script src="polyglot/polyglot.js"></script>
<script src="polyglot/main-polyglot.js"></script>
[...]
Для этой демонстрации мы будем использовать шаблонизатор Underscore.js, который будет использоваться для визуализации контента (хотя вы можете придерживаться Handlebars или другого решения). Если вы раньше не работали с такими шаблонами, идея довольно проста: у вас есть разметка с некоторыми параметрами. Затем этот шаблон «компилируется» (имеется в виду, что параметры получают свои значения) и отображается на странице, как и любой другой HTML.
Поместите рабочую версию Underscore в общую папку и потребуйте ее:
polyglot.html
[...]
<script src="common/jquery.js"></script>
<script src="common/underscore.js"></script>
[...]
Я собираюсь разместить наш шаблон прямо на странице, в самом низу. Обратите внимание, что он должен быть обернут script
тегом специального типа (чтобы браузеры не пытались обработать его как код JavaScript):
polyglot.html
<script type="text/template" id="main-content">
<p><%= hello %></p>
<small><%= unread %></small>
</script>
Теперь внутри скрипта давайте подождем, пока документ не будет готов, а затем создадим экземпляр класса Polyglot, предоставив два сообщения:
Основные-polyglot.js
jQuery(document).ready(function() {
var polyglot = new Polyglot({
phrases: {
"hello": "Hello, %{name}!",
"unread": "You have %{smart_count} unread message |||| You have %{smart_count} unread messages"
}
});
});
Здесь мы использовали интерполяцию и множественное число (обратите внимание на имя параметра smart_count
— при использовании другого имени множественное число перестает работать). Теперь давайте возьмем шаблон
var main_content_temp = _.template($('#main-content').html());
а затем предоставить значения для параметров внутри него
$('body').prepend(main_content_temp({
hello: polyglot.t('hello', {name: 'John'}),
unread: polyglot.t('unread', {smart_count: 2})
}));
Вот результирующий код:
Основные-polyglot.js
jQuery(document).ready(function() {
var polyglot = new Polyglot({
phrases: {
"hello": "Hello, %{name}!",
"unread": "You have %{smart_count} unread message |||| You have %{smart_count} unread messages"
}
});
var main_content_temp = _.template($('#main-content').html());
$('body').prepend(main_content_temp({
hello: polyglot.t('hello', {name: 'John'}),
unread: polyglot.t('unread', {smart_count: 2})
}));
});
Загрузите HTML-документ и протестируйте его!
Globalize
Глобализация — довольно большая библиотека для интернационализации, разработанная членами основной команды jQuery. Он работает в браузере ( поддерживает все современные браузеры и IE, начиная с версии 9) и с Node.js, предоставляя множество полезных функций, включая разбор числа, даты и времени, множественное число, интерполяцию, поддержку единиц и многое другое. В нем используются ключевые строительные блоки для программного обеспечения для поддержки языков мира, а также самый большой и обширный стандартный репозиторий доступных данных локали. Более того, Globalize является модульным и не содержит данных I18n — вы можете загрузить его самостоятельно.
Существует три основных функции API:
Globalize.load()
загружает данные локали CLDR в формате JSON (например, форматы даты и времени, названия месяца и т. д.)Globalize.locale()
— геттер и сеттер для локали[new] Globalize
— создает новый объект Globalize
Также есть множество различных функций для каждого модуля, которые вы можете найти здесь .
На практике
Давайте посмотрим Глобализация в действии прямо сейчас. Скопируйте index.html и назовите его globalize.html . Также создайте папку globalize с файлом globalize-main.js внутри. Пока Globalize является модульным, зависимости должны загружаться в правильном порядке (есть даже онлайн-инструмент, помогающий вам выяснить, какие зависимости необходимы).
Поэтому вам необходимо загрузить последнюю версию Globalize, а также CLDR . Вот список файлов Globalize для захвата (поместите их в папку globalize вашего проекта):
- расстояние / globalize.js
- расстояние / Globalize / date.js
- расстояние / Globalize / number.js
- расстояние / Globalize / currency.js
- расстояние / Globalize / message.js
- расстояние / Globalize / plural.js
К сожалению, это еще не все. Вот необходимые файлы CLDR, которые должны быть помещены в папку cldr (создайте ее сейчас):
- DIST / cldr.js
- расстояние / CLDR / event.js
- расстояние / CLDR / supplemental.js
Уф. Последняя часть предоставляет некоторые общие данные о локали для CLDR — загрузите их здесь и поместите под именем cldr / cldr_data.js . Наконец, подключите все эти файлы в правильном порядке:
globalize.html
[...]
<script src="common/jquery.js"></script>
<script src="cldr/cldr.js"></script>
<script src="cldr/event.js"></script>
<script src="cldr/supplemental.js"></script>
<script src="globalize/globalize.js"></script>
<script src="globalize/message.js"></script>
<script src="globalize/number.js"></script>
<script src="globalize/plural.js"></script>
<script src="globalize/currency.js"></script>
<script src="globalize/date.js"></script>
<script src="cldr/cldr_data.js"></script>
<script src="globalize/globalize-main.js"></script>
[...]
Также добавьте пару заполнителей для нашего контента:
globalize.html
[...]
<body>
<h1 id="welcome"></h1>
<p id="earnings"></p>
</body>
[...]
Теперь давайте загрузим приветственное сообщение:
глобализовать-main.js
jQuery(document).ready(function() {
Globalize.loadMessages({
"en": {
'welcome': 'Welcome, {name}!'
}
});
});
Здесь мы используем модуль форматирования сообщений . Затем создайте экземпляр класса Globalize.
var globalize = new Globalize("en");
и заполните #welcome
раздел:
$('#welcome').text( globalize.messageFormatter('welcome')({name: 'John'}) );
Обратите внимание, что messageFormatter
возвращается функция, которую мы затем вызываем и передаем объект, содержащий имя. Это может быть переписано как
var welcome_message = globalize.messageFormatter('welcome');
$('#welcome').text( welcome_message({name: 'John'}) );
На самом деле, параметры сообщения не нужно называть — вы можете сказать 0
, 1
и 2
т. Д .:
'welcome': 'Welcome, {0}!'
В этом случае передайте массив в форматтер:
$('#welcome').text(globalize.messageFormatter('welcome')(['John']));
Далее укажите другое сообщение, содержащее сегодняшний день и общий доход:
"en": {
'welcome': 'Welcome, {0}!',
'earned': 'Today is {date} and you\'ve earned {amount}!'
}
В этом примере мы будем использовать форматеры даты и валюты :
$('#earnings').text(
globalize.messageFormatter('earned')({
amount: globalize.formatCurrency(500.5, 'USD'),
date: globalize.formatDate( new Date(), {
datetime: "medium"
})
})
)
При форматировании валюты мы передаем USD
второй аргумент. Этот аргумент используется для отображения правильного символа при отображении результата. Сам символ был определен внутри файла clrd_data.js :
"currencies": {
"USD": {
"symbol": "$"
}
}
medium
это имя формата datetime — оно также было определено в файле clrd_data.js как
"dateTimeFormats": {
"medium": "{1}, {0}"
}
Вот 1
дата и 0
время. Дата и время, в свою очередь, форматируются с использованием следующих масок:
"dateFormats": {
"medium": "MMM d, y"
},
"timeFormats": {
"medium": "h:mm:ss a"
}
Теперь, когда все готово, вы можете наблюдать за результатом!
PhraseApp снова спасает день
Управление переводами для нескольких языков действительно может быть утомительным. Однако с PhraseApp весь процесс становится намного проще.
PhraseApp поддерживает широкий спектр различных форматов от простого до вложенного JSON (и специальные форматы для AngularJS или React). Затем просто добавьте столько локалей, сколько вам нужно, и загрузите существующие файлы JSON с переводами, если они у вас есть.
Сделав это, вы сможете быстро понять, какие переводы отсутствуют, управлять своими ключами и сообщениями, а также загружать обновленные переводы одним щелчком мыши. Что еще круче, вы можете легко запросить поддержку у профессиональных переводчиков (что, вероятно, лучше, поскольку локализация касается не только перевода). Поэтому я настоятельно рекомендую вам попробовать PhraseApp!
Заключение
Итак, в этой статье мы рассмотрели различные решения, помогающие вам локализовать ваше приложение: jQuery.I18n, Globalize и Polyglot. Polyglot оказался самой маленькой и простой библиотекой, тогда как Globalize и jQuery.I18n довольно большие и сложные — выбор за вами!
Надеюсь, вы нашли эту статью полезной и интересной. Спасибо, что остаетесь со мной и счастливого кодирования!