130 lines
3.2 KiB
TypeScript
130 lines
3.2 KiB
TypeScript
import { Metadata } from 'next';
|
|
import { getSiteInfo } from './i18n';
|
|
import type { Locale } from './i18n';
|
|
|
|
export interface SEOParams {
|
|
title?: string;
|
|
description?: string;
|
|
locale?: Locale;
|
|
canonical?: string;
|
|
ogType?: 'website' | 'article' | 'product';
|
|
ogImages?: string[];
|
|
publishedTime?: string;
|
|
updatedTime?: string;
|
|
author?: string;
|
|
}
|
|
|
|
export function generateSEOMetadata({
|
|
title,
|
|
description,
|
|
locale = 'en',
|
|
canonical,
|
|
ogType = 'website',
|
|
ogImages = [],
|
|
publishedTime,
|
|
updatedTime,
|
|
author,
|
|
}: SEOParams): Metadata {
|
|
const site = getSiteInfo(locale);
|
|
|
|
const pageTitle = title ? `${title} | ${site.title}` : site.title;
|
|
const pageDescription = description || site.description;
|
|
|
|
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || site.baseUrl;
|
|
const path = canonical || '/';
|
|
const fullUrl = `${baseUrl}${path}`;
|
|
|
|
// Generate alternate URLs for both locales
|
|
const alternates = {
|
|
canonical: fullUrl,
|
|
languages: {
|
|
'en': `${baseUrl}${path.replace('/de/', '/')}`,
|
|
'de': `${baseUrl}${path.startsWith('/de') ? path : `/de${path}`}`,
|
|
},
|
|
};
|
|
|
|
const openGraph = {
|
|
title: pageTitle,
|
|
description: pageDescription,
|
|
url: fullUrl,
|
|
siteName: site.title,
|
|
locale: locale === 'de' ? 'de_DE' : 'en_US',
|
|
type: ogType,
|
|
...(ogImages.length > 0 && { images: ogImages }),
|
|
...(publishedTime && { publishedTime }),
|
|
...(updatedTime && { updatedTime }),
|
|
...(author && { authors: [author] }),
|
|
};
|
|
|
|
const twitter = {
|
|
card: 'summary_large_image',
|
|
title: pageTitle,
|
|
description: pageDescription,
|
|
...(ogImages.length > 0 && { images: ogImages }),
|
|
};
|
|
|
|
return {
|
|
title: pageTitle,
|
|
description: pageDescription,
|
|
alternates,
|
|
openGraph,
|
|
twitter,
|
|
authors: author ? [{ name: author }] : undefined,
|
|
metadataBase: new URL(baseUrl),
|
|
};
|
|
}
|
|
|
|
// Helper for blog posts
|
|
export function getPostSEO(post: any, locale: Locale): Metadata {
|
|
return generateSEOMetadata({
|
|
title: post.title,
|
|
description: post.excerptHtml?.replace(/<[^>]*>/g, '') || '',
|
|
canonical: post.path,
|
|
locale: locale,
|
|
ogType: 'article',
|
|
ogImages: post.featuredImage ? [post.featuredImage] : [],
|
|
publishedTime: post.datePublished,
|
|
updatedTime: post.updatedAt,
|
|
author: 'KLZ Cables Team',
|
|
});
|
|
}
|
|
|
|
// Helper for products
|
|
export function getProductSEO(product: any, locale: Locale): Metadata {
|
|
return generateSEOMetadata({
|
|
title: product.name,
|
|
description: product.shortDescriptionHtml?.replace(/<[^>]*>/g, '') || '',
|
|
canonical: product.path,
|
|
locale: locale,
|
|
ogType: 'product',
|
|
ogImages: product.images || [],
|
|
});
|
|
}
|
|
|
|
// Helper for categories
|
|
export function getCategorySEO(category: any, locale: Locale): Metadata {
|
|
return generateSEOMetadata({
|
|
title: category.name,
|
|
description: category.description || `Products in ${category.name}`,
|
|
canonical: category.path,
|
|
locale: locale,
|
|
ogType: 'website',
|
|
});
|
|
}
|
|
|
|
export function generateSitemapItem({
|
|
path,
|
|
lastmod,
|
|
priority = 0.7,
|
|
}: {
|
|
path: string;
|
|
lastmod?: string;
|
|
priority?: number;
|
|
}) {
|
|
return {
|
|
url: path,
|
|
lastmod: lastmod || new Date().toISOString().split('T')[0],
|
|
changefreq: 'weekly',
|
|
priority,
|
|
};
|
|
} |