The documentation page on Prices describes how a Price consists of a number and a currency code, both stored as strings. So, for example, suppose the total price for an order is "$464,230.13". That value would be stored in the database as "464230.130000" for the total price number and "USD" for the total price currency.
How exactly do we get from "464230.130000" and "USD" to the "$464,230.13" we see displayed in the order summary?
First, we determine the correct format to use for the price. This is the job of the Currency and Number format Repositories. The format specifies things like how many digits to use for the fractional part of the price and which character to use to separate the fractional part and how the currency should be displayed.
Second, we apply that Number format to the raw price data. This is the job of the Currency and Number Formatters.
The Commerce Price module's CurrencyRepository
and NumberFormatRepository
services provide the formats used by the CurrencyFormatter
and NumberFormatter
services.
The currency repository provides formats in the form of CommerceGuys\Intl\Currency\Currency
value objects, loaded from the Currency entities that have been created for the site. See the Currencies documentation for a list of the Currency configuration entity properties that are loaded into the value objects. In addition, the locale
property is set based on the language of the Currency entity. The repository doesn't support loading currencies in a non-default locale, since it would be imprecise to map $locale
to Drupal's languages.
The number format repository service constructs a CommerceGuys\Intl\NumberFormat\NumberFormat
value object for a given locale. These value objects have the following properties:
Property | ID | Description | Example value |
---|---|---|---|
Locale | locale | The locale. | 'en_US' |
Decimal pattern | decimal_pattern | The number pattern used to format decimal numbers. | '#,##0.###' |
Percent pattern | percent_pattern | The number pattern used to format percentages. | '#,##0%' |
Currency pattern | currency_pattern | The number pattern used to format currency amounts. | '¤#,##0.00' |
Accounting currency pattern | accounting_currency_pattern | The number pattern used to format accounting currency amounts. | '¤#,##0.00;(¤#,##0.00)' |
Numbering system | numbering_system | The numbering system, one of Arabic-Indic, Extended Arabic-Indic, Bengali, Devanagari, or Latin. Default is Latin. | 'latn' |
Decimal separator | decimal_separator | The decimal separator. The default is '.' . |
',' |
Grouping separator | grouping_separator | The grouping separator. The default is ',' . |
' ' |
Plus sign | plus_sign | The plus sign. The default is '+' . |
'+' |
Minus sign | minus_sign | The minus sign. The default is '+' . |
'-' |
Percent sign | percent_sign | The percent sign. The default is '%' . |
'٪' |
The number format definitions are specified directly in the CommerceGuys\Intl\NumberFormat\NumberFormatRepository::getDefinitions()
method. For example, here is the definition for for the default 'en' locale:
'en' => [
'numbering_system' => 'latn',
'decimal_pattern' => '#,##0.###',
'percent_pattern' => '#,##0%',
'currency_pattern' => '¤#,##0.00',
'accounting_currency_pattern' => '¤#,##0.00;(¤#,##0.00)',
],
The NumberFormatDefinitionEvent
event can be used to customize the number format definition for any locale. The event is dispatched by the Commerce Price NumberFormatterRepository
service after the number format definition is proccessed by the CommerceGuys\Intl\NumberFormat\NumberFormatterRepository
service.
For example, the format definition for the German ('de'
) locale specifies that ','
should be used for the decimal separator, and '.'
should be used for the grouping separator. Suppose you'd like to switch those for your site. Here's an example event subscriber that does that.
<?php
namespace Drupal\custom_module\EventSubscriber;
use Drupal\commerce_price\Event\PriceEvents;
use Drupal\commerce_price\Event\NumberFormatDefinitionEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Modifies the number format definition for the 'de' locale
*/
class NumberFormatDefinitionEventSubscriber implements EventSubscriberInterface {
public static function getSubscribedEvents() {
$events[PriceEvents::NUMBER_FORMAT][] = ['onNumberFormat'];
return $events;
}
public function onNumberFormat(NumberFormatDefinitionEvent $event) {
$definition = $event->getDefinition();
if ($definition['locale'] == 'de') {
$definition['decimal_separator'] = ',';
$definition['grouping_separator'] = '.';
$event->setDefinition($definition);
}
}
}
Don't forget to include a service definition for your Event Subscriber in your custom module's services.yml
file and clear caches.
The Commerce Price module's CurrencyFormatter
and NumberFormatter
services extend Internationalization Library services of the same names to provide better defaults. The locale is set to the current locale, based on the current country and language for the Drupal user. The default locale is 'en'
. And for the currency formatter, the maximum fraction digits is set to 6 (the storage max), and the rounding mode is set to 'none'
, to show prices as-is.
Both formatters rely on the logic provided by the CommerceGuys\Intl\Formatter\FormatterTrait
trait to format numbers using locale-specific patterns.
Found errors? Think you can improve this documentation?
edit this page