diff --git a/packages/twenty-server/src/engine/dataloaders/dataloader.interface.ts b/packages/twenty-server/src/engine/dataloaders/dataloader.interface.ts index 880250ee8a..e79094ae21 100644 --- a/packages/twenty-server/src/engine/dataloaders/dataloader.interface.ts +++ b/packages/twenty-server/src/engine/dataloaders/dataloader.interface.ts @@ -1,7 +1,7 @@ import type DataLoader from 'dataloader'; import { - type ApplicationRegistrationIdLoaderPayload, + type ApplicationTranslationCatalogLoaderPayload, type FieldMetadataLoaderPayload, type IndexFieldMetadataLoaderPayload, type IndexMetadataLoaderPayload, @@ -100,8 +100,8 @@ export interface IDataloaders { string >; - applicationRegistrationIdLoader: DataLoader< - ApplicationRegistrationIdLoaderPayload, - string | null + applicationTranslationCatalogLoader: DataLoader< + ApplicationTranslationCatalogLoaderPayload, + Record | undefined >; } diff --git a/packages/twenty-server/src/engine/dataloaders/dataloader.service.ts b/packages/twenty-server/src/engine/dataloaders/dataloader.service.ts index f735344f68..268e46197e 100644 --- a/packages/twenty-server/src/engine/dataloaders/dataloader.service.ts +++ b/packages/twenty-server/src/engine/dataloaders/dataloader.service.ts @@ -124,9 +124,10 @@ export type StandardApplicationIdLoaderPayload = { workspaceId: string; }; -export type ApplicationRegistrationIdLoaderPayload = { - workspaceId: string; +export type ApplicationTranslationCatalogLoaderPayload = { applicationId: string; + workspaceId: string; + locale: keyof typeof APP_LOCALES; }; @Injectable() @@ -158,8 +159,8 @@ export class DataloaderService { const isConfiguredLoader = this.createIsConfiguredLoader(); const standardApplicationIdLoader = this.createStandardApplicationIdLoader(); - const applicationRegistrationIdLoader = - this.createApplicationRegistrationIdLoader(); + const applicationTranslationCatalogLoader = + this.createApplicationTranslationCatalogLoader(); return { relationLoader, @@ -177,7 +178,7 @@ export class DataloaderService { viewFilterGroupsByViewIdLoader, isConfiguredLoader, standardApplicationIdLoader, - applicationRegistrationIdLoader, + applicationTranslationCatalogLoader, }; } @@ -831,12 +832,13 @@ export class DataloaderService { ); } - private createApplicationRegistrationIdLoader() { + private createApplicationTranslationCatalogLoader() { return new DataLoader< - ApplicationRegistrationIdLoaderPayload, - string | null - >(async (params: ApplicationRegistrationIdLoaderPayload[]) => { + ApplicationTranslationCatalogLoaderPayload, + Record | undefined + >(async (params: ApplicationTranslationCatalogLoaderPayload[]) => { const workspaceId = params[0].workspaceId; + const locale = params[0].locale; const { flatApplicationMaps } = await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps( @@ -846,11 +848,29 @@ export class DataloaderService { }, ); - return params.map( - ({ applicationId }) => - flatApplicationMaps.byId[applicationId]?.applicationRegistrationId ?? - null, - ); + const standardApplicationId = + getTwentyStandardApplicationIdOrThrow(flatApplicationMaps); + + const catalogByRegistrationId = + await this.loadApplicationCatalogByRegistrationId({ + applicationIds: params.map((param) => param.applicationId), + flatApplicationMaps, + locale, + }); + + return params.map((param) => { + if (param.applicationId === standardApplicationId) { + return undefined; + } + + const applicationRegistrationId = + flatApplicationMaps.byId[param.applicationId] + ?.applicationRegistrationId; + + return isDefined(applicationRegistrationId) + ? catalogByRegistrationId.get(applicationRegistrationId) + : undefined; + }); }); } diff --git a/packages/twenty-server/src/engine/metadata-modules/command-menu-item/command-menu-item.resolver.ts b/packages/twenty-server/src/engine/metadata-modules/command-menu-item/command-menu-item.resolver.ts index f88cd44845..edbf31725f 100644 --- a/packages/twenty-server/src/engine/metadata-modules/command-menu-item/command-menu-item.resolver.ts +++ b/packages/twenty-server/src/engine/metadata-modules/command-menu-item/command-menu-item.resolver.ts @@ -52,6 +52,8 @@ export class CommandMenuItemResolver { objectMetadataLoader: context.loaders.objectMetadataLoader, standardApplicationIdLoader: context.loaders.standardApplicationIdLoader, + applicationTranslationCatalogLoader: + context.loaders.applicationTranslationCatalogLoader, workspaceId: workspace.id, locale: context.req.locale, })) ?? '' @@ -69,6 +71,8 @@ export class CommandMenuItemResolver { fieldName: 'shortLabel', objectMetadataLoader: context.loaders.objectMetadataLoader, standardApplicationIdLoader: context.loaders.standardApplicationIdLoader, + applicationTranslationCatalogLoader: + context.loaders.applicationTranslationCatalogLoader, workspaceId: workspace.id, locale: context.req.locale, }); @@ -85,6 +89,8 @@ export class CommandMenuItemResolver { fieldName: 'icon', objectMetadataLoader: context.loaders.objectMetadataLoader, standardApplicationIdLoader: context.loaders.standardApplicationIdLoader, + applicationTranslationCatalogLoader: + context.loaders.applicationTranslationCatalogLoader, workspaceId: workspace.id, locale: context.req.locale, }); diff --git a/packages/twenty-server/src/engine/metadata-modules/command-menu-item/command-menu-item.service.ts b/packages/twenty-server/src/engine/metadata-modules/command-menu-item/command-menu-item.service.ts index 4b01c693a4..eba6ceeec6 100644 --- a/packages/twenty-server/src/engine/metadata-modules/command-menu-item/command-menu-item.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/command-menu-item/command-menu-item.service.ts @@ -7,6 +7,7 @@ import { isDefined } from 'twenty-shared/utils'; import { ApplicationService } from 'src/engine/core-modules/application/application.service'; import { I18nService } from 'src/engine/core-modules/i18n/i18n.service'; import { + type ApplicationTranslationCatalogLoaderPayload, type ObjectMetadataLoaderPayload, type StandardApplicationIdLoaderPayload, } from 'src/engine/dataloaders/dataloader.service'; @@ -449,6 +450,7 @@ export class CommandMenuItemService { fieldName, objectMetadataLoader, standardApplicationIdLoader, + applicationTranslationCatalogLoader, workspaceId, locale, }: { @@ -462,6 +464,10 @@ export class CommandMenuItemService { StandardApplicationIdLoaderPayload, string >; + applicationTranslationCatalogLoader: DataLoader< + ApplicationTranslationCatalogLoaderPayload, + Record | undefined + >; workspaceId: string; locale: keyof typeof APP_LOCALES | undefined; }): Promise { @@ -479,6 +485,16 @@ export class CommandMenuItemService { ? objectMetadata.applicationId === standardApplicationId : false; + // The loader returns undefined for the standard app, so the standard-app + // short-circuit lives in the loader, not here. + const applicationCatalog = isDefined(objectMetadata) + ? await applicationTranslationCatalogLoader.load({ + applicationId: objectMetadata.applicationId, + workspaceId, + locale: locale ?? SOURCE_LOCALE, + }) + : undefined; + return interpolateNavigationCommandMenuItemField({ commandMenuItem, fieldName, @@ -486,6 +502,7 @@ export class CommandMenuItemService { isStandardApp, locale, i18nInstance: this.i18nService.getI18nInstance(locale ?? SOURCE_LOCALE), + applicationCatalog, }); } diff --git a/packages/twenty-server/src/engine/metadata-modules/command-menu-item/utils/build-navigation-interpolation-context.util.ts b/packages/twenty-server/src/engine/metadata-modules/command-menu-item/utils/build-navigation-interpolation-context.util.ts index dec953e600..aab60cee71 100644 --- a/packages/twenty-server/src/engine/metadata-modules/command-menu-item/utils/build-navigation-interpolation-context.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/command-menu-item/utils/build-navigation-interpolation-context.util.ts @@ -17,11 +17,13 @@ export const buildNavigationInterpolationContext = ({ isStandardApp, locale, i18nInstance, + applicationCatalog, }: { objectMetadata: NavigationInterpolationObjectMetadata; isStandardApp: boolean; locale: keyof typeof APP_LOCALES | undefined; i18nInstance: I18n; + applicationCatalog?: Record; }): Record => { const overrideInput = { labelPlural: objectMetadata.labelPlural, @@ -37,6 +39,7 @@ export const buildNavigationInterpolationContext = ({ locale, i18nInstance, isStandardApp, + applicationCatalog, ); const resolvedIcon = resolveObjectMetadataStandardOverride( @@ -45,6 +48,7 @@ export const buildNavigationInterpolationContext = ({ locale, i18nInstance, isStandardApp, + applicationCatalog, ); return { diff --git a/packages/twenty-server/src/engine/metadata-modules/command-menu-item/utils/interpolate-navigation-command-menu-item-field.util.ts b/packages/twenty-server/src/engine/metadata-modules/command-menu-item/utils/interpolate-navigation-command-menu-item-field.util.ts index 2eec9f8dc0..e3d583a6e0 100644 --- a/packages/twenty-server/src/engine/metadata-modules/command-menu-item/utils/interpolate-navigation-command-menu-item-field.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/command-menu-item/utils/interpolate-navigation-command-menu-item-field.util.ts @@ -19,6 +19,7 @@ export const interpolateNavigationCommandMenuItemField = ({ isStandardApp, locale, i18nInstance, + applicationCatalog, }: { commandMenuItem: CommandMenuItemDTO; fieldName: 'label' | 'shortLabel' | 'icon'; @@ -26,6 +27,7 @@ export const interpolateNavigationCommandMenuItemField = ({ isStandardApp: boolean; locale: keyof typeof APP_LOCALES | undefined; i18nInstance: I18n; + applicationCatalog?: Record; }): string | undefined => { const rawValue = commandMenuItem[fieldName]; @@ -49,6 +51,7 @@ export const interpolateNavigationCommandMenuItemField = ({ isStandardApp, locale, i18nInstance, + applicationCatalog, }); return ( diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.resolver.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.resolver.ts index 839ca20972..1d4b5f068d 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.resolver.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.resolver.ts @@ -5,7 +5,6 @@ import { PermissionFlagType } from 'twenty-shared/constants'; import { isDefined } from 'twenty-shared/utils'; import { MetadataResolver } from 'src/engine/api/graphql/graphql-config/decorators/metadata-resolver.decorator'; -import { ApplicationTranslationCacheService } from 'src/engine/core-modules/application/application-translation/application-translation-cache.service'; import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter'; import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; import { ForbiddenError } from 'src/engine/core-modules/graphql/utils/graphql-errors.util'; @@ -45,7 +44,6 @@ export class FieldMetadataResolver { constructor( private readonly fieldMetadataService: FieldMetadataService, private readonly i18nService: I18nService, - private readonly applicationTranslationCacheService: ApplicationTranslationCacheService, ) {} @ResolveField(() => Boolean, { @@ -71,19 +69,12 @@ export class FieldMetadataResolver { const isStandardApp = fieldMetadata.applicationId === standardApplicationId; - const applicationRegistrationId = isStandardApp - ? null - : await context.loaders.applicationRegistrationIdLoader.load({ - workspaceId, - applicationId: fieldMetadata.applicationId, - }); - - const applicationCatalog = isDefined(applicationRegistrationId) - ? await this.applicationTranslationCacheService.getCatalog({ - applicationRegistrationId, - locale: context.req.locale, - }) - : undefined; + const applicationCatalog = + await context.loaders.applicationTranslationCatalogLoader.load({ + applicationId: fieldMetadata.applicationId, + workspaceId, + locale: context.req.locale, + }); return resolveFieldMetadataStandardOverride( fieldMetadata, diff --git a/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.resolver.ts b/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.resolver.ts index 080dd99d41..c498025ba1 100644 --- a/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.resolver.ts +++ b/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.resolver.ts @@ -9,10 +9,8 @@ import { } from '@nestjs/graphql'; import { PermissionFlagType } from 'twenty-shared/constants'; -import { isDefined } from 'twenty-shared/utils'; import { MetadataResolver } from 'src/engine/api/graphql/graphql-config/decorators/metadata-resolver.decorator'; -import { ApplicationTranslationCacheService } from 'src/engine/core-modules/application/application-translation/application-translation-cache.service'; import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter'; import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; import { I18nService } from 'src/engine/core-modules/i18n/i18n.service'; @@ -48,7 +46,6 @@ export class ObjectMetadataResolver { private readonly objectMetadataService: ObjectMetadataService, private readonly objectRecordCountService: ObjectRecordCountService, private readonly i18nService: I18nService, - private readonly applicationTranslationCacheService: ApplicationTranslationCacheService, ) {} @ResolveField(() => Boolean, { @@ -87,19 +84,12 @@ export class ObjectMetadataResolver { const isStandardApp = objectMetadata.applicationId === standardApplicationId; - const applicationRegistrationId = isStandardApp - ? null - : await context.loaders.applicationRegistrationIdLoader.load({ - workspaceId, - applicationId: objectMetadata.applicationId, - }); - - const applicationCatalog = isDefined(applicationRegistrationId) - ? await this.applicationTranslationCacheService.getCatalog({ - applicationRegistrationId, - locale: context.req.locale, - }) - : undefined; + const applicationCatalog = + await context.loaders.applicationTranslationCatalogLoader.load({ + applicationId: objectMetadata.applicationId, + workspaceId, + locale: context.req.locale, + }); return resolveObjectMetadataStandardOverride( objectMetadata, diff --git a/packages/twenty-server/src/engine/metadata-modules/page-layout-tab/resolvers/page-layout-tab.resolver.ts b/packages/twenty-server/src/engine/metadata-modules/page-layout-tab/resolvers/page-layout-tab.resolver.ts index d91ce2310f..9172deaf2c 100644 --- a/packages/twenty-server/src/engine/metadata-modules/page-layout-tab/resolvers/page-layout-tab.resolver.ts +++ b/packages/twenty-server/src/engine/metadata-modules/page-layout-tab/resolvers/page-layout-tab.resolver.ts @@ -16,11 +16,11 @@ import { import { PermissionFlagType } from 'twenty-shared/constants'; import { MetadataResolver } from 'src/engine/api/graphql/graphql-config/decorators/metadata-resolver.decorator'; -import { ApplicationService } from 'src/engine/core-modules/application/application.service'; import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; import { I18nService } from 'src/engine/core-modules/i18n/i18n.service'; import { type I18nContext } from 'src/engine/core-modules/i18n/types/i18n-context.type'; import { type WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; +import { type IDataloaders } from 'src/engine/dataloaders/dataloader.interface'; import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator'; import { NoPermissionGuard } from 'src/engine/guards/no-permission.guard'; import { SettingsPermissionGuard } from 'src/engine/guards/settings-permission.guard'; @@ -42,28 +42,35 @@ export class PageLayoutTabResolver { constructor( private readonly pageLayoutTabService: PageLayoutTabService, private readonly i18nService: I18nService, - private readonly applicationService: ApplicationService, ) {} @ResolveField(() => String) async title( @Parent() tab: PageLayoutTabDTO, - @Context() context: I18nContext, + @Context() context: { loaders: IDataloaders } & I18nContext, @AuthWorkspace() workspace: WorkspaceEntity, ): Promise { const i18n = this.i18nService.getI18nInstance(context.req.locale); - const { twentyStandardFlatApplication } = - await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow( - { workspace }, - ); + const standardApplicationId = + await context.loaders.standardApplicationIdLoader.load({ + workspaceId: workspace.id, + }); + + const applicationCatalog = + await context.loaders.applicationTranslationCatalogLoader.load({ + applicationId: tab.applicationId, + workspaceId: workspace.id, + locale: context.req.locale, + }); return resolvePageLayoutTabTitle({ title: tab.title, applicationId: tab.applicationId, - twentyStandardApplicationId: twentyStandardFlatApplication.id, + twentyStandardApplicationId: standardApplicationId, overrides: tab.overrides, i18nInstance: i18n, + applicationCatalog, }); } diff --git a/packages/twenty-server/src/engine/metadata-modules/page-layout-tab/utils/resolve-page-layout-tab-title.util.ts b/packages/twenty-server/src/engine/metadata-modules/page-layout-tab/utils/resolve-page-layout-tab-title.util.ts index 3cb698dcc3..64db8480ce 100644 --- a/packages/twenty-server/src/engine/metadata-modules/page-layout-tab/utils/resolve-page-layout-tab-title.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/page-layout-tab/utils/resolve-page-layout-tab-title.util.ts @@ -2,7 +2,7 @@ import { type I18n } from '@lingui/core'; import { isDefined } from 'twenty-shared/utils'; -import { generateMessageId } from 'src/engine/core-modules/i18n/utils/generateMessageId'; +import { translateStandardLabel } from 'src/engine/core-modules/i18n/utils/translate-standard-label.util'; import { type PageLayoutTabOverrides } from 'src/engine/metadata-modules/page-layout-tab/entities/page-layout-tab.entity'; export const resolvePageLayoutTabTitle = ({ @@ -11,27 +11,25 @@ export const resolvePageLayoutTabTitle = ({ twentyStandardApplicationId, overrides, i18nInstance, + applicationCatalog, }: { title: string; applicationId: string; twentyStandardApplicationId: string; overrides?: PageLayoutTabOverrides | null; i18nInstance: I18n; + applicationCatalog?: Record; }): string => { - if (applicationId !== twentyStandardApplicationId) { - return title; - } + const isStandardApp = applicationId === twentyStandardApplicationId; if (isDefined(overrides?.title)) { return title; } - const messageId = generateMessageId(title); - const translatedMessage = i18nInstance._(messageId); - - if (translatedMessage === messageId) { - return title; - } - - return translatedMessage; + return translateStandardLabel({ + sourceValue: title, + isStandardApp, + applicationCatalog, + i18nInstance, + }); }; diff --git a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/resolvers/page-layout-widget.resolver.ts b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/resolvers/page-layout-widget.resolver.ts index b7c5a9e6c6..8b436da5af 100644 --- a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/resolvers/page-layout-widget.resolver.ts +++ b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/resolvers/page-layout-widget.resolver.ts @@ -15,11 +15,11 @@ import { import { PermissionFlagType } from 'twenty-shared/constants'; -import { ApplicationService } from 'src/engine/core-modules/application/application.service'; import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; import { I18nService } from 'src/engine/core-modules/i18n/i18n.service'; import { type I18nContext } from 'src/engine/core-modules/i18n/types/i18n-context.type'; import { type WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; +import { type IDataloaders } from 'src/engine/dataloaders/dataloader.interface'; import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator'; import { MetadataResolver } from 'src/engine/api/graphql/graphql-config/decorators/metadata-resolver.decorator'; import { NoPermissionGuard } from 'src/engine/guards/no-permission.guard'; @@ -43,28 +43,35 @@ export class PageLayoutWidgetResolver { constructor( private readonly pageLayoutWidgetService: PageLayoutWidgetService, private readonly i18nService: I18nService, - private readonly applicationService: ApplicationService, ) {} @ResolveField(() => String) async title( @Parent() widget: PageLayoutWidgetDTO, - @Context() context: I18nContext, + @Context() context: { loaders: IDataloaders } & I18nContext, @AuthWorkspace() workspace: WorkspaceEntity, ): Promise { const i18n = this.i18nService.getI18nInstance(context.req.locale); - const { twentyStandardFlatApplication } = - await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow( - { workspace }, - ); + const standardApplicationId = + await context.loaders.standardApplicationIdLoader.load({ + workspaceId: workspace.id, + }); + + const applicationCatalog = + await context.loaders.applicationTranslationCatalogLoader.load({ + applicationId: widget.applicationId, + workspaceId: workspace.id, + locale: context.req.locale, + }); return resolvePageLayoutWidgetTitle({ title: widget.title, applicationId: widget.applicationId, - twentyStandardApplicationId: twentyStandardFlatApplication.id, + twentyStandardApplicationId: standardApplicationId, overrides: widget.overrides, i18nInstance: i18n, + applicationCatalog, }); } diff --git a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/utils/resolve-page-layout-widget-title.util.ts b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/utils/resolve-page-layout-widget-title.util.ts index 01148028c2..9245b1ffac 100644 --- a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/utils/resolve-page-layout-widget-title.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/utils/resolve-page-layout-widget-title.util.ts @@ -2,7 +2,7 @@ import { type I18n } from '@lingui/core'; import { isDefined } from 'twenty-shared/utils'; -import { generateMessageId } from 'src/engine/core-modules/i18n/utils/generateMessageId'; +import { translateStandardLabel } from 'src/engine/core-modules/i18n/utils/translate-standard-label.util'; import { type PageLayoutWidgetOverrides } from 'src/engine/metadata-modules/page-layout-widget/entities/page-layout-widget.entity'; export const resolvePageLayoutWidgetTitle = ({ @@ -11,27 +11,25 @@ export const resolvePageLayoutWidgetTitle = ({ twentyStandardApplicationId, overrides, i18nInstance, + applicationCatalog, }: { title: string; applicationId: string; twentyStandardApplicationId: string; overrides?: PageLayoutWidgetOverrides | null; i18nInstance: I18n; + applicationCatalog?: Record; }): string => { - if (applicationId !== twentyStandardApplicationId) { - return title; - } + const isStandardApp = applicationId === twentyStandardApplicationId; if (isDefined(overrides?.title)) { return title; } - const messageId = generateMessageId(title); - const translatedMessage = i18nInstance._(messageId); - - if (translatedMessage === messageId) { - return title; - } - - return translatedMessage; + return translateStandardLabel({ + sourceValue: title, + isStandardApp, + applicationCatalog, + i18nInstance, + }); }; diff --git a/packages/twenty-server/src/engine/metadata-modules/view-field-group/resolvers/view-field-group.resolver.ts b/packages/twenty-server/src/engine/metadata-modules/view-field-group/resolvers/view-field-group.resolver.ts index df3f47f262..64be19d1ed 100644 --- a/packages/twenty-server/src/engine/metadata-modules/view-field-group/resolvers/view-field-group.resolver.ts +++ b/packages/twenty-server/src/engine/metadata-modules/view-field-group/resolvers/view-field-group.resolver.ts @@ -11,7 +11,6 @@ import { import { isArray } from '@sniptt/guards'; import { MetadataResolver } from 'src/engine/api/graphql/graphql-config/decorators/metadata-resolver.decorator'; -import { ApplicationService } from 'src/engine/core-modules/application/application.service'; import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; import { I18nService } from 'src/engine/core-modules/i18n/i18n.service'; import { type I18nContext } from 'src/engine/core-modules/i18n/types/i18n-context.type'; @@ -42,28 +41,35 @@ export class ViewFieldGroupResolver { private readonly viewFieldGroupService: ViewFieldGroupService, private readonly fieldsWidgetUpsertService: FieldsWidgetUpsertService, private readonly i18nService: I18nService, - private readonly applicationService: ApplicationService, ) {} @ResolveField(() => String) async name( @Parent() viewFieldGroup: ViewFieldGroupDTO, - @Context() context: I18nContext, + @Context() context: { loaders: IDataloaders } & I18nContext, @AuthWorkspace() workspace: WorkspaceEntity, ): Promise { const i18n = this.i18nService.getI18nInstance(context.req.locale); - const { twentyStandardFlatApplication } = - await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow( - { workspace }, - ); + const standardApplicationId = + await context.loaders.standardApplicationIdLoader.load({ + workspaceId: workspace.id, + }); + + const applicationCatalog = + await context.loaders.applicationTranslationCatalogLoader.load({ + applicationId: viewFieldGroup.applicationId, + workspaceId: workspace.id, + locale: context.req.locale, + }); return resolveViewFieldGroupName({ name: viewFieldGroup.name, applicationId: viewFieldGroup.applicationId, - twentyStandardApplicationId: twentyStandardFlatApplication.id, + twentyStandardApplicationId: standardApplicationId, overrides: viewFieldGroup.overrides, i18nInstance: i18n, + applicationCatalog, }); } diff --git a/packages/twenty-server/src/engine/metadata-modules/view-field-group/utils/resolve-view-field-group-name.util.ts b/packages/twenty-server/src/engine/metadata-modules/view-field-group/utils/resolve-view-field-group-name.util.ts index 6d28a610c0..e6912f9e7e 100644 --- a/packages/twenty-server/src/engine/metadata-modules/view-field-group/utils/resolve-view-field-group-name.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/view-field-group/utils/resolve-view-field-group-name.util.ts @@ -2,7 +2,7 @@ import { type I18n } from '@lingui/core'; import { isDefined } from 'twenty-shared/utils'; -import { generateMessageId } from 'src/engine/core-modules/i18n/utils/generateMessageId'; +import { translateStandardLabel } from 'src/engine/core-modules/i18n/utils/translate-standard-label.util'; import { type ViewFieldGroupOverrides } from 'src/engine/metadata-modules/view-field-group/entities/view-field-group.entity'; export const resolveViewFieldGroupName = ({ @@ -11,27 +11,25 @@ export const resolveViewFieldGroupName = ({ twentyStandardApplicationId, overrides, i18nInstance, + applicationCatalog, }: { name: string; applicationId: string; twentyStandardApplicationId: string; overrides?: ViewFieldGroupOverrides | null; i18nInstance: I18n; + applicationCatalog?: Record; }): string => { - if (applicationId !== twentyStandardApplicationId) { - return name; - } + const isStandardApp = applicationId === twentyStandardApplicationId; if (isDefined(overrides?.name)) { return name; } - const messageId = generateMessageId(name); - const translatedMessage = i18nInstance._(messageId); - - if (translatedMessage === messageId) { - return name; - } - - return translatedMessage; + return translateStandardLabel({ + sourceValue: name, + isStandardApp, + applicationCatalog, + i18nInstance, + }); }; diff --git a/packages/twenty-server/src/engine/metadata-modules/view/resolvers/view.resolver.ts b/packages/twenty-server/src/engine/metadata-modules/view/resolvers/view.resolver.ts index ad07ecf757..5643cc77aa 100644 --- a/packages/twenty-server/src/engine/metadata-modules/view/resolvers/view.resolver.ts +++ b/packages/twenty-server/src/engine/metadata-modules/view/resolvers/view.resolver.ts @@ -72,6 +72,14 @@ export class ViewResolver { await context.loaders.standardApplicationIdLoader.load({ workspaceId: workspace.id, }); + const isStandardApp = + objectMetadata.applicationId === standardApplicationId; + const applicationCatalog = + await context.loaders.applicationTranslationCatalogLoader.load({ + applicationId: objectMetadata.applicationId, + workspaceId: workspace.id, + locale: context.req.locale, + }); const translatedObjectLabel = resolveObjectMetadataStandardOverride( { labelPlural: objectMetadata.labelPlural, @@ -83,7 +91,8 @@ export class ViewResolver { 'labelPlural', context.req.locale, i18n, - objectMetadata.applicationId === standardApplicationId, + isStandardApp, + applicationCatalog, ); return this.viewService.processViewNameWithTemplate(