From e44fb0c358f6654eb0cb633edb9491832b26e54e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Gon=C3=A7alves=20Temponi?= <61257762+LucasTemponi@users.noreply.github.com> Date: Fri, 6 Dec 2024 13:40:32 -0300 Subject: [PATCH] feat(Internationalization): adds support for internationalization (#497) * feat(Translation): adds translation hook. applies translation on all hardcoded text Signed-off-by: Lucas Temponi * feat(Translation): changeset Signed-off-by: Lucas Temponi * feat(Translation): fix types Signed-off-by: Lucas Temponi --------- Signed-off-by: Lucas Temponi Co-authored-by: Lucas Temponi Co-authored-by: Kurt King --- .changeset/three-sloths-argue.md | 6 + .../announcements-react/src/hooks/index.ts | 1 + .../src/hooks/useAnnouncementsTranslation.ts | 6 + .../announcements-react/src/translation.ts | 150 ++++++++++++++++++ .../Admin/AdminPortal/AdminPortal.tsx | 17 +- .../AnnouncementsContent.tsx | 55 +++++-- .../CategoriesContent/CategoriesContent.tsx | 32 +++- .../AnnouncementForm/AnnouncementForm.tsx | 20 ++- .../AnnouncementForm/CategoryInput.tsx | 22 ++- .../AnnouncementSearchResultListItem.tsx | 8 +- .../AnnouncementsCard/AnnouncementsCard.tsx | 18 ++- .../AnnouncementsPage/AnnouncementsPage.tsx | 22 ++- .../AnnouncementsPage/ContextMenu.tsx | 8 +- .../DeleteAnnouncementDialog.tsx | 10 +- .../AnnouncementsTimeline.tsx | 12 +- .../CategoriesForm/CategoriesForm.tsx | 18 ++- .../CategoriesPage/CategoriesPage.tsx | 31 +++- .../CreateAnnouncementPage.tsx | 6 +- .../EditAnnouncementPage.tsx | 15 +- .../NewAnnouncementBanner.tsx | 4 +- .../NewCategoryDialog/NewCategoryDialog.tsx | 19 ++- 21 files changed, 396 insertions(+), 84 deletions(-) create mode 100644 .changeset/three-sloths-argue.md create mode 100644 plugins/announcements-react/src/hooks/useAnnouncementsTranslation.ts create mode 100644 plugins/announcements-react/src/translation.ts diff --git a/.changeset/three-sloths-argue.md b/.changeset/three-sloths-argue.md new file mode 100644 index 00000000..757c186c --- /dev/null +++ b/.changeset/three-sloths-argue.md @@ -0,0 +1,6 @@ +--- +'@procore-oss/backstage-plugin-announcements-react': minor +'@procore-oss/backstage-plugin-announcements': minor +--- + +Adds internacionalization following Backstage's guidelines diff --git a/plugins/announcements-react/src/hooks/index.ts b/plugins/announcements-react/src/hooks/index.ts index c95fba10..7601c7d1 100644 --- a/plugins/announcements-react/src/hooks/index.ts +++ b/plugins/announcements-react/src/hooks/index.ts @@ -1,2 +1,3 @@ export { useAnnouncements } from './useAnnouncements'; export { useCategories } from './useCategories'; +export { useAnnouncementsTranslation } from './useAnnouncementsTranslation'; diff --git a/plugins/announcements-react/src/hooks/useAnnouncementsTranslation.ts b/plugins/announcements-react/src/hooks/useAnnouncementsTranslation.ts new file mode 100644 index 00000000..91f0dcba --- /dev/null +++ b/plugins/announcements-react/src/hooks/useAnnouncementsTranslation.ts @@ -0,0 +1,6 @@ +import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; +import { translationRef } from '../translation'; + +export const useAnnouncementsTranslation = () => { + return useTranslationRef(translationRef); +}; diff --git a/plugins/announcements-react/src/translation.ts b/plugins/announcements-react/src/translation.ts new file mode 100644 index 00000000..3f47170c --- /dev/null +++ b/plugins/announcements-react/src/translation.ts @@ -0,0 +1,150 @@ +/* + * Copyright 2024 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/core-plugin-api/alpha'; + +/** @public */ +export const translationRef = createTranslationRef({ + id: 'announcements', + messages: { + announcementForm: { + title: 'Title', + excerpt: 'Excerpt', + active: 'Active', + submit: 'Submit', + editAnnouncement: 'Edit announcement', + newAnnouncement: 'New announcement', + categoryInput: { + create: 'Create', + label: 'Category', + }, + }, + announcementsPage: { + newAnnouncement: 'New announcement', + genericNew: 'New', + card: { + by: 'By', + in: 'in', + edit: 'EDIT', + delete: 'DELETE', + }, + grid: { + announcementDeleted: 'Announcement deleted.', + }, + contextMenu: { + admin: 'Admin', + categories: 'Categories', + }, + }, + deleteDialog: { + title: 'Are you sure you want to delete this announcement?', + cancel: 'Cancel', + delete: 'Delete', + }, + announcementsCard: { + seeAll: 'See all', + announcements: 'Announcements', + new: 'New', + in: 'in', + noAnnouncements: 'No announcements yet, want to', + addOne: 'add one', + }, + announcementSearchResultListItem: { + published: 'Published', + announcement: 'Announcement', + }, + announcementsTimeline: { + noAnnouncements: 'No announcements', + error: 'Error', + }, + categoriesForm: { + newCategory: 'New category', + editCategory: 'Edit category', + titleLabel: 'Title', + submit: 'Submit', + }, + categoriesTable: { + categoryDeleted: 'Category deleted.', + slug: 'Slug', + title: 'Title', + actions: 'Actions', + addTooltip: 'Add', + noCategoriesFound: 'No categories found.', + }, + categoriesPage: { + title: 'Categories', + subtitle: 'Manage announcement categories', + }, + createAnnouncementPage: { + alertMessage: 'Announcement created.', + alertMessageWithNewCategory: 'with new category', + }, + editAnnouncementPage: { + updatedMessage: 'Announcement updated.', + notFoundMessage: 'Unable to find announcement', + edit: 'Edit', + }, + newAnnouncementBanner: { + markAsSeen: 'Mark as seen', + }, + newCategoryDialog: { + createdMessage: 'Category created.', + newCategory: 'New category', + title: 'Title', + cancelButton: 'Cancel', + createButton: 'Create', + }, + admin: { + adminPortal: { + announcementsLabels: 'Announcements', + categoriesLabel: 'Categories', + title: 'Admin Portal for Announcements', + subtitle: 'Manage announcements and categories', + }, + announecementsContent: { + alertMessage: 'Announcement created.', + alertMessageWithNewCategory: 'with new category', + cancelButton: 'Cancel', + createButton: 'Create Announcement', + announcements: 'Announcements', + noAnnouncementsFound: 'No announcements found', + table: { + title: 'Title', + body: 'Body', + publisher: 'Publisher', + category: 'Category', + status: 'Status', + actions: 'Actions', + active: 'Active', + inactive: 'Inactive', + }, + }, + categoriesContent: { + createdMessage: 'created', + deletedMessage: 'Category deleted.', + cancelButton: 'Cancel', + createButton: 'Create category', + table: { + categoryDeleted: 'Category deleted.', + slug: 'Slug', + title: 'Title', + actions: 'Actions', + addTooltip: 'Add', + noCategoriesFound: 'No categories found.', + }, + }, + }, + }, +}); diff --git a/plugins/announcements/src/components/Admin/AdminPortal/AdminPortal.tsx b/plugins/announcements/src/components/Admin/AdminPortal/AdminPortal.tsx index 74eae4c6..4ebb37aa 100644 --- a/plugins/announcements/src/components/Admin/AdminPortal/AdminPortal.tsx +++ b/plugins/announcements/src/components/Admin/AdminPortal/AdminPortal.tsx @@ -9,6 +9,7 @@ import TabList from '@mui/lab/TabList'; import TabPanel from '@mui/lab/TabPanel'; import { RequirePermission } from '@backstage/plugin-permission-react'; import { announcementCreatePermission } from '@procore-oss/backstage-plugin-announcements-common'; +import { useAnnouncementsTranslation } from '@procore-oss/backstage-plugin-announcements-react'; const useStyles = makeStyles(() => ({ tabPanel: { @@ -26,6 +27,7 @@ type AdminPortalProps = { const AdminPortalContent = () => { const classes = useStyles(); const [tab, setTab] = useState('announcements'); + const { t } = useAnnouncementsTranslation(); const handleChange = (_event: React.ChangeEvent<{}>, tabValue: string) => { setTab(tabValue); }; @@ -33,8 +35,14 @@ const AdminPortalContent = () => { return ( - - + + @@ -49,12 +57,13 @@ const AdminPortalContent = () => { /** @public */ export const AdminPortal = (props?: AdminPortalProps) => { const { title, subtitle, themeId } = props ?? {}; + const { t } = useAnnouncementsTranslation(); return (
diff --git a/plugins/announcements/src/components/Admin/AnnouncementsContent/AnnouncementsContent.tsx b/plugins/announcements/src/components/Admin/AnnouncementsContent/AnnouncementsContent.tsx index 6ce96531..9bb2f745 100644 --- a/plugins/announcements/src/components/Admin/AnnouncementsContent/AnnouncementsContent.tsx +++ b/plugins/announcements/src/components/Admin/AnnouncementsContent/AnnouncementsContent.tsx @@ -12,6 +12,7 @@ import PreviewIcon from '@mui/icons-material/Preview'; import { announcementsApiRef, CreateAnnouncementRequest, + useAnnouncementsTranslation, useCategories, } from '@procore-oss/backstage-plugin-announcements-react'; import { @@ -41,6 +42,7 @@ export const AnnouncementsContent = () => { const announcementsApi = useApi(announcementsApiRef); const navigate = useNavigate(); const { categories } = useCategories(); + const { t } = useAnnouncementsTranslation(); const { loading: loadingCreatePermission, allowed: canCreateAnnouncement } = usePermission({ @@ -107,7 +109,7 @@ export const AnnouncementsContent = () => { const { category } = request; const slugs = categories.map((c: Category) => c.slug); - let alertMsg = 'Announcement created.'; + let alertMsg = t('admin.announecementsContent.alertMessage') as string; try { if (category) { @@ -116,7 +118,9 @@ export const AnnouncementsContent = () => { }); if (slugs.indexOf(categorySlug) === -1) { alertMsg = alertMsg.replace('.', ''); - alertMsg = `${alertMsg} with new category ${category}.`; + alertMsg = `${alertMsg} ${t( + 'admin.announecementsContent.alertMessage', + )} ${category}.`; await announcementsApi.createCategory({ title: category, @@ -146,37 +150,58 @@ export const AnnouncementsContent = () => { const columns: TableColumn[] = [ { - title: Title, + title: ( + {t('admin.announecementsContent.table.title')} + ), sorting: true, field: 'title', render: rowData => rowData.title, }, { - title: Body, + title: ( + {t('admin.announecementsContent.table.body')} + ), sorting: true, field: 'body', render: rowData => rowData.body, }, { - title: Publisher, + title: ( + + {t('admin.announecementsContent.table.publisher')} + + ), sorting: true, field: 'publisher', render: rowData => rowData.publisher, }, { - title: Category, + title: ( + + {t('admin.announecementsContent.table.category')} + + ), sorting: true, field: 'category', render: rowData => rowData.category?.title ?? '', }, { - title: Status, + title: ( + {t('admin.announecementsContent.table.status')} + ), sorting: true, field: 'category', - render: rowData => (rowData.active ? 'Active' : 'Inactive'), + render: rowData => + rowData.active + ? t('admin.announecementsContent.table.active') + : t('admin.announecementsContent.table.inactive'), }, { - title: Actions, + title: ( + + {t('admin.announecementsContent.table.actions')} + + ), render: rowData => { return ( <> @@ -217,7 +242,9 @@ export const AnnouncementsContent = () => { variant="contained" onClick={() => onCreateButtonClick()} > - {showCreateAnnouncementForm ? 'Cancel' : 'Create Announcement'} + {showCreateAnnouncementForm + ? t('admin.announecementsContent.cancelButton') + : t('admin.announecementsContent.createButton')} @@ -232,11 +259,15 @@ export const AnnouncementsContent = () => { No announcements found} + emptyContent={ + + {t('admin.announecementsContent.noAnnouncementsFound')} + + } /> { const { categories, loading, error, retry: refresh } = useCategories(); const announcementsApi = useApi(announcementsApiRef); const alertApi = useApi(alertApiRef); + const { t } = useAnnouncementsTranslation(); const { isOpen: isDeleteDialogOpen, @@ -61,7 +63,10 @@ export const CategoriesContent = () => { title, }); - alertApi.post({ message: `${title} created`, severity: 'success' }); + alertApi.post({ + message: `${title} ${t('admin.categoriesContent.createdMessage')}`, + severity: 'success', + }); refresh(); } catch (err) { @@ -83,7 +88,10 @@ export const CategoriesContent = () => { try { await announcementsApi.deleteCategory(categoryToDelete!.slug); - alertApi.post({ message: 'Category deleted.', severity: 'success' }); + alertApi.post({ + message: t('admin.categoriesContent.table.categoryDeleted'), + severity: 'success', + }); } catch (err) { alertApi.post({ message: (err as ResponseError).body.error.message, @@ -103,19 +111,23 @@ export const CategoriesContent = () => { const columns: TableColumn[] = [ { - title: Title, + title: ( + {t('admin.categoriesContent.table.title')} + ), sorting: true, field: 'title', render: rowData => rowData.title, }, { - title: Slug, + title: {t('admin.categoriesContent.table.slug')}, sorting: true, field: 'slug', render: rowData => rowData.slug, }, { - title: Actions, + title: ( + {t('admin.categoriesContent.table.actions')} + ), render: rowData => { return ( <> @@ -141,7 +153,9 @@ export const CategoriesContent = () => { variant="contained" onClick={() => onCreateButtonClick()} > - {showNewCategoryForm ? 'Cancel' : 'Create category'} + {showNewCategoryForm + ? t('admin.categoriesContent.cancelButton') + : t('admin.categoriesContent.createButton')} @@ -157,7 +171,11 @@ export const CategoriesContent = () => { options={{ pageSize: 20, search: true }} columns={columns} data={categories ?? []} - emptyContent={No categories found} + emptyContent={ + + {t('admin.categoriesContent.table.noCategoriesFound')} + + } /> diff --git a/plugins/announcements/src/components/AnnouncementForm/AnnouncementForm.tsx b/plugins/announcements/src/components/AnnouncementForm/AnnouncementForm.tsx index 90f32533..860a4e69 100644 --- a/plugins/announcements/src/components/AnnouncementForm/AnnouncementForm.tsx +++ b/plugins/announcements/src/components/AnnouncementForm/AnnouncementForm.tsx @@ -3,7 +3,10 @@ import MDEditor from '@uiw/react-md-editor'; import { InfoCard } from '@backstage/core-components'; import { identityApiRef, useApi } from '@backstage/core-plugin-api'; import makeStyles from '@mui/styles/makeStyles'; -import { CreateAnnouncementRequest } from '@procore-oss/backstage-plugin-announcements-react'; +import { + CreateAnnouncementRequest, + useAnnouncementsTranslation, +} from '@procore-oss/backstage-plugin-announcements-react'; import { Announcement } from '@procore-oss/backstage-plugin-announcements-common'; import CategoryInput from './CategoryInput'; import TextField from '@mui/material/TextField'; @@ -31,6 +34,7 @@ export const AnnouncementForm = ({ }: AnnouncementFormProps) => { const classes = useStyles(); const identityApi = useApi(identityApiRef); + const { t } = useAnnouncementsTranslation(); const [form, setForm] = React.useState({ ...initialData, @@ -70,13 +74,17 @@ export const AnnouncementForm = ({ return (
} - label="Active" + label={t('announcementForm.active')} />
diff --git a/plugins/announcements/src/components/AnnouncementForm/CategoryInput.tsx b/plugins/announcements/src/components/AnnouncementForm/CategoryInput.tsx index 12ac09bc..ba5775f8 100644 --- a/plugins/announcements/src/components/AnnouncementForm/CategoryInput.tsx +++ b/plugins/announcements/src/components/AnnouncementForm/CategoryInput.tsx @@ -2,7 +2,10 @@ import * as React from 'react'; import TextField from '@mui/material/TextField'; import Autocomplete, { createFilterOptions } from '@mui/material/Autocomplete'; import { Category } from '@procore-oss/backstage-plugin-announcements-common'; -import { useCategories } from '@procore-oss/backstage-plugin-announcements-react'; +import { + useAnnouncementsTranslation, + useCategories, +} from '@procore-oss/backstage-plugin-announcements-react'; import CircularProgress from '@mui/material/CircularProgress'; type CategoryInputProps = { @@ -33,11 +36,14 @@ type CategoryInputProps = { const filter = createFilterOptions(); -function prepareCategoryFromInput(inputCategory: Category | string): string { +function prepareCategoryFromInput( + inputCategory: Category | string, + localizedCreate?: string, +): string { return ( typeof inputCategory === 'string' ? inputCategory : inputCategory.title ) - .replace('Create ', '') + .replace(localizedCreate ? `${localizedCreate} ` : 'Create ', '') .replaceAll('"', ''); } @@ -47,6 +53,7 @@ export default function CategoryInput({ initialValue, }: CategoryInputProps) { const { categories, loading: categoriesLoading } = useCategories(); + const { t } = useAnnouncementsTranslation(); return ( { @@ -74,7 +84,7 @@ export default function CategoryInput({ ); if (inputValue !== '' && !isExisting) { filtered.push({ - title: `Create "${inputValue}"`, + title: `${t('announcementForm.categoryInput.create')} "${inputValue}"`, slug: inputValue.toLowerCase(), }); } @@ -96,7 +106,7 @@ export default function CategoryInput({ { const classes = useStyles(); + const { t } = useAnnouncementsTranslation(); if (!result) { return null; @@ -66,7 +68,7 @@ export const AnnouncementSearchResultListItem = ({ const excerpt = ( <> - Published{' '} + {`${t('announcementSearchResultListItem.published')} `} {DateTime.fromISO(document.createdAt).toRelative()} @@ -88,7 +90,9 @@ export const AnnouncementSearchResultListItem = ({ return ( <> - + @@ -89,7 +91,7 @@ export const AnnouncementsCard = ({ {lastSeen < DateTime.fromISO(announcement.created_at) && ( @@ -106,8 +108,7 @@ export const AnnouncementsCard = ({ {DateTime.fromISO(announcement.created_at).toRelative()} {announcement.category && ( <> - {' '} - in{' '} + {` ${t('announcementsCard.in')} `} - No announcements yet, want to{' '} - add one? + {`${t('announcementsCard.noAnnouncements')} `} + + {t('announcementsCard.addOne')} + + ? )} diff --git a/plugins/announcements/src/components/AnnouncementsPage/AnnouncementsPage.tsx b/plugins/announcements/src/components/AnnouncementsPage/AnnouncementsPage.tsx index 8eac897c..d8a53cda 100644 --- a/plugins/announcements/src/components/AnnouncementsPage/AnnouncementsPage.tsx +++ b/plugins/announcements/src/components/AnnouncementsPage/AnnouncementsPage.tsx @@ -42,6 +42,7 @@ import { ContextMenu } from './ContextMenu'; import { announcementsApiRef, useAnnouncements, + useAnnouncementsTranslation, } from '@procore-oss/backstage-plugin-announcements-react'; import Tooltip from '@mui/material/Tooltip'; import IconButton from '@mui/material/IconButton'; @@ -89,6 +90,7 @@ const AnnouncementCard = ({ const viewAnnouncementLink = useRouteRef(announcementViewRouteRef); const editAnnouncementLink = useRouteRef(announcementEditRouteRef); const entityLink = useRouteRef(entityRouteRef); + const { t } = useAnnouncementsTranslation(); const publisherRef = parseEntityRef(announcement.publisher); const title = ( @@ -107,7 +109,7 @@ const AnnouncementCard = ({ ); const subTitle = ( <> - By{' '} + {t('announcementsPage.card.by')}{' '} @@ -116,7 +118,7 @@ const AnnouncementCard = ({ {announcement.category && ( <> {' '} - in{' '} + {t('announcementsPage.card.in')}{' '} @@ -174,7 +176,7 @@ const AnnouncementCard = ({ - EDIT + {t('announcementsPage.card.edit')} )} {!loadingDeletePermission && canDelete && ( @@ -182,7 +184,7 @@ const AnnouncementCard = ({ - DELETE + {t('announcementsPage.card.delete')} )} @@ -237,6 +239,8 @@ const AnnouncementsGrid = ({ { dependencies: [maxPerPage, page, category] }, ); + const { t } = useAnnouncementsTranslation(); + const { isOpen: isDeleteDialogOpen, open: openDeleteDialog, @@ -259,7 +263,10 @@ const AnnouncementsGrid = ({ try { await announcementsApi.deleteAnnouncementByID(announcementToDelete!.id); - alertApi.post({ message: 'Announcement deleted.', severity: 'success' }); + alertApi.post({ + message: t('announcementsPage.grid.announcementDeleted'), + severity: 'success', + }); } catch (err) { alertApi.post({ message: (err as Error).message, severity: 'error' }); } @@ -325,6 +332,7 @@ export const AnnouncementsPage = (props: AnnouncementsPageProps) => { const newAnnouncementLink = useRouteRef(announcementCreateRouteRef); const { loading: loadingCreatePermission, allowed: canCreate } = usePermission({ permission: announcementCreatePermission }); + const { t } = useAnnouncementsTranslation(); const { hideContextMenu, @@ -353,7 +361,9 @@ export const AnnouncementsPage = (props: AnnouncementsPageProps) => { color="primary" variant="contained" > - {buttonOptions ? `New ${buttonOptions.name}` : 'New announcement'} + {buttonOptions + ? `${t('announcementsPage.genericNew')} ${buttonOptions.name}` + : t('announcementsPage.newAnnouncement')} )} diff --git a/plugins/announcements/src/components/AnnouncementsPage/ContextMenu.tsx b/plugins/announcements/src/components/AnnouncementsPage/ContextMenu.tsx index e2fd8963..7fb89f8e 100644 --- a/plugins/announcements/src/components/AnnouncementsPage/ContextMenu.tsx +++ b/plugins/announcements/src/components/AnnouncementsPage/ContextMenu.tsx @@ -15,6 +15,7 @@ import { categoriesListRouteRef, } from '../../routes'; import Box from '@mui/material/Box'; +import { useAnnouncementsTranslation } from '@procore-oss/backstage-plugin-announcements-react'; const useStyles = makeStyles({ button: { @@ -28,6 +29,7 @@ export function ContextMenu() { const announcementsLink = useRouteRef(announcementAdminRouteRef); const categoriesLink = useRouteRef(categoriesListRouteRef); const navigate = useNavigate(); + const { t } = useAnnouncementsTranslation(); const onOpen = (event: React.SyntheticEvent) => { setAnchorEl(event.currentTarget); @@ -63,13 +65,15 @@ export function ContextMenu() { - + navigate(categoriesLink())}> - + diff --git a/plugins/announcements/src/components/AnnouncementsPage/DeleteAnnouncementDialog.tsx b/plugins/announcements/src/components/AnnouncementsPage/DeleteAnnouncementDialog.tsx index 6b0fb301..e8e4836f 100644 --- a/plugins/announcements/src/components/AnnouncementsPage/DeleteAnnouncementDialog.tsx +++ b/plugins/announcements/src/components/AnnouncementsPage/DeleteAnnouncementDialog.tsx @@ -4,6 +4,7 @@ import Dialog from '@mui/material/Dialog'; import DialogActions from '@mui/material/DialogActions'; import DialogTitle from '@mui/material/DialogTitle'; import { announcementDeletePermission } from '@procore-oss/backstage-plugin-announcements-common'; +import { useAnnouncementsTranslation } from '@procore-oss/backstage-plugin-announcements-react'; import React from 'react'; type DeleteAnnouncementDialogProps = { @@ -21,21 +22,20 @@ export const DeleteAnnouncementDialog = ( usePermission({ permission: announcementDeletePermission, }); + const { t } = useAnnouncementsTranslation(); return ( - - Are you sure you want to delete this announcement? - + {t('deleteDialog.title')} - + diff --git a/plugins/announcements/src/components/AnnouncementsTimeline/AnnouncementsTimeline.tsx b/plugins/announcements/src/components/AnnouncementsTimeline/AnnouncementsTimeline.tsx index 7030aba2..1ded9b1c 100644 --- a/plugins/announcements/src/components/AnnouncementsTimeline/AnnouncementsTimeline.tsx +++ b/plugins/announcements/src/components/AnnouncementsTimeline/AnnouncementsTimeline.tsx @@ -13,7 +13,10 @@ import { Link } from 'react-router-dom'; import Stack from '@mui/material/Stack'; import { DateTime } from 'luxon'; import { announcementViewRouteRef } from '../../routes'; -import { useAnnouncements } from '@procore-oss/backstage-plugin-announcements-react'; +import { + useAnnouncements, + useAnnouncementsTranslation, +} from '@procore-oss/backstage-plugin-announcements-react'; import { Progress } from '@backstage/core-components'; import Box from '@mui/material/Box'; import Typography from '@mui/material/Typography'; @@ -82,14 +85,17 @@ export const AnnouncementsTimeline = ({ max: maxResults, active: hideInactive, }); + const { t } = useAnnouncementsTranslation(); if (loading) { return ; } - if (!announcements || announcements.count === 0) return <>No announcements; + if (!announcements || announcements.count === 0) + return <>{t('announcementsTimeline.noAnnouncements')}; - if (error) return <>Error: {error.message}; + if (error) + return <>{`${t('announcementsTimeline.error')}: ${error.message}`}; return ( +
- Submit + {t('categoriesForm.submit')}
diff --git a/plugins/announcements/src/components/CategoriesPage/CategoriesPage.tsx b/plugins/announcements/src/components/CategoriesPage/CategoriesPage.tsx index 26a0fb50..bf42798b 100644 --- a/plugins/announcements/src/components/CategoriesPage/CategoriesPage.tsx +++ b/plugins/announcements/src/components/CategoriesPage/CategoriesPage.tsx @@ -10,7 +10,10 @@ import { import AddIcon from '@mui/icons-material/Add'; import DeleteIcon from '@mui/icons-material/Delete'; import { NewCategoryDialog } from '../NewCategoryDialog'; -import { useCategories } from '@procore-oss/backstage-plugin-announcements-react'; +import { + useAnnouncementsTranslation, + useCategories, +} from '@procore-oss/backstage-plugin-announcements-react'; import { Category } from '@procore-oss/backstage-plugin-announcements-common'; import { useDeleteCategoryDialogState } from './useDeleteCategoryDialogState'; import { alertApiRef, useApi } from '@backstage/core-plugin-api'; @@ -33,6 +36,7 @@ const CategoriesTable = () => { close: closeDeleteDialog, category: categoryToDelete, } = useDeleteCategoryDialogState(); + const { t } = useAnnouncementsTranslation(); if (error) { return ; @@ -52,7 +56,10 @@ const CategoriesTable = () => { try { await announcementsApi.deleteCategory(categoryToDelete!.slug); - alertApi.post({ message: 'Category deleted.', severity: 'success' }); + alertApi.post({ + message: t('categoriesTable.categoryDeleted'), + severity: 'success', + }); } catch (err) { alertApi.post({ message: (err as ResponseError).body.error.message, @@ -65,16 +72,16 @@ const CategoriesTable = () => { const columns: TableColumn[] = [ { - title: 'Slug', + title: t('categoriesTable.slug'), field: 'slug', highlight: true, }, { - title: 'Title', + title: t('categoriesTable.title'), field: 'title', }, { - title: 'Actions', + title: t('categoriesTable.actions'), field: 'actions', render: category => { return ( @@ -97,12 +104,16 @@ const CategoriesTable = () => { actions={[ { icon: () => , - tooltip: 'Add', + tooltip: t('categoriesTable.addTooltip'), isFreeAction: true, onClick: _event => setNewCategoryDialogOpen(true), }, ]} - emptyContent={No categories found} + emptyContent={ + + {t('categoriesTable.noCategoriesFound')} + + } /> { + const { t } = useAnnouncementsTranslation(); return ( -
+
diff --git a/plugins/announcements/src/components/CreateAnnouncementPage/CreateAnnouncementPage.tsx b/plugins/announcements/src/components/CreateAnnouncementPage/CreateAnnouncementPage.tsx index 56c5004f..d5db79ec 100644 --- a/plugins/announcements/src/components/CreateAnnouncementPage/CreateAnnouncementPage.tsx +++ b/plugins/announcements/src/components/CreateAnnouncementPage/CreateAnnouncementPage.tsx @@ -8,6 +8,7 @@ import { AnnouncementForm } from '../AnnouncementForm'; import { CreateAnnouncementRequest, announcementsApiRef, + useAnnouncementsTranslation, useCategories, } from '@procore-oss/backstage-plugin-announcements-react'; import { @@ -27,12 +28,13 @@ export const CreateAnnouncementPage = (props: CreateAnnouncementPageProps) => { const alertApi = useApi(alertApiRef); const navigate = useNavigate(); const { categories } = useCategories(); + const { t } = useAnnouncementsTranslation(); const onSubmit = async (request: CreateAnnouncementRequest) => { const { category } = request; const slugs = categories.map((c: Category) => c.slug); - let alertMsg = 'Announcement created.'; + let alertMsg = t('createAnnouncementPage.alertMessage') as string; try { if (category) { @@ -41,7 +43,7 @@ export const CreateAnnouncementPage = (props: CreateAnnouncementPageProps) => { }); if (slugs.indexOf(categorySlug) === -1) { alertMsg = alertMsg.replace('.', ''); - alertMsg = `${alertMsg} with new category ${category}.`; + alertMsg = `${alertMsg} ${t('createAnnouncementPage.alertMessageWithNewCategory')} ${category}.`; await announcementsApi.createCategory({ title: category, diff --git a/plugins/announcements/src/components/EditAnnouncementPage/EditAnnouncementPage.tsx b/plugins/announcements/src/components/EditAnnouncementPage/EditAnnouncementPage.tsx index a2dab7ee..27465c72 100644 --- a/plugins/announcements/src/components/EditAnnouncementPage/EditAnnouncementPage.tsx +++ b/plugins/announcements/src/components/EditAnnouncementPage/EditAnnouncementPage.tsx @@ -11,6 +11,7 @@ import { announcementEditRouteRef } from '../../routes'; import { announcementsApiRef, CreateAnnouncementRequest, + useAnnouncementsTranslation, } from '@procore-oss/backstage-plugin-announcements-react'; import Alert from '@mui/material/Alert'; @@ -27,6 +28,7 @@ export const EditAnnouncementPage = (props: EditAnnouncementPageProps) => { const { value, loading, error } = useAsync(async () => announcementsApi.announcementByID(id), ); + const { t } = useAnnouncementsTranslation(); let title = props.title; let content: React.ReactNode = ; @@ -34,7 +36,10 @@ export const EditAnnouncementPage = (props: EditAnnouncementPageProps) => { const onSubmit = async (request: CreateAnnouncementRequest) => { try { await announcementsApi.updateAnnouncement(id, request); - alertApi.post({ message: 'Announcement updated.', severity: 'success' }); + alertApi.post({ + message: t('editAnnouncementPage.updatedMessage'), + severity: 'success', + }); } catch (err) { alertApi.post({ message: (err as Error).message, severity: 'error' }); } @@ -45,9 +50,13 @@ export const EditAnnouncementPage = (props: EditAnnouncementPageProps) => { } else if (error) { content = {error.message}; } else if (!value) { - content = Unable to find announcement; + content = ( + + {t('editAnnouncementPage.notFoundMessage')} + + ); } else { - title = `Edit "${value.title}" – ${title}`; + title = `${t('editAnnouncementPage.edit')} "${value.title}" – ${title}`; content = ; } diff --git a/plugins/announcements/src/components/NewAnnouncementBanner/NewAnnouncementBanner.tsx b/plugins/announcements/src/components/NewAnnouncementBanner/NewAnnouncementBanner.tsx index 9a50ef2d..e93278b8 100644 --- a/plugins/announcements/src/components/NewAnnouncementBanner/NewAnnouncementBanner.tsx +++ b/plugins/announcements/src/components/NewAnnouncementBanner/NewAnnouncementBanner.tsx @@ -8,6 +8,7 @@ import { announcementViewRouteRef } from '../../routes'; import { announcementsApiRef, useAnnouncements, + useAnnouncementsTranslation, } from '@procore-oss/backstage-plugin-announcements-react'; import { Announcement, @@ -61,6 +62,7 @@ const AnnouncementBanner = (props: AnnouncementBannerProps) => { const classes = useStyles(); const announcementsApi = useApi(announcementsApiRef); const viewAnnouncementLink = useRouteRef(announcementViewRouteRef); + const { t } = useAnnouncementsTranslation(); const [bannerOpen, setBannerOpen] = useState(true); const variant = props.variant || 'block'; const announcement = props.announcement; @@ -98,7 +100,7 @@ const AnnouncementBanner = (props: AnnouncementBannerProps) => { action={[ { const announcementsApi = useApi(announcementsApiRef); + const { t } = useAnnouncementsTranslation(); const alertApi = useApi(alertApiRef); const [title, setTitle] = React.useState(''); @@ -28,7 +32,10 @@ export const NewCategoryDialog = (props: NewCategoryDialogProps) => { await announcementsApi.createCategory({ title, }); - alertApi.post({ message: 'Category created.', severity: 'success' }); + alertApi.post({ + message: t('newCategoryDialog.createdMessage'), + severity: 'success', + }); props.onClose(); } catch (err) { alertApi.post({ message: (err as Error).message, severity: 'error' }); @@ -41,12 +48,12 @@ export const NewCategoryDialog = (props: NewCategoryDialogProps) => { return ( - New category + {t('newCategoryDialog.newCategory')} { /> - +