Update project configuration and enhance translation support. Added caching mechanism for CMS responses with a new script, updated .gitignore to include cache files, and introduced translation handling in various components. Enhanced package.json with new scripts for cache warming and deployment. Added new translation-related components and improved existing ones for better localization support.
This commit is contained in:
+92
-42
@@ -23,6 +23,10 @@ import {
|
||||
SITE_NAME,
|
||||
SOCIAL_IMAGE_TRANSFORM,
|
||||
} from "../lib/constants";
|
||||
import TranslationProvider from "../components/TranslationProvider.svelte";
|
||||
import { t, T } from "../lib/translations";
|
||||
import { getCmsFromCache } from "../lib/cms-cache-state";
|
||||
import type { TranslationKey } from "../lib/translations";
|
||||
import { marked } from "marked";
|
||||
|
||||
marked.setOptions({ gfm: true });
|
||||
@@ -47,8 +51,8 @@ interface Props {
|
||||
showBannerInLayout?: boolean;
|
||||
/** Optional: absoluter oder relativer URL für og:image / twitter:image (z. B. Post-Bild). */
|
||||
image?: string | null;
|
||||
/** Optional: Breadcrumb-Items. Letzter Eintrag ohne href = aktuelle Seite. */
|
||||
breadcrumbItems?: { href?: string; label: string }[];
|
||||
/** Optional: Breadcrumb-Items. Letzter Eintrag ohne href = aktuelle Seite. translationKey überschreibt label (übersetzt). */
|
||||
breadcrumbItems?: { href?: string; label?: string; translationKey?: TranslationKey }[];
|
||||
}
|
||||
|
||||
const {
|
||||
@@ -62,6 +66,21 @@ const {
|
||||
breadcrumbItems = undefined,
|
||||
} = Astro.props;
|
||||
|
||||
// Übersetzungen aus Middleware (Astro.locals.translations) – für TranslationProvider und Breadcrumbs
|
||||
const translations: Record<string, string> = Astro.locals.translations ?? {};
|
||||
/** JSON für script type="application/json": </ escapen, damit das Tag nicht geschlossen wird. */
|
||||
function safeJsonScriptContent(obj: Record<string, string>): string {
|
||||
return JSON.stringify(obj).replace(/<\//g, "\\u003c/");
|
||||
}
|
||||
const resolvedBreadcrumbItems =
|
||||
breadcrumbItems?.map((item) => ({
|
||||
href: item.href,
|
||||
label:
|
||||
item.translationKey != null
|
||||
? t(translations, item.translationKey)
|
||||
: (item.label ?? ""),
|
||||
})) ?? [];
|
||||
|
||||
// Header: Navigation laden und Links bauen
|
||||
interface NavLink {
|
||||
href: string;
|
||||
@@ -251,7 +270,8 @@ const baseUrl = isLocalhost
|
||||
const pathname = Astro.url.pathname || "/";
|
||||
const canonical = `${baseUrl}${pathname}`;
|
||||
|
||||
// Social-Image immer über RustyImage (externe URLs transformieren; lokale Pfade unverändert nutzen)
|
||||
// Social-Image immer über RustyImage (externe URLs transformieren; lokale Pfade unverändert nutzen).
|
||||
// Fallback: Wenn kein Bild/Logo und Transform fehlschlägt → DEFAULT_SOCIAL_IMAGE_URL (absolut), damit WhatsApp/og immer ein Vorschaubild hat.
|
||||
const socialImageSource = imageProp ?? logoUrl ?? null;
|
||||
const needsTransform =
|
||||
!socialImageSource ||
|
||||
@@ -275,7 +295,9 @@ if (needsTransform) {
|
||||
// Bereits lokaler Pfad (z. B. von Post), nur baseUrl davor
|
||||
socialImageResolved = `${baseUrl}${urlToTransform.startsWith("/") ? "" : "/"}${urlToTransform}`;
|
||||
}
|
||||
const socialImage = socialImageResolved ?? "";
|
||||
const socialImage =
|
||||
socialImageResolved ??
|
||||
(urlToTransform.startsWith("http") ? urlToTransform : "");
|
||||
const siteName =
|
||||
(import.meta.env.PUBLIC_SITE_NAME as string | undefined) || SITE_NAME;
|
||||
|
||||
@@ -342,15 +364,19 @@ const titleSubheadlineHtml =
|
||||
showTitleInContent && subheadline?.trim()
|
||||
? (marked.parseInline(subheadline) as string)
|
||||
: "";
|
||||
const cmsFromCache = getCmsFromCache();
|
||||
---
|
||||
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<meta name="theme-color" content="#2d7a45" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<link rel="apple-touch-icon" href="/apple-touch-icon.svg" />
|
||||
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#2d7a45" />
|
||||
<meta name="msapplication-TileColor" content="#2d7a45" />
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
<title>{seoTitle}</title>
|
||||
{seoDescription && <meta name="description" content={seoDescription} />}
|
||||
@@ -411,47 +437,71 @@ const titleSubheadlineHtml =
|
||||
/>
|
||||
)
|
||||
}
|
||||
<script
|
||||
src="https://analytics.pm86.de/api/script.js"
|
||||
data-site-id="ad2858cf2db1"
|
||||
defer
|
||||
/>
|
||||
</head>
|
||||
<body class="flex min-h-screen flex-col text-zinc-900">
|
||||
<Header
|
||||
links={headerLinks}
|
||||
socialLinks={socialLinks}
|
||||
logoUrl={logoUrl}
|
||||
client:load
|
||||
/>
|
||||
<HeaderOverlay client:load />
|
||||
{
|
||||
showBannerInLayout &&
|
||||
(topBanner != null || topBannerResolvedImages.length > 0) && (
|
||||
<TopBanner
|
||||
banner={topBanner}
|
||||
resolvedImages={topBannerResolvedImages}
|
||||
headline={headline}
|
||||
subheadline={subheadline}
|
||||
/>
|
||||
)
|
||||
import.meta.env.DEV && cmsFromCache && (
|
||||
<div
|
||||
class="sticky top-0 z-50 bg-amber-500 text-amber-950 text-center py-1.5 px-2 text-sm font-medium"
|
||||
role="status"
|
||||
aria-live="polite"
|
||||
>
|
||||
CMS nicht erreichbar – Daten werden aus dem Cache (.cache/cms) geladen.
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<main class="flex-1 min-h-[60em]">
|
||||
<div class="container-custom pb-12">
|
||||
{breadcrumbItems && breadcrumbItems.length > 0 && (
|
||||
<Breadcrumbs items={breadcrumbItems} client:load />
|
||||
)}
|
||||
{
|
||||
showTitleInContent && (titleHeadlineHtml || titleSubheadlineHtml) && (
|
||||
<div class="mb-6 pt-10 pageTitle">
|
||||
{titleHeadlineHtml && (
|
||||
<h1 class="pageTitle" set:html={titleHeadlineHtml} />
|
||||
)}
|
||||
{titleSubheadlineHtml && (
|
||||
<h2 class="pageTitle" set:html={titleSubheadlineHtml} />
|
||||
)}
|
||||
</div>
|
||||
<script id="astro-translations" type="application/json" set:html={safeJsonScriptContent(translations)}></script>
|
||||
<script define:vars={{ translations }}>
|
||||
try { window.__TRANSLATIONS__ = typeof translations !== "undefined" && translations != null ? translations : {}; } catch (e) {}
|
||||
</script>
|
||||
<TranslationProvider translations={translations}>
|
||||
<Header
|
||||
links={headerLinks}
|
||||
socialLinks={socialLinks}
|
||||
logoUrl={logoUrl}
|
||||
translations={translations}
|
||||
client:load
|
||||
/>
|
||||
<HeaderOverlay client:load />
|
||||
{
|
||||
showBannerInLayout &&
|
||||
(topBanner != null || topBannerResolvedImages.length > 0) && (
|
||||
<TopBanner
|
||||
banner={topBanner}
|
||||
resolvedImages={topBannerResolvedImages}
|
||||
headline={headline}
|
||||
subheadline={subheadline}
|
||||
/>
|
||||
)
|
||||
}
|
||||
<slot />
|
||||
</div>
|
||||
</main>
|
||||
<Footer footer={footerData} />
|
||||
}
|
||||
<main class="flex-1 min-h-[60em]">
|
||||
<div class="container-custom pb-12">
|
||||
{resolvedBreadcrumbItems.length > 0 && (
|
||||
<Breadcrumbs items={resolvedBreadcrumbItems} client:load />
|
||||
)}
|
||||
{
|
||||
showTitleInContent &&
|
||||
(titleHeadlineHtml || titleSubheadlineHtml) && (
|
||||
<div class="mb-6 pt-10 pageTitle">
|
||||
{titleHeadlineHtml && (
|
||||
<h1 class="pageTitle" set:html={titleHeadlineHtml} />
|
||||
)}
|
||||
{titleSubheadlineHtml && (
|
||||
<h2 class="pageTitle" set:html={titleSubheadlineHtml} />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<slot />
|
||||
</div>
|
||||
</main>
|
||||
<Footer footer={footerData} translations={translations} />
|
||||
</TranslationProvider>
|
||||
<script src="/pagefind/pagefind-ui.js" is:inline></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user