i18n: fix the final pre-merge review findings

Fixes the P1 and the confirmed P2 findings of the final review of the
i18n branch.

- CI: the air-quality model no longer imports i18n (portable module),
  so check:boundaries passes again.
- Air quality layer: named in the layer panel, HUD and voice; its
  Weather card, bands and errors translate. A test keeps the layer-name
  table in step with the layer registry.
- Layer statuses that still reached ru/es in English: GNSS, CelesTrak,
  Transit loading and feed errors, launches, perimeters, ALPR, ECCC,
  wind "field unavailable", Local ADS-B streaming lines.
- Voice and HUD: preset ids show city names ("Fly to San Francisco"),
  the HUD city translates, cockpit refusals cover frame, aircraft and
  satellite; empty Transit notes translate on the analyst card.
- tn() keeps a caller's formatted count ("12,345 map objects").
- Cyber HUD: the language button no longer covers the bottom-right
  card or the cockpit position readout.
- CCTV buttons wrap only in non-English locales; English layout matches
  main again.
- Switching language asks first when Provider Settings holds unsaved
  keys.

Catalogs: es/ru 2611 messages each, complete.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
German KhokhlovandClaude Opus 5.5 committed 2026-10-16 16:39:25 +04:00
1 parent fada8b161b
commit 3adce33cc6
43 files changed
+1350 -97

No files matched your search

+16 -1
View File
@@ -20,7 +20,7 @@ import {
* the chip and the dialog are removed outright.
*/
import { t, msg, tn } from './i18n/index.js';
import { t, msg, tn, registerUnsavedWork } from './i18n/index.js';
/**
* Chip label — pure, exported for tests. The exact English values are pinned
@@ -386,9 +386,24 @@ export async function initKeySetup({
let disposed = false;
let disposeControls = () => {};
let disposePlacement = () => {};
// A pasted key lives only in its field until Save: switching language (a
// reload) asks first while any field holds one.
const unregisterUnsavedWork = registerUnsavedWork(() =>
!disposed &&
Object.keys(
collectKeyUpdates(
[...(root.querySelectorAll?.('input[data-env-var]') || [])].map(
(input) => ({ envVar: input.dataset?.envVar, value: input.value }),
),
),
).length
? msg('unsaved provider keys')
: null,
);
const destroy = () => {
if (disposed) return;
disposed = true;
unregisterUnsavedWork();
lifetime.abort();
signal?.removeEventListener('abort', destroy);
disposeControls();