feat: add comparison page functionality and related routes

- Added a new route for comparison pages in routes.ts.
- Introduced a TOOL_WORKFLOWS object in seoData.ts to define tool usage sequences.
- Updated internal link generation to include workflow slugs.
- Added Arabic, English, and French translations for comparison features and FAQs in respective i18n files.
- Implemented the ComparisonPage component to display feature comparisons, advantages, verdicts, and related tools.
- Enhanced sitemap generation script to include comparison pages.
This commit is contained in:
Your Name
2026-04-03 02:29:14 +02:00
parent 209fdb6509
commit 92ca0af3c7
11 changed files with 885 additions and 2 deletions

View File

@@ -151,6 +151,22 @@ const toolEntries = dedupeEntries(
})),
).map((entry) => makeUrlTag(entry));
const comparisonSlugs = [
'compress-pdf-vs-ilovepdf',
'merge-pdf-vs-smallpdf',
'pdf-to-word-vs-adobe-acrobat',
'compress-image-vs-tinypng',
'ocr-vs-adobe-scan',
];
const comparisonEntries = dedupeEntries(
comparisonSlugs.map((slug) => ({
loc: `${siteOrigin}/compare/${slug}`,
changefreq: 'monthly',
priority: '0.7',
})),
).map((entry) => makeUrlTag(entry));
const seoEntries = dedupeEntries([
...seoConfig.toolPageSeeds.flatMap((page) => ([
{ loc: `${siteOrigin}/${page.slug}`, changefreq: 'weekly', priority: '0.88' },
@@ -167,6 +183,7 @@ const sitemapIndex = makeSitemapIndex([
{ loc: `${siteOrigin}/sitemaps/blog.xml` },
{ loc: `${siteOrigin}/sitemaps/tools.xml` },
{ loc: `${siteOrigin}/sitemaps/seo.xml` },
{ loc: `${siteOrigin}/sitemaps/comparisons.xml` },
]);
const robots = [
@@ -192,6 +209,7 @@ await writeFile(path.join(sitemapDir, 'static.xml'), makeSitemapUrlSet(staticEnt
await writeFile(path.join(sitemapDir, 'blog.xml'), makeSitemapUrlSet(blogEntries), 'utf8');
await writeFile(path.join(sitemapDir, 'tools.xml'), makeSitemapUrlSet(toolEntries), 'utf8');
await writeFile(path.join(sitemapDir, 'seo.xml'), makeSitemapUrlSet(seoEntries), 'utf8');
await writeFile(path.join(sitemapDir, 'comparisons.xml'), makeSitemapUrlSet(comparisonEntries), 'utf8');
await writeFile(path.join(publicDir, 'robots.txt'), robots, 'utf8');
console.log(`Generated SEO assets for ${siteOrigin}`);

View File

@@ -31,6 +31,7 @@ const DevelopersPage = lazy(() => import('@/pages/DevelopersPage'));
const AllToolsPage = lazy(() => import('@/pages/AllToolsPage'));
const InternalAdminPage = lazy(() => import('@/pages/InternalAdminPage'));
const SeoRoutePage = lazy(() => import('@/pages/SeoRoutePage'));
const ComparisonPage = lazy(() => import('@/pages/ComparisonPage'));
const CookieConsent = lazy(() => import('@/components/layout/CookieConsent'));
const SiteAssistant = lazy(() => import('@/components/layout/SiteAssistant'));
@@ -122,6 +123,7 @@ export default function App() {
<Route path="/developers" element={<DevelopersPage />} />
<Route path="/tools" element={<AllToolsPage />} />
<Route path="/internal/admin" element={<InternalAdminPage />} />
<Route path="/compare/:slug" element={<ComparisonPage />} />
<Route path="/ar/:slug" element={<SeoRoutePage />} />
<Route path="/:slug" element={<SeoRoutePage />} />

View File

@@ -36,6 +36,13 @@ const FOOTER_TOOLS = {
{ slug: 'free-pdf-tools-online', label: 'Free PDF Tools Online', isLanding: true },
{ slug: 'convert-files-online', label: 'Convert Files Online', isLanding: true },
],
Comparisons: [
{ slug: 'compress-pdf-vs-ilovepdf', label: 'Dociva vs iLovePDF', isComparison: true },
{ slug: 'merge-pdf-vs-smallpdf', label: 'Dociva vs Smallpdf', isComparison: true },
{ slug: 'pdf-to-word-vs-adobe-acrobat', label: 'Dociva vs Adobe Acrobat', isComparison: true },
{ slug: 'compress-image-vs-tinypng', label: 'Dociva vs TinyPNG', isComparison: true },
{ slug: 'ocr-vs-adobe-scan', label: 'Dociva vs Adobe Scan', isComparison: true },
],
};
export default function Footer() {
@@ -55,7 +62,7 @@ export default function Footer() {
{tools.map((tool) => (
<li key={tool.slug}>
<Link
to={(tool as { slug: string; isLanding?: boolean }).isLanding ? `/${tool.slug}` : `/tools/${tool.slug}`}
to={(tool as { slug: string; isLanding?: boolean; isComparison?: boolean }).isComparison ? `/compare/${tool.slug}` : (tool as { slug: string; isLanding?: boolean }).isLanding ? `/${tool.slug}` : `/tools/${tool.slug}`}
className="text-sm text-slate-500 transition-colors hover:text-primary-600 dark:text-slate-400 dark:hover:text-primary-400"
>
{tool.label}

View File

@@ -0,0 +1,145 @@
/**
* Comparison page data — defines competitor comparisons for SEO landing pages.
*
* Adding a new comparison:
* 1. Append an entry to COMPARISON_PAGES below.
* 2. Add matching i18n keys under pages.comparison.<i18nKey>.* in en/ar/fr.json.
* 3. Add the slug to both sitemap generators (generate_sitemap.py + generate-seo-assets.mjs).
*/
export interface ComparisonFeature {
/** i18n key suffix under pages.comparison.features.* */
key: string;
/** true = has the feature, false = missing, 'partial' = limited */
us: boolean | 'partial';
competitor: boolean | 'partial';
}
export interface ComparisonPage {
/** URL slug: /compare/<slug> */
slug: string;
/** i18n key prefix: pages.comparison.<i18nKey>.* */
i18nKey: string;
/** Our tool slug (links to /tools/<ourToolSlug>) */
ourToolSlug: string;
/** Competitor display name */
competitorName: string;
/** SEO category */
category: 'PDF' | 'Image' | 'AI' | 'Convert' | 'Utility';
/** Feature comparison rows */
features: ComparisonFeature[];
/** Related comparison page slugs */
relatedComparisonSlugs: string[];
/** Tool slugs to show as related */
relatedToolSlugs: string[];
}
export const COMPARISON_PAGES: ComparisonPage[] = [
{
slug: 'compress-pdf-vs-ilovepdf',
i18nKey: 'compressPdfVsIlovepdf',
ourToolSlug: 'compress-pdf',
competitorName: 'iLovePDF',
category: 'PDF',
features: [
{ key: 'freeUnlimited', us: true, competitor: false },
{ key: 'noSignup', us: true, competitor: false },
{ key: 'batchProcessing', us: true, competitor: 'partial' },
{ key: 'compressionLevels', us: true, competitor: true },
{ key: 'autoDelete', us: true, competitor: true },
{ key: 'noAds', us: true, competitor: false },
{ key: 'apiAccess', us: true, competitor: true },
{ key: 'offlineMode', us: false, competitor: false },
],
relatedComparisonSlugs: ['merge-pdf-vs-smallpdf', 'pdf-to-word-vs-adobe-acrobat'],
relatedToolSlugs: ['compress-pdf', 'merge-pdf', 'split-pdf', 'compress-image'],
},
{
slug: 'merge-pdf-vs-smallpdf',
i18nKey: 'mergePdfVsSmallpdf',
ourToolSlug: 'merge-pdf',
competitorName: 'Smallpdf',
category: 'PDF',
features: [
{ key: 'freeUnlimited', us: true, competitor: false },
{ key: 'noSignup', us: true, competitor: false },
{ key: 'batchProcessing', us: true, competitor: true },
{ key: 'dragReorder', us: true, competitor: true },
{ key: 'autoDelete', us: true, competitor: true },
{ key: 'noAds', us: true, competitor: false },
{ key: 'apiAccess', us: true, competitor: 'partial' },
{ key: 'offlineMode', us: false, competitor: false },
],
relatedComparisonSlugs: ['compress-pdf-vs-ilovepdf', 'pdf-to-word-vs-adobe-acrobat'],
relatedToolSlugs: ['merge-pdf', 'split-pdf', 'compress-pdf', 'reorder-pdf'],
},
{
slug: 'pdf-to-word-vs-adobe-acrobat',
i18nKey: 'pdfToWordVsAdobeAcrobat',
ourToolSlug: 'pdf-to-word',
competitorName: 'Adobe Acrobat',
category: 'PDF',
features: [
{ key: 'freeUnlimited', us: true, competitor: false },
{ key: 'noSignup', us: true, competitor: false },
{ key: 'preserveFormatting', us: true, competitor: true },
{ key: 'batchProcessing', us: true, competitor: true },
{ key: 'autoDelete', us: true, competitor: true },
{ key: 'noAds', us: true, competitor: true },
{ key: 'noInstall', us: true, competitor: false },
{ key: 'offlineMode', us: false, competitor: true },
],
relatedComparisonSlugs: ['compress-pdf-vs-ilovepdf', 'merge-pdf-vs-smallpdf'],
relatedToolSlugs: ['pdf-to-word', 'word-to-pdf', 'pdf-to-excel', 'compress-pdf'],
},
{
slug: 'compress-image-vs-tinypng',
i18nKey: 'compressImageVsTinypng',
ourToolSlug: 'compress-image',
competitorName: 'TinyPNG',
category: 'Image',
features: [
{ key: 'freeUnlimited', us: true, competitor: false },
{ key: 'noSignup', us: true, competitor: true },
{ key: 'multiFormat', us: true, competitor: 'partial' },
{ key: 'batchProcessing', us: true, competitor: 'partial' },
{ key: 'qualityControl', us: true, competitor: false },
{ key: 'autoDelete', us: true, competitor: true },
{ key: 'apiAccess', us: true, competitor: true },
{ key: 'offlineMode', us: false, competitor: false },
],
relatedComparisonSlugs: ['compress-pdf-vs-ilovepdf', 'ocr-vs-adobe-scan'],
relatedToolSlugs: ['compress-image', 'image-converter', 'image-resize', 'remove-background'],
},
{
slug: 'ocr-vs-adobe-scan',
i18nKey: 'ocrVsAdobeScan',
ourToolSlug: 'ocr',
competitorName: 'Adobe Scan',
category: 'AI',
features: [
{ key: 'freeUnlimited', us: true, competitor: false },
{ key: 'noSignup', us: true, competitor: false },
{ key: 'noInstall', us: true, competitor: false },
{ key: 'multiLanguageOcr', us: true, competitor: true },
{ key: 'batchProcessing', us: true, competitor: false },
{ key: 'autoDelete', us: true, competitor: true },
{ key: 'apiAccess', us: true, competitor: false },
{ key: 'offlineMode', us: false, competitor: true },
],
relatedComparisonSlugs: ['pdf-to-word-vs-adobe-acrobat', 'compress-image-vs-tinypng'],
relatedToolSlugs: ['ocr', 'chat-pdf', 'summarize-pdf', 'pdf-to-word'],
},
];
export function getComparisonPage(slug: string): ComparisonPage | undefined {
return COMPARISON_PAGES.find((p) => p.slug === slug);
}
export function getAllComparisonSlugs(): string[] {
return COMPARISON_PAGES.map((p) => p.slug);
}
export function getComparisonPagesByTool(toolSlug: string): ComparisonPage[] {
return COMPARISON_PAGES.filter((p) => p.ourToolSlug === toolSlug);
}

View File

@@ -27,6 +27,7 @@ const STATIC_PAGE_ROUTES = [
'/tools',
'/internal/admin',
'/pricing-transparency',
'/compare/:slug',
] as const;
const SEO_PAGE_ROUTES = getAllSeoLandingPaths();

View File

@@ -917,6 +917,31 @@ const POPULAR_TOOL_SLUGS = [
'video-to-gif',
] as const;
/** Workflow chains: tools users are likely to use in sequence */
const TOOL_WORKFLOWS: Record<string, string[]> = {
'compress-pdf': ['merge-pdf', 'split-pdf', 'pdf-to-word'],
'merge-pdf': ['compress-pdf', 'split-pdf', 'rotate-pdf'],
'split-pdf': ['merge-pdf', 'compress-pdf', 'pdf-to-word'],
'pdf-to-word': ['word-to-pdf', 'compress-pdf', 'ocr'],
'word-to-pdf': ['compress-pdf', 'merge-pdf', 'pdf-editor'],
'pdf-to-excel': ['pdf-to-word', 'ocr', 'compress-pdf'],
'pdf-to-pptx': ['pdf-to-word', 'compress-pdf', 'merge-pdf'],
'rotate-pdf': ['merge-pdf', 'split-pdf', 'compress-pdf'],
'pdf-editor': ['compress-pdf', 'merge-pdf', 'watermark-pdf'],
'watermark-pdf': ['compress-pdf', 'pdf-editor', 'merge-pdf'],
'protect-pdf': ['watermark-pdf', 'compress-pdf', 'merge-pdf'],
'unlock-pdf': ['compress-pdf', 'pdf-to-word', 'merge-pdf'],
'ocr': ['pdf-to-word', 'compress-pdf', 'image-converter'],
'compress-image': ['image-resize', 'image-converter', 'image-to-pdf'],
'image-resize': ['compress-image', 'image-converter', 'image-crop'],
'image-converter': ['compress-image', 'image-resize', 'image-to-pdf'],
'image-to-pdf': ['compress-pdf', 'merge-pdf', 'compress-image'],
'image-crop': ['image-resize', 'compress-image', 'image-converter'],
'html-to-pdf': ['compress-pdf', 'merge-pdf', 'pdf-editor'],
'qr-code': ['barcode-generator', 'html-to-pdf'],
'barcode-generator': ['qr-code', 'html-to-pdf'],
};
function dedupeExistingToolSlugs(slugs: string[], excludeSlugs: string[] = []): string[] {
const excluded = new Set(excludeSlugs);
const seen = new Set<string>();
@@ -952,12 +977,14 @@ export function getInternalLinkToolSlugs(currentSlug: string, limit = 8): string
return [];
}
const workflowSlugs = TOOL_WORKFLOWS[currentSlug] || [];
const sameCategorySlugs = TOOLS_SEO
.filter((tool) => tool.category === currentTool.category && tool.slug !== currentSlug)
.map((tool) => tool.slug);
const internalLinks = dedupeExistingToolSlugs(
[...currentTool.relatedSlugs, ...sameCategorySlugs, ...POPULAR_TOOL_SLUGS],
[...currentTool.relatedSlugs, ...workflowSlugs, ...sameCategorySlugs, ...POPULAR_TOOL_SLUGS],
[currentSlug]
);

View File

@@ -411,6 +411,134 @@
"pricingCta": "قارن بين Free وPro",
"toolsCta": "استكشف الأدوات"
},
"comparison": {
"badge": "مقارنة",
"featureComparison": "مقارنة المميزات",
"feature": "الميزة",
"verdictTitle": "الحكم النهائي",
"faqTitle": "الأسئلة الشائعة",
"relatedComparisons": "مقارنات أخرى",
"relatedTools": "أدوات ذات صلة",
"ctaSubtext": "مجاني، بدون تسجيل — جرّبه الآن.",
"notFound": "المقارنة غير موجودة",
"browseTools": "تصفح جميع الأدوات",
"features": {
"freeUnlimited": "مجاني وبدون حدود",
"noSignup": "بدون تسجيل حساب",
"batchProcessing": "معالجة دفعات",
"compressionLevels": "مستويات ضغط متعددة",
"autoDelete": "حذف تلقائي للملفات",
"noAds": "بدون إعلانات أو نوافذ منبثقة",
"apiAccess": "وصول API",
"offlineMode": "وضع سطح المكتب بدون اتصال",
"dragReorder": "إعادة ترتيب بالسحب والإفلات",
"preserveFormatting": "الحفاظ على التنسيق",
"noInstall": "بدون حاجة لتثبيت برامج",
"multiFormat": "دعم صيغ متعددة",
"qualityControl": "شريط تحكم بالجودة",
"multiLanguageOcr": "OCR متعدد اللغات"
},
"compressPdfVsIlovepdf": {
"title": "Dociva مقابل iLovePDF — مقارنة ضغط PDF المجاني",
"metaDescription": "قارن بين Dociva وiLovePDF لضغط ملفات PDF. شاهد المميزات والحدود والأسعار جنبًا إلى جنب.",
"heading": "Dociva مقابل iLovePDF",
"subtitle": "كلتا الأداتين تضغطان ملفات PDF، لكن Dociva تقدم استخدامًا مجانيًا غير محدود بدون تسجيل وبدون إعلانات.",
"advantagesTitle": "لماذا تختار Dociva بدلاً من iLovePDF",
"advantages": [
"بلا حدود حقيقية — لا قيود يومية على الملفات",
"بدون حاجة لإنشاء حساب — ابدأ الضغط فورًا",
"صفر إعلانات، صفر نوافذ منبثقة، واجهة نظيفة",
"الملفات تُحذف تلقائيًا بعد 30 دقيقة",
"وصول API متاح في خطة Pro"
],
"verdict": "للمستخدمين الذين يحتاجون ضاغط PDF مجاني وموثوق بدون حدود يومية أو جدران تسجيل، Dociva هي الخيار الأوضح.",
"faqs": [
{ "q": "هل Dociva مجاني فعلًا لضغط PDF؟", "a": "نعم. ضاغط PDF في Dociva مجاني بدون حدود يومية وبدون حاجة لحساب." },
{ "q": "هل يحدّ iLovePDF المستخدمين المجانيين؟", "a": "نعم. الطبقة المجانية في iLovePDF بها حدود يومية للمهام وتعرض إعلانات." },
{ "q": "أي أداة تضغط أكثر؟", "a": "كلتا الأداتين تقدمان نسب ضغط متقاربة. Dociva توفر ثلاث مستويات ضغط لتوازن الجودة والحجم." }
]
},
"mergePdfVsSmallpdf": {
"title": "Dociva مقابل Smallpdf — مقارنة دمج PDF المجاني",
"metaDescription": "قارن بين Dociva وSmallpdf لدمج ملفات PDF. شاهد المميزات والحدود والأسعار جنبًا إلى جنب.",
"heading": "Dociva مقابل Smallpdf",
"subtitle": "كلتا المنصتين تدمجان ملفات PDF أونلاين، لكن Dociva تقدم دمجًا غير محدود بدون تسجيل.",
"advantagesTitle": "لماذا تختار Dociva بدلاً من Smallpdf",
"advantages": [
"دمج PDF غير محدود — بدون حدود يومية",
"بدون حاجة لحساب لدمج الملفات",
"واجهة نظيفة بدون إعلانات",
"إعادة ترتيب الصفحات بالسحب والإفلات",
"الملفات تُحذف تلقائيًا بعد المعالجة"
],
"verdict": "Smallpdf أنيقة لكن تقفل معظم المميزات خلف حاجز دفع بعد مهمتين مجانيتين يوميًا. Dociva تقدم نفس وظيفة الدمج الأساسية بلا حدود وبلا تسجيل.",
"faqs": [
{ "q": "هل يمكنني دمج PDF مجانًا مع Dociva؟", "a": "نعم. أداة دمج Dociva مجانية تمامًا بدون حدود يومية." },
{ "q": "هل يحدّ Smallpdf عمليات الدمج المجانية؟", "a": "نعم. Smallpdf تسمح فقط بمهمتين مجانيتين يوميًا." },
{ "q": "هل يمكنني إعادة ترتيب الصفحات قبل الدمج؟", "a": "نعم. كلتا الأداتين تدعمان إعادة الترتيب بالسحب والإفلات قبل الدمج النهائي." }
]
},
"pdfToWordVsAdobeAcrobat": {
"title": "Dociva مقابل Adobe Acrobat — مقارنة تحويل PDF إلى Word",
"metaDescription": "قارن بين Dociva وAdobe Acrobat لتحويل PDF إلى Word. أداة أونلاين مجانية مقابل برنامج سطح مكتب مدفوع.",
"heading": "Dociva مقابل Adobe Acrobat",
"subtitle": "Adobe Acrobat هو المعيار الصناعي، لكن Dociva تقدم نفس تحويل PDF إلى Word مجانًا وأونلاين بدون تثبيت.",
"advantagesTitle": "لماذا تختار Dociva بدلاً من Adobe Acrobat",
"advantages": [
"مجاني 100% — بدون رسوم اشتراك",
"بدون تحميل أو تثبيت برامج",
"يعمل على أي جهاز بمتصفح",
"بدون حاجة لمعرف Adobe",
"تحويل سريع مع الحفاظ على التنسيق"
],
"verdict": "Adobe Acrobat يقدم أقوى مجموعة أدوات PDF، لكن بسعر مرتفع. لتحويل PDF إلى Word المباشر، Dociva تطابق الجودة مجانًا وأونلاين.",
"faqs": [
{ "q": "هل جودة تحويل Dociva بمستوى Adobe؟", "a": "للمستندات العادية، تحافظ Dociva على التنسيق والخطوط والتخطيط بشكل مقارب لمحول Adobe Acrobat." },
{ "q": "هل يتطلب Adobe Acrobat اشتراكًا؟", "a": "نعم. مميزات التحويل الكاملة تتطلب اشتراك Acrobat Pro." },
{ "q": "هل يمكنني تحويل ملفات PDF كبيرة؟", "a": "Dociva تدعم ملفات حتى 20 ميجابايت. Adobe Acrobat يدعم ملفات أكبر عبر تطبيق سطح المكتب." }
]
},
"compressImageVsTinypng": {
"title": "Dociva مقابل TinyPNG — مقارنة ضغط الصور المجاني",
"metaDescription": "قارن بين Dociva وTinyPNG لضغط الصور. شاهد دعم الصيغ والحدود والجودة.",
"heading": "Dociva مقابل TinyPNG",
"subtitle": "TinyPNG ضاغط صور شهير، لكن Dociva تدعم صيغ أكثر وبدون حدود على عدد الملفات.",
"advantagesTitle": "لماذا تختار Dociva بدلاً من TinyPNG",
"advantages": [
"ضغط غير محدود — بدون حدود يومية",
"تدعم JPEG وPNG وWebP وGIF والمزيد",
"شريط تحكم بالجودة لنتائج دقيقة",
"رفع ومعالجة صور متعددة دفعة واحدة",
"بدون قيود على عدد الملفات في الطبقة المجانية"
],
"verdict": "TinyPNG تتفوق في تحسين PNG/JPEG بواجهة بسيطة، لكنها تحدّ المستخدمين المجانيين بـ 20 صورة لكل دفعة. Dociva تزيل هذه الحدود وتضيف مرونة في الصيغ.",
"faqs": [
{ "q": "هل لدى TinyPNG حدود؟", "a": "نعم. أداة TinyPNG المجانية تحدّك بـ 20 صورة لكل دفعة و5 ميجابايت لكل ملف." },
{ "q": "ما الصيغ التي تدعمها Dociva؟", "a": "Dociva تضغط JPEG وPNG وWebP وGIF وSVG والمزيد." },
{ "q": "هل يمكنني التحكم بجودة الضغط؟", "a": "نعم. Dociva توفر شريط تحكم بالجودة لتوازن حجم الملف والوضوح البصري." }
]
},
"ocrVsAdobeScan": {
"title": "Dociva OCR مقابل Adobe Scan — مقارنة التعرف على النص",
"metaDescription": "قارن بين Dociva OCR وAdobe Scan للتعرف على النص. أداة متصفح مقابل تطبيق جوال.",
"heading": "Dociva OCR مقابل Adobe Scan",
"subtitle": "Adobe Scan تطبيق مسح ضوئي للجوال، بينما Dociva OCR تعمل مباشرة في متصفحك بدون تثبيت.",
"advantagesTitle": "لماذا تختار Dociva OCR بدلاً من Adobe Scan",
"advantages": [
"تعمل في أي متصفح — بدون تثبيت تطبيق",
"معالجة OCR غير محدودة",
"بدون حاجة لحساب Adobe",
"تدعم معالجة دفعات لملفات متعددة",
"تعرف نصي متعدد اللغات (إنجليزي، عربي، فرنسي)"
],
"verdict": "Adobe Scan تطبيق مسح ضوئي جوال ممتاز مع دعم العمل بدون اتصال. لكن لـ OCR عبر الويب مع معالجة دفعات وبدون تسجيل، Dociva أسرع وأكثر مرونة.",
"faqs": [
{ "q": "هل يعمل Dociva OCR بدون اتصال؟", "a": "لا. Dociva OCR يتطلب اتصال إنترنت لمعالجة الملفات على خوادم آمنة." },
{ "q": "هل Adobe Scan مجاني؟", "a": "المميزات الأساسية مجانية، لكن OCR المتقدم والتصدير يتطلبان اشتراك Acrobat." },
{ "q": "ما اللغات التي يدعمها Dociva OCR؟", "a": "Dociva OCR يدعم التعرف على النص بالإنجليزية والعربية والفرنسية." }
]
}
},
"developers": {
"metaDescription": "استكشف بوابة مطوري Dociva، وتدفق API غير المتزامن، والنقاط الجاهزة لأتمتة المستندات.",
"badge": "بوابة المطورين",

View File

@@ -411,6 +411,134 @@
"pricingCta": "Compare Free and Pro",
"toolsCta": "Explore tools"
},
"comparison": {
"badge": "Comparison",
"featureComparison": "Feature Comparison",
"feature": "Feature",
"verdictTitle": "The Verdict",
"faqTitle": "Frequently Asked Questions",
"relatedComparisons": "More Comparisons",
"relatedTools": "Related Tools",
"ctaSubtext": "Free, no signup required — try it now.",
"notFound": "Comparison not found",
"browseTools": "Browse all tools",
"features": {
"freeUnlimited": "Free & unlimited use",
"noSignup": "No signup required",
"batchProcessing": "Batch processing",
"compressionLevels": "Multiple compression levels",
"autoDelete": "Auto-delete files",
"noAds": "No ads or popups",
"apiAccess": "API access",
"offlineMode": "Offline desktop mode",
"dragReorder": "Drag-and-drop reorder",
"preserveFormatting": "Preserve formatting",
"noInstall": "No software install needed",
"multiFormat": "Multi-format support",
"qualityControl": "Quality control slider",
"multiLanguageOcr": "Multi-language OCR"
},
"compressPdfVsIlovepdf": {
"title": "Dociva vs iLovePDF — Free PDF Compression Compared",
"metaDescription": "Compare Dociva and iLovePDF for PDF compression. See features, limits, and pricing side by side.",
"heading": "Dociva vs iLovePDF",
"subtitle": "Both tools compress PDFs, but Dociva offers unlimited free use with no signup and no ads. See how they compare feature by feature.",
"advantagesTitle": "Why choose Dociva over iLovePDF",
"advantages": [
"Truly unlimited — no daily file caps or task limits",
"No account required — start compressing immediately",
"Zero ads, zero popups, clean interface",
"Files automatically deleted after 30 minutes",
"API access included on Pro plan"
],
"verdict": "For users who need a reliable, free PDF compressor without daily limits or sign-up walls, Dociva is the clear choice. iLovePDF offers a broader desktop suite, but its free tier is restrictive.",
"faqs": [
{ "q": "Is Dociva really free for PDF compression?", "a": "Yes. Dociva's PDF compressor is free with no daily limits, no account required, and no hidden paywalls." },
{ "q": "Does iLovePDF limit free users?", "a": "Yes. iLovePDF's free tier has daily task limits and shows ads. Premium features require a paid subscription." },
{ "q": "Which tool compresses PDFs more?", "a": "Both tools offer comparable compression ratios. Dociva provides three compression levels to balance quality and file size." }
]
},
"mergePdfVsSmallpdf": {
"title": "Dociva vs Smallpdf — Free PDF Merge Compared",
"metaDescription": "Compare Dociva and Smallpdf for merging PDFs. See features, limits, and pricing side by side.",
"heading": "Dociva vs Smallpdf",
"subtitle": "Both platforms merge PDFs online, but Dociva offers unlimited merges with no signup. See the differences.",
"advantagesTitle": "Why choose Dociva over Smallpdf",
"advantages": [
"Unlimited PDF merges — no daily caps",
"No account required to merge files",
"Clean, ad-free interface",
"Drag-and-drop page reordering",
"Files auto-deleted after processing"
],
"verdict": "Smallpdf is polished but locks most features behind a paywall after two free tasks per day. Dociva provides the same core merge functionality with no limits and no signup.",
"faqs": [
{ "q": "Can I merge PDFs for free with Dociva?", "a": "Yes. Dociva's merge tool is completely free with no daily limits." },
{ "q": "Does Smallpdf limit free merges?", "a": "Yes. Smallpdf allows only two free tasks per day on its free tier." },
{ "q": "Can I reorder pages before merging?", "a": "Yes. Both tools support drag-and-drop reordering before the final merge." }
]
},
"pdfToWordVsAdobeAcrobat": {
"title": "Dociva vs Adobe Acrobat — Free PDF to Word Compared",
"metaDescription": "Compare Dociva and Adobe Acrobat for PDF to Word conversion. Free online tool vs premium desktop software.",
"heading": "Dociva vs Adobe Acrobat",
"subtitle": "Adobe Acrobat is the industry standard, but Dociva delivers the same PDF-to-Word conversion for free, online, with no install.",
"advantagesTitle": "Why choose Dociva over Adobe Acrobat",
"advantages": [
"100% free — no subscription fees",
"No software download or installation",
"Works on any device with a browser",
"No Adobe ID required",
"Fast conversion with formatting preserved"
],
"verdict": "Adobe Acrobat offers the most powerful PDF toolkit, but at a high price. For straightforward PDF-to-Word conversion, Dociva matches the quality for free, online, with zero friction.",
"faqs": [
{ "q": "Is Dociva's conversion quality as good as Adobe?", "a": "For standard documents, Dociva preserves formatting, fonts, and layout comparable to Adobe Acrobat's online converter." },
{ "q": "Does Adobe Acrobat require a subscription?", "a": "Yes. Adobe Acrobat's full conversion features require an Acrobat Pro subscription starting at $19.99/month." },
{ "q": "Can I convert large PDFs?", "a": "Dociva supports files up to 20MB. Adobe Acrobat supports larger files with its desktop app." }
]
},
"compressImageVsTinypng": {
"title": "Dociva vs TinyPNG — Free Image Compression Compared",
"metaDescription": "Compare Dociva and TinyPNG for image compression. See format support, limits, and quality differences.",
"heading": "Dociva vs TinyPNG",
"subtitle": "TinyPNG is a popular image compressor, but Dociva supports more formats and has no file count limits. Compare them here.",
"advantagesTitle": "Why choose Dociva over TinyPNG",
"advantages": [
"Unlimited compressions — no daily file caps",
"Supports JPEG, PNG, WebP, GIF, and more",
"Quality control slider for precise results",
"Batch upload and process multiple images",
"No file count restrictions on free tier"
],
"verdict": "TinyPNG excels at PNG/JPEG optimization with a simple interface, but limits free users to 20 images per batch. Dociva removes those limits and adds format flexibility.",
"faqs": [
{ "q": "Does TinyPNG have limits?", "a": "Yes. TinyPNG's free web tool limits you to 20 images per batch and 5MB per file." },
{ "q": "Which formats does Dociva support?", "a": "Dociva compresses JPEG, PNG, WebP, GIF, SVG, and more image formats." },
{ "q": "Can I control compression quality?", "a": "Yes. Dociva provides a quality slider so you can balance file size and visual quality." }
]
},
"ocrVsAdobeScan": {
"title": "Dociva OCR vs Adobe Scan — Free Text Recognition Compared",
"metaDescription": "Compare Dociva OCR and Adobe Scan for text recognition. Browser-based vs mobile app — see features side by side.",
"heading": "Dociva OCR vs Adobe Scan",
"subtitle": "Adobe Scan is a mobile scanning app, while Dociva OCR works directly in your browser with no install. See how they compare.",
"advantagesTitle": "Why choose Dociva OCR over Adobe Scan",
"advantages": [
"Works in any browser — no app install",
"Unlimited OCR processing",
"No Adobe account required",
"Supports batch processing of multiple files",
"Multi-language text recognition (English, Arabic, French)"
],
"verdict": "Adobe Scan is a great mobile scanning app with offline support. But for web-based OCR with batch processing and no login, Dociva is faster and more flexible.",
"faqs": [
{ "q": "Does Dociva OCR work offline?", "a": "No. Dociva OCR requires an internet connection as it processes files on secure servers." },
{ "q": "Is Adobe Scan free?", "a": "Adobe Scan's basic features are free, but advanced OCR and export features require an Acrobat subscription." },
{ "q": "Which languages does Dociva OCR support?", "a": "Dociva OCR supports English, Arabic, and French text recognition out of the box." }
]
}
},
"developers": {
"metaDescription": "Explore the Dociva developer portal, async API flow, and production-ready endpoints for document automation.",
"badge": "Developer Portal",

View File

@@ -411,6 +411,134 @@
"pricingCta": "Comparer Gratuit et Pro",
"toolsCta": "Explorer les outils"
},
"comparison": {
"badge": "Comparaison",
"featureComparison": "Comparaison des fonctionnalités",
"feature": "Fonctionnalité",
"verdictTitle": "Le verdict",
"faqTitle": "Questions fréquentes",
"relatedComparisons": "Autres comparaisons",
"relatedTools": "Outils associés",
"ctaSubtext": "Gratuit, sans inscription — essayez maintenant.",
"notFound": "Comparaison introuvable",
"browseTools": "Parcourir tous les outils",
"features": {
"freeUnlimited": "Gratuit et illimité",
"noSignup": "Aucune inscription requise",
"batchProcessing": "Traitement par lots",
"compressionLevels": "Plusieurs niveaux de compression",
"autoDelete": "Suppression automatique des fichiers",
"noAds": "Pas de publicités ni de popups",
"apiAccess": "Accès API",
"offlineMode": "Mode bureau hors ligne",
"dragReorder": "Réorganisation par glisser-déposer",
"preserveFormatting": "Préservation du formatage",
"noInstall": "Aucune installation requise",
"multiFormat": "Support multi-format",
"qualityControl": "Curseur de contrôle qualité",
"multiLanguageOcr": "OCR multilingue"
},
"compressPdfVsIlovepdf": {
"title": "Dociva vs iLovePDF — Compression PDF gratuite comparée",
"metaDescription": "Comparez Dociva et iLovePDF pour la compression PDF. Fonctionnalités, limites et prix côte à côte.",
"heading": "Dociva vs iLovePDF",
"subtitle": "Les deux outils compressent les PDF, mais Dociva offre une utilisation gratuite illimitée sans inscription ni publicité.",
"advantagesTitle": "Pourquoi choisir Dociva plutôt qu'iLovePDF",
"advantages": [
"Vraiment illimité — aucun quota quotidien",
"Aucun compte requis — commencez immédiatement",
"Zéro publicité, zéro popup, interface épurée",
"Fichiers supprimés automatiquement après 30 minutes",
"Accès API inclus dans le plan Pro"
],
"verdict": "Pour les utilisateurs qui ont besoin d'un compresseur PDF fiable et gratuit sans limites quotidiennes ni mur d'inscription, Dociva est le choix évident.",
"faqs": [
{ "q": "Dociva est-il vraiment gratuit pour la compression PDF ?", "a": "Oui. Le compresseur PDF de Dociva est gratuit sans limites quotidiennes ni compte requis." },
{ "q": "iLovePDF limite-t-il les utilisateurs gratuits ?", "a": "Oui. Le niveau gratuit d'iLovePDF a des limites quotidiennes et affiche des publicités." },
{ "q": "Quel outil compresse le plus ?", "a": "Les deux offrent des taux de compression comparables. Dociva propose trois niveaux pour équilibrer qualité et taille." }
]
},
"mergePdfVsSmallpdf": {
"title": "Dociva vs Smallpdf — Fusion PDF gratuite comparée",
"metaDescription": "Comparez Dociva et Smallpdf pour la fusion de PDF. Fonctionnalités, limites et prix côte à côte.",
"heading": "Dociva vs Smallpdf",
"subtitle": "Les deux plateformes fusionnent les PDF en ligne, mais Dociva offre des fusions illimitées sans inscription.",
"advantagesTitle": "Pourquoi choisir Dociva plutôt que Smallpdf",
"advantages": [
"Fusions PDF illimitées — aucun quota quotidien",
"Aucun compte requis pour fusionner",
"Interface propre sans publicité",
"Réorganisation par glisser-déposer",
"Fichiers supprimés après traitement"
],
"verdict": "Smallpdf est élégant mais verrouille la plupart des fonctionnalités derrière un paywall après deux tâches gratuites par jour. Dociva offre la même fonctionnalité sans limites.",
"faqs": [
{ "q": "Puis-je fusionner des PDF gratuitement avec Dociva ?", "a": "Oui. L'outil de fusion de Dociva est entièrement gratuit sans limites quotidiennes." },
{ "q": "Smallpdf limite-t-il les fusions gratuites ?", "a": "Oui. Smallpdf ne permet que deux tâches gratuites par jour." },
{ "q": "Puis-je réorganiser les pages avant la fusion ?", "a": "Oui. Les deux outils supportent la réorganisation par glisser-déposer." }
]
},
"pdfToWordVsAdobeAcrobat": {
"title": "Dociva vs Adobe Acrobat — Conversion PDF vers Word comparée",
"metaDescription": "Comparez Dociva et Adobe Acrobat pour la conversion PDF vers Word. Outil gratuit en ligne vs logiciel premium.",
"heading": "Dociva vs Adobe Acrobat",
"subtitle": "Adobe Acrobat est la référence du secteur, mais Dociva offre la même conversion PDF vers Word gratuitement, en ligne.",
"advantagesTitle": "Pourquoi choisir Dociva plutôt qu'Adobe Acrobat",
"advantages": [
"100% gratuit — aucun abonnement",
"Aucun téléchargement ni installation",
"Fonctionne sur tout appareil avec un navigateur",
"Aucun identifiant Adobe requis",
"Conversion rapide avec formatage préservé"
],
"verdict": "Adobe Acrobat offre la suite PDF la plus puissante, mais à un prix élevé. Pour une conversion PDF vers Word directe, Dociva égale la qualité gratuitement.",
"faqs": [
{ "q": "La qualité de conversion de Dociva est-elle aussi bonne qu'Adobe ?", "a": "Pour les documents standard, Dociva préserve le formatage de manière comparable au convertisseur en ligne d'Adobe." },
{ "q": "Adobe Acrobat nécessite-t-il un abonnement ?", "a": "Oui. Les fonctionnalités complètes nécessitent un abonnement Acrobat Pro." },
{ "q": "Puis-je convertir de gros PDF ?", "a": "Dociva supporte les fichiers jusqu'à 20 Mo. Adobe Acrobat supporte des fichiers plus volumineux via son application." }
]
},
"compressImageVsTinypng": {
"title": "Dociva vs TinyPNG — Compression d'images gratuite comparée",
"metaDescription": "Comparez Dociva et TinyPNG pour la compression d'images. Formats, limites et qualité.",
"heading": "Dociva vs TinyPNG",
"subtitle": "TinyPNG est un compresseur d'images populaire, mais Dociva supporte plus de formats sans limites de fichiers.",
"advantagesTitle": "Pourquoi choisir Dociva plutôt que TinyPNG",
"advantages": [
"Compressions illimitées — aucun quota quotidien",
"Supporte JPEG, PNG, WebP, GIF et plus",
"Curseur de qualité pour des résultats précis",
"Upload et traitement par lots",
"Aucune restriction sur le nombre de fichiers"
],
"verdict": "TinyPNG excelle dans l'optimisation PNG/JPEG avec une interface simple, mais limite les utilisateurs gratuits à 20 images par lot. Dociva supprime ces limites.",
"faqs": [
{ "q": "TinyPNG a-t-il des limites ?", "a": "Oui. L'outil gratuit de TinyPNG limite à 20 images par lot et 5 Mo par fichier." },
{ "q": "Quels formats Dociva supporte-t-il ?", "a": "Dociva compresse JPEG, PNG, WebP, GIF, SVG et d'autres formats." },
{ "q": "Puis-je contrôler la qualité de compression ?", "a": "Oui. Dociva propose un curseur de qualité pour équilibrer taille et qualité visuelle." }
]
},
"ocrVsAdobeScan": {
"title": "Dociva OCR vs Adobe Scan — Reconnaissance de texte comparée",
"metaDescription": "Comparez Dociva OCR et Adobe Scan pour la reconnaissance de texte. Navigateur vs application mobile.",
"heading": "Dociva OCR vs Adobe Scan",
"subtitle": "Adobe Scan est une application mobile de numérisation, tandis que Dociva OCR fonctionne directement dans votre navigateur.",
"advantagesTitle": "Pourquoi choisir Dociva OCR plutôt qu'Adobe Scan",
"advantages": [
"Fonctionne dans tout navigateur — aucune installation",
"Traitement OCR illimité",
"Aucun compte Adobe requis",
"Traitement par lots de plusieurs fichiers",
"Reconnaissance multilingue (anglais, arabe, français)"
],
"verdict": "Adobe Scan est une excellente application de numérisation mobile avec support hors ligne. Mais pour l'OCR web avec traitement par lots et sans connexion, Dociva est plus rapide et flexible.",
"faqs": [
{ "q": "Dociva OCR fonctionne-t-il hors ligne ?", "a": "Non. Dociva OCR nécessite une connexion Internet pour traiter les fichiers sur des serveurs sécurisés." },
{ "q": "Adobe Scan est-il gratuit ?", "a": "Les fonctionnalités de base sont gratuites, mais l'OCR avancé nécessite un abonnement Acrobat." },
{ "q": "Quelles langues Dociva OCR supporte-t-il ?", "a": "Dociva OCR supporte la reconnaissance en anglais, arabe et français." }
]
}
},
"developers": {
"metaDescription": "Explorez le portail développeur Dociva, le flux API asynchrone et les endpoints prêts pour l'automatisation documentaire.",
"badge": "Portail développeur",

View File

@@ -0,0 +1,278 @@
import { useParams, Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { Helmet } from 'react-helmet-async';
import { CheckCircle, XCircle, MinusCircle, ArrowRight, Swords, Trophy, ExternalLink } from 'lucide-react';
import { getComparisonPage, getComparisonPagesByTool, type ComparisonFeature } from '@/config/comparisonData';
import { getToolSEO } from '@/config/seoData';
import { getSiteOrigin, buildSocialImageUrl, getOgLocale, generateWebPage, generateFAQ } from '@/utils/seo';
function FeatureIcon({ value }: { value: boolean | 'partial' }) {
if (value === true) return <CheckCircle className="h-5 w-5 text-green-500" />;
if (value === 'partial') return <MinusCircle className="h-5 w-5 text-amber-500" />;
return <XCircle className="h-5 w-5 text-red-400" />;
}
function FeatureLabel({ value }: { value: boolean | 'partial' }) {
if (value === true) return <span className="sr-only">Yes</span>;
if (value === 'partial') return <span className="sr-only">Partial</span>;
return <span className="sr-only">No</span>;
}
export default function ComparisonPage() {
const { slug } = useParams<{ slug: string }>();
const { t, i18n } = useTranslation();
const comparison = slug ? getComparisonPage(slug) : undefined;
if (!comparison) {
return (
<div className="mx-auto max-w-3xl py-16 text-center">
<h1 className="text-2xl font-bold text-slate-900 dark:text-white">
{t('pages.comparison.notFound')}
</h1>
<Link to="/tools" className="mt-4 inline-block text-primary-600 hover:underline">
{t('pages.comparison.browseTools')}
</Link>
</div>
);
}
const ourTool = getToolSEO(comparison.ourToolSlug);
const origin = getSiteOrigin(typeof window !== 'undefined' ? window.location.origin : '');
const canonicalUrl = `${origin}/compare/${comparison.slug}`;
const socialImageUrl = buildSocialImageUrl(origin);
const currentOgLocale = getOgLocale(i18n.language);
const prefix = `pages.comparison.${comparison.i18nKey}`;
const title = t(`${prefix}.title`);
const metaDescription = t(`${prefix}.metaDescription`);
const faqItems = t(`${prefix}.faqs`, { returnObjects: true }) as Array<{ q: string; a: string }>;
const faqs = Array.isArray(faqItems) ? faqItems : [];
const webPageSchema = generateWebPage({
name: title,
description: metaDescription,
url: canonicalUrl,
});
const faqSchema = faqs.length > 0
? generateFAQ(faqs.map((f) => ({ question: f.q, answer: f.a })))
: null;
// Related comparisons
const relatedComparisons = comparison.relatedComparisonSlugs
.map((s) => getComparisonPage(s))
.filter(Boolean);
// Related tools
const relatedTools = comparison.relatedToolSlugs
.map((s) => getToolSEO(s))
.filter(Boolean);
return (
<>
<Helmet>
<title>{title} | {t('common.appName')}</title>
<meta name="description" content={metaDescription} />
<meta name="robots" content="index,follow,max-image-preview:large,max-snippet:-1" />
<link rel="canonical" href={canonicalUrl} />
<meta property="og:title" content={title} />
<meta property="og:description" content={metaDescription} />
<meta property="og:url" content={canonicalUrl} />
<meta property="og:type" content="website" />
<meta property="og:image" content={socialImageUrl} />
<meta property="og:locale" content={currentOgLocale} />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={title} />
<meta name="twitter:description" content={metaDescription} />
<script type="application/ld+json">{JSON.stringify(webPageSchema)}</script>
{faqSchema && (
<script type="application/ld+json">{JSON.stringify(faqSchema)}</script>
)}
</Helmet>
<div className="mx-auto max-w-4xl px-4">
{/* Hero */}
<section className="mb-12 text-center">
<span className="mb-4 inline-flex items-center gap-2 rounded-full bg-primary-50 px-4 py-1.5 text-sm font-semibold text-primary-700 dark:bg-primary-900/30 dark:text-primary-300">
<Swords className="h-4 w-4" />
{t('pages.comparison.badge')}
</span>
<h1 className="mt-4 text-3xl font-extrabold text-slate-900 dark:text-white sm:text-4xl">
{t(`${prefix}.heading`)}
</h1>
<p className="mx-auto mt-4 max-w-2xl text-lg text-slate-600 dark:text-slate-400">
{t(`${prefix}.subtitle`)}
</p>
</section>
{/* Feature Comparison Table */}
<section className="mb-12">
<h2 className="mb-6 text-xl font-bold text-slate-900 dark:text-white">
{t('pages.comparison.featureComparison')}
</h2>
<div className="overflow-hidden rounded-2xl border border-slate-200 dark:border-slate-700">
{/* Table header */}
<div className="grid grid-cols-3 bg-slate-100 px-4 py-3 dark:bg-slate-800">
<div className="text-sm font-semibold text-slate-600 dark:text-slate-300">
{t('pages.comparison.feature')}
</div>
<div className="text-center text-sm font-semibold text-primary-700 dark:text-primary-400">
{t('common.appName')}
</div>
<div className="text-center text-sm font-semibold text-slate-600 dark:text-slate-300">
{comparison.competitorName}
</div>
</div>
{/* Feature rows */}
{comparison.features.map((feature: ComparisonFeature, idx: number) => (
<div
key={feature.key}
className={`grid grid-cols-3 items-center px-4 py-3 ${
idx % 2 === 0
? 'bg-white dark:bg-slate-900'
: 'bg-slate-50 dark:bg-slate-800/50'
}`}
>
<div className="text-sm text-slate-700 dark:text-slate-300">
{t(`pages.comparison.features.${feature.key}`)}
</div>
<div className="flex items-center justify-center">
<FeatureIcon value={feature.us} />
<FeatureLabel value={feature.us} />
</div>
<div className="flex items-center justify-center">
<FeatureIcon value={feature.competitor} />
<FeatureLabel value={feature.competitor} />
</div>
</div>
))}
</div>
</section>
{/* Our Advantages */}
<section className="mb-12">
<h2 className="mb-4 text-xl font-bold text-slate-900 dark:text-white">
<Trophy className="mr-2 inline-block h-5 w-5 text-amber-500" />
{t(`${prefix}.advantagesTitle`)}
</h2>
<div className="rounded-2xl border border-green-200 bg-green-50 p-6 dark:border-green-800/40 dark:bg-green-900/10">
{(() => {
const advantages = t(`${prefix}.advantages`, { returnObjects: true }) as string[];
return Array.isArray(advantages) ? (
<ul className="space-y-3">
{advantages.map((adv, idx) => (
<li key={idx} className="flex items-start gap-3">
<CheckCircle className="mt-0.5 h-5 w-5 shrink-0 text-green-600 dark:text-green-400" />
<span className="text-slate-700 dark:text-slate-300">{adv}</span>
</li>
))}
</ul>
) : null;
})()}
</div>
</section>
{/* Verdict */}
<section className="mb-12">
<h2 className="mb-4 text-xl font-bold text-slate-900 dark:text-white">
{t('pages.comparison.verdictTitle')}
</h2>
<div className="rounded-2xl border border-primary-200 bg-primary-50 p-6 dark:border-primary-800/40 dark:bg-primary-900/10">
<p className="text-slate-700 dark:text-slate-300">
{t(`${prefix}.verdict`)}
</p>
</div>
</section>
{/* CTA */}
<section className="mb-12 text-center">
<Link
to={`/tools/${comparison.ourToolSlug}`}
className="inline-flex items-center gap-2 rounded-xl bg-primary-600 px-8 py-3 text-lg font-semibold text-white shadow-md transition-all hover:bg-primary-700 hover:shadow-lg dark:bg-primary-500 dark:hover:bg-primary-600"
>
{ourTool ? t(`tools.${ourTool.i18nKey}.title`) : comparison.ourToolSlug}
<ArrowRight className="h-5 w-5" />
</Link>
<p className="mt-3 text-sm text-slate-500 dark:text-slate-400">
{t('pages.comparison.ctaSubtext')}
</p>
</section>
{/* FAQ */}
{faqs.length > 0 && (
<section className="mb-12">
<h2 className="mb-6 text-xl font-bold text-slate-900 dark:text-white">
{t('pages.comparison.faqTitle')}
</h2>
<div className="space-y-4">
{faqs.map((faq, idx) => (
<details
key={idx}
className="group rounded-xl border border-slate-200 bg-white p-4 dark:border-slate-700 dark:bg-slate-800"
>
<summary className="cursor-pointer text-sm font-semibold text-slate-800 dark:text-slate-200">
{faq.q}
</summary>
<p className="mt-3 text-sm text-slate-600 dark:text-slate-400">
{faq.a}
</p>
</details>
))}
</div>
</section>
)}
{/* Related Comparisons */}
{relatedComparisons.length > 0 && (
<section className="mb-12">
<h2 className="mb-4 text-xl font-bold text-slate-900 dark:text-white">
{t('pages.comparison.relatedComparisons')}
</h2>
<div className="grid gap-4 sm:grid-cols-2">
{relatedComparisons.map((comp) => (
<Link
key={comp!.slug}
to={`/compare/${comp!.slug}`}
className="group flex items-center gap-3 rounded-xl border border-slate-200 bg-white p-4 transition-all hover:border-primary-300 hover:shadow-md dark:border-slate-700 dark:bg-slate-800 dark:hover:border-primary-600"
>
<ExternalLink className="h-4 w-4 shrink-0 text-slate-400 group-hover:text-primary-500" />
<span className="text-sm font-medium text-slate-700 group-hover:text-primary-600 dark:text-slate-300 dark:group-hover:text-primary-400">
{t(`pages.comparison.${comp!.i18nKey}.heading`)}
</span>
</Link>
))}
</div>
</section>
)}
{/* Related Tools */}
{relatedTools.length > 0 && (
<section className="mb-12">
<h2 className="mb-4 text-xl font-bold text-slate-900 dark:text-white">
{t('pages.comparison.relatedTools')}
</h2>
<div className="grid gap-3 sm:grid-cols-2">
{relatedTools.map((tool) => (
<Link
key={tool!.slug}
to={`/tools/${tool!.slug}`}
className="group rounded-xl border border-slate-200 bg-white p-4 transition-all hover:border-primary-300 hover:shadow-md dark:border-slate-700 dark:bg-slate-800 dark:hover:border-primary-600"
>
<h3 className="font-semibold text-slate-800 group-hover:text-primary-600 dark:text-slate-200 dark:group-hover:text-primary-400">
{t(`tools.${tool!.i18nKey}.title`)}
</h3>
<p className="mt-1 text-sm text-slate-500 dark:text-slate-400 line-clamp-2">
{t(`tools.${tool!.i18nKey}.shortDesc`)}
</p>
</Link>
))}
</div>
</section>
)}
</div>
</>
);
}