mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-01 08:12:43 +00:00
fix: name the message body and tie its error to it for screen readers
This commit is contained in:
@@ -325,3 +325,24 @@ describe('moving a recipient by keyboard (MO-07)', () => {
|
||||
expect(await screen.findByTestId('recipient-move-bcc')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('message body (SR-07)', () => {
|
||||
it('labels the plain-text body', () => {
|
||||
settings.plainTextMode = true;
|
||||
render(<EmailComposer initialData={DRAFT} />);
|
||||
expect(screen.getByLabelText('body_label').tagName).toBe('TEXTAREA');
|
||||
});
|
||||
|
||||
it('marks an empty body invalid and links the error text', async () => {
|
||||
render(<EmailComposer initialData={{ ...DRAFT, to: 'bob@example.com', body: '' }} />);
|
||||
const editor = screen.getByTestId('rich-text-editor');
|
||||
expect(editor).not.toHaveAttribute('data-invalid');
|
||||
|
||||
fireEvent.keyDown(screen.getByTestId('composer-subject'), { key: 'Enter', ctrlKey: true });
|
||||
await act(async () => {});
|
||||
|
||||
expect(editor).toHaveAttribute('data-invalid', 'true');
|
||||
const error = document.getElementById(editor.getAttribute('data-describedby')!);
|
||||
expect(error).toHaveTextContent('validation.body_required');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import { render, waitFor } from '@testing-library/react';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import React from 'react';
|
||||
import type { Editor } from '@tiptap/core';
|
||||
|
||||
import { RichTextEditor } from '../rich-text-editor';
|
||||
|
||||
type Props = Partial<React.ComponentProps<typeof RichTextEditor>>;
|
||||
|
||||
async function setup(props: Props = {}) {
|
||||
let editor: Editor | null = null;
|
||||
const view = render(
|
||||
<RichTextEditor
|
||||
content="<p>Hello</p>"
|
||||
onChange={() => {}}
|
||||
onEditorReady={(ed) => { editor = ed as unknown as Editor; }}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
await waitFor(() => expect(editor).not.toBeNull());
|
||||
const rerender = (next: Props) => view.rerender(
|
||||
<RichTextEditor content="<p>Hello</p>" onChange={() => {}} {...props} {...next} />
|
||||
);
|
||||
return { ...view, editor: editor as unknown as Editor, rerender };
|
||||
}
|
||||
|
||||
describe('RichTextEditor editing area (SR-07)', () => {
|
||||
it('is a named multi-line textbox', async () => {
|
||||
const { editor } = await setup({ ariaLabel: 'Message' });
|
||||
const dom = editor.view.dom;
|
||||
expect(dom).toHaveAttribute('role', 'textbox');
|
||||
expect(dom).toHaveAttribute('aria-multiline', 'true');
|
||||
expect(dom).toHaveAttribute('aria-label', 'Message');
|
||||
expect(dom).not.toHaveAttribute('aria-invalid');
|
||||
});
|
||||
|
||||
it('marks the body invalid and points at the error once validation fails', async () => {
|
||||
const { editor, rerender } = await setup({ ariaLabel: 'Message', ariaDescribedBy: 'body-error' });
|
||||
expect(editor.view.dom).not.toHaveAttribute('aria-describedby');
|
||||
|
||||
rerender({ ariaInvalid: true });
|
||||
await waitFor(() => expect(editor.view.dom).toHaveAttribute('aria-invalid', 'true'));
|
||||
expect(editor.view.dom).toHaveAttribute('aria-describedby', 'body-error');
|
||||
// Still the editor's own class and name.
|
||||
expect(editor.view.dom).toHaveClass('tiptap');
|
||||
expect(editor.view.dom).toHaveAttribute('aria-label', 'Message');
|
||||
|
||||
rerender({ ariaInvalid: false });
|
||||
await waitFor(() => expect(editor.view.dom).not.toHaveAttribute('aria-invalid'));
|
||||
});
|
||||
});
|
||||
@@ -4394,7 +4394,10 @@ export function EmailComposer({
|
||||
)}
|
||||
{/* Body */}
|
||||
{plainTextMode ? (
|
||||
<>
|
||||
<label htmlFor={`${fieldIdBase}-body`} className="sr-only">{t('body_label')}</label>
|
||||
<textarea
|
||||
id={`${fieldIdBase}-body`}
|
||||
ref={bodyRef}
|
||||
value={body}
|
||||
onChange={(e) => {
|
||||
@@ -4408,8 +4411,10 @@ export function EmailComposer({
|
||||
)}
|
||||
style={{ height: 'calc(100vh - 350px)' }}
|
||||
aria-invalid={validationErrors.body || undefined}
|
||||
aria-describedby={validationErrors.body ? `${fieldIdBase}-body-error` : undefined}
|
||||
onContextMenu={onBodyContextMenu}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<div ref={editorContainerRef} onContextMenu={onBodyContextMenu}>
|
||||
<RichTextEditor
|
||||
@@ -4421,6 +4426,9 @@ export function EmailComposer({
|
||||
onImageUpload={handleImageUpload}
|
||||
placeholder={t('body_placeholder')}
|
||||
hasError={validationErrors.body}
|
||||
ariaLabel={t('body_label')}
|
||||
ariaInvalid={validationErrors.body}
|
||||
ariaDescribedBy={`${fieldIdBase}-body-error`}
|
||||
collapseToolbarOnMobile={!showFormattingOnMobile}
|
||||
toolbarPlacement={mvWindow ? (mvShowFormatting ? 'bottom' : 'hidden') : 'top'}
|
||||
onEditorReady={handleEditorReady}
|
||||
@@ -4431,6 +4439,12 @@ export function EmailComposer({
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{validationErrors.body && (
|
||||
<p id={`${fieldIdBase}-body-error`} className="flex items-center gap-1 px-4 py-1 text-xs text-red-600 dark:text-red-400">
|
||||
<AlertCircle className="w-3.5 h-3.5 shrink-0" aria-hidden />
|
||||
{t('validation.body_required')}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{/* Hide the visual signature preview when the signature has already been
|
||||
embedded into the body (compose, above-quote replies, re-opened
|
||||
|
||||
@@ -126,6 +126,24 @@ interface RichTextEditorProps {
|
||||
* away entirely.
|
||||
*/
|
||||
toolbarPlacement?: 'top' | 'bottom' | 'hidden';
|
||||
/** The editing area's accessible name ("Message"). */
|
||||
ariaLabel?: string;
|
||||
/** The body failed validation: exposed as aria-invalid on the editing area. */
|
||||
ariaInvalid?: boolean;
|
||||
/** The id of the text that explains the error. */
|
||||
ariaDescribedBy?: string;
|
||||
}
|
||||
|
||||
/** What the editing area tells assistive technology, besides TipTap's role=textbox. */
|
||||
export function editorA11yAttributes({ ariaLabel, ariaInvalid, ariaDescribedBy }: Pick<RichTextEditorProps, 'ariaLabel' | 'ariaInvalid' | 'ariaDescribedBy'>): Record<string, string> {
|
||||
return {
|
||||
class: "tiptap min-h-[100px] px-4 py-3 text-sm text-foreground",
|
||||
role: "textbox",
|
||||
"aria-multiline": "true",
|
||||
...(ariaLabel ? { "aria-label": ariaLabel } : {}),
|
||||
...(ariaInvalid ? { "aria-invalid": "true" } : {}),
|
||||
...(ariaInvalid && ariaDescribedBy ? { "aria-describedby": ariaDescribedBy } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
function ToolbarButton({
|
||||
@@ -223,6 +241,9 @@ export function RichTextEditor({
|
||||
onMentionAdd,
|
||||
collapseToolbarOnMobile = false,
|
||||
toolbarPlacement = 'top',
|
||||
ariaLabel,
|
||||
ariaInvalid,
|
||||
ariaDescribedBy,
|
||||
}: RichTextEditorProps) {
|
||||
const rtlEditingSupport = useSettingsStore((st) => st.rtlEditingSupport);
|
||||
const tComposer = useTranslations("email_composer");
|
||||
@@ -329,9 +350,7 @@ export function RichTextEditor({
|
||||
},
|
||||
content,
|
||||
editorProps: {
|
||||
attributes: {
|
||||
class: "tiptap min-h-[100px] px-4 py-3 text-sm text-foreground",
|
||||
},
|
||||
attributes: editorA11yAttributes({ ariaLabel, ariaInvalid, ariaDescribedBy }),
|
||||
handleDrop: (view, event) => {
|
||||
if (!event.dataTransfer?.files?.length) return false;
|
||||
const upload = onImageUploadRef.current;
|
||||
@@ -397,6 +416,18 @@ export function RichTextEditor({
|
||||
}
|
||||
}, [content, editor]);
|
||||
|
||||
// The name and error state change after the editor is built (a failed
|
||||
// send marks the body invalid): hand them to the editing area.
|
||||
useEffect(() => {
|
||||
if (!editor || editor.isDestroyed) return;
|
||||
editor.setOptions({
|
||||
editorProps: {
|
||||
...editor.options.editorProps,
|
||||
attributes: editorA11yAttributes({ ariaLabel, ariaInvalid, ariaDescribedBy }),
|
||||
},
|
||||
});
|
||||
}, [editor, ariaLabel, ariaInvalid, ariaDescribedBy]);
|
||||
|
||||
// Expose the editor instance once it's ready so parents can target
|
||||
// specific nodes (e.g. swap the embedded signature on identity change).
|
||||
useEffect(() => {
|
||||
|
||||
@@ -1009,7 +1009,8 @@
|
||||
"recipient_moved": "تم نقل {name} إلى {field}",
|
||||
"recipients_added_count": "{count, plural, zero {لم تتم إضافة أي مستلم} one {تمت إضافة مستلم واحد} two {تمت إضافة مستلمَين} few {تمت إضافة # مستلمين} many {تمت إضافة # مستلمًا} other {تمت إضافة # مستلم}} إلى {field}",
|
||||
"recipients_removed_count": "{count, plural, zero {لم تتم إزالة أي مستلم} one {تمت إزالة مستلم واحد} two {تمت إزالة مستلمَين} few {تمت إزالة # مستلمين} many {تمت إزالة # مستلمًا} other {تمت إزالة # مستلم}} من {field}",
|
||||
"recipients_moved_count": "{count, plural, zero {لم يتم نقل أي مستلم} one {تم نقل مستلم واحد} two {تم نقل مستلمَين} few {تم نقل # مستلمين} many {تم نقل # مستلمًا} other {تم نقل # مستلم}} إلى {field}"
|
||||
"recipients_moved_count": "{count, plural, zero {لم يتم نقل أي مستلم} one {تم نقل مستلم واحد} two {تم نقل مستلمَين} few {تم نقل # مستلمين} many {تم نقل # مستلمًا} other {تم نقل # مستلم}} إلى {field}",
|
||||
"body_label": "الرسالة"
|
||||
},
|
||||
"confirm_dialog": {
|
||||
"confirm": "تأكيد",
|
||||
|
||||
@@ -1009,7 +1009,8 @@
|
||||
"recipient_moved": "S'ha mogut {name} a {field}",
|
||||
"recipients_added_count": "{count, plural, one {S'ha afegit # destinatari} other {S'han afegit # destinataris}} a {field}",
|
||||
"recipients_removed_count": "{count, plural, one {S'ha tret # destinatari} other {S'han tret # destinataris}} de {field}",
|
||||
"recipients_moved_count": "{count, plural, one {S'ha mogut # destinatari} other {S'han mogut # destinataris}} a {field}"
|
||||
"recipients_moved_count": "{count, plural, one {S'ha mogut # destinatari} other {S'han mogut # destinataris}} a {field}",
|
||||
"body_label": "Missatge"
|
||||
},
|
||||
"confirm_dialog": {
|
||||
"confirm": "Confirma",
|
||||
|
||||
@@ -1009,7 +1009,8 @@
|
||||
"recipient_moved": "{name} přesunut do {field}",
|
||||
"recipients_added_count": "Do {field} {count, plural, one {přidán # příjemce} few {přidáni # příjemci} many {přidáno # příjemce} other {přidáno # příjemců}}",
|
||||
"recipients_removed_count": "Z {field} {count, plural, one {odebrán # příjemce} few {odebráni # příjemci} many {odebráno # příjemce} other {odebráno # příjemců}}",
|
||||
"recipients_moved_count": "Do {field} {count, plural, one {přesunut # příjemce} few {přesunuti # příjemci} many {přesunuto # příjemce} other {přesunuto # příjemců}}"
|
||||
"recipients_moved_count": "Do {field} {count, plural, one {přesunut # příjemce} few {přesunuti # příjemci} many {přesunuto # příjemce} other {přesunuto # příjemců}}",
|
||||
"body_label": "Zpráva"
|
||||
},
|
||||
"confirm_dialog": {
|
||||
"confirm": "Potvrdit",
|
||||
|
||||
@@ -1009,7 +1009,8 @@
|
||||
"recipient_moved": "{name} flyttet til {field}",
|
||||
"recipients_added_count": "{count, plural, one {# modtager} other {# modtagere}} tilføjet til {field}",
|
||||
"recipients_removed_count": "{count, plural, one {# modtager} other {# modtagere}} fjernet fra {field}",
|
||||
"recipients_moved_count": "{count, plural, one {# modtager} other {# modtagere}} flyttet til {field}"
|
||||
"recipients_moved_count": "{count, plural, one {# modtager} other {# modtagere}} flyttet til {field}",
|
||||
"body_label": "Besked"
|
||||
},
|
||||
"confirm_dialog": {
|
||||
"confirm": "Bekræft",
|
||||
|
||||
@@ -1009,7 +1009,8 @@
|
||||
"recipient_moved": "{name} nach {field} verschoben",
|
||||
"recipients_added_count": "{count, plural, one {# Empfänger} other {# Empfänger}} zu {field} hinzugefügt",
|
||||
"recipients_removed_count": "{count, plural, one {# Empfänger} other {# Empfänger}} aus {field} entfernt",
|
||||
"recipients_moved_count": "{count, plural, one {# Empfänger} other {# Empfänger}} nach {field} verschoben"
|
||||
"recipients_moved_count": "{count, plural, one {# Empfänger} other {# Empfänger}} nach {field} verschoben",
|
||||
"body_label": "Nachricht"
|
||||
},
|
||||
"confirm_dialog": {
|
||||
"confirm": "Bestätigen",
|
||||
|
||||
@@ -1009,7 +1009,8 @@
|
||||
"recipient_moved": "Moved {name} to {field}",
|
||||
"recipients_added_count": "Added {count, plural, one {# recipient} other {# recipients}} to {field}",
|
||||
"recipients_removed_count": "Removed {count, plural, one {# recipient} other {# recipients}} from {field}",
|
||||
"recipients_moved_count": "Moved {count, plural, one {# recipient} other {# recipients}} to {field}"
|
||||
"recipients_moved_count": "Moved {count, plural, one {# recipient} other {# recipients}} to {field}",
|
||||
"body_label": "Message"
|
||||
},
|
||||
"confirm_dialog": {
|
||||
"confirm": "Confirm",
|
||||
|
||||
@@ -1009,7 +1009,8 @@
|
||||
"recipient_moved": "Se movió a {name} a {field}",
|
||||
"recipients_added_count": "{count, plural, one {Se añadió # destinatario} other {Se añadieron # destinatarios}} en {field}",
|
||||
"recipients_removed_count": "{count, plural, one {Se quitó # destinatario} other {Se quitaron # destinatarios}} de {field}",
|
||||
"recipients_moved_count": "{count, plural, one {Se movió # destinatario} other {Se movieron # destinatarios}} a {field}"
|
||||
"recipients_moved_count": "{count, plural, one {Se movió # destinatario} other {Se movieron # destinatarios}} a {field}",
|
||||
"body_label": "Mensaje"
|
||||
},
|
||||
"confirm_dialog": {
|
||||
"confirm": "Confirmar",
|
||||
|
||||
@@ -1009,7 +1009,8 @@
|
||||
"recipient_moved": "{name} teisaldati väljale {field}",
|
||||
"recipients_added_count": "Väljale {field} lisati {count, plural, one {# saaja} other {# saajat}}",
|
||||
"recipients_removed_count": "Väljalt {field} eemaldati {count, plural, one {# saaja} other {# saajat}}",
|
||||
"recipients_moved_count": "Väljale {field} teisaldati {count, plural, one {# saaja} other {# saajat}}"
|
||||
"recipients_moved_count": "Väljale {field} teisaldati {count, plural, one {# saaja} other {# saajat}}",
|
||||
"body_label": "Sõnum"
|
||||
},
|
||||
"confirm_dialog": {
|
||||
"confirm": "Kinnita",
|
||||
|
||||
@@ -1009,7 +1009,8 @@
|
||||
"recipient_moved": "{name} به {field} منتقل شد",
|
||||
"recipients_added_count": "{count, plural, one {# گیرنده} other {# گیرنده}} به {field} افزوده شد",
|
||||
"recipients_removed_count": "{count, plural, one {# گیرنده} other {# گیرنده}} از {field} حذف شد",
|
||||
"recipients_moved_count": "{count, plural, one {# گیرنده} other {# گیرنده}} به {field} منتقل شد"
|
||||
"recipients_moved_count": "{count, plural, one {# گیرنده} other {# گیرنده}} به {field} منتقل شد",
|
||||
"body_label": "پیام"
|
||||
},
|
||||
"confirm_dialog": {
|
||||
"confirm": "تأیید",
|
||||
|
||||
@@ -1009,7 +1009,8 @@
|
||||
"recipient_moved": "{name} déplacé vers {field}",
|
||||
"recipients_added_count": "{count, plural, one {# destinataire ajouté} other {# destinataires ajoutés}} à {field}",
|
||||
"recipients_removed_count": "{count, plural, one {# destinataire retiré} other {# destinataires retirés}} de {field}",
|
||||
"recipients_moved_count": "{count, plural, one {# destinataire déplacé} other {# destinataires déplacés}} vers {field}"
|
||||
"recipients_moved_count": "{count, plural, one {# destinataire déplacé} other {# destinataires déplacés}} vers {field}",
|
||||
"body_label": "Message"
|
||||
},
|
||||
"confirm_dialog": {
|
||||
"confirm": "Confirmer",
|
||||
|
||||
@@ -974,7 +974,8 @@
|
||||
"recipient_moved": "{name} הועבר אל {field}",
|
||||
"recipients_added_count": "{count, plural, one {נמען אחד נוסף} two {שני נמענים נוספו} other {# נמענים נוספו}} אל {field}",
|
||||
"recipients_removed_count": "{count, plural, one {נמען אחד הוסר} two {שני נמענים הוסרו} other {# נמענים הוסרו}} מ{field}",
|
||||
"recipients_moved_count": "{count, plural, one {נמען אחד הועבר} two {שני נמענים הועברו} other {# נמענים הועברו}} אל {field}"
|
||||
"recipients_moved_count": "{count, plural, one {נמען אחד הועבר} two {שני נמענים הועברו} other {# נמענים הועברו}} אל {field}",
|
||||
"body_label": "הודעה"
|
||||
},
|
||||
"confirm_dialog": {
|
||||
"confirm": "אשר",
|
||||
|
||||
@@ -1009,7 +1009,8 @@
|
||||
"recipient_moved": "{name} áthelyezve ide: {field}",
|
||||
"recipients_added_count": "{count, plural, one {# címzett} other {# címzett}} hozzáadva ide: {field}",
|
||||
"recipients_removed_count": "{count, plural, one {# címzett} other {# címzett}} eltávolítva innen: {field}",
|
||||
"recipients_moved_count": "{count, plural, one {# címzett} other {# címzett}} áthelyezve ide: {field}"
|
||||
"recipients_moved_count": "{count, plural, one {# címzett} other {# címzett}} áthelyezve ide: {field}",
|
||||
"body_label": "Üzenet"
|
||||
},
|
||||
"confirm_dialog": {
|
||||
"confirm": "Megerősítés",
|
||||
|
||||
@@ -1009,7 +1009,8 @@
|
||||
"recipient_moved": "{name} spostato in {field}",
|
||||
"recipients_added_count": "{count, plural, one {# destinatario aggiunto} other {# destinatari aggiunti}} a {field}",
|
||||
"recipients_removed_count": "{count, plural, one {# destinatario rimosso} other {# destinatari rimossi}} da {field}",
|
||||
"recipients_moved_count": "{count, plural, one {# destinatario spostato} other {# destinatari spostati}} in {field}"
|
||||
"recipients_moved_count": "{count, plural, one {# destinatario spostato} other {# destinatari spostati}} in {field}",
|
||||
"body_label": "Messaggio"
|
||||
},
|
||||
"confirm_dialog": {
|
||||
"confirm": "Conferma",
|
||||
|
||||
@@ -1009,7 +1009,8 @@
|
||||
"recipient_moved": "{name} を {field} に移動しました",
|
||||
"recipients_added_count": "{count, plural, other {# 件の宛先}}を {field} に追加しました",
|
||||
"recipients_removed_count": "{count, plural, other {# 件の宛先}}を {field} から削除しました",
|
||||
"recipients_moved_count": "{count, plural, other {# 件の宛先}}を {field} に移動しました"
|
||||
"recipients_moved_count": "{count, plural, other {# 件の宛先}}を {field} に移動しました",
|
||||
"body_label": "メッセージ"
|
||||
},
|
||||
"confirm_dialog": {
|
||||
"confirm": "確認",
|
||||
|
||||
@@ -1009,7 +1009,8 @@
|
||||
"recipient_moved": "{name}을(를) {field}(으)로 옮겼어요",
|
||||
"recipients_added_count": "{field}에 {count, plural, other {받는 사람 #명}}을 추가했어요",
|
||||
"recipients_removed_count": "{field}에서 {count, plural, other {받는 사람 #명}}을 제거했어요",
|
||||
"recipients_moved_count": "{field}(으)로 {count, plural, other {받는 사람 #명}}을 옮겼어요"
|
||||
"recipients_moved_count": "{field}(으)로 {count, plural, other {받는 사람 #명}}을 옮겼어요",
|
||||
"body_label": "메시지"
|
||||
},
|
||||
"confirm_dialog": {
|
||||
"confirm": "확인",
|
||||
|
||||
@@ -1009,7 +1009,8 @@
|
||||
"recipient_moved": "{name} pārvietots uz {field}",
|
||||
"recipients_added_count": "Laukam {field} {count, plural, zero {pievienoti # adresāti} one {pievienots # adresāts} other {pievienoti # adresāti}}",
|
||||
"recipients_removed_count": "No lauka {field} {count, plural, zero {noņemti # adresāti} one {noņemts # adresāts} other {noņemti # adresāti}}",
|
||||
"recipients_moved_count": "Uz {field} {count, plural, zero {pārvietoti # adresāti} one {pārvietots # adresāts} other {pārvietoti # adresāti}}"
|
||||
"recipients_moved_count": "Uz {field} {count, plural, zero {pārvietoti # adresāti} one {pārvietots # adresāts} other {pārvietoti # adresāti}}",
|
||||
"body_label": "Ziņojums"
|
||||
},
|
||||
"confirm_dialog": {
|
||||
"confirm": "Apstiprināt",
|
||||
|
||||
@@ -1009,7 +1009,8 @@
|
||||
"recipient_moved": "{name}-г {field} руу зөөлөө",
|
||||
"recipients_added_count": "{field}-д {count, plural, one {# хүлээн авагч} other {# хүлээн авагч}} нэмлээ",
|
||||
"recipients_removed_count": "{field}-ээс {count, plural, one {# хүлээн авагч} other {# хүлээн авагч}} хаслаа",
|
||||
"recipients_moved_count": "{field} руу {count, plural, one {# хүлээн авагч} other {# хүлээн авагч}} зөөлөө"
|
||||
"recipients_moved_count": "{field} руу {count, plural, one {# хүлээн авагч} other {# хүлээн авагч}} зөөлөө",
|
||||
"body_label": "Мессеж"
|
||||
},
|
||||
"confirm_dialog": {
|
||||
"confirm": "Баталгаажуулах",
|
||||
|
||||
@@ -1009,7 +1009,8 @@
|
||||
"recipient_moved": "{name} flyttet til {field}",
|
||||
"recipients_added_count": "{count, plural, one {# mottaker} other {# mottakere}} lagt til i {field}",
|
||||
"recipients_removed_count": "{count, plural, one {# mottaker} other {# mottakere}} fjernet fra {field}",
|
||||
"recipients_moved_count": "{count, plural, one {# mottaker} other {# mottakere}} flyttet til {field}"
|
||||
"recipients_moved_count": "{count, plural, one {# mottaker} other {# mottakere}} flyttet til {field}",
|
||||
"body_label": "Melding"
|
||||
},
|
||||
"confirm_dialog": {
|
||||
"confirm": "Bekreft",
|
||||
|
||||
@@ -1009,7 +1009,8 @@
|
||||
"recipient_moved": "{name} verplaatst naar {field}",
|
||||
"recipients_added_count": "{count, plural, one {# ontvanger} other {# ontvangers}} toegevoegd aan {field}",
|
||||
"recipients_removed_count": "{count, plural, one {# ontvanger} other {# ontvangers}} verwijderd uit {field}",
|
||||
"recipients_moved_count": "{count, plural, one {# ontvanger} other {# ontvangers}} verplaatst naar {field}"
|
||||
"recipients_moved_count": "{count, plural, one {# ontvanger} other {# ontvangers}} verplaatst naar {field}",
|
||||
"body_label": "Bericht"
|
||||
},
|
||||
"confirm_dialog": {
|
||||
"confirm": "Bevestigen",
|
||||
|
||||
@@ -1009,7 +1009,8 @@
|
||||
"recipient_moved": "Przeniesiono {name} do {field}",
|
||||
"recipients_added_count": "Dodano {count, plural, one {# odbiorcę} few {# odbiorców} many {# odbiorców} other {# odbiorcy}} do {field}",
|
||||
"recipients_removed_count": "Usunięto {count, plural, one {# odbiorcę} few {# odbiorców} many {# odbiorców} other {# odbiorcy}} z {field}",
|
||||
"recipients_moved_count": "Przeniesiono {count, plural, one {# odbiorcę} few {# odbiorców} many {# odbiorców} other {# odbiorcy}} do {field}"
|
||||
"recipients_moved_count": "Przeniesiono {count, plural, one {# odbiorcę} few {# odbiorców} many {# odbiorców} other {# odbiorcy}} do {field}",
|
||||
"body_label": "Wiadomość"
|
||||
},
|
||||
"confirm_dialog": {
|
||||
"confirm": "Potwierdź",
|
||||
|
||||
@@ -1009,7 +1009,8 @@
|
||||
"recipient_moved": "{name} movido para {field}",
|
||||
"recipients_added_count": "{count, plural, one {# destinatário adicionado} other {# destinatários adicionados}} a {field}",
|
||||
"recipients_removed_count": "{count, plural, one {# destinatário removido} other {# destinatários removidos}} de {field}",
|
||||
"recipients_moved_count": "{count, plural, one {# destinatário movido} other {# destinatários movidos}} para {field}"
|
||||
"recipients_moved_count": "{count, plural, one {# destinatário movido} other {# destinatários movidos}} para {field}",
|
||||
"body_label": "Mensagem"
|
||||
},
|
||||
"confirm_dialog": {
|
||||
"confirm": "Confirmar",
|
||||
|
||||
@@ -1009,7 +1009,8 @@
|
||||
"recipient_moved": "{name} a fost mutat la {field}",
|
||||
"recipients_added_count": "{count, plural, one {A fost adăugat # destinatar} few {Au fost adăugați # destinatari} other {Au fost adăugați # de destinatari}} la {field}",
|
||||
"recipients_removed_count": "{count, plural, one {A fost eliminat # destinatar} few {Au fost eliminați # destinatari} other {Au fost eliminați # de destinatari}} din {field}",
|
||||
"recipients_moved_count": "{count, plural, one {A fost mutat # destinatar} few {Au fost mutați # destinatari} other {Au fost mutați # de destinatari}} la {field}"
|
||||
"recipients_moved_count": "{count, plural, one {A fost mutat # destinatar} few {Au fost mutați # destinatari} other {Au fost mutați # de destinatari}} la {field}",
|
||||
"body_label": "Mesaj"
|
||||
},
|
||||
"confirm_dialog": {
|
||||
"confirm": "Confirmare",
|
||||
|
||||
@@ -1009,7 +1009,8 @@
|
||||
"recipient_moved": "{name}: перемещён в «{field}»",
|
||||
"recipients_added_count": "В «{field}» {count, plural, one {добавлен # получатель} few {добавлено # получателя} many {добавлено # получателей} other {добавлено # получателя}}",
|
||||
"recipients_removed_count": "Из «{field}» {count, plural, one {убран # получатель} few {убрано # получателя} many {убрано # получателей} other {убрано # получателя}}",
|
||||
"recipients_moved_count": "В «{field}» {count, plural, one {перемещён # получатель} few {перемещено # получателя} many {перемещено # получателей} other {перемещено # получателя}}"
|
||||
"recipients_moved_count": "В «{field}» {count, plural, one {перемещён # получатель} few {перемещено # получателя} many {перемещено # получателей} other {перемещено # получателя}}",
|
||||
"body_label": "Сообщение"
|
||||
},
|
||||
"confirm_dialog": {
|
||||
"confirm": "Подтвердить",
|
||||
|
||||
@@ -1009,7 +1009,8 @@
|
||||
"recipient_moved": "{name} presunutý do {field}",
|
||||
"recipients_added_count": "Do {field} {count, plural, one {pridaný # príjemca} few {pridaní # príjemcovia} many {pridaných # príjemcov} other {pridaných # príjemcov}}",
|
||||
"recipients_removed_count": "Z {field} {count, plural, one {odstránený # príjemca} few {odstránení # príjemcovia} many {odstránených # príjemcov} other {odstránených # príjemcov}}",
|
||||
"recipients_moved_count": "Do {field} {count, plural, one {presunutý # príjemca} few {presunutí # príjemcovia} many {presunutých # príjemcov} other {presunutých # príjemcov}}"
|
||||
"recipients_moved_count": "Do {field} {count, plural, one {presunutý # príjemca} few {presunutí # príjemcovia} many {presunutých # príjemcov} other {presunutých # príjemcov}}",
|
||||
"body_label": "Správa"
|
||||
},
|
||||
"confirm_dialog": {
|
||||
"confirm": "Potvrdiť",
|
||||
|
||||
@@ -1257,7 +1257,8 @@
|
||||
"recipient_moved": "ย้าย {name} ไปที่ {field} แล้ว",
|
||||
"recipients_added_count": "เพิ่มผู้รับ {count, plural, other {# คน}} ใน {field} แล้ว",
|
||||
"recipients_removed_count": "นำผู้รับ {count, plural, other {# คน}} ออกจาก {field} แล้ว",
|
||||
"recipients_moved_count": "ย้ายผู้รับ {count, plural, other {# คน}} ไปที่ {field} แล้ว"
|
||||
"recipients_moved_count": "ย้ายผู้รับ {count, plural, other {# คน}} ไปที่ {field} แล้ว",
|
||||
"body_label": "ข้อความ"
|
||||
},
|
||||
"email_viewer": {
|
||||
"read_receipt": {
|
||||
|
||||
@@ -1009,7 +1009,8 @@
|
||||
"recipient_moved": "{name}, {field} alanına taşındı",
|
||||
"recipients_added_count": "{field} alanına {count, plural, one {# alıcı} other {# alıcı}} eklendi",
|
||||
"recipients_removed_count": "{field} alanından {count, plural, one {# alıcı} other {# alıcı}} kaldırıldı",
|
||||
"recipients_moved_count": "{field} alanına {count, plural, one {# alıcı} other {# alıcı}} taşındı"
|
||||
"recipients_moved_count": "{field} alanına {count, plural, one {# alıcı} other {# alıcı}} taşındı",
|
||||
"body_label": "İleti"
|
||||
},
|
||||
"confirm_dialog": {
|
||||
"confirm": "Onayla",
|
||||
|
||||
@@ -1009,7 +1009,8 @@
|
||||
"recipient_moved": "{name}: переміщено в «{field}»",
|
||||
"recipients_added_count": "У «{field}» {count, plural, one {додано # одержувача} few {додано # одержувачі} many {додано # одержувачів} other {додано # одержувача}}",
|
||||
"recipients_removed_count": "З «{field}» {count, plural, one {прибрано # одержувача} few {прибрано # одержувачі} many {прибрано # одержувачів} other {прибрано # одержувача}}",
|
||||
"recipients_moved_count": "У «{field}» {count, plural, one {переміщено # одержувача} few {переміщено # одержувачі} many {переміщено # одержувачів} other {переміщено # одержувача}}"
|
||||
"recipients_moved_count": "У «{field}» {count, plural, one {переміщено # одержувача} few {переміщено # одержувачі} many {переміщено # одержувачів} other {переміщено # одержувача}}",
|
||||
"body_label": "Повідомлення"
|
||||
},
|
||||
"confirm_dialog": {
|
||||
"confirm": "Підтвердити",
|
||||
|
||||
@@ -1009,7 +1009,8 @@
|
||||
"recipient_moved": "已將 {name} 移到{field}",
|
||||
"recipients_added_count": "已將 {count, plural, other {# 位收件者}}新增到{field}",
|
||||
"recipients_removed_count": "已將 {count, plural, other {# 位收件者}}從{field}移除",
|
||||
"recipients_moved_count": "已將 {count, plural, other {# 位收件者}}移到{field}"
|
||||
"recipients_moved_count": "已將 {count, plural, other {# 位收件者}}移到{field}",
|
||||
"body_label": "郵件內容"
|
||||
},
|
||||
"confirm_dialog": {
|
||||
"confirm": "確認",
|
||||
|
||||
@@ -1009,7 +1009,8 @@
|
||||
"recipient_moved": "已将 {name} 移到{field}",
|
||||
"recipients_added_count": "已将 {count, plural, other {# 位收件人}}添加到{field}",
|
||||
"recipients_removed_count": "已将 {count, plural, other {# 位收件人}}从{field}移除",
|
||||
"recipients_moved_count": "已将 {count, plural, other {# 位收件人}}移到{field}"
|
||||
"recipients_moved_count": "已将 {count, plural, other {# 位收件人}}移到{field}",
|
||||
"body_label": "邮件正文"
|
||||
},
|
||||
"confirm_dialog": {
|
||||
"confirm": "确认",
|
||||
|
||||
Reference in New Issue
Block a user