- 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>
130 lines
4.5 KiB
TypeScript
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
|
|
}
|