export const dynamic = "force-dynamic"; import type { Metadata } from "next"; import { notFound } from "next/navigation"; import { getAllGlossaryEntries } from "@/lib/api"; import { buildOgMeta, extractQuoteFromHl } from "@/lib/og"; import { GlossaryClient } from "../glossary-client"; import { JsonLd } from "@/components/seo/json-ld"; import { articleJsonLd } from "@/lib/jsonld"; // --------------------------------------------------------------------------- // Diacritic → base letter mapping // --------------------------------------------------------------------------- const DIACRITIC_TO_BASE: Record = { Ā: "A", Ä: "A", Ī: "I", É: "I", Ū: "U", Ü: "U", Ṛ: "R", Ṝ: "R", Ś: "S", Ç: "S", Ṣ: "S", Ñ: "N", Ṇ: "N", Ṅ: "N", Ṭ: "T", Ḍ: "D", Ḥ: "H", }; function getBaseLetter(term: string): string { if (!term) return "?"; const first = term[0].toUpperCase(); return DIACRITIC_TO_BASE[first] || first; } // --------------------------------------------------------------------------- // Metadata // --------------------------------------------------------------------------- export async function generateMetadata({ params, searchParams, }: { params: Promise<{ letter: string }>; searchParams: Promise>; }): Promise { const { letter } = await params; const sp = await searchParams; const l = letter.toUpperCase(); // If a specific term is referenced via ?term= param, show it in the OG image const termParam = typeof sp.term === "string" ? sp.term : null; if (termParam) { const allEntries = await getAllGlossaryEntries(); const entry = allEntries.find( (e) => e.term.toLowerCase() === termParam.toLowerCase(), ); if (entry) { // Strip HTML tags from definition for OG quote const plainDef = entry.definitionHtml .replace(/<[^>]*>/g, "") .replace(/&[^;]+;/g, " ") .trim(); // Check for ?hl= highlight param — extract actual selected text from definition const hlQuote = extractQuoteFromHl(sp, [entry.definitionHtml]); return buildOgMeta({ title: entry.term, detail: "Glossary — Prabhupāda Says", quote: hlQuote || (plainDef.length > 260 ? plainDef.slice(0, 257) + "..." : plainDef), }); } } // Check for ?hl= without ?term= — try to extract quote from all entries for this letter const hlQuote = extractQuoteFromHl(sp); if (hlQuote) { return buildOgMeta({ title: `Glossary — ${l}`, detail: "Sanskrit & Vedic terms", quote: hlQuote, }); } // Default: letter-level OG return buildOgMeta({ title: `Glossary — ${l}`, detail: `Sanskrit & Vedic terms starting with ${l}`, quote: "A comprehensive glossary of Sanskrit and Vedic terms from the books of Śrīla Prabhupāda.", }); } // --------------------------------------------------------------------------- // Page // --------------------------------------------------------------------------- export default async function GlossaryLetterPage({ params, }: { params: Promise<{ letter: string }>; }) { const { letter: rawLetter } = await params; const activeLetter = rawLetter.toUpperCase(); // Validate — single letter A-Z if (!/^[A-Z]$/.test(activeLetter)) notFound(); // Fetch all entries (cached by Next.js request dedup within a single render) const allEntries = await getAllGlossaryEntries(); // Group all entries by base letter const byLetter: Record = {}; for (const entry of allEntries) { const base = getBaseLetter(entry.term); if (!byLetter[base]) byLetter[base] = []; byLetter[base].push(entry); } // All available letters (sorted) const letters = Object.keys(byLetter).sort(); // Entries for the active letter const letterEntries = byLetter[activeLetter] || []; if (letterEntries.length === 0) notFound(); // Prepare client data — only the active letter's entries const clientEntries = letterEntries.map((e) => ({ term: e.term, definition: e.definition, definition_html: e.definitionHtml, })); // Letter counts for the nav const letterCounts: Record = {}; for (const [l, entries] of Object.entries(byLetter)) { letterCounts[l] = entries.length; } return (
); }