diff --git a/.env.example b/.env.example index c53637e..018d569 100644 --- a/.env.example +++ b/.env.example @@ -2,7 +2,7 @@ FLASK_ENV=production FLASK_DEBUG=0 SECRET_KEY=replace-with-a-long-random-secret-key -INTERNAL_ADMIN_EMAILS=admin@dociva.io +INTERNAL_ADMIN_EMAILS=support@dociva.io # Site Domain (used in sitemap, robots.txt, emails) SITE_DOMAIN=https://dociva.io diff --git a/frontend/index.html b/frontend/index.html index 4d2e5b1..6ff05ac 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -7,12 +7,13 @@ + + + - - + - \ No newline at end of file + diff --git a/frontend/package.json b/frontend/package.json index a69e911..17503f1 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -6,7 +6,7 @@ "scripts": { "dev": "vite", "prebuild": "node scripts/merge-keywords.mjs && node scripts/generate-seo-assets.mjs", - "build": "tsc --noEmit && vite build", + "build": "tsc --noEmit && vite build && node scripts/render-seo-shells.mjs", "preview": "vite preview", "lint": "eslint .", "test": "vitest run", diff --git a/frontend/public/sitemap.xml b/frontend/public/sitemap.xml index 2eea23f..b80c3c2 100644 --- a/frontend/public/sitemap.xml +++ b/frontend/public/sitemap.xml @@ -1,1473 +1,19 @@ - - - https://dociva.io/ + + + https://dociva.io/sitemaps/static.xml 2026-03-29 - daily - 1.0 - - - https://dociva.io/about + + + https://dociva.io/sitemaps/blog.xml 2026-03-29 - monthly - 0.4 - - - https://dociva.io/contact + + + https://dociva.io/sitemaps/tools.xml 2026-03-29 - monthly - 0.4 - - - https://dociva.io/privacy + + + https://dociva.io/sitemaps/seo.xml 2026-03-29 - yearly - 0.3 - - - https://dociva.io/terms - 2026-03-29 - yearly - 0.3 - - - https://dociva.io/pricing - 2026-03-29 - monthly - 0.7 - - - https://dociva.io/blog - 2026-03-29 - weekly - 0.6 - - - https://dociva.io/developers - 2026-03-29 - monthly - 0.5 - - - https://dociva.io/blog/how-to-compress-pdf-online - 2026-03-29 - monthly - 0.6 - - - https://dociva.io/blog/convert-images-without-losing-quality - 2026-03-29 - monthly - 0.6 - - - https://dociva.io/blog/ocr-extract-text-from-images - 2026-03-29 - monthly - 0.6 - - - https://dociva.io/blog/merge-split-pdf-files - 2026-03-29 - monthly - 0.6 - - - https://dociva.io/blog/ai-chat-with-pdf-documents - 2026-03-29 - monthly - 0.6 - - - https://dociva.io/tools/pdf-to-word - 2026-03-29 - weekly - 0.9 - - - https://dociva.io/tools/word-to-pdf - 2026-03-29 - weekly - 0.9 - - - https://dociva.io/tools/compress-pdf - 2026-03-29 - weekly - 0.9 - - - https://dociva.io/tools/merge-pdf - 2026-03-29 - weekly - 0.9 - - - https://dociva.io/tools/split-pdf - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/tools/rotate-pdf - 2026-03-29 - weekly - 0.7 - - - https://dociva.io/tools/pdf-to-images - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/tools/images-to-pdf - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/tools/watermark-pdf - 2026-03-29 - weekly - 0.7 - - - https://dociva.io/tools/protect-pdf - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/tools/unlock-pdf - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/tools/page-numbers - 2026-03-29 - weekly - 0.7 - - - https://dociva.io/tools/pdf-editor - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/tools/pdf-flowchart - 2026-03-29 - weekly - 0.7 - - - https://dociva.io/tools/pdf-to-excel - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/tools/remove-watermark-pdf - 2026-03-29 - weekly - 0.7 - - - https://dociva.io/tools/reorder-pdf - 2026-03-29 - weekly - 0.7 - - - https://dociva.io/tools/extract-pages - 2026-03-29 - weekly - 0.7 - - - https://dociva.io/tools/image-converter - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/tools/image-resize - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/tools/compress-image - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/tools/ocr - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/tools/remove-background - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/tools/image-to-svg - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/tools/html-to-pdf - 2026-03-29 - weekly - 0.7 - - - https://dociva.io/tools/chat-pdf - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/tools/summarize-pdf - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/tools/translate-pdf - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/tools/extract-tables - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/tools/qr-code - 2026-03-29 - weekly - 0.7 - - - https://dociva.io/tools/video-to-gif - 2026-03-29 - weekly - 0.7 - - - https://dociva.io/tools/word-counter - 2026-03-29 - weekly - 0.6 - - - https://dociva.io/tools/text-cleaner - 2026-03-29 - weekly - 0.6 - - - https://dociva.io/tools/pdf-to-pptx - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/tools/excel-to-pdf - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/tools/pptx-to-pdf - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/tools/sign-pdf - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/tools/crop-pdf - 2026-03-29 - weekly - 0.7 - - - https://dociva.io/tools/flatten-pdf - 2026-03-29 - weekly - 0.7 - - - https://dociva.io/tools/repair-pdf - 2026-03-29 - weekly - 0.7 - - - https://dociva.io/tools/pdf-metadata - 2026-03-29 - weekly - 0.6 - - - https://dociva.io/tools/image-crop - 2026-03-29 - weekly - 0.7 - - - https://dociva.io/tools/image-rotate-flip - 2026-03-29 - weekly - 0.7 - - - https://dociva.io/tools/barcode-generator - 2026-03-29 - weekly - 0.7 - - - https://dociva.io/pdf-to-word - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/pdf-to-word - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/word-to-pdf - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/word-to-pdf - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/compress-pdf-online - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/compress-pdf-online - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/convert-jpg-to-pdf - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/convert-jpg-to-pdf - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/merge-pdf-files - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/merge-pdf-files - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/remove-pdf-password - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/remove-pdf-password - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/pdf-to-word-editable - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/pdf-to-word-editable - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/convert-pdf-to-text - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/convert-pdf-to-text - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/split-pdf-online - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/split-pdf-online - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/jpg-to-pdf - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/jpg-to-pdf - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/png-to-pdf - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/png-to-pdf - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/images-to-pdf-online - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/images-to-pdf-online - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/pdf-to-jpg - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/pdf-to-jpg - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/pdf-to-png - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/pdf-to-png - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/compress-pdf-for-email - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/compress-pdf-for-email - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/compress-scanned-pdf - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/compress-scanned-pdf - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/merge-pdf-online-free - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/merge-pdf-online-free - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/combine-pdf-files - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/combine-pdf-files - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/extract-pages-from-pdf - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/extract-pages-from-pdf - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/reorder-pdf-pages - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/reorder-pdf-pages - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/rotate-pdf-pages - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/rotate-pdf-pages - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/add-page-numbers-to-pdf - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/add-page-numbers-to-pdf - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/protect-pdf-with-password - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/protect-pdf-with-password - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/unlock-pdf-online - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/unlock-pdf-online - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/watermark-pdf-online - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/watermark-pdf-online - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/remove-watermark-from-pdf - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/remove-watermark-from-pdf - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/edit-pdf-online-free - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/edit-pdf-online-free - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/pdf-to-excel-online - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/pdf-to-excel-online - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/extract-tables-from-pdf - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/extract-tables-from-pdf - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/html-to-pdf-online - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/html-to-pdf-online - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/scan-pdf-to-text - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/scan-pdf-to-text - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/chat-with-pdf - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/chat-with-pdf - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/summarize-pdf-online - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/summarize-pdf-online - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/translate-pdf-online - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/translate-pdf-online - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/convert-image-to-pdf - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/convert-image-to-pdf - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/convert-webp-to-jpg - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/convert-webp-to-jpg - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/resize-image-online - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/resize-image-online - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/compress-image-online - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/compress-image-online - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/remove-image-background - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/remove-image-background - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/pdf-to-word-editable-free - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/pdf-to-word-editable-free - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/compress-pdf-to-100kb - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/compress-pdf-to-100kb - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/ai-extract-text-from-pdf - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/ai-extract-text-from-pdf - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/pdf-to-excel-accurate-free - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/pdf-to-excel-accurate-free - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/split-pdf-online-free - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/split-pdf-online-free - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/compress-pdf-online-free - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/compress-pdf-online-free - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/unlock-pdf-online-free - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/unlock-pdf-online-free - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/summarize-pdf-ai - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/summarize-pdf-ai - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/convert-pdf-to-text-ai - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/convert-pdf-to-text-ai - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/pdf-to-jpg-high-quality - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/pdf-to-jpg-high-quality - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/jpg-to-pdf-online-free - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/jpg-to-pdf-online-free - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/reduce-pdf-size-for-email - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/reduce-pdf-size-for-email - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/ocr-for-scanned-pdfs - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/ocr-for-scanned-pdfs - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/remove-watermark-from-pdf-online - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/remove-watermark-from-pdf-online - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/add-watermark-to-pdf-online - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/add-watermark-to-pdf-online - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/repair-corrupted-pdf-online - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/repair-corrupted-pdf-online - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/rotate-pdf-pages-online - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/rotate-pdf-pages-online - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/reorder-pdf-pages-online - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/reorder-pdf-pages-online - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/pdf-to-png-online - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/pdf-to-png-online - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/images-to-pdf-multiple - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/images-to-pdf-multiple - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/split-pdf-by-range-online - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/split-pdf-by-range-online - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/compress-scanned-pdf-online - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/compress-scanned-pdf-online - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/pdf-metadata-editor-online - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/pdf-metadata-editor-online - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/add-page-numbers-to-pdf-online - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/add-page-numbers-to-pdf-online - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/protect-pdf-with-password-online - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/protect-pdf-with-password-online - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/unlock-encrypted-pdf-online - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/unlock-encrypted-pdf-online - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/ocr-table-extraction-from-pdf - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/ocr-table-extraction-from-pdf - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/pdf-to-excel-converter-online - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/pdf-to-excel-converter-online - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/extract-text-from-protected-pdf - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/extract-text-from-protected-pdf - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/bulk-convert-pdf-to-word - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/bulk-convert-pdf-to-word - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/compress-pdf-for-web-upload - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/compress-pdf-for-web-upload - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/ocr-multi-language-pdf - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/ocr-multi-language-pdf - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/summarize-long-pdf-ai - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/summarize-long-pdf-ai - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/convert-pdf-to-ppt-online - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/convert-pdf-to-ppt-online - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/pdf-to-pptx-free-online - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/pdf-to-pptx-free-online - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/دمج-ملفات-pdf-مجاناً - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/دمج-ملفات-pdf-مجاناً - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/ضغط-بي-دي-اف-اونلاين - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/ضغط-بي-دي-اف-اونلاين - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/تحويل-pdf-الى-word-قابل-للتعديل - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/تحويل-pdf-الى-word-قابل-للتعديل - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/تحويل-jpg-الى-pdf-اونلاين - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/تحويل-jpg-الى-pdf-اونلاين - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/فصل-صفحات-pdf-اونلاين - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/فصل-صفحات-pdf-اونلاين - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/ازالة-كلمة-مرور-من-pdf - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/ازالة-كلمة-مرور-من-pdf - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/تحويل-pdf-الى-نص-باستخدام-ocr - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/تحويل-pdf-الى-نص-باستخدام-ocr - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/تحويل-pdf-الى-excel-اونلاين - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/تحويل-pdf-الى-excel-اونلاين - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/تحويل-pdf-الى-صور - 2026-03-29 - weekly - 0.88 - - - https://dociva.io/ar/تحويل-pdf-الى-صور - 2026-03-29 - weekly - 0.8 - - - https://dociva.io/best-pdf-tools - 2026-03-29 - weekly - 0.82 - - - https://dociva.io/ar/best-pdf-tools - 2026-03-29 - weekly - 0.74 - - - https://dociva.io/free-pdf-tools-online - 2026-03-29 - weekly - 0.82 - - - https://dociva.io/ar/free-pdf-tools-online - 2026-03-29 - weekly - 0.74 - - - https://dociva.io/convert-files-online - 2026-03-29 - weekly - 0.82 - - - https://dociva.io/ar/convert-files-online - 2026-03-29 - weekly - 0.74 - - - https://dociva.io/pdf-converter-tools - 2026-03-29 - weekly - 0.82 - - - https://dociva.io/ar/pdf-converter-tools - 2026-03-29 - weekly - 0.74 - - - https://dociva.io/secure-pdf-tools - 2026-03-29 - weekly - 0.82 - - - https://dociva.io/ar/secure-pdf-tools - 2026-03-29 - weekly - 0.74 - - - https://dociva.io/ai-document-tools - 2026-03-29 - weekly - 0.82 - - - https://dociva.io/ar/ai-document-tools - 2026-03-29 - weekly - 0.74 - - - https://dociva.io/image-to-pdf-tools - 2026-03-29 - weekly - 0.82 - - - https://dociva.io/ar/image-to-pdf-tools - 2026-03-29 - weekly - 0.74 - - - https://dociva.io/online-image-tools - 2026-03-29 - weekly - 0.82 - - - https://dociva.io/ar/online-image-tools - 2026-03-29 - weekly - 0.74 - - - https://dociva.io/office-to-pdf-tools - 2026-03-29 - weekly - 0.82 - - - https://dociva.io/ar/office-to-pdf-tools - 2026-03-29 - weekly - 0.74 - - - https://dociva.io/scanned-document-tools - 2026-03-29 - weekly - 0.82 - - - https://dociva.io/ar/scanned-document-tools - 2026-03-29 - weekly - 0.74 - - - https://dociva.io/arabic-pdf-tools - 2026-03-29 - weekly - 0.82 - - - https://dociva.io/ar/arabic-pdf-tools - 2026-03-29 - weekly - 0.74 - - + + diff --git a/frontend/public/sitemaps/blog.xml b/frontend/public/sitemaps/blog.xml new file mode 100644 index 0000000..e80f10a --- /dev/null +++ b/frontend/public/sitemaps/blog.xml @@ -0,0 +1,33 @@ + + + + https://dociva.io/blog/how-to-compress-pdf-online + 2026-03-29 + monthly + 0.6 + + + https://dociva.io/blog/convert-images-without-losing-quality + 2026-03-29 + monthly + 0.6 + + + https://dociva.io/blog/ocr-extract-text-from-images + 2026-03-29 + monthly + 0.6 + + + https://dociva.io/blog/merge-split-pdf-files + 2026-03-29 + monthly + 0.6 + + + https://dociva.io/blog/ai-chat-with-pdf-documents + 2026-03-29 + monthly + 0.6 + + diff --git a/frontend/public/sitemaps/seo.xml b/frontend/public/sitemaps/seo.xml new file mode 100644 index 0000000..87ce702 --- /dev/null +++ b/frontend/public/sitemaps/seo.xml @@ -0,0 +1,1131 @@ + + + + https://dociva.io/pdf-to-word + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/pdf-to-word + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/word-to-pdf + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/word-to-pdf + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/compress-pdf-online + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/compress-pdf-online + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/convert-jpg-to-pdf + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/convert-jpg-to-pdf + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/merge-pdf-files + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/merge-pdf-files + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/remove-pdf-password + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/remove-pdf-password + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/pdf-to-word-editable + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/pdf-to-word-editable + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/convert-pdf-to-text + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/convert-pdf-to-text + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/split-pdf-online + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/split-pdf-online + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/jpg-to-pdf + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/jpg-to-pdf + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/png-to-pdf + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/png-to-pdf + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/images-to-pdf-online + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/images-to-pdf-online + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/pdf-to-jpg + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/pdf-to-jpg + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/pdf-to-png + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/pdf-to-png + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/compress-pdf-for-email + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/compress-pdf-for-email + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/compress-scanned-pdf + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/compress-scanned-pdf + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/merge-pdf-online-free + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/merge-pdf-online-free + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/combine-pdf-files + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/combine-pdf-files + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/extract-pages-from-pdf + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/extract-pages-from-pdf + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/reorder-pdf-pages + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/reorder-pdf-pages + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/rotate-pdf-pages + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/rotate-pdf-pages + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/add-page-numbers-to-pdf + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/add-page-numbers-to-pdf + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/protect-pdf-with-password + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/protect-pdf-with-password + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/unlock-pdf-online + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/unlock-pdf-online + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/watermark-pdf-online + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/watermark-pdf-online + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/remove-watermark-from-pdf + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/remove-watermark-from-pdf + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/edit-pdf-online-free + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/edit-pdf-online-free + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/pdf-to-excel-online + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/pdf-to-excel-online + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/extract-tables-from-pdf + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/extract-tables-from-pdf + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/html-to-pdf-online + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/html-to-pdf-online + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/scan-pdf-to-text + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/scan-pdf-to-text + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/chat-with-pdf + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/chat-with-pdf + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/summarize-pdf-online + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/summarize-pdf-online + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/translate-pdf-online + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/translate-pdf-online + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/convert-image-to-pdf + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/convert-image-to-pdf + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/convert-webp-to-jpg + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/convert-webp-to-jpg + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/resize-image-online + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/resize-image-online + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/compress-image-online + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/compress-image-online + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/remove-image-background + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/remove-image-background + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/pdf-to-word-editable-free + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/pdf-to-word-editable-free + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/compress-pdf-to-100kb + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/compress-pdf-to-100kb + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/ai-extract-text-from-pdf + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/ai-extract-text-from-pdf + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/pdf-to-excel-accurate-free + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/pdf-to-excel-accurate-free + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/split-pdf-online-free + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/split-pdf-online-free + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/compress-pdf-online-free + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/compress-pdf-online-free + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/unlock-pdf-online-free + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/unlock-pdf-online-free + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/summarize-pdf-ai + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/summarize-pdf-ai + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/convert-pdf-to-text-ai + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/convert-pdf-to-text-ai + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/pdf-to-jpg-high-quality + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/pdf-to-jpg-high-quality + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/jpg-to-pdf-online-free + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/jpg-to-pdf-online-free + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/reduce-pdf-size-for-email + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/reduce-pdf-size-for-email + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/ocr-for-scanned-pdfs + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/ocr-for-scanned-pdfs + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/remove-watermark-from-pdf-online + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/remove-watermark-from-pdf-online + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/add-watermark-to-pdf-online + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/add-watermark-to-pdf-online + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/repair-corrupted-pdf-online + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/repair-corrupted-pdf-online + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/rotate-pdf-pages-online + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/rotate-pdf-pages-online + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/reorder-pdf-pages-online + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/reorder-pdf-pages-online + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/pdf-to-png-online + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/pdf-to-png-online + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/images-to-pdf-multiple + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/images-to-pdf-multiple + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/split-pdf-by-range-online + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/split-pdf-by-range-online + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/compress-scanned-pdf-online + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/compress-scanned-pdf-online + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/pdf-metadata-editor-online + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/pdf-metadata-editor-online + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/add-page-numbers-to-pdf-online + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/add-page-numbers-to-pdf-online + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/protect-pdf-with-password-online + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/protect-pdf-with-password-online + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/unlock-encrypted-pdf-online + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/unlock-encrypted-pdf-online + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/ocr-table-extraction-from-pdf + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/ocr-table-extraction-from-pdf + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/pdf-to-excel-converter-online + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/pdf-to-excel-converter-online + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/extract-text-from-protected-pdf + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/extract-text-from-protected-pdf + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/bulk-convert-pdf-to-word + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/bulk-convert-pdf-to-word + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/compress-pdf-for-web-upload + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/compress-pdf-for-web-upload + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/ocr-multi-language-pdf + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/ocr-multi-language-pdf + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/summarize-long-pdf-ai + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/summarize-long-pdf-ai + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/convert-pdf-to-ppt-online + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/convert-pdf-to-ppt-online + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/pdf-to-pptx-free-online + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/pdf-to-pptx-free-online + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/دمج-ملفات-pdf-مجاناً + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/دمج-ملفات-pdf-مجاناً + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/ضغط-بي-دي-اف-اونلاين + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/ضغط-بي-دي-اف-اونلاين + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/تحويل-pdf-الى-word-قابل-للتعديل + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/تحويل-pdf-الى-word-قابل-للتعديل + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/تحويل-jpg-الى-pdf-اونلاين + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/تحويل-jpg-الى-pdf-اونلاين + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/فصل-صفحات-pdf-اونلاين + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/فصل-صفحات-pdf-اونلاين + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/ازالة-كلمة-مرور-من-pdf + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/ازالة-كلمة-مرور-من-pdf + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/تحويل-pdf-الى-نص-باستخدام-ocr + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/تحويل-pdf-الى-نص-باستخدام-ocr + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/تحويل-pdf-الى-excel-اونلاين + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/تحويل-pdf-الى-excel-اونلاين + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/تحويل-pdf-الى-صور + 2026-03-29 + weekly + 0.88 + + + https://dociva.io/ar/تحويل-pdf-الى-صور + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/best-pdf-tools + 2026-03-29 + weekly + 0.82 + + + https://dociva.io/ar/best-pdf-tools + 2026-03-29 + weekly + 0.74 + + + https://dociva.io/free-pdf-tools-online + 2026-03-29 + weekly + 0.82 + + + https://dociva.io/ar/free-pdf-tools-online + 2026-03-29 + weekly + 0.74 + + + https://dociva.io/convert-files-online + 2026-03-29 + weekly + 0.82 + + + https://dociva.io/ar/convert-files-online + 2026-03-29 + weekly + 0.74 + + + https://dociva.io/pdf-converter-tools + 2026-03-29 + weekly + 0.82 + + + https://dociva.io/ar/pdf-converter-tools + 2026-03-29 + weekly + 0.74 + + + https://dociva.io/secure-pdf-tools + 2026-03-29 + weekly + 0.82 + + + https://dociva.io/ar/secure-pdf-tools + 2026-03-29 + weekly + 0.74 + + + https://dociva.io/ai-document-tools + 2026-03-29 + weekly + 0.82 + + + https://dociva.io/ar/ai-document-tools + 2026-03-29 + weekly + 0.74 + + + https://dociva.io/image-to-pdf-tools + 2026-03-29 + weekly + 0.82 + + + https://dociva.io/ar/image-to-pdf-tools + 2026-03-29 + weekly + 0.74 + + + https://dociva.io/online-image-tools + 2026-03-29 + weekly + 0.82 + + + https://dociva.io/ar/online-image-tools + 2026-03-29 + weekly + 0.74 + + + https://dociva.io/office-to-pdf-tools + 2026-03-29 + weekly + 0.82 + + + https://dociva.io/ar/office-to-pdf-tools + 2026-03-29 + weekly + 0.74 + + + https://dociva.io/scanned-document-tools + 2026-03-29 + weekly + 0.82 + + + https://dociva.io/ar/scanned-document-tools + 2026-03-29 + weekly + 0.74 + + + https://dociva.io/arabic-pdf-tools + 2026-03-29 + weekly + 0.82 + + + https://dociva.io/ar/arabic-pdf-tools + 2026-03-29 + weekly + 0.74 + + diff --git a/frontend/public/sitemaps/static.xml b/frontend/public/sitemaps/static.xml new file mode 100644 index 0000000..b8e0d48 --- /dev/null +++ b/frontend/public/sitemaps/static.xml @@ -0,0 +1,57 @@ + + + + https://dociva.io/ + 2026-03-29 + daily + 1.0 + + + https://dociva.io/tools + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/about + 2026-03-29 + monthly + 0.4 + + + https://dociva.io/contact + 2026-03-29 + monthly + 0.4 + + + https://dociva.io/privacy + 2026-03-29 + yearly + 0.3 + + + https://dociva.io/terms + 2026-03-29 + yearly + 0.3 + + + https://dociva.io/pricing + 2026-03-29 + monthly + 0.7 + + + https://dociva.io/blog + 2026-03-29 + weekly + 0.6 + + + https://dociva.io/developers + 2026-03-29 + monthly + 0.5 + + diff --git a/frontend/public/sitemaps/tools.xml b/frontend/public/sitemaps/tools.xml new file mode 100644 index 0000000..b0c77e0 --- /dev/null +++ b/frontend/public/sitemaps/tools.xml @@ -0,0 +1,267 @@ + + + + https://dociva.io/tools/pdf-to-word + 2026-03-29 + weekly + 0.9 + + + https://dociva.io/tools/word-to-pdf + 2026-03-29 + weekly + 0.9 + + + https://dociva.io/tools/compress-pdf + 2026-03-29 + weekly + 0.9 + + + https://dociva.io/tools/merge-pdf + 2026-03-29 + weekly + 0.9 + + + https://dociva.io/tools/split-pdf + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/tools/rotate-pdf + 2026-03-29 + weekly + 0.7 + + + https://dociva.io/tools/pdf-to-images + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/tools/images-to-pdf + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/tools/watermark-pdf + 2026-03-29 + weekly + 0.7 + + + https://dociva.io/tools/protect-pdf + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/tools/unlock-pdf + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/tools/page-numbers + 2026-03-29 + weekly + 0.7 + + + https://dociva.io/tools/pdf-editor + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/tools/pdf-flowchart + 2026-03-29 + weekly + 0.7 + + + https://dociva.io/tools/pdf-to-excel + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/tools/remove-watermark-pdf + 2026-03-29 + weekly + 0.7 + + + https://dociva.io/tools/reorder-pdf + 2026-03-29 + weekly + 0.7 + + + https://dociva.io/tools/extract-pages + 2026-03-29 + weekly + 0.7 + + + https://dociva.io/tools/image-converter + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/tools/image-resize + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/tools/compress-image + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/tools/ocr + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/tools/remove-background + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/tools/image-to-svg + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/tools/html-to-pdf + 2026-03-29 + weekly + 0.7 + + + https://dociva.io/tools/chat-pdf + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/tools/summarize-pdf + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/tools/translate-pdf + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/tools/extract-tables + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/tools/qr-code + 2026-03-29 + weekly + 0.7 + + + https://dociva.io/tools/video-to-gif + 2026-03-29 + weekly + 0.7 + + + https://dociva.io/tools/word-counter + 2026-03-29 + weekly + 0.6 + + + https://dociva.io/tools/text-cleaner + 2026-03-29 + weekly + 0.6 + + + https://dociva.io/tools/pdf-to-pptx + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/tools/excel-to-pdf + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/tools/pptx-to-pdf + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/tools/sign-pdf + 2026-03-29 + weekly + 0.8 + + + https://dociva.io/tools/crop-pdf + 2026-03-29 + weekly + 0.7 + + + https://dociva.io/tools/flatten-pdf + 2026-03-29 + weekly + 0.7 + + + https://dociva.io/tools/repair-pdf + 2026-03-29 + weekly + 0.7 + + + https://dociva.io/tools/pdf-metadata + 2026-03-29 + weekly + 0.6 + + + https://dociva.io/tools/image-crop + 2026-03-29 + weekly + 0.7 + + + https://dociva.io/tools/image-rotate-flip + 2026-03-29 + weekly + 0.7 + + + https://dociva.io/tools/barcode-generator + 2026-03-29 + weekly + 0.7 + + diff --git a/frontend/scripts/generate-seo-assets.mjs b/frontend/scripts/generate-seo-assets.mjs index 92deb37..c86a615 100644 --- a/frontend/scripts/generate-seo-assets.mjs +++ b/frontend/scripts/generate-seo-assets.mjs @@ -1,4 +1,4 @@ -import { readFile, writeFile } from 'node:fs/promises'; +import { mkdir, readFile, writeFile } from 'node:fs/promises'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; @@ -6,6 +6,7 @@ const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const frontendRoot = path.resolve(__dirname, '..'); const publicDir = path.join(frontendRoot, 'public'); +const sitemapDir = path.join(publicDir, 'sitemaps'); const siteOrigin = String(process.env.VITE_SITE_DOMAIN || 'https://dociva.io').trim().replace(/\/$/, ''); const today = new Date().toISOString().slice(0, 10); @@ -26,6 +27,7 @@ const routeRegistrySource = await readFile(path.join(frontendRoot, 'src', 'confi const staticPages = [ { path: '/', changefreq: 'daily', priority: '1.0' }, + { path: '/tools', changefreq: 'weekly', priority: '0.8' }, { path: '/about', changefreq: 'monthly', priority: '0.4' }, { path: '/contact', changefreq: 'monthly', priority: '0.4' }, { path: '/privacy', changefreq: 'yearly', priority: '0.3' }, @@ -94,6 +96,18 @@ function makeUrlTag({ loc, changefreq, priority }) { return ` \n ${loc}\n ${today}\n ${changefreq}\n ${priority}\n `; } +function makeSitemapUrlSet(entries) { + return `\n\n${entries.join('\n')}\n\n`; +} + +function makeSitemapIndex(entries) { + const items = entries + .map((entry) => ` \n ${entry.loc}\n ${today}\n `) + .join('\n'); + + return `\n\n${items}\n\n`; +} + function dedupeEntries(entries) { const seen = new Set(); return entries.filter((entry) => { @@ -110,22 +124,33 @@ const blogSource = await readFile(path.join(frontendRoot, 'src', 'content', 'blo const blogSlugs = extractBlogSlugs(blogSource); const toolSlugs = extractToolSlugs(routeRegistrySource); -const sitemapEntries = dedupeEntries([ - ...staticPages.map((page) => ({ +await mkdir(sitemapDir, { recursive: true }); + +const staticEntries = dedupeEntries( + staticPages.map((page) => ({ loc: `${siteOrigin}${page.path}`, changefreq: page.changefreq, priority: page.priority, })), - ...blogSlugs.map((slug) => ({ +).map((entry) => makeUrlTag(entry)); + +const blogEntries = dedupeEntries( + blogSlugs.map((slug) => ({ loc: `${siteOrigin}/blog/${slug}`, changefreq: 'monthly', priority: '0.6', })), - ...toolSlugs.map((slug) => ({ +).map((entry) => makeUrlTag(entry)); + +const toolEntries = dedupeEntries( + toolSlugs.map((slug) => ({ loc: `${siteOrigin}/tools/${slug}`, changefreq: 'weekly', priority: toolRoutePriorities.get(slug) || '0.6', })), +).map((entry) => makeUrlTag(entry)); + +const seoEntries = dedupeEntries([ ...seoConfig.toolPageSeeds.flatMap((page) => ([ { loc: `${siteOrigin}/${page.slug}`, changefreq: 'weekly', priority: '0.88' }, { loc: `${siteOrigin}/ar/${page.slug}`, changefreq: 'weekly', priority: '0.8' }, @@ -136,7 +161,12 @@ const sitemapEntries = dedupeEntries([ ])), ]).map((entry) => makeUrlTag(entry)); -const sitemap = `\n\n${sitemapEntries.join('\n')}\n\n`; +const sitemapIndex = makeSitemapIndex([ + { loc: `${siteOrigin}/sitemaps/static.xml` }, + { loc: `${siteOrigin}/sitemaps/blog.xml` }, + { loc: `${siteOrigin}/sitemaps/tools.xml` }, + { loc: `${siteOrigin}/sitemaps/seo.xml` }, +]); const robots = [ '# robots.txt — Dociva', @@ -156,7 +186,11 @@ const robots = [ '', ].join('\n'); -await writeFile(path.join(publicDir, 'sitemap.xml'), sitemap, 'utf8'); +await writeFile(path.join(publicDir, 'sitemap.xml'), sitemapIndex, 'utf8'); +await writeFile(path.join(sitemapDir, 'static.xml'), makeSitemapUrlSet(staticEntries), 'utf8'); +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(publicDir, 'robots.txt'), robots, 'utf8'); -console.log(`Generated SEO assets for ${siteOrigin}`); \ No newline at end of file +console.log(`Generated SEO assets for ${siteOrigin}`); diff --git a/frontend/scripts/render-seo-shells.mjs b/frontend/scripts/render-seo-shells.mjs new file mode 100644 index 0000000..c6674d0 --- /dev/null +++ b/frontend/scripts/render-seo-shells.mjs @@ -0,0 +1,166 @@ +import { mkdir, readFile, writeFile } from 'node:fs/promises'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const frontendRoot = path.resolve(__dirname, '..'); +const distRoot = path.join(frontendRoot, 'dist'); +const siteOrigin = String(process.env.VITE_SITE_DOMAIN || 'https://dociva.io').trim().replace(/\/$/, ''); +const seoPagesPath = path.join(frontendRoot, 'src', 'config', 'seo-tools.json'); +const seoToolsPath = path.join(frontendRoot, 'src', 'config', 'seoData.ts'); +const blogPath = path.join(frontendRoot, 'src', 'content', 'blogArticles.ts'); + +const baseHtml = await readFile(path.join(distRoot, 'index.html'), 'utf8'); +const seoPages = JSON.parse(await readFile(seoPagesPath, 'utf8')); +const seoToolsSource = await readFile(seoToolsPath, 'utf8'); +const blogSource = await readFile(blogPath, 'utf8'); + +const defaultTitle = 'Dociva — Free Online File Tools'; +const defaultDescription = 'Free online tools for PDF, image, video, and text processing. Merge, split, compress, convert, watermark, protect & more — instantly.'; + +function escapeHtml(value) { + return value + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); +} + +function extractToolMetadata(source) { + const entries = new Map(); + const pattern = /i18nKey:\s*'([^']+)'[\s\S]*?slug:\s*'([^']+)'[\s\S]*?titleSuffix:\s*'([^']+)'[\s\S]*?metaDescription:\s*'([^']+)'/g; + + for (const match of source.matchAll(pattern)) { + const [, i18nKey, slug, titleSuffix, metaDescription] = match; + entries.set(slug, { + i18nKey, + title: `${titleSuffix} | Dociva`, + description: metaDescription, + }); + } + + return entries; +} + +function extractBlogEntries(source) { + const entries = []; + const pattern = /slug:\s*'([^']+)'[\s\S]*?title:\s*\{[\s\S]*?en:\s*'([^']+)'[\s\S]*?seoDescription:\s*\{[\s\S]*?en:\s*'([^']+)'/g; + + for (const match of source.matchAll(pattern)) { + const [, slug, title, description] = match; + entries.push({ slug, title: `${title} — Dociva`, description }); + } + + return entries; +} + +function interpolate(template, values) { + return template.replace(/{{\s*([a-zA-Z0-9_]+)\s*}}/g, (_, key) => values[key] ?? ''); +} + +function withMeta(html, { title, description, url }) { + const safeTitle = escapeHtml(title); + const safeDescription = escapeHtml(description); + const safeUrl = escapeHtml(url); + + let result = html + .replace(/.*?<\/title>/, `<title>${safeTitle}`) + .replace(//, ``) + .replace(//, ``) + .replace(//, ``) + .replace(//, ``) + .replace(//, ``); + + result = result.replace( + '', + ` \n \n`, + ); + + return result; +} + +async function writeRouteShell(routePath, title, description) { + const normalizedPath = routePath === '/' ? '' : routePath.replace(/^\//, ''); + const targetDir = normalizedPath ? path.join(distRoot, normalizedPath) : distRoot; + const html = withMeta(baseHtml, { + title, + description, + url: `${siteOrigin}${routePath}`, + }); + + await mkdir(targetDir, { recursive: true }); + await writeFile(path.join(targetDir, 'index.html'), html, 'utf8'); +} + +const staticPages = [ + { path: '/', title: defaultTitle, description: defaultDescription }, + { path: '/tools', title: 'All Tools — Dociva', description: 'Browse every Dociva tool in one place. Explore PDF, image, AI, conversion, and utility workflows from a single search-friendly directory.' }, + { path: '/about', title: 'About Dociva', description: 'Learn about Dociva — free, fast, and secure online file tools for PDFs, images, video, and text. No registration required.' }, + { path: '/contact', title: 'Contact Dociva', description: 'Contact the Dociva team. Report bugs, request features, or send us a message.' }, + { path: '/privacy', title: 'Privacy Policy — Dociva', description: 'Privacy policy for Dociva. Learn how we handle your files and data with full transparency.' }, + { path: '/terms', title: 'Terms of Service — Dociva', description: 'Terms of service for Dociva. Understand the rules and guidelines for using our free online tools.' }, + { path: '/pricing', title: 'Pricing — Dociva', description: 'Compare free and pro plans for Dociva. Access 30+ tools for free, or upgrade for unlimited processing.' }, + { path: '/blog', title: 'Blog — Tips, Tutorials & Updates', description: 'Learn how to compress, convert, edit, and manage PDF files with our expert guides and tutorials.' }, + { path: '/developers', title: 'Developers — Dociva', description: 'Explore the Dociva developer portal, async API flow, and production-ready endpoints for document automation.' }, +]; + +for (const page of staticPages) { + await writeRouteShell(page.path, page.title, page.description); +} + +for (const blog of extractBlogEntries(blogSource)) { + await writeRouteShell(`/blog/${blog.slug}`, blog.title, blog.description); +} + +for (const [slug, tool] of extractToolMetadata(seoToolsSource)) { + await writeRouteShell(`/tools/${slug}`, tool.title, tool.description); +} + +for (const page of seoPages.toolPages) { + const englishTitle = interpolate(page.titleTemplate.en, { + brand: 'Dociva', + focusKeyword: page.focusKeyword.en, + }); + const arabicTitle = interpolate(page.titleTemplate.ar, { + brand: 'Dociva', + focusKeyword: page.focusKeyword.ar, + }); + + const englishDescription = interpolate(page.descriptionTemplate.en, { + brand: 'Dociva', + focusKeyword: page.focusKeyword.en, + }); + const arabicDescription = interpolate(page.descriptionTemplate.ar, { + brand: 'Dociva', + focusKeyword: page.focusKeyword.ar, + }); + + await writeRouteShell(`/${page.slug}`, `${englishTitle} — Dociva`, englishDescription); + await writeRouteShell(`/ar/${page.slug}`, `${arabicTitle} — Dociva`, arabicDescription); +} + +for (const page of seoPages.collectionPages) { + const englishTitle = interpolate(page.titleTemplate.en, { + brand: 'Dociva', + focusKeyword: page.focusKeyword.en, + }); + const arabicTitle = interpolate(page.titleTemplate.ar, { + brand: 'Dociva', + focusKeyword: page.focusKeyword.ar, + }); + + const englishDescription = interpolate(page.descriptionTemplate.en, { + brand: 'Dociva', + focusKeyword: page.focusKeyword.en, + }); + const arabicDescription = interpolate(page.descriptionTemplate.ar, { + brand: 'Dociva', + focusKeyword: page.focusKeyword.ar, + }); + + await writeRouteShell(`/${page.slug}`, `${englishTitle} — Dociva`, englishDescription); + await writeRouteShell(`/ar/${page.slug}`, `${arabicTitle} — Dociva`, arabicDescription); +} + +console.log('Rendered route-specific SEO shells.'); diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index d008eaf..2745264 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -24,6 +24,7 @@ const PricingPage = lazy(() => import('@/pages/PricingPage')); const BlogPage = lazy(() => import('@/pages/BlogPage')); const BlogPostPage = lazy(() => import('@/pages/BlogPostPage')); 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 CookieConsent = lazy(() => import('@/components/layout/CookieConsent')); @@ -129,6 +130,7 @@ export default function App() { } /> } /> } /> + } /> } /> } /> } /> diff --git a/frontend/src/components/layout/Footer.tsx b/frontend/src/components/layout/Footer.tsx index 4ec5e7c..11f56b0 100644 --- a/frontend/src/components/layout/Footer.tsx +++ b/frontend/src/components/layout/Footer.tsx @@ -92,6 +92,12 @@ export default function Footer() { > {t('common.terms')} + + {t('common.allTools')} + alternate.hrefLang === 'en')?.href ?? canonicalUrl; const schemas = jsonLd ? (Array.isArray(jsonLd) ? jsonLd : [jsonLd]) : []; @@ -45,7 +43,8 @@ export default function SEOHead({ title, description, keywords, path, type = 'we {fullTitle} - {keywords ? : null} + + {languageAlternates.map((alternate) => ( + {languageAlternates diff --git a/frontend/src/components/seo/ToolLandingPage.tsx b/frontend/src/components/seo/ToolLandingPage.tsx index ecae8c6..57bd929 100644 --- a/frontend/src/components/seo/ToolLandingPage.tsx +++ b/frontend/src/components/seo/ToolLandingPage.tsx @@ -2,7 +2,7 @@ import { Helmet } from 'react-helmet-async'; import { useTranslation } from 'react-i18next'; import { CheckCircle } from 'lucide-react'; import { getToolSEO } from '@/config/seoData'; -import { buildLanguageAlternates, buildSocialImageUrl, generateToolSchema, generateBreadcrumbs, generateFAQ, generateHowTo, getOgLocale, getSiteOrigin } from '@/utils/seo'; +import { buildSocialImageUrl, generateToolSchema, generateBreadcrumbs, generateFAQ, generateHowTo, getOgLocale, getSiteOrigin } from '@/utils/seo'; import BreadcrumbNav from './BreadcrumbNav'; import FAQSection from './FAQSection'; import RelatedTools from './RelatedTools'; @@ -43,7 +43,6 @@ export default function ToolLandingPage({ slug, children }: ToolLandingPageProps const path = `/tools/${slug}`; const canonicalUrl = `${origin}${path}`; const socialImageUrl = buildSocialImageUrl(origin); - const languageAlternates = buildLanguageAlternates(origin, path); const currentOgLocale = getOgLocale(i18n.language); const toolSchema = generateToolSchema({ @@ -77,18 +76,8 @@ export default function ToolLandingPage({ slug, children }: ToolLandingPageProps {toolTitle} — {seo.titleSuffix} | {t('common.appName')} - - {languageAlternates.map((alternate) => ( - - ))} - {/* Open Graph */} @@ -98,11 +87,6 @@ export default function ToolLandingPage({ slug, children }: ToolLandingPageProps - {languageAlternates - .filter((alternate) => alternate.ogLocale !== currentOgLocale) - .map((alternate) => ( - - ))} {/* Twitter */} diff --git a/frontend/src/config/routes.ts b/frontend/src/config/routes.ts index c35fad9..b774deb 100644 --- a/frontend/src/config/routes.ts +++ b/frontend/src/config/routes.ts @@ -21,6 +21,7 @@ const STATIC_PAGE_ROUTES = [ '/blog', '/blog/:slug', '/developers', + '/tools', '/internal/admin', ] as const; diff --git a/frontend/src/i18n/ar.json b/frontend/src/i18n/ar.json index 74d94a7..a60e3c5 100644 --- a/frontend/src/i18n/ar.json +++ b/frontend/src/i18n/ar.json @@ -256,6 +256,19 @@ "contactTitle": "8. الاتصال", "contactText": "أسئلة حول هذه الشروط؟ تواصل معنا على" }, + "toolsHub": { + "metaTitle": "كل الأدوات", + "metaDescription": "تصفح جميع أدوات Dociva في مكان واحد. استكشف مسارات PDF والصور والذكاء الاصطناعي والتحويل والأدوات المساعدة من دليل واحد سهل للأرشفة والزحف.", + "title": "جميع أدوات Dociva", + "description": "استخدم هذا الدليل لاستكشاف كل مسارات Dociva حسب الفئة ثم انتقل مباشرة إلى الأداة التي تحتاجها.", + "categories": { + "PDF": "أدوات PDF", + "Convert": "أدوات التحويل", + "Image": "أدوات الصور", + "AI": "أدوات الذكاء الاصطناعي", + "Utility": "أدوات مساعدة" + } + }, "cookie": { "title": "إعدادات ملفات الارتباط", "message": "نستخدم ملفات الارتباط لتحسين تجربتك وتحليل حركة الموقع. بالموافقة، فإنك توافق على ملفات الارتباط التحليلية.", diff --git a/frontend/src/i18n/en.json b/frontend/src/i18n/en.json index ba03ae4..475e303 100644 --- a/frontend/src/i18n/en.json +++ b/frontend/src/i18n/en.json @@ -256,6 +256,19 @@ "contactTitle": "8. Contact", "contactText": "Questions about these terms? Contact us at" }, + "toolsHub": { + "metaTitle": "All Tools", + "metaDescription": "Browse every Dociva tool in one place. Explore PDF, image, AI, conversion, and utility workflows from a single search-friendly directory.", + "title": "All Dociva Tools", + "description": "Use this directory to explore every Dociva workflow by category and jump directly to the tool you need.", + "categories": { + "PDF": "PDF Tools", + "Convert": "Convert Tools", + "Image": "Image Tools", + "AI": "AI Tools", + "Utility": "Utility Tools" + } + }, "cookie": { "title": "Cookie Settings", "message": "We use cookies to improve your experience and analyze site traffic. By accepting, you consent to analytics cookies.", diff --git a/frontend/src/i18n/fr.json b/frontend/src/i18n/fr.json index 24f1dc9..27bc679 100644 --- a/frontend/src/i18n/fr.json +++ b/frontend/src/i18n/fr.json @@ -256,6 +256,19 @@ "contactTitle": "8. Contact", "contactText": "Des questions sur ces conditions ? Contactez-nous à" }, + "toolsHub": { + "metaTitle": "Tous les outils", + "metaDescription": "Parcourez tous les outils Dociva depuis une seule page. Explorez les workflows PDF, image, IA, conversion et utilitaires dans un répertoire clair et optimisé pour la découverte.", + "title": "Tous les outils Dociva", + "description": "Utilisez ce répertoire pour parcourir chaque workflow Dociva par catégorie et ouvrir directement l'outil dont vous avez besoin.", + "categories": { + "PDF": "Outils PDF", + "Convert": "Outils de conversion", + "Image": "Outils d'image", + "AI": "Outils IA", + "Utility": "Outils utilitaires" + } + }, "cookie": { "title": "Paramètres des cookies", "message": "Nous utilisons des cookies pour améliorer votre expérience et analyser le trafic du site. En acceptant, vous consentez aux cookies analytiques.", diff --git a/frontend/src/pages/AllToolsPage.tsx b/frontend/src/pages/AllToolsPage.tsx new file mode 100644 index 0000000..97f0b3b --- /dev/null +++ b/frontend/src/pages/AllToolsPage.tsx @@ -0,0 +1,99 @@ +import { Link } from 'react-router-dom'; +import { useTranslation } from 'react-i18next'; +import SEOHead from '@/components/seo/SEOHead'; +import BreadcrumbNav from '@/components/seo/BreadcrumbNav'; +import { TOOLS_SEO } from '@/config/seoData'; +import { generateBreadcrumbs, generateCollectionPage, generateItemList, getSiteOrigin } from '@/utils/seo'; + +const CATEGORY_ORDER = ['PDF', 'Convert', 'Image', 'AI', 'Utility'] as const; + +export default function AllToolsPage() { + const { t } = useTranslation(); + const origin = getSiteOrigin(typeof window !== 'undefined' ? window.location.origin : ''); + const path = '/tools'; + const url = `${origin}${path}`; + + const groupedTools = CATEGORY_ORDER.map((category) => ({ + category, + items: TOOLS_SEO.filter((tool) => tool.category === category), + })).filter((group) => group.items.length > 0); + + const jsonLd = [ + generateCollectionPage({ + name: t('pages.toolsHub.metaTitle'), + description: t('pages.toolsHub.metaDescription'), + url, + }), + generateBreadcrumbs([ + { name: t('common.home'), url: origin }, + { name: t('common.allTools'), url }, + ]), + generateItemList( + TOOLS_SEO.map((tool) => ({ + name: t(`tools.${tool.i18nKey}.title`), + url: `${origin}/tools/${tool.slug}`, + })), + ), + ]; + + return ( + <> + + +
+
+ + +

+ {t('pages.toolsHub.title')} +

+

+ {t('pages.toolsHub.description')} +

+
+ + {groupedTools.map((group) => ( +
+

+ {t(`pages.toolsHub.categories.${group.category}`)} +

+ +
+ {group.items.map((tool) => ( + +

+ {group.category} +

+

+ {t(`tools.${tool.i18nKey}.title`)} +

+

+ {t(`tools.${tool.i18nKey}.shortDesc`)} +

+ + ))} +
+
+ ))} +
+ + ); +} diff --git a/frontend/src/pages/BlogPage.tsx b/frontend/src/pages/BlogPage.tsx index a098367..5c7ba99 100644 --- a/frontend/src/pages/BlogPage.tsx +++ b/frontend/src/pages/BlogPage.tsx @@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next'; import { Link } from 'react-router-dom'; import { useSearchParams } from 'react-router-dom'; import SEOHead from '@/components/seo/SEOHead'; -import { generateWebPage, getSiteOrigin } from '@/utils/seo'; +import { generateCollectionPage, generateItemList, getSiteOrigin } from '@/utils/seo'; import { BookOpen, Calendar, ArrowRight, Search, X } from 'lucide-react'; import { BLOG_ARTICLES, @@ -44,11 +44,17 @@ export default function BlogPage() { title={t('pages.blog.metaTitle')} description={t('pages.blog.metaDescription')} path="/blog" - jsonLd={generateWebPage({ - name: t('pages.blog.metaTitle'), - description: t('pages.blog.metaDescription'), - url: `${siteOrigin}/blog`, - })} + jsonLd={[ + generateCollectionPage({ + name: t('pages.blog.metaTitle'), + description: t('pages.blog.metaDescription'), + url: `${siteOrigin}/blog`, + }), + generateItemList(posts.map((post) => ({ + name: post.title, + url: `${siteOrigin}/blog/${post.slug}`, + }))), + ]} />
diff --git a/frontend/src/pages/HomePage.tsx b/frontend/src/pages/HomePage.tsx index e850299..1bfc329 100644 --- a/frontend/src/pages/HomePage.tsx +++ b/frontend/src/pages/HomePage.tsx @@ -2,7 +2,7 @@ import { useDeferredValue } from 'react'; import { useTranslation } from 'react-i18next'; import { useSearchParams } from 'react-router-dom'; import SEOHead from '@/components/seo/SEOHead'; -import { generateOrganization, getSiteOrigin } from '@/utils/seo'; +import { generateOrganization, generateWebSite, getSiteOrigin } from '@/utils/seo'; import { FileText, FileOutput, @@ -121,18 +121,10 @@ export default function HomePage() { description={t('home.heroSub')} path="/" jsonLd={[ - { - '@context': 'https://schema.org', - '@type': 'WebSite', - name: t('common.appName'), - url: siteOrigin, + generateWebSite({ + origin: siteOrigin, description: t('home.heroSub'), - potentialAction: { - '@type': 'SearchAction', - target: `${siteOrigin}/?q={search_term_string}`, - 'query-input': 'required name=search_term_string', - }, - }, + }), generateOrganization(siteOrigin), ]} /> diff --git a/frontend/src/pages/SeoCollectionPage.tsx b/frontend/src/pages/SeoCollectionPage.tsx index b4073d4..bbc88f2 100644 --- a/frontend/src/pages/SeoCollectionPage.tsx +++ b/frontend/src/pages/SeoCollectionPage.tsx @@ -6,13 +6,19 @@ import SEOHead from '@/components/seo/SEOHead'; import FAQSection from '@/components/seo/FAQSection'; import { getLocalizedText, - getLocalizedTextList, getSeoCollectionPage, interpolateTemplate, normalizeSeoLocale, } from '@/config/seoPages'; import { getToolSEO } from '@/config/seoData'; -import { generateBreadcrumbs, generateFAQ, generateWebPage, getSiteOrigin } from '@/utils/seo'; +import { + generateBreadcrumbs, + generateCollectionPage, + generateFAQ, + generateItemList, + generateWebPage, + getSiteOrigin, +} from '@/utils/seo'; import NotFoundPage from '@/pages/NotFoundPage'; interface SeoCollectionPageProps { @@ -64,7 +70,6 @@ export default function SeoCollectionPage({ slug }: SeoCollectionPageProps) { const title = interpolateTemplate(getLocalizedText(page.titleTemplate, locale), tokens); const description = interpolateTemplate(getLocalizedText(page.descriptionTemplate, locale), tokens); const intro = interpolateTemplate(getLocalizedText(page.introTemplate, locale), tokens); - const keywords = [focusKeyword, ...getLocalizedTextList(page.supportingKeywords, locale)].join(', '); const siteOrigin = getSiteOrigin(typeof window !== 'undefined' ? window.location.origin : ''); const faqItems = page.faqTemplates.map((item) => ({ question: getLocalizedText(item.question, locale), @@ -82,6 +87,11 @@ export default function SeoCollectionPage({ slug }: SeoCollectionPageProps) { ]; const jsonLd = [ + generateCollectionPage({ + name: title, + description, + url, + }), generateWebPage({ name: title, description, @@ -93,11 +103,18 @@ export default function SeoCollectionPage({ slug }: SeoCollectionPageProps) { { name: title, url }, ]), generateFAQ(faqItems), + generateItemList(page.targetToolSlugs.map((toolSlug) => { + const tool = getToolSEO(toolSlug); + return { + name: tool ? t(`tools.${tool.i18nKey}.title`) : toolSlug, + url: `${siteOrigin}/tools/${toolSlug}`, + }; + })), ]; return ( <> - +
@@ -227,4 +244,4 @@ export default function SeoCollectionPage({ slug }: SeoCollectionPageProps) {
); -} \ No newline at end of file +} diff --git a/frontend/src/pages/SeoPage.tsx b/frontend/src/pages/SeoPage.tsx index 1411125..a626820 100644 --- a/frontend/src/pages/SeoPage.tsx +++ b/frontend/src/pages/SeoPage.tsx @@ -8,7 +8,6 @@ import RelatedTools from '@/components/seo/RelatedTools'; import SuggestedTools from '@/components/seo/SuggestedTools'; import { getLocalizedText, - getLocalizedTextList, getProgrammaticToolPage, getSeoCollectionPage, interpolateTemplate, @@ -19,6 +18,7 @@ import { generateBreadcrumbs, generateFAQ, generateHowTo, + generateItemList, generateToolSchema, generateWebPage, getSiteOrigin, @@ -82,7 +82,6 @@ export default function SeoPage({ slug }: SeoPageProps) { const useCases = t(`seo.${tool.i18nKey}.useCases`, { returnObjects: true }) as string[]; const focusKeyword = getLocalizedText(page.focusKeyword, locale); - const keywords = [focusKeyword, ...getLocalizedTextList(page.supportingKeywords, locale)].join(', '); const tokens = { brand: 'Dociva', focusKeyword, @@ -139,11 +138,25 @@ export default function SeoPage({ slug }: SeoPageProps) { url, }), generateFAQ(faqItems), + generateItemList(page.relatedCollectionSlugs.map((collectionSlug) => { + const collection = getSeoCollectionPage(collectionSlug); + const collectionTitle = collection + ? interpolateTemplate(getLocalizedText(collection.titleTemplate, locale), { + brand: 'Dociva', + focusKeyword: getLocalizedText(collection.focusKeyword, locale), + }) + : collectionSlug; + + return { + name: collectionTitle, + url: `${siteOrigin}${localizedCollectionPath(collectionSlug)}`, + }; + })), ]; return ( <> - +
@@ -312,4 +325,4 @@ export default function SeoPage({ slug }: SeoPageProps) {
); -} \ No newline at end of file +} diff --git a/frontend/src/utils/seo.ts b/frontend/src/utils/seo.ts index 72e8a29..21ffcea 100644 --- a/frontend/src/utils/seo.ts +++ b/frontend/src/utils/seo.ts @@ -9,6 +9,7 @@ export interface ToolSeoData { category?: string; ratingValue?: number; ratingCount?: number; + features?: string[]; } export interface LanguageAlternate { @@ -19,6 +20,7 @@ export interface LanguageAlternate { const DEFAULT_SOCIAL_IMAGE_PATH = '/social-preview.svg'; const DEFAULT_SITE_ORIGIN = 'https://dociva.io'; +const DEFAULT_SITE_NAME = 'Dociva'; const LANGUAGE_CONFIG: Record<'en' | 'ar' | 'fr', { hrefLang: string; ogLocale: string }> = { en: { hrefLang: 'en', ogLocale: 'en_US' }, @@ -35,13 +37,16 @@ export function getOgLocale(language: string): string { return LANGUAGE_CONFIG[normalizeSiteLanguage(language)].ogLocale; } -export function buildLanguageAlternates(origin: string, path: string): LanguageAlternate[] { - const separator = path.includes('?') ? '&' : '?'; - return (Object.entries(LANGUAGE_CONFIG) as Array<[keyof typeof LANGUAGE_CONFIG, (typeof LANGUAGE_CONFIG)[keyof typeof LANGUAGE_CONFIG]]>) - .map(([language, config]) => ({ - hrefLang: config.hrefLang, - href: `${origin}${path}${separator}lng=${language}`, - ogLocale: config.ogLocale, +export function buildLanguageAlternates( + origin: string, + localizedPaths: Partial>, +): LanguageAlternate[] { + return (Object.entries(localizedPaths) as Array<[keyof typeof LANGUAGE_CONFIG, string | undefined]>) + .filter(([, path]) => Boolean(path)) + .map(([language, path]) => ({ + hrefLang: LANGUAGE_CONFIG[language].hrefLang, + href: `${origin}${path}`, + ogLocale: LANGUAGE_CONFIG[language].ogLocale, })); } @@ -68,20 +73,33 @@ export function buildSocialImageUrl(origin: string): string { export function generateToolSchema(tool: ToolSeoData): object { const schema: Record = { '@context': 'https://schema.org', - '@type': 'WebApplication', + '@type': 'SoftwareApplication', name: tool.name, url: tool.url, applicationCategory: tool.category || 'UtilitiesApplication', + applicationSubCategory: tool.category || 'UtilitiesApplication', operatingSystem: 'Any', + browserRequirements: 'Requires JavaScript. Works in modern browsers.', + isAccessibleForFree: true, offers: { '@type': 'Offer', price: '0', priceCurrency: 'USD', + availability: 'https://schema.org/InStock', }, description: tool.description, inLanguage: ['en', 'ar', 'fr'], + provider: { + '@type': 'Organization', + name: DEFAULT_SITE_NAME, + url: getSiteOrigin(), + }, }; + if (tool.features && tool.features.length > 0) { + schema.featureList = tool.features; + } + if (tool.ratingValue && tool.ratingCount && tool.ratingCount > 0) { schema.aggregateRating = { '@type': 'AggregateRating', @@ -161,10 +179,14 @@ export function generateOrganization(origin: string): object { return { '@context': 'https://schema.org', '@type': 'Organization', - name: 'Dociva', + '@id': `${origin}/#organization`, + name: DEFAULT_SITE_NAME, + alternateName: 'Dociva File Tools', url: origin, - logo: `${origin}/favicon.svg`, - sameAs: [], + logo: { + '@type': 'ImageObject', + url: `${origin}/logo.svg`, + }, contactPoint: { '@type': 'ContactPoint', email: 'support@dociva.io', @@ -188,13 +210,68 @@ export function generateWebPage(page: { name: page.name, description: page.description, url: page.url, + inLanguage: ['en', 'ar', 'fr'], isPartOf: { '@type': 'WebSite', - name: 'Dociva', + '@id': `${getSiteOrigin()}/#website`, + name: DEFAULT_SITE_NAME, }, }; } +export function generateWebSite(data: { + origin: string; + description: string; +}): object { + return { + '@context': 'https://schema.org', + '@type': 'WebSite', + '@id': `${data.origin}/#website`, + name: DEFAULT_SITE_NAME, + url: data.origin, + description: data.description, + publisher: { + '@id': `${data.origin}/#organization`, + }, + inLanguage: ['en', 'ar', 'fr'], + potentialAction: { + '@type': 'SearchAction', + target: `${data.origin}/?q={search_term_string}`, + 'query-input': 'required name=search_term_string', + }, + }; +} + +export function generateCollectionPage(data: { + name: string; + description: string; + url: string; +}): object { + return { + '@context': 'https://schema.org', + '@type': 'CollectionPage', + name: data.name, + description: data.description, + url: data.url, + isPartOf: { + '@id': `${getSiteOrigin()}/#website`, + }, + }; +} + +export function generateItemList(items: { name: string; url: string }[]): object { + return { + '@context': 'https://schema.org', + '@type': 'ItemList', + itemListElement: items.map((item, index) => ({ + '@type': 'ListItem', + position: index + 1, + name: item.name, + url: item.url, + })), + }; +} + export function generateBlogPosting(post: { headline: string; description: string; @@ -202,6 +279,7 @@ export function generateBlogPosting(post: { datePublished: string; inLanguage: string; }): object { + const origin = getSiteOrigin(); return { '@context': 'https://schema.org', '@type': 'BlogPosting', @@ -211,14 +289,23 @@ export function generateBlogPosting(post: { datePublished: post.datePublished, dateModified: post.datePublished, inLanguage: post.inLanguage, + isAccessibleForFree: true, author: { '@type': 'Organization', - name: 'Dociva', + name: DEFAULT_SITE_NAME, }, publisher: { '@type': 'Organization', - name: 'Dociva', + '@id': `${origin}/#organization`, + name: DEFAULT_SITE_NAME, + logo: { + '@type': 'ImageObject', + url: `${origin}/logo.svg`, + }, + }, + mainEntityOfPage: { + '@type': 'WebPage', + '@id': post.url, }, - mainEntityOfPage: post.url, }; }