Building a Multi-Language Website with Next.js 15 & Modern i18n

Himmat Regar Jun 30, 2025, 10:44 PM
nextjs
Views 164
Blog Thumbnail

1. Why bother with i18n in 2025?

  • 58 % of the web’s visitors prefer content in their own language, even if they can read English.

  • Search engines rank locale-specific pages higher for users in that region.

  • Next.js 15 ships powerful server-component-first primitives that make localisation practically free in terms of bundle size. nextjs.org


2. Understanding Next.js i18n choices

Router Built-in i18n routing Typical libs When to pick
Pages Router i18n key in next.config.js (since v10) next-i18next, react-intl Legacy apps, quick upgrades
App Router (v13 → 15) Dynamic [lang] segment + middleware; no i18n key next-intl, i18next, lingui, paraglide-next New projects, Server Components, finest perf

Tip: You can mix both routers during migration – App Router files in /app, legacy pages in /pages.


3. Quick project setup

bash
npx create-next-app@latest my-multilang-site cd my-multilang-site npm install next-intl

4. Declaring locales (App Router)

Create a top-level dynamic segment:

text
/app/[lang]/layout.tsx /app/[lang]/page.tsx

Add middleware to auto-detect the visitor’s language (using @formatjs/intl-localematcher or similar):

ts
// middleware.ts import { NextResponse } from 'next/server' import Negotiator from 'negotiator' import { match } from '@formatjs/intl-localematcher' const locales = ['en', 'de', 'fr'] const defaultLocale = 'en' function getLocale(request: Request) { const negotiator = new Negotiator({ headers: request.headers }) return match(negotiator.languages(), locales, defaultLocale) } export function middleware(request: any) { const { pathname } = request.nextUrl if (locales.some(l => pathname.startsWith(`/${l}`))) return request.nextUrl.pathname = `/${getLocale(request)}${pathname}` return NextResponse.redirect(request.nextUrl) }

This pattern is lifted from the official docs and keeps language logic on the edge without impacting core code. nextjs.org


5. Loading translations efficiently

Create dictionaries inside each locale directory:

bash
/app/[lang]/dictionaries/en.json /app/[lang]/dictionaries/de.json

Then a tiny helper:

ts
// app/[lang]/dictionaries.ts import 'server-only' const dict = { en: () => import('./dictionaries/en.json').then(m => m.default), de: () => import('./dictionaries/de.json').then(m => m.default), } export const getDictionary = async (locale: 'en' | 'de') => dict[locale]()

Because this runs only on the server, none of these JSON files bloat the client bundle. nextjs.org


6. Rendering a page with translations

tsx
// app/[lang]/page.tsx import { getDictionary } from './dictionaries' export default async function Home({ params: { lang } }: { params: { lang: 'en' | 'de' } }) { const t = await getDictionary(lang) return ( <section> <h1>{t.home.heroTitle}</h1> <button>{t.home.cta}</button> </section> ) }

7. Adding next-intl for rich features (formatting, plurals)

bash
npm install next-intl
tsx
// app/[lang]/provider.tsx import { NextIntlClientProvider } from 'next-intl' import messages from './dictionaries/en.json' export default function LocaleProvider({ children, lang }: { children: React.ReactNode; lang: string }) { return ( <html lang={lang}> <body> <NextIntlClientProvider locale={lang} messages={messages}> {children} </NextIntlClientProvider> </body> </html> ) }

next-intl works seamlessly with Server Components, static exports, ISR, and type-safety. github.com

Use it in your client components:

tsx
'use client' import { useTranslations } from 'next-intl' export default function LangSwitcher() { const t = useTranslations('nav') return <nav>{t('home')}</nav> }

8. Generating static pages for every locale

ts
// app/[lang]/layout.tsx export async function generateStaticParams() { return [{ lang: 'en' }, { lang: 'de' }, { lang: 'fr' }] }

This tells Next.js to pre-render /en, /de, /fr, while still supporting dynamic fallback for new languages. nextjs.org


9. SEO best-practices

  1. <html lang> – already set in layout.tsx.

  2. <link rel="alternate" hreflang> – add via the metadata API.

  3. Consistent URL strategy:

    • /de/about (sub-path) or

    • de.example.com/about (domain).

  4. Localised metadata (generateMetadata) per page.


10. Asset & CMS localisation tips

  • Keep language-specific images in /public/[lang]/….

  • If using a headless CMS, add a locale field and fetch via the same [lang] param.

  • Remember to localise date & currency formatting via Intl APIs or next-intl helpers.


11. Migration snapshot (Pages → App Router)

Task Effort Notes
Move pages into /app/[lang] folders 🟒 Low Copy JSX, remove getStaticProps
Replace next-i18next with next-intl 🟑 Medium Map namespaces → message JSON
Adjust dynamic imports 🟒 Low Server Components handle them
Update next.config.js (remove i18n) 🟒 Low Rely on middleware instead

12. Useful starter templates & refs

  • Minimal i18n routing repo – official example. nextjs.org

  • Blog: Implementing i18n in Next.js 15 with i18next – great for cookie-based language switching. azharzaman.com

  • Ali-Dev tutorial on App Router i18n – in-depth guide with animated switcher component. ali-dev.com


Conclusion

Next.js 15’s App Router, combined with libraries like next-intl or i18next, makes shipping a blazing-fast, SEO-friendly multi-language site simpler than ever. By pairing server-side translation loading with smart middleware and static generation, you get the best of all worlds: razor-thin bundles, instant locale routing, and happy global users. πŸŒπŸš€

Happy coding – and Guten Code!

Frequently Asked Questions (FAQs) about building a multi-language site with Next.js 15 + i18n

# Question Answer (summary)
1 Does Next.js have “built-in” i18n? Pages Router still supports the original i18n key in next.config.js. In the newer App Router, you model the locale with a dynamic [lang] segment and handle redirection in middleware.ts—that’s now the officially documented path. nextjs.org
2 Which library should I use? For App Router projects, next-intl is the most “native” choice (server-component friendly, type-safe, supports React 19). Alternatives like i18next, lingui, or paraglide-next work too—pick the one that matches your team’s workflow. ali-dev.com
3 How does automatic language detection work? Next.js doesn’t auto-detect for you, but the docs show a pattern that reads the browser’s Accept-Language header with Negotiator and redirects to /{locale} in middleware. nextjs.org
4 Will my translation JSON files bloat the client bundle? Not if you load them on the server: the doc’s “dictionary” pattern (import 'server-only') fetches JSON in Server Components, so only rendered HTML reaches the browser. nextjs.org
5 What URL strategy is best—sub-path (/fr) or sub-domain (fr.example.com)? Both are supported. Sub-paths are easy and keep a single deployment; sub-domains give you per-country hosting and clearer analytics. Choose based on CDN/SEO requirements. nextjs.org
6 How do I statically prerender every locale? Expose all supported locales in generateStaticParams (e.g. return [{lang:'en'},{lang:'de'}]). Next.js will emit /en, /de, etc., at build time. nextjs.org
7 Can I mix Pages Router and App Router while migrating? Yes. Keep legacy pages in /pages (still using the old i18n routing) and move new screens into /app/[lang]/… until the migration is complete. This is fully supported by Next.js 15. nextjs.org
8 Does changing language trigger a full page reload? No. Navigating between /en/* and /fr/* uses the client router; only the segments that change are re-rendered, so the switch feels instant. (Behaviour verified in App Router docs.) nextjs.org
9 What about SEO—do I need <link rel="alternate" hreflang>? Absolutely. Add them with the Metadata API or inside <Head>. Combined with an explicit <html lang> (set in layout.tsx), this lets search engines index the correct locale. nextjs.org
10 How hard is it to add a new language later? Drop a new JSON dictionary, add it to your locales array, and redeploy. With fallback routing (or ISR) users can hit the new locale immediately without a full rebuild.
11 Can I fall back to English if a key is missing? Libraries like next-intl and i18next include fallback logic (fallbackLocale, fallbackLng). Treat missing keys as build-time warnings to avoid broken UI in production.
12 Is localisation really worth the effort? Studies show 65 %+ of users prefer content in their own language and many won’t buy in another one, so multilingual sites typically see higher engagement and conversion. weglot.com

TL;DR: Use a dynamic [lang] segment + middleware for routing, load translations on the server to keep bundles lean, and lean on next-intl for formatting, plural rules, and fallbacks. With Next.js 15’s App Router, building a fast, SEO-friendly multilingual site is mostly plumbing, not pain. πŸŒπŸš€

 

Comments

Please login to leave a comment.

No comments yet.

Related Posts

nextjs-explained-beginners-guide-2025
374 viewsnextjs
Himmat Regar β€’ Jun 27, 2025, 10:12 AM

Next.js Explained: A 2025 Beginner’s Guide to the React...

nextjs-file-based-routing-guide
369 viewsnextjs
Himmat Regar β€’ Jun 27, 2025, 11:23 AM

Understanding File-Based Routing in Next.js

nextjs-tailwind-css-perfect-ui-pairing
161 viewsnextjs
Himmat Regar β€’ Jun 30, 2025, 5:25 PM

Next.js 15 + Tailwind CSS 4: The Perfect UI Pairing

image-optimization-nextjs-everything-you-should-know
287 viewsnextjs
Himmat Regar β€’ Jun 29, 2025, 5:20 PM

Image Optimization in Next.js: Everything You Should Kn...

nextjs-incremental-static-regeneration-isr-guide
292 viewsnextjs
Himmat Regar β€’ Jun 29, 2025, 5:18 PM

Incremental Static Regeneration (ISR) Explained with Ex...

nextjs-vs-react-differences
353 viewsnextjs
Himmat Regar β€’ Jun 27, 2025, 11:09 AM

Next.js vs React: What’s the Difference and When to Use...

why-every-developer-should-learn-typescript-in-2025
30 viewsnextjs
Himmat Regar β€’ Jul 3, 2025, 5:56 PM

Why Every Developer Should Learn TypeScript in 2025

nextjs-markdown-blog-tutorial
375 viewsnextjs
Himmat Regar β€’ Jun 27, 2025, 10:18 AM

How to Build Your First Blog Using Next.js and Markdown

nextjs-api-routes-backend-functionality
279 viewsnextjs
Himmat Regar β€’ Jun 29, 2025, 5:03 PM

How to Use API Routes in Next.js for Backend Functional...

what-is-a-blog
183 viewsMarketing
Himmat Kumar β€’ Mar 12, 2025, 7:06 AM

What is a Blog? Understanding Its Purpose, Structure, a...