This article summarizes how to implement language switching without page reload in a multilingual application using Next.js App Router and next-intl.
Environment
- Next.js 16 (App Router)
- next-intl
- TypeScript
Configuration Overview
localePrefix: ‘as-needed’
Using the localePrefix: 'as-needed' setting in next-intl, the default language does not have a URL prefix, while other languages do.
Example (when the default language is Japanese):
- Japanese:
/,/gallery,/viewer - English:
/en,/en/gallery,/en/viewer
Implementation Steps
1. Middleware Configuration (Important)
next-intl uses middleware to handle locale routing. It is important to configure the matcher so that static files are not redirected by the middleware.
Note: .*\\..* excludes “paths containing dots.” This allows static files like /og-image.png to skip middleware and be accessed normally.
2. Routing Configuration
3. Creating Navigation Helpers
Use createNavigation provided by next-intl to create locale-aware navigation helpers.
4. Language Switching Component
Key Points
Why Use next-intl Navigation
The reasons for importing useRouter and usePathname from next-intl/navigation instead of next/navigation:
- Locale-aware paths:
usePathname()returns the pure path without the locale prefix - locale option: You can pass
{ locale: 'en' }option torouter.replace()orrouter.push() - No-reload transitions: Smooth language switching via client-side navigation
Preserving Query Parameters
To preserve query parameters like ?videoId=xxx&gpxUrl=yyy during language switching, use useSearchParams.
router.replace vs router.push
router.replace: Does not add a new entry to browser history (recommended)router.push: Adds a new entry to browser history
Using replace for language switching prevents going back to the previous language with the back button.
Failure Patterns and Solutions
Pattern 1: Using window.location.href
This works but the entire page reloads, resulting in poor UX.
Pattern 2: Using useRouter from next/navigation
You need to manually build the path, which doesn’t work well with localePrefix: 'as-needed'.
Pattern 3: Manual Path Construction
Behavior changes depending on localePrefix settings, making it difficult to maintain.
Summary
When implementing language switching with next-intl, keep the following points in mind:
- Create navigation helpers with
createNavigation(routing) - Import
useRouterandusePathnamefromnext-intl/navigation - Specify locale with
router.replace(path, { locale }) - Preserve query parameters with
useSearchParams