
When dealing with i18n web applications the need to support localized number representations becomes obvious. Thus, users should be able to enter localized numbers such as “1.000.50 $” when for instance using the locale en_US or “1,000.50 $” when using de_AT. Now, when it comes to validating user input throughZend_Form Zend provides special validators to do so. As we want to store only normalized numeric values we need to convert user input from possible localized representations to their normalized counterparts.
Zend_Filter_LocalizedToNormalized to the rescue
Luckily, in order to convert localized representations to normalized versions Zend provides us with Zend_Filter_LocalizedToNormalized. The following example shows a form element that makes use of this filter:
$this->addElement(new Form_Element_Float('some_float', array( 'filters' => array('StringTrim', 'LocalizedToNormalized'), 'required' => false, 'label' => _('some float'), 'decorators' => array( 'viewHelper', array( 'Label', array('class' => 'label') ), 'Errors' ), 'validators' => array( new Form_Validate_FloatNormalized(), new Zend_Validate_Between(array('min' => 0, 'max' => 100)) ), 'id' => 'SomeFloat' )));
As you see I’ve used 2 filter: StringTrim and LocalizedToNormalized. Furthermore, I have used a custom form element type called Form_Element_Float:
class Form_Element_Float extends Zend_Form_Element_Text { public function render(Zend_View_Interface $view = null) { $content = parent::render($view); if (!is_numeric($this->_value)) { return $content; } return preg_replace('/value="' . $this->_value . '"/', 'value="' . ($this->_value != null ? Zend_Locale_Format::toNumber($this->_value, array('locale' => Zend_Registry::get('Zend_Locale'), 'precision' => 2)) : '') . '"', $content); } }
This custom form element makes sure that Zend renders numeric values correctly, i.e. in a localized manner, based on the user’s locale. Now that we have defined the filters and the view rendering, it is time to finally talk about the validator: Form_Validate_FloatNormalized. As you might hasve guessed correctly this validator is for float values. The idea behind it is to check the outcome of the filters described above. So, ideally Zend_Filter_LocalizedToNormalized successfully converted the raw user input into the corresponding normalized representation (1.000.50 to 1000.5). Now it is time for Form_Validate_FloatNormalized to actually validate the converted value:
class Form_Validate_FloatNormalized extends Zend_Validate_Float { public function isValid($value) { // do not validate parent as we want to check against *normalized* values only (ignoring any locale) try { if (Zend_Locale_Format::getFloat($value, array('precision' => 2, 'locale' => 'en_US')) !== (float) $value) { //normalized check only (use locale "en_US" as template pattern), i.e. allow only '.' as decimal seperator $this->_error(self::NOT_FLOAT); return false; } } catch (Zend_Locale_Exception $e) { $this->_error(self::NOT_FLOAT); return false; } return true; } }
Form_Validate_FloatNormalized does nothing more then rely on Zend_Locale_Format to convert the filtered (i.e. normalized) value to the expected value. If they are they same the input is correct, otherwise we set the corresponding error message. Using this approach you can be sure to detect any non-numeric inputs. Furthermore, you support all languages and their corresponding localized representations that Zend supports.