fix(next): ensures requested lang header is supported (#5765)
This commit is contained in:
@@ -1,2 +1,2 @@
|
||||
export { getNextI18n } from '../utilities/getNextI18n.js'
|
||||
export { getNextRequestI18n } from '../utilities/getNextRequestI18n.js'
|
||||
export { getPayloadHMR } from '../utilities/getPayloadHMR.js'
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
import type { AcceptedLanguages, I18n } from '@payloadcms/translations'
|
||||
import type { SanitizedConfig } from 'payload/types'
|
||||
|
||||
import { initI18n } from '@payloadcms/translations'
|
||||
import { cookies, headers } from 'next/headers.js'
|
||||
|
||||
import { getRequestLanguage } from './getRequestLanguage.js'
|
||||
|
||||
export const getNextI18n = async ({
|
||||
config,
|
||||
language,
|
||||
}: {
|
||||
config: SanitizedConfig
|
||||
language?: AcceptedLanguages
|
||||
}): Promise<I18n> =>
|
||||
initI18n({
|
||||
config: config.i18n,
|
||||
context: 'client',
|
||||
language: language || getRequestLanguage({ config, cookies: cookies(), headers: headers() }),
|
||||
})
|
||||
19
packages/next/src/utilities/getNextRequestI18n.ts
Normal file
19
packages/next/src/utilities/getNextRequestI18n.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import type { I18n } from '@payloadcms/translations'
|
||||
import type { SanitizedConfig } from 'payload/types'
|
||||
|
||||
import { initI18n } from '@payloadcms/translations'
|
||||
import { cookies, headers } from 'next/headers.js'
|
||||
|
||||
import { getRequestLanguage } from './getRequestLanguage.js'
|
||||
|
||||
/**
|
||||
* In the context of NextJS, this function initializes the i18n object for the current request.
|
||||
*
|
||||
* It must be called on the server side, and within the lifecycle of a request since it relies on the request headers and cookies.
|
||||
*/
|
||||
export const getNextRequestI18n = async ({ config }: { config: SanitizedConfig }): Promise<I18n> =>
|
||||
initI18n({
|
||||
config: config.i18n,
|
||||
context: 'client',
|
||||
language: getRequestLanguage({ config, cookies: cookies(), headers: headers() }),
|
||||
})
|
||||
@@ -2,7 +2,7 @@ import type { AcceptedLanguages } from '@payloadcms/translations'
|
||||
import type { ReadonlyRequestCookies } from 'next/dist/server/web/spec-extension/adapters/request-cookies.js'
|
||||
import type { SanitizedConfig } from 'payload/config'
|
||||
|
||||
import { matchLanguage } from '@payloadcms/translations'
|
||||
import { extractHeaderLanguage } from '@payloadcms/translations'
|
||||
|
||||
type GetRequestLanguageArgs = {
|
||||
config: SanitizedConfig
|
||||
@@ -17,14 +17,20 @@ export const getRequestLanguage = ({
|
||||
defaultLanguage = 'en',
|
||||
headers,
|
||||
}: GetRequestLanguageArgs): AcceptedLanguages => {
|
||||
const acceptLanguage = headers.get('Accept-Language')
|
||||
const cookieLanguage = cookies.get(`${config.cookiePrefix || 'payload'}-lng`)
|
||||
const langCookie = cookies.get(`${config.cookiePrefix || 'payload'}-lng`)
|
||||
const languageFromCookie = typeof langCookie === 'string' ? langCookie : langCookie?.value
|
||||
const languageFromHeader = extractHeaderLanguage(headers.get('Accept-Language'))
|
||||
const fallbackLang = config?.i18n?.fallbackLanguage || defaultLanguage
|
||||
|
||||
const reqLanguage =
|
||||
(typeof cookieLanguage === 'string' ? cookieLanguage : cookieLanguage?.value) ||
|
||||
acceptLanguage ||
|
||||
config.i18n.fallbackLanguage ||
|
||||
defaultLanguage
|
||||
const supportedLanguageKeys = Object.keys(config?.i18n?.supportedLanguages || {})
|
||||
|
||||
return matchLanguage(reqLanguage) || defaultLanguage
|
||||
if (languageFromCookie && supportedLanguageKeys.includes(languageFromCookie)) {
|
||||
return languageFromCookie as AcceptedLanguages
|
||||
}
|
||||
|
||||
if (languageFromHeader && supportedLanguageKeys.includes(languageFromHeader)) {
|
||||
return languageFromHeader
|
||||
}
|
||||
|
||||
return supportedLanguageKeys.includes(fallbackLang) ? (fallbackLang as AcceptedLanguages) : 'en'
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { SanitizedCollectionConfig, SanitizedGlobalConfig } from 'payload/t
|
||||
|
||||
import type { GenerateViewMetadata } from '../Root/index.js'
|
||||
|
||||
import { getNextI18n } from '../../utilities/getNextI18n.js'
|
||||
import { getNextRequestI18n } from '../../utilities/getNextRequestI18n.js'
|
||||
import { generateMetadata as apiMeta } from '../API/meta.js'
|
||||
import { generateMetadata as editMeta } from '../Edit/meta.js'
|
||||
import { generateMetadata as livePreviewMeta } from '../LivePreview/meta.js'
|
||||
@@ -89,7 +89,7 @@ export const getMetaBySegment: GenerateEditViewMetadata = async ({
|
||||
}
|
||||
}
|
||||
|
||||
const i18n = await getNextI18n({
|
||||
const i18n = await getNextRequestI18n({
|
||||
config,
|
||||
})
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ import { HydrateClientUser } from '@payloadcms/ui/elements/HydrateClientUser'
|
||||
import { DefaultTemplate } from '@payloadcms/ui/templates/Default'
|
||||
import React, { Fragment } from 'react'
|
||||
|
||||
import { getNextRequestI18n } from '../../utilities/getNextRequestI18n.js'
|
||||
import { initPage } from '../../utilities/initPage.js'
|
||||
import { getNextI18n } from '.././../utilities/getNextI18n.js'
|
||||
import { NotFoundClient } from './index.client.js'
|
||||
|
||||
export const generatePageMetadata = async ({
|
||||
@@ -19,7 +19,7 @@ export const generatePageMetadata = async ({
|
||||
}): Promise<Metadata> => {
|
||||
const config = await configPromise
|
||||
|
||||
const i18n = await getNextI18n({
|
||||
const i18n = await getNextRequestI18n({
|
||||
config,
|
||||
})
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Metadata } from 'next'
|
||||
import type { SanitizedConfig } from 'payload/types'
|
||||
|
||||
import { getNextI18n } from '../../utilities/getNextI18n.js'
|
||||
import { getNextRequestI18n } from '../../utilities/getNextRequestI18n.js'
|
||||
import { generateAccountMetadata } from '../Account/index.js'
|
||||
import { generateCreateFirstUserMetadata } from '../CreateFirstUser/index.js'
|
||||
import { generateDashboardMetadata } from '../Dashboard/index.js'
|
||||
@@ -49,7 +49,7 @@ export const generatePageMetadata = async ({ config: configPromise, params }: Ar
|
||||
const isGlobal = segmentOne === 'globals'
|
||||
const isCollection = segmentOne === 'collections'
|
||||
|
||||
const i18n = await getNextI18n({
|
||||
const i18n = await getNextRequestI18n({
|
||||
config,
|
||||
})
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export { importDateFNSLocale } from '../importDateFNSLocale.js'
|
||||
export { getTranslation } from '../utilities/getTranslation.js'
|
||||
export { initI18n, t } from '../utilities/init.js'
|
||||
export { acceptedLanguages, matchLanguage, rtlLanguages } from '../utilities/languages.js'
|
||||
export { acceptedLanguages, extractHeaderLanguage, rtlLanguages } from '../utilities/languages.js'
|
||||
export type * from '../types.js'
|
||||
|
||||
@@ -149,7 +149,7 @@ function parseAcceptLanguage(acceptLanguageHeader: string): LanguagePreference[]
|
||||
.sort((a, b) => b.quality - a.quality) // Sort by quality, highest to lowest
|
||||
}
|
||||
|
||||
export function matchLanguage(acceptLanguageHeader: string): AcceptedLanguages | undefined {
|
||||
export function extractHeaderLanguage(acceptLanguageHeader: string): AcceptedLanguages | undefined {
|
||||
const parsedHeader = parseAcceptLanguage(acceptLanguageHeader)
|
||||
|
||||
let matchedLanguage: AcceptedLanguages
|
||||
|
||||
Reference in New Issue
Block a user