fix: remove DeepSeek/MiniMax branding, fix glossary visibility, improve translate page UX
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m7s

- US1: Replace exposed provider names (DeepSeek V3 → IA Express, MiniMax → IA Avancée)
  in legacy_routes.py, remove 52 DeepSeek references from i18n pricing/landing keys,
  add generic modelName key across all 13 languages
- US2: Fix glossary selector filtering (fallback to all glossaries when source lang
  filter returns empty), show templates by default, fix 3 broken presets
  (hvac/construction/automotive → hr/scientific/ecommerce)
- US3: Replace 15 hardcoded French strings with i18n t() calls, increase font sizes
  from 8-9px to 11px, improve text contrast (opacity /20→/40, /25→/45, /30→/50)
- US4: Add missing provider labels in admin ProviderStatus (deepseek, minimax, zai,
  google_cloud, openrouter, openrouter_premium)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-05-31 08:24:33 +02:00
parent b472dcd4b7
commit 9532fef2cd
7 changed files with 417 additions and 136 deletions

View File

@@ -15,10 +15,15 @@ interface ProviderStatusProps {
const PROVIDER_LABELS: Record<string, string> = {
google: "Google Translate",
google_cloud: "Google Cloud",
deepl: "DeepL",
ollama: "Ollama (Local)",
openai: "OpenAI",
openrouter: "OpenRouter",
openrouter: "IA Essentielle",
openrouter_premium: "IA Premium",
deepseek: "IA Express",
minimax: "IA Avancée",
zai: "Grok (xAI)",
};
const STATUS_CONFIG = {

View File

@@ -5,7 +5,7 @@ import {
BookText, Plus, Library, Calendar, Hash,
Zap, Save, Trash2, MessageSquare, Loader2,
Wrench, HardHat, Monitor, Scale, Stethoscope, BarChart3,
Megaphone, Car,
Megaphone, Car, ShoppingCart, FlaskConical, Users,
} from 'lucide-react';
import { useUser } from '@/app/dashboard/useUser';
import { useI18n } from '@/lib/i18n';
@@ -21,14 +21,14 @@ import { useTranslationStore } from '@/lib/store';
import { API_BASE } from '@/lib/config';
const PRESETS = [
{ key: 'hvac', title: 'HVAC / Génie climatique', desc: 'Thermique, ventilation, climatisation', icon: Wrench, templateId: 'hvac' },
{ key: 'construction', title: 'BTP / Construction', desc: 'Gros œuvre, second œuvre, normes', icon: HardHat, templateId: 'construction' },
{ key: 'it', title: 'IT / Logiciel', desc: 'Développement, infrastructure, DevOps', icon: Monitor, templateId: 'technology' },
{ key: 'legal', title: 'Juridique / Contrats', desc: 'Droit des affaires, contentieux', icon: Scale, templateId: 'legal' },
{ key: 'medical', title: 'Médical / Santé', desc: 'Pharmacologie, chirurgie, diagnostic', icon: Stethoscope, templateId: 'medical' },
{ key: 'finance', title: 'Finance / Comptabilité', desc: 'IFRS, bilans, fiscalité', icon: BarChart3, templateId: 'finance' },
{ key: 'marketing', title: 'Marketing / Publicité', desc: 'Digital, branding, analytics', icon: Megaphone, templateId: 'marketing' },
{ key: 'automotive', title: 'Automobile', desc: 'Motorisation, ADAS, homologation', icon: Car, templateId: 'automotive' },
{ key: 'hr', title: 'RH / Ressources Humaines', desc: 'Contrats, politiques, recrutement', icon: Users, templateId: 'hr' },
{ key: 'scientific', title: 'Scientifique / Recherche', desc: 'Publications, thèses, articles', icon: FlaskConical, templateId: 'scientific' },
{ key: 'ecommerce', title: 'E-commerce / Vente', desc: 'Boutiques en ligne, catalogues, CRM', icon: ShoppingCart, templateId: 'ecommerce' },
];
export default function GlossariesPage() {

View File

@@ -40,7 +40,7 @@ export function GlossarySelector({ sourceLang, targetLang, isPro, glossaryId, on
const [importingId, setImportingId] = useState<string | null>(null);
const [error, setError] = useState<string | null>(null);
const [isOpen, setIsOpen] = useState(false);
const [showTemplates, setShowTemplates] = useState(false);
const [showTemplates, setShowTemplates] = useState(true);
const containerRef = useRef<HTMLDivElement>(null);
const fetchData = useCallback(async () => {
@@ -125,17 +125,20 @@ export function GlossarySelector({ sourceLang, targetLang, isPro, glossaryId, on
const sourceFlag = SUPPORTED_LANGUAGES.find(l => l.code === sourceLang)?.flag ?? '';
const targetFlag = SUPPORTED_LANGUAGES.find(l => l.code === targetLang)?.flag ?? '';
const filteredGlossaries = sourceLang === 'auto'
const langFiltered = sourceLang === 'auto'
? glossaries
: glossaries.filter(g => g.source_language === sourceLang);
// If filtering by source language yields nothing, show all glossaries
const filteredGlossaries = langFiltered.length > 0 ? langFiltered : glossaries;
const selected = glossaries.find(g => g.id === glossaryId);
return (
<div className="space-y-2 relative" ref={containerRef}>
<label className="text-[9px] font-black text-brand-dark/40 dark:text-white/40 uppercase tracking-[0.2em] block">
<BookText size={10} className="inline mr-1.5 text-brand-accent" />
{t('translate.glossary.title')} <span className="normal-case tracking-normal font-normal text-brand-dark/25 dark:text-white/25">({sourceFlag}{targetFlag})</span>
<label className="text-[11px] font-black text-brand-dark/50 dark:text-white/50 uppercase tracking-[0.15em] block">
<BookText size={12} className="inline mr-1.5 text-brand-accent" />
{t('translate.glossary.title')} <span className="normal-case tracking-normal font-normal text-brand-dark/40 dark:text-white/40">({sourceFlag}{targetFlag})</span>
</label>
{/* Dropdown trigger */}
@@ -158,12 +161,12 @@ export function GlossarySelector({ sourceLang, targetLang, isPro, glossaryId, on
</div>
<div className="min-w-0 flex-1">
<div className="text-[11px] font-bold truncate text-brand-accent">{selected.name}</div>
<div className="text-[9px] text-brand-dark/30 dark:text-white/30">{selected.terms_count} {t('translate.glossary.terms')}</div>
<div className="text-[11px] text-brand-dark/50 dark:text-white/50">{selected.terms_count} {t('translate.glossary.terms')}</div>
</div>
<button
type="button"
onClick={(e) => { e.stopPropagation(); onChange(null); }}
className="shrink-0 w-5 h-5 rounded-full flex items-center justify-center text-brand-dark/20 hover:text-brand-dark hover:bg-brand-muted dark:text-white/20 dark:hover:text-white dark:hover:bg-white/10 transition-colors"
className="shrink-0 w-5 h-5 rounded-full flex items-center justify-center text-brand-dark/40 hover:text-brand-dark hover:bg-brand-muted dark:text-white/40 dark:hover:text-white dark:hover:bg-white/10 transition-colors"
>
<X size={11} />
</button>
@@ -182,12 +185,12 @@ export function GlossarySelector({ sourceLang, targetLang, isPro, glossaryId, on
</span>
</>
)}
<ChevronDown size={14} className={cn("shrink-0 text-brand-dark/20 dark:text-white/20 transition-transform", isOpen && "rotate-180")} />
<ChevronDown size={14} className={cn("shrink-0 text-brand-dark/40 dark:text-white/40 transition-transform", isOpen && "rotate-180")} />
</button>
{/* Error */}
{error && (
<p className="text-[10px] text-red-500 pl-1">{error}</p>
<p className="text-[11px] text-red-500 pl-1">{error}</p>
)}
{/* Dropdown panel */}
@@ -221,7 +224,7 @@ export function GlossarySelector({ sourceLang, targetLang, isPro, glossaryId, on
{g.name}
</div>
</div>
<span className="text-[9px] text-brand-dark/25 dark:text-white/25">{g.terms_count} {t('translate.glossary.terms')}</span>
<span className="text-[10px] text-brand-dark/45 dark:text-white/45">{g.terms_count} {t('translate.glossary.terms')}</span>
</button>
);
})}

View File

@@ -83,32 +83,32 @@ export default function TranslatePage() {
/* ── Human-friendly error messages ──────────────────────────── */
const humanFriendlyError = (raw: string | null): string => {
if (!raw) return 'Une erreur inattendue est survenue. Réessayez.';
if (!raw) return t('dashboard.translate.error.unexpected');
const r = raw.toLowerCase();
if (r.includes('0 out of') || r.includes('0 textes') || r.includes('0 texts'))
return "La traduction n'a produit aucun résultat. Vérifiez que le document contient du texte, puis réessayez ou choisissez un autre moteur.";
return t('dashboard.translate.error.noResult');
if (r.includes('api key') || r.includes('api_key') || r.includes('unauthorized') || r.includes('401'))
return "Clé API invalide ou manquante. Contactez l'administrateur pour configurer les clés.";
return t('dashboard.translate.error.apiKey');
if (r.includes('quota') || r.includes('rate limit') || r.includes('429'))
return "Limite d'utilisation atteinte. Réessayez dans quelques minutes ou choisissez un autre moteur.";
return t('dashboard.translate.error.quota');
if (r.includes('timeout') || r.includes('timed out') || r.includes('connexion'))
return "La connexion au service de traduction a expiré. Vérifiez votre réseau et réessayez.";
return t('dashboard.translate.error.timeout');
if (r.includes('not found') || r.includes('404'))
return "La session a expiré. Cliquez sur Réessayer pour relancer la traduction.";
return t('dashboard.translate.error.sessionExpired');
if (r.includes('empty') || r.includes('vide') || r.includes('no translatable'))
return "Le document semble vide ou ne contient pas de texte traduisible (PDF image ?).";
return t('dashboard.translate.error.empty');
if (r.includes('unsupported') || r.includes('format'))
return "Format de fichier non supporté ou fichier corrompu.";
return t('dashboard.translate.error.unsupported');
if (r.includes('lost connection') || r.includes('internet'))
return "Connexion perdue. Vérifiez votre réseau et réessayez.";
return t('dashboard.translate.error.connection');
// Generic fallback — show raw but readable
return `Traduction échouée : ${raw.charAt(0).toUpperCase() + raw.slice(1)}`;
return t('dashboard.translate.error.generic', { detail: raw.charAt(0).toUpperCase() + raw.slice(1) });
};
useEffect(() => {
if (submit.error && submit.error !== lastErrorRef.current) {
lastErrorRef.current = submit.error;
showError({ title: 'Traduction échouée', description: humanFriendlyError(submit.error) });
showError({ title: t('dashboard.translate.error.title'), description: humanFriendlyError(submit.error) });
}
}, [submit.error, showError]);
@@ -275,7 +275,7 @@ export default function TranslatePage() {
{/* ── CONFIGURING STATE: File strip ─────────────────── */}
{showConfiguring && (
<div className="editorial-card p-10 bg-white border-none shadow-editorial dark:bg-[#141414]">
<h4 className="text-[10px] font-black uppercase tracking-[0.3em] mb-10 text-brand-dark/30 border-b border-black/5 pb-6 dark:text-white/30 dark:border-white/5">
<h4 className="text-[10px] font-black uppercase tracking-[0.3em] mb-10 text-brand-dark/50 border-b border-black/5 pb-6 dark:text-white/50 dark:border-white/5">
{t('landing.translate.sourceDocument')}
</h4>
<FileStrip file={upload.file!} onRemove={upload.removeFile} onReplace={() => replaceInputRef.current?.click()} t={t} />
@@ -295,7 +295,7 @@ export default function TranslatePage() {
<h3 className="text-2xl font-black uppercase tracking-tight mb-2 text-brand-dark dark:text-white">
{t('dashboard.translate.translating')}
</h3>
<p className="text-[10px] text-brand-dark/30 font-black uppercase tracking-widest dark:text-white/30">
<p className="text-[10px] text-brand-dark/50 font-black uppercase tracking-widest dark:text-white/50">
{submit.fileName || upload.file?.name}
</p>
</div>
@@ -311,7 +311,7 @@ export default function TranslatePage() {
'w-12 h-12 rounded-2xl border-4 border-white dark:border-[#141414] shadow-xl flex items-center justify-center z-10 transition-all duration-500',
submit.progress > (i * 25)
? 'bg-brand-dark text-white scale-110 dark:bg-brand-accent'
: 'bg-brand-muted text-brand-dark/20 dark:bg-white/10 dark:text-white/20'
: 'bg-brand-muted text-brand-dark/40 dark:bg-white/10 dark:text-white/40'
)}
>
<Icon size={18} />
@@ -325,7 +325,7 @@ export default function TranslatePage() {
</div>
<div className="flex justify-between items-end mb-8">
<span className="text-[10px] font-black text-brand-dark/30 uppercase tracking-[0.3em] dark:text-white/30">
<span className="text-[10px] font-black text-brand-dark/50 uppercase tracking-[0.3em] dark:text-white/50">
{activeStepIdx < 2 ? t('dashboard.translate.steps.uploading') : t('dashboard.translate.steps.starting')}
</span>
<span className="text-7xl font-black text-brand-dark dark:text-white">
@@ -360,7 +360,7 @@ export default function TranslatePage() {
</p>
</div>
</div>
<span className="px-5 py-2 bg-white dark:bg-[#1a1a1a] rounded-full text-[9px] font-black uppercase tracking-widest text-brand-accent border border-brand-accent/20 shadow-sm">
<span className="px-5 py-2 bg-white dark:bg-[#1a1a1a] rounded-full text-[11px] font-black uppercase tracking-widest text-brand-accent border border-brand-accent/20 shadow-sm">
{qualityLabel}
</span>
</div>
@@ -375,7 +375,7 @@ export default function TranslatePage() {
</button>
<button
onClick={handleNewTranslation}
className="text-[10px] font-black uppercase tracking-[0.3em] text-brand-dark/20 hover:text-brand-dark transition-colors dark:text-white/20 dark:hover:text-white"
className="text-[10px] font-black uppercase tracking-[0.3em] text-brand-dark/40 hover:text-brand-dark transition-colors dark:text-white/40 dark:hover:text-white"
>
+ {t('dashboard.translate.complete.newTranslation')}
</button>
@@ -394,7 +394,7 @@ export default function TranslatePage() {
<AlertTriangle className="size-5 text-red-500" />
</div>
<div className="flex-1 min-w-0">
<p className="text-sm font-black uppercase tracking-tight text-red-600 dark:text-red-400 mb-2">Traduction échouée</p>
<p className="text-sm font-black uppercase tracking-tight text-red-600 dark:text-red-400 mb-2">{t('dashboard.translate.error.title')}</p>
<p className="text-sm text-red-600/80 dark:text-red-300/80 leading-relaxed">{humanFriendlyError(submit.error)}</p>
</div>
</div>
@@ -416,7 +416,7 @@ export default function TranslatePage() {
className="premium-button w-full py-5 text-[12px] uppercase tracking-[0.25em] flex items-center justify-center gap-3 !rounded-2xl"
>
<RotateCcw size={18} />
Réessayer la traduction
{t('dashboard.translate.retry')}
</button>
)}
<button
@@ -424,7 +424,7 @@ export default function TranslatePage() {
className="w-full py-4 border-2 border-black/10 dark:border-white/10 rounded-2xl text-[11px] font-black uppercase tracking-[0.25em] text-brand-dark/50 dark:text-white/50 hover:text-brand-dark dark:hover:text-white hover:border-brand-dark/20 dark:hover:border-white/20 transition-all flex items-center justify-center gap-3"
>
<Upload size={16} />
Nouveau fichier
{t('dashboard.translate.newFile')}
</button>
</div>
</div>
@@ -441,7 +441,7 @@ export default function TranslatePage() {
<div className="editorial-card bg-white border-none shadow-editorial dark:bg-[#141414] overflow-hidden flex flex-col lg:sticky lg:top-8 lg:max-h-[calc(100vh-6rem)]">
{/* Scrollable config content */}
<div className="flex-1 overflow-y-auto p-10 pb-6">
<h4 className="text-[10px] font-black uppercase tracking-[0.3em] mb-10 text-brand-dark/30 border-b border-black/5 pb-6 dark:text-white/30 dark:border-white/5">
<h4 className="text-[10px] font-black uppercase tracking-[0.3em] mb-10 text-brand-dark/50 border-b border-black/5 pb-6 dark:text-white/50 dark:border-white/5">
{t('landing.translate.configuration')}
</h4>
@@ -463,20 +463,20 @@ export default function TranslatePage() {
{config.provider && (
<div className="flex items-center gap-2">
<span className={cn(
"inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full text-[9px] font-black uppercase tracking-[0.15em]",
"inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full text-[10px] font-black uppercase tracking-[0.15em]",
config.mode === 'llm'
? "bg-brand-accent/10 text-brand-accent border border-brand-accent/20"
: "bg-brand-muted/50 text-brand-dark/40 dark:bg-white/5 dark:text-white/40 border border-transparent"
)}>
{config.mode === 'llm' ? (
<><Zap size={10} /> Mode IA</>
<><Zap size={10} /> {t('dashboard.translate.modeAI')}</>
) : (
<><Languages size={10} /> Mode Classique</>
<><Languages size={10} /> {t('dashboard.translate.modeClassic')}</>
)}
</span>
{config.mode === 'classic' && config.isPro && (
<span className="text-[9px] text-brand-dark/20 dark:text-white/20 italic">
Glossaires disponibles en mode IA
<span className="text-[11px] text-brand-dark/40 dark:text-white/40 italic">
{t('dashboard.translate.glossaryLLMHint')}
</span>
)}
</div>
@@ -497,7 +497,7 @@ export default function TranslatePage() {
{/* PDF mode selector */}
{isPdf && (
<div className="space-y-2">
<label className="text-[9px] font-black text-brand-dark/40 uppercase tracking-[0.2em] block mb-3 dark:text-white/40">
<label className="text-[11px] font-black text-brand-dark/50 uppercase tracking-[0.15em] block mb-3 dark:text-white/50">
{t('dashboard.translate.pdfMode.title')}
</label>
<div className="grid grid-cols-2 gap-2">
@@ -515,7 +515,7 @@ export default function TranslatePage() {
<FileText className="size-4 text-brand-accent" />
{t('dashboard.translate.pdfMode.preserveLayout')}
</div>
<p className="mt-1 text-[9px] text-brand-dark/50 font-bold uppercase tracking-widest leading-relaxed dark:text-white/40">
<p className="mt-1 text-[11px] text-brand-dark/55 font-bold uppercase tracking-widest leading-relaxed dark:text-white/50">
{t('dashboard.translate.pdfMode.preserveLayoutDesc')}
</p>
</button>
@@ -533,7 +533,7 @@ export default function TranslatePage() {
<Languages className="size-4 text-brand-accent" />
{t('dashboard.translate.pdfMode.textOnly')}
</div>
<p className="mt-1 text-[9px] text-brand-dark/50 font-bold uppercase tracking-widest leading-relaxed dark:text-white/40">
<p className="mt-1 text-[11px] text-brand-dark/55 font-bold uppercase tracking-widest leading-relaxed dark:text-white/50">
{t('dashboard.translate.pdfMode.textOnlyDesc')}
</p>
</button>
@@ -552,24 +552,24 @@ export default function TranslatePage() {
'w-full py-5 rounded-2xl text-[13px] font-black uppercase tracking-[0.2em] flex items-center justify-center gap-3 transition-all duration-200',
config.isConfigValid && upload.file && !submit.isSubmitting
? 'bg-brand-dark text-white hover:bg-brand-dark/90 shadow-lg shadow-brand-dark/20 dark:bg-brand-accent dark:text-brand-dark dark:hover:bg-brand-accent/90'
: 'bg-black/5 dark:bg-white/5 text-brand-dark/25 dark:text-white/25 cursor-not-allowed'
: 'bg-black/5 dark:bg-white/5 text-brand-dark/45 dark:text-white/45 cursor-not-allowed'
)}
>
{submit.isSubmitting ? (
<><Loader2 className="size-5 animate-spin" />Envoi en cours...</>
<><Loader2 className="size-5 animate-spin" />{t('dashboard.translate.submitting')}</>
) : (
<><ArrowRight size={20} />Lancer la traduction</>
<><ArrowRight size={20} />{t('dashboard.translate.submit')}</>
)}
</button>
{!upload.file && (
<p className="text-center text-[10px] text-brand-dark/30 dark:text-white/30 mt-2 font-bold uppercase tracking-widest"> Déposez d'abord un fichier</p>
<p className="text-center text-[11px] text-brand-dark/40 dark:text-white/40 mt-2 font-bold uppercase tracking-widest"> {t('dashboard.translate.noFile')}</p>
)}
{upload.file && !config.targetLang && (
<p className="text-center text-[10px] text-brand-dark/30 dark:text-white/30 mt-2 font-bold uppercase tracking-widest">↑ Sélectionnez une langue cible</p>
<p className="text-center text-[11px] text-brand-dark/40 dark:text-white/40 mt-2 font-bold uppercase tracking-widest"> {t('dashboard.translate.noTargetLang')}</p>
)}
</div>
<div className="shrink-0 flex justify-between px-6 pb-5 pt-1 text-[9px] font-black uppercase tracking-[0.2em] text-brand-dark/20 dark:text-white/20">
<div className="shrink-0 flex justify-between px-6 pb-5 pt-1 text-[10px] font-black uppercase tracking-[0.15em] text-brand-dark/40 dark:text-white/40">
<span className="flex items-center gap-2">
<ShieldCheck size={12} /> {t('landing.translate.zeroRetention')}
</span>
@@ -583,7 +583,7 @@ export default function TranslatePage() {
{/* ── MONITOR (processing) ────────────────────────────── */}
{showProcessing && (
<div className="editorial-card p-10 bg-white border-none shadow-editorial h-full dark:bg-[#141414]">
<h4 className="text-[10px] font-black uppercase tracking-[0.4em] mb-12 flex items-center gap-3 text-brand-dark/30 dark:text-white/30">
<h4 className="text-[11px] font-black uppercase tracking-[0.3em] mb-12 flex items-center gap-3 text-brand-dark/45 dark:text-white/45">
<div className="w-2 h-2 bg-brand-accent rounded-full animate-ping" />
{t('dashboard.translate.liveMonitor')}
</h4>
@@ -603,7 +603,7 @@ export default function TranslatePage() {
<p className="text-[11px] font-black uppercase tracking-tight truncate text-brand-dark dark:text-white">
{submit.fileName || upload.file?.name}
</p>
<p className="text-[9px] text-brand-dark/40 font-bold uppercase tracking-widest mt-1 dark:text-white/40">
<p className="text-[11px] text-brand-dark/45 font-bold uppercase tracking-widest mt-1 dark:text-white/45">
{upload.file ? `${fmt(upload.file.size)} ` : ''}{(submit.fileName || upload.file?.name || '').split('.').pop()?.toUpperCase()}
</p>
</div>
@@ -612,16 +612,16 @@ export default function TranslatePage() {
{/* Config summary */}
<div className="space-y-8 mb-16 px-2">
<div className="flex justify-between items-center text-[10px] font-black uppercase tracking-[0.4em] text-brand-dark/30 dark:text-white/30">
<div className="flex justify-between items-center text-[10px] font-black uppercase tracking-[0.4em] text-brand-dark/50 dark:text-white/50">
<span>{t('dashboard.translate.language.source')}</span>
<span className="text-brand-dark dark:text-white">{srcLangName.toUpperCase()}</span>
</div>
<div className="flex justify-between items-center text-[10px] font-black uppercase tracking-[0.4em] text-brand-dark/30 dark:text-white/30">
<div className="flex justify-between items-center text-[10px] font-black uppercase tracking-[0.4em] text-brand-dark/50 dark:text-white/50">
<span>{t('dashboard.translate.language.target')}</span>
<span className="text-brand-accent">{tgtLangName.toUpperCase()}</span>
</div>
{currentProvider && (
<div className="flex justify-between items-center text-[10px] font-black uppercase tracking-[0.4em] text-brand-dark/30 dark:text-white/30">
<div className="flex justify-between items-center text-[10px] font-black uppercase tracking-[0.4em] text-brand-dark/50 dark:text-white/50">
<span>{t('dashboard.translate.engine')}</span>
<span className="text-brand-dark dark:text-white">{currentProvider.label.toUpperCase()}</span>
</div>
@@ -631,7 +631,7 @@ export default function TranslatePage() {
{/* Quality progress */}
<div className="pt-10 border-t border-black/10 dark:border-white/10">
<div className="flex justify-between text-[10px] font-black uppercase tracking-[0.4em] mb-4">
<span className="text-brand-dark/30 dark:text-white/30">{t('dashboard.translate.quality')}</span>
<span className="text-brand-dark/50 dark:text-white/50">{t('dashboard.translate.quality')}</span>
<span className="text-brand-accent">{qualityLabel}</span>
</div>
<div className="h-2 bg-brand-muted rounded-full overflow-hidden p-0.5 dark:bg-white/10">
@@ -654,22 +654,22 @@ export default function TranslatePage() {
{/* ── SUMMARY (complete) ──────────────────────────────── */}
{showComplete && (
<div className="editorial-card p-10 bg-white border-none shadow-editorial h-full dark:bg-[#141414]">
<h4 className="text-[10px] font-black uppercase tracking-[0.4em] mb-12 flex items-center gap-3 text-brand-dark/30 dark:text-white/30">
<h4 className="text-[11px] font-black uppercase tracking-[0.3em] mb-12 flex items-center gap-3 text-brand-dark/45 dark:text-white/45">
<CheckCircle2 size={14} className="text-emerald-500" />
{t('dashboard.translate.summary')}
</h4>
<div className="space-y-8 mb-16 px-2">
<div className="flex justify-between items-center text-[10px] font-black uppercase tracking-[0.4em] text-brand-dark/30 dark:text-white/30">
<div className="flex justify-between items-center text-[10px] font-black uppercase tracking-[0.4em] text-brand-dark/50 dark:text-white/50">
<span>{t('dashboard.translate.language.source')}</span>
<span className="text-brand-dark dark:text-white">{srcLangName.toUpperCase()}</span>
</div>
<div className="flex justify-between items-center text-[10px] font-black uppercase tracking-[0.4em] text-brand-dark/30 dark:text-white/30">
<div className="flex justify-between items-center text-[10px] font-black uppercase tracking-[0.4em] text-brand-dark/50 dark:text-white/50">
<span>{t('dashboard.translate.language.target')}</span>
<span className="text-brand-accent">{tgtLangName.toUpperCase()}</span>
</div>
{currentProvider && (
<div className="flex justify-between items-center text-[10px] font-black uppercase tracking-[0.4em] text-brand-dark/30 dark:text-white/30">
<div className="flex justify-between items-center text-[10px] font-black uppercase tracking-[0.4em] text-brand-dark/50 dark:text-white/50">
<span>{t('dashboard.translate.engine')}</span>
<span className="text-brand-dark dark:text-white">{currentProvider.label.toUpperCase()}</span>
</div>
@@ -678,7 +678,7 @@ export default function TranslatePage() {
<div className="pt-10 border-t border-black/10 dark:border-white/10">
<div className="flex justify-between text-[10px] font-black uppercase tracking-[0.4em] mb-4">
<span className="text-brand-dark/30 dark:text-white/30">{t('dashboard.translate.quality')}</span>
<span className="text-brand-dark/50 dark:text-white/50">{t('dashboard.translate.quality')}</span>
<span className="text-brand-accent">{qualityLabel}</span>
</div>
<div className="h-2 bg-brand-muted rounded-full overflow-hidden p-0.5 dark:bg-white/10">
@@ -701,7 +701,7 @@ export default function TranslatePage() {
<div className="flex-1">
<div className="flex items-center gap-4 mb-4">
<span className="accent-pill !px-3 !py-1 text-[9px] italic">{t('memento.title')}</span>
<span className="accent-pill !px-3 !py-1 text-[11px] italic">{t('memento.title')}</span>
<h3 className="text-2xl font-black uppercase tracking-tight text-brand-dark dark:text-white">{t('memento.title')}</h3>
</div>
<p className="text-sm text-brand-dark/40 font-medium leading-relaxed max-w-2xl dark:text-white/40">
@@ -736,12 +736,12 @@ function FileStrip({ file, onRemove, onReplace, t }: { file: File; onRemove: ()
<FileIcon className={`size-5 shrink-0 ${color}`} />
<div className="flex min-w-0 flex-1 flex-col">
<span className="truncate text-[11px] font-black uppercase tracking-tight text-brand-dark dark:text-white">{file.name}</span>
<span className="text-[9px] text-brand-dark/40 font-bold uppercase tracking-widest dark:text-white/40">{fmt(file.size)} .{ext.toUpperCase()}</span>
<span className="text-[10px] text-brand-dark/45 font-bold uppercase tracking-widest dark:text-white/45">{fmt(file.size)} .{ext.toUpperCase()}</span>
</div>
<button type="button" onClick={onReplace} className="flex shrink-0 items-center gap-1 rounded-xl px-2 py-1 text-[10px] font-black uppercase tracking-widest text-brand-dark/30 transition hover:bg-brand-muted hover:text-brand-dark dark:text-white/30 dark:hover:bg-white/10 dark:hover:text-white">
<button type="button" onClick={onReplace} className="flex shrink-0 items-center gap-1 rounded-xl px-2 py-1 text-[10px] font-black uppercase tracking-widest text-brand-dark/50 transition hover:bg-brand-muted hover:text-brand-dark dark:text-white/50 dark:hover:bg-white/10 dark:hover:text-white">
<Upload className="size-3.5" />{t('dashboard.translate.replace')}
</button>
<button type="button" aria-label="Remove" onClick={onRemove} className="flex size-7 shrink-0 items-center justify-center rounded-xl text-brand-dark/20 transition hover:bg-brand-muted hover:text-brand-dark dark:text-white/20 dark:hover:bg-white/10 dark:hover:text-white">
<button type="button" aria-label="Remove" onClick={onRemove} className="flex size-7 shrink-0 items-center justify-center rounded-xl text-brand-dark/40 transition hover:bg-brand-muted hover:text-brand-dark dark:text-white/40 dark:hover:bg-white/10 dark:hover:text-white">
<X className="size-4" />
</button>
</div>
@@ -789,7 +789,7 @@ function StatBox({ icon, value, label }: { icon: React.ReactNode; value: string;
<div className="p-6 bg-brand-muted/30 rounded-3xl text-center border border-transparent hover:border-brand-accent/10 transition-all dark:bg-white/5 dark:border-white/5">
<div className="text-brand-accent flex justify-center mb-4">{icon}</div>
<p className="text-[12px] font-black text-brand-dark mb-1 uppercase tracking-tight dark:text-white">{value}</p>
<p className="text-[8px] font-black text-brand-dark/30 uppercase tracking-[0.2em] dark:text-white/30">{label}</p>
<p className="text-[11px] font-black text-brand-dark/50 uppercase tracking-[0.15em] dark:text-white/50">{label}</p>
</div>
);
}

View File

@@ -814,7 +814,7 @@ export default function PricingPage() {
<Badge className="ml-auto text-xs bg-blue-500/10 text-blue-600 border-blue-500/30 dark:text-blue-300">{t('pricing.aiModels.essential.plan')}</Badge>
</div>
<div className="text-sm text-muted-foreground mb-3">
{t('pricing.aiModels.essential.descPrefix')} <strong className="text-foreground">DeepSeek V3.2</strong> {t('pricing.aiModels.essential.descSuffix')}
{t('pricing.aiModels.essential.descPrefix')} <strong className="text-foreground">{t('pricing.aiModels.essential.modelName')}</strong> {t('pricing.aiModels.essential.descSuffix')}
</div>
<div className="flex flex-wrap gap-2 text-xs">
<span className="px-2 py-1 bg-muted rounded">{t('pricing.aiModels.essential.context')}</span>

View File

@@ -140,7 +140,7 @@ const messages: Record<Locale, Record<string, string>> = {
"landing.pricing.pro.desc": "For demanding professionals",
"landing.pricing.pro.f1": "200 documents / month",
"landing.pricing.pro.f2": "Up to 200 pages per doc",
"landing.pricing.pro.f3": "AI-powered translation (DeepSeek)",
"landing.pricing.pro.f3": "AI-powered translation",
"landing.pricing.pro.f4": "Google + DeepL included",
"landing.pricing.pro.f5": "Custom glossaries & prompts",
"landing.pricing.pro.f6": "Priority support",
@@ -244,6 +244,26 @@ const messages: Record<Locale, Record<string, string>> = {
"dashboard.translate.pipeline.rebuild": "Rebuild",
"dashboard.translate.pipeline.finalize": "Finalize",
"dashboard.translate.progress.failedTitle": "Translation failed",
"dashboard.translate.error.unexpected": "An unexpected error occurred. Please try again.",
"dashboard.translate.error.noResult": "Translation produced no results. Verify the document contains text, then try again or choose another engine.",
"dashboard.translate.error.apiKey": "Invalid or missing API key. Contact the administrator to configure API keys.",
"dashboard.translate.error.quota": "Usage limit reached. Try again in a few minutes or choose another engine.",
"dashboard.translate.error.timeout": "Connection to the translation service timed out. Check your network and try again.",
"dashboard.translate.error.sessionExpired": "Session expired. Click Retry to restart the translation.",
"dashboard.translate.error.empty": "The document appears empty or contains no translatable text (scanned PDF image?).",
"dashboard.translate.error.unsupported": "Unsupported file format or corrupted file.",
"dashboard.translate.error.connection": "Connection lost. Check your network and try again.",
"dashboard.translate.error.generic": "Translation failed: {detail}",
"dashboard.translate.error.title": "Translation failed",
"dashboard.translate.retry": "Retry translation",
"dashboard.translate.newFile": "New file",
"dashboard.translate.modeAI": "AI Mode",
"dashboard.translate.modeClassic": "Classic Mode",
"dashboard.translate.glossaryLLMHint": "Glossaries available in AI mode",
"dashboard.translate.submitting": "Submitting...",
"dashboard.translate.submit": "Start translation",
"dashboard.translate.noFile": "Upload a file first",
"dashboard.translate.noTargetLang": "Select a target language",
"glossaries.yourGlossaries": "Your glossaries",
"glossaries.title": "Glossaries & Context",
"glossaries.description": "Manage your glossaries and context instructions for more accurate translations.",
@@ -341,7 +361,7 @@ const messages: Record<Locale, Record<string, string>> = {
"pricing.plans.pro.feat1": "200 documents / month",
"pricing.plans.pro.feat2": "Up to 200 pages per document",
"pricing.plans.pro.feat3": "Essential AI Translation (DeepSeek V3.2)",
"pricing.plans.pro.feat3": "Essential AI Translation",
"pricing.plans.pro.feat4": "Google Translation + DeepL",
"pricing.plans.pro.feat5": "Files up to 25 MB",
"pricing.plans.pro.feat6": "Custom glossaries",
@@ -428,7 +448,8 @@ const messages: Record<Locale, Record<string, string>> = {
"pricing.aiModels.essential.title": "Essential AI Translation",
"pricing.aiModels.essential.plan": "Pro Plan",
"pricing.aiModels.essential.descPrefix": "Based on",
"pricing.aiModels.essential.descSuffix": "— the most cost-effective AI model of 2026. Quality comparable to frontier models at 1/50th of the cost.",
"pricing.aiModels.essential.descSuffix": "— the most cost-effective AI model of 2026. Quality comparable to frontier models at a fraction of the cost.",
"pricing.aiModels.essential.modelName": "our Essential AI model",
"pricing.aiModels.essential.context": "163K tokens of context",
"pricing.aiModels.essential.value": "Excellent value for money",
"pricing.aiModels.premium.title": "Premium AI Translation",
@@ -442,9 +463,9 @@ const messages: Record<Locale, Record<string, string>> = {
"pricing.faq.q1": "Can I change plans at any time?",
"pricing.faq.a1": "Yes. Upgrading is immediate and prorated. Downgrading takes effect at the end of the current period.",
"pricing.faq.q2": "What is \"Essential AI Translation\"?",
"pricing.faq.a2": "It's our AI engine based on DeepSeek V3.2 via OpenRouter. It understands your documents' context, preserves layout and handles technical terms much better than classic translation.",
"pricing.faq.a2": "It's our AI engine. It understands your documents' context, preserves layout and handles technical terms much better than classic translation.",
"pricing.faq.q3": "What's the difference between Essential and Premium AI?",
"pricing.faq.a3": "Essential AI uses DeepSeek V3.2 (excellent value for money). Premium AI uses Anthropic's Claude 3.5 Haiku, more accurate on legal, medical and complex technical documents.",
"pricing.faq.a3": "Essential AI uses an optimized model (excellent value for money). Premium AI uses Anthropic's Claude 3.5 Haiku, more accurate on legal, medical and complex technical documents.",
"pricing.faq.q4": "Are my documents kept after translation?",
"pricing.faq.a4": "Translated files are available according to your plan (30 days Starter, 90 days Pro, 1 year Business). They are encrypted at rest and in transit.",
"pricing.faq.q5": "What happens if I exceed my monthly quota?",
@@ -933,7 +954,7 @@ const messages: Record<Locale, Record<string, string>> = {
"landing.pricing.pro.desc": "Pour les professionnels exigeants",
"landing.pricing.pro.f1": "200 documents / mois",
"landing.pricing.pro.f2": "Jusqu'à 200 pages par doc",
"landing.pricing.pro.f3": "Traduction par IA (DeepSeek)",
"landing.pricing.pro.f3": "Traduction par IA",
"landing.pricing.pro.f4": "Google + DeepL inclus",
"landing.pricing.pro.f5": "Glossaires et prompts",
"landing.pricing.pro.f6": "Support prioritaire",
@@ -1032,6 +1053,26 @@ const messages: Record<Locale, Record<string, string>> = {
"dashboard.translate.pipeline.rebuild": "Reconstruction",
"dashboard.translate.pipeline.finalize": "Finalisation",
"dashboard.translate.progress.failedTitle": "Traduction échouée",
"dashboard.translate.error.unexpected": "Une erreur inattendue est survenue. Réessayez.",
"dashboard.translate.error.noResult": "La traduction n'a produit aucun résultat. Vérifiez que le document contient du texte, puis réessayez ou choisissez un autre moteur.",
"dashboard.translate.error.apiKey": "Clé API invalide ou manquante. Contactez l'administrateur pour configurer les clés.",
"dashboard.translate.error.quota": "Limite d'utilisation atteinte. Réessayez dans quelques minutes ou choisissez un autre moteur.",
"dashboard.translate.error.timeout": "La connexion au service de traduction a expiré. Vérifiez votre réseau et réessayez.",
"dashboard.translate.error.sessionExpired": "La session a expiré. Cliquez sur Réessayer pour relancer la traduction.",
"dashboard.translate.error.empty": "Le document semble vide ou ne contient pas de texte traduisible (PDF image ?).",
"dashboard.translate.error.unsupported": "Format de fichier non supporté ou fichier corrompu.",
"dashboard.translate.error.connection": "Connexion perdue. Vérifiez votre réseau et réessayez.",
"dashboard.translate.error.generic": "Traduction échouée : {detail}",
"dashboard.translate.error.title": "Traduction échouée",
"dashboard.translate.retry": "Réessayer la traduction",
"dashboard.translate.newFile": "Nouveau fichier",
"dashboard.translate.modeAI": "Mode IA",
"dashboard.translate.modeClassic": "Mode Classique",
"dashboard.translate.glossaryLLMHint": "Glossaires disponibles en mode IA",
"dashboard.translate.submitting": "Envoi en cours...",
"dashboard.translate.submit": "Lancer la traduction",
"dashboard.translate.noFile": "Déposez d'abord un fichier",
"dashboard.translate.noTargetLang": "Sélectionnez une langue cible",
"glossaries.yourGlossaries": "Vos glossaires",
"glossaries.title": "Glossaires & Contexte",
"glossaries.description": "Gérez vos glossaires et instructions de contexte pour des traductions plus précises.",
@@ -1132,7 +1173,7 @@ const messages: Record<Locale, Record<string, string>> = {
"pricing.plans.pro.feat1": "200 documents / mois",
"pricing.plans.pro.feat2": "Jusqu'à 200 pages par document",
"pricing.plans.pro.feat3": "Traduction IA Essentielle (DeepSeek V3.2)",
"pricing.plans.pro.feat3": "Traduction IA Essentielle",
"pricing.plans.pro.feat4": "Google Traduction + DeepL",
"pricing.plans.pro.feat5": "Fichiers jusqu'à 25 Mo",
"pricing.plans.pro.feat6": "Glossaires personnalisés",
@@ -1219,7 +1260,8 @@ const messages: Record<Locale, Record<string, string>> = {
"pricing.aiModels.essential.title": "Traduction IA Essentielle",
"pricing.aiModels.essential.plan": "Forfait Pro",
"pricing.aiModels.essential.descPrefix": "Basée sur",
"pricing.aiModels.essential.descSuffix": "— le modèle IA le plus rentable de 2026. Qualité comparable aux modèles frontier à 1/50ème du coût.",
"pricing.aiModels.essential.descSuffix": "— le modèle IA le plus rentable de 2026. Qualité comparable aux modèles frontier à une fraction du coût.",
"pricing.aiModels.essential.modelName": "notre modèle IA Essentiel",
"pricing.aiModels.essential.context": "163K tokens de contexte",
"pricing.aiModels.essential.value": "Excellent rapport qualité/prix",
"pricing.aiModels.premium.title": "Traduction IA Premium",
@@ -1233,9 +1275,9 @@ const messages: Record<Locale, Record<string, string>> = {
"pricing.faq.q1": "Puis-je changer de forfait à tout moment ?",
"pricing.faq.a1": "Oui. Le passage à un forfait supérieur est immédiat et proratisé. La rétrogradation prend effet à la fin de la période en cours.",
"pricing.faq.q2": "Qu'est-ce que la « Traduction IA Essentielle » ?",
"pricing.faq.a2": "C'est notre moteur IA basé sur DeepSeek V3.2 via OpenRouter. Il comprend le contexte de vos documents, préserve la mise en page et gère les termes techniques bien mieux qu'une traduction classique.",
"pricing.faq.a2": "C'est notre moteur IA. Il comprend le contexte de vos documents, préserve la mise en page et gère les termes techniques bien mieux qu'une traduction classique.",
"pricing.faq.q3": "Quelle est la différence entre IA Essentielle et IA Premium ?",
"pricing.faq.a3": "L'IA Essentielle utilise DeepSeek V3.2 (excellent rapport qualité/prix). L'IA Premium utilise Claude 3.5 Haiku d'Anthropic, plus précis sur les documents juridiques, médicaux et techniques complexes.",
"pricing.faq.a3": "L'IA Essentielle utilise un modèle optimisé (excellent rapport qualité/prix). L'IA Premium utilise Claude 3.5 Haiku d'Anthropic, plus précis sur les documents juridiques, médicaux et techniques complexes.",
"pricing.faq.q4": "Mes documents sont-ils conservés après traduction ?",
"pricing.faq.a4": "Les fichiers traduits sont disponibles selon votre forfait (30 jours Starter, 90 jours Pro, 1 an Business). Ils sont chiffrés au repos et en transit.",
"pricing.faq.q5": "Que se passe-t-il si je dépasse mon quota mensuel ?",
@@ -1728,7 +1770,7 @@ const messages: Record<Locale, Record<string, string>> = {
"landing.pricing.pro.desc": "Para profesionales exigentes",
"landing.pricing.pro.f1": "200 documentos / mes",
"landing.pricing.pro.f2": "Hasta 200 páginas por doc",
"landing.pricing.pro.f3": "Traducción con IA (DeepSeek)",
"landing.pricing.pro.f3": "Traducción con IA",
"landing.pricing.pro.f4": "Google + DeepL incluidos",
"landing.pricing.pro.f5": "Glosarios y prompts personalizados",
"landing.pricing.pro.f6": "Soporte prioritario",
@@ -1817,6 +1859,26 @@ const messages: Record<Locale, Record<string, string>> = {
"dashboard.translate.pipeline.rebuild": "Reconstruir",
"dashboard.translate.pipeline.finalize": "Finalizar",
"dashboard.translate.progress.failedTitle": "Traducción fallida",
"dashboard.translate.error.unexpected": "Ocurrió un error inesperado. Inténtelo de nuevo.",
"dashboard.translate.error.noResult": "La traducción no produjo resultados. Verifique que el documento contenga texto e intente de nuevo o elija otro motor.",
"dashboard.translate.error.apiKey": "Clave API no válida o faltante. Contacte al administrador para configurar las claves.",
"dashboard.translate.error.quota": "Límite de uso alcanzado. Intente de nuevo en unos minutos o elija otro motor.",
"dashboard.translate.error.timeout": "La conexión al servicio de traducción expiró. Verifique su red e intente de nuevo.",
"dashboard.translate.error.sessionExpired": "Sesión expirada. Haga clic en Reintentar para reiniciar la traducción.",
"dashboard.translate.error.empty": "El documento parece vacío o no contiene texto traducible (¿PDF escaneado?).",
"dashboard.translate.error.unsupported": "Formato de archivo no compatible o archivo dañado.",
"dashboard.translate.error.connection": "Conexión perdida. Verifique su red e intente de nuevo.",
"dashboard.translate.error.generic": "Traducción fallida: {detail}",
"dashboard.translate.error.title": "Traducción fallida",
"dashboard.translate.retry": "Reintentar traducción",
"dashboard.translate.newFile": "Nuevo archivo",
"dashboard.translate.modeAI": "Modo IA",
"dashboard.translate.modeClassic": "Modo Clásico",
"dashboard.translate.glossaryLLMHint": "Glosarios disponibles en modo IA",
"dashboard.translate.submitting": "Enviando...",
"dashboard.translate.submit": "Iniciar traducción",
"dashboard.translate.noFile": "Suba un archivo primero",
"dashboard.translate.noTargetLang": "Seleccione un idioma de destino",
"glossaries.yourGlossaries": "Tus glosarios",
"glossaries.title": "Glosarios y Contexto",
"glossaries.description": "Gestiona tus glosarios e instrucciones de contexto para traducciones más precisas.",
@@ -1905,7 +1967,7 @@ const messages: Record<Locale, Record<string, string>> = {
"pricing.plans.starter.feat6": "Historial de 30 días",
"pricing.plans.pro.feat1": "200 documentos / mes",
"pricing.plans.pro.feat2": "Hasta 200 páginas por documento",
"pricing.plans.pro.feat3": "Traducción IA Esencial (DeepSeek V3.2)",
"pricing.plans.pro.feat3": "Traducción IA Esencial",
"pricing.plans.pro.feat4": "Traductor de Google + DeepL",
"pricing.plans.pro.feat5": "Archivos de hasta 25 MB",
"pricing.plans.pro.feat6": "Glosarios personalizados",
@@ -1985,7 +2047,8 @@ const messages: Record<Locale, Record<string, string>> = {
"pricing.aiModels.essential.title": "Traducción IA Esencial",
"pricing.aiModels.essential.plan": "Plan Pro",
"pricing.aiModels.essential.descPrefix": "Basado en",
"pricing.aiModels.essential.descSuffix": "— el modelo de IA más rentable de 2026. Calidad comparable a los modelos frontier a 1/50 del coste.",
"pricing.aiModels.essential.descSuffix": "— el modelo de IA más rentable de 2026. Calidad comparable a los modelos frontier a una fracción del coste.",
"pricing.aiModels.essential.modelName": "nuestro modelo IA Esencial",
"pricing.aiModels.essential.context": "163K tokens de contexto",
"pricing.aiModels.essential.value": "Excelente relación calidad-precio",
"pricing.aiModels.premium.title": "Traducción IA Premium",
@@ -1998,9 +2061,9 @@ const messages: Record<Locale, Record<string, string>> = {
"pricing.faq.q1": "¿Puedo cambiar de plan en cualquier momento?",
"pricing.faq.a1": "Sí. La mejora es inmediata y prorrateada. La bajada se aplica al final del periodo actual.",
"pricing.faq.q2": "¿Qué es la «Traducción IA Esencial»?",
"pricing.faq.a2": "Es nuestro motor de IA basado en DeepSeek V3.2 a través de OpenRouter. Comprende el contexto de tus documentos, conserva el diseño y maneja términos técnicos mucho mejor que una traducción clásica.",
"pricing.faq.a2": "Es nuestro motor de IA. Comprende el contexto de tus documentos, conserva el diseño y maneja términos técnicos mucho mejor que una traducción clásica.",
"pricing.faq.q3": "¿Cuál es la diferencia entre IA Esencial e IA Premium?",
"pricing.faq.a3": "La IA Esencial usa DeepSeek V3.2 (excelente relación calidad-precio). La IA Premium usa Claude 3.5 Haiku de Anthropic, más precisa en documentos jurídicos, médicos y técnicos complejos.",
"pricing.faq.a3": "La IA Esencial usa un modelo optimizado (excelente relación calidad-precio). La IA Premium usa Claude 3.5 Haiku de Anthropic, más precisa en documentos jurídicos, médicos y técnicos complejos.",
"pricing.faq.q4": "¿Se conservan mis documentos después de la traducción?",
"pricing.faq.a4": "Los archivos traducidos están disponibles según tu plan (30 días Starter, 90 días Pro, 1 año Business). Están cifrados en reposo y en tránsito.",
"pricing.faq.q5": "¿Qué ocurre si supero mi cuota mensual?",
@@ -2465,7 +2528,7 @@ const messages: Record<Locale, Record<string, string>> = {
"landing.pricing.pro.desc": "Für anspruchsvolle Profis",
"landing.pricing.pro.f1": "200 Dokumente / Monat",
"landing.pricing.pro.f2": "Bis zu 200 Seiten pro Dokument",
"landing.pricing.pro.f3": "KI-gestützte Übersetzung (DeepSeek)",
"landing.pricing.pro.f3": "KI-gestützte Übersetzung",
"landing.pricing.pro.f4": "Google + DeepL inklusive",
"landing.pricing.pro.f5": "Individuelle Glossare & Prompts",
"landing.pricing.pro.f6": "Prioritäts-Support",
@@ -2554,6 +2617,26 @@ const messages: Record<Locale, Record<string, string>> = {
"dashboard.translate.pipeline.rebuild": "Rekonstruieren",
"dashboard.translate.pipeline.finalize": "Finalisieren",
"dashboard.translate.progress.failedTitle": "Übersetzung fehlgeschlagen",
"dashboard.translate.error.unexpected": "Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es erneut.",
"dashboard.translate.error.noResult": "Die Übersetzung hat keine Ergebnisse geliefert. Stellen Sie sicher, dass das Dokument Text enthält, und versuchen Sie es erneut oder wählen Sie eine andere Engine.",
"dashboard.translate.error.apiKey": "Ungültiger oder fehlender API-Schlüssel. Kontaktieren Sie den Administrator zur Konfiguration.",
"dashboard.translate.error.quota": "Nutzungslimit erreicht. Versuchen Sie es in wenigen Minuten erneut oder wählen Sie eine andere Engine.",
"dashboard.translate.error.timeout": "Verbindung zum Übersetzungsdienst zeitüberschritten. Prüfen Sie Ihr Netzwerk und versuchen Sie es erneut.",
"dashboard.translate.error.sessionExpired": "Sitzung abgelaufen. Klicken Sie auf Erneut versuchen, um die Übersetzung neu zu starten.",
"dashboard.translate.error.empty": "Das Dokument scheint leer zu sein oder enthält keinen übersetzbaren Text (eingescanntes PDF?).",
"dashboard.translate.error.unsupported": "Nicht unterstütztes Dateiformat oder beschädigte Datei.",
"dashboard.translate.error.connection": "Verbindung verloren. Prüfen Sie Ihr Netzwerk und versuchen Sie es erneut.",
"dashboard.translate.error.generic": "Übersetzung fehlgeschlagen: {detail}",
"dashboard.translate.error.title": "Übersetzung fehlgeschlagen",
"dashboard.translate.retry": "Übersetzung erneut versuchen",
"dashboard.translate.newFile": "Neue Datei",
"dashboard.translate.modeAI": "KI-Modus",
"dashboard.translate.modeClassic": "Klassischer Modus",
"dashboard.translate.glossaryLLMHint": "Glossare im KI-Modus verfügbar",
"dashboard.translate.submitting": "Wird gesendet...",
"dashboard.translate.submit": "Übersetzung starten",
"dashboard.translate.noFile": "Laden Sie zuerst eine Datei hoch",
"dashboard.translate.noTargetLang": "Wählen Sie eine Zielsprache",
"glossaries.yourGlossaries": "Ihre Glossare",
"glossaries.title": "Glossare & Kontext",
"glossaries.description": "Verwalten Sie Ihre Glossare und Kontextanweisungen für genauere Übersetzungen.",
@@ -2642,7 +2725,7 @@ const messages: Record<Locale, Record<string, string>> = {
"pricing.plans.starter.feat6": "30 Tage Verlauf",
"pricing.plans.pro.feat1": "200 Dokumente / Monat",
"pricing.plans.pro.feat2": "Bis zu 200 Seiten pro Dokument",
"pricing.plans.pro.feat3": "KI-Basisübersetzung (DeepSeek V3.2)",
"pricing.plans.pro.feat3": "KI-Basisübersetzung",
"pricing.plans.pro.feat4": "Google Übersetzer + DeepL",
"pricing.plans.pro.feat5": "Dateien bis zu 25 MB",
"pricing.plans.pro.feat6": "Benutzerdefinierte Glossare",
@@ -2722,7 +2805,8 @@ const messages: Record<Locale, Record<string, string>> = {
"pricing.aiModels.essential.title": "KI-Basisübersetzung",
"pricing.aiModels.essential.plan": "Pro-Plan",
"pricing.aiModels.essential.descPrefix": "Basierend auf",
"pricing.aiModels.essential.descSuffix": "— dem kosteneffizientesten KI-Modell 2026. Qualität vergleichbar mit Frontier-Modellen zu 1/50 der Kosten.",
"pricing.aiModels.essential.descSuffix": "— dem kosteneffizientesten KI-Modell 2026. Qualität vergleichbar mit Frontier-Modellen zu einem Bruchteil der Kosten.",
"pricing.aiModels.essential.modelName": "unser Essentielles KI-Modell",
"pricing.aiModels.essential.context": "163K Token Kontext",
"pricing.aiModels.essential.value": "Hervorragendes Preis-Leistungs-Verhältnis",
"pricing.aiModels.premium.title": "KI-Premiumübersetzung",
@@ -2735,9 +2819,9 @@ const messages: Record<Locale, Record<string, string>> = {
"pricing.faq.q1": "Kann ich jederzeit den Plan wechseln?",
"pricing.faq.a1": "Ja. Ein Upgrade erfolgt sofort und anteilig. Ein Downgrade wird am Ende des aktuellen Zeitraums wirksam.",
"pricing.faq.q2": "Was ist die «KI-Basisübersetzung»?",
"pricing.faq.a2": "Unser KI-Motor basiert auf DeepSeek V3.2 über OpenRouter. Er versteht den Kontext Ihrer Dokumente, erhält das Layout und verarbeitet Fachbegriffe deutlich besser als klassische Übersetzungen.",
"pricing.faq.a2": "Unser KI-Motor versteht den Kontext Ihrer Dokumente, erhält das Layout und verarbeitet Fachbegriffe deutlich besser als klassische Übersetzungen.",
"pricing.faq.q3": "Was ist der Unterschied zwischen Basis- und Premium-KI?",
"pricing.faq.a3": "Die Basis-KI nutzt DeepSeek V3.2 (hervorragendes Preis-Leistungs-Verhältnis). Die Premium-KI verwendet Claude 3.5 Haiku von Anthropic und ist genauer bei juristischen, medizinischen und komplexen technischen Dokumenten.",
"pricing.faq.a3": "Die Basis-KI nutzt ein optimiertes Modell (hervorragendes Preis-Leistungs-Verhältnis). Die Premium-KI verwendet Claude 3.5 Haiku von Anthropic und ist genauer bei juristischen, medizinischen und komplexen technischen Dokumenten.",
"pricing.faq.q4": "Werden meine Dokumente nach der Übersetzung gespeichert?",
"pricing.faq.a4": "Übersetzte Dateien sind je nach Plan verfügbar (30 Tage Starter, 90 Tage Pro, 1 Jahr Business). Sie sind im Ruhezustand und bei der Übertragung verschlüsselt.",
"pricing.faq.q5": "Was passiert, wenn ich mein monatliches Kontingent überschreite?",
@@ -3202,7 +3286,7 @@ const messages: Record<Locale, Record<string, string>> = {
"landing.pricing.pro.desc": "Para profissionais exigentes",
"landing.pricing.pro.f1": "200 documentos / mês",
"landing.pricing.pro.f2": "Até 200 páginas por doc",
"landing.pricing.pro.f3": "Tradução com IA (DeepSeek)",
"landing.pricing.pro.f3": "Tradução com IA",
"landing.pricing.pro.f4": "Google + DeepL incluídos",
"landing.pricing.pro.f5": "Glossários e prompts personalizados",
"landing.pricing.pro.f6": "Suporte prioritário",
@@ -3291,6 +3375,26 @@ const messages: Record<Locale, Record<string, string>> = {
"dashboard.translate.pipeline.rebuild": "Reconstruir",
"dashboard.translate.pipeline.finalize": "Finalizar",
"dashboard.translate.progress.failedTitle": "Tradução falhou",
"dashboard.translate.error.unexpected": "Ocorreu um erro inesperado. Tente novamente.",
"dashboard.translate.error.noResult": "A tradução não produziu resultados. Verifique se o documento contém texto e tente novamente ou escolha outro motor.",
"dashboard.translate.error.apiKey": "Chave de API inválida ou ausente. Contacte o administrador para configurar as chaves.",
"dashboard.translate.error.quota": "Limite de uso atingido. Tente novamente em alguns minutos ou escolha outro motor.",
"dashboard.translate.error.timeout": "A conexão ao serviço de tradução expirou. Verifique a sua rede e tente novamente.",
"dashboard.translate.error.sessionExpired": "Sessão expirada. Clique em Repetir para reiniciar a tradução.",
"dashboard.translate.error.empty": "O documento parece estar vazio ou não contém texto traduzível (PDF digitalizado?).",
"dashboard.translate.error.unsupported": "Formato de ficheiro não suportado ou ficheiro corrompido.",
"dashboard.translate.error.connection": "Conexão perdida. Verifique a sua rede e tente novamente.",
"dashboard.translate.error.generic": "Tradução falhou: {detail}",
"dashboard.translate.error.title": "Tradução falhou",
"dashboard.translate.retry": "Tentar novamente",
"dashboard.translate.newFile": "Novo ficheiro",
"dashboard.translate.modeAI": "Modo IA",
"dashboard.translate.modeClassic": "Modo Clássico",
"dashboard.translate.glossaryLLMHint": "Glossários disponíveis no modo IA",
"dashboard.translate.submitting": "A enviar...",
"dashboard.translate.submit": "Iniciar tradução",
"dashboard.translate.noFile": "Carregue um ficheiro primeiro",
"dashboard.translate.noTargetLang": "Selecione um idioma de destino",
"glossaries.yourGlossaries": "Seus glossários",
"glossaries.title": "Glossários e Contexto",
"glossaries.description": "Gerencie seus glossários e instruções de contexto para traduções mais precisas.",
@@ -3379,7 +3483,7 @@ const messages: Record<Locale, Record<string, string>> = {
"pricing.plans.starter.feat6": "Histórico de 30 dias",
"pricing.plans.pro.feat1": "200 documentos / mês",
"pricing.plans.pro.feat2": "Até 200 páginas por documento",
"pricing.plans.pro.feat3": "Tradução IA Essencial (DeepSeek V3.2)",
"pricing.plans.pro.feat3": "Tradução IA Essencial",
"pricing.plans.pro.feat4": "Google Tradutor + DeepL",
"pricing.plans.pro.feat5": "Arquivos de até 25 MB",
"pricing.plans.pro.feat6": "Glossários personalizados",
@@ -3459,7 +3563,8 @@ const messages: Record<Locale, Record<string, string>> = {
"pricing.aiModels.essential.title": "Tradução IA Essencial",
"pricing.aiModels.essential.plan": "Plano Pro",
"pricing.aiModels.essential.descPrefix": "Baseado em",
"pricing.aiModels.essential.descSuffix": "— o modelo de IA mais econômico de 2026. Qualidade comparável a modelos frontier a 1/50 do custo.",
"pricing.aiModels.essential.descSuffix": "— o modelo de IA mais econômico de 2026. Qualidade comparável a modelos frontier a uma fração do custo.",
"pricing.aiModels.essential.modelName": "nosso modelo IA Essencial",
"pricing.aiModels.essential.context": "163K tokens de contexto",
"pricing.aiModels.essential.value": "Excelente custo-benefício",
"pricing.aiModels.premium.title": "Tradução IA Premium",
@@ -3472,9 +3577,9 @@ const messages: Record<Locale, Record<string, string>> = {
"pricing.faq.q1": "Posso trocar de plano a qualquer momento?",
"pricing.faq.a1": "Sim. O upgrade é imediato e proporcional. O downgrade entra em vigor no final do período atual.",
"pricing.faq.q2": "O que é a «Tradução IA Essencial»?",
"pricing.faq.a2": "É nosso motor de IA baseado no DeepSeek V3.2 via OpenRouter. Ele compreende o contexto dos seus documentos, preserva a formatação e lida com termos técnicos muito melhor do que a tradução clássica.",
"pricing.faq.a2": "É nosso motor de IA. Ele compreende o contexto dos seus documentos, preserva a formatação e lida com termos técnicos muito melhor do que a tradução clássica.",
"pricing.faq.q3": "Qual a diferença entre IA Essencial e IA Premium?",
"pricing.faq.a3": "A IA Essencial usa DeepSeek V3.2 (excelente custo-benefício). A IA Premium usa Claude 3.5 Haiku da Anthropic, mais precisa em documentos jurídicos, médicos e técnicos complexos.",
"pricing.faq.a3": "A IA Essencial usa um modelo otimizado (excelente custo-benefício). A IA Premium usa Claude 3.5 Haiku da Anthropic, mais precisa em documentos jurídicos, médicos e técnicos complexos.",
"pricing.faq.q4": "Meus documentos são mantidos após a tradução?",
"pricing.faq.a4": "Os arquivos traduzidos ficam disponíveis de acordo com seu plano (30 dias Starter, 90 dias Pro, 1 ano Business). São criptografados em repouso e em trânsito.",
"pricing.faq.q5": "O que acontece se eu exceder minha cota mensal?",
@@ -3939,7 +4044,7 @@ const messages: Record<Locale, Record<string, string>> = {
"landing.pricing.pro.desc": "Per professionisti esigenti",
"landing.pricing.pro.f1": "200 documenti / mese",
"landing.pricing.pro.f2": "Fino a 200 pagine per documento",
"landing.pricing.pro.f3": "Traduzione IA (DeepSeek)",
"landing.pricing.pro.f3": "Traduzione con IA",
"landing.pricing.pro.f4": "Google + DeepL inclusi",
"landing.pricing.pro.f5": "Glossari e prompt personalizzati",
"landing.pricing.pro.f6": "Supporto prioritario",
@@ -4028,6 +4133,26 @@ const messages: Record<Locale, Record<string, string>> = {
"dashboard.translate.pipeline.rebuild": "Ricostruisci",
"dashboard.translate.pipeline.finalize": "Finalizza",
"dashboard.translate.progress.failedTitle": "Traduzione fallita",
"dashboard.translate.error.unexpected": "Si è verificato un errore imprevisto. Riprova.",
"dashboard.translate.error.noResult": "La traduzione non ha prodotto risultati. Verifica che il documento contenga testo, quindi riprova o scegli un altro motore.",
"dashboard.translate.error.apiKey": "Chiave API non valida o mancante. Contatta l'amministratore per configurare le chiavi.",
"dashboard.translate.error.quota": "Limite di utilizzo raggiunto. Riprova tra qualche minuto o scegli un altro motore.",
"dashboard.translate.error.timeout": "Connessione al servizio di traduzione scaduta. Controlla la rete e riprova.",
"dashboard.translate.error.sessionExpired": "Sessione scaduta. Clicca su Riprova per riavviare la traduzione.",
"dashboard.translate.error.empty": "Il documento sembra vuoto o non contiene testo traducibile (PDF scansionato?).",
"dashboard.translate.error.unsupported": "Formato file non supportato o file danneggiato.",
"dashboard.translate.error.connection": "Connessione persa. Controlla la rete e riprova.",
"dashboard.translate.error.generic": "Traduzione fallita: {detail}",
"dashboard.translate.error.title": "Traduzione fallita",
"dashboard.translate.retry": "Riprova traduzione",
"dashboard.translate.newFile": "Nuovo file",
"dashboard.translate.modeAI": "Modalità IA",
"dashboard.translate.modeClassic": "Modalità Classica",
"dashboard.translate.glossaryLLMHint": "Glossari disponibili in modalità IA",
"dashboard.translate.submitting": "Invio in corso...",
"dashboard.translate.submit": "Avvia traduzione",
"dashboard.translate.noFile": "Carica prima un file",
"dashboard.translate.noTargetLang": "Seleziona una lingua di destinazione",
"glossaries.yourGlossaries": "I tuoi glossari",
"glossaries.title": "Glossari e Contesto",
"glossaries.description": "Gestisci i tuoi glossari e le istruzioni di contesto per traduzioni più precise.",
@@ -4116,7 +4241,7 @@ const messages: Record<Locale, Record<string, string>> = {
"pricing.plans.starter.feat6": "Cronologia di 30 giorni",
"pricing.plans.pro.feat1": "200 documenti / mese",
"pricing.plans.pro.feat2": "Fino a 200 pagine per documento",
"pricing.plans.pro.feat3": "Traduzione IA Essenziale (DeepSeek V3.2)",
"pricing.plans.pro.feat3": "Traduzione IA Essenziale",
"pricing.plans.pro.feat4": "Google Traduttore + DeepL",
"pricing.plans.pro.feat5": "File fino a 25 MB",
"pricing.plans.pro.feat6": "Glossari personalizzati",
@@ -4196,7 +4321,8 @@ const messages: Record<Locale, Record<string, string>> = {
"pricing.aiModels.essential.title": "Traduzione IA Essenziale",
"pricing.aiModels.essential.plan": "Piano Pro",
"pricing.aiModels.essential.descPrefix": "Basato su",
"pricing.aiModels.essential.descSuffix": "— il modello IA più conveniente del 2026. Qualità paragonabile ai modelli frontier a 1/50 del costo.",
"pricing.aiModels.essential.descSuffix": "— il modello IA più conveniente del 2026. Qualità paragonabile ai modelli frontier a una frazione del costo.",
"pricing.aiModels.essential.modelName": "il nostro modello IA Essenziale",
"pricing.aiModels.essential.context": "163K token di contesto",
"pricing.aiModels.essential.value": "Eccellente rapporto qualità-prezzo",
"pricing.aiModels.premium.title": "Traduzione IA Premium",
@@ -4209,9 +4335,9 @@ const messages: Record<Locale, Record<string, string>> = {
"pricing.faq.q1": "Posso cambiare piano in qualsiasi momento?",
"pricing.faq.a1": "Sì. Il passaggio a un piano superiore è immediato e proporzionale. Il downgrade sarà attivo alla fine del periodo corrente.",
"pricing.faq.q2": "Cos'è la «Traduzione IA Essenziale»?",
"pricing.faq.a2": "È il nostro motore IA basato su DeepSeek V3.2 tramite OpenRouter. Comprende il contesto dei tuoi documenti, preserva il layout e gestisce i termini tecnici molto meglio della traduzione classica.",
"pricing.faq.a2": "È il nostro motore IA. Comprende il contesto dei vostri documenti, preserva il layout e gestisce i termini tecnici molto meglio della traduzione classica.",
"pricing.faq.q3": "Qual è la differenza tra IA Essenziale e IA Premium?",
"pricing.faq.a3": "L'IA Essenziale utilizza DeepSeek V3.2 (eccellente rapporto qualità-prezzo). L'IA Premium utilizza Claude 3.5 Haiku di Anthropic, più preciso su documenti legali, medici e tecnici complessi.",
"pricing.faq.a3": "La IA Essenziale usa un modello ottimizzato (eccellente rapporto qualità/prezzo). La IA Premium usa Claude 3.5 Haiku di Anthropic, più precisa su documenti legali, medici e tecnici complessi.",
"pricing.faq.q4": "I miei documenti vengono conservati dopo la traduzione?",
"pricing.faq.a4": "I file tradotti sono disponibili secondo il tuo piano (30 giorni Starter, 90 giorni Pro, 1 anno Business). Sono crittografati a riposo e in transito.",
"pricing.faq.q5": "Cosa succede se supero la quota mensile?",
@@ -4676,7 +4802,7 @@ const messages: Record<Locale, Record<string, string>> = {
"landing.pricing.pro.desc": "Voor veeleisende professionals",
"landing.pricing.pro.f1": "200 documenten / maand",
"landing.pricing.pro.f2": "Tot 200 pagina's per doc",
"landing.pricing.pro.f3": "AI-aangedreven vertaling (DeepSeek)",
"landing.pricing.pro.f3": "IA-aangedreven vertaling",
"landing.pricing.pro.f4": "Google + DeepL inbegrepen",
"landing.pricing.pro.f5": "Eigen glossaria en prompts",
"landing.pricing.pro.f6": "Prioriteitsondersteuning",
@@ -4765,6 +4891,26 @@ const messages: Record<Locale, Record<string, string>> = {
"dashboard.translate.pipeline.rebuild": "Reconstrueren",
"dashboard.translate.pipeline.finalize": "Afronden",
"dashboard.translate.progress.failedTitle": "Vertaling mislukt",
"dashboard.translate.error.unexpected": "Er is een onverwachte fout opgetreden. Probeer het opnieuw.",
"dashboard.translate.error.noResult": "De vertaling leverde geen resultaten op. Controleer of het document tekst bevat en probeer het opnieuw of kies een andere engine.",
"dashboard.translate.error.apiKey": "Ongeldige of ontbrekende API-sleutel. Neem contact op met de beheerder om API-sleutels te configureren.",
"dashboard.translate.error.quota": "Gebruikslimiet bereikt. Probeer het over een paar minuten opnieuw of kies een andere engine.",
"dashboard.translate.error.timeout": "Verbinding met de vertaalservice is verlopen. Controleer uw netwerk en probeer het opnieuw.",
"dashboard.translate.error.sessionExpired": "Sessie verlopen. Klik op Opnieuw proberen om de vertaling te herstarten.",
"dashboard.translate.error.empty": "Het document lijkt leeg of bevat geen vertaalbare tekst (gescande PDF?).",
"dashboard.translate.error.unsupported": "Niet-ondersteund bestandsformaat of beschadigd bestand.",
"dashboard.translate.error.connection": "Verbinding verbroken. Controleer uw netwerk en probeer het opnieuw.",
"dashboard.translate.error.generic": "Vertaling mislukt: {detail}",
"dashboard.translate.error.title": "Vertaling mislukt",
"dashboard.translate.retry": "Probeer opnieuw",
"dashboard.translate.newFile": "Nieuw bestand",
"dashboard.translate.modeAI": "AI-modus",
"dashboard.translate.modeClassic": "Klassieke modus",
"dashboard.translate.glossaryLLMHint": "Woordenlijsten beschikbaar in AI-modus",
"dashboard.translate.submitting": "Bezig met verzenden...",
"dashboard.translate.submit": "Vertaling starten",
"dashboard.translate.noFile": "Upload eerst een bestand",
"dashboard.translate.noTargetLang": "Selecteer een doeltaal",
"glossaries.yourGlossaries": "Uw woordenlijsten",
"glossaries.title": "Woordenlijsten & Context",
"glossaries.description": "Beheer uw woordenlijsten en contextinstructies voor nauwkeurigere vertalingen.",
@@ -4853,7 +4999,7 @@ const messages: Record<Locale, Record<string, string>> = {
"pricing.plans.starter.feat6": "30 dagen geschiedenis",
"pricing.plans.pro.feat1": "200 documenten / maand",
"pricing.plans.pro.feat2": "Tot 200 pagina's per document",
"pricing.plans.pro.feat3": "AI-basisvertaling (DeepSeek V3.2)",
"pricing.plans.pro.feat3": "Essentiële IA-vertaling",
"pricing.plans.pro.feat4": "Google Vertalen + DeepL",
"pricing.plans.pro.feat5": "Bestanden tot 25 MB",
"pricing.plans.pro.feat6": "Aangepaste woordenlijsten",
@@ -4933,7 +5079,8 @@ const messages: Record<Locale, Record<string, string>> = {
"pricing.aiModels.essential.title": "AI-basisvertaling",
"pricing.aiModels.essential.plan": "Pro-abonnement",
"pricing.aiModels.essential.descPrefix": "Gebaseerd op",
"pricing.aiModels.essential.descSuffix": "— het meest kostenefficiënte AI-model van 2026. Kwaliteit vergelijkbaar met frontier-modellen voor 1/50 van de kosten.",
"pricing.aiModels.essential.descSuffix": "— het meest kostenefficiënte AI-model van 2026. Kwaliteit vergelijkbaar met frontier-modellen voor een fractie van de kosten.",
"pricing.aiModels.essential.modelName": "ons Essential AI-model",
"pricing.aiModels.essential.context": "163K tokens context",
"pricing.aiModels.essential.value": "Uitstekende prijs-kwaliteitverhouding",
"pricing.aiModels.premium.title": "AI-premiumvertaling",
@@ -4946,9 +5093,9 @@ const messages: Record<Locale, Record<string, string>> = {
"pricing.faq.q1": "Kan ik op elk moment van abonnement wisselen?",
"pricing.faq.a1": "Ja. Upgraden gebeurt direct en naar rato. Downgraden gaat in aan het einde van de lopende periode.",
"pricing.faq.q2": "Wat is «AI-basisvertaling»?",
"pricing.faq.a2": "Het is onze AI-engine op basis van DeepSeek V3.2 via OpenRouter. Hij begrijpt de context van uw documenten, behoudt de opmaak en verwerkt vaktermen veel beter dan klassieke vertaling.",
"pricing.faq.a2": "Het is onze IA-engine. Hij begrijpt de context van uw documenten, behoudt de lay-out en behandelt technische termen veel beter dan klassieke vertaling.",
"pricing.faq.q3": "Wat is het verschil tussen Basis- en Premium-AI?",
"pricing.faq.a3": "Basis-AI gebruikt DeepSeek V3.2 (uitstekende prijs-kwaliteit). Premium-AI gebruikt Claude 3.5 Haiku van Anthropic, nauwkeuriger bij juridische, medische en complexe technische documenten.",
"pricing.faq.a3": "Essential IA gebruikt een geoptimaliseerd model (uitstekende prijs-kwaliteitverhouding). Premium IA gebruikt Claude 3.5 Haiku van Anthropic, nauwkeuriger bij juridische, medische en complexe technische documenten.",
"pricing.faq.q4": "Worden mijn documenten bewaard na vertaling?",
"pricing.faq.a4": "Vertaalde bestanden zijn beschikbaar volgens uw abonnement (30 dagen Starter, 90 dagen Pro, 1 jaar Business). Ze zijn versleuteld at-rest en in transit.",
"pricing.faq.q5": "Wat gebeurt er als ik mijn maandelijkse quotum overschrijd?",
@@ -5413,7 +5560,7 @@ const messages: Record<Locale, Record<string, string>> = {
"landing.pricing.pro.desc": "Для требовательных профессионалов",
"landing.pricing.pro.f1": "200 документов / мес.",
"landing.pricing.pro.f2": "До 200 страниц на документ",
"landing.pricing.pro.f3": "ИИ-перевод (DeepSeek)",
"landing.pricing.pro.f3": "Перевод на базе ИИ",
"landing.pricing.pro.f4": "Google + DeepL включены",
"landing.pricing.pro.f5": "Пользовательские глоссарии и промпты",
"landing.pricing.pro.f6": "Приоритетная поддержка",
@@ -5502,6 +5649,26 @@ const messages: Record<Locale, Record<string, string>> = {
"dashboard.translate.pipeline.rebuild": "Восстановление",
"dashboard.translate.pipeline.finalize": "Завершение",
"dashboard.translate.progress.failedTitle": "Перевод не удался",
"dashboard.translate.error.unexpected": "Произошла непредвиденная ошибка. Попробуйте снова.",
"dashboard.translate.error.noResult": "Перевод не дал результатов. Убедитесь, что документ содержит текст, и попробуйте снова или выберите другой движок.",
"dashboard.translate.error.apiKey": "Недействительный или отсутствующий API-ключ. Обратитесь к администратору для настройки ключей.",
"dashboard.translate.error.quota": "Лимит использования достигнут. Попробуйте снова через несколько минут или выберите другой движок.",
"dashboard.translate.error.timeout": "Время соединения с сервисом перевода истекло. Проверьте сеть и попробуйте снова.",
"dashboard.translate.error.sessionExpired": "Сессия истекла. Нажмите «Повторить» для перезапуска перевода.",
"dashboard.translate.error.empty": "Документ пуст или не содержит переводимого текста (сканированный PDF?).",
"dashboard.translate.error.unsupported": "Неподдерживаемый формат файла или повреждённый файл.",
"dashboard.translate.error.connection": "Соединение потеряно. Проверьте сеть и попробуйте снова.",
"dashboard.translate.error.generic": "Перевод не удался: {detail}",
"dashboard.translate.error.title": "Перевод не удался",
"dashboard.translate.retry": "Повторить перевод",
"dashboard.translate.newFile": "Новый файл",
"dashboard.translate.modeAI": "Режим ИИ",
"dashboard.translate.modeClassic": "Классический режим",
"dashboard.translate.glossaryLLMHint": "Глоссарии доступны в режиме ИИ",
"dashboard.translate.submitting": "Отправка...",
"dashboard.translate.submit": "Начать перевод",
"dashboard.translate.noFile": "Сначала загрузите файл",
"dashboard.translate.noTargetLang": "Выберите целевой язык",
"glossaries.yourGlossaries": "Ваши глоссарии",
"glossaries.title": "Глоссарии и контекст",
"glossaries.description": "Управляйте глоссариями и контекстными инструкциями для более точных переводов.",
@@ -5590,7 +5757,7 @@ const messages: Record<Locale, Record<string, string>> = {
"pricing.plans.starter.feat6": "История за 30 дней",
"pricing.plans.pro.feat1": "200 документов / месяц",
"pricing.plans.pro.feat2": "До 200 страниц на документ",
"pricing.plans.pro.feat3": "Базовый ИИ-перевод (DeepSeek V3.2)",
"pricing.plans.pro.feat3": "Базовый ИИ-перевод",
"pricing.plans.pro.feat4": "Google Переводчик + DeepL",
"pricing.plans.pro.feat5": "Файлы до 25 МБ",
"pricing.plans.pro.feat6": "Пользовательские глоссарии",
@@ -5670,7 +5837,8 @@ const messages: Record<Locale, Record<string, string>> = {
"pricing.aiModels.essential.title": "Базовый ИИ-перевод",
"pricing.aiModels.essential.plan": "Тариф Pro",
"pricing.aiModels.essential.descPrefix": "На базе",
"pricing.aiModels.essential.descSuffix": "— самой экономичной ИИ-модели 2026 года. Качество на уровне frontier-моделей при стоимости в 50 раз ниже.",
"pricing.aiModels.essential.descSuffix": "— самой экономичной ИИ-модели 2026 года. Качество на уровне frontier-моделей при стоимости в десятки раз ниже.",
"pricing.aiModels.essential.modelName": "наша базовая ИИ-модель",
"pricing.aiModels.essential.context": "163K токенов контекста",
"pricing.aiModels.essential.value": "Отличное соотношение цены и качества",
"pricing.aiModels.premium.title": "Премиум ИИ-перевод",
@@ -5683,9 +5851,9 @@ const messages: Record<Locale, Record<string, string>> = {
"pricing.faq.q1": "Могу ли я сменить тариф в любое время?",
"pricing.faq.a1": "Да. Повышение тарифа применяется сразу с пересчётом. Понижение вступает в силу в конце текущего периода.",
"pricing.faq.q2": "Что такое «Базовый ИИ-перевод»?",
"pricing.faq.a2": "Это наш ИИ-движок на базе DeepSeek V3.2 через OpenRouter. Он понимает контекст документов, сохраняет форматирование и значительно лучше обрабатывает технические термины по сравнению с классическим переводом.",
"pricing.faq.a2": "Это наш ИИ-движок. Он понимает контекст ваших документов, сохраняет вёрстку и обрабатывает технические термины намного лучше классического перевода.",
"pricing.faq.q3": "В чём разница между базовым и премиум ИИ-переводом?",
"pricing.faq.a3": "Базовый ИИ использует DeepSeek V3.2 (отличное соотношение цены и качества). Премиум ИИ использует Claude 3.5 Haiku от Anthropic более точный на юридических, медицинских и сложных технических документах.",
"pricing.faq.a3": "Базовый ИИ использует оптимизированную модель (отличное соотношение цены и качества). Премиум ИИ использует Claude 3.5 Haiku от Anthropic, более точный на юридических, медицинских и сложных технических документах.",
"pricing.faq.q4": "Сохраняются ли мои документы после перевода?",
"pricing.faq.a4": "Переведённые файлы доступны в зависимости от тарифа (30 дней Starter, 90 дней Pro, 1 год Business). Они зашифрованы при хранении и передаче.",
"pricing.faq.q5": "Что произойдёт при превышении месячной квоты?",
@@ -6152,7 +6320,7 @@ const messages: Record<Locale, Record<string, string>> = {
"landing.pricing.pro.desc": "プロフェッショナル向け",
"landing.pricing.pro.f1": "月200ドキュメント",
"landing.pricing.pro.f2": "1ドキュメント最大200ページ",
"landing.pricing.pro.f3": "AI翻訳DeepSeek",
"landing.pricing.pro.f3": "AI翻訳",
"landing.pricing.pro.f4": "Google + DeepL込み",
"landing.pricing.pro.f5": "カスタム用語集とプロンプト",
"landing.pricing.pro.f6": "優先サポート",
@@ -6241,6 +6409,26 @@ const messages: Record<Locale, Record<string, string>> = {
"dashboard.translate.pipeline.rebuild": "再構築",
"dashboard.translate.pipeline.finalize": "完了",
"dashboard.translate.progress.failedTitle": "翻訳失敗",
"dashboard.translate.error.unexpected": "予期しないエラーが発生しました。もう一度お試しください。",
"dashboard.translate.error.noResult": "翻訳結果が得られませんでした。ドキュメントにテキストが含まれていることを確認し、再試行するか別のエンジンを選択してください。",
"dashboard.translate.error.apiKey": "APIキーが無効または不足しています。管理者に連絡してAPIキーを設定してください。",
"dashboard.translate.error.quota": "利用制限に達しました。数分後にもう一度お試しいただくか、別のエンジンを選択してください。",
"dashboard.translate.error.timeout": "翻訳サービスへの接続がタイムアウトしました。ネットワークを確認して再試行してください。",
"dashboard.translate.error.sessionExpired": "セッションが期限切れです。再試行をクリックして翻訳を再開してください。",
"dashboard.translate.error.empty": "ドキュメントが空か、翻訳可能なテキストが含まれていませんスキャンPDF画像。",
"dashboard.translate.error.unsupported": "サポートされていないファイル形式または破損したファイルです。",
"dashboard.translate.error.connection": "接続が切断されました。ネットワークを確認して再試行してください。",
"dashboard.translate.error.generic": "翻訳失敗:{detail}",
"dashboard.translate.error.title": "翻訳失敗",
"dashboard.translate.retry": "再翻訳",
"dashboard.translate.newFile": "新しいファイル",
"dashboard.translate.modeAI": "AIモード",
"dashboard.translate.modeClassic": "クラシックモード",
"dashboard.translate.glossaryLLMHint": "AIモードで用語集が利用可能",
"dashboard.translate.submitting": "送信中...",
"dashboard.translate.submit": "翻訳を開始",
"dashboard.translate.noFile": "先にファイルをアップロードしてください",
"dashboard.translate.noTargetLang": "翻訳先の言語を選択してください",
"glossaries.yourGlossaries": "あなたの用語集",
"glossaries.title": "用語集とコンテキスト",
"glossaries.description": "用語集とコンテキスト指示を管理して、より正確な翻訳を実現します。",
@@ -6329,7 +6517,7 @@ const messages: Record<Locale, Record<string, string>> = {
"pricing.plans.starter.feat6": "30日間の履歴",
"pricing.plans.pro.feat1": "月200ドキュメント",
"pricing.plans.pro.feat2": "1ドキュメントあたり最大200ページ",
"pricing.plans.pro.feat3": "エッセンシャルAI翻訳DeepSeek V3.2",
"pricing.plans.pro.feat3": "Essential AI翻訳",
"pricing.plans.pro.feat4": "Google翻訳 + DeepL",
"pricing.plans.pro.feat5": "最大25 MBのファイル",
"pricing.plans.pro.feat6": "カスタム用語集",
@@ -6409,7 +6597,8 @@ const messages: Record<Locale, Record<string, string>> = {
"pricing.aiModels.essential.title": "エッセンシャルAI翻訳",
"pricing.aiModels.essential.plan": "Proプラン",
"pricing.aiModels.essential.descPrefix": "ベース:",
"pricing.aiModels.essential.descSuffix": "— 2026年で最も費用対効果の高いAIモデル。フロンティアモデルと同等の品質を50分の1のコストで実現。",
"pricing.aiModels.essential.descSuffix": "— 2026年で最も費用対効果の高いAIモデル。フロンティアモデルと同等の品質をわずかなコストで実現。",
"pricing.aiModels.essential.modelName": "Essential AIモデル",
"pricing.aiModels.essential.context": "163Kトークンのコンテキスト",
"pricing.aiModels.essential.value": "優れた費用対効果",
"pricing.aiModels.premium.title": "プレミアムAI翻訳",
@@ -6422,9 +6611,9 @@ const messages: Record<Locale, Record<string, string>> = {
"pricing.faq.q1": "いつでもプランを変更できますか?",
"pricing.faq.a1": "はい。アップグレードは即時反映され、日割り計算されます。ダウングレードは現在の期間の終了時に適用されます。",
"pricing.faq.q2": "「エッセンシャルAI翻訳」とは何ですか",
"pricing.faq.a2": "OpenRouter経由のDeepSeek V3.2をベースにしたAIエンジンです。ドキュメントのコンテキストを理解し、レイアウトを保持し、専門用語を従来の翻訳よりはるかに正確に処理します。",
"pricing.faq.a2": "独自のAIエンジンです。ドキュメントの文脈を理解し、レイアウトを保持し、技術用語を従来の翻訳よりはるかに適切に処理します。",
"pricing.faq.q3": "エッセンシャルAIとプレミアムAIの違いは何ですか",
"pricing.faq.a3": "エッセンシャルAIはDeepSeek V3.2を使用(優れた費用対効果)。プレミアムAIはAnthropicのClaude 3.5 Haikuを使用し、法、医、複雑な技術文書より高精度です。",
"pricing.faq.a3": "Essential AIは最適化されたモデルを使用していますコストパフォーマンスに優れています。Premium AIはAnthropicのClaude 3.5 Haikuを使用し、法、医学的、複雑な技術文書より正確です。",
"pricing.faq.q4": "翻訳後もドキュメントは保存されますか?",
"pricing.faq.a4": "翻訳済みファイルはプランに応じて利用可能ですStarter 30日、Pro 90日、Business 1年。保存時および通信時は暗号化されています。",
"pricing.faq.q5": "月間枠を超えた場合はどうなりますか?",
@@ -6888,7 +7077,7 @@ const messages: Record<Locale, Record<string, string>> = {
"landing.pricing.pro.desc": "전문가용",
"landing.pricing.pro.f1": "월 200개 문서",
"landing.pricing.pro.f2": "문서당 최대 200페이지",
"landing.pricing.pro.f3": "AI 번역 (DeepSeek)",
"landing.pricing.pro.f3": "AI 번역",
"landing.pricing.pro.f4": "Google + DeepL 포함",
"landing.pricing.pro.f5": "맞춤 용어집 및 프롬프트",
"landing.pricing.pro.f6": "우선 지원",
@@ -6977,6 +7166,26 @@ const messages: Record<Locale, Record<string, string>> = {
"dashboard.translate.pipeline.rebuild": "재구성",
"dashboard.translate.pipeline.finalize": "완료",
"dashboard.translate.progress.failedTitle": "번역 실패",
"dashboard.translate.error.unexpected": "예기치 않은 오류가 발생했습니다. 다시 시도해 주세요.",
"dashboard.translate.error.noResult": "번역 결과가 없습니다. 문서에 텍스트가 포함되어 있는지 확인하고 다시 시도하거나 다른 엔진을 선택하세요.",
"dashboard.translate.error.apiKey": "API 키가 유효하지 않거나 없습니다. 관리자에게 문의하여 API 키를 설정하세요.",
"dashboard.translate.error.quota": "사용 한도에 도달했습니다. 몇 분 후에 다시 시도하거나 다른 엔진을 선택하세요.",
"dashboard.translate.error.timeout": "번역 서비스 연결 시간이 초과되었습니다. 네트워크를 확인하고 다시 시도하세요.",
"dashboard.translate.error.sessionExpired": "세션이 만료되었습니다. 다시 시도를 클릭하여 번역을 재시작하세요.",
"dashboard.translate.error.empty": "문서가 비어 있거나 번역 가능한 텍스트가 없습니다 (스캔 PDF 이미지?).",
"dashboard.translate.error.unsupported": "지원되지 않는 파일 형식이거나 손상된 파일입니다.",
"dashboard.translate.error.connection": "연결이 끊겼습니다. 네트워크를 확인하고 다시 시도하세요.",
"dashboard.translate.error.generic": "번역 실패: {detail}",
"dashboard.translate.error.title": "번역 실패",
"dashboard.translate.retry": "번역 다시 시도",
"dashboard.translate.newFile": "새 파일",
"dashboard.translate.modeAI": "AI 모드",
"dashboard.translate.modeClassic": "클래식 모드",
"dashboard.translate.glossaryLLMHint": "AI 모드에서 용어집 사용 가능",
"dashboard.translate.submitting": "제출 중...",
"dashboard.translate.submit": "번역 시작",
"dashboard.translate.noFile": "먼저 파일을 업로드하세요",
"dashboard.translate.noTargetLang": "대상 언어를 선택하세요",
"glossaries.yourGlossaries": "내 용어집",
"glossaries.title": "용어집 및 컨텍스트",
"glossaries.description": "용어집과 컨텍스트 지침을 관리하여 더 정확한 번역을하세요.",
@@ -7065,7 +7274,7 @@ const messages: Record<Locale, Record<string, string>> = {
"pricing.plans.starter.feat6": "30일 기록",
"pricing.plans.pro.feat1": "월 200개 문서",
"pricing.plans.pro.feat2": "문서당 최대 200페이지",
"pricing.plans.pro.feat3": "에센셜 AI 번역 (DeepSeek V3.2)",
"pricing.plans.pro.feat3": "Essential AI 번역",
"pricing.plans.pro.feat4": "Google 번역 + DeepL",
"pricing.plans.pro.feat5": "최대 25 MB 파일",
"pricing.plans.pro.feat6": "사용자 지정 용어집",
@@ -7145,7 +7354,8 @@ const messages: Record<Locale, Record<string, string>> = {
"pricing.aiModels.essential.title": "에센셜 AI 번역",
"pricing.aiModels.essential.plan": "Pro 플랜",
"pricing.aiModels.essential.descPrefix": "기반:",
"pricing.aiModels.essential.descSuffix": "— 2026년 가장 비용 효율적인 AI 모델. 프론티어 모델과 동등한 품질을 50분의 1 비용으로 제공.",
"pricing.aiModels.essential.descSuffix": "— 2026년 가장 비용 효율적인 AI 모델. 프론티어 모델과 동등한 품질을 극소수의 비용으로 제공.",
"pricing.aiModels.essential.modelName": "Essential AI 모델",
"pricing.aiModels.essential.context": "163K 토큰 컨텍스트",
"pricing.aiModels.essential.value": "우수한 가성비",
"pricing.aiModels.premium.title": "프리미엄 AI 번역",
@@ -7158,9 +7368,9 @@ const messages: Record<Locale, Record<string, string>> = {
"pricing.faq.q1": "언제든지 플랜을 변경할 수 있나요?",
"pricing.faq.a1": "네. 업그레이드는 즉시 적용되며 일할 계산됩니다. 다운그레이드는 현재 기간 종료 시 적용됩니다.",
"pricing.faq.q2": "「에센셜 AI 번역」이란 무엇인가요?",
"pricing.faq.a2": "OpenRouter를 통한 DeepSeek V3.2 기반 AI 엔진입니다. 문서의 컨텍스트를 이해하고, 레이아웃을 유지하며, 전문 용어를 기존 번역보다 훨씬 정확하게 처리합니다.",
"pricing.faq.a2": "당사의 AI 엔진입니다. 문서의 맥락을 이해하고, 레이아웃을 보존하며, 기술 용어를 기존 번역보다 훨씬 처리합니다.",
"pricing.faq.q3": "에센셜 AI와 프리미엄 AI의 차이점은 무엇인가요?",
"pricing.faq.a3": "에센셜 AI는 DeepSeek V3.2를 사용 (우수한 가성비). 프리미엄 AI는 Anthropic의 Claude 3.5 Haiku를 사용하여 법률, 의료 및 복잡한 기술 문서에서 더 높은 정확도를 제공합니다.",
"pricing.faq.a3": "Essential AI는 최적화된 모델을 사용합니다 (우수한 가성비). Premium AI는 Anthropic의 Claude 3.5 Haiku를 사용하여 법률, 의료 및 복잡한 기술 문서에서 더 정확합니다.",
"pricing.faq.q4": "번역 후 문서가 보관되나요?",
"pricing.faq.a4": "번역된 파일은 플랜에 따라 이용 가능합니다 (Starter 30일, Pro 90일, Business 1년). 저장 시 및 전송 중 암호화됩니다.",
"pricing.faq.q5": "월간 할당량을 초과하면 어떻게 되나요?",
@@ -7624,7 +7834,7 @@ const messages: Record<Locale, Record<string, string>> = {
"landing.pricing.pro.desc": "适合专业用户",
"landing.pricing.pro.f1": "每月200份文档",
"landing.pricing.pro.f2": "每份文档最多200页",
"landing.pricing.pro.f3": "AI驱动翻译DeepSeek",
"landing.pricing.pro.f3": "AI翻译",
"landing.pricing.pro.f4": "包含Google + DeepL",
"landing.pricing.pro.f5": "自定义术语表和提示词",
"landing.pricing.pro.f6": "优先支持",
@@ -7713,6 +7923,26 @@ const messages: Record<Locale, Record<string, string>> = {
"dashboard.translate.pipeline.rebuild": "重建",
"dashboard.translate.pipeline.finalize": "完成",
"dashboard.translate.progress.failedTitle": "翻译失败",
"dashboard.translate.error.unexpected": "发生了意外错误,请重试。",
"dashboard.translate.error.noResult": "翻译未产生结果。请确认文档包含文本,然后重试或选择其他引擎。",
"dashboard.translate.error.apiKey": "API 密钥无效或缺失。请联系管理员配置 API 密钥。",
"dashboard.translate.error.quota": "已达到使用限制。请稍后重试或选择其他引擎。",
"dashboard.translate.error.timeout": "翻译服务连接超时。请检查网络后重试。",
"dashboard.translate.error.sessionExpired": "会话已过期。点击重试以重新开始翻译。",
"dashboard.translate.error.empty": "文档似乎为空或不包含可翻译文本(扫描 PDF 图像?)。",
"dashboard.translate.error.unsupported": "不支持的文件格式或文件已损坏。",
"dashboard.translate.error.connection": "连接丢失。请检查网络后重试。",
"dashboard.translate.error.generic": "翻译失败:{detail}",
"dashboard.translate.error.title": "翻译失败",
"dashboard.translate.retry": "重试翻译",
"dashboard.translate.newFile": "新文件",
"dashboard.translate.modeAI": "AI 模式",
"dashboard.translate.modeClassic": "经典模式",
"dashboard.translate.glossaryLLMHint": "AI 模式下可使用术语表",
"dashboard.translate.submitting": "提交中...",
"dashboard.translate.submit": "开始翻译",
"dashboard.translate.noFile": "请先上传文件",
"dashboard.translate.noTargetLang": "请选择目标语言",
"glossaries.yourGlossaries": "您的术语表",
"glossaries.title": "术语表与上下文",
"glossaries.description": "管理您的术语表和上下文指令,以获得更准确的翻译。",
@@ -7801,7 +8031,7 @@ const messages: Record<Locale, Record<string, string>> = {
"pricing.plans.starter.feat6": "30 天历史记录",
"pricing.plans.pro.feat1": "每月 200 份文档",
"pricing.plans.pro.feat2": "每份文档最多 200 页",
"pricing.plans.pro.feat3": "基础 AI 翻译DeepSeek V3.2",
"pricing.plans.pro.feat3": "Essential AI翻译",
"pricing.plans.pro.feat4": "Google 翻译 + DeepL",
"pricing.plans.pro.feat5": "文件最大 25 MB",
"pricing.plans.pro.feat6": "自定义术语表",
@@ -7881,7 +8111,8 @@ const messages: Record<Locale, Record<string, string>> = {
"pricing.aiModels.essential.title": "基础 AI 翻译",
"pricing.aiModels.essential.plan": "专业版方案",
"pricing.aiModels.essential.descPrefix": "基于",
"pricing.aiModels.essential.descSuffix": "— 2026 年最具性价比的 AI 模型。质量媲美前沿模型,成本仅为其 1/50。",
"pricing.aiModels.essential.descSuffix": "— 2026 年最具性价比的 AI 模型。质量媲美前沿模型,成本仅为其一小部分。",
"pricing.aiModels.essential.modelName": "Essential AI模型",
"pricing.aiModels.essential.context": "163K 上下文 Token",
"pricing.aiModels.essential.value": "性价比极佳",
"pricing.aiModels.premium.title": "高级 AI 翻译",
@@ -7894,9 +8125,9 @@ const messages: Record<Locale, Record<string, string>> = {
"pricing.faq.q1": "我可以随时更换方案吗?",
"pricing.faq.a1": "可以。升级立即生效并按比例计费。降级在当前周期结束时生效。",
"pricing.faq.q2": "什么是「基础 AI 翻译」?",
"pricing.faq.a2": "这是我们基于 DeepSeek V3.2 通过 OpenRouter 提供的 AI 引擎。它能理解文档上下文保留排版,在处理专业术语方面远优于传统翻译。",
"pricing.faq.a2": "这是我们AI引擎。它能理解文档上下文保留排版,并比传统翻译更好地处理技术术语。",
"pricing.faq.q3": "基础 AI 和高级 AI 有什么区别?",
"pricing.faq.a3": "基础 AI 使用 DeepSeek V3.2(性价比极佳)。高级 AI 使用 AnthropicClaude 3.5 Haiku在法律、医和复杂技术文档方面精度更高。",
"pricing.faq.a3": "Essential AI使用优化模型(性价比极佳)。Premium AI使用AnthropicClaude 3.5 Haiku在法律、医和复杂技术文档上更准确。",
"pricing.faq.q4": "翻译后文档会被保留吗?",
"pricing.faq.a4": "翻译文件根据您的方案保留(入门版 30 天、专业版 90 天、企业版 1 年)。存储和传输过程中均已加密。",
"pricing.faq.q5": "超过月度配额会怎样?",
@@ -8318,7 +8549,7 @@ const messages: Record<Locale, Record<string, string>> = {
"landing.pricing.pro.desc": "للمحترفين المتميزين",
"landing.pricing.pro.f1": "200 مستند / شهر",
"landing.pricing.pro.f2": "حتى 200 صفحة لكل مستند",
"landing.pricing.pro.f3": "ترجمة بالذكاء الاصطناعي (DeepSeek)",
"landing.pricing.pro.f3": "ترجمة بالذكاء الاصطناعي",
"landing.pricing.pro.f4": "Google + DeepL مشمولان",
"landing.pricing.pro.f5": "قواميس وأوامر مخصصة",
"landing.pricing.pro.f6": "دعم ذو أولوية",
@@ -8407,6 +8638,26 @@ const messages: Record<Locale, Record<string, string>> = {
"dashboard.translate.pipeline.rebuild": "إعادة بناء",
"dashboard.translate.pipeline.finalize": "إنهاء",
"dashboard.translate.progress.failedTitle": "فشلت الترجمة",
"dashboard.translate.error.unexpected": "حدث خطأ غير متوقع. يرجى المحاولة مرة أخرى.",
"dashboard.translate.error.noResult": "لم تُنتج الترجمة أي نتائج. تحقق من أن المستند يحتوي على نص، ثم أعد المحاولة أو اختر محركًا آخر.",
"dashboard.translate.error.apiKey": "مفتاح API غير صالح أو مفقود. اتصل بالمسؤول لإعداد مفاتيح API.",
"dashboard.translate.error.quota": "تم بلوغ حد الاستخدام. أعد المحاولة بعد بضع دقائق أو اختر محركًا آخر.",
"dashboard.translate.error.timeout": "انتهت مهلة الاتصال بخدمة الترجمة. تحقق من شبكتك وأعد المحاولة.",
"dashboard.translate.error.sessionExpired": "انتهت الجلسة. انقر على إعادة المحاولة لإعادة تشغيل الترجمة.",
"dashboard.translate.error.empty": "يبدو أن المستند فارغ أو لا يحتوي على نص قابل للترجمة (صورة PDF ممسوحة ضوئيًا؟).",
"dashboard.translate.error.unsupported": "تنسيق ملف غير مدعوم أو ملف تالف.",
"dashboard.translate.error.connection": "فُقد الاتصال. تحقق من شبكتك وأعد المحاولة.",
"dashboard.translate.error.generic": "فشلت الترجمة: {detail}",
"dashboard.translate.error.title": "فشلت الترجمة",
"dashboard.translate.retry": "إعادة محاولة الترجمة",
"dashboard.translate.newFile": "ملف جديد",
"dashboard.translate.modeAI": "وضع الذكاء الاصطناعي",
"dashboard.translate.modeClassic": "الوضع الكلاسيكي",
"dashboard.translate.glossaryLLMHint": "القواميس المصطلحية متاحة في وضع الذكاء الاصطناعي",
"dashboard.translate.submitting": "جارٍ الإرسال...",
"dashboard.translate.submit": "بدء الترجمة",
"dashboard.translate.noFile": "قم بتحميل ملف أولاً",
"dashboard.translate.noTargetLang": "اختر لغة الهدف",
"glossaries.yourGlossaries": "معاجمك",
"glossaries.title": "المعاجم والسياق",
"glossaries.description": "قم بإدارة معاجمك وتعليمات السياق للحصول على ترجمات أكثر دقة.",
@@ -8495,7 +8746,7 @@ const messages: Record<Locale, Record<string, string>> = {
"pricing.plans.starter.feat6": "سجل 30 يومًا",
"pricing.plans.pro.feat1": "200 مستند / شهر",
"pricing.plans.pro.feat2": "حتى 200 صفحة لكل مستند",
"pricing.plans.pro.feat3": "ترجمة AI أساسية (DeepSeek V3.2)",
"pricing.plans.pro.feat3": "ترجمة الذكاء الاصطناعي الأساسية",
"pricing.plans.pro.feat4": "ترجمة Google + DeepL",
"pricing.plans.pro.feat5": "ملفات حتى 25 ميغابايت",
"pricing.plans.pro.feat6": "معاجم مخصصة",
@@ -8575,7 +8826,8 @@ const messages: Record<Locale, Record<string, string>> = {
"pricing.aiModels.essential.title": "ترجمة AI أساسية",
"pricing.aiModels.essential.plan": "خطة Pro",
"pricing.aiModels.essential.descPrefix": "مبني على",
"pricing.aiModels.essential.descSuffix": "— أنموذج الذكاء الاصطناعي الأكثر فعالية من حيث التكلفة لعام 2026. جودة مماثلة للنماذج المتقدمة بتكلفة 1/50.",
"pricing.aiModels.essential.descSuffix": "— أنموذج الذكاء الاصطناعي الأكثر فعالية من حيث التكلفة لعام 2026. جودة مماثلة للنماذج المتقدمة بتكلفة أقل بكثير.",
"pricing.aiModels.essential.modelName": "نموذج الذكاء الاصطناعي الأساسي",
"pricing.aiModels.essential.context": "163K رمز سياقي",
"pricing.aiModels.essential.value": "قيمة ممتازة مقابل المال",
"pricing.aiModels.premium.title": "ترجمة AI متميزة",
@@ -8588,9 +8840,9 @@ const messages: Record<Locale, Record<string, string>> = {
"pricing.faq.q1": "هل يمكنني تغيير خطتي في أي وقت؟",
"pricing.faq.a1": "نعم. الترقية فورية ومُحاسب عليها بالتناسب. التخفيض يسري في نهاية الفترة الحالية.",
"pricing.faq.q2": "ما هي «ترجمة AI الأساسية»؟",
"pricing.faq.a2": "إنها محرك الذكاء الاصطناعي لدينا المبني على DeepSeek V3.2 عبر OpenRouter. يفهم سياق مستنداتك ويحافظ على التنسيق ويتعامل مع المصطلحات التقنية بشكل أفضل بكثير من الترجمة التقليدية.",
"pricing.faq.a2": "إنه محرك الذكاء الاصطناعي الخاص بنا. يفهم سياق مستنداتك، ويحافظ على التخطيط، ويتعامل مع المصطلحات التقنية بشكل أفضل بكثير من الترجمة الكلاسيكية.",
"pricing.faq.q3": "ما الفرق بين AI الأساسية وAI المتميزة؟",
"pricing.faq.a3": "AI الأساسية تستخدم DeepSeek V3.2 (قيمة ممتازة مقابل المال). AI المتميزة تستخدم Claude 3.5 Haiku من Anthropic وهي أكثر دقة في المستندات القانونية والطبية والتقنية المعقدة.",
"pricing.faq.a3": "الذكاء الاصطناعي الأساسي يستخدم نموذجًا محسّنًا (قيمة ممتازة مقابل المال). الذكاء الاصطناعي المتميز يستخدم Claude 3.5 Haiku من Anthropic، أكثر دقة في المستندات القانونية والطبية والتقنية المعقدة.",
"pricing.faq.q4": "هل تُحفظ مستنداتي بعد الترجمة؟",
"pricing.faq.a4": "الملفات المترجمة متاحة حسب خطتك (30 يومًا لـ Starter، 90 يومًا لـ Pro، سنة واحدة لـ Business). وهي مشفرة أثناء التخزين والنقل.",
"pricing.faq.q5": "ماذا يحدث إذا تجاوزت حصتي الشهرية؟",
@@ -9012,7 +9264,7 @@ const messages: Record<Locale, Record<string, string>> = {
"landing.pricing.pro.desc": "برای متخصصان حرفه‌ای",
"landing.pricing.pro.f1": "۲۰۰ سند / ماه",
"landing.pricing.pro.f2": "تا ۲۰۰ صفحه برای هر سند",
"landing.pricing.pro.f3": "ترجمه با هوش مصنوعی (DeepSeek)",
"landing.pricing.pro.f3": "ترجمه مبتنی بر هوش مصنوعی",
"landing.pricing.pro.f4": "Google + DeepL شامل است",
"landing.pricing.pro.f5": "واژه‌نامه و پرامپت سفارشی",
"landing.pricing.pro.f6": "پشتیبانی اولویت‌دار",
@@ -9110,6 +9362,26 @@ const messages: Record<Locale, Record<string, string>> = {
"dashboard.translate.pipeline.rebuild": "بازسازی",
"dashboard.translate.pipeline.finalize": "نهایی‌سازی",
"dashboard.translate.progress.failedTitle": "ترجمه ناموفق",
"dashboard.translate.error.unexpected": "خطای غیرمنتظره‌ای رخ داد. لطفاً دوباره تلاش کنید.",
"dashboard.translate.error.noResult": "ترجمه نتیجه‌ای نداشت. تأیید کنید که سند حاوی متن است، سپس دوباره تلاش کنید یا موتور دیگری انتخاب کنید.",
"dashboard.translate.error.apiKey": "کلید API نامعتبر یا ناموجود است. با مدیر تماس بگیرید تا کلیدها را پیکربندی کند.",
"dashboard.translate.error.quota": "محدودیت استفاده به پایان رسیده. چند دقیقه دیگر دوباره تلاش کنید یا موتور دیگری انتخاب کنید.",
"dashboard.translate.error.timeout": "اتصال به سرویس ترجمه منقضی شد. شبکه خود را بررسی کنید و دوباره تلاش کنید.",
"dashboard.translate.error.sessionExpired": "نشست منقضی شده. برای شروع مجدد ترجمه روی تلاش مجدد کلیک کنید.",
"dashboard.translate.error.empty": "سند خالی به نظر می‌رسد یا متن قابل ترجمه‌ای ندارد (تصویر PDF اسکن شده؟).",
"dashboard.translate.error.unsupported": "فرمت فایل پشتیبانی نمی‌شود یا فایل خراب است.",
"dashboard.translate.error.connection": "اتصال قطع شد. شبکه خود را بررسی کنید و دوباره تلاش کنید.",
"dashboard.translate.error.generic": "ترجمه ناموفق: {detail}",
"dashboard.translate.error.title": "ترجمه ناموفق",
"dashboard.translate.retry": "تلاش مجدد ترجمه",
"dashboard.translate.newFile": "فایل جدید",
"dashboard.translate.modeAI": "حالت هوش مصنوعی",
"dashboard.translate.modeClassic": "حالت کلاسیک",
"dashboard.translate.glossaryLLMHint": "واژه‌نامه‌ها در حالت هوش مصنوعی موجودند",
"dashboard.translate.submitting": "در حال ارسال...",
"dashboard.translate.submit": "شروع ترجمه",
"dashboard.translate.noFile": "ابتدا یک فایل آپلود کنید",
"dashboard.translate.noTargetLang": "زبان مقصد را انتخاب کنید",
"glossaries.yourGlossaries": "واژه‌نامه‌های شما",
"glossaries.title": "واژه‌نامه‌ها و زمینه",
"glossaries.description": "واژه‌نامه‌ها و دستورالعمل‌های زمینه خود را برای ترجمه دقیق‌تر مدیریت کنید.",
@@ -9207,7 +9479,7 @@ const messages: Record<Locale, Record<string, string>> = {
"pricing.plans.pro.feat1": "۲۰۰ سند / ماه",
"pricing.plans.pro.feat2": "تا ۲۰۰ صفحه برای هر سند",
"pricing.plans.pro.feat3": "ترجمه هوش مصنوعی پایه (DeepSeek V3.2)",
"pricing.plans.pro.feat3": "ترجمه هوش مصنوعی Essential",
"pricing.plans.pro.feat4": "Google Translation + DeepL",
"pricing.plans.pro.feat5": "فایل‌ها تا ۲۵ مگابایت",
"pricing.plans.pro.feat6": "واژه‌نامه‌های سفارشی",
@@ -9294,7 +9566,8 @@ const messages: Record<Locale, Record<string, string>> = {
"pricing.aiModels.essential.title": "ترجمه هوش مصنوعی پایه",
"pricing.aiModels.essential.plan": "طرح حرفه‌ای",
"pricing.aiModels.essential.descPrefix": "مبتنی بر",
"pricing.aiModels.essential.descSuffix": "— مقرون‌به‌صرفه‌ترین مدل هوش مصنوعی سال ۲۰۲۶. کیفیت قابل مقایسه با مدل‌های پیشرو با یک‌پنجاهم هزینه.",
"pricing.aiModels.essential.descSuffix": "— مقرون‌به‌صرفه‌ترین مدل هوش مصنوعی سال ۲۰۲۶. کیفیت قابل مقایسه با مدل‌های پیشرو با کسر هزینه.",
"pricing.aiModels.essential.modelName": "مدل هوش مصنوعی Essential",
"pricing.aiModels.essential.context": "۱۶۳هزار توکن زمینه",
"pricing.aiModels.essential.value": "ارزش عالی نسبت به قیمت",
"pricing.aiModels.premium.title": "ترجمه هوش مصنوعی پیشرفته",
@@ -9308,9 +9581,9 @@ const messages: Record<Locale, Record<string, string>> = {
"pricing.faq.q1": "آیا می‌توانم طرح را هر زمان بخواهم تغییر دهم؟",
"pricing.faq.a1": "بله. ارتقا فوری و به‌نسبت است. تنزل در پایان دوره جاری اعمال می‌شود.",
"pricing.faq.q2": "\"ترجمه هوش مصنوعی پایه\" چیست؟",
"pricing.faq.a2": "It is our AI engine based on DeepSeek V3.2 via OpenRouter.",
"pricing.faq.a2": "این موتور هوش مصنوعی ماست. زمینه اسناد شما را درک می‌کند، طرح‌بندی را حفظ می‌کند و اصطلاحات فنی را بسیار بهتر از ترجمه کلاسیک مدیریت می‌کند.",
"pricing.faq.q3": "تفاوت هوش مصنوعی پایه و پیشرفته چیست؟",
"pricing.faq.a3": "هوش مصنوعی پایه از DeepSeek V3.2 استفاده می‌کند (ارزش عالی نسبت به قیمت). هوش مصنوعی پیشرفته از Claude 3.5 Haiku شرکت Anthropic استفاده می‌کند که در اسناد حقوقی، پزشکی و فنی پیچیده دقیق‌تر است.",
"pricing.faq.a3": "هوش مصنوعی Essential از یک مدل بهینه‌شده استفاده می‌کند (ارزش عالی برای پول). هوش مصنوعی Premium از Claude 3.5 Haiku انترپیک استفاده می‌کند که در اسناد حقوقی، پزشکی و فنی پیچیده دقیق‌تر است.",
"pricing.faq.q4": "آیا اسناد من پس از ترجمه نگهداری می‌شوند؟",
"pricing.faq.a4": "فایل‌های ترجمه‌شده طبق طرح شما در دسترس هستند (۳۰ روز مبتدی، ۹۰ روز حرفه‌ای، ۱ سال سازمانی). آنها در حالت استراحت و هنگام انتقال رمزنگاری می‌شوند.",
"pricing.faq.q5": "اگر از سهمیه ماهانه خود فراتر رویم چه می‌شود؟",

View File

@@ -167,8 +167,8 @@ async def get_available_providers():
)
available.append({
"id": "deepseek",
"label": "DeepSeek V3",
"description": "Traduction IA avancée via DeepSeek",
"label": "Traduction IA Express",
"description": "Traduction IA rapide et optimisée",
"mode": "llm",
"tier": "pro",
"model": model,
@@ -184,8 +184,8 @@ async def get_available_providers():
)
available.append({
"id": "minimax",
"label": "MiniMax",
"description": "Traduction IA via MiniMax",
"label": "Traduction IA Avancée",
"description": "Traduction IA haute performance",
"mode": "llm",
"tier": "pro",
"model": model,