Issues

ZF-9275: Zend_Form_Element overrides Zend_Validate DefaultTranslator

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.


    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);
    }

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

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

Added example code

Fixed in [ZF-9364]

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


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.

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.

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().


// edit, deleted diff code

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 :


        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:


        Zend_Registry::set('Zend_Translate', $translate);

and:


        $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.


        $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.

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

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


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

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

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

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.

proove that it works


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)

This unit test shows the problem:


    /**
     * @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']);
        
    }    

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:


$ 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().

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:


    /**
     * @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:


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.

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

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

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

This code is wrong:


        // 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:


    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 ;     
    }

code should be:


        if (Zend_Validate_Abstract::hasDefaultTranslator())
        {
            $translator = Zend_Validate_Abstract::getDefaultTranslator();
        } else {
            $translator = $this->getTranslator();
        }

"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?

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.


    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:


            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:


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"

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

Christian, only if I remove the registry:


    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:


    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.


         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.

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

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.

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.

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


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


  Zend_Validate_Abstract::setDefaultTranslator($resourcetranslator);

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


    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.


    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:


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

In Form::isValid:


        $translator = $this->getTranslator();

We look if the form has a translator.

This looks if the form has a default translator:


        if (null === $this->_translator) {
            return self::getDefaultTranslator();
        }

This in turn looks if Zend_Translate is set:


            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:


       $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:


            $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.

$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,


$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,

Reopening.

Erasing fix version as the issue was reopened

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.

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;

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

3) 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.

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

Fixed on trunk in svn r22833.

Need to check with alab before merging to 1.10 branch.

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