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

+38 -7
View File
@@ -344,13 +344,38 @@ function shownPair(items) {
: items[0] || '';
}
/** Ids such as `austin` or `local-firms` read as names on screen. */
// A place as the card shows it: a preset city in the interface language,
// whether the call named it ("Tokyo") or gave its id ("tokyo").
function shownPlace(value, max) {
return localizePlaceName(displayName(value, max));
/**
* The preset city ids the voice tools take (the `locationId` enum in
* actionSchemas.js) and the CITY_POIS names they stand for, so `sf` reads
* "San Francisco", not "Sf". A copy, not an import: src/locations.js pulls in
* Cesium. planStepPresetIds.i18n.test.mjs keeps it in step with CITY_POIS.
*/
const PRESET_PLACE_NAMES = Object.freeze({
austin: 'Austin',
sf: 'San Francisco',
nyc: 'New York',
tokyo: 'Tokyo',
london: 'London',
paris: 'Paris',
dubai: 'Dubai',
dc: 'Washington DC',
tallinn: 'Tallinn',
});
/** A preset city id as its English name; any other value unchanged. */
function presetPlaceName(value) {
const id = typeof value === 'string' ? value.trim() : '';
return Object.hasOwn(PRESET_PLACE_NAMES, id) ? PRESET_PLACE_NAMES[id] : value;
}
// A place as the card shows it: a preset city in the interface language,
// whether the call named it ("Tokyo") or gave its id ("tokyo", "nyc").
function shownPlace(value, max) {
return localizePlaceName(displayName(presetPlaceName(value), max));
}
/** Ids such as `austin` or `local-firms` read as names on screen. */
function displayName(value, max) {
const label = spokenLabel(value, max);
return /^[a-z0-9-]+$/.test(label)
@@ -915,9 +940,15 @@ export function narrationLabel(name, args = {}) {
case 'annotate_map':
return annotationTargets(args)[0] || '';
case 'fly_to_location':
return displayName(args.query || args.locationId || '', 32);
return displayName(
presetPlaceName(args.query || args.locationId || ''),
32,
);
case 'select_nearest_aircraft':
return displayName(args.locationQuery || args.locationId || '', 32);
return displayName(
presetPlaceName(args.locationQuery || args.locationId || ''),
32,
);
default:
return '';
}