I was trying to internationalize a website for a client of mine.
The website should have the standard languages IT and then EN.
I’m having a problem with the next-intl library and I can’t seem to figure it out.
I am using the Router App and currently my project has the following configuration:
- @/messages contains both JSON files for the two languages IT and EN
- This is the next.config.mjs file:
import createNextIntlPlugin from "next-intl/plugin";
const withNextIntl = createNextIntlPlugin();
const nextConfig = {};
export default withNextIntl(nextConfig);
- This is @/i18n/request.js file:
import { getRequestConfig } from "next-intl/server";
export default getRequestConfig(async ({ locale }) => {
const l = locale ?? "it"; // fallback
return {
locale: l,
messages: (await import(`../messages/${l}.json`)).default,
};
});
- This is the middleware.js root file
import createMiddleware from "next-intl/middleware";
export default createMiddleware({
locales: ["it", "en"],
defaultLocale: "it",
localePrefix: "always",
});
// Escludi asset, api, ecc.
export const config = {
matcher: ["/((?!api|_next|.*\..*).*)"],
};
- This is the Root Layout in [locale]
// app/[locale]/layout.js
import localFont from "next/font/local";
import { Montserrat } from "next/font/google";
import "../globals.css";
import Header from "@/components/header/header";
import Footer from "@/components/footer/footer";
import Script from "next/script";
import { notFound } from "next/navigation";
import { getMessages, setRequestLocale } from "next-intl/server";
import { NextIntlClientProvider } from "next-intl";
export const dynamic = "force-static"; // opzionale, se tutto è statico
const operetta = localFont({
src: [
{
path: "../../public/fonts/Operetta12-Regular.otf",
weight: "400",
style: "normal",
},
{
path: "../../public/fonts/Operetta12-ExtraLight.otf",
weight: "300",
style: "normal",
},
{
path: "../../public/fonts/Operetta12-SemiBold.otf",
weight: "600",
style: "normal",
},
],
variable: "--fontHeading",
display: "swap",
});
const montserrat = Montserrat({
variable: "--fontBody",
subsets: ["latin"],
weight: "400",
});
export function generateStaticParams() {
return ["en", "it"].map((l) => ({ locale: l }));
}
export const metadata = {
title: {
default: "Cantina Scrinzi Saltarius",
template: "%s | Cantina Scrinzi Saltarius",
},
description:
"Saltarius Trento Doc: eleganza e complessità da Chardonnay e Pinot Nero, oltre 60 mesi di affinamento e una tradizione familiare tramandata da generazioni.",
icons: {
icon: [
{ rel: "icon", url: "/favicon.ico" },
{ rel: "icon", type: "image/svg+xml", url: "/favicon.svg" },
{
rel: "icon",
type: "image/png",
sizes: "96x96",
url: "/favicon-96x96.png",
},
],
apple: [
{
rel: "apple-touch-icon",
sizes: "180x180",
url: "/apple-touch-icon.png",
},
],
},
manifest: "/site.webmanifest",
openGraph: {
title: "Cantina Scrinzi Saltarius",
description:
"Saltarius Trento Doc: eleganza e complessità da Chardonnay e Pinot Nero, oltre 60 mesi di affinamento e una tradizione familiare tramandata da generazioni.",
url: "https://scrinzi.com",
siteName: "Cantina Scrinzi Saltarius",
images: [
{
url: "https://scrinzi.com/og-image.webp",
width: 1200,
height: 630,
alt: "Cantina Scrinzi Saltarius",
},
],
locale: "it_IT",
type: "website",
},
twitter: {
card: "summary_large_image",
title: "Cantina Scrinzi Saltarius",
description:
"Saltarius Trento Doc: eleganza e complessità da Chardonnay e Pinot Nero, oltre 60 mesi di affinamento e una tradizione familiare tramandata da generazioni.",
images: ["https://scrinzi.com/og-image.webp"],
},
};
export const viewport = { width: "device-width", initialScale: 1 };
export default async function RootLayout({ children, params }) {
const { locale } = await params;
if (!["en", "it"].includes(locale)) {
return notFound();
}
console.log("Requested Locale: ", locale);
setRequestLocale(locale);
const messages = await getMessages();
console.log("Layout Messages: ", messages);
console.log("Layout Locale: ", locale);
return (
<html lang={locale}>
<Script src="https://cdn-cookieyes.com/client_data/9b29b34c372b76504e2ff609/script.js" />
<body className={`${operetta.variable} ${montserrat.variable}`}>
<NextIntlClientProvider messages={messages} locale={locale}>
<Header locale={locale} />
{children}
<Footer locale={locale} />
</NextIntlClientProvider>
</body>
</html>
);
}
- This is the Header component where I had to change only one word:
import Link from 'next/link';
import styles from './header.module.css';
import Image from 'next/image';
import { useTranslations } from 'next-intl';
import { getTranslations } from 'next-intl/server';
export default async function Header({ locale }) {
const t = await getTranslations('Header');
console.log("Header Messages: ", t('contacts'));
return (
<header className={styles.header}>
<div className={`container ${styles.headerContainer}`}>
<Link href={`/${locale}`} className={styles.headerLogo} aria-label="Home">
<Image src="/logo.svg" alt="Cantina Scrinzi Saltarius Logo" width={50} height={50} />
<span className={styles.logoText}>Saltarius</span>
</Link>
<nav className={styles.nav}>
<Link href={`/${locale}/contatti`} className={styles.navLink}>
{t('contacts')}
</Link>
</nav>
</div>
</header>
);
}
I can’t understand why, however, even if I visit /it or /en, the languages are always taken from /it, unless I remove the fallback, but in that case, it tells me that it can’t find the undefined.json file.
Am I doing something wrong when passing parameters?
These are the console.logs:
Requested Locale: en
Layout Messages: {
Header: { contacts: 'Contatti' },
Intro: {
leftTitle: 'L’eleganza di un Metodo Classico Trento Doc',
leftText: '**Saltarius** nasce da un perfetto equilibrio tra **Chardonnay (60%)** e **Pinot Nero (40%)**, coltivati con cura nelle colline roveretane. Le uve, allevate a pergola semplice trentina e raccolte a rese contenute, riposano oltre 60 mesi sui lieviti, sviluppando complessità, finezza e grande eleganza. **Un Trento Doc capace di raccontare la sua terra in ogni sorso.**',
rightTitle: 'Una passione di famiglia tramandata da generazioni',
rightText: 'Saltarius è molto più di un vino: è la storia di una famiglia che da quattro generazioni custodisce l’arte della viticoltura. Dal bisnonno Achille al nipote Andrea, giovane enotecnico, ogni generazione ha portato avanti con dedizione un sogno fatto di tradizione e innovazione. Il primo raccolto del 2019, realizzato insieme al nonno Franco, ha segnato l’inizio di una missione: preservare un sapere artigianale sempre più raro e valorizzare un territorio unico.'
},
Pills: [
{
title: 'Territorio unico',
text: 'Uve allevate sulle colline roveretane, accarezzate dal sole e dal vento dell’Ora del Garda.'
},
{
title: 'Metodo artigianale',
text: 'Oltre 60 mesi di affinamento sui lieviti per un Trento Doc di rara complessità.'
},
{
title: 'Tradizione di famiglia',
text: 'Quattro generazioni di passione e dedizione, unite dall’amore per la viticoltura.'
}
]
}
Layout Locale: en
Header Messages: Contatti
(As you can see the locale is /en but the data is taken in Italian)
Can anyone else shed some light on this? Am I doing something wrong?
Thanks in advance