DEV Community

Cover image for Implementing i18n in Next.js - A Complete Guide to next-intl with App Router
Sho Ayuba
Sho Ayuba

Posted on

Implementing i18n in Next.js - A Complete Guide to next-intl with App Router

Introduction

Recently, I explored various internationalization solutions for my web application and settled on next-intl. In this article, I'll share my implementation journey with next-intl, including the setup process and the challenges I encountered along the way.

Target Audience

  • Developers considering i18n implementation in Next.js
  • Teams working with App Router
  • Web developers building multilingual applications

Development Environment

  • Next.js: 14.2.x
  • next-intl: 3.26.x
  • TypeScript: 5.x

Understanding i18n (Internationalization)

i18n stands for "Internationalization" - the term comes from counting the 18 letters between 'i' and 'n'. It refers to the process of designing and implementing web applications to support multiple languages and cultures.

Key components include:

  • Language translation: UI text, messages, and error messages
  • Regional settings: Date, time, number, and currency format localization
  • Character encoding: Unicode (UTF-8) support for multilingual content
  • RTL support: Right-to-left language support for Arabic, Hebrew, etc.

Evaluating the Need for i18n

Business Benefits

  • Expanded global market reach
  • Larger user base
  • Regional compliance (e.g., GDPR)

Technical Benefits

  • Centralized content management
  • Consistent language switching
  • Enhanced SEO capabilities

Why I Chose next-intl

  • Strong compatibility with Next.js App Router
  • Robust TypeScript support
  • Seamless SSR integration
  • Lightweight implementation

Alternative options:

  • react-intl
  • i18next

Choosing an i18n Routing Strategy

Before implementing next-intl, you need to select an appropriate routing strategy.

With i18n Routing

  • Language codes in URL paths (/en/about, /ja/about)
  • Domain-based language switching
  • SEO-optimized URL structure
  • Best for:
    • Public websites with multilingual content
    • SEO-focused sites requiring language-specific URLs
    • Cases requiring explicit language selection

Without i18n Routing

  • No language codes in URLs
  • Clean URL structure
  • System-based language switching
  • Best for:
    • Single-language applications
    • Cases prioritizing simple URL structure
    • Automatic language switching based on user settings

Implementation Steps

Reference: Next.js App Router with i18n routing

Project Directory Structure

├── messages/
│   ├── en.json
│   └── ja.json
├── next.config.mjs
└── src/
    ├── i18n/
    │   ├── routing.ts
    │   └── request.ts
    ├── middleware.ts
    └── app/
        └── [locale]/
            ├── layout.tsx
            └── page.tsx
Enter fullscreen mode Exit fullscreen mode

1. Package Installation

npm install next-intl@latest
Enter fullscreen mode Exit fullscreen mode

2. Message File Creation

// messages/en.json
{
  "HomePage": {
    "title": "Hello world!",
    "about": "Go to the about page"
  }
}
Enter fullscreen mode Exit fullscreen mode

3. Next.js Configuration

// next.config.mjs
import createNextIntlPlugin from 'next-intl/plugin';

const withNextIntl = createNextIntlPlugin();

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

export default withNextIntl(config);
Enter fullscreen mode Exit fullscreen mode

4. Routing Configuration

// src/i18n/routing.ts
import { defineRouting } from "next-intl/routing";
import { createNavigation } from "next-intl/navigation";

export const routing = defineRouting({
  locales: ["en", "ja"],
  defaultLocale: "en",
});

export const { Link, redirect, usePathname, useRouter, getPathname } = createNavigation(routing);
Enter fullscreen mode Exit fullscreen mode

5. Middleware Setup

// src/middleware.ts
import createMiddleware from 'next-intl/middleware';
import {routing} from './i18n/routing';

export default createMiddleware(routing);

export const config = {
  matcher: ['/', '/(de|en)/:path*']
};
Enter fullscreen mode Exit fullscreen mode

6. Request Configuration

// src/i18n/request.ts
import { getRequestConfig } from "next-intl/server";
import { routing } from "./routing";

export default getRequestConfig(async ({ requestLocale }) => {
  let locale = await requestLocale;

  if (!locale || !routing.locales.includes(locale as "en" | "ja")) {
    locale = routing.defaultLocale;
  }

  return {
    locale,
    messages: (await import(`../../messages/${locale}.json`)).default,
  };
});
Enter fullscreen mode Exit fullscreen mode

7. Layout Configuration

// src/app/[locale]/layout.tsx
import {NextIntlClientProvider} from 'next-intl';
import {getMessages} from 'next-intl/server';
import {notFound} from 'next/navigation';
import {routing} from '@/i18n/routing';

export default async function LocaleLayout({
  children,
  params: {locale}
}: {
  children: React.ReactNode;
  params: {locale: string};
}) {
  if (!routing.locales.includes(locale as any)) {
    notFound();
  }

  const messages = await getMessages();

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

Using useTranslations

Here's how to implement translations in your components:

import {useTranslations} from 'next-intl';
import {Link} from '@/i18n/routing';

export default function HomePage() {
  const t = useTranslations('HomePage');
  return (
    <div>
      <h1>{t('title')}</h1>
      <Link href="/about">{t('about')}</Link>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Common Challenges and Solutions

During my first implementation of i18n using next-intl, I encountered several challenges. Here's what I learned:

1. Version and Routing Method Compatibility

Challenges

  • Different implementations for Pages Router vs App Router
  • Evolving recommended implementations across versions
  • Mixed information in the development community

Solutions

  1. Environment Verification

    • Check Next.js version
    • Verify routing approach
    • Confirm next-intl version
  2. Implementation Pattern Selection

    • App Router: Use src/i18n/ directory structure
    • Pages Router: Use single i18n.ts file

2. Link and Navigation Implementation

Challenges

  • Default next/link incompatible with i18n routing
  • Language handling issues with next/navigation's useRouter

Solutions

  1. Link Component Migration

    • Switch from next/link to @/i18n/routing Link
    • Use relative paths for href attributes
  2. Router Migration

    • Replace next/navigation with @/i18n/routing useRouter
    • Enable automatic locale handling

Conclusion

When implementing i18n with next-intl for the first time, these key points proved crucial:

  1. Selecting the appropriate routing strategy based on project requirements
  2. Ensuring version compatibility between Next.js and next-intl
  3. Properly implementing Link components and useRouter for multilingual support

References

I hope this guide helps you with your Next.js internationalization implementation!

Top comments (0)