feat(plugin-multi-tenant): prompt the user to confirm the change of tenant before actually updating (#12382)

This commit is contained in:
Jarrod Flesch
2025-05-14 09:45:00 -04:00
committed by GitHub
parent 98283ca18c
commit faa7794cc7
69 changed files with 1028 additions and 93 deletions

View File

@@ -74,6 +74,7 @@ export const rootEslintConfig = [
'no-console': 'off',
'perfectionist/sort-object-types': 'off',
'perfectionist/sort-objects': 'off',
'payload/no-relative-monorepo-imports': 'off',
},
},
]

View File

@@ -56,6 +56,16 @@
"import": "./src/exports/utilities.ts",
"types": "./src/exports/utilities.ts",
"default": "./src/exports/utilities.ts"
},
"./translations/languages/all": {
"import": "./src/translations/index.ts",
"types": "./src/translations/index.ts",
"default": "./src/translations/index.ts"
},
"./translations/languages/*": {
"import": "./src/translations/languages/*.ts",
"types": "./src/translations/languages/*.ts",
"default": "./src/translations/languages/*.ts"
}
},
"main": "./src/index.ts",
@@ -118,6 +128,16 @@
"import": "./dist/exports/utilities.js",
"types": "./dist/exports/utilities.d.ts",
"default": "./dist/exports/utilities.js"
},
"./translations/languages/all": {
"import": "./dist/translations/index.js",
"types": "./dist/translations/index.d.ts",
"default": "./dist/translations/index.js"
},
"./translations/languages/*": {
"import": "./dist/translations/languages/*.js",
"types": "./dist/translations/languages/*.d.ts",
"default": "./dist/translations/languages/*.js"
}
},
"main": "./dist/index.js",

View File

@@ -3,18 +3,52 @@ import type { ReactSelectOption } from '@payloadcms/ui'
import type { ViewTypes } from 'payload'
import { getTranslation } from '@payloadcms/translations'
import { SelectInput, useTranslation } from '@payloadcms/ui'
import {
ConfirmationModal,
SelectInput,
Translation,
useModal,
useTranslation,
} from '@payloadcms/ui'
import React from 'react'
import type {
PluginMultiTenantTranslationKeys,
PluginMultiTenantTranslations,
} from '../../translations/index.js'
import { useTenantSelection } from '../../providers/TenantSelectionProvider/index.client.js'
import './index.scss'
export const TenantSelector = ({ label, viewType }: { label: string; viewType?: ViewTypes }) => {
const { options, selectedTenantID, setTenant } = useTenantSelection()
const { i18n } = useTranslation()
const confirmSwitchTenantSlug = 'confirmSwitchTenant'
const handleChange = React.useCallback(
(option: ReactSelectOption | ReactSelectOption[]) => {
export const TenantSelector = ({ label, viewType }: { label: string; viewType?: ViewTypes }) => {
const { options, preventRefreshOnChange, selectedTenantID, setTenant } = useTenantSelection()
const { openModal } = useModal()
const { i18n, t } = useTranslation<
PluginMultiTenantTranslations,
PluginMultiTenantTranslationKeys
>()
const [tenantSelection, setTenantSelection] = React.useState<
ReactSelectOption | ReactSelectOption[]
>()
const selectedValue = React.useMemo(() => {
if (selectedTenantID) {
return options.find((option) => option.value === selectedTenantID)
}
return undefined
}, [options, selectedTenantID])
const newSelectedValue = React.useMemo(() => {
if (tenantSelection && 'value' in tenantSelection) {
return options.find((option) => option.value === tenantSelection.value)
}
return undefined
}, [options, tenantSelection])
const switchTenant = React.useCallback(
(option: ReactSelectOption | ReactSelectOption[] | undefined) => {
if (option && 'value' in option) {
setTenant({ id: option.value as string, refresh: true })
} else {
@@ -24,6 +58,19 @@ export const TenantSelector = ({ label, viewType }: { label: string; viewType?:
[setTenant],
)
const onChange = React.useCallback(
(option: ReactSelectOption | ReactSelectOption[]) => {
if (!preventRefreshOnChange) {
switchTenant(option)
return
} else {
setTenantSelection(option)
openModal(confirmSwitchTenantSlug)
}
},
[openModal, preventRefreshOnChange, switchTenant],
)
if (options.length <= 1) {
return null
}
@@ -34,11 +81,46 @@ export const TenantSelector = ({ label, viewType }: { label: string; viewType?:
isClearable={viewType === 'list'}
label={getTranslation(label, i18n)}
name="setTenant"
onChange={handleChange}
onChange={onChange}
options={options}
path="setTenant"
value={selectedTenantID as string | undefined}
/>
<ConfirmationModal
body={
<Translation
elements={{
0: ({ children }) => {
return <b>{children}</b>
},
}}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
i18nKey="plugin-multi-tenant:confirm-tenant-switch--body"
t={t}
variables={{
fromTenant: selectedValue?.label,
toTenant: newSelectedValue?.label,
}}
/>
}
heading={
<Translation
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
i18nKey="plugin-multi-tenant:confirm-tenant-switch--heading"
t={t}
variables={{
tenantLabel: label.toLowerCase(),
}}
/>
}
modalSlug={confirmSwitchTenantSlug}
onConfirm={() => {
switchTenant(tenantSelection)
}}
/>
</div>
)
}

View File

@@ -0,0 +1,35 @@
'use client'
import type { ClientCollectionConfig } from 'payload'
import { useConfig, useDocumentInfo, useEffectEvent, useFormFields } from '@payloadcms/ui'
import React from 'react'
import { useTenantSelection } from '../../providers/TenantSelectionProvider/index.client.js'
export const WatchTenantCollection = () => {
const { id, collectionSlug, title } = useDocumentInfo()
const { getEntityConfig } = useConfig()
const [useAsTitleName] = React.useState(
() => (getEntityConfig({ collectionSlug }) as ClientCollectionConfig).admin.useAsTitle,
)
const titleField = useFormFields(([fields]) => fields[useAsTitleName])
const { updateTenants } = useTenantSelection()
const syncTenantTitle = useEffectEvent(() => {
if (id) {
updateTenants({ id, label: title })
}
})
React.useEffect(() => {
// only update the tenant selector when the document saves
// → aka when initial value changes
if (id && titleField?.initialValue) {
syncTenantTitle()
}
}, [id, titleField?.initialValue])
return null
}

View File

@@ -1,3 +1,4 @@
export { TenantField } from '../components/TenantField/index.client.js'
export { TenantSelector } from '../components/TenantSelector/index.js'
export { WatchTenantCollection } from '../components/WatchTenantCollection/index.js'
export { useTenantSelection } from '../providers/TenantSelectionProvider/index.client.js'

View File

@@ -1,6 +1,9 @@
import type { AcceptedLanguages } from '@payloadcms/translations'
import type { CollectionConfig, Config } from 'payload'
import { deepMergeSimple } from 'payload'
import type { PluginDefaultTranslationsObject } from './translations/types.js'
import type { MultiTenantPluginConfig } from './types.js'
import { defaults } from './defaults.js'
@@ -10,6 +13,7 @@ import { addTenantCleanup } from './hooks/afterTenantDelete.js'
import { filterDocumentsBySelectedTenant } from './list-filters/filterDocumentsBySelectedTenant.js'
import { filterTenantsBySelectedTenant } from './list-filters/filterTenantsBySelectedTenant.js'
import { filterUsersBySelectedTenant } from './list-filters/filterUsersBySelectedTenant.js'
import { translations } from './translations/index.js'
import { addCollectionAccess } from './utilities/addCollectionAccess.js'
import { addFilterOptionsToFields } from './utilities/addFilterOptionsToFields.js'
import { combineListFilters } from './utilities/combineListFilters.js'
@@ -229,6 +233,21 @@ export const multiTenantPlugin =
usersTenantsArrayTenantFieldName: tenantsArrayTenantFieldName,
})
}
/**
* Add custom tenant field that watches and dispatches updates to the selector
*/
collection.fields.push({
name: '_watchTenant',
type: 'ui',
admin: {
components: {
Field: {
path: '@payloadcms/plugin-multi-tenant/client#WatchTenantCollection',
},
},
},
})
} else if (pluginConfig.collections?.[collection.slug]) {
const isGlobal = Boolean(pluginConfig.collections[collection.slug]?.isGlobal)
@@ -340,5 +359,25 @@ export const multiTenantPlugin =
path: '@payloadcms/plugin-multi-tenant/client#TenantSelector',
})
/**
* Merge plugin translations
*/
const simplifiedTranslations = Object.entries(translations).reduce(
(acc, [key, value]) => {
acc[key] = value.translations
return acc
},
{} as Record<string, PluginDefaultTranslationsObject>,
)
incomingConfig.i18n = {
...incomingConfig.i18n,
translations: deepMergeSimple(
simplifiedTranslations,
incomingConfig.i18n?.translations ?? {},
),
}
return incomingConfig
}

View File

@@ -11,6 +11,7 @@ type ContextType = {
* Array of options to select from
*/
options: OptionObject[]
preventRefreshOnChange: boolean
/**
* The currently selected tenant ID
*/
@@ -28,20 +29,26 @@ type ContextType = {
* @param args.refresh - Whether to refresh the page after changing the tenant
*/
setTenant: (args: { id: number | string | undefined; refresh?: boolean }) => void
/**
*
*/
updateTenants: (args: { id: number | string; label: string }) => void
}
const Context = createContext<ContextType>({
options: [],
preventRefreshOnChange: false,
selectedTenantID: undefined,
setPreventRefreshOnChange: () => null,
setTenant: () => null,
updateTenants: () => null,
})
export const TenantSelectionProviderClient = ({
children,
initialValue,
tenantCookie,
tenantOptions,
tenantOptions: tenantOptionsFromProps,
}: {
children: React.ReactNode
initialValue?: number | string
@@ -54,6 +61,9 @@ export const TenantSelectionProviderClient = ({
const [preventRefreshOnChange, setPreventRefreshOnChange] = React.useState(false)
const { user } = useAuth()
const userID = React.useMemo(() => user?.id, [user?.id])
const [tenantOptions, setTenantOptions] = React.useState<OptionObject[]>(
() => tenantOptionsFromProps,
)
const selectedTenantLabel = React.useMemo(
() => tenantOptions.find((option) => option.value === selectedTenantID)?.label,
[selectedTenantID, tenantOptions],
@@ -91,6 +101,20 @@ export const TenantSelectionProviderClient = ({
[deleteCookie, preventRefreshOnChange, router, setCookie, setSelectedTenantID, tenantOptions],
)
const updateTenants = React.useCallback<ContextType['updateTenants']>(({ id, label }) => {
setTenantOptions((prev) => {
return prev.map((currentTenant) => {
if (id === currentTenant.value) {
return {
label,
value: id,
}
}
return currentTenant
})
})
}, [])
React.useEffect(() => {
if (selectedTenantID && !tenantOptions.find((option) => option.value === selectedTenantID)) {
if (tenantOptions?.[0]?.value) {
@@ -105,13 +129,14 @@ export const TenantSelectionProviderClient = ({
if (userID && !tenantCookie) {
// User is logged in, but does not have a tenant cookie, set it
setSelectedTenantID(initialValue)
setTenantOptions(tenantOptionsFromProps)
if (initialValue) {
setCookie(String(initialValue))
} else {
deleteCookie()
}
}
}, [userID, tenantCookie, initialValue, setCookie, deleteCookie, router])
}, [userID, tenantCookie, initialValue, setCookie, deleteCookie, router, tenantOptionsFromProps])
React.useEffect(() => {
if (!userID && tenantCookie) {
@@ -132,9 +157,11 @@ export const TenantSelectionProviderClient = ({
<Context
value={{
options: tenantOptions,
preventRefreshOnChange,
selectedTenantID,
setPreventRefreshOnChange,
setTenant,
updateTenants,
}}
>
{children}

View File

@@ -0,0 +1,91 @@
import type {
GenericTranslationsObject,
NestedKeysStripped,
SupportedLanguages,
} from '@payloadcms/translations'
import type { PluginDefaultTranslationsObject } from './types.js'
import { ar } from './languages/ar.js'
import { az } from './languages/az.js'
import { bg } from './languages/bg.js'
import { ca } from './languages/ca.js'
import { cs } from './languages/cs.js'
import { da } from './languages/da.js'
import { de } from './languages/de.js'
import { en } from './languages/en.js'
import { es } from './languages/es.js'
import { et } from './languages/et.js'
import { fa } from './languages/fa.js'
import { fr } from './languages/fr.js'
import { he } from './languages/he.js'
import { hr } from './languages/hr.js'
import { hu } from './languages/hu.js'
import { hy } from './languages/hy.js'
import { it } from './languages/it.js'
import { ja } from './languages/ja.js'
import { ko } from './languages/ko.js'
import { lt } from './languages/lt.js'
import { my } from './languages/my.js'
import { nb } from './languages/nb.js'
import { nl } from './languages/nl.js'
import { pl } from './languages/pl.js'
import { pt } from './languages/pt.js'
import { ro } from './languages/ro.js'
import { rs } from './languages/rs.js'
import { rsLatin } from './languages/rsLatin.js'
import { ru } from './languages/ru.js'
import { sk } from './languages/sk.js'
import { sl } from './languages/sl.js'
import { sv } from './languages/sv.js'
import { th } from './languages/th.js'
import { tr } from './languages/tr.js'
import { uk } from './languages/uk.js'
import { vi } from './languages/vi.js'
import { zh } from './languages/zh.js'
import { zhTw } from './languages/zhTw.js'
export const translations = {
ar,
az,
bg,
ca,
cs,
da,
de,
en,
es,
et,
fa,
fr,
he,
hr,
hu,
hy,
it,
ja,
ko,
lt,
my,
nb,
nl,
pl,
pt,
ro,
rs,
'rs-latin': rsLatin,
ru,
sk,
sl,
sv,
th,
tr,
uk,
vi,
zh,
'zh-TW': zhTw,
} as SupportedLanguages<PluginDefaultTranslationsObject>
export type PluginMultiTenantTranslations = GenericTranslationsObject
export type PluginMultiTenantTranslationKeys = NestedKeysStripped<PluginMultiTenantTranslations>

View File

@@ -0,0 +1,14 @@
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
export const arTranslations: PluginDefaultTranslationsObject = {
'plugin-multi-tenant': {
'confirm-tenant-switch--body':
'أنت على وشك تغيير الملكية من <0>{{fromTenant}}</0> إلى <0>{{toTenant}}</0>',
'confirm-tenant-switch--heading': 'تأكيد تغيير {{tenantLabel}}',
},
}
export const ar: PluginLanguage = {
dateFNSKey: 'ar',
translations: arTranslations,
}

View File

@@ -0,0 +1,14 @@
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
export const azTranslations: PluginDefaultTranslationsObject = {
'plugin-multi-tenant': {
'confirm-tenant-switch--body':
'Siz <0>{{fromTenant}}</0> mülkiyyətini <0>{{toTenant}}</0> mülkiyyətinə dəyişdirəcəksiniz.',
'confirm-tenant-switch--heading': '{{tenantLabel}} dəyişikliyini təsdiqləyin',
},
}
export const az: PluginLanguage = {
dateFNSKey: 'az',
translations: azTranslations,
}

View File

@@ -0,0 +1,14 @@
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
export const bgTranslations: PluginDefaultTranslationsObject = {
'plugin-multi-tenant': {
'confirm-tenant-switch--body':
'Предстои да промените собствеността от <0>{{fromTenant}}</0> на <0>{{toTenant}}</0>',
'confirm-tenant-switch--heading': 'Потвърдете промяната на {{tenantLabel}}',
},
}
export const bg: PluginLanguage = {
dateFNSKey: 'bg',
translations: bgTranslations,
}

View File

@@ -0,0 +1,14 @@
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
export const caTranslations: PluginDefaultTranslationsObject = {
'plugin-multi-tenant': {
'confirm-tenant-switch--body':
'Estàs a punt de canviar la propietat de <0>{{fromTenant}}</0> a <0>{{toTenant}}</0>',
'confirm-tenant-switch--heading': 'Confirmeu el canvi de {{tenantLabel}}',
},
}
export const ca: PluginLanguage = {
dateFNSKey: 'ca',
translations: caTranslations,
}

View File

@@ -0,0 +1,14 @@
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
export const csTranslations: PluginDefaultTranslationsObject = {
'plugin-multi-tenant': {
'confirm-tenant-switch--body':
'Chystáte se změnit vlastnictví z <0>{{fromTenant}}</0> na <0>{{toTenant}}</0>',
'confirm-tenant-switch--heading': 'Potvrďte změnu {{tenantLabel}}',
},
}
export const cs: PluginLanguage = {
dateFNSKey: 'cs',
translations: csTranslations,
}

View File

@@ -0,0 +1,14 @@
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
export const daTranslations: PluginDefaultTranslationsObject = {
'plugin-multi-tenant': {
'confirm-tenant-switch--body':
'Du er ved at ændre ejerskab fra <0>{{fromTenant}}</0> til <0>{{toTenant}}</0>',
'confirm-tenant-switch--heading': 'Bekræft {{tenantLabel}} ændring',
},
}
export const da: PluginLanguage = {
dateFNSKey: 'da',
translations: daTranslations,
}

View File

@@ -0,0 +1,14 @@
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
export const deTranslations: PluginDefaultTranslationsObject = {
'plugin-multi-tenant': {
'confirm-tenant-switch--body':
'Sie sind dabei, den Besitz von <0>{{fromTenant}}</0> auf <0>{{toTenant}}</0> zu übertragen.',
'confirm-tenant-switch--heading': 'Bestätigen Sie die Änderung von {{tenantLabel}}.',
},
}
export const de: PluginLanguage = {
dateFNSKey: 'de',
translations: deTranslations,
}

View File

@@ -0,0 +1,14 @@
import type { PluginLanguage } from '../types.js'
export const enTranslations = {
'plugin-multi-tenant': {
'confirm-tenant-switch--body':
'You are about to change ownership from <0>{{fromTenant}}</0> to <0>{{toTenant}}</0>',
'confirm-tenant-switch--heading': 'Confirm {{tenantLabel}} change',
},
}
export const en: PluginLanguage = {
dateFNSKey: 'en-US',
translations: enTranslations,
}

View File

@@ -0,0 +1,14 @@
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
export const esTranslations: PluginDefaultTranslationsObject = {
'plugin-multi-tenant': {
'confirm-tenant-switch--body':
'Está a punto de cambiar la propiedad de <0>{{fromTenant}}</0> a <0>{{toTenant}}</0>',
'confirm-tenant-switch--heading': 'Confirme el cambio de {{tenantLabel}}',
},
}
export const es: PluginLanguage = {
dateFNSKey: 'es',
translations: esTranslations,
}

View File

@@ -0,0 +1,14 @@
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
export const etTranslations: PluginDefaultTranslationsObject = {
'plugin-multi-tenant': {
'confirm-tenant-switch--body':
'Te olete tegemas omandiõiguse muudatust <0>{{fromTenant}}</0>lt <0>{{toTenant}}</0>le.',
'confirm-tenant-switch--heading': 'Kinnita {{tenantLabel}} muutus',
},
}
export const et: PluginLanguage = {
dateFNSKey: 'et',
translations: etTranslations,
}

View File

@@ -0,0 +1,14 @@
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
export const faTranslations: PluginDefaultTranslationsObject = {
'plugin-multi-tenant': {
'confirm-tenant-switch--body':
'شما در حال تغییر مالکیت از <0>{{fromTenant}}</0> به <0>{{toTenant}}</0> هستید',
'confirm-tenant-switch--heading': 'تایید تغییر {{tenantLabel}}',
},
}
export const fa: PluginLanguage = {
dateFNSKey: 'fa-IR',
translations: faTranslations,
}

View File

@@ -0,0 +1,14 @@
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
export const frTranslations: PluginDefaultTranslationsObject = {
'plugin-multi-tenant': {
'confirm-tenant-switch--body':
'Vous êtes sur le point de changer la propriété de <0>{{fromTenant}}</0> à <0>{{toTenant}}</0>',
'confirm-tenant-switch--heading': 'Confirmer le changement de {{tenantLabel}}',
},
}
export const fr: PluginLanguage = {
dateFNSKey: 'fr',
translations: frTranslations,
}

View File

@@ -0,0 +1,14 @@
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
export const heTranslations: PluginDefaultTranslationsObject = {
'plugin-multi-tenant': {
'confirm-tenant-switch--body':
'אתה עומד לשנות בעלות מ- <0>{{fromTenant}}</0> ל- <0>{{toTenant}}</0>',
'confirm-tenant-switch--heading': 'אשר שינוי {{tenantLabel}}',
},
}
export const he: PluginLanguage = {
dateFNSKey: 'he',
translations: heTranslations,
}

View File

@@ -0,0 +1,14 @@
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
export const hrTranslations: PluginDefaultTranslationsObject = {
'plugin-multi-tenant': {
'confirm-tenant-switch--body':
'Upravo ćete promijeniti vlasništvo sa <0>{{fromTenant}}</0> na <0>{{toTenant}}</0>',
'confirm-tenant-switch--heading': 'Potvrdi promjenu {{tenantLabel}}',
},
}
export const hr: PluginLanguage = {
dateFNSKey: 'hr',
translations: hrTranslations,
}

View File

@@ -0,0 +1,14 @@
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
export const huTranslations: PluginDefaultTranslationsObject = {
'plugin-multi-tenant': {
'confirm-tenant-switch--body':
'Ön azon van, hogy megváltoztassa a tulajdonjogot <0>{{fromTenant}}</0>-ről <0>{{toTenant}}</0>-re.',
'confirm-tenant-switch--heading': 'Erősítse meg a(z) {{tenantLabel}} változtatást',
},
}
export const hu: PluginLanguage = {
dateFNSKey: 'hu',
translations: huTranslations,
}

View File

@@ -0,0 +1,14 @@
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
export const hyTranslations: PluginDefaultTranslationsObject = {
'plugin-multi-tenant': {
'confirm-tenant-switch--body':
'Դուք պատրաստ եք փոխել գերեցդիմատնին ընկերությունը <0>{{fromTenant}}</0>-ից <0>{{toTenant}}</0>-ին',
'confirm-tenant-switch--heading': 'Հաստատեք {{tenantLabel}} փոփոխությունը',
},
}
export const hy: PluginLanguage = {
dateFNSKey: 'hy-AM',
translations: hyTranslations,
}

View File

@@ -0,0 +1,14 @@
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
export const itTranslations: PluginDefaultTranslationsObject = {
'plugin-multi-tenant': {
'confirm-tenant-switch--body':
'Stai per cambiare proprietà da <0>{{fromTenant}}</0> a <0>{{toTenant}}</0>',
'confirm-tenant-switch--heading': 'Conferma il cambiamento di {{tenantLabel}}',
},
}
export const it: PluginLanguage = {
dateFNSKey: 'it',
translations: itTranslations,
}

View File

@@ -0,0 +1,14 @@
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
export const jaTranslations: PluginDefaultTranslationsObject = {
'plugin-multi-tenant': {
'confirm-tenant-switch--body':
'あなたは所有権を<0>{{fromTenant}}</0>から<0>{{toTenant}}</0>へ変更しようとしています',
'confirm-tenant-switch--heading': '{{tenantLabel}}の変更を確認してください',
},
}
export const ja: PluginLanguage = {
dateFNSKey: 'ja',
translations: jaTranslations,
}

View File

@@ -0,0 +1,14 @@
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
export const koTranslations: PluginDefaultTranslationsObject = {
'plugin-multi-tenant': {
'confirm-tenant-switch--body':
'<0>{{fromTenant}}</0>에서 <0>{{toTenant}}</0>으로 소유권을 변경하려고 합니다.',
'confirm-tenant-switch--heading': '{{tenantLabel}} 변경을 확인하세요',
},
}
export const ko: PluginLanguage = {
dateFNSKey: 'ko',
translations: koTranslations,
}

View File

@@ -0,0 +1,14 @@
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
export const ltTranslations: PluginDefaultTranslationsObject = {
'plugin-multi-tenant': {
'confirm-tenant-switch--body':
'Jūs ketinate pakeisti nuosavybės teisę iš <0>{{fromTenant}}</0> į <0>{{toTenant}}</0>',
'confirm-tenant-switch--heading': 'Patvirtinkite {{tenantLabel}} pakeitimą',
},
}
export const lt: PluginLanguage = {
dateFNSKey: 'lt',
translations: ltTranslations,
}

View File

@@ -0,0 +1,14 @@
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
export const myTranslations: PluginDefaultTranslationsObject = {
'plugin-multi-tenant': {
'confirm-tenant-switch--body':
'Anda akan mengubah pemilikan dari <0>{{fromTenant}}</0> ke <0>{{toTenant}}</0>',
'confirm-tenant-switch--heading': 'Sahkan perubahan {{tenantLabel}}',
},
}
export const my: PluginLanguage = {
dateFNSKey: 'en-US',
translations: myTranslations,
}

View File

@@ -0,0 +1,14 @@
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
export const nbTranslations: PluginDefaultTranslationsObject = {
'plugin-multi-tenant': {
'confirm-tenant-switch--body':
'Du er i ferd med å endre eierskap fra <0>{{fromTenant}}</0> til <0>{{toTenant}}</0>',
'confirm-tenant-switch--heading': 'Bekreft {{tenantLabel}} endring',
},
}
export const nb: PluginLanguage = {
dateFNSKey: 'nb',
translations: nbTranslations,
}

View File

@@ -0,0 +1,14 @@
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
export const nlTranslations: PluginDefaultTranslationsObject = {
'plugin-multi-tenant': {
'confirm-tenant-switch--body':
'U staat op het punt het eigendom te wijzigen van <0>{{fromTenant}}</0> naar <0>{{toTenant}}</0>',
'confirm-tenant-switch--heading': 'Bevestig wijziging van {{tenantLabel}}',
},
}
export const nl: PluginLanguage = {
dateFNSKey: 'nl',
translations: nlTranslations,
}

View File

@@ -0,0 +1,14 @@
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
export const plTranslations: PluginDefaultTranslationsObject = {
'plugin-multi-tenant': {
'confirm-tenant-switch--body':
'Za chwilę nastąpi zmiana właściciela z <0>{{fromTenant}}</0> na <0>{{toTenant}}</0>',
'confirm-tenant-switch--heading': 'Potwierdź zmianę {{tenantLabel}}',
},
}
export const pl: PluginLanguage = {
dateFNSKey: 'pl',
translations: plTranslations,
}

View File

@@ -0,0 +1,14 @@
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
export const ptTranslations: PluginDefaultTranslationsObject = {
'plugin-multi-tenant': {
'confirm-tenant-switch--body':
'Você está prestes a alterar a propriedade de <0>{{fromTenant}}</0> para <0>{{toTenant}}</0>',
'confirm-tenant-switch--heading': 'Confirme a alteração de {{tenantLabel}}',
},
}
export const pt: PluginLanguage = {
dateFNSKey: 'pt',
translations: ptTranslations,
}

View File

@@ -0,0 +1,3 @@
for file in *.js; do
mv -- "$file" "${file%.js}.ts"
done

View File

@@ -0,0 +1,14 @@
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
export const roTranslations: PluginDefaultTranslationsObject = {
'plugin-multi-tenant': {
'confirm-tenant-switch--body':
'Sunteți pe punctul de a schimba proprietatea de la <0>{{fromTenant}}</0> la <0>{{toTenant}}</0>',
'confirm-tenant-switch--heading': 'Confirmați schimbarea {{tenantLabel}}',
},
}
export const ro: PluginLanguage = {
dateFNSKey: 'ro',
translations: roTranslations,
}

View File

@@ -0,0 +1,14 @@
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
export const rsTranslations: PluginDefaultTranslationsObject = {
'plugin-multi-tenant': {
'confirm-tenant-switch--body':
'Upravo ćete promeniti vlasništvo sa <0>{{fromTenant}}</0> na <0>{{toTenant}}</0>',
'confirm-tenant-switch--heading': 'Potvrdi promena {{tenantLabel}}',
},
}
export const rs: PluginLanguage = {
dateFNSKey: 'rs',
translations: rsTranslations,
}

View File

@@ -0,0 +1,14 @@
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
export const rsLatinTranslations: PluginDefaultTranslationsObject = {
'plugin-multi-tenant': {
'confirm-tenant-switch--body':
'Uskoro ćete promeniti vlasništvo sa <0>{{fromTenant}}</0> na <0>{{toTenant}}</0>',
'confirm-tenant-switch--heading': 'Potvrdite promenu {{tenantLabel}}',
},
}
export const rsLatin: PluginLanguage = {
dateFNSKey: 'rs-Latin',
translations: rsLatinTranslations,
}

View File

@@ -0,0 +1,14 @@
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
export const ruTranslations: PluginDefaultTranslationsObject = {
'plugin-multi-tenant': {
'confirm-tenant-switch--body':
'Вы собираетесь изменить владельца с <0>{{fromTenant}}</0> на <0>{{toTenant}}</0>',
'confirm-tenant-switch--heading': 'Подтвердите изменение {{tenantLabel}}',
},
}
export const ru: PluginLanguage = {
dateFNSKey: 'ru',
translations: ruTranslations,
}

View File

@@ -0,0 +1,14 @@
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
export const skTranslations: PluginDefaultTranslationsObject = {
'plugin-multi-tenant': {
'confirm-tenant-switch--body':
'Chystáte sa zmeniť vlastníctvo z <0>{{fromTenant}}</0> na <0>{{toTenant}}</0>',
'confirm-tenant-switch--heading': 'Potvrďte zmenu {{tenantLabel}}',
},
}
export const sk: PluginLanguage = {
dateFNSKey: 'sk',
translations: skTranslations,
}

View File

@@ -0,0 +1,14 @@
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
export const slTranslations: PluginDefaultTranslationsObject = {
'plugin-multi-tenant': {
'confirm-tenant-switch--body':
'Ravno ste pred spremembo lastništva iz <0>{{fromTenant}}</0> na <0>{{toTenant}}</0>',
'confirm-tenant-switch--heading': 'Potrdi spremembo {{tenantLabel}}',
},
}
export const sl: PluginLanguage = {
dateFNSKey: 'sl-SI',
translations: slTranslations,
}

View File

@@ -0,0 +1,14 @@
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
export const svTranslations: PluginDefaultTranslationsObject = {
'plugin-multi-tenant': {
'confirm-tenant-switch--body':
'Du är på väg att ändra ägare från <0>{{fromTenant}}</0> till <0>{{toTenant}}</0>',
'confirm-tenant-switch--heading': 'Bekräfta ändring av {{tenantLabel}}',
},
}
export const sv: PluginLanguage = {
dateFNSKey: 'sv',
translations: svTranslations,
}

View File

@@ -0,0 +1,14 @@
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
export const thTranslations: PluginDefaultTranslationsObject = {
'plugin-multi-tenant': {
'confirm-tenant-switch--body':
'คุณกำลังจะเปลี่ยนความเป็นเจ้าของจาก <0>{{fromTenant}}</0> เป็น <0>{{toTenant}}</0>',
'confirm-tenant-switch--heading': 'ยืนยันการเปลี่ยนแปลง {{tenantLabel}}',
},
}
export const th: PluginLanguage = {
dateFNSKey: 'th',
translations: thTranslations,
}

View File

@@ -0,0 +1,14 @@
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
export const trTranslations: PluginDefaultTranslationsObject = {
'plugin-multi-tenant': {
'confirm-tenant-switch--body':
"Sahipliği <0>{{fromTenant}}</0>'den <0>{{toTenant}}</0>'e değiştirmek üzeresiniz.",
'confirm-tenant-switch--heading': '{{tenantLabel}} değişikliğini onayla',
},
}
export const tr: PluginLanguage = {
dateFNSKey: 'tr',
translations: trTranslations,
}

View File

@@ -0,0 +1,14 @@
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
export const ukTranslations: PluginDefaultTranslationsObject = {
'plugin-multi-tenant': {
'confirm-tenant-switch--body':
'Ви збираєтесь змінити власність з <0>{{fromTenant}}</0> на <0>{{toTenant}}</0>',
'confirm-tenant-switch--heading': 'Підтвердіть зміну {{tenantLabel}}',
},
}
export const uk: PluginLanguage = {
dateFNSKey: 'uk',
translations: ukTranslations,
}

View File

@@ -0,0 +1,14 @@
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
export const viTranslations: PluginDefaultTranslationsObject = {
'plugin-multi-tenant': {
'confirm-tenant-switch--body':
'Bạn đang chuẩn bị chuyển quyền sở hữu từ <0>{{fromTenant}}</0> sang <0>{{toTenant}}</0>',
'confirm-tenant-switch--heading': 'Xác nhận thay đổi {{tenantLabel}}',
},
}
export const vi: PluginLanguage = {
dateFNSKey: 'vi',
translations: viTranslations,
}

View File

@@ -0,0 +1,13 @@
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
export const zhTranslations: PluginDefaultTranslationsObject = {
'plugin-multi-tenant': {
'confirm-tenant-switch--body': '您即将将所有权从<0>{{fromTenant}}</0>更改为<0>{{toTenant}}</0>',
'confirm-tenant-switch--heading': '确认更改{{tenantLabel}}',
},
}
export const zh: PluginLanguage = {
dateFNSKey: 'zh-CN',
translations: zhTranslations,
}

View File

@@ -0,0 +1,14 @@
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
export const zhTwTranslations: PluginDefaultTranslationsObject = {
'plugin-multi-tenant': {
'confirm-tenant-switch--body':
'您即將將所有權從 <0>{{fromTenant}}</0> 轉移至 <0>{{toTenant}}</0>',
'confirm-tenant-switch--heading': '確認{{tenantLabel}}更改',
},
}
export const zhTw: PluginLanguage = {
dateFNSKey: 'zh-TW',
translations: zhTwTranslations,
}

View File

@@ -0,0 +1,12 @@
import type { Language } from '@payloadcms/translations'
import type { enTranslations } from './languages/en.js'
export type PluginLanguage = Language<{
'plugin-multi-tenant': {
'confirm-tenant-switch--body': string
'confirm-tenant-switch--heading': string
}
}>
export type PluginDefaultTranslationsObject = typeof enTranslations

View File

@@ -344,8 +344,7 @@
"copyfiles": "copyfiles -u 1 \"src/**/*.{html,css,ttf,woff,woff2,eot,svg,jpg,png,json}\" dist/",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"prepublishOnly": "pnpm clean && pnpm turbo build",
"translateNewKeys": "node --no-deprecation --import @swc-node/register/esm-register scripts/translateNewKeys.ts"
"prepublishOnly": "pnpm clean && pnpm turbo build"
},
"lint-staged": {
"**/package.json": "sort-package-json",

View File

@@ -1 +0,0 @@
OPENAI_KEY=sk-

View File

@@ -51,8 +51,7 @@
"clean": "rimraf -g {dist,*.tsbuildinfo}",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"prepublishOnly": "pnpm clean && pnpm turbo build",
"translateNewKeys": "node --no-deprecation --import @swc-node/register/esm-register scripts/translateNewKeys/run.ts"
"prepublishOnly": "pnpm clean && pnpm turbo build"
},
"dependencies": {
"date-fns": "4.1.0"

View File

@@ -1,32 +0,0 @@
import path from 'path'
import { fileURLToPath } from 'url'
import type { AcceptedLanguages, GenericTranslationsObject } from '../../src/types.js'
import { translations } from '../../src/exports/all.js'
import { enTranslations } from '../../src/languages/en.js'
import { translateObject } from './index.js'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
const allTranslations: {
[key in AcceptedLanguages]?: {
dateFNSKey: string
translations: GenericTranslationsObject
}
} = {}
for (const key of Object.keys(translations)) {
allTranslations[key] = {
dateFNSKey: translations[key].dateFNSKey,
translations: translations[key].translations,
}
}
void translateObject({
allTranslationsObject: allTranslations,
fromTranslationsObject: enTranslations,
//languages: ['de'],
targetFolder: path.resolve(dirname, '../../src/languages'),
})

13
pnpm-lock.yaml generated
View File

@@ -1958,6 +1958,19 @@ importers:
open:
specifier: ^10.1.0
version: 10.1.0
payload:
specifier: workspace:*
version: link:../../packages/payload
devDependencies:
'@payloadcms/plugin-multi-tenant':
specifier: workspace:*
version: link:../../packages/plugin-multi-tenant
'@payloadcms/richtext-lexical':
specifier: workspace:*
version: link:../../packages/richtext-lexical
'@payloadcms/translations':
specifier: workspace:*
version: link:../../packages/translations
packages:

View File

@@ -41,7 +41,7 @@ export default buildConfigWithDefaults({
isGlobal: true,
},
},
tenantSelectorLabel: 'Sites',
tenantSelectorLabel: 'Site',
}),
],
typescript: {

1
tools/.env.example Normal file
View File

@@ -0,0 +1 @@
OPENAI_KEY=sk-your-key-here

View File

@@ -13,9 +13,11 @@
},
"main": "src/index.ts",
"scripts": {
"build": "tsc",
"build": "tsc --project tsconfig.build.json",
"build-template-with-local-pkgs": "pnpm runts src/build-template-with-local-pkgs.ts",
"gen-templates": "pnpm runts src/generate-template-variations.ts",
"generateTranslations:core": "node --no-deprecation --import @swc-node/register/esm-register src/generateTranslations/core.ts",
"generateTranslations:plugin-multi-tenant": "node --no-deprecation --import @swc-node/register/esm-register src/generateTranslations/plugin-multi-tenant.ts",
"license-check": "pnpm runts src/license-check.ts",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
@@ -32,6 +34,12 @@
"create-payload-app": "workspace:*",
"csv-stringify": "^6.5.2",
"license-checker": "25.0.1",
"open": "^10.1.0"
"open": "^10.1.0",
"payload": "workspace:*"
},
"devDependencies": {
"@payloadcms/plugin-multi-tenant": "workspace:*",
"@payloadcms/richtext-lexical": "workspace:*",
"@payloadcms/translations": "workspace:*"
}
}

View File

@@ -0,0 +1,31 @@
import type { AcceptedLanguages, GenericTranslationsObject } from '@payloadcms/translations'
import { translations } from '@payloadcms/translations/all'
import { enTranslations } from '@payloadcms/translations/languages/en'
import path from 'path'
import { fileURLToPath } from 'url'
import { translateObject } from './utils/index.js'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
const allTranslations: {
[key in AcceptedLanguages]?: {
dateFNSKey: string
translations: GenericTranslationsObject
}
} = {}
for (const key of Object.keys(translations) as AcceptedLanguages[]) {
allTranslations[key] = {
dateFNSKey: translations[key]?.dateFNSKey || 'unknown-date-fns-key',
translations: translations[key]?.translations || {},
}
}
void translateObject({
allTranslationsObject: allTranslations,
fromTranslationsObject: enTranslations,
targetFolder: path.resolve(dirname, '../../../../packages/translations/src/languages'),
})

View File

@@ -0,0 +1,39 @@
import type { AcceptedLanguages, GenericTranslationsObject } from '@payloadcms/translations'
import { translations } from '@payloadcms/plugin-multi-tenant/translations/languages/all'
import { enTranslations } from '@payloadcms/plugin-multi-tenant/translations/languages/en'
import path from 'path'
import { fileURLToPath } from 'url'
import { translateObject } from './utils/index.js'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
const allTranslations: {
[key in AcceptedLanguages]?: {
dateFNSKey: string
translations: GenericTranslationsObject
}
} = {}
for (const key of Object.keys(translations)) {
allTranslations[key as AcceptedLanguages] = {
dateFNSKey: translations[key as AcceptedLanguages]?.dateFNSKey ?? 'unknown-date-fns-key',
translations: translations[key as AcceptedLanguages]?.translations ?? {},
}
}
void translateObject({
allTranslationsObject: allTranslations,
fromTranslationsObject: enTranslations,
targetFolder: path.resolve(
dirname,
'../../../../packages/plugin-multi-tenant/src/translations/languages',
),
tsFilePrefix: `import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'\n\nexport const {{locale}}Translations: PluginDefaultTranslationsObject = `,
tsFileSuffix: `\n\nexport const {{locale}}: PluginLanguage = {
dateFNSKey: {{dateFNSKey}},
translations: {{locale}}Translations,
} `,
})

View File

@@ -8,13 +8,13 @@ import * as fs from 'node:fs'
import path from 'path'
import { fileURLToPath } from 'url'
import { translateObject } from '../../translations/scripts/translateNewKeys/index.js'
import { translateObject } from './utils/index.js'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
// Function to get all files with a specific name recursively in all subdirectories
function findFilesRecursively(startPath, filter) {
let results = []
function findFilesRecursively(startPath: string, filter: string): string[] {
let results: string[] = []
const entries = fs.readdirSync(startPath, { withFileTypes: true })
@@ -33,7 +33,10 @@ function findFilesRecursively(startPath, filter) {
return results
}
const i18nFilePaths = findFilesRecursively(path.resolve(dirname, '../src'), 'i18n.ts')
const i18nFilePaths = findFilesRecursively(
path.resolve(dirname, '../../../../packages/richtext-lexical/src'),
'i18n.ts',
)
async function translate() {
for (const i18nFilePath of i18nFilePaths) {
@@ -46,12 +49,13 @@ async function translate() {
}
} = {}
for (const lang in translationsObject) {
allTranslations[lang] = {
allTranslations[lang as AcceptedLanguages] = {
dateFNSKey: 'en',
translations: translationsObject[lang],
translations: translationsObject?.[lang as keyof GenericLanguages] || {},
}
}
if (translationsObject.en) {
console.log('Translating', i18nFilePath)
await translateObject({
allTranslationsObject: allTranslations,
@@ -59,9 +63,12 @@ async function translate() {
inlineFile: i18nFilePath,
tsFilePrefix: `import { GenericLanguages } from '@payloadcms/translations'
export const i18n: Partial<GenericLanguages> = `,
export const i18n: Partial<GenericLanguages> = `,
tsFileSuffix: ``,
})
} else {
console.error(`No English translations found in ${i18nFilePath}`)
}
}
}

View File

@@ -1,4 +1,4 @@
import type { GenericTranslationsObject } from '../../src/types.js'
import type { GenericTranslationsObject } from '@payloadcms/translations'
/**
* Returns keys which are present in baseObj but not in targetObj
@@ -8,7 +8,7 @@ export function findMissingKeys(
targetObj: GenericTranslationsObject,
prefix = '',
): string[] {
let missingKeys = []
let missingKeys: string[] = []
for (const key in baseObj) {
const baseValue = baseObj[key]

View File

@@ -1,5 +1,7 @@
export function generateTsObjectLiteral(obj: any): string {
const lines = []
import type { JsonObject } from 'payload'
export function generateTsObjectLiteral(obj: JsonObject): string {
const lines: string[] = []
const entries = Object.entries(obj)
for (const [key, value] of entries) {
const safeKey = /^[\w$]+$/.test(key) ? key : JSON.stringify(key)

View File

@@ -1,17 +1,17 @@
/* eslint no-console: 0 */
import fs from 'fs'
import path from 'path'
import { format } from 'prettier'
import type {
AcceptedLanguages,
GenericLanguages,
GenericTranslationsObject,
} from '../../src/types.js'
} from '@payloadcms/translations'
import { acceptedLanguages } from '@payloadcms/translations'
import fs from 'fs'
import path from 'path'
import { deepMergeSimple } from 'payload/shared'
import { format } from 'prettier'
import { deepMergeSimple } from '../../src/utilities/deepMergeSimple.js'
import { acceptedLanguages } from '../../src/utilities/languages.js'
import { applyEslintFixes } from './applyEslintFixes.js'
import { findMissingKeys } from './findMissingKeys.js'
import { generateTsObjectLiteral } from './generateTsObjectLiteral.js'
@@ -104,16 +104,16 @@ export async function translateObject(props: {
*/
for (const key of keysWhichDoNotExistInFromlang) {
// Delete those keys in the target language object obj[lang]
const keys = key.split('.')
const keys: string[] = key.split('.')
let targetObj = allTranslatedTranslationsObject?.[targetLang].translations
for (let i = 0; i < keys.length - 1; i += 1) {
const nextObj = targetObj[keys[i]]
const nextObj = targetObj[keys[i] as string]
if (typeof nextObj !== 'object') {
throw new Error(`Key ${keys[i]} is not an object in ${targetLang} (1)`)
}
targetObj = nextObj
}
delete targetObj[keys[keys.length - 1]]
delete targetObj[keys[keys.length - 1] as string]
}
if (!allTranslatedTranslationsObject?.[targetLang].translations) {
@@ -128,7 +128,10 @@ export async function translateObject(props: {
for (const missingKey of missingKeys) {
const keys: string[] = missingKey.split('.')
const sourceText = keys.reduce((acc, key) => acc[key], fromTranslationsObject)
const sourceText = keys.reduce(
(acc, key) => acc[key] as GenericTranslationsObject,
fromTranslationsObject,
)
if (!sourceText || typeof sourceText !== 'string') {
throw new Error(
`Missing key ${missingKey} or key not "leaf" in fromTranslationsObject for lang ${targetLang}. (2)`,
@@ -147,20 +150,20 @@ export async function translateObject(props: {
}
let targetObj = allOnlyNewTranslatedTranslationsObject?.[targetLang]
for (let i = 0; i < keys.length - 1; i += 1) {
if (!targetObj[keys[i]]) {
targetObj[keys[i]] = {}
if (!targetObj[keys[i] as string]) {
targetObj[keys[i] as string] = {}
}
const nextObj = targetObj[keys[i]]
const nextObj = targetObj[keys[i] as string]
if (typeof nextObj !== 'object') {
throw new Error(`Key ${keys[i]} is not an object in ${targetLang} (3)`)
}
targetObj = nextObj
}
targetObj[keys[keys.length - 1]] = translated
targetObj[keys[keys.length - 1] as string] = translated
allTranslatedTranslationsObject[targetLang].translations = sortKeys(
allTranslatedTranslationsObject[targetLang]!.translations = sortKeys(
deepMergeSimple(
allTranslatedTranslationsObject[targetLang].translations,
allTranslatedTranslationsObject[targetLang]!.translations,
allOnlyNewTranslatedTranslationsObject[targetLang],
),
)
@@ -180,9 +183,14 @@ export async function translateObject(props: {
console.log('New translations:', allOnlyNewTranslatedTranslationsObject)
if (inlineFile?.length) {
const simpleTranslationsObject = {}
const simpleTranslationsObject: GenericTranslationsObject = {}
for (const lang in allTranslatedTranslationsObject) {
simpleTranslationsObject[lang] = allTranslatedTranslationsObject[lang].translations
if (lang in allTranslatedTranslationsObject) {
simpleTranslationsObject[lang as keyof typeof allTranslatedTranslationsObject] =
allTranslatedTranslationsObject[
lang as keyof typeof allTranslatedTranslationsObject
]!.translations
}
}
// write allTranslatedTranslationsObject
@@ -218,10 +226,10 @@ export async function translateObject(props: {
const filePath = path.resolve(targetFolder, `${sanitizedKey}.ts`)
// prefix & translations
let fileContent: string = `${tsFilePrefix.replace('{{locale}}', sanitizedKey)}${generateTsObjectLiteral(allTranslatedTranslationsObject[key].translations)}\n`
let fileContent: string = `${tsFilePrefix.replace('{{locale}}', sanitizedKey)}${generateTsObjectLiteral(allTranslatedTranslationsObject[key]?.translations || {})}\n`
// suffix
fileContent += `${tsFileSuffix.replaceAll('{{locale}}', sanitizedKey).replaceAll('{{dateFNSKey}}', `'${allTranslatedTranslationsObject[key].dateFNSKey}'`)}\n`
fileContent += `${tsFileSuffix.replaceAll('{{locale}}', sanitizedKey).replaceAll('{{dateFNSKey}}', `'${allTranslatedTranslationsObject[key]?.dateFNSKey}'`)}\n`
// eslint
fileContent = await applyEslintFixes(fileContent, filePath)

View File

@@ -4,7 +4,7 @@ import path from 'path'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
dotenv.config({ path: path.resolve(dirname, '../../', '.env') })
dotenv.config({ path: path.resolve(dirname, '../../../../', '.env') })
export async function translateText(text: string, targetLang: string) {
const response = await fetch('https://api.openai.com/v1/chat/completions', {

View File

@@ -0,0 +1,12 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"strict": true
},
"references": [
{ "path": "../../packages/translations" },
{ "path": "../../packages/richtext-lexical" },
{ "path": "../../packages/plugin-multi-tenant" }
],
"exclude": ["./src/generateTranslations"]
}

View File

@@ -2,5 +2,6 @@
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"strict": true,
}
},
"references": [{ "path": "../../packages/translations" }, { "path": "../../packages/richtext-lexical"}, { "path": "../../packages/plugin-multi-tenant"}]
}

View File

@@ -31,7 +31,7 @@
}
],
"paths": {
"@payload-config": ["./test/admin/config.ts"],
"@payload-config": ["./test/plugin-multi-tenant/config.ts"],
"@payloadcms/admin-bar": ["./packages/admin-bar/src"],
"@payloadcms/live-preview": ["./packages/live-preview/src"],
"@payloadcms/live-preview-react": ["./packages/live-preview-react/src/index.ts"],
@@ -70,6 +70,12 @@
"./packages/plugin-multi-tenant/src/exports/client.ts"
],
"@payloadcms/plugin-multi-tenant": ["./packages/plugin-multi-tenant/src/index.ts"],
"@payloadcms/plugin-multi-tenant/translations/languages/all": [
"./packages/plugin-multi-tenant/src/translations/index.ts"
],
"@payloadcms/plugin-multi-tenant/translations/languages/*": [
"./packages/plugin-multi-tenant/src/translations/languages/*.ts"
],
"@payloadcms/next": ["./packages/next/src/exports/*"],
"@payloadcms/storage-s3/client": ["./packages/storage-s3/src/exports/client.ts"],
"@payloadcms/storage-vercel-blob/client": [