Побудова списку неперекладених фраз в Zend_Translate

Пн, 9 Лют 2009

Zend_Translate чудова компонента Zend Framework для створення інтерфейсу перекладеного багатьма мовами. Я спробував підняти багатомовний інтерфейс використовуючи цей інструмент, але зіткнувся з певними архітектурними проблемами для вирішення яких мені випало поспілкуватися на Issue Tracker з Томасом Вейднером (Thomas Weidner) лідером команди інтернаціоналізації Zend Framework I18N. Але про все по порядку.

На етапі розробки/тестування багатомовного інтерфейсу хотілося вивести список фраз що не мають перекладу для поточної локалі (Locale).

Переглянувши вихідний код Zend_Translate_Adapter я зрозумів що Zend Framework не надає ніяких засобів для збору неперекладених фраз окрім скупого методу isTranslated(). 

Хотілося б щоб адаптер перекладів сам автоматично фіксував неперекладені фрази і давав можливість отримати їх перелік

Дивувало те що такої важливої функції ніхто ніде не обговорював/не розглядав і оскільки мої пошуки готового рішення не увінчалися успіхом – вирішив описати цю проблему детально в темі “Allow to build the list of untranslated message IDs” на Zend Framework Issue Tracker.

Я використовую Action Helper щоб додати необхідний функціонал до мого Front Page Controller-а.

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
    /**
     * Initiate internationalization
     * 
     * @return Trc_Initializer
     */
    private function _initL10n()
    {
        $translate = new Zend_Translate('tmx', $this->_root . '/languages/general.tmx');
 
        // get locale
        $locale = Zend_Registry::get('Zend_Locale'); 
        $current_lang = $locale->getLanguage();
 
        if (!$translate->isAvailable($current_lang)) { 
            $locale->setLocale('en'); 
        }
 
        $translate->setLocale($locale);
 
        // save locale state into user session
        $user = Wise_User_Session::getInstance();
	$user->locale = $locale->getLanguage();
 
        Zend_Registry::set('Zend_Translate', $translate);
 
        return $this;
    }

Ініціалізований об’єкт Zend_Translate_Adapter_xxx зберігаю в реєстрі під зарезервованим за ним простором імен “Zend_Translate”.

Першим власним рішенням в мене було звертатися до Zend_Translate через написаний мною ViewHelper що має наступний вигляд:

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
/**
 * T (stands for Translation) Helper 
 * 
 * The main goal of this view helper is to collect the list 
 * of untranslated Message Ids and provide them as an array 
 */
class Zend_View_Helper_T
{
    /**
     * The list of messageIds without translation for current locale 
     * @var array
     */
    static public $missedTranslations = null;	 
 
    /**
     * Zend_View
     * 
     * @var Zend_View_Interface
     */
    protected $_view = null;
 
    /**
     * Set the view object 
     */
    public function setView(Zend_View_Interface $view)
    {
        $this->_view = $view;
    }
 
    /**
     * Translates provided message Id
     * 
     * You can give multiple params or an array of params.
     * If you want to output another locale just set it as last single parameter
     * Example 1: translate('%1\$s + %2\$s', $value1, $value2, $locale);
     * Example 2: translate('%1\$s + %2\$s', array($value1, $value2), $locale);
     *
     * @param  string $messageid Id of the message to be translated
     * @return string Translated message
     */
    public function T($messageid = null)
    {
    	/**
    	 * Process the arguments
    	 */
        $options = func_get_args();
        array_shift($options);
 
        $count  = count($options);
        $locale = null;
        if ($count > 0) {
            if (Zend_Locale::isLocale($options[($count - 1)], null, false) !== false) {
                $locale = array_pop($options);
            }
        }
 
        if ((count($options) === 1) and (is_array($options[0]) === true)) {
            $options = $options[0];
        }
 
	/**
	 * Get Zend_Translate_Adapter
	 */
        $translator = $this->_view->translate()  	  // get Zend_View_Helper_Translate
        				   ->getTranslator(); // Get Zend_Translate_Adapter
 
        /**
         * Collect the list of untranslated Message Ids into array 
         */
        if (false === $translator->isTranslated($messageid)) {
        	self::$missedTranslations[] = $messageid;
        }
 
        /**
         * Proxify the call to Zend_Translate_Adapter
         */
        $message = $translator->translate($messageid, $locale);
 
        /**
         * If no any options provided then just return message 
         */
        if ($count === 0) {
            return $message;
        }
 
        /**
         * Apply options in case we have them
         */
        return vsprintf($message, $options);
    }
}

Вище приведене рішення працює в мене і до тепер, однак недолік такого підходу в тому що при звертанні до Zend_Translate_Adapter безпосередньо (наприклад в Model або Controller Action, поза межами файлів представлення) – неперекладені фрагменти не потрапляють в наш чорний список :) .

Томас Вейднер спочатку відхилив мою пропозицію ZF-5547, мотивуючи відповідь тим що єдиним найпрактичнішим рішенням є використання методу isTranslated().

Однак через два тижні він все таки видав нову редакцію Zend_Translate_Adapter  що реалізовувала можливість використання Zend_Log для фіксування неперекладених фраз. Цей файл можна знайти в SVN репозиторії ось тут:  svn://framework.zend.com/svn/framework/standard/incubator/library/Zend/Translate/Adapter.php, адаптувавши мою початкову ідею у відповідності до ідеології Zend Framework.

Зараз тестую новий функціонал. Результати тестування можна відслідковувати на Issue Tracker.

Дописів 3 до “Побудова списку неперекладених фраз в Zend_Translate”

  1. Роман сказав:

    Почему не сделать просто статический метод, который будет реализововать приведенный вами пример?
    Объект вида всегда можно получить через ViewRender

  2. joss сказав:

    Не зовсім зрозумів, що саме пане Романе Ви маєте на увазі. Приведіть приклад.

    Варіант з View Helper як на мене є оптимальним в плані легкості застосування в шаблонах представлення, ось так:

    1
    
    echo $this->T('Hello, dear friend!');

    Оскільки була потреба лише в тимчасовому робочому рішенні – це перше і найпростіше що спало на думку та як на мене найкраще відповідає ідеології MVC.

    Зараз я відмовився від view helper на користь експериментальної версії Zend_Translate в SVN інкубаторі з підтримкою потібного функціоналу.

    До слова, вчора отримав повідомлення про те що запропонований функціонал одобрено dev-team але ще не одобрено з боку community. Є ймовірність що можливо нововведення може бути включено до дистрибутиву, якщо буде корисним спільноті zend framework. Тішуся дурницею як дитина, їй-богу :) .

  3. joss сказав:

    Побудова списку не перекладених фраз буде доступна в Zend Framework 1.8 ! :D

    Можна прочитати статтю Томаса на його персональному блозі

Дописати

для додавання коду використовуйте тег
<pre line="1" lang="php"> echo 'hello!'; </pre>

Безкоштовний хостинг TOPUA