diff --git a/.changeset/calm-pans-leave.md b/.changeset/calm-pans-leave.md new file mode 100644 index 0000000000..8c0bf7888d --- /dev/null +++ b/.changeset/calm-pans-leave.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-api-docs': patch +--- + +api-docs plugin support i18n diff --git a/plugins/api-docs/src/components/ApiDefinitionDialog/ApiDefinitionDialog.tsx b/plugins/api-docs/src/components/ApiDefinitionDialog/ApiDefinitionDialog.tsx index 1e5f63e943..4a7a249b3c 100644 --- a/plugins/api-docs/src/components/ApiDefinitionDialog/ApiDefinitionDialog.tsx +++ b/plugins/api-docs/src/components/ApiDefinitionDialog/ApiDefinitionDialog.tsx @@ -29,6 +29,8 @@ import { makeStyles } from '@material-ui/core/styles'; import { ReactNode, useState, useEffect } from 'react'; import { apiDocsConfigRef } from '../../config'; import { PlainApiDefinitionWidget } from '../PlainApiDefinitionWidget'; +import { useTranslationRef } from '@backstage/frontend-plugin-api'; +import { apiDocsTranslationRef } from '../../translation'; const useStyles = makeStyles(theme => ({ fullHeightDialog: { @@ -108,6 +110,7 @@ export function ApiDefinitionDialog(props: { const { open, entity, onClose } = props; const [activeTab, setActiveTab] = useState(0); const classes = useStyles(); + const { t } = useTranslationRef(apiDocsTranslationRef); useEffect(() => { setActiveTab(0); @@ -142,7 +145,7 @@ export function ApiDefinitionDialog(props: { variant="scrollable" value={activeTab} onChange={(_, newValue) => setActiveTab(newValue)} - aria-label="API definition options" + aria-label={t('apiDefinitionDialog.tabsAriaLabel')} className={classes.tabs} > {definitionWidget ? ( @@ -165,7 +168,7 @@ export function ApiDefinitionDialog(props: { diff --git a/plugins/api-docs/src/components/ApiExplorerPage/DefaultApiExplorerPage.tsx b/plugins/api-docs/src/components/ApiExplorerPage/DefaultApiExplorerPage.tsx index 725b088859..625fdbe31c 100644 --- a/plugins/api-docs/src/components/ApiExplorerPage/DefaultApiExplorerPage.tsx +++ b/plugins/api-docs/src/components/ApiExplorerPage/DefaultApiExplorerPage.tsx @@ -41,6 +41,8 @@ import { import { registerComponentRouteRef } from '../../routes'; import { usePermission } from '@backstage/plugin-permission-react'; import { catalogEntityCreatePermission } from '@backstage/plugin-catalog-common/alpha'; +import { useTranslationRef } from '@backstage/frontend-plugin-api'; +import { apiDocsTranslationRef } from '../../translation'; const defaultColumns: TableColumn[] = [ CatalogTable.columns.createTitleColumn({ hidden: true }), @@ -79,9 +81,10 @@ export const DefaultApiExplorerPage = (props: DefaultApiExplorerPageProps) => { } = props; const configApi = useApi(configApiRef); - const generatedSubtitle = `${ - configApi.getOptionalString('organization.name') ?? 'Backstage' - } API Explorer`; + const { t } = useTranslationRef(apiDocsTranslationRef); + const generatedSubtitle = t('defaultApiExplorerPage.subtitle', { + orgName: configApi.getOptionalString('organization.name') ?? 'Backstage', + }); const registerComponentLink = useRouteRef(registerComponentRouteRef); const { allowed } = usePermission({ permission: catalogEntityCreatePermission, @@ -90,19 +93,21 @@ export const DefaultApiExplorerPage = (props: DefaultApiExplorerPageProps) => { return ( {allowed && ( )} - All your APIs + + {t('defaultApiExplorerPage.supportButtonTitle')} + diff --git a/plugins/api-docs/src/components/ApisCards/ConsumedApisCard.tsx b/plugins/api-docs/src/components/ApisCards/ConsumedApisCard.tsx index 9bb48f2dfc..5dc6e4a473 100644 --- a/plugins/api-docs/src/components/ApisCards/ConsumedApisCard.tsx +++ b/plugins/api-docs/src/components/ApisCards/ConsumedApisCard.tsx @@ -21,7 +21,7 @@ import { useEntity, useRelatedEntities, } from '@backstage/plugin-catalog-react'; -import { apiEntityColumns } from './presets'; +import { getApiEntityColumns } from './presets'; import { CodeSnippet, InfoCard, @@ -32,6 +32,8 @@ import { TableOptions, WarningPanel, } from '@backstage/core-components'; +import { useTranslationRef } from '@backstage/frontend-plugin-api'; +import { apiDocsTranslationRef } from '../../translation'; /** * @public @@ -42,10 +44,11 @@ export const ConsumedApisCard = (props: { columns?: TableColumn[]; tableOptions?: TableOptions; }) => { + const { t } = useTranslationRef(apiDocsTranslationRef); const { variant = 'gridItem', - title = 'Consumed APIs', - columns = apiEntityColumns, + title = t('consumedApisCard.title'), + columns = getApiEntityColumns(t), tableOptions = {}, } = props; const { entity } = useEntity(); @@ -66,7 +69,7 @@ export const ConsumedApisCard = (props: { } /> @@ -80,15 +83,16 @@ export const ConsumedApisCard = (props: { emptyContent={
- This {entity.kind.toLocaleLowerCase('en-US')} does not consume any - APIs. + {t('consumedApisCard.emptyContent.title', { + entity: entity.kind.toLocaleLowerCase('en-US'), + })} - Learn how to change this + {t('apisCardHelpLinkTitle')}
diff --git a/plugins/api-docs/src/components/ApisCards/HasApisCard.tsx b/plugins/api-docs/src/components/ApisCards/HasApisCard.tsx index aa0fb82be2..f0777e87a8 100644 --- a/plugins/api-docs/src/components/ApisCards/HasApisCard.tsx +++ b/plugins/api-docs/src/components/ApisCards/HasApisCard.tsx @@ -21,6 +21,7 @@ import { useEntity, useRelatedEntities, } from '@backstage/plugin-catalog-react'; +import { useMemo } from 'react'; import { createSpecApiTypeColumn } from './presets'; import { CodeSnippet, @@ -32,14 +33,8 @@ import { TableOptions, WarningPanel, } from '@backstage/core-components'; - -const presetColumns: TableColumn[] = [ - EntityTable.columns.createEntityRefColumn({ defaultKind: 'API' }), - EntityTable.columns.createOwnerColumn(), - createSpecApiTypeColumn(), - EntityTable.columns.createSpecLifecycleColumn(), - EntityTable.columns.createMetadataDescriptionColumn(), -]; +import { useTranslationRef } from '@backstage/frontend-plugin-api'; +import { apiDocsTranslationRef } from '../../translation'; /** * @public @@ -50,9 +45,19 @@ export const HasApisCard = (props: { columns?: TableColumn[]; tableOptions?: TableOptions; }) => { + const { t } = useTranslationRef(apiDocsTranslationRef); + const presetColumns: TableColumn[] = useMemo(() => { + return [ + EntityTable.columns.createEntityRefColumn({ defaultKind: 'API' }), + EntityTable.columns.createOwnerColumn(), + createSpecApiTypeColumn(t), + EntityTable.columns.createSpecLifecycleColumn(), + EntityTable.columns.createMetadataDescriptionColumn(), + ]; + }, [t]); const { variant = 'gridItem', - title = 'APIs', + title = t('hasApisCard.title'), columns = presetColumns, tableOptions = {}, } = props; @@ -75,7 +80,7 @@ export const HasApisCard = (props: { } /> @@ -89,12 +94,13 @@ export const HasApisCard = (props: { emptyContent={
- This {entity.kind.toLocaleLowerCase('en-US')} does not contain any - APIs. + {t('hasApisCard.emptyContent.title', { + entity: entity.kind.toLocaleLowerCase('en-US'), + })} - Learn how to change this. + {t('apisCardHelpLinkTitle')}
diff --git a/plugins/api-docs/src/components/ApisCards/ProvidedApisCard.tsx b/plugins/api-docs/src/components/ApisCards/ProvidedApisCard.tsx index 475f9be5c6..784bfe2a2a 100644 --- a/plugins/api-docs/src/components/ApisCards/ProvidedApisCard.tsx +++ b/plugins/api-docs/src/components/ApisCards/ProvidedApisCard.tsx @@ -21,7 +21,7 @@ import { useEntity, useRelatedEntities, } from '@backstage/plugin-catalog-react'; -import { apiEntityColumns } from './presets'; +import { getApiEntityColumns } from './presets'; import { CodeSnippet, InfoCard, @@ -32,6 +32,8 @@ import { TableOptions, WarningPanel, } from '@backstage/core-components'; +import { useTranslationRef } from '@backstage/frontend-plugin-api'; +import { apiDocsTranslationRef } from '../../translation'; /** * @public @@ -42,10 +44,11 @@ export const ProvidedApisCard = (props: { columns?: TableColumn[]; tableOptions?: TableOptions; }) => { + const { t } = useTranslationRef(apiDocsTranslationRef); const { variant = 'gridItem', - title = 'Provided APIs', - columns = apiEntityColumns, + title = t('providedApisCard.title'), + columns = getApiEntityColumns(t), tableOptions = {}, } = props; const { entity } = useEntity(); @@ -66,7 +69,7 @@ export const ProvidedApisCard = (props: { } /> @@ -80,15 +83,16 @@ export const ProvidedApisCard = (props: { emptyContent={
- This {entity.kind.toLocaleLowerCase('en-US')} does not provide any - APIs. + {t('providedApisCard.emptyContent.title', { + entity: entity.kind.toLocaleLowerCase('en-US'), + })} - Learn how to change this + {t('apisCardHelpLinkTitle')}
diff --git a/plugins/api-docs/src/components/ApisCards/presets.tsx b/plugins/api-docs/src/components/ApisCards/presets.tsx index 7a3eefe3a4..8341ce2c2b 100644 --- a/plugins/api-docs/src/components/ApisCards/presets.tsx +++ b/plugins/api-docs/src/components/ApisCards/presets.tsx @@ -22,10 +22,17 @@ import ToggleButton from '@material-ui/lab/ToggleButton'; import { useState } from 'react'; import { ApiTypeTitle } from '../ApiDefinitionCard'; import { ApiDefinitionDialog } from '../ApiDefinitionDialog'; +import { + TranslationFunction, + useTranslationRef, +} from '@backstage/core-plugin-api/alpha'; +import { apiDocsTranslationRef } from '../../translation'; -export function createSpecApiTypeColumn(): TableColumn { +export function createSpecApiTypeColumn( + t: TranslationFunction, +): TableColumn { return { - title: 'Type', + title: t('apiEntityColumns.typeTitle'), field: 'spec.type', render: entity => , }; @@ -33,10 +40,11 @@ export function createSpecApiTypeColumn(): TableColumn { const ApiDefinitionButton = ({ apiEntity }: { apiEntity: ApiEntity }) => { const [dialogOpen, setDialogOpen] = useState(false); + const { t } = useTranslationRef(apiDocsTranslationRef); return ( <> setDialogOpen(!dialogOpen)} value={dialogOpen} > @@ -51,19 +59,25 @@ const ApiDefinitionButton = ({ apiEntity }: { apiEntity: ApiEntity }) => { ); }; -function createApiDefinitionColumn(): TableColumn { +function createApiDefinitionColumn( + t: TranslationFunction, +): TableColumn { return { - title: 'API Definition', + title: t('apiEntityColumns.apiDefinitionTitle'), render: entity => , }; } -export const apiEntityColumns: TableColumn[] = [ - EntityTable.columns.createEntityRefColumn({ defaultKind: 'API' }), - EntityTable.columns.createSystemColumn(), - EntityTable.columns.createOwnerColumn(), - createSpecApiTypeColumn(), - EntityTable.columns.createSpecLifecycleColumn(), - EntityTable.columns.createMetadataDescriptionColumn(), - createApiDefinitionColumn(), -]; +export const getApiEntityColumns = ( + t: TranslationFunction, +): TableColumn[] => { + return [ + EntityTable.columns.createEntityRefColumn({ defaultKind: 'API' }), + EntityTable.columns.createSystemColumn(), + EntityTable.columns.createOwnerColumn(), + createSpecApiTypeColumn(t), + EntityTable.columns.createSpecLifecycleColumn(), + EntityTable.columns.createMetadataDescriptionColumn(), + createApiDefinitionColumn(t), + ]; +}; diff --git a/plugins/api-docs/src/components/ComponentsCards/ConsumingComponentsCard.tsx b/plugins/api-docs/src/components/ComponentsCards/ConsumingComponentsCard.tsx index 876898ebb2..12bc930c37 100644 --- a/plugins/api-docs/src/components/ComponentsCards/ConsumingComponentsCard.tsx +++ b/plugins/api-docs/src/components/ComponentsCards/ConsumingComponentsCard.tsx @@ -33,6 +33,8 @@ import { TableColumn, WarningPanel, } from '@backstage/core-components'; +import { useTranslationRef } from '@backstage/frontend-plugin-api'; +import { apiDocsTranslationRef } from '../../translation'; /** * @public @@ -47,10 +49,11 @@ export const ConsumingComponentsCard = (props: { const { entities, loading, error } = useRelatedEntities(entity, { type: RELATION_API_CONSUMED_BY, }); + const { t } = useTranslationRef(apiDocsTranslationRef); if (loading) { return ( - + ); @@ -58,10 +61,10 @@ export const ConsumingComponentsCard = (props: { if (error || !entities) { return ( - + } /> @@ -70,16 +73,16 @@ export const ConsumingComponentsCard = (props: { return ( - No component consumes this API. + {t('consumingComponentsCard.emptyContent.title')} - Learn how to change this. + {t('apisCardHelpLinkTitle')} diff --git a/plugins/api-docs/src/components/ComponentsCards/ProvidingComponentsCard.tsx b/plugins/api-docs/src/components/ComponentsCards/ProvidingComponentsCard.tsx index e207b1c882..c271c33380 100644 --- a/plugins/api-docs/src/components/ComponentsCards/ProvidingComponentsCard.tsx +++ b/plugins/api-docs/src/components/ComponentsCards/ProvidingComponentsCard.tsx @@ -33,6 +33,8 @@ import { TableColumn, WarningPanel, } from '@backstage/core-components'; +import { useTranslationRef } from '@backstage/frontend-plugin-api'; +import { apiDocsTranslationRef } from '../../translation'; /** @public */ export const ProvidingComponentsCard = (props: { @@ -45,10 +47,11 @@ export const ProvidingComponentsCard = (props: { const { entities, loading, error } = useRelatedEntities(entity, { type: RELATION_API_PROVIDED_BY, }); + const { t } = useTranslationRef(apiDocsTranslationRef); if (loading) { return ( - + ); @@ -56,10 +59,10 @@ export const ProvidingComponentsCard = (props: { if (error || !entities) { return ( - + } /> @@ -68,16 +71,16 @@ export const ProvidingComponentsCard = (props: { return ( - No component provides this API. + {t('providingComponentsCard.emptyContent.title')} - Learn how to change this. + {t('apisCardHelpLinkTitle')} diff --git a/plugins/api-docs/src/translation.ts b/plugins/api-docs/src/translation.ts new file mode 100644 index 0000000000..5fb125849f --- /dev/null +++ b/plugins/api-docs/src/translation.ts @@ -0,0 +1,85 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { createTranslationRef } from '@backstage/frontend-plugin-api'; + +export const apiDocsTranslationRef = createTranslationRef({ + id: 'api-docs', + messages: { + apiDefinitionDialog: { + closeButtonTitle: 'Close', + tabsAriaLabel: 'API definition options', + toggleButtonAriaLabel: 'Toggle API Definition Dialog', + }, + defaultApiExplorerPage: { + title: 'APIs', + subtitle: '{{orgName}} API Explorer', + pageTitleOverride: 'APIs', + createButtonTitle: 'Register Existing API', + supportButtonTitle: 'All your APIs', + }, + consumedApisCard: { + title: 'Consumed APIs', + error: { + title: 'Could not load APIs', + }, + emptyContent: { + title: 'This {{entity}} does not consume any APIs.', + }, + }, + hasApisCard: { + title: 'APIs', + error: { + title: 'Could not load APIs', + }, + emptyContent: { + title: 'This {{entity}} does not contain any APIs.', + }, + }, + providedApisCard: { + title: 'Provided APIs', + error: { + title: 'Could not load APIs', + }, + emptyContent: { + title: 'This {{entity}} does not provide any APIs.', + }, + }, + apiEntityColumns: { + typeTitle: 'Type', + apiDefinitionTitle: 'API Definition', + }, + consumingComponentsCard: { + title: 'Consumers', + error: { + title: 'Could not load components', + }, + emptyContent: { + title: 'No component consumes this API.', + }, + }, + providingComponentsCard: { + title: 'Providers', + error: { + title: 'Could not load components', + }, + emptyContent: { + title: 'No component provides this API.', + }, + }, + apisCardHelpLinkTitle: 'Learn how to change this', + }, +}); diff --git a/plugins/scaffolder/report-alpha.api.md b/plugins/scaffolder/report-alpha.api.md index bca3c37b25..d6c3487aab 100644 --- a/plugins/scaffolder/report-alpha.api.md +++ b/plugins/scaffolder/report-alpha.api.md @@ -413,8 +413,8 @@ export const scaffolderTranslationRef: TranslationRef< readonly 'templateListPage.pageTitle': 'Create a new component'; readonly 'templateListPage.templateGroups.defaultTitle': 'Templates'; readonly 'templateListPage.templateGroups.otherTitle': 'Other Templates'; - readonly 'templateListPage.contentHeader.registerExistingButtonTitle': 'Register Existing Component'; readonly 'templateListPage.contentHeader.supportButtonTitle': 'Create new software components using standard templates. Different templates create different kinds of components (services, websites, documentation, ...).'; + readonly 'templateListPage.contentHeader.registerExistingButtonTitle': 'Register Existing Component'; readonly 'templateListPage.additionalLinksForEntity.viewTechDocsTitle': 'View TechDocs'; readonly 'templateWizardPage.title': 'Create a new component'; readonly 'templateWizardPage.subtitle': 'Create new software components using standard templates in your organization';