A minimal Next.js reference project demonstrating internationalization (i18n) in the Pages Router (as opposed to the App Router), using next-i18next.
Live demo: https://next-page-router-i18n.vercel.app/
- Serves two pages (
/and/second) that each render a translatedhellostring sourced from a sharedcommontranslation namespace. - Translation strings live in
public/locales/<locale>/common.json— one JSON file per locale. - Locale-prefixed routing is provided out of the box by Next.js's built-in i18n support, configured in
next-i18next.config.js:- Supported locales:
en,es,fr - Default locale:
es - Automatic browser-locale detection: disabled (
localeDetection: false), so visiting/always serves the default locale (es) instead of redirecting based on the browser'sAccept-Languageheader.
- Supported locales:
- Each page fetches its translations at build time via
getStaticProps+serverSideTranslations, and renders them with theuseTranslationhook fromreact-i18next. - Each page links to the other. A plain
<Link href="...">preserves the locale currently being viewed, so navigating from/frlands on/fr/second. - A
<LocaleSwitcher />dropdown on both pages switches locale while staying on the current page. - Styling uses Tailwind CSS, which follows the OS light/dark preference.
public/locales/
en/common.json # "Hello!"
es/common.json # "¡Hola!"
fr/common.json # "Bonjour!"
src/
components/
LocaleSwitcher.js # locale dropdown
pages/
_app.js # wrapped in appWithTranslation()
index.js # "/"
second.js # "/second"
styles/globals.css
next-i18next.config.js # locales, defaultLocale, localeDetection
next.config.js # re-exports the i18n config to Next.js
LocaleSwitcher.js reads the available locales off the router and navigates imperatively on change:
router.push({ pathname, query }, asPath, { locale: e.target.value })router.push takes (href, as, options). Passing the route pattern plus params ({ pathname, query }) as the href — rather than the already-resolved asPath — is what keeps this correct once dynamic routes such as /blog/[slug] are added, since Next.js interpolates the params itself instead of reverse-matching a concrete path.
Install dependencies and start the dev server:
npm install
npm run devOpen http://localhost:3000 in your browser.
This project has no automated test suite — verify the i18n behavior manually:
- Start the dev server (
npm run dev). - Visit each locale-prefixed URL and confirm the page renders the correctly translated
hellotext:- http://localhost:3000 — default locale (
es), no prefix: "¡Hola!" - http://localhost:3000/en — "Hello!"
- http://localhost:3000/fr — "Bonjour!"
- http://localhost:3000 — default locale (
- Repeat for the second page (append
/second, e.g. http://localhost:3000/en/second). - Check that navigation preserves the locale: from
/fr, click Go to Second Page and confirm you land on/fr/secondwith the French text still showing. - Check the locale dropdown: on either page, pick a different locale and confirm the URL prefix updates, the text switches language, and you stay on the same page (
/secondstays/second). - Confirm locale detection is off: visiting
/with a browser set to a non-default language (e.g. English) should still serve theesdefault rather than redirecting.
To add or change translations, edit the corresponding public/locales/<locale>/common.json file and reload the page. To add a locale, create a new folder under public/locales/ and add it to the locales array in next-i18next.config.js — the dropdown picks it up automatically.
npm run build
npm run startnext build prerenders every page once per locale, so the two pages produce six static HTML files.