You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
451 lines
14 KiB
451 lines
14 KiB
11 years ago
|
Internationalization
|
||
|
====================
|
||
|
|
||
|
Internationalization (I18N) refers to the process of designing a software application so that it can be adapted to
|
||
|
various languages and regions without engineering changes. For Web applications, this is of particular importance
|
||
|
because the potential users may be worldwide.
|
||
|
|
||
11 years ago
|
Locale and Language
|
||
|
-------------------
|
||
11 years ago
|
|
||
11 years ago
|
There are two languages defined in Yii application: [[yii\base\Application::$sourceLanguage|source language]] and
|
||
|
[[yii\base\Application::$language|target language]].
|
||
11 years ago
|
|
||
11 years ago
|
Source language is the language original application messages are written in such as:
|
||
11 years ago
|
|
||
11 years ago
|
```php
|
||
|
echo \Yii::t('app', 'I am a message!');
|
||
|
```
|
||
|
|
||
|
> **Tip**: Default is English and it's not recommended to change it. The reason is that it's easier to find people translating from
|
||
|
> English to any language than from non-English to non-English.
|
||
|
|
||
|
Target language is what's currently used. It's defined in application configuration like the following:
|
||
|
|
||
|
```php
|
||
|
// ...
|
||
11 years ago
|
return [
|
||
11 years ago
|
'id' => 'applicationID',
|
||
|
'basePath' => dirname(__DIR__),
|
||
|
'language' => 'ru-RU' // ← here!
|
||
11 years ago
|
```
|
||
|
|
||
|
Later you can easily change it in runtime:
|
||
|
|
||
|
```php
|
||
11 years ago
|
\Yii::$app->language = 'zh-CN';
|
||
11 years ago
|
```
|
||
|
|
||
11 years ago
|
Format is `ll-CC` where `ll` is two- or three-letter lowercase code for a language according to
|
||
|
[ISO-639](http://www.loc.gov/standards/iso639-2/) and `CC` is country code according to
|
||
|
[ISO-3166](http://www.iso.org/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html).
|
||
|
|
||
11 years ago
|
If there's no translation for `ru-RU` Yii will try `ru` as well before failing.
|
||
|
|
||
11 years ago
|
> **Note**: you can further customize details specifying language
|
||
|
> [as documented in ICU project](http://userguide.icu-project.org/locale#TOC-The-Locale-Concept).
|
||
11 years ago
|
|
||
11 years ago
|
Message translation
|
||
|
-------------------
|
||
|
|
||
|
### Basics
|
||
11 years ago
|
|
||
11 years ago
|
Yii basic message translation in its basic variant works without additional PHP extension. What it does is finding a
|
||
11 years ago
|
translation of the message from source language into target language. Message itself is specified as the second
|
||
11 years ago
|
`\Yii::t` method parameter:
|
||
11 years ago
|
|
||
|
```php
|
||
|
echo \Yii::t('app', 'This is a string to translate!');
|
||
|
```
|
||
11 years ago
|
|
||
11 years ago
|
Yii tries to load appropriate translation from one of the message sources defined via `i18n` component configuration:
|
||
11 years ago
|
|
||
11 years ago
|
```php
|
||
11 years ago
|
'components' => [
|
||
11 years ago
|
// ...
|
||
|
'i18n' => [
|
||
|
'translations' => [
|
||
|
'app*' => [
|
||
|
'class' => 'yii\i18n\PhpMessageSource',
|
||
|
//'basePath' => '@app/messages',
|
||
|
//'sourceLanguage' => 'en',
|
||
|
'fileMap' => [
|
||
|
'app' => 'app.php',
|
||
|
'app/error' => 'error.php',
|
||
|
],
|
||
|
],
|
||
|
],
|
||
|
],
|
||
11 years ago
|
],
|
||
11 years ago
|
```
|
||
|
|
||
|
In the above `app*` is a pattern that specifies which categories are handled by the message source. In this case we're
|
||
11 years ago
|
handling everything that begins with `app`. You can also specify default translation, for more info see [this example](i18n.md#examples).
|
||
11 years ago
|
|
||
11 years ago
|
`class` defines which message source is used. The following message sources are available:
|
||
11 years ago
|
|
||
|
- PhpMessageSource that uses PHP files.
|
||
|
- GettextMessageSource that uses GNU Gettext MO or PO files.
|
||
|
- DbMessageSource that uses database.
|
||
|
|
||
|
`basePath` defines where to store messages for the currently used message source. In this case it's `messages` directory
|
||
|
in your application directory. In case of using database this option should be skipped.
|
||
|
|
||
|
`sourceLanguage` defines which language is used in `\Yii::t` second argument. If not specified, application's source
|
||
|
language is used.
|
||
|
|
||
|
`fileMap` specifies how message categories specified in the first argument of `\Yii::t()` are mapped to files when
|
||
|
`PhpMessageSource` is used. In the example we're defining two categories `app` and `app/error`.
|
||
|
|
||
11 years ago
|
Instead of configuring `fileMap` you can rely on convention which is `BasePath/messages/LanguageID/CategoryName.php`.
|
||
11 years ago
|
|
||
11 years ago
|
#### Named placeholders
|
||
11 years ago
|
|
||
11 years ago
|
You can add parameters to a translation message that will be substituted with the corresponding value after translation.
|
||
|
The format for this is to use curly brackets around the parameter name as you can see in the following example:
|
||
|
|
||
11 years ago
|
```php
|
||
|
$username = 'Alexander';
|
||
11 years ago
|
echo \Yii::t('app', 'Hello, {username}!', [
|
||
11 years ago
|
'username' => $username,
|
||
11 years ago
|
]);
|
||
11 years ago
|
```
|
||
|
|
||
11 years ago
|
Note that the parameter assignment is without the brackets.
|
||
|
|
||
11 years ago
|
#### Positional placeholders
|
||
11 years ago
|
|
||
|
```php
|
||
|
$sum = 42;
|
||
|
echo \Yii::t('app', 'Balance: {0}', $sum);
|
||
|
```
|
||
|
|
||
11 years ago
|
> **Tip**: Try keep message strings meaningful and avoid using too many positional parameters. Remember that
|
||
|
> translator has source string only so it should be obvious about what will replace each placeholder.
|
||
11 years ago
|
|
||
11 years ago
|
### Advanced placeholder formatting
|
||
|
|
||
11 years ago
|
|
||
|
In order to use advanced features you need to install and enable [intl](http://www.php.net/manual/en/intro.intl.php) PHP
|
||
|
extension. After installing and enabling it you will be able to use extended syntax for placeholders. Either short form
|
||
|
`{placeholderName, argumentType}` that means default setting or full form `{placeholderName, argumentType, argumentStyle}`
|
||
|
that allows you to specify formatting style.
|
||
|
|
||
|
Full reference is [available at ICU website](http://icu-project.org/apiref/icu4c/classMessageFormat.html) but since it's
|
||
11 years ago
|
a bit cryptic we have our own reference below.
|
||
11 years ago
|
|
||
11 years ago
|
#### Numbers
|
||
11 years ago
|
|
||
|
```php
|
||
|
$sum = 42;
|
||
|
echo \Yii::t('app', 'Balance: {0, number}', $sum);
|
||
|
```
|
||
|
|
||
|
You can specify one of the built-in styles (`integer`, `currency`, `percent`):
|
||
|
|
||
|
```php
|
||
|
$sum = 42;
|
||
|
echo \Yii::t('app', 'Balance: {0, number, currency}', $sum);
|
||
|
```
|
||
|
|
||
|
Or specify custom pattern:
|
||
|
|
||
|
```php
|
||
|
$sum = 42;
|
||
|
echo \Yii::t('app', 'Balance: {0, number, ,000,000000}', $sum);
|
||
|
```
|
||
|
|
||
|
[Formatting reference](http://icu-project.org/apiref/icu4c/classicu_1_1DecimalFormat.html).
|
||
|
|
||
11 years ago
|
#### Dates
|
||
11 years ago
|
|
||
|
```php
|
||
|
echo \Yii::t('app', 'Today is {0, date}', time());
|
||
|
```
|
||
|
|
||
|
Built in formats (`short`, `medium`, `long`, `full`):
|
||
|
|
||
|
```php
|
||
|
echo \Yii::t('app', 'Today is {0, date, short}', time());
|
||
|
```
|
||
|
|
||
|
Custom pattern:
|
||
|
|
||
|
```php
|
||
|
echo \Yii::t('app', 'Today is {0, date, YYYY-MM-dd}', time());
|
||
|
```
|
||
|
|
||
|
[Formatting reference](http://icu-project.org/apiref/icu4c/classicu_1_1SimpleDateFormat.html).
|
||
|
|
||
11 years ago
|
#### Time
|
||
11 years ago
|
|
||
|
```php
|
||
|
echo \Yii::t('app', 'It is {0, time}', time());
|
||
|
```
|
||
|
|
||
|
Built in formats (`short`, `medium`, `long`, `full`):
|
||
|
|
||
|
```php
|
||
|
echo \Yii::t('app', 'It is {0, time, short}', time());
|
||
|
```
|
||
|
|
||
|
Custom pattern:
|
||
|
|
||
|
```php
|
||
|
echo \Yii::t('app', 'It is {0, date, HH:mm}', time());
|
||
|
```
|
||
|
|
||
|
[Formatting reference](http://icu-project.org/apiref/icu4c/classicu_1_1SimpleDateFormat.html).
|
||
|
|
||
|
|
||
11 years ago
|
#### Spellout
|
||
11 years ago
|
|
||
|
```php
|
||
11 years ago
|
echo \Yii::t('app', '{n,number} is spelled as {n, spellout}', ['n' => 42]);
|
||
11 years ago
|
```
|
||
|
|
||
11 years ago
|
#### Ordinal
|
||
11 years ago
|
|
||
|
```php
|
||
11 years ago
|
echo \Yii::t('app', 'You are {n, ordinal} visitor here!', ['n' => 42]);
|
||
11 years ago
|
```
|
||
|
|
||
|
Will produce "You are 42nd visitor here!".
|
||
|
|
||
11 years ago
|
#### Duration
|
||
11 years ago
|
|
||
|
|
||
|
```php
|
||
11 years ago
|
echo \Yii::t('app', 'You are here for {n, duration} already!', ['n' => 47]);
|
||
11 years ago
|
```
|
||
|
|
||
|
Will produce "You are here for 47 sec. already!".
|
||
|
|
||
11 years ago
|
#### Plurals
|
||
11 years ago
|
|
||
|
Different languages have different ways to inflect plurals. Some rules are very complex so it's very handy that this
|
||
|
functionality is provided without the need to specify inflection rule. Instead it only requires your input of inflected
|
||
|
word in certain situations.
|
||
|
|
||
|
```php
|
||
11 years ago
|
echo \Yii::t('app', 'There {n, plural, =0{are no cats} =1{is one cat} other{are # cats}}!', ['n' => 0]);
|
||
11 years ago
|
```
|
||
|
|
||
|
Will give us "There are no cats!".
|
||
|
|
||
|
In the plural rule arguments above `=0` means exactly zero, `=1` stands for exactly one `other` is for any other number.
|
||
|
`#` is replaced with the `n` argument value. It's not that simple for languages other than English. Here's an example
|
||
|
for Russian:
|
||
|
|
||
|
```
|
||
|
Здесь {n, plural, =0{котов нет} =1{есть один кот} one{# кот} few{# кота} many{# котов} other{# кота}}!
|
||
|
```
|
||
|
|
||
|
In the above it worth mentioning that `=1` matches exactly `n = 1` while `one` matches `21` or `101`.
|
||
|
|
||
11 years ago
|
Note that if you are using placeholder twice and one time it's used as plural another one should be used as number else
|
||
|
you'll get "Inconsistent types declared for an argument: U_ARGUMENT_TYPE_MISMATCH" error:
|
||
|
|
||
|
```
|
||
|
Total {count, number} {count, plural, one{item} other{items}}.
|
||
|
```
|
||
|
|
||
11 years ago
|
To learn which inflection forms you should specify for your language you can referrer to
|
||
11 years ago
|
[rules reference at unicode.org](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html).
|
||
|
|
||
11 years ago
|
#### Selections
|
||
11 years ago
|
|
||
|
You can select phrases based on keywords. The pattern in this case specifies how to map keywords to phrases and
|
||
|
provides a default phrase.
|
||
|
|
||
|
```php
|
||
11 years ago
|
echo \Yii::t('app', '{name} is {gender} and {gender, select, female{she} male{he} other{it}} loves Yii!', [
|
||
11 years ago
|
'name' => 'Snoopy',
|
||
|
'gender' => 'dog',
|
||
11 years ago
|
]);
|
||
11 years ago
|
```
|
||
|
|
||
|
Will produce "Snoopy is dog and it loves Yii!".
|
||
|
|
||
|
In the expression `female` and `male` are possible values. `other` handler values that do not match. Strings inside
|
||
|
brackets are sub-expressions so could be just a string or a string with more placeholders.
|
||
|
|
||
11 years ago
|
### Specifying default translation
|
||
11 years ago
|
|
||
11 years ago
|
You can specify default translation that will be used as a fallback for categories that don't match any other translation.
|
||
|
This translation should be marked with `*`. In order to do it add the following to the config file (for the `yii2-basic` application it will be `web.php`):
|
||
11 years ago
|
|
||
|
```php
|
||
|
//configure i18n component
|
||
|
|
||
|
'i18n' => [
|
||
11 years ago
|
'translations' => [
|
||
|
'*' => [
|
||
|
'class' => 'yii\i18n\PhpMessageSource'
|
||
|
],
|
||
|
],
|
||
11 years ago
|
],
|
||
|
```
|
||
|
|
||
11 years ago
|
Now you can use categories without configuring each one that is similar to Yii 1.1 behavior.
|
||
|
Messages for the category will be loaded from a file under default translation `basePath` that is `@app/messages`:
|
||
11 years ago
|
|
||
11 years ago
|
```php
|
||
11 years ago
|
echo Yii::t('not_specified_category', 'message from unspecified category');
|
||
11 years ago
|
```
|
||
|
|
||
11 years ago
|
Message will be loaded from `@app/messages/<LanguageCode>/not_specified_category.php`.
|
||
11 years ago
|
|
||
11 years ago
|
### Translating module messages
|
||
11 years ago
|
|
||
11 years ago
|
If you want to translate messages for a module and avoid using a single translation file for all messages, you can make it like the following:
|
||
11 years ago
|
|
||
|
```php
|
||
|
<?php
|
||
|
|
||
|
namespace app\modules\users;
|
||
|
|
||
|
use Yii;
|
||
|
|
||
|
class Module extends \yii\base\Module
|
||
|
{
|
||
11 years ago
|
public $controllerNamespace = 'app\modules\users\controllers';
|
||
|
|
||
|
public function init()
|
||
|
{
|
||
|
parent::init();
|
||
|
$this->registerTranslations();
|
||
|
}
|
||
|
|
||
|
public function registerTranslations()
|
||
|
{
|
||
|
Yii::$app->i18n->translations['modules/users/*'] = [
|
||
|
'class' => 'yii\i18n\PhpMessageSource',
|
||
|
'sourceLanguage' => 'en',
|
||
|
'basePath' => '@app/modules/users/messages',
|
||
|
'fileMap' => [
|
||
|
'modules/users/validation' => 'validation.php',
|
||
|
'modules/users/form' => 'form.php',
|
||
|
...
|
||
|
],
|
||
|
];
|
||
|
}
|
||
|
|
||
|
public static function t($category, $message, $params = [], $language = null)
|
||
|
{
|
||
|
return Yii::t('modules/users/' . $category, $message, $params, $language);
|
||
|
}
|
||
11 years ago
|
|
||
|
}
|
||
|
```
|
||
|
|
||
11 years ago
|
In the example above we are using wildcard for matching and then filtering each category per needed file. Instead of using `fileMap` you can simply
|
||
11 years ago
|
use convention of category mapping to the same named file and use `Module::t('validation', 'your custom validation message')` or `Module::t('form', 'some form label')` directly.
|
||
11 years ago
|
|
||
11 years ago
|
### Translating widgets messages
|
||
11 years ago
|
|
||
|
Same rules can be applied for widgets too, for example:
|
||
|
|
||
|
```php
|
||
|
<?php
|
||
|
|
||
|
namespace app\widgets\menu;
|
||
|
|
||
|
use yii\base\Widget;
|
||
|
use Yii;
|
||
|
|
||
|
class Menu extends Widget
|
||
|
{
|
||
|
|
||
11 years ago
|
public function init()
|
||
|
{
|
||
|
parent::init();
|
||
|
$this->registerTranslations();
|
||
|
}
|
||
|
|
||
|
public function registerTranslations()
|
||
|
{
|
||
|
$i18n = Yii::$app->i18n;
|
||
|
$i18n->translations['widgets/menu/*'] = [
|
||
|
'class' => 'yii\i18n\PhpMessageSource',
|
||
|
'sourceLanguage' => 'en',
|
||
|
'basePath' => '@app/widgets/menu/messages',
|
||
|
'fileMap' => [
|
||
|
'widgets/menu/messages' => 'messages.php',
|
||
|
],
|
||
|
];
|
||
|
}
|
||
|
|
||
|
public function run()
|
||
|
{
|
||
|
echo $this->render('index');
|
||
|
}
|
||
|
|
||
|
public static function t($category, $message, $params = [], $language = null)
|
||
|
{
|
||
|
return Yii::t('widgets/menu/' . $category, $message, $params, $language);
|
||
|
}
|
||
11 years ago
|
|
||
|
}
|
||
|
```
|
||
|
|
||
11 years ago
|
Instead of using `fileMap` you can simply use convention of category mapping to the same named file and use `Menu::t('messages', 'new messages {messages}', ['{messages}' => 10])` directly.
|
||
11 years ago
|
|
||
11 years ago
|
> **Note**: For widgets you also can use i18n views, same rules as for controllers are applied to them too.
|
||
|
|
||
11 years ago
|
|
||
|
### Translating framework messages
|
||
|
|
||
|
Sometimes you want to correct default framework message translation for your application. In order to do so configure
|
||
|
`i18n` component like the following:
|
||
|
|
||
|
```php
|
||
|
'components' => [
|
||
|
'i18n' => [
|
||
|
'translations' => [
|
||
|
'yii' => [
|
||
|
'class' => 'yii\i18n\PhpMessageSource',
|
||
|
'sourceLanguage' => 'en',
|
||
|
'basePath' => '/path/to/my/message/files'
|
||
|
],
|
||
|
],
|
||
|
],
|
||
|
],
|
||
|
```
|
||
|
|
||
|
Now you can place your adjusted translations to `/path/to/my/message/files`.
|
||
|
|
||
11 years ago
|
Views
|
||
|
-----
|
||
|
|
||
|
You can use i18n in your views to provide support for different languages. For example, if you have view `views/site/index.php` and
|
||
|
you want to create special case for russian language, you create `ru-RU` folder under the view path of current controller/widget and
|
||
|
put there file for russian language as follows `views/site/ru-RU/index.php`.
|
||
|
|
||
|
> **Note**: If language is specified as `en-US` and there are no corresponding views, Yii will try views under `en`
|
||
|
> before using original ones.
|
||
|
|
||
|
i18n formatter
|
||
|
--------------
|
||
|
|
||
|
i18n formatter component is the localized version of formatter that supports formatting of date, time and numbers based
|
||
|
on current locale. In order to use it you need to configure formatter application component as follows:
|
||
|
|
||
|
```php
|
||
|
return [
|
||
11 years ago
|
// ...
|
||
|
'components' => [
|
||
|
'formatter' => [
|
||
|
'class' => 'yii\i18n\Formatter',
|
||
|
],
|
||
|
],
|
||
11 years ago
|
];
|
||
|
```
|
||
|
|
||
|
After cofiguring the component can be accessed as `Yii::$app->formatter`.
|
||
|
|
||
|
Note that in order to use i18n formatter you need to install and enable
|
||
|
[intl](http://www.php.net/manual/en/intro.intl.php) PHP extension.
|
||
|
|
||
11 years ago
|
In order to learn about formatter methods refer to its API documentation: [[yii\i18n\Formatter]].
|