Implements sigils for ICU MessageFormat 2 messages.
Two sigils are provided:
~M— validates and canonicalises an MF2 message at compile time. Useful for static messages that need a stable canonical form (for example, as keys in a translation table or seed data).~t— compile-time translation sigil. Combines Gettext lookup with MF2 interpolation. Elixir-style#{expr}interpolations become MF2{$name}placeholders with bindings derived automatically from the interpolated expression. Requires the calling module to opt in viause Localize.Message.Sigils, backend: MyApp.Gettext.
Compile-time validation
Both sigils parse the message at compile time. A syntax error fails
compilation and the raised CompileError points at the exact line
and column of the error inside the sigil body, adjusted to the
sigil's source location so editors can jump directly to the
offending character (including inside multi-line heredoc sigils).
Using ~t
defmodule MyAppWeb.HomeLive do
use Localize.Message.Sigils,
backend: MyApp.Gettext,
sigils: [domain: "messages"]
def render(assigns) do
~H"""
<h1>{~t"Hello, #{@user.name}!"}</h1>
"""
end
endAt compile time, ~t"Hello, #{@user.name}!" becomes:
Gettext.Macros.dpgettext_with_backend(
MyApp.Gettext,
"messages",
nil,
"Hello, {$user_name}!",
%{user_name: @user.name}
)The msgid stored in the .po file is the MF2-canonical form with
{$name} placeholders, so translators can use MF2 features
(selectors, formatters, markup) per-locale.
Gettext backend requirements
The configured backend MUST use MF2-aware interpolation:
defmodule MyApp.Gettext do
use Gettext.Backend,
otp_app: :my_app,
interpolation: Localize.Gettext.Interpolation
endWithout this, {$name} placeholders are returned literally because
the default Gettext interpolation only recognises %{name}.
Binding key derivation
Binding names are derived from the interpolated expression:
#{name}— variable →name.#{@count}— Phoenix assign →count.#{fruit.name}— dot access →fruit_name.#{String.upcase(x)}— remote call →string_upcase.#{key = expr}— explicit key. Always overrides automatic derivation. Use this for collisions or complex expressions.
Identical expressions interpolated twice share a single binding. Different expressions that derive the same key raise a compile error.
Summary
Functions
Configures the calling module to use the ~t sigil.
Handles the sigil ~M for ICU MessageFormat 2 message strings.
Handles the sigil ~t for compile-time MF2 translation.
Functions
Configures the calling module to use the ~t sigil.
Options
:backend— the Gettext backend module. Required.:sigils— a keyword list of sigil-level options::domain— default Gettext domain. The default is:default, which resolves to the backend's configured default domain.:context— default Gettext message context. The default isnil.
Examples
use Localize.Message.Sigils,
backend: MyApp.Gettext,
sigils: [domain: "messages"]
Handles the sigil ~M for ICU MessageFormat 2 message strings.
It returns a canonically formatted string without interpolations and without escape characters, except for the escaping of the closing sigil character itself.
A canonically formatted string is pretty-printed by default returning a potentially multi-line string. This is intended to produce a result which is easier to comprehend for translators.
Modifiers
p(default) — pretty-print the message with indentation.u— return a non-pretty-printed (compact) string.
Examples
iex> import Localize.Message.Sigils
iex> ~M(An ICU message)
"An ICU message"
Handles the sigil ~t for compile-time MF2 translation.
Rewrites Elixir #{expr} interpolations as MF2 {$name} placeholders
with bindings derived from the interpolated expressions. The resulting
msgid is canonicalised and routed through Gettext, which performs
both the translation lookup and the MF2 interpolation (via the
configured Localize.Gettext.Interpolation module).
The calling module must opt in with:
use Localize.Message.Sigils, backend: MyApp.GettextModifiers are reserved for a future release and are rejected at compile time.
See the Localize.Message.Sigils moduledoc for binding-derivation
rules and a full example.