feat(i18n): Complete localization of marketing frontend with react-i18next, prefixed URLs, JSON migrations, and automation
This commit is contained in:
@@ -1,45 +1,66 @@
|
||||
import React, { ReactNode } from 'react';
|
||||
import { Head } from '@inertiajs/react';
|
||||
import MarketingHeader from '@/components/marketing/MarketingHeader';
|
||||
import MarketingFooter from '@/components/marketing/MarketingFooter';
|
||||
import React from 'react';
|
||||
import { Head, Link, usePage } from '@inertiajs/react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
interface MarketingLayoutProps {
|
||||
children: ReactNode;
|
||||
children: React.ReactNode;
|
||||
title?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
const MarketingLayout: React.FC<MarketingLayoutProps> = ({
|
||||
children,
|
||||
title = 'Fotospiel - Event-Fotos einfach und sicher mit QR-Codes',
|
||||
description = 'Sammle Gastfotos für Events mit QR-Codes und unserer PWA-Plattform. Für Hochzeiten, Firmenevents und mehr. Kostenloser Einstieg.'
|
||||
}) => {
|
||||
const MarketingLayout: React.FC<MarketingLayoutProps> = ({ children, title }) => {
|
||||
const { t } = useTranslation('marketing');
|
||||
const { url } = usePage();
|
||||
|
||||
const { translations } = usePage().props as any;
|
||||
const marketing = translations?.marketing || {};
|
||||
|
||||
const getString = (key: string, fallback: string) => {
|
||||
const value = marketing[key];
|
||||
return typeof value === 'string' ? value : fallback;
|
||||
};
|
||||
|
||||
const currentLocale = url.startsWith('/en/') ? 'en' : 'de';
|
||||
const alternateLocale = currentLocale === 'de' ? 'en' : 'de';
|
||||
const path = url.replace(/^\/(de|en)?/, '');
|
||||
const canonicalUrl = `https://fotospiel.app/${currentLocale}${path}`;
|
||||
const alternateUrl = `https://fotospiel.app/${alternateLocale}${path}`;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{title}</title>
|
||||
<meta name="description" content={description} />
|
||||
<style>{`
|
||||
@keyframes aurora {
|
||||
0%, 100% { background-position: 0% 50%; }
|
||||
50% { background-position: 100% 50%; }
|
||||
}
|
||||
.bg-aurora {
|
||||
background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab);
|
||||
background-size: 400% 400%;
|
||||
animation: aurora 15s ease infinite;
|
||||
}
|
||||
`}</style>
|
||||
<title>{title || t('meta.title', getString('title', 'Fotospiel'))}</title>
|
||||
<meta name="description" content={t('meta.description', getString('description', 'Sammle Gastfotos für Events mit QR-Codes'))} />
|
||||
<meta property="og:title" content={title || t('meta.title', getString('title', 'Fotospiel'))} />
|
||||
<meta property="og:description" content={t('meta.description', getString('description', 'Sammle Gastfotos für Events mit QR-Codes'))} />
|
||||
<meta property="og:url" content={canonicalUrl} />
|
||||
<link rel="canonical" href={canonicalUrl} />
|
||||
<link rel="alternate" hrefLang="de" href={`https://fotospiel.app/de${path}`} />
|
||||
<link rel="alternate" hrefLang="en" href={`https://fotospiel.app/en${path}`} />
|
||||
<link rel="alternate" hrefLang="x-default" href="https://fotospiel.app/de" />
|
||||
</Head>
|
||||
<div className="bg-gray-50 text-gray-900 min-h-screen flex flex-col font-sans antialiased">
|
||||
<MarketingHeader />
|
||||
<main className="flex-grow">
|
||||
<div className="min-h-screen bg-white">
|
||||
<main>
|
||||
{children}
|
||||
</main>
|
||||
<MarketingFooter />
|
||||
<footer className="bg-gray-800 text-white py-8">
|
||||
<div className="container mx-auto px-4 text-center">
|
||||
<p>© 2025 Fotospiel. Alle Rechte vorbehalten.</p>
|
||||
<div className="mt-4 space-x-4">
|
||||
<Link href="/datenschutz" className="hover:underline">
|
||||
{t('nav.privacy', getString('nav.privacy', 'Datenschutz'))}
|
||||
</Link>
|
||||
<Link href="/impressum" className="hover:underline">
|
||||
{t('nav.impressum', getString('nav.impressum', 'Impressum'))}
|
||||
</Link>
|
||||
<Link href="/kontakt" className="hover:underline">
|
||||
{t('nav.contact', getString('nav.contact', 'Kontakt'))}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default MarketingLayout;
|
||||
export default MarketingLayout;
|
||||
|
||||
Reference in New Issue
Block a user