#nextjs
#i18n
#localization
#translation
Day 26 — Internationalization (i18n): Going Global
🧭 Day 26 — Internationalization (i18n): Going Global
Zero to Hero — Hands-on Next.js Tutorial
Users prefer apps in their native language.
Next.js supports i18n routing out of the box (e.g., /en/about, /es/about), but we need to implement the translation logic ourselves.
🟦 1. Directory Structure
We move everything inside a [lang] dynamic segment.
src/
app/
[lang]/
page.tsx
layout.tsx
Now params.lang is available to every page and layout!
🟩 2. Middleware for Detection
We need Middleware to detect the user’s preferred language (Accept-Language header) and redirect them if the locale is missing.
// middleware.ts
import { match } from '@formatjs/intl-localematcher'
import Negotiator from 'negotiator'
let locales = ['en', 'es', 'fr']
let defaultLocale = 'en'
export function middleware(request) {
const pathname = request.nextUrl.pathname
// Check if there is any supported locale in the pathname
const pathnameIsMissingLocale = locales.every(
(locale) => !pathname.startsWith(`/${locale}/`) && pathname !== `/${locale}`
)
// Redirect if there is no locale
if (pathnameIsMissingLocale) {
const locale = getLocale(request) // Implement using Negotiator
return NextResponse.redirect(
new URL(`/${locale}/${pathname}`, request.url)
)
}
}
🟧 3. Dictionaries
The simplest way to handle text is JSON files on the server.
dictionaries/en.json:
{ "hello": "Hello World", "cart": "Add to Cart" }
dictionaries/es.json:
{ "hello": "Hola Mundo", "cart": "Añadir al Carrito" }
Fetching the dictionary:
// src/app/[lang]/get-dictionary.ts
import 'server-only'
const dictionaries = {
en: () => import('./dictionaries/en.json').then((module) => module.default),
es: () => import('./dictionaries/es.json').then((module) => module.default),
}
export const getDictionary = async (locale) => dictionaries[locale]()
🟥 4. Use in Component
import { getDictionary } from './get-dictionary'
export default async function Page({ params: { lang } }) {
const dict = await getDictionary(lang)
return <button>{dict.cart}</button> // "Add to Cart" or "Añadir al Carrito"
}
🧪 Challenge: Day 26
- Move your
page.tsxinto[lang]. - Create
en.jsonandes.jsonwith a greeting. - Update middleware to enforce the prefix.
- Visit
/-> Redirects to/en. - Visit
/es-> Shows “Hola Mundo”.
See you tomorrow for Performance Optimization! 🏎️