feat(i18n): ship Spanish

Register es next to ru (selector shows EN | ES | RU) and complete the
catalog against the current English: the 13 messages added since #602,
the "many" plural form Intl.PluralRules('es') selects for exact millions,
and fixes where the English had changed (e.g. "hace hace" in LIVE ·
updated, minutes written as metres). Terminology made consistent (buque,
aeronave, incendio activo); key names stay in English. Meir's wording is
kept everywhere else. Adds docs/translations/es.md.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
German KhokhlovandClaude Opus 5.5 committed 2026-10-12 14:33:43 +04:00
1 parent 47cfdb823e
commit 70936b58b9
9 files changed
+180 -51

No files matched your search

+2 -2
View File
@@ -3011,7 +3011,7 @@ test:track` 43 tracking invariants · headless QA harnesses under
### Interface localization (October 2026)
Shipped locales: **en** (source and fallback) and **ru**. The code lives in
Shipped locales: **en** (source and fallback), **es** and **ru**. The code lives in
`src/i18n/`. How to mark UI text: [`docs/I18N.md`](./I18N.md); translator workflow:
[`docs/TRANSLATORS.md`](./TRANSLATORS.md).
@@ -3022,7 +3022,7 @@ Shipped locales: **en** (source and fallback) and **ru**. The code lives in
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
`navigator.languages` → English. The language 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
+1 -1
View File
@@ -56,7 +56,7 @@ npm run i18n -- --prune # drop entries whose English no longer exists
Changing the English changes the key, so the old translation goes unused and the new text shows as missing. Add the translation to `src/i18n/locales/ru.json` (or ask a translator), prune, and run `npm test`, which runs the same check and fails until every language is complete.
To see a language, open the app with `?lang=ru`, or use the EN/RU switch in the Visual Presets dock.
To see a language, open the app with `?lang=ru`, or use the language switch in the Visual Presets dock.
## Where things live
+3 -3
View File
@@ -1,6 +1,6 @@
# Translating God's Eye View
Shipped languages: **English** (source and fallback) and **Russian** (`ru`).
Shipped languages: **English** (source and fallback), **Spanish** (`es`) and **Russian** (`ru`).
## How it works
@@ -22,7 +22,7 @@ Every other language is one flat JSON file, `src/i18n/locales/<code>.json`, keye
A `tn()` entry is keyed by the English plural form and holds one string per `Intl.PluralRules` category of that language. A missing entry falls back to English.
The page picks its language from `?lang=`, then the choice stored by the EN/RU switch in the Visual Presets dock, then the browser languages, then English. Switching reloads the page.
The page picks its language from `?lang=`, then the choice stored by the language switch in the Visual Presets dock, then the browser languages, then English. Switching reloads the page.
## The check
@@ -63,4 +63,4 @@ These are contracts, not copy: layer ids and registry keys, status enums and oth
## Language notes
Each language keeps its glossary, plural forms, units and style in its own file, `docs/translations/<code>.md`: [Russian](./translations/ru.md). Read it before translating, and add a term there when you settle on one.
Each language keeps its glossary, plural forms, units and style in its own file, `docs/translations/<code>.md`: [Spanish](./translations/es.md), [Russian](./translations/ru.md). Read it before translating, and add a term there when you settle on one.
+70
View File
@@ -0,0 +1,70 @@
# Spanish (`es`)
Catalog: `src/i18n/locales/es.json`. General rules: [TRANSLATORS.md](../TRANSLATORS.md).
## Style
- Neutral Spanish that reads naturally in Spain and Latin America; avoid regionalisms.
- Address the user with informal "tú" imperatives: «Añade», «Activa», «Elige», «Pega», «Inténtalo de nuevo». Tooltips that name an action may use the infinitive: «Expandir el panel», «Restablecer la cámara».
- Sentence case, not Title Case: "Live Flights" → «Vuelos en vivo». UPPERCASE English stays UPPERCASE: «EN VIVO», «SIN DATOS».
- Quotes inside Spanish text are straight double quotes: «¿Eliminar el plano "{shot}"?». Questions and exclamations open with ¿ and ¡.
- Short labels, chips and HUD readouts stay short. A standard abbreviation with a period beats a wrapped line: ANT., SIG., VEL., DIST., SINCR., cám., cob.
- The HUD codes HDG, BRG, CRS, FOV, DEST, SPD, RANGE and ALT stay in English everywhere, as in cockpit avionics. Words used in a sentence are translated: heading → «rumbo», range → «alcance».
- Keyboard keys stay in English: Space, Esc, Enter, Tab, Shift, H, 1–9 («Mantén pulsado Space para hablar»).
- Units: km, m, km/h, m/s, kn (knots), ft (feet), h, min, s; in UPPERCASE text KM, KM/S, KN, FT, MIN, H. "m" is always metres, so minutes are «min» («hace 5 min»). MW stays MW. `°` and `%` stay.
- "ago" goes before the time: «hace {count} min». When a placeholder already holds a localized "ago" text (`{age}`, `{ago}`), do not add «hace» again.
- Spelling: «costo» (not «coste»).
## Plurals
A `tn()` entry needs three forms. `Intl.PluralRules('es')` picks:
| Form | Counts | Example |
| ------- | -------------------------------------- | -------------------- |
| `one` | 1 | 1 cámara |
| `many` | 1 000 000, 2 000 000… (exact millions) | 1 000 000 de cámaras |
| `other` | everything else: 0, 2–999 999, 1.5… | 3 cámaras |
The `many` form is the `other` text with «de» after the number: «{count} de cámaras cargadas», «{count} DE CLAVES EN ESPERA».
## Glossary
| English | Español |
| --------------------------------------------- | ----------------------------------------------------------------- |
| layer / data layer | capa / capa de datos |
| feed / source / data source | fuente / fuente / fuente de datos |
| LIVE (status) / live data | EN VIVO / datos en vivo |
| SIMULATED | SIMULADO |
| stale / degraded / unavailable | desactualizado / degradado / no disponible |
| retry / retrying | reintento / reintentando |
| key (API key) / POWER UP / POWERED UP | clave / ENCENDER / ENCENDIDO |
| Provider Settings | Ajustes de proveedores |
| ON / OFF (state chips) | ACTIVADO / DESACTIVADO |
| enable / disable | activar / desactivar |
| globe / map / map source / map stack | globo / mapa / fuente del mapa / conjunto de mapas |
| aircraft, plane / flight / military flights | aeronave / vuelo / vuelos militares |
| vessel, ship | buque |
| satellite / shell (Starlink) / DENSE | satélite / capa / DENSO |
| launch (rocket) / launch site / space mission | lanzamiento / lugar de lanzamiento / misión espacial |
| ascent / replay / payload / stage | ascenso / repetición / carga útil / etapa |
| contact / tracking | contacto / rastreo |
| installation / site | instalación / sitio |
| mapped / observed | mapeado / observado |
| camera / CCTV camera | cámara / cámara CCTV |
| earthquake / active fire | terremoto / incendio activo |
| radio station | emisora |
| road / flow (traffic) | vía / flujo |
| scene / shot / scene recipe | escena / plano / receta de escena |
| briefing / panel / dock | información / panel / dock |
| cockpit | cabina |
| preset / visual preset | preajuste / preajuste visual |
| Scope (circular viewport mask) / keyhole | mirilla / ojo de cerradura |
| feather / fade | difuminado / desvanecimiento |
| detection overlay | superposición de detección |
| heading, course / altitude / ground speed | rumbo / altitud / velocidad respecto al suelo (short: VEL. SUELO) |
| HDG / BRG / CRS / FOV / DEST / SPD / RANGE | HDG / BRG / CRS / FOV / DEST / SPD / RANGE (unchanged) |
| PREV / NEXT | ANT. / SIG. |
| Previous / Next (button text) | Anterior / Siguiente |
| L / R (relative bearing) | I / D |
| NOT AN ALL-CLEAR | NO IMPLICA TODO DESPEJADO |
| click / click to … | haz clic / clic para … |