Skip to content

Commit c8f280b

Browse files
authored
fix: detect and redirect only on rendered routes (#3846)
1 parent 0ee32a5 commit c8f280b

8 files changed

Lines changed: 102 additions & 24 deletions

File tree

pnpm-lock.yaml

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// https://nuxt.com/docs/api/configuration/nuxt-config
2+
export default defineNuxtConfig({
3+
modules: ["@nuxtjs/i18n"],
4+
i18n: {
5+
locales: ["en", "fr"],
6+
defaultLocale: "en",
7+
strategy: "prefix",
8+
detectBrowserLanguage: {
9+
useCookie: true,
10+
redirectOn: 'no prefix',
11+
},
12+
},
13+
});
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"name": "fixture-3842",
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+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<template>
2+
<div>
3+
Catch all
4+
</div>
5+
</template>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<template>
2+
<div>
3+
Hello world!
4+
</div>
5+
</template>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default defineEventHandler(() => {
2+
return { message: 'Hello from test endpoint!' }
3+
})

specs/issues/3842.spec.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { test, expect, describe } from 'vitest'
2+
import { fileURLToPath } from 'node:url'
3+
import { fetch, setup, url } from '../utils'
4+
5+
describe('#3842 - prefix strategy catch-all redirects on server route', async () => {
6+
await setup({
7+
rootDir: fileURLToPath(new URL(`../fixtures/issues/3842`, import.meta.url)),
8+
})
9+
10+
test('should not redirect API calls', async () => {
11+
const res = await fetch(url('/api/test'))
12+
13+
expect(res.redirected).toBe(false)
14+
expect(await res.json()).toMatchInlineSnapshot(`
15+
{
16+
"message": "Hello from test endpoint!",
17+
}
18+
`)
19+
})
20+
})

src/runtime/server/plugin.ts

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { stringify } from 'devalue'
22
import { defineI18nMiddleware } from '@intlify/h3'
33
import { defineNitroPlugin, useStorage } from 'nitropack/runtime'
4-
import { tryUseI18nContext, createI18nContext } from './context'
4+
import { tryUseI18nContext, createI18nContext, useI18nContext } from './context'
55
import { createUserLocaleDetector } from './utils/locale-detector'
66
import { pickNested } from './utils/messages-utils'
77
import { createLocaleConfigs, getDefaultLocaleForDomain, isSupportedLocale } from '../shared/locales'
@@ -142,39 +142,48 @@ export default defineNitroPlugin(async nitro => {
142142
}
143143

144144
const baseUrlGetter = createBaseUrlGetter()
145-
nitro.hooks.hook('request', async (event: H3Event) => {
145+
146+
async function initialize(event: H3Event) {
146147
const options = await setupVueI18nOptions(getDefaultLocaleForDomain(getHost(event)) || _defaultLocale)
147-
const url = getRequestURL(event)
148+
const localeConfigs = createLocaleConfigs(options.fallbackLocale)
148149
const ctx = createI18nContext()
149150

150-
const localeConfigs = createLocaleConfigs(options.fallbackLocale)
151151
ctx.vueI18nOptions = options
152152
ctx.localeConfigs = localeConfigs
153153

154154
event.context.nuxtI18n = ctx
155+
return ctx
156+
}
157+
158+
nitro.hooks.hook('request', async (event: H3Event) => {
159+
await initialize(event)
160+
})
155161

156-
if (__I18N_SERVER_REDIRECT__) {
157-
const detector = useDetectors(event, detection)
158-
const localeSegment = detector.route(event.path)
159-
const pathLocale = (isSupportedLocale(localeSegment) && localeSegment) || undefined
160-
const path = (pathLocale && url.pathname.slice(pathLocale.length + 1)) || url.pathname
162+
nitro.hooks.hook('render:before', async ({ event }) => {
163+
if (!__I18N_SERVER_REDIRECT__) return
161164

162-
// attempt to only run i18n detection for nuxt pages and i18n server routes
163-
if (!url.pathname.includes('/_i18n/') && !isExistingNuxtRoute(path)) {
164-
return
165-
}
165+
const ctx = import.meta.prerender && !event.context.nuxtI18n ? await initialize(event) : useI18nContext(event)
166+
const url = getRequestURL(event)
167+
const detector = useDetectors(event, detection)
168+
const localeSegment = detector.route(event.path)
169+
const pathLocale = (isSupportedLocale(localeSegment) && localeSegment) || undefined
170+
const path = (pathLocale && url.pathname.slice(pathLocale.length + 1)) || url.pathname
171+
172+
// attempt to only run i18n detection for nuxt pages and i18n server routes
173+
if (!url.pathname.includes('/_i18n/') && !isExistingNuxtRoute(path)) {
174+
return
175+
}
166176

167-
const resolved = resolveRedirectPath(event, path, pathLocale, options.defaultLocale, detector)
168-
if (resolved.path && resolved.path !== url.pathname) {
169-
ctx.detectLocale = resolved.locale
170-
detection.useCookie && setCookie(event, detection.cookieKey, resolved.locale, cookieOptions)
171-
await sendRedirect(
172-
event,
173-
joinURL(baseUrlGetter(event, options.defaultLocale), resolved.path + url.search),
174-
resolved.code
175-
)
176-
return
177-
}
177+
const resolved = resolveRedirectPath(event, path, pathLocale, ctx.vueI18nOptions!.defaultLocale, detector)
178+
if (resolved.path && resolved.path !== url.pathname) {
179+
ctx.detectLocale = resolved.locale
180+
detection.useCookie && setCookie(event, detection.cookieKey, resolved.locale, cookieOptions)
181+
await sendRedirect(
182+
event,
183+
joinURL(baseUrlGetter(event, ctx.vueI18nOptions!.defaultLocale), resolved.path + url.search),
184+
resolved.code,
185+
)
186+
return
178187
}
179188
})
180189

0 commit comments

Comments
 (0)