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.

iiee}mmxxppppmmoooairrrrtdttttScdkhlc{dcieereoprwerfn:aaoasmrtuuti[eetld'.Mitcd/tinol(sdgcne(drfw?l}eia!eagrawftepare=iroMf|emi{o_drnfderlsxo/ettmswa|rat.'cri*n/ec\ei(\x1rf.t8oi.-nul*i/te)nris.ton*luga)/t)n'mid]indgAd'PlIewraoruet'es

Note: .*\\..* excludes “paths containing dots.” This allows static files like /og-image.png to skip middleware and be accessed normally.

2. Routing Configuration

ie}mx)ppldlsoooeorrrcfccttaaa/luli{cele1ostP8dn:Lrnesoe/ft[cfri'aionrjlxueoae:tRu':iot,'nui'agtn'js.igea-tnn'nsg=',e]e}d,deeffdri'onmeR'onuetxitn-gi(n{tl/routing'

3. Creating Navigation Helpers

Use createNavigation provided by next-intl to create locale-aware navigation helpers.

iiemmxpppsooorrrrcttt/i{{c1o8crnnros/eutnatati{veniNgLgaiav}ntikigf,oarntor.imetodsnir}/ercfotru,otmiuns'gen'Peaxtth-nianmtel,/nuasveiRgoauttieorn'}=createNavigation(routing)

4. Language Switching Component

'iiiie}ummmmxspppppccccc}rseooooooooooerrrrrrnnnnnccrt<cctttttsssssooous//ltttttnnurevoc{sci{{{{dPsstnlanlr)eoeelrpshrtteelCao}lmnuuurfooaeaer(cuhsuoeptsssoacutansqf.teastp{oco'eeeuuathrdeuur=nNitlptnLRStllenclrele{ganiot>eooeiterahevrlplemgocincuanmPCeyPlo=e.notatrgf==eahSaac{=l=nslecuraqttca("ok=>/erh}nuu=anurhelepce=L,Pcssmgei(e)xaya}afteeusern=f}-l='nurriLRsygu=2e{jgfsaooooe==ql>slauremmncuPp=ulp.o'aoPsatau(aePhymcgma'Lletsnrsraa-a}?et}@aerheeaeytn1pS'h/n((nSwmaShd(v'wnnfsg))aeLert,lr(aiearrumaotcreolltxmocaercehi{Cuouctem/g(carPnhnce'h-ie)hlsaglad)=ei}'1SPerone{:rnn8wa:a?cgd=l.tfenirmae>o'tlrx/tass`l(tcEs'otrcmt.$eee}nxm/ohsrt{:.x>gnue(iopttl'atr)nSana-i@i(gtterss/in))rhwgmhsgginLe'ra'{=naotc}ct>gmc.u/i(eavrio{)}las1n?elo8'$urn{}e-/q))pnu}oaeivrniytgSeatrtr"iionng'}`:pathname

Key Points

Why Use next-intl Navigation

The reasons for importing useRouter and usePathname from next-intl/navigation instead of next/navigation:

  1. Locale-aware paths: usePathname() returns the pure path without the locale prefix
  2. locale option: You can pass { locale: 'en' } option to router.replace() or router.push()
  3. 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.

ccoonnssttqfuuelrlyPSattrhin=gq=uesreyaSrtcrhiPnagra?ms`.$t{opSattrhinnagm(e)}?${queryString}`:pathname

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

winNdGo:w.Plaogceatrieolno.ahdrsef=newPath

This works but the entire page reloads, resulting in poor UX.

Pattern 2: Using useRouter from next/navigation

irmopuNotGre:tr.l{poucusashle(eR'o/oueptnte/irgoan}llnfeorrtoym'a)v'anielxatb/lneavigation'

You need to manually build the path, which doesn’t work well with localePrefix: 'as-needed'.

Pattern 3: Manual Path Construction

conNsGt:nCeowmPpaltehx=anndeweLrorcoarl-epr=o=n=e'ja'?pathWithoutLocale:`/${newLocale}${pathWithoutLocale}`

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:

  1. Create navigation helpers with createNavigation(routing)
  2. Import useRouter and usePathname from next-intl/navigation
  3. Specify locale with router.replace(path, { locale })
  4. Preserve query parameters with useSearchParams