Интернационализация приложения в React (и не только)
Я достаточно много времени потратил на поиски подходящей библиотеки для этой цели. А когда я ее все таки нашел, долго не мог понять как правильно ей пользоваться, ибо нормальную документацию разработчики написать поленились. Собственно сама библиотека — i18next. Она предоставляет много возможностей, но в данной статье будут рассмотрены две из них.
1. Интернационализация. i18next позволяет нам создать словарь слов для каждого конкретного языка, а потом внутри приложений вместо текста писать переменные из этих словарей. При инициализации компонента мы выбираем язык, а вместо переменной автоматически подставляется слово из нужного словаря. Ссылку на репозиторий с полным исходным кодом вы найдете ниже, а сейчас я буду приводить небольшие фрагменты тестового приложения.
Мы будем использовать 2 языка — английский и русский. Каждый из них хранится в файле en.json и ru.json соответственно. Выглядеть словарь может следующим образом:
en.json
{
"en": {
"translation": {
"test_message": "This is a test message"
}
}
}
ru.json
{
"ru": {
"translation": {
"test_message": "Это тестовое сообщение"
}
}
}
Необходимо отметить, что названия ключей для текста (в данном случае это test_message) должны совпадать во всех словарях. В данном примере у нас есть один ключ, который мы можем использовать внутри нашего компонента. Для этого нужно перед инициализацией компонента (внутри функции componentWillMount ()) задать язык по умолчанию, например английский
componentWillMount() {
this.setLanguage('en');
}
Функция setLanguage () может выглядеть следующим образом:
setLanguage(language) {
i18next.init({
lng: language,
resources: require(`json!./${language}.json`)
});
this.props.actions.changeLanguage(i18next);
}
В параметре мы передаем название языка, а дальше используем его для инициализации библиотеки. Важный момент, внутри функции инициализации значение ключа lng должно соответствовать названию языка внутри словаря (в данном случае lng == en, как и первый объект внутри файла en.json). В resources должен быть передан путь к файлу словаря. Я использовал json-loader, поэтому функция выглядит таким образом. Также, следует не забыть обновить стейт компонента, иначе язык изменится, а рендер не вызовется и текст не поменяется.
Основные приготовления сделаны, теперь можно начинать использовать интернационализацию внутри компонента.
render() {
return (
{i18next.t('test_message')}
)
}
Сверху расположены 2 кнопки для каждого из языков, при нажатии на которые и будет происходить смена языка. Важно использовать bind, иначе функция setLanguage будет вызываться бесконечно и приведет к зацикливанию. После рендера, вместо странной конструкции i18next.t ('test_message') пользователю будет выдан корректный перевод, а именно This is a test message либо Это тестовое сообщение, в зависимости от установленного языка.
По сути, это все. Вот так незамысловато работает интернационализация приложения с помощью библиотеки i18next. Теперь же, как и было обещано, рассмотрим еще одну полезную функцию библиотеки — плюрализацию.
2. Плюрализация. Если вкратце, этот термин означает, что в зависимости от количественного признака окончание слова будет меняться. Например, в английском языке к слову в конце прибавляется s (one car → five cars). А вот в русском ситуация несколько хуже, ибо для каждого слова есть 3 различных варианта окончания, например: 1 банан, 2 банана, 5 бананов. Конечно, внутри приложения можно сделать что-то вроде «Количество бананов: 2», но смотрится это не так эффектно. А с помощью i18next эту ситуацию можно исправить очень просто!
На сайте библиотеки есть табличка с pluraforms, в которой для каждого языка показывается сколько различных вариантов окончаний может быть. В русском их 3.
Для использования это функции, необходимо добавить в наш словарь следующие ключи:
en.json
"amount_of_bananas": "{{count}} banana",
"amount_of_bananas_plural": "{{count}} bananas"
При рендере компонента будет выбран вариант в зависимости от цифры, которая будет передана как count. Т.е. если внутри компонента мы передадим 1, то будет выбран первый вариант, иначе будет выбран второй вариант. Как библиотека понимает, какой вариант выбрать? Второй ключ оканчивается на _plural, который и помогает библиотеке с выбором. Для каждого языка есть свои окончания, найти их можно в таблице, ссылку на которую я оставил выше. Например, для русского языка словарь будет выглядеть следующим образом:
"amount_of_bananas_0": "{{count}} банан",
"amount_of_bananas_1": "{{count}} банана",
"amount_of_bananas_2": "{{count}} бананов"
Т.е. в компоненте при передаче 1 будет отдано »1 банан», при передаче 2 — »2 банана» и при передаче 5 — »5 бананов». Очень удобно, что мы сами задаем перевод слова для того или иного количества.
В итоге, возвращаясь к компоненту, выглядеть это будет следующим образом:
{i18next.t('amount_of_bananas', {count: 5})}
, где на выходе человек получит »5 banans» или же »5 бананов»Вот так вот, достаточно просто, мы может перевести свое приложение на языки, которые необходимо поддерживать. Отдельно стоит сказать про словари. В данном случае, у нас есть всего-лишь 2 слова, но в больших приложениях их может быть гораздо больше. Выбор организации языка зависит только от вас. Это может быть либо разбитие по компонентам, либо же, во избежание повторения, по каким-либо признакам (например, кнопки, формы и пр).
На этом все, спасибо за внимание!
Ссылка на репозиторий с примером — https://github.com/theWaR13/i18next-example
Комментарии (6)
12 августа 2016 в 17:57
0↑
↓
Почему amount_ofbananas0 соответствует 1 банан, а не 0 бананов? И что будет если передать 11 и 21?
12 августа 2016 в 17:58 (комментарий был изменён)
0↑
↓
Действительно, нумерация внутри словаря идет несколько странно. Однако, один раз все настроив, про это можно забыть.
Передавать можно любые цифры, конкретно 5 я взял для примера.
12 августа 2016 в 19:47
0↑
↓
Вопрос про 11 и 21 был задан потому, что
0, 10, 20 и т.д.бананов
1 банан, 11 бананов, 21 банан
2,3,4 банана, 12,13,14 бананов, 22,23,24 банана
5 и далее как и ноль.12 августа 2016 в 21:17
0↑
↓
Language Plural Rules:
http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html12 августа 2016 в 22:40
0↑
↓
Есть ли возможность сразу загружать только один язык, а остальные подтягивать по мере необходимости?
12 августа 2016 в 22:42
0↑
↓
Собственно в примере оно так и работает. Каждый раз, когда пользователь меняет язык, с помощью require подгружается нужный файл.