feat: harden PDF translation workflow
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -2,31 +2,31 @@
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<url>
|
||||
<loc>https://dociva.io/blog/how-to-compress-pdf-online</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.6</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/blog/convert-images-without-losing-quality</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.6</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/blog/ocr-extract-text-from-images</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.6</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/blog/merge-split-pdf-files</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.6</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/blog/ai-chat-with-pdf-documents</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.6</priority>
|
||||
</url>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,55 +2,55 @@
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<url>
|
||||
<loc>https://dociva.io/</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>daily</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/about</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.4</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/contact</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.4</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/privacy</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>yearly</changefreq>
|
||||
<priority>0.3</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/terms</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>yearly</changefreq>
|
||||
<priority>0.3</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/pricing</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/blog</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.6</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/developers</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.5</priority>
|
||||
</url>
|
||||
|
||||
@@ -2,265 +2,265 @@
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/pdf-to-word</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/word-to-pdf</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/compress-pdf</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/merge-pdf</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/split-pdf</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/rotate-pdf</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/pdf-to-images</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/images-to-pdf</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/watermark-pdf</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/protect-pdf</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/unlock-pdf</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/page-numbers</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/pdf-editor</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/pdf-flowchart</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/pdf-to-excel</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/remove-watermark-pdf</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/reorder-pdf</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/extract-pages</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/image-converter</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/image-resize</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/compress-image</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/ocr</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/remove-background</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/image-to-svg</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/html-to-pdf</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/chat-pdf</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/summarize-pdf</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/translate-pdf</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/extract-tables</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/qr-code</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/video-to-gif</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/word-counter</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.6</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/text-cleaner</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.6</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/pdf-to-pptx</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/excel-to-pdf</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/pptx-to-pdf</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/sign-pdf</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/crop-pdf</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/flatten-pdf</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/repair-pdf</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/pdf-metadata</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.6</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/image-crop</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/image-rotate-flip</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dociva.io/tools/barcode-generator</loc>
|
||||
<lastmod>2026-03-29</lastmod>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Helmet } from 'react-helmet-async';
|
||||
import { Languages } from 'lucide-react';
|
||||
import { Languages, ShieldCheck, Sparkles } from 'lucide-react';
|
||||
import FileUploader from '@/components/shared/FileUploader';
|
||||
import ProgressBar from '@/components/shared/ProgressBar';
|
||||
import AdSlot from '@/components/layout/AdSlot';
|
||||
@@ -26,11 +26,22 @@ const LANGUAGES = [
|
||||
{ value: 'it', label: 'Italiano' },
|
||||
];
|
||||
|
||||
const getLanguageLabel = (value: string) => {
|
||||
if (!value || value === 'auto') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return LANGUAGES.find((language) => language.value === value)?.label ?? value;
|
||||
};
|
||||
|
||||
export default function TranslatePdf() {
|
||||
const { t } = useTranslation();
|
||||
const [phase, setPhase] = useState<'upload' | 'processing' | 'done'>('upload');
|
||||
const [sourceLang, setSourceLang] = useState('auto');
|
||||
const [targetLang, setTargetLang] = useState('en');
|
||||
const [translation, setTranslation] = useState('');
|
||||
const [provider, setProvider] = useState('');
|
||||
const [detectedSourceLanguage, setDetectedSourceLanguage] = useState('');
|
||||
|
||||
const {
|
||||
file, uploadProgress, isUploading, taskId,
|
||||
@@ -39,7 +50,7 @@ export default function TranslatePdf() {
|
||||
endpoint: '/pdf-ai/translate',
|
||||
maxSizeMB: 20,
|
||||
acceptedTypes: ['pdf'],
|
||||
extraData: { target_language: targetLang },
|
||||
extraData: { target_language: targetLang, source_language: sourceLang },
|
||||
});
|
||||
|
||||
const { status, result, error: taskError } = useTaskPolling({
|
||||
@@ -47,6 +58,8 @@ export default function TranslatePdf() {
|
||||
onComplete: (r) => {
|
||||
setPhase('done');
|
||||
setTranslation(r.translation || '');
|
||||
setProvider(r.provider || '');
|
||||
setDetectedSourceLanguage(r.detected_source_language || '');
|
||||
dispatchRatingPrompt('translate-pdf');
|
||||
},
|
||||
onError: () => setPhase('done'),
|
||||
@@ -63,7 +76,17 @@ export default function TranslatePdf() {
|
||||
if (id) setPhase('processing');
|
||||
};
|
||||
|
||||
const handleReset = () => { reset(); setPhase('upload'); setTargetLang('en'); setTranslation(''); };
|
||||
const handleReset = () => {
|
||||
reset();
|
||||
setPhase('upload');
|
||||
setSourceLang('auto');
|
||||
setTargetLang('en');
|
||||
setTranslation('');
|
||||
setProvider('');
|
||||
setDetectedSourceLanguage('');
|
||||
};
|
||||
|
||||
const resolvedDetectedLanguage = getLanguageLabel(detectedSourceLanguage) || getLanguageLabel(sourceLang);
|
||||
|
||||
const schema = generateToolSchema({
|
||||
name: t('tools.translatePdf.title'),
|
||||
@@ -103,15 +126,44 @@ export default function TranslatePdf() {
|
||||
{file && !isUploading && (
|
||||
<>
|
||||
<div className="rounded-2xl bg-white p-5 ring-1 ring-slate-200 dark:bg-slate-800 dark:ring-slate-700">
|
||||
<label className="mb-2 block text-sm font-medium text-slate-700 dark:text-slate-300">
|
||||
{t('tools.translatePdf.targetLang')}
|
||||
</label>
|
||||
<select value={targetLang} onChange={(e) => setTargetLang(e.target.value)}
|
||||
className="w-full rounded-lg border border-slate-300 px-3 py-2 text-sm dark:border-slate-600 dark:bg-slate-700 dark:text-slate-200">
|
||||
{LANGUAGES.map((lang) => (
|
||||
<option key={lang.value} value={lang.value}>{lang.label}</option>
|
||||
))}
|
||||
</select>
|
||||
<div className="mb-4 flex items-start gap-3 rounded-xl bg-slate-50 p-4 dark:bg-slate-900/60">
|
||||
<ShieldCheck className="mt-0.5 h-5 w-5 text-emerald-600 dark:text-emerald-400" />
|
||||
<div>
|
||||
<p className="text-sm font-semibold text-slate-900 dark:text-slate-100">
|
||||
{t('tools.translatePdf.engineTitle')}
|
||||
</p>
|
||||
<p className="mt-1 text-sm text-slate-600 dark:text-slate-400">
|
||||
{t('tools.translatePdf.engineDescription')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-4 md:grid-cols-2">
|
||||
<div>
|
||||
<label className="mb-2 block text-sm font-medium text-slate-700 dark:text-slate-300">
|
||||
{t('tools.translatePdf.sourceLang')}
|
||||
</label>
|
||||
<select value={sourceLang} onChange={(e) => setSourceLang(e.target.value)}
|
||||
className="w-full rounded-lg border border-slate-300 px-3 py-2 text-sm dark:border-slate-600 dark:bg-slate-700 dark:text-slate-200">
|
||||
<option value="auto">{t('tools.translatePdf.autoDetect')}</option>
|
||||
{LANGUAGES.map((lang) => (
|
||||
<option key={`source-${lang.value}`} value={lang.value}>{lang.label}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="mb-2 block text-sm font-medium text-slate-700 dark:text-slate-300">
|
||||
{t('tools.translatePdf.targetLang')}
|
||||
</label>
|
||||
<select value={targetLang} onChange={(e) => setTargetLang(e.target.value)}
|
||||
className="w-full rounded-lg border border-slate-300 px-3 py-2 text-sm dark:border-slate-600 dark:bg-slate-700 dark:text-slate-200">
|
||||
{LANGUAGES.map((lang) => (
|
||||
<option key={lang.value} value={lang.value}>{lang.label}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button onClick={handleUpload} className="btn-primary w-full">
|
||||
{t('tools.translatePdf.shortDesc')}
|
||||
@@ -122,11 +174,39 @@ export default function TranslatePdf() {
|
||||
)}
|
||||
|
||||
{phase === 'processing' && !result && (
|
||||
<ProgressBar state={status?.state || 'PENDING'} message={status?.progress} />
|
||||
<div className="space-y-4">
|
||||
<ProgressBar state={status?.state || 'PENDING'} message={status?.progress} />
|
||||
<div className="rounded-xl bg-white p-4 ring-1 ring-slate-200 dark:bg-slate-800 dark:ring-slate-700">
|
||||
<div className="flex items-start gap-3">
|
||||
<Sparkles className="mt-0.5 h-5 w-5 text-purple-600 dark:text-purple-400" />
|
||||
<p className="text-sm text-slate-600 dark:text-slate-400">
|
||||
{t('tools.translatePdf.processingHint')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{phase === 'done' && translation && (
|
||||
<div className="space-y-4">
|
||||
<div className="grid gap-3 sm:grid-cols-2">
|
||||
<div className="rounded-xl bg-white p-4 ring-1 ring-slate-200 dark:bg-slate-800 dark:ring-slate-700">
|
||||
<p className="text-xs font-semibold uppercase tracking-wide text-slate-500 dark:text-slate-400">
|
||||
{t('tools.translatePdf.sourceDetected')}
|
||||
</p>
|
||||
<p className="mt-1 text-sm font-medium text-slate-900 dark:text-slate-100">
|
||||
{resolvedDetectedLanguage || t('tools.translatePdf.autoDetect')}
|
||||
</p>
|
||||
</div>
|
||||
<div className="rounded-xl bg-white p-4 ring-1 ring-slate-200 dark:bg-slate-800 dark:ring-slate-700">
|
||||
<p className="text-xs font-semibold uppercase tracking-wide text-slate-500 dark:text-slate-400">
|
||||
{t('tools.translatePdf.translationEngine')}
|
||||
</p>
|
||||
<p className="mt-1 text-sm font-medium text-slate-900 dark:text-slate-100">
|
||||
{provider || 'auto'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="rounded-2xl bg-white p-6 ring-1 ring-slate-200 dark:bg-slate-800 dark:ring-slate-700">
|
||||
<h3 className="mb-3 text-sm font-semibold text-slate-700 dark:text-slate-300">
|
||||
{t('tools.translatePdf.resultTitle')}
|
||||
|
||||
@@ -827,9 +827,16 @@
|
||||
},
|
||||
"translatePdf": {
|
||||
"title": "ترجمة PDF",
|
||||
"description": "ترجم محتوى مستند PDF إلى أي لغة باستخدام الذكاء الاصطناعي.",
|
||||
"description": "ترجم ملفات PDF عبر مسار ترجمة احترافي مع fallback تلقائي وتعامل أفضل مع الملفات الممسوحة ضوئياً.",
|
||||
"shortDesc": "ترجمة PDF",
|
||||
"sourceLang": "لغة المصدر",
|
||||
"targetLang": "اللغة المستهدفة",
|
||||
"autoDetect": "اكتشاف تلقائي",
|
||||
"engineTitle": "ترجمة مستندات بجاهزية إنتاجية",
|
||||
"engineDescription": "يتم إرسال الملف أولاً إلى مزود ترجمة احترافي، ثم يتم التحويل تلقائياً إلى مسار AI فقط عند الحاجة. هذا يقلل مشاكل الضغط ويحسن ثبات النتيجة.",
|
||||
"processingHint": "قد تتم ترجمة المستندات الكبيرة على عدة أجزاء مع retries وfallback بين المزودات. اترك الصفحة مفتوحة حتى يكتمل الطلب.",
|
||||
"sourceDetected": "لغة المصدر المكتشفة",
|
||||
"translationEngine": "محرك الترجمة",
|
||||
"resultTitle": "الترجمة"
|
||||
},
|
||||
"tableExtractor": {
|
||||
|
||||
@@ -827,9 +827,16 @@
|
||||
},
|
||||
"translatePdf": {
|
||||
"title": "Translate PDF",
|
||||
"description": "Translate your PDF document content to any language using AI.",
|
||||
"description": "Translate PDF documents with a premium translation pipeline, automatic fallback, and better handling for scanned files.",
|
||||
"shortDesc": "Translate PDF",
|
||||
"sourceLang": "Source Language",
|
||||
"targetLang": "Target Language",
|
||||
"autoDetect": "Auto detect",
|
||||
"engineTitle": "Production-grade document translation",
|
||||
"engineDescription": "Your file is translated with a premium translation provider first, then automatically falls back to AI only if needed. This reduces high-demand failures and improves consistency.",
|
||||
"processingHint": "Large documents may be translated in multiple chunks with retries and provider fallback. Keep this page open until the job completes.",
|
||||
"sourceDetected": "Detected source",
|
||||
"translationEngine": "Translation engine",
|
||||
"resultTitle": "Translation"
|
||||
},
|
||||
"tableExtractor": {
|
||||
|
||||
@@ -827,9 +827,16 @@
|
||||
},
|
||||
"translatePdf": {
|
||||
"title": "Traduire un PDF",
|
||||
"description": "Traduisez le contenu de votre document PDF dans n'importe quelle langue grâce à l'IA.",
|
||||
"description": "Traduisez vos PDF avec un pipeline premium, un fallback automatique et une meilleure prise en charge des fichiers scannés.",
|
||||
"shortDesc": "Traduire le PDF",
|
||||
"sourceLang": "Langue source",
|
||||
"targetLang": "Langue cible",
|
||||
"autoDetect": "Détection automatique",
|
||||
"engineTitle": "Traduction documentaire de niveau production",
|
||||
"engineDescription": "Votre fichier passe d'abord par un fournisseur de traduction premium, puis bascule automatiquement vers l'IA seulement si nécessaire. Cela réduit les erreurs de forte demande et améliore la stabilité.",
|
||||
"processingHint": "Les documents volumineux peuvent être traduits en plusieurs segments avec retries et fallback entre fournisseurs. Laissez cette page ouverte jusqu'à la fin du traitement.",
|
||||
"sourceDetected": "Source détectée",
|
||||
"translationEngine": "Moteur de traduction",
|
||||
"resultTitle": "Traduction"
|
||||
},
|
||||
"tableExtractor": {
|
||||
|
||||
@@ -237,6 +237,10 @@ export interface TaskResult {
|
||||
summary?: string;
|
||||
translation?: string;
|
||||
target_language?: string;
|
||||
source_language?: string;
|
||||
detected_source_language?: string;
|
||||
provider?: string;
|
||||
chunks_translated?: number;
|
||||
pages_analyzed?: number;
|
||||
// Table extraction fields
|
||||
tables?: Array<{ page: number; table_index: number; headers: string[]; rows: string[][] }>;
|
||||
|
||||
Reference in New Issue
Block a user