Consumer-side boot engine for compile-time .po -> BEAM catalogs.
This module is the OPT-IN front door a consuming application calls — once, in its
own start/2, BEFORE starting its supervision tree — to register every catalog
that the rebar3_erli18n build-time codegen baked into generated BEAM modules.
A project that uses no compiled catalogs never calls register/1, never loads a
carrier module, and sees ZERO behavioural change: runtime .po loading via
erli18n:ensure_loaded/3,4 stays the default.
What a "carrier" is
The build-time codegen emits one generated module per catalog. Each carrier:
- has a module name prefixed
erli18n_cc_(so discovery can find it from the consumer app's own module list without a registry); - carries the marker attribute
-erli18n_compiled_catalog(...)(so a module that merely shares the prefix is never mistaken for a carrier); - exports
catalog/0returning anerli18n_server:compiled_spec()— the ALREADY-parsed entries plus an ALREADY-compiledPlural-Formsrule. Boot does NO parse and NO plural compile: the heavy work happened at build time.
How registration works
register/1 resolves the carriers from the consumer app's OWN
application:get_key(App, modules) list (NOT a global scan), confirms each
carrier's marker, reads every catalog/0, and hands ALL specs to
erli18n_server:register_compiled_many/1 in a SINGLE call — one serialized
write, the same critical section the .po loader uses. The catalogs then serve
through the lock-free erli18n_server:lookup_* hot path, untouched by this
module.
Registration is idempotent (it reuses ensure_loaded semantics): a catalog
already loaded — by a prior register/1, by erli18n:ensure_loaded/3, or by
reload/3 — reports {ok, already} and is NOT overwritten.
Failure modes (loud, never silent)
- The app atom is wrong or the app is not loaded
(
application:get_key/2->undefined): crashes witherror({erli18n_compiled_app_not_loaded, App}). This is a programming error (boot ordering / wrong atom), so it fails fast rather than degrading. - The app IS loaded but no carriers are discovered (its module list is empty, or
it has modules but none are confirmed carriers): emits ONE
?LOG_WARNINGtaggederli18n_compiled_no_carriersand returns[]. It is never a silent no-op — a missing-codegen misconfiguration is surfaced.
See erli18n:register_compiled_catalogs/1 (the documented facade door),
erli18n_server:register_compiled_many/1, and erli18n_server:compiled_spec().
Summary
Functions
Confirms that Mod is a genuine compiled-catalog carrier.
Discovers the confirmed compiled-catalog carrier modules of application App.
Reads the compiled spec a confirmed carrier Mod carries.
Registers every compiled catalog carried by application App and returns the
per-catalog install result.
Functions
Confirms that Mod is a genuine compiled-catalog carrier.
Ensures Mod is loaded (code:ensure_loaded/1) and then checks that it declares
the -erli18n_compiled_catalog(...) marker attribute. Returns true only when
both hold; a module that cannot be loaded, or that shares the erli18n_cc_
prefix but lacks the marker, returns false (and is skipped, never registered).
See discover/1.
Discovers the confirmed compiled-catalog carrier modules of application App.
Reads App's OWN module list via application:get_key(App, modules), keeps the
modules whose name is prefixed erli18n_cc_, and confirms each one is a genuine
carrier (confirm_catalog/1). Returns the confirmed carrier modules (possibly
[]).
Crashes with error({erli18n_compiled_app_not_loaded, App}) when
application:get_key/2 returns undefined — i.e. App is not loaded. That is a
programming/boot-ordering error (a wrong atom, or discovery running before the
app is loaded), so it fails fast rather than masking the misconfiguration as an
empty result.
See register/1 and confirm_catalog/1.
-spec read_catalog(module()) -> erli18n_server:compiled_spec().
Reads the compiled spec a confirmed carrier Mod carries.
Performs the single dynamic Mod:catalog() apply, returning the
erli18n_server:compiled_spec() — the ALREADY-parsed entries plus the
ALREADY-compiled Plural-Forms rule — that register/1 hands to
erli18n_server:register_compiled_many/1. Call only on a module already
confirmed by confirm_catalog/1.
See register/1 and erli18n_server:compiled_spec().
-spec register(atom()) -> [{erli18n_server:domain(), erli18n_server:locale(), erli18n_server:ensure_result()}].
Registers every compiled catalog carried by application App and returns the
per-catalog install result.
Resolves the carrier modules from App's OWN application:get_key(App, modules)
list, confirms each carrier's marker, reads every catalog/0, and installs ALL
specs through erli18n_server:register_compiled_many/1 in a SINGLE serialized
write — boot does NO .po parse and NO plural compile. Returns
[{Domain, Locale, ensure_result()}]: a freshly installed catalog reports
{ok, NumEntries}, one already present reports {ok, already} (idempotent).
Call this ONCE, in the consuming app's start/2, BEFORE its supervision tree
starts, so the catalogs are live before any worker can look one up.
%% my_app_app.erl
start(_Type, _Args) ->
_ = erli18n:register_compiled_catalogs(my_app),
my_app_sup:start_link().Failure modes:
error({erli18n_compiled_app_not_loaded, App})ifAppis not loaded (wrong atom, orregister/1ran before the app was loaded) — a loud crash, seediscover/1.- ONE
?LOG_WARNING(erli18n_compiled_no_carriers) and[]ifAppis loaded but carries no compiled catalogs — never a silent no-op.
See discover/1, read_catalog/1, and erli18n_server:register_compiled_many/1.