feat: add i18n to plugin-catalog

Signed-off-by: mario ma <mario.ma.node@gmail.com>
This commit is contained in:
mario ma
2024-03-04 20:07:29 +08:00
parent ea1809c0ac
commit c324de8ccd
27 changed files with 314 additions and 86 deletions
@@ -64,6 +64,8 @@ import { catalogEntityRefreshPermission } from '@backstage/plugin-catalog-common
import { useSourceTemplateCompoundEntityRef } from './hooks';
import { taskCreatePermission } from '@backstage/plugin-scaffolder-common/alpha';
import { usePermission } from '@backstage/plugin-permission-react';
import { catalogTranslationRef } from '../../translation';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
const TECHDOCS_ANNOTATION = 'backstage.io/techdocs-ref';
@@ -116,6 +118,7 @@ export function AboutCard(props: AboutCardProps) {
const { allowed: canRefresh } = useEntityPermission(
catalogEntityRefreshPermission,
);
const { t } = useTranslationRef(catalogTranslationRef);
const { allowed: canCreateTemplateTask } = usePermission({
permission: taskCreatePermission,
@@ -142,13 +145,13 @@ export function AboutCard(props: AboutCardProps) {
}
const viewInSource: IconLinkVerticalProps = {
label: 'View Source',
label: t('aboutCard.viewSource'),
disabled: !entitySourceLocation,
icon: <ScmIntegrationIcon type={entitySourceLocation?.integrationType} />,
href: entitySourceLocation?.locationTargetUrl,
};
const viewInTechDocs: IconLinkVerticalProps = {
label: 'View TechDocs',
label: t('aboutCard.viewTechdocs'),
disabled:
!(
entity.metadata.annotations?.[TECHDOCS_ANNOTATION] ||
@@ -176,7 +179,7 @@ export function AboutCard(props: AboutCardProps) {
const Icon = app.getSystemIcon('scaffolder') ?? CreateComponentIcon;
const launchTemplate: IconLinkVerticalProps = {
label: 'Launch Template',
label: t('aboutCard.launchTemplate'),
icon: <Icon />,
disabled: !templateRoute || !canCreateTemplateTask,
href:
@@ -212,25 +215,25 @@ export function AboutCard(props: AboutCardProps) {
try {
await catalogApi.refreshEntity(stringifyEntityRef(entity));
alertApi.post({
message: 'Refresh scheduled',
message: t('aboutCard.refreshScheduledMessage'),
severity: 'info',
display: 'transient',
});
} catch (e) {
errorApi.post(e);
}
}, [catalogApi, alertApi, errorApi, entity]);
}, [catalogApi, entity, alertApi, t, errorApi]);
return (
<Card className={cardClass}>
<CardHeader
title="About"
title={t('aboutCard.title')}
action={
<>
{allowRefresh && canRefresh && (
<IconButton
aria-label="Refresh"
title="Schedule entity refresh"
title={t('aboutCard.refreshButtonTitle')}
onClick={refreshEntity}
>
<CachedIcon />
@@ -240,7 +243,7 @@ export function AboutCard(props: AboutCardProps) {
component={Link}
aria-label="Edit"
disabled={!entityMetadataEditUrl}
title="Edit Metadata"
title={t('aboutCard.editButtonTitle')}
to={entityMetadataEditUrl ?? '#'}
>
<EditIcon />
@@ -248,7 +251,7 @@ export function AboutCard(props: AboutCardProps) {
{sourceTemplateRef && templateRoute && (
<IconButton
component={Link}
title="Create something similar"
title={t('aboutCard.createSimilarButtonTitle')}
to={templateRoute({
namespace: sourceTemplateRef.namespace,
templateName: sourceTemplateRef.name,
@@ -32,6 +32,8 @@ import { MarkdownContent } from '@backstage/core-components';
import React from 'react';
import { AboutField } from './AboutField';
import { LinksGridList } from '../EntityLinksCard/LinksGridList';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
import { catalogTranslationRef } from '../../translation';
const useStyles = makeStyles({
description: {
@@ -76,6 +78,8 @@ function getLocationTargetHref(
export function AboutContent(props: AboutContentProps) {
const { entity } = props;
const classes = useStyles();
const { t } = useTranslationRef(catalogTranslationRef);
const isSystem = entity.kind.toLocaleLowerCase('en-US') === 'system';
const isResource = entity.kind.toLocaleLowerCase('en-US') === 'resource';
const isComponent = entity.kind.toLocaleLowerCase('en-US') === 'component';
@@ -113,15 +117,21 @@ export function AboutContent(props: AboutContentProps) {
return (
<Grid container>
<AboutField label="Description" gridSizes={{ xs: 12 }}>
<AboutField
label={t('aboutCard.descriptionField.label')}
gridSizes={{ xs: 12 }}
>
<MarkdownContent
className={classes.description}
content={entity?.metadata?.description || 'No description'}
content={
entity?.metadata?.description ||
t('aboutCard.descriptionField.value')
}
/>
</AboutField>
<AboutField
label="Owner"
value="No Owner"
label={t('aboutCard.ownerField.label')}
value={t('aboutCard.ownerField.value')}
gridSizes={{ xs: 12, sm: 6, lg: 4 }}
>
{ownedByRelations.length > 0 && (
@@ -130,8 +140,8 @@ export function AboutContent(props: AboutContentProps) {
</AboutField>
{(isSystem || partOfDomainRelations.length > 0) && (
<AboutField
label="Domain"
value="No Domain"
label={t('aboutCard.domainField.label')}
value={t('aboutCard.domainField.value')}
gridSizes={{ xs: 12, sm: 6, lg: 4 }}
>
{partOfDomainRelations.length > 0 && (
@@ -147,8 +157,8 @@ export function AboutContent(props: AboutContentProps) {
isResource ||
partOfSystemRelations.length > 0) && (
<AboutField
label="System"
value="No System"
label={t('aboutCard.systemField.label')}
value={t('aboutCard.systemField.value')}
gridSizes={{ xs: 12, sm: 6, lg: 4 }}
>
{partOfSystemRelations.length > 0 && (
@@ -161,8 +171,8 @@ export function AboutContent(props: AboutContentProps) {
)}
{isComponent && partOfComponentRelations.length > 0 && (
<AboutField
label="Parent Component"
value="No Parent Component"
label={t('aboutCard.parentComponentField.label')}
value={t('aboutCard.parentComponentField.value')}
gridSizes={{ xs: 12, sm: 6, lg: 4 }}
>
<EntityRefLinks
@@ -179,7 +189,7 @@ export function AboutContent(props: AboutContentProps) {
isLocation ||
typeof entity?.spec?.type === 'string') && (
<AboutField
label="Type"
label={t('aboutCard.typeField.label')}
value={entity?.spec?.type as string}
gridSizes={{ xs: 12, sm: 6, lg: 4 }}
/>
@@ -188,22 +198,25 @@ export function AboutContent(props: AboutContentProps) {
isComponent ||
typeof entity?.spec?.lifecycle === 'string') && (
<AboutField
label="Lifecycle"
label={t('aboutCard.lifecycleField.label')}
value={entity?.spec?.lifecycle as string}
gridSizes={{ xs: 12, sm: 6, lg: 4 }}
/>
)}
<AboutField
label="Tags"
value="No Tags"
label={t('aboutCard.tagsField.label')}
value={t('aboutCard.tagsField.value')}
gridSizes={{ xs: 12, sm: 6, lg: 4 }}
>
{(entity?.metadata?.tags || []).map(t => (
<Chip key={t} size="small" label={t} />
{(entity?.metadata?.tags || []).map(tag => (
<Chip key={tag} size="small" label={tag} />
))}
</AboutField>
{isLocation && (entity?.spec?.targets || entity?.spec?.target) && (
<AboutField label="Targets" gridSizes={{ xs: 12 }}>
<AboutField
label={t('aboutCard.targetsField.label')}
gridSizes={{ xs: 12 }}
>
<LinksGridList
cols={1}
items={((entity.spec.targets as JsonArray) || [entity.spec.target])
@@ -69,7 +69,7 @@ export function BaseCatalogPage(props: BaseCatalogPageProps) {
to={createComponentLink && createComponentLink()}
/>
)}
<SupportButton>All your software catalog entities</SupportButton>
<SupportButton>{t('indexPage.supportButtonContent')}</SupportButton>
</ContentHeader>
<EntityListProvider pagination={pagination}>
<CatalogFilterLayout>
@@ -27,6 +27,8 @@ import {
ResultHighlight,
} from '@backstage/plugin-search-common';
import { HighlightedSearchResultText } from '@backstage/plugin-search-react';
import { catalogTranslationRef } from '../../translation';
import { useTranslationRef } from '@backstage/frontend-plugin-api';
const useStyles = makeStyles(
{
@@ -66,6 +68,7 @@ export function CatalogSearchResultListItem(
const highlight = props.highlight as ResultHighlight;
const classes = useStyles();
const { t } = useTranslationRef(catalogTranslationRef);
if (!result) return null;
@@ -121,10 +124,16 @@ export function CatalogSearchResultListItem(
{result.kind && <Chip label={`Kind: ${result.kind}`} size="small" />}
{result.type && <Chip label={`Type: ${result.type}`} size="small" />}
{result.lifecycle && (
<Chip label={`Lifecycle: ${result.lifecycle}`} size="small" />
<Chip
label={`${t('searchResultItem.lifecycle')}: ${result.lifecycle}`}
size="small"
/>
)}
{result.owner && (
<Chip label={`Owner: ${result.owner}`} size="small" />
<Chip
label={`${t('searchResultItem.Owner')}: ${result.owner}`}
size="small"
/>
)}
</Box>
</div>
@@ -48,6 +48,8 @@ import { columnFactories } from './columns';
import { CatalogTableColumnsFunc, CatalogTableRow } from './types';
import { PaginatedCatalogTable } from './PaginatedCatalogTable';
import { defaultCatalogTableColumnsFunc } from './defaultCatalogTableColumnsFunc';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
import { catalogTranslationRef } from '../../translation';
/**
* Props for {@link CatalogTable}.
@@ -96,13 +98,14 @@ export const CatalogTable = (props: CatalogTableProps) => {
typeof columns === 'function' ? columns(entityListContext) : columns,
[columns, entityListContext],
);
const { t } = useTranslationRef(catalogTranslationRef);
if (error) {
return (
<div>
<WarningPanel
severity="error"
title="Could not fetch catalog entities."
title={t('catalogTable.warningPanelTitle')}
>
<CodeSnippet language="text" text={error.toString()} />
</WarningPanel>
@@ -113,7 +116,7 @@ export const CatalogTable = (props: CatalogTableProps) => {
const defaultActions: TableProps<CatalogTableRow>['actions'] = [
({ entity }) => {
const url = entity.metadata.annotations?.[ANNOTATION_VIEW_URL];
const title = 'View';
const title = t('catalogTable.viewActionTitle');
return {
icon: () => (
@@ -132,7 +135,7 @@ export const CatalogTable = (props: CatalogTableProps) => {
},
({ entity }) => {
const url = entity.metadata.annotations?.[ANNOTATION_EDIT_URL];
const title = 'Edit';
const title = t('catalogTable.editActionTitle');
return {
icon: () => (
@@ -151,7 +154,9 @@ export const CatalogTable = (props: CatalogTableProps) => {
},
({ entity }) => {
const isStarred = isStarredEntity(entity);
const title = isStarred ? 'Remove from favorites' : 'Add to favorites';
const title = isStarred
? t('catalogTable.unStarActionTitle')
: t('catalogTable.starActionTitle');
return {
cellStyle: { paddingLeft: '1em' },
@@ -23,6 +23,8 @@ import {
componentEntityHelpLink,
RelatedEntitiesCard,
} from '../RelatedEntitiesCard';
import { catalogTranslationRef } from '../../translation';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
/** @public */
export interface DependencyOfComponentsCardProps {
@@ -33,7 +35,11 @@ export interface DependencyOfComponentsCardProps {
export function DependencyOfComponentsCard(
props: DependencyOfComponentsCardProps,
) {
const { variant = 'gridItem', title = 'Dependency of components' } = props;
const { t } = useTranslationRef(catalogTranslationRef);
const {
variant = 'gridItem',
title = t('dependencyOfComponentsCard.title'),
} = props;
return (
<RelatedEntitiesCard
variant={variant}
@@ -41,7 +47,7 @@ export function DependencyOfComponentsCard(
entityKind="Component"
relationType={RELATION_DEPENDENCY_OF}
columns={componentEntityColumns}
emptyMessage="No component depends on this component"
emptyMessage={t('dependencyOfComponentsCard.emptyMessage')}
emptyHelpLink={componentEntityHelpLink}
asRenderableEntities={asComponentEntities}
/>
@@ -27,6 +27,8 @@ import {
componentEntityHelpLink,
RelatedEntitiesCard,
} from '../RelatedEntitiesCard';
import { catalogTranslationRef } from '../../translation';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
/** @public */
export interface DependsOnComponentsCardProps {
@@ -37,9 +39,10 @@ export interface DependsOnComponentsCardProps {
}
export function DependsOnComponentsCard(props: DependsOnComponentsCardProps) {
const { t } = useTranslationRef(catalogTranslationRef);
const {
variant = 'gridItem',
title = 'Depends on components',
title = t('dependsOnComponentsCard.title'),
columns = componentEntityColumns,
tableOptions = {},
} = props;
@@ -50,7 +53,7 @@ export function DependsOnComponentsCard(props: DependsOnComponentsCardProps) {
entityKind="Component"
relationType={RELATION_DEPENDS_ON}
columns={columns}
emptyMessage="No component is a dependency of this component"
emptyMessage={t('dependsOnComponentsCard.emptyMessage')}
emptyHelpLink={componentEntityHelpLink}
asRenderableEntities={asComponentEntities}
tableOptions={tableOptions}
@@ -27,6 +27,8 @@ import {
RelatedEntitiesCard,
resourceEntityColumns,
} from '../RelatedEntitiesCard';
import { catalogTranslationRef } from '../../translation';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
/** @public */
export interface DependsOnResourcesCardProps {
@@ -37,9 +39,10 @@ export interface DependsOnResourcesCardProps {
}
export function DependsOnResourcesCard(props: DependsOnResourcesCardProps) {
const { t } = useTranslationRef(catalogTranslationRef);
const {
variant = 'gridItem',
title = 'Depends on resources',
title = t('dependsOnResourcesCard.title'),
columns = resourceEntityColumns,
tableOptions = {},
} = props;
@@ -50,7 +53,7 @@ export function DependsOnResourcesCard(props: DependsOnResourcesCardProps) {
entityKind="Resource"
relationType={RELATION_DEPENDS_ON}
columns={columns}
emptyMessage="No resource is a dependency of this component"
emptyMessage={t('dependsOnResourcesCard.emptyMessage')}
emptyHelpLink={componentEntityHelpLink}
asRenderableEntities={asResourceEntities}
tableOptions={tableOptions}
@@ -33,6 +33,8 @@ import { catalogEntityDeletePermission } from '@backstage/plugin-catalog-common/
import { UnregisterEntity, UnregisterEntityOptions } from './UnregisterEntity';
import { useApi, alertApiRef } from '@backstage/core-plugin-api';
import useCopyToClipboard from 'react-use/esm/useCopyToClipboard';
import { catalogTranslationRef } from '../../translation';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
/** @public */
export type EntityContextMenuClassKey = 'button';
@@ -70,6 +72,7 @@ export function EntityContextMenu(props: EntityContextMenuProps) {
onUnregisterEntity,
onInspectEntity,
} = props;
const { t } = useTranslationRef(catalogTranslationRef);
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement>();
const classes = useStyles();
const unregisterPermission = useEntityPermission(
@@ -90,12 +93,12 @@ export function EntityContextMenu(props: EntityContextMenuProps) {
useEffect(() => {
if (!copyState.error && copyState.value) {
alertApi.post({
message: 'Copied!',
message: t('entityContextMenu.copiedMessage'),
severity: 'info',
display: 'transient',
});
}
}, [copyState, alertApi]);
}, [copyState, alertApi, t]);
const extraItems = UNSTABLE_extraContextMenuItems && [
...UNSTABLE_extraContextMenuItems.map(item => (
@@ -117,7 +120,7 @@ export function EntityContextMenu(props: EntityContextMenuProps) {
return (
<>
<Tooltip title="More" arrow>
<Tooltip title={t('entityContextMenu.moreButtonTitle')} arrow>
<IconButton
aria-label="more"
aria-controls="long-menu"
@@ -157,7 +160,7 @@ export function EntityContextMenu(props: EntityContextMenuProps) {
<ListItemIcon>
<BugReportIcon fontSize="small" />
</ListItemIcon>
<ListItemText primary="Inspect entity" />
<ListItemText primary={t('entityContextMenu.inspectMenuTitle')} />
</MenuItem>
<MenuItem
onClick={() => {
@@ -168,7 +171,7 @@ export function EntityContextMenu(props: EntityContextMenuProps) {
<ListItemIcon>
<FileCopyTwoToneIcon fontSize="small" />
</ListItemIcon>
<ListItemText primary="Copy entity URL" />
<ListItemText primary={t('entityContextMenu.copyURLMenuTitle')} />
</MenuItem>
</MenuList>
</Popover>
@@ -19,6 +19,8 @@ import ListItemIcon from '@material-ui/core/ListItemIcon';
import ListItemText from '@material-ui/core/ListItemText';
import MenuItem from '@material-ui/core/MenuItem';
import CancelIcon from '@material-ui/icons/Cancel';
import { catalogTranslationRef } from '../../translation';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
type VisibleType = 'visible' | 'hidden' | 'disable';
@@ -40,6 +42,7 @@ export function UnregisterEntity(props: UnregisterEntityProps) {
onUnregisterEntity,
onClose,
} = props;
const { t } = useTranslationRef(catalogTranslationRef);
const isBoolean =
typeof unregisterEntityOptions?.disableUnregister === 'boolean';
@@ -65,7 +68,7 @@ export function UnregisterEntity(props: UnregisterEntityProps) {
<ListItemIcon>
<CancelIcon fontSize="small" />
</ListItemIcon>
<ListItemText primary="Unregister entity" />
<ListItemText primary={t('entityContextMenu.unregisterMenuTitle')} />
</MenuItem>
);
}
@@ -25,6 +25,8 @@ import {
import { EntityLabelsEmptyState } from './EntityLabelsEmptyState';
import Typography from '@material-ui/core/Typography';
import { makeStyles } from '@material-ui/core/styles';
import { catalogTranslationRef } from '../../translation';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
/** @public */
export interface EntityLabelsCardProps {
@@ -42,6 +44,7 @@ export const EntityLabelsCard = (props: EntityLabelsCardProps) => {
const { variant, title } = props;
const { entity } = useEntity();
const classes = useStyles();
const { t } = useTranslationRef(catalogTranslationRef);
const columns: TableColumn<{ key: string; value: string }>[] = [
{
@@ -61,7 +64,7 @@ export const EntityLabelsCard = (props: EntityLabelsCardProps) => {
const labels = entity?.metadata?.labels;
return (
<InfoCard title={title || 'Labels'} variant={variant}>
<InfoCard title={title || t('entityLabelsCard.title')} variant={variant}>
{!labels || Object.keys(labels).length === 0 ? (
<EntityLabelsEmptyState />
) : (
@@ -19,6 +19,8 @@ import Typography from '@material-ui/core/Typography';
import { makeStyles } from '@material-ui/core/styles';
import React from 'react';
import { CodeSnippet } from '@backstage/core-components';
import { catalogTranslationRef } from '../../translation';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
const ENTITY_YAML = `metadata:
name: example
@@ -39,12 +41,12 @@ const useStyles = makeStyles(
export function EntityLabelsEmptyState() {
const classes = useStyles();
const { t } = useTranslationRef(catalogTranslationRef);
return (
<>
<Typography variant="body1">
No labels defined for this entity. You can add labels to your entity
YAML as shown in the highlighted example below:
{t('entityLabelsCard.emptyDescription')}
</Typography>
<div className={classes.code}>
<CodeSnippet
@@ -61,7 +63,7 @@ export function EntityLabelsEmptyState() {
target="_blank"
href="https://backstage.io/docs/features/software-catalog/descriptor-format#labels-optional"
>
Read more
{t('entityLabelsCard.readMoreButtonTitle')}
</Button>
</>
);
@@ -53,6 +53,8 @@ import React, { useEffect, useState } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
import { EntityContextMenu } from '../EntityContextMenu/EntityContextMenu';
import { rootRouteRef, unregisterRedirectRouteRef } from '../../routes';
import { catalogTranslationRef } from '../../translation';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
/** @public */
export type EntityLayoutRouteProps = {
@@ -117,11 +119,12 @@ function headerProps(
function EntityLabels(props: { entity: Entity }) {
const { entity } = props;
const ownedByRelations = getEntityRelations(entity, RELATION_OWNED_BY);
const { t } = useTranslationRef(catalogTranslationRef);
return (
<>
{ownedByRelations.length > 0 && (
<HeaderLabel
label="Owner"
label={t('entityLabels.ownerLabel')}
contentTypograpyRootComponent="p"
value={
<EntityRefLinks
@@ -134,7 +137,7 @@ function EntityLabels(props: { entity: Entity }) {
)}
{entity.spec?.lifecycle && (
<HeaderLabel
label="Lifecycle"
label={t('entityLabels.lifecycleLabel')}
value={entity.spec.lifecycle?.toString()}
/>
)}
@@ -234,6 +237,7 @@ export const EntityLayout = (props: EntityLayoutProps) => {
const navigate = useNavigate();
const catalogRoute = useRouteRef(rootRouteRef);
const unregisterRedirectRoute = useRouteRef(unregisterRedirectRouteRef);
const { t } = useTranslationRef(catalogTranslationRef);
const cleanUpAfterRemoval = async () => {
setConfirmationDialogOpen(false);
@@ -286,7 +290,7 @@ export const EntityLayout = (props: EntityLayoutProps) => {
{NotFoundComponent ? (
NotFoundComponent
) : (
<WarningPanel title="Entity not found">
<WarningPanel title={t('entityLabels.warningPanelTitle')}>
There is no {kind} with the requested{' '}
<Link to="https://backstage.io/docs/features/software-catalog/references">
kind, namespace, and name
@@ -22,6 +22,8 @@ import { LinksGridList } from './LinksGridList';
import { ColumnBreakpoints } from './types';
import { IconComponent, useApp } from '@backstage/core-plugin-api';
import { InfoCard, InfoCardVariants } from '@backstage/core-components';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
import { catalogTranslationRef } from '../../translation';
/** @public */
export interface EntityLinksCardProps {
@@ -33,6 +35,7 @@ export const EntityLinksCard = (props: EntityLinksCardProps) => {
const { cols = undefined, variant } = props;
const { entity } = useEntity();
const app = useApp();
const { t } = useTranslationRef(catalogTranslationRef);
const iconResolver = (key?: string): IconComponent =>
key ? app.getSystemIcon(key) ?? LanguageIcon : LanguageIcon;
@@ -40,7 +43,7 @@ export const EntityLinksCard = (props: EntityLinksCardProps) => {
const links = entity?.metadata?.links;
return (
<InfoCard title="Links" variant={variant}>
<InfoCard title={t('entityLinksCard.title')} variant={variant}>
{!links || links.length === 0 ? (
<EntityLinksEmptyState />
) : (
@@ -19,6 +19,8 @@ import Typography from '@material-ui/core/Typography';
import { makeStyles } from '@material-ui/core/styles';
import React from 'react';
import { CodeSnippet } from '@backstage/core-components';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
import { catalogTranslationRef } from '../../translation';
const ENTITY_YAML = `metadata:
name: example
@@ -44,12 +46,12 @@ const useStyles = makeStyles(
export function EntityLinksEmptyState() {
const classes = useStyles();
const { t } = useTranslationRef(catalogTranslationRef);
return (
<>
<Typography variant="body1">
No links defined for this entity. You can add links to your entity YAML
as shown in the highlighted example below:
{t('entityLinksCard.emptyDescription')}
</Typography>
<div className={classes.code}>
<CodeSnippet
@@ -66,7 +68,7 @@ export function EntityLinksEmptyState() {
target="_blank"
href="https://backstage.io/docs/features/software-catalog/descriptor-format#links-optional"
>
Read more
{t('entityLinksCard.readMoreButtonTitle')}
</Button>
</>
);
@@ -20,6 +20,8 @@ import Button from '@material-ui/core/Button';
import Typography from '@material-ui/core/Typography';
import { makeStyles } from '@material-ui/core/styles';
import { Illo } from './Illo';
import { catalogTranslationRef } from '../../translation';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
const useStyles = makeStyles(theme => ({
container: {
@@ -45,24 +47,24 @@ const useStyles = makeStyles(theme => ({
export function EntityNotFound() {
const classes = useStyles();
const { t } = useTranslationRef(catalogTranslationRef);
return (
<Grid container spacing={0} className={classes.container}>
<Illo />
<Grid item xs={12} sm={6}>
<Typography variant="h2" className={classes.title}>
Entity was not found
{t('entityNotFound.title')}
</Typography>
<Typography variant="body1" className={classes.body}>
Want to help us build this? Check out our Getting Started
documentation.
{t('entityNotFound.description')}
</Typography>
<Button
variant="contained"
color="primary"
href="https://backstage.io/docs"
>
DOCS
{t('entityNotFound.docButtonTitle')}
</Button>
</Grid>
</Grid>
@@ -23,6 +23,8 @@ import DialogTitle from '@material-ui/core/DialogTitle';
import React, { useState } from 'react';
import { alertApiRef, useApi } from '@backstage/core-plugin-api';
import { assertError } from '@backstage/errors';
import { catalogTranslationRef } from '../../translation';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
interface DeleteEntityDialogProps {
open: boolean;
@@ -36,6 +38,7 @@ export function DeleteEntityDialog(props: DeleteEntityDialogProps) {
const [busy, setBusy] = useState(false);
const catalogApi = useApi(catalogApiRef);
const alertApi = useApi(alertApiRef);
const { t } = useTranslationRef(catalogTranslationRef);
const onDelete = async () => {
setBusy(true);
@@ -54,7 +57,7 @@ export function DeleteEntityDialog(props: DeleteEntityDialogProps) {
return (
<Dialog open={open} onClose={onClose}>
<DialogTitle id="responsive-dialog-title">
Are you sure you want to delete this entity?
{t('deleteEntity.dialogTitle')}
</DialogTitle>
<DialogActions>
<Button
@@ -63,10 +66,10 @@ export function DeleteEntityDialog(props: DeleteEntityDialogProps) {
disabled={busy}
onClick={onDelete}
>
Delete
{t('deleteEntity.deleteButtonTitle')}
</Button>
<Button onClick={onClose} color="primary">
Cancel
{t('deleteEntity.cancelButtonTitle')}
</Button>
</DialogActions>
</Dialog>
@@ -22,6 +22,8 @@ import { useNavigate } from 'react-router-dom';
import { DeleteEntityDialog } from './DeleteEntityDialog';
import { useRouteRef } from '@backstage/core-plugin-api';
import { rootRouteRef } from '../../routes';
import { catalogTranslationRef } from '../../translation';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
/**
* Returns true if the given entity has the orphan annotation given by the
@@ -44,6 +46,7 @@ export function EntityOrphanWarning() {
const catalogLink = useRouteRef(rootRouteRef);
const [confirmationDialogOpen, setConfirmationDialogOpen] = useState(false);
const { entity } = useEntity();
const { t } = useTranslationRef(catalogTranslationRef);
const cleanUpAfterRemoval = async () => {
setConfirmationDialogOpen(false);
@@ -53,8 +56,7 @@ export function EntityOrphanWarning() {
return (
<>
<Alert severity="warning" onClick={() => setConfirmationDialogOpen(true)}>
This entity is not referenced by any location and is therefore not
receiving updates. Click here to delete.
{t('deleteEntity.description')}
</Alert>
<DeleteEntityDialog
open={confirmationDialogOpen}
@@ -31,6 +31,8 @@ import {
import { useApi, ApiHolder } from '@backstage/core-plugin-api';
import useAsync from 'react-use/esm/useAsync';
import { SerializedError } from '@backstage/errors';
import { catalogTranslationRef } from '../../translation';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
const errorFilter = (i: EntityStatusItem) =>
i.error &&
@@ -95,6 +97,7 @@ export function EntityProcessingErrorsPanel() {
const { loading, error, value } = useAsync(async () => {
return getOwnAndAncestorsErrors(entityRef, catalogApi);
}, [entityRef, catalogApi]);
const { t } = useTranslationRef(catalogTranslationRef);
if (error) {
return (
@@ -115,7 +118,7 @@ export function EntityProcessingErrorsPanel() {
{stringifyEntityRef(entity) !==
stringifyEntityRef(ancestorError.entity) && (
<Box p={1}>
The error below originates from{' '}
{t('entityProcessingErrorsDescription')}{' '}
<EntityRefLink entityRef={ancestorError.entity} />
</Box>
)}
@@ -26,6 +26,8 @@ import useAsync from 'react-use/esm/useAsync';
import Box from '@material-ui/core/Box';
import { ResponseErrorPanel } from '@backstage/core-components';
import { useApi, ApiHolder } from '@backstage/core-plugin-api';
import { catalogTranslationRef } from '../../translation';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
async function getRelationWarnings(entity: Entity, catalogApi: CatalogApi) {
const entityRefRelations = entity.relations?.map(
@@ -80,6 +82,7 @@ export function EntityRelationWarning() {
const { loading, error, value } = useAsync(async () => {
return getRelationWarnings(entity, catalogApi);
}, [entity, catalogApi]);
const { t } = useTranslationRef(catalogTranslationRef);
if (error) {
return (
@@ -95,10 +98,8 @@ export function EntityRelationWarning() {
return (
<>
<Alert severity="warning">
This entity has relations to other entities, which can't be found in the
catalog. <br />
Entities not found are: {value.join(', ')}
<Alert severity="warning" style={{ whiteSpace: 'pre-line' }}>
{t('entityRelationWarningDescription')} {value.join(', ')}
</Alert>
</>
);
@@ -23,6 +23,8 @@ import {
componentEntityHelpLink,
RelatedEntitiesCard,
} from '../RelatedEntitiesCard';
import { catalogTranslationRef } from '../../translation';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
/** @public */
export interface HasComponentsCardProps {
@@ -31,7 +33,8 @@ export interface HasComponentsCardProps {
}
export function HasComponentsCard(props: HasComponentsCardProps) {
const { variant = 'gridItem', title = 'Has components' } = props;
const { t } = useTranslationRef(catalogTranslationRef);
const { variant = 'gridItem', title = t('hasComponentsCard.title') } = props;
return (
<RelatedEntitiesCard
variant={variant}
@@ -39,7 +42,7 @@ export function HasComponentsCard(props: HasComponentsCardProps) {
entityKind="Component"
relationType={RELATION_HAS_PART}
columns={componentEntityColumns}
emptyMessage="No component is part of this system"
emptyMessage={t('hasComponentsCard.emptyMessage')}
emptyHelpLink={componentEntityHelpLink}
asRenderableEntities={asComponentEntities}
/>
@@ -23,6 +23,8 @@ import {
resourceEntityColumns,
resourceEntityHelpLink,
} from '../RelatedEntitiesCard';
import { catalogTranslationRef } from '../../translation';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
/** @public */
export interface HasResourcesCardProps {
@@ -31,7 +33,8 @@ export interface HasResourcesCardProps {
}
export function HasResourcesCard(props: HasResourcesCardProps) {
const { variant = 'gridItem', title = 'Has resources' } = props;
const { t } = useTranslationRef(catalogTranslationRef);
const { variant = 'gridItem', title = t('hasResourcesCard.title') } = props;
return (
<RelatedEntitiesCard
variant={variant}
@@ -40,7 +43,7 @@ export function HasResourcesCard(props: HasResourcesCardProps) {
relationType={RELATION_HAS_PART}
columns={resourceEntityColumns}
asRenderableEntities={asResourceEntities}
emptyMessage="No resource is part of this system"
emptyMessage={t('hasResourcesCard.emptyMessage')}
emptyHelpLink={resourceEntityHelpLink}
/>
);
@@ -22,6 +22,8 @@ import {
componentEntityColumns,
RelatedEntitiesCard,
} from '../RelatedEntitiesCard';
import { catalogTranslationRef } from '../../translation';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
/** @public */
export interface HasSubcomponentsCardProps {
@@ -31,10 +33,11 @@ export interface HasSubcomponentsCardProps {
}
export function HasSubcomponentsCard(props: HasSubcomponentsCardProps) {
const { t } = useTranslationRef(catalogTranslationRef);
const {
variant = 'gridItem',
tableOptions = {},
title = 'Has subcomponents',
title = t('hasSubcomponentsCard.title'),
} = props;
return (
<RelatedEntitiesCard
@@ -44,7 +47,7 @@ export function HasSubcomponentsCard(props: HasSubcomponentsCardProps) {
relationType={RELATION_HAS_PART}
columns={componentEntityColumns}
asRenderableEntities={asComponentEntities}
emptyMessage="No subcomponent is part of this component"
emptyMessage={t('hasSubcomponentsCard.emptyMessage')}
emptyHelpLink="https://backstage.io/docs/features/software-catalog/descriptor-format#specsubcomponentof-optional"
tableOptions={tableOptions}
/>
@@ -23,6 +23,8 @@ import {
systemEntityColumns,
systemEntityHelpLink,
} from '../RelatedEntitiesCard';
import { catalogTranslationRef } from '../../translation';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
/** @public */
export interface HasSystemsCardProps {
@@ -31,7 +33,8 @@ export interface HasSystemsCardProps {
}
export function HasSystemsCard(props: HasSystemsCardProps) {
const { variant = 'gridItem', title = 'Has systems' } = props;
const { t } = useTranslationRef(catalogTranslationRef);
const { variant = 'gridItem', title = t('hasSystemsCard.title') } = props;
return (
<RelatedEntitiesCard
variant={variant}
@@ -40,7 +43,7 @@ export function HasSystemsCard(props: HasSystemsCardProps) {
relationType={RELATION_HAS_PART}
columns={systemEntityColumns}
asRenderableEntities={asSystemEntities}
emptyMessage="No system is part of this domain"
emptyMessage={t('hasSystemsCard.emptyMessage')}
emptyHelpLink={systemEntityHelpLink}
/>
);
@@ -31,6 +31,8 @@ import {
TableColumn,
TableOptions,
} from '@backstage/core-components';
import { catalogTranslationRef } from '../../translation';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
/** @public */
export type RelatedEntitiesCardProps<T extends Entity> = {
@@ -72,6 +74,7 @@ export function RelatedEntitiesCard<T extends Entity>(
tableOptions = {},
} = props;
const { t } = useTranslationRef(catalogTranslationRef);
const { entity } = useEntity();
const { entities, loading, error } = useRelatedEntities(entity, {
type: relationType,
@@ -102,7 +105,9 @@ export function RelatedEntitiesCard<T extends Entity>(
<div style={{ textAlign: 'center' }}>
<Typography variant="body1">{emptyMessage}</Typography>
<Typography variant="body2">
<Link to={emptyHelpLink}>Learn how to change this.</Link>
<Link to={emptyHelpLink}>
{t('relatedEntitiesCard.emptyHelpLinkTitle')}
</Link>
</Typography>
</div>
}
@@ -46,6 +46,8 @@ import {
} from '@backstage/core-components';
import { useApi, useRouteRef } from '@backstage/core-plugin-api';
import { catalogTranslationRef } from '../../translation';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
/** @public */
export type SystemDiagramCardClassKey =
@@ -164,6 +166,7 @@ function RenderNode(props: DependencyGraphTypes.RenderNodeProps<any>) {
export function SystemDiagramCard() {
const { entity } = useEntity();
const theme = useTheme();
const { t } = useTranslationRef(catalogTranslationRef);
const currentSystemName = entity.metadata.name;
const currentSystemNode = stringifyEntityRef(entity);
const systemNodes = new Array<{ id: string; kind: string; name: string }>();
@@ -211,7 +214,7 @@ export function SystemDiagramCard() {
systemEdges.push({
from: currentSystemNode,
to: stringifyEntityRef(foundDomain),
label: 'part of',
label: t('systemDiagramCard.edgeLabels.partOf'),
}),
);
@@ -235,7 +238,7 @@ export function SystemDiagramCard() {
systemEdges.push({
from: stringifyEntityRef(catalogItem),
to: stringifyEntityRef(foundRelation),
label: 'part of',
label: t('systemDiagramCard.edgeLabels.partOf'),
}),
);
@@ -247,7 +250,7 @@ export function SystemDiagramCard() {
systemEdges.push({
from: stringifyEntityRef(catalogItem),
to: stringifyEntityRef(foundRelation),
label: 'provides',
label: t('systemDiagramCard.edgeLabels.provides'),
}),
);
@@ -259,7 +262,7 @@ export function SystemDiagramCard() {
systemEdges.push({
from: stringifyEntityRef(catalogItem),
to: stringifyEntityRef(foundRelation),
label: 'depends on',
label: t('systemDiagramCard.edgeLabels.dependsOn'),
}),
);
}
@@ -272,7 +275,7 @@ export function SystemDiagramCard() {
}
return (
<InfoCard title="System Diagram">
<InfoCard title={t('systemDiagramCard.title')}>
<DependencyGraph
nodes={systemNodes}
edges={systemEdges}
@@ -287,8 +290,8 @@ export function SystemDiagramCard() {
variant="caption"
style={{ display: 'block', textAlign: 'right' }}
>
<ZoomOutMap style={{ verticalAlign: 'bottom' }} /> Use pinch &amp; zoom
to move around the diagram.
<ZoomOutMap style={{ verticalAlign: 'bottom' }} />
{t('systemDiagramCard.description')}
</Typography>
</InfoCard>
);
+135
View File
@@ -23,6 +23,141 @@ export const catalogTranslationRef = createTranslationRef({
indexPage: {
title: `{{orgName}} Catalog`,
createButtonTitle: 'Create',
supportButtonContent: 'All your software catalog entities',
},
aboutCard: {
title: 'About',
refreshButtonTitle: 'Schedule entity refresh',
editButtonTitle: 'Edit Metadata',
createSimilarButtonTitle: 'Create something similar',
refreshScheduledMessage: 'Refresh scheduled',
launchTemplate: 'Launch Template',
viewTechdocs: 'View TechDocs',
viewSource: 'View Source',
descriptionField: {
label: 'Description',
value: 'No description',
},
ownerField: {
label: 'Owner',
value: 'No Owner',
},
domainField: {
label: 'Domain',
value: 'No Domain',
},
systemField: {
label: 'System',
value: 'No System',
},
parentComponentField: {
label: 'Parent Component',
value: 'No Parent Component',
},
typeField: {
label: 'Type',
},
lifecycleField: {
label: 'Lifecycle',
},
tagsField: {
label: 'Tags',
value: 'No Tags',
},
targetsField: {
label: 'Targets',
},
},
searchResultItem: {
lifecycle: 'Lifecycle',
Owner: 'Owner',
},
catalogTable: {
warningPanelTitle: 'Could not fetch catalog entities.',
viewActionTitle: 'View',
editActionTitle: 'Edit',
starActionTitle: 'Add to favorites',
unStarActionTitle: 'Remove from favorites',
},
dependencyOfComponentsCard: {
title: 'Dependency of components',
emptyMessage: 'No component depends on this component',
},
dependsOnComponentsCard: {
title: 'Depends on components',
emptyMessage: 'No component is a dependency of this component',
},
dependsOnResourcesCard: {
title: 'Depends on resources',
emptyMessage: 'No resource is a dependency of this component',
},
entityContextMenu: {
copiedMessage: 'Copied!',
moreButtonTitle: 'More',
inspectMenuTitle: 'Inspect entity',
copyURLMenuTitle: 'Copy entity URL',
unregisterMenuTitle: 'Unregister entity',
},
entityLabelsCard: {
title: 'Labels',
emptyDescription:
'No labels defined for this entity. You can add labels to your entity YAML as shown in the highlighted example below:',
readMoreButtonTitle: 'Read more',
},
entityLabels: {
warningPanelTitle: 'Entity not found',
ownerLabel: 'Owner',
lifecycleLabel: 'Lifecycle',
},
entityLinksCard: {
title: 'Links',
emptyDescription:
'No links defined for this entity. You can add links to your entity YAML as shown in the highlighted example below:',
readMoreButtonTitle: 'Read more',
},
entityNotFound: {
title: 'Entity was not found',
description:
'Want to help us build this? Check out our Getting Started documentation.',
docButtonTitle: 'DOCS',
},
deleteEntity: {
dialogTitle: 'Are you sure you want to delete this entity?',
deleteButtonTitle: 'Delete',
cancelButtonTitle: 'Cancel',
description:
'This entity is not referenced by any location and is therefore not receiving updates. Click here to delete.',
},
entityProcessingErrorsDescription: 'The error below originates from',
entityRelationWarningDescription:
"This entity has relations to other entities, which can't be found in the catalog.\n Entities not found are: ",
hasComponentsCard: {
title: 'Has components',
emptyMessage: 'No component is part of this system',
},
hasResourcesCard: {
title: 'Has resources',
emptyMessage: 'No resource is part of this system',
},
hasSubcomponentsCard: {
title: 'Has subcomponents',
emptyMessage: 'No subcomponent is part of this component',
},
hasSystemsCard: {
title: 'Has systems',
emptyMessage: 'No system is part of this domain',
},
relatedEntitiesCard: {
emptyHelpLinkTitle: 'Learn how to change this.',
},
systemDiagramCard: {
title: 'System Diagram',
description: 'Use pinch & zoo to move around the diagram.',
edgeLabels: {
partOf: 'part of',
provides: 'provides',
dependsOn: 'depends on',
},
},
},
});