feat(plugin-multi-tenant): prompt the user to confirm the change of tenant before actually updating (#12382)
This commit is contained in:
@@ -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',
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
|
||||
91
packages/plugin-multi-tenant/src/translations/index.ts
Normal file
91
packages/plugin-multi-tenant/src/translations/index.ts
Normal 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>
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
for file in *.js; do
|
||||
mv -- "$file" "${file%.js}.ts"
|
||||
done
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
12
packages/plugin-multi-tenant/src/translations/types.ts
Normal file
12
packages/plugin-multi-tenant/src/translations/types.ts
Normal 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
|
||||
@@ -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",
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
OPENAI_KEY=sk-
|
||||
@@ -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"
|
||||
|
||||
@@ -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
13
pnpm-lock.yaml
generated
@@ -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:
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ export default buildConfigWithDefaults({
|
||||
isGlobal: true,
|
||||
},
|
||||
},
|
||||
tenantSelectorLabel: 'Sites',
|
||||
tenantSelectorLabel: 'Site',
|
||||
}),
|
||||
],
|
||||
typescript: {
|
||||
|
||||
1
tools/.env.example
Normal file
1
tools/.env.example
Normal file
@@ -0,0 +1 @@
|
||||
OPENAI_KEY=sk-your-key-here
|
||||
@@ -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:*"
|
||||
}
|
||||
}
|
||||
|
||||
31
tools/scripts/src/generateTranslations/core.ts
Normal file
31
tools/scripts/src/generateTranslations/core.ts
Normal 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'),
|
||||
})
|
||||
@@ -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,
|
||||
} `,
|
||||
})
|
||||
@@ -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,
|
||||
@@ -62,6 +66,9 @@ async function translate() {
|
||||
export const i18n: Partial<GenericLanguages> = `,
|
||||
tsFileSuffix: ``,
|
||||
})
|
||||
} else {
|
||||
console.error(`No English translations found in ${i18nFilePath}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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', {
|
||||
12
tools/scripts/tsconfig.build.json
Normal file
12
tools/scripts/tsconfig.build.json
Normal 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"]
|
||||
}
|
||||
@@ -2,5 +2,6 @@
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
}
|
||||
},
|
||||
"references": [{ "path": "../../packages/translations" }, { "path": "../../packages/richtext-lexical"}, { "path": "../../packages/plugin-multi-tenant"}]
|
||||
}
|
||||
|
||||
@@ -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": [
|
||||
|
||||
Reference in New Issue
Block a user