Files
biblical-guide.com/app/sitemap.ts
Andrei 7e91013c3a feat: implement comprehensive dynamic sitemap with SEO-friendly Bible URLs
- Created dynamic sitemap.ts using Next.js 15 sitemap feature
- Generates 23,188 URLs (within Google's 50K limit)
- Includes all static pages for 4 locales (en, ro, es, it)
- Includes Bible chapters for top 10 versions per language
- Uses SEO-friendly URL format: /{locale}/bible/{version}/{book}/{chapter}
- Replaces static sitemap.xml with dynamic generation
- Configured with force-dynamic and 24-hour revalidation
- Prioritizes relevant Bible versions per locale (ENG-ASV, ENG-KJV, ROO, etc.)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-13 09:04:16 +00:00

130 lines
4.5 KiB
TypeScript

import { MetadataRoute } from 'next'
import { prisma } from '@/lib/db'
export const dynamic = 'force-dynamic'
export const revalidate = 86400 // Revalidate once per day
const BASE_URL = 'https://biblical-guide.com'
const LOCALES = ['en', 'ro', 'es', 'it']
// Map locales to Bible version languages
const LOCALE_TO_LANGUAGE: Record<string, string> = {
'en': 'en',
'ro': 'ro',
'es': 'es',
'it': 'it'
}
// Prioritized versions for each language (to limit sitemap size)
const PRIORITY_VERSIONS: Record<string, string[]> = {
'en': ['ENG-ASV', 'ENG-KJV', 'ENG-WEB', 'ENGKJVCPB', 'ENGEMTV'],
'ro': ['ROO', 'RONDCV', 'ROCOR'],
'es': ['SPAV1602P', 'SPABES', 'SPARVG', 'SPAPDDPT'],
'it': ['ITNRV', 'ITPRV', 'ITCEI']
}
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const urls: MetadataRoute.Sitemap = []
// Static pages for each locale
const staticPages = [
{ path: '', priority: 1.0, changeFrequency: 'daily' as const },
{ path: '/bible', priority: 0.9, changeFrequency: 'weekly' as const },
{ path: '/prayers', priority: 0.8, changeFrequency: 'daily' as const },
{ path: '/search', priority: 0.7, changeFrequency: 'weekly' as const },
{ path: '/contact', priority: 0.6, changeFrequency: 'monthly' as const },
{ path: '/donate', priority: 0.7, changeFrequency: 'monthly' as const },
{ path: '/subscription', priority: 0.8, changeFrequency: 'weekly' as const },
{ path: '/reading-plans', priority: 0.7, changeFrequency: 'weekly' as const },
{ path: '/bookmarks', priority: 0.6, changeFrequency: 'weekly' as const },
{ path: '/settings', priority: 0.5, changeFrequency: 'monthly' as const },
{ path: '/profile', priority: 0.5, changeFrequency: 'monthly' as const },
{ path: '/login', priority: 0.5, changeFrequency: 'monthly' as const },
{ path: '/auth/login', priority: 0.5, changeFrequency: 'monthly' as const },
]
// Add static pages for all locales
for (const locale of LOCALES) {
for (const page of staticPages) {
urls.push({
url: `${BASE_URL}/${locale}${page.path}`,
lastModified: new Date(),
changeFrequency: page.changeFrequency,
priority: page.priority,
})
}
}
try {
// Get priority Bible versions for each language
for (const locale of LOCALES) {
const language = LOCALE_TO_LANGUAGE[locale]
const priorityAbbreviations = PRIORITY_VERSIONS[language] || []
// Get versions for this language (prioritize specific versions, then default, then by language)
const versions = await prisma.bibleVersion.findMany({
where: {
OR: [
{ abbreviation: { in: priorityAbbreviations } },
{ language: language, isDefault: true },
{ language: language }
]
},
select: {
id: true,
abbreviation: true,
isDefault: true,
},
take: 10, // Limit to top 10 versions per language
orderBy: [
{ isDefault: 'desc' },
{ abbreviation: 'asc' }
]
})
console.log(`[Sitemap] Locale ${locale}: Found ${versions.length} relevant Bible versions`)
// For each version, get all books and chapters
for (const version of versions) {
const books = await prisma.bibleBook.findMany({
where: { versionId: version.id },
select: {
id: true,
bookKey: true,
},
orderBy: { orderNum: 'asc' },
})
// Add URLs for each book and chapter
for (const book of books) {
const bookSlug = book.bookKey.toLowerCase()
const versionSlug = version.abbreviation.toLowerCase()
// Get chapters for this book
const chapters = await prisma.bibleChapter.findMany({
where: { bookId: book.id },
select: { chapterNum: true },
orderBy: { chapterNum: 'asc' },
})
// Add URL for each chapter (only for this locale to avoid duplicates)
for (const chapter of chapters) {
urls.push({
url: `${BASE_URL}/${locale}/bible/${versionSlug}/${bookSlug}/${chapter.chapterNum}`,
lastModified: new Date(),
changeFrequency: 'monthly',
priority: version.isDefault ? 0.7 : 0.6,
})
}
}
}
}
console.log(`[Sitemap] Generated ${urls.length} total URLs`)
} catch (error) {
console.error('[Sitemap] Error generating Bible URLs:', error)
}
return urls
}