Zend Framework і переклад

Мітки: zend framework, zend_translate, zend_locale, мультимовність

Zend Framework и перевод Zend Framework и перевод
Zend Framework and Translation Zend Framework and Translation

Мультимовні сайти стають все більш популярні, я хотів би показати два можливі способи перекладу блоків статичного тексту у вашому додатку на Zend Framework. Zend Framework вже надає нам декілька пакетів для полегшення життя, наприклад Zend_Locale і Zend_Translate, але як звести ці компоненти разом?

Створення додатку Zend Framework
Насамперед нам потрібний додаток Zend Framework. Ви також можете використовувати свій існуючий додаток, або створити новий прямо в Zend Studio for Eclipse:
Zend Framework і переклад
Якщо ви не знаєте як встановити Zend Framework, зверніться до відповідного керівництва Zend Framework Quickstart.

Zend_Locale і Zend_Translate
Як говорилося раніше, нам потрібна сутність Zend_Locale і Zend_Translate. Я ініціалізував обидва об'єкти в класі Initializer який був створений майстром проекту Zend Framework:
Прибрати підсвітку коду
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
/**
 * Ініціалізація Locale і Translation
 * 
 * @return void
 */
public function initLocale() {
    $localeValue = 'en';
 
    $locale = new Zend_Locale($localeValue);
    Zend_Registry::set('Zend_Locale', $locale);
 
    $translationFile = $this->_root . DIRECTORY_SEPARATOR .
 'lang' . DIRECTORY_SEPARATOR . $localeValue . '.inc.php';
 
    $translate = new Zend_Translate('array', $translationFile, $localeValue);
    Zend_Registry::set('Zend_Translate', $translate);
}

Метод Initializer::initLocale() викликається методом Initializer::_routeStartup(). Звичайно, ви можете реалізувати цю функціональність у файлі bootstrap. Я вибрав дуже простий спосіб ініціалізації об'єкту Zend_Locale для цього прикладу: я встановлюю змінну $localeValue безпосередньо в методі. Звичайно ж це не рекомендується робити! Можливо ви можете узяти розміщення користувача з сесії або Zend_Locale, на ваш розсуд. Для цього ми скористаємося Zend_Translate і Array Adapter. Це означає, що нам слід створити php-файл, який повертатиме масив перекладу.
Якщо у вас є об'єкт Zend_Locale, ви можете завершити додаток додаванням ключа "Zend_Locale" у Zend_Registry. Можливо, там вже будуть деякі компоненти ZF. На наступному кроці, ми створимо необхідні компоненти для перекладу.
Zend Framework і переклад
Це найбільш швидкий спосіб створення механізму перекладу.
Файл en.inc.php виглядає таким чином:
Прибрати підсвітку коду
1
2
3
4
5
6
7
8
9
10
<?php
return array(
        'Berlin' => 'Berlin',
        'Hamburg' => 'Hamburg',
        'München' => 'Munich',
        'Köln' => 'Cologne',
        'Stuttgart' => 'Stuttgart',
        'Hauptstadt' => 'capital',
        'Hafen' => 'harbor',
// ...

Ключ масиву є ключем перекладу, який використовується в нашому шаблоні Виду, а його значення є самим перекладом.
Повернемося до нашого методу Initializer::initLocale(), ми створили об'єкт Zend_Translate з ім'ям адаптера, файлу перекладів і розміщенням, поклавши даний аналог об'єкту в об'єкт Zend_Locale, який знаходиться в Zend_Registry. Наприклад, Zend_Form тепер використовуватиме інформацію для перекладу міток Zend_Form_Element автоматично.
Це майже все, що ми повинні підготувати перш ніж ми зможемо використовувати переклади в нашому view script.

Zend_View_Helper_Translate
Так як ми поклали об'єкт Zend_Translate в registry, тепер можна використовувати view script в Translate View Helper. Давайте подивимось на приклад phtml:
Прибрати підсвітку коду
1
2
3
4
5
6
7
8
9
10
11
12
<?php
$this->headTitle('Translate with Filter');
$this->placeholder('title')->set($this->translate('Das höchste Gut und Uebel'));
?>
<h2>Cicero</h2>
<small><?= $this->translate('Absatz') ?> 1.10.32 - 1.10.33</small>
<p>
<?= $this->translate('Damit Ihr indess erkennt, woher dieser ganze Irrthum
 gekommen ist, und weshalb man die Lust anklagt und den Schmerz lobet,
 so will ich Euch Alles eröffnen und auseinander setzen, was jener Begründer
 der Wahrheit und gleichsam Baumeister des glücklichen Lebens selbst darüber gesagt hat.') ?> 
<!-- … -->

Як бачите, кожен блок статичного тексту використовується як параметр методу translate, об'єкту Zend_View. Суть Zend_View_Helper_Translate буде створена об'єктом view автоматично. Вона використовуватиме об'єкт Zend_Translate з registry, перекладати параметр рядок (рядок має бути ключем масиву перекладу) і повертати переведений рядок, який буде виведений у view script. Дуже просто, чи не так?

Спосіб <i18n>
Можливо, вам це знайомо: "Занадто багато php-коду в шаблоні виду". Тоді у нас буде така ж думка. Що ви думаєте про цей шаблон:
Прибрати підсвітку коду
1
2
3
4
5
6
7
8
9
10
11
<?php
$this->headTitle('Translate with Filter');
$this->placeholder('title')->set('<i18n>Das höchste Gut und Uebel</i18n>');
?>
<h2>Cicero</h2>
<small><i18n>Absatz</i18n> 1.10.32 - 1.10.33</small>
<p>
<i18n>Damit Ihr indess erkennt, woher dieser ganze Irrthum gekommen ist,
 und weshalb man die Lust anklagt und den Schmerz lobet, so will ich Euch Alles
 eröffnen und auseinander setzen, was jener Begründer der Wahrheit und gleichsam
 Baumeister des glücklichen Lebens selbst darüber gesagt hat.</i18n>

Я включив кожен блок статичного тексту в тег <i18n>. На мій погляд, це легко читати і в цього способу є більше спільного з тегамі HTML (можливо хтось підкаже мені як додавати власні html-теги у валідатор HTML в Eclipse — без реалізації нового валідатора, тоді ми зможемо позбавиться від повідомлень про некорректні теги). Я знаю, що дані теги не валідні, але ми відфільтруємо їх пізніше — за допомогою об'єкту Zend_Filter. В об'єкті Zend_View є можливість установки Zend_Filter для фільтрації контенту, що виводиться. Значить, нам варто реалізувати новий клас, наприклад Zx_View_Filter_Translate. З урахуванням угоди Zend Framework про іменування, ви повинні створити файл library/Zx/View/Filter/Translate.php і реалізувати там клас (Zx це мій особистий ZendFramework-Demo-Extension-Prefix, виберіть собі будь-який).
Давайте поглянемо на код (якщо ви також використовуєте Zend_Loader необов'язково включати які-небудь файли):
Прибрати підсвітку коду
1
2
3
4
5
6
7
8
9
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
<?php
class Zx_View_Filter_Translate implements Zend_Filter_Interface
{
    /**
     * Початковий розділитель блоку перекладу в Виді
     *
     */
    const I18N_DELIMITER_START = '<i18n>';
 
    /**
     *  Кінцевий розділитель блоку в Виді
     *
     */
    const I18N_DELIMITER_END = '</i18n>';
 
    /**
     * Фільтрація значення для тексту всередині тегів i18n і переклад
     * 
     * @param string $value
     * @return string
     */
    public function filter($value) 
    {
        $startDelimiterLength = strlen(self::I18N_DELIMITER_START);
        $endDelimiterLength = strlen(self::I18N_DELIMITER_END);
 
        $translator = Zend_Registry::get('Zend_Translate');
 
        $offset = 0;
        while (($posStart = strpos($value, self::I18N_DELIMITER_START, $offset)) !== false) {
            $offset = $posStart + $startDelimiterLength;
            if (($posEnd = strpos($value, self::I18N_DELIMITER_END, $offset)) === false) {
                throw new Zx_Exception("No ending tag after position [$offset] found!");        
            }
            $translate = substr($value, $offset, $posEnd - $offset);
 
            $translate = $translator->_($translate);
 
            $offset = $posEnd + $endDelimiterLength;
            $value = substr_replace($value, $translate, $posStart, $offset - $posStart);
            $offset = $offset - $startDelimiterLength - $endDelimiterLength;
        }
 
        return $value;    
    }
}

Ми повинні реалізувати лише один метод: Zx_View_Filter_Translate::filter(). Параметр $value містить повний текст з Виду. У циклі ми перевіряємо кожне розташування тексту, що починається з <i18n> і закінчується на </i18n>. Потім ми витягуємо текст між цими тегамі і перекладаємо його за допомогою об'єкту Zend_Translate (взятого з registry). В кінці ми записуємо перекладений рядок в те саме місце, але вже без тегів <i18n>.
По причинам швидкодії, я не почав використовувати регулярні вирази. Я згоден, що з регулярними виразами код був би чистіший, але строкові операції виконуються швидше. Це моє перше рішення задачі, приймаються поради з його поліпшення.
Тепер у нас є фільтр, який не використовується. Значить, нам потрібно сказати Виду де його шукати. Мій варіант: використовувати метод Initializer::initView(). Щоб отримати Вид в цьому місці додатку, потрібний невеликий прийом:
Прибрати підсвітку коду
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
/**
 * Ініціалізація view 
 * 
 * @return void
 */
public function initView()
{
    //
 
    $view = new Zend_View();
    $view->addFilterPath('Zx/View/Filter', 'Zx_View_Filter');
    $view->setFilter('Translate');
 
    $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('ViewRenderer');
 
    $viewRenderer->setView($view);
}

Створюємо новий об'єкт Zend_View, додаємо новий шлях до фільтру (шлях, який ми використовували до нашого фільтру Translate) і новий префікс фільтру ("Zx_View_Filter", у іншому випадку, по замовчуванню "Zend_View_Filter", який використовувався для створення імені класу). Додатково ми повинні встановити ім'я фільтру, який ми хотіли б використовувати. В кінці ми повинні встановити об'єкт Виду в статичному action helper "ViewRenderer". Тепер настав час перевірити як це працює.

Швидкодія
Оскільки я встановив цей демо-додаток на Zend Server 4.0 beta (VMware, Ubuntu JeOS 8.04), мені було не важко провести тест на продуктивність з Zend Studio for Eclipse, а також інтегрованим Profiler.
Декілька слів про моє "середовище тестування": я реалізував клас Translatecontroller з двома діями helperAction і filterAction. Обидві дії не роблять нічого, окрім рендера Виду з ViewHelper або Фільтром. Я також реалізував метод postDispatch(), який повторюється 150 разів і рендерить скрипт дії, що викликаються. Я думаю, що буде простіше зрозуміти, якщо ви поглянете на код:
Прибрати підсвітку коду
1
2
3
4
5
6
7
8
9
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
<?php
/**
 * TranslateController
 * 
 * @author  $LastChangedBy: $
 * @version $Id: TranslateController.php 148 2009-03-27 09:48:37Z  $
 */
class TranslateController extends Zend_Controller_Action {
 
    const ITERATIONS = 150;
    private static $cnt = 0;
    private $_script;
 
    private $_currentActionName;
 
    /**
     * побудова імені view script, яке рендерить postDispatch()
     *
     */
    public function init() {
        $this->_currentActionName = $this->getRequest()
                                             ->getActionName();
 
        $this->_script = $this->getRequest()->getControllerName()
                      . DIRECTORY_SEPARATOR 
                      . $this->_currentActionName
                      . '.phtml';
    }
 
    /**
     * Побудова ланцюгу дій з повторенням self::ITERATIONS.
     * В кожній ітерації буде рендеритись відповідний скрипт
     *
     */
    public function postDispatch() {
        if (++self::$cnt >= self::ITERATIONS) return;
 
        $this->view->render($this->_script);
        $this->_forward($this->_currentActionName);     
    }
 
    /**
     * використання Translate ViewHelper Видом, для перекладу
     *
     */
    public function helperAction() {
    }
 
    /**
     * використання фільтру Видом, для перекладу
     *
     */
    public function filterAction() {
    }
}

Виклик обох запитів (/translate/helper і /translate/filter) в Profiler дає нам наступні результати:
Zend Framework і переклад
/translate/helper

Zend Framework і переклад
/translate/filter

Як бачите, метод postDispatch() вимагає майже на 20% менше спільного часу для виклику фільтру, ніж виклик хелперу. Я пробував кілька разів і отримував один і той самий результат. Звичайно, це не наукові виміри, але я думаю, що це гідно уваги.

Оригінал: Zend Framework and Translation

Рейтинг: 12345   << Ви можете поставити оцінку цій статті


Подібні статті:
   Інтеграція FCKeditor в Zend_Form
   Автоматизоване тестування з використанням Zend Framework
   Паттерн кешування для моделей
   Два (або більше) проекти Zend Framework на спільному хостингу
   Memcached в PHP - просто з Zend Framework


 
 

Залишити коментар:

Ім'я


E-mail


Повідомлення