Files
Portfolio/src/app/[locale]/layout.tsx
T
damjan_savicandClaude Opus 4.5 43484c5023 Add blog posts, cleanup unused files, update components
- Add 100 blog posts covering AI, development, and tech topics
- Add .env.example for environment configuration
- Add accessibility and lighthouse audit scripts
- Remove obsolete SEO reports and temporary files
- Remove dev-dist build artifacts and backup files
- Remove unused portrait images (moved/consolidated elsewhere)
- Update contact form and component improvements

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 11:42:11 +01:00

130 lines
4.4 KiB
TypeScript

import { notFound } from 'next/navigation';
import { NextIntlClientProvider } from 'next-intl';
import { getMessages, setRequestLocale } from 'next-intl/server';
import { Inter } from 'next/font/google';
import { locales, localeMetadata, seoKeywords, type Locale } from '@/i18n/config';
import type { Metadata } from 'next';
import { Header, Footer, GlobalBackground } from '@/components/layout';
import { PersonJsonLd, ProfessionalServiceJsonLd, OrganizationJsonLd, WebSiteJsonLd } from '@/components/seo';
import '../globals.css';
const inter = Inter({
subsets: ['latin'],
display: 'swap',
variable: '--font-inter',
});
type Props = {
children: React.ReactNode;
params: Promise<{ locale: string }>;
};
export function generateStaticParams() {
return locales.map((locale) => ({ locale }));
}
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { locale } = await params;
const currentLocale = locale as Locale;
const meta = localeMetadata[currentLocale] || localeMetadata.de;
const keywords = seoKeywords[currentLocale] || seoKeywords.de;
const BASE_URL = process.env.NEXT_PUBLIC_SITE_URL || 'https://damjan-savic.com';
const titles: Record<Locale, string> = {
de: 'Damjan Savić | Fullstack Developer aus Köln',
en: 'Damjan Savić | Fullstack Developer from Cologne',
sr: 'Damjan Savić | Fullstack Developer iz Kelna',
};
const descriptions: Record<Locale, string> = {
de: 'Fullstack Entwicklung für Websites, Apps und SaaS mit Next.js, React und TypeScript.',
en: 'Fullstack development for websites, apps and SaaS with Next.js, React and TypeScript.',
sr: 'Fullstack razvoj za web stranice, aplikacije i SaaS sa Next.js, React i TypeScript.',
};
// Generate alternates using localeMetadata
const languageAlternates: Record<string, string> = {
'x-default': `${BASE_URL}/de`,
};
locales.forEach((loc) => {
languageAlternates[loc] = `${BASE_URL}/${loc}`;
});
// Generate OG alternate locales
const allOgLocales = locales.map((loc) => localeMetadata[loc].ogLocale);
const alternateOgLocales = allOgLocales.filter((l) => l !== meta.ogLocale);
return {
title: {
template: '%s | Damjan Savić',
default: titles[currentLocale] || titles.de,
},
description: descriptions[currentLocale] || descriptions.de,
keywords: keywords,
alternates: {
canonical: `${BASE_URL}/${locale}`,
languages: languageAlternates,
},
openGraph: {
type: 'website',
locale: meta.ogLocale,
alternateLocale: alternateOgLocales,
siteName: 'Damjan Savić',
images: [
{
url: '/images/og-image.jpg',
width: 1200,
height: 630,
alt: locale === 'de' ? 'Damjan Savić - KI & Automatisierung Spezialist' : locale === 'sr' ? 'Damjan Savić - AI & Automatizacija Specijalista' : 'Damjan Savić - AI & Automation Specialist',
},
],
},
twitter: {
card: 'summary_large_image',
creator: '@damjansavic',
},
};
}
export default async function LocaleLayout({ children, params }: Props) {
const { locale } = await params;
if (!locales.includes(locale as Locale)) {
notFound();
}
setRequestLocale(locale);
const messages = await getMessages();
return (
<html lang={locale} className={`${inter.variable} dark`}>
<head>
{/* Preconnect für bessere Performance */}
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" />
<link rel="dns-prefetch" href="https://mxadgucxhmstlzsbgmoz.supabase.co" />
{/* Theme Color für mobile Browser */}
<meta name="theme-color" content="#18181b" />
{/* JSON-LD Structured Data */}
<PersonJsonLd locale={locale as Locale} />
<ProfessionalServiceJsonLd locale={locale as Locale} />
<OrganizationJsonLd locale={locale as Locale} />
<WebSiteJsonLd locale={locale as Locale} />
</head>
<body className="min-h-screen bg-zinc-900 text-zinc-50 antialiased">
<NextIntlClientProvider messages={messages}>
<div className="min-h-screen flex flex-col relative">
<GlobalBackground />
<Header />
<main className="flex-1 pt-16 relative z-10">{children}</main>
<Footer />
</div>
</NextIntlClientProvider>
</body>
</html>
);
}