1. ZFConf 2010
Zend Framework и мультиязычность
Степан Танасийчук
ceo@stfalcon.com
2. Чем я занимаюсь?
Web разработкой занялся в
2003 году
С Zend Framework начал
работать в 2008 году
Руковожу собственной веб-
студией с 2009 года
Активный участник сообщества
zendframework.ru
Люблю прикольные смайлы :]
3. Содержание доклада
Простейшее подключение Zend_Translate
Работа с view хелпером translate
Plural forms или формы множественного числа
Почему я отдаю предпочтение gettext?
Работаем с poedit
Хаки для работы с gettext
Перевод сообщений валидаторов
Сравнение различных схем передачи языка в URL
Zend_Translate и кеширование
4. Самый простой вариант подключения
Zend_Translate
Добавляем в application.ini следующие настройки:
resources.translate.data = APPLICATION_PATH "/languages"
resources.translate.adapter = "array"
resources.translate.locale = "auto"
resources.translate.options.scan = "directory"
resources.translate.options.disableNotices = true
Редактриуем IndexAction() в дефолтном контроллере:
./application/controllers/IndexController.php
public function indexAction()
{
echo $this->view->translate('Hello');
}
5. Создаем файл переводов для русского
языка
Структура каталога languages:
./application/languages/
`-- ru
`-- application.php
Файл переводов:
./application/languages/ru/application.php
<?php
return array(
'Hello' => 'Привет',
);
Результат:
Привет
6. Почему ”Привет”, а не ”Hello”?
Потому что в моих настройках браузера русский язык по
приоритету выше английского:
7. Отдельные можно выводить
сообщения на указанном языке
Для этого нужно указать язык или локаль в последнем
аргументе vie хелпера translate():
./application/controllers/IndexController.php
public function indexAction() {
echo $this->view->translate('Hello', 'en_GB');
// или
echo $this->view->translate('Hello', 'en');
}
Результат:
Hello
Эту возможность удобно использовать при
формировании текстов для многоязычной рассылки.
8. Plural forms или формы
множественного числа
Поддержка plural forms есть в адаптерах:
Array
Csv
Gettext
Разберем на примере:
./application/controllers/IndexController.php
public function indexAction()
{
echo $this->view->translate('Hello') . '! ';
$count = 5;
echo sprintf($this->view->translate(array('%s day', '%s days',
$count)), $count) . ' ' . $this->view->translate('left before the
conference');
}
9. Plural Forms (продолжение)
Обновим файл переводов для русского языка:
./application/languages/ru/application.php
<?php
return array(
'Hello' => 'Привет',
'left before the conference' => 'осталось до начала конференции',
'%s day' => array(
'%s день',
'%s дня',
'%s дней'
)
);
Результат:
Привет! 5 дней осталось до начала конференции
10. Почему я отдаю предпочтение gettext?
В коде отображаются оригиналы сообщений. Пример
сообщения в view шаблоне:
<h2><?php echo $this->translate('Create new brand'); ?>:</h2>
Не нужно искать где и какие строки были добавлены или
удалены в исходном коде — gettext сам найдет все
изменения.
Есть готовые программы для работы с файлами переводов
(особенно актуально для НЕпрограммистов). Достаточно
предоставить заказчику или перводчику .po файл и
программу для его редактирования (например
кроссплатформенная Poedit).
11. Работаем с poedit. Настройки каталога
Создаем новый каталог Файл→Создать каталог.
Указываем необходимые настройки:
12. Работаем с poedit. Настройка каталога
(продолжение)
Обязательно указывайте формы множественного числа для
каждого перевода!
Полный список форм можно найти на странице
http://translate.sourceforge.net/wiki/l10n/pluralforms
Например
для русского:
nplurals=3; plural=(n%10==1 && n%100!=11 ? 0
: n%10>=2 && n%10<=4 && (n%100<10 or n
%100>=20) ? 1 : 2)
для немецкого:
nplurals=2; plural=(n != 1)
13. Работаем с poedit. Пути
На вкладке «Пути» указываем путь к каталогу проекта или
пути к каталогам которые нужно сканировать:
14. Работаем poedit. Ключевые слова
Также добавляем названия функций, строковые аргументы
которых должны добавлятся в языковый файл (вкладка
«Ключевые слова»):
15. Работаем с poedit. Сканировать *.phtml
Чтобы xgettext парсил *.phtml шаблоны нужно добавить это
расширение в настройки анализатора PHP в
Правка→Параметры→Анализаторы
*.php;*.phtml
16. Для чего я добавил plural:1,2?
Я не успел разобраться как заставить парсер xgettext
доставать строки из такой конструкции:
$this->view->translate(array('%s day', '%s days', $count)
Но он успешно достает их из конструкции вида:
$this->view->translate()->getTranslator()->plural('%s day', '%s days',
$count)
17. Работаем с Poedit. Обновляем каталог
из исходных файлов
Сохраняем каталог ./application/languages/ru/application.po
И обовляем его Каталог→Обновить из исходного кода
Получаем следующую картину:
18. Работаем с Poedit. Добавляем
переводы и сохраняем каталог
Сохраняем результат. В
параметрах должна быть
отмечена опция ”При
сохранении автоматически
компилировать файл .mo”.
В application.ini меняем
адаптер на:
resources.translate.adapter =
"gettext"
Запускаем:
Привет! 5 дней
осталось до начала
конференции
19. Ньюансы и хаки для работы с gettext
Лейблы формы нужно оборачивать в _(). Пример:
$username = $form->createElement('text', 'username');
$username->setLabel(_('Имя пользователя'));
Только что подумал о том, что setLabel тоже можно
добавить в ключевые слова :).
Та же ситуация с названиями пунктов меню для
Zend_Navigation:
array(
'controller' => 'users',
'action' => 'list',
'resource' => 'mvc:users',
'privilege' => 'list',
'label' => _('Users'),
'route' => 'default',
)
20. Перевод сообщений валидаторов для
адаптеров != array
Раньше мы делали отдельный файлик, который содержал
все сообщения обернутые в _():
_("A record matching "%value%" was found");
_("Password and confirmed password do not match.");
xgettext этот файл парсил, а мы переводили и
компилировали .mo файл, который уже подключали ко всем
проектам.
Теперь в ZF появилась папочка resources, в которой лежат
переводы сообщений валидаторов. Но как ихподключить
если для основного сайта используется адаптер отличный
от array я пока не успел разобраться. Думаю, что вскоре
эта проблема будет решена и решение будет опубликовано
на сайте или форуме http://zendframework.ru
21. Перевод сообщений валидаторов для
адаптера array
Для array все намного проще. Добавляем в Bootstrap.php
такой метод:
/**
* Init translator to Zend_Validate
* @return Zend_Translate
*/
public function _initZendValidateTranslator()
{
$this->bootstrap('translate');
$translate = $this->getResource('translate');
$translate->addTranslation(APPLICATION_PATH .
'/../resources/languages');
return $translate;
}
22. Варианты передачи языка в URL
Язык на поддомене:
en.wikipedia.org
ru.wikipedia.org
Язык в поддиректории:
1й вариант:
mota.ru – русский
mota.ru/en/ – английский
2й вариант:
preorder.it – ”auto” или по базе GeoIP
preorder.it/ru/ – русский
preorder.it/en/ – английский
23. Язык на поддомене
+
Ускоренная индексация.
Для доменов первого уровня, которые не имеют
георгафической привязки можно настроить разные
географические цели для каждой языковой версии сайта
(в Google Webmaster Tools).
Вес с основного домена передается на поддомены.
Сайты можно разнести на разные сервера и делать
независимые изменения в коде.
-
Бюджет на продвижение и эффект от него будет
дробиться на все сайты.
24. Язык в поддиректории
preorder.it/ru/ и preorder.it/en/
+
Достаточно просто реализовать в ZF.
Хорошо подходит для сайтов-услуг.
Идет продвижения одного домена, т.е. тот же бюджет
что и в первом варианте даст больший эффект.
-
Более медленная индексация.
Нельзя сделать разные георгафические цели для
разных языковых версий сайта.
С технической стороны будет проблематично разнести
разные языковые версии сайта на разные сервера.
25. Несколько строчек кода, которые
делают приложение быстрее :)
Если есть возможность что-то закешировать, значит нужно
её использовать:
$cache = Zend_Cache::factory(
'Core',
'File',
array(
'caching' => true,
'automatic_serialization' => true,
'lifetime' => 3600,
),
array(
'cache_dir' => realpath(APPLICATION_PATH . '/../tmp'),
)
);
Zend_Translate::setCache($cache);