Issues

ZF-9275: Zend_Form_Element overrides Zend_Validate DefaultTranslator

Issue Type: Bug Created: 2010-02-26T12:46:04.000+0000 Last Updated: 2010-08-30T03:02:23.000+0000 Status: Resolved Fix version(s): - 1.11.0 (02/Nov/10)

Reporter: Thomas Rothe (burnred) Assignee: Rob Allen (rob) Tags: - Zend_Form

Related issues: - ZF-9330

Attachments: - Form.diff

Description

If I set Zend_Validate_Abstract::$_defaultTranslator to an other Translator than that one of Zend_Form. Zend_Form_Element will allways use it's global default Translator from the registry.

<pre class="highlight">
    protected function _initTranslator()
    {
        $translate = new Zend_Translate('gettext',APPLICATION_PATH.'/languages/de/de.mo','de');
        Zend_Registry::set('Zend_Translate', $translate);
        
        $translateValidate = new Zend_Translate('array', APPLICATION_PATH.'/languages/de/Zend_Validate.php','de');
        Zend_Validate_Abstract::setDefaultTranslator($translateValidate);
    }



<pre class="highlight">
public function testtranslateAction()
    {
        $form = new Zend_Form();
        $input = new Zend_Form_Element_Text
        (
            'example',
            array
            (
                'required' => true
            )
        );
        $submit = new Zend_Form_Element_Submit
        (
            'test',
            array
            (
                'label' => _('submit')
            )
        );
        $form->addElements(array($input, $submit));
        if($this->getRequest()->isPost())
        {
            $form->isValid($this->getRequest()->getPost());
        }
        $this->view->form = $form;
    }

If I send this form with an empty post, the error message should be the german text "Es wird ein Wert benötigt. Dieser darf nicht leer sein" ,but it's allways the original message "Value is required and can't be empty".

Comments

Posted by Matthew Weier O'Phinney (matthew) on 2010-02-26T14:02:15.000+0000

Can you please provide some reproduce code, and clearly indicate expected and actual results?

Posted by Thomas Rothe (burnred) on 2010-02-26T14:33:35.000+0000

Added example code

Posted by Christian Albrecht (alab) on 2010-03-18T08:27:57.000+0000

Fixed in [ZF-9364]

Posted by Dominique Lorre (dlorre) on 2010-03-24T06:34:31.000+0000

Please reopen this issue as the validator translator is overriden by the form element translator. ZF 9364 addresses another issue see the comments.

Posted by Christian Albrecht (alab) on 2010-03-24T07:28:51.000+0000

<pre class="highlight">
Index: library/Zend/Form/Element.php
===================================================================
--- library/Zend/Form/Element.php       (Revision 21633)
+++ library/Zend/Form/Element.php       (Arbeitskopie)
@@ -1329,7 +1329,9 @@
         $isArray         = $this->isArray();
         foreach ($this->getValidators() as $key => $validator) {
             if (method_exists($validator, 'setTranslator')) {
-                $validator->setTranslator($this->getTranslator());
+                if (!$validator->getTranslator()) {
+                    $validator->setTranslator($this->getTranslator());
+                }
             }
 
             if (method_exists($validator, 'setDisableTranslator')) {

However i am not sure, if that is correct since $validator will return Translator from Zend_Registry when it has none set and therefor break BC because the default Translator may differ from that set to Zend_Form or Zend_Form_Element.

Posted by Dominique Lorre (dlorre) on 2010-03-24T08:03:57.000+0000

Thank you for reopening. Yes, the fix is tricky.

I suggest checking $validator->getTranslator() against Zend_Registry::get('Zend_Translate').

If they are identical then it would be fine to override the validator. That follows the logic of going from general to specific.

Posted by Christian Albrecht (alab) on 2010-03-25T06:32:11.000+0000

Suggested fix, add new members to Zend_Validate_Abstract

protected $_isSpecificTranslator = false; protected static $_isSpecificDefaultTranslator = false;

with appropriate getter Methods and set the new members true within setTranslator() and setDefaultTranslator().

<pre class="highlight">
// edit, deleted diff code

Posted by Dominique Lorre (dlorre) on 2010-03-25T06:56:17.000+0000

It's an interesting approach but I don't think we need specificc translator flag, because when we use:

Zend_Validate_Abstract::setDefaultTranslator($translateValidate);

we precisely mean that we want the validators to use this translator.

First case: we want to handle all the translations. We do :

<pre class="highlight">
        Zend_Registry::set('Zend_Translate', $translate);

and everything is fine. ==> Zend_Validate_Abstract::getDefaultTranslator() == Zend_Registry::get('Zend_Translate')

Second case: we want to handle translations but want to use the default translations for validators then we do:

<pre class="highlight">
        Zend_Registry::set('Zend_Translate', $translate);

and:

<pre class="highlight">
        $locale = Zend_Registry::get('Zend_Locale') ;
        $translator = new Zend_Translate(
            Zend_Translate::AN_ARRAY,
            APPLICATION_PATH . '/resources/languages',
            $locale, 
            array('scan' => Zend_Translate::LOCALE_DIRECTORY)
        );
        Zend_Validate_Abstract::setDefaultTranslator($translator) ; 

==> Zend_Validate_Abstract::getDefaultTranslator() != Zend_Registry::get('Zend_Translate')

Third case: we don't want to use Zend_Translate but our project is not in english. So we will to use the translator for the validators only.

<pre class="highlight">
        $translator = new Zend_Translate(
            Zend_Translate::AN_ARRAY,
            APPLICATION_PATH . '/resources/languages',
            'fr', 
            array('scan' => Zend_Translate::LOCALE_DIRECTORY)
        );
        Zend_Validate_Abstract::setDefaultTranslator($translator) ; 

==> Zend_Validate_Abstract::getDefaultTranslator() != Zend_Registry::get('Zend_Translate')

So, if Zend_Validate_Abstract::getDefaultTranslator() == Zend_Registry::get('Zend_Translate') then we can assume it is safe to override the validator translator.

Posted by Christian Albrecht (alab) on 2010-03-25T07:15:22.000+0000

Dominique, i think the problem is

$translate = Zend_Registry::get('Zend_Translate'); $translate instanceof Zend_Translate || $translate instanceof Zend_Translate_Adapter

but always

Zend_Validate_Abstract::getDefaultTranslator() instanceof Zend_Translate_Adapter

Posted by Christian Albrecht (alab) on 2010-03-25T07:38:56.000+0000

Baah, much to complicated my solution, here is a simpler one

<pre class="highlight">
Index: library/Zend/Form/Element.php
===================================================================
--- library/Zend/Form/Element.php       (Revision 21641)
+++ library/Zend/Form/Element.php       (Arbeitskopie)
@@ -1329,7 +1329,10 @@
         $isArray         = $this->isArray();
         foreach ($this->getValidators() as $key => $validator) {
             if (method_exists($validator, 'setTranslator')) {
-                $validator->setTranslator($this->getTranslator());
+                if (!$validator->hasTranslator() &&
+                    null !== ($translator = $this->getTranslator())) {
+                    $validator->setTranslator($translator);
+                }
             }
 
             if (method_exists($validator, 'setDisableTranslator')) {
Index: library/Zend/Validate/Abstract.php
===================================================================
--- library/Zend/Validate/Abstract.php  (Revision 21641)
+++ library/Zend/Validate/Abstract.php  (Arbeitskopie)
@@ -353,6 +353,20 @@
     }
 
     /**
+     * Was a (default) translator set?
+     * 
+     * @return Boolean
+     */
+    public function hasTranslator()
+    {
+        if (null === $this->_translator &&
+            null === self::$_defaultTranslator) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
      * Set default translation object for all validate objects
      *
      * @param  Zend_Translate|Zend_Translate_Adapter|null $translator

Posted by Dominique Lorre (dlorre) on 2010-03-25T08:00:08.000+0000

Christian, that code works with my project. It's probably safer to do it that way.

Posted by Christian Albrecht (alab) on 2010-03-25T08:06:28.000+0000

Which one do you refer to with 'that'? My simpler solution, or yours?

Posted by Dominique Lorre (dlorre) on 2010-03-25T08:49:04.000+0000

My safety concern is about Zend_Form::getDefaultTranslator(). Actually Zend_Validate_Abstract::getDefaultTranslator() ignores the default form translator but should the Zend dev team changes this then the registry check would stop working. So, yes your solution is safer in that perspective.

Posted by Christian Albrecht (alab) on 2010-03-25T09:17:50.000+0000

proove that it works

<pre class="highlight">
cd library
php -r 'require_once "Zend/Translate.php"; \
require_once "Zend/Validate/Alnum.php"; \
$translate = new Zend_Translate("Array"); \
$validator = new Zend_Validate_Alnum(); \
var_dump($validator->hasTranslator()); \
Zend_Validate_Abstract::setDefaultTranslator($translate); \
var_dump($validator->hasTranslator());'

// bool(false)
// bool(true)

Posted by Rob Allen (rob) on 2010-03-28T08:22:46.000+0000

This unit test shows the problem:

<pre class="highlight">
    /**
     * @group ZF-9275
     */
    public function testElementDoesntOverrideValidatorsDefaultTranslatorWithDefaultRegistryTranslator()
    {
        $registryTranslations = array('alphaInvalid' => 'Registry message');
        $registryTranslate = new Zend_Translate('array', $registryTranslations);
        Zend_Registry::set('Zend_Translate', $registryTranslate);
        
        $validatorTranslations = array('alphaInvalid' => 'Validator message');
        $validatorTranslate = new Zend_Translate('array', $validatorTranslations);
        Zend_Validate_Abstract::setDefaultTranslator($validatorTranslate);
        
        $elementTranslations = array('alphaInvalid' => 'Element message');
        $elementTranslate = new Zend_Translate('array', $elementTranslations);
       
        // the default validate translator should beat the registry one
        $this->element->addValidator('Alpha');
        $this->assertFalse($this->element->isValid(123));
        $messages = $this->element->getMessages();
        $this->assertEquals('Validator message', $messages['alphaInvalid']);
        
        // however the element's translator should beat both
        $this->element->setTranslator($elementTranslate);
        $this->assertFalse($this->element->isValid(123));
        $messages = $this->element->getMessages();
        $this->assertEquals('Element message', $messages['alphaInvalid']);
        
    }    

Posted by Rob Allen (rob) on 2010-03-28T09:17:26.000+0000

Christian's solution will result in a translator attached directly to an element not being set for that element's validators which is wrong.

This is a potential patch that passes my unit test above:

<pre class="highlight">
$ svn diff library/Zend/Form/Element.php 
Index: library/Zend/Form/Element.php
===================================================================
--- library/Zend/Form/Element.php   (revision 21664)
+++ library/Zend/Form/Element.php   (working copy)
@@ -1332,6 +1332,15 @@
             array_unshift($validators, $notEmpty);
             $this->setValidators($validators);
         }
+        
+        // Find the correct translator. Zend_Validate_Abstract::getDefaultTranslator()
+        // will get either the static translator attached to Zend_Validate_Abstract
+        // or the 'Zend_Translate' from Zend_Registry. 
+        $translator = Zend_Validate_Abstract::getDefaultTranslator();
+        if($this->hasTranslator()) {
+            // only pick up this element's translator if it was attached directly.
+            $translator = $this->getTranslator();
+        }
 
         $this->_messages = array();
         $this->_errors   = array();
@@ -1339,7 +1348,7 @@
         $isArray         = $this->isArray();
         foreach ($this->getValidators() as $key => $validator) {
             if (method_exists($validator, 'setTranslator')) {
-                $validator->setTranslator($this->getTranslator());
+                $validator->setTranslator($translator);
             }
 
             if (method_exists($validator, 'setDisableTranslator')) {

The problem with this patch is that the translator attached to Zend_Form::setDefaultTranslator() is now ignored for validation messages, so you would then have to set them using Zend_Validate_Abstract::setDefaultTranslator().

Posted by Rob Allen (rob) on 2010-03-28T09:54:02.000+0000

The correct order of translators to apply to a validator when validating it via a form element is:

(most generic to most specific) 1. Zend_Registry's 'Zend_Translate' translator 2. Zend_Validate_Abstract's static default translator 3. Zend_Form's static default translator 4. The form instance's directly attached translator 5. The element instance's directly attached translator 6. The validator instance's directly attached translator

To prove this, I've created 4 unit tests:

<pre class="highlight">
    /**
     * @group ZF-9275
     */
    public function testElementDoesntOverrideDefaultValidatorTranslatorWithDefaultRegistryTranslator()
    {
        $registryTranslations = array('alphaInvalid' => 'Registry message');
        $registryTranslate = new Zend_Translate('array', $registryTranslations);
        Zend_Registry::set('Zend_Translate', $registryTranslate);
        
        $validatorTranslations = array('alphaInvalid' => 'Validator message');
        $validatorTranslate = new Zend_Translate('array', $validatorTranslations);
        Zend_Validate_Abstract::setDefaultTranslator($validatorTranslate);
        
        $elementTranslations = array('alphaInvalid' => 'Element message');
        $elementTranslate = new Zend_Translate('array', $elementTranslations);
       
        // the default validate translator should beat the registry one
        $this->element->addValidator('Alpha');
        $this->assertFalse($this->element->isValid(123));
        $messages = $this->element->getMessages();
        $this->assertEquals('Validator message', $messages['alphaInvalid']);
    }
    
    /**
     * @group ZF-9275
     */
    public function testDefaultTranslatorDoesntOverrideElementTranslatorOnValdiation()
    {
        $registryTranslations = array('alphaInvalid' => 'Registry message');
        $registryTranslate = new Zend_Translate('array', $registryTranslations);
        Zend_Registry::set('Zend_Translate', $registryTranslate);
        
        $validatorTranslations = array('alphaInvalid' => 'Validator message');
        $validatorTranslate = new Zend_Translate('array', $validatorTranslations);
        Zend_Validate_Abstract::setDefaultTranslator($validatorTranslate);
        
        $elementTranslations = array('alphaInvalid' => 'Element message');
        $elementTranslate = new Zend_Translate('array', $elementTranslations);
        
        $this->element->addValidator('Alpha');
        $this->element->setTranslator($elementTranslate);
        $this->assertFalse($this->element->isValid(123));
        $messages = $this->element->getMessages();
        $this->assertEquals('Element message', $messages['alphaInvalid']);
    }

    /**
     * @group ZF-9275
     */
    public function testValidatorsDefaultTranslatorDoesntOverrideFormsDefaultTranslator()
    {
        $formTranslations = array('alphaInvalid' => 'Form message');
        $formTranslate = new Zend_Translate('array', $formTranslations);
        Zend_Form::setDefaultTranslator($formTranslate);
        
        $validatorTranslations = array('alphaInvalid' => 'Validator message');
        $validatorTranslate = new Zend_Translate('array', $validatorTranslations);
        Zend_Validate_Abstract::setDefaultTranslator($validatorTranslate);
        
        // the default validate translator should beat the registry one
        $this->element->addValidator('Alpha');
        $this->assertFalse($this->element->isValid(123));
        $messages = $this->element->getMessages();
        $this->assertEquals('Form message', $messages['alphaInvalid']);
    }
    
    /**
     * @group ZF-9275
     */
    public function testElementsTranslatorDoesntOverrideValidatorsDirectlyAttachedTranslator()
    {
        $elementTranslations = array('alphaInvalid' => 'Element message');
        $elementTranslate = new Zend_Translate('array', $elementTranslations);
        
        $validatorTranslations = array('alphaInvalid' => 'Direct validator message');
        $validatorTranslate = new Zend_Translate('array', $validatorTranslations);
        
        $validator = new Zend_Validate_Alpha();
        $validator->setTranslator($validatorTranslate);
        $this->element->addValidator($validator);
        $this->assertFalse($this->element->isValid(123));
        $messages = $this->element->getMessages();
        $this->assertEquals('Direct validator message', $messages['alphaInvalid']);
    }  

This is the patch that passes this set of unit tests:

<pre class="highlight">
Index: library/Zend/Form.php
===================================================================
--- library/Zend/Form.php   (revision 21665)
+++ library/Zend/Form.php   (working copy)
@@ -2776,6 +2776,16 @@
     }
 
     /**
+     * Is there a default translation object set?
+     * 
+     * @return boolean
+     */
+    public static function hasDefaultTranslator()
+    { 
+        return (bool)self::$_translatorDefault;
+    }
+    
+    /**
      * Indicate whether or not translation should be disabled
      *
      * @param  bool $flag
Index: library/Zend/Form/Element.php
===================================================================
--- library/Zend/Form/Element.php   (revision 21664)
+++ library/Zend/Form/Element.php   (working copy)
@@ -1332,6 +1332,21 @@
             array_unshift($validators, $notEmpty);
             $this->setValidators($validators);
         }
+        
+        // Find the correct translator. Zend_Validate_Abstract::getDefaultTranslator()
+        // will get either the static translator attached to Zend_Validate_Abstract
+        // or the 'Zend_Translate' from Zend_Registry. 
+        if (Zend_Validate_Abstract::hasDefaultTranslator() && 
+            !Zend_Form::hasDefaultTranslator()) 
+        {
+            $translator = Zend_Validate_Abstract::getDefaultTranslator();
+            if ($this->hasTranslator()) {
+                // only pick up this element's translator if it was attached directly.
+                $translator = $this->getTranslator();
+            }
+        } else {
+            $translator = $this->getTranslator();
+        }
 
         $this->_messages = array();
         $this->_errors   = array();
@@ -1339,7 +1354,13 @@
         $isArray         = $this->isArray();
         foreach ($this->getValidators() as $key => $validator) {
             if (method_exists($validator, 'setTranslator')) {
-                $validator->setTranslator($this->getTranslator());
+                if (method_exists($validator, 'hasTranslator')) {
+                    if (!$validator->hasTranslator()) {                    
+                        $validator->setTranslator($translator);
+                    }
+                } else {
+                    $validator->setTranslator($translator);
+                }
             }
 
             if (method_exists($validator, 'setDisableTranslator')) {
Index: library/Zend/Validate/Abstract.php
===================================================================
--- library/Zend/Validate/Abstract.php  (revision 21664)
+++ library/Zend/Validate/Abstract.php  (working copy)
@@ -353,6 +353,16 @@
     }
 
     /**
+     * Does this validator have its own specific translator?
+     * 
+     * @return bool
+     */
+    public function hasTranslator()
+    {
+        return (bool)$this->_translator;
+    }  
+    
+    /**
      * Set default translation object for all validate objects
      *
      * @param  Zend_Translate|Zend_Translate_Adapter|null $translator
@@ -393,6 +403,16 @@
     }
 
     /**
+     * Is there a default translation object set?
+     * 
+     * @return boolean
+     */
+    public static function hasDefaultTranslator()
+    { 
+        return (bool)self::$_defaultTranslator;
+    }
+    
+    /**
      * Indicate whether or not translation should be disabled
      *
      * @param  bool $flag

All other Zend_Form tests also continue to pass.

Posted by Rob Allen (rob) on 2010-03-28T09:55:38.000+0000

This patch fixes the reported issue and also ensures that related issues around the order of applied translate objects is now correct.

Posted by Rob Allen (rob) on 2010-03-31T12:57:55.000+0000

Fixed on trunk r21724 (test) r21725 (library) Fixed on branch: r21726

Posted by Dominique Lorre (dlorre) on 2010-05-11T09:19:12.000+0000

Hello, this issue is not fixed as of 1.10.4. Please reopen it.

This code is wrong:

<pre class="highlight">
        // Find the correct translator. Zend_Validate_Abstract::getDefaultTranslator()
        // will get either the static translator attached to Zend_Validate_Abstract
        // or the 'Zend_Translate' from Zend_Registry.
        if (Zend_Validate_Abstract::hasDefaultTranslator() &&
            !Zend_Form::hasDefaultTranslator())
        {
            $translator = Zend_Validate_Abstract::getDefaultTranslator();
            if ($this->hasTranslator()) {
                // only pick up this element's translator if it was attached directly.
                $translator = $this->getTranslator();
            }
        } else {
            $translator = $this->getTranslator();
        }

When Zend_Validate_Abstract::setdefaultTranslator() is used, it means we intend to use the special translation files for the validators which are included in the Zend Framework package under resources/languages/xx/Zend_Validate.php. The one and only case when we don't want to use this translator for the validators is when we directly set the validator translator using $validator->setTranslator() The code above prevents this behavior to work since the element always have a translator set with an init code like this one:

<pre class="highlight">
    protected function _initTranslate()
    {
        $translate = $this->getPluginResource("translate")->getTranslate() ;
        Zend_Registry::set('Zend_Translate', $translate);
        $locale = Zend_Registry::get('Zend_Locale') ;
        $translator = new Zend_Translate(
            Zend_Translate::AN_ARRAY,
            APPLICATION_PATH . '/resources/languages',
            $locale, 
            array('scan' => Zend_Translate::LOCALE_DIRECTORY)
        );
        Zend_Validate_Abstract::setDefaultTranslator($translator) ;     
        return $translate ;     
    }

Posted by Dominique Lorre (dlorre) on 2010-05-11T09:30:45.000+0000

code should be:

<pre class="highlight">
        if (Zend_Validate_Abstract::hasDefaultTranslator())
        {
            $translator = Zend_Validate_Abstract::getDefaultTranslator();
        } else {
            $translator = $this->getTranslator();
        }

Posted by Rob Allen (rob) on 2010-05-11T13:18:05.000+0000

"The one and only case when we don't want to use this translator for the validators is when we directly set the validator translator using $validator->setTranslator()"

I disagree. If a validator is set on the form, then it should override.

Can you provide a use-case where you would want to set a form translator, but expect the global Zend_Validate translator to win?

Alternatively, if you mean that the default Zend_Validate_Abstract static translator is never used, could you provide a unit test that shows this?

Posted by Dominique Lorre (dlorre) on 2010-05-12T02:14:06.000+0000

The part Zend_Form vs Zend_Validate is not the main issue. In my code I did not use Zend_Form default translator in first place.

I expect Zend_Validate translator to "win" when I use the translated files in resources/languages of the Zend Framework full package. I don't know any other case where the default translator for Zend_Validate is useful.

The problem is we get translations for most validators in many languages but there is no simple way to use them!

Actually I'm using a workaround by setting the form default translator. It works but using the form default translator for a validate translator is not correct.

<pre class="highlight">
    protected function _initTranslate()
    {
        $translate = $this->getPluginResource("translate")->getTranslate() ;
        Zend_Registry::set('Zend_Translate', $translate);
        $locale = Zend_Registry::get('Zend_Locale') ;
        $translator = new Zend_Translate(
            Zend_Translate::AN_ARRAY,
            APPLICATION_PATH . '/resources/languages',
            $locale, 
            array('scan' => Zend_Translate::LOCALE_DIRECTORY)
        );
        Zend_Form::setDefaultTranslator($translator);
        Zend_Validate_Abstract::setDefaultTranslator($translator) ;     
        return $translate ;     
    }

You don't need test file, the default Zend_Validate translator is not used if a translator is set for the form element:

<pre class="highlight">
            if ($this->hasTranslator()) {
                // only pick up this element's translator if it was attached directly.
                $translator = $this->getTranslator();
            }

and this happens always if a translator file is used. For instance:

<pre class="highlight">
resources.translate.adapter = Gettext
resources.translate.data = APPLICATION_PATH "/resources/languages/"
resources.translate.options.scan = filename
resources.translate.options.disableNotices = true

Used in conjunction with the init code I already mentioned. This part is the main issue. We are back to the title of this issue: "Zend_Form_Element overrides Zend_Validate DefaultTranslator"

Posted by Christian Albrecht (alab) on 2010-05-12T03:18:49.000+0000

Dominique, my guess is this works as expected now if you stop using your workaround setting the Form default Translator.

Posted by Dominique Lorre (dlorre) on 2010-05-12T05:13:43.000+0000

Christian, only if I remove the registry:

<pre class="highlight">
    Zend_Registry::set('Zend_Translate', $translate);

Then I wouldn't need the workaround, or else this would be set as the translator of all elements in Zend_Form::isValid, because of this code:

<pre class="highlight">
    public static function getDefaultTranslator()
    {
        if (null === self::$_translatorDefault) {
            require_once 'Zend/Registry.php';
            if (Zend_Registry::isRegistered('Zend_Translate')) {
                $translator = Zend_Registry::get('Zend_Translate');
                if ($translator instanceof Zend_Translate_Adapter) {
                    return $translator;
                } elseif ($translator instanceof Zend_Translate) {
                    return $translator->getAdapter();
                }
            }
        }
        return self::$_translatorDefault;
    }

Actually, commenting out this line would not help because this registry is already set in getTranslate() of Zend/Application/Resource/Translate.php, the other workaround would be to change the key name (untested atm)in application.ini.

<pre class="highlight">
         Zend_Registry::set($key, $this->_translate);

Now we clearly see the problem: the element translator is set in Form::isValid because the registry key Zend_Translate is initialized and since the element has a translator then Zend_Validate default translator is overriden.

Once again, setting Zend_Validate default translator has a special meaning, and should not be overriden in order to use the resources/languages translations.

Posted by Christian Albrecht (alab) on 2010-05-12T05:44:27.000+0000

oh, now i see what you mean - So you are arguing that the precedence list from [Rob's comment|#action_39619] should read

  1. Zend_Registry's 'Zend_Translate' translator
  2. Zend_Form's static default translator
  3. The form instance's directly attached translator
  4. The element instance's directly attached translator
  5. Zend_Validate_Abstract's static default translator
  6. The validator instance's directly attached translator

Posted by Rob Allen (rob) on 2010-05-12T06:34:29.000+0000

My list was:

(most generic to most specific) 1. Zend_Registry's 'Zend_Translate' translator 2. Zend_Validate_Abstract's static default translator 3. Zend_Form's static default translator 4. The form instance's directly attached translator 5. The element instance's directly attached translator 6. The validator instance's directly attached translator

I could certainly be persuaded that 2 and 3 should be swapped around.

I definitely think that any translator attached to an instance object should "beat" a global static translator.

Posted by Dominique Lorre (dlorre) on 2010-05-12T08:25:53.000+0000

I am comfortable with any set of priorities as long as we can specify that: a) we use a personal translation file b) we use for the validators the Zend_Validate.php files from resources/languages c) we can use specific translators for some validators (such as the Captcha validator which is not translated).

The problem is that the element translator is attached automatically when Zend_Translate is set in the registry. Thus it breaks your priority list Rob, since Zend_Translate goes between 4 and 5 when it should be 1.

Posted by Dominique Lorre (dlorre) on 2010-05-13T01:37:23.000+0000

Back to priorities. I think the keyword is 'reusability'. When you have a translation file you want to use it on your next projects. This is the case with Zend_Validate.php which is conveniently set with

<pre class="highlight">
Zend_Validate_Abstract::setDefaultTranslator($resourcetranslator);

When I set this I mean to reuse a file that is provided in Zend_Framework package. If I don't want to use this file then I don't set it. This is why I said the only case it should be overriden is when I directly set the validator translator (captcha for instance).

Rob asked: {quote} Can you provide a use-case where you would want to set a form translator, but expect the global Zend_Validate translator to win?{quote} Yes. I create a couple forms (for example registration and options) in project A which uses validators such as emailaddress. I use Zend_Validate.php as

<pre class="highlight">
  Zend_Validate_Abstract::setDefaultTranslator($resourcetranslator);

and my own translation file for the forms which will translate sentences such as "Enter your email address".

<pre class="highlight">
    Zend_Form::setDefaultTranslator($formtranslator);

Now, why on earth would I want to add the validator messages to my form translation since I have them already translated? This goes against the reusability principle. When I create project B with the same forms I will reuse my custom form translation file and the Zend_Validate.php file.

Zend form elements have their own messages such as "Enter your email" or "Confirm deletion", and validators have other messages which are totally different such as "Invalid type given, value should be a string" or "A crc32 hash could not be evaluated for the given file".

Why would I want to insert all the validators error messages in my custom form translation file because I want to use one? This makes no sense.

Priority should be a fallback: I don't have set the validator translator or the validator default translator: then we fallback to the element translator. If the element translator is not set then I fallback to the form translator and so on.

Now lets see why the priority code is broken.

<pre class="highlight">
    protected function _initTranslate()
    {
        $translate = $this->getPluginResource("translate")->getTranslate() ;
        $locale = Zend_Registry::get('Zend_Locale') ;
        $translator = new Zend_Translate(
            Zend_Translate::AN_ARRAY,
            APPLICATION_PATH . '/resources/languages',
            $locale, 
            array('scan' => Zend_Translate::LOCALE_DIRECTORY)
        );
        Zend_Validate_Abstract::setDefaultTranslator($translator) ;     
        return $translate ;     
    }

This init code sets a translator file for the project and uses the default translation for validators. As mentioned already, the call to getTranslate() sets 'Zend_Translate' in Zend_Registry:

<pre class="highlight">
            } else {
                $this->_translate = new Zend_Translate($options);
                Zend_Registry::set($key, $this->_translate);
            }

In Form::isValid:

<pre class="highlight">
        $translator = $this->getTranslator();

We look if the form has a translator.

This looks if the form has a default translator:

<pre class="highlight">
        if (null === $this->_translator) {
            return self::getDefaultTranslator();
        }

This in turn looks if Zend_Translate is set:

<pre class="highlight">
            if (Zend_Registry::isRegistered('Zend_Translate')) {
                $translator = Zend_Registry::get('Zend_Translate');
                if ($translator instanceof Zend_Translate_Adapter) {
                    return $translator;

Therefore the form gets a translator which is set to each element translator:

<pre class="highlight">
       $context = $data;
        foreach ($this->getElements() as $key => $element) {
            if (null !== $translator && !$element->hasTranslator()) {
                $element->setTranslator($translator);
            }

Since the elements translators have been set, then the validator translator is overriden:

<pre class="highlight">
            $translator = Zend_Validate_Abstract::getDefaultTranslator();
            if ($this->hasTranslator()) {
                // only pick up this element's translator if it was attached directly.
                $translator = $this->getTranslator();
            }

This is why Zend_Translate is set to the element translators which in turn override the default translator for Zend_Validate. Your priority list is not working as expected Rob.

Posted by David Salvador (dsalvador) on 2010-06-28T13:53:02.000+0000

$translate = new Zend_Translate('tmx', $tmxFileAndPath, $lang); $translatorForValidationMessages = new Zend_Translate('array',APPLICATION_PATH.'/resources/languages',$lang,array('scan' => Zend_Translate::LOCALE_DIRECTORY)); Zend_Validate_Abstract::setDefaultTranslator($translatorForValidationMessages);
Zend_Registry::set('Zend_Translate', $translate);

As it is, it translates the labels of the form, but leaves the validation messages in english.

And if i comment the line setting the Zend_Registry for Zend_Translate as per below: $translate = new Zend_Translate('tmx', $tmxFileAndPath, $lang); $translatorForValidationMessages = new Zend_Translate('array',APPLICATION_PATH.'/resources/languages',$lang,array('scan' => Zend_Translate::LOCALE_DIRECTORY)); Zend_Validate_Abstract::setDefaultTranslator($translatorForValidationMessages);
//Zend_Registry::set('Zend_Translate', $translate);

Then it gives me the validation errors properly translated, but the form labels remain in english.

Should this issue be reopened? I have tested it with 10.4 and 10.5, none of them work. I do not have the rights to reopen it,

Posted by David Salvador (dsalvador) on 2010-06-28T13:54:45.000+0000

<pre class="highlight">
$translate = new Zend_Translate('tmx', $tmxFileAndPath, $lang);
$translatorForValidationMessages = new Zend_Translate('array',APPLICATION_PATH.'/resources/languages',$lang,array('scan' => Zend_Translate::LOCALE_DIRECTORY));
Zend_Validate_Abstract::setDefaultTranslator($translatorForValidationMessages);
Zend_Registry::set('Zend_Translate', $translate);

As it is, it translates the labels of the form, but leaves the validation messages in english. —

And if i comment the line setting the Zend_Registry for Zend_Translate as per below:

<pre class="highlight">
$translate = new Zend_Translate('tmx', $tmxFileAndPath, $lang);
$translatorForValidationMessages = new Zend_Translate('array',APPLICATION_PATH.'/resources/languages',$lang,array('scan' => Zend_Translate::LOCALE_DIRECTORY));
Zend_Validate_Abstract::setDefaultTranslator($translatorForValidationMessages);
//Zend_Registry::set('Zend_Translate', $translate);

Then it gives me the validation errors properly translated, but the form labels remain in english.

Should this issue be reopened? I have tested it with 10.4 and 10.5, none of them work. I do not have the rights to reopen it,

Posted by Christian Albrecht (alab) on 2010-06-30T14:52:51.000+0000

Reopening.

Posted by Thomas Weidner (thomas) on 2010-07-12T11:27:30.000+0000

Erasing fix version as the issue was reopened

Posted by Kim Blomqvist (kblomqvist) on 2010-07-31T12:38:13.000+0000

Zend_Form::isValid() shouldn't set element translator by it's own default translator, because this leads into situation where form element has set translator which overrides Zend_Validate's default translator.

<pre class="literal">
Index: Form.php
===================================================================
--- Form.php    (revision 22746)
+++ Form.php    (working copy)
@@ -2222,7 +2222,9 @@
         }
         $context = $data;
         foreach ($this->getElements() as $key => $element) {
-            if (null !== $translator && !$element->hasTranslator()) {
+            if (null !== $translator && $this->hasTranslator() &&
+                !$element->hasTranslator())
+            {
                 $element->setTranslator($translator);
             }
             $check = $data;

Posted by Kim Blomqvist (kblomqvist) on 2010-08-01T00:16:02.000+0000

To make it more clear...

Init: Only global Zend_Registry's 'Zend_Translate' translator has been set

What happens? 1) Zend_Form::hasTranslator() returns false 2) Zend_Form::getTranslator() returns Zend_Registry's 'Zend_Translate' translator

  1. Zend_Form_Element::hasTranslator() returns true (wrong, should be false) 4) Zend_Form_Element::getTranslator() returns $this->_translator (wrong, should be Zend_Registry's 'Zend_Translate' translator)

Why it happens? Because Zend_Form used Zend_Form_Element's setTranslator() by its default Zend_Registry's 'Zend_Translate' translator. Zend_Form should override Zend_Form_Element's validator only when its own hasTranslator() returns true && Zend_Form_Element's hasTranslator() returns false.

Posted by Kim Blomqvist (kblomqvist) on 2010-08-07T02:38:27.000+0000

Zend.diff includes patch for Zend_Form Form.diff includes new test for Zend_Form_FormTest

Posted by Rob Allen (rob) on 2010-08-13T07:39:22.000+0000

Fixed on trunk in svn r22833.

Need to check with alab before merging to 1.10 branch.

Posted by Rob Allen (rob) on 2010-08-30T03:02:21.000+0000

As 1.10.8 is out, this fix will be released with 1.11

Have you found an issue?

See the Overview section for more details.

Copyright

© 2006-2016 by Zend, a Rogue Wave Company. Made with by awesome contributors.

This website is built using zend-expressive and it runs on PHP 7.

Contacts