feat: ask before deleting or archiving when the confirm settings are on

This commit is contained in:
Linus Rath
2026-10-29 17:53:48 +01:00
parent ee17c0dbaf
commit 374ae9c8dc
35 changed files with 390 additions and 32 deletions
@@ -282,3 +282,60 @@ describe('MvListToolbar delete prompt', () => {
expect(batchDelete).not.toHaveBeenCalled();
});
});
// Settings > Accessibility: "Confirm before deleting" and "Confirm before
// archiving" ask before the selection goes, also when the bulk-delete prompt
// is off (WCAG 3.3.4).
describe('MvListToolbar removal confirmations', () => {
const batchDelete = vi.fn().mockResolvedValue(true);
const batchArchive = vi.fn().mockResolvedValue(true);
beforeEach(() => {
batchDelete.mockClear();
batchArchive.mockClear();
useAuthStore.setState({ client: {} as never } as never);
useEmailStore.setState({
emails: [message('a'), message('b')],
mailboxes,
selectedMailbox: 'inbox',
selectedEmailKeys: new Set(['a', 'b']),
isUnifiedView: false,
unifiedRole: null,
error: null,
batchDelete,
batchArchive,
} as never);
});
it('asks before deleting with "Confirm before deleting" on, and deletes after Delete', async () => {
useSettingsStore.setState({ confirmBulkDelete: false, permanentlyDeleteJunk: false, confirmDelete: true } as never);
render(<MvListToolbar loadedCount={2} onRefresh={() => {}} />);
fireEvent.click(screen.getByLabelText('delete'));
const dialog = await screen.findByRole('alertdialog');
expect(dialog).toHaveTextContent('delete_title');
expect(batchDelete).not.toHaveBeenCalled();
fireEvent.click(screen.getAllByRole('button', { name: 'delete' }).at(-1)!);
await waitFor(() => expect(batchDelete).toHaveBeenCalledTimes(1));
});
it('asks before archiving with "Confirm before archiving" on, and keeps the mail on Cancel', async () => {
useSettingsStore.setState({ confirmArchive: true } as never);
render(<MvListToolbar loadedCount={2} onRefresh={() => {}} />);
fireEvent.click(screen.getByLabelText('archive'));
const dialog = await screen.findByRole('alertdialog');
expect(dialog).toHaveTextContent('archive_title');
fireEvent.click(screen.getByRole('button', { name: 'cancel' }));
await waitFor(() => expect(screen.queryByRole('alertdialog')).toBeNull());
expect(batchArchive).not.toHaveBeenCalled();
});
it('archives straight away with the setting off', async () => {
useSettingsStore.setState({ confirmArchive: false } as never);
render(<MvListToolbar loadedCount={2} onRefresh={() => {}} />);
fireEvent.click(screen.getByLabelText('archive'));
await waitFor(() => expect(batchArchive).toHaveBeenCalledTimes(1));
expect(screen.queryByRole('alertdialog')).toBeNull();
});
});
+5
View File
@@ -34,6 +34,7 @@ import { useContextMenu } from "@/hooks/use-context-menu";
import { useConfirmDialog } from "@/hooks/use-confirm-dialog";
import { useTranslations } from "next-intl";
import { toast } from "@/stores/toast-store";
import { confirmMailRemoval } from "@/lib/mail-removal-confirm";
import { markSelectionReadWithUndo, runBatchEmailAction, setSelectionTagWithUndo, toastWithUndo, undoLabels, undoMoveAction } from "@/lib/email-action-toast";
import { useVirtualizer } from "@tanstack/react-virtual";
import { TagDisplayContext, useMeasuredTagDisplay } from "@/hooks/use-tag-display";
@@ -188,6 +189,7 @@ export function EmailList({
const tSidebar = useTranslations('sidebar');
const mountainViewList = useSettingsStore((state) => state.interfaceLayout === 'mountain-view');
const undo = undoLabels(tNotifications);
const tRemove = useTranslations('email_list.confirm_remove');
const { client } = useAuthStore(useShallow((state) => ({ client: state.client })));
const {
selectedEmailKeys,
@@ -644,6 +646,7 @@ export function EmailList({
const handleBatchArchive = async () => {
if (!client || isProcessing) return;
if (!(await confirmMailRemoval('archive', selectedEmailKeys.size, confirmDialog, tRemove))) return;
setIsProcessing(true);
const count = selectedEmailKeys.size;
const restore = restoreBatchesForSelection(client);
@@ -700,6 +703,8 @@ export function EmailList({
variant: "destructive",
});
if (!confirmed) return;
} else if (!(await confirmMailRemoval('delete', selectedEmailKeys.size, confirmDialog, tRemove))) {
return;
}
setIsProcessing(true);
+9 -1
View File
@@ -24,6 +24,7 @@ import { useAuthStore } from "@/stores/auth-store";
import { useShallow } from "zustand/react/shallow";
import { useEmailStore, ArchiveMailboxNotFoundError, resolveSelectedEmails, bulkDeleteIsPermanent, shouldConfirmBulkDelete, removalFolder, canRemoveFromFolder, restoreBatchesForSelection , type CrossAccountMoveReceipt } from "@/stores/email-store";
import { useRemoveFromFolder } from "@/hooks/use-remove-from-folder";
import { confirmMailRemoval } from "@/lib/mail-removal-confirm";
import { markSelectionReadWithUndo, runBatchEmailAction, setSelectionTagWithUndo, toastWithUndo, undoLabels, undoMoveAction } from "@/lib/email-action-toast";
import { toast } from "@/stores/toast-store";
import { useConfirmDialog } from "@/hooks/use-confirm-dialog";
@@ -119,6 +120,7 @@ export function MvListToolbar({
const tNotifications = useTranslations("notifications");
const tSpam = useTranslations("email_viewer.spam");
const undo = undoLabels(tNotifications);
const tRemove = useTranslations("email_list.confirm_remove");
const client = useAuthStore((s) => s.client);
const {
@@ -252,6 +254,8 @@ export function MvListToolbar({
variant: "destructive",
});
if (!confirmed) return;
} else if (!(await confirmMailRemoval("delete", selectionCount, confirmDialog, tRemove))) {
return;
}
const restore = permanent ? null : restoreBatchesForSelection(client);
await runBatchEmailAction(() => batchDelete(client, permanent), {
@@ -478,7 +482,11 @@ export function MvListToolbar({
<ToolbarButton
icon={Archive}
label={tMenu("archive")}
onClick={() => run(async () => (client ? batchArchive(client) : false), {
onClick={() => run(async () => (
client && await confirmMailRemoval("archive", selectionCount, confirmDialog, tRemove)
? batchArchive(client)
: false
), {
success: tNotifications("emails_archived", { count: selectionCount }),
error: tNotifications("error_archiving"),
describeError: (error) => error instanceof ArchiveMailboxNotFoundError
+9
View File
@@ -63,6 +63,7 @@ import { usePaneSize } from "@/hooks/use-pane-size";
import { usePaneId } from "@/hooks/use-pane-context";
import { useKeyboardShortcuts, type GoToMailboxTarget } from "@/hooks/use-keyboard-shortcuts";
import { useUndoShortcut } from "@/hooks/use-undo-shortcut";
import { confirmMailRemoval } from "@/lib/mail-removal-confirm";
import { findNextUnreadIndex } from "@/lib/next-unread-email";
import { useRefreshGesture } from "@/hooks/use-refresh-gesture";
import { useConfirmDialog } from "@/hooks/use-confirm-dialog";
@@ -225,6 +226,8 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
const tNotifications = useTranslations('notifications');
// The toasts' Undo after a delete to Trash, archive, move or spam report.
const undo = undoLabels(tNotifications);
// "Confirm before deleting / archiving" (lib/mail-removal-confirm).
const tRemove = useTranslations('email_list.confirm_remove');
const tCommon = useTranslations('common');
const appName = productName(useConfig().appName);
const tQuote = useTranslations('quote_header');
@@ -958,6 +961,7 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
if (isScheduledView) return;
if (shortcutsTargetSelection && client) {
const count = selectedEmailKeys.size;
if (!(await confirmMailRemoval('archive', count, confirmDialog, tRemove))) return;
const restore = restoreBatchesForSelection(client);
await runBatchEmailAction(() => batchArchive(client), {
success: t('notifications.emails_archived', { count }),
@@ -994,6 +998,8 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
variant: "destructive",
});
if (!confirmed) return;
} else if (!(await confirmMailRemoval('delete', selectedEmailKeys.size, confirmDialog, tRemove))) {
return;
}
const count = selectedEmailKeys.size;
const restore = permanent ? null : restoreBatchesForSelection(client);
@@ -2955,6 +2961,8 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
variant: "destructive",
});
if (!confirmed) return;
} else if (!(await confirmMailRemoval('delete', 1, confirmDialog, tRemove))) {
return;
}
// The store's delete reads the "Delete action" setting: move to the
@@ -2976,6 +2984,7 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
const handleArchive = async (emailToArchive: Email | null = selectedEmail) => {
if (!client || !emailToArchive || !isArchiveActionEnabled()) return;
if (!(await confirmMailRemoval('archive', 1, confirmDialog, tRemove))) return;
// In a list that mixes accounts (the unified views, a tag view) the archive
// folder (and any year/month subfolders we create) must live in the email's
+10 -2
View File
@@ -24,6 +24,9 @@ import { cn } from "@/lib/utils";
import { getDraftTextBody } from '@/lib/email-body-selection';
import { messagePriorityFromHeaders } from '@/lib/message-priority';
import { useArchiveActionEnabled } from '@/lib/archive-gate';
import { confirmMailRemoval } from '@/lib/mail-removal-confirm';
import { useConfirmDialog } from '@/hooks/use-confirm-dialog';
import { ConfirmDialog } from '@/components/ui/confirm-dialog';
import { saveQuickReplyDraft } from '@/lib/outgoing/quick-reply-envelope';
interface ProEmailTabBodyProps {
@@ -91,6 +94,8 @@ function buildReplyContext(email: Email): ProReplyContext {
export function ProEmailView({ emailId, client: clientOverride, clientAccountId, accountId, onLoaded, onClose, className }: ProEmailViewProps) {
const t = useTranslations();
const tNotifications = useTranslations('notifications');
const tRemove = useTranslations('email_list.confirm_remove');
const { dialogProps: confirmDialogProps, confirm: confirmDialog } = useConfirmDialog();
const archiveActionEnabled = useArchiveActionEnabled();
const activeClient = useAuthStore((s) => s.client);
@@ -295,6 +300,7 @@ export function ProEmailView({ emailId, client: clientOverride, clientAccountId,
const handleDelete = useCallback(async () => {
if (!client || !email) return;
if (!(await confirmMailRemoval('delete', 1, confirmDialog, tRemove))) return;
try {
// false: not deleted (a plugin stopped it), so the tab stays open.
if (await deleteEmail(client, email.id)) onClose();
@@ -302,12 +308,13 @@ export function ProEmailView({ emailId, client: clientOverride, clientAccountId,
console.error('Delete failed:', err);
toast.error(tNotifications('error_deleting'));
}
}, [client, email, deleteEmail, onClose, tNotifications]);
}, [client, email, deleteEmail, onClose, tNotifications, confirmDialog, tRemove]);
const handleArchive = useCallback(async () => {
if (!client || !email) return;
const archiveMb = mailboxes.find((m) => m.role === 'archive');
if (!archiveMb) return;
if (!(await confirmMailRemoval('archive', 1, confirmDialog, tRemove))) return;
try {
if (!(await moveToMailbox(client, email.id, archiveMb.id, emailKeyFor(email)))) return;
toast.success(tNotifications('email_archived'));
@@ -315,7 +322,7 @@ export function ProEmailView({ emailId, client: clientOverride, clientAccountId,
} catch (err) {
console.error('Archive failed:', err);
}
}, [client, email, mailboxes, moveToMailbox, onClose, tNotifications]);
}, [client, email, mailboxes, moveToMailbox, onClose, tNotifications, confirmDialog, tRemove]);
const handleToggleStar = useCallback(async () => {
if (!client || !email) return;
@@ -490,6 +497,7 @@ export function ProEmailView({ emailId, client: clientOverride, clientAccountId,
className="flex-1"
/>
</ErrorBoundary>
<ConfirmDialog {...confirmDialogProps} />
</div>
);
}
+39
View File
@@ -0,0 +1,39 @@
import { useSettingsStore } from '@/stores/settings-store';
/** What a confirm dialog hook (useConfirmDialog) takes. */
export type ConfirmFn = (options: {
title: string;
message: string;
confirmText?: string;
variant?: 'default' | 'destructive';
}) => Promise<boolean>;
/** `t` of the `email_list.confirm_remove` namespace. */
export type RemovalConfirmT = (key: string, values: { count: number }) => string;
/**
* Asks before messages are deleted or archived when the user turned on
* "Confirm before deleting" or "Confirm before archiving" (Settings >
* Accessibility; WCAG 3.3.4). Applies to single messages and keyboard
* shortcuts as well as selections. Resolves true when the action may go
* ahead: the setting is off, or the user confirmed.
*
* A permanent delete has its own, stronger question; ask this one only when
* no other confirmation is shown, so nobody is asked twice.
*/
export async function confirmMailRemoval(
kind: 'delete' | 'archive',
count: number,
confirm: ConfirmFn,
t: RemovalConfirmT,
): Promise<boolean> {
const settings = useSettingsStore.getState();
if (!(kind === 'delete' ? settings.confirmDelete : settings.confirmArchive)) return true;
if (count <= 0) return true;
return confirm({
title: t(`${kind}_title`, { count }),
message: t(`${kind}_message`, { count }),
confirmText: t(kind, { count }),
variant: kind === 'delete' ? 'destructive' : 'default',
});
}
+9 -1
View File
@@ -363,7 +363,15 @@
"cancel_scheduled_send": "إلغاء الإرسال",
"reschedule_send": "إعادة الجدولة",
"cancel_and_edit": "إلغاء والتعديل",
"cancel_and_compose_again": "إلغاء وإعادة الكتابة"
"cancel_and_compose_again": "إلغاء وإعادة الكتابة",
"confirm_remove": {
"delete_title": "{count, plural, one {حذف هذه الرسالة؟} other {حذف # رسائل؟}}",
"delete_message": "{count, plural, one {ستُنقل إلى سلة المهملات.} other {ستُنقل إلى سلة المهملات.}}",
"archive_title": "{count, plural, one {أرشفة هذه الرسالة؟} other {أرشفة # رسائل؟}}",
"archive_message": "{count, plural, one {ستُنقل إلى الأرشيف.} other {ستُنقل إلى الأرشيف.}}",
"delete": "حذف",
"archive": "أرشفة"
}
},
"shared_tags": {
"title": "الوسوم المشتركة",
+9 -1
View File
@@ -363,7 +363,15 @@
"cancel_scheduled_send": "Cancel·la l'enviament",
"reschedule_send": "Reprograma",
"cancel_and_edit": "Cancel·la i edita",
"cancel_and_compose_again": "Cancel·la i torna a redactar"
"cancel_and_compose_again": "Cancel·la i torna a redactar",
"confirm_remove": {
"delete_title": "{count, plural, one {Vols suprimir aquest correu?} other {Vols suprimir # correus?}}",
"delete_message": "{count, plural, one {Es mourà a la paperera.} other {Es mouran a la paperera.}}",
"archive_title": "{count, plural, one {Vols arxivar aquest correu?} other {Vols arxivar # correus?}}",
"archive_message": "{count, plural, one {Es mourà al teu arxiu.} other {Es mouran al teu arxiu.}}",
"delete": "Suprimeix",
"archive": "Arxiva"
}
},
"shared_tags": {
"title": "Etiquetes compartides",
+9 -1
View File
@@ -363,7 +363,15 @@
"cancel_scheduled_send": "Zrušit odeslání",
"reschedule_send": "Přeplánovat",
"cancel_and_edit": "Zrušit a upravit",
"cancel_and_compose_again": "Zrušit a napsat znovu"
"cancel_and_compose_again": "Zrušit a napsat znovu",
"confirm_remove": {
"delete_title": "{count, plural, one {Odstranit tuto zprávu?} few {Odstranit # zprávy?} other {Odstranit # zpráv?}}",
"delete_message": "{count, plural, one {Přesune se do koše.} other {Přesunou se do koše.}}",
"archive_title": "{count, plural, one {Archivovat tuto zprávu?} few {Archivovat # zprávy?} other {Archivovat # zpráv?}}",
"archive_message": "{count, plural, one {Přesune se do archivu.} other {Přesunou se do archivu.}}",
"delete": "Odstranit",
"archive": "Archivovat"
}
},
"shared_tags": {
"title": "Sdílené štítky",
+9 -1
View File
@@ -363,7 +363,15 @@
"cancel_scheduled_send": "Annuller afsendelse",
"reschedule_send": "Planlæg om",
"cancel_and_edit": "Annuller og redigér",
"cancel_and_compose_again": "Annuller og skriv igen"
"cancel_and_compose_again": "Annuller og skriv igen",
"confirm_remove": {
"delete_title": "{count, plural, one {Slet denne e-mail?} other {Slet # e-mails?}}",
"delete_message": "{count, plural, one {Den flyttes til papirkurven.} other {De flyttes til papirkurven.}}",
"archive_title": "{count, plural, one {Arkivér denne e-mail?} other {Arkivér # e-mails?}}",
"archive_message": "{count, plural, one {Den flyttes til dit arkiv.} other {De flyttes til dit arkiv.}}",
"delete": "Slet",
"archive": "Arkivér"
}
},
"shared_tags": {
"title": "Delte tags",
+9 -1
View File
@@ -363,7 +363,15 @@
"cancel_scheduled_send": "Senden abbrechen",
"reschedule_send": "Neu planen",
"cancel_and_edit": "Abbrechen und bearbeiten",
"cancel_and_compose_again": "Abbrechen und neu verfassen"
"cancel_and_compose_again": "Abbrechen und neu verfassen",
"confirm_remove": {
"delete_title": "{count, plural, one {Diese E-Mail löschen?} other {# E-Mails löschen?}}",
"delete_message": "{count, plural, one {Sie wird in den Papierkorb verschoben.} other {Sie werden in den Papierkorb verschoben.}}",
"archive_title": "{count, plural, one {Diese E-Mail archivieren?} other {# E-Mails archivieren?}}",
"archive_message": "{count, plural, one {Sie wird in Ihr Archiv verschoben.} other {Sie werden in Ihr Archiv verschoben.}}",
"delete": "Löschen",
"archive": "Archivieren"
}
},
"shared_tags": {
"title": "Geteilte Labels",
+9 -1
View File
@@ -363,7 +363,15 @@
"cancel_scheduled_send": "Cancel send",
"reschedule_send": "Reschedule",
"cancel_and_edit": "Cancel and edit",
"cancel_and_compose_again": "Cancel and compose again"
"cancel_and_compose_again": "Cancel and compose again",
"confirm_remove": {
"delete_title": "{count, plural, one {Delete this email?} other {Delete # emails?}}",
"delete_message": "{count, plural, one {It moves to the trash.} other {They move to the trash.}}",
"archive_title": "{count, plural, one {Archive this email?} other {Archive # emails?}}",
"archive_message": "{count, plural, one {It moves to your archive.} other {They move to your archive.}}",
"delete": "Delete",
"archive": "Archive"
}
},
"shared_tags": {
"title": "Shared tags",
+9 -1
View File
@@ -363,7 +363,15 @@
"cancel_scheduled_send": "Cancelar envío",
"reschedule_send": "Reprogramar",
"cancel_and_edit": "Cancelar y editar",
"cancel_and_compose_again": "Cancelar y redactar de nuevo"
"cancel_and_compose_again": "Cancelar y redactar de nuevo",
"confirm_remove": {
"delete_title": "{count, plural, one {¿Eliminar este correo?} other {¿Eliminar # correos?}}",
"delete_message": "{count, plural, one {Se moverá a la papelera.} other {Se moverán a la papelera.}}",
"archive_title": "{count, plural, one {¿Archivar este correo?} other {¿Archivar # correos?}}",
"archive_message": "{count, plural, one {Se moverá a tu archivo.} other {Se moverán a tu archivo.}}",
"delete": "Eliminar",
"archive": "Archivar"
}
},
"shared_tags": {
"title": "Etiquetas compartidas",
+9 -1
View File
@@ -363,7 +363,15 @@
"cancel_scheduled_send": "Tühista saatmine",
"reschedule_send": "Ajasta uuesti",
"cancel_and_edit": "Tühista ja muuda",
"cancel_and_compose_again": "Tühista ja koosta uuesti"
"cancel_and_compose_again": "Tühista ja koosta uuesti",
"confirm_remove": {
"delete_title": "{count, plural, one {Kas kustutada see kiri?} other {Kas kustutada # kirja?}}",
"delete_message": "{count, plural, one {See viiakse prügikasti.} other {Need viiakse prügikasti.}}",
"archive_title": "{count, plural, one {Kas arhiveerida see kiri?} other {Kas arhiveerida # kirja?}}",
"archive_message": "{count, plural, one {See viiakse sinu arhiivi.} other {Need viiakse sinu arhiivi.}}",
"delete": "Kustuta",
"archive": "Arhiveeri"
}
},
"shared_tags": {
"title": "Jagatud sildid",
+9 -1
View File
@@ -363,7 +363,15 @@
"cancel_scheduled_send": "لغو ارسال",
"reschedule_send": "باززمان‌بندی",
"cancel_and_edit": "لغو و ویرایش",
"cancel_and_compose_again": "لغو و نوشتن مجدد"
"cancel_and_compose_again": "لغو و نوشتن مجدد",
"confirm_remove": {
"delete_title": "{count, plural, one {این ایمیل حذف شود؟} other {# ایمیل حذف شوند؟}}",
"delete_message": "{count, plural, one {به سطل زباله منتقل می‌شود.} other {به سطل زباله منتقل می‌شوند.}}",
"archive_title": "{count, plural, one {این ایمیل بایگانی شود؟} other {# ایمیل بایگانی شوند؟}}",
"archive_message": "{count, plural, one {به بایگانی شما منتقل می‌شود.} other {به بایگانی شما منتقل می‌شوند.}}",
"delete": "حذف",
"archive": "بایگانی"
}
},
"shared_tags": {
"title": "برچسب‌های مشترک",
+9 -1
View File
@@ -363,7 +363,15 @@
"cancel_scheduled_send": "Annuler l’envoi",
"reschedule_send": "Replanifier",
"cancel_and_edit": "Annuler et modifier",
"cancel_and_compose_again": "Annuler et rédiger à nouveau"
"cancel_and_compose_again": "Annuler et rédiger à nouveau",
"confirm_remove": {
"delete_title": "{count, plural, one {Supprimer cet e-mail ?} other {Supprimer # e-mails ?}}",
"delete_message": "{count, plural, one {Il sera déplacé dans la corbeille.} other {Ils seront déplacés dans la corbeille.}}",
"archive_title": "{count, plural, one {Archiver cet e-mail ?} other {Archiver # e-mails ?}}",
"archive_message": "{count, plural, one {Il sera déplacé dans vos archives.} other {Ils seront déplacés dans vos archives.}}",
"delete": "Supprimer",
"archive": "Archiver"
}
},
"shared_tags": {
"title": "Étiquettes partagées",
+9 -1
View File
@@ -328,7 +328,15 @@
"cancel_scheduled_send": "ביטול שליחה",
"reschedule_send": "תזמן מחדש",
"cancel_and_edit": "בטל וערוך",
"cancel_and_compose_again": "בטל והרכיב שוב"
"cancel_and_compose_again": "בטל והרכיב שוב",
"confirm_remove": {
"delete_title": "{count, plural, one {למחוק את ההודעה הזו?} other {למחוק # הודעות?}}",
"delete_message": "{count, plural, one {היא תועבר לאשפה.} other {הן יועברו לאשפה.}}",
"archive_title": "{count, plural, one {להעביר את ההודעה הזו לארכיון?} other {להעביר # הודעות לארכיון?}}",
"archive_message": "{count, plural, one {היא תועבר לארכיון שלך.} other {הן יועברו לארכיון שלך.}}",
"delete": "מחק",
"archive": "העבר לארכיון"
}
},
"shared_tags": {
"title": "תגים משותפים",
+9 -1
View File
@@ -363,7 +363,15 @@
"cancel_scheduled_send": "Küldés megszakítása",
"reschedule_send": "Átütemezés",
"cancel_and_edit": "Mégse és szerkesztés",
"cancel_and_compose_again": "Mégse és újraírás"
"cancel_and_compose_again": "Mégse és újraírás",
"confirm_remove": {
"delete_title": "{count, plural, one {Törlöd ezt az e-mailt?} other {Törölsz # e-mailt?}}",
"delete_message": "{count, plural, one {A kukába kerül.} other {A kukába kerülnek.}}",
"archive_title": "{count, plural, one {Archiválod ezt az e-mailt?} other {Archiválsz # e-mailt?}}",
"archive_message": "{count, plural, one {Az archívumodba kerül.} other {Az archívumodba kerülnek.}}",
"delete": "Törlés",
"archive": "Archiválás"
}
},
"shared_tags": {
"title": "Megosztott címkék",
+9 -1
View File
@@ -363,7 +363,15 @@
"cancel_scheduled_send": "Annulla invio",
"reschedule_send": "Riprogramma",
"cancel_and_edit": "Annulla e modifica",
"cancel_and_compose_again": "Annulla e componi di nuovo"
"cancel_and_compose_again": "Annulla e componi di nuovo",
"confirm_remove": {
"delete_title": "{count, plural, one {Eliminare questa email?} other {Eliminare # email?}}",
"delete_message": "{count, plural, one {Verrà spostata nel cestino.} other {Verranno spostate nel cestino.}}",
"archive_title": "{count, plural, one {Archiviare questa email?} other {Archiviare # email?}}",
"archive_message": "{count, plural, one {Verrà spostata nel tuo archivio.} other {Verranno spostate nel tuo archivio.}}",
"delete": "Elimina",
"archive": "Archivia"
}
},
"shared_tags": {
"title": "Etichette condivise",
+9 -1
View File
@@ -363,7 +363,15 @@
"cancel_scheduled_send": "送信をキャンセル",
"reschedule_send": "再予約",
"cancel_and_edit": "キャンセルして編集",
"cancel_and_compose_again": "キャンセルして新規作成"
"cancel_and_compose_again": "キャンセルして新規作成",
"confirm_remove": {
"delete_title": "{count, plural, other {#件のメールを削除しますか?}}",
"delete_message": "{count, plural, other {ゴミ箱に移動します。}}",
"archive_title": "{count, plural, other {#件のメールをアーカイブしますか?}}",
"archive_message": "{count, plural, other {アーカイブに移動します。}}",
"delete": "削除",
"archive": "アーカイブ"
}
},
"shared_tags": {
"title": "共有タグ",
+9 -1
View File
@@ -363,7 +363,15 @@
"cancel_scheduled_send": "보내기 취소",
"reschedule_send": "다시 예약",
"cancel_and_edit": "취소하고 편집",
"cancel_and_compose_again": "취소하고 다시 작성"
"cancel_and_compose_again": "취소하고 다시 작성",
"confirm_remove": {
"delete_title": "{count, plural, other {메일 #개를 삭제할까요?}}",
"delete_message": "{count, plural, other {휴지통으로 이동해요.}}",
"archive_title": "{count, plural, other {메일 #개를 보관할까요?}}",
"archive_message": "{count, plural, other {보관함으로 이동해요.}}",
"delete": "삭제",
"archive": "보관"
}
},
"shared_tags": {
"title": "공유 태그",
+9 -1
View File
@@ -363,7 +363,15 @@
"cancel_scheduled_send": "Atcelt sūtīšanu",
"reschedule_send": "Pārplānot",
"cancel_and_edit": "Atcelt un rediģēt",
"cancel_and_compose_again": "Atcelt un rakstīt no jauna"
"cancel_and_compose_again": "Atcelt un rakstīt no jauna",
"confirm_remove": {
"delete_title": "{count, plural, zero {Dzēst # vēstules?} one {Dzēst šo vēstuli?} other {Dzēst # vēstules?}}",
"delete_message": "{count, plural, zero {Tās tiks pārvietotas uz atkritni.} one {Tā tiks pārvietota uz atkritni.} other {Tās tiks pārvietotas uz atkritni.}}",
"archive_title": "{count, plural, zero {Arhivēt # vēstules?} one {Arhivēt šo vēstuli?} other {Arhivēt # vēstules?}}",
"archive_message": "{count, plural, zero {Tās tiks pārvietotas uz jūsu arhīvu.} one {Tā tiks pārvietota uz jūsu arhīvu.} other {Tās tiks pārvietotas uz jūsu arhīvu.}}",
"delete": "Dzēst",
"archive": "Arhivēt"
}
},
"shared_tags": {
"title": "Kopīgotie tagi",
+9 -1
View File
@@ -363,7 +363,15 @@
"cancel_scheduled_send": "Илгээхийг цуцлах",
"reschedule_send": "Хуваарь солих",
"cancel_and_edit": "Цуцлах, засах",
"cancel_and_compose_again": "Цуцлаад дахин бичих"
"cancel_and_compose_again": "Цуцлаад дахин бичих",
"confirm_remove": {
"delete_title": "{count, plural, one {Энэ имэйлийг устгах уу?} other {# имэйлийг устгах уу?}}",
"delete_message": "{count, plural, other {Хогийн сав руу шилжинэ.}}",
"archive_title": "{count, plural, one {Энэ имэйлийг архивлах уу?} other {# имэйлийг архивлах уу?}}",
"archive_message": "{count, plural, other {Таны архив руу шилжинэ.}}",
"delete": "Устгах",
"archive": "Архивлах"
}
},
"shared_tags": {
"title": "Хуваалцсан тагууд",
+9 -1
View File
@@ -363,7 +363,15 @@
"cancel_scheduled_send": "Avbryt sending",
"reschedule_send": "Planlegg på nytt",
"cancel_and_edit": "Avbryt og rediger",
"cancel_and_compose_again": "Avbryt og skriv meldingen på nytt"
"cancel_and_compose_again": "Avbryt og skriv meldingen på nytt",
"confirm_remove": {
"delete_title": "{count, plural, one {Slette denne e-posten?} other {Slette # e-poster?}}",
"delete_message": "{count, plural, one {Den flyttes til papirkurven.} other {De flyttes til papirkurven.}}",
"archive_title": "{count, plural, one {Arkivere denne e-posten?} other {Arkivere # e-poster?}}",
"archive_message": "{count, plural, one {Den flyttes til arkivet ditt.} other {De flyttes til arkivet ditt.}}",
"delete": "Slett",
"archive": "Arkiver"
}
},
"shared_tags": {
"title": "Delte etiketter",
+9 -1
View File
@@ -363,7 +363,15 @@
"cancel_scheduled_send": "Verzenden annuleren",
"reschedule_send": "Opnieuw plannen",
"cancel_and_edit": "Annuleren en bewerken",
"cancel_and_compose_again": "Annuleren en opnieuw opstellen"
"cancel_and_compose_again": "Annuleren en opnieuw opstellen",
"confirm_remove": {
"delete_title": "{count, plural, one {Deze e-mail verwijderen?} other {# e-mails verwijderen?}}",
"delete_message": "{count, plural, one {Hij gaat naar de prullenbak.} other {Ze gaan naar de prullenbak.}}",
"archive_title": "{count, plural, one {Deze e-mail archiveren?} other {# e-mails archiveren?}}",
"archive_message": "{count, plural, one {Hij gaat naar uw archief.} other {Ze gaan naar uw archief.}}",
"delete": "Verwijderen",
"archive": "Archiveren"
}
},
"shared_tags": {
"title": "Gedeelde labels",
+9 -1
View File
@@ -363,7 +363,15 @@
"cancel_scheduled_send": "Anuluj wysyłkę",
"reschedule_send": "Przełóż",
"cancel_and_edit": "Anuluj i edytuj",
"cancel_and_compose_again": "Anuluj i napisz ponownie"
"cancel_and_compose_again": "Anuluj i napisz ponownie",
"confirm_remove": {
"delete_title": "{count, plural, one {Usunąć tę wiadomość?} few {Usunąć # wiadomości?} many {Usunąć # wiadomości?} other {Usunąć # wiadomości?}}",
"delete_message": "{count, plural, one {Zostanie przeniesiona do kosza.} other {Zostaną przeniesione do kosza.}}",
"archive_title": "{count, plural, one {Zarchiwizować tę wiadomość?} few {Zarchiwizować # wiadomości?} many {Zarchiwizować # wiadomości?} other {Zarchiwizować # wiadomości?}}",
"archive_message": "{count, plural, one {Zostanie przeniesiona do archiwum.} other {Zostaną przeniesione do archiwum.}}",
"delete": "Usuń",
"archive": "Archiwizuj"
}
},
"shared_tags": {
"title": "Wspólne etykiety",
+9 -1
View File
@@ -363,7 +363,15 @@
"cancel_scheduled_send": "Cancelar envio",
"reschedule_send": "Reagendar",
"cancel_and_edit": "Cancelar e editar",
"cancel_and_compose_again": "Cancelar e escrever novamente"
"cancel_and_compose_again": "Cancelar e escrever novamente",
"confirm_remove": {
"delete_title": "{count, plural, one {Excluir este e-mail?} other {Excluir # e-mails?}}",
"delete_message": "{count, plural, one {Ele vai para a lixeira.} other {Eles vão para a lixeira.}}",
"archive_title": "{count, plural, one {Arquivar este e-mail?} other {Arquivar # e-mails?}}",
"archive_message": "{count, plural, one {Ele vai para o seu arquivo.} other {Eles vão para o seu arquivo.}}",
"delete": "Excluir",
"archive": "Arquivar"
}
},
"shared_tags": {
"title": "Etiquetas compartilhadas",
+9 -1
View File
@@ -363,7 +363,15 @@
"cancel_scheduled_send": "Anulează trimiterea",
"reschedule_send": "Reprogramare",
"cancel_and_edit": "Anulează și editează",
"cancel_and_compose_again": "Anulează și redactează din nou"
"cancel_and_compose_again": "Anulează și redactează din nou",
"confirm_remove": {
"delete_title": "{count, plural, one {Ștergi acest e-mail?} few {Ștergi # e-mailuri?} other {Ștergi # de e-mailuri?}}",
"delete_message": "{count, plural, one {Va fi mutat în coșul de gunoi.} other {Vor fi mutate în coșul de gunoi.}}",
"archive_title": "{count, plural, one {Arhivezi acest e-mail?} few {Arhivezi # e-mailuri?} other {Arhivezi # de e-mailuri?}}",
"archive_message": "{count, plural, one {Va fi mutat în arhiva ta.} other {Vor fi mutate în arhiva ta.}}",
"delete": "Șterge",
"archive": "Arhivează"
}
},
"shared_tags": {
"title": "Etichete partajate",
+9 -1
View File
@@ -363,7 +363,15 @@
"cancel_scheduled_send": "Отменить отправку",
"reschedule_send": "Перенести",
"cancel_and_edit": "Отменить и изменить",
"cancel_and_compose_again": "Отменить и написать заново"
"cancel_and_compose_again": "Отменить и написать заново",
"confirm_remove": {
"delete_title": "{count, plural, one {Удалить это письмо?} few {Удалить # письма?} many {Удалить # писем?} other {Удалить # письма?}}",
"delete_message": "{count, plural, one {Оно будет перемещено в корзину.} other {Они будут перемещены в корзину.}}",
"archive_title": "{count, plural, one {Архивировать это письмо?} few {Архивировать # письма?} many {Архивировать # писем?} other {Архивировать # письма?}}",
"archive_message": "{count, plural, one {Оно будет перемещено в архив.} other {Они будут перемещены в архив.}}",
"delete": "Удалить",
"archive": "Архивировать"
}
},
"shared_tags": {
"title": "Общие теги",
+9 -1
View File
@@ -363,7 +363,15 @@
"cancel_scheduled_send": "Zrušiť odoslanie",
"reschedule_send": "Prenaplánovať",
"cancel_and_edit": "Zrušiť a upraviť",
"cancel_and_compose_again": "Zrušiť a napísať znova"
"cancel_and_compose_again": "Zrušiť a napísať znova",
"confirm_remove": {
"delete_title": "{count, plural, one {Odstrániť tento e-mail?} few {Odstrániť # e-maily?} other {Odstrániť # e-mailov?}}",
"delete_message": "{count, plural, one {Presunie sa do koša.} other {Presunú sa do koša.}}",
"archive_title": "{count, plural, one {Archivovať tento e-mail?} few {Archivovať # e-maily?} other {Archivovať # e-mailov?}}",
"archive_message": "{count, plural, one {Presunie sa do archívu.} other {Presunú sa do archívu.}}",
"delete": "Odstrániť",
"archive": "Archivovať"
}
},
"shared_tags": {
"title": "Zdieľané štítky",
+9 -1
View File
@@ -617,7 +617,15 @@
"cancel_scheduled_send": "ยกเลิกการส่ง",
"reschedule_send": "เปลี่ยนเวลาส่งใหม่",
"cancel_and_edit": "ยกเลิกและแก้ไข",
"cancel_and_compose_again": "ยกเลิกและเขียนใหม่"
"cancel_and_compose_again": "ยกเลิกและเขียนใหม่",
"confirm_remove": {
"delete_title": "{count, plural, other {ลบอีเมล # ฉบับหรือไม่}}",
"delete_message": "{count, plural, other {อีเมลจะถูกย้ายไปที่ถังขยะ}}",
"archive_title": "{count, plural, other {เก็บอีเมล # ฉบับถาวรหรือไม่}}",
"archive_message": "{count, plural, other {อีเมลจะถูกย้ายไปที่ที่เก็บถาวร}}",
"delete": "ลบ",
"archive": "เก็บถาวร"
}
},
"shared_tags": {
"title": "ป้ายกำกับที่แชร์",
+9 -1
View File
@@ -363,7 +363,15 @@
"cancel_scheduled_send": "Göndermeyi iptal et",
"reschedule_send": "Yeniden zamanla",
"cancel_and_edit": "İptal et ve düzenle",
"cancel_and_compose_again": "İptal et ve yeniden yaz"
"cancel_and_compose_again": "İptal et ve yeniden yaz",
"confirm_remove": {
"delete_title": "{count, plural, one {Bu e-posta silinsin mi?} other {# e-posta silinsin mi?}}",
"delete_message": "{count, plural, other {Çöp kutusuna taşınır.}}",
"archive_title": "{count, plural, one {Bu e-posta arşivlensin mi?} other {# e-posta arşivlensin mi?}}",
"archive_message": "{count, plural, other {Arşivinize taşınır.}}",
"delete": "Sil",
"archive": "Arşivle"
}
},
"shared_tags": {
"title": "Paylaşılan etiketler",
+9 -1
View File
@@ -363,7 +363,15 @@
"cancel_scheduled_send": "Скасувати надсилання",
"reschedule_send": "Перенести",
"cancel_and_edit": "Скасувати й редагувати",
"cancel_and_compose_again": "Скасувати й написати знову"
"cancel_and_compose_again": "Скасувати й написати знову",
"confirm_remove": {
"delete_title": "{count, plural, one {Видалити цей лист?} few {Видалити # листи?} many {Видалити # листів?} other {Видалити # листа?}}",
"delete_message": "{count, plural, one {Його буде переміщено в кошик.} other {Їх буде переміщено в кошик.}}",
"archive_title": "{count, plural, one {Архівувати цей лист?} few {Архівувати # листи?} many {Архівувати # листів?} other {Архівувати # листа?}}",
"archive_message": "{count, plural, one {Його буде переміщено в архів.} other {Їх буде переміщено в архів.}}",
"delete": "Видалити",
"archive": "Архівувати"
}
},
"shared_tags": {
"title": "Спільні теги",
+9 -1
View File
@@ -363,7 +363,15 @@
"cancel_scheduled_send": "取消傳送",
"reschedule_send": "重新排程",
"cancel_and_edit": "取消並編輯",
"cancel_and_compose_again": "取消並重新撰寫"
"cancel_and_compose_again": "取消並重新撰寫",
"confirm_remove": {
"delete_title": "{count, plural, other {要刪除 # 封郵件嗎?}}",
"delete_message": "{count, plural, other {郵件將移到垃圾桶。}}",
"archive_title": "{count, plural, other {要封存 # 封郵件嗎?}}",
"archive_message": "{count, plural, other {郵件將移到封存。}}",
"delete": "刪除",
"archive": "封存"
}
},
"shared_tags": {
"title": "共用標籤",
+9 -1
View File
@@ -363,7 +363,15 @@
"cancel_scheduled_send": "取消发送",
"reschedule_send": "重新计划",
"cancel_and_edit": "取消并编辑",
"cancel_and_compose_again": "取消并重新撰写"
"cancel_and_compose_again": "取消并重新撰写",
"confirm_remove": {
"delete_title": "{count, plural, other {要删除 # 封邮件吗?}}",
"delete_message": "{count, plural, other {邮件将移到废纸篓。}}",
"archive_title": "{count, plural, other {要归档 # 封邮件吗?}}",
"archive_message": "{count, plural, other {邮件将移到归档。}}",
"delete": "删除",
"archive": "归档"
}
},
"shared_tags": {
"title": "共享标签",