Compile-time verified translations for Phoenix.
Like Phoenix.VerifiedRoutes does for paths, the ~t sigil verifies at
compile time that every message is translated to every configured locale,
and fails compilation otherwise:
use VerifiedTranslations
~t"Welcome back"
~t"Hello #{name}" # source "Hello %{name}", binding name
~t"Hello #{@user.name}" # source "Hello %{user_name}"Each message lives in its own JSON file holding all locales, so parallel branches, worktrees and coding agents never conflict on translation files:
// priv/translations/default/hello-name-<128-bit hash>.json
{
"msgid": "Hello %{name}",
"fi": "Hei %{name}",
"sv": "Hej %{name}"
}Translations are inlined into the calling module at compile time, so there is no runtime lookup table and no Gettext backend to recompile. Editing a JSON file recompiles only the modules that use that message.
Configuration
config :verified_translations,
locales: ["en", "fi", "sv"],
source_locale: "en", # language of the text in ~t
path: "priv/translations", # relative to the project root
create_missing: true # write stub files for new messagesuse VerifiedTranslations accepts the same keys (plus :domain, default
"default") to override the config per module.
Locale
The current locale is per process, like Gettext's:
VerifiedTranslations.put_locale("fi")
VerifiedTranslations.with_locale("sv", fn -> ~t"Hello #{name}" end)Unknown or unset locales render the source text.
Summary
Functions
Returns the locale of the current process.
Configured locales, including the source locale.
Sets the locale of the current process.
Translates the text to the current locale, verified at compile time.
Translates a plural message, choosing the CLDR plural category of count
for the current locale.
Runs fun with locale as the current locale, restoring the previous one.
Functions
Returns the locale of the current process.
Falls back to Gettext's locale (of gettext_backend if given) when
put_locale/1 was not called, so apps that already set the Gettext locale
keep working. Returns nil without Gettext.
@spec known_locales() :: [String.t()]
Configured locales, including the source locale.
@spec put_locale(String.t()) :: :ok
Sets the locale of the current process.
When Gettext is available, also sets Gettext's global locale (and the
configured :gettext_backend's locale) so gettext/ngettext calls stay
in the same language.
Translates the text to the current locale, verified at compile time.
Interpolated variables become bindings named after their access path:
#{name} and #{@name} are %{name}, #{@user.name} is %{user_name}.
Translates a plural message, choosing the CLDR plural category of count
for the current locale.
tn(length(files), one: "%{count} file", other: "%{count} files")
tn(@n, one: "#{@user.name} has one item", other: "#{@user.name} has %{count} items")The forms must be exactly the source locale's CLDR categories (one and
other for English). %{count} is the first argument; other values are
interpolated like in ~t. The message file holds one object of forms per
locale, verified to have exactly that locale's categories:
{"kind": "plural", "msgid": "%{count} files",
"fi": {"one": "%{count} tiedosto", "other": "%{count} tiedostoa"}}~t"#{count} file(s)"N (gettext_sigils compatible) is the same with one
source text for every category.
Ordinals use CLDR's ordinal categories and are stored as "kind": "ordinal":
tn(place, ordinal: true, one: "%{count}st", two: "%{count}nd", few: "%{count}rd", other: "%{count}th")
Runs fun with locale as the current locale, restoring the previous one.