#1 Data Analytics Program in India
₹2,499₹1,499Enroll Now
5 min read
•Question 27 of 47medium

How to implement i18n in Next.js?

Internationalization and routing.

What You'll Learn

  • i18n routing
  • Language detection
  • Content translation

Folder Structure

code.jsJavaScript
app/
ā”œā”€ā”€ [lang]/
│   ā”œā”€ā”€ layout.tsx
│   ā”œā”€ā”€ page.tsx
│   └── about/
│       └── page.tsx
└── dictionaries/
    ā”œā”€ā”€ en.json
    └── es.json

Dictionary Files

data.jsonJSON
// dictionaries/en.json
{
  "welcome": "Welcome",
  "about": "About Us"
}

// dictionaries/es.json
{
  "welcome": "Bienvenido",
  "about": "Sobre Nosotros"
}

Load Dictionary

code.txtTSX
// lib/dictionaries.ts
const dictionaries = {
  en: () => import('../dictionaries/en.json').then((m) => m.default),
  es: () => import('../dictionaries/es.json').then((m) => m.default),
};

export const getDictionary = async (locale: string) => {
  return dictionaries[locale]();
};

Page Usage

code.txtTSX
// app/[lang]/page.tsx
import { getDictionary } from '@/lib/dictionaries';

export default async function Home({ params: { lang } }) {
  const dict = await getDictionary(lang);

  return <h1>{dict.welcome}</h1>;
}

Middleware for Detection

code.txtTSX
import { NextRequest, NextResponse } from 'next/server';

const locales = ['en', 'es'];

export function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl;

  const pathnameHasLocale = locales.some(
    (locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
  );

  if (pathnameHasLocale) return;

  const locale = request.headers.get('accept-language')?.split(',')[0].split('-')[0] || 'en';

  request.nextUrl.pathname = `/${locale}${pathname}`;
  return NextResponse.redirect(request.nextUrl);
}