From 31211e975549f6696db83b997209b9c457c7dc6e Mon Sep 17 00:00:00 2001 From: Jacob Fletcher Date: Thu, 20 Mar 2025 13:43:17 -0400 Subject: [PATCH] feat: pass i18n through field label and description functions (#11802) Passes the `i18n` arg through field label and description functions. This is to avoid using custom components when simply needing to translate a `StaticLabel` object, such as collection labels. Here's an example: ```ts { labels: { singular: { en: 'My Collection' } }, fields: [ // ... { type: 'collapsible', label: ({ i18n }) => `Translate this: ${getTranslation(collectionConfig.labels.singular, i18n)}` // ... } ] } ``` --- packages/next/src/views/Versions/index.tsx | 2 +- .../payload/src/admin/forms/Description.ts | 4 ++-- .../payload/src/collections/config/client.ts | 4 ++-- packages/payload/src/config/types.ts | 5 ++--- packages/payload/src/fields/config/client.ts | 18 +++++++++--------- packages/payload/src/fields/config/types.ts | 1 + packages/payload/src/globals/config/client.ts | 2 +- .../src/utilities/getTranslatedLabel.ts | 2 +- .../src/utilities/getTranslation.ts | 8 +++++--- packages/ui/src/fields/Tabs/index.tsx | 2 +- .../fieldSchemasToFormState/renderField.tsx | 1 + packages/ui/src/utilities/groupNavItems.ts | 4 +++- 12 files changed, 29 insertions(+), 24 deletions(-) diff --git a/packages/next/src/views/Versions/index.tsx b/packages/next/src/views/Versions/index.tsx index 820ed2ce5..b1048573b 100644 --- a/packages/next/src/views/Versions/index.tsx +++ b/packages/next/src/views/Versions/index.tsx @@ -172,7 +172,7 @@ export async function VersionsView(props: DocumentViewServerProps) { const pluralLabel = collectionConfig?.labels?.plural ? typeof collectionConfig.labels.plural === 'function' - ? collectionConfig.labels.plural({ t }) + ? collectionConfig.labels.plural({ i18n, t }) : collectionConfig.labels.plural : globalConfig?.label diff --git a/packages/payload/src/admin/forms/Description.ts b/packages/payload/src/admin/forms/Description.ts index 08a8e0cfc..2fd2220e8 100644 --- a/packages/payload/src/admin/forms/Description.ts +++ b/packages/payload/src/admin/forms/Description.ts @@ -1,9 +1,9 @@ -import type { TFunction } from '@payloadcms/translations' +import type { I18nClient, TFunction } from '@payloadcms/translations' import type { Field } from '../../fields/config/types.js' import type { ClientFieldWithOptionalType, ServerComponentProps } from './Field.js' -export type DescriptionFunction = ({ t }: { t: TFunction }) => string +export type DescriptionFunction = (args: { i18n: I18nClient; t: TFunction }) => string export type FieldDescriptionClientComponent< TFieldClient extends ClientFieldWithOptionalType = ClientFieldWithOptionalType, diff --git a/packages/payload/src/collections/config/client.ts b/packages/payload/src/collections/config/client.ts index 589467086..707cd7375 100644 --- a/packages/payload/src/collections/config/client.ts +++ b/packages/payload/src/collections/config/client.ts @@ -215,11 +215,11 @@ export const createClientCollectionConfig = ({ clientCollection.labels = { plural: typeof collection.labels.plural === 'function' - ? collection.labels.plural({ t: i18n.t }) + ? collection.labels.plural({ i18n, t: i18n.t }) : collection.labels.plural, singular: typeof collection.labels.singular === 'function' - ? collection.labels.singular({ t: i18n.t }) + ? collection.labels.singular({ i18n, t: i18n.t }) : collection.labels.singular, } break diff --git a/packages/payload/src/config/types.ts b/packages/payload/src/config/types.ts index 10a4c9eb6..fd39a85f9 100644 --- a/packages/payload/src/config/types.ts +++ b/packages/payload/src/config/types.ts @@ -508,9 +508,8 @@ export type LocalizationConfig = Prettify< LocalizationConfigWithLabels | LocalizationConfigWithNoLabels > -export type LabelFunction = ({ - t, -}: { +export type LabelFunction = (args: { + i18n: I18nClient t: TFunction }) => string diff --git a/packages/payload/src/fields/config/client.ts b/packages/payload/src/fields/config/client.ts index 2cdf6ef64..408bfedff 100644 --- a/packages/payload/src/fields/config/client.ts +++ b/packages/payload/src/fields/config/client.ts @@ -140,12 +140,12 @@ export const createClientBlocks = ({ if (block.labels.singular) { if (typeof block.labels.singular === 'function') { - clientBlock.labels.singular = block.labels.singular({ t: i18n.t }) + clientBlock.labels.singular = block.labels.singular({ i18n, t: i18n.t }) } else { clientBlock.labels.singular = block.labels.singular } if (typeof block.labels.plural === 'function') { - clientBlock.labels.plural = block.labels.plural({ t: i18n.t }) + clientBlock.labels.plural = block.labels.plural({ i18n, t: i18n.t }) } else { clientBlock.labels.plural = block.labels.plural } @@ -224,7 +224,7 @@ export const createClientField = ({ //@ts-expect-error - would need to type narrow if (typeof incomingField.label === 'function') { //@ts-expect-error - would need to type narrow - clientField.label = incomingField.label({ t: i18n.t }) + clientField.label = incomingField.label({ i18n, t: i18n.t }) } else { //@ts-expect-error - would need to type narrow clientField.label = incomingField.label @@ -246,12 +246,12 @@ export const createClientField = ({ if (incomingField.labels.singular) { if (typeof incomingField.labels.singular === 'function') { - field.labels.singular = incomingField.labels.singular({ t: i18n.t }) + field.labels.singular = incomingField.labels.singular({ i18n, t: i18n.t }) } else { field.labels.singular = incomingField.labels.singular } if (typeof incomingField.labels.plural === 'function') { - field.labels.plural = incomingField.labels.plural({ t: i18n.t }) + field.labels.plural = incomingField.labels.plural({ i18n, t: i18n.t }) } else { field.labels.plural = incomingField.labels.plural } @@ -287,12 +287,12 @@ export const createClientField = ({ if (incomingField.labels.singular) { if (typeof incomingField.labels.singular === 'function') { - field.labels.singular = incomingField.labels.singular({ t: i18n.t }) + field.labels.singular = incomingField.labels.singular({ i18n, t: i18n.t }) } else { field.labels.singular = incomingField.labels.singular } if (typeof incomingField.labels.plural === 'function') { - field.labels.plural = incomingField.labels.plural({ t: i18n.t }) + field.labels.plural = incomingField.labels.plural({ i18n, t: i18n.t }) } else { field.labels.plural = incomingField.labels.plural } @@ -345,7 +345,7 @@ export const createClientField = ({ } field.options[i] = { - label: option.label({ t: i18n.t }), + label: option.label({ i18n, t: i18n.t }), value: option.value, } } @@ -409,7 +409,7 @@ export const createClientField = ({ case 'description': if ('description' in tab.admin) { if (typeof tab.admin?.description === 'function') { - clientTab.admin.description = tab.admin.description({ t: i18n.t }) + clientTab.admin.description = tab.admin.description({ i18n, t: i18n.t }) } else { clientTab.admin.description = tab.admin.description } diff --git a/packages/payload/src/fields/config/types.ts b/packages/payload/src/fields/config/types.ts index 12f622445..cb8df97e5 100644 --- a/packages/payload/src/fields/config/types.ts +++ b/packages/payload/src/fields/config/types.ts @@ -447,6 +447,7 @@ export type OptionObject = { label: OptionLabel value: string } + export type Option = OptionObject | string export type FieldGraphQLType = { diff --git a/packages/payload/src/globals/config/client.ts b/packages/payload/src/globals/config/client.ts index c74493040..11ec0595e 100644 --- a/packages/payload/src/globals/config/client.ts +++ b/packages/payload/src/globals/config/client.ts @@ -105,7 +105,7 @@ export const createClientGlobalConfig = ({ break case 'label': clientGlobal.label = - typeof global.label === 'function' ? global.label({ t: i18n.t }) : global.label + typeof global.label === 'function' ? global.label({ i18n, t: i18n.t }) : global.label break default: { clientGlobal[key] = global[key] diff --git a/packages/payload/src/utilities/getTranslatedLabel.ts b/packages/payload/src/utilities/getTranslatedLabel.ts index 77e9f5b8c..91ff01dc6 100644 --- a/packages/payload/src/utilities/getTranslatedLabel.ts +++ b/packages/payload/src/utilities/getTranslatedLabel.ts @@ -5,7 +5,7 @@ import type { LabelFunction, StaticLabel } from '../config/types.js' export const getTranslatedLabel = (label: LabelFunction | StaticLabel, i18n?: I18n): string => { if (typeof label === 'function') { - return label({ t: i18n.t }) + return label({ i18n, t: i18n.t }) } if (typeof label === 'object') { diff --git a/packages/translations/src/utilities/getTranslation.ts b/packages/translations/src/utilities/getTranslation.ts index 82b9462cc..4dce7b926 100644 --- a/packages/translations/src/utilities/getTranslation.ts +++ b/packages/translations/src/utilities/getTranslation.ts @@ -1,10 +1,10 @@ import type { JSX } from 'react' -import type { I18n, TFunction } from '../types.js' +import type { I18n, I18nClient, TFunction } from '../types.js' type LabelType = | (() => JSX.Element) - | (({ t }: { t: TFunction }) => string) + | ((args: { i18n: I18nClient; t: TFunction }) => string) | JSX.Element | Record | string @@ -35,7 +35,9 @@ export const getTranslation = ( } if (typeof label === 'function') { - return label({ t: i18n.t }) as unknown as T extends JSX.Element ? JSX.Element : string + return label({ i18n: undefined as any, t: i18n.t }) as unknown as T extends JSX.Element + ? JSX.Element + : string } // If it's a React Element or string, then we should just pass it through diff --git a/packages/ui/src/fields/Tabs/index.tsx b/packages/ui/src/fields/Tabs/index.tsx index 1646086cd..5b7e9b6bf 100644 --- a/packages/ui/src/fields/Tabs/index.tsx +++ b/packages/ui/src/fields/Tabs/index.tsx @@ -98,7 +98,7 @@ const TabsFieldComponent: TabsFieldClientComponent = (props) => { const activeTabStaticDescription = typeof activeTabDescription === 'function' - ? activeTabDescription({ t: i18n.t }) + ? activeTabDescription({ i18n, t: i18n.t }) : activeTabDescription const hasVisibleTabs = tabStates.some(({ passesCondition }) => passesCondition) diff --git a/packages/ui/src/forms/fieldSchemasToFormState/renderField.tsx b/packages/ui/src/forms/fieldSchemasToFormState/renderField.tsx index d8c5113ce..0c92bc510 100644 --- a/packages/ui/src/forms/fieldSchemasToFormState/renderField.tsx +++ b/packages/ui/src/forms/fieldSchemasToFormState/renderField.tsx @@ -244,6 +244,7 @@ export const renderField: RenderFieldMethod = ({ fieldState.customComponents.Description = (