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>;
}

🎉 Done!

You now have a fully functional i18n setup in Next.js App Router using next-intl!

Your app will:

  • Route via /en, /bn, etc.
  • Load the right translations
  • Handle locale detection and fallback

💬 Got stuck? Drop a comment or question below — happy to help!