Next.js App Router と next-intl を使用した多言語対応アプリケーションで、リロードなしの言語切り替えを実装する方法をまとめます。

環境

  • Next.js 16 (App Router)
  • next-intl
  • TypeScript

設定概要

localePrefix: ‘as-needed’ とは

next-intllocalePrefix: 'as-needed' 設定を使用すると、デフォルト言語ではURLにプレフィックスが付かず、その他の言語のみプレフィックスが付きます。

例(デフォルト言語が日本語の場合):

  • 日本語: /, /gallery, /viewer
  • 英語: /en, /en/gallery, /en/viewer

実装手順

1. Middleware設定(重要)

next-intl は middleware を使用してロケールのルーティングを処理します。静的ファイルが middleware によってリダイレクトされないよう、matcher の設定が重要です。

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

注意 : .*\\..* は「ドットを含むパス」を除外します。これにより /og-image.png などの静的ファイルがmiddlewareをスキップし、正常にアクセスできるようになります。

2. ルーティング設定

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. ナビゲーションヘルパーの作成

next-intl が提供する createNavigation を使用して、ロケール対応のナビゲーションヘルパーを作成します。

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

4. 言語切り替えコンポーネント

'iiiie}ummmmxspppppccccc}rseooooooooooerrrrrrnnnnnccrt<cctttttsssssooous//ltttttnnurevoc{sci{{{{dsstnlanlr)eoeelrpshtteelCao}lmnuuurfooaear(cuhsuoeptsssoacutanqf.teastp{oco'eeeuuathrduur=nNitlptnLRStllenclele{ganiot>eooeiteraherlplemgocincuanmPCyPlo=e.notatrgf==eahSaac{=l=nslecurattca("ok=>/erh}nuu=anrhelepce=L,Pcssmgi(e)xaya}afteeusen=f}-l='nurriLRsgu=2e{jgfsaooooe==ql>slauremmncuP=ulp.o'aoPsatau(ePhymcgma'Lletsnsraa-a}?et}@aerheeeytn1pS'h/n((nSwaShd(v'wnnfsg))aeLrt,lr(aiearrumaocreolltxmocaerchi{Cuouctem/g(caPnhnce'h-ie)hlaglad)=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

ポイント解説

next-intl のナビゲーションを使う理由

next/navigation ではなく next-intl/navigation から useRouterusePathname をインポートする理由:

  1. ロケール対応のパス : usePathname() はロケールプレフィックスを除いた純粋なパスを返す
  2. locale オプション : router.replace()router.push(){ locale: 'en' } オプションを渡せる
  3. リロードなし遷移 : クライアントサイドナビゲーションでスムーズに言語切り替え

クエリパラメータの維持

言語切り替え時に ?videoId=xxx&gpxUrl=yyy などのクエリパラメータを維持するために、useSearchParams を使用します。

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

router.replace vs router.push

  • router.replace: ブラウザ履歴に新しいエントリを追加しない(推奨)
  • router.push: ブラウザ履歴に新しいエントリを追加

言語切り替えでは replace を使うことで、戻るボタンで前の言語に戻ることを防ぎます。

失敗パターンと解決策

パターン1: window.location.href を使用

winNdGo:w.location.href=newPath

動作はするが、ページ全体がリロードされるためUXが悪い。

パターン2: next/navigation の useRouter を使用

irmopuNotGre:tr.l{poucusashle(eR'o/uetne/rga使}llferroym')'next/navigation'

手動でパスを構築する必要があり、localePrefix: 'as-needed' との相性が悪い。

パターン3: パスの手動構築

conNsGt:newPath=newLocale==='ja'?pathWithoutLocale:`/${newLocale}${pathWithoutLocale}`

localePrefix の設定によって挙動が変わるため、保守が難しい。

まとめ

next-intl で言語切り替えを実装する際は、以下のポイントを押さえましょう:

  1. createNavigation(routing) でナビゲーションヘルパーを作成
  2. useRouter, usePathnamenext-intl/navigation からインポート
  3. router.replace(path, { locale }) でロケールを指定
  4. useSearchParams でクエリパラメータを維持