Localize is the next generation localisation library for Elixir. Think of it as ex_cldr version 3.0. The first version will be released before the end of March 2026 and is under active development.
The goals and objectives are documented on Github, with the headline objectives being:
No “backend” architecture, CLDR data will be stored in :persistent_term.
No compile-time configuration. Runtime all the way.
Dynamic loading of locales. No more compile-time configuration of supported locales. Downloading and storage/access will be pluggable. This may suit organisations with specific security policies or running Localize on devices with limited connectivity or resources.
Simplified library packaging. Localize will have everything except web-oriented and SQL oriented modules which will go in their own libraries.
A Phoenix-based locale explorer will be available to make it easier and clearer to see what capabilities are possible with Localize (enabled by CLDR). At a later stage the locale explorer will also be hosted on a publicly available web server.
Note this will not be a drop-in replacement. However the public API will be very very similar with the main breaking change being the removal of any backend argument.
Feedback, suggestions all very welcome either here, or on GitHub discussions.
Making Localize a good Nerves citizen is definitely in scope too. No compile time configuration will help. But primarily by supporting configurable locale sources and configurable locale storage and access methods. (thanks for the kind words).
I did do some very loose and informal performance comparisons some time ago and the performance seemed comparable. I will certainly be doing comapative performance testing when I get further along the development path.
Localize will officially launch next Monday. I’ve got a full week of launch activity planned. And a shiny new blog at elixir-localize.com where I’m committed to spending more time writing about they why and how localisation matters and how localize can make it almost trivial to write localised Elixir applications.
Each day next week there is something launching:
Monday is the core localize library. It compiles faster than ex_cldr, runs faster, uses less memory, has simpler packaging (nearly everything in one package).
Tuesday is localize_web that includes accept-header parsing and locale setting, localised routes and some HTML helpers.
Wednesday is the launch of localize_person_names for formatting names in a localised manner; in formal, informal and other ways. And the launch of Calendrical which is a consolidated set of calendars now including Gregorian, Julian, Islamic (four variants), Hebrew, Persian, Indian, Buddhist, Composite and user definable. And a few more I probably missed.
Thursday its time for some new fun things. localize_phonenumber that wraps libphonenumber for parsing and formatting phone numbers and localize_address for parsing free-form address data and then formatting its using the OpenCageData templates.
Friday is the “one more thing” release. It’s called Intl and its an API wrapper for Localize that maps very closely to the Javascript Intl API. This should make it easier for developers who are more front-end savvy to adopt localisation in Elixir more easily.
I’ll be posting more long form content at elixir-localize.com as the week progresses.
Eight years into developing ex_cldr and friends I think I can say confidently that localize is better in every way. I hope you’ll try it out, give feedback, open issues and help me make that claim a reality.
Day #1 of Localize launch week and Localize version 0.1.0 is now live on hex. It brings 8 years of experience of building ex_cldr together with a revised and simplified architecture, more capabilities, greater CLDR conformance, faster performance and better developer experience.
My hope is that localize will encourage more developers to build localisation into their apps from the beginning. Whether you plan to deliver an app in a single locale or many, localize just makes it easier. In a single library you get just about everything CLDR can deliver to make localisation and personalisation a pleasurable experience.
It’s a 0.1.0 release but its build on years of experience from ex_cldr. It will look very similar to developers who have used ex_cldr, but there are tons of changes and additions too. So I expect a few rough edges and I would really encourage anyone jumping on board to open issues or start a discussion. I think it won’t take too long to get to a 1.0 release.
I’ll also be blogging about the changes in localize, about localisation in general and about tips and trick on the Elixir Localize blog. There’s an RSS feed too. The blog is built using static_blog that I build for the site - but you might find it useful too.
Design goals met
No backend architecture, runtime everything.
Compiles and runs faster than ex_cldr. A performance report details the performance improvements.
Consistent error returns - always {:error, exception} with exception messages that can be translated through gettext
If you’re migrating from ex_cldr, the migration guide will help. The overall API will look very familiar, with the most obvious change being that there is never a backend function argument or option.
And of course you don’t need to juggle multiple libraries, deal with compile-time configuration or generate backend modules.
Functional Highlights
Current CLDR v48.2 locale data with lazy runtime loading from ETF files cached in :persistent_term. No compile-time backend configuration required.
Number formatting — integers, decimals, percentages, currencies, ranges, and rule-based number formats (RBNF) including Roman numerals and CJK ideographs.
Date, time, and datetime formatting using CLDR calendar patterns with :short, :medium, :long, and :full styles, custom skeleton patterns, and interval formatting.
Unit formatting with plural-aware patterns, SI/binary prefixes, compound units, measurement system conversion, custom unit registration, and Localize.Unit.Operators for natural arithmetic (km + m).
List formatting with locale-appropriate conjunctions, disjunctions, and unit list styles. Per-element formatting via Localize.Chars.
ICU MessageFormat 2 (MF2) parser and interpreter with custom function registry, offset selection, JSON interchange, and bidirectional text support.
Gettext integration — Localize.Gettext.Interpolation provides MF2-based interpolation for Gettext backends.
Localize.Chars protocol — polymorphic locale-aware formatting with built-in implementations for 14 types and Any fallback to Kernel.to_string/1.
Currency metadata, ISO 4217 validation, and territory-to-currency mapping.
Display names for territories, languages, scripts, calendars, and full locale display names per the CLDR algorithm.
Unicode Collation Algorithm (UCA) with CLDR locale-specific tailoring for 97 languages, including digraph expansion and script reordering.
Tomorrow’s launch is localize_web which combines accept-language-aware locale setting, localised routing and some (primitive, some would say legacy) HTML helpers. That will also include setting up for Phoenix.
I’ve been myself an ex_cldr user for years in multiple applications that had heavy localization requirements. I think this wouldn’t have been possible without this library (or at least it would have required a lot of effort and pain).
I’m glad to see the move from compile time backend to runtime loading. I think the only downside of ex_cldr was that it increased compilation times noticeably. The benefits outweighed the downside already, but now there is no downside anymore, so using it is a no-brainer.
Thanks for being a “customer”! Knowing that people use the libraries is very fulfilling and motivating.
I really do hope localize meets - and in some areas exceeds - your expectations. And I hope you’ll give it a try and let me know what needs to be better. I personally thinks its much stronger, more CLDR-conformant, easier to maintain (even though its a much larger single repository) and all around more polished.
Feedback, bug reports, suggestions, new requirements are always very very welcome.
Day #2 of Localize launch week introduces localize_web with support for:
Features
Locale Discovery which detects the user’s locale from the accept-language header, query parameters, URL path, session, cookies, hostname TLD, or custom functions. Configuration is a lot simpler than the current ex_cldr_plugs since we no longer have to deal with ex_cldr backends. And you can now specify more than one Gettext backend if you need to set locale on several of them.
Session Persistence which stores the discovered locale in the session for subsequent requests and LiveView connections.
Compile-time Route Localization which translates route path segments using Gettext at compile time and generates localized routes for each configured locale.
Verified Localized Routes with the ~q sigil providing compile-time verified localized routes that dispatch to the correct translated path based on the current locale.
HTML Form Helpers which generate <select> tags and option lists for currencies, territories, locales, units of measure, and months with localized display names. A bit old school but useful to some.
Getting started
If you’re just getting started with localisation of a web app, you might find the following guides helpful:
Thats it for today, short and sweet. Tomorrow its launch time for localize_person_names, one thats quickly becoming a favourite of mine. Hope you’ll like it too.
Thank you for your work! I just updated the first application from ex_cldr to localize, and it was an easy upgrade. Everything works perfectly, and the new API is nice and clean.