Yii 2 - How to make internationalization (multilanguage)

2016-08-12
Yii

Consider the example of the internationalization of the site in Yii 2 in two languages: "de" and "en".

Yii 2 already have built-in component for make internationalization, it is called i18n. In order that you could start using it, it's enough to add it to the configuration of the application in the components section. Also, for the full operation of this component, requires a PHP extension intl.

In this article, we will consider an example of the internationalization of a site in Yii 2 in two languages: "de" and "en".

There are two variants of language identifiers:

  • abbreviated, for example: "de", "en";
  • full, for example: "de-DE", "en-US".

We will use the abbreviated version ("de" and "en"), since it is quite enough for the required functionality and it is more concisely perceived visually.

As a code base, you can use any site on Yii 2, or the basic application template provided by the framework. How to install the basic application template, you can read in this article.

Goals

Goals, which is necessary to achieve:

  • site switching between two languages: "de" and "en";
  • display the language in the address bar in the form http://site.com/en/;
  • automatic redirection of the user to the language most suitable for him, if he moved to the site without specifying a language;
  • storage of translations should be in PHP files in the arrays;
  • translations of static pages should be contained in separate view scripts for different languages.

Configuration

We begin the implementation with the writing configurations in the file \config\web.php:

[
    ...
    'sourceLanguage' => 'en',
    'language' => 'en',
    ...
    'components' => [
        ...
        'urlManager' => [
            'enablePrettyUrl' => true,
            'showScriptName' => false,
            'rules' => [
            ],
        ],
        ...
        'i18n' => [
            'translations' => [
                'common*' => [
                    'class' => 'yii\i18n\PhpMessageSource',
                    'basePath' => '@app/messages',
                ],
            ],
        ],
        ...
    ]
]

In this configuration:

sourceLanguage - is the source language of messages, i.e. the one that will be used as the translation keys, in this case en.

language - is the default language to which the user will be redirected if it is not possible to determine for him, the most appropriate language based on the data provided by his browser.

components -> urlManager - in this section we enable the "pretty URLs", for a more visual display of URLs.

components -> i18n - this section is intended for configuring the internationalization component. The key of the array common* - means the category to which the translations from the corresponding common.php file will apply. The category name can be specified and any other, or for example make several categories, but for an example we'll manage one. In the section 'class' => 'yii\i18n\PhpMessageSource' it is necessary to specify exactly how we will store messages, in this case it is indicated that in the PHP files (in the associative arrays). In addition to yii\i18n\PhpMessageSource, there are other options, for example, messages can be stored in a database. In the section 'basePath' => '@app/messages' there is a path to which PHP files with translations will be located.

File with translations list

Now you need to create a PHP file that will contain an associative array with translations.

When configuring the i18n component, we specified:

...
'translations' => [
    'common*' => [
        'class' => 'yii\i18n\PhpMessageSource',
        'basePath' => '@app/messages',
    ],
],
...

This means that the translation file must have the same name as the category name, i.e. common.php. It should be stored in this path \messages\de\common.php.

For our example with two languages ("de" and "en"), it is enough to create one file with translations only for the German language, since English translations will be in the form of keys for all other translations. I.e. everywhere on the site will be English messages, and when switching to German, the framework will replace them with the German ones.

For example, if we wanted to add some additional language, for example "fr", then in addition to the file \messages\de\common.php we will be need to create a similar for the French language \messages\fr\common.php. But in our example there will be only one file \messages\de\common.php, create it, while with an empty array:

<?php

return [];

Adding translations

Now you need to add translations in the site code and also add them to the file \messages\de\common.php.

To output translations in Yii 2, there is a special method Yii::t(). Example of use:

<?= Yii::t('common', 'Example text...') ?>

In the first argument, we specify the category, we have one - common. In the second argument, we write English text as it should display on the site.

After this, you need to add the German translation to the file \messages\de\common.php:

return [
    ...
    'Example text...' => 'Beispiel text...',
    ...
];

We use English translation as a key, and German as the value.

It is necessary to perform such actions throughout the code, i.e. all the texts displayed are to be replaced by English, and output them with the Yii::t() method. And also add German translations for all English texts to the file \messages\de\common.php.

Switching languages

The user should be able to switch between languages. To do this, you need to display links for switching site to German or English languages. For this we will create a special view script \views\layouts\main\select-language.php:

<?php

use yii\bootstrap\Html;

if(\Yii::$app->language == 'de'):
    echo Html::a('Go to English', array_merge(
        \Yii::$app->request->get(),
        [\Yii::$app->controller->route, 'language' => 'en']
    ));
else:
    echo Html::a('Wechseln zu Deutsch', array_merge(
        \Yii::$app->request->get(),
        [\Yii::$app->controller->route, 'language' => 'de']
    ));
endif;

Next, you need to render this view script in the main view script \views\layouts\main.php, for this, the following line:

<p class="pull-left">&copy; My Company <?= date('Y') ?></p>

should be replaced by:

<p class="pull-left">&copy; My Company <?= date('Y') ?> | <?= $this->render('main/select-language') ?></p>

In this line, we added the rendering of the view script to switching languages, with the help of it, in the footer, a link will be displayed for switching to another language.

Using an external UrlManager

Now we need to make display the current language in the address bar, for example http://site.com/en. Unfortunately, Yii 2 does not provide any built-in capabilities for this, and even to do this by some adequate roundabout ways does not work out, or at very difficult. The most "correct" way out in this situation is to override the standard class yii\web\UrlManager on your own, in which some methods will be replaced. These replaced methods must take on the implementation of the language displaying in the address bar.

In general, the replacement of standard classes of the framework for third-party ones, personally in my opinion it is a hardcode. But apparently in the Yii 2 framework it is unfortunately a normal practice.

We will not write our own implementation of the class "UrlManager", since this "bicycle" has already been created and it can simply be required via Composer, which we will do. Go to the console in the project's root folder, and execute the command (here it means that Composer is installed globally):

$ composer require codemix/yii2-localeurls

The codemix/yii2-localeurls package gives us the following options:

  • display the current language in the address bar;
  • redirect the user to the most appropriate language for him based on the data sent by his browser.

Next, we need to reconfigure the urlManager component, that he would not use the standard class of the framework, but just required. To do this, in the file \config\web.php, in the section componets -> urlManager we add the following three lines:

[
    ...
    'components' => [
        ...
        'urlManager' => [
            ...
            'class' => 'codemix\localeurls\UrlManager',
            'languages' => ['de', 'en'],
            'enableDefaultLanguageUrlCode' => true,
            ...
        ],
        ...
    ]
    ...
]

A few explanations for this configuration:

'class' => 'codemix\localeurls\UrlManager' - we specify the framework that instead of the standard class UrlManager the one that we just required via Composer was used.

'languages' => ['de', 'en'] - in which languages the project is translated.

'enableDefaultLanguageUrlCode' => true - this flag means that we want the current language displayed in the address bar always, even if the user simply is on the main page (i.e. the address will be of the form http://site.com/en). If desired, this flag can also be omitted, by default it will be set to false, which means that on the main page the language in the address bar will not be displayed (i.e. the address will be of the form http://site.com).

Static pages internationalization

Yii 2 have a built-in class \yii\web\ViewAction, which is designed to implement static pages.

But by default it does not support internationalization.

The bottom line is that static pages have large text, which is not convenient to insert into translation files (i.e. in our case in the file \messages\de\common.php). Translations for static pages are best stored in separate files - in different view scripts which will correspond to different languages.

We implement internationalized static pages, for this purpose in the file \controllers\SiteController.php, in the actions() method, add the following section:

public function actions()
{
    return [
        ...
        'page' => [
            'class' => \yii\web\ViewAction::className(),
            'viewPrefix' => 'pages/' . \Yii::$app->language
        ]
        ...
    ];
}

This section contains the main code that allows to internationalize static pages, this is 'viewPrefix' => 'pages/' . \Yii::$app->language. In the viewPrefix parameter, we specify on what path we will have view scripts for static pages, but in our case we also substitute in this path current language \Yii::$app->language.

Next, create two view scripts that will correspond to one static page, but everyone will be for their language. For English \views\site\pages\en\some.php should contain the text: "English static page...". For German \views\site\pages\de\some.php should contain the text: "Deutsche statische Seite...".

Now we will configure the "pretty URLs" for static pages, for this, in the config \config\web.php in the section urlManager -> rules add the rule:

'urlManager' => [
    ...
    'rules' => [
        'page/<view:[a-zA-Z0-9-]+>' => 'site/page',
    ],
    ...
],

On this, the implementation of static pages is finished, you can check, try to open two addresses in the browser:

  • http://site.com/de/page/some - a German static page will open;
  • http://site.com/en/page/some - an English static page will open;

Conclusion

In general, the implementation of internationalization in Yii 2 does not represent anything complicated, especially if you use the third-party component codemix/yii2-localeurls.

The goals that were described at the beginning of the article are fully implemented:

  • the site switching between the two languages takes place by link which is located in the footer;
  • the current language is displayed in the address bar in the form http://site.com/en/;
  • if the user went to the site without specifying the language (i.e. http://site.com), then it will be redirected to the most appropriate language for it (e.g. http://site.com/en);
  • translations is stored in PHP files in the associative arrays;
  • translations of static pages are stored in separate view scripts for different languages.