diff --git a/CHANGELOG.md b/CHANGELOG.md index f4e740a6..5dd7548d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,8 @@ the frozen-backend fallback mirror it for their toolchains. ### Fixed +- Dubbing translation shares the agent footer with live logs, validated output, cancellation and contextual retries (#2129) + - Agent dubbing translation saves a custom tone and adaptation prompt and preserves it during timing rewrites (#2129) - Dubbing preserves original sound outside dialogue and mixes separated background only beneath replacement speech (#2129) diff --git a/docs/electron-dubbing.md b/docs/electron-dubbing.md index e7ff806b..a3ffd6b9 100644 --- a/docs/electron-dubbing.md +++ b/docs/electron-dubbing.md @@ -255,3 +255,20 @@ a local CLI agent or the configured LLM. Meaning, timing, glossary and structure output requirements remain in effect. Clear the field to restore the default style; changing the brief does not retranslate existing segments until you run translation. The field accepts up to 5,000 characters and is locked while work is running. + +### Translation activity footer + +Translation and timing rewrites use the same footer area as Repair Agent. It opens +with live CLI stdout/stderr in **Logs**; **Translations** shows original text beside +validated translated output. Collapse **Details** to keep the status, language, +elapsed time and Cancel action visible. Output from each language stays available +until dismissed, the app reloads, or translation starts in another project. Logs are bounded to the +latest 250,000 characters per run and are not saved into project files. + +The counter tracks validated returned segments, not estimated progress. A CLI may +stream logs while withholding its translation JSON until completion; API-backed +translation currently returns one response, so its count updates when that response +arrives. No fabricated percentage is shown. Errors remain visible, with Retry for +failed translation work in the same project. API retries use failed segments when +available; an incomplete CLI response requires retrying that language. Cancel stops +the active translation and prevents late results from applying. diff --git a/electron/src/main/repair-agents.ts b/electron/src/main/repair-agents.ts index 217dff1f..ab4db279 100644 --- a/electron/src/main/repair-agents.ts +++ b/electron/src/main/repair-agents.ts @@ -9,6 +9,7 @@ import { writeFileSync, } from 'node:fs'; import { dirname, extname, join, resolve } from 'node:path'; +import { StringDecoder } from 'node:string_decoder'; import { randomUUID } from 'node:crypto'; import { app, BrowserWindow, dialog, ipcMain, type IpcMainInvokeEvent } from 'electron'; import type { BackendSupervisor } from './backend'; @@ -35,6 +36,7 @@ export const REPAIR_CHANNELS = { translate: 'repair:translate', stopTranslation: 'repair:stopTranslation', event: 'repair:event', + translationEvent: 'repair:translationEvent', } as const; const DEFINITIONS: Array<{ id: RepairAgentId; label: string; command: string }> = [ @@ -355,6 +357,8 @@ function validateDubTranslationRequest( ): asserts value is DubAgentTranslationRequest { if (!value || typeof value !== 'object') throw new Error('Invalid agent translation request'); const request = value as DubAgentTranslationRequest; + if (request.requestId !== undefined && (typeof request.requestId !== 'string' || request.requestId.length > 100)) + throw new Error('Invalid translation request id'); if (!DEFINITIONS.some((item) => item.id === request.agent)) throw new Error('Unknown agent'); if (request.purpose !== 'translate' && request.purpose !== 'fit') throw new Error('Invalid agent translation purpose'); @@ -831,8 +835,22 @@ export function registerRepairAgents( ); } let output = ''; - const append = (value: Buffer) => { - output = (output + value.toString('utf8')).slice(-MAX_TRANSLATION_OUTPUT); + const stdoutDecoder = new StringDecoder('utf8'); + const stderrDecoder = new StringDecoder('utf8'); + let pendingLog = ''; + let logTimer: ReturnType | undefined; + const flushLog = () => { + if (logTimer) clearTimeout(logTimer); + logTimer = undefined; + if (pendingLog && request.requestId) + sendToLiveWindow(getMainWindow(), REPAIR_CHANNELS.translationEvent, + { requestId: request.requestId, text: pendingLog }); + pendingLog = ''; + }; + const append = (text: string, stdout = true) => { + if (stdout) output = (output + text).slice(-MAX_TRANSLATION_OUTPUT); + pendingLog = (pendingLog + text).slice(-250_000); + if (!logTimer) logTimer = setTimeout(flushLog, 100); }; try { return await new Promise((resolvePromise, rejectPromise) => { @@ -841,6 +859,9 @@ export function registerRepairAgents( if (settled) return; settled = true; clearTimeout(timeout); + append(stdoutDecoder.end()); + append(stderrDecoder.end(), false); + flushLog(); translationChild = null; if (translationTemp) rmSync(translationTemp, { recursive: true, force: true }); translationTemp = null; @@ -866,8 +887,8 @@ export function registerRepairAgents( stdio: ['pipe', 'pipe', 'pipe'], }); guardAgentProcessStreams(translationChild, (error) => finish(() => rejectPromise(error))); - translationChild.stdout.on('data', append); - translationChild.stderr.on('data', append); + translationChild.stdout.on('data', (value: Buffer) => append(stdoutDecoder.write(value))); + translationChild.stderr.on('data', (value: Buffer) => append(stderrDecoder.write(value), false)); translationChild.on('error', (error) => finish(() => rejectPromise(new Error(`Agent could not start: ${error.message}`))), ); @@ -890,6 +911,7 @@ export function registerRepairAgents( else translationChild.stdin.end(prompt); }); } catch (error) { + flushLog(); if (translationTemp) rmSync(translationTemp, { recursive: true, force: true }); translationTemp = null; translationChild = null; @@ -914,7 +936,7 @@ export function registerRepairAgents( translationChild = null; translationTemp = null; Object.values(REPAIR_CHANNELS) - .filter((channel) => channel !== REPAIR_CHANNELS.event) + .filter((channel) => channel !== REPAIR_CHANNELS.event && channel !== REPAIR_CHANNELS.translationEvent) .forEach((channel) => ipcMain.removeHandler(channel)); }; } diff --git a/electron/src/preload/index.d.ts b/electron/src/preload/index.d.ts index 1bc4cb96..fc9bc159 100644 --- a/electron/src/preload/index.d.ts +++ b/electron/src/preload/index.d.ts @@ -164,6 +164,7 @@ export interface DubAgentTranslationSegment { measuredSeconds?: number; } export interface DubAgentTranslationRequest { + requestId?: string; agent: RepairAgentId; purpose: 'translate' | 'fit'; sourceLanguage?: string; @@ -279,6 +280,7 @@ export interface VoiceStudioBridge { stop(): Promise; translate(request: DubAgentTranslationRequest): Promise; stopTranslation(): Promise; + onTranslationEvent(callback: (event: { requestId: string; text: string }) => void): () => void; onEvent(callback: (event: RepairAgentEvent) => void): () => void; }; permissions: { diff --git a/electron/src/preload/index.ts b/electron/src/preload/index.ts index e39e9563..d1fbe640 100644 --- a/electron/src/preload/index.ts +++ b/electron/src/preload/index.ts @@ -27,6 +27,7 @@ const bridge: VoiceStudioBridge = { stop: () => ipcRenderer.invoke('repair:stop'), translate: (request) => ipcRenderer.invoke('repair:translate', request), stopTranslation: () => ipcRenderer.invoke('repair:stopTranslation'), + onTranslationEvent: (callback) => subscribe('repair:translationEvent', callback), onEvent: (callback) => subscribe('repair:event', callback), }, permissions: { diff --git a/electron/src/renderer/src/components/app-shell/agent-dock-frame.tsx b/electron/src/renderer/src/components/app-shell/agent-dock-frame.tsx new file mode 100644 index 00000000..ecca92a5 --- /dev/null +++ b/electron/src/renderer/src/components/app-shell/agent-dock-frame.tsx @@ -0,0 +1,11 @@ +import type { ReactNode } from 'react'; +import { cn } from '@/lib/utils'; + +export function AgentDockFrame({ label, expanded = true, children }: { + label: string; expanded?: boolean; children: ReactNode; +}) { + return
{children}
; +} diff --git a/electron/src/renderer/src/components/app-shell/repair-agent-dock.tsx b/electron/src/renderer/src/components/app-shell/repair-agent-dock.tsx index 900e07c2..e75844e9 100644 --- a/electron/src/renderer/src/components/app-shell/repair-agent-dock.tsx +++ b/electron/src/renderer/src/components/app-shell/repair-agent-dock.tsx @@ -1,3 +1,7 @@ +import { useStore } from '@tanstack/react-store'; +import { translationActivity } from '@/features/dub/translation-activity'; +import { TranslationAgentDock } from './translation-agent-dock'; +import { AgentDockFrame } from './agent-dock-frame'; import { useEffect, useRef, useState } from 'react'; import { createPortal } from 'react-dom'; import { useRouterState } from '@tanstack/react-router'; @@ -72,6 +76,7 @@ function RepairGlyph({ className }: { className?: string }) { export function RepairAgentDock() { const { t } = useTranslation(); + const translation = useStore(translationActivity); const pathname = useRouterState({ select: (state) => state.location.pathname }); const backend = useBackendStatus(); const bridge = getBridge(); @@ -90,6 +95,10 @@ export function RepairAgentDock() { const [autoFixReport, setAutoFixReport] = useState(''); const [chooseDefault, setChooseDefault] = useState(false); const terminal = useRef(null); + const translationRunId = translation.runs.at(-1)?.id; + useEffect(() => { + if (translationRunId) setOpen(false); + }, [translationRunId]); useEffect(() => { if (!bridge) return; @@ -249,6 +258,8 @@ export function RepairAgentDock() { } }; + if (!open && status !== 'running' && translation.runs.length) return ; + if (!open) { return createPortal( } + + {!running && } + + {activity.expanded &&
+
+ {(['output', 'logs'] as const).map((tab) => )} +
+
{ const el = log.current; if (el) following.current = el.scrollHeight - el.scrollTop - el.clientHeight < 40; }} + className="studio-scrollbar min-h-0 flex-1 overflow-auto p-3"> + {activity.runs.map((run) =>
+

{run.target} · {run.agent} · {run.purpose === 'fit' ? t('dubActivity.fitting') : t('dub.translate')}

+ {activity.tab === 'logs' ?
{run.logs || t('dubActivity.waiting')}
+ : run.rows.some((row) => row.text || row.error) ? run.rows.filter((row) => row.text || row.error).map((row) => +
+

{row.source}

+

{row.error || row.text}

+
) :

{t('dubActivity.waiting')}

} + {run.error &&

{run.error}

} + {run.status === 'failed' && run.retry && activity.runs.filter((r) => r.target === run.target && r.purpose === run.purpose).at(-1)?.id === run.id && } +
)} +
+
} + ; +} diff --git a/electron/src/renderer/src/features/dub/dub-session.test.ts b/electron/src/renderer/src/features/dub/dub-session.test.ts index deb5040b..866c4b72 100644 --- a/electron/src/renderer/src/features/dub/dub-session.test.ts +++ b/electron/src/renderer/src/features/dub/dub-session.test.ts @@ -1,3 +1,4 @@ +import { translationActivity } from './translation-activity'; import { ingestDubUrl, isDubUrl } from './dub-session'; import { expect, it, vi } from 'vitest'; import { apiJson } from '@/lib/api/client'; @@ -32,17 +33,25 @@ vi.mock('@/lib/api/event-stream', async (load) => ({ })); it('translates the current dubbing segments with an installed local CLI agent', async () => { - const translate = vi.fn().mockResolvedValue({ + let logListener: ((event: { requestId: string; text: string }) => void) | undefined; + const unsubscribe = vi.fn(); + + const translate = vi.fn().mockImplementation(async (request) => { + logListener?.({ requestId: 'another-request', text: 'must not appear' }); + logListener?.({ requestId: request.requestId, text: 'Translating two segments' }); + return { agent: 'codex', translations: [ { id: 'a', text: 'Hola' }, { id: 'b', text: 'Adiós' }, ], - }); + }; }); Object.defineProperty(window, 'voicestudio', { configurable: true, value: { - repair: { translate, stopTranslation: vi.fn().mockResolvedValue(undefined) }, + repair: { translate, stopTranslation: vi.fn().mockResolvedValue(undefined), + onTranslationEvent: (callback: typeof logListener) => { logListener = callback; return unsubscribe; }, + }, } as unknown as Window['voicestudio'], }); vi.mocked(apiJson).mockReset(); @@ -64,6 +73,10 @@ it('translates the current dubbing segments with an installed local CLI agent', })); await expect(translateDubWithAgent('es', 'codex')).resolves.toBe(true); + expect(unsubscribe).toHaveBeenCalledOnce(); + expect(translationActivity.state.runs.at(-1)).toMatchObject({ + status: 'complete', logs: 'Translating two segments', + }); expect(translate).toHaveBeenCalledWith( expect.objectContaining({ diff --git a/electron/src/renderer/src/features/dub/dub-session.ts b/electron/src/renderer/src/features/dub/dub-session.ts index 1d7c7d9f..49c7a6f3 100644 --- a/electron/src/renderer/src/features/dub/dub-session.ts +++ b/electron/src/renderer/src/features/dub/dub-session.ts @@ -1,3 +1,4 @@ +import { startTranslationRun, appendTranslationLog, updateTranslationRun, finishTranslationRun } from './translation-activity'; import type { DubExportPreferences } from './dub-export'; import { MAX_COOKIE_EXPORT_BYTES, @@ -1053,6 +1054,7 @@ export async function ingestDubUrl(value: string, cookieFile?: File, fetchSubs = async function runLocalTranslationAgent( request: DubAgentTranslationRequest, signal: AbortSignal, + retry?: () => Promise, ): Promise { const bridge = window.voicestudio?.repair; if (!bridge?.translate) throw new Error('LOCAL_TRANSLATION_AGENT_UNAVAILABLE'); @@ -1061,10 +1063,30 @@ async function runLocalTranslationAgent( stop(); throw new DOMException('Cancelled', 'AbortError'); } + const id = startTranslationRun({ + jobId: dubSession.state.jobId || '', agent: request.agent, + target: request.targetLanguage, purpose: request.purpose, retry, + rows: request.segments.map((segment) => ({ id: segment.id, source: segment.sourceText })), + }); + const unsubscribe = bridge.onTranslationEvent?.((event) => { + if (event.requestId === id) appendTranslationLog(id, event.text); + }); signal.addEventListener('abort', stop, { once: true }); try { - return await bridge.translate(request); + const result = await bridge.translate({ ...request, requestId: id }); + if (signal.aborted) throw new DOMException('Cancelled', 'AbortError'); + const texts = new Map(result.translations.map((row) => [row.id, row.text])); + updateTranslationRun(id, { + rows: request.segments.map((segment) => ({ id: segment.id, source: segment.sourceText, text: texts.get(segment.id) })), + }); + finishTranslationRun(id, 'complete'); + return result; + } catch (error) { + finishTranslationRun(id, signal.aborted ? 'cancelled' : 'failed', + signal.aborted ? undefined : (error instanceof Error ? error.message : String(error))); + throw error; } finally { + unsubscribe?.(); signal.removeEventListener('abort', stop); } } @@ -1100,6 +1122,7 @@ export async function translateDubWithAgent( })), }, signal, + () => translateDubWithAgent(target, agent, targetLabel), ); const rows = new Map(translated.translations.map((row) => [row.id, row.text])); clearDubEditHistory(); @@ -1155,8 +1178,17 @@ export async function translateDub( if (!requestedSegments.length) return false; const finishActivity = beginAppActivity('translation'); let agentFallback = false; + let activityId: string | undefined; + let activityAborted = false; try { const completed = await run('translating', async (signal) => { + activityId = startTranslationRun({ + jobId: snapshot.jobId!, agent: provider, target, purpose: 'translate', + rows: requestedSegments.map((segment) => ({ id: segment.id, source: segment.text_original || segment.text })), + retry: () => translateDub(target, provider, { retryFailed: Boolean(dubSession.state.segments.some((s) => s.translate_errors?.[target])) }), + }); + signal.addEventListener('abort', () => { activityAborted = true; }, { once: true }); + const glossary = await apiJson>( `/glossary/${encodeURIComponent(snapshot.jobId!)}`, { signal }, @@ -1211,6 +1243,13 @@ export async function translateDub( })), }), }); + if (signal.aborted) throw new DOMException('Cancelled', 'AbortError'); + updateTranslationRun(activityId!, { + rows: requestedSegments.map((segment) => { + const row = translated.translated.find((r) => String(r.id) === segment.id); + return { id: segment.id, source: segment.text_original || segment.text, text: row?.error ? undefined : row?.text, error: row?.error }; + }), + }); const fallback = translated.cinematic_skipped === 'no-llm-configured'; agentFallback = fallback && snapshot.quality === 'agent'; const rows = new Map(translated.translated.map((row) => [String(row.id), row])); @@ -1254,6 +1293,9 @@ export async function translateDub( if (translated.translated.some((row) => row.error)) throw new Error('Some translation segments failed'); }); + if (activityId) finishTranslationRun(activityId, + activityAborted ? 'cancelled' : completed && !agentFallback ? 'complete' : 'failed', + completed && !agentFallback ? undefined : dubSession.state.error || undefined); return completed && !agentFallback; } finally { finishActivity(); diff --git a/electron/src/renderer/src/features/dub/translation-activity.test.ts b/electron/src/renderer/src/features/dub/translation-activity.test.ts new file mode 100644 index 00000000..c53e0c0b --- /dev/null +++ b/electron/src/renderer/src/features/dub/translation-activity.test.ts @@ -0,0 +1,32 @@ +import { beforeEach, expect, it } from 'vitest'; +import { appendTranslationLog, finishTranslationRun, startTranslationRun, translationActivity, updateTranslationRun } from './translation-activity'; + +beforeEach(() => translationActivity.setState(() => ({ runs: [], expanded: false, tab: 'output' }))); +const request = { jobId: 'job', agent: 'codex', target: 'Bengali', purpose: 'translate' as const, rows: [{ id: 'a', source: 'Hello' }] }; +it('opens logs without inventing completed segments, then retains validated output', () => { + const id = startTranslationRun(request); + appendTranslationLog(id, 'Working…'); + expect(translationActivity.state.expanded).toBe(true); + expect(translationActivity.state.tab).toBe('logs'); + expect(translationActivity.state.runs[0].rows[0].text).toBeUndefined(); + updateTranslationRun(id, { rows: [{ id: 'a', source: 'Hello', text: 'হ্যালো' }] }); + finishTranslationRun(id, 'complete'); + expect(translationActivity.state.runs[0].rows[0].text).toBe('হ্যালো'); + expect(translationActivity.state.runs[0].endedAt).toBeDefined(); +}); +it('isolates run logs and ignores late output after cancellation', () => { + const first = startTranslationRun(request); + finishTranslationRun(first, 'cancelled'); + const second = startTranslationRun({ ...request, target: 'Spanish' }); + appendTranslationLog(first, 'late output'); + appendTranslationLog(second, 'current output'); + expect(translationActivity.state.runs.map((r) => r.logs)).toEqual(['', 'current output']); +}); +it('bounds log memory while preserving separate batch language results', () => { + const first = startTranslationRun(request); + appendTranslationLog(first, 'x'.repeat(300_000)); + finishTranslationRun(first, 'complete'); + startTranslationRun({ ...request, target: 'Spanish' }); + expect(translationActivity.state.runs).toHaveLength(2); + expect(translationActivity.state.runs[0].logs).toHaveLength(250_000); +}); diff --git a/electron/src/renderer/src/features/dub/translation-activity.ts b/electron/src/renderer/src/features/dub/translation-activity.ts new file mode 100644 index 00000000..eaa9e7f1 --- /dev/null +++ b/electron/src/renderer/src/features/dub/translation-activity.ts @@ -0,0 +1,52 @@ +import { Store } from '@tanstack/store'; + +export interface TranslationRun { + id: string; + jobId: string; + agent: string; + target: string; + purpose: 'translate' | 'fit'; + status: 'running' | 'complete' | 'failed' | 'cancelled'; + startedAt: number; + endedAt?: number; + logs: string; + error?: string; + rows: Array<{ id: string; source: string; text?: string; error?: string }>; + retry?: () => Promise; +} + +export const translationActivity = new Store<{ + runs: TranslationRun[]; + expanded: boolean; + tab: 'output' | 'logs'; +}>({ runs: [], expanded: true, tab: 'output' }); + +export function startTranslationRun(run: Omit): string { + const id = crypto.randomUUID(); + translationActivity.setState((state) => ({ + ...state, + expanded: true, + tab: state.runs.length ? state.tab : 'logs', + runs: [...state.runs.filter((r) => r.jobId === run.jobId), { ...run, id, status: 'running', startedAt: Date.now(), logs: '' }], + })); + return id; +} + +export function updateTranslationRun(id: string, update: Partial) { + translationActivity.setState((state) => ({ + ...state, + runs: state.runs.map((run) => run.id === id ? { ...run, ...update } : run), + })); +} + +export function appendTranslationLog(id: string, text: string) { + translationActivity.setState((state) => ({ + ...state, + runs: state.runs.map((run) => run.id === id && run.status === 'running' + ? { ...run, logs: (run.logs + text).slice(-250_000) } : run), + })); +} + +export function finishTranslationRun(id: string, status: TranslationRun['status'], error?: string) { + updateTranslationRun(id, { status, error, endedAt: Date.now() }); +} diff --git a/electron/src/renderer/src/i18n/locales/ar.json b/electron/src/renderer/src/i18n/locales/ar.json index e0be0906..7a2d7232 100644 --- a/electron/src/renderer/src/i18n/locales/ar.json +++ b/electron/src/renderer/src/i18n/locales/ar.json @@ -2587,5 +2587,12 @@ "dubStyle": { "label": "تعليمات أسلوب الترجمة", "help": "حدّد النبرة والجمهور وأسلوب التكييف، مثل الحفاظ على النكات وتكييف التعابير طبيعيًا. تُحفظ مع المشروع وتُستخدم للترجمة وإعادة الصياغة الزمنية. اتركها فارغة للأسلوب الافتراضي." + }, + "dubActivity": { + "output": "الترجمات", + "waiting": "لم يصل أي ناتج بعد.", + "progress": "تم التحقق من {{done}} / {{total}} مقطع", + "cancelled": "أُلغي", + "fitting": "ضبط التوقيت" } } diff --git a/electron/src/renderer/src/i18n/locales/de.json b/electron/src/renderer/src/i18n/locales/de.json index 61d092db..2fb47018 100644 --- a/electron/src/renderer/src/i18n/locales/de.json +++ b/electron/src/renderer/src/i18n/locales/de.json @@ -2579,5 +2579,12 @@ "dubStyle": { "label": "Vorgabe für den Übersetzungsstil", "help": "Beschreibe Ton, Zielgruppe und Anpassung, etwa Witze erhalten und Redewendungen natürlich übertragen. Wird im Projekt gespeichert und für Übersetzung und Zeitanpassung verwendet. Leer lassen für den Standardstil." + }, + "dubActivity": { + "output": "Übersetzungen", + "waiting": "Noch keine Ausgabe empfangen.", + "progress": "{{done}} / {{total}} Segmente geprüft", + "cancelled": "Abgebrochen", + "fitting": "Timing anpassen" } } diff --git a/electron/src/renderer/src/i18n/locales/en.json b/electron/src/renderer/src/i18n/locales/en.json index ac1d8d76..5c9a08fd 100644 --- a/electron/src/renderer/src/i18n/locales/en.json +++ b/electron/src/renderer/src/i18n/locales/en.json @@ -2579,5 +2579,12 @@ "dubStyle": { "label": "Translation style prompt", "help": "Describe tone, audience and adaptation style—for example: conversational Bengali, preserve jokes, adapt idioms naturally. Saved with this project; used for translation and timing rewrites. Leave blank for the default style." + }, + "dubActivity": { + "output": "Translations", + "waiting": "No output received yet.", + "progress": "{{done}} / {{total}} segments validated", + "cancelled": "Cancelled", + "fitting": "Adjusting timing" } } diff --git a/electron/src/renderer/src/i18n/locales/es.json b/electron/src/renderer/src/i18n/locales/es.json index d37d1035..a7bc0f97 100644 --- a/electron/src/renderer/src/i18n/locales/es.json +++ b/electron/src/renderer/src/i18n/locales/es.json @@ -2581,5 +2581,12 @@ "dubStyle": { "label": "Instrucciones de estilo de traducción", "help": "Describe el tono, el público y la adaptación, por ejemplo conservar los chistes y adaptar los modismos con naturalidad. Se guarda en el proyecto y se aplica a la traducción y los ajustes de duración. Déjalo vacío para el estilo predeterminado." + }, + "dubActivity": { + "output": "Traducciones", + "waiting": "Aún no se ha recibido ninguna salida.", + "progress": "{{done}} / {{total}} segmentos validados", + "cancelled": "Cancelado", + "fitting": "Ajustando tiempos" } } diff --git a/electron/src/renderer/src/i18n/locales/fr.json b/electron/src/renderer/src/i18n/locales/fr.json index 74bb029a..e9ccea80 100644 --- a/electron/src/renderer/src/i18n/locales/fr.json +++ b/electron/src/renderer/src/i18n/locales/fr.json @@ -2581,5 +2581,12 @@ "dubStyle": { "label": "Consignes de style de traduction", "help": "Décrivez le ton, le public et l’adaptation, par exemple préserver les blagues et adapter naturellement les expressions. Enregistré dans le projet et utilisé pour la traduction et les ajustements de durée. Laissez vide pour le style par défaut." + }, + "dubActivity": { + "output": "Traductions", + "waiting": "Aucune sortie reçue pour le moment.", + "progress": "{{done}} / {{total}} segments validés", + "cancelled": "Annulé", + "fitting": "Ajustement du minutage" } } diff --git a/electron/src/renderer/src/i18n/locales/hi.json b/electron/src/renderer/src/i18n/locales/hi.json index 19020b0e..1bd8444c 100644 --- a/electron/src/renderer/src/i18n/locales/hi.json +++ b/electron/src/renderer/src/i18n/locales/hi.json @@ -2579,5 +2579,12 @@ "dubStyle": { "label": "अनुवाद शैली के निर्देश", "help": "लहजा, दर्शक और रूपांतरण शैली बताएँ, जैसे चुटकुले बनाए रखना और मुहावरों को स्वाभाविक बनाना। परियोजना में सहेजा जाता है और अनुवाद व समय के अनुसार पुनर्लेखन में उपयोग होता है। डिफ़ॉल्ट शैली के लिए खाली छोड़ें।" + }, + "dubActivity": { + "output": "अनुवाद", + "waiting": "अभी कोई आउटपुट नहीं मिला।", + "progress": "{{done}} / {{total}} खंड सत्यापित", + "cancelled": "रद्द किया गया", + "fitting": "समय समायोजित हो रहा है" } } diff --git a/electron/src/renderer/src/i18n/locales/id.json b/electron/src/renderer/src/i18n/locales/id.json index 34d6c84f..732f10db 100644 --- a/electron/src/renderer/src/i18n/locales/id.json +++ b/electron/src/renderer/src/i18n/locales/id.json @@ -2579,5 +2579,12 @@ "dubStyle": { "label": "Instruksi gaya terjemahan", "help": "Jelaskan nada, audiens, dan gaya adaptasi, misalnya pertahankan lelucon dan sesuaikan idiom secara alami. Disimpan dalam proyek untuk terjemahan dan penyesuaian durasi. Kosongkan untuk gaya bawaan." + }, + "dubActivity": { + "output": "Terjemahan", + "waiting": "Belum ada keluaran yang diterima.", + "progress": "{{done}} / {{total}} segmen divalidasi", + "cancelled": "Dibatalkan", + "fitting": "Menyesuaikan waktu" } } diff --git a/electron/src/renderer/src/i18n/locales/it.json b/electron/src/renderer/src/i18n/locales/it.json index ee917ffd..6739bc2c 100644 --- a/electron/src/renderer/src/i18n/locales/it.json +++ b/electron/src/renderer/src/i18n/locales/it.json @@ -2581,5 +2581,12 @@ "dubStyle": { "label": "Istruzioni sullo stile di traduzione", "help": "Descrivi tono, pubblico e adattamento, ad esempio mantenere le battute e adattare gli idiomi in modo naturale. Salvate nel progetto e usate per traduzione e riscritture temporali. Lascia vuoto per lo stile predefinito." + }, + "dubActivity": { + "output": "Traduzioni", + "waiting": "Nessun risultato ricevuto finora.", + "progress": "{{done}} / {{total}} segmenti convalidati", + "cancelled": "Annullato", + "fitting": "Regolazione dei tempi" } } diff --git a/electron/src/renderer/src/i18n/locales/ja.json b/electron/src/renderer/src/i18n/locales/ja.json index 9c2aaaaa..0ed0ef9d 100644 --- a/electron/src/renderer/src/i18n/locales/ja.json +++ b/electron/src/renderer/src/i18n/locales/ja.json @@ -2579,5 +2579,12 @@ "dubStyle": { "label": "翻訳スタイルの指示", "help": "口調、対象者、翻案方針を指定します。例:冗談を残し、慣用句を自然に訳す。プロジェクトに保存され、翻訳と尺調整の書き直しに使われます。空欄なら既定のスタイルを使います。" + }, + "dubActivity": { + "output": "翻訳結果", + "waiting": "まだ出力を受信していません。", + "progress": "{{done}} / {{total}} セグメントを検証済み", + "cancelled": "キャンセル済み", + "fitting": "タイミングを調整中" } } diff --git a/electron/src/renderer/src/i18n/locales/ko.json b/electron/src/renderer/src/i18n/locales/ko.json index de018941..4a498e1e 100644 --- a/electron/src/renderer/src/i18n/locales/ko.json +++ b/electron/src/renderer/src/i18n/locales/ko.json @@ -2579,5 +2579,12 @@ "dubStyle": { "label": "번역 스타일 지침", "help": "어조, 대상 독자, 각색 방식을 설명하세요. 예: 농담을 살리고 관용구를 자연스럽게 번역하기. 프로젝트에 저장되며 번역과 길이 조정에 사용됩니다. 비워 두면 기본 스타일을 사용합니다." + }, + "dubActivity": { + "output": "번역 결과", + "waiting": "아직 수신된 출력이 없습니다.", + "progress": "{{done}} / {{total}}개 구간 검증됨", + "cancelled": "취소됨", + "fitting": "타이밍 조정 중" } } diff --git a/electron/src/renderer/src/i18n/locales/nl.json b/electron/src/renderer/src/i18n/locales/nl.json index 272ae5b0..309189b4 100644 --- a/electron/src/renderer/src/i18n/locales/nl.json +++ b/electron/src/renderer/src/i18n/locales/nl.json @@ -2579,5 +2579,12 @@ "dubStyle": { "label": "Instructies voor vertaalstijl", "help": "Beschrijf toon, doelgroep en aanpassing, zoals grappen behouden en uitdrukkingen natuurlijk vertalen. Opgeslagen in dit project voor vertaling en herschrijven op tijdsduur. Laat leeg voor de standaardstijl." + }, + "dubActivity": { + "output": "Vertalingen", + "waiting": "Nog geen uitvoer ontvangen.", + "progress": "{{done}} / {{total}} segmenten gevalideerd", + "cancelled": "Geannuleerd", + "fitting": "Timing aanpassen" } } diff --git a/electron/src/renderer/src/i18n/locales/pl.json b/electron/src/renderer/src/i18n/locales/pl.json index ed604daa..ad97b814 100644 --- a/electron/src/renderer/src/i18n/locales/pl.json +++ b/electron/src/renderer/src/i18n/locales/pl.json @@ -2583,5 +2583,12 @@ "dubStyle": { "label": "Instrukcje stylu tłumaczenia", "help": "Opisz ton, odbiorców i sposób adaptacji, np. zachowanie żartów i naturalne tłumaczenie idiomów. Zapisywane w projekcie i stosowane przy tłumaczeniu oraz dopasowaniu czasu. Pozostaw puste dla stylu domyślnego." + }, + "dubActivity": { + "output": "Tłumaczenia", + "waiting": "Nie otrzymano jeszcze żadnych wyników.", + "progress": "Zweryfikowano {{done}} / {{total}} segmentów", + "cancelled": "Anulowano", + "fitting": "Dostosowywanie czasu" } } diff --git a/electron/src/renderer/src/i18n/locales/pt.json b/electron/src/renderer/src/i18n/locales/pt.json index d880fda1..09d67953 100644 --- a/electron/src/renderer/src/i18n/locales/pt.json +++ b/electron/src/renderer/src/i18n/locales/pt.json @@ -2581,5 +2581,12 @@ "dubStyle": { "label": "Instruções de estilo de tradução", "help": "Descreva o tom, o público e a adaptação, como preservar piadas e adaptar expressões naturalmente. Salvas no projeto para tradução e ajustes de duração. Deixe em branco para usar o estilo padrão." + }, + "dubActivity": { + "output": "Traduções", + "waiting": "Nenhum resultado recebido ainda.", + "progress": "{{done}} / {{total}} segmentos validados", + "cancelled": "Cancelado", + "fitting": "Ajustando o tempo" } } diff --git a/electron/src/renderer/src/i18n/locales/ru.json b/electron/src/renderer/src/i18n/locales/ru.json index 71678388..52c8bcda 100644 --- a/electron/src/renderer/src/i18n/locales/ru.json +++ b/electron/src/renderer/src/i18n/locales/ru.json @@ -2583,5 +2583,12 @@ "dubStyle": { "label": "Инструкции по стилю перевода", "help": "Опишите тон, аудиторию и подход к адаптации, например сохранять шутки и естественно передавать идиомы. Сохраняется в проекте для перевода и подгонки длительности. Оставьте пустым для стиля по умолчанию." + }, + "dubActivity": { + "output": "Переводы", + "waiting": "Вывод пока не получен.", + "progress": "Проверено сегментов: {{done}} / {{total}}", + "cancelled": "Отменено", + "fitting": "Настройка времени" } } diff --git a/electron/src/renderer/src/i18n/locales/sv.json b/electron/src/renderer/src/i18n/locales/sv.json index 466d520c..94ba7eab 100644 --- a/electron/src/renderer/src/i18n/locales/sv.json +++ b/electron/src/renderer/src/i18n/locales/sv.json @@ -2579,5 +2579,12 @@ "dubStyle": { "label": "Instruktioner för översättningsstil", "help": "Beskriv ton, målgrupp och anpassning, till exempel bevara skämt och översätta idiom naturligt. Sparas i projektet för översättning och tidsanpassning. Lämna tomt för standardstilen." + }, + "dubActivity": { + "output": "Översättningar", + "waiting": "Ingen utdata har tagits emot ännu.", + "progress": "{{done}} / {{total}} segment validerade", + "cancelled": "Avbruten", + "fitting": "Justerar tidsplacering" } } diff --git a/electron/src/renderer/src/i18n/locales/th.json b/electron/src/renderer/src/i18n/locales/th.json index 7cbcff07..52b88a2d 100644 --- a/electron/src/renderer/src/i18n/locales/th.json +++ b/electron/src/renderer/src/i18n/locales/th.json @@ -2579,5 +2579,12 @@ "dubStyle": { "label": "คำสั่งรูปแบบการแปล", "help": "ระบุน้ำเสียง กลุ่มผู้ฟัง และแนวทางดัดแปลง เช่น รักษามุกตลกและปรับสำนวนให้เป็นธรรมชาติ บันทึกในโครงการและใช้ทั้งการแปลและปรับความยาวบท เว้นว่างเพื่อใช้รูปแบบเริ่มต้น" + }, + "dubActivity": { + "output": "คำแปล", + "waiting": "ยังไม่ได้รับผลลัพธ์", + "progress": "ตรวจสอบแล้ว {{done}} / {{total}} ช่วง", + "cancelled": "ยกเลิกแล้ว", + "fitting": "กำลังปรับเวลา" } } diff --git a/electron/src/renderer/src/i18n/locales/tr.json b/electron/src/renderer/src/i18n/locales/tr.json index cb3b21e4..34a4771e 100644 --- a/electron/src/renderer/src/i18n/locales/tr.json +++ b/electron/src/renderer/src/i18n/locales/tr.json @@ -2579,5 +2579,12 @@ "dubStyle": { "label": "Çeviri üslubu talimatları", "help": "Tonu, hedef kitleyi ve uyarlamayı açıklayın; örneğin şakaları koruyun ve deyimleri doğal biçimde aktarın. Projeye kaydedilir, çeviri ve süre düzenlemelerinde kullanılır. Varsayılan üslup için boş bırakın." + }, + "dubActivity": { + "output": "Çeviriler", + "waiting": "Henüz çıktı alınmadı.", + "progress": "{{done}} / {{total}} bölüm doğrulandı", + "cancelled": "İptal edildi", + "fitting": "Zamanlama ayarlanıyor" } } diff --git a/electron/src/renderer/src/i18n/locales/uk.json b/electron/src/renderer/src/i18n/locales/uk.json index 4e5a6c7a..b259ccd2 100644 --- a/electron/src/renderer/src/i18n/locales/uk.json +++ b/electron/src/renderer/src/i18n/locales/uk.json @@ -2583,5 +2583,12 @@ "dubStyle": { "label": "Інструкції щодо стилю перекладу", "help": "Опишіть тон, аудиторію та підхід до адаптації, наприклад збереження жартів і природний переклад ідіом. Зберігається в проєкті для перекладу й підгонки тривалості. Залиште порожнім для типового стилю." + }, + "dubActivity": { + "output": "Переклади", + "waiting": "Вивід ще не отримано.", + "progress": "Перевірено сегментів: {{done}} / {{total}}", + "cancelled": "Скасовано", + "fitting": "Налаштування часу" } } diff --git a/electron/src/renderer/src/i18n/locales/vi.json b/electron/src/renderer/src/i18n/locales/vi.json index 49b5cc28..2b53ab30 100644 --- a/electron/src/renderer/src/i18n/locales/vi.json +++ b/electron/src/renderer/src/i18n/locales/vi.json @@ -2579,5 +2579,12 @@ "dubStyle": { "label": "Hướng dẫn phong cách dịch", "help": "Mô tả giọng điệu, đối tượng và cách chuyển thể, ví dụ giữ câu đùa và dịch thành ngữ tự nhiên. Được lưu trong dự án, dùng cho dịch và chỉnh thời lượng. Để trống để dùng phong cách mặc định." + }, + "dubActivity": { + "output": "Bản dịch", + "waiting": "Chưa nhận được đầu ra.", + "progress": "Đã xác thực {{done}} / {{total}} đoạn", + "cancelled": "Đã hủy", + "fitting": "Đang điều chỉnh thời gian" } } diff --git a/electron/src/renderer/src/i18n/locales/zh-CN.json b/electron/src/renderer/src/i18n/locales/zh-CN.json index 681efc6d..59403595 100644 --- a/electron/src/renderer/src/i18n/locales/zh-CN.json +++ b/electron/src/renderer/src/i18n/locales/zh-CN.json @@ -2583,5 +2583,12 @@ "dubStyle": { "label": "翻译风格提示词", "help": "描述语气、受众和改编方式,例如保留笑点、自然转换习语。随项目保存,用于翻译和时长调整。留空使用默认风格。" + }, + "dubActivity": { + "output": "翻译结果", + "waiting": "尚未收到输出。", + "progress": "已验证 {{done}} / {{total}} 个片段", + "cancelled": "已取消", + "fitting": "正在调整时间" } } diff --git a/electron/src/renderer/src/i18n/locales/zh-TW.json b/electron/src/renderer/src/i18n/locales/zh-TW.json index 95d7b849..80e3be3b 100644 --- a/electron/src/renderer/src/i18n/locales/zh-TW.json +++ b/electron/src/renderer/src/i18n/locales/zh-TW.json @@ -2579,5 +2579,12 @@ "dubStyle": { "label": "翻譯風格提示詞", "help": "描述語氣、受眾和改編方式,例如保留笑點、自然轉換慣用語。隨專案儲存,用於翻譯和時長調整。留空使用預設風格。" + }, + "dubActivity": { + "output": "翻譯結果", + "waiting": "尚未收到輸出。", + "progress": "已驗證 {{done}} / {{total}} 個片段", + "cancelled": "已取消", + "fitting": "正在調整時間" } } diff --git a/frontend/src/i18n/locales/ar.json b/frontend/src/i18n/locales/ar.json index 7f16abaf..e71ec4c8 100644 --- a/frontend/src/i18n/locales/ar.json +++ b/frontend/src/i18n/locales/ar.json @@ -2841,5 +2841,12 @@ "dubStyle": { "label": "تعليمات أسلوب الترجمة", "help": "حدّد النبرة والجمهور وأسلوب التكييف، مثل الحفاظ على النكات وتكييف التعابير طبيعيًا. تُحفظ مع المشروع وتُستخدم للترجمة وإعادة الصياغة الزمنية. اتركها فارغة للأسلوب الافتراضي." + }, + "dubActivity": { + "output": "الترجمات", + "waiting": "لم يصل أي ناتج بعد.", + "progress": "تم التحقق من {{done}} / {{total}} مقطع", + "cancelled": "أُلغي", + "fitting": "ضبط التوقيت" } } diff --git a/frontend/src/i18n/locales/de.json b/frontend/src/i18n/locales/de.json index b5d85ba6..b5afb85b 100644 --- a/frontend/src/i18n/locales/de.json +++ b/frontend/src/i18n/locales/de.json @@ -2841,5 +2841,12 @@ "dubStyle": { "label": "Vorgabe für den Übersetzungsstil", "help": "Beschreibe Ton, Zielgruppe und Anpassung, etwa Witze erhalten und Redewendungen natürlich übertragen. Wird im Projekt gespeichert und für Übersetzung und Zeitanpassung verwendet. Leer lassen für den Standardstil." + }, + "dubActivity": { + "output": "Übersetzungen", + "waiting": "Noch keine Ausgabe empfangen.", + "progress": "{{done}} / {{total}} Segmente geprüft", + "cancelled": "Abgebrochen", + "fitting": "Timing anpassen" } } diff --git a/frontend/src/i18n/locales/en.json b/frontend/src/i18n/locales/en.json index 61f05c1b..57b149b6 100644 --- a/frontend/src/i18n/locales/en.json +++ b/frontend/src/i18n/locales/en.json @@ -3322,5 +3322,12 @@ "dubStyle": { "label": "Translation style prompt", "help": "Describe tone, audience and adaptation style—for example: conversational Bengali, preserve jokes, adapt idioms naturally. Saved with this project; used for translation and timing rewrites. Leave blank for the default style." + }, + "dubActivity": { + "output": "Translations", + "waiting": "No output received yet.", + "progress": "{{done}} / {{total}} segments validated", + "cancelled": "Cancelled", + "fitting": "Adjusting timing" } } diff --git a/frontend/src/i18n/locales/es.json b/frontend/src/i18n/locales/es.json index ec2b65c3..837fc711 100644 --- a/frontend/src/i18n/locales/es.json +++ b/frontend/src/i18n/locales/es.json @@ -2841,5 +2841,12 @@ "dubStyle": { "label": "Instrucciones de estilo de traducción", "help": "Describe el tono, el público y la adaptación, por ejemplo conservar los chistes y adaptar los modismos con naturalidad. Se guarda en el proyecto y se aplica a la traducción y los ajustes de duración. Déjalo vacío para el estilo predeterminado." + }, + "dubActivity": { + "output": "Traducciones", + "waiting": "Aún no se ha recibido ninguna salida.", + "progress": "{{done}} / {{total}} segmentos validados", + "cancelled": "Cancelado", + "fitting": "Ajustando tiempos" } } diff --git a/frontend/src/i18n/locales/fr.json b/frontend/src/i18n/locales/fr.json index 77b92764..12ad22d9 100644 --- a/frontend/src/i18n/locales/fr.json +++ b/frontend/src/i18n/locales/fr.json @@ -2841,5 +2841,12 @@ "dubStyle": { "label": "Consignes de style de traduction", "help": "Décrivez le ton, le public et l’adaptation, par exemple préserver les blagues et adapter naturellement les expressions. Enregistré dans le projet et utilisé pour la traduction et les ajustements de durée. Laissez vide pour le style par défaut." + }, + "dubActivity": { + "output": "Traductions", + "waiting": "Aucune sortie reçue pour le moment.", + "progress": "{{done}} / {{total}} segments validés", + "cancelled": "Annulé", + "fitting": "Ajustement du minutage" } } diff --git a/frontend/src/i18n/locales/hi.json b/frontend/src/i18n/locales/hi.json index 8843db22..7c83f671 100644 --- a/frontend/src/i18n/locales/hi.json +++ b/frontend/src/i18n/locales/hi.json @@ -2841,5 +2841,12 @@ "dubStyle": { "label": "अनुवाद शैली के निर्देश", "help": "लहजा, दर्शक और रूपांतरण शैली बताएँ, जैसे चुटकुले बनाए रखना और मुहावरों को स्वाभाविक बनाना। परियोजना में सहेजा जाता है और अनुवाद व समय के अनुसार पुनर्लेखन में उपयोग होता है। डिफ़ॉल्ट शैली के लिए खाली छोड़ें।" + }, + "dubActivity": { + "output": "अनुवाद", + "waiting": "अभी कोई आउटपुट नहीं मिला।", + "progress": "{{done}} / {{total}} खंड सत्यापित", + "cancelled": "रद्द किया गया", + "fitting": "समय समायोजित हो रहा है" } } diff --git a/frontend/src/i18n/locales/id.json b/frontend/src/i18n/locales/id.json index 9d8ea588..c2aba4c8 100644 --- a/frontend/src/i18n/locales/id.json +++ b/frontend/src/i18n/locales/id.json @@ -2841,5 +2841,12 @@ "dubStyle": { "label": "Instruksi gaya terjemahan", "help": "Jelaskan nada, audiens, dan gaya adaptasi, misalnya pertahankan lelucon dan sesuaikan idiom secara alami. Disimpan dalam proyek untuk terjemahan dan penyesuaian durasi. Kosongkan untuk gaya bawaan." + }, + "dubActivity": { + "output": "Terjemahan", + "waiting": "Belum ada keluaran yang diterima.", + "progress": "{{done}} / {{total}} segmen divalidasi", + "cancelled": "Dibatalkan", + "fitting": "Menyesuaikan waktu" } } diff --git a/frontend/src/i18n/locales/it.json b/frontend/src/i18n/locales/it.json index da63132e..3419a0b7 100644 --- a/frontend/src/i18n/locales/it.json +++ b/frontend/src/i18n/locales/it.json @@ -2841,5 +2841,12 @@ "dubStyle": { "label": "Istruzioni sullo stile di traduzione", "help": "Descrivi tono, pubblico e adattamento, ad esempio mantenere le battute e adattare gli idiomi in modo naturale. Salvate nel progetto e usate per traduzione e riscritture temporali. Lascia vuoto per lo stile predefinito." + }, + "dubActivity": { + "output": "Traduzioni", + "waiting": "Nessun risultato ricevuto finora.", + "progress": "{{done}} / {{total}} segmenti convalidati", + "cancelled": "Annullato", + "fitting": "Regolazione dei tempi" } } diff --git a/frontend/src/i18n/locales/ja.json b/frontend/src/i18n/locales/ja.json index dca37561..533f3361 100644 --- a/frontend/src/i18n/locales/ja.json +++ b/frontend/src/i18n/locales/ja.json @@ -2841,5 +2841,12 @@ "dubStyle": { "label": "翻訳スタイルの指示", "help": "口調、対象者、翻案方針を指定します。例:冗談を残し、慣用句を自然に訳す。プロジェクトに保存され、翻訳と尺調整の書き直しに使われます。空欄なら既定のスタイルを使います。" + }, + "dubActivity": { + "output": "翻訳結果", + "waiting": "まだ出力を受信していません。", + "progress": "{{done}} / {{total}} セグメントを検証済み", + "cancelled": "キャンセル済み", + "fitting": "タイミングを調整中" } } diff --git a/frontend/src/i18n/locales/ko.json b/frontend/src/i18n/locales/ko.json index 934add41..0692df2c 100644 --- a/frontend/src/i18n/locales/ko.json +++ b/frontend/src/i18n/locales/ko.json @@ -3322,5 +3322,12 @@ "dubStyle": { "label": "번역 스타일 지침", "help": "어조, 대상 독자, 각색 방식을 설명하세요. 예: 농담을 살리고 관용구를 자연스럽게 번역하기. 프로젝트에 저장되며 번역과 길이 조정에 사용됩니다. 비워 두면 기본 스타일을 사용합니다." + }, + "dubActivity": { + "output": "번역 결과", + "waiting": "아직 수신된 출력이 없습니다.", + "progress": "{{done}} / {{total}}개 구간 검증됨", + "cancelled": "취소됨", + "fitting": "타이밍 조정 중" } } diff --git a/frontend/src/i18n/locales/nl.json b/frontend/src/i18n/locales/nl.json index 397be236..dcb69a07 100644 --- a/frontend/src/i18n/locales/nl.json +++ b/frontend/src/i18n/locales/nl.json @@ -2841,5 +2841,12 @@ "dubStyle": { "label": "Instructies voor vertaalstijl", "help": "Beschrijf toon, doelgroep en aanpassing, zoals grappen behouden en uitdrukkingen natuurlijk vertalen. Opgeslagen in dit project voor vertaling en herschrijven op tijdsduur. Laat leeg voor de standaardstijl." + }, + "dubActivity": { + "output": "Vertalingen", + "waiting": "Nog geen uitvoer ontvangen.", + "progress": "{{done}} / {{total}} segmenten gevalideerd", + "cancelled": "Geannuleerd", + "fitting": "Timing aanpassen" } } diff --git a/frontend/src/i18n/locales/pl.json b/frontend/src/i18n/locales/pl.json index 19426922..069efad8 100644 --- a/frontend/src/i18n/locales/pl.json +++ b/frontend/src/i18n/locales/pl.json @@ -2841,5 +2841,12 @@ "dubStyle": { "label": "Instrukcje stylu tłumaczenia", "help": "Opisz ton, odbiorców i sposób adaptacji, np. zachowanie żartów i naturalne tłumaczenie idiomów. Zapisywane w projekcie i stosowane przy tłumaczeniu oraz dopasowaniu czasu. Pozostaw puste dla stylu domyślnego." + }, + "dubActivity": { + "output": "Tłumaczenia", + "waiting": "Nie otrzymano jeszcze żadnych wyników.", + "progress": "Zweryfikowano {{done}} / {{total}} segmentów", + "cancelled": "Anulowano", + "fitting": "Dostosowywanie czasu" } } diff --git a/frontend/src/i18n/locales/pt.json b/frontend/src/i18n/locales/pt.json index 0777c18b..c9a609cf 100644 --- a/frontend/src/i18n/locales/pt.json +++ b/frontend/src/i18n/locales/pt.json @@ -2841,5 +2841,12 @@ "dubStyle": { "label": "Instruções de estilo de tradução", "help": "Descreva o tom, o público e a adaptação, como preservar piadas e adaptar expressões naturalmente. Salvas no projeto para tradução e ajustes de duração. Deixe em branco para usar o estilo padrão." + }, + "dubActivity": { + "output": "Traduções", + "waiting": "Nenhum resultado recebido ainda.", + "progress": "{{done}} / {{total}} segmentos validados", + "cancelled": "Cancelado", + "fitting": "Ajustando o tempo" } } diff --git a/frontend/src/i18n/locales/ru.json b/frontend/src/i18n/locales/ru.json index 73f50cdf..08bbb9a1 100644 --- a/frontend/src/i18n/locales/ru.json +++ b/frontend/src/i18n/locales/ru.json @@ -2841,5 +2841,12 @@ "dubStyle": { "label": "Инструкции по стилю перевода", "help": "Опишите тон, аудиторию и подход к адаптации, например сохранять шутки и естественно передавать идиомы. Сохраняется в проекте для перевода и подгонки длительности. Оставьте пустым для стиля по умолчанию." + }, + "dubActivity": { + "output": "Переводы", + "waiting": "Вывод пока не получен.", + "progress": "Проверено сегментов: {{done}} / {{total}}", + "cancelled": "Отменено", + "fitting": "Настройка времени" } } diff --git a/frontend/src/i18n/locales/sv.json b/frontend/src/i18n/locales/sv.json index 76f14121..e3e6460d 100644 --- a/frontend/src/i18n/locales/sv.json +++ b/frontend/src/i18n/locales/sv.json @@ -2841,5 +2841,12 @@ "dubStyle": { "label": "Instruktioner för översättningsstil", "help": "Beskriv ton, målgrupp och anpassning, till exempel bevara skämt och översätta idiom naturligt. Sparas i projektet för översättning och tidsanpassning. Lämna tomt för standardstilen." + }, + "dubActivity": { + "output": "Översättningar", + "waiting": "Ingen utdata har tagits emot ännu.", + "progress": "{{done}} / {{total}} segment validerade", + "cancelled": "Avbruten", + "fitting": "Justerar tidsplacering" } } diff --git a/frontend/src/i18n/locales/th.json b/frontend/src/i18n/locales/th.json index e8c75a2b..c16c359c 100644 --- a/frontend/src/i18n/locales/th.json +++ b/frontend/src/i18n/locales/th.json @@ -2841,5 +2841,12 @@ "dubStyle": { "label": "คำสั่งรูปแบบการแปล", "help": "ระบุน้ำเสียง กลุ่มผู้ฟัง และแนวทางดัดแปลง เช่น รักษามุกตลกและปรับสำนวนให้เป็นธรรมชาติ บันทึกในโครงการและใช้ทั้งการแปลและปรับความยาวบท เว้นว่างเพื่อใช้รูปแบบเริ่มต้น" + }, + "dubActivity": { + "output": "คำแปล", + "waiting": "ยังไม่ได้รับผลลัพธ์", + "progress": "ตรวจสอบแล้ว {{done}} / {{total}} ช่วง", + "cancelled": "ยกเลิกแล้ว", + "fitting": "กำลังปรับเวลา" } } diff --git a/frontend/src/i18n/locales/tr.json b/frontend/src/i18n/locales/tr.json index 0490d8a1..3fed2213 100644 --- a/frontend/src/i18n/locales/tr.json +++ b/frontend/src/i18n/locales/tr.json @@ -2841,5 +2841,12 @@ "dubStyle": { "label": "Çeviri üslubu talimatları", "help": "Tonu, hedef kitleyi ve uyarlamayı açıklayın; örneğin şakaları koruyun ve deyimleri doğal biçimde aktarın. Projeye kaydedilir, çeviri ve süre düzenlemelerinde kullanılır. Varsayılan üslup için boş bırakın." + }, + "dubActivity": { + "output": "Çeviriler", + "waiting": "Henüz çıktı alınmadı.", + "progress": "{{done}} / {{total}} bölüm doğrulandı", + "cancelled": "İptal edildi", + "fitting": "Zamanlama ayarlanıyor" } } diff --git a/frontend/src/i18n/locales/uk.json b/frontend/src/i18n/locales/uk.json index 27fd6e34..22cfad33 100644 --- a/frontend/src/i18n/locales/uk.json +++ b/frontend/src/i18n/locales/uk.json @@ -2841,5 +2841,12 @@ "dubStyle": { "label": "Інструкції щодо стилю перекладу", "help": "Опишіть тон, аудиторію та підхід до адаптації, наприклад збереження жартів і природний переклад ідіом. Зберігається в проєкті для перекладу й підгонки тривалості. Залиште порожнім для типового стилю." + }, + "dubActivity": { + "output": "Переклади", + "waiting": "Вивід ще не отримано.", + "progress": "Перевірено сегментів: {{done}} / {{total}}", + "cancelled": "Скасовано", + "fitting": "Налаштування часу" } } diff --git a/frontend/src/i18n/locales/vi.json b/frontend/src/i18n/locales/vi.json index 6e96f9a2..73924a2b 100644 --- a/frontend/src/i18n/locales/vi.json +++ b/frontend/src/i18n/locales/vi.json @@ -2841,5 +2841,12 @@ "dubStyle": { "label": "Hướng dẫn phong cách dịch", "help": "Mô tả giọng điệu, đối tượng và cách chuyển thể, ví dụ giữ câu đùa và dịch thành ngữ tự nhiên. Được lưu trong dự án, dùng cho dịch và chỉnh thời lượng. Để trống để dùng phong cách mặc định." + }, + "dubActivity": { + "output": "Bản dịch", + "waiting": "Chưa nhận được đầu ra.", + "progress": "Đã xác thực {{done}} / {{total}} đoạn", + "cancelled": "Đã hủy", + "fitting": "Đang điều chỉnh thời gian" } } diff --git a/frontend/src/i18n/locales/zh-CN.json b/frontend/src/i18n/locales/zh-CN.json index d14eae1e..51a7f706 100644 --- a/frontend/src/i18n/locales/zh-CN.json +++ b/frontend/src/i18n/locales/zh-CN.json @@ -3322,5 +3322,12 @@ "dubStyle": { "label": "翻译风格提示词", "help": "描述语气、受众和改编方式,例如保留笑点、自然转换习语。随项目保存,用于翻译和时长调整。留空使用默认风格。" + }, + "dubActivity": { + "output": "翻译结果", + "waiting": "尚未收到输出。", + "progress": "已验证 {{done}} / {{total}} 个片段", + "cancelled": "已取消", + "fitting": "正在调整时间" } } diff --git a/frontend/src/i18n/locales/zh-TW.json b/frontend/src/i18n/locales/zh-TW.json index b421c32a..35f65ae7 100644 --- a/frontend/src/i18n/locales/zh-TW.json +++ b/frontend/src/i18n/locales/zh-TW.json @@ -2841,5 +2841,12 @@ "dubStyle": { "label": "翻譯風格提示詞", "help": "描述語氣、受眾和改編方式,例如保留笑點、自然轉換慣用語。隨專案儲存,用於翻譯和時長調整。留空使用預設風格。" + }, + "dubActivity": { + "output": "翻譯結果", + "waiting": "尚未收到輸出。", + "progress": "已驗證 {{done}} / {{total}} 個片段", + "cancelled": "已取消", + "fitting": "正在調整時間" } }