mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 10:53:59 +00:00
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:
1 parent
fada8b161b
commit
3adce33cc6
43 files changed
+1350
-97
No files matched your search
@@ -31,6 +31,14 @@ const LEVEL_LABELS = Object.freeze({
|
||||
high: msg('Low-accuracy score > 10%'),
|
||||
});
|
||||
|
||||
/** A source failure (English data, source.js) in the UI language. */
|
||||
function shownSourceError(text) {
|
||||
const http = /^GNSS feed unavailable \(HTTP (\d{3})\)$/.exec(text);
|
||||
return http
|
||||
? t('GNSS feed unavailable (HTTP {status})', { status: http[1] })
|
||||
: translateKnown(text);
|
||||
}
|
||||
|
||||
/** Degrees at the centre of the current view, or null before the camera settles. */
|
||||
export function defaultGnssViewAnchor(viewer) {
|
||||
const canvas = viewer?.scene?.canvas;
|
||||
@@ -377,7 +385,7 @@ export function createGnssIntegrityLayer({
|
||||
'Regional sample · 250 nm radius · cells from visited views over 30 min',
|
||||
) +
|
||||
(_lastError && _retryAt
|
||||
? ` · ${translateKnown(_lastError)} · ${
|
||||
? ` · ${shownSourceError(_lastError)} · ${
|
||||
_retryAt <= 8640000000000000
|
||||
? t('Retrying automatically at {time} UTC', {
|
||||
time: new Date(_retryAt).toISOString().slice(11, 19),
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { readResponseJsonCapped } from '../../sources/httpBody.js';
|
||||
|
||||
// A portable export (package.json): no i18n here. Failures stay English data;
|
||||
// the Layers panel shows them translated.
|
||||
const msg = (text) => text;
|
||||
|
||||
function retryDelay(raw, nowMs) {
|
||||
if (!raw) return null;
|
||||
const seconds = Number(raw);
|
||||
@@ -21,7 +25,7 @@ export function createAdsbGnssSource({
|
||||
async getSnapshot({ latitude, longitude } = {}, { signal } = {}) {
|
||||
signal?.throwIfAborted();
|
||||
if (!Number.isFinite(latitude) || !Number.isFinite(longitude))
|
||||
throw new Error('GNSS snapshot needs a view anchor');
|
||||
throw new Error(msg('GNSS snapshot needs a view anchor'));
|
||||
const params = new URLSearchParams({
|
||||
lat: String(Math.round(latitude)),
|
||||
lon: String(Math.round(longitude)),
|
||||
@@ -37,8 +41,10 @@ export function createAdsbGnssSource({
|
||||
);
|
||||
throw Object.assign(
|
||||
new Error(
|
||||
// The status code form is matched by FEED_ERROR_PATTERNS
|
||||
// (ui/layerPanel.js) and gnss/index.js.
|
||||
limited
|
||||
? 'GNSS feed rate limited (HTTP 429)'
|
||||
? msg('GNSS feed rate limited (HTTP 429)')
|
||||
: `GNSS feed unavailable (HTTP ${response.status})`,
|
||||
),
|
||||
{
|
||||
@@ -55,12 +61,12 @@ export function createAdsbGnssSource({
|
||||
);
|
||||
signal?.throwIfAborted();
|
||||
if (!Array.isArray(payload?.rows))
|
||||
throw new Error('Malformed GNSS integrity snapshot');
|
||||
throw new Error(msg('Malformed GNSS integrity snapshot'));
|
||||
// The snapshot's age (measured on the proxy's clock) dates every row in
|
||||
// the rolling window; an undated snapshot could only be treated as new,
|
||||
// so it is refused.
|
||||
if (!Number.isFinite(payload.ageMs) || payload.ageMs < 0)
|
||||
throw new Error('GNSS integrity snapshot has no observation time');
|
||||
throw new Error(msg('GNSS integrity snapshot has no observation time'));
|
||||
return {
|
||||
rows: payload.rows,
|
||||
ageMs: payload.ageMs,
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
// N-03: the GNSS source's own failures are English data (source.js); the
|
||||
// card's info line shows them in the UI language, the HTTP form included.
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import * as Cesium from 'cesium';
|
||||
import { setLocale } from '../../i18n/index.js';
|
||||
import { createGnssIntegrityLayer } from './index.js';
|
||||
|
||||
async function failedLayer(message) {
|
||||
const layer = createGnssIntegrityLayer({
|
||||
source: {
|
||||
async getSnapshot() {
|
||||
throw Object.assign(new Error(message), {
|
||||
retryable: true,
|
||||
retryAfterMs: 60_000,
|
||||
});
|
||||
},
|
||||
},
|
||||
viewAnchor: () => ({ latitude: 50, longitude: 30 }),
|
||||
now: () => 1_000,
|
||||
monotonicNow: () => 1_000,
|
||||
});
|
||||
layer.init({
|
||||
camera: { moveEnd: new Cesium.Event(), changed: new Cesium.Event() },
|
||||
scene: {
|
||||
globe: { show: true },
|
||||
groundPrimitives: { add() {}, remove() {} },
|
||||
},
|
||||
});
|
||||
layer.enable();
|
||||
await layer.update();
|
||||
return layer;
|
||||
}
|
||||
|
||||
for (const locale of ['es', 'ru']) {
|
||||
test(`${locale}: the GNSS source failure is translated on the card`, async (t) => {
|
||||
t.mock.timers.enable({ apis: ['setTimeout', 'setInterval'] });
|
||||
for (const message of [
|
||||
'GNSS feed unavailable (HTTP 503)',
|
||||
'GNSS feed rate limited (HTTP 429)',
|
||||
]) {
|
||||
const layer = await failedLayer(message);
|
||||
setLocale(locale);
|
||||
try {
|
||||
const { info } = layer.getRowControls();
|
||||
assert.ok(info.includes('HTTP'), info);
|
||||
assert.doesNotMatch(info, /feed (unavailable|rate limited)/, info);
|
||||
} finally {
|
||||
setLocale('en');
|
||||
layer.destroy();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user