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

+5
View File
@@ -11,6 +11,8 @@ test('explicit build inputs preserve browser-only defines, plugin order and loop
plugins: [plugin],
googleApiKey: 'browser-fixture',
cesiumToken: 'ion-fixture',
defaultLocale: 'fr',
secondaryLocale: 'en',
});
assert.equal(config.plugins[2], plugin);
assert.equal(config.server.host, 'localhost');
@@ -30,6 +32,9 @@ test('explicit build inputs preserve browser-only defines, plugin order and loop
assert.deepEqual(config.define, {
'import.meta.env.GOOGLE_MAPS_API_KEY': '"browser-fixture"',
'import.meta.env.CESIUM_ION_TOKEN': '"ion-fixture"',
// Locale pair (i18n): threaded like the other explicit browser inputs.
'import.meta.env.GEV_DEFAULT_LOCALE': '"fr"',
'import.meta.env.GEV_SECONDARY_LOCALE': '"en"',
});
assert.equal(
createBrowserViteConfig({ host: '0.0.0.0', port: '4800' }).server