chore: corrects dateFNS keys, stricter types
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
import type { AcceptedLanguages } from '@payloadcms/translations'
|
||||
import type { SanitizedConfig } from 'payload/types'
|
||||
|
||||
import { rtlLanguages } from '@payloadcms/translations'
|
||||
import { initI18n } from '@payloadcms/translations'
|
||||
import { RootProvider } from '@payloadcms/ui/providers/Root'
|
||||
import '@payloadcms/ui/scss/app.scss'
|
||||
@@ -19,8 +21,6 @@ export const metadata = {
|
||||
title: 'Next.js',
|
||||
}
|
||||
|
||||
const rtlLanguages = ['ar', 'fa', 'ha', 'ku', 'ur', 'ps', 'dv', 'ks', 'khw', 'he', 'yi']
|
||||
|
||||
export const RootLayout = async ({
|
||||
children,
|
||||
config: configPromise,
|
||||
@@ -33,17 +33,18 @@ export const RootLayout = async ({
|
||||
const headers = getHeaders()
|
||||
const cookies = parseCookies(headers)
|
||||
|
||||
const languageCode =
|
||||
getRequestLanguage({
|
||||
config,
|
||||
cookies,
|
||||
headers,
|
||||
}) ?? config.i18n.fallbackLanguage
|
||||
const languageCode = getRequestLanguage({
|
||||
config,
|
||||
cookies,
|
||||
headers,
|
||||
})
|
||||
|
||||
const i18n = await initI18n({ config: config.i18n, context: 'client', language: languageCode })
|
||||
const clientConfig = await createClientConfig({ config, t: i18n.t })
|
||||
|
||||
const dir = rtlLanguages.includes(languageCode) ? 'RTL' : 'LTR'
|
||||
const dir = (rtlLanguages as unknown as AcceptedLanguages[]).includes(languageCode)
|
||||
? 'RTL'
|
||||
: 'LTR'
|
||||
|
||||
const languageOptions = Object.entries(config.i18n.supportedLanguages || {}).reduce(
|
||||
(acc, [language, languageConfig]) => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { I18n } from '@payloadcms/translations'
|
||||
import type { AcceptedLanguages, I18n } from '@payloadcms/translations'
|
||||
import type { SanitizedConfig } from 'payload/types'
|
||||
|
||||
import { initI18n } from '@payloadcms/translations'
|
||||
@@ -11,7 +11,7 @@ export const getNextI18n = async ({
|
||||
language,
|
||||
}: {
|
||||
config: SanitizedConfig
|
||||
language?: string
|
||||
language?: AcceptedLanguages
|
||||
}): Promise<I18n> =>
|
||||
initI18n({
|
||||
config: config.i18n,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
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'
|
||||
|
||||
@@ -15,13 +16,14 @@ export const getRequestLanguage = ({
|
||||
cookies,
|
||||
defaultLanguage = 'en',
|
||||
headers,
|
||||
}: GetRequestLanguageArgs): string => {
|
||||
}: GetRequestLanguageArgs): AcceptedLanguages => {
|
||||
const acceptLanguage = headers.get('Accept-Language')
|
||||
const cookieLanguage = cookies.get(`${config.cookiePrefix || 'payload'}-lng`)
|
||||
|
||||
const reqLanguage =
|
||||
(typeof cookieLanguage === 'string' ? cookieLanguage : cookieLanguage?.value) ||
|
||||
acceptLanguage ||
|
||||
config.i18n.fallbackLanguage ||
|
||||
defaultLanguage
|
||||
|
||||
return matchLanguage(reqLanguage)
|
||||
|
||||
@@ -43,3 +43,121 @@ These are the translations for Payload. Translations are used on both the server
|
||||
// or
|
||||
pnpm build
|
||||
```
|
||||
|
||||
Here is a full list of language keys. Note that these are not all implemented, but if you would like to contribute and add a new language, you can use this list as a reference:
|
||||
|
||||
| Language Code | Language Name |
|
||||
| -------------- | ------------------------------------------ |
|
||||
| af | Afrikaans |
|
||||
| am | Amharic |
|
||||
| ar-sa | Arabic (Saudi Arabia) |
|
||||
| as | Assamese |
|
||||
| az-Latn | Azerbaijani (Latin) |
|
||||
| be | Belarusian |
|
||||
| bg | Bulgarian |
|
||||
| bn-BD | Bangla (Bangladesh) |
|
||||
| bn-IN | Bangla (India) |
|
||||
| bs | Bosnian (Latin) |
|
||||
| ca | Catalan Spanish |
|
||||
| ca-ES-valencia | Valencian |
|
||||
| cs | Czech |
|
||||
| cy | Welsh |
|
||||
| da | Danish |
|
||||
| de | German (Germany) |
|
||||
| el | Greek |
|
||||
| en-GB | English (United Kingdom) |
|
||||
| en-US | English (United States) |
|
||||
| es | Spanish (Spain) |
|
||||
| es-ES | Spanish (Spain) |
|
||||
| es-US | Spanish (United States) |
|
||||
| es-MX | Spanish (Mexico) |
|
||||
| et | Estonian |
|
||||
| eu | Basque |
|
||||
| fa | Persian |
|
||||
| fi | Finnish |
|
||||
| fil-Latn | Filipino |
|
||||
| fr | French (France) |
|
||||
| fr-FR | French (France) |
|
||||
| fr-CA | French (Canada) |
|
||||
| ga | Irish |
|
||||
| gd-Latn | Scottish Gaelic |
|
||||
| gl | Galician |
|
||||
| gu | Gujarati |
|
||||
| ha-Latn | Hausa (Latin) |
|
||||
| he | Hebrew |
|
||||
| hi | Hindi |
|
||||
| hr | Croatian |
|
||||
| hu | Hungarian |
|
||||
| hy | Armenian |
|
||||
| id | Indonesian |
|
||||
| ig-Latn | Igbo |
|
||||
| is | Icelandic |
|
||||
| it | Italian (Italy) |
|
||||
| it-it | Italian (Italy) |
|
||||
| ja | Japanese |
|
||||
| ka | Georgian |
|
||||
| kk | Kazakh |
|
||||
| km | Khmer |
|
||||
| kn | Kannada |
|
||||
| ko | Korean |
|
||||
| kok | Konkani |
|
||||
| ku-Arab | Central Kurdish |
|
||||
| ky-Cyrl | Kyrgyz |
|
||||
| lb | Luxembourgish |
|
||||
| lt | Lithuanian |
|
||||
| lv | Latvian |
|
||||
| mi-Latn | Maori |
|
||||
| mk | Macedonian |
|
||||
| ml | Malayalam |
|
||||
| mn-Cyrl | Mongolian (Cyrillic) |
|
||||
| mr | Marathi |
|
||||
| ms | Malay (Malaysia) |
|
||||
| mt | Maltese |
|
||||
| nb | Norwegian (Bokmål) |
|
||||
| ne | Nepali (Nepal) |
|
||||
| nl | Dutch (Netherlands) |
|
||||
| nl-BE | Dutch (Netherlands) |
|
||||
| nn | Norwegian (Nynorsk) |
|
||||
| nso | Sesotho sa Leboa |
|
||||
| or | Odia |
|
||||
| pa | Punjabi (Gurmukhi) |
|
||||
| pa-Arab | Punjabi (Arabic) |
|
||||
| pl | Polish |
|
||||
| prs-Arab | Dari |
|
||||
| pt-BR | Portuguese (Brazil) |
|
||||
| pt-PT | Portuguese (Portugal) |
|
||||
| qut-Latn | K’iche’ |
|
||||
| quz | Quechua (Peru) |
|
||||
| ro | Romanian (Romania) |
|
||||
| ru | Russian |
|
||||
| rw | Kinyarwanda |
|
||||
| sd-Arab | Sindhi (Arabic) |
|
||||
| si | Sinhala |
|
||||
| sk | Slovak |
|
||||
| sl | Slovenian |
|
||||
| sq | Albanian |
|
||||
| sr-Cyrl-BA | Serbian (Cyrillic, Bosnia and Herzegovina) |
|
||||
| sr-Cyrl-RS | Serbian (Cyrillic, Serbia) |
|
||||
| sr-Latn-RS | Serbian (Latin, Serbia) |
|
||||
| sv | Swedish (Sweden) |
|
||||
| sw | Kiswahili |
|
||||
| ta | Tamil |
|
||||
| te | Telugu |
|
||||
| tg-Cyrl | Tajik (Cyrillic) |
|
||||
| th | Thai |
|
||||
| ti | Tigrinya |
|
||||
| tk-Latn | Turkmen (Latin) |
|
||||
| tn | Setswana |
|
||||
| tr | Turkish |
|
||||
| tt-Cyrl | Tatar (Cyrillic) |
|
||||
| ug-Arab | Uyghur |
|
||||
| uk | Ukrainian |
|
||||
| ur | Urdu |
|
||||
| uz-Latn | Uzbek (Latin) |
|
||||
| vi | Vietnamese |
|
||||
| wo | Wolof |
|
||||
| xh | isiXhosa |
|
||||
| yo-Latn | Yoruba |
|
||||
| zh-Hans | Chinese (Simplified) |
|
||||
| zh-Hant | Chinese (Traditional) |
|
||||
| zu | isiZulu |
|
||||
|
||||
@@ -61,5 +61,5 @@ export const translations = {
|
||||
ua,
|
||||
vi,
|
||||
zh,
|
||||
'zh-tw': zhTw,
|
||||
'zh-TW': zhTw,
|
||||
} as SupportedLanguages
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Language } from '../types.js'
|
||||
|
||||
export const fa: Language = {
|
||||
dateFNSKey: 'fa',
|
||||
dateFNSKey: 'fa-IR',
|
||||
translations: {
|
||||
authentication: {
|
||||
account: 'نمایه',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Language } from '../types.js'
|
||||
|
||||
export const rs: Language = {
|
||||
dateFNSKey: 'rs',
|
||||
dateFNSKey: 'en-US',
|
||||
translations: {
|
||||
authentication: {
|
||||
account: 'Налог',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Language } from '../types.js'
|
||||
|
||||
export const rsLatin: Language = {
|
||||
dateFNSKey: 'rs-latin',
|
||||
dateFNSKey: 'en-US',
|
||||
translations: {
|
||||
authentication: {
|
||||
account: 'Nalog',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Language } from '../types.js'
|
||||
|
||||
export const zh: Language = {
|
||||
dateFNSKey: 'zh',
|
||||
dateFNSKey: 'zh-CN',
|
||||
translations: {
|
||||
authentication: {
|
||||
account: '帐户',
|
||||
|
||||
@@ -1,9 +1,37 @@
|
||||
import type { Locale } from 'date-fns'
|
||||
|
||||
import type { acceptedLanguages } from './utilities/init.js'
|
||||
import type { acceptedLanguages } from './utilities/languages.js'
|
||||
|
||||
type DateFNSKeys =
|
||||
| 'ar'
|
||||
| 'az'
|
||||
| 'bg'
|
||||
| 'cs'
|
||||
| 'de'
|
||||
| 'en-US'
|
||||
| 'es'
|
||||
| 'fa-IR'
|
||||
| 'fr'
|
||||
| 'hr'
|
||||
| 'hu'
|
||||
| 'it'
|
||||
| 'ja'
|
||||
| 'ko'
|
||||
| 'nb'
|
||||
| 'nl'
|
||||
| 'pl'
|
||||
| 'pt'
|
||||
| 'ro'
|
||||
| 'ru'
|
||||
| 'sv'
|
||||
| 'th'
|
||||
| 'tr'
|
||||
| 'vi'
|
||||
| 'zh-CN'
|
||||
| 'zh-TW'
|
||||
|
||||
export type Language = {
|
||||
dateFNSKey: string
|
||||
dateFNSKey: DateFNSKeys
|
||||
translations: {
|
||||
[namespace: string]: {
|
||||
[key: string]: string
|
||||
@@ -22,7 +50,7 @@ export type TFunction = (key: string, options?: Record<string, any>) => string
|
||||
export type I18n = {
|
||||
dateFNS: Locale
|
||||
/** Corresponding dateFNS key */
|
||||
dateFNSKey: string
|
||||
dateFNSKey: DateFNSKeys
|
||||
/** The fallback language */
|
||||
fallbackLanguage: string
|
||||
/** The language of the request */
|
||||
@@ -52,5 +80,10 @@ export type InitTFunction = (args: {
|
||||
export type InitI18n = (args: {
|
||||
config: I18nOptions
|
||||
context: 'api' | 'client'
|
||||
language?: string
|
||||
language?: AcceptedLanguages
|
||||
}) => Promise<I18n>
|
||||
|
||||
export type LanguagePreference = {
|
||||
language: AcceptedLanguages
|
||||
quality?: number
|
||||
}
|
||||
|
||||
@@ -54,10 +54,10 @@ function sortObject(obj) {
|
||||
return sortedObject
|
||||
}
|
||||
|
||||
export const getTranslationsByContext = (translations: Language, context: 'api' | 'client') => {
|
||||
export const getTranslationsByContext = (selectedLanguage: Language, context: 'api' | 'client') => {
|
||||
if (context === 'client') {
|
||||
return sortObject(filterKeys(translations, '', clientTranslationKeys))
|
||||
return sortObject(filterKeys(selectedLanguage.translations, '', clientTranslationKeys))
|
||||
} else {
|
||||
return translations
|
||||
return selectedLanguage.translations
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,74 +130,9 @@ export const t: TFunctionConstructor = ({ key, translations, vars }) => {
|
||||
return translationString
|
||||
}
|
||||
|
||||
type LanguagePreference = {
|
||||
language: string
|
||||
quality?: number
|
||||
}
|
||||
|
||||
function parseAcceptLanguage(header: string): LanguagePreference[] {
|
||||
return header
|
||||
.split(',')
|
||||
.map((lang) => {
|
||||
const [language, quality] = lang.trim().split(';q=')
|
||||
return {
|
||||
language,
|
||||
quality: quality ? parseFloat(quality) : 1,
|
||||
}
|
||||
})
|
||||
.sort((a, b) => b.quality - a.quality) // Sort by quality, highest to lowest
|
||||
}
|
||||
|
||||
export const acceptedLanguages = [
|
||||
'ar',
|
||||
'az',
|
||||
'bg',
|
||||
'cs',
|
||||
'de',
|
||||
'en',
|
||||
'es',
|
||||
'fa',
|
||||
'fr',
|
||||
'hr',
|
||||
'hu',
|
||||
'it',
|
||||
'ja',
|
||||
'ko',
|
||||
'my',
|
||||
'nb',
|
||||
'nl',
|
||||
'pl',
|
||||
'pt',
|
||||
'ro',
|
||||
'rs',
|
||||
'rsLatin',
|
||||
'ru',
|
||||
'sv',
|
||||
'th',
|
||||
'tr',
|
||||
'ua',
|
||||
'vi',
|
||||
'zh',
|
||||
'zhTw',
|
||||
] as const
|
||||
|
||||
export function matchLanguage(header: string): string | undefined {
|
||||
const parsedHeader = parseAcceptLanguage(header)
|
||||
|
||||
for (const { language } of parsedHeader) {
|
||||
for (const acceptedLanguage of acceptedLanguages) {
|
||||
if (language.startsWith(acceptedLanguage)) {
|
||||
return acceptedLanguage
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
const initTFunction: InitTFunction = (args) => {
|
||||
const { config, language, translations } = args
|
||||
const mergedTranslations = deepMerge(config?.translations?.[language] ?? {}, translations)
|
||||
const mergedTranslations = deepMerge(translations, config?.translations?.[language] ?? {})
|
||||
|
||||
return {
|
||||
t: (key, vars) => {
|
||||
@@ -230,10 +165,7 @@ function memoize(fn: (args: unknown) => Promise<I18n>, keys: string[]) {
|
||||
|
||||
export const initI18n: InitI18n = memoize(
|
||||
async ({ config, context, language = 'en' }: Parameters<InitI18n>[0]) => {
|
||||
const translations = getTranslationsByContext(
|
||||
config.supportedLanguages[language].translations,
|
||||
context,
|
||||
)
|
||||
const translations = getTranslationsByContext(config.supportedLanguages[language], context)
|
||||
|
||||
const { t, translations: mergedTranslations } = initTFunction({
|
||||
config,
|
||||
|
||||
165
packages/translations/src/utilities/languages.ts
Normal file
165
packages/translations/src/utilities/languages.ts
Normal file
@@ -0,0 +1,165 @@
|
||||
import type { AcceptedLanguages, LanguagePreference } from '../types.js'
|
||||
|
||||
export const rtlLanguages = ['ar', 'fa'] as const
|
||||
|
||||
export const acceptedLanguages = [
|
||||
'ar',
|
||||
'az',
|
||||
'bg',
|
||||
'cs',
|
||||
'de',
|
||||
'en',
|
||||
'es',
|
||||
'fa',
|
||||
'fr',
|
||||
'hr',
|
||||
'hu',
|
||||
'it',
|
||||
'ja',
|
||||
'ko',
|
||||
'my',
|
||||
'nb',
|
||||
'nl',
|
||||
'pl',
|
||||
'pt',
|
||||
'ro',
|
||||
'rs',
|
||||
'rsLatin',
|
||||
'ru',
|
||||
'sv',
|
||||
'th',
|
||||
'tr',
|
||||
'ua',
|
||||
'vi',
|
||||
'zh',
|
||||
'zh-TW',
|
||||
|
||||
/**
|
||||
* Languages not implemented:
|
||||
*
|
||||
* 'af',
|
||||
* 'am',
|
||||
* 'ar-sa',
|
||||
* 'as',
|
||||
* 'az-latin',
|
||||
* 'be',
|
||||
* 'bn-BD',
|
||||
* 'bn-IN',
|
||||
* 'bs',
|
||||
* 'ca',
|
||||
* 'ca-ES-valencia',
|
||||
* 'cy',
|
||||
* 'da',
|
||||
* 'el',
|
||||
* 'en-GB',
|
||||
* 'en-US',
|
||||
* 'es-ES',
|
||||
* 'es-US',
|
||||
* 'es-MX',
|
||||
* 'et',
|
||||
* 'eu',
|
||||
* 'fi',
|
||||
* 'fil-Latn',
|
||||
* 'fr-FR',
|
||||
* 'fr-CA',
|
||||
* 'ga',
|
||||
* 'gd-Latn',
|
||||
* 'gl',
|
||||
* 'gu',
|
||||
* 'ha-Latn',
|
||||
* 'he',
|
||||
* 'hi',
|
||||
* 'hr',
|
||||
* 'hy',
|
||||
* 'id',
|
||||
* 'ig-Latn',
|
||||
* 'is',
|
||||
* 'it-it',
|
||||
* 'ka',
|
||||
* 'kk',
|
||||
* 'km',
|
||||
* 'kn',
|
||||
* 'kok',
|
||||
* 'ku-Arab',
|
||||
* 'ky-Cyrl',
|
||||
* 'lb',
|
||||
* 'lt',
|
||||
* 'lv',
|
||||
* 'mi-Latn',
|
||||
* 'mk',
|
||||
* 'ml',
|
||||
* 'mn-Cyrl',
|
||||
* 'mr',
|
||||
* 'ms',
|
||||
* 'mt',
|
||||
* 'ne',
|
||||
* 'nl-BE',
|
||||
* 'nn',
|
||||
* 'nso',
|
||||
* 'or',
|
||||
* 'pa',
|
||||
* 'pa-Arab',
|
||||
* 'prs-Arab',
|
||||
* 'pt-BR',
|
||||
* 'pt-PT',
|
||||
* 'qut-Latn',
|
||||
* 'quz',
|
||||
* 'rw',
|
||||
* 'sd-Arab',
|
||||
* 'si',
|
||||
* 'sk',
|
||||
* 'sl',
|
||||
* 'sq',
|
||||
* 'sr-Cyrl-BA',
|
||||
* 'sr-Cyrl-RS',
|
||||
* 'sr-Latn-RS',
|
||||
* 'sw',
|
||||
* 'ta',
|
||||
* 'te',
|
||||
* 'tg-Cyrl',
|
||||
* 'ti',
|
||||
* 'tk-Latn',
|
||||
* 'tn',
|
||||
* 'tt-Cyrl',
|
||||
* 'ug-Arab',
|
||||
* 'uk',
|
||||
* 'ur',
|
||||
* 'uz-Latn',
|
||||
* 'wo',
|
||||
* 'xh',
|
||||
* 'yo-Latn',
|
||||
* 'zh-Hans',
|
||||
* 'zh-Hant',
|
||||
* 'zu',
|
||||
*/
|
||||
] as const
|
||||
|
||||
function parseAcceptLanguage(acceptLanguageHeader: string): LanguagePreference[] {
|
||||
return acceptLanguageHeader
|
||||
.split(',')
|
||||
.map((lang) => {
|
||||
const [language, quality] = lang.trim().split(';q=') as [
|
||||
AcceptedLanguages,
|
||||
string | undefined,
|
||||
]
|
||||
return {
|
||||
language,
|
||||
quality: quality ? parseFloat(quality) : 1,
|
||||
}
|
||||
})
|
||||
.sort((a, b) => b.quality - a.quality) // Sort by quality, highest to lowest
|
||||
}
|
||||
|
||||
export function matchLanguage(acceptLanguageHeader: string): AcceptedLanguages | undefined {
|
||||
const parsedHeader = parseAcceptLanguage(acceptLanguageHeader)
|
||||
|
||||
let matchedLanguage: AcceptedLanguages
|
||||
|
||||
for (const { language } of parsedHeader) {
|
||||
if (!matchedLanguage && acceptedLanguages.includes(language)) {
|
||||
matchedLanguage = language
|
||||
}
|
||||
}
|
||||
|
||||
return matchedLanguage
|
||||
}
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
} from '@payloadcms/richtext-lexical'
|
||||
import { de } from '@payloadcms/translations/languages/de'
|
||||
import { en } from '@payloadcms/translations/languages/en'
|
||||
import { es } from '@payloadcms/translations/languages/es'
|
||||
// import { slateEditor } from '@payloadcms/richtext-slate'
|
||||
import { type Config, buildConfig } from 'payload/config'
|
||||
import sharp from 'sharp'
|
||||
@@ -173,16 +174,18 @@ export async function buildConfigWithDefaults(
|
||||
}),
|
||||
sharp,
|
||||
telemetry: false,
|
||||
i18n: {
|
||||
supportedLanguages: {
|
||||
en,
|
||||
de,
|
||||
},
|
||||
},
|
||||
typescript: {
|
||||
declare: false,
|
||||
},
|
||||
...testConfig,
|
||||
i18n: {
|
||||
supportedLanguages: {
|
||||
en,
|
||||
es,
|
||||
de,
|
||||
},
|
||||
...(testConfig?.i18n || {}),
|
||||
},
|
||||
}
|
||||
|
||||
config.admin = {
|
||||
|
||||
Reference in New Issue
Block a user