Кажется, что PHP сопротивляется времени и по-прежнему остается довольно популярным выбором для создания веб-приложений. Мы уже обсуждали как переводить PHP-приложения. Сегодня мы сосредоточимся на Zend Framework, объектно-ориентированной среде PHP с открытым исходным кодом, состоящей из более чем 60 пакетов, каждый из которых служит определенной цели. Именно пакет zend-i18n делает PHP i18n веб-приложений возможным. Существует еще один, называемый zend-mvc-i18n, который необходим для интеграции пакета zend-i18n в структуру MVC. Поскольку большинство веб-приложений, которые мы создаем, следуют шаблону проектирования MVC, в этом руководстве мы будем использовать zend-mvc-i18n.

Вы можете установить пакет Zend, используя следующую команду:

composer require zendframework/zendframework

Вы также можете установить только mvc-i18n, используя следующую команду:

composer require zendframework/zend-mvc-i18n

Composer — это инструмент управления зависимостями, который установит для вас все необходимые зависимости Zend Framework. Если вы еще не установили Composer на свой компьютер, вы можете получить его здесь.

Для этого урока я создал пример проекта. Вот как выглядит структура папок простого проекта:

Для нас важны три раздела: languages, public и src. Мы обсудим их по мере продвижения к руководству.

Объект переводчика

zend-i18n поставляется с полным пакетом переводов, поддерживающим все основные форматы. Он также включает в себя популярные функции, которые поставляются с объектом-переводчиком. Наша следующая задача — определить объект Translator. Для этого необходимо настроить три аспекта:

  • Формат перевода
  • Местоположение ресурса перевода
  • Файл ресурсов перевода

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

  • Массивы PHP (хранить переведенные тексты в массивах PHP),
  • gettext (переведенные тексты как обычные тексты в файле)
  • Tmx (стандарт XML под названием Translation Memory Exchange)
  • Xliff (формат файла на основе XML)

Использование массивов PHP — не самая оптимальная практика. Вы всегда должны отделять строки перевода от исходного кода; gettext — самый простой и, вероятно, самый распространенный метод — именно его я буду использовать в этом приложении. Тем не менее, я покажу вам, как хранить переведенные тексты в форматах xliff и Tmx.

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

  • Приложение/язык
  • данные/язык

Как видите, я использую путь к приложению/языку для хранения файлов перевода.

Третья конфигурация передает файлы перевода объекту Translator. Вы можете добавить каждый файл по отдельности или добавить все файлы сразу, используя шаблон. Предположим, у вас есть 10 исходных файлов перевода для 10 разных языков. Добавлять каждый файл по отдельности может быть довольно обременительно. Поэтому рекомендуется использовать шаблон файла для добавления всех файлов в переводчик.

Теперь создадим объект Translator:

<?php
use Zend\I18n\Translator\Translator;

return Translator::factory([
    'translation_file_patterns' => [
        [
            'type'     => 'gettext',
            'base_dir' => __DIR__ . '/../languages',
            'pattern'  => '%s.mo',
        ],
    ],
]);

Обратите внимание, как я установил шаблон — что-то вроде шаблона sprintf.%s.mo говорит переводчику принять любую строку с расширением MO. На моем языке есть четыре файла: es.po, es.mo, fr.po и fr.mo (ES означает испанский, а FR — французский); Файлы .PO содержат фактический перевод, а файлы .MO содержат машинный объект или файл двоичных данных. Переводчику нужны бинарные файлы. Поэтому %s.mo передает es.mo и fr.mo транслятору.

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

После создания объекта Translator мы можем перейти к переводу сообщений. Это можно сделать с помощью метода translate объекта Translator. Перевод текста выполняется в представлении. В нашем приложении представление — это файл index.php в папке Public. Прежде чем использовать объект Translator, вам необходимо включить translation.php в файл index.php.

Формат метода перевода:

$translator->translate($message, $textDomain, $locale);

Его параметры представляют собой следующее:

  • $message — сообщение для перевода
  • $textDomain — текстовый домен, в котором находится перевод. Это необязательный параметр. Значение по умолчанию — «по умолчанию».
  • $locale — Должна использоваться локаль. Это необязательный параметр. Если не установлено, будет использоваться локаль по умолчанию.

Пример метода перевода:

$translator->translate("Translated text from a custom text domain.", "customDomain",”de_DE“);

В любом случае, в нашем приложении мы будем использовать метод перевода немного по-другому. Не имеет особого смысла устанавливать локаль в каждом переведенном сообщении. Вряд ли вы когда-нибудь будете использовать несколько местных жителей в одном и том же представлении. Вы можете установить локаль, используя метод setLocale().

$lang = isset($_GET['lang']) ? $_GET['lang'] : 'en'; 
$translator->setLocale($lang);

Наконец, наш файл index.php будет выглядеть так:

<?php
include __DIR__ . '/../vendor/autoload.php';

$lang = isset($_GET['lang']) ? $_GET['lang'] : 'en';
/** @var Zend\I18n\Translator\Translator $translator */
$translator = include __DIR__ . '/../src/translator.php';
$translator->setLocale($lang);
?>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
    </head>
    <body>
        <p><?php echo $translator->translate('Hello my friend'); ?></p>
        <p><?php echo $translator->translate('How are you?'); ?></p>
        <p><?php echo $translator->translate('My name is Adam Crik'); ?></p>
    </body>
</html>

Форматы перевода

Как уже отмечалось в начале, я также хотел бы показать вам, как хранить переведенные тексты в разных форматах. В нашем приложении есть три строки. В gettext переведенные тексты хранятся в файлах .PO.

Наш файл fr.po выглядит так:

#: ../public/index.php:1
msgid "Hello my friend"
msgstr "Bonjour, mon ami"


#: ../public/index.php:2
msgid "How are you?"
msgstr "Comment allez-vous?"

#: ../public/index.php:3
msgid "My name is Adam Crik"
msgstr "Je m'appelle Adam Crik"

Если мы используем формат XLIFF, это будет выглядеть примерно так:

<xliff xmlns="urn:oasis:names:tc:xliff:document:2.0" version="2.0"
 srcLang="en-US" trgLang="fr">
 <file id="f1">
  <unit id="1">
   <segment>
    <source>Hello my friend</source>
    <target>Bonjour, mon ami</target>
   </segment>
  </unit>
  <unit id="2">
   <segment>
    <source>How are you?</source>
    <target>Comment allez-vous?</target>
   </segment>
  </unit>
  <unit id="3">
   <segment>
    <source>My name is Adam Crik</source>
    <target>Je m'appelle Adam Crik</target>
   </segment>
  </unit>
 </file>
</xliff>

Если мы используем формат TMX, это будет примерно так:

<tmx version="1.4">
  <body>
    <tu>
      <tuv xml:lang="en">
        <seg>Hello my friend</seg>
      </tuv>
      <tuv xml:lang="fr">
        <seg>Bonjour, mon ami</seg>
      </tuv>
	<tuv xml:lang="en">
        <seg>How are you?</seg>
      </tuv>
      <tuv xml:lang="fr">
        <seg>Comment allez-vous?</seg>
      </tuv>
	<tuv xml:lang="en">
        <seg>My name is John Doe</seg>
      </tuv>
      <tuv xml:lang="fr">
        <seg>Je m'appelle John Doe</seg>
      </tuv>
    </tu>
  </body>
</tmx>

Вспомогательные классы Zend I18n

Если вы читаете наш блог, то уже знаете, что есть понятия, для которых очень важна локальность — некоторые вещи просто не могут существовать без локальности. Мы можем привести валюту, формат данных, числовые форматы в качестве примеров. Если вы собираетесь обеспечить поддержку интернационализации для своего приложения, важно, чтобы вы соответствующим образом переводили эти объекты. Вспомогательные классы Zend упрощают процесс, вводя соответствующие классы с поддержкой локальности:

  • Помощник NumberFormat
  • Помощник CurrencyFormat
  • Помощник DateFormt

Давайте познакомимся с ними поближе, взглянув на несколько примеров.

Помощник NumberFormat

Одним из наиболее очевидных различий в разных локалях является то, как форматируются числа. Класс NumberFormat обрабатывает эти отклонения за вас. Посмотрите на следующие два примера:

echo $this->numberFormat(
    2453678.26,
    NumberFormatter::DECIMAL,
    NumberFormatter::TYPE_DEFAULT,
    "de_DE"
);

Он отобразит 2.453.678,26.

echo $this->numberFormat(
    2453678.26,
    NumberFormatter::DECIMAL,
    NumberFormatter::TYPE_DEFAULT,
    "en_US"
);

Отображается 2 453 678,26.

Помощник CurrencyFormat

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

echo $this->currencyFormat(2543.91, "USD", "en_US");

То, что он покажет, составляет 2543,91 доллара США.

echo $this->currencyFormat(2543.91, "EUR", "de_DE");

Он будет отображать 2.543,91 €.

При использовании en_US местной валютой является доллар США. Когда вы используете de_DE, что означает Германия, валюта превращается в евро. Однако это не так. Если вы присмотритесь, формат числа цены также изменился. Причина этого в том, что метод CurrencyFormat является оболочкой для класса NumberFormat.

Помощник формата даты

Последний важный вспомогательный класс, который мы рассмотрим, — это вспомогательный класс DateFormat. В разных странах используются разные форматы даты. В следующем примере сравниваются форматы даты, созданные theen_US и de_DElocales.

echo $this->dateFormat(
    new DateTime(),
    IntlDateFormatter::MEDIUM, // date
    "en_US"
);

1 мая 2019 года отображается результат.

echo $this->dateFormat(
    new DateTime(),
    IntlDateFormatter::MEDIUM, // date
    "de_DE"
);

В результате отображается 01.05.2019.

Zend Framework предоставляет намного больше вспомогательных классов. Полный список вы можете найти в их документации.

Подведение итогов

В этом учебном пособии вы познакомились с PHP i18n с Zend Framework. Я искренне надеюсь, что вам понравилось и вы многому научились на этом пути. Если вы все еще пытаетесь найти надежную службу локализации, попробуйте Phrase. Phrase — широко известная универсальная платформа для локализации проектов. Помимо PHP, Phrase также поддерживает другие языки программирования, включая Java, Python, Ruby или JavaScript. Подпишитесь на бесплатную 14-дневную пробную версию и попробуйте сами.

Первоначально опубликовано в The Phrase Blog.