SEO
Per-Page Metadata
export async function generateMetadata({ params }): Promise<Metadata> {
const { locale, productSlug } = await params;
const product = await getProduct(productSlug);
const name = locale.includes('ar') ? product.name_ar : product.name_en;
return {
title: name,
description: locale.includes('ar') ? product.metaDescription_ar : product.metaDescription_en,
openGraph: { type: 'website', images: [getMediaUrl(product.imageUrl)] },
alternates: {
canonical: `${BASE_URL}/${locale}/products/${productSlug}`,
languages: { 'en-ae': '...', 'ar-ae': '...', 'en-gl': '...', 'ar-gl': '...', 'x-default': '...' },
},
};
}
Helpers
lib/seo.ts — generateSEOMetadata(), generateProductSEO(), getKeywords()
JSON-LD (PDP)
<script type="application/ld+json" dangerouslySetInnerHTML={{
__html: JSON.stringify({
'@context': 'https://schema.org',
'@type': 'Product',
name, image, offers: { '@type': 'Offer', price, priceCurrency: 'AED' },
}),
}} />
ISR Revalidation
| Page | revalidate |
|---|---|
| Homepage | 300s |
| PDP | 900s |
| Server product fetch | 300s |
Sitemap
app/sitemap.ts — MetadataRoute.Sitemap with hreflang for all 4 locales.
Robots
Middleware sets X-Robots-Tag: noindex for checkout, auth, profile pages.
Static Params
export async function generateStaticParams() {
const products = await getAllSlugs();
return locales.flatMap(locale => products.map(p => ({ locale, productSlug: p.slug })));
}
Env
NEXT_PUBLIC_BASE_URL, GOOGLE_SITE_VERIFICATION