From 36d75826876d50bfb0cc2d7166efd24d9b9ed230 Mon Sep 17 00:00:00 2001 From: Eswaraiahsapram Date: Mon, 3 Nov 2025 16:21:27 +0530 Subject: [PATCH] fix: add missing i18n support for catalog react plugin Signed-off-by: Eswaraiahsapram --- .changeset/eleven-lights-taste.md | 5 ++ plugins/catalog-react/report-alpha.api.md | 25 +++++++++ .../CardActionComponents/EmailCardAction.tsx | 2 +- .../EntityCardActions.tsx | 2 +- .../InspectEntityDialog.tsx | 42 ++++++++------- .../components/AncestryPage.tsx | 14 ++--- .../components/ColocatedPage.tsx | 10 +++- .../components/OverviewPage.tsx | 28 +++++++--- .../MissingAnnotationEmptyState.tsx | 51 +++++++++++-------- plugins/catalog-react/src/translation.ts | 40 +++++++++++++++ 10 files changed, 160 insertions(+), 59 deletions(-) create mode 100644 .changeset/eleven-lights-taste.md diff --git a/.changeset/eleven-lights-taste.md b/.changeset/eleven-lights-taste.md new file mode 100644 index 0000000000..e575b909e6 --- /dev/null +++ b/.changeset/eleven-lights-taste.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-react': patch +--- + +Added missing i18n diff --git a/plugins/catalog-react/report-alpha.api.md b/plugins/catalog-react/report-alpha.api.md index cf28d80c11..ef101c9e1b 100644 --- a/plugins/catalog-react/report-alpha.api.md +++ b/plugins/catalog-react/report-alpha.api.md @@ -45,8 +45,10 @@ export const catalogReactTranslationRef: TranslationRef< readonly 'entityProcessingStatusPicker.title': 'Processing Status'; readonly 'entityTagPicker.title': 'Tags'; readonly 'entityPeekAheadPopover.title': 'Drill into the entity to see all of the tags.'; + readonly 'entityPeekAheadPopover.entityCardActionsAriaLabel': 'Show'; readonly 'entityPeekAheadPopover.entityCardActionsTitle': 'Show details'; readonly 'entityPeekAheadPopover.emailCardAction.title': 'Email {{email}}'; + readonly 'entityPeekAheadPopover.emailCardAction.ariaLabel': 'Email'; readonly 'entityPeekAheadPopover.emailCardAction.subTitle': 'mailto {{email}}'; readonly 'entitySearchBar.placeholder': 'Search'; readonly 'entityTypePicker.title': 'Type'; @@ -56,16 +58,34 @@ export const catalogReactTranslationRef: TranslationRef< readonly 'favoriteEntity.removeFromFavorites': 'Remove from favorites'; readonly 'inspectEntityDialog.title': 'Entity Inspector'; readonly 'inspectEntityDialog.closeButtonTitle': 'Close'; + readonly 'inspectEntityDialog.tabsAriaLabel': 'Inspector options'; readonly 'inspectEntityDialog.ancestryPage.title': 'Ancestry'; + readonly 'inspectEntityDialog.ancestryPage.descriptionPart1': 'This is the ancestry of entities above the current one - as in, the chain(s) of entities down to the current one, where'; + readonly 'inspectEntityDialog.ancestryPage.processorsLink': 'processors emitted'; + readonly 'inspectEntityDialog.ancestryPage.descriptionPart2': 'child entities that ultimately led to the current one existing. Note that this is a completely different mechanism from relations.'; readonly 'inspectEntityDialog.colocatedPage.title': 'Colocated'; readonly 'inspectEntityDialog.colocatedPage.description': 'These are the entities that are colocated with this entity - as in, they originated from the same data source (e.g. came from the same YAML file), or from the same origin (e.g. the originally registered URL).'; readonly 'inspectEntityDialog.colocatedPage.alertNoLocation': 'Entity had no location information.'; readonly 'inspectEntityDialog.colocatedPage.alertNoEntity': 'There were no other entities on this location.'; + readonly 'inspectEntityDialog.colocatedPage.locationHeader': 'At the same location'; + readonly 'inspectEntityDialog.colocatedPage.originHeader': 'At the same origin'; readonly 'inspectEntityDialog.jsonPage.title': 'Entity as JSON'; readonly 'inspectEntityDialog.jsonPage.description': 'This is the raw entity data as received from the catalog, on JSON form.'; readonly 'inspectEntityDialog.overviewPage.title': 'Overview'; + readonly 'inspectEntityDialog.overviewPage.labels': 'Labels'; + readonly 'inspectEntityDialog.overviewPage.annotations': 'Annotations'; + readonly 'inspectEntityDialog.overviewPage.tags': 'Tags'; + readonly 'inspectEntityDialog.overviewPage.relationTitle': 'Relations'; + readonly 'inspectEntityDialog.overviewPage.statusTitle': 'Status'; + readonly 'inspectEntityDialog.overviewPage.identityTitle': 'Identity'; + readonly 'inspectEntityDialog.overviewPage.metadataTitle': 'Metadata'; readonly 'inspectEntityDialog.yamlPage.title': 'Entity as YAML'; readonly 'inspectEntityDialog.yamlPage.description': 'This is the raw entity data as received from the catalog, on YAML form.'; + readonly 'inspectEntityDialog.tabNames.json': 'Raw JSON'; + readonly 'inspectEntityDialog.tabNames.yaml': 'Raw YAML'; + readonly 'inspectEntityDialog.tabNames.overview': 'Overview'; + readonly 'inspectEntityDialog.tabNames.ancestry': 'Ancestry'; + readonly 'inspectEntityDialog.tabNames.colocated': 'Colocated'; readonly 'unregisterEntityDialog.title': 'Are you sure you want to unregister this entity?'; readonly 'unregisterEntityDialog.cancelButtonTitle': 'Cancel'; readonly 'unregisterEntityDialog.deleteButtonTitle': 'Delete Entity'; @@ -98,6 +118,11 @@ export const catalogReactTranslationRef: TranslationRef< readonly 'entityTableColumnTitle.lifecycle': 'Lifecycle'; readonly 'entityTableColumnTitle.owner': 'Owner'; readonly 'entityTableColumnTitle.targets': 'Targets'; + readonly 'missingAnnotationEmptyState.title': 'Missing Annotation'; + readonly 'missingAnnotationEmptyState.readMore': 'Read more'; + readonly 'missingAnnotationEmptyState.annotationYaml': 'Add the annotation to your {{entityKind}} YAML as shown in the highlighted example below:'; + readonly 'missingAnnotationEmptyState.generateDescription.multiple': 'The annotations {{annotations}} are missing. You need to add the annotations to your {{entityKind}} if you want to enable this tool.'; + readonly 'missingAnnotationEmptyState.generateDescription.single': 'The annotation {{annotations}} is missing. You need to add the annotation to your {{entityKind}} if you want to enable this tool.'; } >; diff --git a/plugins/catalog-react/src/components/EntityPeekAheadPopover/CardActionComponents/EmailCardAction.tsx b/plugins/catalog-react/src/components/EntityPeekAheadPopover/CardActionComponents/EmailCardAction.tsx index 8c9c0cf2bf..24913bf5df 100644 --- a/plugins/catalog-react/src/components/EntityPeekAheadPopover/CardActionComponents/EmailCardAction.tsx +++ b/plugins/catalog-react/src/components/EntityPeekAheadPopover/CardActionComponents/EmailCardAction.tsx @@ -30,7 +30,7 @@ export const EmailCardAction = (props: { email: string }) => { return ( { return ( diff --git a/plugins/catalog-react/src/components/InspectEntityDialog/InspectEntityDialog.tsx b/plugins/catalog-react/src/components/InspectEntityDialog/InspectEntityDialog.tsx index 1d8a351701..8ba81dcb41 100644 --- a/plugins/catalog-react/src/components/InspectEntityDialog/InspectEntityDialog.tsx +++ b/plugins/catalog-react/src/components/InspectEntityDialog/InspectEntityDialog.tsx @@ -24,7 +24,7 @@ import DialogTitle from '@material-ui/core/DialogTitle'; import Tab from '@material-ui/core/Tab'; import Tabs from '@material-ui/core/Tabs'; import { makeStyles } from '@material-ui/core/styles'; -import { ComponentProps, useEffect, useState, ReactNode } from 'react'; +import { ComponentProps, useEffect, useState, ReactNode, useMemo } from 'react'; import { AncestryPage } from './components/AncestryPage'; import { ColocatedPage } from './components/ColocatedPage'; import { JsonPage } from './components/JsonPage'; @@ -85,18 +85,12 @@ function a11yProps(index: number) { }; } -const tabNames: Record< +type TabKey = 'overview' | 'ancestry' | 'colocated' | 'json' | 'yaml'; + +type TabNames = Record< NonNullable['initialTab']>, string -> = { - overview: 'Overview', - ancestry: 'Ancestry', - colocated: 'Colocated', - json: 'Raw JSON', - yaml: 'Raw YAML', -} as const; - -const tabs = Object.keys(tabNames) as Array; +>; /** * A dialog that lets users inspect the low level details of their entities. @@ -106,20 +100,33 @@ const tabs = Object.keys(tabNames) as Array; export function InspectEntityDialog(props: { open: boolean; entity: Entity; - initialTab?: 'overview' | 'ancestry' | 'colocated' | 'json' | 'yaml'; + initialTab?: TabKey; onClose: () => void; onSelect?: (tab: string) => void; }) { const classes = useStyles(); + const { t } = useTranslationRef(catalogReactTranslationRef); + + const tabNames: TabNames = useMemo( + () => ({ + overview: t('inspectEntityDialog.tabNames.overview'), + ancestry: t('inspectEntityDialog.tabNames.ancestry'), + colocated: t('inspectEntityDialog.tabNames.colocated'), + json: t('inspectEntityDialog.tabNames.json'), + yaml: t('inspectEntityDialog.tabNames.yaml'), + }), + [t], + ); + + const tabs = Object.keys(tabNames) as TabKey[]; const [activeTab, setActiveTab] = useState( getTabIndex(tabs, props.initialTab), ); - const { t } = useTranslationRef(catalogReactTranslationRef); useEffect(() => { getTabIndex(tabs, props.initialTab); - }, [props.open, props.initialTab]); + }, [props.open, props.initialTab, tabs]); if (!props.entity) { return null; @@ -147,7 +154,7 @@ export function InspectEntityDialog(props: { setActiveTab(tabIndex); props.onSelect?.(tabs[tabIndex]); }} - aria-label="Inspector options" + aria-label={t('inspectEntityDialog.tabsAriaLabel')} className={classes.tabs} > {tabs.map((tab, index) => ( @@ -181,9 +188,6 @@ export function InspectEntityDialog(props: { ); } -function getTabIndex( - allTabs: string[], - initialTab: keyof typeof tabNames | undefined, -) { +function getTabIndex(allTabs: string[], initialTab: TabKey | undefined) { return initialTab ? allTabs.indexOf(initialTab) : 0; } diff --git a/plugins/catalog-react/src/components/InspectEntityDialog/components/AncestryPage.tsx b/plugins/catalog-react/src/components/InspectEntityDialog/components/AncestryPage.tsx index c81009e3f5..771eb7b279 100644 --- a/plugins/catalog-react/src/components/InspectEntityDialog/components/AncestryPage.tsx +++ b/plugins/catalog-react/src/components/InspectEntityDialog/components/AncestryPage.tsx @@ -213,13 +213,13 @@ export function AncestryPage(props: { entity: Entity }) { {t('inspectEntityDialog.ancestryPage.title')} - This is the ancestry of entities above the current one - as in, the - chain(s) of entities down to the current one, where{' '} - - processors emitted - {' '} - child entities that ultimately led to the current one existing. Note - that this is a completely different mechanism from relations. + {t('inspectEntityDialog.ancestryPage.description', { + processorsLink: ( + + {t('inspectEntityDialog.ancestryPage.processorsLink')} + + ), + })} 0 && ( )} {atOrigin.length > 0 && ( )} diff --git a/plugins/catalog-react/src/components/InspectEntityDialog/components/OverviewPage.tsx b/plugins/catalog-react/src/components/InspectEntityDialog/components/OverviewPage.tsx index b9085ecb60..4feabf9c38 100644 --- a/plugins/catalog-react/src/components/InspectEntityDialog/components/OverviewPage.tsx +++ b/plugins/catalog-react/src/components/InspectEntityDialog/components/OverviewPage.tsx @@ -69,7 +69,7 @@ export function OverviewPage(props: { entity: AlphaEntity }) { {t('inspectEntityDialog.overviewPage.title')}
- + @@ -110,13 +110,13 @@ export function OverviewPage(props: { entity: AlphaEntity }) { - + {!!Object.keys(metadata.annotations || {}).length && ( - Annotations + {t('inspectEntityDialog.overviewPage.annotations')} } @@ -127,14 +127,28 @@ export function OverviewPage(props: { entity: AlphaEntity }) { )} {!!Object.keys(metadata.labels || {}).length && ( - Labels}> + + {t('inspectEntityDialog.overviewPage.labels')} + + } + > {Object.entries(metadata.labels!).map(entry => ( ))} )} {!!metadata.tags?.length && ( - Tags}> + + {t('inspectEntityDialog.overviewPage.tags')} + + } + > {metadata.tags.map((tag, index) => ( @@ -147,7 +161,7 @@ export function OverviewPage(props: { entity: AlphaEntity }) { {!!relations.length && ( {Object.entries(groupedRelations).map( @@ -172,7 +186,7 @@ export function OverviewPage(props: { entity: AlphaEntity }) { {!!status.items?.length && ( {status.items.map((item, index) => ( diff --git a/plugins/catalog-react/src/components/MissingAnnotationEmptyState/MissingAnnotationEmptyState.tsx b/plugins/catalog-react/src/components/MissingAnnotationEmptyState/MissingAnnotationEmptyState.tsx index 4472ecc8a2..cf6add2512 100644 --- a/plugins/catalog-react/src/components/MissingAnnotationEmptyState/MissingAnnotationEmptyState.tsx +++ b/plugins/catalog-react/src/components/MissingAnnotationEmptyState/MissingAnnotationEmptyState.tsx @@ -20,7 +20,12 @@ import { makeStyles } from '@material-ui/core/styles'; import Typography from '@material-ui/core/Typography'; import { CodeSnippet, Link, EmptyState } from '@backstage/core-components'; import { Entity } from '@backstage/catalog-model'; +import { + TranslationFunction, + useTranslationRef, +} from '@backstage/core-plugin-api/alpha'; import { useEntity } from '../../hooks'; +import { catalogReactTranslationRef } from '../../translation'; /** @public */ export type MissingAnnotationEmptyStateClassKey = 'code'; @@ -68,23 +73,24 @@ spec: }; } -function generateDescription(annotations: string[], entityKind = 'Component') { - const isSingular = annotations.length <= 1; - return ( - <> - The {isSingular ? 'annotation' : 'annotations'}{' '} - {annotations - .map(ann => {ann}) - .reduce((prev, curr) => ( - <> - {prev}, {curr} - - ))}{' '} - {isSingular ? 'is' : 'are'} missing. You need to add the{' '} - {isSingular ? 'annotation' : 'annotations'} to your {entityKind} if you - want to enable this tool. - - ); +function generateDescription( + annotations: string[], + entityKind = 'Component', + t: TranslationFunction, +) { + const annotationList = annotations + .map(ann => {ann}) + .reduce((prev, curr) => ( + <> + {prev}, {curr} + + )); + + return t('missingAnnotationEmptyState.generateDescription', { + count: annotations.length, + entityKind, + annotations: annotationList, + }); } /** @@ -95,6 +101,8 @@ export function MissingAnnotationEmptyState(props: { annotation: string | string[]; readMoreUrl?: string; }) { + const { t } = useTranslationRef(catalogReactTranslationRef); + let entity: Entity | undefined; try { const entityContext = useEntity(); @@ -115,13 +123,12 @@ export function MissingAnnotationEmptyState(props: { return ( - Add the annotation to your {entityKind} YAML as shown in the - highlighted example below: + {t('missingAnnotationEmptyState.annotationYaml', { entityKind })} } diff --git a/plugins/catalog-react/src/translation.ts b/plugins/catalog-react/src/translation.ts index 6e831886cd..4411f7e004 100644 --- a/plugins/catalog-react/src/translation.ts +++ b/plugins/catalog-react/src/translation.ts @@ -48,7 +48,9 @@ export const catalogReactTranslationRef = createTranslationRef({ emailCardAction: { title: 'Email {{email}}', subTitle: 'mailto {{email}}', + ariaLabel: 'Email', }, + entityCardActionsAriaLabel: 'Show', entityCardActionsTitle: 'Show details', }, entitySearchBar: { @@ -68,6 +70,9 @@ export const catalogReactTranslationRef = createTranslationRef({ closeButtonTitle: 'Close', ancestryPage: { title: 'Ancestry', + description: + 'This is the ancestry of entities above the current one - as in, the chain(s) of entities down to the current one, where {{processorsLink}} child entities that ultimately led to the current one existing. Note that this is a completely different mechanism from relations.', + processorsLink: 'processors emitted', }, colocatedPage: { title: 'Colocated', @@ -75,6 +80,8 @@ export const catalogReactTranslationRef = createTranslationRef({ 'These are the entities that are colocated with this entity - as in, they originated from the same data source (e.g. came from the same YAML file), or from the same origin (e.g. the originally registered URL).', alertNoLocation: 'Entity had no location information.', alertNoEntity: 'There were no other entities on this location.', + locationHeader: 'At the same location', + originHeader: 'At the same origin', }, jsonPage: { title: 'Entity as JSON', @@ -83,12 +90,35 @@ export const catalogReactTranslationRef = createTranslationRef({ }, overviewPage: { title: 'Overview', + relation: { + title: 'Relations', + }, + status: { + title: 'Status', + }, + identity: { + title: 'Identity', + }, + metadata: { + title: 'Metadata', + }, + annotations: 'Annotations', + labels: 'Labels', + tags: 'Tags', }, yamlPage: { title: 'Entity as YAML', description: 'This is the raw entity data as received from the catalog, on YAML form.', }, + tabNames: { + overview: 'Overview', + ancestry: 'Ancestry', + colocated: 'Colocated', + json: 'Raw JSON', + yaml: 'Raw YAML', + }, + tabsAriaLabel: 'Inspector options', }, unregisterEntityDialog: { title: 'Are you sure you want to unregister this entity?', @@ -138,5 +168,15 @@ export const catalogReactTranslationRef = createTranslationRef({ label: 'Label', domain: 'Domain', }, + missingAnnotationEmptyState: { + title: 'Missing Annotation', + readMore: 'Read more', + annotationYaml: + 'Add the annotation to your {{entityKind}} YAML as shown in the highlighted example below:', + generateDescription_one: + 'The annotation {{annotations}} is missing. You need to add the annotation to your {{entityKind}} if you want to enable this tool.', + generateDescription_other: + 'The annotations {{annotations}} are missing. You need to add the annotations to your {{entityKind}} if you want to enable this tool.', + }, }, });