Mirin webspace

Nejbohatší život má ten, kdo žije s minimem nároků

10. 6. 2008 - Komentáře (2) Zend Framework

Zend_Form III - překlady

Dnes naposled nějaké poznatky, co jsem získal na Zend Form. Bude se jednat o to, jak zajistit překlad labelů ve formuláři a také jak se postarat o překlad chybových hlášek, které vrací validátory. Chybové hlášky od validátorů upotřebíte i jinde než jen u formulářů, např. při validování vstupů od POST, GET apod.

Jak už jsem kdysi zmínil v článku o Zend_Translate, tak inicializaci Zend_Translate objektu a jeho adaptéru provedeme nejčastěji v bootstrapu. Já používám aplikační třídu, která mi zajišťuje většinu věcí z bootstrapu a objektově mě celou inicializaci aplikace obaluje (více v tomto článku). Tam to vypadá nějak takto

 $lang=self::$_instance->_locale->getLanguage();
 self::$_instance->_translator=new Zend_Translate('ExtGtTransAdp'
  ,self::$_instance->getTranslatedFile($lang)
  ,$lang
 );
 
 //save translation into registry for all translate aware components
 Zend_Registry::set('Zend_Translate', self::$_instance->_translator);

Jako adaptér používám svůj adaptér (ExtGtTransAdp) odvozený od gettext adaptéru ve frameworku. Udělal jsem to proto, že dříve jsem používal výstupní kódování iso-8859-2 a gettext adaptér ve frameworku podporuje jen utf-8, jako většina věcí ve frameworku. Další věc, kvůli které to používám, je překlad množných čísel, protože to gettext adaptér ve frameworku neumí (alespoň v 1.5.0 to tak bylo) tak jsem k tomu používal právě svůj subclassnutý adaptér. Nakonec jsem stejně celou aplikaci předělal na utf-8, takže první důvod pominul, ale už se mi do toho nechce hrabat.

Uložení objektu translátoru do registru je poměrně důležitá věc, je to jeden ze způsobů, jak globálně používat překlady. Všechny třídy frameworku, které nějakým způsobem mohou použít Zend_Translate na překlady, se do registru podívají po klíči Zend_Translate a pokusí se ho používat pro překlady. Jedná se zejména o třídy Zend_Form, Zend_Validate a Zend_View_Helper_Translate.

Labely u formulářů

Tím máme to nejdůležitější z krku a teď k těm textům, které se mají překládat. Předpokládám použití gettextu a na scanování shell script s xgettextem dle tohoto článku.

 $commentForm = new Zend_Form();
 $commentForm->setAction('/data/action')
             ->setMethod('post')
             ->setAttrib('id', 'comentForm');
 
 $username = $commentForm->createElement('text','username');
 $username->addFilter('StringTrim');
 $username->setLabel('Name (Required)');
 $username->setAttrib('size',30);
 $username->setRequired(true);

V tomto případě potřebujeme překládat řetězec Name (Required). Xgettext nám to nesežere, ten žere jen řetězce jako _(), trans(), translate() atd., které představují volání funkce translate objektu. V případě Zend_Form se objekt formuláře chová tak, že aplikuje volání překladu pomocí Zend_Translate objektu na

  • labely
  • legendy fieldsetů
  • popisy form elementů a popisy formulářů
  • multi-option hodnoty - to jsou elementy odvozené od Zend_Form_Element_Multi
  • labely tlačítek (submit a button elementy)

Kdesi na mailing listu ZF se objevilo vtipné řešení. Stačí si napsat nějaký soubor a do něj napsat seznam volání funkcí, které xgettext zbaští a jako parametry těchto funkcí uvést řetězce, o které nám jde. Takže v našem případě si vytvoříme soubor třeba app/languages/staticMessages.php, ať máme věci ohledně překladů hezky na jednom místě. Do něj pak napíšeme pro náš username label a hotovo.

<?php
 
_('Name (Required)');
 
?>

Xgettext nám oskenuje tento soubor, patřičně nám upraví .po soubory, z nich pak uděláme .mo a ty nám už načtě Zend_Translate.

Hlášky od validátorů

Validátory vyhazují chybová hlášení na základě identifikátorů. Právě tyto identifikátory používají třídy Zend_Validate a jejich potomci jako řetězce, které cpou jako překládaný řetězec do Zend_Translate objektů. Nezbývá nám tedy nic jiného, než tyto řetězce ve třídách validátorů nalézt a ty pak uvést do našeho souboru. Například hlášky NotEmpty validátoru vypadají takto

/**
 * @category   Zend
 * @package    Zend_Validate
 * @copyright  Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 */
class Zend_Validate_NotEmpty extends Zend_Validate_Abstract
{
 
    const IS_EMPTY = 'isEmpty';
 
    /**
     * @var array
     */
    protected $_messageTemplates = array(
        self::IS_EMPTY => "Value is empty, but a non-empty value is required"
    );
    ...
    ...
}

Do našeho souboru pro překlad napíšeme tedy řetězec isEmpty

<?php
/* validator messages ids*/
_('isEmpty');
 
/* form labels*/
_('Name (Required)');
?>

Pak proskenujeme xgettextem, doplníme překlady do .po souborů

$cat app/languages/messages.en.po
...
...
#: ../../app/languages/staticMessages.php:10
msgid "isEmpty"
msgstr "Value is empty, but a non-empty value is required"
...
...

$cat app/languages/messages.cs.po
...
...
#: ../../app/languages/staticMessages.php:10
msgid "isEmpty"
msgstr "Hodnota je prázdná, ale požadována je neprázdná hodnota"
...
...

Pak už vyrobíme binární .mo a hlášky se nám překládají.

Nevýhoda tedy je, že musíme hledat identifikátory ve validátorech, ale to se dá, dobré je, že funguje dál skenování přes xgettext jako dřív. Všechno, včetně bash skriptíku na skenování je v svn od minule tam byla drobná chybka.


Komentáře (2)

  1. Techi - 20. 6. 2008 11:30

    Problém ale nastane, když chci pokaždé jinou hlášku pro ten samý validátor. Většinou notEmpty. Když se po odeslání prázdného formuláře objeví 10x Value is empty, but a non-empty value is required tak je to trochu psycho

  2. koubel - 21. 6. 2008 16:37

    [1] - pravda, viz. ZF-2954 a ZF-3313, zatím si budeš muset vystačit s tím nadefinovat si pro každý message vlastní Id a to pak výše zmíněnou metodou překládat a stkat přes setMessage metodu do validátorů.

    $password->addValidator('NotEmpty', true);
    $password->getValidator('NotEmpty')->setMessage('form.login.password.errBlank');
    
    já k tomu zatím nedošel, psycho mě moc nevadilo :-)

Komentáře jsou uzavřeny.