rsnext/packages/next/shared/lib/i18n/get-locale-metadata.ts

117 lines
3.6 KiB
TypeScript
Raw Normal View History

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}`,
})
}
}
}