feat(i18n): locale core + English catalogs + parity gates

Add the i18n foundation: src/i18n/locale.js (locale resolution with a
configurable GEV_DEFAULT_LOCALE / GEV_SECONDARY_LOCALE pair, guarded
preference storage, one-shot ?lang= override, <html lang>/<html dir>
metadata) and src/i18n/index.js (catalog registry, t(), Intl formatters,
applyDocumentTranslations, persistLocaleAndReload).

English is the only shipped locale in this PR: the four en catalogs
(shell/cockpit/layers/setup) are the reference and the always-shipped
fallback, the built-in pair degenerates to en-only, and English output
is byte-identical to the strings these replace.

Parity gates land with the core (catalog key/placeholder/plural-shape
parity, markup coverage of every data-i18n* attribute, repair-pass
anchors pinning en byte-identity); additional locales ship as stacked
follow-up PRs through the recipe in docs/TRANSLATORS.md.

Rebased onto current main: the client defines live in build/vite.js /
server/standalone/vite.config.js as before.
This commit is contained in:
Meir Michanie committed 2026-10-01 12:26:30 +02:00
1 parent e7707d9a0f
commit bde1e430ef
12 files changed
+2702

No files matched your search

+14
View File
@@ -91,6 +91,20 @@ LL2_API_TOKEN=
# Optional: OpenSky credentials JSON file path (alternative to above)
# OPENSKY_CREDENTIALS_FILE=/path/to/credentials.json
# Optional: interface language pair (i18n). GEV_DEFAULT_LOCALE is the default
# UI locale; GEV_SECONDARY_LOCALE is the second locale offered in the dock
# language switch. English is ALWAYS shipped as the fallback catalog and is
# resolvable no matter the pair. Only 'en' ships in this PR: both values are
# validated against the shipped catalogs, so a secondary without a shipped
# catalog degenerates the switch to a single English button (and an invalid,
# unshipped, or degenerate pair falls back to that en-only shape with a
# dev-server-only console.warn). Later locale PRs re-open the pair — see
# docs/TRANSLATORS.md. CLIENT-EXPOSED (non-secret): both values reach the
# browser bundle via import.meta.env defines — do not put anything sensitive
# here.
# GEV_DEFAULT_LOCALE=en
# GEV_SECONDARY_LOCALE=es
# Optional: dev server config
PORT=4173
# Network binding. Default is localhost — the server is reachable ONLY from