import { apiI18n } from '../i18n/api-i18n'; /** * The document shell shared by every server-rendered public page (#56's * page view, #82's legal pages): identical for every viewer, crawler-safe, * no session-dependent content. Chrome (footer links) renders in the * instance default locale (ADR 0012). `bodyHtml` must already be safe — * it comes from the sanitizing editor renderers, never from raw input. */ export interface HtmlShellOptions { lang: 'de' | 'en'; /** Plain text; escaped here. */ title: string; canonical?: string; /** Atom feed of the surrounding pond (issue #149), advertised to readers. */ feedUrl?: string; bodyHtml: string; } export function htmlDocument({ lang, title, canonical, feedUrl, bodyHtml, }: HtmlShellOptions): string { const canonicalTag = canonical ? `\n` : ''; const feedTag = feedUrl ? `\n` : ''; const imprintLabel = escapeHtml(apiI18n.t('legal:links.imprint', { lng: lang })); const privacyLabel = escapeHtml(apiI18n.t('legal:links.privacy', { lng: lang })); return ` ${escapeHtml(title)}${canonicalTag}${feedTag}
${bodyHtml}
`; } /** Minimal HTML escaping for the values interpolated into the shell (not the * already-sanitized body HTML). */ export function escapeHtml(value: string): string { return value .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"'); }