fix(i18n): second screenshot review: remaining English, numbers and wraps

From the re-shot Russian screenshots:
- Translated: weather frame/imagery status errors, HUD summary provenance
  ("НЕДОСТУПНО: СУДА AIS ОНЛАЙН"), Bhote Koshi map labels, cockpit signal
  distances (КМ). The HUD summary fields are separated with " · " in every
  language, so the style name and view band no longer read as one phrase.
- Numbers follow the locale: cockpit altitude ("26 175"), precipitation.
- No breaks inside codes and dates: ADS-B and similar in layer rows,
  Sentinel-2, cyclone issue dates (word joiner; the UI font has no U+2011),
  no-break space after label colons in cyclone details.
- Layout: count lines under ALPR, short unavailable chip (context
  "status chip"), roomier draw segments, tighter CCTV calibration chips,
  two-line beat titles and media card titles, wrapping weather status.
- Wording: ru advisory is «сводка» (was «бюллетень»; "Advisory center" is
  «Центр циклона по сводке»), Street View is «Просмотр улиц», OPACITY
  «НЕПРОЗР.», synced «синхр.», Area «Зона»; the import dialog no longer says
  "this scene" for several scenes. Glossary updated.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
German KhokhlovandClaude Opus 5.5 committed 2026-10-13 19:13:12 +04:00
1 parent 2b6ccad732
commit 54dce5b3bd
32 files changed
+1204 -346

No files matched your search

+111 -39
View File
@@ -5,7 +5,7 @@ import {
HUD_SUMMARY_INSTRUCTIONS,
HUD_SUMMARY_UNCONFIGURED_CODE,
hudSummaryLayerContext,
hudTelemetryProvenanceTag,
hudTelemetryProvenance,
isHudSummaryUnconfigured,
keylessHudSummaryResponse,
} from './hudSummaryResponse.js';
@@ -30,7 +30,10 @@ function installOpenAiRoutes() {
return routes;
}
function invokeRoute(handler, { method = 'GET', url = '/', remoteAddress = '127.0.0.1' } = {}) {
function invokeRoute(
handler,
{ method = 'GET', url = '/', remoteAddress = '127.0.0.1' } = {},
) {
return new Promise((resolve, reject) => {
const headers = new Map();
const req = {
@@ -71,33 +74,54 @@ test('builds an HTTP-success capability response only for a blank key', () => {
test('recognizes only the exact deliberate no-key fallback response', () => {
assert.equal(isHudSummaryUnconfigured(200, UNCONFIGURED_PAYLOAD), true);
assert.equal(isHudSummaryUnconfigured(503, UNCONFIGURED_PAYLOAD), false);
assert.equal(isHudSummaryUnconfigured(200, {
...UNCONFIGURED_PAYLOAD,
error: 'provider failed',
}), false);
assert.equal(isHudSummaryUnconfigured(200, {
...UNCONFIGURED_PAYLOAD,
summary: 'Unexpected provider output',
}), false);
assert.equal(isHudSummaryUnconfigured(200, {
...UNCONFIGURED_PAYLOAD,
configured: true,
}), false);
assert.equal(isHudSummaryUnconfigured(200, {
...UNCONFIGURED_PAYLOAD,
unexpected: true,
}), false);
assert.equal(isHudSummaryUnconfigured(200, {
code: HUD_SUMMARY_UNCONFIGURED_CODE,
}), false);
assert.equal(
isHudSummaryUnconfigured(200, {
...UNCONFIGURED_PAYLOAD,
error: 'provider failed',
}),
false,
);
assert.equal(
isHudSummaryUnconfigured(200, {
...UNCONFIGURED_PAYLOAD,
summary: 'Unexpected provider output',
}),
false,
);
assert.equal(
isHudSummaryUnconfigured(200, {
...UNCONFIGURED_PAYLOAD,
configured: true,
}),
false,
);
assert.equal(
isHudSummaryUnconfigured(200, {
...UNCONFIGURED_PAYLOAD,
unexpected: true,
}),
false,
);
assert.equal(
isHudSummaryUnconfigured(200, {
code: HUD_SUMMARY_UNCONFIGURED_CODE,
}),
false,
);
});
test('does not hide real provider and HTTP failures', () => {
assert.equal(isHudSummaryUnconfigured(502, {
code: HUD_SUMMARY_UNCONFIGURED_CODE,
}), false);
assert.equal(
isHudSummaryUnconfigured(502, {
code: HUD_SUMMARY_UNCONFIGURED_CODE,
}),
false,
);
assert.equal(isHudSummaryUnconfigured(503, UNCONFIGURED_PAYLOAD), false);
assert.equal(isHudSummaryUnconfigured(200, { error: 'provider failed' }), false);
assert.equal(
isHudSummaryUnconfigured(200, { error: 'provider failed' }),
false,
);
});
test('the installed keyless HUD route stays successful after the voice quota is exhausted', async () => {
@@ -121,14 +145,18 @@ test('the installed keyless HUD route stays successful after the voice quota is
for (let attempt = 0; attempt < 2; attempt += 1) {
const response = await invokeRoute(hud, { method: 'POST' });
assert.equal(response.statusCode, 200);
assert.equal(response.headers['content-type'], 'application/json; charset=utf-8');
assert.equal(
response.headers['content-type'],
'application/json; charset=utf-8',
);
assert.equal(response.headers['cache-control'], 'no-store');
assert.deepEqual(response.body, UNCONFIGURED_PAYLOAD);
}
} finally {
if (previousKey === undefined) delete process.env.OPENAI_API_KEY;
else process.env.OPENAI_API_KEY = previousKey;
if (previousLimit === undefined) delete process.env.GEV_RATELIMIT_OPENAI_PER_MIN;
if (previousLimit === undefined)
delete process.env.GEV_RATELIMIT_OPENAI_PER_MIN;
else process.env.GEV_RATELIMIT_OPENAI_PER_MIN = previousLimit;
}
});
@@ -140,7 +168,12 @@ test('HUD summary context and telemetry tag carry chip-identical feed-state', ()
id: 'flights',
name: 'Live Flights',
enabled: true,
stats: { stale: true, count: 12, lastUpdate: now - 240_000, source: 'OpenSky Network' },
stats: {
stale: true,
count: 12,
lastUpdate: now - 240_000,
source: 'OpenSky Network',
},
},
{
id: 'earthquakes',
@@ -153,27 +186,66 @@ test('HUD summary context and telemetry tag carry chip-identical feed-state', ()
assert.deepEqual(context.enabledLayerLabels, ['Live Flights']);
assert.equal(context.enabledLayers[0].feedState, 'stale');
assert.equal(context.feedProvenance.overall, 'stale');
assert.equal(hudTelemetryProvenanceTag(layers, { now }), 'STALE LIVE FLIGHTS');
assert.equal(hudTelemetryProvenanceTag([{
id: 'flights', name: 'Live Flights', enabled: true, stats: { count: 4, lastUpdate: now },
}], { now }), null);
assert.deepEqual(hudTelemetryProvenance(layers, { now }), {
state: 'stale',
names: ['Live Flights'],
});
assert.equal(
hudTelemetryProvenance(
[
{
id: 'flights',
name: 'Live Flights',
enabled: true,
stats: { count: 4, lastUpdate: now },
},
],
{ now },
),
null,
);
});
test('HUD summary instructions require non-nominal feedState in the five words', () => {
assert.match(HUD_SUMMARY_INSTRUCTIONS, /five words MUST include that feedState token/);
assert.match(HUD_SUMMARY_INSTRUCTIONS, /STALE, DEGRADED, FALLBACK, LOADING, or UNAVAILABLE/);
assert.match(
HUD_SUMMARY_INSTRUCTIONS,
/five words MUST include that feedState token/,
);
assert.match(
HUD_SUMMARY_INSTRUCTIONS,
/STALE, DEGRADED, FALLBACK, LOADING, or UNAVAILABLE/,
);
assert.match(HUD_SUMMARY_INSTRUCTIONS, /feedProvenance/);
});
test('the HUD proxy uses the shared provenance instructions', () => {
const local = readFileSync(new URL('../server/providers/openai/hud-summary.js', import.meta.url), 'utf8');
const local = readFileSync(
new URL('../server/providers/openai/hud-summary.js', import.meta.url),
'utf8',
);
assert.match(local, /HUD_SUMMARY_INSTRUCTIONS/);
assert.doesNotMatch(local, /enabled-layer text labels/);
});
test('AI summaries missing a non-nominal provenance token fall back deterministically', async () => {
const { hudSummaryMatchesProvenance } = await import('./hudSummaryResponse.js');
assert.equal(hudSummaryMatchesProvenance('Austin flights operating normally today', { overall: 'stale' }), false);
assert.equal(hudSummaryMatchesProvenance('Austin stale flights over downtown', { overall: 'stale' }), true);
assert.equal(hudSummaryMatchesProvenance('Austin flights operating normally today', { overall: 'nominal' }), true);
const { hudSummaryMatchesProvenance } =
await import('./hudSummaryResponse.js');
assert.equal(
hudSummaryMatchesProvenance('Austin flights operating normally today', {
overall: 'stale',
}),
false,
);
assert.equal(
hudSummaryMatchesProvenance('Austin stale flights over downtown', {
overall: 'stale',
}),
true,
);
assert.equal(
hudSummaryMatchesProvenance('Austin flights operating normally today', {
overall: 'nominal',
}),
true,
);
});