Switch locale with a page load instead of the router - #1037
Open
marinaaisa wants to merge 3 commits into
Open
marinaaisa wants to merge 3 commits into
marinaaisa wants to merge 3 commits into
Conversation
The locale selector offers every locale in `availableLocales`, but a page can offer a locale whose data does not exist. The route guard already falls back to the English page when the data answers 404, but as an in-app navigation that fallback resolves to the location the router is already at, which vue-router rejects as a duplicated navigation [1]. The redirect never happened, leaving the page claiming a locale that was never rendered. This change resolves the target href with `router.resolve` [2] and hands it to `location.assign` [3], so switching locales is a normal page load. The guard then runs as an initial navigation and the English fallback works. Resolving is what makes the href usable outside the router: the locale is a route param, and router paths are relative to the router base, which is the `<base>` path the site is deployed under. The browser needs the full path instead, so `resolve` is the piece that fills the locale into the matched route pattern and prefixes the base, something a hand built string from the current path would miss when the site is not served from the domain root. The UI is unchanged, and the reloaded app reads its locale from the URL, so updating the i18n state by hand is no longer needed. [1] https://v3.router.vuejs.org/guide/advanced/navigation-failures.html [2] https://v3.router.vuejs.org/api/#router-resolve [3] https://developer.mozilla.org/en-US/docs/Web/API/Location/assign
The banner suggesting the reader's preferred locale used a router link, so picking it stayed inside the app. The suggested locale may have no data for the current page. The route guard falls back to the English page when the data answers 404, but from an in-app navigation that fallback resolves to the location the router is already at, which vue-router rejects as a duplicated navigation [1]. The redirect never happened, and the reader was left on a page that claimed a locale it was never rendered in. This change renders a plain anchor instead, with the target resolved through `router.resolve` [2] so the href keeps the base URL and the locale path segment. The browser performs the navigation, the app boots again at the localized URL, and its guard runs as an initial navigation, so the English fallback works. The click handler still stores the preference, since local storage writes finish before the page is left. The test file is renamed after the component it covers. [1] https://v3.router.vuejs.org/guide/advanced/navigation-failures.html [2] https://v3.router.vuejs.org/api/#router-resolve
marinaaisa
force-pushed
the
locale-selector-navigation
branch
from
September 18, 2026 20:22
3516213 to
b1aa8b8
Compare
This change resets the preference to the default locale as part of that fallback. The banner only renders when the preferred locale differs from the one being displayed, so it stays hidden, and because the preference is also persisted the banner does not come back after a reload. The reset is site wide rather than per page, which keeps the guard simple at the cost of forgetting the preference on pages that do have the translation.
Member
Author
|
@swift-ci test |
3 tasks
Member
Author
|
@swift-ci test |
mportiz08
reviewed
Sep 19, 2026
| AppStore.setPreferredLocale(defaultLocale); | ||
| // Call `next` with the same route but without the locale param, | ||
| // redirecting to the non-localized version of the route. | ||
| next({ ...to, params: paramsWithoutLocale }); |
Member
There was a problem hiding this comment.
I think the issue with the original fallback PR that we missed is that this should probably be throw false to abort the request instead of doing the next() and return null, which is what was causing the page data to disappear.
We might also need to add something similar to this in the global onError hook for the router to gracefully handle aborted requests without showing the server error page:
router.onError((error) => {
if (isNavigationFailure(error, NavigationFailureType.aborted)) {
return;
}
...
}
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.
Bug/issue #187696128, if applicable:
Summary
The locale selector offers every locale in
availableLocales, but a page can offer a locale whose data does not exist. The route guard already falls back to the English page when the data answers 404, but as an in-app navigation that fallback resolves to the location the router is already at, which vue-router rejects as a duplicated navigation [1]. The redirect never happened, leaving the page claiming a locale that was never rendered.This change resolves the target href with
router.resolve[2] and hands it tolocation.assign[3], so switching locales is a normal page load. The guard then runs as an initial navigation and the English fallback works. Resolving is what makes the href usable outside the router: the locale is a route param, and router paths are relative to the router base, which is the<base>path the site is deployed under. The browser needs the full path instead, soresolveis the piece that fills the locale into the matched route pattern and prefixes the base, something a hand built string from the current path would miss when the site is not served from the domain root.The UI is unchanged, and the reloaded app reads its locale from the URL, so updating the i18n state by hand is no longer needed.
[1] https://v3.router.vuejs.org/guide/advanced/navigation-failures.html
[2] https://v3.router.vuejs.org/api/#router-resolve
[3] https://developer.mozilla.org/en-US/docs/Web/API/Location/assign
Dependencies
NA
Testing
Steps:
Checklist
Make sure you check off the following items. If they cannot be completed, provide a reason.
npm test, and it succeeded