Skip to content

Commit 1ce4aae

Browse files
authored
fix: sync cookie when setting locale (#2877)
1 parent b7a6c66 commit 1ce4aae

7 files changed

Lines changed: 44 additions & 23 deletions

File tree

docs/content/docs/2.guide/5.browser-language-detection.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ For better SEO, it's recommended to set `redirectOn` to `root` (which is the def
2323

2424
Browser language is detected either from `navigator` when running on client-side, or from the `accept-language` HTTP header. Configured `locales` (or locales `iso` and/or `code` when locales are specified in object form) are matched against locales reported by the browser (for example `en-US,en;q=0.9,no;q=0.8`). If there is no exact match for the full locale, the language code (letters before `-`) are matched against configured locales.
2525

26-
To prevent redirecting users every time they visit the app, **Nuxt i18n module** sets a cookie after the first redirection. You can change the cookie's name by setting `detectBrowserLanguage.cookieKey` option to whatever you'd like, the default is _i18n_redirected_.
26+
To prevent redirecting users every time they visit the app, **Nuxt i18n module** sets a cookie using the detected locale. You can change the cookie's name by setting `detectBrowserLanguage.cookieKey` option to whatever you'd like, the default is _i18n_redirected_.
2727

2828
```ts [nuxt.config.ts]
2929
i18n: {

docs/content/docs/5.v7/3.options-reference.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ Supported properties:
160160
- `all` - detect browser locale on all paths.
161161
- `root` (recommended for improved SEO) - only detect the browser locale on the root path (`/`) of the site. Only effective when using strategy other than `'no_prefix'`.
162162
- `no prefix` - a more permissive variant of `root` that will detect the browser locale on the root path (`/`) and also on paths that have no locale prefix (like `/foo`). Only effective when using strategy other than `'no_prefix'`.
163-
- `useCookie` (default: `true`) - If enabled, a cookie is set once the user has been redirected to browser's preferred locale, to prevent subsequent redirections. Set to `false` to redirect every time.
163+
- `useCookie` (default: `true`) - If enabled, a cookie is set (if unset) using the browser's preferred locale, to prevent subsequent redirections. Set to `false` to redirect every time.
164164
- `cookieAge` (default: `365`) - Sets the max age of the cookie in days.
165165
- `cookieKey` (default: `'i18n_redirected'`) - Cookie name.
166166
- `cookieDomain` (default: `null`) - Set to override the default domain of the cookie. Defaults to the **host** of the site.

specs/browser_language_detection/no_prefix.spec.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ test('detection with cookie', async () => {
3838
})
3939
const { page } = await renderPage('/', { locale: 'en' })
4040
const ctx = await page.context()
41+
expect(await ctx.cookies()).toMatchObject([
42+
{ name: 'my_custom_cookie_name', value: 'en', secure: true, sameSite: 'None' }
43+
])
44+
4145
// click `fr` lang switch link
4246
await page.locator('#set-locale-link-fr').click()
4347
expect(await ctx.cookies()).toMatchObject([

specs/browser_language_detection/prefix_and_default.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ test('alwaysRedirect: no prefix', async () => {
110110

111111
// detect locale from navigator language
112112
expect(await getText(page, '#lang-switcher-current-locale code')).toEqual('en')
113+
expect(await ctx.cookies()).toMatchObject([{ name: 'i18n_redirected', value: 'en' }])
113114

114115
// click `fr` lang switch with nutlink
115116
await page.locator('#set-locale-link-fr').click()

specs/issues/2262.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ describe('#2262', async () => {
1616
const ctx = await page.context()
1717

1818
expect(await getText(page, '#msg')).toEqual('Welcome')
19+
expect(await ctx.cookies()).toMatchObject([{ name: 'i18n_redirected', value: 'en' }])
1920

2021
// change to `fr`
2122
await page.locator('#fr').click()

src/runtime/plugins/i18n.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ export default defineNuxtPlugin({
166166
)
167167
composer.setLocale = async (locale: string) => {
168168
const localeSetup = isInitialLocaleSetup(locale)
169-
const [modified] = await loadAndSetLocale(locale, i18n, runtimeI18n, localeSetup)
169+
const modified = await loadAndSetLocale(locale, i18n, runtimeI18n, localeSetup)
170170

171171
if (modified && localeSetup) {
172172
notInitialSetup = false
@@ -450,7 +450,7 @@ export default defineNuxtPlugin({
450450
const localeSetup = isInitialLocaleSetup(locale)
451451
__DEBUG__ && console.log('localeSetup', localeSetup)
452452

453-
const [modified] = await loadAndSetLocale(locale, i18n, runtimeI18n, localeSetup)
453+
const modified = await loadAndSetLocale(locale, i18n, runtimeI18n, localeSetup)
454454

455455
if (modified && localeSetup) {
456456
notInitialSetup = false

src/runtime/utils.ts

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -107,39 +107,57 @@ export async function loadAndSetLocale(
107107
i18n: I18n,
108108
runtimeI18n: ModulePublicRuntimeConfig['i18n'],
109109
initial: boolean = false
110-
): Promise<[boolean, string]> {
110+
): Promise<boolean> {
111111
const { differentDomains, skipSettingLocaleOnNavigate, lazy } = runtimeI18n
112112
const opts = runtimeDetectBrowserLanguage(runtimeI18n)
113113
const nuxtApp = useNuxtApp()
114114

115-
let ret = false
116115
const oldLocale = getLocale(i18n)
116+
const localeCodes = getLocaleCodes(i18n)
117+
118+
// sets the locale cookie if unset or not up to date
119+
function syncCookie(locale: Locale = oldLocale) {
120+
if (opts === false || !opts.useCookie) return
121+
if (skipSettingLocaleOnNavigate) return
122+
123+
setCookieLocale(i18n, locale)
124+
}
125+
117126
__DEBUG__ && console.log('setLocale: new -> ', newLocale, ' old -> ', oldLocale, ' initial -> ', initial)
127+
128+
// `newLocale` is unset or empty
118129
if (!newLocale) {
119-
return [ret, oldLocale]
130+
syncCookie()
131+
return false
120132
}
121133

122-
// abort if different domains option enabled
134+
// no change if different domains option enabled
123135
if (!initial && differentDomains) {
124-
return [ret, oldLocale]
136+
syncCookie()
137+
return false
125138
}
126139

127140
if (oldLocale === newLocale) {
128-
return [ret, oldLocale]
141+
syncCookie()
142+
return false
129143
}
130144

131-
// call onBeforeLanguageSwitch
145+
// call `onBeforeLanguageSwitch` which may return an override for `newLocale`
132146
const localeOverride = await onBeforeLanguageSwitch(i18n, oldLocale, newLocale, initial, nuxtApp)
133-
const localeCodes = getLocaleCodes(i18n)
134-
if (localeOverride && localeCodes && localeCodes.includes(localeOverride)) {
135-
if (localeOverride === oldLocale) {
136-
return [ret, oldLocale]
147+
if (localeOverride && localeCodes.includes(localeOverride)) {
148+
// resolved `localeOverride` is already in use
149+
if (oldLocale === localeOverride) {
150+
syncCookie()
151+
return false
137152
}
153+
138154
newLocale = localeOverride
139155
}
140156

141-
const i18nFallbackLocales = getVueI18nPropertyValue<FallbackLocale>(i18n, 'fallbackLocale')
157+
// load locale messages required by `newLocale`
142158
if (lazy) {
159+
const i18nFallbackLocales = getVueI18nPropertyValue<FallbackLocale>(i18n, 'fallbackLocale')
160+
143161
const setter = (locale: Locale, message: Record<string, any>) => mergeLocaleMessage(i18n, locale, message)
144162
if (i18nFallbackLocales) {
145163
const fallbackLocales = makeFallbackLocaleCodes(i18nFallbackLocales, [newLocale])
@@ -149,19 +167,16 @@ export async function loadAndSetLocale(
149167
}
150168

151169
if (skipSettingLocaleOnNavigate) {
152-
return [ret, oldLocale]
170+
return false
153171
}
154172

155-
// set the locale
156-
if (opts !== false && opts.useCookie) {
157-
setCookieLocale(i18n, newLocale)
158-
}
173+
// sync cookie and set the locale
174+
syncCookie(newLocale)
159175
setLocale(i18n, newLocale)
160176

161177
await onLanguageSwitched(i18n, oldLocale, newLocale)
162178

163-
ret = true
164-
return [ret, oldLocale]
179+
return true
165180
}
166181

167182
type LocaleLoader = () => Locale

0 commit comments

Comments
 (0)