Skip to content

fix(site): keep the page path when switching locale - #1059

Open
Orlando275 wants to merge 4 commits into
mainfrom
fix/1057-locale-switch-404
Open

fix(site): keep the page path when switching locale#1059
Orlando275 wants to merge 4 commits into
mainfrom
fix/1057-locale-switch-404

Conversation

@Orlando275

Copy link
Copy Markdown
Contributor

Description

Fixes #1057

The footer language menu built the target URL with segments[1] = newLocale, which replaces the
first path segment instead of adding a locale prefix. That is only correct when a prefix is already
there.

Routing runs with localePrefix: 'as-needed' (i18n/config.ts), so
English is served unprefixed and Spanish with /es:

/research/index  +  Español
  ['', 'research', 'index']  →  segments[1] = 'es'  →  ['', 'es', 'index']  →  /es/index  → 404

research was overwritten. Every nested marketing page was affected. The homepage was not, because
/ has no segment to overwrite. The Spanish → English direction only looked fine because
next-intl redirected /en/... back to the unprefixed path.

The fix

A shared helper, localizePathname, in a new src/i18n/locale-path.ts — placed next to config.ts
because it implements the localePrefix: 'as-needed' rule declared there. It strips a leading
locale segment when present, then re-adds the prefix only for non-default locales.

From Choose Before After
/research/index Español /es/index → 404 /es/research/index
/es/research/index English /en/research/index → redirect /research/index
/ Español /es /es (unchanged)
/es English / / (unchanged)

The buggy logic existed in three places, not two as the issue estimated — twice in footer.tsx
(once in handleLocaleChange, again in the useEffect) and once in the unused locale-switcher
component. All three now call the helper so they cannot drift apart again.

Deliberately left alone

The footer writes the NEXT_LOCALE cookie and then does a full window.location.assign reload.
That is not an oversight: proxy/i18n.ts reads NEXT_LOCALE to decide
where to send unprefixed paths, so the reload is what makes the middleware run. Only the path
construction was wrong, so only the path construction changed.

proxy/special-redirect.ts also indexes segments[1], but it only reads it for a comparison and is
not affected.

Tests

  • locale-path.test.ts — 14 cases covering both directions, the homepage, paths that resemble a
    locale prefix (/fr/about, /es/es/about), and a round trip.
  • footer.test.tsx — 4 cases asserting the URL actually handed to window.location.assign, plus
    the no-op when the active locale is re-selected.

Both suites were confirmed to fail against the original logic before being committed. Full site
suite: 607 tests passing.

Checklist

  • You've included unit or integration tests for your change, where applicable.
  • You've included inline docs for your change, where applicable.
  • Any components that you've modified are accessible.
  • You've used conventional commits where appropriate

The footer language menu replaced the first path segment with the new locale
instead of adding or removing a locale prefix. Routing runs with
`localePrefix: 'as-needed'`, so English is served unprefixed: on
/research/index, choosing Spanish produced /es/index and 404'd. Every nested
marketing page was affected. The homepage was not, since / has no segment to
overwrite. The reverse direction only appeared to work because next-intl
redirected /en/... back to the unprefixed path.

Adds a shared helper in src/i18n/locale-path.ts that strips a leading locale
segment when one is present, then re-adds the prefix only for non-default
locales. The same logic existed in three places — twice in the footer, once in
the unused locale switcher component — and all three now call the helper so
they cannot drift apart again.

The cookie write and full page reload in the footer are left untouched: the
proxy reads NEXT_LOCALE to route unprefixed paths, so the reload is deliberate
rather than an oversight.

Closes #1057
@vercel

vercel Bot commented Aug 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
internal-dashboard Ready Ready Preview Aug 12, 2026 9:55pm
nightcrawler Ready Ready Preview Aug 12, 2026 9:55pm

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes locale switching URL construction so changing languages preserves the current page path under localePrefix: 'as-needed' (default locale unprefixed, non-default locales prefixed). This addresses the production 404s when switching from English → Spanish on nested marketing routes (Issue #1057) by centralizing the path-rewrite logic in a shared helper and updating all known call sites to use it.

Changes:

  • Added localizePathname helper that strips an existing leading locale segment (if present) and re-adds a prefix only for non-default locales.
  • Updated footer and the (currently unused) locale-switcher component to use the shared helper instead of overwriting segments[1].
  • Added focused unit tests for localizePathname and integration-style tests asserting the footer calls window.location.assign with the correct localized URL.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.

Show a summary per file
File Description
apps/site/src/i18n/locale-path.ts Introduces localizePathname implementing localePrefix: 'as-needed' path rewriting.
apps/site/src/i18n/locale-path.test.ts Adds unit coverage for locale path rewriting across edge cases and round-trips.
apps/site/src/components/common/locale-switcher/locale-switcher.tsx Replaces brittle segment overwrite logic with localizePathname.
apps/site/src/app/(unauthenticated)/[locale]/(marketing)/components/footer/footer.tsx Fixes footer locale switch navigation to preserve the page path via localizePathname.
apps/site/src/app/(unauthenticated)/[locale]/(marketing)/components/footer/footer.test.tsx Adds tests validating the exact URL passed to window.location.assign during locale switching.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

The branch widened the test-utils import to pull in within and fireEvent for
the locale-switching tests. Main rewrote that same import line, and the merge
kept main's version alongside the branch's usages, so type-check and four
tests failed.

Git reported no conflict: each side touched a different region of the file.
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.

[Bug]: Switching from English to Spanish 404s — the locale prefix overwrites the first path segment

2 participants