erli18n_compiled (erli18n v0.8.0)

Copy Markdown View Source

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/0 returning an erli18n_server:compiled_spec() — the ALREADY-parsed entries plus an ALREADY-compiled Plural-Forms rule. 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 with error({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_WARNING tagged erli18n_compiled_no_carriers and 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

confirm_catalog(Mod)

-spec confirm_catalog(module()) -> boolean().

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.

discover(App)

-spec discover(atom()) -> [module()].

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.

read_catalog(Mod)

-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().

register(App)

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}) if App is not loaded (wrong atom, or register/1 ran before the app was loaded) — a loud crash, see discover/1.
  • ONE ?LOG_WARNING (erli18n_compiled_no_carriers) and [] if App is loaded but carries no compiled catalogs — never a silent no-op.

See discover/1, read_catalog/1, and erli18n_server:register_compiled_many/1.