Skip to content

Release erli18n-v0.7.0

Choose a tag to compare

@github-actions github-actions released this 01 Jul 23:18

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
    of ensure_loaded/3 for catalogs frozen into the release at build time. Given
    the consuming application's atom, it discovers that app's generated carrier
    modules (the erli18n_cc_* modules the rebar3_erli18n compile provider
    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_server mailbox 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 .po parse / 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 with ensure_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's start/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}) when App is
      not loaded (a wrong atom or a wrong boot order — a programming error, surfaced
      loudly), and emits a single ?LOG_WARNING and returns [] when App is
      loaded but ships no compiled catalogs.

  • erli18n_server:register_compiled_many/1 — the server-side install of a
    list of compiled_spec() values. It stages each pre-built catalog (the
    already-parsed entries + the baked header) and commits them through the
    existing serialized commit_many path, reusing the same staging/idempotency
    machinery as ensure_loaded. The embedded '$header' keeps the catalog's
    real baked divergence (so lookup_header/2 still reports it), but the
    staged value the install path receives carries divergence => none and
    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/0 and compiled_spec/0 public types.
    baked_header() is the build-time-computed header of a compiled catalog (its
    pre-compiled plural, the raw plural_raw fallback, the source po_path, the
    pre-computed vs-CLDR divergence, fuzzy_included, and num_entries);
    compiled_spec() is the {Domain, Locale, Entries, baked_header()} tuple a
    generated carrier's catalog/0 returns. They are exported so the separate
    rebar3_erli18n codegen can construct the exact term register_compiled_many/1
    installs across the published {deps, [erli18n]} boundary.
  • erli18n_compiled — the internal discovery + registration helper
    register_compiled_catalogs/1 delegates to. It finds an application's generated
    carrier modules by their -erli18n_compiled_catalog([...]) attribute, applies
    each module's catalog/0, and hands the resulting compiled_spec() list to
    erli18n_server:register_compiled_many/1. kernel + stdlib only.
  • erli18n_server:default_max_bytes/0 and default_max_entries/0 are now
    exported.
    They return the resource-bound defaults (max_po_bytes, 16 MiB;
    max_po_entries, 500,000), each read from application:env and narrowed to a
    non_neg_integer() | infinity. Exporting them makes the runtime the single
    source of truth for the caps: the rebar3_erli18n build-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,4 enforces on a runtime load — so a compiled catalog can
    never carry more than a runtime load would accept. Set either env key to
    infinity to disable that cap.

Notes

  • No behavior change for runtime-loaded catalogs. Lookup, fallback,
    idempotency, telemetry, and the ensure_loaded/3,4 / reload/3,4 contracts 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 call register_compiled_catalogs/1 explicitly.