diff --git a/specs/basic_usage.spec.ts b/specs/basic_usage.spec.ts
index 0bc380cc6..07f816b86 100644
--- a/specs/basic_usage.spec.ts
+++ b/specs/basic_usage.spec.ts
@@ -355,7 +355,3 @@ test('dynamic parameters', async () => {
const product2dom = getDom(product2Html)
expect(product2dom.querySelector('#i18n-alt-en').href).toEqual('/products/red-mug')
})
-
-test('(#2554) using `setLocale` in plugin should not throw an error', async () => {
- await expect($fetch('/?pluginSetLocale')).resolves.to.not.toThrowError()
-})
diff --git a/specs/fixtures/basic_usage/plugins/set-locale-plugin.ts b/specs/fixtures/basic_usage/plugins/set-locale-plugin.ts
deleted file mode 100644
index 50eb9451a..000000000
--- a/specs/fixtures/basic_usage/plugins/set-locale-plugin.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { defineNuxtPlugin } from '#imports'
-
-export default defineNuxtPlugin(async nuxtApp => {
- if ('pluginSetLocale' in nuxtApp._route.query) {
- const app = useNuxtApp()
- await app.$i18n.setLocale('fr')
- }
-})
diff --git a/specs/fixtures/issues/2554/app.vue b/specs/fixtures/issues/2554/app.vue
new file mode 100644
index 000000000..8f62b8bf9
--- /dev/null
+++ b/specs/fixtures/issues/2554/app.vue
@@ -0,0 +1,3 @@
+
+
+
diff --git a/specs/fixtures/issues/2554/nuxt.config.ts b/specs/fixtures/issues/2554/nuxt.config.ts
new file mode 100644
index 000000000..dede1304e
--- /dev/null
+++ b/specs/fixtures/issues/2554/nuxt.config.ts
@@ -0,0 +1,7 @@
+export default defineNuxtConfig({
+ modules: ['@nuxtjs/i18n'],
+ i18n: {
+ locales: ['en', 'fr'],
+ strategy: 'no_prefix'
+ }
+})
diff --git a/specs/fixtures/issues/2554/package.json b/specs/fixtures/issues/2554/package.json
new file mode 100644
index 000000000..733066968
--- /dev/null
+++ b/specs/fixtures/issues/2554/package.json
@@ -0,0 +1,14 @@
+{
+ "name": "nuxt3-test-issues-2473",
+ "private": true,
+ "scripts": {
+ "build": "nuxt build",
+ "dev": "nuxt dev",
+ "generate": "nuxt generate",
+ "preview": "nuxt preview"
+ },
+ "devDependencies": {
+ "@nuxtjs/i18n": "latest",
+ "nuxt": "latest"
+ }
+}
diff --git a/specs/fixtures/issues/2554/pages/index.vue b/specs/fixtures/issues/2554/pages/index.vue
new file mode 100644
index 000000000..89eb55fef
--- /dev/null
+++ b/specs/fixtures/issues/2554/pages/index.vue
@@ -0,0 +1,3 @@
+
+ Hello
+
diff --git a/specs/fixtures/issues/2554/plugins/set-locale-plugin.ts b/specs/fixtures/issues/2554/plugins/set-locale-plugin.ts
new file mode 100644
index 000000000..530afed87
--- /dev/null
+++ b/specs/fixtures/issues/2554/plugins/set-locale-plugin.ts
@@ -0,0 +1,8 @@
+import { defineNuxtPlugin } from '#imports'
+
+export default defineNuxtPlugin(async nuxtApp => {
+ if ('pluginSetLocale' in nuxtApp._route.query && typeof nuxtApp._route.query.pluginSetLocale === 'string') {
+ const app = useNuxtApp()
+ await app.$i18n.setLocale(nuxtApp._route.query.pluginSetLocale)
+ }
+})
diff --git a/specs/issues/2554.spec.ts b/specs/issues/2554.spec.ts
new file mode 100644
index 000000000..9222afa6b
--- /dev/null
+++ b/specs/issues/2554.spec.ts
@@ -0,0 +1,22 @@
+import { test, expect, describe } from 'vitest'
+import { fileURLToPath } from 'node:url'
+import { URL } from 'node:url'
+import { setup, url } from '../utils'
+import { renderPage } from '../helper'
+
+describe('#2554', async () => {
+ await setup({
+ rootDir: fileURLToPath(new URL(`../fixtures/issues/2554`, import.meta.url)),
+ browser: true
+ })
+
+ test('should not throw an error when using `setLocale` from plugin', async () => {
+ const { page } = await renderPage('/')
+
+ const res1 = await page.goto(url('/?pluginSetLocale=fr'))
+ expect(res1?.ok()).toBeTruthy()
+
+ const res2 = await page.goto(url('/?pluginSetLocale=en'))
+ expect(res2?.ok()).toBeTruthy()
+ })
+})
diff --git a/src/runtime/internal.ts b/src/runtime/internal.ts
index b72ca4d4b..6a70c95c5 100644
--- a/src/runtime/internal.ts
+++ b/src/runtime/internal.ts
@@ -25,6 +25,7 @@ import { initCommonComposableOptions, type CommonComposableOptions } from './uti
import type { Locale } from 'vue-i18n'
import type { DetectBrowserLanguageOptions, LocaleObject } from '#build/i18n.options.mjs'
import type { RouteLocationNormalized, RouteLocationNormalizedLoaded } from 'vue-router'
+import type { CookieRef } from 'nuxt/app'
export function formatMessage(message: string) {
return NUXT_I18N_MODULE_ID + ' ' + message
@@ -93,7 +94,25 @@ export function getBrowserLocale(): string | undefined {
return ret
}
-export function getLocaleCookie(): string | undefined {
+export function getI18nCookie() {
+ const detect = nuxtI18nOptions.detectBrowserLanguage
+ const cookieKey = (detect && detect.cookieKey) || nuxtI18nOptionsDefault.detectBrowserLanguage.cookieKey
+ const date = new Date()
+ const cookieOptions: Record = {
+ expires: new Date(date.setDate(date.getDate() + 365)),
+ path: '/',
+ sameSite: detect && detect.cookieCrossOrigin ? 'none' : 'lax',
+ secure: (detect && detect.cookieCrossOrigin) || (detect && detect.cookieSecure)
+ }
+
+ if (detect && detect.cookieDomain) {
+ cookieOptions.domain = detect.cookieDomain
+ }
+
+ return useNuxtCookie(cookieKey, cookieOptions)
+}
+
+export function getLocaleCookie(cookieRef: CookieRef): string | undefined {
const detect = nuxtI18nOptions.detectBrowserLanguage
__DEBUG__ &&
@@ -107,8 +126,7 @@ export function getLocaleCookie(): string | undefined {
return
}
- const localeCookie = useNuxtCookie(detect.cookieKey)
- const localeCode: string | undefined = localeCookie.value ?? undefined
+ const localeCode: string | undefined = cookieRef.value ?? undefined
__DEBUG__ && console.log(`getLocaleCookie cookie (${process.client ? 'client' : 'server'}) -`, localeCode)
if (localeCode && localeCodes.includes(localeCode)) {
@@ -116,28 +134,14 @@ export function getLocaleCookie(): string | undefined {
}
}
-export function setLocaleCookie(locale: string) {
- const { useCookie, cookieKey, cookieDomain, cookieSecure, cookieCrossOrigin } =
- nuxtI18nOptions.detectBrowserLanguage || nuxtI18nOptionsDefault.detectBrowserLanguage
+export function setLocaleCookie(cookieRef: CookieRef, locale: string) {
+ const { useCookie } = nuxtI18nOptions.detectBrowserLanguage || nuxtI18nOptionsDefault.detectBrowserLanguage
if (!useCookie) {
return
}
- const date = new Date()
- const cookieOptions: Record = {
- expires: new Date(date.setDate(date.getDate() + 365)),
- path: '/',
- sameSite: cookieCrossOrigin ? 'none' : 'lax',
- secure: cookieCrossOrigin || cookieSecure
- }
-
- if (cookieDomain) {
- cookieOptions.domain = cookieDomain
- }
-
- const localeCookie = useNuxtCookie(cookieKey, cookieOptions)
- localeCookie.value = locale
+ cookieRef.value = locale
}
export type DetectBrowserLanguageNotDetectReason =
@@ -160,6 +164,7 @@ export type DetectLocaleContext = {
ssg: DetectLocaleForSSGStatus
callType: DetectLocaleCallType
firstAccess: boolean
+ localeCookie: CookieRef
}
export const DefaultDetectBrowserLanguageFromResult: DetectBrowserLanguageFromResult = {
@@ -176,7 +181,7 @@ export function detectBrowserLanguage(
locale: Locale = ''
): DetectBrowserLanguageFromResult {
const { strategy } = nuxtI18nOptions
- const { ssg, callType, firstAccess } = detectLocaleContext
+ const { ssg, callType, firstAccess, localeCookie } = detectLocaleContext
__DEBUG__ && console.log('detectBrowserLanguage: (ssg, callType, firstAccess) - ', ssg, callType, firstAccess)
// browser detection is ignored if it's a nuxt generate.
@@ -223,7 +228,7 @@ export function detectBrowserLanguage(
// get preferred language from cookie if present and enabled
if (useCookie) {
- matchedLocale = cookieLocale = getLocaleCookie()
+ matchedLocale = cookieLocale = localeCookie.value
localeFrom = 'cookie'
__DEBUG__ && console.log('detectBrowserLanguage: cookieLocale', cookieLocale)
}
diff --git a/src/runtime/plugins/i18n.ts b/src/runtime/plugins/i18n.ts
index 084ad2e2e..e922b823d 100644
--- a/src/runtime/plugins/i18n.ts
+++ b/src/runtime/plugins/i18n.ts
@@ -25,7 +25,8 @@ import {
getLocaleCookie as _getLocaleCookie,
setLocaleCookie as _setLocaleCookie,
detectBrowserLanguage,
- DefaultDetectBrowserLanguageFromResult
+ DefaultDetectBrowserLanguageFromResult,
+ getI18nCookie
} from '../internal'
import { getComposer, getLocale, setLocale } from '../routing/utils'
import { extendI18n, createLocaleFromRouteGetter } from '../routing/extends'
@@ -68,6 +69,7 @@ export default defineNuxtPlugin({
const getLocaleFromRoute = createLocaleFromRouteGetter()
const getDefaultLocale = (defaultLocale: string) => defaultLocale || vueI18nOptions.locale || 'en-US'
+ const localeCookie = getI18nCookie()
// detect initial locale
let initialLocale = detectLocale(
route,
@@ -77,7 +79,8 @@ export default defineNuxtPlugin({
{
ssg: isSSG && nuxtI18nOptions.strategy === 'no_prefix' ? 'ssg_ignore' : 'normal',
callType: 'setup',
- firstAccess: true
+ firstAccess: true,
+ localeCookie
}
)
__DEBUG__ && console.log('first detect initial locale', initialLocale)
@@ -124,7 +127,7 @@ export default defineNuxtPlugin({
? detectBrowserLanguage(
route,
vueI18nOptions.locale,
- { ssg: 'ssg_setup', callType: 'setup', firstAccess: true },
+ { ssg: 'ssg_setup', callType: 'setup', firstAccess: true, localeCookie },
initialLocale
)
: DefaultDetectBrowserLanguageFromResult
@@ -187,8 +190,8 @@ export default defineNuxtPlugin({
composer.differentDomains = nuxtI18nOptions.differentDomains
composer.defaultLocale = nuxtI18nOptions.defaultLocale
composer.getBrowserLocale = () => _getBrowserLocale()
- composer.getLocaleCookie = () => _getLocaleCookie()
- composer.setLocaleCookie = (locale: string) => _setLocaleCookie(locale)
+ composer.getLocaleCookie = () => _getLocaleCookie(localeCookie)
+ composer.setLocaleCookie = (locale: string) => _setLocaleCookie(localeCookie, locale)
composer.onBeforeLanguageSwitch = (oldLocale, newLocale, initialSetup, context) =>
nuxt.callHook('i18n:beforeLocaleSwitch', { oldLocale, newLocale, initialSetup, context })
@@ -394,7 +397,8 @@ export default defineNuxtPlugin({
{
ssg: isSSGModeInitialSetup() && nuxtI18nOptions.strategy === 'no_prefix' ? 'ssg_ignore' : 'normal',
callType: 'routing',
- firstAccess: routeChangeCount === 0
+ firstAccess: routeChangeCount === 0,
+ localeCookie
}
)
__DEBUG__ && console.log('detect locale', locale)
diff --git a/src/runtime/utils.ts b/src/runtime/utils.ts
index 4a3bb9499..2a82e819f 100644
--- a/src/runtime/utils.ts
+++ b/src/runtime/utils.ts
@@ -18,7 +18,6 @@ import {
import {
wrapComposable,
detectBrowserLanguage,
- getLocaleCookie,
callVueI18nInterfaces,
getVueI18nPropertyValue,
defineGetter,
@@ -179,7 +178,7 @@ export function detectLocale(
const initialLocale = isFunction(initialLocaleLoader) ? initialLocaleLoader() : initialLocaleLoader
__DEBUG__ && console.log('detectLocale: initialLocale -', initialLocale)
- const { ssg, callType, firstAccess } = detectLocaleContext
+ const { ssg, callType, firstAccess, localeCookie } = detectLocaleContext
__DEBUG__ && console.log('detectLocale: (ssg, callType, firstAccess) - ', ssg, callType, firstAccess)
const {
@@ -232,7 +231,7 @@ export function detectLocale(
_detectBrowserLanguage
)
if (!finalLocale && _detectBrowserLanguage && _detectBrowserLanguage.useCookie) {
- finalLocale = getLocaleCookie() || ''
+ finalLocale = localeCookie.value || ''
}
__DEBUG__ && console.log('detectLocale: finalLocale last (finalLocale, defaultLocale) -', finalLocale, defaultLocale)