How to Use i18n in Next.js Without Routing

A guide to adding multi-language support in Next.js using the without-routing approach — language is handled internally with no URL changes.

4 July, 2025
3 min read
How to Use i18n in Next.js Without Routing
Next.js
next-intl
React.js
Internationalization
i18n

Internationalization (i18n) is essential for modern apps. If you're building a global product using Next.js, adding multiple languages to your website can be a game-changer and easier than you think!

In this guide, we'll explore the without routing approach of i18n in Next.js:

  • Without routing approach: language is handled internally. No change in URL.

Let's dive in!


Project Setup

Start with a new or existing Next.js app (with the App Router):

npx create-next-app@latest my-i18n-app
cd my-i18n-app

1. Install the package

npm install next-intl

2. Create translation files

Create a folder named translations with files for each language you want:

/translations
    /en.json
    /fr.json

Each file might look like this:

// translations/en.json
{
  "hello": "Hello!",
  "welcome": "Welcome to our site"
}
// translations/fr.json
{
  "hello": "Bonjour!",
  "welcome": "Bienvenue sur notre site"
}

3. Add next.config.mjs

// next.config.mjs

/** @type {import('next').NextConfig} */

import createNextIntlPlugin from "next-intl/plugin";

const withNextIntl = createNextIntlPlugin();

const nextConfig = {};

export default withNextIntl(nextConfig);

4. Create a server action to set cookie

"use server";

import { cookies } from "next/headers";

const setCookie = async (key: string, value: string) => {
  const cookieStore = await cookies();
  cookieStore.set(key, value);
};

export default setCookie;

5. Create the i18n/request.ts file

/i18n
    /request.ts

Add the following code in request.ts:

// i18n/request.ts

import { getRequestConfig } from "next-intl/server";
import { cookies } from "next/headers";

export default getRequestConfig(async () => {
  // Check if there is any language set in cookie
  const cookieStore = await cookies();
  const language = cookieStore.get("LANGUAGE")?.value;

  // If cookie is set, use it; otherwise default to "en"
  const locale = language || "en";

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

6. Add NextIntlClientProvider in app/layout.tsx

// app/layout.tsx

import { NextIntlClientProvider } from "next-intl";
import { getLocale, getMessages } from "next-intl/server";

export default async function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  const locale = await getLocale();
  const messages = await getMessages();

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

7. Use translations in your page

import { useTranslations } from "next-intl";

function HomePage() {
  const t = useTranslations("Homepage");
  return <div>{t("title")}</div>;
}

export default HomePage;

8. Change the language by clicking buttons

// components/navbar.tsx

import setCookie from "@/actions/setCookie";

function Navbar() {
  const handleChangeLanguage = (val: string) => {
    setCookie("LANGUAGE", val);
  };

  return (
    <div>
      <button onClick={() => handleChangeLanguage("en")}>EN</button>
      <button onClick={() => handleChangeLanguage("fr")}>FR</button>
    </div>
  );
}

export default Navbar;

That's it! With this setup, your Next.js app handles multiple languages entirely through cookies — no URL routing changes required.