Интернационализация
===================

Интернационализация (сокращённо I18N) — процесс создания приложения, которое может
работать на различных языках и с различными региональными особенностями без каких-либо
дополнительных изменений. Для веб-приложений это особенно важно, так как пользователь
может быть из любой точки мира.

Yii поддерживает интернационализацию на нескольких уровнях:

   - Предоставляет региональные данные для всех возможных языков и их вариаций.
   - Сервис для перевода сообщений и файлов.
   - Форматирование дат и чисел в зависимости от региональных настроек.

В последующих разделах мы рассмотрим перечисленные возможности более подробно.

Региональные настройки и язык
-----------------------------

Региональные настройки — это набор параметров, задающих язык пользователя,
страну и любые другие специальные настройки интерфейса. Обычно настройки определяются идентификатором,
состоящем из языка и региона. К примеру, идентификатор `en_US` соответствует английскому языку и настройкам для США.
В Yii все идентификаторы региональных настроек названы единообразно: `LanguageID` (язык) или `LanguageID_RegionID` (язык_регион)
в нижнем регистре. Например: `ru`, `en_us`.

Региональные настройки хранятся в объекте класса [CLocale], который можно использовать для получения
зависимой от них информации, такой как символы и форматы валют и чисел, форматы дат и времени, название
месяцев и дней недели. Так как информация о языке уже содержится в идентификаторе,
в [CLocale] она не дублируется. По этой причине мы часто применяем в одном контексте термины «язык», «региональные настройки» и «локаль».

Имея идентификатор языка, мы можем получить соответствующий ему объект [CLocale]:
`CLocale::getInstance($localeID)` или `CApplication::getLocale($localeID)`.

> Info|Информация: в состав Yii включены региональные данные практически по
всем языкам и регионам. Данные получены из
[Common Locale Data Repository](http://unicode.org/cldr/) (CLDR). Доступны не
все данные, предоставляемые CLDR, так как там содержится большое количество
редко используемой информации. Пользователи также могут
предоставлять свои собственные специальные региональные данные. Для этого
настройте свойство [CApplication::localeDataPath], чтобы оно указывало на
директорию, содержащую специальные региональные данные. Обратитесь к файлам
региональных данных в директории `framework/i18n/data` для того, чтобы создать
файлы специальных региональных данных.

В приложении Yii мы различаем [язык приложения|CApplication::language] и [исходный язык приложения|CApplication::sourceLanguage].
Язык приложения — это язык (локаль) пользователя, который работает с приложением. Исходный язык приложения — язык, который используется
в исходном коде приложения. Интернационализация требуется только в том случае, если эти два языка различаются.

> Tip|Подсказка: Лучше всего в качестве исходного языка оставить английский.
  Вам будет несложно найти переводчиков с английского на любой другой язык, чего
  нельзя сказать о переводчиках с любого другого языка.

Вы можете установить [язык приложения|CApplication::language] в
[настройках приложения](/doc/guide/basics.application#application-configuration) или
изменить его непосредственно перед использованием возможностей интернационализации.

> Tip|Подсказка: Иногда нам требуется считать язык пользователя из браузера.
Мы можем получить идентификатор локали используя [CHttpRequest::preferredLanguage].

Перевод
-------

Самой востребованой возможностью интернационализации, скорее всего, является
перевод. Он включает в себя перевод сообщений и строк в представлениях. Первый используется
для перевода отдельных сообщений, второй — для перевода файлов целиком.

В процессе перевода участвуют объект перевода, исходный язык и конечный язык.
В Yii исходный язык по умолчанию приравнивается к [исходному языку приложения|CApplication::sourceLanguage], а
язык — к [языку приложения|CApplication::language]. Если оба языка совпадают — перевод не производится.

### Перевод сообщений

Перевод сообщений осуществляется при помощи метода [Yii::t()|YiiBase::t]. Метод переводит
данное сообщение с [исходного языка приложения|CApplication::sourceLanguage] на [текущий язык приложения|CApplication::language].

При переводе необходимо указать категорию сообщения, так как
оно может иметь различные переводы в разных категориях (контекстах).
Категория с именем `yii` является зарезервированной для использования ядром фреймворка.

Сообщения могут содержать параметры, которые при вызове [Yii::t()|YiiBase::t] заменяются
соответствующими им значениями. К примеру, следующий вызов метода заменит
`{alias}` на значение соответствующей переменной:

~~~
[php]
Yii::t('app', 'Path alias "{alias}" is redefined.',
	array('{alias}'=>$alias))
~~~

> Note|Примечание: переводимые сообщения не должны содержать переменных, изменяющих
строку (`"Invalid {$message} content."`). Если есть необходимость подставлять в строку
какие-либо значения — используйте параметры.

Переведённые сообщения хранятся в репозитории, называемом *источник сообщений*. Источник
сообщений представляет собой экземпляр класса [CMessageSource] или его наследника. При выполнении
[Yii::t()|YiiBase::t] производится поиск сообщения в источнике сообщений и, если оно найдено —
возвращается его переведённая версия.

Yii поддерживает несколько типов источников сообщений, перечисленных ниже. Также вы можете
создать свой источник, унаследовав его от [CMessageSource].

   - [CPhpMessageSource]: переводы сообщений хранятся как пары ключ-значение
в массиве PHP. Исходное сообщение при этом является ключом, а переведённое —
значением. Каждый массив содержит переводы для определённой категории сообщений
и находится в отдельном файле, имя которого совпадает с названием категории.
Файлы с переводом для одного и того же языка хранятся в одной директории,
имеющей такое же имя, как и идентификатор языка. Директории для всех языков
располагаются в директории, указанной в [basePath|CPhpMessageSource::basePath];

   - [CGettextMessageSource]: переводы сообщений хранятся в формате [GNU
Gettext](http://www.gnu.org/software/gettext/);

   - [CDbMessageSource]: переводы сообщений хранятся в базе данных. Подробнее см. [CDbMessageSource].

Источник сообщений загружается как [компонент приложения](/doc/guide/basics.application#application-component).
Сообщения, которые используются в приложении, хранятся в компоненте [messages|CApplication::messages].
По умолчанию тип данного источника сообщений — [CPhpMessageSource]. Путь, по которому хранятся файлы перевода — `protected/messages`.

Таким образом, для того, чтобы использовать механизм перевода сообщений, необходимо следующее:

   1. Вызвать [Yii::t()|YiiBase::t] в нужных местах;

   2. Создать файлы перевода `protected/messages/IdЯзыка/ИмяКатегории.php`. Каждый такой файл
просто возвращает массив переведённых сообщений. Обратите внимание, что при этом используется [CPhpMessageSource];

   3. В файле конфигурации установите значения [CApplication::sourceLanguage] и [CApplication::language].

> Tip|Подсказка: Если в качестве источника сообщений используется [CPhpMessageSource], для работы с сообщениями
может использоваться утилита `yiic`. При помощи команды `message` возможно выбрать из исходного кода все сообщения,
для которых необходим перевод и, при необходимости, объединить их с уже существующим переводом. Подробное
описание команды `message` можно получить набрав в консоли `yiic help message`.

При использовании [CPhpMessageSource], сообщения для расширений, таких, как виджет или модуль, могут быть использованы особым образом.
То есть, если сообщение принадлежит расширению с именем класса `Xyz`, то категория сообщений может быть указана в формате
`Xyz.имяКатегории`. Соответствующий ей файл сообщений будет
`ПутьДоРасширения/messages/IDЯзыка/имяКатегории.php`, где `ПутьДоРасширения` — директория,
в которой находится класс расширения. При использовании `Yii::t()` для перевода
сообщения расширений должен использоваться следующий формат:

~~~
[php]
Yii::t('Xyz.имяКатегории', 'сообщение для перевода')
~~~

Yii поддерживает [формат выбора|CChoiceFormat], известный также как множественные
формы. Формат выбора предназначен
для выбора перевода в зависимости от заданного числа. К примеру, в английском языке
слово 'book' может быть единственного или множественного числа в зависимости от количества книг. В других
языках слово может не иметь специальной формы (как в китайском) или может подчиняться более сложным правилам
для множественного числа (как в русском). Формат выбора решает данную проблему простым, но в то же время
эффективным способом.

Для использования формата выбора перевод должен содержать последовательность пар выражение-сообщение, разделённых
символом `|`:

~~~
[php]
'expr1#message1|expr2#message2|expr3#message3'
~~~

где `exprN` — выражение PHP, возвращающее логическое значение. Если выражение равно true —
используется соответствующий ему перевод и дальнейшие выражения не вычисляются. Выражение
может содержать специальную переменную `n` (не `$n`!), которая содержит число, переданное
первым параметром. Допустим, если мы используем перевод

~~~
[php]
'n==1#one book|n>1#many books'
~~~

и передаём число 2 параметром [Yii::t()|YiiBase::t], то получим `many books`:

~~~
[php]
Yii::t('app', 'n==1#one book|n>1#many books', array(1)));
//or since 1.1.6
Yii::t('app', 'n==1#one book|n>1#many books', 1));
~~~

Если проверяется соответствие определённому числу, можно использовать сокращённую запись,
которая будет рассматриваться как `n==Number`:

~~~
[php]
'1#one book|n>1#many books'
~~~

### Формат для множественных форм

С версии 1.1.6 доступен ещё один механизм, выполняющий перевод множественных
форм слова по правилам CLDR. Он позволяет использовать более простую запись
правил, что особенно актуально для языков со сложными правилами для множественных
форм слова.

Правило для множественных форм английского языка, приведённое выше, можно
записать так:

~~~
[php]
Yii::t('test', 'cucumber|cucumbers', 1);
Yii::t('test', 'cucumber|cucumbers', 2);
Yii::t('test', 'cucumber|cucumbers', 0);
~~~

что даст на выходе:

~~~
cucumber
cucumbers
cucumbers
~~~

Если требуется включить в сообщение число, можно использовать следующий код:

~~~
[php]
echo Yii::t('test', '{n} cucumber|{n} cucumbers', 1);
~~~

Здесь `{n}` — специальный токен, который будет заменён на переданное число.
В данном случае будет напечатано `1 cucumber`.

Можно передать дополнительные параметры:

~~~
[php]
Yii::t('test', '{username} has a cucumber|{username} has {n} cucumbers',
array(5, '{username}' => 'samdark'));
~~~

и даже заменить число чем-нибудь ещё:

~~~
[php]
function convertNumber($number)
{
	// число прописью
	return $number;
}

Yii::t('test', '{n} cucumber|{n} cucumbers',
array(5, '{n}' => convertNumber(5)));
~~~

Количество множественных форм будет варьироваться от языка к языку. К примеру:

~~~
[php]
Yii::t('app', '{n} cucumber|{n} cucumbers', 62);
Yii::t('app', '{n} cucumber|{n} cucumbers', 1.5);
Yii::t('app', '{n} cucumber|{n} cucumbers', 1);
Yii::t('app', '{n} cucumber|{n} cucumbers', 7);
~~~

при переводе на русский будет содержать четыре множественных формы вместо двух:

~~~
[php]
'{n} cucumber|{n} cucumbers' => '{n} огурец|{n} огурца|{n} огурцов|{n} огурца',
~~~

На выходе:

~~~
62 огурца
1.5 огурца
1 огурец
7 огурцов
~~~




> Info|Информация: число и порядок выражений можно узнать в разделе
 [Language Plural Rules](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
 на сайте CLDR.

### Перевод файлов

Перевод файлов осуществляется вызовом [CApplication::findLocalizedFile()]. Параметром
передаётся путь к файлу, который необходимо перевести. Метод ищет одноимённый файл в
подпапке `LocaleID`. Если файл найден — возвращается его путь, иначе — путь к исходному файлу.

Перевод файла используется в основном при отображении представлений. При вызове одного из методов
отображения контроллера или виджета, файлы представления будут переведены автоматически. К примеру, если
 [язык приложения|CApplication::language] установлен как `zh_cn`, а [исходный язык|CApplication::sourceLanguage] как `en_us`
 — при отображении представления `edit` будет произведён поиск представления
`protected/views/ControllerID/zh_cn/edit.php`. Если оно найдено — будет использована переведённая версия, если нет — исходная,
расположенная в `protected/views/ControllerID/edit.php`.

Перевод файлом можно использовать и для других целей. Например, для того, чтобы
отобразить переведённое изображение или загрузить локализованную версию файла.

Форматирование даты и времени
-----------------------------

Дата и время в разных странах и регионах часто форматируются по-разному. Задача форматирования
даты и времени таким образом сводится к генерации строки, подходящей для данной страны и региона.
Для этого в Yii используется [CDateFormatter].

Каждый экземпляр [CDateFormatter] соответствует некому языку приложения. Чтобы получить
форматтер для выбранного языка, мы можем просто обратиться к свойству приложения
[dateFormatter|CApplication::dateFormatter].

Класс [CDateFormatter] содержит два метода, предназначенных для форматирования UNIX
timestamp:

   - [format|CDateFormatter::format]: форматирует переданную в формате UNIX timestamp дату
согласно шаблону даты-времени (например, `$dateFormatter->format('dd.MM.yyyy', $timestamp)`);

   - [formatDateTime|CDateFormatter::formatDateTime]: форматирует переданную в формате UNIX timestamp дату
согласно шаблону, заданному для выбранного языка (например формат даты `short`, формат времени `long`).

Форматирование чисел
--------------------

Также, как дата и время, числа могут писаться по-разному в разных странах и регионах.
Форматирование чисел включает в себя форматирование десятичных дробей,
валют и чисел с процентами. Для выполнения данных задач в Yii используется класс [CNumberFormatter].

Для того, чтобы воспользоваться форматтером чисел, соответствующим выбранному языку, мы можем обратиться к
свойству приложения [numberFormatter|CApplication::numberFormatter].

Для форматирования целых чисел и дробей в классе [CNumberFormatter] есть следующие методы:

   - [format|CNumberFormatter::format]: форматирует число в соответствии с заданным форматом
(например, `$numberFormatter->format('#,##0.00',$number)`);

   - [formatDecimal|CNumberFormatter::formatDecimal]: форматирует число в соответствии с форматом,
   заданным для текущего языка приложения;

   - [formatCurrency|CNumberFormatter::formatCurrency]: форматирует число и код валюты в соответствии с
   форматом, заданным для текущего языка приложения;

   - [formatPercentage|CNumberFormatter::formatPercentage]: форматирует число в соответствии с форматом форматирования процентов,
   заданным для текущего языка приложения.