Skip to content

Commit

Permalink
fix: initial redirect breaks reactivity in static mode
Browse files Browse the repository at this point in the history
When we've detected browser language and need to redirect we need to do
it from the middleware for the page to transition correctly. The problem
is Nuxt doesn't trigger middleware for initial navigation in static
mode (the idea is that it already ran when generating page on the server),
so we can't do it properly.

Fixed by hooking into the VueRouter "beforeEach" navigation hook so that
we can handle it from the right place. Still, as Nuxt doesn't know that
we are redirecting, it would throw some errors trying to load previous
page. So use "location.assign()" to "hard" redirect.

Resolves #737
  • Loading branch information
rchl committed Jun 3, 2020
1 parent 145f3b2 commit ef80b0d
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 11 deletions.
6 changes: 5 additions & 1 deletion src/templates/middleware.js
@@ -1,11 +1,15 @@
import middleware from '../middleware'

/** @type {import('@nuxt/types').Middleware} */
middleware.nuxti18n = async (context) => {
const { app, isHMR } = context

if (isHMR) {
return
}

await app.i18n.__onNavigate()
const [status, redirectPath] = await app.i18n.__onNavigate(context.route)
if (status && redirectPath) {
context.redirect(status, redirectPath)
}
}
18 changes: 9 additions & 9 deletions src/templates/plugin.main.js
Expand Up @@ -160,9 +160,7 @@ export default async (context) => {
}

// Called by middleware on navigation (also on the initial one).
const onNavigate = async () => {
const { route } = context

const onNavigate = async route => {
// Handle root path redirect
if (route.path === '/' && rootRedirect) {
let statusCode = 302
Expand All @@ -173,15 +171,13 @@ export default async (context) => {
path = rootRedirect.path
}

redirect(statusCode, `/${path}`, route.query)
return
return [statusCode, `/${path}`]
}

const storedRedirect = app.i18n.__redirect
if (storedRedirect) {
app.i18n.__redirect = null
redirect(storedRedirect)
return
return [302, storedRedirect]
}

app.i18n.__baseUrl = resolveBaseUrl(baseUrl, context)
Expand All @@ -191,6 +187,8 @@ export default async (context) => {
getLocaleFromRoute(route) || app.i18n.locale || app.i18n.defaultLocale || ''

await app.i18n.setLocale(finalLocale)

return [null, null]
}

const extendVueI18nInstance = i18n => {
Expand Down Expand Up @@ -248,8 +246,10 @@ export default async (context) => {
finalLocale = detectedBrowserLocale || finalLocale
await loadAndSetLocale(finalLocale, { initialSetup: true })

// Trigger onNavigate manually for Nuxt generate in universal mode as Nuxt doesn't do that on load.
if (process.client && process.static && IS_UNIVERSAL_MODE) {
await onNavigate()
const redirectTo = (await onNavigate(context.route))[1]
if (redirectTo) {
location.assign(redirectTo)
}
}
}
29 changes: 28 additions & 1 deletion test/browser.test.js
Expand Up @@ -163,6 +163,33 @@ describe(`${browserString} (generate)`, () => {
await navigate(page, '/')
expect(await (await page.$('body')).textContent()).toContain('locale: en')
})

// Issue https://github.com/nuxt-community/nuxt-i18n/issues/737
test('reactivity works after redirecting to detected browser locale (root path)', async () => {
page = await browser.newPage({ locale: 'fr' })
await page.goto(server.getUrl('/'))
expect(page.url()).toBe(server.getUrl('/fr/'))
// Need to delay a bit due to vue-meta batching with 10ms timeout.
await page.waitForTimeout(20)
expect(await page.title()).toBe('Accueil')

await navigate(page, '/')
await page.waitForTimeout(20)
expect(await page.title()).toBe('Homepage')
})

test('reactivity works after redirecting to detected browser locale (sub-path)', async () => {
page = await browser.newPage({ locale: 'fr' })
await page.goto(server.getUrl('/posts/'))
expect(page.url()).toBe(server.getUrl('/fr/articles/'))
// Need to delay a bit due to vue-meta batching with 10ms timeout.
await page.waitForTimeout(20)
expect(await page.title()).toBe('Articles')

await navigate(page, '/posts/')
await page.waitForTimeout(20)
expect(await page.title()).toBe('Posts')
})
})

describe(`${browserString} (generate, prefix strategy)`, () => {
Expand Down Expand Up @@ -252,7 +279,7 @@ describe(`${browserString} (generate with detectBrowserLanguage.fallbackLocale)`
test('redirects to browser locale', async () => {
page = await browser.newPage({ locale: 'fr' })
await page.goto(server.getUrl('/'))
expect(page.url()).toBe(server.getUrl('/fr'))
expect(page.url()).toBe(server.getUrl('/fr/'))
expect(await (await page.$('body')).textContent()).toContain('locale: fr')
})
})
Expand Down
5 changes: 5 additions & 0 deletions test/fixture/basic/pages/index.vue
Expand Up @@ -13,6 +13,11 @@ import LangSwitcher from '../components/LangSwitcher'
export default {
components: {
LangSwitcher
},
head () {
return {
title: this.$t('home')
}
}
}
</script>
5 changes: 5 additions & 0 deletions test/fixture/basic/pages/posts.vue
Expand Up @@ -13,6 +13,11 @@ export default {
components: {
LangSwitcher
},
head () {
return {
title: this.$t('posts')
}
},
nuxtI18n: {
paths: {
fr: '/articles'
Expand Down

0 comments on commit ef80b0d

Please sign in to comment.