fix(site): keep the page path when switching locale - #1059
Open
Orlando275 wants to merge 4 commits into
Open
Conversation
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
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
There was a problem hiding this comment.
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
localizePathnamehelper 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
localizePathnameand integration-style tests asserting the footer callswindow.location.assignwith 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.
Orlando275
enabled auto-merge (squash)
August 12, 2026 21:56
rcjasub
self-requested a review
August 14, 2026 05:56
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Fixes #1057
The footer language menu built the target URL with
segments[1] = newLocale, which replaces thefirst 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), soEnglish is served unprefixed and Spanish with
/es:researchwas overwritten. Every nested marketing page was affected. The homepage was not, because/has no segment to overwrite. The Spanish → English direction only looked fine becausenext-intl redirected
/en/...back to the unprefixed path.The fix
A shared helper,
localizePathname, in a newsrc/i18n/locale-path.ts— placed next toconfig.tsbecause it implements the
localePrefix: 'as-needed'rule declared there. It strips a leadinglocale segment when present, then re-adds the prefix only for non-default locales.
/research/index/es/index→ 404/es/research/index/es/research/index/en/research/index→ redirect/research/index//es/es(unchanged)/es//(unchanged)The buggy logic existed in three places, not two as the issue estimated — twice in
footer.tsx(once in
handleLocaleChange, again in theuseEffect) and once in the unusedlocale-switchercomponent. All three now call the helper so they cannot drift apart again.
Deliberately left alone
The footer writes the
NEXT_LOCALEcookie and then does a fullwindow.location.assignreload.That is not an oversight: proxy/i18n.ts reads
NEXT_LOCALEto decidewhere 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.tsalso indexessegments[1], but it only reads it for a comparison and isnot affected.
Tests
locale-path.test.ts— 14 cases covering both directions, the homepage, paths that resemble alocale prefix (
/fr/about,/es/es/about), and a round trip.footer.test.tsx— 4 cases asserting the URL actually handed towindow.location.assign, plusthe 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