feat(i18n): presentation extraction — t() call sites, data-i18n templates, locale selector

Route every application-owned presentation string through the i18n
core: t() call sites across the app/layers/ui/data/voice surfaces,
data-i18n* attributes in the src/ui/templates/*.html markup (applied at
boot by applyDocumentTranslations from src/standalone/application.js),
and the dock locale selector (runtime-rendered from the offered pair —
a single EN button while only English ships).

Rebased onto current main, where the extraction follows the module
splits: the shell wiring now lands in the owner modules
(panelChrome, visualSettings, locationNavigation, shareRestoration,
shellFeedback, applicationShell) and the voice status corpus in the
realtime* modules; scene status lives in director.js +
playbackAdapter.js.

Modules shared with the node runtime (loadingFeedback,
installationFeedback, regionalModel, keySetupCore, vessels ingestion)
stay environment-neutral English and localize at their browser render
sites (chip maps in shellFeedback/installations controls, the weather
table in cockpitBriefing, keySetupRequirementEnvVars compositions), so
the package-boundary graph stays clean; the browser boundary groups
now own the i18n modules they reach (scripts/package-boundaries.json).
This commit is contained in:
Meir Michanie committed 2026-10-01 12:41:20 +02:00
1 parent bde1e430ef
commit 284c4d4601
98 files changed
+3474 -1625

No files matched your search

+55 -6
View File
@@ -5,6 +5,53 @@ import {
DISTANCE_PREFILTER_MARGIN_M,
ANCHOR_REFRESH_M,
} from './policy.js';
import { t } from '../../i18n/index.js';
// installationFeedback.js composes its guidance inside the portable
// node/browser graph (the server-side HUD summary reads the same module), so
// its copy stays English there. getStats is the browser presentation
// boundary: map the closed message vocabulary to the catalog, passing any
// live provider error through verbatim (docs/TRANSLATORS.md).
const INSTALLATION_FEEDBACK_KEYS = Object.freeze({
'Retrying mapped sites…': 'layers.installations.feedback.retrying',
'Fetching mapped sites…': 'layers.installations.feedback.fetching',
'Zoom in to search mapped installations':
'layers.installations.feedback.zoomIn',
'Showing cached mapped sites': 'layers.installations.feedback.cached',
'Mapped sites not loaded': 'layers.installations.feedback.notLoaded',
'Mapped sites loaded': 'layers.installations.feedback.loaded',
'Overpass rate-limited': 'layers.installations.feedback.reason.rateLimited',
'Overpass timed out': 'layers.installations.feedback.reason.timeout',
'Overpass could not complete the query':
'layers.installations.feedback.reason.queryFailed',
'Overpass temporarily unavailable':
'layers.installations.feedback.reason.unavailable',
});
const INSTALLATION_RETRY_IN_RE = /^(.*) — retrying in (\d+)s$/;
const INSTALLATION_RETRY_PENDING_RE = /^(.*) — retry pending$/;
function localizeInstallationFeedback(message) {
if (typeof message !== 'string' || !message) return message;
const key = INSTALLATION_FEEDBACK_KEYS[message];
if (key) return t(key);
const retryIn = message.match(INSTALLATION_RETRY_IN_RE);
if (retryIn) {
const reasonKey = INSTALLATION_FEEDBACK_KEYS[retryIn[1]];
return t('layers.installations.feedback.retryIn', {
reason: reasonKey ? t(reasonKey) : retryIn[1],
seconds: retryIn[2],
});
}
const pending = message.match(INSTALLATION_RETRY_PENDING_RE);
if (pending) {
const reasonKey = INSTALLATION_FEEDBACK_KEYS[pending[1]];
return t('layers.installations.feedback.retryPending', {
reason: reasonKey ? t(reasonKey) : pending[1],
});
}
return message;
}
export function createControls({ state: layerState, services, parts, source }) {
const methods = {
@@ -196,13 +243,15 @@ export function createControls({ state: layerState, services, parts, source }) {
retryAt: layerState.retryAt,
retrying: layerState.loading && Boolean(layerState.failureReason),
failureReason: layerState.failureReason,
statusMessage: installationFeedback({
...layerState,
count: layerState.lastUpdate ? mappedCount : undefined,
retrying: layerState.loading && Boolean(layerState.failureReason),
}),
statusMessage: localizeInstallationFeedback(
installationFeedback({
...layerState,
count: layerState.lastUpdate ? mappedCount : undefined,
retrying: layerState.loading && Boolean(layerState.failureReason),
}),
),
loadingLabel: layerState.loading
? 'loading mapped installation context'
? t('layers.installations.loading')
: '',
coverage: layerState.coverage,
coverageLabel: