/** * schema.org JSON-LD builders for content pages. * * Emitting structured data gives Google rich-result eligibility and gives * LLMs clean, linked facts (who authored it, which work it belongs to, when). * Every page renders one of these via the component * (components/seo/json-ld.tsx). * * The author entity carries `sameAs` links to Wikipedia/Wikidata so search * engines and LLMs can resolve it to the canonical Śrīla Prabhupāda entity. */ const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL || "https://prabhupadasays.org"; const AUTHOR = { "@type": "Person", name: "His Divine Grace A.C. Bhaktivedanta Swami Prabhupāda", alternateName: "A.C. Bhaktivedanta Swami Prabhupāda", sameAs: [ "https://en.wikipedia.org/wiki/A._C._Bhaktivedanta_Swami_Prabhupada", "https://www.wikidata.org/wiki/Q318439", ], } as const; const PUBLISHER = { "@type": "Organization", name: "Prabhupāda Says", url: SITE_URL, } as const; function abs(path: string): string { return path.startsWith("http") ? path : `${SITE_URL}${path}`; } /** Trim a (possibly HTML) string to a clean plain-text description. */ function clean(text: string | undefined, max = 300): string | undefined { if (!text) return undefined; const plain = text .replace(/<[^>]+>/g, "") .replace(/\s+/g, " ") .trim(); if (!plain) return undefined; return plain.length > max ? plain.slice(0, max - 1).trimEnd() + "…" : plain; } /** * An Article — used for every leaf content page (verses, letters, transcripts, * prose chapters, essays, magazine/journal pages). `isPartOf` names the parent * work (book/magazine); `datePublished` must be ISO 8601 (omit otherwise). */ export function articleJsonLd(params: { path: string; headline: string; description?: string; datePublished?: string; isPartOf?: string; }) { const url = abs(params.path); const obj: Record = { "@context": "https://schema.org", "@type": "Article", headline: params.headline, author: AUTHOR, publisher: PUBLISHER, inLanguage: "en", isAccessibleForFree: true, url, mainEntityOfPage: url, }; const desc = clean(params.description); if (desc) obj.description = desc; if (params.datePublished) obj.datePublished = params.datePublished; if (params.isPartOf) { obj.isPartOf = { "@type": "Book", name: params.isPartOf, author: AUTHOR }; } return obj; } /** A public profile page → ProfilePage wrapping the Person. */ export function profileJsonLd(params: { path: string; name: string; handle: string; image?: string | null; }) { const url = abs(params.path); const person: Record = { "@type": "Person", name: params.name, alternateName: `@${params.handle}`, url, }; if (params.image) person.image = params.image; return { "@context": "https://schema.org", "@type": "ProfilePage", url, mainEntity: person, }; } /** A Book — used for book landing pages. */ export function bookJsonLd(params: { path: string; name: string; description?: string; }) { const obj: Record = { "@context": "https://schema.org", "@type": "Book", name: params.name, author: AUTHOR, publisher: PUBLISHER, inLanguage: "en", url: abs(params.path), }; const desc = clean(params.description); if (desc) obj.description = desc; return obj; }