未验证 提交 245499a0 编写于 作者: J JJ Kasper 提交者: GitHub

Handle preferring default locale over accept-lang preferred locale (#17883)

This updates to set the `NEXT_LOCALE` cookie to the default locale when the user prefers a different locale from the default in their `accept-language` header but visits the default locale path e.g. `/en-US` with a `accept-language` preferred header of `nl` will set the `NEXT_LOCALE=en-US` header and then redirect to `/`

x-ref: https://github.com/vercel/next.js/pull/17370
上级 7cf7c2f1
......@@ -221,12 +221,17 @@ const nextServerlessLoader: loader.Loader = function () {
// get pathname from URL with basePath stripped for locale detection
const i18n = ${i18n}
const accept = require('@hapi/accept')
const cookie = require('next/dist/compiled/cookie')
const { detectLocaleCookie } = require('next/dist/next-server/lib/i18n/detect-locale-cookie')
const { detectDomainLocale } = require('next/dist/next-server/lib/i18n/detect-domain-locale')
const { normalizeLocalePath } = require('next/dist/next-server/lib/i18n/normalize-locale-path')
let locales = i18n.locales
let defaultLocale = i18n.defaultLocale
let detectedLocale = detectLocaleCookie(req, i18n.locales)
let acceptPreferredLocale = accept.language(
req.headers['accept-language'],
i18n.locales
)
const detectedDomain = detectDomainLocale(
i18n.domains,
......@@ -237,12 +242,8 @@ const nextServerlessLoader: loader.Loader = function () {
detectedLocale = defaultLocale
}
if (!detectedLocale) {
detectedLocale = accept.language(
req.headers['accept-language'],
i18n.locales
)
}
// if not domain specific locale use accept-language preferred
detectedLocale = detectedLocale || acceptPreferredLocale
let localeDomainRedirect
const localePathResult = normalizeLocalePath(parsedUrl.pathname, i18n.locales)
......@@ -279,6 +280,7 @@ const nextServerlessLoader: loader.Loader = function () {
const shouldStripDefaultLocale =
detectedDefaultLocale &&
denormalizedPagePath.toLowerCase() === \`/\${i18n.defaultLocale.toLowerCase()}\`
const shouldAddLocalePrefix =
!detectedDefaultLocale && denormalizedPagePath === '/'
......@@ -294,6 +296,30 @@ const nextServerlessLoader: loader.Loader = function () {
shouldStripDefaultLocale
)
) {
// set the NEXT_LOCALE cookie when a user visits the default locale
// with the locale prefix so that they aren't redirected back to
// their accept-language preferred locale
if (
shouldStripDefaultLocale &&
acceptPreferredLocale !== defaultLocale
) {
const previous = res.getHeader('set-cookie')
res.setHeader(
'set-cookie',
[
...(typeof previous === 'string'
? [previous]
: Array.isArray(previous)
? previous
: []),
cookie.serialize('NEXT_LOCALE', defaultLocale, {
httpOnly: true,
path: '/',
})
])
}
res.setHeader(
'Location',
formatUrl({
......
......@@ -80,6 +80,7 @@ import { normalizeLocalePath } from '../lib/i18n/normalize-locale-path'
import { detectLocaleCookie } from '../lib/i18n/detect-locale-cookie'
import * as Log from '../../build/output/log'
import { detectDomainLocale } from '../lib/i18n/detect-domain-locale'
import cookie from 'next/dist/compiled/cookie'
const getCustomRouteMatcher = pathMatch(true)
......@@ -310,6 +311,10 @@ export default class Server {
const { pathname, ...parsed } = parseUrl(req.url || '/')
let defaultLocale = i18n.defaultLocale
let detectedLocale = detectLocaleCookie(req, i18n.locales)
let acceptPreferredLocale = accept.language(
req.headers['accept-language'],
i18n.locales
)
const detectedDomain = detectDomainLocale(i18n.domains, req)
if (detectedDomain) {
......@@ -317,12 +322,8 @@ export default class Server {
detectedLocale = defaultLocale
}
if (!detectedLocale) {
detectedLocale = accept.language(
req.headers['accept-language'],
i18n.locales
)
}
// if not domain specific locale use accept-language preferred
detectedLocale = detectedLocale || acceptPreferredLocale
let localeDomainRedirect: string | undefined
const localePathResult = normalizeLocalePath(pathname!, i18n.locales)
......@@ -360,6 +361,7 @@ export default class Server {
detectedDefaultLocale &&
denormalizedPagePath.toLowerCase() ===
`/${i18n.defaultLocale.toLowerCase()}`
const shouldAddLocalePrefix =
!detectedDefaultLocale && denormalizedPagePath === '/'
......@@ -371,6 +373,28 @@ export default class Server {
shouldAddLocalePrefix ||
shouldStripDefaultLocale)
) {
// set the NEXT_LOCALE cookie when a user visits the default locale
// with the locale prefix so that they aren't redirected back to
// their accept-language preferred locale
if (
shouldStripDefaultLocale &&
acceptPreferredLocale !== defaultLocale
) {
const previous = res.getHeader('set-cookie')
res.setHeader('set-cookie', [
...(typeof previous === 'string'
? [previous]
: Array.isArray(previous)
? previous
: []),
cookie.serialize('NEXT_LOCALE', defaultLocale, {
httpOnly: true,
path: '/',
}),
])
}
res.setHeader(
'Location',
formatUrl({
......
......@@ -132,6 +132,43 @@ function runTests(isDev) {
expect(result2.query).toEqual({})
})
it('should set locale cookie when removing default locale and accept-lang doesnt match', async () => {
const res = await fetchViaHTTP(appPort, '/en-US', undefined, {
headers: {
'accept-language': 'nl',
},
redirect: 'manual',
})
expect(res.status).toBe(307)
const parsedUrl = url.parse(res.headers.get('location'), true)
expect(parsedUrl.pathname).toBe('/')
expect(parsedUrl.query).toEqual({})
expect(res.headers.get('set-cookie')).toContain('NEXT_LOCALE=en-US')
})
it('should not redirect to accept-lang preferred locale with locale cookie', async () => {
const res = await fetchViaHTTP(appPort, '/', undefined, {
headers: {
'accept-language': 'nl',
cookie: 'NEXT_LOCALE=en-US',
},
redirect: 'manual',
})
expect(res.status).toBe(200)
const html = await res.text()
const $ = cheerio.load(html)
expect($('#router-locale').text()).toBe('en-US')
expect(JSON.parse($('#router-locales').text())).toEqual(locales)
expect($('html').attr('lang')).toBe('en-US')
expect($('#router-pathname').text()).toBe('/')
expect($('#router-as-path').text()).toBe('/')
})
it('should redirect to correct locale domain', async () => {
const checks = [
// test domain, locale prefix, redirect result
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册