Lazy loaded image
🌍 How to integrate next-intl to achieve multi-language support in Next.js (App Router)
Words 889Read Time 3 min
2026-1-22
2026-1-22
type
Post
status
Published
date
Jan 22, 2026
slug
summary
Learn how to achieve multi-language support in Next.js
tags
Next.js
开发
category
技术分享
icon
password
😀
This article documents a production-ready internationalization (i18n) setup using next-intl with the Next.js App Router, based on a real project configuration (not just a demo).
 

📝 Cookie-Based i18n with next-intl (App Router) — A Real-World Guide

✨ Key highlights of this approach:

  • No locale prefix in URLs (/about instead of /en/about)
  • ✅ Language preference stored in Cookies
  • ✅ Works seamlessly with Server & Client Components
  • ✅ Clean URLs + persistent user preference
If you’re building a SaaS / dashboard / web app and want i18n without polluting URLs, this setup is for you.

🧠 Overall Idea (Read This First)

Unlike the official next-intl example that uses locale-based routing (/[locale]), this solution is Cookie-driven.

How it works

  1. The user’s language is stored in a cookie: locale=en | zh | ko
  1. During server rendering, Next.js reads the cookie
  1. next-intl loads the corresponding message file
  1. When switching language:
      • Update the cookie
      • Refresh the current route (URL stays the same)
🎯 Result:
  • /about always stays /about
  • Language persists across visits
  • The whole app updates instantly

📁 Project Structure

Install the dependency

🗂️ Message Files

🇺🇸 messages/en.json

🇨🇳 messages/zh.json

🇰🇷 messages/ko.json

💡
Tip: Group keys by feature or page instead of flattening everything.

⚙️ i18n/request.ts (Core Configuration)

This file is the heart of the entire setup.
It:
  • Reads the locale from cookies
  • Tells next-intl which locale to use
  • Dynamically loads the correct message file

i18n/request.ts

✅ Why this works

  • cookies() is server-only, perfect for App Router
  • No global state or middleware needed
  • Message files are loaded on demand, not bundled together

🧩 Root Layout Provider (Do NOT Skip This)

No matter which i18n strategy you use, NextIntlClientProvider must be mounted at the root.
app/layout.tsx
🔑 This enables:
  • useTranslations()
  • useLocale()
in any Client Component.

🖥️ Using Translations in Pages

Server Component Example

🔁 Language Switcher (Cookie-Based, No URL Change)

This is a clean and user-friendly language switcher that keeps URLs untouched.

components/language-switcher.tsx

🔍 What happens when you switch language?

  1. User selects a language
  1. Cookie is updated on the client
  1. router.refresh() triggers server re-render
  1. next-intl re-reads the cookie
  1. New messages are loaded 🎉

⚖️ Why Choose This Approach?

Feature
Locale in URL
Cookie-based
Clean URLs
Persistent preference
⚠️
App / Dashboard UX
⚠️
⭐⭐⭐
SEO
⭐⭐⭐
⭐⭐
📌 Best for: Apps, dashboards, authenticated products

⚠️ Common Pitfalls

  • cookies() only works on the server
  • Client-side switching must call router.refresh()
  • Missing message files will throw runtime errors (add fallbacks if needed)

✅ Final Thoughts

This Cookie-based next-intl setup:
  • Keeps URLs clean ✨
  • Fully embraces the App Router
  • Scales well for real-world applications
If you don’t need locale-prefixed URLs for SEO, this is one of the cleanest i18n solutions available today.
 
🚀 Want to go further?
  • Type-safe translation keys with TypeScript
  • Hybrid SEO + Cookie strategies
  • Integrating locale with Auth / Supabase
Happy coding! 👋

📎 参考文章

上一篇
LangChain Python Tutorial
下一篇
🚀 用Zod&React Hook Form 实现前后端统一的API校验

Comments
Loading...