Internationalization in Next.js
Jakaria Masum

Jakaria Masum @jakaria

About: Full-Stack Developer | MERN Stack | Next.js | TypeScript

Location:
Bogura, Bangladesh
Joined:
Jun 26, 2024

Internationalization in Next.js

Publish Date: Apr 27
0 0

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

2. Create the /i18n directory

Inside your app, create a locales folder with translation files:

/locales
  ├── en.json
  └── bn.json
Enter fullscreen mode Exit fullscreen mode

Example en.json:

{
  "home": {
    "title": "Hello world!",
    "about": "Go to the about page"
  }
}
Enter fullscreen mode Exit fullscreen mode

Example bn.json:

{
  "home": {
    "title": "হ্যালো বিশ্ব!",
    "about": "সম্বন্ধে পৃষ্ঠায় যান"
  }
}
Enter fullscreen mode Exit fullscreen mode

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'
});
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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|.*\\..*).*)'
};
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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
  };
});
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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>;
}
Enter fullscreen mode Exit fullscreen mode

🎉 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!

Comments 0 total

    Add comment