Capping of a hectic few days is the announcement of ex_cldr_messages version 2.0. This significant update adds support Unicode MessageFormat 2 (MF2) as well as the existing ICU Message Format for Elixir, integrated with the ex_cldr ecosystem.
MessageFormat 2 is the next generation of ICU message formatting, designed to be more expressive, extensible and easier to work with than the legacy format. It introduces a clearer syntax with explicit declarations, functions, and pattern matching, while maintaining the same goals: enabling translatable, locale-aware messages with built-in support for plurals, gender selection, and formatted values.
Full gettext integration
ex_cldr_messages fully integrates with gettext. Messages are extracted into .POT files with mix gettext.extract as usual. They are interpolated at runtime as usual. The only difference is the message format.
Opt-in NIF
A high performance NIF is included and is opt-in for usage. There are full details of the small differences between the Elixir implementation and the NIF.
Examples
The message formatting syntax is much richer than straight interpolation and its specifically intended to make life easier for developers and to encourage the use of messages with enough context to make it easier for translators to create better translations.
MF2 currently defines declarative formatting of numbers, dates, times, units of measure and currency (money). ex_cldr_messages supports them all.
Simple Messages
A simple MF2 message is wrapped in {{ }}:
iex> Cldr.Message.format! "{{Hello, world!}}"
"Hello, world!"
iex> Cldr.Message.format! "{{Hello, {$name}!}}", %{"name" => "Alice"}
"Hello, Alice!"
Number Formatting
MF2 provides :number, :integer, :percent and :currency functions:
# Note the localised number formatting
iex> Cldr.Message.format! ".input {$count :number}\n{{You have {$count} items.}}", %{"count" => 1042}
"You have 1,042 items."
iex> Cldr.Message.format! ".input {$pct :percent}\n{{Score: {$pct}}}", %{"pct" => 0.85}
"Score: 85%"
Pattern Matching with .match
The .match declaration selects a variant based on the value of one or more selectors:
iex> Cldr.Message.format! """
...> .input {$count :number}
...> .match $count
...> 1 {{You have one item.}}
...> * {{You have {$count} items.}}
...> """, %{count: => 5}
"You have 5 items."
The real power of the format comes when multiple matches are required. In this case the translator can more easily understand the different combinations of matches and more easily see the intent behind the messages to be translated.
.input {$pronoun :string}
.input {$count :number}
.match $pronoun $count
he one {{He has {$count} notification.}}
he * {{He has {$count} notifications.}}
she one {{She has {$count} notification.}}
she * {{She has {$count} notifications.}}
* one {{They have {$count} notification.}}
* * {{They have {$count} notifications.}}
Here the input variables are clear; the various combinations of them is clear and the resulting messages are clear.






















