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

Apr 27, 2025 - 05:17
 0
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<{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>;
}