Localizing Validator Errors

A common request I’ve seen is a way to localize the Validator error messages. Unfortunately the built-in Validator classes don’t make that easy in their current state (something we’re hoping to work on in the next version). In the meantime, here is one approach (and I’m sure there are more if you wanted) to changing the error messages using some form of resource file.

First we need a resouce file, I’m gonna format mine to be as close to the validator names as possible:

validatorStrings.xml<validators>
<StringValidator>
<tooLongError>Localized too long</tooLongError>
<tooShortError>Localized too short</tooShortError>
</StringValidator>
</validators>

Note that the validators is just an outer tag to make this valid XML. Inside I would put one tag for every validator. Here I just did StringValidator, but you can imagine a NumberValidator tag sitting right underneath it. Inside the StringValidator tag I put an entry for each error message, the name of the tag is the error name and the body of the tag is the actual value.

Now we need this resource loaded, so I’m just gonna use an HTTPService to do that, then I’m going to store the result in a Model that will be accessible from anywhere.

app.mxml<mx:Application xmlns:mx="http://www.macromedia.com/2003/mxml" xmlns="*" initialize="validatorStringsSvc.send()"><mx:HTTPService id="validatorStringsSvc" url="validatorStrings.xml" />
<mx:Model id="validatorStrings">
{validatorStringsSvc.result.validators}
</mx:Model>

Now we need a Validator to actually use this resource.

StringValidator.asclass StringValidator extends mx.validators.StringValidator{private var stringsCopied : Boolean = false;private function copyStrings() : Void{//if for some reason the HTTPService is slow to load this may need to re-execute, hence the extra checks and repeated callsif (!stringsCopied){var strings : Object = mx.core.Application.application.validatorStrings.StringValidator;if (strings != null){for (var i in strings){this[i] = strings[i];}stringsCopied = true;}}}public function doValidation(value) : Void{copyStrings();super.doValidation(value);}}

You could basically copy this code and do it for any validator that already exists. I’m sure there is a better way to optimize the copyStrings approach to avoid the repetitive calls (for example you could put the if check in the doValidation method so you pay the price of a compare instead of a function call and THEN a compare), but I think it’s not a big deal.

And now finally, how do you use this lovely new Validator? Just use the normal validator tag and change the namespace to point to your local version.

TADA!

The story of why Validators are architected in such a way that this is difficult is a story for another time :-)