mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 10:53:59 +00:00
i18n: fix the LGTM review findings and tidy the branch texts
- i18n scanner: lastError sink, no false sinks on defaults and ternaries - shared src/i18n/marker.js for modules that only mark strings - wind field messages move to wind/fields.js; CITY_POIS imports from cityPois.js - drop review IDs, change history and stale references from comments and docs Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
0ed05568f7
commit
20f4cf3763
178 files changed
+918
-948
No files matched your search
@@ -211,3 +211,70 @@ test('unmarkedScript sees human-readable status, loadingLabel and statusMessage
|
||||
assert.deepEqual(texts("return { status: 'auth-failed' };"), []);
|
||||
assert.deepEqual(texts("return { status: msg('Source unavailable') };"), []);
|
||||
});
|
||||
|
||||
// The Layers panel shows `stats.error || stats.lastError`, so a
|
||||
// lastError is status text like error.
|
||||
test('unmarkedScript sees status text assigned to lastError', () => {
|
||||
const texts = (js) => unmarkedScript('f.js', js).map((f) => f.text);
|
||||
assert.deepEqual(
|
||||
texts(
|
||||
"state.coverage.lastError = error?.message || 'Coverage tile failed';",
|
||||
),
|
||||
['Coverage tile failed'],
|
||||
);
|
||||
assert.deepEqual(texts("layerState._lastError = 'CelesTrak unreachable';"), [
|
||||
'CelesTrak unreachable',
|
||||
]);
|
||||
assert.deepEqual(
|
||||
texts(
|
||||
"state.coverage.lastError = error?.message || msg('Coverage tile failed');",
|
||||
),
|
||||
[],
|
||||
);
|
||||
assert.deepEqual(texts('this._lastError = message;'), []);
|
||||
});
|
||||
|
||||
test('unmarkedScript skips default values and ternary branches, not later assignments', () => {
|
||||
const texts = (js) => unmarkedScript('f.js', js).map((f) => f.text);
|
||||
// A default value in a parameter list or destructuring is a local's.
|
||||
assert.deepEqual(texts("function f(error = 'Feed went away') {}"), []);
|
||||
assert.deepEqual(texts("const f = (status = 'Not ready', x) => x;"), []);
|
||||
assert.deepEqual(
|
||||
texts("function f(\n status = 'Not ready',\n x,\n) {}"),
|
||||
[],
|
||||
);
|
||||
assert.deepEqual(texts("const { status = 'Ready now' } = options;"), []);
|
||||
assert.deepEqual(
|
||||
texts("const {\n error = 'No data yet',\n} = options;"),
|
||||
[],
|
||||
);
|
||||
assert.deepEqual(
|
||||
texts("for (const [id, status = 'Waiting'] of rows) {}"),
|
||||
[],
|
||||
);
|
||||
// A name right after `?` is a ternary branch, not a `status:` property.
|
||||
assert.deepEqual(texts("const s = ok ? status : 'Source unavailable';"), []);
|
||||
assert.deepEqual(texts("const s = ok\n ? title\n : 'Untitled view';"), []);
|
||||
// …while the property itself still is.
|
||||
assert.deepEqual(texts("return { title: x ? title : 'Untitled view' };"), [
|
||||
'Untitled view',
|
||||
]);
|
||||
// A later assignment is checked even when the name is a declared local:
|
||||
// a layer keeps its status in `let error` (wind, AIS watchdog) and builds
|
||||
// `loadingLabel` in a `let` before returning it (FIRMS).
|
||||
assert.deepEqual(
|
||||
texts(
|
||||
"function f() {\n let error = null;\n if (x) error = 'Feed closed';\n}",
|
||||
),
|
||||
['Feed closed'],
|
||||
);
|
||||
assert.deepEqual(
|
||||
texts(
|
||||
"let loadingLabel = '';\nif (x) {\n loadingLabel = 'KEY REQUIRED';\n}",
|
||||
),
|
||||
['KEY REQUIRED'],
|
||||
);
|
||||
assert.deepEqual(texts("let a = 1,\n error = 'Feed closed';"), [
|
||||
'Feed closed',
|
||||
]);
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
// K-21: an empty or blank translation must not count as done.
|
||||
// An empty or blank translation must not count as done.
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import {
|
||||
|
||||
+1
-1
@@ -179,7 +179,7 @@ const UTC_DAY_MONTH = Object.freeze({
|
||||
|
||||
/**
|
||||
* The UTC day and month of a time, for "MM-DD HH:MM UTC" readouts: "10-13" in
|
||||
* English (as before), "13.10" in Russian, "13/10" in Spanish, never read as a
|
||||
* English, "13.10" in Russian, "13/10" in Spanish, never read as a
|
||||
* range. Takes an ISO string (its date part is used as written) or a time.
|
||||
* @param {string|number|Date} value
|
||||
* @returns {string} '' when the value is not a time.
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
// K-194: the KartaView provider's failure and state texts are English data
|
||||
// (msg() in kartaview/{policy,source,viewer,index}.js) that the Street Level
|
||||
// panel and the chip tooltip show through translateKnown, so every one needs
|
||||
// an entry in each language's catalog.
|
||||
// KartaView texts are English data (msg() in providers/kartaview/) shown
|
||||
// through translateKnown, so each needs an entry in every catalog.
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { setLocale, translateKnown } from './index.js';
|
||||
|
||||
@@ -3,10 +3,8 @@ import assert from 'node:assert/strict';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { setLocale, translateKnown } from './index.js';
|
||||
|
||||
// Status and error texts the layers write straight into their state
|
||||
// (loadingLabel / _error / street.error) as English data; the layer panel and
|
||||
// the Street Level panel show them through translateKnown, so each needs a
|
||||
// catalog entry in every language (K-36).
|
||||
// Layers keep these status texts as English data, and the panels show them
|
||||
// through translateKnown, so each needs an entry in every catalog.
|
||||
const SITES = [
|
||||
['../layers/streetLevel/index.js', 'loading coverage...'],
|
||||
['../layers/firms/ingestion.js', 'live feed unavailable'],
|
||||
@@ -33,8 +31,8 @@ const SITES = [
|
||||
['../layers/wind/source.js', 'Wind request timed out'],
|
||||
['../layers/wind/source.js', 'Malformed wind manifest'],
|
||||
['../layers/wind/source.js', 'Malformed wind grid'],
|
||||
['../layers/wind/source.js', 'Temperature field unavailable'],
|
||||
['../layers/wind/source.js', 'Mean sea level pressure field unavailable'],
|
||||
['../layers/wind/fields.js', 'Temperature field unavailable'],
|
||||
['../layers/wind/fields.js', 'Mean sea level pressure field unavailable'],
|
||||
];
|
||||
|
||||
// A fallback that reaches the panel must be marked, or `npm run i18n` cannot
|
||||
@@ -55,7 +53,7 @@ for (const locale of ['es', 'ru']) {
|
||||
for (const [file, text] of SITES) {
|
||||
test(`${locale}: "${text}" from ${file} is translated`, () => {
|
||||
const source = readFileSync(new URL(file, import.meta.url), 'utf8');
|
||||
assert.ok(source.includes(`'${text}'`), `literal still in ${file}`);
|
||||
assert.ok(source.includes(`'${text}'`), `literal not found in ${file}`);
|
||||
setLocale(locale);
|
||||
try {
|
||||
assert.notEqual(translateKnown(text), text);
|
||||
|
||||
+1
-5
@@ -7,11 +7,7 @@ export const LOCALE_STORAGE_KEY = 'gev:locale:v1';
|
||||
/** Source language and fallback. */
|
||||
export const DEFAULT_LOCALE = 'en';
|
||||
|
||||
/**
|
||||
* Shipped locales. The picker (src/ui/localePicker.js) lists the active one
|
||||
* first and the rest by LOCALE_NAMES. Adding one: locales/<code>.json, an
|
||||
* import in index.js, and an entry here (see docs/TRANSLATORS.md).
|
||||
*/
|
||||
/** Shipped locales; order does not matter. Adding one: docs/TRANSLATORS.md. */
|
||||
export const LOCALES = Object.freeze(['en', 'es', 'ru']);
|
||||
|
||||
/** Each locale named in its own language, for the selector. */
|
||||
|
||||
@@ -2883,5 +2883,7 @@
|
||||
"Included in bundle": "Incluido en el paquete",
|
||||
"Missing bundle file — reimport its bundle": "Falta el archivo en el paquete — vuelve a importar su paquete",
|
||||
"Source configured; file checked when loaded": "Fuente configurada; el archivo se comprobará al cargarlo",
|
||||
"Source unavailable": "Fuente no disponible"
|
||||
"Source unavailable": "Fuente no disponible",
|
||||
"Coverage tile failed": "No se pudo cargar la tesela de cobertura",
|
||||
"Unknown": "Desconocido"
|
||||
}
|
||||
@@ -2950,5 +2950,7 @@
|
||||
"Included in bundle": "Входит в архив",
|
||||
"Missing bundle file — reimport its bundle": "Файла нет в архиве — импортируйте этот архив снова",
|
||||
"Source configured; file checked when loaded": "Источник настроен; файл будет проверен при загрузке",
|
||||
"Source unavailable": "Источник недоступен"
|
||||
"Source unavailable": "Источник недоступен",
|
||||
"Coverage tile failed": "Не удалось загрузить тайл покрытия",
|
||||
"Unknown": "Неизвестно"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// msg() for modules the server, tools or other portable code also import:
|
||||
// marks English that stays in data for `npm run i18n`, without loading the
|
||||
// catalogs (src/i18n/index.js). The UI shows it later through t().
|
||||
// Browser-only modules import msg from src/i18n/index.js instead.
|
||||
|
||||
/** Mark English text that is stored in data and translated later by t(). */
|
||||
export const msg = (text) => text;
|
||||
@@ -6,9 +6,7 @@ import {
|
||||
missingTemplate,
|
||||
} from '../../scripts/i18n.mjs';
|
||||
|
||||
// A plural entry whose every form is still the English text must be reported
|
||||
// the same way a string entry is ("is still English"). Today the plural
|
||||
// branch of checkCatalog never compares forms with the English.
|
||||
// A plural entry with forms left in English is reported like a string entry.
|
||||
const fixture = new Map([
|
||||
['{count} cameras', { plural: true, one: '{count} camera' }],
|
||||
['Save keys', { plural: false }],
|
||||
@@ -64,9 +62,8 @@ for (const [locale, entry] of [
|
||||
});
|
||||
}
|
||||
|
||||
// `npm run i18n -- --missing <code>` prints a template; pasted without edits,
|
||||
// none of its entries may pass as a translation (K-21, K-52), and a plural
|
||||
// entry carries every form the language needs (K-97).
|
||||
// The `--missing` template, pasted without edits, must fail entry by entry,
|
||||
// and its plural entries carry every form the language needs.
|
||||
const messages = extractMessages();
|
||||
for (const locale of ['de', 'ja', 'es', 'ru']) {
|
||||
test(`${locale}: an untouched --missing template fails the check entry by entry`, () => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// K-23: scripts/i18n.mjs must skip index entries that are gone from the
|
||||
// working tree (plain `mv`/`rm` without `git mv`/`git rm`).
|
||||
// scripts/i18n.mjs must skip index entries gone from the working tree
|
||||
// (plain `mv`/`rm` without `git mv`/`git rm`).
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { execFileSync } from 'node:child_process';
|
||||
|
||||
Reference in new issue
Block a user