|
1 | | -const BASE_URL = "https://ruehe.me"; |
| 1 | +export const SITE_URL = "https://ruehe.me"; |
2 | 2 |
|
3 | | -export function bilingualLinks(enPath: string, dePath: string, current: "en" | "de") { |
4 | | - const canonicalPath = current === "en" ? enPath : dePath; |
| 3 | +export type Lang = "en" | "de"; |
| 4 | +export type PagePair = { en: string; de: string }; |
| 5 | + |
| 6 | +/** |
| 7 | + * EN ↔ DE equivalents for every indexable page. Single source of truth: per-page |
| 8 | + * canonical/hreflang links (`bilingualLinks`) and the generated sitemap |
| 9 | + * (`renderSitemap`) both read from this, so a new page added here flows to both. |
| 10 | + */ |
| 11 | +export const PAGES = { |
| 12 | + home: { en: "/", de: "/de" }, |
| 13 | + imprint: { en: "/en/imprint", de: "/de/impressum" }, |
| 14 | +} as const satisfies Record<string, PagePair>; |
| 15 | + |
| 16 | +/** Canonical + hreflang alternate <link>s for one page in one language. */ |
| 17 | +export function bilingualLinks({ en, de }: PagePair, current: Lang) { |
5 | 18 | return [ |
6 | | - { rel: "canonical", href: `${BASE_URL}${canonicalPath}` }, |
7 | | - { rel: "alternate", hrefLang: "en", href: `${BASE_URL}${enPath}` }, |
8 | | - { rel: "alternate", hrefLang: "de", href: `${BASE_URL}${dePath}` }, |
9 | | - { rel: "alternate", hrefLang: "x-default", href: `${BASE_URL}${enPath}` }, |
| 19 | + { rel: "canonical", href: `${SITE_URL}${current === "en" ? en : de}` }, |
| 20 | + { rel: "alternate", hrefLang: "en", href: `${SITE_URL}${en}` }, |
| 21 | + { rel: "alternate", hrefLang: "de", href: `${SITE_URL}${de}` }, |
| 22 | + { rel: "alternate", hrefLang: "x-default", href: `${SITE_URL}${en}` }, |
10 | 23 | ]; |
11 | 24 | } |
| 25 | + |
| 26 | +/** The sitemap.xml document, derived from PAGES. */ |
| 27 | +export function renderSitemap(): string { |
| 28 | + const urls = Object.values(PAGES) |
| 29 | + .flatMap(({ en, de }) => |
| 30 | + [en, de].map( |
| 31 | + (loc) => ` <url> |
| 32 | + <loc>${SITE_URL}${loc}</loc> |
| 33 | + <xhtml:link rel="alternate" hreflang="en" href="${SITE_URL}${en}" /> |
| 34 | + <xhtml:link rel="alternate" hreflang="de" href="${SITE_URL}${de}" /> |
| 35 | + <xhtml:link rel="alternate" hreflang="x-default" href="${SITE_URL}${en}" /> |
| 36 | + </url>`, |
| 37 | + ), |
| 38 | + ) |
| 39 | + .join("\n"); |
| 40 | + |
| 41 | + return `<?xml version="1.0" encoding="UTF-8"?> |
| 42 | +<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" |
| 43 | + xmlns:xhtml="http://www.w3.org/1999/xhtml"> |
| 44 | +${urls} |
| 45 | +</urlset> |
| 46 | +`; |
| 47 | +} |
| 48 | + |
| 49 | +/** Every URL listed in the sitemap (both languages), for build-time drift checks. */ |
| 50 | +export function sitemapPaths(): string[] { |
| 51 | + return Object.values(PAGES).flatMap(({ en, de }) => [en, de]); |
| 52 | +} |
0 commit comments