I second this. And for all the generous support that comes with it!
TLDR; I plan to delay releases of ex_cldr libraries based upon CLDR 44 that is due to drop in October by 6-8 weeks to complete some refactoring and ready ex_cldr_person_names for release. Please let me know if this causes you an issue.
CLDR version 44 will be out of beta soon and my usual practise is to ensure that ex_cldr and related libraries are updated and available the same day.
This time I propose to hold off a new release cycle for a few weeks in order to complete the following:
- ex_cldr_person_names which requires some rework given the revised specification for CLDR 44. I think this is going to be a very useful library so I also want to make sure it passes all the conformance tests at 100%.
- Update ex_cldr_units to separate localisation from unit manipulation. And in particular to refactor the whole algebra engine so it works correctly. This was a kind of experimental feature but it shouldn’t be.
- Add an integration test suite that is basically an app that configures all locales with all backends. This will detect, at least at a compile time level, any incompatibilities.
- Fix or close the 9 outstanding issues agains the approx. 40 repositories in the family.
I expect this will take about 6 to 8 weeks to complete. I am looking for feedback as to whether this is ok for you. If you have any immediate need for an interim release of a CLDR 44-based version then please let me know. I have done preliminary integration of the last few betas and there aren’t any blocking issues so far.
Many thanks to everyone that has been supporting this endeavour which is now in its 8th year and which continues to see community adoption (its going to hit 3m downloads this week. The first 1m took 5 years, the second took 2 years and the third just about a year).
I’ve published new versions of several of the ex_cldr_* libraries in the last 24 hours to remove any warnings on Elixir 1.16. Some of the less-used libraries I still need to review.
If anyone finds any Elixir 1.16 warnings being emitted for any ex_cldr_* library, please do open an issue - I would be greatly appreciated.
My goal is to always have zero regressions, errors or warnings on Day #1 when a new Elixir release is officially published.
CLDR 45 was released on April 17th and now the updated ex_cldr libraries have been published. This release cycle has minimal changes to the underlying locale data.
New Person Name Formatting
This release cycle also sees the introduction of a new Person Name Formatting library, unsurprisingly called ex_cldr_person_names. This continues the efforts of the CLDR team to simplying localisation of applications to create a more personal and engaging experience.
I had the opportunity to introduce this new library at the Sydney Elixir Meetup and a video of that presentation is available on Youtube. The associated slides are also available in Powerpoint format and PDF format.
There is also a Livebook you can experiment with:
Elixir Mix Interview on CLDR and ex_cldr
The team at Elixir Mix kindly invited me to their podcast which was great fun. The podcast episode is now available online.
What’s next?
Attention now turns to the following priorities:
-
Fix adding custom units in
ex_cldr_units. This is a long-standing issue that has been very difficult to pin down. It’s the next immediate priority. -
Fix (actually, properly implement) the arithmetic operators in
Cldr.Unit.Math. I really made a mess of this implementation and it simply doesn’t do the right thing. I will get this fixed next. -
Add an implementation of the new Message Format 2 to ex_cldr_messages. This time it looks like the CLDR community, including the big players, are committed to standardising on this message format.
-
Revisit collations. It’s time I got this properly done.
Other library updates
As usual, several ex_cldr-related libraries have been updated. Here’s the brief changlog entries for the key libraries:
ex_cldr
-
Update to CLDR 45.0 data.
-
Adds
Cldr.validate_locale!/2. Thanks to @jarrodmoldrich for the suggestion. -
Add decimal separator and grouping separator to the currency data for each locale. In some rare cases, like the currency CVE in the locale pt-CV the currency symbol is placed where decimal separator is normally placed. The same can apply for the grouping separator although it appears no locale uses this field.
-
Adjust the
Inspectprotocol implementation fort:Cldr.LanguageTag.t/0types. When the language tag is resolved to a CLDR locale then the output is executable code. For example:
iex> MyApp.Cldr.Locale.new!("en-US")
MyApp.Cldr.Locale.new!("en-US")
-
Fix dialyzer warnings. Thanks to @Munksgaard for the PR. Closes #220. Also fixes
:underspecswarning and the:underspecsdialyzer flag is now configured. -
Add configuration for the new ex_cldr_person_names backend module generator.
ex_cldr_numbers
-
Update to CLDR 45.0 data.
-
Support currency formatting when the given currency in a given locale uses a specific symbol that replaces the decimal separator. The only known example is the Cape Verde escudo. A formatted example formatting 20 CVE is
20$00.
ex_cldr_dates_times
-
Fix formatting with formats that have may have pluralization like the
:MMMMWand:ywformats. -
Fix
:underspecsfor dialyzer. -
Update to CLDR 45.0 data.
-
Adds support for formats that have both unicode whitespace and ascii whitespace versions. The option
:preferis added toCldr.DateTime.to_string/3. The default isprefer: :unicode. The optionprefer: :asciiis included for backwards compatibility of older applications. The formats that provide both:unicodeand:asciiversions can be seen from the results ofCldr.DateTime.Format.date_time_available_formats/2.
ex_cldr_units
-
Fixes canonical unit name formation. More units can now be resolved to a base unit and therefore compared and converted with other units. The primary change is to add elimination of common factors in “per” units.
-
Add conversion support for Beaufort. This conversion is non linear and therefore cannot be expressed using the normal unit conversion method.
-
Cldr.Unit.Math.mult/2andCldr.Unit.Math.div/2now support scalar values as the second argument. Its therefore now possible to sayCldr.Unit.Math.mult(Cldr.Unit.new!(:meter, 10), 2).
ex_money
-
Money.sum/2default exchange rates is always%{}even if the exchange rate server is not running. Thanks to @haste for the report. Closes #168. -
When parsing numbers, use the localized number system separators where they exist. Thanks to @pshoukry for the report. Closes #167.
-
Surface errors when starting the exchange rates retrieveer. Thanks to @danschultzer for the PR. Closes #165.
-
Update to CLDR 45.0 data.
-
Return structured errors for
Money.ExchangeRates.latest_rates/0,Money.ExchangeRates.historic_rates/1,Money.ExchangeRates.last_updated/0andMoney.ExchangeRates.latest_rates_available?/0when the exchange rates retrieval process is not running.
Thank you for your great work, as always!
Thanks @kip for the fix!
I am so embarrassed that I’ve thought for years that CLDR is just short for calendar and there’s no need for me to look into that…
After a long period of reflection, I have decided to embark on ex_cldr version 3.0. There are several objectives and there’s a full writeup in the github discussion.
The primary changes will be:
- Rename to
localizeto better reflect the libraries intent and to improve discoverability - Remove the “backend” approach to hosting CLDR data (this is the main architectural change that will enable runtime locale configuration and improve compilation times significantly)
- Separate core CLDR data ingestion into a standalone library
- Simplify library packaging and testing. For example, I might combine current
ex_cldr,ex_cldr_numbers,ex_cldr_currencies,ex_cldr_dates_times,ex_cldr_calendars,ex_cldr_units(formatting only),ex_cldr_territoriesandex_cldr_languagesinto a single library. - Use canonical Elixir error and exception handling
- Leverage the emerging type system
- Build a new translator library and ecosystem
- Re-implement units of measure
- Fill out the missing pieces of CLDR (collations, transforms, …)
I will continue to support and update ex_cldr 2.x for the foreseeable future - and of course until at least localize 1.0 is ready. Which I anticipate to be towards the end of 2025.
The user-facing API should also be sufficiently similar for a shim library to be able to aid migration to the new set of libraries.
Enjoying your continuously work on ex_cldr a LOT!
Last week, Unicode released CLDR 47. Any affected ex_cldr_* libraries have now been updated and are available for use.
Major features of CLDR 47
- [Not available in ex_cldr yet] MessageFormat has advanced from Final Candidate to Stable. For details, see below.
- New locales:
- Core data for Coptic (cop), Haitian Creole (ht)
- Locale data for 11 English locales and Cantonese (Macau) (yue_Hant_MO)
- Updated time zone data to tzdata 2025a
- RBNF (Rule Based Number Formatting): Number spellout data improvements for multiple languages
- Assorted transforms improvements
- Updated and revised population data
- [No library in ex_cldr] Addition of derived emoji annotations that were missing: emoji with skin tones facing right
- Fixes to make the ja, ko, yue, zh datetimeSkeletons useful for generating the standard patterns
- Improved date/time test data
Updated libraries
- ex_cldr
- ex_cldr_numbers
- ex_cldr_calendars
- ex_cldr_dates_times
- ex_cldr_locale_display
- ex_cldr_person_names
- ex_cldr_units
- ex_money
- ex_cldr_messages
Several add-on calendars and calendar-related libraries are also updated and several moved to 1.0.0 release status:
- ex_cldr_calendars_persian
- ex_cldr_calendars_coptic
- ex_cldr_calendars_lunisolar
- ex_cldr_calendars_ethiopic. This library now includes both variants of the Ethiopian calendar: standard and Amete Alem.
- ex_cldr_calendars_format
- ex_cldr_calendars_composite
Other ex_cldr_* libraries do not currently appear to require any update (I’m still testing the various add-on calendars). Please open issues if you find a bug!
Thanks for all the amazing work you do on the ex_cldr_* libraries!
I’ve published updates to ex_cldr, ex_cldr_numbers, ex_cldr_units and ex_cldr_calendars to be compatible with the upcoming Elixir 1.19 and also with OTP 28.
If I’ve done my job properly then you won’t see any differences with one small exception. The updated OTP re module upon which Elixir’s Regex module is based, is not 100% compatible with previous versions. For the ex_cldr_* libraries there are two considerations:
- Assigning compiled regular expressions to a module attribute and then
unquotingthem into code is not supported. There are quite a few of these inex_cldr_*and they’ve all been replaced with runtime compilation. I have not assessed if this creates material performance impacts but I suspect not. - The version of Unicode used by the old/current
:remodule is Unicode 7.0 from 2014 (thanks @adamu). The Bitcoin symbol₿was introduced in Unicode 10 in 2017. The consequence is that if you’re formatting money with a bitcoin currency symbol, the formatting will be ever so slightly different between OTP 27 and OTP 28.
CLDR 48 was released last week which has triggered new versions of several of the core ex_cldr libraries. Here’s a summary of newsworthy changes:
Ex_cldr
Bug Fixes
- Parsing language tags with the
und(unknown) language now correctly substituted for a known language name if one can be found inCldr.Locale.likely_subtags/0. For example:
iex> Cldr.validate_locale("und-TW")
{:ok, TestBackend.Cldr.Locale.new!("zh-Hant-TW")}
-
The
:gettext_locale_namefield of at:Cldr.LanguageTag.t/0is now set exactly as returned fromGettext.known_locale_names/1. Previously it was being tranformed to a BCP 47 format locale (replacing “_” with “-”). That is no longer the case. -
Territory containment was previously not considering nested containers and is now doing so. For example, territory “019” (Americas) includes “419” (Latin America and the Carribbean) but “419” was not appearing in the list for “019” since CLDR categorises “grouping” territories separately. This should not affect any consumers, only library writers.
Enhancements
-
Update to CLDR 48 data.
-
Add
Cldr.Locale.Match.best_match/2andCldr.Locale.Match.match_distance/3to implement the CLDR Language Matching algorithm. This is a more formal and testable approach that the previous mechanism. This new function will be use to implement better matching between a known CLDR locale name and supported Gettext locale names. Therefore it is possible that thegettext_locale_namefield of at:Cldr.LanguageTag.t/0may change with this release.
Cldr Numbers
-
Adds
Cldr.Number.to_ratio_string/3to format numbers as a rational string. Numbers can also be formatted as an integer and a ratio. Since different rendering systems (browsers, terminals etc) reproduce ratios with differing fidelity, there are options to format the ratio as a precomposed fraction, super- and subscript numerals separated by a fraction slash or normal numerals separated by a fraction slash. -
For number systems other than
Latn, the:prefer: :precomposedandprefer: :super_suboptions have no effect.
Examples
| Function call | Result |
|---|---|
Cldr.Number.to_ratio_string(0.75) |
{:ok, "3⁄4"} |
Cldr.Number.to_ratio_string(0.75, prefer: :super_sub) |
{:ok, "¹⁄₂"} |
Cldr.Number.to_ratio_string(0.75, prefer: :precomposed). |
{:ok, "½"} |
Cldr.Number.to_ratio_string(0.875, prefer: [:precomposed, :super_sub]) |
{:ok, "⅞"} |
Cldr.Number.to_ratio_string(3.875, prefer: [:precomposed, :super_sub]) |
{:ok, "3\u2060⅞"} |
Cldr.Number.to_ratio_string(0.923, prefer: [:precomposed, :super_sub]) |
{:ok, "⁹⁄₁₀"} |
Cldr Dates Times
Breaking Changes
- Changes in the implemementation of time zone formatting may result in different (but semantically the same) strings for formats that include time zone information. See Bug Fixes below.
Bug Fixes
-
Fixes the generation of time zone formats to be in accordance with CLDR Time Zone Names. This is particularly true of the following time zone formats:
- Generic non-location format (eg “Pacific Time”, “PT”)
- Generic partial location format (eg “Pacific Time (Canada)”)
- Generic location format (eg “France Time”, “Adelaide Time”)
- Specific non-location format (eg “Pacific Standard Time”, “PST”)
- Localized GMT format (eg “GMT+03:30”, “Гринуич+03:30”, “GMT+?”)
-
Fixes
Cldr.DateTime.Relative.to_string/3when the relative value is an integer of2or-2and the locale provides a specific word for that value. For example, in English there are words for 1 day in advance or behind (“today” and “yesterday”). When specifing 2 days ago, English has no specific word so the result is “2 days ago”. In German the result is “vorgestern”. Similary for the English “in 2 days”, the German result will be “übermorgen”.
Enhancements
-
Updates to CLDR 48 data.
-
Adds
Cldr.DateTime.Format.gmt_unknown_format/1to return a string representing an unknown GMT offset. -
Adds
style: :attoCldr.DateTime.Relative.to_string/2. This allows formatting of string like “tomorrow at 3:30 PM”. The default isstyle: :standard. -
Significant improvement in support of date time skeletons. Skeletons are a flexible way to express desired formatting in a locale-indepdendent way.
-
Support is added for the “j”, “J” and “C” input skeleton symbols which will be substituted with the locales preferred hour formats. These changes also improve implicit format generation derived from whatever date and time data is passed to the
Cldr.Date.to_string/3,Cldr.Time.to_string/3,Cldr.DateTime.to_string/3functions.
Cldr Calendars
Primarily bug fixes to the Julian calendar and enhancements to make the Julian calendar configurable (in history, the Julian calendar new year has celebrated at different times of year).
Bug Fixes
-
Fix
year/1,month/2anddays_in_month/2for Julian calendars. -
CLDR 48 fixes the dates for some Japanese eras.
Enhancements
-
Use CLDR 48 calendar data. The primary change for calendars is to the era dates for the Japanese calendar.
-
Adds
use Cldr.Calendar.Julian, new_year_starting_month_and_day: {month_of_year, day_of_month}to allow modelling Julian calendars that don’t start on January 1st. -
Adds
Cldr.Calendar.convert/2which converts either a date or a date range to another calendar.
Cldr Calendars Japanese
Bug Fixes
-
Several era dates were fixed in CLDR 48.
-
The
calendar_year/3callback was fixed to return the regnal year (year of the reign of the then-current emperor) as expected by date time formatting.
Other library updates
These are updates primarily to be compatible with CLDR 48
- ex_cldr_units
- ex_cldr_routes including REAME update to recognise the original work by @BartOtten
- ex_cldr_locale_display
- ex_cldr_person_names
- ex_cldr_territories
Evolution of ex_cldr
Work continues on localize as the “version 3” of ex_cldr. The objective is to get the initial release covering the core, numbers, calendars and date/times released in the first quarter of 2026.
It’s my intention to continue to maintain ex_cldr and friends for as long as there is usage and bug reports. However I hope this will be the last update for ex_cldr to use a new version of CLDR data so that I can focus more time on localize. That scenario can be revised if required around March 2026 with CLDR 49.
CLDR 48
Additional units were added for scientific contexts (coulombs, farads, teslas, etc.) and for English systems (fortnights, imperial pints, etc.)
Looking forward to having my app say “2.6 fortnights ago” ![]()
Seriously though, thank you Kip for all your work on these libraries. It’s such a service to the rest of us.
Thanks for the kind words @nathanl. And just for fun using your example:
iex> fortnights = Cldr.Unit.new!(2.6, :fortnight)
Cldr.Unit.new!(:fortnight, "2.6")
iex> Cldr.Unit.to_string fortnights, format: :ratio
{:ok, "2 3⁄5 fortnights"}
iex> Cldr.Unit.to_string fortnights, format: :ratio, prefer: :precomposed
{:ok, "2 ⅗ fortnights"}
iex> Cldr.Unit.to_string fortnights, format: :ratio, prefer: [:precomposed, :super_sub]
{:ok, "2\u2060⅗ fortnights"}
(there aren’t any translations for “fortnight” in CLDR 48 that I’m aware of).
Whoa! I’ve totally overlooked the :ratio format, so cool!
Thanks for the update on Cldr, and for all your amazing contributions to the community, Kip.
Now you mention it, I don’t think it’s documented properly on Cldr.Unit.to_string/2. You can see more about this in Cldr.Number.to_ratio_string/2 and Cldr.Number.Formatter.Ratio
Once again an impressive release; keeping the quality high.
Psssst, wanna buy a Hex-package name? Just kidding, I was planning to write a localize suite and initial work was published on Hex but life got in the way. I’ll transfer the namespace to you before Q1 2026.
Any roadmap or architecture we can see for Localize?
Ps. thanks for the attribution !
Another great release Kip! You are an real asset to the community!
Is this maybe supposed to return {:ok, “3/4”}? Or is this some sort of imperial thing I’m too euro to understand?![]()
D’Oh, thanks for pointing that out. Fixed now in the original post. Documentation error - not a code error ![]()






















