mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 02:44:02 +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
@@ -7,6 +7,8 @@
|
||||
* integer category (states index 17) and ADS-B emitter strings ("A1".."B1").
|
||||
*/
|
||||
|
||||
import { msg } from '../i18n/marker.js';
|
||||
|
||||
const HELI = new Set([
|
||||
'EC20',
|
||||
'EC25',
|
||||
@@ -438,10 +440,6 @@ export function classifyAircraft({ typeCode, category } = {}) {
|
||||
return 'airliner';
|
||||
}
|
||||
|
||||
// Portable flight records import this module, so it has no i18n: msg() only
|
||||
// marks the labels, and the card translates them where it shows them.
|
||||
const msg = (text) => text;
|
||||
|
||||
/** Readable name of each class, for cards and readouts (English data). */
|
||||
export const AIRCRAFT_CLASS_LABELS = Object.freeze({
|
||||
light: msg('Light aircraft'),
|
||||
|
||||
@@ -13,12 +13,11 @@
|
||||
* broadcast, which does not confirm an incident or its resolution.
|
||||
*/
|
||||
|
||||
import { msg } from '../i18n/marker.js';
|
||||
|
||||
/** @constant {string} Tint for aircraft broadcasting an emergency (not a priority). */
|
||||
export const EMERGENCY_TINT_CSS = '#FF4444';
|
||||
|
||||
// Marks catalog text without importing i18n: tools and the server load this module.
|
||||
const msg = (text) => text;
|
||||
|
||||
const SQUAWK_KINDS = { 7500: 'unlawful', 7600: 'nordo', 7700: 'general' };
|
||||
|
||||
const KINDS = {
|
||||
|
||||
@@ -249,7 +249,7 @@ export function createAisWatchdog(options = {}) {
|
||||
if (!env.hasKey) {
|
||||
const actions = terminateOwned('key-absent');
|
||||
status = 'missing-key';
|
||||
// i18n-ignore: server detail; the chip shows a translated reason (vessels/queries.js)
|
||||
// i18n-ignore: server detail; layers/vessels/queries.js shows a translated reason
|
||||
error = 'AISSTREAM_API_KEY is not set';
|
||||
reconnectAttempt = 0;
|
||||
keyFingerprint = null;
|
||||
@@ -258,7 +258,7 @@ export function createAisWatchdog(options = {}) {
|
||||
if (env.hasTransport === false) {
|
||||
const actions = terminateOwned('no-transport');
|
||||
status = 'unsupported';
|
||||
// i18n-ignore: server detail; the chip shows a translated reason (vessels/queries.js)
|
||||
// i18n-ignore: server detail; layers/vessels/queries.js shows a translated reason
|
||||
error = 'Node WebSocket transport is unavailable';
|
||||
return actions;
|
||||
}
|
||||
@@ -331,7 +331,7 @@ export function createAisWatchdog(options = {}) {
|
||||
if (!silenceWatchArmed) return [];
|
||||
const silentFor = monoNow - silenceSinceMono;
|
||||
if (silentFor >= recycleAfterMs) {
|
||||
// i18n-ignore: server detail; the chip shows a translated reason (vessels/queries.js)
|
||||
// i18n-ignore: server detail; layers/vessels/queries.js shows a translated reason
|
||||
error = `AISStream delivered no data for ${Math.round(silentFor / 1000)}s`;
|
||||
const actions = terminateOwned('silent');
|
||||
scheduleRetry('transport');
|
||||
@@ -404,7 +404,7 @@ export function createAisWatchdog(options = {}) {
|
||||
function onClose(eventGeneration) {
|
||||
if (!ownsGeneration(eventGeneration)) return [];
|
||||
release();
|
||||
// i18n-ignore: server detail; the chip shows a translated reason (vessels/queries.js)
|
||||
// i18n-ignore: server detail; layers/vessels/queries.js shows a translated reason
|
||||
if (!error) error = 'AISStream websocket closed';
|
||||
scheduleRetry('transport');
|
||||
return [];
|
||||
|
||||
@@ -40,10 +40,8 @@ const MEDIA_PRECONNECT_ORIGINS = Object.freeze({
|
||||
facebook: Object.freeze(['https://www.facebook.com']),
|
||||
});
|
||||
|
||||
// Authored English in public/events/bhote-koshi-2026/event.json. The pack is
|
||||
// data and stays English; these known strings translate at display. Anything
|
||||
// else (a newer pack, a credit, a source's own title) shows as authored. A
|
||||
// string that is not known whole translates by its " · " parts.
|
||||
// Known English from public/events/bhote-koshi-2026/event.json, translated at
|
||||
// display (whole or by " · " parts). Anything else shows as authored.
|
||||
const EVENT_PACK_TEXT = new Set([
|
||||
// Field reports: our labels (credits after " · " stay) and their statuses.
|
||||
msg('Rasuwagadhi witness video'),
|
||||
|
||||
@@ -826,11 +826,9 @@ export function updateEvidenceFrame(
|
||||
const EVIDENCE_SOURCE_MIN_FONT_SCALE = 0.8;
|
||||
|
||||
/**
|
||||
* Set the font for the «{publisher} / OPEN» line and return the line so it
|
||||
* fits `maxWidth`: a translation («ИСТОЧНИК YOUTUBE / ОТКРЫТЬ») is longer than
|
||||
* the English and the portrait card cut its action word. A line that fits is
|
||||
* drawn exactly as before; one that does not first shrinks its font (to 80%),
|
||||
* then shortens the publisher — the «/ OPEN» action always stays whole.
|
||||
* Set the font for the «{publisher} / OPEN» line and return a line that fits
|
||||
* `maxWidth`. Translations run longer: first shrink the font (to 80%), then
|
||||
* shorten the publisher; «/ OPEN» always stays whole.
|
||||
* @param {CanvasRenderingContext2D} ctx
|
||||
* @param {string} publisher Display publisher, already upper-cased.
|
||||
* @param {string} font CSS font with a px size.
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Proposal 5 / E4: the evidence frame's «{publisher} / OPEN» line is drawn on
|
||||
// a canvas, so CSS cannot widen it; on the portrait card the Russian
|
||||
// «ИСТОЧНИК YOUTUBE / ОТКРЫТЬ» lost its last letter. The line is measured and
|
||||
// shrunk to fit, keeping the action word whole; English draws as before.
|
||||
// The «{publisher} / OPEN» line is drawn on a canvas, so CSS cannot wrap it:
|
||||
// a long translation shrinks to fit the portrait card, action word whole.
|
||||
// English is drawn at full size.
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import test from 'node:test';
|
||||
|
||||
@@ -2,8 +2,8 @@ import assert from 'node:assert/strict';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import test from 'node:test';
|
||||
|
||||
// K-30: the publisher on the evidence frame ("YouTube source") is pack text
|
||||
// and is shown in the UI language; English stays "YOUTUBE SOURCE / OPEN".
|
||||
// The publisher on the evidence frame ("YouTube source") is pack text shown
|
||||
// in the UI language; English stays "YOUTUBE SOURCE / OPEN".
|
||||
import { setLocale } from '../i18n/index.js';
|
||||
import { createEvidenceFrame } from './bhoteKoshiEvent.js';
|
||||
|
||||
|
||||
@@ -52,9 +52,8 @@ const TRIGGER_ORBIT_DURATION_SEC = 2.8;
|
||||
const TRIGGER_SOURCE_URL =
|
||||
'https://earthquake.usgs.gov/earthquakes/eventpage/us7000tbwb/executive';
|
||||
|
||||
// Overview pin titles that are more than a place name translate at display,
|
||||
// through the same entries the event panel uses. The data stays English and
|
||||
// place names stay Latin; everything else shows as authored.
|
||||
// Descriptive pin titles translate at display (same entries as the event
|
||||
// panel); place names stay Latin, anything else shows as authored.
|
||||
const PLACE_TITLE_TEXT = new Map(
|
||||
[
|
||||
msg('Collapse viewpoint'),
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
import * as Cesium from 'cesium';
|
||||
import { createImportImageBudget } from './geoImportImages.js';
|
||||
import { createImportedPins } from './geoImportPins.js';
|
||||
|
||||
// Marks catalog text without importing i18n; the UI translates the message.
|
||||
const msg = (text) => text;
|
||||
import { msg } from '../i18n/marker.js';
|
||||
|
||||
/** Largest file accepted, in bytes. */
|
||||
export const MAX_IMPORT_BYTES = 50 * 1024 * 1024;
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// Portable node/browser module (the HUD summary in loadingFeedback.js reads
|
||||
// it too), so it marks its copy with a local msg() instead of importing i18n;
|
||||
// installations/controls.js translates describeInstallationFeedback().
|
||||
const msg = (text) => text;
|
||||
import { msg } from '../i18n/marker.js';
|
||||
|
||||
// layers/installations/controls.js translates describeInstallationFeedback().
|
||||
const REASONS = Object.freeze({
|
||||
rate_limited: msg('Overpass rate-limited'),
|
||||
timeout: msg('Overpass timed out'),
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
import { msg } from '../i18n/marker.js';
|
||||
|
||||
/**
|
||||
* The English names the data gives a military installation that has no
|
||||
* mapped name. They stay English in data — the server provider, the AI tools
|
||||
* and the voice context read the records — and the display translates them
|
||||
* (installationTitle in src/layers/installations/classLabel.js and
|
||||
* formatAwarenessLabel in src/data/militaryAwarenessEngine.js).
|
||||
*
|
||||
* The server, tools and source modules load this file, so it marks the names
|
||||
* with a local msg() instead of importing i18n.
|
||||
* English names for installations without a mapped name. Data keeps them
|
||||
* English (server, AI tools, voice context); classLabel.js and
|
||||
* militaryAwarenessEngine.js translate them.
|
||||
*/
|
||||
const msg = (text) => text;
|
||||
|
||||
/** Name of an unnamed feature, by installation class. */
|
||||
export const UNNAMED_INSTALLATION_LABEL_BY_CLASS = Object.freeze({
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Marks restore labels for i18n extraction; shareRestoration translates them.
|
||||
// Kept local so this module stays free of the i18n import.
|
||||
// Restore labels are marked with msg(); ui/shareRestoration.js translates them.
|
||||
// Local, not i18n/marker.js: the token-ledger tests run this file in isolation.
|
||||
const msg = (text) => text;
|
||||
import { normalizeIdSwitches, patchRequestedIdSwitches } from './idSwitches.js';
|
||||
import reservationRows from './layerStateTokenReservations.json' with { type: 'json' };
|
||||
|
||||
@@ -32,13 +32,6 @@ const CLASS_BY_MILITARY_TAG = {
|
||||
const LABEL_BY_CLASS = UNNAMED_INSTALLATION_LABEL_BY_CLASS;
|
||||
const UNNAMED_FALLBACK = UNNAMED_INSTALLATION_FALLBACK;
|
||||
|
||||
/** Every name humanizeInstallationClass gives a record of a class
|
||||
* CLASS_BY_MILITARY_TAG can produce, for the display to translate. */
|
||||
export const UNNAMED_INSTALLATION_NAMES = Object.freeze([
|
||||
...Object.values(LABEL_BY_CLASS),
|
||||
UNNAMED_FALLBACK,
|
||||
]);
|
||||
|
||||
const MAX_FOOTPRINT_POINTS = 400;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
/**
|
||||
* The tool name to show for a pointer owner.
|
||||
*
|
||||
* Pointer-owner ids (src/data/inputOwnership.js) are stable internal
|
||||
* identifiers — 'draw', 'recent-imagery-box' — and never belong on screen.
|
||||
* This maps each to the English name of the tool, which the caller translates
|
||||
* with translateKnown() where it is shown. Pure and stateless, so any package
|
||||
* can carry it without a second copy of the pointer claim.
|
||||
*/
|
||||
import { msg } from '../i18n/marker.js';
|
||||
|
||||
// English marker for the i18n extractor; this module imports no i18n.
|
||||
const msg = (text) => text;
|
||||
/**
|
||||
* Tool name for a pointer owner. Owner ids (inputOwnership.js) never go on
|
||||
* screen; callers translate the English name with translateKnown().
|
||||
*/
|
||||
|
||||
const POINTER_OWNER_NAMES = Object.freeze({
|
||||
draw: msg('Draw'),
|
||||
|
||||
@@ -20,53 +20,43 @@
|
||||
import { t, msg } from '../i18n/index.js';
|
||||
|
||||
/**
|
||||
* Class registry. `label` is the card/legend token, `color` the point color,
|
||||
* `blurb` the plain-language gloss for the legend tooltip.
|
||||
* @type {Readonly<Record<string, { label: string, color: string, blurb: string }>>}
|
||||
* Class registry: the point color of each class. The card and legend text is
|
||||
* in CLASS_DISPLAY_KEYS.
|
||||
* @type {Readonly<Record<string, { color: string }>>}
|
||||
*/
|
||||
export const SATELLITE_CLASSES = Object.freeze({
|
||||
station: Object.freeze({
|
||||
label: 'STATION', // i18n-ignore: English token; shown through CLASS_DISPLAY_KEYS
|
||||
// Warm white — "crewed", and the brightest class in the sky. Deliberately
|
||||
// NOT amber: 40-48deg amber is the app's known-military convention
|
||||
// (flights.js MIL_TINT / militaryFlights.js MIL_ICON_COLOR) and must not
|
||||
// leak into another domain. Slightly warm so it also separates from the
|
||||
// pure white the civilian air fleet draws in.
|
||||
color: '#fff6e5',
|
||||
blurb: 'Crewed stations and their visiting vehicles', // i18n-ignore: shown through CLASS_DISPLAY_KEYS
|
||||
}),
|
||||
nav: Object.freeze({
|
||||
label: 'NAV', // i18n-ignore: English token; shown through CLASS_DISPLAY_KEYS
|
||||
// Cyan — the strongest accent goes to the class users actually name
|
||||
// ("how many GPS satellites are up?"). Takes over the slot the catch-all
|
||||
// VISUAL group used to occupy, so this adds no new hue to the app.
|
||||
color: '#4fd8ff',
|
||||
blurb: 'GNSS navigation — GPS, GLONASS, Galileo', // i18n-ignore: shown through CLASS_DISPLAY_KEYS
|
||||
}),
|
||||
geo: Object.freeze({
|
||||
label: 'GEO', // i18n-ignore: English token; shown through CLASS_DISPLAY_KEYS
|
||||
// Violet, unchanged. Already the app's "space" semantic (the detection
|
||||
// overlay paints the SAT tier #bda4ff), and the geostationary belt draws
|
||||
// as one clean equatorial ring at globe scale.
|
||||
color: '#c89bff',
|
||||
blurb: 'Geostationary belt — comms and weather, fixed over the equator', // i18n-ignore: shown through CLASS_DISPLAY_KEYS
|
||||
}),
|
||||
visual: Object.freeze({
|
||||
label: 'VISUAL', // i18n-ignore: English token; shown through CLASS_DISPLAY_KEYS
|
||||
// Muted blue-gray. This is the catch-all bucket, so it gives up the bright
|
||||
// cyan it used to hold and recedes behind the three classes that mean
|
||||
// something. Still well clear of the dense shell by luminance.
|
||||
color: '#9fb3c4',
|
||||
blurb: 'Brightest naked-eye objects — CelesTrak visual group', // i18n-ignore: shown through CLASS_DISPLAY_KEYS
|
||||
}),
|
||||
comms: Object.freeze({
|
||||
label: 'COMMS', // i18n-ignore: English token; shown through CLASS_DISPLAY_KEYS
|
||||
// Dim slate — thousands of these appear in DENSE mode. They must read as
|
||||
// texture behind the core catalog, never compete with it. Rec.601 luma
|
||||
// ~0.40 against VISUAL's ~0.69 keeps the two separable even when the
|
||||
// NVG/FLIR shaders collapse the scene to a single channel.
|
||||
color: '#54697f',
|
||||
blurb: 'Broadband constellation shell — shown only in DENSE mode', // i18n-ignore: shown through CLASS_DISPLAY_KEYS
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -79,9 +69,7 @@ export const SATELLITE_CLASS_ORDER = Object.freeze([
|
||||
'comms',
|
||||
]);
|
||||
|
||||
// Display keys for the class table above: `label`/`blurb` stay the English
|
||||
// source-of-truth tokens (tests read them), while the card and legend accessors
|
||||
// translate through these keys at the presentation boundary.
|
||||
// What the card and legend show for each class.
|
||||
const CLASS_DISPLAY_KEYS = Object.freeze({
|
||||
station: Object.freeze({
|
||||
label: msg('STATION'),
|
||||
@@ -164,7 +152,8 @@ export function satelliteClassColor(group) {
|
||||
* The ISS is always a STATION regardless of which group ingested it — see
|
||||
* satelliteClassOf, which owns that rule for every consumer.
|
||||
* @param {string|undefined|null} group CelesTrak group tag.
|
||||
* @param {{ isIss?: boolean }} [options] Whether this is the ISS.
|
||||
* @param {{ isIss?: boolean, localize?: boolean }} [options] isIss: the ISS;
|
||||
* localize: false keeps the English label (data for the AI tools).
|
||||
* @returns {string} Display label.
|
||||
*/
|
||||
export function satelliteClassLabel(
|
||||
@@ -172,7 +161,6 @@ export function satelliteClassLabel(
|
||||
{ isIss = false, localize = true } = {},
|
||||
) {
|
||||
const { klass, subtype } = satelliteClassOf(group, { isIss });
|
||||
// localize: false keeps the English label for data the AI tools query.
|
||||
const label = CLASS_DISPLAY_KEYS[klass].label;
|
||||
const base = localize ? t(label) : label;
|
||||
return subtype ? `${base} · ${subtype}` : base;
|
||||
|
||||
@@ -48,9 +48,8 @@ function run(locale) {
|
||||
return details;
|
||||
}
|
||||
|
||||
test('K-32 es: tracked ISS card', () => {
|
||||
test('es: tracked ISS card is localized', () => {
|
||||
const details = run('es');
|
||||
console.log('es details:', JSON.stringify(details));
|
||||
assert.equal(details[0], 'ESTACIÓN · ISS');
|
||||
assert.doesNotMatch(
|
||||
details[2],
|
||||
@@ -59,9 +58,8 @@ test('K-32 es: tracked ISS card', () => {
|
||||
);
|
||||
});
|
||||
|
||||
test('K-32 ru: tracked ISS card', () => {
|
||||
test('ru: tracked ISS card is localized', () => {
|
||||
const details = run('ru');
|
||||
console.log('ru details:', JSON.stringify(details));
|
||||
assert.equal(details[0], 'СТАНЦИЯ · ISS');
|
||||
assert.match(details[1], /км/, 'ru glossary: km → км');
|
||||
assert.doesNotMatch(
|
||||
|
||||
@@ -68,9 +68,8 @@ const NUMERIC_TYPE_FAMILIES = {
|
||||
9: 'OTHER',
|
||||
};
|
||||
|
||||
// Display keys for the normalized English type tokens above. The tokens stay
|
||||
// machine values — TYPE_STYLES matches on them and tests pin them — so card
|
||||
// text translates only through this map at the presentation boundary.
|
||||
// Card text for the type tokens above. The tokens stay English: TYPE_STYLES
|
||||
// and tests match on them.
|
||||
const TYPE_DISPLAY_KEYS = Object.freeze({
|
||||
FISHING: msg('FISHING'),
|
||||
TOWING: msg('TOWING'),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// K-12: a translated vessel type is never cut mid-word on the card.
|
||||
// A translated vessel type is never cut mid-word on the card.
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import * as Cesium from 'cesium';
|
||||
|
||||
Reference in new issue
Block a user