From a28e29e05e9487c447595aa824c67bc59fa3653b Mon Sep 17 00:00:00 2001 From: German Khokhlov Date: Tue, 13 Oct 2026 20:00:03 +0400 Subject: [PATCH] fix(i18n): restore English output changed by the i18n foundation A side-by-side English comparison with main found text the branch changed: - Layer list names: #601 let its own catalog names win over the panel's English names, so "Live Vessels" read "Live AIS Vessels", "Bike Share" "Bikeshare", "Cameras" "CCTV", "Data Centers" "Datacenters", "Active Fires" "FIRMS Active Fires". The panel names win again. - HUD feed-state suffix is "UNAVAILABLE LIVE AIS VESSELS" again (Russian keeps its colon); the mission panel aria-label is "Selected Space Mission" again; scene shot timing is "4.0s + 0.9s" again. Co-Authored-By: Claude Opus 5.5 --- src/hud.js | 2 +- src/i18n/locales/es.json | 6 ++++-- src/i18n/locales/ru.json | 6 ++++-- src/layers/launches/panel.js | 2 +- src/ui/layerPanel.js | 5 +++-- src/ui/layerPanel.test.mjs | 2 +- src/ui/scenePresentation.js | 12 ++++++++---- 7 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/hud.js b/src/hud.js index 6c157ddd3..400a4d51e 100644 --- a/src/hud.js +++ b/src/hud.js @@ -113,7 +113,7 @@ function localizedProvenanceTag(provenance) { ? t(FEED_STATE_LABELS[provenance.state]) : provenance.state.toUpperCase(); if (!provenance.names.length) return state; - return t('{state}: {layers}', { + return t('{state} {layers}', { state, layers: provenance.names .map((name) => localizeLayerName(name).toUpperCase()) diff --git a/src/i18n/locales/es.json b/src/i18n/locales/es.json index 4245a62e3..c9459c31c 100644 --- a/src/i18n/locales/es.json +++ b/src/i18n/locales/es.json @@ -2010,7 +2010,6 @@ "Box and images cleared": "Recuadro e imágenes borrados", "Box is {size} wide · limit {limit}": "El recuadro mide {size} · límite {limit}", "View is {size} wide · limit {limit}": "La vista mide {size} · límite {limit}", - "{state}: {layers}": "{state}: {layers}", "{mode} · {band} · {locality} | {region} | ALT {alt} | WINDOW {window} | SUN {sun}° | ONA {ona}° | {zone}": "{mode} · {band} · {locality} | {region} | ALT {alt} | VENTANA {window} | SUN {sun}° | ONA {ona}° | {zone}", "Frame unavailable; previous observation retained": "Fotograma no disponible; se mantiene la observación anterior", "Weather imagery unavailable": "Imágenes meteorológicas no disponibles", @@ -2023,5 +2022,8 @@ "Globe imagery unavailable": "Imágenes del globo no disponibles", "Globe field image unavailable": "Imagen del campo en el globo no disponible", "status chip|UNAVAILABLE": "NO DISP.", - "Uses registered content or linked media. Those external files are not included in a scene bundle. Their original notices still apply.": "Usa contenido registrado o medios vinculados. Esos archivos externos no se incluyen en un paquete de escena. Sus avisos originales siguen vigentes." + "Uses registered content or linked media. Those external files are not included in a scene bundle. Their original notices still apply.": "Usa contenido registrado o medios vinculados. Esos archivos externos no se incluyen en un paquete de escena. Sus avisos originales siguen vigentes.", + "{state} {layers}": "{state}: {layers}", + "Selected Space Mission": "Misión espacial seleccionada", + "{duration}s + {hold}s": "{duration} s + {hold} s" } diff --git a/src/i18n/locales/ru.json b/src/i18n/locales/ru.json index 223db7d44..bb32eb792 100644 --- a/src/i18n/locales/ru.json +++ b/src/i18n/locales/ru.json @@ -2042,7 +2042,6 @@ "Box and images cleared": "Область и снимки сброшены", "Box is {size} wide · limit {limit}": "Область шириной {size} · предел {limit}", "View is {size} wide · limit {limit}": "Вид шириной {size} · предел {limit}", - "{state}: {layers}": "{state}: {layers}", "{mode} · {band} · {locality} | {region} | ALT {alt} | WINDOW {window} | SUN {sun}° | ONA {ona}° | {zone}": "{mode} · {band} · {locality} | {region} | ALT {alt} | ОКНО {window} | SUN {sun}° | ONA {ona}° | {zone}", "Frame unavailable; previous observation retained": "Кадр недоступен; показано предыдущее наблюдение", "Weather imagery unavailable": "Погодные снимки недоступны", @@ -2055,5 +2054,8 @@ "Globe imagery unavailable": "Снимки глобуса недоступны", "Globe field image unavailable": "Изображение поля на глобусе недоступно", "status chip|UNAVAILABLE": "НЕДОСТ.", - "Uses registered content or linked media. Those external files are not included in a scene bundle. Their original notices still apply.": "Используется зарегистрированный контент или связанные медиафайлы. Эти внешние файлы не входят в архив сцены. Их исходные уведомления по-прежнему действуют." + "Uses registered content or linked media. Those external files are not included in a scene bundle. Their original notices still apply.": "Используется зарегистрированный контент или связанные медиафайлы. Эти внешние файлы не входят в архив сцены. Их исходные уведомления по-прежнему действуют.", + "{state} {layers}": "{state}: {layers}", + "Selected Space Mission": "Выбранная космическая миссия", + "{duration}s + {hold}s": "{duration} с + {hold} с" } diff --git a/src/layers/launches/panel.js b/src/layers/launches/panel.js index 65ee3fbdc..291a0ecba 100644 --- a/src/layers/launches/panel.js +++ b/src/layers/launches/panel.js @@ -470,7 +470,7 @@ export function createPanel({ state: layerState, services, parts, source }) { layerState._missionPanel.className = 'context-space-mission-detail'; layerState._missionPanel.setAttribute( 'aria-label', - t('SELECTED SPACE MISSION'), + t('Selected Space Mission'), ); layerState._missionPanel.innerHTML = `
${t('SELECTED SPACE MISSION')}
${t('MISSION')}${t('STATUS · ')}${t('LAUNCH SITE · ')}${t('LAUNCH TIME · ')}${t('ORBIT · ')}${t('ASCENT PATH · ')}${t('CURRENT DISTANCE FROM EARTH · ')}${t('SATELLITE SPEED · ')}

${t('PAYLOAD')}

${t('NAME')}${t('TYPE')}${t('DESTINATION')}

${t('STAGE / RE-ENTRY / RECOVERY')}

${t('STAGE')}${t('STATUS')}${t('FINAL POSITION')}
1×
—
`; layerState._missionPanel diff --git a/src/ui/layerPanel.js b/src/ui/layerPanel.js index f32cb2b73..75cecad62 100644 --- a/src/ui/layerPanel.js +++ b/src/ui/layerPanel.js @@ -129,10 +129,11 @@ const PANEL_LABELS = { 'local-firms': msg('Active Fires'), }; +// The panel's own English names win, as before i18n; module names otherwise. function panelLabel(layer) { + if (PANEL_LABELS[layer.id]) return t(PANEL_LABELS[layer.id]); const key = LAYER_NAME_KEYS[layer.id]; - if (key) return t(key); - return PANEL_LABELS[layer.id] ? t(PANEL_LABELS[layer.id]) : layer.name; + return key ? t(key) : layer.name; } // English module names (layer.name) that other surfaces print, such as the diff --git a/src/ui/layerPanel.test.mjs b/src/ui/layerPanel.test.mjs index aa4d30f28..5e7305f80 100644 --- a/src/ui/layerPanel.test.mjs +++ b/src/ui/layerPanel.test.mjs @@ -327,5 +327,5 @@ test('the unavailable chip may abbreviate, but its accessible name keeps the ful }); assert.equal(button.textContent, 'UNAVAILABLE'); assert.equal(button.dataset.feedState, 'unavailable'); - assert.match(attrs.get('aria-label'), /^FIRMS Active Fires: UNAVAILABLE\./); + assert.match(attrs.get('aria-label'), /^Active Fires: UNAVAILABLE\./); }); diff --git a/src/ui/scenePresentation.js b/src/ui/scenePresentation.js index 727975a6b..258ee5059 100644 --- a/src/ui/scenePresentation.js +++ b/src/ui/scenePresentation.js @@ -1,6 +1,5 @@ /** Scene panel elements and text-only project presentation. */ -import { msg, t } from '../i18n/index.js'; -import { formatQuantity } from './unitLabels.js'; +import { formatNumber, msg, t } from '../i18n/index.js'; // Labels for what a shot stores; anything else shows as stored. Style names // match STYLE_STATUS_LABELS in visualPresets.js, which this package cannot @@ -145,11 +144,16 @@ export function renderSceneShots( ? t(DETECTION_MODE_LABELS[mode]) : mode; const seconds = (value) => - formatQuantity(value, 's', { + formatNumber(value, { minimumFractionDigits: 1, maximumFractionDigits: 1, }); - meta.textContent = `${styleLabel} · ${modeLabel} · ${seconds(shot.durationSec)} + ${seconds(shot.holdSec)}`; + // English keeps its compact "4.0s + 0.9s"; other languages space the unit. + const timing = t('{duration}s + {hold}s', { + duration: seconds(shot.durationSec), + hold: seconds(shot.holdSec), + }); + meta.textContent = `${styleLabel} · ${modeLabel} · ${timing}`; row.appendChild(top); row.appendChild(meta); element.appendChild(row);