Localization of numeric/monetary data

When developing a medium to large project, there is a problem with localizing numeric/money data. In this note, I will talk about the difficulties of using the Symfony framework, Sonata Admin Bundle, and the client-side. But first, let's discuss the essence of the problem, as it is not very obvious at first glance.

So, let's say we have a project that renders numeric/money data on the server side (php/template engine). They should be displayed according to the set locale. The user can enter data (in their own representation). At the same time, the data can be processed on the client side (javascript). For example, in most European countries, except for the UK and Ireland, the decimal separator is a comma while in the UK and Ireland it is a period. Naturally, a user from Germany will enter data with a comma separator.

If we represent the data on the server side in the desired format (using NumberFormatter, for example), what about the client-side? There is no native cross-browser localization for numeric data. JavaScript only works with numbers in the international format (e.g., 12.32), regardless of the user's browser settings.

But let's go through the details. First, let's discuss the server-side solution.

When using the Sonata Admin Bundle, it is worth using fields of type money, number - they already implement the localization mechanism. For additional localization settings, you can also install the sonata-project/intl-bundle. If used outside of the admin panel, it is advisable to use either the twig extension for intl (twig filters localizednumber, localizedcurrency) or write your own wrapper for NumberFormatter and use it. The latter is useful if you don't want to pass the currency code (e.g., EUR) each time and if the project uses a single currency. For this purpose, we will write a service that uses NumberFormatter, which will receive the currency (EUR, for example) from the configuration and, if necessary, a custom currency symbol. It is more convenient to create separate formatters for numbers and currency. On the client side, we will use accounting.js:

composer require bower-asset/accounting.js

(If using the assetic plugin for composer)

You will need to write configuration for it:

accounting.settings = _.defaults({
    number: {
        decimal: ',',
        thousand: ' ',
        precision: 2,
        grouping: 3
    },
    currency: {
        decimal: ',',
        thousand: ' ',
        precision: 2,
        grouping: 3,
        symbol: '€',
        format: '%v %s'
    },
}, accounting.settings);

Then, wrap any number output with:

accounting.formatNumber(12.32)

 Reverse conversion (e.g., for calculations):

accounting.unformat('12,32')

In the case of money:

accounting.formatMoney(12.32)