import { useEffect, useState, useCallback } from 'react'; import Layout from '../components/Layout'; import { apiFetch, apiPost } from '../lib/api'; const SOURCE_LABELS = { words: 'Wörter', questions: 'Fragen', statements: 'Statements' }; const LANGS = [ { code: 'de', label: 'Deutsch', flag: '🇩🇪' }, { code: 'en', label: 'English', flag: '🇬🇧' }, { code: 'sv', label: 'Svenska', flag: '🇸🇪' }, ]; const SOURCES = ['words', 'questions', 'statements']; function pct(have, total) { return total ? Math.round((have / total) * 100) : 0; } export default function TranslationHub() { const [coverage, setCoverage] = useState(null); // 'words|de' → {total, have, missing} const [busy, setBusy] = useState(null); // `${table}|${to}` const [progress, setProgress] = useState(null); const [error, setError] = useState(null); const load = useCallback(async () => { try { const { coverage } = await apiFetch('/claude/translation-coverage'); const map = {}; for (const g of coverage) map[`${g.source_table}|${g.language}`] = g; setCoverage(map); } catch (e) { setError(e.message); } }, []); useEffect(() => { load(); }, [load]); async function translateMissing(table, to) { const key = `${table}|${to}`; setBusy(key); setError(null); setProgress(`Übersetze fehlende ${SOURCE_LABELS[table]} nach ${to.toUpperCase()} …`); try { const res = await apiPost('/claude/translate-missing', { source_table: table, to }); setProgress(`Fertig: ${res.translated} übersetzt${res.failed ? `, ${res.failed} fehlgeschlagen` : ''}.`); await load(); } catch (e) { setError(e.message); setProgress(null); } finally { setBusy(null); setTimeout(() => setProgress(null), 4000); } } return (

Übersetzungen

Fehlende Sprachen per Claude automatisch übersetzen. Placeholder (z.B. {`{{Apfel.w:...}}`}) bleiben strukturell erhalten, das Label wird mit-übersetzt und korrekt gebeugt.

Logik: Es werden nur Zeilen übersetzt, die in mindestens einer Sprache Text haben und in der Zielsprache leer sind. Quell-Sprache wird automatisch gewählt (Default: erste gefüllte Sprache). Nach „Alle übersetzen" werden die Wörter mit allen 3 Sprachen automatisch auf Status translated gesetzt.
{error &&
{error}
} {progress &&
{progress}
}
{SOURCES.map(table => (

{SOURCE_LABELS[table]}

{LANGS.map(lang => { const g = coverage?.[`${table}|${lang.code}`]; const total = g?.total ?? 0; const have = g?.have ?? 0; const missing = g?.missing ?? 0; const key = `${table}|${lang.code}`; const p = pct(have, total); return (
{lang.flag} {lang.label} {coverage ? `${have}/${total}` : '…'}
{missing ? `${missing} fehlen` : 'vollständig'}
); })}
))}
); }