diff --git a/src/frontend/apps/impress/src/components/ErrorPage.tsx b/src/frontend/apps/impress/src/components/ErrorPage.tsx
index e3c789448..9e013ad6a 100644
--- a/src/frontend/apps/impress/src/components/ErrorPage.tsx
+++ b/src/frontend/apps/impress/src/components/ErrorPage.tsx
@@ -47,18 +47,23 @@ export const ErrorPage = ({
}: ErrorPageProps) => {
const { t } = useTranslation();
- const errorTitle = t('An unexpected error occurred.');
+ const errorTitle = t('An unexpected error occurred.', {
+ description: 'Generic error page title and message',
+ });
const safeTarget = getSafeRefreshUrl(refreshTarget);
return (
<>
- {errorTitle} - {t('Docs')}
+ {errorTitle} -{' '}
+ {t('Docs', {
+ description: 'Product name, shown in page titles and the logo',
+ })}
@@ -69,7 +74,10 @@ export const ErrorPage = ({
$padding={{ bottom: '2rem' }}
>
- {errorTitle} - {t('Docs')}
+ {errorTitle} -{' '}
+ {t('Docs', {
+ description: 'Product name, shown in page titles and the logo',
+ })}
}
>
- {t('Home')}
+ {t('Home', {
+ description: 'Button/link label to go back to the home page',
+ })}
@@ -125,7 +135,9 @@ export const ErrorPage = ({
: window.location.reload()
}
>
- {t('Refresh page')}
+ {t('Refresh page', {
+ description: 'Button on the error page to reload the page',
+ })}
)}
diff --git a/src/frontend/apps/impress/src/components/InfiniteScroll.tsx b/src/frontend/apps/impress/src/components/InfiniteScroll.tsx
index 9b47a1c6b..7b81ed5f3 100644
--- a/src/frontend/apps/impress/src/components/InfiniteScroll.tsx
+++ b/src/frontend/apps/impress/src/components/InfiniteScroll.tsx
@@ -43,7 +43,10 @@ export const InfiniteScroll = ({
variant="bordered"
icon={}
>
- {buttonLabel ?? t('Load more')}
+ {buttonLabel ??
+ t('Load more', {
+ description: 'Button to load the next items of a list',
+ })}
)}
diff --git a/src/frontend/apps/impress/src/components/Loading.tsx b/src/frontend/apps/impress/src/components/Loading.tsx
index 50b1229ae..699e22970 100644
--- a/src/frontend/apps/impress/src/components/Loading.tsx
+++ b/src/frontend/apps/impress/src/components/Loading.tsx
@@ -30,7 +30,9 @@ export const LoadMoreText = ({
$size="md"
/>
- {t('Load more')}
+ {t('Load more', {
+ description: 'Button to load the next items of a list',
+ })}
);
diff --git a/src/frontend/apps/impress/src/components/SkipToContent.tsx b/src/frontend/apps/impress/src/components/SkipToContent.tsx
index 96fab2ada..d08026af4 100644
--- a/src/frontend/apps/impress/src/components/SkipToContent.tsx
+++ b/src/frontend/apps/impress/src/components/SkipToContent.tsx
@@ -76,7 +76,10 @@ export const SkipToContent = () => {
whiteSpace: 'nowrap',
}}
>
- {t('Go to content')}
+ {t('Go to content', {
+ description:
+ 'Skip link for keyboard users to jump to the main content',
+ })}
);
diff --git a/src/frontend/apps/impress/src/components/TextErrors.tsx b/src/frontend/apps/impress/src/components/TextErrors.tsx
index 60372c8a8..0c54071dd 100644
--- a/src/frontend/apps/impress/src/components/TextErrors.tsx
+++ b/src/frontend/apps/impress/src/components/TextErrors.tsx
@@ -64,7 +64,10 @@ export const TextOnlyErrors = ({
{!causes && (
- {defaultMessage || t('Something bad happens, please retry.')}
+ {defaultMessage ||
+ t('Something bad happens, please retry.', {
+ description: 'Generic error message shown when a request fails',
+ })}
)}
diff --git a/src/frontend/apps/impress/src/components/Title.tsx b/src/frontend/apps/impress/src/components/Title.tsx
index f6ec1cf69..2a7a8a5a4 100644
--- a/src/frontend/apps/impress/src/components/Title.tsx
+++ b/src/frontend/apps/impress/src/components/Title.tsx
@@ -25,7 +25,9 @@ export const Title = ({
$color="var(--c--contextuals--content--logo1)"
{...props}
>
- {t('Docs')}
+ {t('Docs', {
+ description: 'Product name, shown in page titles and the logo',
+ })}
);
};
diff --git a/src/frontend/apps/impress/src/components/Waffle.tsx b/src/frontend/apps/impress/src/components/Waffle.tsx
index 21f68fb47..5cf0459e6 100644
--- a/src/frontend/apps/impress/src/components/Waffle.tsx
+++ b/src/frontend/apps/impress/src/components/Waffle.tsx
@@ -131,8 +131,14 @@ export const Waffle = () => {
>
);
diff --git a/src/frontend/apps/impress/src/components/modal/AlertModal.tsx b/src/frontend/apps/impress/src/components/modal/AlertModal.tsx
index 4917ed828..3735fda5a 100644
--- a/src/frontend/apps/impress/src/components/modal/AlertModal.tsx
+++ b/src/frontend/apps/impress/src/components/modal/AlertModal.tsx
@@ -56,20 +56,27 @@ export const AlertModal = ({
rightActions={
}
diff --git a/src/frontend/apps/impress/src/components/quick-search/QuickSearchInput.tsx b/src/frontend/apps/impress/src/components/quick-search/QuickSearchInput.tsx
index c8947e39b..814ac6885 100644
--- a/src/frontend/apps/impress/src/components/quick-search/QuickSearchInput.tsx
+++ b/src/frontend/apps/impress/src/components/quick-search/QuickSearchInput.tsx
@@ -56,7 +56,10 @@ export const QuickSearchInput = ({
autoFocus={true}
aria-controls={listId}
value={inputValue}
- placeholder={placeholder ?? t('Search')}
+ placeholder={
+ placeholder ??
+ t('Search', { description: 'Placeholder of a search input' })
+ }
onValueChange={onFilter}
maxLength={254}
data-testid="quick-search-input"
diff --git a/src/frontend/apps/impress/src/features/auth/components/ButtonLogin.tsx b/src/frontend/apps/impress/src/features/auth/components/ButtonLogin.tsx
index fd1299663..d72b585ec 100644
--- a/src/frontend/apps/impress/src/features/auth/components/ButtonLogin.tsx
+++ b/src/frontend/apps/impress/src/features/auth/components/ButtonLogin.tsx
@@ -33,7 +33,7 @@ export const ButtonLogin = ({
{...props}
href={href}
>
- {children ?? t('Sign in')}
+ {children ?? t('Sign in', { description: 'Button to log in' })}
);
};
@@ -44,7 +44,10 @@ export const ProConnectButton = () => {
return (
gotoLogin()}
- aria-label={t('Proconnect Login')}
+ aria-label={t('Proconnect Login', {
+ description:
+ 'Accessible name of the ProConnect (French government SSO) login button',
+ })}
$css={css`
background-color: var(
--c--contextuals--background--semantic--brand--primary
diff --git a/src/frontend/apps/impress/src/features/auth/components/UserReconciliation.tsx b/src/frontend/apps/impress/src/features/auth/components/UserReconciliation.tsx
index 3c0b2c424..3a248ed72 100644
--- a/src/frontend/apps/impress/src/features/auth/components/UserReconciliation.tsx
+++ b/src/frontend/apps/impress/src/features/auth/components/UserReconciliation.tsx
@@ -55,8 +55,14 @@ export const UserReconciliation = ({
$variation="primary"
>
{isError
- ? t('An error occurred during email validation.')
- : t('Email Address Confirmed')}
+ ? t('An error occurred during email validation.', {
+ description:
+ 'Title shown when the email confirmation of an account merge fails',
+ })
+ : t('Email Address Confirmed', {
+ description:
+ 'Title shown after an email address is confirmed during an account merge',
+ })}
{!isError && (
{t(
'To complete the unification of your user accounts, please click the confirmation links sent to all the email addresses you provided.',
+ { description: 'Instruction of the account merge page' },
)}
)}
diff --git a/src/frontend/apps/impress/src/features/docs/doc-comments/components/CommentSideBar.tsx b/src/frontend/apps/impress/src/features/docs/doc-comments/components/CommentSideBar.tsx
index e1174e9f4..3a3f3fe63 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-comments/components/CommentSideBar.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-comments/components/CommentSideBar.tsx
@@ -56,18 +56,26 @@ export const CommentSideBar = ({ onClose }: CommentSideBarProps) => {
- {t('Comments')}
+ {t('Comments', {
+ description: 'Title of the comments side panel',
+ })}
setFilter('open'),
isChecked: filter === 'open',
},
{
- label: t('Resolved'),
+ label: t('Resolved', {
+ description:
+ 'Dropdown menu item to filter the comments sidebar to only show resolved comment threads.',
+ }),
callback: () => setFilter('resolved'),
isChecked: filter === 'resolved',
},
@@ -76,9 +84,18 @@ export const CommentSideBar = ({ onClose }: CommentSideBarProps) => {
shouldCloseOnInteractOutside={() => true}
onOpenChange={setOpen}
>
-
+
@@ -130,8 +150,12 @@ export const CommentSideBarButton = () => {
const isActive = isPanelOpen && activePanel === 'comments';
const ariaLabel = isActive
- ? t('Hide the comments sidebar')
- : t('Show the comments sidebar');
+ ? t('Hide the comments sidebar', {
+ description: 'Accessible name of the toggle hiding the comments panel',
+ })
+ : t('Show the comments sidebar', {
+ description: 'Accessible name of the toggle showing the comments panel',
+ });
return (
);
}
diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/AI/AIToolbarButton.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/AI/AIToolbarButton.tsx
index 4216ac5b5..c2cc24c42 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/AI/AIToolbarButton.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/AI/AIToolbarButton.tsx
@@ -92,7 +92,9 @@ export const AIToolbarButton = () => {
$padding={{ right: '2xs' }}
>
- {t('Ask AI')}
+ {t('Ask AI', {
+ description: 'Editor toolbar button opening the AI prompt',
+ })}
{
const aiExtension = useAI?.(doc.id, aiBlockNoteAllowed);
const collabName = user?.full_name || user?.email;
- const cursorName = collabName || t('Anonymous');
+ const cursorName =
+ collabName ||
+ t('Anonymous', {
+ description: 'Display name of a user without a known name',
+ });
const showCursorLabels: 'always' | 'activity' | (string & {}) = 'activity';
// Comments
@@ -363,7 +367,9 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
sideMenu={false}
theme="light"
comments={false}
- aria-label={t('Document editor')}
+ aria-label={t('Document editor', {
+ description: 'Accessible name of the editor area',
+ })}
// To not clipped the floating part in the editor area
portalElements={{ default: null }}
>
diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteToolBar/MarkdownButton.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteToolBar/MarkdownButton.tsx
index 4c08595bd..7689e85f5 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteToolBar/MarkdownButton.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteToolBar/MarkdownButton.tsx
@@ -82,7 +82,10 @@ export function MarkdownButton() {
return (
onClose()}
- aria-label={t('Warning')}
+ aria-label={t('Warning', { description: 'Title of a warning dialog' })}
rightActions={
<>
>
}
@@ -57,7 +66,7 @@ export const ModalConfirmDownloadUnsafe = ({
$margin="0"
>
- {t('Warning')}
+ {t('Warning', { description: 'Title of a warning dialog' })}
}
>
@@ -65,10 +74,16 @@ export const ModalConfirmDownloadUnsafe = ({
- {t('This file is flagged as unsafe.')}
+ {t('This file is flagged as unsafe.', {
+ description:
+ 'Warning shown before downloading a file detected as malicious',
+ })}
- {t('Please download it only if it comes from a trusted source.')}
+ {t('Please download it only if it comes from a trusted source.', {
+ description:
+ 'Advice shown before downloading a file flagged as unsafe',
+ })}
diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/DocsSideMenu/LinkToBlockItem.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/DocsSideMenu/LinkToBlockItem.tsx
index cc5defd27..685a1ce34 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/DocsSideMenu/LinkToBlockItem.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/DocsSideMenu/LinkToBlockItem.tsx
@@ -32,8 +32,12 @@ export const LinkToBlockItem = () => {
copyToClipboard(
`${window.location.origin}${window.location.pathname}#${block.id}`,
- t('Link Copied !'),
- t('Failed to copy link'),
+ t('Link Copied !', {
+ description: 'Toast confirming a link was copied to the clipboard',
+ }),
+ t('Failed to copy link', {
+ description: 'Toast shown when copying a link failed',
+ }),
);
if (!isMobile) {
@@ -54,7 +58,10 @@ export const LinkToBlockItem = () => {
>
- {t('Copy link to block')}
+ {t('Copy link to block', {
+ description:
+ 'Editor side menu action copying a link to the current block',
+ })}
);
diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/CalloutBlock.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/CalloutBlock.tsx
index 74c00457e..869bd731f 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/CalloutBlock.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/CalloutBlock.tsx
@@ -207,7 +207,10 @@ export const getCalloutReactSlashMenuItems = (
) => [
{
key: 'callout',
- title: t('Callout'),
+ title: t('Callout', {
+ description:
+ 'Name of the editor block type: a highlighted box with an emoji',
+ }),
onItemClick: () => {
insertOrUpdateBlockForSlashMenu(editor, {
type: 'callout',
@@ -216,14 +219,19 @@ export const getCalloutReactSlashMenuItems = (
aliases: ['callout', 'encadré', 'hervorhebung', 'benadrukken'],
group,
icon: ,
- subtext: t('Add a callout block'),
+ subtext: t('Add a callout block', {
+ description: 'Description of the callout block in the editor slash menu',
+ }),
},
];
export const getCalloutFormattingToolbarItems = (
t: TFunction<'translation', undefined>,
): BlockTypeSelectItem => ({
- name: t('Callout'),
+ name: t('Callout', {
+ description:
+ 'Name of the editor block type: a highlighted box with an emoji',
+ }),
type: 'callout',
icon: () => ,
});
diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/PdfBlock.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/PdfBlock.tsx
index 596a44b16..dc9bb1816 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/PdfBlock.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/PdfBlock.tsx
@@ -74,15 +74,21 @@ const PdfBlockComponent = ({ editor, block }: PdfBlockComponentProps) => {
useEffect(() => {
if (lang && locales[lang as keyof typeof locales]) {
locales[lang as keyof typeof locales].file_blocks.add_button_text['pdf'] =
- t('Add PDF');
+ t('Add PDF', {
+ description: 'Button label to add a PDF file in the editor',
+ });
(
locales[lang as keyof typeof locales].file_panel.embed
.embed_button as Record
- )['pdf'] = t('Add PDF');
+ )['pdf'] = t('Add PDF', {
+ description: 'Button label to add a PDF file in the editor',
+ });
(
locales[lang as keyof typeof locales].file_panel.upload
.file_placeholder as Record
- )['pdf'] = t('Upload PDF');
+ )['pdf'] = t('Upload PDF', {
+ description: 'Placeholder text of the PDF block upload area',
+ });
}
}, [lang, t]);
@@ -129,7 +135,10 @@ const PdfBlockComponent = ({ editor, block }: PdfBlockComponentProps) => {
draggable={false}
>
- {t('Invalid or missing PDF file.')}
+ {t('Invalid or missing PDF file.', {
+ description:
+ 'Error shown in the PDF block when the file cannot be displayed',
+ })}
);
}
@@ -153,7 +162,12 @@ const PdfBlockComponent = ({ editor, block }: PdfBlockComponentProps) => {
$width="100%"
$height="450px"
src={pdfUrl}
- aria-label={block.props.name || t('PDF document')}
+ aria-label={
+ block.props.name ||
+ t('PDF document', {
+ description: 'Accessible name of an embedded PDF',
+ })
+ }
contentEditable={false}
draggable={false}
/>
@@ -191,13 +205,34 @@ export const getPdfReactSlashMenuItems = (
group: string,
) => [
{
- title: t('PDF'),
+ title: t('PDF', {
+ description: 'Name of the PDF file format / editor block',
+ }),
onItemClick: () => {
insertOrUpdateBlockForSlashMenu(editor, { type: 'pdf' });
},
- aliases: [t('pdf'), t('document'), t('embed'), t('file')],
+ aliases: [
+ t('pdf', {
+ description:
+ 'Search keyword for the PDF block in the editor slash menu',
+ }),
+ t('document', {
+ description:
+ 'Search keyword for the PDF block in the editor slash menu',
+ }),
+ t('embed', {
+ description:
+ 'Search keyword for the PDF block in the editor slash menu',
+ }),
+ t('file', {
+ description:
+ 'Search keyword for the PDF block in the editor slash menu',
+ }),
+ ],
group,
icon: ,
- subtext: t('Embed a PDF file'),
+ subtext: t('Embed a PDF file', {
+ description: 'Description of the PDF block in the editor slash menu',
+ }),
},
];
diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/UploadLoaderBlock.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/UploadLoaderBlock.tsx
index 019a2af88..f477d50fc 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/UploadLoaderBlock.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/UploadLoaderBlock.tsx
@@ -121,6 +121,10 @@ const UploadLoaderBlockComponent = ({
type: 'warning',
information: t(
'The antivirus has detected an anomaly in your file.',
+ {
+ description:
+ 'Message shown in the editor when an uploaded file is flagged by the antivirus',
+ },
),
},
});
diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-inline-content/Interlinking/InterlinkingLinkInlineContent.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-inline-content/Interlinking/InterlinkingLinkInlineContent.tsx
index 9d96099a9..022624f98 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-inline-content/Interlinking/InterlinkingLinkInlineContent.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-inline-content/Interlinking/InterlinkingLinkInlineContent.tsx
@@ -117,7 +117,9 @@ export const getInterlinkinghMenuItems = (
) => [
{
key: 'link-doc',
- title: t('Link a doc'),
+ title: t('Link a doc', {
+ description: 'Editor menu entry to insert a link to another doc',
+ }),
onItemClick: () => {
editor.insertInlineContent([
{
@@ -131,16 +133,22 @@ export const getInterlinkinghMenuItems = (
aliases: ['interlinking', 'link', 'anchor', 'a'],
group,
icon: ,
- subtext: t('Link this doc to another doc'),
+ subtext: t('Link this doc to another doc', {
+ description: 'Description of the "Link a doc" editor menu entry',
+ }),
},
{
key: 'new-sub-doc',
- title: t('New sub-doc'),
+ title: t('New sub-doc', {
+ description: 'Action to create a doc nested under the current doc',
+ }),
onItemClick: createPage,
aliases: ['new sub-doc'],
group,
icon: ,
- subtext: t('Create a new sub-doc'),
+ subtext: t('Create a new sub-doc', {
+ description: 'Description of the "New sub-doc" editor menu entry',
+ }),
},
];
@@ -151,7 +159,15 @@ export const useGetInterlinkingMenuItems = () => {
return (
editor: DocsBlockNoteEditor,
t: TFunction<'translation', undefined>,
- ) => getInterlinkinghMenuItems(editor, t, t('Links'), createChildDoc);
+ ) =>
+ getInterlinkinghMenuItems(
+ editor,
+ t,
+ t('Links', {
+ description: 'Group title of the link entries in the editor menu',
+ }),
+ createChildDoc,
+ );
};
const DisableInvalidInterlink = ({
diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-inline-content/Interlinking/SearchPage.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-inline-content/Interlinking/SearchPage.tsx
index ef20e42b5..ebb8e6a41 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-inline-content/Interlinking/SearchPage.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-inline-content/Interlinking/SearchPage.tsx
@@ -165,13 +165,19 @@ export const SearchPage = ({
as="input"
name="doc-search-input"
role="combobox"
- aria-label={t('Search for a document')}
+ aria-label={t('Search for a document', {
+ description:
+ 'Accessible name or placeholder of a document search field',
+ })}
aria-expanded={popoverOpened}
aria-haspopup="listbox"
aria-autocomplete="list"
aria-controls={dropdownId}
$padding={{ left: '3px' }}
- placeholder={t('mention a sub-doc...')}
+ placeholder={t('mention a sub-doc...', {
+ description:
+ 'Placeholder of the field to search a doc to link in the editor',
+ })}
$css={inputStyle}
ref={inputRef}
$display="inline-flex"
@@ -189,7 +195,10 @@ export const SearchPage = ({
ref={modalRef}
id={dropdownId}
role="listbox"
- aria-label={t('Search results')}
+ aria-label={t('Search results', {
+ description:
+ 'Accessible name or heading of the list of search results',
+ })}
$minWidth={isDesktop ? '330px' : '220px'}
$width="fit-content"
$zIndex="10"
@@ -261,7 +270,10 @@ export const SearchPage = ({
$padding="none"
>
string,
+ t: TFunction<'translation', undefined>,
): string | null => {
const isMod = event.ctrlKey || event.metaKey;
if (!isMod) {
@@ -18,11 +19,20 @@ const getFormattingShortcutLabel = (
if (event.altKey) {
switch (event.code) {
case 'Digit1':
- return t('Heading 1 applied');
+ return t('Heading 1 applied', {
+ description:
+ 'Screen reader announcement after a keyboard shortcut applied a heading level 1',
+ });
case 'Digit2':
- return t('Heading 2 applied');
+ return t('Heading 2 applied', {
+ description:
+ 'Screen reader announcement after a keyboard shortcut applied a heading level 2',
+ });
case 'Digit3':
- return t('Heading 3 applied');
+ return t('Heading 3 applied', {
+ description:
+ 'Screen reader announcement after a keyboard shortcut applied a heading level 3',
+ });
default:
return null;
}
@@ -31,17 +41,35 @@ const getFormattingShortcutLabel = (
if (event.shiftKey) {
switch (event.code) {
case 'Digit0':
- return t('Paragraph applied');
+ return t('Paragraph applied', {
+ description:
+ 'Screen reader announcement after a keyboard shortcut turned a block into a paragraph',
+ });
case 'Digit6':
- return t('Toggle list applied');
+ return t('Toggle list applied', {
+ description:
+ 'Screen reader announcement after a keyboard shortcut applied a collapsible list',
+ });
case 'Digit7':
- return t('Numbered list applied');
+ return t('Numbered list applied', {
+ description:
+ 'Screen reader announcement after a keyboard shortcut applied a numbered list',
+ });
case 'Digit8':
- return t('Bulleted list applied');
+ return t('Bulleted list applied', {
+ description:
+ 'Screen reader announcement after a keyboard shortcut applied a bulleted list',
+ });
case 'Digit9':
- return t('Checklist applied');
+ return t('Checklist applied', {
+ description:
+ 'Screen reader announcement after a keyboard shortcut applied a checklist',
+ });
case 'KeyC':
- return t('Code block applied');
+ return t('Code block applied', {
+ description:
+ 'Screen reader announcement after a keyboard shortcut applied a code block',
+ });
default:
return null;
}
diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/hook/useUploadFile.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/hook/useUploadFile.tsx
index 9c0fff081..ec2e24b01 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-editor/hook/useUploadFile.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-editor/hook/useUploadFile.tsx
@@ -62,6 +62,8 @@ export const useUploadFile = (docId: string) => {
t(
'The file "{{fileName}}" is too large. Maximum file size is {{maxFileSize}}.',
{
+ description:
+ 'Toast shown when an uploaded file exceeds the size limit',
fileName: file.name,
maxFileSize: formatFileSize(maxFileSize),
},
@@ -132,7 +134,10 @@ export const useUploadStatus = (editor: DocsBlockNoteEditor) => {
{
type: 'uploadLoader',
props: {
- information: t('Analyzing file...'),
+ information: t('Analyzing file...', {
+ description:
+ 'Placeholder shown while an uploaded file is scanned by the antivirus',
+ }),
type: 'loading',
blockUploadName,
blockUploadType,
diff --git a/src/frontend/apps/impress/src/features/docs/doc-export/components/ModalExport.tsx b/src/frontend/apps/impress/src/features/docs/doc-export/components/ModalExport.tsx
index e04be65a7..dfb58bf09 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-export/components/ModalExport.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-export/components/ModalExport.tsx
@@ -61,14 +61,21 @@ export const ModalExport = ({ onClose, doc }: ModalExportProps) => {
const formatSelect = useMemo(() => {
const formatOptions = (exportAGPL?.formats || []).concat([
{
- label: t('Markdown'),
+ label: t('Markdown', {
+ description: 'Name of the Markdown export format',
+ }),
value: 'markdown',
- labelDescription: t('.md(zip)'),
+ labelDescription: t('.md(zip)', {
+ description:
+ 'File extension hint of the Markdown export, a zip archive',
+ }),
},
{
- label: t('HTML'),
+ label: t('HTML', { description: 'Name of the HTML export format' }),
value: 'html',
- labelDescription: t('.html(zip)'),
+ labelDescription: t('.html(zip)', {
+ description: 'File extension hint of the HTML export, a zip archive',
+ }),
},
]);
@@ -92,7 +99,12 @@ export const ModalExport = ({ onClose, doc }: ModalExportProps) => {
/** Exports the selected format and always releases the loading state. */
async function onSubmit() {
if (!editor) {
- toast(t('The export failed'), VariantType.ERROR);
+ toast(
+ t('The export failed', {
+ description: 'Toast shown when exporting the document fails',
+ }),
+ VariantType.ERROR,
+ );
return;
}
@@ -169,7 +181,12 @@ export const ModalExport = ({ onClose, doc }: ModalExportProps) => {
}
if (!blobExport) {
- toast(t('The export failed'), VariantType.ERROR);
+ toast(
+ t('The export failed', {
+ description: 'Toast shown when exporting the document fails',
+ }),
+ VariantType.ERROR,
+ );
return;
}
@@ -177,6 +194,8 @@ export const ModalExport = ({ onClose, doc }: ModalExportProps) => {
toast(
t('Your {{format}} was downloaded succesfully', {
+ description:
+ 'Toast shown after an export; format is PDF, Docx, ODT...',
format,
}),
VariantType.SUCCESS,
@@ -184,7 +203,12 @@ export const ModalExport = ({ onClose, doc }: ModalExportProps) => {
shouldClose = true;
} catch {
- toast(t('The export failed'), VariantType.ERROR);
+ toast(
+ t('The export failed', {
+ description: 'Toast shown when exporting the document fails',
+ }),
+ VariantType.ERROR,
+ );
} finally {
setIsExporting(false);
}
@@ -206,16 +230,23 @@ export const ModalExport = ({ onClose, doc }: ModalExportProps) => {
rightActions={
<>
>
}
@@ -238,11 +271,16 @@ export const ModalExport = ({ onClose, doc }: ModalExportProps) => {
$align="flex-start"
data-testid="modal-export-title"
>
- {t('Export')}
+ {t('Export', {
+ description: 'Title of the dialog to export a document',
+ })}
onClose()}
disabled={isExporting}
/>
@@ -262,6 +300,7 @@ export const ModalExport = ({ onClose, doc }: ModalExportProps) => {
id="modal-export-description"
>
{t('Export your document to download in {{format}} format.', {
+ description: 'Explanation in the export dialog',
format: formatSelect.allFormatsLabel,
})}
@@ -269,7 +308,9 @@ export const ModalExport = ({ onClose, doc }: ModalExportProps) => {
)}
diff --git a/src/frontend/apps/impress/src/features/docs/doc-header/components/AlertOffline.tsx b/src/frontend/apps/impress/src/features/docs/doc-header/components/AlertOffline.tsx
index f0d912444..3249db190 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-header/components/AlertOffline.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-header/components/AlertOffline.tsx
@@ -10,7 +10,10 @@ export const AlertOffline = () => {
return (
{
{t(
"You're offline. You can keep editing, and your changes will sync automatically once you're back online.",
+ { description: 'Banner shown when the connection is lost' },
)}
diff --git a/src/frontend/apps/impress/src/features/docs/doc-header/components/AlertRestore.tsx b/src/frontend/apps/impress/src/features/docs/doc-header/components/AlertRestore.tsx
index 93dc0aba8..623190eed 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-header/components/AlertRestore.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-header/components/AlertRestore.tsx
@@ -28,13 +28,20 @@ export const AlertRestore = ({ doc }: { doc: Doc }) => {
],
options: {
onSuccess: (_data) => {
- toast(t('The document has been restored.'), VariantType.SUCCESS, {
- duration: 4000,
- });
+ toast(
+ t('The document has been restored.', {
+ description: 'Toast shown after a deleted document is restored',
+ }),
+ VariantType.SUCCESS,
+ {
+ duration: 4000,
+ },
+ );
},
onError: () => {
toast(
t('An error occurred while restoring the document: {{error}}', {
+ description: 'Toast shown when restoring a deleted document fails',
error: error?.message,
}),
VariantType.ERROR,
@@ -49,7 +56,10 @@ export const AlertRestore = ({ doc }: { doc: Doc }) => {
return (
{
iconName="delete"
variant="symbols-outlined"
/>
- {t('Document deleted')}
+ {t('Document deleted', {
+ description: 'Banner text shown on a document in the trashbin',
+ })}
{doc.abilities.restore && (
{
/>
}
>
- {t('Restore')}
+ {t('Restore', {
+ description:
+ 'Button to restore a deleted document or an older version',
+ })}
)}
diff --git a/src/frontend/apps/impress/src/features/docs/doc-header/components/DocHeader.tsx b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocHeader.tsx
index 63d809652..68e1cc624 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-header/components/DocHeader.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocHeader.tsx
@@ -43,7 +43,10 @@ export const DocHeader = ({ doc }: DocHeaderProps) => {
<>
{
isAprilFools ? '🐟' : '📄',
);
}}
- aria-label={emoji ? t('Remove emoji') : t('Add emoji')}
+ aria-label={
+ emoji
+ ? t('Remove emoji', {
+ description: 'Button to remove the emoji of a document',
+ })
+ : t('Add emoji', {
+ description: 'Button to add an emoji to a document',
+ })
+ }
color="neutral"
variant="tertiary"
icon={
@@ -100,7 +111,13 @@ export const DocHeader = ({ doc }: DocHeaderProps) => {
}
style={{ width: 'fit-content' }}
>
- {emoji ? t('Remove emoji') : t('Add emoji')}
+ {emoji
+ ? t('Remove emoji', {
+ description: 'Button to remove the emoji of a document',
+ })
+ : t('Add emoji', {
+ description: 'Button to add an emoji to a document',
+ })}
)}
diff --git a/src/frontend/apps/impress/src/features/docs/doc-header/components/DocHeaderInfo.tsx b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocHeaderInfo.tsx
index d89852ed0..65dac1d20 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-header/components/DocHeaderInfo.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocHeaderInfo.tsx
@@ -34,10 +34,16 @@ export const DocHeaderInfo = ({ doc }: DocHeaderInfoProps) => {
if (trashbinCutoff && doc.deleted_at) {
const daysLeft = calculateDaysLeft(doc.deleted_at, trashbinCutoff);
- dateLabel = t('Days remaining:');
- dateValue = `${daysLeft} ${t('days', { count: daysLeft })}`;
+ dateLabel = t('Days remaining:', {
+ description:
+ 'Label before the number of days left before a trashed document is permanently deleted',
+ });
+ dateValue = `${daysLeft} ${t('days', { description: 'Unit after a number of days, plural form', count: daysLeft })}`;
} else {
- dateLabel = t('Last update:');
+ dateLabel = t('Last update:', {
+ description:
+ 'Label before the date of the last modification of a document',
+ });
dateValue = relativeOnly;
}
@@ -46,7 +52,10 @@ export const DocHeaderInfo = ({ doc }: DocHeaderInfoProps) => {
{doc.is_favorite && (
<>
- {t('This document is starred')}
+ {t('This document is starred', {
+ description:
+ "Screen reader text indicating the document is in the user's favorites",
+ })}
{
>
)}
- {t('Role')}
+ {t('Role', {
+ description:
+ "Screen reader label before the user's role on the document",
+ })}
{
return (
<>
- {t('Public')} ·
+
+ {t('Public', {
+ description: 'Document visibility: anyone with the link can access',
+ })}
+ ·
>
);
}
@@ -128,7 +144,12 @@ const VisibilityDoc = ({ doc }: { doc: Doc }) => {
return (
<>
- {t('Internal')} ·
+
+ {t('Internal', {
+ description:
+ 'Document visibility: any logged-in user with the link can access',
+ })}
+ ·
>
);
}
diff --git a/src/frontend/apps/impress/src/features/docs/doc-header/components/DocLeftPanelCollapseButton.tsx b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocLeftPanelCollapseButton.tsx
index 927fa51b0..7618af318 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-header/components/DocLeftPanelCollapseButton.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocLeftPanelCollapseButton.tsx
@@ -87,8 +87,14 @@ export const DocLeftPanelCollapseButton = () => {
const docTitle = titleWithoutEmoji || untitledDocument;
const shouldShowButtonTitle = !isPanelOpen && !isDocTitleVisible;
const ariaLabel = isPanelOpen
- ? t('Hide the side panel for {{title}}', { title: docTitle })
- : t('Show the side panel for {{title}}', { title: docTitle });
+ ? t('Hide the side panel for {{title}}', {
+ description: 'Accessible name of the button collapsing the left panel',
+ title: docTitle,
+ })
+ : t('Show the side panel for {{title}}', {
+ description: 'Accessible name of the button expanding the left panel',
+ title: docTitle,
+ });
return (
{
width="25px"
height="25px"
aria-hidden="true"
- aria-label={t('Simple document icon')}
+ aria-label={t('Simple document icon', {
+ description: 'Accessible name of the default icon of a document',
+ })}
color={colorsTokens['brand-500']}
/>
}
@@ -244,7 +246,7 @@ const DocTitleInput = ({ doc, onTitleUpdate }: DocTitleProps) => {
defaultValue={titleDisplay || undefined}
onKeyDownCapture={handleKeyDown}
suppressContentEditableWarning={true}
- aria-label={`${t('Document title')}`}
+ aria-label={`${t('Document title', { description: 'Accessible name of the editable document title' })}`}
aria-multiline={false}
onBlurCapture={(event) =>
handleTitleSubmit(event.target.textContent || '')
diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/api/useCreateFavoriteDoc.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/api/useCreateFavoriteDoc.tsx
index 70647aaf4..c7a0dd2e8 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-management/api/useCreateFavoriteDoc.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-management/api/useCreateFavoriteDoc.tsx
@@ -46,13 +46,17 @@ export function useCreateFavoriteDoc({
syncDocInTree(treeContext, id, { is_favorite: true });
- const message = t('Document starred successfully!');
+ const message = t('Document starred successfully!', {
+ description: 'Toast shown after adding a document to favorites',
+ });
announce(message, 'polite');
onSuccess?.();
},
onError: () => {
- const message = t('Failed to star the document.');
+ const message = t('Failed to star the document.', {
+ description: 'Toast shown when adding a document to favorites failed',
+ });
announce(message, 'assertive');
},
});
diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/api/useDeleteFavoriteDoc.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/api/useDeleteFavoriteDoc.tsx
index fc61f7589..45dca201c 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-management/api/useDeleteFavoriteDoc.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-management/api/useDeleteFavoriteDoc.tsx
@@ -46,13 +46,18 @@ export function useDeleteFavoriteDoc({
syncDocInTree(treeContext, id, { is_favorite: false });
- const message = t('Document unstarred successfully!');
+ const message = t('Document unstarred successfully!', {
+ description: 'Toast shown after removing a document from favorites',
+ });
announce(message, 'polite');
onSuccess?.();
},
onError: () => {
- const message = t('Failed to unstar the document.');
+ const message = t('Failed to unstar the document.', {
+ description:
+ 'Toast shown when removing a document from favorites failed',
+ });
announce(message, 'assertive');
},
});
diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/api/useDuplicateDoc.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/api/useDuplicateDoc.tsx
index f3abf767b..d24d04629 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-management/api/useDuplicateDoc.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-management/api/useDuplicateDoc.tsx
@@ -94,7 +94,9 @@ export function useDuplicateDoc(options?: DuplicateDocOptions) {
void options?.onSuccess?.(data, variables, onMutateResult, context);
},
onError: (error, variables, onMutateResult, context) => {
- const message = t('Failed to duplicate the document...');
+ const message = t('Failed to duplicate the document...', {
+ description: 'Toast shown when duplicating a document failed',
+ });
toast(message, VariantType.ERROR, {
duration: 3000,
});
diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/api/useImportDoc.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/api/useImportDoc.tsx
index 9f4b48cee..27819caaa 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-management/api/useImportDoc.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-management/api/useImportDoc.tsx
@@ -122,6 +122,7 @@ export function useImportDoc(props?: UseImportDocOptions) {
toast(
t('The document "{{documentName}}" has been successfully imported', {
+ description: 'Toast shown after a file is imported as a document',
documentName: importedDoc.title || '',
}),
VariantType.SUCCESS,
@@ -132,6 +133,7 @@ export function useImportDoc(props?: UseImportDocOptions) {
onError: (...errorProps) => {
toast(
t(`The document "{{documentName}}" import has failed`, {
+ description: 'Toast shown when importing a file as a document failed',
documentName: errorProps?.[1][0].name || '',
}),
VariantType.ERROR,
diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/components/ConfirmationDuplicateModal.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/components/ConfirmationDuplicateModal.tsx
index 2ce711efd..850e08b40 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-management/components/ConfirmationDuplicateModal.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-management/components/ConfirmationDuplicateModal.tsx
@@ -70,8 +70,13 @@ export const useDuplicatedDoc = ({
toast(
isTopRoot && isCurrentDoc
- ? t('Document duplicated to My docs')
- : t('Document duplicated'),
+ ? t('Document duplicated to My docs', {
+ description:
+ "Toast title after duplicating a root document into the user's own docs",
+ })
+ : t('Document duplicated', {
+ description: 'Toast title after duplicating a document',
+ }),
VariantType.INFO,
{
duration: 10000,
@@ -115,22 +120,32 @@ export const ConfirmationDuplicateModal = ({
closeOnClickOutside
hideCloseButton
onClose={onClose}
- aria-label={t('Confirmation to duplicate the document')}
+ aria-label={t('Confirmation to duplicate the document', {
+ description: 'Accessible name of the duplicate dialog',
+ })}
aria-labelledby="modal-duplicate-doc-title"
aria-describedby={isTopRoot ? 'modal-duplicate-doc-desc' : undefined}
rightActions={
<>
- {t('Cancel')}
+ {t('Cancel', {
+ description: 'Button to dismiss a dialog without confirming',
+ })}
{
@@ -141,7 +156,9 @@ export const ConfirmationDuplicateModal = ({
});
}}
>
- {t('Duplicate')}
+ {t('Duplicate', {
+ description: 'Dropdown menu item to duplicate the document',
+ })}
>
}
@@ -155,11 +172,16 @@ export const ConfirmationDuplicateModal = ({
$margin="0"
$align="flex-start"
>
- {t('Duplicate')}
+ {t('Duplicate', {
+ description: 'Dropdown menu item to duplicate the document',
+ })}
@@ -176,12 +198,17 @@ export const ConfirmationDuplicateModal = ({
as="p"
$margin="0"
>
- {t('The copy will be private and added to My docs.')}
+ {t('The copy will be private and added to My docs.', {
+ description: 'Explanation in the duplicate dialog',
+ })}
)}
setIsWithSubdocs(e.target.checked)}
/>
@@ -221,9 +248,16 @@ export const ToastActions = ({
onError: (error) => {
// A 401 is already handled globally (redirect to login), avoid a duplicate toast.
if (error.status !== 401) {
- toast(t('The document could not be deleted.'), VariantType.ERROR, {
- duration: 4000,
- });
+ toast(
+ t('The document could not be deleted.', {
+ description:
+ 'Toast shown when deleting a document failed (undo of a duplicate)',
+ }),
+ VariantType.ERROR,
+ {
+ duration: 4000,
+ },
+ );
}
},
},
@@ -278,7 +312,9 @@ export const ToastActions = ({
});
}}
>
- {t('Undo')}
+ {t('Undo', {
+ description: 'Toast button to cancel the previous action',
+ })}
);
diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/components/DocIcon.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/components/DocIcon.tsx
index ca3269773..b56f9d1f5 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-management/components/DocIcon.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-management/components/DocIcon.tsx
@@ -58,10 +58,15 @@ export const DocIcon = ({
const emojiLabel = withEmojiPicker
? emoji
- ? t('Edit document emoji')
- : t('Add emoji')
+ ? t('Edit document emoji', {
+ description:
+ 'Accessible name of the button changing a document emoji',
+ })
+ : t('Add emoji', { description: 'Button to add an emoji to a document' })
: emoji
- ? t('Document emoji')
+ ? t('Document emoji', {
+ description: 'Accessible name of the emoji of a document',
+ })
: undefined;
const toggleEmojiPicker = (e: MouseEvent) => {
diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/components/DocMoveModal.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/components/DocMoveModal.tsx
index d7564f9c7..c4362c5cc 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-management/components/DocMoveModal.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-management/components/DocMoveModal.tsx
@@ -129,7 +129,9 @@ export const DocMoveModal = ({
})
.catch(() => {
toast(
- t(`An error occurred while moving the document.`),
+ t(`An error occurred while moving the document.`, {
+ description: 'Toast shown when moving a document failed',
+ }),
VariantType.ERROR,
);
});
@@ -144,7 +146,9 @@ export const DocMoveModal = ({
closeOnClickOutside
size={isModal ? ModalSize.MEDIUM : ModalSize.FULL}
hideCloseButton
- aria-label={t('Move Modal')}
+ aria-label={t('Move Modal', {
+ description: 'Accessible name of the dialog to move a document',
+ })}
rightActions={
{
@@ -170,26 +177,39 @@ export const DocMoveModal = ({
}}
disabled={!docSelected}
>
- {t('Move here')}
+ {t('Move here', {
+ description:
+ 'Button to move the document to the selected parent',
+ })}
- {t('Cancel')}
+ {t('Cancel', {
+ description: 'Button to dismiss a dialog without confirming',
+ })}
}
title={
<>
- {t('Choose a new parent doc')}
+ {t('Choose a new parent doc', {
+ description: 'Heading of the dialog to move a document',
+ })}
@@ -211,8 +231,14 @@ export const DocMoveModal = ({
}}
>
@@ -223,7 +249,17 @@ export const DocMoveModal = ({
>
docResults.id !== doc.id}
onSelect={handleSelect}
@@ -303,7 +339,9 @@ export const DocMoveModal = ({
onClose();
}}
targetDocumentTitle={docTargetTitle}
- title={t('Move document')}
+ title={t('Move document', {
+ description: 'Title of the dialog to move a document',
+ })}
/>
)}
>
diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/components/DocPage403.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/components/DocPage403.tsx
index f2b5669d8..f56e8dbfa 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-management/components/DocPage403.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-management/components/DocPage403.tsx
@@ -47,11 +47,17 @@ export const DocPage403 = ({ id }: DocProps) => {
- {t('Access Denied - Error 403')} - {t('Docs')}
+ {t('Access Denied - Error 403', {
+ description: 'Page title when the user may not open a document',
+ })}{' '}
+ -{' '}
+ {t('Docs', {
+ description: 'Product name, shown in page titles and the logo',
+ })}
@@ -73,7 +79,10 @@ export const DocPage403 = ({ id }: DocProps) => {
$theme="neutral"
$variation="primary"
>
- {t('Access denied')}
+ {t('Access denied', {
+ description:
+ 'Heading shown when the user may not open a document',
+ })}
{
$size="xs"
>
{hasRequested
- ? t('Your access request for this document is pending.')
- : t('Insufficient access rights to view the document.')}
+ ? t('Your access request for this document is pending.', {
+ description:
+ 'Message on the access denied page after requesting access',
+ })
+ : t('Insufficient access rights to view the document.', {
+ description: 'Message on the access denied page',
+ })}
{isSubDocument && (
{
>
{t(
"You're currently viewing a sub-document. To gain access, please request permission from the main document.",
+ {
+ description:
+ 'Message on the access denied page of a sub-document',
+ },
)}
)}
@@ -133,7 +151,9 @@ export const DocPage403 = ({ id }: DocProps) => {
$variation="tertiary"
$margin="0"
>
- {t('Home')}
+ {t('Home', {
+ description: 'Button/link label to go back to the home page',
+ })}
{!isSubDocument && (
diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/components/DocToolBox.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/components/DocToolBox.tsx
index 9b597fa2d..c6d129f79 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-management/components/DocToolBox.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-management/components/DocToolBox.tsx
@@ -287,7 +287,13 @@ const DocToolBoxComponent = ({
},
{ type: 'separator' },
{
- label: doc.is_favorite ? t('Unstar') : t('Star'),
+ label: doc.is_favorite
+ ? t('Unstar', {
+ description: 'Menu action to remove a document from favorites',
+ })
+ : t('Star', {
+ description: 'Menu action to add a document to favorites',
+ }),
icon: doc.is_favorite ? (
) : (
@@ -324,7 +330,9 @@ const DocToolBoxComponent = ({
isHidden: !doc.abilities.duplicate,
},
{
- label: t('Move to my docs'),
+ label: t('Move to my docs', {
+ description: "Menu action to move a document into the user's own docs",
+ }),
isHidden: isTopParent || doc.user_role !== Role.OWNER,
icon: ,
callback: () => {
@@ -350,7 +358,9 @@ const DocToolBoxComponent = ({
},
},
{
- label: t('Move into a doc'),
+ label: t('Move into a doc', {
+ description: 'Menu action to move a document under another doc',
+ }),
icon: ,
callback: () => {
setIsModalMoveOpen(true);
@@ -415,6 +425,7 @@ const DocToolBoxComponent = ({
>
}
- confirmLabel={t('Move')}
+ confirmLabel={t('Move', {
+ description: 'Button confirming the move of a document',
+ })}
onConfirm={onConfirm}
/>
);
diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/components/ModalRemoveDoc.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/components/ModalRemoveDoc.tsx
index 6e2671bb3..3bbe4e45e 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-management/components/ModalRemoveDoc.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-management/components/ModalRemoveDoc.tsx
@@ -56,9 +56,15 @@ export const ModalRemoveDoc = ({
onClose();
- toast(t('The document has been deleted.'), VariantType.SUCCESS, {
- duration: 4000,
- });
+ toast(
+ t('The document has been deleted.', {
+ description: 'Toast shown after deleting a document',
+ }),
+ VariantType.SUCCESS,
+ {
+ duration: 4000,
+ },
+ );
},
},
});
@@ -82,26 +88,38 @@ export const ModalRemoveDoc = ({
closeOnClickOutside
hideCloseButton
onClose={onClose}
- aria-label={t('Delete a doc')}
+ aria-label={t('Delete a doc', {
+ description: 'Title and accessible name of the delete document dialog',
+ })}
rightActions={
<>
- {t('Cancel')}
+ {t('Cancel', {
+ description: 'Button to dismiss a dialog without confirming',
+ })}
- {t('Delete')}
+ {t('Delete', {
+ description: 'Dropdown menu item to delete the document',
+ })}
>
}
@@ -115,11 +133,17 @@ export const ModalRemoveDoc = ({
$margin="0"
$align="flex-start"
>
- {t('Delete a doc')}
+ {t('Delete a doc', {
+ description:
+ 'Title and accessible name of the delete document dialog',
+ })}
@@ -143,7 +167,10 @@ export const ModalRemoveDoc = ({
) : (
t(
'This document will be placed in the trashbin. You can restore it within {{days}} days.',
- { days: trashBinCutoffDays },
+ {
+ description: 'Explanation in the delete dialog',
+ days: trashBinCutoffDays,
+ },
)
)}
diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/components/NewDocButton.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/components/NewDocButton.tsx
index 83de715d6..7af95e474 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-management/components/NewDocButton.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-management/components/NewDocButton.tsx
@@ -48,7 +48,7 @@ export const NewDocButton = ({ onClose }: NewDocButtonProps) => {
}}
>
- {t('New')}
+ {t('New', { description: 'Button to create a new document' })}
{isDropdownEnabled && }
@@ -92,7 +92,9 @@ export function DropdownArrow() {
const options = useMemo(
() => [
{
- label: t('New sub-doc'),
+ label: t('New sub-doc', {
+ description: 'Action to create a doc nested under the current doc',
+ }),
icon: ,
callback: () => {
if (currentDoc) {
@@ -104,7 +106,9 @@ export function DropdownArrow() {
isHidden: !currentDoc,
},
{
- label: t('Import a document'),
+ label: t('Import a document', {
+ description: 'Menu action to create a document from a file',
+ }),
icon: ,
callback: openImport,
isHidden: !isImportEnabled || !!currentDoc,
@@ -121,7 +125,10 @@ export function DropdownArrow() {
onOpenChange={setIsMenuOpen}
>
{
return async (asFormat: 'html' | 'markdown') => {
if (!editor) {
- const message = t('Editor unavailable');
+ const message = t('Editor unavailable', {
+ description:
+ 'Toast shown when copying content while the editor is not ready',
+ });
toast(message, VariantType.ERROR, { duration: 3000 });
return;
}
@@ -24,16 +27,24 @@ export const useCopyCurrentEditorToClipboard = () => {
await navigator.clipboard.writeText(editorContentFormatted);
const successMessage =
asFormat === 'markdown'
- ? t('Copied as Markdown to clipboard')
- : t('Copied to clipboard');
+ ? t('Copied as Markdown to clipboard', {
+ description: 'Toast after copying the document as Markdown',
+ })
+ : t('Copied to clipboard', {
+ description: 'Toast after copying something to the clipboard',
+ });
toast(successMessage, VariantType.SUCCESS, { duration: 3000 });
} catch (error) {
console.error(error);
const errorMessage =
asFormat === 'markdown'
- ? t('Failed to copy as Markdown to clipboard')
- : t('Failed to copy to clipboard');
+ ? t('Failed to copy as Markdown to clipboard', {
+ description: 'Toast when copying as Markdown failed',
+ })
+ : t('Failed to copy to clipboard', {
+ description: 'Toast when copying to the clipboard failed',
+ });
toast(errorMessage, VariantType.ERROR, {
duration: 3000,
diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/hooks/useCopyDocLink.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/hooks/useCopyDocLink.tsx
index a202a3647..ea3b70ba8 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-management/hooks/useCopyDocLink.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-management/hooks/useCopyDocLink.tsx
@@ -12,8 +12,12 @@ export const useCopyDocLink = (docId: Doc['id']) => {
return useCallback(() => {
copyToClipboard(
`${window.location.origin}/docs/${docId}/`,
- t('Link Copied !'),
- t('Failed to copy link'),
+ t('Link Copied !', {
+ description: 'Toast confirming a link was copied to the clipboard',
+ }),
+ t('Failed to copy link', {
+ description: 'Toast shown when copying a link failed',
+ }),
);
}, [copyToClipboard, docId, t]);
};
diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/hooks/useImport.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/hooks/useImport.tsx
index 16f6fd9db..d7439543d 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-management/hooks/useImport.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-management/hooks/useImport.tsx
@@ -62,6 +62,8 @@ export const useImport = ({ onDragOver, onImportSuccess }: UseImportProps) => {
? `The document "{{documentName}}" import has failed (only {{allowedExtensions}} files are allowed)`
: `The document "{{documentName}}" import has failed`,
{
+ description:
+ 'Toast shown when a file to import has an unsupported type',
documentName: fileName,
allowedExtensions,
},
@@ -108,6 +110,8 @@ export const useImport = ({ onDragOver, onImportSuccess }: UseImportProps) => {
t(
'The document "{{documentName}}" is too large. Maximum file size is {{maxFileSize}}.',
{
+ description:
+ 'Toast shown when an imported file exceeds the size limit',
documentName: rejection.file.name,
maxFileSize: MAX_FILE_SIZE.text,
},
diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/hooks/useTrans.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/hooks/useTrans.tsx
index 654e6ba46..2b020a089 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-management/hooks/useTrans.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-management/hooks/useTrans.tsx
@@ -6,18 +6,34 @@ export const useTrans = () => {
const { t } = useTranslation();
const translatedRoles = {
- [Role.READER]: t('Reader'),
- [Role.EDITOR]: t('Editor'),
- [Role.ADMIN]: t('Administrator'),
- [Role.OWNER]: t('Owner'),
+ [Role.READER]: t('Reader', {
+ description: 'Role on a document: can only read',
+ }),
+ [Role.EDITOR]: t('Editor', { description: 'Role on a document: can edit' }),
+ [Role.ADMIN]: t('Administrator', {
+ description: 'Role on a document: can edit and manage sharing',
+ }),
+ [Role.OWNER]: t('Owner', {
+ description: 'Role on a document: full control, including deletion',
+ }),
};
const translatedFilters = {
- [DocDefaultFilter.ALL_DOCS]: t('Recent'),
- [DocDefaultFilter.MY_DOCS]: t('My docs'),
- [DocDefaultFilter.SHARED_WITH_ME]: t('Shared with me'),
- [DocDefaultFilter.STARRED]: t('Starred'),
- [DocDefaultFilter.TRASHBIN]: t('Trashbin'),
+ [DocDefaultFilter.ALL_DOCS]: t('Recent', {
+ description: 'Name of the filter listing recently opened docs',
+ }),
+ [DocDefaultFilter.MY_DOCS]: t('My docs', {
+ description: 'Name of the filter listing docs owned by the user',
+ }),
+ [DocDefaultFilter.SHARED_WITH_ME]: t('Shared with me', {
+ description: 'Name of the filter listing docs shared with the user',
+ }),
+ [DocDefaultFilter.STARRED]: t('Starred', {
+ description: "Name of the filter listing the user's favorite docs",
+ }),
+ [DocDefaultFilter.TRASHBIN]: t('Trashbin', {
+ description: 'Name of the filter listing deleted docs',
+ }),
};
return {
@@ -27,7 +43,9 @@ export const useTrans = () => {
transFilter: (filter: DocDefaultFilter) => {
return translatedFilters[filter];
},
- untitledDocument: t('Untitled document'),
+ untitledDocument: t('Untitled document', {
+ description: 'Name displayed for a document without a title',
+ }),
translatedRoles,
translatedFilters,
};
diff --git a/src/frontend/apps/impress/src/features/docs/doc-presenter/components/PresentBlockItem.tsx b/src/frontend/apps/impress/src/features/docs/doc-presenter/components/PresentBlockItem.tsx
index 534309253..dc324d20a 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-presenter/components/PresentBlockItem.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-presenter/components/PresentBlockItem.tsx
@@ -48,7 +48,10 @@ export const PresentBlockItem = () => {
>
- {t('Present from here')}
+ {t('Present from here', {
+ description:
+ 'Editor side menu action starting the presentation from the current block',
+ })}
);
diff --git a/src/frontend/apps/impress/src/features/docs/doc-presenter/components/PresenterFloatingBar.tsx b/src/frontend/apps/impress/src/features/docs/doc-presenter/components/PresenterFloatingBar.tsx
index bb1b4cf4c..c78602f08 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-presenter/components/PresenterFloatingBar.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-presenter/components/PresenterFloatingBar.tsx
@@ -138,12 +138,16 @@ export const PresenterFloatingBar = ({
const actionOptions = useMemo(
() => [
{
- label: t('Copy link to slide'),
+ label: t('Copy link to slide', {
+ description: 'Menu action copying a link to the current slide',
+ }),
icon: ,
callback: onCopyLink,
},
{
- label: t('Download PDF'),
+ label: t('Download PDF', {
+ description: 'Menu action downloading the presentation as PDF',
+ }),
icon: ,
callback: onExportPdf,
isDisabled: isExportingPdf,
@@ -161,7 +165,9 @@ export const PresenterFloatingBar = ({
$align="center"
$css={barCss}
role="toolbar"
- aria-label={t('Presenter controls')}
+ aria-label={t('Presenter controls', {
+ description: 'Accessible name of the presentation toolbar',
+ })}
>
}
/>
@@ -181,7 +190,10 @@ export const PresenterFloatingBar = ({
variant="tertiary"
aria-disabled={isLast}
onClick={isLast ? undefined : onNext}
- aria-label={t('Next slide')}
+ aria-label={t('Next slide', {
+ description:
+ 'Accessible name of the button going to the next slide',
+ })}
icon={}
/>
@@ -197,7 +209,10 @@ export const PresenterFloatingBar = ({
color="neutral"
variant="tertiary"
onClick={toggleActions}
- aria-label={t('More options')}
+ aria-label={t('More options', {
+ description:
+ 'Accessible name of the button opening more presentation actions',
+ })}
aria-expanded={isActionsOpen}
aria-haspopup="menu"
icon={}
@@ -209,7 +224,15 @@ export const PresenterFloatingBar = ({
variant="tertiary"
onClick={onToggleFullscreen}
aria-label={
- isFullscreen ? t('Exit fullscreen') : t('Enter fullscreen')
+ isFullscreen
+ ? t('Exit fullscreen', {
+ description:
+ 'Accessible name of the button leaving fullscreen',
+ })
+ : t('Enter fullscreen', {
+ description:
+ 'Accessible name of the button entering fullscreen',
+ })
}
icon={
isFullscreen ? :
@@ -220,7 +243,10 @@ export const PresenterFloatingBar = ({
color="neutral"
variant="tertiary"
onClick={onClose}
- aria-label={t('Close presenter')}
+ aria-label={t('Close presenter', {
+ description:
+ 'Accessible name of the button leaving the presentation mode',
+ })}
icon={}
/>
diff --git a/src/frontend/apps/impress/src/features/docs/doc-presenter/components/PresenterOverlay.tsx b/src/frontend/apps/impress/src/features/docs/doc-presenter/components/PresenterOverlay.tsx
index b4c9154c9..25edec9ea 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-presenter/components/PresenterOverlay.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-presenter/components/PresenterOverlay.tsx
@@ -80,7 +80,13 @@ export const PresenterOverlay = ({
const contentSlides = useSlides(snapshotBlocks);
const title = useMemo(() => {
const { emoji, titleWithoutEmoji } = getEmojiAndTitle(doc.title ?? '');
- return [emoji, titleWithoutEmoji.trim() || t('Untitled document')]
+ return [
+ emoji,
+ titleWithoutEmoji.trim() ||
+ t('Untitled document', {
+ description: 'Name displayed for a document without a title',
+ }),
+ ]
.filter(Boolean)
.join(' ');
}, [doc.title, t]);
@@ -184,11 +190,14 @@ export const PresenterOverlay = ({
: getSlideTitle(currentSlide?.blocks ?? []);
const message = slideTitle
? t('Slide {{current}} of {{total}}: {{title}}', {
+ description: 'Screen reader announcement of the current slide',
current: currentIndex + 1,
total,
title: slideTitle,
})
: t('Slide {{current}} of {{total}}', {
+ description:
+ 'Screen reader announcement or accessible name of a slide',
current: currentIndex + 1,
total,
});
@@ -206,7 +215,9 @@ export const PresenterOverlay = ({
$css={overlayCss}
role="dialog"
aria-modal="true"
- aria-label={t('Presenter mode')}
+ aria-label={t('Presenter mode', {
+ description: 'Accessible name of the presentation mode',
+ })}
>
{mountedIndices.map((i) => (
@@ -216,6 +227,8 @@ export const PresenterOverlay = ({
isCurrent={i === currentIndex}
slide={slides[i]}
ariaLabel={t('Slide {{current}} of {{total}}', {
+ description:
+ 'Screen reader announcement or accessible name of a slide',
current: i + 1,
total,
})}
diff --git a/src/frontend/apps/impress/src/features/docs/doc-presenter/components/PresenterSlide.tsx b/src/frontend/apps/impress/src/features/docs/doc-presenter/components/PresenterSlide.tsx
index aa61e072a..9fd73df5d 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-presenter/components/PresenterSlide.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-presenter/components/PresenterSlide.tsx
@@ -103,8 +103,16 @@ export const PresenterSlide = ({
$css={outerCss}
style={outerStyle}
role="group"
- aria-roledescription={t('slide')}
- aria-label={ariaLabel ?? t('Presenter slide')}
+ aria-roledescription={t('slide', {
+ description:
+ 'Role description of a presentation slide for screen readers',
+ })}
+ aria-label={
+ ariaLabel ??
+ t('Presenter slide', {
+ description: 'Accessible name of a presentation slide',
+ })
+ }
aria-hidden={!isCurrent}
>
diff --git a/src/frontend/apps/impress/src/features/docs/doc-presenter/hooks/useCopyPresenterLink.tsx b/src/frontend/apps/impress/src/features/docs/doc-presenter/hooks/useCopyPresenterLink.tsx
index 20d53f31c..394f08cb5 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-presenter/hooks/useCopyPresenterLink.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-presenter/hooks/useCopyPresenterLink.tsx
@@ -17,8 +17,12 @@ export const useCopyPresenterLink = (docId: Doc['id']) => {
(index: number) => {
copyToClipboard(
`${window.location.origin}/docs/${docId}/?view=present&slide=${index + 1}`,
- t('Link Copied !'),
- t('Failed to copy link'),
+ t('Link Copied !', {
+ description: 'Toast confirming a link was copied to the clipboard',
+ }),
+ t('Failed to copy link', {
+ description: 'Toast shown when copying a link failed',
+ }),
);
},
[copyToClipboard, docId, t],
diff --git a/src/frontend/apps/impress/src/features/docs/doc-search/components/DocSearchButtonModal.tsx b/src/frontend/apps/impress/src/features/docs/doc-search/components/DocSearchButtonModal.tsx
index aeed79ac4..ae8786108 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-search/components/DocSearchButtonModal.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-search/components/DocSearchButtonModal.tsx
@@ -50,7 +50,9 @@ export const DocSearchButtonModal = ({ ...props }: ButtonProps) => {
size="medium"
color="brand"
variant="tertiary"
- aria-label={t('Search docs')}
+ aria-label={t('Search docs', {
+ description: 'Accessible name of the button opening the doc search',
+ })}
icon={}
{...props}
/>
diff --git a/src/frontend/apps/impress/src/features/docs/doc-search/components/DocSearchContent.tsx b/src/frontend/apps/impress/src/features/docs/doc-search/components/DocSearchContent.tsx
index e6c335d66..63ae6243b 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-search/components/DocSearchContent.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-search/components/DocSearchContent.tsx
@@ -59,14 +59,23 @@ export const DocSearchContent = ({
groupName: '',
groupKey: 'docs',
elements: [],
- emptyString: t('Loading documents...'),
+ emptyString: t('Loading documents...', {
+ description:
+ 'Placeholder and screen reader announcement while search results load',
+ }),
endActions: [],
});
useEffect(() => {
if (loading) {
if (search || isSearchNotMandatory) {
- announce(t('Loading documents...'), 'polite');
+ announce(
+ t('Loading documents...', {
+ description:
+ 'Placeholder and screen reader announcement while search results load',
+ }),
+ 'polite',
+ );
}
return;
}
@@ -101,8 +110,14 @@ export const DocSearchContent = ({
if (search && !loading) {
announce(
elements.length === 0
- ? t('No documents found')
- : t('{{count}} document found', { count: elements.length }),
+ ? t('No documents found', {
+ description: 'Message when a search has no result',
+ })
+ : t('{{count}} document found', {
+ description:
+ 'Screen reader announcement of the number of search results, plural form',
+ count: elements.length,
+ }),
'polite',
);
}
diff --git a/src/frontend/apps/impress/src/features/docs/doc-search/components/DocSearchFilters.tsx b/src/frontend/apps/impress/src/features/docs/doc-search/components/DocSearchFilters.tsx
index 6d3b71ccd..b8b89f6d8 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-search/components/DocSearchFilters.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-search/components/DocSearchFilters.tsx
@@ -21,7 +21,10 @@ export const DocSearchFilters = () => {
{
if (e.key === ' ' || e.key === 'Enter') {
@@ -52,11 +55,14 @@ export const DocSearchFilters = () => {
>
diff --git a/src/frontend/apps/impress/src/features/docs/doc-search/components/DocSearchModal.tsx b/src/frontend/apps/impress/src/features/docs/doc-search/components/DocSearchModal.tsx
index b8760b915..78ddfae38 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-search/components/DocSearchModal.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-search/components/DocSearchModal.tsx
@@ -93,7 +93,10 @@ const DocSearchModalGlobal = ({
closeOnClickOutside
size={isLargeScreen ? ModalSize.LARGE : ModalSize.FULL}
hideCloseButton
- aria-label={t('Search for a document')}
+ aria-label={t('Search for a document', {
+ description:
+ 'Accessible name or placeholder of a document search field',
+ })}
aria-labelledby="doc-search-modal-title"
aria-describedby="doc-search-modal-description"
title={
@@ -105,16 +108,25 @@ const DocSearchModalGlobal = ({
$align="flex-start"
id="doc-search-modal-title"
>
- {t('Search for a document')}
+ {t('Search for a document', {
+ description:
+ 'Accessible name or placeholder of a document search field',
+ })}
{t(
'Search documents by name, navigate using arrows, and select a result with Enter.',
+ {
+ description: 'Screen reader instructions of the search dialog',
+ },
)}
@@ -129,8 +141,12 @@ const DocSearchModalGlobal = ({
$padding={{ bottom: 'base' }}
>
@@ -63,23 +66,31 @@ export const AlertModalRequestAccess = ({
$margin={{ right: 'xxs' }}
variant="symbols-outlined"
/>
- {t('You have already requested access to this document.')}
+ {t('You have already requested access to this document.', {
+ description: 'Message in the request access dialog',
+ })}
)}
>
}
- confirmLabel={t('Request access')}
+ confirmLabel={t('Request access', {
+ description: 'Button to ask the owners for access to a document',
+ })}
onConfirm={onConfirm}
rightActions={
- {t('Cancel')}
+ {t('Cancel', {
+ description: 'Button to dismiss a dialog without confirming',
+ })}
diff --git a/src/frontend/apps/impress/src/features/docs/doc-share/components/ConfirmationLeaveModal.tsx b/src/frontend/apps/impress/src/features/docs/doc-share/components/ConfirmationLeaveModal.tsx
index 843876d27..334fc1019 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-share/components/ConfirmationLeaveModal.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-share/components/ConfirmationLeaveModal.tsx
@@ -47,7 +47,9 @@ export const ConfirmationLeaveModal = ({
onClose={onClose}
// TODO: add a fix on the Modal component on Cunningham side
// If aria-label is not set the modal add by default [object Object]
- aria-label={t('Confirmation to leave the document')}
+ aria-label={t('Confirmation to leave the document', {
+ description: 'Accessible name of the leave document dialog',
+ })}
aria-labelledby="modal-leave-doc-title"
aria-describedby="modal-leave-doc-desc"
rightActions={
@@ -78,11 +80,16 @@ export const ConfirmationLeaveModal = ({
$margin="0"
$align="flex-start"
>
- {t('Leave a doc')}
+ {t('Leave a doc', {
+ description: 'Title of the dialog to leave a shared document',
+ })}
@@ -173,23 +180,31 @@ const ButtonsLeaveDoc = ({
return (
<>
- {t('Cancel')}
+ {t('Cancel', {
+ description: 'Button to dismiss a dialog without confirming',
+ })}
- {t('Leave')}
+ {t('Leave', {
+ description: 'Dropdown menu item to leave the document',
+ })}
>
);
diff --git a/src/frontend/apps/impress/src/features/docs/doc-share/components/DocDesynchronized.tsx b/src/frontend/apps/impress/src/features/docs/doc-share/components/DocDesynchronized.tsx
index 496f35068..37467cfc2 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-share/components/DocDesynchronized.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-share/components/DocDesynchronized.tsx
@@ -23,9 +23,16 @@ export const DocDesynchronized = ({ doc }: DocDesynchronizedProps) => {
const { mutate: updateDocLink } = useUpdateDocLink({
listInvalidQueries: [KEY_LIST_DOC, KEY_DOC],
onSuccess: () => {
- toast(t('The document visibility restored.'), VariantType.SUCCESS, {
- duration: 2000,
- });
+ toast(
+ t('The document visibility restored.', {
+ description:
+ "Toast shown after resetting link sharing to the parent's settings",
+ }),
+ VariantType.SUCCESS,
+ {
+ duration: 2000,
+ },
+ );
},
});
@@ -46,7 +53,10 @@ export const DocDesynchronized = ({ doc }: DocDesynchronizedProps) => {
>
- {t('The link sharing rules differ from the parent document')}
+ {t('The link sharing rules differ from the parent document', {
+ description:
+ 'Banner shown on a sub-doc whose link settings differ from its parent',
+ })}
{doc.abilities.accesses_manage && (
@@ -63,7 +73,10 @@ export const DocDesynchronized = ({ doc }: DocDesynchronizedProps) => {
variant="tertiary"
icon={}
>
- {t('Restore')}
+ {t('Restore', {
+ description:
+ 'Button to restore a deleted document or an older version',
+ })}
)}
diff --git a/src/frontend/apps/impress/src/features/docs/doc-share/components/DocInheritedShareContent.tsx b/src/frontend/apps/impress/src/features/docs/doc-share/components/DocInheritedShareContent.tsx
index 98df4e8c9..832d0a5c2 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-share/components/DocInheritedShareContent.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-share/components/DocInheritedShareContent.tsx
@@ -42,7 +42,10 @@ export const DocInheritedShareContent = ({
>
- {t('People with access via the parent document')}
+ {t('People with access via the parent document', {
+ description:
+ 'Heading of the list of members inherited from the parent doc',
+ })}
diff --git a/src/frontend/apps/impress/src/features/docs/doc-share/components/DocRoleDropdown.tsx b/src/frontend/apps/impress/src/features/docs/doc-share/components/DocRoleDropdown.tsx
index 3bdb7fe2b..b3cb0442f 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-share/components/DocRoleDropdown.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-share/components/DocRoleDropdown.tsx
@@ -40,7 +40,10 @@ export const DocRoleDropdown = ({
const { mutate: removeDocInvitation } = useDeleteDocInvitation({
onError: (error) => {
toast(
- error?.data?.role?.[0] ?? t('Error during delete invitation'),
+ error?.data?.role?.[0] ??
+ t('Error during delete invitation', {
+ description: 'Toast shown when deleting an invitation failed',
+ }),
VariantType.ERROR,
{
duration: 4000,
@@ -51,9 +54,15 @@ export const DocRoleDropdown = ({
const { mutate: removeDocAccess } = useDeleteDocAccess({
onError: () => {
- toast(t('Error while deleting invitation'), VariantType.ERROR, {
- duration: 4000,
- });
+ toast(
+ t('Error while deleting invitation', {
+ description: 'Toast shown when deleting an invitation failed',
+ }),
+ VariantType.ERROR,
+ {
+ duration: 4000,
+ },
+ );
},
});
@@ -83,7 +92,9 @@ export const DocRoleDropdown = ({
if (rolesAllowed.length < allRoles.length) {
let result = message ? `${message}\n\n` : '';
- result += t('This user has access inherited from a parent page.');
+ result += t('This user has access inherited from a parent page.', {
+ description: "Tooltip explaining why a member's role cannot be changed",
+ });
return result;
}
@@ -109,7 +120,9 @@ export const DocRoleDropdown = ({
return (
@@ -122,6 +135,7 @@ export const DocRoleDropdown = ({
{
const { mutate: removeDocAccess } = useDeleteDocAccessRequest({
onError: () => {
- toast(t('Error while removing the request.'), VariantType.ERROR, {
- duration: 4000,
- });
+ toast(
+ t('Error while removing the request.', {
+ description: 'Toast shown when deleting an access request failed',
+ }),
+ VariantType.ERROR,
+ {
+ duration: 4000,
+ },
+ );
},
});
@@ -95,6 +101,7 @@ const DocShareAccessRequestItem = ({ doc, accessRequest }: Props) => {
canUpdate={doc.abilities.accesses_manage}
rolesAllowed={accessRequest.abilities.set_role_to}
ariaLabel={t('Change role for {{name}}', {
+ description: 'Accessible name of the role dropdown of a member',
name: accessRequest.user.full_name || accessRequest.user.email,
})}
/>
@@ -110,7 +117,9 @@ const DocShareAccessRequestItem = ({ doc, accessRequest }: Props) => {
}
size={isSmallMobile ? 'nano' : 'small'}
>
- {t('Approve')}
+ {t('Approve', {
+ description: 'Button to accept an access request',
+ })}
{doc.abilities.accesses_manage && (
@@ -121,7 +130,10 @@ const DocShareAccessRequestItem = ({ doc, accessRequest }: Props) => {
docId: doc.id,
})
}
- aria-label={t('Close the access request modal')}
+ aria-label={t('Close the access request modal', {
+ description:
+ 'Accessible name of the button closing the access request dialog',
+ })}
>
@@ -148,7 +160,9 @@ export const QuickSearchGroupAccessRequest = ({
accessRequestQuery.data?.pages.flatMap((page) => page.results) || [];
return {
- groupName: t('Access Requests'),
+ groupName: t('Access Requests', {
+ description: 'Heading of the list of pending access requests',
+ }),
elements: accessRequests,
endActions: accessRequestQuery.hasNextPage
? [
@@ -168,7 +182,9 @@ export const QuickSearchGroupAccessRequest = ({
return (
<>
@@ -212,9 +228,15 @@ export const ButtonAccessRequest = ({
const { toast } = useToast();
const { mutate: createRequest } = useCreateDocAccessRequest({
onSuccess: () => {
- toast(t('Access request sent successfully.'), VariantType.SUCCESS, {
- duration: 3000,
- });
+ toast(
+ t('Access request sent successfully.', {
+ description: 'Toast shown after requesting access',
+ }),
+ VariantType.SUCCESS,
+ {
+ duration: 3000,
+ },
+ );
},
});
@@ -227,6 +249,10 @@ export const ButtonAccessRequest = ({
{t(
'As this is a sub-document, please request access to the parent document to enable these features.',
+ {
+ description:
+ 'Message in the share dialog when the user lacks rights on a sub-doc',
+ },
)}
);
@@ -249,7 +275,10 @@ export const ButtonAccessRequest = ({
disabled={hasRequested}
{...buttonProps}
>
- {buttonProps.children || t('Request access')}
+ {buttonProps.children ||
+ t('Request access', {
+ description: 'Button to ask the owners for access to a document',
+ })}
);
};
diff --git a/src/frontend/apps/impress/src/features/docs/doc-share/components/DocShareAddMemberList.tsx b/src/frontend/apps/impress/src/features/docs/doc-share/components/DocShareAddMemberList.tsx
index 380ce7358..2509d4219 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-share/components/DocShareAddMemberList.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-share/components/DocShareAddMemberList.tsx
@@ -48,15 +48,19 @@ export const DocShareAddMemberList = ({
let messageError =
dataError['data']?.type === OptionType.INVITATION
? t(`Failed to create the invitation for {{email}}.`, {
+ description: 'Toast shown when inviting an email failed',
email: dataError['data']?.value,
})
- : t(`Failed to add the member in the document.`);
+ : t(`Failed to add the member in the document.`, {
+ description: 'Toast shown when adding a member failed',
+ });
if (
dataError.cause?.[0] ===
'Document invitation with this Email address and Document already exists.'
) {
messageError = t('"{{email}}" is already invited to the document.', {
+ description: 'Error shown when inviting an email already invited',
email: dataError['data']?.value,
});
}
@@ -66,6 +70,7 @@ export const DocShareAddMemberList = ({
'This email is already associated to a registered user.'
) {
messageError = t('"{{email}}" is already member of the document.', {
+ description: 'Error shown when inviting an existing member',
email: dataError['data']?.value,
});
}
@@ -108,9 +113,13 @@ export const DocShareAddMemberList = ({
const inviteLabel =
selectedUsers.length === 1
? t('Invite {{name}}', {
+ description: 'Button to invite one person',
name: selectedUsers[0].full_name || selectedUsers[0].email,
})
- : t('Invite {{count}} members', { count: selectedUsers.length });
+ : t('Invite {{count}} members', {
+ description: 'Button to invite several people, plural form',
+ count: selectedUsers.length,
+ });
return (
void onInvite()}
@@ -160,7 +172,9 @@ export const DocShareAddMemberList = ({
data-testid="doc-share-invite-button"
size={isSmallMobile ? 'small' : 'medium'}
>
- {t('Invite')}
+ {t('Invite', {
+ description: 'Button to invite people to a document',
+ })}
diff --git a/src/frontend/apps/impress/src/features/docs/doc-share/components/DocShareAddMemberListItem.tsx b/src/frontend/apps/impress/src/features/docs/doc-share/components/DocShareAddMemberListItem.tsx
index a973acd73..cd00b59f0 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-share/components/DocShareAddMemberListItem.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-share/components/DocShareAddMemberListItem.tsx
@@ -44,6 +44,8 @@ export const DocShareAddMemberListItem = ({ user, onRemoveUser }: Props) => {
onRemoveUser?.(user)}
aria-label={t('Remove {{name}} from the invite list', {
+ description:
+ 'Accessible name of the button removing a person from the pending invitees',
name: user.full_name || user.email,
})}
$withThemeInherited
diff --git a/src/frontend/apps/impress/src/features/docs/doc-share/components/DocShareButton.tsx b/src/frontend/apps/impress/src/features/docs/doc-share/components/DocShareButton.tsx
index c357e7306..14f08a40d 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-share/components/DocShareButton.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-share/components/DocShareButton.tsx
@@ -71,10 +71,26 @@ export const DocShareButton = ({
) : undefined
}
- aria-label={hasAccesses ? t('Shared') : t('Share')}
+ aria-label={
+ hasAccesses
+ ? t('Shared', {
+ description:
+ 'Share button label when the document is already shared',
+ })
+ : t('Share', {
+ description: 'Dropdown menu item to share the document',
+ })
+ }
data-test="share-button"
>
- {hasAccesses ? t('Shared') : t('Share')}
+ {hasAccesses
+ ? t('Shared', {
+ description:
+ 'Share button label when the document is already shared',
+ })
+ : t('Share', {
+ description: 'Dropdown menu item to share the document',
+ })}
{modalShare.isOpen && (
diff --git a/src/frontend/apps/impress/src/features/docs/doc-share/components/DocShareInvitation.tsx b/src/frontend/apps/impress/src/features/docs/doc-share/components/DocShareInvitation.tsx
index c90d22ecc..66fee5777 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-share/components/DocShareInvitation.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-share/components/DocShareInvitation.tsx
@@ -49,7 +49,11 @@ export const DocShareInvitationItem = ({
const { mutate: updateDocInvitation } = useUpdateDocInvitation({
onError: (error) => {
toast(
- error?.data?.role?.[0] ?? t('Error during update invitation'),
+ error?.data?.role?.[0] ??
+ t('Error during update invitation', {
+ description:
+ 'Toast shown when changing the role of an invitation failed',
+ }),
VariantType.ERROR,
{
duration: 4000,
@@ -85,6 +89,8 @@ export const DocShareInvitationItem = ({
doc={doc}
access={invitation}
ariaLabel={t('Change role for {{email}}', {
+ description:
+ 'Accessible name of the role dropdown of an invitation',
email: invitation.email,
})}
/>
@@ -123,7 +129,10 @@ export const DocShareModalInviteUserRow = ({
$gap="var(--c--globals--spacings--xxxs)"
>
- {t('Add')}
+ {t('Add', {
+ description:
+ 'Label of the action adding people to the document',
+ })}
@@ -149,7 +158,9 @@ export const QuickSearchGroupInvitation = ({
const invitations = data?.pages.flatMap((page) => page.results) || [];
return {
- groupName: t('Pending invitations'),
+ groupName: t('Pending invitations', {
+ description: 'Heading of the list of invitations not yet accepted',
+ }),
elements: invitations,
endActions: hasNextPage
? [
@@ -169,7 +180,9 @@ export const QuickSearchGroupInvitation = ({
return (
<>
{
- toast(t('Error while updating the member role.'), VariantType.ERROR, {
- duration: 4000,
- });
+ toast(
+ t('Error while updating the member role.', {
+ description: 'Toast shown when changing a member role failed',
+ }),
+ VariantType.ERROR,
+ {
+ duration: 4000,
+ },
+ );
},
});
@@ -78,6 +88,7 @@ export const DocShareMemberItem = ({
access={access}
doc={doc}
ariaLabel={t('Change role for {{name}}', {
+ description: 'Accessible name of the role dropdown of a member',
name: access.user.full_name || access.user.email,
})}
/>
@@ -108,8 +119,12 @@ export const QuickSearchGroupMember = ({
return {
groupName:
count === 1
- ? t('Document owner')
+ ? t('Document owner', {
+ description: 'Label of the single owner of a document',
+ })
: t('Share with {{count}} users', {
+ description:
+ 'Number of people a document is shared with, plural form',
count: count,
}),
elements: members,
@@ -119,7 +134,9 @@ export const QuickSearchGroupMember = ({
return (
{
t(
'{{name}} added to invite list. Add more members or press Tab to select role and invite.',
{
+ description:
+ 'Screen reader announcement after adding a person to the invitees',
name: userName,
},
),
@@ -133,6 +135,8 @@ export const DocShareModal = ({ doc, onClose, isRootDoc = true }: Props) => {
const userName = row.full_name || row.email;
announce(
t('{{name}} removed from invite list', {
+ description:
+ 'Screen reader announcement after removing a person from the invitees',
name: userName,
}),
'polite',
@@ -180,7 +184,9 @@ export const DocShareModal = ({ doc, onClose, isRootDoc = true }: Props) => {
isOpen
closeOnClickOutside
data-testid="doc-share-modal"
- aria-label={t('Share the document')}
+ aria-label={t('Share the document', {
+ description: 'Title and accessible name of the share dialog',
+ })}
size={isLargeScreen ? ModalSize.LARGE : ModalSize.FULL}
aria-modal="true"
onClose={onClose}
@@ -194,10 +200,15 @@ export const DocShareModal = ({ doc, onClose, isRootDoc = true }: Props) => {
$weight="600"
$margin="0"
>
- {t('Share the document')}
+ {t('Share the document', {
+ description: 'Title and accessible name of the share dialog',
+ })}
@@ -258,6 +269,10 @@ export const DocShareModal = ({ doc, onClose, isRootDoc = true }: Props) => {
>
{t(
'You can view this document but need additional access to see its members or modify settings.',
+ {
+ description:
+ 'Message in the share dialog for users with read-only access',
+ },
)}
{
)}
{canViewAccesses && (
{
setInputValue(str);
onFilter(str);
@@ -277,7 +295,10 @@ export const DocShareModal = ({ doc, onClose, isRootDoc = true }: Props) => {
inputValue={inputValue}
showInput={canShare}
loading={searchUsersQuery.isLoading}
- placeholder={t('Type a name or email')}
+ placeholder={t('Type a name or email', {
+ description:
+ 'Placeholder of the field searching users to invite',
+ })}
>
{showInheritedShareContent && (
{
if (userQuery.length < minLength) {
return t('Type at least {{minLength}} characters to display user names', {
+ description: 'Hint shown before the user search starts',
minLength,
});
}
if (isValidEmail(userQuery)) {
- return t('Choose the email');
+ return t('Choose the email', {
+ description: 'Hint to pick the typed email address as invitee',
+ });
}
if (!searchUsersRawData?.length) {
- return t('No results. Type a full email address to invite someone.');
+ return t('No results. Type a full email address to invite someone.', {
+ description: 'Message when the user search has no result',
+ });
}
- return t('Choose a user');
+ return t('Choose a user', {
+ description: 'Hint to pick a user in the search results',
+ });
}, [minLength, searchUsersRawData?.length, t, userQuery]);
useEffect(() => {
@@ -385,7 +413,9 @@ const QuickSearchInviteInputSection = ({
return (
}
>
- {t('Copy link')}
+ {t('Copy link', {
+ description: 'Dropdown menu item to copy the document link',
+ })}
+
+
+ {t('OK', { description: 'Button closing a dialog' })}
- {t('OK')}
);
diff --git a/src/frontend/apps/impress/src/features/docs/doc-share/components/DocVisibility.tsx b/src/frontend/apps/impress/src/features/docs/doc-share/components/DocVisibility.tsx
index 864487cd8..42f175ecd 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-share/components/DocVisibility.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-share/components/DocVisibility.tsx
@@ -54,7 +54,9 @@ export const DocVisibility = ({ doc }: DocVisibilityProps) => {
listInvalidQueries: [KEY_LIST_DOC, KEY_DOC],
onSuccess: () => {
toast(
- t('The document visibility has been updated.'),
+ t('The document visibility has been updated.', {
+ description: 'Toast shown after changing the link sharing settings',
+ }),
VariantType.SUCCESS,
{
duration: 2000,
@@ -131,12 +133,16 @@ export const DocVisibility = ({ doc }: DocVisibilityProps) => {
return (
- {t('Link settings')}
+ {t('Link settings', {
+ description: 'Heading of the link sharing settings',
+ })}
{isDesynchronized && }
{
>
{
haveDisabledOptions
? t(
'You cannot restrict access to a subpage relative to its parent page.',
+ {
+ description:
+ 'Tooltip on a disabled link sharing option of a sub-doc',
+ },
)
: undefined
}
@@ -222,10 +235,17 @@ export const DocVisibility = ({ doc }: DocVisibilityProps) => {
haveDisabledLinkRoleOptions
? t(
'You cannot restrict access to a subpage relative to its parent page.',
+ {
+ description:
+ 'Tooltip on a disabled link sharing option of a sub-doc',
+ },
)
: undefined
}
- label={t('Document access mode')}
+ label={t('Document access mode', {
+ description:
+ 'Label of the selector of what link users can do: read or edit',
+ })}
>
{
const { t } = useTranslation();
const linkReachTranslations = {
- [LinkReach.RESTRICTED]: t('Private'),
- [LinkReach.AUTHENTICATED]: t('Connected'),
- [LinkReach.PUBLIC]: t('Public'),
+ [LinkReach.RESTRICTED]: t('Private', {
+ description: 'Link sharing level: only invited people',
+ }),
+ [LinkReach.AUTHENTICATED]: t('Connected', {
+ description: 'Link sharing level: any logged-in user',
+ }),
+ [LinkReach.PUBLIC]: t('Public', {
+ description: 'Document visibility: anyone with the link can access',
+ }),
};
const linkModeTranslations = {
- [LinkRole.READER]: t('Reading'),
- [LinkRole.EDITOR]: t('Editing'),
+ [LinkRole.READER]: t('Reading', {
+ description: 'Link sharing role: read only',
+ }),
+ [LinkRole.EDITOR]: t('Editing', {
+ description: 'Link sharing role: can edit',
+ }),
};
const linkReachChoices = {
@@ -21,8 +31,12 @@ export const useTranslatedShareSettings = () => {
label: linkReachTranslations[LinkReach.RESTRICTED],
icon: 'lock',
value: LinkReach.RESTRICTED,
- descriptionReadOnly: t('Only invited people can access'),
- descriptionEdit: t('Only invited people can access'),
+ descriptionReadOnly: t('Only invited people can access', {
+ description: 'Description of the private link sharing level',
+ }),
+ descriptionEdit: t('Only invited people can access', {
+ description: 'Description of the private link sharing level',
+ }),
},
[LinkReach.AUTHENTICATED]: {
label: linkReachTranslations[LinkReach.AUTHENTICATED],
@@ -30,17 +44,23 @@ export const useTranslatedShareSettings = () => {
value: LinkReach.AUTHENTICATED,
descriptionReadOnly: t(
'Anyone with the link can view the document if they are logged in',
+ { description: 'Description of the connected level, read only' },
),
descriptionEdit: t(
'Anyone with the link can edit the document if they are logged in',
+ { description: 'Description of the connected level, can edit' },
),
},
[LinkReach.PUBLIC]: {
label: linkReachTranslations[LinkReach.PUBLIC],
icon: 'public',
value: LinkReach.PUBLIC,
- descriptionReadOnly: t('Anyone with the link can see the document'),
- descriptionEdit: t('Anyone with the link can edit the document'),
+ descriptionReadOnly: t('Anyone with the link can see the document', {
+ description: 'Description of the public level, read only',
+ }),
+ descriptionEdit: t('Anyone with the link can edit the document', {
+ description: 'Description of the public level, can edit',
+ }),
},
};
diff --git a/src/frontend/apps/impress/src/features/docs/doc-table-content/components/TableContentSideBar.tsx b/src/frontend/apps/impress/src/features/docs/doc-table-content/components/TableContentSideBar.tsx
index bc6e93ee9..afd60e4bf 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-table-content/components/TableContentSideBar.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-table-content/components/TableContentSideBar.tsx
@@ -102,10 +102,16 @@ export const TableContentSideBar = ({ onClose }: TableContentSideBarProps) => {
$size="16px"
$margin="0"
>
- {t('Table of Contents')}
+ {t('Table of Contents', {
+ description:
+ 'Title of the side panel listing the document headings',
+ })}
@@ -161,8 +167,14 @@ export const TableContentSideBarButton = () => {
const isActive = isPanelOpen && activePanel === 'tableContent';
const ariaLabel = isActive
- ? t('Hide the table of contents sidebar')
- : t('Show the table of contents sidebar');
+ ? t('Hide the table of contents sidebar', {
+ description:
+ 'Accessible name of the toggle hiding the table of contents',
+ })
+ : t('Show the table of contents sidebar', {
+ description:
+ 'Accessible name of the toggle showing the table of contents',
+ });
return (
) => {
$justify="center"
$padding={{ vertical: 'xs' }}
role="status"
- aria-label={t('Loading more documents')}
+ aria-label={t('Loading more documents', {
+ description: 'Screen reader status while more tree items load',
+ })}
>
@@ -189,8 +191,15 @@ const DocSubPageItemContent = (props: TreeViewNodeProps) => {
tabIndex={-1}
aria-label={
isDeleted
- ? t('{{title}} (deleted)', { title: displayTitle })
- : t('Open document {{title}}', { title: displayTitle })
+ ? t('{{title}} (deleted)', {
+ description: 'Accessible name of a deleted doc in the tree',
+ title: displayTitle,
+ })
+ : t('Open document {{title}}', {
+ description:
+ 'Accessible name of a document link, an action to open it',
+ title: displayTitle,
+ })
}
aria-current={isCurrentPage ? 'page' : undefined}
data-testid={`doc-sub-page-item-${doc.id}`}
diff --git a/src/frontend/apps/impress/src/features/docs/doc-tree/components/DocTree.tsx b/src/frontend/apps/impress/src/features/docs/doc-tree/components/DocTree.tsx
index 9fe952662..92756ad9e 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-tree/components/DocTree.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-tree/components/DocTree.tsx
@@ -173,7 +173,9 @@ export const DocTree = ({ currentDoc }: DocTreeProps) => {
data-testid="doc-tree"
$height="100%"
role="tree"
- aria-label={t('Document tree')}
+ aria-label={t('Document tree', {
+ description: 'Accessible name of the tree of documents',
+ })}
aria-describedby="doc-tree-keyboard-instructions"
$css={css`
/**
@@ -208,6 +210,10 @@ export const DocTree = ({ currentDoc }: DocTreeProps) => {
{t(
'Use the up and down arrow keys to move between documents, and Enter to open one. Press F2 to reach the actions of a document and to move between them, use Escape to go back to the document list.',
+ {
+ description:
+ 'Screen reader keyboard instructions of the document tree',
+ },
)}
diff --git a/src/frontend/apps/impress/src/features/docs/doc-versioning/components/DocVersionHeader.tsx b/src/frontend/apps/impress/src/features/docs/doc-versioning/components/DocVersionHeader.tsx
index b3cc0a880..3ce33b091 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-versioning/components/DocVersionHeader.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-versioning/components/DocVersionHeader.tsx
@@ -15,7 +15,9 @@ export const DocVersionHeader = () => {
$width="100%"
$padding={{ vertical: 'base' }}
$gap={spacingsTokens['base']}
- aria-label={t('It is the document title')}
+ aria-label={t('It is the document title', {
+ description: 'Accessible name of the title in the version history',
+ })}
className="--docs--doc-version-header"
>
diff --git a/src/frontend/apps/impress/src/features/docs/doc-versioning/components/ModalConfirmationVersion.tsx b/src/frontend/apps/impress/src/features/docs/doc-versioning/components/ModalConfirmationVersion.tsx
index c4d86c255..efb1e31b2 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-versioning/components/ModalConfirmationVersion.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-versioning/components/ModalConfirmationVersion.tsx
@@ -45,7 +45,12 @@ export const ModalConfirmationVersion = ({
*/
const { mutate: restoreVersion, isPending } = useRestoreDocVersion({
onSuccess: () => {
- toast(t('Version restored successfully'), VariantType.SUCCESS);
+ toast(
+ t('Version restored successfully', {
+ description: 'Toast shown after restoring an older version',
+ }),
+ VariantType.SUCCESS,
+ );
onSuccess();
threadStore?.refreshThreads();
@@ -57,26 +62,34 @@ export const ModalConfirmationVersion = ({
isOpen
closeOnClickOutside
onClose={() => onClose()}
- aria-label={t('Warning')}
+ aria-label={t('Warning', { description: 'Title of a warning dialog' })}
rightActions={
<>
onClose()}
>
- {t('Cancel')}
+ {t('Cancel', {
+ description: 'Button to dismiss a dialog without confirming',
+ })}
restoreVersion({ docId, versionId })}
>
- {t('Restore')}
+ {t('Restore', {
+ description:
+ 'Button to restore a deleted document or an older version',
+ })}
>
}
@@ -89,7 +102,9 @@ export const ModalConfirmationVersion = ({
$size="h6"
$align="flex-start"
>
- {t('Restoring an older version')}
+ {t('Restoring an older version', {
+ description: 'Title of the dialog confirming restoring a version',
+ })}
}
>
@@ -99,6 +114,7 @@ export const ModalConfirmationVersion = ({
{t(
"The current document will be replaced, but you'll still find it in the version history.",
+ { description: 'Explanation in the restore version dialog' },
)}
diff --git a/src/frontend/apps/impress/src/features/docs/doc-versioning/components/ModalSelectVersion.tsx b/src/frontend/apps/impress/src/features/docs/doc-versioning/components/ModalSelectVersion.tsx
index 6f91ed287..0b06d3332 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-versioning/components/ModalSelectVersion.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-versioning/components/ModalSelectVersion.tsx
@@ -60,7 +60,9 @@ export const ModalSelectVersion = ({
closeOnClickOutside={true}
size={ModalSize.EXTRA_LARGE}
onClose={onClose}
- aria-label={t('Version history')}
+ aria-label={t('Version history', {
+ description: 'Title of the dialog listing past versions',
+ })}
>
- {t('Version history')}
+ {t('Version history', {
+ description: 'Title of the dialog listing past versions',
+ })}
- {t('Select a version on the right to restore')}
+ {t('Select a version on the right to restore', {
+ description: 'Hint in the version history dialog',
+ })}
)}
@@ -119,7 +125,9 @@ export const ModalSelectVersion = ({
`}
>
- {t('History')}
+ {t('History', {
+ description:
+ 'Dropdown menu item to view the document history',
+ })}
- {t('Restore')}
+ {t('Restore', {
+ description:
+ 'Button to restore a deleted document or an older version',
+ })}
)}
diff --git a/src/frontend/apps/impress/src/features/docs/doc-versioning/components/VersionItem.tsx b/src/frontend/apps/impress/src/features/docs/doc-versioning/components/VersionItem.tsx
index 231ea0cc9..71eb57277 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-versioning/components/VersionItem.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-versioning/components/VersionItem.tsx
@@ -15,7 +15,10 @@ export const VersionItem = ({ text, isActive, onSelect }: VersionItemProps) => {
return (
- {t('No versions')}
+ {t('No versions', {
+ description: 'Message when a document has no past version',
+ })}
)}
@@ -135,7 +137,10 @@ export const VersionList = ({
{selectedVersionDate
- ? t('Selected version {{date}}', { date: selectedVersionDate })
+ ? t('Selected version {{date}}', {
+ description: 'Screen reader text of the selected version',
+ date: selectedVersionDate,
+ })
: ''}
diff --git a/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocGridContentList.tsx b/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocGridContentList.tsx
index fba889ed1..4cd55c5bd 100644
--- a/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocGridContentList.tsx
+++ b/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocGridContentList.tsx
@@ -121,11 +121,18 @@ export const DraggableDocGridContentList = ({
screenReaderInstructions: {
draggable: t(
'To pick up a draggable item, press space or enter. While dragging, use the arrow keys to move the item. Press space or enter again to drop the item in its new position, or press escape to cancel.',
+ {
+ description:
+ 'Screen reader instructions for keyboard drag and drop',
+ },
),
},
announcements: {
onDragStart({ active }: { active: { id: UniqueIdentifier } }) {
- return t('Picked up document {{id}}.', { id: active.id });
+ return t('Picked up document {{id}}.', {
+ description: 'Screen reader announcement when dragging starts',
+ id: active.id,
+ });
},
onDragOver({
active,
@@ -136,11 +143,13 @@ export const DraggableDocGridContentList = ({
}) {
if (over) {
return t('Document {{activeId}} is over document {{overId}}.', {
+ description: 'Screen reader announcement while dragging',
activeId: active.id,
overId: over.id,
});
}
return t('Document {{id}} is no longer over a droppable area.', {
+ description: 'Screen reader announcement while dragging',
id: active.id,
});
},
@@ -154,13 +163,23 @@ export const DraggableDocGridContentList = ({
if (over) {
return t(
'Document {{activeId}} was dropped over document {{overId}}.',
- { activeId: active.id, overId: over.id },
+ {
+ description:
+ 'Screen reader announcement when a doc is dropped on another',
+ activeId: active.id,
+ overId: over.id,
+ },
);
}
- return t('Document {{id}} was dropped.', { id: active.id });
+ return t('Document {{id}} was dropped.', {
+ description: 'Screen reader announcement when a doc is dropped',
+ id: active.id,
+ });
},
onDragCancel({ active }: { active: { id: UniqueIdentifier } }) {
return t('Dragging was cancelled. Document {{id}} was dropped.', {
+ description:
+ 'Screen reader announcement when dragging is cancelled',
id: active.id,
});
},
@@ -171,10 +190,15 @@ export const DraggableDocGridContentList = ({
const overlayText = useMemo(() => {
if (!canDrag) {
- return t('You must be the owner to move the document');
+ return t('You must be the owner to move the document', {
+ description: 'Tooltip on a doc that cannot be dragged',
+ });
}
if (!canDrop) {
- return t('You must be at least the administrator of the target document');
+ return t(
+ 'You must be at least the administrator of the target document',
+ { description: 'Tooltip on a drop target the user cannot move into' },
+ );
}
return selectedDoc?.title || untitledDocument;
@@ -228,7 +252,9 @@ export const DraggableDocGridContentList = ({
$radius="12px"
data-testid="drag-doc-overlay"
role="alert"
- aria-label={t('Drag and drop status')}
+ aria-label={t('Drag and drop status', {
+ description: 'Accessible name of the drag and drop live region',
+ })}
$theme={cannotMoveDoc ? 'error' : 'brand'}
$variation="tertiary"
$scope="semantic"
diff --git a/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGrid.tsx b/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGrid.tsx
index ee1d20746..0ca3b7238 100644
--- a/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGrid.tsx
+++ b/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGrid.tsx
@@ -121,7 +121,9 @@ export const DocsGrid = ({
>
- {t('More docs')}
+ {t('More docs', {
+ description: 'Button loading more documents',
+ })}
)}
@@ -178,20 +182,30 @@ const DocGridTitleBar = ({
const { t } = useTranslation();
const { isDesktop } = useResponsiveStore();
- let title = t('Recent');
+ let title = t('Recent', {
+ description: 'Name of the filter listing recently opened docs',
+ });
let icon = ;
if (target === DocDefaultFilter.MY_DOCS) {
icon = ;
- title = t('My docs');
+ title = t('My docs', {
+ description: 'Name of the filter listing docs owned by the user',
+ });
} else if (target === DocDefaultFilter.SHARED_WITH_ME) {
icon = ;
- title = t('Shared with me');
+ title = t('Shared with me', {
+ description: 'Name of the filter listing docs shared with the user',
+ });
} else if (target === DocDefaultFilter.STARRED) {
icon = ;
- title = t('Starred');
+ title = t('Starred', {
+ description: "Name of the filter listing the user's favorite docs",
+ });
} else if (target === DocDefaultFilter.TRASHBIN) {
icon = ;
- title = t('Trashbin');
+ title = t('Trashbin', {
+ description: 'Name of the filter listing deleted docs',
+ });
}
return (
@@ -229,18 +243,24 @@ const DocGridNoDocs = ({ target }: { target: DocDefaultFilter }) => {
<>
- {t('No doc yet')}
+ {t('No doc yet', {
+ description: 'Empty state heading when the user has no doc',
+ })}
{[DocDefaultFilter.ALL_DOCS, DocDefaultFilter.MY_DOCS].includes(
target,
) && (
- {t('Your docs will appear here.')}
+ {t('Your docs will appear here.', {
+ description: 'Empty state text of the docs list',
+ })}
)}
{target === DocDefaultFilter.SHARED_WITH_ME && (
- {t('Your shared docs will appear here.')}
+ {t('Your shared docs will appear here.', {
+ description: 'Empty state text of the shared docs list',
+ })}
)}
>
@@ -249,10 +269,14 @@ const DocGridNoDocs = ({ target }: { target: DocDefaultFilter }) => {
<>
- {t('No doc deleted')}
+ {t('No doc deleted', {
+ description: 'Empty state heading of the trashbin',
+ })}
- {t('Deleted docs will appear here.')}
+ {t('Deleted docs will appear here.', {
+ description: 'Empty state text of the trashbin',
+ })}
>
)}
diff --git a/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGridActions.tsx b/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGridActions.tsx
index 91222c220..cf5760104 100644
--- a/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGridActions.tsx
+++ b/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGridActions.tsx
@@ -60,13 +60,20 @@ export const DocsGridTrashbinActions = ({
],
options: {
onSuccess: (_data) => {
- toast(t('The document has been restored.'), VariantType.SUCCESS, {
- duration: 4000,
- });
+ toast(
+ t('The document has been restored.', {
+ description: 'Toast shown after a deleted document is restored',
+ }),
+ VariantType.SUCCESS,
+ {
+ duration: 4000,
+ },
+ );
},
onError: (error) => {
toast(
t('An error occurred while restoring the document: {{error}}', {
+ description: 'Toast shown when restoring a deleted document fails',
error: error?.message,
}),
VariantType.ERROR,
@@ -84,7 +91,9 @@ export const DocsGridTrashbinActions = ({
const options: DropdownMenuItem[] = [
{
- label: t('Restore'),
+ label: t('Restore', {
+ description: 'Button to restore a deleted document or an older version',
+ }),
icon: (
{' '}
- {t('Last modified')}
+ {t('Last modified', {
+ description:
+ 'Column header and sort button: date of last modification',
+ })}
}
- ariaLabel={t('Last modified')}
+ ariaLabel={t('Last modified', {
+ description:
+ 'Column header and sort button: date of last modification',
+ })}
ordering={ordering}
field="updated_at"
defaultOrdering={defaultOrdering.updated_at}
@@ -114,7 +124,9 @@ const DocGridColumnWithoutSort = ({ target }: { target: DocDefaultFilter }) => {
<>
- {t('Name')}
+ {t('Name', {
+ description: 'Column header and sort button: document name',
+ })}
{!isSmallMobile && (
@@ -129,8 +141,14 @@ const DocGridColumnWithoutSort = ({ target }: { target: DocDefaultFilter }) => {
>
{' '}
{target === DocDefaultFilter.STARRED
- ? t('Last modified')
- : t('Days remaining')}
+ ? t('Last modified', {
+ description:
+ 'Column header and sort button: date of last modification',
+ })
+ : t('Days remaining', {
+ description:
+ 'Column header of the trashbin: days before permanent deletion',
+ })}
)}
@@ -171,11 +189,21 @@ const DocGridSortButton = ({
? t(
'Sorted documents by {{label}}, {{direction}}. Activate to reverse.',
{
+ description: 'Accessible name of a sort button, current sort',
label: ariaLabel,
- direction: isDesc ? t('descending') : t('ascending'),
+ direction: isDesc
+ ? t('descending', {
+ description: 'Sort direction, from highest to lowest',
+ })
+ : t('ascending', {
+ description: 'Sort direction, from lowest to highest',
+ }),
},
)
- : t('Sort documents by {{label}}', { label: ariaLabel })
+ : t('Sort documents by {{label}}', {
+ description: 'Accessible name of a sort button',
+ label: ariaLabel,
+ })
}
iconPosition="right"
icon={}
diff --git a/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGridItem.tsx b/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGridItem.tsx
index 9fdb94b34..fe79983d4 100644
--- a/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGridItem.tsx
+++ b/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGridItem.tsx
@@ -79,6 +79,7 @@ export const DocsGridItem = ({
`}
className="--docs--doc-grid-item"
aria-label={t('Open document: {{title}}', {
+ description: 'Accessible name of a document row, an action to open it',
title: doc.title || untitledDocument,
})}
{...boxProps}
@@ -132,6 +133,8 @@ export const DocsGridItem = ({
href={`/docs/${doc.id}`}
tabIndex={-1}
aria-label={t('{{title}}, updated {{date}}', {
+ description:
+ 'Accessible name of a document row with its last update',
title: doc.title || untitledDocument,
date: dateToDisplay,
})}
@@ -191,7 +194,12 @@ export const DocsGridItemTitle = ({
$size="sm"
icon={}
/>
- {t('This document is starred')}
+
+ {t('This document is starred', {
+ description:
+ "Screen reader text indicating the document is in the user's favorites",
+ })}
+
>
)}
{isShared && !withTooltip && }
@@ -200,8 +208,13 @@ export const DocsGridItemTitle = ({
content={
{isPublic
- ? t('Accessible to anyone')
- : t('Accessible to authenticated users')}
+ ? t('Accessible to anyone', {
+ description: 'Tooltip: the document is public',
+ })
+ : t('Accessible to authenticated users', {
+ description:
+ 'Tooltip: the document is open to logged-in users',
+ })}
}
placement="top"
@@ -227,8 +240,12 @@ const IconPublic = ({ isPublic }: { isPublic: boolean }) => {
$size="sm"
aria-label={
isPublic
- ? t('Accessible to anyone')
- : t('Accessible to authenticated users')
+ ? t('Accessible to anyone', {
+ description: 'Tooltip: the document is public',
+ })
+ : t('Accessible to authenticated users', {
+ description: 'Tooltip: the document is open to logged-in users',
+ })
}
icon={
isPublic ? (
@@ -255,7 +272,7 @@ const useDateToDisplay = (doc: Doc, isInTrashbin: boolean) => {
config.TRASHBIN_CUTOFF_DAYS,
);
- dateToDisplay = `${daysLeft} ${t('days', { count: daysLeft })}`;
+ dateToDisplay = `${daysLeft} ${t('days', { description: 'Unit after a number of days, plural form', count: daysLeft })}`;
isRelativeDate = false;
}
diff --git a/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGridItemSharedButton.tsx b/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGridItemSharedButton.tsx
index 669011dbc..fe18304d0 100644
--- a/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGridItemSharedButton.tsx
+++ b/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGridItemSharedButton.tsx
@@ -34,7 +34,11 @@ export const DocsGridItemSharedButton = ({ doc, disabled }: Props) => {
- {t('Shared with {{count}} users', { count: sharedCount })}
+ {t('Shared with {{count}} users', {
+ description:
+ 'Tooltip: number of people a doc is shared with, plural form',
+ count: sharedCount,
+ })}
}
placement="top"
@@ -42,7 +46,10 @@ export const DocsGridItemSharedButton = ({ doc, disabled }: Props) => {
>
{
const { user } = useAuth();
const userMenu = user || {
- full_name: t('Guest'),
+ full_name: t('Guest', {
+ description: 'Name displayed for a user not logged in',
+ }),
email: '',
};
diff --git a/src/frontend/apps/impress/src/features/header/components/HeaderFloatingBar.tsx b/src/frontend/apps/impress/src/features/header/components/HeaderFloatingBar.tsx
index 64331b294..d09f7d309 100644
--- a/src/frontend/apps/impress/src/features/header/components/HeaderFloatingBar.tsx
+++ b/src/frontend/apps/impress/src/features/header/components/HeaderFloatingBar.tsx
@@ -21,7 +21,12 @@ export const HeaderFloatingBar = (props: PropsWithChildren) => {
return (
{isTablet && (
-
+
)}
diff --git a/src/frontend/apps/impress/src/features/help/components/HelpMenu.tsx b/src/frontend/apps/impress/src/features/help/components/HelpMenu.tsx
index b92b1385d..6e0582d00 100644
--- a/src/frontend/apps/impress/src/features/help/components/HelpMenu.tsx
+++ b/src/frontend/apps/impress/src/features/help/components/HelpMenu.tsx
@@ -75,7 +75,9 @@ export const HelpMenu = ({
const options = useMemo(
() => [
{
- label: t('Get Support'),
+ label: t('Get Support', {
+ description: 'Help menu link to the support',
+ }),
icon: ,
callback: () => {
if (supportMailto) {
@@ -85,7 +87,9 @@ export const HelpMenu = ({
isHidden: !supportMailto,
},
{
- label: t('Documentation'),
+ label: t('Documentation', {
+ description: 'Help menu link to the documentation',
+ }),
icon: ,
callback: () => {
if (documentationUrl) {
@@ -95,18 +99,22 @@ export const HelpMenu = ({
isHidden: !documentationUrl,
},
{
- label: t('Onboarding'),
+ label: t('Onboarding', {
+ description: 'Help menu entry replaying the product tour',
+ }),
icon: ,
callback: modalOnbording.open,
isHidden: !onboardingEnabled,
},
{
- label: t('Legal'),
+ label: t('Legal', { description: 'Help menu group of legal pages' }),
icon: ,
isHidden: !hasLegalLinks,
children: [
{
- label: t('Personal data and cookies'),
+ label: t('Personal data and cookies', {
+ description: 'Help menu link to the privacy policy',
+ }),
callback: () => {
if (legalLinks?.personal_data) {
openExternalLink(legalLinks.personal_data);
@@ -115,7 +123,9 @@ export const HelpMenu = ({
isHidden: !legalLinks?.personal_data,
},
{
- label: t('Terms of use'),
+ label: t('Terms of use', {
+ description: 'Help menu link to the terms of use',
+ }),
callback: () => {
if (legalLinks?.terms_of_use) {
openExternalLink(legalLinks.terms_of_use);
@@ -124,7 +134,9 @@ export const HelpMenu = ({
isHidden: !legalLinks?.terms_of_use,
},
{
- label: t('Accessibility statement'),
+ label: t('Accessibility statement', {
+ description: 'Help menu link to the accessibility statement',
+ }),
callback: () => {
if (legalLinks?.accessibility_statement) {
openExternalLink(legalLinks.accessibility_statement);
@@ -133,7 +145,9 @@ export const HelpMenu = ({
isHidden: !legalLinks?.accessibility_statement,
},
{
- label: t('Legal notice'),
+ label: t('Legal notice', {
+ description: 'Help menu link to the legal notice',
+ }),
callback: () => {
if (legalLinks?.legal_notice) {
openExternalLink(legalLinks.legal_notice);
@@ -179,7 +193,10 @@ export const HelpMenu = ({
>
{
{props.isOpen ? : null}
{
),
- title: t('Compose your doc easily'),
+ title: t('Compose your doc easily', {
+ description: 'Product tour step title',
+ }),
description: t(
'Move, duplicate, and transform your texts, headings, lists, images without breaking your layout.',
+ { description: 'Product tour step description' },
),
content: (
)}
{!isMobile && (
- {t('Show more')}
+ {t('Show more', {
+ description: 'Button revealing more of the home page',
+ })}
diff --git a/src/frontend/apps/impress/src/features/home/components/HomeBottom.tsx b/src/frontend/apps/impress/src/features/home/components/HomeBottom.tsx
index 5374e6330..441199687 100644
--- a/src/frontend/apps/impress/src/features/home/components/HomeBottom.tsx
+++ b/src/frontend/apps/impress/src/features/home/components/HomeBottom.tsx
@@ -46,11 +46,18 @@ function HomeProConnect() {
$css="zoom: 1.9;"
$theme="brand"
>
-
+
- {t('Docs is already available, log in to use it now.')}
+ {t('Docs is already available, log in to use it now.', {
+ description: 'Call-to-action text at the bottom of the home page',
+ })}
diff --git a/src/frontend/apps/impress/src/features/home/components/HomeContent.tsx b/src/frontend/apps/impress/src/features/home/components/HomeContent.tsx
index f2adf3803..8b6f2de8c 100644
--- a/src/frontend/apps/impress/src/features/home/components/HomeContent.tsx
+++ b/src/frontend/apps/impress/src/features/home/components/HomeContent.tsx
@@ -37,7 +37,9 @@ export function HomeContent() {
id={MAIN_LAYOUT_ID}
tabIndex={-1}
className="--docs--home-content"
- aria-label={t('Main content')}
+ aria-label={t('Main content', {
+ description: 'Accessible name of the main area of the page',
+ })}
$css={css`
&:focus {
outline: 3px solid var(--c--globals--colors--primary-600);
@@ -75,8 +77,11 @@ export function HomeContent() {
isColumn={false}
isSmallDevice={isTablet}
illustration={SC5}
- title={t('Govs ❤️ Open Source.')}
- tag={t('Open Source')}
+ title={t('Govs ❤️ Open Source.', {
+ description:
+ 'Home page section title about public sector and open source',
+ })}
+ tag={t('Open Source', { description: 'Home page section tag' })}
textWidth="60%"
$css={`min-height: calc(100vh - ${getHeaderHeight(isSmallMobile)}px);`}
description={
@@ -195,10 +200,13 @@ export function HomeContent() {
video={
isFrLanguage ? `/assets/SC1-fr.webm` : `/assets/SC1-en.webm`
}
- title={t('An uncompromising writing experience.')}
- tag={t('Write')}
+ title={t('An uncompromising writing experience.', {
+ description: 'Home page section title',
+ })}
+ tag={t('Write', { description: 'Home page section tag' })}
description={t(
'Docs offers an intuitive writing experience. Its minimalist interface favors content over layout, while offering the essentials: media import, offline mode and keyboard shortcuts for greater efficiency.',
+ { description: 'Home page section text' },
)}
/>
@@ -206,10 +214,13 @@ export function HomeContent() {
isColumn={false}
isSmallDevice={isMobile}
illustration={isFrLanguage ? SC2Fr : SC2En}
- title={t('Simple and secure collaboration.')}
- tag={t('Collaborate')}
+ title={t('Simple and secure collaboration.', {
+ description: 'Home page section title',
+ })}
+ tag={t('Collaborate', { description: 'Home page section tag' })}
description={t(
'Docs makes real-time collaboration simple. Invite collaborators - public officials or external partners - with one click to see their changes live, while maintaining precise access control for data security.',
+ { description: 'Home page section text' },
)}
/>
diff --git a/src/frontend/apps/impress/src/features/home/components/HomeSection.tsx b/src/frontend/apps/impress/src/features/home/components/HomeSection.tsx
index 53141418e..fba1ddb43 100644
--- a/src/frontend/apps/impress/src/features/home/components/HomeSection.tsx
+++ b/src/frontend/apps/impress/src/features/home/components/HomeSection.tsx
@@ -109,7 +109,13 @@ export const HomeSection = ({
{availableSoon && (
-
+
)}
{
await changeLanguageSynchronized(backendLocale, user);
announce(
t('Language changed to {{language}}', {
+ description:
+ 'Screen reader announcement after switching the interface language',
language: backendLabel,
defaultValue: `Language changed to ${backendLabel}`,
}),
@@ -69,7 +71,9 @@ export const LanguagePickerLegacy = () => {
{
void changeLanguageSynchronized(value, user).then(() => {
announce(
t('Language changed to {{language}}', {
+ description:
+ 'Screen reader announcement after switching the interface language',
language: backendLabel,
defaultValue: `Language changed to ${backendLabel}`,
}),
diff --git a/src/frontend/apps/impress/src/features/left-panel/components/LefPanelTargetFilters.tsx b/src/frontend/apps/impress/src/features/left-panel/components/LefPanelTargetFilters.tsx
index 467c013d8..840fc8004 100644
--- a/src/frontend/apps/impress/src/features/left-panel/components/LefPanelTargetFilters.tsx
+++ b/src/frontend/apps/impress/src/features/left-panel/components/LefPanelTargetFilters.tsx
@@ -34,7 +34,9 @@ export const LeftPanelTargetFilters = () => {
$variation="tertiary"
/>
),
- label: t('Recent'),
+ label: t('Recent', {
+ description: 'Name of the filter listing recently opened docs',
+ }),
targetQuery: DocDefaultFilter.ALL_DOCS,
},
{
@@ -45,7 +47,9 @@ export const LeftPanelTargetFilters = () => {
$variation="tertiary"
/>
),
- label: t('My docs'),
+ label: t('My docs', {
+ description: 'Name of the filter listing docs owned by the user',
+ }),
targetQuery: DocDefaultFilter.MY_DOCS,
},
{
@@ -56,7 +60,9 @@ export const LeftPanelTargetFilters = () => {
$variation="tertiary"
/>
),
- label: t('Shared with me'),
+ label: t('Shared with me', {
+ description: 'Name of the filter listing docs shared with the user',
+ }),
targetQuery: DocDefaultFilter.SHARED_WITH_ME,
},
{
@@ -67,7 +73,9 @@ export const LeftPanelTargetFilters = () => {
$variation="tertiary"
/>
),
- label: t('Starred'),
+ label: t('Starred', {
+ description: "Name of the filter listing the user's favorite docs",
+ }),
targetQuery: DocDefaultFilter.STARRED,
},
{
@@ -78,7 +86,9 @@ export const LeftPanelTargetFilters = () => {
$variation="tertiary"
/>
),
- label: t('Trashbin'),
+ label: t('Trashbin', {
+ description: 'Name of the filter listing deleted docs',
+ }),
targetQuery: DocDefaultFilter.TRASHBIN,
},
];
diff --git a/src/frontend/apps/impress/src/features/left-panel/components/LeftPanel.tsx b/src/frontend/apps/impress/src/features/left-panel/components/LeftPanel.tsx
index 436b43450..6dd7602c3 100644
--- a/src/frontend/apps/impress/src/features/left-panel/components/LeftPanel.tsx
+++ b/src/frontend/apps/impress/src/features/left-panel/components/LeftPanel.tsx
@@ -35,7 +35,9 @@ export const LeftPanel = ({ isResizable }: { isResizable?: boolean }) => {
as="nav"
className="--docs--left-panel"
data-testid="left-panel"
- aria-label={t('Left panel')}
+ aria-label={t('Left panel', {
+ description: 'Accessible name of the left navigation panel',
+ })}
$width={isResizable ? '100%' : '300px'}
$height="100dvh"
$overflow="hidden"
diff --git a/src/frontend/apps/impress/src/features/left-panel/components/LeftPanelHeader.tsx b/src/frontend/apps/impress/src/features/left-panel/components/LeftPanelHeader.tsx
index 76a431b6a..bead84e5b 100644
--- a/src/frontend/apps/impress/src/features/left-panel/components/LeftPanelHeader.tsx
+++ b/src/frontend/apps/impress/src/features/left-panel/components/LeftPanelHeader.tsx
@@ -71,7 +71,10 @@ export const LeftPanelHeaderActions = () => {
string,
+ t: TFunction<'translation', undefined>,
): string => {
if (max <= min) {
- return t('Sidebar width: medium');
+ return t('Sidebar width: medium', {
+ description: 'Screen reader description of the resizable panel size',
+ });
}
const ratio = (current - min) / (max - min);
if (ratio < 1 / 3) {
- return t('Sidebar width: narrow');
+ return t('Sidebar width: narrow', {
+ description: 'Screen reader description of the resizable panel size',
+ });
}
if (ratio < 2 / 3) {
- return t('Sidebar width: medium');
+ return t('Sidebar width: medium', {
+ description: 'Screen reader description of the resizable panel size',
+ });
}
- return t('Sidebar width: wide');
+ return t('Sidebar width: wide', {
+ description: 'Screen reader description of the resizable panel size',
+ });
};
type ResizableLeftPanelProps = {
@@ -175,7 +184,10 @@ export const ResizableLeftPanel = ({
{isPanelOpen && (
{
const panelLabel =
renderedPanel === 'comments'
- ? t('Comments side panel')
+ ? t('Comments side panel', {
+ description: 'Accessible name of the right panel showing comments',
+ })
: renderedPanel === 'tableContent'
- ? t('Table of contents side panel')
- : t('Side panel');
+ ? t('Table of contents side panel', {
+ description:
+ 'Accessible name of the right panel showing the table of contents',
+ })
+ : t('Side panel', {
+ description: 'Accessible name of the right panel',
+ });
const handleClose = () => {
setIsPanelOpen(false);
diff --git a/src/frontend/apps/impress/src/hooks/useClipboard.tsx b/src/frontend/apps/impress/src/hooks/useClipboard.tsx
index a41b49bb3..ecb7682e2 100644
--- a/src/frontend/apps/impress/src/hooks/useClipboard.tsx
+++ b/src/frontend/apps/impress/src/hooks/useClipboard.tsx
@@ -13,13 +13,21 @@ export const useClipboard = () => {
navigator.clipboard
.writeText(text)
.then(() => {
- const message = successMessage ?? t('Copied to clipboard');
+ const message =
+ successMessage ??
+ t('Copied to clipboard', {
+ description: 'Toast after copying something to the clipboard',
+ });
toast(message, VariantType.SUCCESS, {
duration: 3000,
});
})
.catch(() => {
- const message = errorMessage ?? t('Failed to copy to clipboard');
+ const message =
+ errorMessage ??
+ t('Failed to copy to clipboard', {
+ description: 'Toast when copying to the clipboard failed',
+ });
toast(message, VariantType.ERROR, {
duration: 3000,
});
diff --git a/src/frontend/apps/impress/src/hooks/useDate.tsx b/src/frontend/apps/impress/src/hooks/useDate.tsx
index cc6abaa86..51a6710f3 100644
--- a/src/frontend/apps/impress/src/hooks/useDate.tsx
+++ b/src/frontend/apps/impress/src/hooks/useDate.tsx
@@ -38,7 +38,10 @@ export const useDate = () => {
return Math.abs(differenceInSeconds) >= 5
? dateToCompare.toRelative({ base: dateNow, locale: i18n.language })
- : t('just now');
+ : t('just now', {
+ description:
+ 'Relative date for something that happened a few seconds ago',
+ });
};
const calculateDaysLeft = (date: string, daysLimit: number): number =>
diff --git a/src/frontend/apps/impress/src/layouts/MainLayout.tsx b/src/frontend/apps/impress/src/layouts/MainLayout.tsx
index 835bce5aa..271079140 100644
--- a/src/frontend/apps/impress/src/layouts/MainLayout.tsx
+++ b/src/frontend/apps/impress/src/layouts/MainLayout.tsx
@@ -90,7 +90,9 @@ export const MainContent = ({
{
return (
- {t('Try it now')}
+
+ {t('Try it now', {
+ description: 'Button on the sign-in page to log in',
+ })}
+
);
@@ -24,7 +28,7 @@ const Page: NextPageWithLayout = () => {
const { t } = useTranslation();
const { authenticated } = useAuth();
const { replace } = useRouter();
- const pageTitle = `${t('401 Unauthorized')} - ${t('Docs')}`;
+ const pageTitle = `${t('401 Unauthorized', { description: 'Page title when authentication is required' })} - ${t('Docs', { description: 'Product name, shown in page titles and the logo' })}`;
useEffect(() => {
if (authenticated) {
@@ -56,7 +60,9 @@ const Page: NextPageWithLayout = () => {
$theme="neutral"
$variation="primary"
>
- {t('Please sign in')}
+ {t('Please sign in', {
+ description: 'Heading of the sign-in required page',
+ })}
{
$margin="0"
$size="xs"
>
- {t('You need to sign in before accessing the document')}
+ {t('You need to sign in before accessing the document', {
+ description: 'Message of the sign-in required page',
+ })}
diff --git a/src/frontend/apps/impress/src/pages/404.tsx b/src/frontend/apps/impress/src/pages/404.tsx
index 3ca6d3940..e1a34d8cb 100644
--- a/src/frontend/apps/impress/src/pages/404.tsx
+++ b/src/frontend/apps/impress/src/pages/404.tsx
@@ -11,7 +11,7 @@ import { NextPageWithLayout } from '@/types/next';
const Page: NextPageWithLayout = () => {
const { t } = useTranslation();
- const pageTitle = `${t('Page Not Found - Error 404')} - ${t('Docs')}`;
+ const pageTitle = `${t('Page Not Found - Error 404', { description: 'Page title of the not found error' })} - ${t('Docs', { description: 'Product name, shown in page titles and the logo' })}`;
return (
<>
@@ -36,7 +36,9 @@ const Page: NextPageWithLayout = () => {
$theme="neutral"
$variation="primary"
>
- {t('Error 404')}
+ {t('Error 404', {
+ description: 'Heading of the not found error page',
+ })}
{
>
{t(
'It seems that the page you are looking for does not exist or cannot be displayed correctly.',
+ { description: 'Message of the not found error page' },
)}
@@ -80,7 +83,9 @@ const Page: NextPageWithLayout = () => {
$variation="tertiary"
$margin="0"
>
- {t('Home')}
+ {t('Home', {
+ description: 'Button/link label to go back to the home page',
+ })}
diff --git a/src/frontend/apps/impress/src/pages/500.tsx b/src/frontend/apps/impress/src/pages/500.tsx
index f69f2ede1..c49b400e9 100644
--- a/src/frontend/apps/impress/src/pages/500.tsx
+++ b/src/frontend/apps/impress/src/pages/500.tsx
@@ -19,6 +19,7 @@ const Page: NextPageWithLayout = () => {
image={error_img}
description={t(
'An unexpected error occurred. Go grab a coffee or try to refresh the page.',
+ { description: 'Message of the server error page, informal tone' },
)}
refreshTarget={refreshTarget}
/>
diff --git a/src/frontend/apps/impress/src/pages/_app.tsx b/src/frontend/apps/impress/src/pages/_app.tsx
index a11bbc428..8cd8f54bb 100644
--- a/src/frontend/apps/impress/src/pages/_app.tsx
+++ b/src/frontend/apps/impress/src/pages/_app.tsx
@@ -24,12 +24,26 @@ export default function App({ Component, pageProps }: AppPropsWithLayout) {
return (
<>
- {t('Docs')}
-
+
+ {t('Docs', {
+ description: 'Product name, shown in page titles and the logo',
+ })}
+
+
diff --git a/src/frontend/apps/impress/src/pages/_error.tsx b/src/frontend/apps/impress/src/pages/_error.tsx
index e06bfc324..790205843 100644
--- a/src/frontend/apps/impress/src/pages/_error.tsx
+++ b/src/frontend/apps/impress/src/pages/_error.tsx
@@ -14,7 +14,9 @@ const Error = () => {
return (
);
diff --git a/src/frontend/apps/impress/src/pages/docs/[id]/index.tsx b/src/frontend/apps/impress/src/pages/docs/[id]/index.tsx
index bf763ecc4..362faaeec 100644
--- a/src/frontend/apps/impress/src/pages/docs/[id]/index.tsx
+++ b/src/frontend/apps/impress/src/pages/docs/[id]/index.tsx
@@ -207,11 +207,14 @@ const DocPage = ({ id }: DocProps) => {
<>
- {doc.title || untitledDocument} - {t('Docs')}
+ {doc.title || untitledDocument} -{' '}
+ {t('Docs', {
+ description: 'Product name, shown in page titles and the logo',
+ })}
diff --git a/src/frontend/apps/impress/src/pages/docs/index.tsx b/src/frontend/apps/impress/src/pages/docs/index.tsx
index d98cd7b89..8263f628e 100644
--- a/src/frontend/apps/impress/src/pages/docs/index.tsx
+++ b/src/frontend/apps/impress/src/pages/docs/index.tsx
@@ -24,10 +24,10 @@ const Page: NextPageWithLayout = () => {
return (
<>
- {`${pageTitle} - ${t('Docs')}`}
+ {`${pageTitle} - ${t('Docs', { description: 'Product name, shown in page titles and the logo' })}`}
diff --git a/src/frontend/apps/impress/src/pages/docs/new/index.tsx b/src/frontend/apps/impress/src/pages/docs/new/index.tsx
index 224325ac5..28286a6bf 100644
--- a/src/frontend/apps/impress/src/pages/docs/new/index.tsx
+++ b/src/frontend/apps/impress/src/pages/docs/new/index.tsx
@@ -101,10 +101,10 @@ const Page: NextPageWithLayout = () => {
return (
<>
- {`${t('New document')} - ${t('Docs')}`}
+ {`${t('New document', { description: 'Page title when creating a document' })} - ${t('Docs', { description: 'Product name, shown in page titles and the logo' })}`}
diff --git a/src/frontend/apps/impress/src/pages/home/index.tsx b/src/frontend/apps/impress/src/pages/home/index.tsx
index 742535866..416bbd2f6 100644
--- a/src/frontend/apps/impress/src/pages/home/index.tsx
+++ b/src/frontend/apps/impress/src/pages/home/index.tsx
@@ -49,10 +49,10 @@ const Page: NextPageWithLayout = () => {
return (
<>
- {`${t('Home')} - ${t('Docs')}`}
+ {`${t('Home', { description: 'Button/link label to go back to the home page' })} - ${t('Docs', { description: 'Product name, shown in page titles and the logo' })}`}
diff --git a/src/frontend/apps/impress/src/pages/offline/index.tsx b/src/frontend/apps/impress/src/pages/offline/index.tsx
index f76b6726f..05ff57261 100644
--- a/src/frontend/apps/impress/src/pages/offline/index.tsx
+++ b/src/frontend/apps/impress/src/pages/offline/index.tsx
@@ -21,10 +21,10 @@ const Page: NextPageWithLayout = () => {
return (
<>
- {`${t('Offline')} - ${t('Docs')}`}
+ {`${t('Offline', { description: 'Page title when there is no internet connection' })} - ${t('Docs', { description: 'Product name, shown in page titles and the logo' })}`}
@@ -32,17 +32,21 @@ const Page: NextPageWithLayout = () => {
- {t('Offline ?!')}
+ {t('Offline ?!', { description: 'Heading of the offline page' })}
- {t("Can't load this page, please check your internet connection.")}
+ {t("Can't load this page, please check your internet connection.", {
+ description: 'Message of the offline page',
+ })}
}>
- {t('Home')}
+ {t('Home', {
+ description: 'Button/link label to go back to the home page',
+ })}
diff --git a/src/frontend/apps/impress/src/pages/user-reconciliations/active/[id]/index.tsx b/src/frontend/apps/impress/src/pages/user-reconciliations/active/[id]/index.tsx
index b8b288299..180dd608f 100644
--- a/src/frontend/apps/impress/src/pages/user-reconciliations/active/[id]/index.tsx
+++ b/src/frontend/apps/impress/src/pages/user-reconciliations/active/[id]/index.tsx
@@ -21,10 +21,10 @@ const Page: NextPageWithLayout = () => {
<>
- {`${t('User reconciliation')} - ${t('Docs')}`}
+ {`${t('User reconciliation', { description: 'Page title of the account merge pages' })} - ${t('Docs', { description: 'Product name, shown in page titles and the logo' })}`}
diff --git a/src/frontend/apps/impress/src/pages/user-reconciliations/inactive/[id]/index.tsx b/src/frontend/apps/impress/src/pages/user-reconciliations/inactive/[id]/index.tsx
index 918450fe3..bb076164f 100644
--- a/src/frontend/apps/impress/src/pages/user-reconciliations/inactive/[id]/index.tsx
+++ b/src/frontend/apps/impress/src/pages/user-reconciliations/inactive/[id]/index.tsx
@@ -21,10 +21,10 @@ const Page: NextPageWithLayout = () => {
<>
- {`${t('User reconciliation')} - ${t('Docs')}`}
+ {`${t('User reconciliation', { description: 'Page title of the account merge pages' })} - ${t('Docs', { description: 'Product name, shown in page titles and the logo' })}`}