Optional Elli middleware that negotiates the request locale and sets it before
the handler runs — the Elli counterpart to erli18n_cowboy.
elli is an optional dependency (declared via optional_applications, like
telemetry): erli18n does not require it and the published package still builds
on kernel + stdlib alone. This module only runs when an Elli application
installs it via elli_middleware, so Elli is present whenever the code runs.
elli_request is therefore an externally-provided runtime module, not a build
dependency (the same situation as the rebar3 host API in rebar3_erli18n_host):
its calls are confined to a small seam (see the "elli_request seam" section
below) and the default build's xref/dialyzer/eqwalizer are kept clean by
suppressions scoped to exactly those external edges (-ignore_xref + the root
{xref_ignores,...}, a function-scoped -dialyzer({no_unknown,...}), and a
% elp:ignore W0017 per call site). Every other call stays fully checked.
Install it
Add elli to your deps (erli18n does not pull it in), then list erli18n_elli
in the elli_middleware mods ahead of your real handler. Per-middleware
options are the second element of the {Mod, Args} pair:
application:ensure_all_started(erli18n),
{ok, _} = erli18n_server:ensure_loaded(my_domain, <<"pt_BR">>, "priv/.../my_domain.po"),
Config = [
{mods, [
{erli18n_elli, #{cookie_name => <<"locale">>, query_param => <<"lang">>}},
{my_callback, []}
]}
],
elli:start_link([{callback, elli_middleware}, {callback_args, Config}, {port, 8080}]).It runs as an elli_middleware pre-processor: preprocess/2 sets the locale on
the request process before your handler. The module deliberately exports only
preprocess/2, not handle/2: elli_middleware skips the handle phase for any
mod that does not export handle/2 (its ?IF_NOT_EXPORTED guard), so this
middleware never intercepts a real handler. A handle/2 -> ignore clause would
be unreachable dead code.
Options (the Args map, all optional)
| Key | Default | Meaning |
|---|---|---|
sources | [query, cookie, header] | precedence order, highest first |
query_param | <<"locale">> | query-string parameter carrying a locale |
cookie_name | <<"locale">> | cookie carrying a locale |
available | erli18n:loaded_locales/0 | the authoritative supported-locale set |
default | erli18n:default_locale/0 | fallback when nothing matches |
set_logger_metadata | true | also set logger process metadata #{locale => L} |
Default precedence is query > cookie > Accept-Language header > default, and
available/default are resolved per request. Negotiation, canonicalization,
and cookie parsing are delegated to erli18n_http / erli18n_negotiate. The
path source is Cowboy-only (Elli exposes the path as raw segments); listing it
here is a harmless no-op.
Per-process locale, the cross-process hazard, and Phoenix interop
Identical to the Cowboy adapter: Elli runs the middleware and handler in one
request process, so the locale set here is visible to the handler, but it is
not inherited across a spawn (worker pools, gen_servers, Task-style
spawns). See erli18n_cowboy for the full discussion of the hazard, the
mitigations (capture-and-re-set, pass explicitly, propagate logger metadata), and
the Phoenix / mixed-Elixir interop note — all of which apply unchanged here.
References
- Elli middleware: https://github.com/elli-lib/elli/blob/main/src/elli_middleware.erl
elli_request: https://elli.hexdocs.pm/elli_request.html
Summary
Types
Per-middleware options (the Args map). An alias of the framework-agnostic
erli18n_http_apply:options(): the Elli adapter adds no adapter-specific keys
(the path source is Cowboy-only — see the module docs). Every key is optional.
Stands in for the elli request record (a tuple). A module-local alias so the
default kernel + stdlib build needs no elli dependency.
Functions
elli_middleware pre-processor: negotiates the locale from the request, sets it
on the request process (and, by default, in logger metadata), and returns the
request unchanged so the chain continues.
Types
-type options() :: erli18n_http_apply:options().
Per-middleware options (the Args map). An alias of the framework-agnostic
erli18n_http_apply:options(): the Elli adapter adds no adapter-specific keys
(the path source is Cowboy-only — see the module docs). Every key is optional.
-type req() :: tuple().
Stands in for the elli request record (a tuple). A module-local alias so the
default kernel + stdlib build needs no elli dependency.
Functions
elli_middleware pre-processor: negotiates the locale from the request, sets it
on the request process (and, by default, in logger metadata), and returns the
request unchanged so the chain continues.
Req is the Elli request record; Args is the per-middleware options map (see
the module docs). The -behaviour(elli_handler) attribute is omitted on purpose
so the module compiles warning-free (under warnings_as_errors) when Elli is
absent; the contract is exercised by the test suite against the real framework.