+ {/* Title */}
+
+
+
- {/* Classic providers */}
- {classicProviders.length > 0 && (
-
- {classicProviders.map((p) => renderCard(p, false))}
-
- )}
-
- {/* LLM providers — Pro only */}
- {llmProviders.length > 0 && (
-
-
-
-
-
- {t('dashboard.translate.provider.llmDivider')}
- {!isPro && (
-
- {t('dashboard.translate.provider.llmDividerPro')}
-
- )}
-
-
-
- {llmProviders.map((p) => renderCard(p, !isPro))}
- {!isPro && (
-
-
- {t('dashboard.translate.provider.upgrade')}
- {' '}
- {t('dashboard.translate.provider.upgradeSuffix')}
-
+ {/* Tabs Container */}
+
+
+
+
+
+ {/* Active Tab List */}
+
+ {activeTab === 'classic' ? (
+ classicProviders.length > 0 ? (
+
+ {classicProviders.map((p) => renderClassicCard(p))}
+
+ ) : (
+
+ Aucun traducteur standard disponible.
+
+ )
+ ) : (
+ llmProviders.length > 0 ? (
+
+ {llmProviders.map((p) => renderLlmCard(p, !isPro))}
+
+ ) : (
+
+ Aucun modèle IA configuré.
+
+ )
+ )}
+
+
+ {/* Pro upgrade banner when llm is active and user is not pro */}
+ {!isPro && activeTab === 'llm' && (
+
)}
diff --git a/frontend/src/app/dashboard/translate/page.tsx b/frontend/src/app/dashboard/translate/page.tsx
index b214251..856da1b 100644
--- a/frontend/src/app/dashboard/translate/page.tsx
+++ b/frontend/src/app/dashboard/translate/page.tsx
@@ -7,6 +7,7 @@ import {
Zap, CheckCircle2,
Search, Languages, Wrench, Activity, Timer,
Download, AlertTriangle, FileType,
+ Image as ImageIcon,
} from 'lucide-react';
import { useFileUpload } from './useFileUpload';
import { useTranslationConfig } from './useTranslationConfig';
@@ -14,6 +15,7 @@ import { useTranslationSubmit } from './useTranslationSubmit';
import LanguageSelector from './LanguageSelector';
import { ProviderSelector } from './ProviderSelector';
import { GlossarySelector } from './GlossarySelector';
+import { Switch } from '@/components/ui/switch';
import { useNotification } from '@/components/ui/notification';
import { useI18n } from '@/lib/i18n';
import { API_BASE } from '@/lib/config';
@@ -181,86 +183,96 @@ export default function TranslatePage() {
const activeStepIdx = getActiveStepIdx(submit.progress);
const qualityLabel = useMemo(() => getQualityLabel(t, config.provider), [t, config.provider]);
- /* ═══════════════════════════════════════════════════════════════ */
- /* EDITORIAL LAYOUT */
- /* ═══════════════════════════════════════════════════════════════ */
return (
-
+
- {/* ── HEADER ────────────────────────────────────────────── */}
+ {/* ── HEADER (Landing Page Style) ───────────────────────── */}
{showProcessing ? (
<>
-
{t('landing.translate.processing')}
-
- {t('landing.translate.aiAnalysis')}
+ Traitement en cours
+
+ Analyse IA Active
-
- {t('landing.translate.preservingLayout')}
+
+ Votre mise en page est en cours de préservation par notre moteur contextuel.
>
) : showComplete ? (
<>
-
{t('dashboard.translate.completed')}
-
- {t('dashboard.translate.completed')}
+ Complété
+
+ Traduction terminée
-
+
{submit.fileName}
>
) : (
<>
-
{t('landing.translate.newProject')}
-
- {t('landing.translate.title')}
+ Espace Pro
+
+ Traduire un document
-
- {t('landing.translate.subtitle')}
+
+ Conservez la mise en page d'origine grâce au moteur de traduction ultra-haute fidélité.
>
)}
- {/* ── GRID: 8/4 SPLIT ───────────────────────────────────── */}
-
+ {/* ── GRID: 7/5 SPLIT ───────────────────────────────────── */}
+
{/* ═══════════════════════════════════════════════════════ */}
- {/* LEFT (8 cols) — content swaps based on state */}
+ {/* LEFT (7 cols) — content swaps based on state */}
{/* ═══════════════════════════════════════════════════════ */}
-
+
{/* ── UPLOAD STATE: Editorial Dropzone ──────────────── */}
{showUpload && (
dropzoneInputRef.current?.click()}
>
-
-
+
+ Format natif
-
+
+
+
+
+
+
{t('landing.translate.dropHere')}
-
+
{t('landing.translate.supportedFormats')}
-
+
+ {/* Simulated file triggers */}
+
e.stopPropagation()}>
{[
- { label: 'Word', icon:
},
- { label: 'Excel', icon:
},
- { label: 'Slides', icon:
},
- { label: 'PDF', icon:
},
+ { label: 'Word (.docx)', type: 'word' as const, icon:
},
+ { label: 'Excel (.xlsx)', type: 'excel' as const, icon:
},
+ { label: 'Slides (.pptx)', type: 'slides' as const, icon:
},
+ { label: 'PDF (.pdf)', type: 'pdf' as const, icon:
},
].map(f => (
-
+
+
))}
+
{/* Hidden file input for click-to-upload */}
)}
- {/* ── CONFIGURING STATE: File strip ─────────────────── */}
+ {/* ── CONFIGURING STATE: File indicator ──────────────── */}
{showConfiguring && (
-
-
- {t('landing.translate.sourceDocument')}
+
+
+ {t('landing.translate.sourceDocument') || 'Document Source'}
replaceInputRef.current?.click()} t={t} />
- {upload.error && {upload.error}
}
+ {upload.error && {upload.error}
}
)}
{/* ── PROCESSING STATE: Rich progress ───────────────── */}
{showProcessing && (
-
-
-
+
+
+
-
-
- {t('dashboard.translate.translating')}
+
+
+ Moteur contextuel actif
-
+
{submit.fileName || upload.file?.name}
{/* Progress Line with step icons */}
-
+
{PIPELINE_ICONS.map((Icon, i) => (
(i * 25)
- ? 'bg-brand-dark text-white scale-110 dark:bg-brand-accent'
- : 'bg-brand-muted text-brand-dark/40 dark:bg-white/10 dark:text-white/40'
+ ? 'bg-brand-dark text-white scale-110 dark:bg-brand-accent dark:text-brand-dark font-bold'
+ : 'bg-brand-muted text-brand-dark/25 dark:bg-[#1f1f1f] dark:text-white/20'
)}
>
@@ -324,107 +336,100 @@ export default function TranslatePage() {
/>
-
-
- {activeStepIdx < 2 ? t('dashboard.translate.steps.uploading') : t('dashboard.translate.steps.starting')}
+
+
+ {activeStepIdx < 2 ? 'Phase 1: Initialisation' : 'Phase 2: Reconstruction Contextuelle'}
-
+
{Math.round(submit.progress)}%
-
-
} value={`${Math.round(submit.progress)}%`} label={t('dashboard.translate.segments')} />
-
} value="99.9%" label={t('dashboard.translate.quality')} />
-
} value="Turbo" label={t('dashboard.translate.segPerMin')} />
-
} value={formatElapsed(elapsed)} label={t('dashboard.translate.elapsed')} />
+
+ } value={`${Math.round(submit.progress)}%`} label="segments" />
+ } value="99.9%" label="précision" />
+ } value="Turbo" label="vitesse" />
+ } value={formatElapsed(elapsed)} label="temps" />
)}
{/* ── COMPLETE STATE: Success with download ─────────── */}
{showComplete && (
-
-
-
-
-
-
-
-
-
- {t('dashboard.translate.completed')}
-
-
- {submit.fileName}
-
-
+
+
+
+
+
+
+
+
+ Traduction terminée
+
+
+ {submit.fileName}
+
-
- {qualityLabel}
-
+
+ ✓ Qualité Maître
+
+
-
-
-
-
+
+
+
)}
{/* ── FAILED STATE ───────────────────────────────────── */}
{showFailed && (
-
- {/* Error message — friendly language */}
-
+
+
-
+
-
-
{t('dashboard.translate.error.title')}
-
{humanFriendlyError(submit.error)}
+
+
Erreur lors de la traduction
+
{humanFriendlyError(submit.error)}
- {/* File strip */}
{(submit.fileName || upload.file?.name) && upload.file && (
-
- replaceInputRef.current?.click()} t={t} />
-
-
+
replaceInputRef.current?.click()} t={t} />
)}
+
- {/* Action buttons */}
{upload.file && config.isConfigValid && (
)}
@@ -432,20 +437,20 @@ export default function TranslatePage() {
{/* ═══════════════════════════════════════════════════════ */}
- {/* RIGHT (4 cols) — Config / Monitor / Summary */}
+ {/* RIGHT (5 cols) — Config / Monitor / Summary */}
{/* ═══════════════════════════════════════════════════════ */}
-
+
{/* ── CONFIG (upload / configuring / failed) ──────────── */}
{(showUpload || showConfiguring || showFailed) && (
-
+
{/* Scrollable config content */}
-
-
- {t('landing.translate.configuration')}
+
+
+ {t('landing.translate.configuration') || 'Configuration'}
-
+
{config.mode === 'classic' && config.isPro && (
-
+
{t('dashboard.translate.glossaryLLMHint')}
)}
@@ -494,47 +499,70 @@ export default function TranslatePage() {
/>
)}
+ {/* Translate Images — Office files and LLM mode only */}
+ {!isPdf && config.mode === 'llm' && (
+
+
+
+
+
+ {t('dashboard.translate.translateImages') || 'Traduire les images'}
+
+
+ {t('dashboard.translate.translateImagesDesc') || 'Traduire les textes incrustés'}
+
+
+
+
+
+ )}
+
{/* PDF mode selector */}
{isPdf && (
-
-