a815ba9f79
This PR adds support for [Middleware as per RFC ](https://github.com/vercel/next.js/discussions/29750). ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have helpful link attached, see `contributing.md` ## Documentation / Examples - [ ] Make sure the linting passes
116 lines
3.6 KiB
TypeScript
116 lines
3.6 KiB
TypeScript
import { acceptLanguage } from '../../../server/accept-header'
|
|
import { denormalizePagePath } from '../../../server/denormalize-page-path'
|
|
import { detectDomainLocale } from './detect-domain-locale'
|
|
import { formatUrl } from '../router/utils/format-url'
|
|
import { normalizeLocalePath } from './normalize-locale-path'
|
|
import type { I18NConfig, DomainLocale } from '../../../server/config-shared'
|
|
|
|
interface Params {
|
|
cookies(): { [key: string]: string }
|
|
headers?: { [key: string]: string | string[] | undefined }
|
|
nextConfig: { basePath?: string; i18n: I18NConfig; trailingSlash?: boolean }
|
|
url: { hostname?: string | null; pathname: string }
|
|
}
|
|
|
|
export function getLocaleMetadata(params: Params) {
|
|
const { i18n } = params.nextConfig
|
|
const { cookies, headers, nextConfig, url } = params
|
|
const path = normalizeLocalePath(url.pathname, i18n.locales)
|
|
const domain = detectDomainLocale(i18n.domains, getHostname(url, headers))
|
|
const defaultLocale = domain?.defaultLocale || i18n.defaultLocale
|
|
const preferredLocale = getAcceptPreferredLocale(i18n, headers)
|
|
return {
|
|
path,
|
|
domain,
|
|
defaultLocale,
|
|
locale: path?.detectedLocale || defaultLocale,
|
|
redirect: getRedirect({
|
|
locale: {
|
|
preferred: preferredLocale,
|
|
default: defaultLocale,
|
|
detected:
|
|
path?.detectedLocale ||
|
|
domain?.defaultLocale ||
|
|
getLocaleFromCookie(i18n, cookies) ||
|
|
preferredLocale ||
|
|
i18n.defaultLocale,
|
|
},
|
|
domain,
|
|
nextConfig,
|
|
url,
|
|
}),
|
|
trailingSlash:
|
|
url.pathname !== '/'
|
|
? url.pathname.endsWith('/')
|
|
: nextConfig.trailingSlash,
|
|
}
|
|
}
|
|
|
|
function getLocaleFromCookie(
|
|
i18n: I18NConfig,
|
|
cookies: () => { [key: string]: string }
|
|
) {
|
|
const nextLocale = cookies()?.NEXT_LOCALE?.toLowerCase()
|
|
return nextLocale
|
|
? i18n.locales.find((locale) => nextLocale === locale.toLowerCase())
|
|
: undefined
|
|
}
|
|
|
|
function getAcceptPreferredLocale(
|
|
i18n: I18NConfig,
|
|
headers?: { [key: string]: string | string[] | undefined }
|
|
) {
|
|
const value = headers?.['accept-language']
|
|
if (i18n.localeDetection !== false && value && !Array.isArray(value)) {
|
|
try {
|
|
return acceptLanguage(value, i18n.locales)
|
|
} catch (err) {}
|
|
}
|
|
}
|
|
|
|
function getHostname(
|
|
parsed: { hostname?: string | null },
|
|
headers?: { [key: string]: string | string[] | undefined }
|
|
) {
|
|
return ((!Array.isArray(headers?.host) && headers?.host) || parsed.hostname)
|
|
?.split(':')[0]
|
|
.toLowerCase()
|
|
}
|
|
|
|
function getRedirect({
|
|
domain,
|
|
locale,
|
|
nextConfig,
|
|
url,
|
|
}: {
|
|
domain?: DomainLocale
|
|
locale: { default: string; detected: string; preferred?: string }
|
|
nextConfig: { basePath?: string; i18n: I18NConfig; trailingSlash?: boolean }
|
|
url: { hostname?: string | null; pathname: string }
|
|
}) {
|
|
const isRootPath = denormalizePagePath(url.pathname) === '/'
|
|
if (nextConfig.i18n.localeDetection !== false && isRootPath) {
|
|
const preferredDomain = detectDomainLocale(
|
|
nextConfig.i18n.domains,
|
|
undefined,
|
|
locale.preferred
|
|
)
|
|
|
|
if (domain && preferredDomain) {
|
|
const isPDomain = preferredDomain.domain === domain.domain
|
|
const isPLocale = preferredDomain.defaultLocale === locale.preferred
|
|
if (!isPDomain || !isPLocale) {
|
|
const scheme = `http${preferredDomain.http ? '' : 's'}`
|
|
const rlocale = isPLocale ? '' : locale.preferred
|
|
return `${scheme}://${preferredDomain.domain}/${rlocale}`
|
|
}
|
|
}
|
|
|
|
if (locale.detected.toLowerCase() !== locale.default.toLowerCase()) {
|
|
return formatUrl({
|
|
...url,
|
|
pathname: `${nextConfig.basePath || ''}/${locale.detected}`,
|
|
})
|
|
}
|
|
}
|
|
}
|