Internationalization in Next.js
What is Internationalization (i18n)? Internationalization, or i18n, is the process of designing your app so it can easily be adapted to different languages and regions without changing the codebase. In web apps, this typically means: Displaying text in different languages (e.g., English, Bengali) Formatting dates, numbers, and currencies based on the locale Changing URLs to reflect the language (/en/about vs /bn/about) Why Use next-intl? next-intl is a powerful library that enhances i18n support in Next.js. Here's why it's awesome: ✅ Fully supports App Router in Next.js 13+ ✅ Enables per-locale routing (/en, /bn, etc.) ✅ Provides hooks like useTranslations for client-side translations ✅ Offers getTranslations for server-side rendering ✅ Handles locale detection automatically ✅ Middleware support for redirecting users based on their browser's locale ✅ Scales well for large multilingual apps Let’s go step by step 1. Install next-intl First, install the package: npm install next-intl Folder Structure src ├── app │ ├── [locale] │ ├── layout.tsx │ ├── page.tsx ├──i18n │ ├── locales │ │ ├── en.json │ │ ├── bn,json │ ├── routing.ts │ ├── navigation.ts │ ├── request.ts ├── middleware.ts ├── next.config.js 2. Create the /i18n directory Inside your app, create a locales folder with translation files: /locales ├── en.json └── bn.json Example en.json: { "home": { "title": "Hello world!", "about": "Go to the about page" } } Example bn.json: { "home": { "title": "হ্যালো বিশ্ব!", "about": "সম্বন্ধে পৃষ্ঠায় যান" } } 3. Define Supported Locales Create a file i18n/routing.ts to hold your config: import {defineRouting} from 'next-intl/routing'; export const routing = defineRouting({ // A list of all locales that are supported locales: ['en', 'bn'], // Used when no locale matches defaultLocale: 'en' }); 4. Define Navigation Once we have our routing configuration in place, we can use it to set up the navigation APIs. Create a file i18n/navigation.ts to navigation: import {createNavigation} from 'next-intl/navigation'; import {routing} from './routing'; // Lightweight wrappers around Next.js' navigation // APIs that consider the routing configuration export const {Link, redirect, usePathname, useRouter, getPathname} = createNavigation(routing); 5. Setup Middleware for Locale Detection import createMiddleware from 'next-intl/middleware'; import {routing} from './i18n/routing'; export default createMiddleware(routing); export const config = { // Match all pathnames except for // - … if they start with `/api`, `/trpc`, `/_next` or `/_vercel` // - … the ones containing a dot (e.g. `favicon.ico`) matcher: '/((?!api|trpc|_next|_vercel|.*\\..*).*)' }; This middleware will redirect users to the right locale based on the path or browser settings. 6. Configure next.config.js Update your next.config.js: import {NextConfig} from 'next'; import createNextIntlPlugin from 'next-intl/plugin'; const nextConfig: NextConfig = {}; const withNextIntl = createNextIntlPlugin(); export default withNextIntl(nextConfig); Make sure you do not use i18n field in this config. next-intl handles that routing now. 7. Provide Translations via getRequestConfig import {getRequestConfig} from 'next-intl/server'; import {hasLocale} from 'next-intl'; import {routing} from './routing'; export default getRequestConfig(async ({requestLocale}) => { // Typically corresponds to the `[locale]` segment const requested = await requestLocale; const locale = hasLocale(routing.locales, requested) ? requested : routing.defaultLocale; return { locale, messages: (await import(`./locales/${locale}.json`)).default }; }); 8. Use NextIntlClientProvider in layout.tsx import {NextIntlClientProvider, hasLocale} from 'next-intl'; import {notFound} from 'next/navigation'; import {routing} from '@/i18n/routing'; export default async function LocaleLayout({ children, params }: { children: React.ReactNode; params: Promise; }) { // Ensure that the incoming `locale` is valid const {locale} = await params; if (!hasLocale(routing.locales, locale)) { notFound(); } return ( {children} ); } 9. Use Translations in Your Page import {useTranslations} from 'next-intl'; import {Link} from '@/i18n/navigation'; export default function HomePage() { const t = useTranslations('home'); return ( {t('title')} {t('about')} ); } In case of async components, you can use the awaitable getTranslations function instead: import {getTranslations} from 'next-intl/server'; export default async function HomePage() { const t = await

What is Internationalization (i18n)?
Internationalization, or i18n, is the process of designing your app so it can easily be adapted to different languages and regions without changing the codebase.
In web apps, this typically means:
- Displaying text in different languages (e.g., English, Bengali)
- Formatting dates, numbers, and currencies based on the locale
- Changing URLs to reflect the language (
/en/about
vs/bn/about
)
Why Use next-intl
?
next-intl
is a powerful library that enhances i18n support in Next.js. Here's why it's awesome:
✅ Fully supports App Router in Next.js 13+
✅ Enables per-locale routing (/en
, /bn
, etc.)
✅ Provides hooks like useTranslations
for client-side translations
✅ Offers getTranslations
for server-side rendering
✅ Handles locale detection automatically
✅ Middleware support for redirecting users based on their browser's locale
✅ Scales well for large multilingual apps
Let’s go step by step
1. Install next-intl
First, install the package:
npm install next-intl
Folder Structure
src
├── app
│ ├── [locale]
│ ├── layout.tsx
│ ├── page.tsx
├──i18n
│ ├── locales
│ │ ├── en.json
│ │ ├── bn,json
│ ├── routing.ts
│ ├── navigation.ts
│ ├── request.ts
├── middleware.ts
├── next.config.js
2. Create the /i18n
directory
Inside your app, create a locales
folder with translation files:
/locales
├── en.json
└── bn.json
Example en.json
:
{
"home": {
"title": "Hello world!",
"about": "Go to the about page"
}
}
Example bn.json
:
{
"home": {
"title": "হ্যালো বিশ্ব!",
"about": "সম্বন্ধে পৃষ্ঠায় যান"
}
}
3. Define Supported Locales
Create a file i18n/routing.ts
to hold your config:
import {defineRouting} from 'next-intl/routing';
export const routing = defineRouting({
// A list of all locales that are supported
locales: ['en', 'bn'],
// Used when no locale matches
defaultLocale: 'en'
});
4. Define Navigation
Once we have our routing configuration in place, we can use it to set up the navigation APIs.
Create a file i18n/navigation.ts
to navigation:
import {createNavigation} from 'next-intl/navigation';
import {routing} from './routing';
// Lightweight wrappers around Next.js' navigation
// APIs that consider the routing configuration
export const {Link, redirect, usePathname, useRouter, getPathname} =
createNavigation(routing);
5. Setup Middleware for Locale Detection
import createMiddleware from 'next-intl/middleware';
import {routing} from './i18n/routing';
export default createMiddleware(routing);
export const config = {
// Match all pathnames except for
// - … if they start with `/api`, `/trpc`, `/_next` or `/_vercel`
// - … the ones containing a dot (e.g. `favicon.ico`)
matcher: '/((?!api|trpc|_next|_vercel|.*\\..*).*)'
};
This middleware will redirect users to the right locale based on the path or browser settings.
6. Configure next.config.js
Update your next.config.js
:
import {NextConfig} from 'next';
import createNextIntlPlugin from 'next-intl/plugin';
const nextConfig: NextConfig = {};
const withNextIntl = createNextIntlPlugin();
export default withNextIntl(nextConfig);
Make sure you do not use i18n
field in this config. next-intl
handles that routing now.
7. Provide Translations via getRequestConfig
import {getRequestConfig} from 'next-intl/server';
import {hasLocale} from 'next-intl';
import {routing} from './routing';
export default getRequestConfig(async ({requestLocale}) => {
// Typically corresponds to the `[locale]` segment
const requested = await requestLocale;
const locale = hasLocale(routing.locales, requested)
? requested
: routing.defaultLocale;
return {
locale,
messages: (await import(`./locales/${locale}.json`)).default
};
});
8. Use NextIntlClientProvider
in layout.tsx
import {NextIntlClientProvider, hasLocale} from 'next-intl';
import {notFound} from 'next/navigation';
import {routing} from '@/i18n/routing';
export default async function LocaleLayout({
children,
params
}: {
children: React.ReactNode;
params: Promise<{locale: string}>;
}) {
// Ensure that the incoming `locale` is valid
const {locale} = await params;
if (!hasLocale(routing.locales, locale)) {
notFound();
}
return (
<html lang={locale}>
<body>
<NextIntlClientProvider>{children}NextIntlClientProvider>
body>
html>
);
}
9. Use Translations in Your Page
import {useTranslations} from 'next-intl';
import {Link} from '@/i18n/navigation';
export default function HomePage() {
const t = useTranslations('home');
return (
<div>
<h1>{t('title')}h1>
<Link href="/about">{t('about')}Link>
div>
);
}
In case of async components, you can use the awaitable getTranslations
function instead:
import {getTranslations} from 'next-intl/server';
export default async function HomePage() {
const t = await getTranslations('home');
return <h1>{t('title')}h1>;
}