Files
manoon-headless/src/app/[locale]/layout.tsx
T
Unchained 57bae7ed6f fix: canonical URLs to always include locale prefix
Fixed Canonical Redirect Error in Google Search Console by ensuring
canonical URLs always include the locale prefix (/sr, /en, /de, /fr)
instead of omitting it for the default locale.

Changes:
- layout.tsx: Fixed canonical and hreflang URL generation
- page.tsx: Fixed homepage canonical URL
- contact/page.tsx: Fixed contact page canonical URL
- about/page.tsx: Fixed about page canonical URL
- products/page.tsx: Fixed products page canonical URL
- products/[slug]/page.tsx: Fixed product detail canonical URL

Before: /contact (when locale=sr)
After: /sr/contact (all locales)

Fixes: Canonical Redirect Error in Google Search Console
2026-04-06 15:32:24 +02:00

78 lines
2.3 KiB
TypeScript

import { Metadata } from "next";
import { NextIntlClientProvider } from "next-intl";
import { getMessages, setRequestLocale } from "next-intl/server";
import { SUPPORTED_LOCALES, DEFAULT_LOCALE, isValidLocale } from "@/lib/i18n/locales";
import Script from "next/script";
import ExitIntentDetector from "@/components/home/ExitIntentDetector";
const RYBBIT_SITE_ID = process.env.NEXT_PUBLIC_RYBBIT_SITE_ID || "1";
const RYBBIT_HOST = process.env.NEXT_PUBLIC_RYBBIT_HOST || "https://rybbit.nodecrew.me";
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || "https://manoonoils.com";
export function generateStaticParams() {
return SUPPORTED_LOCALES.map((locale) => ({ locale }));
}
export async function generateMetadata({
params,
}: {
params: Promise<{ locale: string }>;
}): Promise<Metadata> {
const { locale } = await params;
const validLocale = isValidLocale(locale) ? locale : DEFAULT_LOCALE;
const localePrefix = `/${locale}`;
const languages: Record<string, string> = {};
for (const loc of SUPPORTED_LOCALES) {
const prefix = `/${loc}`;
languages[loc] = `${baseUrl}${prefix}`;
}
return {
alternates: {
canonical: `${baseUrl}${localePrefix}`,
languages,
},
};
}
export default async function LocaleLayout({
children,
params,
}: {
children: React.ReactNode;
params: Promise<{ locale: string }>;
}) {
const { locale } = await params;
setRequestLocale(locale);
const messages = await getMessages();
return (
<>
<Script
id="mautic-tracking"
strategy="afterInteractive"
dangerouslySetInnerHTML={{
__html: `
(function(w,d,t,u,n,a,m){w['MauticTrackingObject']=n;
w[n]=w[n]||function(){(w[n].q=w[n].q||[]).push(arguments)},a=d.createElement(t),
m=d.getElementsByTagName(t)[0];a.async=1;a.src=u;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://mautic.nodecrew.me/mtc.js','mt');
mt('send', 'pageview');
`,
}}
/>
<Script
src={`${RYBBIT_HOST}/api/script.js`}
data-site-id={RYBBIT_SITE_ID}
strategy="afterInteractive"
/>
<NextIntlClientProvider messages={messages}>
{children}
<ExitIntentDetector />
</NextIntlClientProvider>
</>
);
}