kip

kip

ex_cldr Core Team

Cldr Messages - Localised message formatting following the MF2 spec with opt-in NIF

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.

More details

https://github.com/elixir-cldr/cldr_messages

Most Liked

kip

kip

ex_cldr Core Team

Done. Sloppy timing of me. The release is now properly tagged. Thanks for the prompt and the kind words.

LostKobrakai

LostKobrakai

With 2.0 I really need to try to use message format to see how I like it in practise. The lack of structure for plain gettext comes up every now and then in my projects of what I see others asking about. Message format is quite extensive and something one needs to learn – but it’s also not a homegrown approximation oneself might come up with.

mudasobwa

mudasobwa

Creator of Cure

Please git tag v2.0.0 so that backlinks from hex.pm work.

P. S. Amazingly done, as always.

Where Next?

Popular in Announcing Top

zorbash
I created Kitto a framework for dashboards inspired by Dashing. The distributed characteristics of Elixir and the low memory footprint...
New
tmbb
I’ve published the first version of my Makeup library. It’s a syntax highlighter for Elixir in the spirit of Pygments, Currently it highl...
New
josevalim
Yes, yet another parser combinator library! Most of the parser combinators in the ecosystem are either compile-time, often using AST tra...
159 19834 141
New
danschultzer
None of the current solutions worked well for me, so I went ahead and built a user management system from scratch. This project took far...
548 30338 241
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 44139 214
New
maltoe
Hello! Came here to announce ChromicPDF, a pet project PDF generator I’ve been working on for the past few months. Why another PDF gener...
New
zoltanszogyenyi
Hey everyone :waving_hand: Excited to join this forum - I am one of the founders and current project maintainers of a popular and open-s...
New

Other popular topics Top

Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New
saif
Hello everyone, Long time lurker first time poster here. I’ve recently begun working on Elixir full-time again! :raised_hands: It’s been...
New
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New

We're in Beta

About us Mission Statement