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

+10 -4
View File
@@ -1,6 +1,10 @@
import { readResponseJsonCapped } from '../../sources/httpBody.js';
import { normalizeAqhiStations, normalizeAqhiObservations } from './model.js';
// A portable export (package.json): no i18n here. Failures stay English data;
// the Layers panel and the Weather card show them translated.
const msg = (text) => text;
const COLLECTIONS_ROOT = 'https://api.weather.gc.ca/collections';
const STATIONS_URL = `${COLLECTIONS_ROOT}/aqhi-stations/items`;
const OBSERVATIONS_URL = `${COLLECTIONS_ROOT}/aqhi-observations-realtime/items`;
@@ -47,7 +51,7 @@ export function createEcccAirQualitySource({
const abort = () => controller.abort(signal.reason);
signal?.addEventListener('abort', abort, { once: true });
const timer = setTimeout(
() => controller.abort(new Error('ECCC request timed out')),
() => controller.abort(new Error(msg('ECCC request timed out'))),
timeoutMs,
);
try {
@@ -82,14 +86,15 @@ export function createEcccAirQualitySource({
signal,
);
const next = normalizeAqhiStations(payload);
if (!next) throw new Error('Malformed AQHI station response');
if (!next) throw new Error(msg('Malformed AQHI station response'));
// An empty refresh must not wipe a good catalog.
if (next.size > 0) {
stations = next;
stationsFetchedAt = clock;
}
}
if (stations.size === 0) throw new Error('No AQHI stations available');
if (stations.size === 0)
throw new Error(msg('No AQHI stations available'));
const payload = await getJson(
`${OBSERVATIONS_URL}?f=json&latest=true&limit=${OBSERVATION_LIMIT}`,
@@ -97,7 +102,8 @@ export function createEcccAirQualitySource({
signal,
);
const readings = normalizeAqhiObservations(payload, stations, clock);
if (!readings) throw new Error('Malformed AQHI observation response');
if (!readings)
throw new Error(msg('Malformed AQHI observation response'));
return readings;
},
};