diff --git a/.changeset/lucky-days-hug.md b/.changeset/lucky-days-hug.md new file mode 100644 index 0000000000..4d76caa098 --- /dev/null +++ b/.changeset/lucky-days-hug.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-kubernetes-react': patch +'@backstage/plugin-kubernetes': patch +--- + +add missing i18n support for kubernetes and kubernetes-react diff --git a/plugins/kubernetes-react/report-alpha.api.md b/plugins/kubernetes-react/report-alpha.api.md index 00a8b6b819..e1131d103e 100644 --- a/plugins/kubernetes-react/report-alpha.api.md +++ b/plugins/kubernetes-react/report-alpha.api.md @@ -9,7 +9,84 @@ import { TranslationRef } from '@backstage/core-plugin-api/alpha'; export const kubernetesReactTranslationRef: TranslationRef< 'kubernetes-react', { + readonly 'events.noEventsFound': 'No events found'; + readonly 'events.eventTooltip': '{{eventType}} event'; + readonly 'events.firstEvent': 'First event {{timeAgo}} (count: {{count}})'; + readonly 'namespace.label': 'namespace:'; + readonly 'namespace.labelWithValue': 'namespace: {{namespace}}'; + readonly 'cluster.label': 'Cluster'; + readonly 'cluster.pods': 'pods'; + readonly 'cluster.pods_one': '{{count}} pod'; + readonly 'cluster.pods_other': '{{count}} pods'; + readonly 'cluster.podsWithErrors': 'pods with errors'; + readonly 'cluster.podsWithErrors_one': '{{count}} pod with errors'; + readonly 'cluster.podsWithErrors_other': '{{count}} pods with errors'; + readonly 'cluster.noPodsWithErrors': 'No pods with errors'; + readonly 'errorPanel.message': 'There was a problem retrieving some Kubernetes resources for the entity: {{entityName}}. This could mean that the Error Reporting card is not completely accurate.'; + readonly 'errorPanel.title': 'There was a problem retrieving Kubernetes objects'; + readonly 'errorPanel.errorsLabel': 'Errors'; + readonly 'errorPanel.clusterLabel': 'Cluster'; + readonly 'errorPanel.clusterLabelValue': 'Cluster: {{cluster}}'; + readonly 'errorPanel.fetchError': 'Error communicating with Kubernetes: {{errorType}}, message: {{message}}'; + readonly 'errorPanel.resourceError': "Error fetching Kubernetes resource: '{{resourcePath}}', error: {{errorType}}, status code: {{statusCode}}"; + readonly 'pods.pods_one': '{{count}} pod'; + readonly 'pods.pods_other': '{{count}} pods'; + readonly 'podsTable.columns.name': 'name'; + readonly 'podsTable.columns.id': 'ID'; + readonly 'podsTable.columns.status': 'status'; + readonly 'podsTable.columns.phase': 'phase'; + readonly 'podsTable.columns.containersReady': 'containers ready'; + readonly 'podsTable.columns.totalRestarts': 'total restarts'; + readonly 'podsTable.columns.cpuUsage': 'CPU usage %'; + readonly 'podsTable.columns.memoryUsage': 'Memory usage %'; + readonly 'podsTable.unknown': 'unknown'; + readonly 'podsTable.status.running': 'Running'; + readonly 'podsTable.status.ok': 'OK'; + readonly 'fixDialog.title': '{{podName}} - {{errorType}}'; + readonly 'fixDialog.events': 'Events:'; + readonly 'fixDialog.helpButton': 'Help'; + readonly 'fixDialog.detectedError': 'Detected error:'; + readonly 'fixDialog.causeExplanation': 'Cause explanation:'; + readonly 'fixDialog.fix': 'Fix:'; + readonly 'fixDialog.crashLogs': 'Crash logs:'; + readonly 'fixDialog.openDocs': 'Open docs'; + readonly 'fixDialog.ariaLabels.close': 'close'; + readonly 'fixDialog.ariaLabels.fixIssue': 'fix issue'; readonly 'podDrawer.buttons.delete': 'Delete Pod'; + readonly 'podDrawer.cpuRequests': 'CPU requests'; + readonly 'podDrawer.cpuLimits': 'CPU limits'; + readonly 'podDrawer.memoryRequests': 'Memory requests'; + readonly 'podDrawer.memoryLimits': 'Memory limits'; + readonly 'podDrawer.resourceUtilization': 'Resource utilization'; + readonly 'hpa.minReplicas': 'min replicas'; + readonly 'hpa.maxReplicas': 'max replicas'; + readonly 'hpa.replicasSummary': 'min replicas {{min}} / max replicas {{max}}'; + readonly 'hpa.currentCpuUsage': 'current CPU usage:'; + readonly 'hpa.currentCpuUsageLabel': 'current CPU usage: {{value}}%'; + readonly 'hpa.targetCpuUsage': 'target CPU usage:'; + readonly 'hpa.targetCpuUsageLabel': 'target CPU usage: {{value}}%'; + readonly 'errorReporting.columns.name': 'name'; + readonly 'errorReporting.columns.kind': 'kind'; + readonly 'errorReporting.columns.messages': 'messages'; + readonly 'errorReporting.columns.namespace': 'namespace'; + readonly 'errorReporting.columns.cluster': 'cluster'; + readonly 'errorReporting.title': 'Error Reporting'; + readonly 'podLogs.title': 'No logs emitted'; + readonly 'podLogs.description': 'No logs were emitted by the container'; + readonly 'podLogs.buttonText': 'Logs'; + readonly 'podLogs.titleTemplate': '{{podName}} - {{containerName}} logs on cluster {{clusterName}}'; + readonly 'podLogs.buttonAriaLabel': 'get logs'; + readonly 'podExecTerminal.buttonText': 'Terminal'; + readonly 'podExecTerminal.titleTemplate': '{{podName}} - {{containerName}} terminal shell on cluster {{clusterName}}'; + readonly 'podExecTerminal.buttonAriaLabel': 'open terminal'; + readonly 'kubernetesDrawer.yaml': 'YAML'; + readonly 'kubernetesDrawer.closeDrawer': 'Close the drawer'; + readonly 'kubernetesDrawer.managedFields': 'Managed Fields'; + readonly 'kubernetesDrawer.unknownName': 'unknown name'; + readonly 'linkErrorPanel.message': "Could not format the link to the dashboard of your cluster named '{{clusterName}}'. Its dashboardApp property has been set to '{{dashboardApp}}.'"; + readonly 'linkErrorPanel.title': 'There was a problem formatting the link to the Kubernetes dashboard'; + readonly 'linkErrorPanel.errorsLabel': 'Errors:'; + readonly 'kubernetesDialog.closeAriaLabel': 'close'; } >; diff --git a/plugins/kubernetes-react/src/components/Cluster/Cluster.tsx b/plugins/kubernetes-react/src/components/Cluster/Cluster.tsx index 4e8c4a1947..65fc44e170 100644 --- a/plugins/kubernetes-react/src/components/Cluster/Cluster.tsx +++ b/plugins/kubernetes-react/src/components/Cluster/Cluster.tsx @@ -42,6 +42,8 @@ import { import { StatusError, StatusOK } from '@backstage/core-components'; import { PodMetricsContext } from '../../hooks/usePodMetrics'; +import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; +import { kubernetesReactTranslationRef } from '../../translation'; type ClusterSummaryProps = { clusterName: string; @@ -55,6 +57,7 @@ const ClusterSummary = ({ totalNumberOfPods, numberOfPodsWithErrors, }: ClusterSummaryProps) => { + const { t } = useTranslationRef(kubernetesReactTranslationRef); return ( {clusterName} - Cluster + {t('cluster.label')} @@ -89,13 +92,15 @@ const ClusterSummary = ({ spacing={0} > - {totalNumberOfPods} pods + {t('cluster.pods', { count: totalNumberOfPods })} {numberOfPodsWithErrors > 0 ? ( - {numberOfPodsWithErrors} pods with errors + + {t('cluster.podsWithErrors', { count: numberOfPodsWithErrors })} + ) : ( - No pods with errors + {t('cluster.noPodsWithErrors')} )} diff --git a/plugins/kubernetes-react/src/components/ConfigmapsAccordions/ConfigmapsDrawer.tsx b/plugins/kubernetes-react/src/components/ConfigmapsAccordions/ConfigmapsDrawer.tsx index 97e8aab723..729355de5f 100644 --- a/plugins/kubernetes-react/src/components/ConfigmapsAccordions/ConfigmapsDrawer.tsx +++ b/plugins/kubernetes-react/src/components/ConfigmapsAccordions/ConfigmapsDrawer.tsx @@ -18,6 +18,8 @@ import Typography from '@material-ui/core/Typography'; import Grid from '@material-ui/core/Grid'; import Chip from '@material-ui/core/Chip'; import type { V1ConfigMap } from '@kubernetes/client-node'; +import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; +import { kubernetesReactTranslationRef } from '../../translation'; export const ConfigmapsDrawer = ({ configmap, @@ -26,6 +28,7 @@ export const ConfigmapsDrawer = ({ configmap: V1ConfigMap; expanded?: boolean; }) => { + const { t } = useTranslationRef(kubernetesReactTranslationRef); const namespace = configmap.metadata?.namespace; return ( {namespace && ( - + )} diff --git a/plugins/kubernetes-react/src/components/CronJobsAccordions/CronJobsDrawer.tsx b/plugins/kubernetes-react/src/components/CronJobsAccordions/CronJobsDrawer.tsx index a7ffdca854..8d21f73ce3 100644 --- a/plugins/kubernetes-react/src/components/CronJobsAccordions/CronJobsDrawer.tsx +++ b/plugins/kubernetes-react/src/components/CronJobsAccordions/CronJobsDrawer.tsx @@ -18,6 +18,8 @@ import { KubernetesStructuredMetadataTableDrawer } from '../KubernetesDrawer'; import Typography from '@material-ui/core/Typography'; import Grid from '@material-ui/core/Grid'; import Chip from '@material-ui/core/Chip'; +import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; +import { kubernetesReactTranslationRef } from '../../translation'; export const CronJobDrawer = ({ cronJob, @@ -26,6 +28,7 @@ export const CronJobDrawer = ({ cronJob: V1CronJob; expanded?: boolean; }) => { + const { t } = useTranslationRef(kubernetesReactTranslationRef); const namespace = cronJob.metadata?.namespace; return ( {namespace && ( - + )} diff --git a/plugins/kubernetes-react/src/components/CustomResources/ArgoRollouts/Rollout.tsx b/plugins/kubernetes-react/src/components/CustomResources/ArgoRollouts/Rollout.tsx index a299d412c9..93334c2b35 100644 --- a/plugins/kubernetes-react/src/components/CustomResources/ArgoRollouts/Rollout.tsx +++ b/plugins/kubernetes-react/src/components/CustomResources/ArgoRollouts/Rollout.tsx @@ -39,6 +39,8 @@ import { } from '../../../utils/owner'; import { StatusError, StatusOK } from '@backstage/core-components'; import { READY_COLUMNS, RESOURCE_COLUMNS } from '../../Pods/PodsTable'; +import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; +import { kubernetesReactTranslationRef } from '../../../translation'; type RolloutAccordionsProps = { rollouts: any[]; @@ -89,6 +91,7 @@ const RolloutSummary = ({ numberOfPodsWithErrors, hpa, }: RolloutSummaryProps) => { + const { t } = useTranslationRef(kubernetesReactTranslationRef); const pauseTime: string | undefined = rollout.status?.pauseConditions?.find( (p: any) => p.reason === 'CanaryPauseStep', )?.startTime; @@ -125,18 +128,24 @@ const RolloutSummary = ({ > - min replicas {hpa.spec?.minReplicas ?? '?'} / max replicas{' '} - {hpa.spec?.maxReplicas ?? '?'} + {t('hpa.replicasSummary', { + min: String(hpa.spec?.minReplicas ?? '?'), + max: String(hpa.spec?.maxReplicas ?? '?'), + })} - current CPU usage: {cpuUtil ?? '?'}% + {t('hpa.currentCpuUsageLabel', { + value: String(cpuUtil ?? '?'), + })} - target CPU usage: {specCpuUtil ?? '?'}% + {t('hpa.targetCpuUsageLabel', { + value: String(specCpuUtil ?? '?'), + })} @@ -153,16 +162,15 @@ const RolloutSummary = ({ spacing={0} > - {numberOfCurrentPods} pods + {t('pods.pods', { count: numberOfCurrentPods })} {numberOfPodsWithErrors > 0 ? ( - {numberOfPodsWithErrors} pod - {numberOfPodsWithErrors > 1 ? 's' : ''} with errors + {t('cluster.podsWithErrors', { count: numberOfPodsWithErrors })} ) : ( - No pods with errors + {t('cluster.noPodsWithErrors')} )} diff --git a/plugins/kubernetes-react/src/components/CustomResources/ArgoRollouts/RolloutDrawer.tsx b/plugins/kubernetes-react/src/components/CustomResources/ArgoRollouts/RolloutDrawer.tsx index 5a42e0971c..d02d9bd9fe 100644 --- a/plugins/kubernetes-react/src/components/CustomResources/ArgoRollouts/RolloutDrawer.tsx +++ b/plugins/kubernetes-react/src/components/CustomResources/ArgoRollouts/RolloutDrawer.tsx @@ -18,6 +18,8 @@ import { KubernetesStructuredMetadataTableDrawer } from '../../KubernetesDrawer' import Typography from '@material-ui/core/Typography'; import Grid from '@material-ui/core/Grid'; import Chip from '@material-ui/core/Chip'; +import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; +import { kubernetesReactTranslationRef } from '../../../translation'; export const RolloutDrawer = ({ rollout, @@ -26,6 +28,7 @@ export const RolloutDrawer = ({ rollout: any; expanded?: boolean; }) => { + const { t } = useTranslationRef(kubernetesReactTranslationRef); const namespace = rollout.metadata?.namespace; return ( {namespace && ( - + )} diff --git a/plugins/kubernetes-react/src/components/DaemonSetsAccordions/DaemonSetsAccordions.tsx b/plugins/kubernetes-react/src/components/DaemonSetsAccordions/DaemonSetsAccordions.tsx index a96034a012..b2f5645444 100644 --- a/plugins/kubernetes-react/src/components/DaemonSetsAccordions/DaemonSetsAccordions.tsx +++ b/plugins/kubernetes-react/src/components/DaemonSetsAccordions/DaemonSetsAccordions.tsx @@ -30,6 +30,8 @@ import { } from '../../hooks'; import { StatusError, StatusOK } from '@backstage/core-components'; import { READY_COLUMNS, RESOURCE_COLUMNS } from '../Pods/PodsTable'; +import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; +import { kubernetesReactTranslationRef } from '../../translation'; type DaemonSetsAccordionsProps = { children?: ReactNode; @@ -53,6 +55,7 @@ const DaemonSetSummary = ({ numberOfCurrentPods, numberOfPodsWithErrors, }: DaemonSetSummaryProps) => { + const { t } = useTranslationRef(kubernetesReactTranslationRef); return ( - {numberOfCurrentPods} pods + {t('pods.pods', { count: numberOfCurrentPods })} {numberOfPodsWithErrors > 0 ? ( - {numberOfPodsWithErrors} pod - {numberOfPodsWithErrors > 1 ? 's' : ''} with errors + {t('cluster.podsWithErrors', { count: numberOfPodsWithErrors })} ) : ( - No pods with errors + {t('cluster.noPodsWithErrors')} )} diff --git a/plugins/kubernetes-react/src/components/DaemonSetsAccordions/DaemonSetsDrawer.tsx b/plugins/kubernetes-react/src/components/DaemonSetsAccordions/DaemonSetsDrawer.tsx index 400512f0d2..00f41cdf69 100644 --- a/plugins/kubernetes-react/src/components/DaemonSetsAccordions/DaemonSetsDrawer.tsx +++ b/plugins/kubernetes-react/src/components/DaemonSetsAccordions/DaemonSetsDrawer.tsx @@ -19,6 +19,8 @@ import { KubernetesStructuredMetadataTableDrawer } from '../KubernetesDrawer'; import Typography from '@material-ui/core/Typography'; import Grid from '@material-ui/core/Grid'; import Chip from '@material-ui/core/Chip'; +import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; +import { kubernetesReactTranslationRef } from '../../translation'; export const DaemonSetDrawer = ({ daemonset, @@ -27,6 +29,7 @@ export const DaemonSetDrawer = ({ daemonset: V1DaemonSet; expanded?: boolean; }) => { + const { t } = useTranslationRef(kubernetesReactTranslationRef); const namespace = daemonset.metadata?.namespace; return ( {namespace && ( - + )} diff --git a/plugins/kubernetes-react/src/components/DeploymentsAccordions/DeploymentDrawer.tsx b/plugins/kubernetes-react/src/components/DeploymentsAccordions/DeploymentDrawer.tsx index c94059ccc2..31bee930d3 100644 --- a/plugins/kubernetes-react/src/components/DeploymentsAccordions/DeploymentDrawer.tsx +++ b/plugins/kubernetes-react/src/components/DeploymentsAccordions/DeploymentDrawer.tsx @@ -21,6 +21,8 @@ import { renderCondition } from '../../utils/pod'; import Typography from '@material-ui/core/Typography'; import Grid from '@material-ui/core/Grid'; import Chip from '@material-ui/core/Chip'; +import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; +import { kubernetesReactTranslationRef } from '../../translation'; export const DeploymentDrawer = ({ deployment, @@ -29,6 +31,7 @@ export const DeploymentDrawer = ({ deployment: V1Deployment; expanded?: boolean; }) => { + const { t } = useTranslationRef(kubernetesReactTranslationRef); const namespace = deployment.metadata?.namespace; return ( {namespace && ( - + )} diff --git a/plugins/kubernetes-react/src/components/DeploymentsAccordions/DeploymentsAccordions.tsx b/plugins/kubernetes-react/src/components/DeploymentsAccordions/DeploymentsAccordions.tsx index 75da5e7815..961d07b761 100644 --- a/plugins/kubernetes-react/src/components/DeploymentsAccordions/DeploymentsAccordions.tsx +++ b/plugins/kubernetes-react/src/components/DeploymentsAccordions/DeploymentsAccordions.tsx @@ -39,6 +39,8 @@ import { } from '../../hooks'; import { StatusError, StatusOK } from '@backstage/core-components'; import { READY_COLUMNS, RESOURCE_COLUMNS } from '../Pods/PodsTable'; +import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; +import { kubernetesReactTranslationRef } from '../../translation'; type DeploymentsAccordionsProps = { children?: ReactNode; @@ -65,6 +67,7 @@ const DeploymentSummary = ({ numberOfPodsWithErrors, hpa, }: DeploymentSummaryProps) => { + const { t } = useTranslationRef(kubernetesReactTranslationRef); const specCpuUtil = hpa?.spec?.metrics?.find( metric => metric.type === 'Resource' && metric.resource?.name === 'cpu', )?.resource?.target.averageUtilization; @@ -97,18 +100,24 @@ const DeploymentSummary = ({ > - min replicas {hpa.spec?.minReplicas ?? '?'} / max replicas{' '} - {hpa.spec?.maxReplicas ?? '?'} + {t('hpa.replicasSummary', { + min: String(hpa.spec?.minReplicas ?? '?'), + max: String(hpa.spec?.maxReplicas ?? '?'), + })} - current CPU usage: {cpuUtil ?? '?'}% + {t('hpa.currentCpuUsageLabel', { + value: String(cpuUtil ?? '?'), + })} - target CPU usage: {specCpuUtil ?? '?'}% + {t('hpa.targetCpuUsageLabel', { + value: String(specCpuUtil ?? '?'), + })} @@ -125,16 +134,15 @@ const DeploymentSummary = ({ spacing={0} > - {numberOfCurrentPods} pods + {t('pods.pods', { count: numberOfCurrentPods })} {numberOfPodsWithErrors > 0 ? ( - {numberOfPodsWithErrors} pod - {numberOfPodsWithErrors > 1 ? 's' : ''} with errors + {t('cluster.podsWithErrors', { count: numberOfPodsWithErrors })} ) : ( - No pods with errors + {t('cluster.noPodsWithErrors')} )} diff --git a/plugins/kubernetes-react/src/components/ErrorPanel/ErrorPanel.tsx b/plugins/kubernetes-react/src/components/ErrorPanel/ErrorPanel.tsx index 5238a76e70..b036c2b089 100644 --- a/plugins/kubernetes-react/src/components/ErrorPanel/ErrorPanel.tsx +++ b/plugins/kubernetes-react/src/components/ErrorPanel/ErrorPanel.tsx @@ -18,22 +18,37 @@ import { ReactNode } from 'react'; import Typography from '@material-ui/core/Typography'; import { ClusterObjects } from '@backstage/plugin-kubernetes-common'; import { WarningPanel } from '@backstage/core-components'; +import { + useTranslationRef, + TranslationFunction, +} from '@backstage/core-plugin-api/alpha'; +import { kubernetesReactTranslationRef } from '../../translation'; const clustersWithErrorsToErrorMessage = ( clustersWithErrors: ClusterObjects[], + t: TranslationFunction, ): ReactNode => { return clustersWithErrors.map((c, i) => { return (
- {`Cluster: ${ - c.cluster.title || c.cluster.name - }`} + + {t('errorPanel.clusterLabelValue', { + cluster: c.cluster.title || c.cluster.name, + })} + {c.errors.map((e, j) => { return ( {e.errorType === 'FETCH_ERROR' - ? `Error communicating with Kubernetes: ${e.errorType}, message: ${e.message}` - : `Error fetching Kubernetes resource: '${e.resourcePath}', error: ${e.errorType}, status code: ${e.statusCode}`} + ? t('errorPanel.fetchError', { + errorType: e.errorType, + message: e.message, + }) + : t('errorPanel.resourceError', { + resourcePath: e.resourcePath ?? '', + errorType: e.errorType, + statusCode: String(e.statusCode ?? ''), + })} ); })} @@ -64,16 +79,24 @@ export const ErrorPanel = ({ entityName, errorMessage, clustersWithErrors, -}: ErrorPanelProps) => ( - - {clustersWithErrors && ( -
Errors: {clustersWithErrorsToErrorMessage(clustersWithErrors)}
- )} - {errorMessage && ( - Errors: {errorMessage} - )} -
-); +}: ErrorPanelProps) => { + const { t } = useTranslationRef(kubernetesReactTranslationRef); + return ( + + {clustersWithErrors && ( +
+ {t('errorPanel.errorsLabel')}:{' '} + {clustersWithErrorsToErrorMessage(clustersWithErrors, t)} +
+ )} + {errorMessage && ( + + {t('errorPanel.errorsLabel')}: {errorMessage} + + )} +
+ ); +}; diff --git a/plugins/kubernetes-react/src/components/ErrorReporting/ErrorReporting.tsx b/plugins/kubernetes-react/src/components/ErrorReporting/ErrorReporting.tsx index f9e86110c2..df23328b3f 100644 --- a/plugins/kubernetes-react/src/components/ErrorReporting/ErrorReporting.tsx +++ b/plugins/kubernetes-react/src/components/ErrorReporting/ErrorReporting.tsx @@ -13,12 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { useMemo } from 'react'; import { ClusterAttributes, DetectedError, DetectedErrorsByCluster, } from '@backstage/plugin-kubernetes-common'; import { Table, TableColumn } from '@backstage/core-components'; +import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; +import { kubernetesReactTranslationRef } from '../../translation'; /** * @@ -30,36 +33,6 @@ export type ErrorReportingProps = { clusters: ClusterAttributes[]; }; -const columns: TableColumn[] = [ - { - title: 'cluster', - width: '10%', - render: (row: Row) => row.cluster.title || row.cluster.name, - }, - { - title: 'namespace', - width: '10%', - render: (row: Row) => row.error.sourceRef.namespace, - }, - { - title: 'kind', - width: '10%', - render: (row: Row) => row.error.sourceRef.kind, - }, - { - title: 'name', - width: '30%', - render: (row: Row) => { - return <>{row.error.sourceRef.name} ; - }, - }, - { - title: 'messages', - width: '40%', - render: (row: Row) => row.error.message, - }, -]; - interface Row { cluster: ClusterAttributes; error: DetectedError; @@ -83,6 +56,7 @@ export const ErrorReporting = ({ detectedErrors, clusters, }: ErrorReportingProps) => { + const { t } = useTranslationRef(kubernetesReactTranslationRef); const errors = Array.from(detectedErrors.entries()) .flatMap(([clusterName, resourceErrors]) => { return resourceErrors.map(e => ({ @@ -92,11 +66,44 @@ export const ErrorReporting = ({ }) .sort(sortBySeverity); + const columns: TableColumn[] = useMemo( + () => [ + { + title: String(t('errorReporting.columns.cluster')), + width: '10%', + render: (row: Row) => row.cluster.title || row.cluster.name, + }, + { + title: String(t('errorReporting.columns.namespace')), + width: '10%', + render: (row: Row) => row.error.sourceRef.namespace, + }, + { + title: String(t('errorReporting.columns.kind')), + width: '10%', + render: (row: Row) => row.error.sourceRef.kind, + }, + { + title: String(t('errorReporting.columns.name')), + width: '30%', + render: (row: Row) => { + return <>{row.error.sourceRef.name} ; + }, + }, + { + title: String(t('errorReporting.columns.messages')), + width: '40%', + render: (row: Row) => row.error.message, + }, + ], + [t], + ); + return ( <> {errors.length !== 0 && ( { + const { t } = useTranslationRef(kubernetesReactTranslationRef); const namespace = ingress.metadata?.namespace; return ( {namespace && ( - + )} diff --git a/plugins/kubernetes-react/src/components/KubernetesDialog/KubernetesDialog.tsx b/plugins/kubernetes-react/src/components/KubernetesDialog/KubernetesDialog.tsx index d1a2a8a825..182265d23e 100644 --- a/plugins/kubernetes-react/src/components/KubernetesDialog/KubernetesDialog.tsx +++ b/plugins/kubernetes-react/src/components/KubernetesDialog/KubernetesDialog.tsx @@ -22,6 +22,8 @@ import IconButton from '@material-ui/core/IconButton'; import { Theme, createStyles, makeStyles } from '@material-ui/core/styles'; import CloseIcon from '@material-ui/icons/Close'; import { ReactNode, useState } from 'react'; +import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; +import { kubernetesReactTranslationRef } from '../../translation'; const useStyles = makeStyles((theme: Theme) => createStyles({ @@ -59,6 +61,7 @@ export const KubernetesDialog = ({ title, }: KubernetesDialogProps) => { const classes = useStyles(); + const { t } = useTranslationRef(kubernetesReactTranslationRef); const [open, setOpen] = useState(false); const openDialog = () => { @@ -80,7 +83,7 @@ export const KubernetesDialog = ({ {title} diff --git a/plugins/kubernetes-react/src/components/KubernetesDrawer/KubernetesDrawer.tsx b/plugins/kubernetes-react/src/components/KubernetesDrawer/KubernetesDrawer.tsx index 323ce650d9..aa0609ca06 100644 --- a/plugins/kubernetes-react/src/components/KubernetesDrawer/KubernetesDrawer.tsx +++ b/plugins/kubernetes-react/src/components/KubernetesDrawer/KubernetesDrawer.tsx @@ -31,6 +31,8 @@ import { } from '@material-ui/core/styles'; import CloseIcon from '@material-ui/icons/Close'; import { ManifestYaml } from './ManifestYaml'; +import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; +import { kubernetesReactTranslationRef } from '../../translation'; const useDrawerContentStyles = makeStyles((_theme: Theme) => createStyles({ @@ -72,6 +74,7 @@ const KubernetesDrawerContent = ({ close, }: KubernetesDrawerContentProps) => { const classes = useDrawerContentStyles(); + const { t } = useTranslationRef(kubernetesReactTranslationRef); const [isYaml, setIsYaml] = useState(false); return ( @@ -86,7 +89,7 @@ const KubernetesDrawerContent = ({ close()} color="inherit" > @@ -104,10 +107,10 @@ const KubernetesDrawerContent = ({ onChange={event => { setIsYaml(event.target.checked); }} - name="YAML" + name={t('kubernetesDrawer.yaml')} /> } - label="YAML" + label={t('kubernetesDrawer.yaml')} /> diff --git a/plugins/kubernetes-react/src/components/KubernetesDrawer/KubernetesStructuredMetadataTableDrawer.tsx b/plugins/kubernetes-react/src/components/KubernetesDrawer/KubernetesStructuredMetadataTableDrawer.tsx index fb52063177..fb9977fb4a 100644 --- a/plugins/kubernetes-react/src/components/KubernetesDrawer/KubernetesStructuredMetadataTableDrawer.tsx +++ b/plugins/kubernetes-react/src/components/KubernetesDrawer/KubernetesStructuredMetadataTableDrawer.tsx @@ -38,6 +38,8 @@ import { ManifestYaml } from './ManifestYaml'; import { useApi } from '@backstage/core-plugin-api'; import { kubernetesClusterLinkFormatterApiRef } from '../../api'; import useAsync from 'react-use/esm/useAsync'; +import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; +import { kubernetesReactTranslationRef } from '../../translation'; const useDrawerStyles = makeStyles((theme: Theme) => createStyles({ @@ -102,20 +104,24 @@ export type LinkErrorPanelProps = { export const LinkErrorPanel = ({ cluster, errorMessage, -}: LinkErrorPanelProps) => ( - - {errorMessage && ( - Errors: {errorMessage} - )} - -); +}: LinkErrorPanelProps) => { + const { t } = useTranslationRef(kubernetesReactTranslationRef); + return ( + + {errorMessage && ( + + {t('linkErrorPanel.errorsLabel')} {errorMessage} + + )} + + ); +}; /** * @@ -152,6 +158,7 @@ const KubernetesStructuredMetadataTableDrawerContent = < renderObject, kind, }: KubernetesStructuredMetadataTableDrawerContentProps) => { + const { t } = useTranslationRef(kubernetesReactTranslationRef); const [isYaml, setIsYaml] = useState(false); const formatter = useApi(kubernetesClusterLinkFormatterApiRef); @@ -175,13 +182,13 @@ const KubernetesStructuredMetadataTableDrawerContent = < - {object.metadata?.name ?? 'unknown name'} + {object.metadata?.name ?? t('kubernetesDrawer.unknownName')} toggleDrawer(e, false)} color="inherit" > @@ -201,10 +208,10 @@ const KubernetesStructuredMetadataTableDrawerContent = < onChange={event => { setIsYaml(event.target.checked); }} - name="YAML" + name={t('kubernetesDrawer.yaml')} /> } - label="YAML" + label={t('kubernetesDrawer.yaml')} /> diff --git a/plugins/kubernetes-react/src/components/KubernetesDrawer/ManifestYaml.tsx b/plugins/kubernetes-react/src/components/KubernetesDrawer/ManifestYaml.tsx index 3ccd32fcfb..604efc3055 100644 --- a/plugins/kubernetes-react/src/components/KubernetesDrawer/ManifestYaml.tsx +++ b/plugins/kubernetes-react/src/components/KubernetesDrawer/ManifestYaml.tsx @@ -18,6 +18,8 @@ import FormControlLabel from '@material-ui/core/FormControlLabel'; import Switch from '@material-ui/core/Switch'; import jsyaml from 'js-yaml'; import { useState } from 'react'; +import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; +import { kubernetesReactTranslationRef } from '../../translation'; /** * Props of ManifestYaml @@ -34,6 +36,7 @@ export interface ManifestYamlProps { * @public */ export const ManifestYaml = ({ object }: ManifestYamlProps) => { + const { t } = useTranslationRef(kubernetesReactTranslationRef); // Toggle whether the Kubernetes resource managed fields should be shown in // the YAML display. This toggle is only available when the YAML is being // shown because managed fields are never visible in the structured display. @@ -47,10 +50,10 @@ export const ManifestYaml = ({ object }: ManifestYamlProps) => { onChange={event => { setManagedFields(event.target.checked); }} - name="Managed Fields" + name={t('kubernetesDrawer.managedFields')} /> } - label="Managed Fields" + label={t('kubernetesDrawer.managedFields')} /> { const { cluster, containerName, podName } = props; - + const { t } = useTranslationRef(kubernetesReactTranslationRef); const isPodExecTerminalSupported = useIsPodExecTerminalSupported(); return ( !isPodExecTerminalSupported.loading && isPodExecTerminalSupported.value && ( } - buttonText="Terminal" + buttonText={t('podExecTerminal.buttonText')} disabled={ isPodExecTerminalSupported.loading || !isPodExecTerminalSupported.value } - title={`${podName} - ${containerName} terminal shell on cluster ${ - cluster.title || cluster.name - }`} + title={t('podExecTerminal.titleTemplate', { + podName, + containerName, + clusterName: cluster.title || cluster.name, + })} > diff --git a/plugins/kubernetes-react/src/components/Pods/ErrorList/ErrorList.test.tsx b/plugins/kubernetes-react/src/components/Pods/ErrorList/ErrorList.test.tsx index e460abea8b..ec7b24644a 100644 --- a/plugins/kubernetes-react/src/components/Pods/ErrorList/ErrorList.test.tsx +++ b/plugins/kubernetes-react/src/components/Pods/ErrorList/ErrorList.test.tsx @@ -17,44 +17,57 @@ import { render } from '@testing-library/react'; import { ErrorList } from './ErrorList'; import { Pod } from 'kubernetes-models/v1'; +import { TestApiProvider, mockApis } from '@backstage/test-utils'; +import { translationApiRef } from '@backstage/core-plugin-api/alpha'; +import { errorApiRef } from '@backstage/core-plugin-api'; describe('ErrorList', () => { + const translationApi = mockApis.translation(); + const errorApi = { post: jest.fn(), error$: jest.fn() }; + it('error highlight should render', () => { const { getByText } = render( - + , + ], + }, + ]} + /> + , ); expect(getByText('some-pod')).toBeInTheDocument(); expect(getByText('some error message')).toBeInTheDocument(); diff --git a/plugins/kubernetes-react/src/components/Pods/Events/Events.test.tsx b/plugins/kubernetes-react/src/components/Pods/Events/Events.test.tsx index 3f70bb700a..b74929a2e5 100644 --- a/plugins/kubernetes-react/src/components/Pods/Events/Events.test.tsx +++ b/plugins/kubernetes-react/src/components/Pods/Events/Events.test.tsx @@ -17,63 +17,25 @@ import { EventsContent } from './Events'; import { render } from '@testing-library/react'; import { Event } from 'kubernetes-models/v1'; import { DateTime } from 'luxon'; +import { TestApiProvider, mockApis } from '@backstage/test-utils'; +import { translationApiRef } from '@backstage/core-plugin-api/alpha'; +import { errorApiRef } from '@backstage/core-plugin-api'; describe('EventsContent', () => { const oneHourAgo = DateTime.now().minus({ hours: 1 }).toISO(); + const translationApi = mockApis.translation(); + const errorApi = { post: jest.fn(), error$: jest.fn() }; + it('should show info events', () => { const { getByText } = render( - , - ); - expect(getByText('First event 1 hour ago (count: 52)')).toBeInTheDocument(); - expect(getByText('something happened: hello there')).toBeInTheDocument(); - }); - it('should show warning events', () => { - const { getByText } = render( - , - ); - expect(getByText('First event 1 hour ago (count: 23)')).toBeInTheDocument(); - expect(getByText('something happened: uh oh')).toBeInTheDocument(); - }); - - it('should only show warning events when warningEventsOnly set', () => { - const { getByText, queryByText } = render( - + { metadata: { creationTimestamp: oneHourAgo, }, - }, - ] as Event[] - } - />, + } as Event, + ]} + /> + , + ); + expect(getByText('First event 1 hour ago (count: 52)')).toBeInTheDocument(); + expect(getByText('something happened: hello there')).toBeInTheDocument(); + }); + it('should show warning events', () => { + const { getByText } = render( + + + , + ); + expect(getByText('First event 1 hour ago (count: 23)')).toBeInTheDocument(); + expect(getByText('something happened: uh oh')).toBeInTheDocument(); + }); + + it('should only show warning events when warningEventsOnly set', () => { + const { getByText, queryByText } = render( + + + , ); expect(queryByText('First event 1 hour ago (count: 52)')).toBeNull(); expect(queryByText('something happened: hello there')).toBeNull(); diff --git a/plugins/kubernetes-react/src/components/Pods/Events/Events.tsx b/plugins/kubernetes-react/src/components/Pods/Events/Events.tsx index 94fc768e61..dc6abb6fd9 100644 --- a/plugins/kubernetes-react/src/components/Pods/Events/Events.tsx +++ b/plugins/kubernetes-react/src/components/Pods/Events/Events.tsx @@ -31,6 +31,8 @@ import { useEvents } from './useEvents'; import Skeleton from '@material-ui/lab/Skeleton'; import { DismissableBanner } from '@backstage/core-components'; import { Event } from 'kubernetes-models/v1'; +import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; +import { kubernetesReactTranslationRef } from '../../../translation'; /** * Props for Events @@ -59,8 +61,9 @@ export const EventsContent = ({ events, warningEventsOnly, }: EventsContentProps) => { + const { t } = useTranslationRef(kubernetesReactTranslationRef); if (events.length === 0) { - return No events found; + return {t('events.noEventsFound')}; } return ( diff --git a/plugins/kubernetes-react/src/components/Pods/FixDialog/FixDialog.tsx b/plugins/kubernetes-react/src/components/Pods/FixDialog/FixDialog.tsx index 644c17606b..65961dc22d 100644 --- a/plugins/kubernetes-react/src/components/Pods/FixDialog/FixDialog.tsx +++ b/plugins/kubernetes-react/src/components/Pods/FixDialog/FixDialog.tsx @@ -35,6 +35,8 @@ import { Events } from '../Events'; import { LinkButton } from '@backstage/core-components'; import List from '@material-ui/core/List'; import ListItem from '@material-ui/core/ListItem'; +import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; +import { kubernetesReactTranslationRef } from '../../../translation'; const useStyles = makeStyles((theme: Theme) => createStyles({ @@ -72,6 +74,7 @@ export const FixDialog: FC = ({ }: FixDialogProps) => { const [isOpen, setOpen] = useState(!!open); const classes = useStyles(); + const { t } = useTranslationRef(kubernetesReactTranslationRef); const openDialog = () => { setOpen(true); @@ -87,21 +90,27 @@ export const FixDialog: FC = ({ return ( - Detected error: + {t('fixDialog.detectedError')} {error.message} - Cause explanation: + + {t('fixDialog.causeExplanation')} + - {error.proposedFix?.rootCauseExplanation ?? 'unknown'} + {error.proposedFix?.rootCauseExplanation ?? t('podsTable.unknown')} - Fix: + {t('fixDialog.fix')} {(error.proposedFix?.actions ?? []).map((fix, i) => { return ( - + {fix} ); @@ -112,14 +121,15 @@ export const FixDialog: FC = ({ {pf && pf.type === 'logs' && ( <> - Crash logs: + {t('fixDialog.crashLogs')} = ({ {pf && pf.type === 'events' && ( <> - Events: + {t('fixDialog.events')} = ({ <> - {pod.metadata?.name} - {error.type} + {t('fixDialog.title', { + podName: pod.metadata?.name ?? '', + errorType: error.type, + })} @@ -178,7 +191,7 @@ export const FixDialog: FC = ({ target="_blank" rel="noopener" > - Open docs + {t('fixDialog.openDocs')} )} diff --git a/plugins/kubernetes-react/src/components/Pods/PodDrawer/ContainerCard.tsx b/plugins/kubernetes-react/src/components/Pods/PodDrawer/ContainerCard.tsx index 74ef538bf1..c2b750a7d9 100644 --- a/plugins/kubernetes-react/src/components/Pods/PodDrawer/ContainerCard.tsx +++ b/plugins/kubernetes-react/src/components/Pods/PodDrawer/ContainerCard.tsx @@ -30,6 +30,8 @@ import { bytesToMiB, formatMillicores } from '../../../utils/resources'; import { PodExecTerminalDialog } from '../../PodExecTerminal/PodExecTerminalDialog'; import { ResourceUtilization } from '../../ResourceUtilization'; import { PodLogsDialog, PodScope } from '../PodLogs'; +import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; +import { kubernetesReactTranslationRef } from '../../../translation'; const getContainerHealthChecks = ( containerSpec: IContainer, @@ -112,6 +114,7 @@ export const ContainerCard: FC = ({ containerStatus, containerMetrics, }: ContainerCardProps) => { + const { t } = useTranslationRef(kubernetesReactTranslationRef); const isPodExecTerminalEnabled = useIsPodExecTerminalEnabled(); // This should never be undefined @@ -183,13 +186,13 @@ export const ContainerCard: FC = ({ - Resource utilization + {t('podDrawer.resourceUtilization')} = ({ /> = ({ /> = ({ /> createStyles({ @@ -77,6 +79,7 @@ export const PodDrawer = ({ podAndErrors, open }: PodDrawerProps) => { const classes = useDrawerContentStyles(); const podMetrics = usePodMetrics(podAndErrors.cluster.name, podAndErrors.pod); const isPodDeleteEnabled = useIsPodDeleteEnabled(); + const { t } = useTranslationRef(kubernetesReactTranslationRef); return ( { {podMetrics && ( - Resource utilization + + {t('podDrawer.resourceUtilization')} + { = ({ containerScope, previous, }: PodLogsProps) => { + const { t } = useTranslationRef(kubernetesReactTranslationRef); const { value, error, loading } = usePodLogs({ containerScope, previous, @@ -72,8 +75,8 @@ export const PodLogs: FC = ({ (value.text === '' ? ( ) : ( diff --git a/plugins/kubernetes-react/src/components/Pods/PodLogs/PodLogsDialog.tsx b/plugins/kubernetes-react/src/components/Pods/PodLogs/PodLogsDialog.tsx index cb4ccfdf5d..59524e4ed1 100644 --- a/plugins/kubernetes-react/src/components/Pods/PodLogs/PodLogsDialog.tsx +++ b/plugins/kubernetes-react/src/components/Pods/PodLogs/PodLogsDialog.tsx @@ -18,6 +18,8 @@ import SubjectIcon from '@material-ui/icons/Subject'; import { KubernetesDialog } from '../../KubernetesDialog'; import { PodLogs } from './PodLogs'; import { ContainerScope } from './types'; +import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; +import { kubernetesReactTranslationRef } from '../../../translation'; /** * Props for PodLogsDialog @@ -34,17 +36,19 @@ export interface PodLogsDialogProps { * @public */ export const PodLogsDialog = ({ containerScope }: PodLogsDialogProps) => { + const { t } = useTranslationRef(kubernetesReactTranslationRef); return ( } - buttonText="Logs" + buttonText={t('podLogs.buttonText')} disabled={false} - title={`${containerScope.podName} - ${ - containerScope.containerName - } logs on cluster ${ - containerScope.cluster.title || containerScope.cluster.name - }`} + title={t('podLogs.titleTemplate', { + podName: containerScope.podName, + containerName: containerScope.containerName, + clusterName: + containerScope.cluster.title || containerScope.cluster.name, + })} > diff --git a/plugins/kubernetes-react/src/components/Pods/PodsTable.tsx b/plugins/kubernetes-react/src/components/Pods/PodsTable.tsx index 8d3c14d86a..ee2fce2822 100644 --- a/plugins/kubernetes-react/src/components/Pods/PodsTable.tsx +++ b/plugins/kubernetes-react/src/components/Pods/PodsTable.tsx @@ -30,6 +30,8 @@ import { Pod } from 'kubernetes-models/v1/Pod'; import type { V1Pod } from '@kubernetes/client-node'; import { usePodMetrics } from '../../hooks/usePodMetrics'; import Typography from '@material-ui/core/Typography'; +import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; +import { kubernetesReactTranslationRef } from '../../translation'; /** * @@ -63,22 +65,6 @@ export type PodsTablesProps = { children?: ReactNode; }; -const READY: TableColumn[] = [ - { - title: 'containers ready', - align: 'center', - render: containersReady, - width: 'auto', - }, - { - title: 'total restarts', - align: 'center', - render: totalRestarts, - type: 'numeric', - width: 'auto', - }, -]; - const PodDrawerTrigger = ({ pod }: { pod: Pod }) => { const errors = useMatchingErrors({ kind: 'Pod', @@ -98,9 +84,10 @@ const PodDrawerTrigger = ({ pod }: { pod: Pod }) => { const Cpu = ({ clusterName, pod }: { clusterName: string; pod: Pod }) => { const metrics = usePodMetrics(clusterName, pod); + const { t } = useTranslationRef(kubernetesReactTranslationRef); if (!metrics) { - return unknown; + return {t('podsTable.unknown')}; } return <>{podStatusToCpuUtil(metrics)}; @@ -108,9 +95,10 @@ const Cpu = ({ clusterName, pod }: { clusterName: string; pod: Pod }) => { const Memory = ({ clusterName, pod }: { clusterName: string; pod: Pod }) => { const metrics = usePodMetrics(clusterName, pod); + const { t } = useTranslationRef(kubernetesReactTranslationRef); if (!metrics) { - return unknown; + return {t('podsTable.unknown')}; } return <>{podStatusToMemoryUtil(metrics)}; @@ -123,26 +111,44 @@ const Memory = ({ clusterName, pod }: { clusterName: string; pod: Pod }) => { */ export const PodsTable = ({ pods, extraColumns = [] }: PodsTablesProps) => { const cluster = useContext(ClusterContext); + const { t } = useTranslationRef(kubernetesReactTranslationRef); + + const READY: TableColumn[] = [ + { + title: t('podsTable.columns.containersReady'), + align: 'center', + render: containersReady, + width: 'auto', + }, + { + title: t('podsTable.columns.totalRestarts'), + align: 'center', + render: totalRestarts, + type: 'numeric', + width: 'auto', + }, + ]; + const defaultColumns: TableColumn[] = [ { - title: 'ID', + title: t('podsTable.columns.id'), field: 'metadata.uid', hidden: true, }, { - title: 'name', + title: t('podsTable.columns.name'), highlight: true, render: (pod: Pod) => { return ; }, }, { - title: 'phase', - render: (pod: Pod) => pod.status?.phase ?? 'unknown', + title: t('podsTable.columns.phase'), + render: (pod: Pod) => pod.status?.phase ?? t('podsTable.unknown'), width: 'auto', }, { - title: 'status', + title: t('podsTable.columns.status'), render: containerStatuses, }, ]; @@ -154,14 +160,14 @@ export const PodsTable = ({ pods, extraColumns = [] }: PodsTablesProps) => { if (extraColumns.includes(RESOURCE_COLUMNS)) { const resourceColumns: TableColumn[] = [ { - title: 'CPU usage %', + title: t('podsTable.columns.cpuUsage'), render: (pod: Pod) => { return ; }, width: 'auto', }, { - title: 'Memory usage %', + title: t('podsTable.columns.memoryUsage'), render: (pod: Pod) => { return ; }, diff --git a/plugins/kubernetes-react/src/components/ServicesAccordions/ServiceDrawer.tsx b/plugins/kubernetes-react/src/components/ServicesAccordions/ServiceDrawer.tsx index a9f7b7b5ad..d9d08d04cb 100644 --- a/plugins/kubernetes-react/src/components/ServicesAccordions/ServiceDrawer.tsx +++ b/plugins/kubernetes-react/src/components/ServicesAccordions/ServiceDrawer.tsx @@ -19,6 +19,8 @@ import { KubernetesStructuredMetadataTableDrawer } from '../KubernetesDrawer'; import Typography from '@material-ui/core/Typography'; import Grid from '@material-ui/core/Grid'; import Chip from '@material-ui/core/Chip'; +import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; +import { kubernetesReactTranslationRef } from '../../translation'; export const ServiceDrawer = ({ service, @@ -27,6 +29,7 @@ export const ServiceDrawer = ({ service: V1Service; expanded?: boolean; }) => { + const { t } = useTranslationRef(kubernetesReactTranslationRef); const namespace = service.metadata?.namespace; return ( {namespace && ( - + )} diff --git a/plugins/kubernetes-react/src/components/StatefulSetsAccordions/StatefulSetDrawer.tsx b/plugins/kubernetes-react/src/components/StatefulSetsAccordions/StatefulSetDrawer.tsx index 7b7a2b7bb2..ebb446b40c 100644 --- a/plugins/kubernetes-react/src/components/StatefulSetsAccordions/StatefulSetDrawer.tsx +++ b/plugins/kubernetes-react/src/components/StatefulSetsAccordions/StatefulSetDrawer.tsx @@ -21,6 +21,8 @@ import { renderCondition } from '../../utils/pod'; import Typography from '@material-ui/core/Typography'; import Grid from '@material-ui/core/Grid'; import Chip from '@material-ui/core/Chip'; +import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; +import { kubernetesReactTranslationRef } from '../../translation'; export const StatefulSetDrawer = ({ statefulset, @@ -29,6 +31,7 @@ export const StatefulSetDrawer = ({ statefulset: V1StatefulSet; expanded?: boolean; }) => { + const { t } = useTranslationRef(kubernetesReactTranslationRef); const namespace = statefulset.metadata?.namespace; return ( {namespace && ( - + )} diff --git a/plugins/kubernetes-react/src/components/StatefulSetsAccordions/StatefulSetsAccordions.tsx b/plugins/kubernetes-react/src/components/StatefulSetsAccordions/StatefulSetsAccordions.tsx index e3ae9776bd..e1a33474f2 100644 --- a/plugins/kubernetes-react/src/components/StatefulSetsAccordions/StatefulSetsAccordions.tsx +++ b/plugins/kubernetes-react/src/components/StatefulSetsAccordions/StatefulSetsAccordions.tsx @@ -36,6 +36,8 @@ import { } from '../../hooks'; import { StatusError, StatusOK } from '@backstage/core-components'; import { READY_COLUMNS, RESOURCE_COLUMNS } from '../Pods/PodsTable'; +import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; +import { kubernetesReactTranslationRef } from '../../translation'; type StatefulSetsAccordionsProps = { children?: ReactNode; @@ -62,6 +64,7 @@ const StatefulSetSummary = ({ numberOfPodsWithErrors, hpa, }: StatefulSetSummaryProps) => { + const { t } = useTranslationRef(kubernetesReactTranslationRef); const specCpuUtil = hpa?.spec?.metrics?.find( metric => metric.type === 'Resource' && metric.resource?.name === 'cpu', )?.resource?.target.averageUtilization; @@ -94,18 +97,18 @@ const StatefulSetSummary = ({ > - min replicas {hpa.spec?.minReplicas ?? '?'} / max replicas{' '} - {hpa.spec?.maxReplicas ?? '?'} + {t('hpa.minReplicas')} {hpa.spec?.minReplicas ?? '?'} /{' '} + {t('hpa.maxReplicas')} {hpa.spec?.maxReplicas ?? '?'} - current CPU usage: {cpuUtil ?? '?'}% + {t('hpa.currentCpuUsage')} {cpuUtil ?? '?'}% - target CPU usage: {specCpuUtil ?? '?'}% + {t('hpa.targetCpuUsage')} {specCpuUtil ?? '?'}% @@ -122,16 +125,15 @@ const StatefulSetSummary = ({ spacing={0} > - {numberOfCurrentPods} pods + {t('pods.pods', { count: numberOfCurrentPods })} {numberOfPodsWithErrors > 0 ? ( - {numberOfPodsWithErrors} pod - {numberOfPodsWithErrors > 1 ? 's' : ''} with errors + {t('cluster.podsWithErrors', { count: numberOfPodsWithErrors })} ) : ( - No pods with errors + {t('cluster.noPodsWithErrors')} )} diff --git a/plugins/kubernetes-react/src/translation.ts b/plugins/kubernetes-react/src/translation.ts index 5b8a26b0d5..0420cc04bf 100644 --- a/plugins/kubernetes-react/src/translation.ts +++ b/plugins/kubernetes-react/src/translation.ts @@ -20,10 +20,130 @@ import { createTranslationRef } from '@backstage/core-plugin-api/alpha'; export const kubernetesReactTranslationRef = createTranslationRef({ id: 'kubernetes-react', messages: { + cluster: { + label: 'Cluster', + pods: 'pods', + pods_one: '{{count}} pod', + pods_other: '{{count}} pods', + podsWithErrors: 'pods with errors', + podsWithErrors_one: '{{count}} pod with errors', + podsWithErrors_other: '{{count}} pods with errors', + noPodsWithErrors: 'No pods with errors', + }, + podsTable: { + columns: { + id: 'ID', + name: 'name', + phase: 'phase', + status: 'status', + containersReady: 'containers ready', + totalRestarts: 'total restarts', + cpuUsage: 'CPU usage %', + memoryUsage: 'Memory usage %', + }, + unknown: 'unknown', + status: { + running: 'Running', + ok: 'OK', + }, + }, + errorPanel: { + title: 'There was a problem retrieving Kubernetes objects', + message: + 'There was a problem retrieving some Kubernetes resources for the entity: {{entityName}}. This could mean that the Error Reporting card is not completely accurate.', + errorsLabel: 'Errors', + clusterLabel: 'Cluster', + clusterLabelValue: 'Cluster: {{cluster}}', + fetchError: + 'Error communicating with Kubernetes: {{errorType}}, message: {{message}}', + resourceError: + "Error fetching Kubernetes resource: '{{resourcePath}}', error: {{errorType}}, status code: {{statusCode}}", + }, + fixDialog: { + helpButton: 'Help', + title: '{{podName}} - {{errorType}}', + detectedError: 'Detected error:', + causeExplanation: 'Cause explanation:', + fix: 'Fix:', + crashLogs: 'Crash logs:', + events: 'Events:', + openDocs: 'Open docs', + ariaLabels: { + fixIssue: 'fix issue', + close: 'close', + }, + }, podDrawer: { buttons: { delete: 'Delete Pod', }, + cpuRequests: 'CPU requests', + cpuLimits: 'CPU limits', + memoryRequests: 'Memory requests', + memoryLimits: 'Memory limits', + resourceUtilization: 'Resource utilization', + }, + hpa: { + minReplicas: 'min replicas', + maxReplicas: 'max replicas', + replicasSummary: 'min replicas {{min}} / max replicas {{max}}', + currentCpuUsage: 'current CPU usage:', + currentCpuUsageLabel: 'current CPU usage: {{value}}%', + targetCpuUsage: 'target CPU usage:', + targetCpuUsageLabel: 'target CPU usage: {{value}}%', + }, + pods: { + pods_one: '{{count}} pod', + pods_other: '{{count}} pods', + }, + errorReporting: { + title: 'Error Reporting', + columns: { + cluster: 'cluster', + namespace: 'namespace', + kind: 'kind', + name: 'name', + messages: 'messages', + }, + }, + podLogs: { + title: 'No logs emitted', + description: 'No logs were emitted by the container', + buttonText: 'Logs', + buttonAriaLabel: 'get logs', + titleTemplate: + '{{podName}} - {{containerName}} logs on cluster {{clusterName}}', + }, + podExecTerminal: { + buttonText: 'Terminal', + buttonAriaLabel: 'open terminal', + titleTemplate: + '{{podName}} - {{containerName}} terminal shell on cluster {{clusterName}}', + }, + events: { + noEventsFound: 'No events found', + eventTooltip: '{{eventType}} event', + firstEvent: 'First event {{timeAgo}} (count: {{count}})', + }, + kubernetesDrawer: { + closeDrawer: 'Close the drawer', + yaml: 'YAML', + managedFields: 'Managed Fields', + unknownName: 'unknown name', + }, + linkErrorPanel: { + title: + 'There was a problem formatting the link to the Kubernetes dashboard', + message: + "Could not format the link to the dashboard of your cluster named '{{clusterName}}'. Its dashboardApp property has been set to '{{dashboardApp}}.'", + errorsLabel: 'Errors:', + }, + namespace: { + label: 'namespace:', + labelWithValue: 'namespace: {{namespace}}', + }, + kubernetesDialog: { + closeAriaLabel: 'close', }, }, }); diff --git a/plugins/kubernetes/report-alpha.api.md b/plugins/kubernetes/report-alpha.api.md index 2872ab89e4..16604bc769 100644 --- a/plugins/kubernetes/report-alpha.api.md +++ b/plugins/kubernetes/report-alpha.api.md @@ -180,6 +180,10 @@ export default _default; export const kubernetesTranslationRef: TranslationRef< 'kubernetes', { + readonly 'entityContent.title': 'Kubernetes'; + readonly 'kubernetesContentPage.title': 'Your Clusters'; + readonly 'kubernetesContentPage.emptyState.title': 'No Kubernetes resources'; + readonly 'kubernetesContentPage.emptyState.description': 'No resources on any known clusters for {{entityName}}'; readonly 'kubernetesContentPage.permissionAlert.message': "To view Kubernetes objects, contact your portal administrator to give you the 'kubernetes.clusters.read' and 'kubernetes.resources.read' permission."; readonly 'kubernetesContentPage.permissionAlert.title': 'Permission required'; } diff --git a/plugins/kubernetes/src/KubernetesContent.tsx b/plugins/kubernetes/src/KubernetesContent.tsx index a8d62a5a3d..4f7c7e73e2 100644 --- a/plugins/kubernetes/src/KubernetesContent.tsx +++ b/plugins/kubernetes/src/KubernetesContent.tsx @@ -31,6 +31,8 @@ import { } from '@backstage/plugin-kubernetes-common'; import { EmptyState, Progress } from '@backstage/core-components'; import { RequireKubernetesPermissions } from './RequireKubernetesPermissions'; +import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; +import { kubernetesTranslationRef } from './alpha/translation'; type KubernetesContentProps = { entity: Entity; @@ -46,6 +48,7 @@ export const KubernetesContent = ({ entity, refreshIntervalMs, ); + const { t } = useTranslationRef(kubernetesTranslationRef); const clusters = kubernetesObjects?.items.map(item => item.cluster) ?? []; @@ -97,7 +100,9 @@ export const KubernetesContent = ({ /> - Your Clusters + + {t('kubernetesContentPage.title')} + {kubernetesObjects?.items.length <= 0 && ( @@ -111,8 +116,11 @@ export const KubernetesContent = ({ diff --git a/plugins/kubernetes/src/alpha/translation.ts b/plugins/kubernetes/src/alpha/translation.ts index 7f9704af84..238bd3b084 100644 --- a/plugins/kubernetes/src/alpha/translation.ts +++ b/plugins/kubernetes/src/alpha/translation.ts @@ -25,6 +25,14 @@ export const kubernetesTranslationRef = createTranslationRef({ message: "To view Kubernetes objects, contact your portal administrator to give you the 'kubernetes.clusters.read' and 'kubernetes.resources.read' permission.", }, + title: 'Your Clusters', + emptyState: { + title: 'No Kubernetes resources', + description: 'No resources on any known clusters for {{entityName}}', + }, + }, + entityContent: { + title: 'Kubernetes', }, }, });