wip
This commit is contained in:
@@ -2,6 +2,7 @@ import { notFound } from 'next/navigation';
|
||||
import { MDXRemote } from 'next-mdx-remote/rsc';
|
||||
import { Section, Container, Heading, Badge } from '@/components/ui';
|
||||
import { getTranslations } from 'next-intl/server';
|
||||
import { Metadata } from 'next';
|
||||
|
||||
interface PageProps {
|
||||
params: {
|
||||
@@ -10,6 +11,45 @@ interface PageProps {
|
||||
};
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params: { locale, slug } }: PageProps): Promise<Metadata> {
|
||||
const { getPageBySlug } = await import('@/lib/pages');
|
||||
const pageData = await getPageBySlug(slug, locale);
|
||||
|
||||
if (!pageData) return {};
|
||||
|
||||
return {
|
||||
title: pageData.frontmatter.title,
|
||||
description: pageData.frontmatter.excerpt || '',
|
||||
alternates: {
|
||||
canonical: `/${locale}/${slug}`,
|
||||
languages: {
|
||||
'de': `/de/${slug}`,
|
||||
'en': `/en/${slug}`,
|
||||
'x-default': `/en/${slug}`,
|
||||
},
|
||||
},
|
||||
openGraph: {
|
||||
title: `${pageData.frontmatter.title} | KLZ Cables`,
|
||||
description: pageData.frontmatter.excerpt || '',
|
||||
url: `https://klz-cables.com/${locale}/${slug}`,
|
||||
images: [
|
||||
{
|
||||
url: '/uploads/2025/02/og-2.webp',
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: pageData.frontmatter.title,
|
||||
},
|
||||
],
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: `${pageData.frontmatter.title} | KLZ Cables`,
|
||||
description: pageData.frontmatter.excerpt || '',
|
||||
images: ['/uploads/2025/02/og-2.webp'],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default async function StandardPage({ params: { locale, slug } }: PageProps) {
|
||||
const { getPageBySlug } = await import('@/lib/pages');
|
||||
const pageData = await getPageBySlug(slug, locale);
|
||||
|
||||
@@ -16,22 +16,40 @@ export async function generateMetadata({ params: { locale, slug } }: BlogPostPro
|
||||
if (!post) return {};
|
||||
|
||||
const description = post.frontmatter.excerpt || '';
|
||||
const imageUrl = post.frontmatter.featuredImage || '/uploads/2025/02/og-2.webp';
|
||||
|
||||
return {
|
||||
title: post.frontmatter.title,
|
||||
description: description,
|
||||
alternates: {
|
||||
canonical: `/${locale}/blog/${slug}`,
|
||||
languages: {
|
||||
'de': `/de/blog/${slug}`,
|
||||
'en': `/en/blog/${slug}`,
|
||||
'x-default': `/en/blog/${slug}`,
|
||||
},
|
||||
},
|
||||
openGraph: {
|
||||
title: post.frontmatter.title,
|
||||
title: `${post.frontmatter.title} | KLZ Cables`,
|
||||
description: description,
|
||||
type: 'article',
|
||||
publishedTime: post.frontmatter.date,
|
||||
authors: ['KLZ Cables'],
|
||||
url: `https://klz-cables.com/${locale}/blog/${slug}`,
|
||||
images: [
|
||||
{
|
||||
url: imageUrl,
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: post.frontmatter.title,
|
||||
},
|
||||
],
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: post.frontmatter.title,
|
||||
title: `${post.frontmatter.title} | KLZ Cables`,
|
||||
description: description,
|
||||
images: [imageUrl],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -15,6 +15,33 @@ export async function generateMetadata({ params: { locale } }: BlogIndexProps) {
|
||||
return {
|
||||
title: t('title'),
|
||||
description: t('description'),
|
||||
alternates: {
|
||||
canonical: `/${locale}/blog`,
|
||||
languages: {
|
||||
'de': '/de/blog',
|
||||
'en': '/en/blog',
|
||||
'x-default': '/en/blog',
|
||||
},
|
||||
},
|
||||
openGraph: {
|
||||
title: `${t('title')} | KLZ Cables`,
|
||||
description: t('description'),
|
||||
url: `https://klz-cables.com/${locale}/blog`,
|
||||
images: [
|
||||
{
|
||||
url: '/uploads/2025/02/og-2.webp',
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: t('title'),
|
||||
},
|
||||
],
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: `${t('title')} | KLZ Cables`,
|
||||
description: t('description'),
|
||||
images: ['/uploads/2025/02/og-2.webp'],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,49 @@
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { getTranslations } from 'next-intl/server';
|
||||
import { Metadata } from 'next';
|
||||
import { Section, Container, Button, Heading, Card, Input, Textarea, Label } from '@/components/ui';
|
||||
|
||||
interface ContactPageProps {
|
||||
params: {
|
||||
locale: string;
|
||||
};
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params: { locale } }: ContactPageProps): Promise<Metadata> {
|
||||
const t = await getTranslations({ locale, namespace: 'Contact' });
|
||||
return {
|
||||
title: t('title'),
|
||||
description: t('subtitle'),
|
||||
alternates: {
|
||||
canonical: `/${locale}/contact`,
|
||||
languages: {
|
||||
'de': '/de/contact',
|
||||
'en': '/en/contact',
|
||||
'x-default': '/en/contact',
|
||||
},
|
||||
},
|
||||
openGraph: {
|
||||
title: `${t('title')} | KLZ Cables`,
|
||||
description: t('subtitle'),
|
||||
url: `https://klz-cables.com/${locale}/contact`,
|
||||
images: [
|
||||
{
|
||||
url: '/uploads/2025/02/og-2.webp',
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: t('title'),
|
||||
},
|
||||
],
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: `${t('title')} | KLZ Cables`,
|
||||
description: t('subtitle'),
|
||||
images: ['/uploads/2025/02/og-2.webp'],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default function ContactPage() {
|
||||
const t = useTranslations('Contact');
|
||||
|
||||
|
||||
@@ -13,15 +13,20 @@ export async function generateMetadata({params: {locale}}: {params: {locale: str
|
||||
return {
|
||||
title: {
|
||||
default: t('title'),
|
||||
template: `%s | ${t('title')}`
|
||||
template: `%s | KLZ Cables`
|
||||
},
|
||||
description: t('description'),
|
||||
metadataBase: new URL('https://klz-cables.com'),
|
||||
icons: {
|
||||
icon: '/favicon.ico',
|
||||
apple: '/apple-touch-icon.png',
|
||||
},
|
||||
alternates: {
|
||||
canonical: `/${locale}`,
|
||||
languages: {
|
||||
'de-DE': '/de',
|
||||
'en-US': '/en',
|
||||
'de': '/de',
|
||||
'en': '/en',
|
||||
'x-default': '/en',
|
||||
},
|
||||
},
|
||||
openGraph: {
|
||||
@@ -31,11 +36,20 @@ export async function generateMetadata({params: {locale}}: {params: {locale: str
|
||||
siteName: 'KLZ Cables',
|
||||
title: t('title'),
|
||||
description: t('description'),
|
||||
images: [
|
||||
{
|
||||
url: '/uploads/2025/02/og-2.webp',
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: 'KLZ Cables',
|
||||
},
|
||||
],
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: t('title'),
|
||||
description: t('description'),
|
||||
images: ['/uploads/2025/02/og-2.webp'],
|
||||
},
|
||||
robots: {
|
||||
index: true,
|
||||
|
||||
@@ -34,30 +34,71 @@ export async function generateMetadata({ params }: ProductPageProps): Promise<Me
|
||||
return {
|
||||
title: categoryTitle,
|
||||
description: categoryDesc,
|
||||
alternates: {
|
||||
canonical: `/${locale}/products/${productSlug}`,
|
||||
languages: {
|
||||
'de': `/de/products/${productSlug}`,
|
||||
'en': `/en/products/${productSlug}`,
|
||||
'x-default': `/en/products/${productSlug}`,
|
||||
},
|
||||
},
|
||||
openGraph: {
|
||||
title: categoryTitle,
|
||||
title: `${categoryTitle} | KLZ Cables`,
|
||||
description: categoryDesc,
|
||||
url: `https://klz-cables.com/${locale}/products/${productSlug}`,
|
||||
}
|
||||
images: [
|
||||
{
|
||||
url: '/uploads/2025/02/og-2.webp',
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: categoryTitle,
|
||||
},
|
||||
],
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: `${categoryTitle} | KLZ Cables`,
|
||||
description: categoryDesc,
|
||||
images: ['/uploads/2025/02/og-2.webp'],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const product = await getProductBySlug(productSlug, locale);
|
||||
if (!product) return {};
|
||||
|
||||
const imageUrl = product.frontmatter.images?.[0] || '/uploads/2025/02/og-2.webp';
|
||||
|
||||
return {
|
||||
title: product.frontmatter.title,
|
||||
description: product.frontmatter.description,
|
||||
alternates: {
|
||||
canonical: `/${locale}/products/${slug.join('/')}`,
|
||||
languages: {
|
||||
'de': `/de/products/${slug.join('/')}`,
|
||||
'en': `/en/products/${slug.join('/')}`,
|
||||
'x-default': `/en/products/${slug.join('/')}`,
|
||||
},
|
||||
},
|
||||
openGraph: {
|
||||
title: product.frontmatter.title,
|
||||
title: `${product.frontmatter.title} | KLZ Cables`,
|
||||
description: product.frontmatter.description,
|
||||
type: 'website',
|
||||
url: `https://klz-cables.com/${locale}/products/${slug.join('/')}`,
|
||||
images: [
|
||||
{
|
||||
url: imageUrl,
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: product.frontmatter.title,
|
||||
},
|
||||
],
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: product.frontmatter.title,
|
||||
title: `${product.frontmatter.title} | KLZ Cables`,
|
||||
description: product.frontmatter.description,
|
||||
images: [imageUrl],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ import Reveal from '@/components/Reveal';
|
||||
import Scribble from '@/components/Scribble';
|
||||
import { Badge, Button, Card, Container, Section } from '@/components/ui';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { getTranslations } from 'next-intl/server';
|
||||
import { Metadata } from 'next';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
|
||||
@@ -11,6 +13,41 @@ interface ProductsPageProps {
|
||||
};
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params: { locale } }: ProductsPageProps): Promise<Metadata> {
|
||||
const t = await getTranslations({ locale, namespace: 'Products' });
|
||||
return {
|
||||
title: t('title'),
|
||||
description: t('subtitle'),
|
||||
alternates: {
|
||||
canonical: `/${locale}/products`,
|
||||
languages: {
|
||||
'de': '/de/products',
|
||||
'en': '/en/products',
|
||||
'x-default': '/en/products',
|
||||
},
|
||||
},
|
||||
openGraph: {
|
||||
title: `${t('title')} | KLZ Cables`,
|
||||
description: t('subtitle'),
|
||||
url: `https://klz-cables.com/${locale}/products`,
|
||||
images: [
|
||||
{
|
||||
url: '/uploads/2025/02/og-2.webp',
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: t('title'),
|
||||
},
|
||||
],
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: `${t('title')} | KLZ Cables`,
|
||||
description: t('subtitle'),
|
||||
images: ['/uploads/2025/02/og-2.webp'],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default function ProductsPage({ params }: ProductsPageProps) {
|
||||
const t = useTranslations('Products');
|
||||
|
||||
|
||||
@@ -1,8 +1,51 @@
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { getTranslations } from 'next-intl/server';
|
||||
import { Metadata } from 'next';
|
||||
import { Section, Container, Heading, Badge, Button } from '@/components/ui';
|
||||
import Image from 'next/image';
|
||||
import Reveal from '@/components/Reveal';
|
||||
|
||||
interface TeamPageProps {
|
||||
params: {
|
||||
locale: string;
|
||||
};
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params: { locale } }: TeamPageProps): Promise<Metadata> {
|
||||
const t = await getTranslations({ locale, namespace: 'Team' });
|
||||
return {
|
||||
title: t('hero.subtitle'),
|
||||
description: t('hero.title'),
|
||||
alternates: {
|
||||
canonical: `/${locale}/team`,
|
||||
languages: {
|
||||
'de': '/de/team',
|
||||
'en': '/en/team',
|
||||
'x-default': '/en/team',
|
||||
},
|
||||
},
|
||||
openGraph: {
|
||||
title: `${t('hero.subtitle')} | KLZ Cables`,
|
||||
description: t('hero.title'),
|
||||
url: `https://klz-cables.com/${locale}/team`,
|
||||
images: [
|
||||
{
|
||||
url: '/uploads/2024/12/DSC07655-Large.webp',
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: t('hero.subtitle'),
|
||||
},
|
||||
],
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: `${t('hero.subtitle')} | KLZ Cables`,
|
||||
description: t('hero.title'),
|
||||
images: ['/uploads/2024/12/DSC07655-Large.webp'],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default function TeamPage() {
|
||||
const t = useTranslations('Team');
|
||||
|
||||
|
||||
25
app/manifest.ts
Normal file
25
app/manifest.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { MetadataRoute } from 'next';
|
||||
|
||||
export default function manifest(): MetadataRoute.Manifest {
|
||||
return {
|
||||
name: 'KLZ Cables',
|
||||
short_name: 'KLZ',
|
||||
description: 'Premium Cable Solutions',
|
||||
start_url: '/',
|
||||
display: 'standalone',
|
||||
background_color: '#001a4d',
|
||||
theme_color: '#001a4d',
|
||||
icons: [
|
||||
{
|
||||
src: '/favicon.ico',
|
||||
sizes: 'any',
|
||||
type: 'image/x-icon',
|
||||
},
|
||||
{
|
||||
src: '/apple-touch-icon.png',
|
||||
sizes: '180x180',
|
||||
type: 'image/png',
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
12
app/robots.ts
Normal file
12
app/robots.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { MetadataRoute } from 'next';
|
||||
|
||||
export default function robots(): MetadataRoute.Robots {
|
||||
return {
|
||||
rules: {
|
||||
userAgent: '*',
|
||||
allow: '/',
|
||||
disallow: ['/api/', '/health/'],
|
||||
},
|
||||
sitemap: 'https://klz-cables.com/sitemap.xml',
|
||||
};
|
||||
}
|
||||
74
app/sitemap.ts
Normal file
74
app/sitemap.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { MetadataRoute } from 'next';
|
||||
import { getAllProducts } from '@/lib/mdx';
|
||||
import { getAllPosts } from '@/lib/blog';
|
||||
import { getAllPages } from '@/lib/pages';
|
||||
|
||||
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
|
||||
const baseUrl = 'https://klz-cables.com';
|
||||
const locales = ['de', 'en'];
|
||||
|
||||
const routes = [
|
||||
'',
|
||||
'/blog',
|
||||
'/contact',
|
||||
'/team',
|
||||
'/products',
|
||||
'/products/low-voltage-cables',
|
||||
'/products/medium-voltage-cables',
|
||||
'/products/high-voltage-cables',
|
||||
'/products/solar-cables',
|
||||
];
|
||||
|
||||
const sitemapEntries: MetadataRoute.Sitemap = [];
|
||||
|
||||
for (const locale of locales) {
|
||||
// Static routes
|
||||
for (const route of routes) {
|
||||
sitemapEntries.push({
|
||||
url: `${baseUrl}/${locale}${route}`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: route === '' ? 'daily' : 'weekly',
|
||||
priority: route === '' ? 1 : 0.8,
|
||||
});
|
||||
}
|
||||
|
||||
// Products
|
||||
const products = await getAllProducts(locale);
|
||||
for (const product of products) {
|
||||
// We need to find the category for the product to build the URL
|
||||
// In this project, products are under /products/[category]/[slug]
|
||||
// The category is in product.frontmatter.categories
|
||||
const category = product.frontmatter.categories[0]?.toLowerCase().replace(/\s+/g, '-') || 'other';
|
||||
sitemapEntries.push({
|
||||
url: `${baseUrl}/${locale}/products/${category}/${product.slug}`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: 'monthly',
|
||||
priority: 0.7,
|
||||
});
|
||||
}
|
||||
|
||||
// Blog posts
|
||||
const posts = await getAllPosts(locale);
|
||||
for (const post of posts) {
|
||||
sitemapEntries.push({
|
||||
url: `${baseUrl}/${locale}/blog/${post.slug}`,
|
||||
lastModified: new Date(post.frontmatter.date),
|
||||
changeFrequency: 'monthly',
|
||||
priority: 0.6,
|
||||
});
|
||||
}
|
||||
|
||||
// Static pages
|
||||
const pages = await getAllPages(locale);
|
||||
for (const page of pages) {
|
||||
sitemapEntries.push({
|
||||
url: `${baseUrl}/${locale}/${page.slug}`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: 'monthly',
|
||||
priority: 0.5,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return sitemapEntries;
|
||||
}
|
||||
14
lib/pages.ts
14
lib/pages.ts
@@ -32,3 +32,17 @@ export async function getPageBySlug(slug: string, locale: string): Promise<PageM
|
||||
content,
|
||||
};
|
||||
}
|
||||
|
||||
export async function getAllPages(locale: string): Promise<PageMdx[]> {
|
||||
const pagesDir = path.join(process.cwd(), 'data', 'pages', locale);
|
||||
if (!fs.existsSync(pagesDir)) return [];
|
||||
|
||||
const files = fs.readdirSync(pagesDir);
|
||||
const pages = await Promise.all(
|
||||
files
|
||||
.filter(file => file.endsWith('.mdx'))
|
||||
.map(file => getPageBySlug(file.replace(/\.mdx$/, ''), locale))
|
||||
);
|
||||
|
||||
return pages.filter((p): p is PageMdx => p !== null);
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
"title": "KLZ Cables - Hochwertige Kabel",
|
||||
"description": "Ihr Partner für hochwertige Kabel.",
|
||||
"meta": {
|
||||
"title": "KLZ Cables",
|
||||
"description": "Premium-Kabellösungen"
|
||||
"title": "KLZ Cables | Hochwertige Strom- & Solarkabel",
|
||||
"description": "Ihr Experte für hochwertige Stromkabel, Mittelspannungslösungen und Solarkabel. Zuverlässige Infrastruktur für eine grüne Energiezukunft."
|
||||
}
|
||||
},
|
||||
"Navigation": {
|
||||
@@ -287,8 +287,8 @@
|
||||
},
|
||||
"Blog": {
|
||||
"meta": {
|
||||
"title": "Neuigkeiten zu Kabeln und Energielösungen",
|
||||
"description": "Bleiben Sie auf dem Laufenden! Lesen Sie aktuelle Themen und Insights zu Kabeltechnologie, Energielösungen und branchenspezifischen Innovationen."
|
||||
"title": "Kabel-News & Energie-Insights | KLZ Cables Blog",
|
||||
"description": "Bleiben Sie informiert über die neuesten Trends in der Kabeltechnologie, Energieinfrastruktur und nachhaltigen Stromlösungen. Expertenwissen von KLZ Cables."
|
||||
},
|
||||
"featuredPost": "Hervorgehobener Beitrag",
|
||||
"readFullArticle": "Vollständigen Artikel lesen",
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
"title": "KLZ Cables - High Quality Cables",
|
||||
"description": "Your partner for high quality cables.",
|
||||
"meta": {
|
||||
"title": "KLZ Cables",
|
||||
"description": "Premium Cable Solutions"
|
||||
"title": "KLZ Cables | High-Quality Power & Solar Cables",
|
||||
"description": "Your expert partner for high-quality power cables, medium voltage solutions, and solar cables. Reliable infrastructure for a green energy future."
|
||||
}
|
||||
},
|
||||
"Navigation": {
|
||||
@@ -287,8 +287,8 @@
|
||||
},
|
||||
"Blog": {
|
||||
"meta": {
|
||||
"title": "News on Cables and Energy Solutions",
|
||||
"description": "Stay up to date! Read current topics and insights on cable technology, energy solutions and industry-specific innovations."
|
||||
"title": "Cable Industry News & Energy Insights | KLZ Cables Blog",
|
||||
"description": "Stay informed with the latest trends in cable technology, energy infrastructure, and sustainable power solutions. Expert insights from KLZ Cables."
|
||||
},
|
||||
"featuredPost": "Featured Post",
|
||||
"readFullArticle": "Read Full Article",
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user