Localized number formats for forms

Often your language is not english and therefore the locale settings for decimals are not . for decimals and , for thousands, but the opposite, for example.
If you do not allow localized input/output in your CakePHP apps it often times confuses users. So it is wise to do so.

You want to convert the localized values to the internal number format on save() and vice versa on find() prior to populating the form on edit.

I use my NumberFormat behavior to accomplish all those things in a clean way.

Important: This is only for form fields. Do not use to generally format your find() results. That’s what the View helpers like Time and Number are for. You need to format and output those values in a localized form in the view as needed using their methods.

Setup

I assume you already got the Tools Plugin up and running.
You can either attach the behavior statically:

public $actsAs = array('Tools.NumberFormat' => array('output' => true, ...));

or you can dynamically load it at runtime for the specific add/edit actions with custom options (usually the better way for customization):

$this->ModelName->Behaviors->load('Tools.NumberFormat', array('output' => true, ...));

Next you should set a global default localization pattern. You can use Configure:

$config['Localization'] = array(
    'thousands' => '.',
    'decimals' => ',',
);

You can also use the system setting

setlocale(LC_NUMERIC, 'de_DE.utf8', 'german');

in combination with localeconv set to true. But this often times can have some side-effect else-where.

Basic usage

For add actions we don’t need any output, so we simply use

$this->ModelName->Behaviors->load('Tools.NumberFormat');

For edit actions we also need the localized output:

$this->ModelName->Behaviors->load('Tools.NumberFormat', array('output' => true, 'fields' => array('custom_float_field')));

We also want the behavior to convert the field custom_float_field here.

Advanced usage

Multiply

If you want your users to input percentages as interger values (0 … 100 instead of 0.0 … 1.0), you can use multiply:

$this->ModelName->Behaviors->load('Tools.NumberFormat', array('multiply' => 0.01));

This would convert 50 to 0.5 on save and undo it back to 50 on read (with output set to true, of course!).

Strict

My experience is, that too lose validation for decimals can result in a mess. Often times, the resulting price in our database was 100 times the inputted value just because the person used . or a invalid combination of multiple ., , or both. You can easily prevent this by expecting a valid localized value using strict:

$this->ModelName->Behaviors->load('Tools.NumberFormat', array('strict' => true));

This would not allow . (dot) as decimal if your localized decimal is currently , (comma) – among other things.

Before

Sometimes you do not need/want to validate your data, use before to delay the converting:

$this->ModelName->Behaviors->load('Tools.NumberFormat', array('before' => 'save'));

This will then execute on save() even if you set $validate to false.

Tips

If you already loaded your behavior statically, and you want to change the settings, use unload() prior to load():

public function edit() {
    $this->ModelName->Behaviors->unload('NumberFormat');
    $this->ModelName->Behaviors->load('Tools.NumberFormat', $myNewConfig);
}

Note: to unload a behavior, do not use the Plugin. prefixed syntax here.

For more examples and use cases see the test case.

Question

How do you manage to provide localized forms? Clearly CakeNumber itself is not enough here.

5.00 avg. rating (93% score) - 1 vote

2 Comments

  1. Hi Marc,

    thanks for your work with the NumberFormatBehavior, it’s well done. I got it up running without the

    $config['Localization'] = array(
        'thousands' => '.',
        'decimals' => ',',
    );

    Where should i add these lines of code? In the AppController?

    I suggest to link the Tools-Plugin at Github in the article – just to find it faster.

    Cheers,
    Florian

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.