Skip to content

Commit 5f56bba

Browse files
ap-artoapavlovskyBobbieGoede
authoredMar 20, 2025··
fix: respect detectBrowserLanguage: false in SSG plugin (#3410)
--------- Co-authored-by: apavlovsky <apavlovsky@greengeeks.com> Co-authored-by: Bobbie Goede <bobbiegoede@gmail.com>
1 parent 2a4d88c commit 5f56bba

File tree

12 files changed

+122
-18
lines changed

12 files changed

+122
-18
lines changed
 

‎specs/fixtures/issues/3407/app.vue

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<template>
2+
<div>
3+
<NuxtRouteAnnouncer />
4+
<NuxtPage />
5+
</div>
6+
</template>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"heading": "i18n SSG issue",
3+
"text": "This is a test case of the nuxt/i18n SSG issue"
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"heading": "Problema de i18n SSG",
3+
"text": "Este es un caso de prueba del problema de nuxt/i18n SSG"
4+
}
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// https://nuxt.com/docs/api/configuration/nuxt-config
2+
export default defineNuxtConfig({
3+
modules: ['@nuxtjs/i18n'],
4+
i18n: {
5+
// some of the options are taken from the runtimeConfig
6+
strategy: 'no_prefix',
7+
differentDomains: true,
8+
lazy: false,
9+
locales: [
10+
{
11+
code: 'en',
12+
language: 'en-US',
13+
isCatchallLocale: true,
14+
domain: '127.0.0.1:7776',
15+
// domain: 'en.localhost',
16+
name: 'English',
17+
files: ['en.json']
18+
},
19+
{
20+
code: 'es',
21+
language: 'es-ES',
22+
domain: '127.0.0.1:7777',
23+
// domain: 'es.localhost',
24+
name: 'Español',
25+
files: ['es.json']
26+
}
27+
],
28+
detectBrowserLanguage: false
29+
}
30+
})
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"name": "fixture-3407",
3+
"private": true,
4+
"scripts": {
5+
"build": "nuxt build",
6+
"dev": "nuxt dev",
7+
"generate": "nuxt generate",
8+
"preview": "nuxt preview"
9+
},
10+
"devDependencies": {
11+
"@nuxtjs/i18n": "latest",
12+
"nuxt": "latest"
13+
}
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<template>
2+
<div>
3+
<h1 id="translated-heading">{{ $t('heading') }}</h1>
4+
</div>
5+
</template>

‎specs/helper.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ export async function renderPage(path = '/', options?: BrowserContextOptions) {
109109

110110
if (path) {
111111
/**
112-
* Nuxt uses `gotoPath` here, ths would throw errors as the given `path` can differ
112+
* Nuxt uses `gotoPath` here, this would throw errors as the given `path` can differ
113113
* from the final path due to language detection and redirects.
114114
*/
115115
// gotoPath(page, path)

‎specs/issues/3407.spec.ts

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { test, expect } from 'vitest'
2+
import { fileURLToPath } from 'node:url'
3+
import { createPage, setup, url } from '../utils'
4+
import { getText } from '../helper'
5+
6+
// this is an SSG test
7+
await setup({
8+
rootDir: fileURLToPath(new URL(`../fixtures/issues/3407`, import.meta.url)),
9+
browser: true,
10+
prerender: true,
11+
port: [7777, 7776]
12+
})
13+
14+
test('does not reset cookie no refresh', async () => {
15+
const page = await createPage('/')
16+
const heading = await getText(page, '#translated-heading')
17+
expect(heading).toEqual(`Problema de i18n SSG`)
18+
19+
await page.goto(url('/', 7776))
20+
const heading2 = await getText(page, '#translated-heading')
21+
expect(heading2).toEqual(`i18n SSG issue`)
22+
})

‎specs/utils/browser.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export async function waitForHydration(page: Page, url: string, waitUntil?: Goto
4949
}
5050
}
5151

52-
export async function createPage(path?: string, options?: BrowserContextOptions) {
52+
export async function createPage(path?: string, options?: BrowserContextOptions, port?: number) {
5353
const browser = await getBrowser()
5454
const page = await browser.newPage(options)
5555

@@ -59,13 +59,14 @@ export async function createPage(path?: string, options?: BrowserContextOptions)
5959
if (waitUntil && ['hydration', 'route'].includes(waitUntil)) {
6060
delete options.waitUntil
6161
}
62+
6263
const res = await _goto(url, options as Parameters<Page['goto']>[1])
6364
await waitForHydration(page, url, waitUntil)
6465
return res
6566
}
6667

6768
if (path) {
68-
await page.goto(url(path), { waitUntil: 'hydration' })
69+
await page.goto(url(path, port), { waitUntil: 'hydration' })
6970
}
7071

7172
return page

‎specs/utils/server.ts

+24-12
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ import { resolve } from 'pathe'
99
import { useTestContext } from './context'
1010
import { request } from 'undici'
1111

12+
function toArray<T>(value: T | T[]): T[] {
13+
return Array.isArray(value) ? value : [value]
14+
}
15+
1216
// @ts-expect-error type cast
1317
// eslint-disable-next-line
1418
const kit: typeof _kit = _kit.default || _kit
@@ -17,8 +21,8 @@ export async function startServer(env: Record<string, unknown> = {}) {
1721
const ctx = useTestContext()
1822
await stopServer()
1923
const host = '127.0.0.1'
20-
const port = ctx.options.port || (await getRandomPort(host))
21-
ctx.url = `http://${host}:${port}`
24+
const ports = ctx.options.port ? toArray(ctx.options.port) : [await getRandomPort(host)]
25+
ctx.url = `http://${host}:${ports[0]}`
2226
if (ctx.options.dev) {
2327
const nuxiCLI = await kit.resolvePath('nuxi/cli')
2428
ctx.serverProcess = exec(nuxiCLI, ['_dev'], {
@@ -27,15 +31,15 @@ export async function startServer(env: Record<string, unknown> = {}) {
2731
stdio: 'inherit',
2832
env: {
2933
...process.env,
30-
_PORT: String(port), // Used by internal _dev command
31-
PORT: String(port),
34+
_PORT: String(ports[0]), // Used by internal _dev command
35+
PORT: String(ports[0]),
3236
HOST: host,
3337
NODE_ENV: 'development',
3438
...env
3539
}
3640
}
3741
})
38-
await waitForPort(port, { retries: 32, host }).catch(() => {})
42+
await waitForPort(ports[0], { retries: 32, host }).catch(() => {})
3943
let lastError
4044
for (let i = 0; i < 150; i++) {
4145
await new Promise(resolve => setTimeout(resolve, 100))
@@ -51,35 +55,36 @@ export async function startServer(env: Record<string, unknown> = {}) {
5155
ctx.serverProcess.kill()
5256
throw lastError || new Error('Timeout waiting for dev server!')
5357
} else if (ctx.options.prerender) {
54-
const command = `pnpx serve ${ctx.nuxt!.options.nitro!.output?.publicDir} -l tcp://${host}:${port} --no-port-switching`
55-
// ; (await import('consola')).consola.restoreConsole()
58+
const listenTo = ports.map(port => `-l tcp://${host}:${port}`).join(' ')
59+
const command = `pnpx serve ${ctx.nuxt!.options.nitro!.output?.publicDir} ${listenTo} --no-port-switching`
60+
// ;(await import('consola')).consola.restoreConsole()
5661
const [_command, ...commandArgs] = command.split(' ')
5762

5863
ctx.serverProcess = exec(_command, commandArgs, {
5964
nodeOptions: {
6065
env: {
6166
...process.env,
62-
PORT: String(port),
67+
PORT: String(ports[0]),
6368
HOST: host,
6469
...env
6570
}
6671
}
6772
})
6873

69-
await waitForPort(port, { retries: 32, host, delay: 1000 })
74+
await waitForPort(ports[0], { retries: 32, host, delay: 1000 })
7075
} else {
7176
ctx.serverProcess = exec('node', [resolve(ctx.nuxt!.options.nitro.output!.dir!, 'server/index.mjs')], {
7277
nodeOptions: {
7378
stdio: 'inherit',
7479
env: {
7580
...process.env,
76-
PORT: String(port),
81+
PORT: String(ports[0]),
7782
HOST: host,
7883
...env
7984
}
8085
}
8186
})
82-
await waitForPort(port, { retries: 20, host })
87+
await waitForPort(ports[0], { retries: 20, host })
8388
}
8489
}
8590

@@ -102,14 +107,21 @@ export function undiciRequest(path: string, options?: Parameters<typeof request>
102107
return request(url(path), options)
103108
}
104109

105-
export function url(path: string) {
110+
export function url(path: string, port?: number) {
106111
const ctx = useTestContext()
107112
if (!ctx.url) {
108113
throw new Error('url is not available (is server option enabled?)')
109114
}
115+
110116
if (path.startsWith(ctx.url)) {
111117
return path
112118
}
119+
120+
// replace port in url
121+
if (port != null) {
122+
return ctx.url.slice(0, ctx.url.lastIndexOf(':')) + `:${port}/` + path
123+
}
124+
113125
return ctx.url + path
114126
}
115127

‎specs/utils/types.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export interface TestOptions {
2626
launch?: LaunchOptions
2727
}
2828
server: boolean
29-
port?: number
29+
port?: number | number[]
3030
}
3131

3232
export interface TestContext {

‎src/runtime/plugins/ssg-detect.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,21 @@ import { unref } from 'vue'
22
import { isSSG } from '#build/i18n.options.mjs'
33
import { defineNuxtPlugin } from '#imports'
44
import { createLogger } from '#nuxt-i18n/logger'
5-
import { detectBrowserLanguage } from '../internal'
5+
import { detectBrowserLanguage, runtimeDetectBrowserLanguage } from '../internal'
66
import type { NuxtApp } from '#app'
7+
import type { I18nPublicRuntimeConfig } from '#internal-i18n-types'
78

89
export default defineNuxtPlugin({
910
name: 'i18n:plugin:ssg-detect',
1011
dependsOn: ['i18n:plugin', 'i18n:plugin:route-locale-detect'],
1112
enforce: 'post',
1213
setup(nuxt) {
13-
if (!isSSG || (nuxt as NuxtApp).$i18n.strategy !== 'no_prefix') return
14+
if (
15+
!isSSG ||
16+
(nuxt as NuxtApp).$i18n.strategy !== 'no_prefix' ||
17+
!runtimeDetectBrowserLanguage(nuxt.$config.public.i18n as I18nPublicRuntimeConfig)
18+
)
19+
return
1420

1521
const nuxtApp = nuxt as NuxtApp
1622
const logger = /*#__PURE__*/ createLogger('plugin:i18n:ssg-detect')

0 commit comments

Comments
 (0)
Please sign in to comment.