Локализация числовых/денежных данных
При разработке более-менее крупного проекта возникает проблема с локализацией числовых/денежных данных. В этой заметке расскажу о мучениях при использовании Symfony framework, Sonata Admin Bundle и клиентской части. Но сначала о сути проблемы, так как на первый взгляд она не очень очевидная.
Итак, допустим у нас есть проект, который на стороне сервера (php/шаблонизатор) рендерит числовые/денежные данные. Они должны отображаться в соответствии с установленной локаллю. Пользователь может вводить данные (в своем представлении). При этом данные могут обрабатываться еще и на клиентской части (javascript). К примеру, в большинстве стран Европы, кроме Великобритании и Ирландии десятичный разделитель запятая, в Великобритании и Ирландии - точка. Естественно, что пользователь с Германии будет вводить данные с раздилителем - запятою.
Если на стороне сервера представлять в нужном формате (с помощью NumberFormatter, к примеру), то как быть на стороне js тогда? Нету нативной кроссбраузерной локализации числовых данных. JS работает только с числами в международном формате (к примеру, 12.32), независимо от пользовательских настроек браузера.
Но обо всем по порядку. Сначала о серверном решении.
При использовании Sonata Admin Bundle стоит использовать поля типу money, number - у них уже реализован механизм локализации. Для дополнительных настроек локализации можно еще установить sonata-project/intl-bundle. При использовании вне админки стоит использовать либо twig расширение для intl (твиг фильтры localizednumber, localizedcurrency), либо писать свою обертку поверх intl NumberFormatter и использовать ее. Второе полезно в том случае, если вам лень передавать каждый раз в localizedcurrency код (EUR, к примеру), и если на всем проекте используется какая-то одна валюта. Для этого напишем сервис, который использует NumberFormatter, который будет получать с конфигурации валюту (EUR) и, если нужно, кастомный символ валюты. Удобнее создать отдельные форматтеры для чисел и currency. На стороне клиента используем accounting.js:
composer require bower-asset/accounting.js
(Если используете assetic plugin для composer)
Для него нужно будет написать конфигурацию:
accounting.settings = _.defaults({
number: {
decimal: ',',
thousand: ' ',
precision: 2,
grouping: 3
},
currency: {
decimal: ',',
thousand: ' ',
precision: 2,
grouping: 3,
symbol: '€',
format: '%v %s'
},
}, accounting.settings);
Дальше, при любом выводе чисел оборачиваем:
accounting.formatNumber(12.32)
Обратное преобразование (например, для рассчетов):
accounting.unformat('12,32')
В случае с деньгами:
accounting.formatMoney(12.32)