mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 10:53:59 +00:00
refactor(i18n): key messages by English text; add Russian
Replace the semantic keys with the English source text, so code reads as the
UI does and English needs no catalog: t('Save keys'), tn(count, one, other)
for plurals, msg() for text stored in data, and bare data-i18n* markup that
translates the element's own English. Other languages are one flat JSON each.
- Simplify src/i18n: drop namespaces, the configurable locale pair
(GEV_DEFAULT_LOCALE / GEV_SECONDARY_LOCALE) and its build defines. Locale
comes from ?lang=, the stored choice, then browser languages.
- Add scripts/i18n.mjs (npm run i18n): extracts every message from t/tn/msg
and markup, and fails on missing, unused, placeholder-mismatched or
wrongly-shaped plural entries. npm test runs it via catalog.test.mjs.
- Add Russian (src/i18n/locales/ru.json, 833 messages), started from the
community catalog in #312 and reviewed against the current English.
- Fix data-i18n placed on parents that would wipe their children (cockpit
LEVEL and local weather cells, first-run EXPLORE MANUALLY tile), and a
doubled degree sign in the TRACK ACQUIRED signal.
- Rewrite docs/TRANSLATORS.md for the new workflow; drop sections the earlier
merge resurrected in docs/CURRENT-STATE.md.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
dfbaeafbd8
commit
13efd411b6
112 files changed
+4419
-4856
No files matched your search
+20
-116
@@ -3009,124 +3009,28 @@ test:track` 43 tracking invariants · headless QA harnesses under
|
||||
> attribution 18/18). New modules: `src/data/{motionModel,aircraftMeta,aircraftClass,aircraftIcons,issPass,routePlausible,dataCredits}.js`.
|
||||
> The live runtime now declares 30 voice tools; the 17→20 count above is retained only as milestone history.
|
||||
|
||||
### Internationalization / catalog locales (September 2026)
|
||||
### Interface localization (October 2026)
|
||||
|
||||
The application-owned UI is fully extracted into message catalogs; **en**
|
||||
is the only shipped locale in this foundation state — additional locales
|
||||
land as stacked follow-up PRs, one per locale (Spanish first), each
|
||||
following the locale-addition recipe in
|
||||
[`docs/TRANSLATORS.md`](./TRANSLATORS.md). English is the default, the
|
||||
source catalog, and the fallback. The subsystem lives in `src/i18n/`:
|
||||
Shipped locales: **en** (source and fallback) and **ru**. The code lives in
|
||||
`src/i18n/`; the contributor workflow is in [`docs/TRANSLATORS.md`](./TRANSLATORS.md).
|
||||
|
||||
- **File map.** `src/i18n/locale.js` owns locale resolution, guarded storage
|
||||
(`gev:locale:v1`), the one-shot `?lang=` override, and `<html lang>`/`<html
|
||||
dir>` metadata. `src/i18n/index.js` owns the catalog registry, `t()`
|
||||
(interpolation + `Intl.PluralRules` plural selection), `formatNumber` /
|
||||
`formatDate`, `applyDocumentTranslations()`, and
|
||||
`persistLocaleAndReload()`. The shipped catalog is four flat message maps
|
||||
— `src/i18n/locales/en/{shell,cockpit,layers,setup}.js`, 912 keys (shell
|
||||
34, cockpit 369, layers 388, setup 121). Plural entries are the en
|
||||
`{ one, other }` shape; a future locale may add only cardinal categories
|
||||
`Intl.PluralRules` reports valid for it, and an entry lacking the selected
|
||||
category degrades to `other` at runtime. Locale-code normalization
|
||||
(`es-MX`/`es_419` → `es`, plus fr/ru/uk and variants) ships ahead of the
|
||||
locales on purpose: knowing a code is hygiene, shipping is catalog-driven
|
||||
through `CATALOG_LOCALES`.
|
||||
- **Selector.** The command dock carries a compact locale switch: a static
|
||||
`.dock-locale-switch` group container in `index.html` whose buttons
|
||||
(`.dock-locale-btn`) are rendered at runtime by `src/ui/applicationShell.js`
|
||||
`_initLocaleSelector()` — one per locale in the configured pair; while
|
||||
only English ships that is a single EN button. A click persists the
|
||||
choice and reloads the page with the hash preserved, so no live re-apply
|
||||
of already-rendered dynamic panels is needed.
|
||||
- **Locale pair config.** `GEV_DEFAULT_LOCALE` and `GEV_SECONDARY_LOCALE` in
|
||||
`.env`, injected as `import.meta.env` defines by `vite.config.js`; both
|
||||
values are validated against the shipped catalogs (`CATALOG_LOCALES`),
|
||||
and the offered set is `dedup([default, secondary, 'en'])`. While English
|
||||
is the only shipped catalog the built-in pair is en-only, so every
|
||||
configuration degenerates to a single offered locale; an invalid,
|
||||
unshipped, or degenerate configured pair falls back to that built-in
|
||||
en-only shape (dev-only warn). The first secondary-locale PR restores the
|
||||
built-in en+es pair.
|
||||
- **Resolution order.** `?lang=<locale>` (search string only; never
|
||||
persisted, never written into share links; stripped by
|
||||
`persistLocaleAndReload`; accepted only for offered locales) → stored
|
||||
`gev:locale:v1` (re-checked against the pair) →
|
||||
`navigator.languages` (regional variants normalize to the primary tag:
|
||||
`es-MX`/`es_419` → `es`) → the CONFIGURED default locale. Unsupported
|
||||
values defer to the next
|
||||
step rather than forcing English.
|
||||
- **Gates** (all under `node --test src/i18n/`, 40 tests):
|
||||
`catalog.test.mjs` enforces per-locale key, placeholder-name, and
|
||||
plural-shape parity with the strict exact-parity flip ON
|
||||
(`REQUIRE_FULL_PARITY`; `GEV_I18N_REQUIRE_FULL_LOCALE_PARITY=0` opts out
|
||||
for staged work; the loops are vacuous while only en ships — each locale
|
||||
PR meets them from day one);
|
||||
`markupCoverage.test.mjs` requires every `data-i18n*` attribute in
|
||||
`index.html` to resolve in every shipped catalog and rejects unknown attribute
|
||||
spellings; `i18n.test.mjs` pins precedence, guards, fallback,
|
||||
interpolation, the plural-selection contract (synthetic missing-category
|
||||
degradation; real-catalog plural pins arrive with each locale PR), and
|
||||
DOM application; `repairPass.test.mjs` anchors en byte-identity for
|
||||
extracted literals and rejects a locale-scoped CCTV wrap rule shipping
|
||||
ahead of its locale (per-locale value anchors arrive with each locale
|
||||
PR).
|
||||
- **Accepted deferrals (do not "fix" silently):**
|
||||
- The military-awareness subject header literal `FLIGHT / VESSEL WINDOW`
|
||||
(`src/data/militaryAwareness.js`) stays English; the literal is
|
||||
test-pinned in `src/data/militaryAwareness.test.mjs`.
|
||||
- The visible cockpit-brief tab tokens `SIG` / `NEWS` / `LOCAL`
|
||||
(`index.html`) stay untranslated to match the runtime keys; their
|
||||
`aria-label`s ARE localized (`cockpit.brief.tab*AriaLabel`).
|
||||
- `src/voice/gevActions.js` tool-result confirmation strings stay English
|
||||
by contract (voice tool schemas and spoken confirmations are
|
||||
keep-English; see the Keep-English boundary in `docs/TRANSLATORS.md`).
|
||||
- Number formatting is still the pre-i18n `toLocaleString` policy at:
|
||||
`src/ui.js:1442`, `src/data/satellites.js:829`,
|
||||
`src/data/flights.js:436` (`'en-US'` pinned), and
|
||||
`src/data/rocketLaunches.js:2281/2299/2364/2371/2374`
|
||||
(default-locale). A locale-aware number-format policy is deferred; the
|
||||
`formatNumber` helper exists in `src/i18n/index.js` when that lands.
|
||||
|
||||
### Overpass proxy mirror rotation (September 2026)
|
||||
|
||||
- `/api/overpass` fans out across four public mirrors. `overpassPayloadIsData()` governs cache reads, writes, and stale fallback: only a 2xx that is neither rate-limited nor a body-level runtime error qualifies. Previously stored refusals are ignored on both fresh and stale reads, so upgrading does not require manually clearing the disk cache.
|
||||
- HTTP refusals such as 406 now rotate alongside the existing network, rate-limit, and runtime-error cases. A refusal from one mirror no longer prevents reaching healthy alternatives or persists under the seven-day road/month-long boundary cache TTLs. Concurrent identical queries share one mirror sequence; if it fails, both the initiating and joined callers can use the same last-good data.
|
||||
- A refusal every mirror agrees on is still reported with the first mirror's status and body, so a genuinely malformed query says what upstream said — but only after every mirror has had the chance to answer it. `fetchOverpassPayload` takes injectable endpoints and fetch so the rotation is tested without a live mirror (`src/overpassProxy.test.mjs`).
|
||||
|
||||
### Share-link v2 layer state (August 2026)
|
||||
|
||||
- Generated share links use a deterministic v2 hash. Existing camera, visual,
|
||||
HUD, detection, post-processing, celestial, scope, and map-stack fields remain,
|
||||
with compact fields for enabled layers, allowlisted layer options, panel state,
|
||||
and the active preset's allowlisted shader controls. An absent layer field uses
|
||||
deterministic defaults; an explicit empty field means no enabled layers.
|
||||
- The registry seals only after all 16 production layers register, and every
|
||||
layer has an explicit serialization disposition. Unknown enabled-layer tokens
|
||||
reject the layer payload; unknown option tokens are ignored. Restoration
|
||||
settles independently per layer so one failed or unavailable source cannot
|
||||
block its siblings.
|
||||
- Stable visible options are limited to aircraft 3D mode, selected civilian and
|
||||
military flight IDs, Satellite catalog and selection, CCTV coverage/projection/
|
||||
auto-hop, and Radio filter/volume. Playback and tuning, live-data health,
|
||||
calibration, caches, lifecycle state, temporary Context ownership, and derived
|
||||
effects are deliberately excluded. Radio restore never selects or plays a
|
||||
station.
|
||||
- Normal loads restore the last successful explicit UI, voice, or tool choice
|
||||
from versioned local storage. Any valid camera share wins for the current load
|
||||
without overwriting recipient preferences. Restore ownership is split by
|
||||
visibility, option/selection, camera, visual, map, and individual panel lane:
|
||||
a newer explicit action supersedes only the field it owns. In particular,
|
||||
navigation cannot turn unrelated layers off, and an option change cannot
|
||||
|
||||
#### Keep-English adjudications (post-merge catch-ups)
|
||||
|
||||
- `src/ui/visualPresets.js` `'ELASTIC'` — detection-allocation STRATEGY IDENTIFIER (persisted
|
||||
under `gev:detection-allocation`, compared via `dataset.allocation ===`), not display copy.
|
||||
- `src/ui/layerPanel.js` guidance meta — the `{source} · {statusMessage}` composition uses a
|
||||
locale-neutral interpunct; the statusMessage itself IS localized via `installationFeedback`.
|
||||
- `src/ui/mapSourceControls.js` `'...'` switching label and the orbit `'↻'` glyph —
|
||||
punctuation/glyphs, identical across locales.
|
||||
- **Keys are the English text.** `t('Save keys')`, `tn(count, '{count} camera',
|
||||
'{count} cameras')`, and `msg('Live Flights')` for text stored in data and
|
||||
translated later. Markup opts in with `data-i18n` (element text) and
|
||||
`data-i18n-title` / `-aria-label` / `-placeholder` (that attribute's English
|
||||
value). English has no catalog; every other locale is
|
||||
`src/i18n/locales/<code>.json`, and a missing entry falls back to English.
|
||||
- **Locale choice.** `?lang=` (one-shot) → stored choice (`gev:locale:v1`) →
|
||||
`navigator.languages` → English. The EN/RU switch in the Visual Presets dock
|
||||
stores the choice and reloads, so nothing re-renders live.
|
||||
- **Gate.** `npm run i18n` (and `src/i18n/catalog.test.mjs` under `npm test`)
|
||||
extracts every message and fails on missing, unused, placeholder-mismatched or
|
||||
wrongly-shaped plural entries.
|
||||
- **Not translated by design:** layer ids, share-link and hash parameters, API
|
||||
fields, voice tool schemas, provider names, feed data (callsigns, vessel and
|
||||
station names) and attribution.
|
||||
- **Gaps.** Strings added after #601's extraction (for example the Street Level
|
||||
panel) are still raw English; there is no lint for unmarked UI text yet.
|
||||
|
||||
## Canonical Docs Order
|
||||
|
||||
|
||||
Reference in new issue
Block a user