Root supervisor of the erli18n application tree.
What it is and what it solves
This is the library's only supervisor: it is the process started by
erli18n_app:start/2 and the root of the OTP tree. Its sole
responsibility is to keep alive the single process that underpins the
translation runtime:
erli18n_server— the worker/writer. It serializes all writes (load/reload/unload of catalogs) through itsgen_serverand is the ONLY process that mutatespersistent_term.
The reader goes through neither this supervisor nor the worker:
lookup_* reads straight from persistent_term in the calling process,
without blocking. This tree exists only to keep the writer alive, not to
mediate the read hot path.
Mental model (why a single worker is enough)
The catalogs are stored in persistent_term (see erli18n_pt_store),
which is owned by the runtime, not by any process. A persistent term
is not destroyed when the process that installed it dies. So a crash of
the worker loses NOTHING: every loaded catalog survives untouched, and
the restarted worker resumes serializing writes against the surviving
terms.
Because nothing owns the catalogs, the tree needs no table owner, no
heir, and no handoff, and therefore no ordering constraint between
children: a single worker under one_for_one is enough.
Fixed configuration
The restart intensity is {intensity => 5, period => 10} (at most 5
restarts in 10 seconds before the supervisor gives up) and is hardcoded
— it is not configurable via
application:get_env/2. The single child is permanent with
shutdown => 5000.
When a dev touches this module
Almost never directly. Library consumers call
application:ensure_all_started(erli18n), which brings up the
application and, through it, this supervisor. Touching things here only
makes sense when altering the tree's topology (adding/removing a child,
changing strategy or intensity).
Quickstart
1> {ok, _Started} = application:ensure_all_started(erli18n).
{ok,[erli18n]}
2> whereis(erli18n_sup) =/= undefined.
true
3> [Id || {Id, _Pid, _Type, _Mods} <- supervisor:which_children(erli18n_sup)].
[erli18n_server]The list in _Started may vary: erli18n.app.src declares telemetry
in optional_applications, so if the optional app is present and has not
yet been started, it shows up alongside (e.g.:
{ok, [telemetry, erli18n]}). kernel and stdlib are already up and
never enter that list. That is why the example matches {ok, _Started}
instead of comparing the output literally.
Key functions
start_link/0— entry point, called byerli18n_app:start/2.init/1— thesupervisorcallback; defines the strategy, intensity, and the single child spec.
Summary
Functions
The supervisor:init/1 callback — defines the shape of the
supervision tree.
Starts the root supervisor, registered locally as erli18n_sup.
Functions
The supervisor:init/1 callback — defines the shape of the
supervision tree.
Called once by start_link/0 (via supervisor:start_link/3). It
receives the [] argument passed in start_link/0 and returns
{ok, {SupFlags, ChildSpecs}}. It is purely declarative: it builds maps
and has no side effects nor error paths of its own.
SupFlags
strategy => one_for_one— a crash of the worker restarts only the worker. There is no owner to keep alive ahead of it and no ordering constraint, because the catalogs live in runtime-ownedpersistent_termand survive a worker crash untouched.intensity => 5,period => 10— at most 5 restarts in 10 seconds; on exceeding it, the supervisor gives up and propagates the failure upward. Fixed values, not configurable.
ChildSpecs
A single child: erli18n_server — permanent, worker,
shutdown => 5000. The catalog writer. In its init/1 it has nothing
to claim (no ETS table, no handoff): the catalogs are already in
persistent_term, so init/1 just returns an empty state.
Return
Always {ok, {SupFlags, ChildSpecs}}. There is no error clause: a
malformed ChildSpec would be a programming bug detected by the
supervisor while validating the tree, not a runtime failure mode.
Example
1> {ok, {SupFlags, Children}} = erli18n_sup:init([]).
2> maps:get(strategy, SupFlags).
one_for_one
3> [maps:get(id, C) || C <- Children].
[erli18n_server]See also start_link/0, which installs this tree.
Starts the root supervisor, registered locally as erli18n_sup.
The tree's entry point: it is what erli18n_app:start/2 calls. It
delegates to supervisor:start_link/3 with {local, ?MODULE}, which
makes the supervisor respond to the name erli18n_sup (usable in
supervisor:which_children/1, whereis/1, etc.) and invokes init/1
to assemble the child.
Return
{ok, Pid}— the supervisor and itserli18n_serverchild started successfully.{error, {already_started, Pid}}— a process is already registered undererli18n_sup(the application is already up). Starting the application twice via OTP does not reach here; this only shows up in manual calls.{error, {shutdown, _}}— the child failed in its owninit/1. The supervisor unwinds what came up and propagates the error.
It crashes (linked to the caller) only if there is a programming error
in the construction of the ChildSpecs in init/1 — which, in this
module, is static and does not depend on external input.
Example
1> {ok, Pid} = erli18n_sup:start_link().
{ok,<0.215.0>}
2> Pid =:= whereis(erli18n_sup).
true
3> erli18n_sup:start_link().
{error,{already_started,<0.215.0>}}The third call demonstrates the failure mode described above: with the
supervisor already registered under erli18n_sup, a second manual
start_link/0 returns {error, {already_started, Pid}} with the Pid
of the existing process — without tearing down or restarting anything.
See also init/1 for the definition of the tree this function installs.