Skip to content

feat: implement path-based i18n routing in Next.js - #809

Merged
Robotron2 merged 2 commits into
Betta-Pay:mainfrom
brodapeethar:feat/i18n-path-routing
Sep 27, 2026
Merged

Robotron2 merged 2 commits into
Betta-Pay:mainfrom
brodapeethar:feat/i18n-path-routing

Conversation

@brodapeethar

Copy link
Copy Markdown
Contributor

Summary

Implements path-based i18n routing for the Next.js App Router, enabling SEO-indexable localized URLs like and .

Closes #753


Changes

middleware.ts

  • Added locale detection from URL path prefix (, , , ).
  • Bare paths (e.g. ) are redirected to locale-prefixed equivalents (e.g. ) using cookie or Accept-Language header for detection.
  • Locale-prefixed paths are rewritten internally to strip the prefix while preserving the user-visible URL for SEO indexability.
  • Sets NEXT_LOCALE cookie on every response so server components and client i18n can both resolve the active locale.
  • All auth redirects (login, dashboard, overview) now include the active locale prefix.

lib/i18n/locales.ts

  • Added LOCALE_COOKIE constant (NEXT_LOCALE).

lib/i18n/config.ts

  • Updated detectPreferredLocale to check the NEXT_LOCALE cookie first (set by middleware), then localStorage, then Accept-Language.

components/i18n/LanguageSelector.tsx

  • When the user changes language, the component now navigates to the locale-prefixed URL (e.g. /fr/dashboard) via router.push(), keeping the URL in sync with the selected language.

app/layout.tsx

  • Reads the NEXT_LOCALE cookie server-side to set <html lang="..."> correctly during SSR.

Tests

  • Expanded middleware tests to cover locale routing (redirect, rewrite, cookie setting, Accept-Language detection, locale-aware redirects).
  • Updated LanguageSelector test to mock next/navigation hooks.
  • All 21 tests pass.

Architecture Notes

This project uses Next.js 14 App Router, where the i18n key in next.config.js is not supported. Instead, locale routing is handled entirely via middleware rewrites — no filesystem restructuring required. The existing client-side i18next setup (bundled dictionaries, localStorage persistence) is preserved and augmented with the middleware-set cookie for SSR compatibility.

How it works

  1. GET /fr/dashboard → middleware detects fr prefix, strips it, rewrites to /dashboard internally, sets NEXT_LOCALE=fr cookie.
  2. GET /dashboard → middleware detects no prefix, redirects to /en/dashboard (using cookie/Accept-Language for locale detection).
  3. Language selector change → navigates to /fr/dashboard (locale-prefixed URL).

Testing Notes

  • Navigate to /fr/dashboard — app boots in French with URL preserved.
  • Navigate to /dashboard (no prefix) — redirects to /en/dashboard.
  • Change language via selector — URL updates to new locale prefix.

- Add locale detection/rewrite in middleware: bare paths (e.g. /dashboard)
  are redirected to locale-prefixed equivalents (/en/dashboard) using
  cookie or Accept-Language header for detection.
- Locale-prefixed paths are rewritten internally to strip the prefix
  while preserving the user-visible URL for SEO indexability.
- Set NEXT_LOCALE cookie in middleware so server components and client
  i18n can both resolve the active locale.
- Update LanguageSelector to navigate to locale-prefixed URLs on change.
- Update root layout to set <html lang> from the NEXT_LOCALE cookie.
- Update detectPreferredLocale to check the cookie before localStorage.
- Expand middleware tests to cover locale routing scenarios.

Refs: Betta-Pay#753
@vercel

vercel Bot commented Sep 24, 2026

Copy link
Copy Markdown

Someone is attempting to deploy a commit to the therealjhay's projects Team on Vercel.

A member of the Team first needs to authorize it.

@drips-wave

drips-wave Bot commented Sep 24, 2026

Copy link
Copy Markdown

@brodapeethar Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@Robotron2
Robotron2 merged commit 6ec3c7d into Betta-Pay:main Sep 27, 2026
6 of 14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

next.config.js i18n: Configure path-based routing

2 participants