phoenix_kit_templates
View SourceLocale-aware rendering for outbound message content — email, push, Telegram, SMS, the in-app inbox. One renderer for every channel, so localized and customizable copy is built the same way everywhere instead of once per channel.
PhoenixKit.Templates.render(
"new_login_alert",
%{subject: gettext("New login to your account"), text: gettext("Hi %{email}, …")},
%{"ip_address" => ip},
locale: "uk",
paths: [Application.app_dir(:my_app, "priv/phoenix_kit_templates")]
)
#=> %{subject: "…", text: "…", html: nil}Two layers
A template has a default, shipped by the package that sends the message, and an optional override, shipped by the host application.
The caller passes the default in already localized. That split is the whole design: translation belongs to the sending package's Gettext backend, and reaching for one here would make this package depend on it. A shipped default expressed as one file per locale would fork the same sentence seven ways and drift; expressed as a Gettext call it rides the extraction and translation pipeline that package already runs.
Files are how a host overrides — a host writes one or two languages, not seven — and how chrome-bearing HTML gets authored.
Resolution runs per part, independently:
1. host override · recipient locale text.en-GB.txt
2. host override · base language text.en.txt
3. host override · locale-less text.txt
4. the default the caller passed inIndependence matters: a host that overrides only html keeps the package's
translated subject and text rather than having to restate them.
Override layout
<root>/<name>/<part>.<locale>.<ext>
<root>/<name>/<part>.<ext>subject and text are .txt; html is .html. Overrides live in the host
application, which compiles separately from this package — so they are read at
runtime and cached in :persistent_term, the absence of a file included.
Parts
subject, text and html, named for what they are rather than for email:
push uses subject-as-title plus text, Telegram and SMS use text alone, the inbox
uses text. html is genuinely optional — a text-only template is the normal
case, not a stub.
Placeholders
{{variable}}, with optional inner whitespace, matching the syntax the
database-backed email templates used so exported content carries over unchanged.
Atom and string keys are both accepted.
An unbound placeholder is left verbatim, not blanked, and never raises. Both
are wrong, but a visible {{user_emial}} announces the typo in a preview or a
test, while a silent empty string reads as finished copy and ships — and a
message with one flawed line still beats a message that never arrives. Callers
that want it to be an error ask missing_variables/4 up front.
No runtime dependencies
Deliberate: phoenix_kit depends on this package, so anything pulled in here
lands upstream of the entire tree.
License
MIT