Commerce 2.x Stories: Currencies
Welcome back. Last week we discussed our efforts around libraries, Composer, dependent modules. This week it’s time to jump into Commerce itself. Let’s start with currencies.
The very first Commerce 2.x story discussed our efforts to replace the hardcoded Commerce 1.x currency list with one generated from an external source. It also discussed our efforts to improve currency formatting. This work resulted in the commerceguys/intl library. It contains a list all currencies in the world, as well as translated currency names and symbols for over 200 languages. This list gets updated and expanded every 6 months (according to the CLDR release schedule).
On the Commerce side, currencies are configuration entities. An import form is provided that allows users to create currencies from library definitions. The importer also takes care of creating translations (name&symbol) for all available languages. When the user adds new languages to the site, Commerce will automatically import currency translations for those new languages as well.
The import form, allowing users to select from a list of 157 active currencies provided by CLDR.
The library also provides a mapping of countries to currencies, which will allow us to automatically import the right currency based on the store country.
Since currencies are now entities, the user can customize the imported data and translations, or define custom currencies, all without going into code.
The price field stores amounts and their currency codes. The amounts are no longer stored in cents, like in Commerce 1.x. Instead, we store them in a decimal column, then use PHP's builtin bcmath extension to perform calculations without precision errors. We're in the process of developing a helper class to make this API easier to use.
The field widget and formatter are locale aware and use the intl library's NumberFormatter to do the heavy lifting. A locale is a combination of a language code and a country code. For example, fr-CA, indicating French in Canada. This concept is missing from Drupal core, which provides only languages. But languages are not specific enough, since currencies are formatted differently in French (France) and French (Canada). Locales are thus implemented in Commerce, by combining the active language with the resolved country (the one in the user’s profile, for example). This whole process is dynamic and can easily be hooked into.
Notice the placeholder, it says “9,99”, telling me that I should use the comma as a decimal separator, since I’m viewing the admin pages in French. This has been a long standing Commerce 1.x feature request, implemented in 2.x thanks to the intl library.
The available currencies are listed in the dropdown next to the amount field. As expected, this dropdown is hidden when there’s only one currency available.
Number formats used for formatting are loaded from the library and cached inside Drupal. An alter event is provided that allows the formatting rules to be modified.
The formatter also supports Arabic, Arabic extended, Bengali, Devanagari digits. Here's $20.99 when viewing the page in Arabic:
This works both ways, if you enter ٢٠٫٩٩ into the widget, the number will be correctly parsed and stored as 20.99.
We've significantly improved our currency handling, by introducing a better currency list, an admin UI, improved widgets and formatters. Furthermore, we've shared these improvements with the wider community through our commerceguys/intl library.
See you next week, when we'll discuss an awesome new concept: stores.
This is incredible work,
This is incredible work, Bojan - so much better than my .cif files in Ubercart and half-implementation in Commerce 1.x. Glad you're on the job. ; )