NextJS i18n: Implement Nextjs Internationalization in 2025


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

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:
- Install NextJS (13.4+ recommended for App Router)
- Set up TypeScript for type safety
- Configure your project structure
2. Essential Libraries for NextJS i18n Implementation
While NextJS has native i18n support, these libraries enhance functionality:
- @formatjs/intl-localematcher - for advanced locale matching
- negotiator - parse Accept-Language headers
- shadcn/ui - for beautiful UI components
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:
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:
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:
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:
"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:
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:
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:
- Lazy load translations
- Only load the language the user needs
- Reduce initial JavaScript payload
- Use static generation
- Pre-render all language versions at build time
- Deliver instant page loads from the edge
- Implement proper caching
- Cache your dictionary files aggressively
- Use stale-while-revalidate strategies
- 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:
- Hardcoding text anywhere
- Always use dictionary references, never raw text
- Not handling RTL languages properly
- Add
dir="rtl"
attribute for Arabic, Hebrew, etc.
- Add
- Forgetting about date and number formats
- Use
Intl.DateTimeFormat
andIntl.NumberFormat
- Use
- Not testing with real users
- Get native speakers to review your translations
- 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:
- Domain-based routing
- Use example.com, example.fr, example.de
- Great for enterprise sites with dedicated country teams
- Translation management system integration
- Connect to Lokalise, Crowdin, or similar platforms
- Automate translation workflows
- Content adaptation
- Adjust not just language but images, examples, and cultural references
- Show different product features based on regional preferences
- 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?
- Slow Loading Speed
- Hard to Use on Phones
- Confusing Layout
- Messy Design
- Not Secure
- Doesn't show on Google
- Can't edit Website Content
- Quick to Load
- Mobile Friendly
- Easy Navigation
- Clean and Organized
- Secure and Safe
- Easy to Find on Google (SEO)
- Easy to edit Website Content
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