diff --git a/.changeset/quick-bugs-speak.md b/.changeset/quick-bugs-speak.md new file mode 100644 index 0000000000..626dba957d --- /dev/null +++ b/.changeset/quick-bugs-speak.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-notifications': patch +--- + +Added i18n support. diff --git a/plugins/notifications/.eslintrc.js b/plugins/notifications/.eslintrc.js index a2d8179106..e487f765b2 100644 --- a/plugins/notifications/.eslintrc.js +++ b/plugins/notifications/.eslintrc.js @@ -1,5 +1,5 @@ module.exports = require('@backstage/cli/config/eslint-factory')(__dirname, { - rules: { - '@backstage/no-top-level-material-ui-4-imports': 'error', - }, + rules: { + '@backstage/no-top-level-material-ui-4-imports': 'error', + }, }); diff --git a/plugins/notifications/knip-report.md b/plugins/notifications/knip-report.md index 97d5b385fd..28921c328d 100644 --- a/plugins/notifications/knip-report.md +++ b/plugins/notifications/knip-report.md @@ -1,3 +1 @@ # Knip report - - diff --git a/plugins/notifications/report-alpha.api.md b/plugins/notifications/report-alpha.api.md index 98d036365e..eb597e1e04 100644 --- a/plugins/notifications/report-alpha.api.md +++ b/plugins/notifications/report-alpha.api.md @@ -13,6 +13,7 @@ import { OverridableExtensionDefinition } from '@backstage/frontend-plugin-api'; import { OverridableFrontendPlugin } from '@backstage/frontend-plugin-api'; import { RouteRef } from '@backstage/core-plugin-api'; import { RouteRef as RouteRef_2 } from '@backstage/frontend-plugin-api'; +import { TranslationRef } from '@backstage/frontend-plugin-api'; // @alpha (undocumented) const _default: OverridableFrontendPlugin< @@ -67,5 +68,70 @@ const _default: OverridableFrontendPlugin< >; export default _default; +// @alpha (undocumented) +export const notificationsTranslationRef: TranslationRef< + 'plugin.notifications', + { + readonly 'table.errors.markAllReadFailed': 'Failed to mark all notifications as read'; + readonly 'table.pagination.firstTooltip': 'First Page'; + readonly 'table.pagination.labelDisplayedRows': '{from}-{to} of {count}'; + readonly 'table.pagination.labelRowsSelect': 'rows'; + readonly 'table.pagination.lastTooltip': 'Last Page'; + readonly 'table.pagination.nextTooltip': 'Next Page'; + readonly 'table.pagination.previousTooltip': 'Previous Page'; + readonly 'table.emptyMessage': 'No records to display'; + readonly 'table.bulkActions.markAllRead': 'Mark all read'; + readonly 'table.bulkActions.markSelectedAsRead': 'Mark selected as read'; + readonly 'table.bulkActions.returnSelectedAmongUnread': 'Return selected among unread'; + readonly 'table.bulkActions.saveSelectedForLater': 'Save selected for later'; + readonly 'table.bulkActions.undoSaveForSelected': 'Undo save for selected'; + readonly 'table.confirmDialog.title': 'Are you sure?'; + readonly 'table.confirmDialog.markAllReadDescription': 'Mark all notifications as read.'; + readonly 'table.confirmDialog.markAllReadConfirmation': 'Mark All'; + readonly 'filters.view.all': 'All'; + readonly 'filters.view.label': 'View'; + readonly 'filters.view.read': 'Read notifications'; + readonly 'filters.view.saved': 'Saved'; + readonly 'filters.view.unread': 'Unread notifications'; + readonly 'filters.title': 'Filters'; + readonly 'filters.severity.normal': 'Normal'; + readonly 'filters.severity.high': 'High'; + readonly 'filters.severity.low': 'Low'; + readonly 'filters.severity.label': 'Min severity'; + readonly 'filters.severity.critical': 'Critical'; + readonly 'filters.topic.label': 'Topic'; + readonly 'filters.topic.anyTopic': 'Any topic'; + readonly 'filters.createdAfter.label': 'Sent out'; + readonly 'filters.createdAfter.placeholder': 'Notifications since'; + readonly 'filters.createdAfter.last24h': 'Last 24h'; + readonly 'filters.createdAfter.lastWeek': 'Last week'; + readonly 'filters.createdAfter.anyTime': 'Any time'; + readonly 'filters.sortBy.origin': 'Origin'; + readonly 'filters.sortBy.label': 'Sort by'; + readonly 'filters.sortBy.placeholder': 'Field to sort by'; + readonly 'filters.sortBy.newest': 'Newest on top'; + readonly 'filters.sortBy.oldest': 'Oldest on top'; + readonly 'filters.sortBy.topic': 'Topic'; + readonly 'settings.table.origin': 'Origin'; + readonly 'settings.table.topic': 'Topic'; + readonly 'settings.title': 'Notification settings'; + readonly 'settings.errors.useNotificationFormat': 'useNotificationFormat must be used within a NotificationFormatProvider'; + readonly 'settings.errorTitle': 'Failed to load settings'; + readonly 'settings.noSettingsAvailable': 'No notification settings available, check back later'; + readonly 'sidebar.title': 'Notifications'; + readonly 'sidebar.errors.markAsReadFailed': 'Failed to mark notification as read'; + readonly 'sidebar.errors.fetchNotificationFailed': 'Failed to fetch notification'; + readonly 'notificationsPage.title': 'Notifications'; + readonly 'notificationsPage.tableTitle.all_one': 'All notifications ({{count}})'; + readonly 'notificationsPage.tableTitle.all_other': 'All notifications ({{count}})'; + readonly 'notificationsPage.tableTitle.saved_one': 'Saved notifications ({{count}})'; + readonly 'notificationsPage.tableTitle.saved_other': 'Saved notifications ({{count}})'; + readonly 'notificationsPage.tableTitle.unread_one': 'Unread notifications ({{count}})'; + readonly 'notificationsPage.tableTitle.unread_other': 'Unread notifications ({{count}})'; + readonly 'notificationsPage.tableTitle.read_one': 'Read notifications ({{count}})'; + readonly 'notificationsPage.tableTitle.read_other': 'Read notifications ({{count}})'; + } +>; + // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/notifications/report.api.md b/plugins/notifications/report.api.md index f42814f983..0d2912638f 100644 --- a/plugins/notifications/report.api.md +++ b/plugins/notifications/report.api.md @@ -13,7 +13,6 @@ import { Notification as Notification_2 } from '@backstage/plugin-notifications- import { NotificationSettings } from '@backstage/plugin-notifications-common'; import { NotificationSeverity } from '@backstage/plugin-notifications-common'; import { NotificationStatus } from '@backstage/plugin-notifications-common'; -import * as React_2 from 'react'; import { RouteRef } from '@backstage/core-plugin-api'; import { TableProps } from '@backstage/core-components'; @@ -111,10 +110,10 @@ export type NotificationSnackbarProperties = { }; dense?: boolean; maxSnack?: number; - snackStyle?: React_2.CSSProperties; - iconVariant?: Partial>; + snackStyle?: React.CSSProperties; + iconVariant?: Partial>; Components?: { - [key in NotificationSeverity]: React_2.JSXElementConstructor; + [key in NotificationSeverity]: React.JSXElementConstructor; }; }; diff --git a/plugins/notifications/src/alpha.tsx b/plugins/notifications/src/alpha.tsx index 32b9ae429a..b930373a46 100644 --- a/plugins/notifications/src/alpha.tsx +++ b/plugins/notifications/src/alpha.tsx @@ -55,3 +55,5 @@ export default createFrontendPlugin({ // TODO(Rugvip): Nav item (i.e. NotificationsSidebarItem) currently needs to be installed manually extensions: [page, api], }); + +export { notificationsTranslationRef } from './translation'; diff --git a/plugins/notifications/src/components/NotificationsFilters/NotificationsFilters.tsx b/plugins/notifications/src/components/NotificationsFilters/NotificationsFilters.tsx index e8fa422fa1..66aacdb72e 100644 --- a/plugins/notifications/src/components/NotificationsFilters/NotificationsFilters.tsx +++ b/plugins/notifications/src/components/NotificationsFilters/NotificationsFilters.tsx @@ -15,10 +15,14 @@ */ import { ChangeEvent } from 'react'; import FormControl from '@material-ui/core/FormControl'; +import Divider from '@material-ui/core/Divider'; import Grid from '@material-ui/core/Grid'; import InputLabel from '@material-ui/core/InputLabel'; import MenuItem from '@material-ui/core/MenuItem'; import Select from '@material-ui/core/Select'; +import Typography from '@material-ui/core/Typography'; +import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; +import { notificationsTranslationRef } from '../../translation'; import { GetNotificationsOptions } from '../../api'; import { NotificationSeverity } from '@backstage/plugin-notifications-common'; @@ -44,58 +48,53 @@ export type NotificationsFiltersProps = { const ALL = '___all___'; -export const CreatedAfterOptions: { - [key: string]: { label: string; getDate: () => Date }; -} = { +type TranslationKey = keyof (typeof notificationsTranslationRef)['T']; + +export const CreatedAfterOptions = { last24h: { - label: 'Last 24h', + labelKey: 'filters.createdAfter.last24h' satisfies TranslationKey, getDate: () => new Date(Date.now() - 24 * 3600 * 1000), }, lastWeek: { - label: 'Last week', + labelKey: 'filters.createdAfter.lastWeek' satisfies TranslationKey, getDate: () => new Date(Date.now() - 7 * 24 * 3600 * 1000), }, all: { - label: 'Any time', + labelKey: 'filters.createdAfter.anyTime' satisfies TranslationKey, getDate: () => new Date(0), }, -}; +} as const; -export const SortByOptions: { - [key: string]: { - label: string; - sortBy: SortBy; - }; -} = { +export const SortByOptions = { newest: { - label: 'Newest on top', + labelKey: 'filters.sortBy.newest' satisfies TranslationKey, sortBy: { - sort: 'created', - sortOrder: 'desc', + sort: 'created' as const, + sortOrder: 'desc' as const, }, }, oldest: { - label: 'Oldest on top', + labelKey: 'filters.sortBy.oldest' satisfies TranslationKey, sortBy: { - sort: 'created', - sortOrder: 'asc', + sort: 'created' as const, + sortOrder: 'asc' as const, }, }, topic: { - label: 'Topic', + labelKey: 'filters.sortBy.topic' satisfies TranslationKey, sortBy: { - sort: 'topic', - sortOrder: 'asc', + sort: 'topic' as const, + sortOrder: 'asc' as const, }, }, origin: { - label: 'Origin', + labelKey: 'filters.sortBy.origin' satisfies TranslationKey, sortBy: { - sort: 'origin', - sortOrder: 'asc', + sort: 'origin' as const, + sortOrder: 'asc' as const, }, }, -}; +} as const; const getSortByText = (sortBy?: SortBy): string => { if (sortBy?.sort === 'created' && sortBy?.sortOrder === 'asc') { @@ -111,13 +110,6 @@ const getSortByText = (sortBy?: SortBy): string => { return 'newest'; }; -const AllSeverityOptions: { [key in NotificationSeverity]: string } = { - critical: 'Critical', - high: 'High', - normal: 'Normal', - low: 'Low', -}; - export const NotificationsFilters = ({ sorting, onSortingChanged, @@ -133,6 +125,7 @@ export const NotificationsFilters = ({ onTopicChanged, allTopics, }: NotificationsFiltersProps) => { + const { t } = useTranslationRef(notificationsTranslationRef); const sortByText = getSortByText(sorting); const handleOnCreatedAfterChanged = ( @@ -163,7 +156,8 @@ export const NotificationsFilters = ({ const handleOnSortByChanged = ( event: ChangeEvent<{ name?: string; value: unknown }>, ) => { - const idx = (event.target.value as string) || 'newest'; + const idx = ((event.target.value as string) || + 'newest') as keyof typeof SortByOptions; const option = SortByOptions[idx]; onSortingChanged({ ...option.sortBy }); }; @@ -197,37 +191,49 @@ export const NotificationsFilters = ({ return ( <> + + {t('filters.title')} + + + - View + + {t('filters.view.label')} + - Sent out + + {t('filters.createdAfter.label')} + @@ -236,18 +242,20 @@ export const NotificationsFilters = ({ - Sort by + + {t('filters.sortBy.label')} + @@ -257,18 +265,20 @@ export const NotificationsFilters = ({ - Min severity + {t('filters.severity.label')} @@ -277,16 +287,18 @@ export const NotificationsFilters = ({ - Topic + + {t('filters.topic.label')} +