04. Мультиязычность с Zend_Translate и создание помощников действий.
Теперь решил, что не плохо было бы рассмотреть поддержку мультиязычности с использованием Zend Framework, т.к. эта часть мануала не переведена на русский язык, покрайней мере пока (версия 1.7.4 - 2009-01-31). Если задача не имеет особых требований, то осмелюсь заявить, что нижеописанный способ поможет понять основные моменты работы с Zent_Translate.
Сразу хочу сказать, что если не хватит сил на эту тему или мультиязычность не нужна, можно пропустить все, что тут написано, просто скачать исходник без поддержки модуля locale и перейти к следующей статье. Но все же про создание помощников действий почитать рекомендую, еще пригодится.
Опять же придеться освещать сразу 2 темы в рамках одной статьи, но так вышло, что в нашем случае они связаны. Конечно же можно добиться того же результата и без использования некоторых компонентов - это же в конце концов фрэймворк, но здесь речь о ZF так что идем дальше.
Локализация приложения это не только перевод текста на различные языки, но и конвертация единиц измерений, разные форматы дат, валюта. Сейчас займемся только переводом текста, но не будем забывать о масштабируемости и по-этому нужно создать нечто подобное отдельному модулю, который можно будет включить/отключить, без труда добавить функционал. Неплохим решением стало - создание помощника.
По пунктам:
1. Создать класс-наследник от Zend_Controller_Action_Helper_Abstract;
2. Так же, как мы поступали с помощниками видов, дать имя классу и сохранить файл, следуя соглашениям по именованию ZF.
lib/Divo/Helper/Action/Locale.php
При вызове хелпера из контроллера, по умолчанию выполняется метод direct(). __construct выполняется только при первом вызове помощника.
4. Указать приложению, где лежат помощники. На всякий случай сделаем это в загрузочном файле:
www/index.php
Первый параметр - путь, второй - префикс классов.
Все теперь помощник доступен во всех контроллерах, обратиться можно следующим образом:
Нам нужно что бы страницы на разных языках имели уникальный URI. Если страница сайта на русском и английском языках будет доступна по одному и тому же адресу, то поисковые системы смогут проиндексировать только одну версию. Выбираем реализацию поддоменами, по-этому нам понадобится поддомен с кодом языка (например en.zf-demo.2developers.net для английского), а русский будет по умолчанию на основном домене.
Изменим файл конфигурации, добавив в него настройки модуля locale:
app/config.ini
Теперь изменим класс нашего помощника:
lib/Divo/Helper/Action/Locale.php
В конструкторе загружаются настройки из файла конфигурации, мы создавали метод Divo_Controller::divoLoadConfig() в прошлой статье.
Все. Остается получить объект с помощью хелпера, в базовом контроллере, что бы его можно было использовать везде:
lib/Divo/Controller.php
А сейчас самое интересное, не знаю с чего начать :)
Нужно утилитой gettext собрать все строковые выражения в файлах проекта т.к. это отдельная тема, я не буду описывать как это сделать, уже и так много написано об инструментах для перевода, но файл перевода в итоге должен получится примерно такой:
app/includes/locale/all.po
Следующим шагом будет скомпилировать файл перевода. В линуксе это делается командой: msgfmt ПУТЬ/all.po -o ПУТЬ/ru.mo
Если у вас нет возможности все это сделать, скачайте архив приложенный к этому посту, получите скомпилированные файлы, положите их в app/includes/locale/ и можно пропустить следующий пункт.
Теперь добавим в файл all.po строки с переводом на английский (можно сделать это с помощью какой нибудь утилиты в KDE например KBabel):
И компилируем в app/includes/locale/en.mo
С этого момента все строки в контроллерах, необходимо выводить через объект $this->translate. Например взять строку "Текст главной страницы" в app/controllers/IndexController.php:12
нужно заменить на:
Теперь вы можете увидеть эту строку на русском по адресу zf-demo.2developers.net и на английском по адресу en.zf-demo.2developers.net
Но самое главное, что большинство строк приходится на скрипты видов, благо в ZF уже есть ViewHelper - translate, пользуемся следующим образом:
например app/views/layout.phtml строка 8
изменяем на:
Это все, что я хотел рассказать о Zend_Translate. Советую вам ознакомиться с другими возможностями этого компонента, т.к. существуют другие форматы файлов перевода и большинство из них поддерживаются фреймворком.
Сразу хочу сказать, что если не хватит сил на эту тему или мультиязычность не нужна, можно пропустить все, что тут написано, просто скачать исходник без поддержки модуля locale и перейти к следующей статье. Но все же про создание помощников действий почитать рекомендую, еще пригодится.
Опять же придеться освещать сразу 2 темы в рамках одной статьи, но так вышло, что в нашем случае они связаны. Конечно же можно добиться того же результата и без использования некоторых компонентов - это же в конце концов фрэймворк, но здесь речь о ZF так что идем дальше.
Локализация приложения это не только перевод текста на различные языки, но и конвертация единиц измерений, разные форматы дат, валюта. Сейчас займемся только переводом текста, но не будем забывать о масштабируемости и по-этому нужно создать нечто подобное отдельному модулю, который можно будет включить/отключить, без труда добавить функционал. Неплохим решением стало - создание помощника.
Создание помощника действий (Action Helper).
По пунктам:
1. Создать класс-наследник от Zend_Controller_Action_Helper_Abstract;
2. Так же, как мы поступали с помощниками видов, дать имя классу и сохранить файл, следуя соглашениям по именованию ZF.
lib/Divo/Helper/Action/Locale.php
require_once 'Zend/Controller/Action/Helper/Abstract.php';
require_once('Zend/Translate.php');
class Divo_Helper_Action_Locale extends Zend_Controller_Action_Helper_Abstract
{
public function __construct()
{ }
public function direct()
{
}
}
При вызове хелпера из контроллера, по умолчанию выполняется метод direct(). __construct выполняется только при первом вызове помощника.
4. Указать приложению, где лежат помощники. На всякий случай сделаем это в загрузочном файле:
www/index.php
Zend_Controller_Action_HelperBroker::addPath(LIBPATH.'/Divo/Helper/Action', 'Divo_Helper_Action');
Первый параметр - путь, второй - префикс классов.
Все теперь помощник доступен во всех контроллерах, обратиться можно следующим образом:
$this->_helper->locale(); // Выполняется метод Divo_Helper_Action_Locale::direct()
Вернемся к Zend_Translate.
Нам нужно что бы страницы на разных языках имели уникальный URI. Если страница сайта на русском и английском языках будет доступна по одному и тому же адресу, то поисковые системы смогут проиндексировать только одну версию. Выбираем реализацию поддоменами, по-этому нам понадобится поддомен с кодом языка (например en.zf-demo.2developers.net для английского), а русский будет по умолчанию на основном домене.
Изменим файл конфигурации, добавив в него настройки модуля locale:
app/config.ini
[locale]
; вкл/выкл
enabled = true
; по умолчанию тот который без поддомена
default = ru
[languages] ; поддерживаемые языки
ru = Русский
en = English
Теперь изменим класс нашего помощника:
lib/Divo/Helper/Action/Locale.php
require_once 'Zend/Controller/Action/Helper/Abstract.php';
require_once('Zend/Translate.php');
class Divo_Helper_Action_Locale extends Zend_Controller_Action_Helper_Abstract
{
public $config;
public function __construct() {
$this->config = Divo_Controller::divoLoadConfig('config.ini', 'locale');
}
public function direct()
{
$path = APPPATH.'/includes/locale/';
/* Если в URI есть поддомен из 2 букв
Внимание! регулярное выражение составлено с учетом того что приложение на домене, 3-го уровня
если же основной домен, второго уровня например 2developers.net -
нужно использовать такое выражение "/([a-z]{2})\..*\..{2,5}/" */
if (preg_match("/([a-z]{2})\..*\..{2,5}/", HOST, $sub)) {
$lang = $sub[1];
} else {
$lang = $this->config->default;
}
$obj = new Zend_Translate('gettext', $path.$lang.'.mo', $lang);
return $obj;
}
}
В конструкторе загружаются настройки из файла конфигурации, мы создавали метод Divo_Controller::divoLoadConfig() в прошлой статье.
Все. Остается получить объект с помощью хелпера, в базовом контроллере, что бы его можно было использовать везде:
lib/Divo/Controller.php
добавить аттрибут класса для хранения объекта
/** @var object - Zend_Translate Модуль Locale */
protected $translate;
и изменить метод init(), что бы он выглядел следующим образом:
public function init()
{
// первое, что сделаем - загрузим настройки
$this->config = $this->divoLoadConfig();
require_once 'Zend/Registry.php';
Zend_Registry::set('Zend_Config', $this->config);
// Если каталог www расположен не в корне сервера (см. 11 строка www/index.php)
if (BASEURL !== "") {
$this->getRequest()->setBaseUrl(BASEURL.'/');
}
/** Настройки вида */
// Указываем где искать скрипты вида
$this->view->setScriptPath(APPPATH.'/views/');
// Путь к помощникам видов
$this->view->setHelperPath(LIBPATH.'/Divo/Helper/View', 'Divo_Helper_View');
// Берем из настроек разделитель для составных заголовков
$this->view->headTitle()->setSeparator($this->config->view->title_separator);
// Запустить layout
$this->_divoStartLayout();
// Включить модуль locale, если необходимо
if ($this->config->locale->enabled == true) {
/** Модуль Locale Execute time: 0.022301 sec. */
$this->translate = $this->_helper->locale();
Zend_Registry::set('Zend_Translate', $this->translate);
//на заметку: время выполнения этого кода 0.012301 сек.
}
$this->divoInit();
}
А сейчас самое интересное, не знаю с чего начать :)
Gettext и файлы перевода
Нужно утилитой gettext собрать все строковые выражения в файлах проекта т.к. это отдельная тема, я не буду описывать как это сделать, уже и так много написано об инструментах для перевода, но файл перевода в итоге должен получится примерно такой:
app/includes/locale/all.po
# zf-demo.2Developers.net
# Copyright (C) 2009 2Developers.net
# This file is distributed under the same license as the zf-demo.2Developers.net package.
# Vladimir, 2009.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-02-09 01:50+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME\n"
"Language-Team: RU\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: /var/www/htdocs/~app/controllers/IndexController.php:12
msgid "Текст главной страницы"
msgstr "Текст главной страницы"
#: /var/www/htdocs/~app/views/layout.phtml:8
#: /var/www/htdocs/~app/views/layout.phtml:18
msgid "Демо-сайт"
msgstr ""
#: /var/www/htdocs/~app/views/layout.phtml:19
msgid "Только для примера"
msgstr ""
#: /var/www/htdocs/~app/views/layout.phtml:22
msgid "Главная"
msgstr ""
#: /var/www/htdocs/~app/views/layout.phtml:23
msgid "О сайте"
msgstr ""
#: /var/www/htdocs/~app/views/index/about.phtml:1
msgid "Страница с текстом о сайте и может быть контактной формой."
msgstr ""
#: /var/www/htdocs/~app/views/index/index.phtml:2
msgid "демо, тест, ZF, фрэймворк, PHP"
msgstr ""
#: /var/www/htdocs/~app/views/index/index.phtml:2
msgid "Демо-сайт. Разработано с использованием Zend Framework"
msgstr ""
#: /var/www/htdocs/~app/views/index/index.phtml:2
msgid "Главная страница"
msgstr ""
Следующим шагом будет скомпилировать файл перевода. В линуксе это делается командой: msgfmt ПУТЬ/all.po -o ПУТЬ/ru.mo
Если у вас нет возможности все это сделать, скачайте архив приложенный к этому посту, получите скомпилированные файлы, положите их в app/includes/locale/ и можно пропустить следующий пункт.
Теперь добавим в файл all.po строки с переводом на английский (можно сделать это с помощью какой нибудь утилиты в KDE например KBabel):
# zf-demo.2Developers.net
# Copyright (C) 2009 2Developers.net
# This file is distributed under the same license as the zf-demo.2Developers.net package.
# Vladimir, 2009.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-02-09 01:50+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME\n"
"Language-Team: RU\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: /var/www/htdocs/~app/controllers/IndexController.php:12
msgid "Текст главной страницы"
msgstr "Content of the main page"
#: /var/www/htdocs/~app/views/layout.phtml:8
#: /var/www/htdocs/~app/views/layout.phtml:18
msgid "Демо-сайт"
msgstr "Demo-site"
#: /var/www/htdocs/~app/views/layout.phtml:19
msgid "Только для примера"
msgstr "Only for example"
#: /var/www/htdocs/~app/views/layout.phtml:22
msgid "Главная"
msgstr "Home"
#: /var/www/htdocs/~app/views/layout.phtml:23
msgid "О сайте"
msgstr "About"
#: /var/www/htdocs/~app/views/index/about.phtml:1
msgid "Страница с текстом о сайте и может быть контактной формой."
msgstr "The page with description of the site and maybe with contact form"
#: /var/www/htdocs/~app/views/index/index.phtml:2
msgid "демо, тест, ZF, фрэймворк, PHP"
msgstr "demo, test, ZF, Framework, PHP"
#: /var/www/htdocs/~app/views/index/index.phtml:2
msgid "Демо-сайт. Разработано с использованием Zend Framework"
msgstr "Demo-site. Powered by Zend Framework"
#: /var/www/htdocs/~app/views/index/index.phtml:2
msgid "Главная страница"
msgstr "Main page"
И компилируем в app/includes/locale/en.mo
С этого момента все строки в контроллерах, необходимо выводить через объект $this->translate. Например взять строку "Текст главной страницы" в app/controllers/IndexController.php:12
$this->view->assign("message", "Текст главной страницы");
нужно заменить на:
$this->view->assign("message", $this->translate->_("Текст главной страницы"));Теперь вы можете увидеть эту строку на русском по адресу zf-demo.2developers.net и на английском по адресу en.zf-demo.2developers.net
Но самое главное, что большинство строк приходится на скрипты видов, благо в ZF уже есть ViewHelper - translate, пользуемся следующим образом:
например app/views/layout.phtml строка 8
$this->headTitle('Демо-сайт');
изменяем на:
$this->headTitle($this->translate('Демо-сайт'));
Это все, что я хотел рассказать о Zend_Translate. Советую вам ознакомиться с другими возможностями этого компонента, т.к. существуют другие форматы файлов перевода и большинство из них поддерживаются фреймворком.
Прикрепленные файлы:
Демо-сайт без поддержки мультиязычности (Загрузок: 65)Демо-сайт с поддержкой мультиязычности (Загрузок: 117)
Файлы перевода (Загрузок: 66)
Комментарии
Все время ошибку выдает /var/www/htdocs/zf-demo/lib/Divo/Helper/Action/Locale.php on line 18
Да поправил, спасибо! Нужно создавать объект класса Zend_Translate и первым аргументом передавать адаптер и он сам все сделает, а не так как я, сразу объект адаптера создал:
$obj = new Zend_Translate_Gettext($path.$lang.'.mo');$obj = new Zend_Translate('gettext', $path.$lang.'.mo', $lang);
Ага, по мануалу сделал уже тоже