fix(routing): resolve 404 on German product pages via rewrites and localized links
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 18s
Build & Deploy / 🧪 QA (push) Successful in 3m7s
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🧪 Smoke Test (push) Has been cancelled
Build & Deploy / ⚡ Lighthouse (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
Build & Deploy / 🏗️ Build (push) Has started running
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 18s
Build & Deploy / 🧪 QA (push) Successful in 3m7s
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🧪 Smoke Test (push) Has been cancelled
Build & Deploy / ⚡ Lighthouse (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
Build & Deploy / 🏗️ Build (push) Has started running
This commit is contained in:
@@ -53,17 +53,17 @@ export async function generateMetadata({ params }: ProductPageProps): Promise<Me
|
|||||||
title: categoryTitle,
|
title: categoryTitle,
|
||||||
description: categoryDesc,
|
description: categoryDesc,
|
||||||
alternates: {
|
alternates: {
|
||||||
canonical: `${SITE_URL}/${locale}/products/${productSlug}`,
|
canonical: `${SITE_URL}/${locale}/${await mapFileSlugToTranslated('products', locale)}/${productSlug}`,
|
||||||
languages: {
|
languages: {
|
||||||
de: `${SITE_URL}/de/products/${await mapFileSlugToTranslated(productSlug, 'de')}`,
|
de: `${SITE_URL}/de/${await mapFileSlugToTranslated('products', 'de')}/${await mapFileSlugToTranslated(productSlug, 'de')}`,
|
||||||
en: `${SITE_URL}/en/products/${await mapFileSlugToTranslated(productSlug, 'en')}`,
|
en: `${SITE_URL}/en/${await mapFileSlugToTranslated('products', 'en')}/${await mapFileSlugToTranslated(productSlug, 'en')}`,
|
||||||
'x-default': `${SITE_URL}/en/products/${await mapFileSlugToTranslated(productSlug, 'en')}`,
|
'x-default': `${SITE_URL}/en/${await mapFileSlugToTranslated('products', 'en')}/${await mapFileSlugToTranslated(productSlug, 'en')}`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
openGraph: {
|
openGraph: {
|
||||||
title: `${categoryTitle} | KLZ Cables`,
|
title: `${categoryTitle} | KLZ Cables`,
|
||||||
description: categoryDesc,
|
description: categoryDesc,
|
||||||
url: `${SITE_URL}/${locale}/products/${productSlug}`,
|
url: `${SITE_URL}/${locale}/${await mapFileSlugToTranslated('products', locale)}/${productSlug}`,
|
||||||
images: getProductOGImageMetadata(fileSlug, categoryTitle, locale),
|
images: getProductOGImageMetadata(fileSlug, categoryTitle, locale),
|
||||||
},
|
},
|
||||||
twitter: {
|
twitter: {
|
||||||
@@ -81,18 +81,18 @@ export async function generateMetadata({ params }: ProductPageProps): Promise<Me
|
|||||||
title: product.frontmatter.title,
|
title: product.frontmatter.title,
|
||||||
description: product.frontmatter.description,
|
description: product.frontmatter.description,
|
||||||
alternates: {
|
alternates: {
|
||||||
canonical: `${SITE_URL}/${locale}/products/${slug.join('/')}`,
|
canonical: `${SITE_URL}/${locale}/${await mapFileSlugToTranslated('products', locale)}/${slug.join('/')}`,
|
||||||
languages: {
|
languages: {
|
||||||
de: `${SITE_URL}/de/products/${await mapFileSlugToTranslated(slug[0], 'de')}/${await mapFileSlugToTranslated(productSlug, 'de')}`,
|
de: `${SITE_URL}/de/${await mapFileSlugToTranslated('products', 'de')}/${await mapFileSlugToTranslated(slug[0], 'de')}/${await mapFileSlugToTranslated(productSlug, 'de')}`,
|
||||||
en: `${SITE_URL}/en/products/${await mapFileSlugToTranslated(slug[0], 'en')}/${await mapFileSlugToTranslated(productSlug, 'en')}`,
|
en: `${SITE_URL}/en/${await mapFileSlugToTranslated('products', 'en')}/${await mapFileSlugToTranslated(slug[0], 'en')}/${await mapFileSlugToTranslated(productSlug, 'en')}`,
|
||||||
'x-default': `${SITE_URL}/en/products/${await mapFileSlugToTranslated(slug[0], 'en')}/${await mapFileSlugToTranslated(productSlug, 'en')}`,
|
'x-default': `${SITE_URL}/en/${await mapFileSlugToTranslated('products', 'en')}/${await mapFileSlugToTranslated(slug[0], 'en')}/${await mapFileSlugToTranslated(productSlug, 'en')}`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
openGraph: {
|
openGraph: {
|
||||||
title: `${product.frontmatter.title} | KLZ Cables`,
|
title: `${product.frontmatter.title} | KLZ Cables`,
|
||||||
description: product.frontmatter.description,
|
description: product.frontmatter.description,
|
||||||
type: 'website',
|
type: 'website',
|
||||||
url: `${SITE_URL}/${locale}/products/${slug.join('/')}`,
|
url: `${SITE_URL}/${locale}/${await mapFileSlugToTranslated('products', locale)}/${slug.join('/')}`,
|
||||||
images: getProductOGImageMetadata(productSlug, product.frontmatter.title, locale),
|
images: getProductOGImageMetadata(productSlug, product.frontmatter.title, locale),
|
||||||
},
|
},
|
||||||
twitter: {
|
twitter: {
|
||||||
@@ -236,7 +236,7 @@ export default async function ProductPage({ params }: ProductPageProps) {
|
|||||||
{productsWithTranslatedSlugs.map((product) => (
|
{productsWithTranslatedSlugs.map((product) => (
|
||||||
<Link
|
<Link
|
||||||
key={product.slug}
|
key={product.slug}
|
||||||
href={`/${locale}/products/${productSlug}/${product.translatedSlug}`}
|
href={`/${locale}/${productSlug}/${product.translatedSlug}`}
|
||||||
className="group block bg-white rounded-[32px] overflow-hidden shadow-sm hover:shadow-2xl transition-all duration-500 border border-neutral-dark/5"
|
className="group block bg-white rounded-[32px] overflow-hidden shadow-sm hover:shadow-2xl transition-all duration-500 border border-neutral-dark/5"
|
||||||
>
|
>
|
||||||
<Card tag="article" className="premium-card-reset">
|
<Card tag="article" className="premium-card-reset">
|
||||||
@@ -381,7 +381,7 @@ export default async function ProductPage({ params }: ProductPageProps) {
|
|||||||
</Link>
|
</Link>
|
||||||
<span className="mx-4 opacity-20">/</span>
|
<span className="mx-4 opacity-20">/</span>
|
||||||
<Link
|
<Link
|
||||||
href={`/${locale}/products/${categorySlug}`}
|
href={`/${locale}/${productSlug}`}
|
||||||
className="hover:text-accent transition-colors"
|
className="hover:text-accent transition-colors"
|
||||||
>
|
>
|
||||||
{categoryTitle}
|
{categoryTitle}
|
||||||
@@ -504,7 +504,7 @@ export default async function ProductPage({ params }: ProductPageProps) {
|
|||||||
'@type': 'Offer',
|
'@type': 'Offer',
|
||||||
availability: 'https://schema.org/InStock',
|
availability: 'https://schema.org/InStock',
|
||||||
priceCurrency: 'EUR',
|
priceCurrency: 'EUR',
|
||||||
url: `${SITE_URL}/${locale}/products/${slug.join('/')}`,
|
url: `${SITE_URL}/${locale}/${await mapFileSlugToTranslated('products', locale)}/${slug.join('/')}`,
|
||||||
itemCondition: 'https://schema.org/NewCondition',
|
itemCondition: 'https://schema.org/NewCondition',
|
||||||
},
|
},
|
||||||
additionalProperty: technicalItems.map((item: any) => ({
|
additionalProperty: technicalItems.map((item: any) => ({
|
||||||
@@ -515,7 +515,7 @@ export default async function ProductPage({ params }: ProductPageProps) {
|
|||||||
category: product.frontmatter.categories.join(', '),
|
category: product.frontmatter.categories.join(', '),
|
||||||
mainEntityOfPage: {
|
mainEntityOfPage: {
|
||||||
'@type': 'WebPage',
|
'@type': 'WebPage',
|
||||||
'@id': `${SITE_URL}/${locale}/products/${slug.join('/')}`,
|
'@id': `${SITE_URL}/${locale}/${await mapFileSlugToTranslated('products', locale)}/${slug.join('/')}`,
|
||||||
},
|
},
|
||||||
} as any
|
} as any
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { getAllProducts } from '@/lib/mdx';
|
|||||||
import { getTranslations } from 'next-intl/server';
|
import { getTranslations } from 'next-intl/server';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { RelatedProductLink } from './RelatedProductLink';
|
import { RelatedProductLink } from './RelatedProductLink';
|
||||||
|
import { mapFileSlugToTranslated } from '@/lib/slugs';
|
||||||
|
|
||||||
interface RelatedProductsProps {
|
interface RelatedProductsProps {
|
||||||
currentSlug: string;
|
currentSlug: string;
|
||||||
@@ -16,6 +17,7 @@ export default async function RelatedProducts({
|
|||||||
}: RelatedProductsProps) {
|
}: RelatedProductsProps) {
|
||||||
const products = await getAllProducts(locale);
|
const products = await getAllProducts(locale);
|
||||||
const t = await getTranslations('Products');
|
const t = await getTranslations('Products');
|
||||||
|
const productsSlug = await mapFileSlugToTranslated('products', locale);
|
||||||
|
|
||||||
// Filter products: same category, not current product
|
// Filter products: same category, not current product
|
||||||
const related = products
|
const related = products
|
||||||
@@ -61,7 +63,7 @@ export default async function RelatedProducts({
|
|||||||
return (
|
return (
|
||||||
<RelatedProductLink
|
<RelatedProductLink
|
||||||
key={product.slug}
|
key={product.slug}
|
||||||
href={`/${locale}/products/${catSlug}/${product.slug}`}
|
href={`/${locale}/${productsSlug}/${catSlug}/${product.slug}`}
|
||||||
productSlug={product.slug}
|
productSlug={product.slug}
|
||||||
productTitle={product.frontmatter.title}
|
productTitle={product.frontmatter.title}
|
||||||
className="group block bg-white rounded-[32px] overflow-hidden shadow-sm hover:shadow-2xl transition-all duration-500 border border-neutral-dark/5"
|
className="group block bg-white rounded-[32px] overflow-hidden shadow-sm hover:shadow-2xl transition-all duration-500 border border-neutral-dark/5"
|
||||||
|
|||||||
@@ -344,6 +344,14 @@ const nextConfig = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
{
|
||||||
|
source: '/de/produkte',
|
||||||
|
destination: '/de/products',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: '/de/produkte/:path*',
|
||||||
|
destination: '/de/products/:path*',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
source: '/cms/:path*',
|
source: '/cms/:path*',
|
||||||
destination: `${directusUrl}/:path*`,
|
destination: `${directusUrl}/:path*`,
|
||||||
|
|||||||
Reference in New Issue
Block a user