I18n Setup in Next.js with next-intl

Internationalization (i18n) is essential for applications targeting multiple languages. The next-intl library makes it easy to implement i18n in Next.js with support for both static and dynamic rendering.

In this article, we’ll walk through a clean and modular setup to localize your app URLs like:

/en/about
/az/about

1. Installation

npm install next-intl

2. Folder Structure

Create a file-based route structure like:

src/
├── app/
│   └── [locale]/
│       ├── layout.tsx
│       └── page.tsx
├── i18n/
│   ├── request.ts
│   └── routing.ts
└── locales/
    ├── az.json
    └── en.json

3. Configure Routing

i18n/routing.ts:

import { defineRouting } from "next-intl/routing";

export const routing = defineRouting({
  locales: ["en", "az"],
  defaultLocale: "en",
  localePrefix: {
    mode: "always",
    prefixes: {
      en: "/en",
      az: "/az",
    },
  },
  pathnames: {
    "/": "/",
    "/about": {
      en: "/about",
      az: "/about",
    },
  },
});

4. Provide Translations

i18n/request.ts:

import { getRequestConfig } from "next-intl/server";
import { routing } from "@/i18n/routing";
import { notFound } from "next/navigation";

export default getRequestConfig(async ({ locale = "en" }) => {
  if (!routing.locales.includes(locale)) notFound();

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

5. Middleware Setup

middleware.ts:

import createMiddleware from "next-intl/middleware";
import { routing } from "@/i18n/routing";

export default createMiddleware({
  locales: routing.locales,
  defaultLocale: routing.defaultLocale,
});

export const config = {
  matcher: ["/", "/(az|en)/:path*"],
};

6. Configure next.config.mjs

import createNextIntlPlugin from "next-intl/plugin";

const withNextIntl = createNextIntlPlugin();

/** @type {import('next').NextConfig} */
const nextConfig = {};

export default withNextIntl(nextConfig);

7. Add Provider in Layout

// layout.tsx
import { getMessages } from "next-intl/server";
import { NextIntlClientProvider } from "next-intl";

export default async function RootLayout({
  children,
  params: { locale },
}: {
  children: React.ReactNode;
  params: { locale: string };
}) {
  const messages = await getMessages();

  return (
    <html lang={locale}>
      <body>
        <NextIntlClientProvider messages={messages} locale={locale}>
          {children}
        </NextIntlClientProvider>
      </body>
    </html>
  );
}

8. Use useTranslations() in Components

import { useTranslations } from "next-intl";

export default function AboutPage() {
  const t = useTranslations("about");

  return <h1>{t("title")}</h1>;
}
import { usePathname, useRouter } from "next-intl/client";
import Link from "next-intl/link";

<Link href="/about">About</Link>;

✅ This automatically uses the active locale in URLs like /en/about or /az/about.

Summary

  • next-intl makes i18n simple in Next.js 13+
  • URL-based language routing is supported
  • Translations are dynamic and work with both SSR and SSG

References

Comments