From d60c2bde37f3de7e235b7df81190fcf5471165b8 Mon Sep 17 00:00:00 2001 From: IAnMove <216241348+IAnMove@users.noreply.github.com> Date: Wed, 2 Sep 2026 11:21:15 +0200 Subject: [PATCH] fix: localize Extra info chrome and Assets inspector Wire leftover Extra info labels to activity.extraInfo and move the Assets Extra info inspector internals into activity.inspector keys. --- ui/scripts/check-i18n-catalogs.mjs | 4 + .../components/MainContent/MediaFeedItem.tsx | 4 +- .../MainContent/VideoExtraInfoDialog.tsx | 4 +- .../components/MainContent/VideoInfoBar.tsx | 4 +- ui/src/features/assets/AssetsPanel.tsx | 74 ++++++++++++++++++- ui/src/i18n/locales/en/activity.json | 13 ++++ ui/src/i18n/locales/es/activity.json | 13 ++++ ui/tests/assetsCatalog.test.mjs | 6 +- ui/tests/i18nFoundation.test.tsx | 37 ++++++++++ 9 files changed, 152 insertions(+), 7 deletions(-) diff --git a/ui/scripts/check-i18n-catalogs.mjs b/ui/scripts/check-i18n-catalogs.mjs index 5c4c93ff..5898f7e1 100644 --- a/ui/scripts/check-i18n-catalogs.mjs +++ b/ui/scripts/check-i18n-catalogs.mjs @@ -34,6 +34,9 @@ export function catalogReport() { const PILOT_FILES = [ 'src/components/MainContent/TabFilter.tsx', 'src/components/MainContent/MainContent.tsx', + 'src/components/MainContent/MediaFeedItem.tsx', + 'src/components/MainContent/VideoExtraInfoDialog.tsx', + 'src/components/MainContent/VideoInfoBar.tsx', 'src/components/SettingsDrawer/SettingsDrawer.tsx', 'src/components/SettingsDrawer/SystemSettingsPanel.tsx', 'src/components/ActivityFooter.tsx', @@ -48,6 +51,7 @@ const FORBIDDEN = [ 'Output folders', 'Pregunta al mago', 'Carpetas de salida', + 'Extra info', ] export function forbiddenLiterals() { diff --git a/ui/src/components/MainContent/MediaFeedItem.tsx b/ui/src/components/MainContent/MediaFeedItem.tsx index 5e94415c..8684aaf6 100644 --- a/ui/src/components/MainContent/MediaFeedItem.tsx +++ b/ui/src/components/MainContent/MediaFeedItem.tsx @@ -2,6 +2,7 @@ import { useState, useRef, useEffect, useCallback, useMemo, type CSSProperties } import { Play, Pencil, RefreshCw, Copy, Trash2, Check, Combine, Loader2, Heart, ArrowLeftToLine, Download, FolderInput, Scissors, FastForward, BookMarked, BookOpen, Box, Film, BadgeInfo, Clock3 } from 'lucide-react' import { SaveRecipeDialog } from '../Recipes/SaveRecipeDialog' import { VideoExtraInfoDialog } from './VideoExtraInfoDialog' +import { useUiTranslation } from '../../i18n' import { useStore } from '../../stores/useStore' import { getStoredAssetUrl, fetchOutputMetadata, getFileUrl, moveOutput, uploadImage, loadComicProject, selectPipelineClipVideo } from '../../api/client' import type { OutputFile, OutputMetadata } from '../../types' @@ -83,6 +84,7 @@ function RetryImage({ url, alt }: { url: string; alt: string }) { } export function MediaFeedItem({ file, index, isActive, onVisible, onMeasured, style }: Props) { + const { t } = useUiTranslation('activity') const setSelectedOutput = useStore(s => s.setSelectedOutput) const setMediaFilter = useStore(s => s.setMediaFilter) const loadSettingsFromOutput = useStore(s => s.loadSettingsFromOutput) @@ -864,7 +866,7 @@ export function MediaFeedItem({ file, index, isActive, onVisible, onMeasured, st title="Generate descriptions and social copy from saved prompts" > - Extra info + {t('extraInfo')} )} diff --git a/ui/src/components/MainContent/VideoExtraInfoDialog.tsx b/ui/src/components/MainContent/VideoExtraInfoDialog.tsx index f0e7bfb3..b5b78d68 100644 --- a/ui/src/components/MainContent/VideoExtraInfoDialog.tsx +++ b/ui/src/components/MainContent/VideoExtraInfoDialog.tsx @@ -2,6 +2,7 @@ import { useEffect, useState } from 'react' import { createPortal } from 'react-dom' import { BadgeInfo, CalendarDays, Check, Clock3, Copy, FileVideo2, Languages, Loader2, MessageSquareText, RefreshCw, SlidersHorizontal, Sparkles, X, Youtube } from 'lucide-react' import { fetchVideoExtraInfo, generateVideoExtraInfo } from '../../api/client' +import { useUiTranslation } from '../../i18n' import { formatGenerationBreakdown, formatGenerationDuration } from '../../lib/generationTiming' import type { VideoClipInfo, VideoExtraInfo, VideoExtraInfoStatus } from '../../types' @@ -110,6 +111,7 @@ function clipInfoAsText(clip: VideoClipInfo) { } export function VideoExtraInfoDialog({ name, onClose }: { name: string; onClose: () => void }) { + const { t } = useUiTranslation('activity') const [language, setLanguage] = useState(initialLanguage) const [status, setStatus] = useState(null) const [data, setData] = useState(null) @@ -192,7 +194,7 @@ export function VideoExtraInfoDialog({ name, onClose }: { name: string; onClose:
-

Extra info

+

{t('extraInfo')}

{name}

{canRemountVideoclip(selected) && ( {loading || !asset ?
Leyendo manifest…
:
{Object.entries(prompts).map(([name, value]) => typeof value === 'string' && value ?

Prompt · {name}

{value}
: null)}

JSON completo

{raw || 'Metadata no disponible'}
} + return ( +
{ if (event.target === event.currentTarget) onClose() }} + > +
+
+
+

{tActivity('extraInfo')}

+

{asset?.filename || tActivity('inspector.loadingAsset')}

+
+ +
+ {loading || !asset ? ( +
+ {tActivity('inspector.readingManifest')} +
+ ) : ( +
+ + + + {Object.entries(prompts).map(([name, value]) => typeof value === 'string' && value ? ( +
+
+

{tActivity('inspector.prompt', { name })}

+ +
+
{value}
+
+ ) : null)} +
+
+

{tActivity('inspector.fullJson')}

+ +
+
{raw || tActivity('inspector.unavailable')}
+
+
+ )} +
+
+ ) } function InfoSection({ title, values }: { title: string; values: Record }) { - return

{title}

{Object.entries(values).map(([name, value]) =>
{name}
{value == null || value === '' ? 'No disponible' : String(value)}
)}
+ const { t: tActivity } = useUiTranslation('activity') + return ( +
+

{title}

+
+ {Object.entries(values).map(([name, value]) => ( +
+
{name}
+
{value == null || value === '' ? tActivity('inspector.unavailable') : String(value)}
+
+ ))} +
+
+ ) } diff --git a/ui/src/i18n/locales/en/activity.json b/ui/src/i18n/locales/en/activity.json index 18a0bc73..5906a948 100644 --- a/ui/src/i18n/locales/en/activity.json +++ b/ui/src/i18n/locales/en/activity.json @@ -3,6 +3,19 @@ "openHistory": "Show canonical task history", "inboxLegacy": "Inbox / Legacy", "extraInfo": "Extra info", + "inspector": { + "loadingAsset": "Loading asset metadata…", + "readingManifest": "Reading manifest…", + "identity": "Identity", + "origin": "Origin and execution", + "modelTiming": "Model and timing", + "copy": "Copy", + "copyJson": "Copy JSON", + "fullJson": "Full JSON", + "unavailable": "Not available", + "loadFailed": "Could not load Extra info", + "prompt": "Prompt · {{name}}" + }, "collectionsHint": "Reference collections. They are not output folders.", "projectCount_one": "{{count}} project", "projectCount_other": "{{count}} projects", diff --git a/ui/src/i18n/locales/es/activity.json b/ui/src/i18n/locales/es/activity.json index 0b64e472..26f94264 100644 --- a/ui/src/i18n/locales/es/activity.json +++ b/ui/src/i18n/locales/es/activity.json @@ -3,6 +3,19 @@ "openHistory": "Mostrar el historial canónico de tareas", "inboxLegacy": "Inbox / Legacy", "extraInfo": "Información adicional", + "inspector": { + "loadingAsset": "Cargando metadata…", + "readingManifest": "Leyendo manifest…", + "identity": "Identidad", + "origin": "Origen y ejecución", + "modelTiming": "Modelo y tiempos", + "copy": "Copiar", + "copyJson": "Copiar JSON", + "fullJson": "JSON completo", + "unavailable": "No disponible", + "loadFailed": "No se pudo cargar Información adicional", + "prompt": "Prompt · {{name}}" + }, "collectionsHint": "Colecciones de referencias. No son carpetas de salida.", "projectCount_one": "{{count}} proyecto", "projectCount_other": "{{count}} proyectos", diff --git a/ui/tests/assetsCatalog.test.mjs b/ui/tests/assetsCatalog.test.mjs index a1d011be..5b8da046 100644 --- a/ui/tests/assetsCatalog.test.mjs +++ b/ui/tests/assetsCatalog.test.mjs @@ -42,6 +42,10 @@ test('Assets is a first-class tab with its own panel, not a fake active workspac assert.match(panel, /tActivity\('inboxLegacy'\)/) assert.match(panel, /tActivity\('extraInfo'\)/) assert.match(panel, /outputFolder\.uploads/) - assert.match(panel, /JSON completo/) + assert.match(panel, /tActivity\('inspector\.fullJson'\)/) + assert.match(panel, /tActivity\('inspector\.loadFailed'\)/) + assert.match(panel, /tCommon\('actions\.close'\)/) + assert.doesNotMatch(panel, /JSON completo/) + assert.doesNotMatch(panel, /No se pudo cargar Extra info/) assert.doesNotMatch(panel, /setActiveWorkspace|switchWorkspace/) }) diff --git a/ui/tests/i18nFoundation.test.tsx b/ui/tests/i18nFoundation.test.tsx index 733323af..f40c1187 100644 --- a/ui/tests/i18nFoundation.test.tsx +++ b/ui/tests/i18nFoundation.test.tsx @@ -70,6 +70,9 @@ test('required glossary keys exist in both languages', async () => { assert.equal(i18n.t('entities.outputFolder', { ns: 'navigation', lng: 'es' }), 'Carpeta de salida') assert.equal(i18n.t('outputFolder.uploads', { ns: 'navigation', lng: 'es' }), 'Subidas') assert.equal(i18n.t('extraInfo', { ns: 'activity', lng: 'es' }), 'Información adicional') + assert.equal(i18n.t('inspector.loadFailed', { ns: 'activity', lng: 'en' }), 'Could not load Extra info') + assert.equal(i18n.t('inspector.loadFailed', { ns: 'activity', lng: 'es' }), 'No se pudo cargar Información adicional') + assert.equal(i18n.t('actions.close', { ns: 'common', lng: 'es' }), 'Cerrar') }) test('missing keys fall back to english without throwing', async () => { @@ -203,3 +206,37 @@ test('catalogs do not rename technical ids', async () => { test('migrated chrome no longer hardcodes the pilot phrases', () => { assert.deepEqual(forbiddenLiterals(), []) }) + +test('Extra info chrome and the Assets inspector use the activity catalog', async () => { + const fs = await import('node:fs/promises') + const files = [ + '../src/components/MainContent/VideoExtraInfoDialog.tsx', + '../src/components/MainContent/MediaFeedItem.tsx', + '../src/components/MainContent/VideoInfoBar.tsx', + '../src/features/assets/AssetsPanel.tsx', + ] + for (const file of files) { + const source = await fs.readFile(new URL(file, import.meta.url), 'utf8') + assert.match(source, /t(?:Activity)?\('extraInfo'\)/, file) + assert.doesNotMatch(source, />Extra info