Статьи

Как программировать с Yii2: локализация с I18n

Конечный продукт
Что вы будете создавать

Если вы спрашиваете: «Что такое Yii?» Посмотрите мой предыдущий учебник: Введение в Yii Framework , который рассматривает преимущества Yii и включает обзор того, что нового в Yii 2.0, выпущенном 12 октября 2014 года.

В этой серии «Программирование с Yii2» я расскажу читателям, как использовать обновленную версию Yii2 Framework для PHP. В первой части мы настроили Yii2 локально, создали приложение Hello World, настроили удаленный сервер и использовали Github для развертывания нашего кода. Во второй части мы узнали о реализации Yii его архитектуры Model View Controller и о том, как создавать веб-страницы и формы, которые собирают и проверяют данные. В третьей части мы использовали базу данных Yii и возможности активной записи для автоматизации генерации кода для базового веб-приложения. И в четвертой части мы узнали, как интегрировать регистрацию пользователей.

В этом руководстве я собираюсь показать вам, как использовать встроенную поддержку Y18n интернационализации Yiin, чтобы подготовить ваше приложение к переводу на несколько языков.

Для этих примеров мы продолжим предполагать, что мы создаем структуру для публикации простых обновлений статуса, например, наш собственный мини-Twitter.

Согласно Википедии , I18n является нумеронимом интернационализации:

18 обозначает число букв между первым i и последним n в интернационализации , использование, придуманное в DEC в 1970-х или 80-х.

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

При создании веб-приложения полезно мыслить глобально с самого начала. Должно ли ваше приложение работать на других языках для пользователей из разных стран? Если это так, то внедрение I18n с самого начала сэкономит вам много времени и головной боли позже.

В нашем случае Yii Framework предоставляет встроенную поддержку I18n, поэтому относительно легко встроить поддержку I18n по мере продвижения.

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

Например, вот как выглядят имена полей атрибутов в модели состояния до I18n:

1
2
3
4
5
6
7
8
9
public function attributeLabels()
{
    return [
      ‘id’ => ‘ID’,
      ‘message’ => ‘Message’,
      ‘permissions’ => ‘Permissions’,
      ‘created_at’ => ‘Created At’,
      ‘updated_at’ => ‘Updated At’, ];
}

Предоставление переведенных версий кода станет очень сложным. Нетехнические переводчики должны будут переводить код на месте, вероятно, нарушая синтаксис.

Вот как выглядит тот же код с I18n:

1
2
3
4
5
6
7
8
9
public function attributeLabels()
{
    return [
      ‘id’ => Yii::t(‘app’, ‘ID’),
      ‘message’ => Yii::t(‘app’, ‘Message’),
      ‘permissions’ => Yii::t(‘app’, ‘Permissions’),
      ‘created_at’ => Yii::t(‘app’, ‘Created At’),
      ‘updated_at’ => Yii::t(‘app’, ‘Updated At’), ];
}

Yii:t() — это вызов функции, который проверяет, какой язык выбран в данный момент, и отображает соответствующую переведенную строку. Параметр 'app' относится к разделу нашего приложения. При желании переводы могут быть организованы в соответствии с различными категориями. Но где эти переведенные строки появляются?

Язык по умолчанию, в данном случае английский, записывается в код, как показано выше. Файлы языковых ресурсов — это списки массивов строк, ключом которых является текст языка по умолчанию, например, 'Message' или 'Permissions' — и каждый файл предоставляет переведенные текстовые значения для соответствующего языка.

Вот пример нашего полного файла перевода на испанский язык с кодом «es». Функция Yii:t() использует этот файл, чтобы найти соответствующий перевод для отображения:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?php
/**
* Message translations.
*
* This file is automatically generated by ‘yii translate’ command.
* It contains the localizable messages extracted from source code.
* You may modify this file by translating the extracted messages.
*
* Each array element represents the translation (value) of a message (key).
* If the value is empty, the message is considered as not translated.
* Messages that no longer need translation will have their translations
* enclosed between a pair of ‘@@’ marks.
*
* Message string can be used with plural forms format.
* of the guide for details.
*
* NOTE: this file must be saved in UTF-8 encoding.
*/
return [
    ‘Get started with Yii’ => ‘Comience con Yii’,
    ‘Heading’ => ‘título’,
    ‘My Yii Application’ => ‘Mi aplicación Yii’,
    ‘Yii Documentation’ => ‘Yii Documentación’,
    ‘Yii Extensions’ => ‘Extensiones Yii’,
    ‘Yii Forum’ => ‘Yii Foro’,
    ‘Are you sure you want to delete this item?’
    ‘Congratulations!’
    ‘Create’ => ‘crear’,
    ‘Create {modelClass}’ => ‘crear {modelClass}’,
    ‘Created At’ => ‘Creado el’,
    ‘Delete’ => ‘borrar’,
    ‘ID’ => ‘identificación’,
    ‘Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
    ‘Message’ => ‘mensaje’,
    ‘Permissions’ => ‘Permisos’,
    ‘Reset’ => ‘reajustar’,
    ‘Search’ => ‘búsqueda’,
    ‘Statuses’ => ‘Los estados’,
    ‘Update’ => ‘actualización’,
    ‘Update {modelClass}: ‘ => ‘actualización {modelClass} :’,
    ‘Updated At’ => ‘Actualizado A’,
    ‘You have successfully created your Yii-powered application.’
];

Хотя это выглядит трудоемким, Yii предоставляет сценарии для автоматизации создания и организации этих шаблонов файлов.

Отделяя текст от кода, мы облегчаем для нетехнических многоязычных экспертов перевод наших приложений для нас — без нарушения кода.

I18n также предлагает специализированные функции для перевода времени, валюты, множественного числа и др. Я не буду вдаваться в детали этого в этом уроке.

К сожалению, документация по Yii2 для I18n еще не очень описательна, и было трудно найти рабочие, пошаговые примеры. К счастью для вас, я проведу вас через то, что я узнал, изучая документы и сеть. Я нашел пример Code Ninja I18n и Yii2 Definitive Guide on I18n полезным, и участник Yii Александр Макаров также предложил мне некоторую помощь.

Мы используем базовый шаблон приложения Yii2 для демонстрационного приложения. Это помещает нашу кодовую базу ниже корневого каталога /hello . Конфигурационные файлы Yii в /hello/config/* загружаются при каждом запросе страницы. Мы будем использовать сценарии сообщений Yiin I18n для создания файла конфигурации для I18n в common/config пути common/config .

Из нашего корня кодовой базы мы запустим скрипт Yii message/config :

1
./yii message/config @app/config/i18n.php

Это создает следующий шаблон файла, который мы можем настроить:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
<?php
 
return [
    // string, required, root directory of all source files
    ‘sourcePath’ => __DIR__,
    // array, required, list of language codes that the extracted messages
    // should be translated to.
    ‘languages’ => [‘de’],
    // string, the name of the function for translating messages.
    // Defaults to ‘Yii::t’.
    // translated.
    // multiple function names.
    ‘translator’ => ‘Yii::t’,
    // boolean, whether to sort messages by keys when merging new messages
    // with the existing ones.
    // messages will be separated from the old (translated) ones.
    ‘sort’ => false,
    // boolean, whether to remove messages that no longer appear in the source code.
    // Defaults to false, which means each of these messages will be enclosed with a pair of ‘@@’ marks.
    ‘removeUnused’ => false,
    // array, list of patterns that specify which files/directories should NOT be processed.
    // If empty or not set, all files/directories will be processed.
    // A path matches a pattern if it contains the pattern string at its end.
    // ‘/a/b’ will match all files and directories ending with ‘/a/b’;
    // the ‘*.svn’ will match all files and directories whose name ends with ‘.svn’.
    // and the ‘.svn’ will match all files and directories named exactly ‘.svn’.
    // Note, the ‘/’ characters in a pattern matches both ‘/’ and ‘\’.
    // See helpers/FileHelper::findFiles() description for more details on pattern matching rules.
    ‘only’ => [‘*.php’],
    // array, list of patterns that specify which files (not directories) should be processed.
    // If empty or not set, all files will be processed.
    // Please refer to «except» for details about the patterns.
    // If a file/directory matches both a pattern in «only» and «except», it will NOT be processed.
    ‘except’ => [
        ‘.svn’,
        ‘.git’,
        ‘.gitignore’,
        ‘.gitkeep’,
        ‘.hgignore’,
        ‘.hgkeep’,
        ‘/messages’,
    ],
 
    // ‘php’ output format is for saving messages to php files.
    ‘format’ => ‘php’,
    // Root directory containing message translations.
    ‘messagePath’ => __DIR__ .
    // boolean, whether the message file should be overwritten with the merged messages
    ‘overwrite’ => true,
 
 
    /*
    // ‘db’ output format is for saving messages to database.
    ‘format’ => ‘db’,
    // Connection component to use.
    ‘db’ => ‘db’,
    // Custom source message table.
    // ‘sourceMessageTable’ => ‘{{%source_message}}’,
    // Custom name for translation message table.
    // ‘messageTable’ => ‘{{%message}}’,
    */
 
    /*
    // ‘po’ output format is for saving messages to gettext po files.
    ‘format’ => ‘po’,
    // Root directory containing message translations.
    ‘messagePath’ => __DIR__ .
    // Name of the file that will be used for translations.
    ‘catalog’ => ‘messages’,
    // boolean, whether the message file should be overwritten with the merged messages
    ‘overwrite’ => true,
    */
];

Я настраиваю файл следующим образом. Я перемещаю messagePath вверх и настраиваю sourcePath и messagePath . Я также указываю языки, которые я хочу, чтобы моя заявка поддерживала помимо английского, в данном случае это испанский (и), немецкий (де), итальянский (ит) и японский (я). Вот список всех кодов языка I18n .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
<?php
 
return [
    // string, required, root directory of all source files
    ‘sourcePath’ => __DIR__.
    // Root directory containing message translations.
    ‘messagePath’ => __DIR__ .
    // array, required, list of language codes that the extracted messages
    // should be translated to.
    ‘languages’ => [‘de’,’es’,’it’,’ja’],
    // string, the name of the function for translating messages.
    // Defaults to ‘Yii::t’.
    // translated.
    // multiple function names.
    ‘translator’ => ‘Yii::t’,

На следующем шаге мы запустим скрипт извлечения Yii, который будет сканировать весь код в дереве sourcePath чтобы сгенерировать строковые файлы по умолчанию для всех меток, используемых в нашем коде. Я настраиваю sourcePath для сканирования всего дерева кода. Я настраиваю messagePath для генерации результирующих файлов в common/messages .

1
./yii message/extract @app/config/i18n.php

Вы увидите, как Yii сканирует все ваши файлы кода:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
Extracting messages from /Users/Jeff/Sites/hello/views/layouts/main.php…
Extracting messages from /Users/Jeff/Sites/hello/views/site/about.php…
Extracting messages from /Users/Jeff/Sites/hello/views/site/contact.php…
Extracting messages from /Users/Jeff/Sites/hello/views/site/error.php…
Extracting messages from /Users/Jeff/Sites/hello/views/site/index.php…
Extracting messages from /Users/Jeff/Sites/hello/views/site/login.php…
Extracting messages from /Users/Jeff/Sites/hello/views/site/say.php…
Extracting messages from /Users/Jeff/Sites/hello/views/status/_form.php…
Extracting messages from /Users/Jeff/Sites/hello/views/status/_search.php…
Extracting messages from /Users/Jeff/Sites/hello/views/status/create.php…
Extracting messages from /Users/Jeff/Sites/hello/views/status/index.php…
Extracting messages from /Users/Jeff/Sites/hello/views/status/update.php…
Extracting messages from /Users/Jeff/Sites/hello/views/status/view.php…
Extracting messages from /Users/Jeff/Sites/hello/web/index-test.php…
Extracting messages from /Users/Jeff/Sites/hello/web/index.php…

Когда он завершится, вы увидите что-то вроде этого в вашей кодовой базе:

Локализация Yii2 с путями каталогов I18n

В общем файле конфигурации, /hello/config/web.php , мы расскажем Yii о нашей новой языковой поддержке. Я сделаю испанский своим языком по умолчанию:

01
02
03
04
05
06
07
08
09
10
<?php
 
$params = require(__DIR__ . ‘/params.php’);
 
$config = [
    ‘id’ => ‘basic’,
    ‘basePath’ => dirname(__DIR__),
    ‘bootstrap’ => [‘log’],
    ‘language’=>’es’, // spanish
    ‘components’ => [

Но это еще не все. Мы должны сделать наш код I18n осведомленным.

В третьей части этой серии мы использовали базу данных Yii и возможности активной записи для автоматизации генерации кода. Но мы не активировали I18n, поэтому во весь наш код были встроены текстовые строки. Давайте переделаем это.

Мы возвращаемся к Gii, вероятно, http: // localhost: 8888 / hello / gii в вашем браузере, и перезапускаем модель и генераторы контроллера с активированным I18n.

Вот пример генерации кода модели Meeting с активированным I18n. Обратите внимание, что мы указываем «приложение» для нашей категории сообщений. Мы помещаем все наши текстовые строки в один файл категории приложения.

Модель состояния генератора кода Yii2 Gii для I18n

Давайте сделаем то же самое для генерации CRUD для контроллеров и представлений:

Yii2 Gii Code Generator для статуса CRUD с I18n

Если вы просматриваете сгенерированный код в моделях, контроллерах и представлениях, вы увидите текстовые строки, замененные функцией Yii:t('app',...) :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<?php
 
use yii\helpers\Html;
use yii\grid\GridView;
 
/* @var $this yii\web\View */
/* @var $searchModel app\models\StatusSearch */
/* @var $dataProvider yii\data\ActiveDataProvider */
 
$this->title = Yii::t(‘app’, ‘Statuses’);
$this->params[‘breadcrumbs’][] = $this->title;
?>
<div class=»status-index»>
 
    <h1><?= Html::encode($this->title) ?></h1>
    <?php // echo $this->render(‘_search’, [‘model’ => $searchModel]);
 
    <p>
        <?= Html::a(Yii::t(‘app’, ‘Create {modelClass}’, [
    ‘modelClass’ => ‘Status’,
]), [‘create’], [‘class’ => ‘btn btn-success’]) ?>
    </p>
 
    <?= GridView::widget([
        ‘dataProvider’ => $dataProvider,
        ‘filterModel’ => $searchModel,
        ‘columns’ => [
            [‘class’ => ‘yii\grid\SerialColumn’],
 
            ‘id’,
            ‘message:ntext’,
            ‘permissions’,
            ‘created_at’,
            ‘updated_at’,
 
            [‘class’ => ‘yii\grid\ActionColumn’],
        ],
    ]);
 
</div>

Поскольку мы генерируем несколько представлений в нашем приложении вручную или в HTML, нам необходимо вручную преобразовать их для использования I18n. Например, нашу панель навигации в /views/layouts/main.php и нашу домашнюю страницу в /views/site/index.php необходимо редактировать вручную.

Вот панель навигации до I18n:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
NavBar::begin([
               ‘brandLabel’ => ‘My Company’,
               ‘brandUrl’ => Yii::$app->homeUrl,
               ‘options’ => [
                   ‘class’ => ‘navbar-inverse navbar-fixed-top’,
               ],
           ]);
           $navItems=[
               [‘label’ => ‘Home’, ‘url’ => [‘/site/index’]],
               [‘label’ => ‘Status’, ‘url’ => [‘/status/index’]],
               [‘label’ => ‘About’, ‘url’ => [‘/site/about’]],
               [‘label’ => ‘Contact’, ‘url’ => [‘/site/contact’]]
             ];
             if (Yii::$app->user->isGuest) {
               array_push($navItems,[‘label’ => ‘Sign In’, ‘url’ => [‘/user/login’]],[‘label’ => ‘Sign Up’, ‘url’ => [‘/user/register’]]);
             } else {
               array_push($navItems,[‘label’ => ‘Logout (‘ . Yii::$app->user->identity->username . ‘)’,
                   ‘url’ => [‘/site/logout’],
                   ‘linkOptions’ => [‘data-method’ => ‘post’]]
               );
             }
           echo Nav::widget([
               ‘options’ => [‘class’ => ‘navbar-nav navbar-right’],
               ‘items’ => $navItems,
           ]);
           NavBar::end();

Вот панель навигации после I18n:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
NavBar::begin([
    ‘brandLabel’ => Yii::t(‘app’, ‘My Company’),
    ‘brandUrl’ => Yii::$app->homeUrl,
    ‘options’ => [
        ‘class’ => ‘navbar-inverse navbar-fixed-top’,
    ],
]);
$navItems=[
    [‘label’ => Yii::t(‘app’, ‘Home’), ‘url’ => [‘/site/index’]],
    [‘label’ => Yii::t(‘app’,’Status’), ‘url’ => [‘/status/index’]],
    [‘label’ => Yii::t(‘app’,’About’), ‘url’ => [‘/site/about’]],
    [‘label’ => Yii::t(‘app’,’Contact’), ‘url’ => [‘/site/contact’]]
  ];
  if (Yii::$app->user->isGuest) {
    array_push($navItems,[‘label’ => Yii::t(‘app’,’Sign In’), ‘url’ => [‘/user/login’]],[‘label’ => Yii::t(‘app’,’Sign Up’), ‘url’ => [‘/user/register’]]);
  } else {
    array_push($navItems,[‘label’ => Yii::t(‘app’,’Logout’).’ (‘ . Yii::$app->user->identity->username . ‘)’,
        ‘url’ => [‘/site/logout’],
        ‘linkOptions’ => [‘data-method’ => ‘post’]]
    );
  }
echo Nav::widget([
    ‘options’ => [‘class’ => ‘navbar-nav navbar-right’],
    ‘items’ => $navItems,
]);
NavBar::end();

Вот фрагмент содержимого домашней страницы из index.php после I18n — большая часть HTML была заменена PHP-вызовами Yii::t() :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
<div class=»jumbotron»>
       <h1><?= Yii::t(‘app’,’Congratulations!’);
 
       <p class=»lead»><?= Yii::t(‘app’,’You have successfully created your Yii-powered application.’);
 
       <p><a class=»btn btn-lg btn-success» href=»http://www.yiiframework.com»><?= Yii::t(‘app’,’Get started with Yii’);
   </div>
 
   <div class=»body-content»>
 
       <div class=»row»>
           <div class=»col-lg-4″>
               <h2><?= Yii::t(‘app’,’Heading’);
 
               <p><?= Yii::t(‘app’,’Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.’);
 
               <p><a class=»btn btn-default» href=»http://www.yiiframework.com/doc/»><?= Yii::t(‘app’,’Yii Documentation’) ?> &raquo;</a></p>
           </div>

Взгляните на наш испанский файл сообщений, /common/messages/es/frontend.php . Это длинный список значений пустого массива:

01
02
03
04
05
06
07
08
09
10
return [
   ‘About’ => »,
   ‘Contact’ => »,
   ‘Home’ => »,
   ‘Logout’ => »,
   ‘My Company’ => »,
   ‘Sign In’ => »,
   ‘Sign Up’ => »,
   ‘Status’ => »,
   …

Для заполнения наших переводов на испанский язык для этого урока я буду использовать Google Translate . Хитрый, а?

Yii2 I18n Использование Google Translator для заполнения файлов сообщений

Затем мы сделаем некоторые вставки с этими переводами обратно в файл сообщений.

1
2
3
4
5
6
7
8
9
return [
   ‘About’ => ‘Acerca de’,
   ‘Contact’ => ‘Contacto’,
   ‘Home’ => ‘Home’,
   ‘Logout’ => ‘Salir’,
   ‘My Company’ => ‘Mi Empresa’,
   ‘Sign In’ => ‘Entrar’,
   ‘Sign Up’ => ‘Registrarse’,
   ‘Status’ => ‘Estado’,

Когда мы посетим домашнюю страницу приложения, вы увидите испанскую версию — хорошо, а?

Yii2 I18n Домашняя страница нашего приложения на испанском языке

Вот форма создания статуса:

Yii2 I18n Создать обновление статуса в испанской форме

Если я хочу переключиться обратно на английский, я просто изменяю файл конфигурации, /config/web.php , обратно на английский:

01
02
03
04
05
06
07
08
09
10
<?php
 
$params = require(__DIR__ . ‘/params.php’);
 
$config = [
    ‘id’ => ‘basic’,
    ‘basePath’ => dirname(__DIR__),
    ‘bootstrap’ => [‘log’],
    ‘language’=>’en’, // back to English
    ‘components’ => [

В процессе работы вы также заметите, что замена строк в JavaScript имеет свои сложности. Я не исследовал это сам, но расширение Yii 1.x JsTrans может предоставить полезную рекомендацию для поддержки этого.

В конечном счете, мы можем захотеть перевести наше приложение на несколько языков. Я написал новый учебник под названием « Использование API Google Translate для локализации вашего приложения I18n» (Tuts +), который автоматически переводит ваше приложение на различные языки. Если он еще не опубликован, он будет опубликован в ближайшее время ( проверьте страницу моего инструктора ). Конечно, это просто обеспечивает базовые переводы. Вы можете нанять профессиональных переводчиков для настройки файлов впоследствии.

Некоторые приложения позволяют пользователям выбирать свой родной язык, чтобы при входе в систему пользовательский интерфейс автоматически переводил их. В Yii установка переменной $app->language делает это:

Другие приложения, такие как JScrambler.com ниже, используют URL-путь для переключения языков. Пользователь просто щелкает языковой префикс, который он хочет, например, «FR» , и приложение автоматически переводится:

JScrambler Динамические языковые пути

Примечание: прочитайте мое недавнее введение в JScrambler, чтобы узнать больше — это довольно полезный сервис.

Менеджер URL-адресов Yii также может предоставить такую ​​функциональность . Вероятно, я буду реализовывать эти функции в следующем уроке из этой серии статей Yii2, когда сосредоточусь на маршрутизации .

Я надеюсь, что вы воодушевлены мощью I18n и преимуществами использования Yii Framework по сравнению с ванильным PHP. Следите за будущими уроками в нашей серии «Программирование с Yii2» .

Если вы хотите узнать, когда появится следующий учебник по Yii2, следуйте за мной @reifman в Твиттере или зайдите на страницу моего инструктора . Моя страница инструктора будет включать все статьи из этой серии, как только они будут опубликованы. Вы также можете написать мне на мой сайт Lookahead Consulting .