Issue

ZF-2730: Zend_Validate_Abstract::setMessgage() does not overwrite the message from the translator.

Issue Type: Improvement Created: 2008-02-25T11:35:29.000+0000 Last Updated: 2009-09-15T10:55:04.000+0000 Status: Resolved Fix version(s): - 1.10.0 (27/Jan/10)

Reporter: Laurent Melmoux (laurent melmoux) Assignee: Thomas Weidner (thomas) Tags: - Zend_Validate

Related issues: Attachments:

Description

When I have set a translation object in Zend_Registry() to be used as default translator, sometime I will need to set a Custom Validator Error Message for a specific field.

But when we call Zend_Validate_Abstract::setMessgage() it does not overwrite the message from the translator.

To have it working I had to add a property in Zend_Validate_Abstract to keep track of the messages template which have been changed by Zend_Validate_Abstract::setMessgage();

And use it to see if the translator message should overwrite it, in Zend_Validate_Abstract:: _createMessage():

<pre class="highlight">
if (null !== ($translator = $this->getTranslator()) && !in_array($messageKey, $this->_messagesOverWriten)) {
    if ($translator->isTranslated($messageKey)) {
        $message = $translator->translate($messageKey);
    }
}

Comments

Posted by Wil Sinclair (wil) on 2008-04-18T16:55:02.000+0000

Please evaluate and categorize/assign as necessary.

Posted by Thomas Weidner (thomas) on 2009-03-08T12:47:03.000+0000

That's correct behaviour. It could be described as:

First output Original message ->> but when set then the own message ->> and then when set the translation

Expect you have set own messages and also translation: A english user would get you own message and a french user would get the translation of the original message.

So, having both you would have to change the translation, otherwise your message is inconsistent.

Posted by Laurent Melmoux (laurent melmoux) on 2009-03-09T01:20:01.000+0000

What I want to say is that sometime you don't want to display the generic translation but a custom one. A use case will make it clearer :

For exemple with the NotEmpty Validator the original message is : * Value is empty, but a non-empty value is required Then my translation is : * le champs est requis But now let say I want so display something more specific, for exemple "the terme of use checkbox" and display * vous devez accepter les conditions d'utilisations du site

The in this specific use case custom message is usefull

Posted by Thomas Weidner (thomas) on 2009-06-26T11:45:42.000+0000

To have a "custom" translation you would have to extend the default translation instance and add your wished "custom" translations.

Then you have to give this "extended" translation instance to the validator you want to customize. It's not possible to do "chained" translations.

Posted by Thomas Weidner (thomas) on 2009-06-26T11:47:15.000+0000

Closing as won't fix due to the previous mentioned reasons.

Posted by Laurent Melmoux (laurent melmoux) on 2009-06-29T01:02:41.000+0000

I'm using Zend_Form and if I use your solution all the field will used the custom translation when I just need an one specific field to used it.

Also the behavior between native language and others language (with i18n translation object) is not the same : With Zen_Form and without i18n support you can use custom message (@see ZF manual > Zend_Form > 23.3.3.1. Custom Error Messages) but as long as you use i18n support by setting the default translator in Zend_Registry() this feature does not work anymore. IMHO it should be the same behaviors. Activating i18n shouldn't change as your application behave.

Posted by Thomas Weidner (thomas) on 2009-06-29T03:37:27.000+0000

As described before do custom translations by using setTranslator() which is a instance method of the abstract class.

Why should the custom translation be used for all elements? This is only the case when you use the same element multiple times.

The solution is to use different instances... one with translation-A and one with translation-B. When you want to use a "specific" field then also initiate it as specific field (seperate instance). Simple OOP behaviour.

And why should translation no longer work when setting a custom error message ? All errors are translated as long as you provide their key and their translation. When you don't want the custom error to be translated use a different key or a different translation.

This works for the default errors the same as for custom errors. There is no change in behaviour.

Posted by Laurent Melmoux (laurent melmoux) on 2009-06-29T06:22:18.000+0000

Thomas,

I probably don't express me very well so let's take an example to illustrate my issue :

Let's say I have a form with severals fields where I need to validate the string length. Most of the time the generic message for Zend_Validate_StringLength string too short is good enough, but I want a more explicit/friendly message for my username field.

Here how it works with no i18n setup :

<pre class="highlight">
$validator = new Zend_Validate_StringLength(8);

// with generic message output
// "'word' is less than %min% characters long"
if (!$validator->isValid('word')) {
    $messages = $validator->getMessages();
    echo current($messages);
}

// I want a custum message for my username field
$validator->setMessage(
    'Your username \'%value%\' is too short; it must be at least %min% ' .
    'characters',
    Zend_Validate_StringLength::TOO_SHORT);

// with custom message the output is
// "Your username 'word' is too short; it must be at least 8 characters"
if (!$validator->isValid('logme')) {
    $messages = $validator->getMessages();
    echo current($messages);
}

So here, all is fine. But when I start to use the i18n feature it doesn't work the same :

<pre class="highlight">

$validator = new Zend_Validate_StringLength(8);

$messageTemplates = array('stringLengthTooShort'=>"La chaine '%value%' est trop courte, fait moins de %min% charactères", 'stringLengthTooLong'=>"La chaine '%value%' est trop longue, faitplus de %min% charactères");
$translator = new Zend_Translate('array', $messageTemplates, 'fr_FR');
Zend_Registry::set('Zend_Translate', $translator);

// default message
// La chaine 'unmot' est trop courte, fait moins de 8 charactères
if (!$validator->isValid('unmot')) {
    $messages = $validator->getMessages();
    echo current($messages);
}

// I want a custom message for my username field
$validator->setMessage(
    "Votre nom d'utilisateur \'%value%\' est trop court; il doit faire au moins %min% charactères",
    Zend_Validate_StringLength::TOO_SHORT);
// Then HERE, custom message is not taken into account. The output is steel :
// La chaine 'logme' est trop courte, fait moins de 8 charactères
// While I was expecting
// Votre nom d'utilisateur 'logme' est trop court; il doit faire au moins 8 charactères
if (!$validator->isValid('logme')) {
    $messages = $validator->getMessages();
    echo current($messages);
}

It is not possible anymore to set an custom message .

Posted by Thomas Weidner (thomas) on 2009-06-29T06:38:04.000+0000

Reopened due to new reproducable example.

Posted by Thomas Weidner (thomas) on 2009-08-29T12:30:16.000+0000

After digging a little bit the reason is logical and also the solution is clear.

There are 2 possibilities to select from: 1.) A message will ALWAYS be translated. Default behaviour because otherwise you would have to call setMessage based on the users locale. Which means that you do the translation yourself.

2.) A message will ONLY be translated when setMessage is not set. This is problematic as you need to give different messages based on the users locale. In this case you do the translation yourself.

To select between this 2 different behaviours you have to handle your translation file differently...

Using the messageKey as source will force the first way. Using the messageContent as source will force the second way.

So for you this means to change your translation file as follows when you want to override translations and not the content of the templates:

<pre class="highlight">
$content = $validator->getMessageTemplates();
$messageTemplates = array($content['stringLengthTooShort'] =>"Votre nom d'utilisateur \'%value%\' est trop court; il doit faire au moins %min% charactères");

Posted by Thomas Weidner (thomas) on 2009-08-29T12:34:43.000+0000

Closing as not an issue due the previous described reasons.

Posted by Laurent Melmoux (laurent melmoux) on 2009-09-14T09:19:58.000+0000

Thomas,

I'm still thinking that the correct behavior should be 2) A message will ONLY be translated when setMessage is not set. :-)

Right now if you use the validators in a i18n context (with an application wide locale within your registry) Zend_Validate::setMessage() is not working at all which means you have a behavior's change bettween the "default" and the localised context. And this is the same with Zend_Form_Element::addErrorMessage() and co.

To set custom message depending on the user local it is easy to do something $validator->setMessage($customMessages[$locale]['stringLengthTooShort']);

Also about your exemple, what should I do with $messageTemplates ?

Thanks.

Posted by Thomas Weidner (thomas) on 2009-09-14T10:57:49.000+0000

I still don't get the point about your problem of things which already work. $messageTemplates in my example is just a variable... name it $something or $dontknow... it's just a variable.

As described before translation does already work for case 2 when things are handled like described.

<pre class="highlight">
// example translation for case 2
// this is already described above
return array(
    "'%value%' is less than %min% characters long" => "my translation %min% for %value%",
    "'%value%' is greater than %max% characters long" => "other translation");

Posted by Laurent Melmoux (laurent melmoux) on 2009-09-15T02:38:41.000+0000

Pardon me if I'm not really clear.

I'm mostly using Zend_Validate with Zend_Form in an Automated CRUD and I'm creating them with a config object, then if I want a custom message I can simply do :

<pre class="highlight">
user.login.elements.username.options.validators.emailAddressInvalidFormat.options.messages.emailAddressInvalidFormat=  "Votre login doit être un email valid";

I really like this feature because I have no additional code to write and I don't really want to rewrite everything by hand only to add a custom message. :-(

To illustrate this here is 2 simples working examples :

With no translator, here the custom message is as expected :

<pre class="highlight">
date_default_timezone_set('Europe/Berlin');

require_once 'Zend/Loader/Autoloader.php';
$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->registerNamespace('Mmx_');


$form = new Zend_Form();

$username = new Zend_Form_Element_Text('username');
$username->setLabel('Utilisateur')
         ->addValidator('NotEmpty')
         ->addValidator('EmailAddress', false, array('messages'=>array('emailAddressInvalidFormat' => "Votre login doit être un email valid")))
         ->addFilter('StringToLower');
         
$password = new Zend_Form_Element_Text('password');     
$password->setLabel('Password')
         ->addValidator('NotEmpty')
         ->addValidator('StringLength',false, array(6, 20))
         ->addValidator('Alnum')
         ->addFilter('StringTrim')
         ->addFilter('StringToLower');
         
$form->addElement($username)
     ->addElement($password)
     ->addElement(new Zend_Form_Element_Submit('login'));


$view = new Zend_View();
$view->addHelperPath($rootPath. '/library/Zend/View/Helper', 'Zend_View_Helper');
$form->setView($view);

echo "Try invalid data: \n";
echo $form->isValid(array('username' => 'FOOBAR', 'password' => 'short')) ? "Form is validated\n" : "Form did not validate.\n";
echo $form->render($view), "\n\n";

But when I'm using a translator within the form my custom message is not display :

<pre class="highlight">
date_default_timezone_set('Europe/Berlin');

require_once 'Zend/Loader/Autoloader.php';
$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->registerNamespace('Mmx_');

$messageTemplates = array(
    //Empty
    'isEmpty'     => 'Le champ est requis !',
    'notEmptyInvalid' => 'Le champ est requis !',
    //EmailAdress
    'emailAddressInvalidFormat'          => "L'adresse email n'est pas valide",
    'emailAddressInvalidHostname'  => "'%hostname%' n'est pas un nom de domaine valide",
    //StringLength
    'stringLengthTooShort'          => "Minimum %min% caractères",
    'stringLengthTooLong'           => "Maximum %max% caractères",
    //Alpha
    'notAlpha'    => "Lettres alphabetiques uniquement",
    'stringEmpty' => "Ne peut être vide"
);

$locale = new Zend_Locale('fr');
Zend_Registry::set('Zend_Locale', $locale);
$translator = new Zend_Translate('array', $messageTemplates, $locale);

$form = new Zend_Form();

$username = new Zend_Form_Element_Text('username');
$username->setLabel('Utilisateur')
         ->addValidator('NotEmpty')
         ->addValidator('EmailAddress', false, array('messages'=>array('emailAddressInvalidFormat' => "Votre login doit être un email valid")))
         ->addFilter('StringToLower');
         
$password = new Zend_Form_Element_Text('password');     
$password->setLabel('Password')
         ->addValidator('NotEmpty')
         ->addValidator('StringLength',false, array(6, 20))
         ->addValidator('Alnum')
         ->addFilter('StringTrim')
         ->addFilter('StringToLower');
         
$form->addElement($username)
     ->addElement($password)
     ->addElement(new Zend_Form_Element_Submit('login'));

$form->setTranslator($translator);

$view = new Zend_View();
$view->addHelperPath($rootPath. '/library/Zend/View/Helper', 'Zend_View_Helper');



$form->setView($view);

echo "Try invalid data: \n";
echo $form->isValid(array('username' => 'FOOBAR', 'password' => 'short')) ? "Form is validated\n" : "Form did not validate.\n";
echo $form->render($view), "\n\n";

Posted by Thomas Weidner (thomas) on 2009-09-15T10:54:57.000+0000

There is no solution to this usecase:

You changed the messageTemplate of the validator and you added translation to the form. Now the form translates returned messages. How should the validator know what should be translated and what not? The validator has no connection to the form.

I see no way how this should be integrated.

Eighter change your translation as mentioned before or handle it different in this case. (You can disable translation on a element).

Have you found an issue?

See the Overview section for more details.

Copyright

© 2006-2022 by Zend by Perforce. Made with by awesome contributors.

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

Contacts