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

+17 -11
View File
@@ -1,4 +1,6 @@
/** Pure formatting and fixed presentation constants used by Cockpit components. */
import { t } from '../i18n/index.js';
export const COCKPIT_HEADING_SLEW_DPS = 28;
export const COCKPIT_FORWARD_OFFSET_M = 7;
@@ -44,20 +46,22 @@ export const COCKPIT_REGIONAL_REFRESH_DISTANCE_M = 25_000;
export const COCKPIT_BRIEF_PAGES = [
{
id: 'signals',
kicker: 'LIVE SIGNALS',
subtitle: 'OBSERVED / MAPPED PINGS',
source: 'SOURCE-BACKED EVENTS · NO SYNTHETIC NEWS',
kickerKey: 'cockpit.brief.kicker',
subtitleKey: 'cockpit.brief.subtitle',
sourceKey: 'cockpit.brief.sourceNote',
},
{
id: 'news',
kicker: 'REGIONAL NEWS',
subtitle: 'LATEST LOCATION-MATCHED REPORTING',
kickerKey: 'cockpit.brief.kickerNews',
subtitleKey: 'cockpit.brief.subtitleNews',
// RSS provider attribution is a keep-English boundary string.
source: 'GOOGLE NEWS RSS · LOCATION QUERY · RECENT',
},
{
id: 'local',
kicker: 'LOCAL INFO',
subtitle: 'PLACE / CONDITIONS / POSITION',
kickerKey: 'cockpit.brief.kickerLocal',
subtitleKey: 'cockpit.brief.subtitleLocal',
// Data-source attribution is a keep-English boundary string.
source: 'NATURAL EARTH · OPEN-METEO · UTC',
},
];
@@ -80,15 +84,17 @@ export function isRenderedOnScreen(element) {
export function formatCockpitBriefAge(value) {
const timestamp = Date.parse(value);
if (!Number.isFinite(timestamp)) return 'TIME UNKNOWN';
if (!Number.isFinite(timestamp)) return t('cockpit.brief.age.timeUnknown');
const minutes = Math.max(0, Math.round((Date.now() - timestamp) / 60_000));
if (minutes < 60) return `${minutes}M AGO`;
if (minutes < 60) return t('cockpit.brief.age.minutes', { count: minutes });
const hours = Math.round(minutes / 60);
return hours < 48 ? `${hours}H AGO` : `${Math.round(hours / 24)}D AGO`;
return hours < 48
? t('cockpit.brief.age.hours', { count: hours })
: t('cockpit.brief.age.days', { count: Math.round(hours / 24) });
}
export function formatCockpitWindDirection(value) {
if (!Number.isFinite(value)) return 'DIR UNKNOWN';
if (!Number.isFinite(value)) return t('cockpit.brief.wind.dirUnknown');
const labels = ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW'];
const normalized = ((value % 360) + 360) % 360;
return `${labels[Math.round(normalized / 45) % labels.length]} · ${Math.round(normalized)}°`;