feat: add missing mail action toasts and show sonner toasts again

This commit is contained in:
Linus Rath
2026-09-21 20:52:02 +02:00
parent b03a0c1dd9
commit ec67ab994b
31 changed files with 225 additions and 67 deletions
+34 -23
View File
@@ -9,7 +9,7 @@ import { Trash2, Mail, MailX, MailOpen, Loader2, SearchX, AlertTriangle, Calenda
import { useState, useEffect, useRef, useCallback, useMemo } from "react";
import { Button } from "@/components/ui/button";
import { ConfirmDialog } from "@/components/ui/confirm-dialog";
import { useEmailStore } from "@/stores/email-store";
import { useEmailStore, ArchiveMailboxNotFoundError } from "@/stores/email-store";
import { useAuthStore } from "@/stores/auth-store";
import { useSettingsStore } from "@/stores/settings-store";
import { useUIStore } from "@/stores/ui-store";
@@ -17,6 +17,8 @@ import { groupEmailsByThread, sortThreadGroups } from "@/lib/thread-utils";
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 { runBatchEmailAction } from "@/lib/email-action-toast";
import { useVirtualizer } from "@tanstack/react-virtual";
import { TagDisplayContext, useMeasuredTagDisplay } from "@/hooks/use-tag-display";
import { SearchChips } from "@/components/search/search-chips";
@@ -86,6 +88,8 @@ export function EmailList({
const t = useTranslations('email_list');
const tContextMenu = useTranslations('context_menu');
const tSpam = useTranslations('email_viewer.spam');
const tNotifications = useTranslations('notifications');
const tViewer = useTranslations('email_viewer');
const { client } = useAuthStore();
const {
selectedEmailIds,
@@ -245,10 +249,8 @@ export function EmailList({
try {
const emailIds = Array.from(selectedEmailIds);
await batchUndoSpam(client, emailIds);
const { toast } = await import('sonner');
toast.success(tSpam('toast_not_spam_batch', { count: emailIds.length }));
} catch {
const { toast } = await import('sonner');
toast.error(tSpam('error_not_spam'));
} finally {
setTimeout(() => setIsProcessing(false), 500);
@@ -276,16 +278,12 @@ export function EmailList({
if (!confirmed) return;
setIsProcessing(true);
const count = selectedEmailIds.size;
try {
await batchDelete(client, isInTrash);
const storeError = useEmailStore.getState().error;
if (storeError) {
const { toast } = await import('sonner');
toast.error(storeError);
}
} catch (err) {
const { toast } = await import('sonner');
toast.error(err instanceof Error ? err.message : 'Failed to delete emails');
await runBatchEmailAction(() => batchDelete(client, isInTrash), {
success: tNotifications('emails_deleted', { count }),
error: tNotifications('error_deleting'),
});
} finally {
setTimeout(() => setIsProcessing(false), 500);
}
@@ -648,27 +646,42 @@ export function EmailList({
onCancelScheduledForEdit={onCancelScheduledForEdit ? () => onCancelScheduledForEdit(contextMenuEmail!) : undefined}
onRescheduleScheduled={onRescheduleScheduled ? () => onRescheduleScheduled(contextMenuEmail!) : undefined}
onBatchMarkAsRead={(read) => client && batchMarkAsRead(client, read)}
onBatchDelete={() => client && batchDelete(client)}
onBatchDelete={async () => {
if (!client) return;
const count = selectedEmailIds.size;
await runBatchEmailAction(() => batchDelete(client), {
success: tNotifications('emails_deleted', { count }),
error: tNotifications('error_deleting'),
});
}}
onBatchArchive={async () => {
if (!client) return;
try {
await batchArchive(client);
} catch (error) {
console.error('Failed to batch archive:', error);
}
const count = selectedEmailIds.size;
await runBatchEmailAction(() => batchArchive(client), {
success: tNotifications('emails_archived', { count }),
error: tNotifications('error_archiving'),
describeError: (error) => error instanceof ArchiveMailboxNotFoundError
? tViewer('archive_mailbox_not_found')
: undefined,
});
}}
onBatchMoveToMailbox={async (mailboxId) => {
if (!client) return;
const count = selectedEmailIds.size;
await runBatchEmailAction(() => batchMoveToMailbox(client, mailboxId), {
success: tNotifications('emails_moved', { count }),
error: tNotifications('move_failed'),
});
}}
onBatchMoveToMailbox={(mailboxId) => client && batchMoveToMailbox(client, mailboxId)}
onBatchMarkAsSpam={async () => {
if (client) {
const emailIds = Array.from(selectedEmailIds);
try {
await batchMarkAsSpam(client, emailIds);
const { toast } = await import('sonner');
toast.success(
tSpam('toast_batch', { count: emailIds.length })
);
} catch {
const { toast } = await import('sonner');
toast.error(tSpam('error'));
}
}
@@ -678,12 +691,10 @@ export function EmailList({
const emailIds = Array.from(selectedEmailIds);
try {
await batchUndoSpam(client, emailIds);
const { toast } = await import('sonner');
toast.success(
tSpam('toast_not_spam_batch', { count: emailIds.length })
);
} catch {
const { toast } = await import('sonner');
toast.error(tSpam('error_not_spam'));
}
}
+53 -40
View File
@@ -33,6 +33,7 @@ import { connectedAccountsGrew } from "@/lib/unified-mailbox";
import { KeyboardShortcutsModal } from "@/components/keyboard-shortcuts-modal";
import { useEmailStore, buildUnifiedAccountClients, ArchiveMailboxNotFoundError, findArchiveMailbox, resolveUnstampedEmailAccountId } from "@/stores/email-store";
import { toast } from "@/stores/toast-store";
import { runBatchEmailAction } from "@/lib/email-action-toast";
import { MailboxShareDialog } from "@/components/layout/mailbox-share-dialog";
import { ShareNotificationToaster } from "@/components/layout/share-notification-toaster";
import { CalendarEventNotificationToaster } from "@/components/layout/calendar-event-notification-toaster";
@@ -51,7 +52,7 @@ import { usePromptDialog } from "@/hooks/use-prompt-dialog";
import { useBrowserNavigation, type NavSnapshot } from "@/hooks/use-browser-navigation";
import { debug } from "@/lib/debug";
import { playNotificationSound } from "@/lib/notification-sound";
import { cn } from "@/lib/utils";
import { cn, getMailboxPath } from "@/lib/utils";
import { localizeMailboxName } from "@/lib/mailbox-label";
import { KEYWORD_PREFIX, KEYWORD_PREFIX_LEGACY, groupEmailsByThread } from "@/lib/thread-utils";
import { resolveThreadRoute } from "@/lib/thread-routing";
@@ -704,15 +705,14 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
onArchive: async () => {
if (isScheduledView) return;
if (selectedEmailIds.size > 0 && client) {
try {
await batchArchive(client);
} catch (error) {
console.error("Failed to batch archive:", error);
if (error instanceof ArchiveMailboxNotFoundError) {
const { toast } = await import('sonner');
toast.error(t('email_viewer.archive_mailbox_not_found'));
}
}
const count = selectedEmailIds.size;
await runBatchEmailAction(() => batchArchive(client), {
success: t('notifications.emails_archived', { count }),
error: t('notifications.error_archiving'),
describeError: (error) => error instanceof ArchiveMailboxNotFoundError
? t('email_viewer.archive_mailbox_not_found')
: undefined,
});
} else if (selectedEmail) {
handleArchive();
}
@@ -738,11 +738,11 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
variant: "destructive",
});
if (!confirmed) return;
try {
await batchDelete(client, permanent);
} catch (error) {
console.error("Failed to batch delete:", error);
}
const count = selectedEmailIds.size;
await runBatchEmailAction(() => batchDelete(client, permanent), {
success: t('notifications.emails_deleted', { count }),
error: t('notifications.error_deleting'),
});
} else if (selectedEmail) {
handleDelete();
}
@@ -778,11 +778,14 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
try {
if (isInJunk) {
await batchUndoSpam(client, ids);
toast.success(t('email_viewer.spam.toast_not_spam_batch', { count: ids.length }));
} else {
await batchMarkAsSpam(client, ids);
toast.success(t('email_viewer.spam.toast_batch', { count: ids.length }));
}
} catch (error) {
console.error("Failed to batch toggle spam:", error);
toast.error(t(isInJunk ? 'email_viewer.spam.error_not_spam' : 'email_viewer.spam.error'));
}
} else if (selectedEmail) {
if (isInJunk) {
@@ -1713,8 +1716,7 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
// removing the old draft) was rejected - warn instead of staying
// silent, so a stale draft row is not mistaken for a failed send
// and re-sent (#592).
const toastInstance = (await import('sonner')).toast;
toastInstance.warning(t('email_composer.send_filing_warning'));
toast.warning(t('email_composer.send_filing_warning'));
}
// Mark the original email with $answered or $forwarded keyword. Route the
// write to the email's own account so the flag lands on shared/group-mailbox
@@ -2178,8 +2180,10 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
try {
await deleteEmail(client, emailToDelete.id, true);
toast.success(t('notifications.email_deleted'));
} catch (error) {
console.error("Failed to permanently delete email:", error);
toast.error(t('notifications.error_deleting'), error instanceof Error ? error.message : undefined);
}
} else {
// Not in trash: always move to trash (in the email's own account). Scope
@@ -2199,13 +2203,12 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
if (trashMailbox) {
try {
await moveToMailbox(client, emailToDelete.id, trashMailbox.id);
toast.success(t('notifications.email_deleted'));
} catch (error) {
console.error("Failed to move email to trash:", error);
const { toast } = await import('sonner');
toast.error(error instanceof Error ? error.message : 'Failed to move email to trash');
}
} else {
const { toast } = await import('sonner');
toast.error('Trash mailbox not found - cannot move email to trash');
}
}
@@ -2243,7 +2246,6 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
// the selected shared folder's owner, otherwise the user's own archive. (#889)
const archiveMailbox = findArchiveMailbox(currentMailboxes, selectedMailbox, archiveAccountId);
if (!archiveMailbox) {
const { toast } = await import('sonner');
toast.error(t('email_viewer.archive_mailbox_not_found'));
return;
}
@@ -2288,11 +2290,30 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
setConversationEmails([]);
}
toast.success(t('notifications.email_archived'));
void refreshMailboxes();
} catch (error) {
console.error("Failed to archive email:", error);
const { toast } = await import('sonner');
toast.error(error instanceof Error ? error.message : 'Failed to archive email');
toast.error(t('notifications.error_archiving'), error instanceof Error ? error.message : undefined);
}
};
const handleMoveToMailbox = async (emailId: string, mailboxId: string) => {
if (!client) return;
const destinationList = [mailboxes, ...Object.values(accountMailboxes)]
.find(list => list.some(m => m.id === mailboxId));
const destination = destinationList?.find(m => m.id === mailboxId);
try {
await moveToMailboxCrossAware(client, emailId, mailboxId);
toast.success(
t('notifications.email_moved'),
destination && destinationList
? t('notifications.moved_to_mailbox', { mailbox: getMailboxPath(destination, destinationList) })
: undefined,
);
} catch (error) {
console.error("Failed to move email:", error);
toast.error(t('notifications.move_failed'), t('notifications.move_error'));
}
};
@@ -2303,6 +2324,7 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
await toggleStar(client, selectedEmail.id);
} catch (error) {
console.error("Failed to toggle star:", error);
toast.error(t('notifications.error_updating'));
}
};
@@ -2314,17 +2336,16 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
try {
await markAsSpam(client, emailId);
const toastInstance = (await import('sonner')).toast;
toastInstance.success(t('email_viewer.spam.toast_success'), {
toast.success(t('email_viewer.spam.toast_success'), {
action: {
label: t('email_viewer.spam.toast_undo'),
onClick: async () => {
try {
await undoSpam(client, emailId);
toastInstance.success(t('notifications.email_moved'));
toast.success(t('notifications.email_moved'));
} catch (_error) {
console.error("Failed to undo spam:", _error);
toastInstance.error(t('email_viewer.spam.error'));
toast.error(t('email_viewer.spam.error'));
}
},
},
@@ -2332,8 +2353,7 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
});
} catch (_error) {
console.error("Failed to mark as spam:", _error);
const toastInstance = (await import('sonner')).toast;
toastInstance.error(t('email_viewer.spam.error'));
toast.error(t('email_viewer.spam.error'));
}
};
@@ -2343,12 +2363,10 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
try {
await undoSpam(client, emailToRestore.id);
const toastInstance = (await import('sonner')).toast;
toastInstance.success(t('email_viewer.spam.toast_not_spam_success'));
toast.success(t('email_viewer.spam.toast_not_spam_success'));
} catch (_error) {
console.error("Failed to restore email:", _error);
const toastInstance = (await import('sonner')).toast;
toastInstance.error(t('email_viewer.spam.error_not_spam'));
toast.error(t('email_viewer.spam.error_not_spam'));
}
};
@@ -2379,6 +2397,7 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
}
} catch (error) {
console.error("Failed to toggle pin:", error);
toast.error(t('notifications.error_updating'));
}
};
@@ -4066,11 +4085,7 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
onSetTag={(emailId, color) => {
handleSetTag(emailId, color);
}}
onMoveToMailbox={async (emailId, mailboxId) => {
if (client) {
await moveToMailboxCrossAware(client, emailId, mailboxId);
}
}}
onMoveToMailbox={handleMoveToMailbox}
onMarkAsSpam={async (email) => {
await handleMarkAsSpam(email);
}}
@@ -4345,9 +4360,7 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
mailboxes={mailboxes}
selectedMailbox={selectedMailbox}
onMoveToMailbox={async (mailboxId) => {
if (client && selectedEmail) {
await moveToMailboxCrossAware(client, selectedEmail.id, mailboxId);
}
if (selectedEmail) await handleMoveToMailbox(selectedEmail.id, mailboxId);
}}
className={isMobile ? "flex-1" : undefined}
/>
+3 -4
View File
@@ -2,6 +2,7 @@
import React, { useEffect, useRef, useState } from "react";
import { useTranslations } from "next-intl";
import { toast } from "@/stores/toast-store";
import {
useSettingsStore,
KEYWORD_PALETTE,
@@ -580,13 +581,11 @@ export function KeywordSettings() {
if (migration.refused > 0) {
// Some mail kept the old keyword. The definition still follows the
// messages that did move; the rest is what the warning is about.
const toastModule = await import('sonner');
toastModule.toast.warning(t("migration_error"));
toast.warning(t("migration_error"));
}
} catch (error) {
console.error("Failed to migrate keyword:", error);
const toastModule = await import('sonner');
toastModule.toast.error(t("migration_error"));
toast.error(t("migration_error"));
setIsMigrating(false);
return;
}
+27
View File
@@ -0,0 +1,27 @@
import { toast } from "@/stores/toast-store";
import { useEmailStore } from "@/stores/email-store";
/**
* Runs a bulk email-store action and toasts the outcome. Most batch actions
* report failure through `state.error` instead of throwing (batchArchive does
* both), so a resolved promise alone does not mean the mail moved.
*/
export async function runBatchEmailAction(
run: () => Promise<void>,
messages: { success: string; error: string; describeError?: (error: unknown) => string | undefined },
): Promise<void> {
try {
await run();
} catch (error) {
console.error(messages.error, error);
const detail = messages.describeError?.(error) ?? (error instanceof Error ? error.message : undefined);
toast.error(messages.error, detail);
return;
}
const storeError = useEmailStore.getState().error;
if (storeError) {
toast.error(messages.error, storeError);
} else {
toast.success(messages.success);
}
}
+4
View File
@@ -778,6 +778,10 @@
"email_sent": "تم إرسال الرسالة بنجاح",
"email_deleted": "تم حذف الرسالة",
"email_archived": "تمت أرشفة الرسالة",
"emails_deleted": "{count, plural, one {تم حذف رسالة واحدة} other {تم حذف # رسائل}}",
"emails_archived": "{count, plural, one {تمت أرشفة رسالة واحدة} other {تمت أرشفة # رسائل}}",
"error_archiving": "فشل أرشفة الرسالة",
"error_updating": "فشل تحديث الرسالة",
"email_starred": "تم تمييز الرسالة بنجمة",
"email_unstarred": "تمت إزالة النجمة عن الرسالة",
"email_marked_read": "تم تمييز الرسالة كمقروءة",
+4
View File
@@ -778,6 +778,10 @@
"email_sent": "Correu enviat correctament",
"email_deleted": "Correu suprimit",
"email_archived": "Correu arxivat",
"emails_deleted": "{count, plural, one {# correu suprimit} other {# correus suprimits}}",
"emails_archived": "{count, plural, one {# correu arxivat} other {# correus arxivats}}",
"error_archiving": "No s'ha pogut arxivar el correu",
"error_updating": "No s'ha pogut actualitzar el correu",
"email_starred": "Correu destacat",
"email_unstarred": "S'ha tret l'estrella del correu",
"email_marked_read": "Correu marcat com a llegit",
+4
View File
@@ -778,6 +778,10 @@
"email_sent": "Zpráva byla úspěšně odeslána",
"email_deleted": "Zpráva byla odstraněna",
"email_archived": "Zpráva byla archivována",
"emails_deleted": "{count, plural, one {# e-mail smazán} few {# e-maily smazány} other {# e-mailů smazáno}}",
"emails_archived": "{count, plural, one {# e-mail archivován} few {# e-maily archivovány} other {# e-mailů archivováno}}",
"error_archiving": "E-mail se nepodařilo archivovat",
"error_updating": "E-mail se nepodařilo aktualizovat",
"email_starred": "Zpráva byla označena hvězdičkou",
"email_unstarred": "Hvězdička byla odebrána",
"email_marked_read": "Zpráva byla označena jako přečtená",
+4
View File
@@ -778,6 +778,10 @@
"email_sent": "E-mail sendt succesfuldt",
"email_deleted": "E-mail slettet",
"email_archived": "E-mail arkiveret",
"emails_deleted": "{count, plural, one {# e-mail slettet} other {# e-mails slettet}}",
"emails_archived": "{count, plural, one {# e-mail arkiveret} other {# e-mails arkiveret}}",
"error_archiving": "Kunne ikke arkivere e-mail",
"error_updating": "Kunne ikke opdatere e-mail",
"email_starred": "E-mail stjernemarkeret",
"email_unstarred": "Stjernemarkering fjernet",
"email_marked_read": "E-mail markeret som læst",
+4
View File
@@ -778,6 +778,10 @@
"email_sent": "E-Mail erfolgreich gesendet",
"email_deleted": "E-Mail gelöscht",
"email_archived": "E-Mail archiviert",
"emails_deleted": "{count, plural, one {# E-Mail gelöscht} other {# E-Mails gelöscht}}",
"emails_archived": "{count, plural, one {# E-Mail archiviert} other {# E-Mails archiviert}}",
"error_archiving": "E-Mail konnte nicht archiviert werden",
"error_updating": "E-Mail konnte nicht aktualisiert werden",
"email_starred": "Stern hinzugefügt",
"email_unstarred": "Stern entfernt",
"email_marked_read": "E-Mail als gelesen markiert",
+4
View File
@@ -778,6 +778,10 @@
"email_sent": "Email sent successfully",
"email_deleted": "Email deleted",
"email_archived": "Email archived",
"emails_deleted": "{count, plural, one {# email deleted} other {# emails deleted}}",
"emails_archived": "{count, plural, one {# email archived} other {# emails archived}}",
"error_archiving": "Failed to archive email",
"error_updating": "Failed to update email",
"email_starred": "Email starred",
"email_unstarred": "Email unstarred",
"email_marked_read": "Email marked as read",
+4
View File
@@ -778,6 +778,10 @@
"email_sent": "Correo enviado exitosamente",
"email_deleted": "Correo eliminado",
"email_archived": "Correo archivado",
"emails_deleted": "{count, plural, one {# correo eliminado} other {# correos eliminados}}",
"emails_archived": "{count, plural, one {# correo archivado} other {# correos archivados}}",
"error_archiving": "No se pudo archivar el correo",
"error_updating": "No se pudo actualizar el correo",
"email_starred": "Correo destacado",
"email_unstarred": "Correo sin destacar",
"email_marked_read": "Correo marcado como leído",
+4
View File
@@ -778,6 +778,10 @@
"email_sent": "ایمیل با موفقیت ارسال شد",
"email_deleted": "ایمیل حذف شد",
"email_archived": "ایمیل بایگانی شد",
"emails_deleted": "{count, plural, other {# ایمیل حذف شد}}",
"emails_archived": "{count, plural, other {# ایمیل بایگانی شد}}",
"error_archiving": "بایگانی ایمیل ناموفق بود",
"error_updating": "به‌روزرسانی ایمیل ناموفق بود",
"email_starred": "ایمیل ستاره‌دار شد",
"email_unstarred": "ستاره ایمیل برداشته شد",
"email_marked_read": "ایمیل خوانده شده علامت‌گذاری شد",
+4
View File
@@ -778,6 +778,10 @@
"email_sent": "Email envoyé avec succès",
"email_deleted": "Email supprimé",
"email_archived": "Email archivé",
"emails_deleted": "{count, plural, one {# e-mail supprimé} other {# e-mails supprimés}}",
"emails_archived": "{count, plural, one {# e-mail archivé} other {# e-mails archivés}}",
"error_archiving": "Impossible d'archiver l'e-mail",
"error_updating": "Impossible de mettre à jour l'e-mail",
"email_starred": "Email ajouté aux favoris",
"email_unstarred": "Email retiré des favoris",
"email_marked_read": "Email marqué comme lu",
+4
View File
@@ -743,6 +743,10 @@
"email_sent": "האימייל נשלח בהצלחה",
"email_deleted": "האימייל נמחק",
"email_archived": "האימייל הועבר לארכיון",
"emails_deleted": "{count, plural, one {הודעה אחת נמחקה} other {# הודעות נמחקו}}",
"emails_archived": "{count, plural, one {הודעה אחת הועברה לארכיון} other {# הודעות הועברו לארכיון}}",
"error_archiving": "העברת ההודעה לארכיון נכשלה",
"error_updating": "עדכון ההודעה נכשל",
"email_starred": "אימייל מסומן בכוכב",
"email_unstarred": "דוא\"ל בוטל בכוכב",
"email_marked_read": "אימייל סומן כנקראה",
+4
View File
@@ -778,6 +778,10 @@
"email_sent": "E-mail sikeresen elküldve",
"email_deleted": "E-mail törölve",
"email_archived": "E-mail archiválva",
"emails_deleted": "{count, plural, other {# e-mail törölve}}",
"emails_archived": "{count, plural, other {# e-mail archiválva}}",
"error_archiving": "Nem sikerült archiválni az e-mailt",
"error_updating": "Nem sikerült frissíteni az e-mailt",
"email_starred": "E-mail csillagozva",
"email_unstarred": "E-mail csillagozás megszüntetve",
"email_marked_read": "E-mail olvasottnak jelölve",
+4
View File
@@ -778,6 +778,10 @@
"email_sent": "Messaggio inviato con successo",
"email_deleted": "Messaggio eliminato",
"email_archived": "Messaggio archiviato",
"emails_deleted": "{count, plural, one {# email eliminata} other {# email eliminate}}",
"emails_archived": "{count, plural, one {# email archiviata} other {# email archiviate}}",
"error_archiving": "Impossibile archiviare l'email",
"error_updating": "Impossibile aggiornare l'email",
"email_starred": "Stella aggiunta al messaggio",
"email_unstarred": "Stella rimossa dal messaggio",
"email_marked_read": "Messaggio segnato come letto",
+4
View File
@@ -778,6 +778,10 @@
"email_sent": "メールを送信しました",
"email_deleted": "メールを削除しました",
"email_archived": "メールをアーカイブしました",
"emails_deleted": "{count, plural, other {#件のメールを削除しました}}",
"emails_archived": "{count, plural, other {#件のメールをアーカイブしました}}",
"error_archiving": "メールをアーカイブできませんでした",
"error_updating": "メールを更新できませんでした",
"email_starred": "スターを付けました",
"email_unstarred": "スターを外しました",
"email_marked_read": "既読にしました",
+4
View File
@@ -778,6 +778,10 @@
"email_sent": "메일이 전송되었어요",
"email_deleted": "메일이 삭제되었어요",
"email_archived": "메일이 보관되었어요",
"emails_deleted": "{count, plural, other {이메일 #개 삭제됨}}",
"emails_archived": "{count, plural, other {이메일 #개 보관됨}}",
"error_archiving": "이메일을 보관하지 못했습니다",
"error_updating": "이메일을 업데이트하지 못했습니다",
"email_starred": "별표가 추가되었어요",
"email_unstarred": "별표가 해제되었어요",
"email_marked_read": "읽은 상태로 변경되었어요",
+4
View File
@@ -778,6 +778,10 @@
"email_sent": "Vēstule veiksmīgi nosūtīta",
"email_deleted": "Vēstule izdzēsta",
"email_archived": "Vēstule arhivēta",
"emails_deleted": "{count, plural, zero {# e-pasti dzēsti} one {# e-pasts dzēsts} other {# e-pasti dzēsti}}",
"emails_archived": "{count, plural, zero {# e-pasti arhivēti} one {# e-pasts arhivēts} other {# e-pasti arhivēti}}",
"error_archiving": "Neizdevās arhivēt e-pastu",
"error_updating": "Neizdevās atjaunināt e-pastu",
"email_starred": "Vēstulei pievienota zvaigznīte",
"email_unstarred": "Zvaigznīte noņemta",
"email_marked_read": "Vēstule atzīmēta kā izlasīta",
+4
View File
@@ -778,6 +778,10 @@
"email_sent": "Имэйл амжилттай илгээгдсэн",
"email_deleted": "Имэйл устгасан",
"email_archived": "Имэйл архивлагдсан",
"emails_deleted": "{count, plural, other {# имэйл устгагдлаа}}",
"emails_archived": "{count, plural, other {# имэйл архивлагдлаа}}",
"error_archiving": "Имэйлийг архивлаж чадсангүй",
"error_updating": "Имэйлийг шинэчилж чадсангүй",
"email_starred": "И-мэйл одоор тэмдэглэгдсэн",
"email_unstarred": "И-мэйл одыг арилгасан",
"email_marked_read": "И-мэйл уншсан гэж тэмдэглэсэн",
+4
View File
@@ -778,6 +778,10 @@
"email_sent": "E-posten er sendt",
"email_deleted": "E-posten er slettet",
"email_archived": "E-posten er arkivert",
"emails_deleted": "{count, plural, one {# e-post slettet} other {# e-poster slettet}}",
"emails_archived": "{count, plural, one {# e-post arkivert} other {# e-poster arkivert}}",
"error_archiving": "Kunne ikke arkivere e-posten",
"error_updating": "Kunne ikke oppdatere e-posten",
"email_starred": "E-posten er stjernemerket",
"email_unstarred": "E-posten er ikke lenger stjernemerket",
"email_marked_read": "E-posten er merket som lest",
+4
View File
@@ -778,6 +778,10 @@
"email_sent": "E-mail succesvol verzonden",
"email_deleted": "E-mail verwijderd",
"email_archived": "E-mail gearchiveerd",
"emails_deleted": "{count, plural, one {# e-mail verwijderd} other {# e-mails verwijderd}}",
"emails_archived": "{count, plural, one {# e-mail gearchiveerd} other {# e-mails gearchiveerd}}",
"error_archiving": "E-mail kon niet worden gearchiveerd",
"error_updating": "E-mail kon niet worden bijgewerkt",
"email_starred": "Ster toegevoegd aan e-mail",
"email_unstarred": "Ster verwijderd van e-mail",
"email_marked_read": "E-mail gemarkeerd als gelezen",
+4
View File
@@ -778,6 +778,10 @@
"email_sent": "Wiadomość wysłana pomyślnie",
"email_deleted": "Wiadomość usunięta",
"email_archived": "Wiadomość zarchiwizowana",
"emails_deleted": "{count, plural, one {# wiadomość usunięta} few {# wiadomości usunięte} many {# wiadomości usuniętych} other {# wiadomości usuniętych}}",
"emails_archived": "{count, plural, one {# wiadomość zarchiwizowana} few {# wiadomości zarchiwizowane} many {# wiadomości zarchiwizowanych} other {# wiadomości zarchiwizowanych}}",
"error_archiving": "Nie udało się zarchiwizować wiadomości",
"error_updating": "Nie udało się zaktualizować wiadomości",
"email_starred": "Wiadomość oznaczona gwiazdką",
"email_unstarred": "Gwiazdka usunięta",
"email_marked_read": "Wiadomość oznaczona jako przeczytana",
+4
View File
@@ -778,6 +778,10 @@
"email_sent": "E-mail enviado com sucesso",
"email_deleted": "E-mail excluído",
"email_archived": "E-mail arquivado",
"emails_deleted": "{count, plural, one {# e-mail excluído} other {# e-mails excluídos}}",
"emails_archived": "{count, plural, one {# e-mail arquivado} other {# e-mails arquivados}}",
"error_archiving": "Falha ao arquivar o e-mail",
"error_updating": "Falha ao atualizar o e-mail",
"email_starred": "E-mail marcado com estrela",
"email_unstarred": "Estrela removida do e-mail",
"email_marked_read": "E-mail marcado como lido",
+4
View File
@@ -778,6 +778,10 @@
"email_sent": "E-mailul a fost trimis cu succes",
"email_deleted": "E-mail șters",
"email_archived": "E-mail arhivat",
"emails_deleted": "{count, plural, one {# e-mail șters} few {# e-mailuri șterse} other {# de e-mailuri șterse}}",
"emails_archived": "{count, plural, one {# e-mail arhivat} few {# e-mailuri arhivate} other {# de e-mailuri arhivate}}",
"error_archiving": "Arhivarea e-mailului a eșuat",
"error_updating": "Actualizarea e-mailului a eșuat",
"email_starred": "E-mail marcat cu stea",
"email_unstarred": "E-mail fără stea",
"email_marked_read": "E-mail marcat ca citit",
+4
View File
@@ -778,6 +778,10 @@
"email_sent": "Письмо успешно отправлено",
"email_deleted": "Письмо удалено",
"email_archived": "Письмо архивировано",
"emails_deleted": "{count, plural, one {# письмо удалено} few {# письма удалены} many {# писем удалено} other {# письма удалено}}",
"emails_archived": "{count, plural, one {# письмо архивировано} few {# письма архивированы} many {# писем архивировано} other {# письма архивировано}}",
"error_archiving": "Не удалось архивировать письмо",
"error_updating": "Не удалось обновить письмо",
"email_starred": "Письмо помечено",
"email_unstarred": "Пометка снята",
"email_marked_read": "Письмо отмечено прочитанным",
+4
View File
@@ -778,6 +778,10 @@
"email_sent": "E-mail bol úspešne odoslaný",
"email_deleted": "E-mail bol odstránený",
"email_archived": "E-mail bol archivovaný",
"emails_deleted": "{count, plural, one {# e-mail odstránený} few {# e-maily odstránené} other {# e-mailov odstránených}}",
"emails_archived": "{count, plural, one {# e-mail archivovaný} few {# e-maily archivované} other {# e-mailov archivovaných}}",
"error_archiving": "E-mail sa nepodarilo archivovať",
"error_updating": "E-mail sa nepodarilo aktualizovať",
"email_starred": "E-mail bol označený hviezdičkou",
"email_unstarred": "Hviezdička bola odstránená",
"email_marked_read": "E-mail bol označený ako prečítaný",
+4
View File
@@ -778,6 +778,10 @@
"email_sent": "E-posta başarıyla gönderildi",
"email_deleted": "E-posta silindi",
"email_archived": "E-posta arşivlendi",
"emails_deleted": "{count, plural, other {# e-posta silindi}}",
"emails_archived": "{count, plural, other {# e-posta arşivlendi}}",
"error_archiving": "E-posta arşivlenemedi",
"error_updating": "E-posta güncellenemedi",
"email_starred": "E-posta yıldızlandı",
"email_unstarred": "E-postanın yıldızı kaldırıldı",
"email_marked_read": "E-posta okundu olarak işaretlendi",
+4
View File
@@ -778,6 +778,10 @@
"email_sent": "Електронна пошта успішно надіслана",
"email_deleted": "Електронну пошту видалено",
"email_archived": "Електронний лист заархівовано",
"emails_deleted": "{count, plural, one {# лист видалено} few {# листи видалено} many {# листів видалено} other {# листа видалено}}",
"emails_archived": "{count, plural, one {# лист архівовано} few {# листи архівовано} many {# листів архівовано} other {# листа архівовано}}",
"error_archiving": "Не вдалося архівувати лист",
"error_updating": "Не вдалося оновити лист",
"email_starred": "Електронна пошта позначена зірочкою",
"email_unstarred": "З електронної пошти знято зірочку",
"email_marked_read": "Електронна пошта позначена як прочитана",
+4
View File
@@ -778,6 +778,10 @@
"email_sent": "郵件傳送成功",
"email_deleted": "郵件已刪除",
"email_archived": "郵件已封存",
"emails_deleted": "{count, plural, other {已刪除 # 封郵件}}",
"emails_archived": "{count, plural, other {已封存 # 封郵件}}",
"error_archiving": "封存郵件失敗",
"error_updating": "更新郵件失敗",
"email_starred": "郵件已加上星號",
"email_unstarred": "郵件已移除星號",
"email_marked_read": "郵件已標示為已讀",
+4
View File
@@ -778,6 +778,10 @@
"email_sent": "邮件发送成功",
"email_deleted": "邮件已删除",
"email_archived": "邮件已归档",
"emails_deleted": "{count, plural, other {已删除 # 封邮件}}",
"emails_archived": "{count, plural, other {已归档 # 封邮件}}",
"error_archiving": "归档邮件失败",
"error_updating": "更新邮件失败",
"email_starred": "邮件已加星标",
"email_unstarred": "邮件已取消星标",
"email_marked_read": "邮件已标记为已读",