Release erli18n-v0.7.0
Opt-in compile-time .po->BEAM catalog registration. A consuming
app can now register catalogs that the rebar3_erli18n build-time codegen baked
into generated BEAM modules — ALREADY parsed, with the Plural-Forms rule
ALREADY compiled — so boot does no .po parse and no plural compile. This
release is additive under the 0.x SemVer policy: one new facade function, one
new erli18n_server API, two new public types, and one new internal module. The
runtime ensure_loaded/3,4 path remains the default; a project that uses
none of the compiled-catalog features sees zero change — no new application-env key, and the read hot
path (erli18n_pt_store:get_singular/4 / get_plural_form/5) is byte-for-byte
unchanged.
Added
erli18n:register_compiled_catalogs/1— the opt-in boot-time counterpart
ofensure_loaded/3for catalogs frozen into the release at build time. Given
the consuming application's atom, it discovers that app's generated carrier
modules (theerli18n_cc_*modules therebar3_erli18n compileprovider
emits), reads each ALREADY-parsed entry list plus its ALREADY-compiled plural
rule, and installs them all in a single serialized write through the same
erli18n_servermailbox every other catalog write uses. Returns
[{Domain, Locale, ensure_result()}]— a fresh catalog reports
{ok, NumEntries}, one already present reports{ok, already}. The honest
framing is no.poparse / no plural compile at startup — NOT zero-load:
registration still installs each catalog (the cost is the install, not the
parsing), so it composes with, rather than replaces, runtime loading.-
Coexistence + idempotency. Registration is additive and idempotent: it
composes withensure_loaded/3(a project may compile some catalogs and load
others at runtime), and a catalog already present — by a prior call or by
ensure_loaded/3— reports{ok, already}and is not overwritten. -
Placement contract (boot ordering). Call it once, in the consuming
app'sstart/2, before its supervision tree starts, so every catalog is
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().
-
It crashes with
error({erli18n_compiled_app_not_loaded, App})whenAppis
not loaded (a wrong atom or a wrong boot order — a programming error, surfaced
loudly), and emits a single?LOG_WARNINGand returns[]whenAppis
loaded but ships no compiled catalogs.
-
erli18n_server:register_compiled_many/1— the server-side install of a
list ofcompiled_spec()values. It stages each pre-built catalog (the
already-parsed entries + the baked header) and commits them through the
existing serializedcommit_manypath, reusing the same staging/idempotency
machinery asensure_loaded. The embedded'$header'keeps the catalog's
real baked divergence (solookup_header/2still reports it), but the
staged value the install path receives carriesdivergence => noneand
fuzzy_skipped => 0, so boot-time registration installs silently — the
vs-CLDR plural-divergence warning a diverging catalog would raise is emitted
once at build time by the codegen provider, not on every boot.erli18n_server:baked_header/0andcompiled_spec/0public types.
baked_header()is the build-time-computed header of a compiled catalog (its
pre-compiledplural, the rawplural_rawfallback, the sourcepo_path, the
pre-computed vs-CLDRdivergence,fuzzy_included, andnum_entries);
compiled_spec()is the{Domain, Locale, Entries, baked_header()}tuple a
generated carrier'scatalog/0returns. They are exported so the separate
rebar3_erli18ncodegen can construct the exact termregister_compiled_many/1
installs across the published{deps, [erli18n]}boundary.erli18n_compiled— the internal discovery + registration helper
register_compiled_catalogs/1delegates to. It finds an application's generated
carrier modules by their-erli18n_compiled_catalog([...])attribute, applies
each module'scatalog/0, and hands the resultingcompiled_spec()list to
erli18n_server:register_compiled_many/1.kernel+stdlibonly.erli18n_server:default_max_bytes/0anddefault_max_entries/0are now
exported. They return the resource-bound defaults (max_po_bytes, 16 MiB;
max_po_entries, 500,000), each read fromapplication:envand narrowed to a
non_neg_integer() | infinity. Exporting them makes the runtime the single
source of truth for the caps: therebar3_erli18nbuild-time codegen consults
them to reject an oversized.po(by file size, before reading it whole) or an
over-cap entry count (after parse) at BUILD time — the same bounds
ensure_loaded/3,4enforces on a runtime load — so a compiled catalog can
never carry more than a runtime load would accept. Set either env key to
infinityto disable that cap.
Notes
- No behavior change for runtime-loaded catalogs. Lookup, fallback,
idempotency, telemetry, and theensure_loaded/3,4/reload/3,4contracts are
all unchanged.ngettext(and family) still returns the plural form; no
existing arity changes meaning or return shape. The only way to reach the new
path is to callregister_compiled_catalogs/1explicitly.