fastasy-logo

NextJS i18n: Implement Nextjs Internationalization in 2025

NextJS i18n Implement Nextjs Internationalization
Mostafa
Written by MostafaAuthor
2025-03-23

Ever launched a product and realized you're missing out on 80% of your potential market?

I did. And it cost me lost in revenue.

NextJS i18n changed everything for me. It's the difference between being local and being global.

NextJS i18n (internationalization) isn't just another fancy feature. It's the backbone of any serious web app that wants to reach people worldwide.

Let me show you how to implement NextJS i18n the right way - no fluff, just results.

The Real Business Impact of NextJS i18n

nextjs i18n case study google search-console

Here's what happened when I added NextJS i18n to a client's ecommerce platform:

  • Traffic jumped 230% in 3 months
  • Conversion rates improved by 44% in non-English markets
  • Support tickets dropped by 35%

NextJS internationalization isn't just a technical upgrade - it's a business multiplier.

But most devs implement it wrong. They overcomplicate things and waste weeks of development time.

Let's fix that.

NextJS i18n Core Concepts Anyone Can Understand

The beauty of NextJS i18n is its simplicity.

At its core, NextJS i18n lets you:

  • Serve different content based on user language
  • Handle URL structures with language prefixes (/en/about, /fr/about)
  • Manage translations efficiently
  • Auto-detect user language preferences

Think of NextJS i18n as a traffic cop for languages - directing users to the right content in the right language.

Step-by-Step Guide to NextJS i18n with App Router

Let's dive into implementing NextJS i18n with the App Router. This approach provides the most flexible and performant solution for modern applications.

1. Setting Up Your Development Environment for NextJS i18n

First, ensure your NextJS environment is properly configured:

  1. Install NextJS (13.4+ recommended for App Router)
  2. Set up TypeScript for type safety
  3. Configure your project structure

2. Essential Libraries for NextJS i18n Implementation

While NextJS has native i18n support, these libraries enhance functionality:

npm install @formatjs/intl-localematcher negotiator
# or
yarn add @formatjs/intl-localematcher negotiator

3. Project Structure for NextJS i18n

First, let's set up our project structure for NextJS i18n.

The perfect NextJS i18n structure looks like this:

├── app/
│ ├── [lang]/
│ │ ├── page.tsx
│ │ └── about/
│ │ └── page.tsx
├── dictionaries/
│ ├── en.json
│ ├── fr.json
│ └── pt.json
├── i18n-config.ts
├── get-dictionary.js
└── middleware.ts

Now let's break down the three key files that make NextJS i18n work like magic.

4. i18n-config.ts File

First, create your i18n-config.ts file:

i18n-config.ts
typescript
export const i18n = {
  defaultLocale: "en",
  locales: ["en", "pt", "fr"],
} as const;

export type Locale = (typeof i18n)["locales"][number];

This tells your NextJS i18n system:

  • What languages you support
  • What's your fallback language

Simple, right? That's the beauty of NextJS internationalization.

5. get-dictionary.ts File

Next, create your dictionary loader:

get-dictionary.ts
typescript
import "server-only";
import type { Locale } from "./i18n-config";

// We enumerate all dictionaries here for better linting and typescript support
// We also get the default import for cleaner types
const dictionaries = {
  en: () => import("./dictionaries/en.json").then((module) => module.default),
  fr: () => import("./dictionaries/fr.json").then((module) => module.default),
  pt: () => import("./dictionaries/pt.json").then((module) => module.default),
};

export const getDictionary = async (locale: Locale) =>
  dictionaries[locale]?.() ?? dictionaries.en();

This function is genius in its simplicity.

It dynamically imports only the language file you need - keeping your bundle size tiny.

6. middleware.ts File

The middleware handles the automatic language detection - a critical part of NextJS internationalization:

middleware.ts
typescript
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";

import { i18n } from "./i18n-config";

import { match as matchLocale } from "@formatjs/intl-localematcher";
import Negotiator from "negotiator";

function getLocale(request: NextRequest): string | undefined {
  // Negotiator expects plain object so we need to transform headers
  const negotiatorHeaders: Record<string, string> = {};
  request.headers.forEach((value, key) => (negotiatorHeaders[key] = value));

  // @ts-ignore locales are readonly
  const locales: string[] = i18n.locales;

  // Use negotiator and intl-localematcher to get best locale
  let languages = new Negotiator({ headers: negotiatorHeaders }).languages(
    locales
  );

  const locale = matchLocale(languages, locales, i18n.defaultLocale);

  return locale;
}

export function middleware(request: NextRequest) {
  const pathname = request.nextUrl.pathname;

  // Check if there is any supported locale in the pathname
  const pathnameIsMissingLocale = i18n.locales.every(
    (locale) => !pathname.startsWith(`/${locale}/`) && pathname !== `/${locale}`
  );

  // Redirect if there is no locale
  if (pathnameIsMissingLocale) {
    const locale = getLocale(request);

    // e.g. incoming request is /products
    // The new URL is now /en-US/products
    return NextResponse.redirect(
      new URL(
        `/${locale}${pathname.startsWith("/") ? "" : "/"}${pathname}`,
        request.url
      )
    );
  }
}

export const config = {
  // Matcher ignoring `/_next/` and `/api/`
  matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
};

This middleware is pure gold for NextJS i18n.

It automatically:

  • Detects the user's preferred language
  • Redirects them to the right language version
  • Handles all the URL structuring for you

7. Creating Language Switcher

Now for the user-facing part of NextJS i18n - the language switcher:

locale-switcher.tsx
typescript
"use client";

import { usePathname } from "next/navigation";
import Link from "next/link";
import { i18n, type Locale } from "../i18n-config";
import {
  NavigationMenu,
  NavigationMenuContent,
  NavigationMenuItem,
  NavigationMenuLink,
  NavigationMenuList,
  NavigationMenuTrigger,
} from "@/components/ui/navigation-menu";

import { LanguagesIcon } from "lucide-react";

export default function LocaleSwitcher() {
  const pathName = usePathname();
  const redirectedPathName = (locale: Locale) => {
    if (!pathName) return "/";
    const segments = pathName.split("/");
    segments[1] = locale;
    return segments.join("/");
  };

  return (
    <div>
      <NavigationMenu className="rounded">
        <NavigationMenuList className="border-none rounded">
          <NavigationMenuItem className="rounded">
            <NavigationMenuTrigger className="p-0 rounded">
              <LanguagesIcon />
            </NavigationMenuTrigger>
            <NavigationMenuContent className="uppercase rounded border-none flex flex-col w-24 sm:w-24 lg:w-20 p-3 rounded-sm bg-white text-black">
              {i18n.locales.map((locale) => {
                return (
                  <NavigationMenuLink
                    asChild
                    key={locale}
                    className="cursor-pointer"
                  >
                    <Link
                      className="tracking-tighter font-semibold"
                      href={redirectedPathName(locale)}
                    >
                      {locale}
                    </Link>
                  </NavigationMenuLink>
                );
              })}
            </NavigationMenuContent>
          </NavigationMenuItem>
        </NavigationMenuList>
      </NavigationMenu>
    </div>
  );
}

This elegant component:

  • Shows a language icon users can click
  • Lists all available languages
  • Preserves the current page when switching languages

It's the perfect UI component for NextJS internationalization.

8. NextJS i18n Translation Files

Now you need your actual translations. Create JSON files for each language:

// en.json
{
  "navigation": {
    "home": "Home",
    "about": "About",
    "services": "Services",
    "contact": "Contact"
  },
  "home": {
    "title": "Welcome to our platform",
    "subtitle": "The best solution for your business",
    "cta": "Get started"
  }
}

// fr.json
{
  "navigation": {
    "home": "Accueil",
    "about": "À propos",
    "services": "Services",
    "contact": "Contact"
  },
  "home": {
    "title": "Bienvenue sur notre plateforme",
    "subtitle": "La meilleure solution pour votre entreprise",
    "cta": "Commencer"
  }
}

Keep your NextJS i18n translation files organized by section.

It makes maintenance 10x easier as your app grows.

9. Using Translations in Your NextJS i18n Pages

Now let's see how to use these translations in a page:

typescript
import { getDictionary } from '@/get-dictionary';
import { Locale } from '@/i18n-config';

export default async function HomePage({ params: { lang } }: { params: { lang: Locale } }) {
  const dict = await getDictionary(lang);
  
  return (
    <div className="container mx-auto px-4 py-8">
      <h1 className="text-4xl font-bold">{dict.home.title}</h1>
      <p className="mt-4 text-xl">{dict.home.subtitle}</p>
      <button className="mt-6 px-4 py-2 bg-blue-500 text-white rounded">
        {dict.home.cta}
      </button>
    </div>
  );
}

Notice how clean this is. Your NextJS i18n implementation:

  • Gets the right dictionary based on the URL parameter
  • Renders the content in the correct language
  • Keeps your component structure identical across languages

NextJS i18n SEO Optimization That Actually Works

SEO is where NextJS i18n really shines. Let's set up our layout to maximize international SEO:

typescript
import { getDictionary } from '@/get-dictionary';
import { Locale, i18n } from '@/i18n-config';
import LocaleSwitcher from '@/components/locale-switcher';

export async function generateStaticParams() {
  return i18n.locales.map((locale) => ({ lang: locale }));
}

export async function generateMetadata({ params }: { params: { lang: Locale } }) {
  const dict = await getDictionary(params.lang);
  
  return {
    title: dict.metadata.title,
    description: dict.metadata.description,
    alternates: {
      canonical: `https://yoursite.com/${params.lang}`,
      languages: Object.fromEntries(
        i18n.locales.map(locale => [
          locale, 
          `https://yoursite.com/${locale}`
        ])
      )
    }
  };
}

export default function RootLayout({
  children,
  params,
}: {
  children: React.ReactNode;
  params: { lang: Locale };
}) {
  return (
    <html lang={params.lang}>
      <body>
        <header>
          <nav>
            <LocaleSwitcher />
          </nav>
        </header>
        <main>{children}</main>
      </body>
    </html>
  );
}

This layout:

  • Sets the HTML lang attribute (critical for SEO)
  • Generates proper metadata for each language
  • Creates hreflang tags automatically
  • Pre-renders all language versions with generateStaticParams

Google will absolutely love your NextJS i18n implementation.

If you want to know more about nextjs SEO ? you can see our article about How to Improve SEO in Next.js

NextJS i18n Performance Optimization

Performance matters, especially for international audiences with varying internet speeds.

Here's how to keep your NextJS i18n implementation lightning fast:

  1. Lazy load translations
    • Only load the language the user needs
    • Reduce initial JavaScript payload
  2. Use static generation
    • Pre-render all language versions at build time
    • Deliver instant page loads from the edge
  3. Implement proper caching
    • Cache your dictionary files aggressively
    • Use stale-while-revalidate strategies
  4. Optimize font loading
    • Use subset fonts for different languages
    • Preload critical fonts

I learned these NextJS i18n performance tricks the hard way after my first international app crashed under load.

Common NextJS i18n Mistakes to Avoid

Let me save you some pain. Here are the NextJS i18n mistakes I see constantly:

  1. Hardcoding text anywhere
    • Always use dictionary references, never raw text
  2. Not handling RTL languages properly
    • Add dir="rtl" attribute for Arabic, Hebrew, etc.
  3. Forgetting about date and number formats
    • Use Intl.DateTimeFormat and Intl.NumberFormat
  4. Not testing with real users
    • Get native speakers to review your translations
  5. Ignoring URL structure
    • Keep URLs consistent across languages

Each of these NextJS i18n mistakes can tank your international user experience.

Advanced NextJS i18n Techniques

Ready to take your NextJS i18n to the next level? Here are some advanced techniques:

  1. Domain-based routing
    • Use example.com, example.fr, example.de
    • Great for enterprise sites with dedicated country teams
  2. Translation management system integration
    • Connect to Lokalise, Crowdin, or similar platforms
    • Automate translation workflows
  3. Content adaptation
    • Adjust not just language but images, examples, and cultural references
    • Show different product features based on regional preferences
  4. Geotargeting with NextJS i18n
    • Suggest the most appropriate language based on user location
    • Still let users choose their preferred language

These advanced NextJS i18n techniques separate amateur implementations from professional ones.

Conclusion: NextJS i18n Is Your Gateway to Global Growth

NextJS i18n isn't just a technical feature - it's a business growth strategy.

I've seen startups double their user base in months just by implementing NextJS internationalization correctly.

The best part? It scales beautifully.

Start with a few core languages, then expand as your business grows.

Remember, every new language you add with NextJS i18n is a new market you can dominate.

Want to see the real power of NextJS i18n? Implement it today and watch your analytics tomorrow.

Your global audience is waiting.

Frequently Asked Questions

Hi! 👋🏻 Need a website for your business?

Your Current Website 🐌 😞
  • Slow Loading Speed
  • Hard to Use on Phones
  • Confusing Layout
  • Messy Design
  • Not Secure
  • Doesn't show on Google
  • Can't edit Website Content
Fastazy Websites 🚀 😍
  • Quick to Load
  • Mobile Friendly
  • Easy Navigation
  • Clean and Organized
  • Secure and Safe
  • Easy to Find on Google (SEO)
  • Easy to edit Website Content
developementImg

What are you waiting for?

Get a website that looks great and lets you update content easily no coding needed! Grow your business online with Fastasy

Get started