Files
klz-cables.com/components/SEO.tsx
2025-12-28 23:28:31 +01:00

144 lines
4.7 KiB
TypeScript

import { getSiteInfo } from '@/lib/i18n';
import { ReactElement } from 'react';
interface SEOProps {
title: string;
description?: string;
locale?: 'en' | 'de';
path?: string;
type?: 'website' | 'article' | 'product';
publishedTime?: string;
modifiedTime?: string;
authors?: string[];
images?: string[];
}
export function SEO({
title,
description,
locale = 'en',
path = '/',
type = 'website',
publishedTime,
modifiedTime,
authors,
images
}: SEOProps): ReactElement {
const site = getSiteInfo();
const fullTitle = title === 'Home' ? site.title : `${title} | ${site.title}`;
const fullDescription = description || site.description;
const canonicalUrl = `${site.baseUrl}${path}`;
// Generate alternate URLs
const alternateLocale = locale === 'en' ? 'de' : 'en';
const alternatePath = path === '/' ? '' : path;
const alternateUrl = `${site.baseUrl}/${alternateLocale}${alternatePath}`;
// Open Graph images
const ogImages = images && images.length > 0
? images
: [`${site.baseUrl}/og-image.jpg`];
return (
<>
{/* Basic Meta Tags */}
<title>{fullTitle}</title>
<meta name="description" content={fullDescription} />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="robots" content="index, follow" />
{/* Canonical URL */}
<link rel="canonical" href={canonicalUrl} />
{/* Alternate Languages */}
<link rel="alternate" hrefLang={locale} href={canonicalUrl} />
<link rel="alternate" hrefLang={alternateLocale} href={alternateUrl} />
<link rel="alternate" hrefLang="x-default" href={`${site.baseUrl}${alternatePath}`} />
{/* Open Graph */}
<meta property="og:type" content={type} />
<meta property="og:title" content={fullTitle} />
<meta property="og:description" content={fullDescription} />
<meta property="og:url" content={canonicalUrl} />
<meta property="og:locale" content={locale === 'en' ? 'en_US' : 'de_DE'} />
<meta property="og:site_name" content={site.title} />
{ogImages.map((image, index) => (
<meta key={index} property="og:image" content={image} />
))}
{publishedTime && (
<meta property="article:published_time" content={publishedTime} />
)}
{modifiedTime && (
<meta property="article:modified_time" content={modifiedTime} />
)}
{authors && authors.length > 0 && (
<meta property="article:author" content={authors.join(', ')} />
)}
{/* Twitter Card */}
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={fullTitle} />
<meta name="twitter:description" content={fullDescription} />
{ogImages[0] && (
<meta name="twitter:image" content={ogImages[0]} />
)}
{/* Site Info */}
<meta name="author" content="KLZ Kabelwerke" />
<meta name="copyright" content="KLZ Kabelwerke" />
{/* Favicon (placeholder) */}
<link rel="icon" href="/favicon.ico" sizes="any" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
</>
);
}
export function generateSEOMetadata(props: SEOProps) {
const site = getSiteInfo();
const fullTitle = props.title === 'Home' ? site.title : `${props.title} | ${site.title}`;
const description = props.description || site.description;
const canonicalUrl = `${site.baseUrl}${props.path || '/'}`;
const alternateLocale = props.locale === 'en' ? 'de' : 'en';
const alternatePath = props.path && props.path !== '/' ? props.path : '';
const alternateUrl = `${site.baseUrl}/${alternateLocale}${alternatePath}`;
return {
title: fullTitle,
description,
metadataBase: new URL(site.baseUrl),
alternates: {
canonical: canonicalUrl,
languages: {
[props.locale || 'en']: canonicalUrl,
[alternateLocale]: alternateUrl,
},
},
openGraph: {
title: fullTitle,
description,
type: props.type || 'website',
locale: props.locale || 'en',
siteName: site.title,
url: canonicalUrl,
...(props.images && props.images.length > 0 && {
images: props.images.map(img => ({ url: img, alt: fullTitle })),
}),
...(props.publishedTime && { publishedTime: props.publishedTime }),
...(props.modifiedTime && { modifiedTime: props.modifiedTime }),
...(props.authors && { authors: props.authors }),
},
twitter: {
card: 'summary_large_image',
title: fullTitle,
description,
...(props.images && props.images[0] && { images: [props.images[0]] }),
},
authors: props.authors ? props.authors.map(name => ({ name })) : undefined,
};
}