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

19 января 2018

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

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

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

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

Что такое I18n?

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

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

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

Цели интернационализации

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

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

Как работает I18

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

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

public function attributeLabels()
{
    return [
      'id' => 'ID',
      'message' => 'Message',
      'permissions' => 'Permissions',
      'created_at' => 'Created At',
      'updated_at' => 'Updated At',        ];
}

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

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

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 () использует этот файл, чтобы найти соответствующий перевод для отображения:

<?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. Check i18n sectio
* 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?' => '¿Seguro que quieres borrar este artículo?',
    'Congratulations!' => '¡Enhorabuena!',
    '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. 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.' => 'Lorem ipsum dolor sit amet, consectetur adipisicing elit , sed no tempor eiusmod ut labore et dolore incididunt magna aliqua . Ut enim ad minim veniam , nostrud quis ullamco exercitation nisi ut laboris aliquip commodo ex ea consequat . Duis Aute Irure dolor en reprehenderit en voluptate velita esse cillum dolore eu nulla fugiat pariatur .',
    '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.' => 'Ha creado su aplicación Yii con alimentación.',
];

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

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

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

Настройка поддержки I18

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

Создание файла конфигурации I18

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

Из нашего корня базы кода мы запустим сценарий конфигурации сообщений Yii:

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

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

<?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. For example, ['zh-CN', 'de'].
    'languages' => ['de'],
    // string, the name of the function for translating messages.
    // Defaults to 'Yii::t'. This is used as a mark to find the messages to be
    // translated. You may use a string for single function name or an array for
    // multiple function names.
    'translator' => 'Yii::t',
    // boolean, whether to sort messages by keys when merging new messages
    // with the existing ones. Defaults to false, which means the new (untranslated)
    // 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. For example,
    // '/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__ . DIRECTORY_SEPARATOR . 'messages',
    // 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',
    // Coection component to use. Optional.
    'db' => 'db',
    // Custom source message table. Optional.
    // 'sourceMessageTable' => '{{%source_message}}',
    // Custom name for translation message table. Optional.
    // 'messageTable' => '{{%message}}',
    */
    /*
    // 'po' output format is for saving messages to gettext po files.
    'format' => 'po',
    // Root directory containing message translations.
    'messagePath' => __DIR__ . DIRECTORY_SEPARATOR . 'messages',
    // 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. Я также указываю языки, на которых хочу, чтобы мое приложение поддерживалось помимо английского - в данном случае испанского (испанского), немецкого (de), итальянского (it) и японского (ja). Вот список всех кодов языка I18n.

<?php
return [
    // string, required, root directory of all source files
    'sourcePath' => __DIR__. DIRECTORY_SEPARATOR .'..',
    // Root directory containing message translations.
    'messagePath' => __DIR__ . DIRECTORY_SEPARATOR .'..'. DIRECTORY_SEPARATOR . 'messages',
    // array, required, list of language codes that the extracted messages
    // should be translated to. For example, ['zh-CN', 'de'].
    'languages' => ['de','es','it','ja'],
    // string, the name of the function for translating messages.
    // Defaults to 'Yii::t'. This is used as a mark to find the messages to be
    // translated. You may use a string for single function name or an array for
    // multiple function names.
    'translator' => 'Yii::t',

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

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

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

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...

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

Активация I18n и выбор языка

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

<?php
$params = require(__DIR__ . '/params.php');
$config = [
    'id' => 'basic',
    'basePath' => dirname(__DIR__),
    'bootstrap' => ['log'],
    'language'=>'es', // spanish
    'components' => [

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

Использование Yii-генератора кода Gii с I18

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

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

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

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

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

<?php
use yiihelpersHtml;
use yiigridGridView;
/* @var $this yiiwebView */
/* @var $searchModel appmodelsStatusSearch */
/* @var $dataProvider yiidataActiveDataProvider */
$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' => 'yiigridSerialColumn'],
            'id',
            'message:ntext',
            'permissions',
            'created_at',
            'updated_at',
            ['class' => 'yiigridActionColumn'],
        ],
    ]); ?>
</div>

Создание статических представлений I18n Ready

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

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

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:

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 ():

<div class="jumbotron">
        <h1><?= Yii::t('app','Congratulations!'); ?></h1>
        <p class="lead"><?= Yii::t('app','You have successfully created your Yii-powered application.'); ?></p>
        <p><a class="btn btn-lg btn-success" href="http://www.yiiframework.com"><?= Yii::t('app','Get started with Yii'); ?></a></p>
    </div>
    <div class="body-content">
        <div class="row">
            <div class="col-lg-4">
                <h2><?= Yii::t('app','Heading'); ?></h2>
                <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>
                <p><a class="btn btn-default" href="http://www.yiiframework.com/doc/"><?= Yii::t('app','Yii Documentation') ?> &raquo;</a></p>
            </div>

Перевод файлов сообщений

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

return [
    'About' => '',
    'Contact' => '',
    'Home' => '',
    'Logout' => '',
    'My Company' => '',
    'Sign In' => '',
    'Sign Up' => '',
    'Status' => '',
    ...

В целях заполнения наших переводов на испанском языке для этого урока я использую Google Translate. Трудно, да?

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

return [
    'About' => 'Acerca de',
    'Contact' => 'Contacto',
    'Home' => 'Home',
    'Logout' => 'Salir',
    'My Company' => 'Mi Empresa',
    'Sign In' => 'Entrar',
    'Sign Up' => 'Registrarse',
    'Status' => 'Estado',

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

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

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

<?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 может служить полезным ориентиром для поддержки этого.

Далее с I18

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

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

 Yii:: $ app-> language =  'es ';

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

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

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

Что дальше?

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

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

Ссылки по теме

Yii Веб-сайт Framework Введение в Yii Framework (Tuts +) Использование API-интерфейса Google Translate для локализации вашего приложения I18n (Tuts +) Создание вашего запуска с PHP: Локализация с помощью I18n (Tuts +) Другие примеры разработчика Yii с открытым исходным кодом с помощью инструктора