Home Quizzes Leaderboard Competitions Learn Hire Us
About Contact
Log In Sign Up
Learn Next.js Internationalisation (i18n)

Internationalisation (i18n)

⏱ 15 min read read
Setup next-intl:

npm install next-intl

// Project structure with i18n routing:

app/

[locale]/

layout.tsx

page.tsx

messages/

en.json

fr.json

es.json

i18n.ts

middleware.ts

Messages File (en.json):

{

"Home": {

"title": "Welcome to the App",

"description": "Learn programming with {count} lessons",

"students": "{count, plural, =0 {No students} =1 {1 student} other
{# students}}"

},

"Navigation": {

"home": "Home",

"courses": "Courses",

"about": "About"

}

}

Using Translations:

// Server Component

import { useTranslations } from 'next-intl';

export default function HomePage() {

const t = useTranslations('Home');

return (

<div>

<h1>{t('title')}</h1>

<p>{t('description', { count: 20 })}</p>

<p>{t('students', { count: 3 })}</p>

</div>

);

}

Locale-Aware Formatting:

import { useFormatter } from 'next-intl';

const format = useFormatter();

format.dateTime(new Date(), { dateStyle: 'long' })

// en: 'December 25, 2024' fr: '25 décembre 2024'

format.number(1234567.89, { style: 'currency', currency: 'USD' })

// en: '$1,234,567.89' fr: '1 234 567,89 $US'

format.relativeTime(-3, 'day')

// en: '3 days ago' fr: 'il y a 3 jours'

next-intl is the most popular i18n library for Next.js App Router.

URLs become /en/about, /fr/about, /es/about automatically.

Middleware detects browser language and redirects to correct locale.

Alternative: next-i18next (Pages Router), i18next (manual setup).
Code Example
// i18n.ts

import { getRequestConfig } from 'next-intl/server';

export default getRequestConfig(async ({ locale }) => ({

messages: (await import(`./messages/${locale}.json`)).default,

}));

// middleware.ts

import createMiddleware from 'next-intl/middleware';

export default createMiddleware({

locales: ['en', 'fr', 'es'],

defaultLocale: 'en',

});

export const config = { matcher:
['/((?!api|\_next|.*\..*).*)'] };

// messages/en.json

// { "Students": { "title": "Students", "count": "{n}
students", "add": "Add Student" } }

// messages/fr.json

// { "Students": { "title": "Étudiants", "count": "{n}
étudiants", "add": "Ajouter un étudiant" } }

// app/[locale]/students/page.tsx

import { useTranslations, useFormatter } from 'next-intl';

export default function StudentsPage({ students }: { students:
any[] }) {

const t = useTranslations('Students');

const format = useFormatter();

return (

<div>

<h1>{t('title')}</h1>

<p>{t('count', { n: students.length })}</p>

<ul>

{students.map(s => (

<li key={s.id}>

{s.name} --- {format.number(s.grade)}

<time>{format.dateTime(s.createdAt, { dateStyle: 'medium'
})}</time>

</li>

))}

</ul>

</div>

);

}

// Language switcher component

'use client';

import { useRouter, usePathname } from 'next/navigation';

import { useLocale } from 'next-intl';

export function LocaleSwitcher() {

const router = useRouter();

const pathname = usePathname();

const locale = useLocale();

const switchTo = (next: string) => {

// Replace current locale prefix in path

const newPath = pathname.replace(`/${locale}`, `/${next}`);

router.push(newPath);

};

return (

<div>

{['en', 'fr', 'es'].map(l => (

<button key={l} onClick={() => switchTo(l)}

style={{ fontWeight: l === locale ? 'bold' : 'normal' }}>

{l.toUpperCase()}

</button>

))}

</div>

);

}
← Testing --- Jest & Playwright Deployment --- Vercel & Environment Vari →

Log in to track your progress and earn badges as you complete lessons.

Log In to Track Progress