mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 19:04:02 +00:00
i18n: fix review findings and polish ru/es strings
Fixes the 51 P0–P2 findings from the full review of the i18n branch and the issues found while checking every new string on screen in ru and es. - Translate errors and statuses that still reached ru/es in English: feed and tile errors, provider statuses, the ALPR chip, the traffic mode chip, voice plan step labels, fire feed values, receiver bands, scene save reasons and the Cesium attribution link. - Show preset city names in the UI language (src/ui/placeNames.js); data and everything sent to the model stay English. - Share layer names between the layer panel, HUD and voice card (src/ui/layerNames.js). - Keep long ru/es strings whole: voice card error lines, analyst titles, the voice tier status, the Nepal evidence button, the ALPR retry chip. English layout is unchanged. - Ask before a language switch drops unsaved work. - Catalog fixes: glossary terms (пуск, пресет), gender agreement and consistent "peticiones" in es, fewer calques in both languages. - Fix English text that was itself wrong (e.g. "Exit cockpit to fly to a weather", raw tool ids in voice plan steps). Catalogs: es/ru 2535 messages each, complete. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
8029747b7e
commit
00704ddc89
212 files changed
+9871
-909
No files matched your search
@@ -0,0 +1,105 @@
|
||||
// K-38: refusals and failures that source modules produce in English reach
|
||||
// the page translated — through a placeholder at the point of display, or
|
||||
// through a catalog entry when the text is stored as data.
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { setLocale, t, translateKnown } from '../i18n/index.js';
|
||||
import { presentStreetLevelPanel } from './streetLevelPresentation.js';
|
||||
import { providerSnapshot } from '../testSupport/streetLevelFakes.mjs';
|
||||
import { createWeatherSource } from '../layers/weather/source.js';
|
||||
import {
|
||||
presentRoutingFailure,
|
||||
presentRoutingRefusal,
|
||||
} from '../layers/directions/index.js';
|
||||
import { AIM_AT_GROUND_MESSAGE } from '../layers/streetLevel/policy.js';
|
||||
|
||||
const NO_IMAGERY = 'No street-level imagery within 50 m of that point';
|
||||
|
||||
function snapshot(error) {
|
||||
return {
|
||||
enabled: true,
|
||||
keyRequired: false,
|
||||
filter: { pano: 'all', sinceDays: 0 },
|
||||
providers: [providerSnapshot()],
|
||||
coverage: { loading: false, count: 0, hint: '', error: null },
|
||||
legend: [],
|
||||
sequence: { providerId: null, selectedId: null, images: 0, loading: false },
|
||||
street: { open: false, loading: false, error, providerId: null },
|
||||
groundClickReady: true,
|
||||
};
|
||||
}
|
||||
|
||||
for (const locale of ['es', 'ru']) {
|
||||
test(`K-38 (${locale}): the nearest-image refusal is built in the language and kept by the panel`, () => {
|
||||
setLocale(locale);
|
||||
try {
|
||||
// What streetLevel/index.js openNearest writes into street.error.
|
||||
const refusal = t(
|
||||
'No street-level imagery within {radius} m of that point',
|
||||
{ radius: 50 },
|
||||
);
|
||||
assert.notEqual(refusal, NO_IMAGERY, 'should be translated');
|
||||
assert.match(refusal, /50/, 'keeps the radius');
|
||||
assert.equal(presentStreetLevelPanel(snapshot(refusal)).error, refusal);
|
||||
// The screen-centre refusal is stored as English data and translated
|
||||
// by the panel.
|
||||
const aim = presentStreetLevelPanel(snapshot(AIM_AT_GROUND_MESSAGE));
|
||||
assert.notEqual(aim.error, AIM_AT_GROUND_MESSAGE, 'should be translated');
|
||||
} finally {
|
||||
setLocale('en');
|
||||
}
|
||||
});
|
||||
|
||||
test(`K-38 (${locale}): the route proxy's refusals are shown translated`, () => {
|
||||
setLocale(locale);
|
||||
try {
|
||||
const shown = t('Routing failed: {error}', {
|
||||
error: presentRoutingRefusal('route leg too long'),
|
||||
});
|
||||
assert.doesNotMatch(shown, /route leg too long/);
|
||||
const limited = presentRoutingFailure(
|
||||
Object.assign(new Error('rate limited — try again in a moment'), {
|
||||
status: 429,
|
||||
refusal: 'rate limited',
|
||||
}),
|
||||
);
|
||||
assert.doesNotMatch(limited, /rate limited|try again in a moment/);
|
||||
const http = presentRoutingFailure(
|
||||
Object.assign(new Error('Routing unavailable (HTTP 503)'), {
|
||||
status: 503,
|
||||
}),
|
||||
);
|
||||
assert.notEqual(http, 'Routing unavailable (HTTP 503)');
|
||||
assert.match(http, /503/);
|
||||
// A text the proxy never sends is shown as given.
|
||||
assert.equal(presentRoutingRefusal('something else'), 'something else');
|
||||
} finally {
|
||||
setLocale('en');
|
||||
}
|
||||
});
|
||||
|
||||
test(`K-38 (${locale}): a weather manifest timeout is shown translated`, async () => {
|
||||
const source = createWeatherSource({
|
||||
fetchImpl: (_url, { signal }) =>
|
||||
new Promise((_resolve, reject) =>
|
||||
signal.addEventListener('abort', () => reject(signal.reason)),
|
||||
),
|
||||
timeoutMs: 10,
|
||||
});
|
||||
let message = null;
|
||||
try {
|
||||
await source.getSnapshot({ product: 'radar' });
|
||||
} catch (error) {
|
||||
message = error.message;
|
||||
}
|
||||
assert.equal(message, 'Weather request timed out', 'stored in English');
|
||||
setLocale(locale);
|
||||
try {
|
||||
// The layer shows `t(problem)` (weather/index.js presentWeatherProblem).
|
||||
assert.notEqual(t(message), message, 'should be translated');
|
||||
assert.notEqual(translateKnown(message), message);
|
||||
} finally {
|
||||
setLocale('en');
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user