i18n: fix the repeat-review findings

- Status pieces translate in one place (src/ui/statusText.js): a Local
  ADS-B error with a single piece now translates, and the analyst card
  splits empty-layer notes into pieces (Transit, Local ADS-B).
- Translate the ALPR retry chip text, the ECCC error on the Air quality
  card, "Upstream response too large" and the "entry {n}" feed label.
- The unmarked-text check also sees error/loadingLabel/statusMessage/
  status assignments, `status:` properties and showToast?.( /
  this._showToast( calls; the scene import preview marks its pack
  statuses, data and model-facing text carry i18n-ignore with a reason.
- Tests check the real code: the HUD locality formatter moves to
  src/hudLocalityFormat.js, the unsaved-keys test pins the teardown.
- One copy each: city names come from src/cityPois.js, the GNSS HTTP
  template and wind "field unavailable" texts; dead fallbacks removed.
- Leftovers of the reverted vessel-label change and the reformatting of
  detectionHost.test.mjs / keySetup.test.mjs are back to main.

Catalogs: es/ru 2617 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 19:28:59 +04:00
1 parent a2463ca2d2
commit 3157888780
60 files changed
+1577 -1606

No files matched your search

+19 -80
View File
@@ -6,7 +6,9 @@ import {
stripKeylessBasemapFromHash,
bindKeySetupPlacement,
waitForChatGptOAuth,
initKeySetup,
} from './keySetup.js';
import { describeUnsavedWork } from './i18n/index.js';
test('setup placement moves the same button only in Cyber and disconnects on teardown', () => {
const home = {
@@ -59,20 +61,10 @@ test('setup placement moves the same button only in Cyber and disconnects on tea
});
test('the chip counts what is missing, and retires the count at zero', () => {
assert.equal(
keySetupChipLabel({ setCount: 0, total: 8 }),
'POWER UP · 8 KEYS WAITING',
);
assert.equal(
keySetupChipLabel({ setCount: 7, total: 8 }),
'POWER UP · 1 KEY WAITING',
);
assert.equal(keySetupChipLabel({ setCount: 0, total: 8 }), 'POWER UP · 8 KEYS WAITING');
assert.equal(keySetupChipLabel({ setCount: 7, total: 8 }), 'POWER UP · 1 KEY WAITING');
assert.equal(keySetupChipLabel({ setCount: 8, total: 8 }), 'POWERED UP');
assert.equal(
keySetupChipLabel(null),
'POWERED UP',
'no status is not a broken label',
);
assert.equal(keySetupChipLabel(null), 'POWERED UP', 'no status is not a broken label');
});
test('collectKeyUpdates keeps only non-empty trimmed values', () => {
@@ -112,27 +104,14 @@ test('OAuth login polling stops as soon as local ChatGPT auth becomes available'
test('OAuth login polling stops on login or HTTP failure instead of waiting for timeout', async () => {
for (const result of [
{
ok: true,
payload: {
available: false,
loginFailed: true,
error: 'Sign-in failed.',
},
},
{ ok: true, payload: { available: false, loginFailed: true, error: 'Sign-in failed.' } },
{ ok: false, payload: { error: 'Sign-in is local only.' } },
]) {
let checks = 0;
await assert.rejects(
waitForChatGptOAuth({
fetchImpl: async () => {
checks += 1;
return { ok: result.ok, json: async () => result.payload };
},
sleep: () => assert.fail('failed login must stop polling'),
}),
{ message: result.payload.error },
);
await assert.rejects(waitForChatGptOAuth({
fetchImpl: async () => { checks += 1; return { ok: result.ok, json: async () => result.payload }; },
sleep: () => assert.fail('failed login must stop polling'),
}), { message: result.payload.error });
assert.equal(checks, 1);
}
});
@@ -140,34 +119,12 @@ test('OAuth login polling stops on login or HTTP failure instead of waiting for
test('OAuth login polling respects timeout and an aborted setup surface', async () => {
let clock = 0;
let checks = 0;
const poll = {
fetchImpl: async () => {
checks += 1;
return {
ok: true,
json: async () => ({ available: false, pending: true }),
};
},
};
assert.equal(
await waitForChatGptOAuth({
...poll,
timeoutMs: 100,
pollMs: 100,
now: () => clock,
sleep: async (ms) => {
clock += ms;
},
}),
false,
);
const poll = { fetchImpl: async () => { checks += 1; return { ok: true, json: async () => ({ available: false, pending: true }) }; } };
assert.equal(await waitForChatGptOAuth({ ...poll, timeoutMs: 100, pollMs: 100, now: () => clock, sleep: async (ms) => { clock += ms; } }), false);
assert.equal(checks, 2);
const controller = new AbortController();
controller.abort();
assert.equal(
await waitForChatGptOAuth({ ...poll, signal: controller.signal }),
false,
);
assert.equal(await waitForChatGptOAuth({ ...poll, signal: controller.signal }), false);
assert.equal(checks, 2, 'aborted polling does not fetch');
});
@@ -175,22 +132,14 @@ test('OAuth polling ignores a ready response received after the surface was abor
const controller = new AbortController();
const available = await waitForChatGptOAuth({
signal: controller.signal,
fetchImpl: async () => ({
ok: true,
json: async () => {
controller.abort();
return { available: true };
},
}),
fetchImpl: async () => ({ ok: true, json: async () => { controller.abort(); return { available: true }; } }),
sleep: () => assert.fail('aborted polling must not continue'),
});
assert.equal(available, false);
});
test('the first Google key strips ONLY the keyless OSM basemap from the share hash', () => {
const stripped = stripKeylessBasemapFromHash(
'lat=30.2&lon=-97.7&map=osm&style=normal',
);
const stripped = stripKeylessBasemapFromHash('lat=30.2&lon=-97.7&map=osm&style=normal');
assert.ok(stripped !== null);
const params = new URLSearchParams(stripped);
assert.equal(params.get('map'), null, 'osm basemap removed');
@@ -198,11 +147,7 @@ test('the first Google key strips ONLY the keyless OSM basemap from the share ha
assert.equal(params.get('style'), 'normal', 'style survives');
// A stack under any other name was chosen or shared on purpose.
assert.equal(stripKeylessBasemapFromHash('map=bing-aerial&lat=1'), null);
assert.equal(
stripKeylessBasemapFromHash('lat=1&lon=2'),
null,
'no stack, nothing to do',
);
assert.equal(stripKeylessBasemapFromHash('lat=1&lon=2'), null, 'no stack, nothing to do');
assert.equal(stripKeylessBasemapFromHash(''), null);
assert.equal(stripKeylessBasemapFromHash(undefined), null);
});
@@ -216,15 +161,11 @@ test('aborting pending setup removes its surface and ignores a late response', a
let requestSignal;
const controller = new AbortController();
const pending = initKeySetup({
documentRef: {
getElementById: (id) => (id === 'key-setup-chip' ? chip : root),
},
documentRef: { getElementById: (id) => id === 'key-setup-chip' ? chip : root },
signal: controller.signal,
fetchImpl: (_url, { signal }) => {
requestSignal = signal;
return new Promise((resolve) => {
resolveResponse = resolve;
});
return new Promise((resolve) => { resolveResponse = resolve; });
},
});
controller.abort();
@@ -233,10 +174,7 @@ test('aborting pending setup removes its surface and ignores a late response', a
resolveResponse({ ok: true, json: async () => ({ keys: [] }) });
assert.equal(await pending, null);
});
test('a pasted, unsaved key makes the language switch ask first until teardown', async () => {
const { initKeySetup } = await import('./keySetup.js');
const { describeUnsavedWork } = await import('./i18n/index.js');
const node = () => ({
dataset: {},
addEventListener() {},
@@ -271,6 +209,7 @@ test('a pasted, unsaved key makes the language switch ask first until teardown',
assert.deepEqual(describeUnsavedWork(), []);
input.value = 'AIza-pasted';
assert.deepEqual(describeUnsavedWork(), ['unsaved provider keys']);
// The field still holds the key, so only teardown dropping the check clears it.
controller.abort();
assert.deepEqual(describeUnsavedWork(), []);
});