chore: support bulk restore emails

This commit is contained in:
rustmailer
2026-01-21 01:09:38 +08:00
parent fcd19b1c9f
commit e1f471b8f4
23 changed files with 342 additions and 137 deletions
+27 -2
View File
@@ -19,7 +19,7 @@
import { useRef } from 'react' import { useRef } from 'react'
import { X, Trash2 } from 'lucide-react' import { X, Trash2, Upload } from 'lucide-react'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
import { Badge } from '@/components/ui/badge' import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button' import { Button } from '@/components/ui/button'
@@ -37,7 +37,7 @@ type MailBulkActionsProps = {
} }
export function MailBulkActions({ children }: MailBulkActionsProps) { export function MailBulkActions({ children }: MailBulkActionsProps) {
const { selected, setSelected, setOpen, setToDelete } = useSearchContext() const { selected, setSelected, setOpen, setToDelete, setCurrentEnvelope } = useSearchContext()
const toolbarRef = useRef<HTMLDivElement>(null) const toolbarRef = useRef<HTMLDivElement>(null)
const { t } = useTranslation() const { t } = useTranslation()
@@ -60,6 +60,12 @@ export function MailBulkActions({ children }: MailBulkActionsProps) {
setOpen('delete') setOpen('delete')
} }
const handleRestore = () => {
setCurrentEnvelope(undefined);
setOpen('restore')
}
const handleKeyDown = (e: React.KeyboardEvent) => { const handleKeyDown = (e: React.KeyboardEvent) => {
const buttons = toolbarRef.current?.querySelectorAll('button') const buttons = toolbarRef.current?.querySelectorAll('button')
if (!buttons || buttons.length === 0) return if (!buttons || buttons.length === 0) return
@@ -161,7 +167,26 @@ export function MailBulkActions({ children }: MailBulkActionsProps) {
</div> </div>
<Separator orientation="vertical" className="h-5" /> <Separator orientation="vertical" className="h-5" />
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="secondary"
size="sm"
onClick={handleRestore}
className="gap-1"
>
<Upload className="h-3.5 w-3.5" />
<span className="hidden sm:inline">
{t('restore_message.restore_to_imap', 'Restore Mail')}
</span>
</Button>
</TooltipTrigger>
<TooltipContent>
{t('search.bulkActions.restoreDesc')}
</TooltipContent>
</Tooltip>
<Separator orientation="vertical" className="h-5" />
{/* Delete */} {/* Delete */}
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
-8
View File
@@ -95,14 +95,6 @@ export default function Search() {
> >
<div className="mx-auto w-full px-4"> <div className="mx-auto w-full px-4">
<div className="flex gap-6"> <div className="flex gap-6">
{/* <aside className="hidden lg:block w-64 flex-shrink-0">
<div className="rounded-lg border bg-card p-4">
<EnvelopeTags
selectedTags={selectedTags}
onTagToggle={handleTagToggle}
/>
</div>
</aside> */}
<div className="flex-1 min-w-0 space-y-4"> <div className="flex-1 min-w-0 space-y-4">
{isLoading && ( {isLoading && (
<Card> <Card>
+18 -2
View File
@@ -19,7 +19,7 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { useMutation } from '@tanstack/react-query'; import { useMutation } from '@tanstack/react-query';
import { Loader, Download, Trash2, MessageSquareMore, FileText, FileImage, FileVideo, FileArchive, FileSpreadsheet, FileCode, FileIcon, FileAudio } from 'lucide-react'; import { Loader, Download, Trash2, MessageSquareMore, FileText, FileImage, FileVideo, FileArchive, FileSpreadsheet, FileCode, FileIcon, FileAudio, Upload } from 'lucide-react';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Separator } from '@/components/ui/separator'; import { Separator } from '@/components/ui/separator';
@@ -119,7 +119,7 @@ export function MailMessageView({
showHeader = true showHeader = true
}: MailMessageViewProps) { }: MailMessageViewProps) {
const { t } = useTranslation() const { t } = useTranslation()
const { setToDelete, setOpen } = useSearchContext(); const { setToDelete, setOpen, setSelected } = useSearchContext();
const [content, setContent] = useState<string | null>(null); const [content, setContent] = useState<string | null>(null);
const [contentType, setContentType] = useState<'Plain' | 'Html' | null>(null); const [contentType, setContentType] = useState<'Plain' | 'Html' | null>(null);
@@ -280,6 +280,22 @@ export function MailMessageView({
</TooltipTrigger> </TooltipTrigger>
<TooltipContent>{t('mail.viewThread')}</TooltipContent> <TooltipContent>{t('mail.viewThread')}</TooltipContent>
</Tooltip> </Tooltip>
<Separator orientation="vertical" className="h-5" />
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
onClick={() => {
setSelected(new Map())
setOpen('restore')
}}
>
<Upload className="h-4 w-4" />
</Button>
</TooltipTrigger>
<TooltipContent>{t('restore_message.restore_to_imap', 'Restore Mail')}</TooltipContent>
</Tooltip>
</div> </div>
</> </>
)} )}
@@ -25,6 +25,52 @@ import { AxiosError } from 'axios'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { ToastAction } from '@/components/ui/toast' import { ToastAction } from '@/components/ui/toast'
import { useSearchContext } from './context' import { useSearchContext } from './context'
import { EmailEnvelope } from '@/api'
function MessageSummary({ envelope, t }: { envelope: EmailEnvelope, t: (key: string) => string }) {
return (
<div className="mt-3 rounded-md border bg-muted/20 p-3 text-sm overflow-hidden">
<div className="grid grid-cols-[auto_1fr] gap-x-2 gap-y-1.5">
<span className="font-medium text-muted-foreground">{t("mail.subject")}:</span>
<div className="break-words font-medium">
{envelope.subject || <em className="italic opacity-70">(No subject)</em>}
</div>
<span className="font-medium text-muted-foreground">{t("mail.from")}:</span>
<div className="break-all text-foreground/90">
{envelope.from}
</div>
{envelope.to?.length > 0 && (
<>
<span className="font-medium text-muted-foreground">{t("mail.to")}:</span>
<div className="break-all text-foreground/90">
{envelope.to.slice(0, 2).join(", ")}
{envelope.to.length > 2 && " …"}
</div>
</>
)}
<span className="font-medium text-muted-foreground">{t("mail.date")}:</span>
<div className="text-foreground/90">
{new Date(envelope.date).toLocaleString()}
</div>
{envelope.mailbox_name && (
<>
<span className="font-medium text-muted-foreground">{t("search.mailbox")}:</span>
<div className="truncate text-foreground/90" title={envelope.mailbox_name}>
{envelope.mailbox_name}
</div>
</>
)}
</div>
</div>
);
}
interface RestoreMessageDialogProps { interface RestoreMessageDialogProps {
open: boolean open: boolean
@@ -36,12 +82,26 @@ export function RestoreMessageDialog({
onOpenChange onOpenChange
}: RestoreMessageDialogProps) { }: RestoreMessageDialogProps) {
const { t } = useTranslation() const { t } = useTranslation()
const { currentEnvelope } = useSearchContext() const { currentEnvelope, selected } = useSearchContext()
const accountsWithSelection = Array.from(selected.entries()).filter(([_, ids]) => ids.size > 0);
const selectedCount = accountsWithSelection.reduce((sum, [_, set]) => sum + set.size, 0);
const accountCount = accountsWithSelection.length;
const isBulk = selectedCount > 0;
const restoreMutation = useMutation({ const restoreMutation = useMutation({
mutationFn: () => mutationFn: async () => {
restore_message(currentEnvelope!.account_id, [currentEnvelope!.id]), if (isBulk) {
const promises = accountsWithSelection.map(([accountId, ids]) =>
restore_message(accountId, Array.from(ids))
);
return Promise.all(promises);
} else if (currentEnvelope) {
return restore_message(currentEnvelope.account_id, [currentEnvelope.id]);
}
},
onSuccess: handleRestoreSuccess, onSuccess: handleRestoreSuccess,
onError: handleRestoreError, onError: handleRestoreError,
}); });
@@ -90,16 +150,37 @@ export function RestoreMessageDialog({
<ConfirmDialog <ConfirmDialog
open={open} open={open}
onOpenChange={onOpenChange} onOpenChange={onOpenChange}
title={t('restore_message.title', 'Restore messages')} title={isBulk ? t('restore_message.bulkTitle', 'Restore multiple messages') : t('restore_message.title', 'Restore message')}
desc={t( desc={<div className="space-y-3">
'restore_message.desc', <p className="text-sm text-muted-foreground">
'This action will append the selected messages from Bichon to their corresponding mailboxes on the IMAP server.' {t(
)} 'restore_message.desc',
'This action will append the selected messages to their corresponding mailboxes on the IMAP server.'
)}
</p>
{isBulk ? (
<div className="rounded-md bg-primary/5 border border-primary/20 p-3 text-sm">
<div className="flex justify-between items-center text-primary font-medium">
<span>{t('restore_message.summary', 'Summary')}</span>
<span className="bg-primary/10 px-2 py-0.5 rounded text-xs">
{selectedCount} {t('restore_message.messages', 'messages')}
</span>
</div>
<div className="mt-2 text-xs space-y-1 text-muted-foreground">
<p> {t('restore_message.accountsInvolved', 'Accounts involved')}: {accountCount}</p>
<p> {t('restore_message.bulkWarning', 'Messages will be restored to their original folders.')}</p>
</div>
</div>
) : (
currentEnvelope && <MessageSummary envelope={currentEnvelope} t={t} />
)}
</div>}
confirmText={t('restore_message.confirm', 'Restore')} confirmText={t('restore_message.confirm', 'Restore')}
handleConfirm={() => restoreMutation.mutate()} handleConfirm={() => restoreMutation.mutate()}
className="sm:max-w-sm" className="sm:max-w-sm"
isLoading={restoreMutation.isPending} isLoading={restoreMutation.isPending}
disabled={restoreMutation.isPending} disabled={restoreMutation.isPending || (!isBulk && !currentEnvelope)}
/> />
) )
} }
@@ -28,7 +28,7 @@ import {
DropdownMenuTrigger, DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu' } from '@/components/ui/dropdown-menu'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { MoreVertical, TagIcon, Trash2 } from 'lucide-react' import { MoreVertical, TagIcon, Trash2, Upload } from 'lucide-react'
import { EmailEnvelope } from '@/api' import { EmailEnvelope } from '@/api'
import { useSearchContext } from '../context' import { useSearchContext } from '../context'
@@ -37,7 +37,7 @@ interface DataTableRowActionsProps {
} }
export function DataTableRowActions({ row }: DataTableRowActionsProps) { export function DataTableRowActions({ row }: DataTableRowActionsProps) {
const { setOpen, setCurrentEnvelope, setToDelete } = useSearchContext() const { setOpen, setCurrentEnvelope, setSelected, setToDelete } = useSearchContext()
const { t } = useTranslation() const { t } = useTranslation()
const toggleToDelete = (accountId: number, mailId: number) => { const toggleToDelete = (accountId: number, mailId: number) => {
@@ -93,12 +93,13 @@ export function DataTableRowActions({ row }: DataTableRowActionsProps) {
onClick={(e) => { onClick={(e) => {
e.stopPropagation() e.stopPropagation()
setCurrentEnvelope(row.original) setCurrentEnvelope(row.original)
setSelected(new Map())
setOpen("restore") setOpen("restore")
}} }}
> >
{t('restore_message.restore_to_imap', 'Restore Mail')} {t('restore_message.restore_to_imap', 'Restore Mail')}
<DropdownMenuShortcut> <DropdownMenuShortcut>
<TagIcon size={16} /> <Upload size={16} />
</DropdownMenuShortcut> </DropdownMenuShortcut>
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuSeparator /> <DropdownMenuSeparator />
+12 -7
View File
@@ -1453,14 +1453,19 @@
"confirm": "تسجيل الخروج" "confirm": "تسجيل الخروج"
}, },
"restore_message": { "restore_message": {
"title": "استعادة الرسائل", "title": "استعادة رسالة واحدة",
"desc": "سيقوم هذا الإجراء بإعادة رفع الرسائل المختارة من Bichon واستعادتها إلى صناديق البريد المقابلة لها على خادم IMAP.", "bulkTitle": "استعادة رسائل متعددة",
"bulkWarning": "ملاحظة: سيتم استعادة الرسائل إلى مجلداتها الأصلية.",
"accountsInvolved": "الحسابات المعنية",
"summary": "تفاصيل الاستعادة",
"messages": "رسائل",
"desc": "سيقوم هذا الإجراء بإعادة رفع الرسائل المحددة من Bichon واستعادتها إلى صناديق البريد المقابلة لها على خادم IMAP.",
"confirm": "تنفيذ الاستعادة", "confirm": "تنفيذ الاستعادة",
"restore_to_imap": "استعادة البريد", "restore_to_imap": "استعادة الرسالة",
"success": "تمت استعادة الرسائل بنجاح", "success": "تمت الاستعادة بنجاح",
"successDesc": "تمت استعادة الرسائل المختارة إلى خادم IMAP بنجاح.", "successDesc": "تمت استعادة الرسائل المحددة بنجاح إلى خادم IMAP.",
"failed": "فشلت استعادة الرسائل", "failed": "فشلت الاستعادة",
"failedTitle": "فشل الاستعادة" "failedTitle": "فشل في الاستعادة"
}, },
"time": { "time": {
"label": "الوقت", "label": "الوقت",
+13 -8
View File
@@ -1453,14 +1453,19 @@
"confirm": "Log ud" "confirm": "Log ud"
}, },
"restore_message": { "restore_message": {
"title": "Gendan meddelelser", "title": "Gendan enkelt besked",
"desc": "Denne handling vil uploade de valgte meddelelser fra Bichon og gendanne dem til deres tilsvarende postkasser på IMAP-serveren.", "bulkTitle": "Gendan flere beskeder",
"confirm": "Gendan", "bulkWarning": "Bemærk: Beskeder vil blive gendannet til deres oprindelige mapper.",
"restore_to_imap": "Gendan e-mail", "accountsInvolved": "Involverede konti",
"success": "Meddelelser gendannet", "summary": "Gendannelsesdetaljer",
"successDesc": "De valgte meddelelser er blevet gendannet til IMAP-serveren.", "messages": "beskeder",
"failed": "Kunne ikke gendanne meddelelser", "desc": "Denne handling vil gen-uploade valgte beskeder fra Bichon og gendanne dem til deres tilsvarende postkasser på IMAP-serveren.",
"failedTitle": "Gendannelse mislykkedes" "confirm": "Udfør gendannelse",
"restore_to_imap": "Gendan besked",
"success": "Gendannelse gennemført",
"successDesc": "De valgte beskeder er blevet gendannet til IMAP-serveren.",
"failed": "Gendannelse mislykkedes",
"failedTitle": "Fejl ved gendannelse"
}, },
"time": { "time": {
"label": "Tid", "label": "Tid",
+10 -5
View File
@@ -1453,11 +1453,16 @@
"confirm": "Abmelden" "confirm": "Abmelden"
}, },
"restore_message": { "restore_message": {
"title": "Nachrichten wiederherstellen", "title": "Einzelne Nachricht wiederherstellen",
"desc": "Diese Aktion lädt die ausgewählten Nachrichten von Bichon hoch und stellt sie in den entsprechenden Postfächern auf dem IMAP-Server wieder her.", "bulkTitle": "Mehrere Nachrichten wiederherstellen",
"confirm": "Wiederherstellen", "bulkWarning": "Hinweis: Nachrichten werden in ihren ursprünglichen Ordnern wiederhergestellt.",
"restore_to_imap": "E-Mail wiederherstellen", "accountsInvolved": "Beteiligte Konten",
"success": "Nachrichten wiederhergestellt", "summary": "Details zur Wiederherstellung",
"messages": "Nachrichten",
"desc": "Diese Aktion lädt die ausgewählten Nachrichten von Bichon erneut hoch und stellt sie in den entsprechenden Postfächern auf dem IMAP-Server wieder her.",
"confirm": "Wiederherstellung ausführen",
"restore_to_imap": "Nachricht wiederherstellen",
"success": "Wiederherstellung erfolgreich",
"successDesc": "Die ausgewählten Nachrichten wurden erfolgreich auf dem IMAP-Server wiederhergestellt.", "successDesc": "Die ausgewählten Nachrichten wurden erfolgreich auf dem IMAP-Server wiederhergestellt.",
"failed": "Wiederherstellung fehlgeschlagen", "failed": "Wiederherstellung fehlgeschlagen",
"failedTitle": "Fehler bei der Wiederherstellung" "failedTitle": "Fehler bei der Wiederherstellung"
+11 -6
View File
@@ -1453,14 +1453,19 @@
"confirm": "Sign out" "confirm": "Sign out"
}, },
"restore_message": { "restore_message": {
"title": "Restore Messages", "title": "Restore Single Message",
"desc": "This action will append the selected messages from Bichon back to their corresponding mailboxes on the IMAP server.", "bulkTitle": "Bulk Restore Messages",
"confirm": "Restore", "bulkWarning": "Note: Messages will be restored to their original storage folders.",
"restore_to_imap": "Restore Mail", "accountsInvolved": "Accounts involved",
"summary": "Restoration Details",
"messages": "messages",
"desc": "This action will re-upload selected messages from Bichon and restore them to their corresponding mailboxes on the IMAP server.",
"confirm": "Restore Now",
"restore_to_imap": "Restore Message",
"success": "Messages Restored", "success": "Messages Restored",
"successDesc": "The selected messages have been successfully restored to the IMAP server.", "successDesc": "The selected messages have been successfully restored to the IMAP server.",
"failed": "Failed to restore messages", "failed": "Restore Failed",
"failedTitle": "Restore Failed" "failedTitle": "Failed to Restore"
}, },
"time": { "time": {
"label": "Time", "label": "Time",
+12 -7
View File
@@ -1453,14 +1453,19 @@
"confirm": "Cerrar sesión" "confirm": "Cerrar sesión"
}, },
"restore_message": { "restore_message": {
"title": "Restaurar mensajes", "title": "Restaurar mensaje individual",
"desc": "Esta acción volverá a cargar los mensajes seleccionados de Bichon y los restaurará en sus carpetas correspondientes en el servidor IMAP.", "bulkTitle": "Restaurar múltiples mensajes",
"confirm": "Restaurar", "bulkWarning": "Nota: Los mensajes se restaurarán en sus carpetas originales.",
"restore_to_imap": "Restaurar correo", "accountsInvolved": "Cuentas involucradas",
"success": "Mensajes restaurados", "summary": "Detalles de restauración",
"messages": "mensajes",
"desc": "Esta acción volverá a cargar los mensajes seleccionados desde Bichon y los restaurará en sus buzones correspondientes en el servidor IMAP.",
"confirm": "Ejecutar restauración",
"restore_to_imap": "Restaurar mensaje",
"success": "Restauración exitosa",
"successDesc": "Los mensajes seleccionados se han restaurado correctamente en el servidor IMAP.", "successDesc": "Los mensajes seleccionados se han restaurado correctamente en el servidor IMAP.",
"failed": "Error al restaurar los mensajes", "failed": "Error al restaurar",
"failedTitle": "Error de restauración" "failedTitle": "Fallo en la restauración"
}, },
"time": { "time": {
"label": "Tiempo", "label": "Tiempo",
+12 -7
View File
@@ -1453,14 +1453,19 @@
"confirm": "Kirjaudu ulos" "confirm": "Kirjaudu ulos"
}, },
"restore_message": { "restore_message": {
"title": "Palauta viestit", "title": "Palauta yksittäinen viesti",
"desc": "Tämä toiminto lataa valitut viestit Bichonista ja palauttaa ne vastaaviin postilaatikoihin IMAP-palvelimella.", "bulkTitle": "Palauta useita viestejä",
"confirm": "Palauta", "bulkWarning": "Huomio: Viestit palautetaan niiden alkuperäisiin kansioihin.",
"restore_to_imap": "Palauta sähköposti", "accountsInvolved": "Osalliset tilit",
"success": "Viestit palautettu", "summary": "Palautuksen tiedot",
"messages": "viestiä",
"desc": "Tämä toiminto lataa valitut viestit uudelleen Bichonista ja palauttaa ne vastaaviin postilaatikoihin IMAP-palvelimella.",
"confirm": "Suorita palautus",
"restore_to_imap": "Palauta viesti",
"success": "Palautus onnistui",
"successDesc": "Valitut viestit on palautettu onnistuneesti IMAP-palvelimelle.", "successDesc": "Valitut viestit on palautettu onnistuneesti IMAP-palvelimelle.",
"failed": "Viestien palautus epäonnistui", "failed": "Palautus epäonnistui",
"failedTitle": "Palautus epäonnistui" "failedTitle": "Palautusvirhe"
}, },
"time": { "time": {
"label": "Aika", "label": "Aika",
+12 -7
View File
@@ -1453,14 +1453,19 @@
"confirm": "Déconnexion" "confirm": "Déconnexion"
}, },
"restore_message": { "restore_message": {
"title": "Restaurer les messages", "title": "Restaurer un message",
"desc": "Cette action téléchargera les messages sélectionnés depuis Bichon et les restaurera dans leurs boîtes aux lettres correspondantes sur le serveur IMAP.", "bulkTitle": "Restaurer plusieurs messages",
"confirm": "Restaurer", "bulkWarning": "Remarque : les messages seront restaurés dans leurs dossiers d'origine.",
"restore_to_imap": "Restaurer le courrier", "accountsInvolved": "Comptes concernés",
"success": "Messages restaurés", "summary": "Détails de la restauration",
"messages": "messages",
"desc": "Cette action va re-télécharger les messages sélectionnés depuis Bichon et les restaurer dans leurs boîtes aux lettres correspondantes sur le serveur IMAP.",
"confirm": "Exécuter la restauration",
"restore_to_imap": "Restaurer le message",
"success": "Restauration réussie",
"successDesc": "Les messages sélectionnés ont été restaurés avec succès sur le serveur IMAP.", "successDesc": "Les messages sélectionnés ont été restaurés avec succès sur le serveur IMAP.",
"failed": "Échec de la restauration des messages", "failed": "Échec de la restauration",
"failedTitle": "Échec de la restauration" "failedTitle": "Erreur de restauration"
}, },
"time": { "time": {
"label": "Temps", "label": "Temps",
+12 -7
View File
@@ -1453,14 +1453,19 @@
"confirm": "Disconnetti" "confirm": "Disconnetti"
}, },
"restore_message": { "restore_message": {
"title": "Ripristina messaggi", "title": "Ripristina singolo messaggio",
"desc": "Questa azione caricherà i messaggi selezionati da Bichon e li ripristinerà nelle rispettive caselle di posta sul server IMAP.", "bulkTitle": "Ripristino multiplo messaggi",
"confirm": "Ripristina", "bulkWarning": "Nota: i messaggi verranno ripristinati nelle loro cartelle originali.",
"restore_to_imap": "Ripristina posta", "accountsInvolved": "Account coinvolti",
"success": "Messaggi ripristinati", "summary": "Dettagli ripristino",
"messages": "messaggi",
"desc": "Questa azione caricherà nuovamente i messaggi selezionati da Bichon e li ripristinerà nelle rispettive caselle di posta sul server IMAP.",
"confirm": "Esegui ripristino",
"restore_to_imap": "Ripristina messaggio",
"success": "Ripristino completato",
"successDesc": "I messaggi selezionati sono stati ripristinati con successo sul server IMAP.", "successDesc": "I messaggi selezionati sono stati ripristinati con successo sul server IMAP.",
"failed": "Impossibile ripristinare i messaggi", "failed": "Ripristino fallito",
"failedTitle": "Ripristino fallito" "failedTitle": "Errore di ripristino"
}, },
"time": { "time": {
"label": "Tempo", "label": "Tempo",
+11 -6
View File
@@ -1453,14 +1453,19 @@
"confirm": "サインアウト" "confirm": "サインアウト"
}, },
"restore_message": { "restore_message": {
"title": "メッセージを復元", "title": "単一メールの復元",
"desc": "この操作により、選択したメッセージをBichonから再アップロードし、IMAPサーバー上の対応するメールボックスに復元します。", "bulkTitle": "複数メールの一括復元",
"bulkWarning": "注意:メールは元の保存先フォルダーに復元されます。",
"accountsInvolved": "対象アカウント",
"summary": "復元の詳細",
"messages": "通のメール",
"desc": "この操作により、選択されたメールがBichonシステムから再アップロードされ、IMAPサーバー上の対応するメールボックスに復元されます。",
"confirm": "復元を実行", "confirm": "復元を実行",
"restore_to_imap": "メールを復元", "restore_to_imap": "メールを復元",
"success": "メッセージを復元しました", "success": "復元成功",
"successDesc": "選択したメッセージがIMAPサーバーに正常に復元されました。", "successDesc": "選択されたメールはIMAPサーバーに正常に復元されました。",
"failed": "メッセージの復元失敗しました", "failed": "復元失敗",
"failedTitle": "復元失敗" "failedTitle": "復元失敗しました"
}, },
"time": { "time": {
"label": "時間", "label": "時間",
+10 -5
View File
@@ -1453,13 +1453,18 @@
"confirm": "로그아웃" "confirm": "로그아웃"
}, },
"restore_message": { "restore_message": {
"title": "메시지 복원", "title": "개별 메시지 복원",
"desc": "이 작업은 선택한 메시지를 Bichon에서 다시 업로드하여 IMAP 서버의 해당 사서함으로 복원합니다.", "bulkTitle": "여러 메시지 일괄 복원",
"bulkWarning": "참고: 메시지는 원래 저장된 폴더로 복원됩니다.",
"accountsInvolved": "관련 계정",
"summary": "복원 세부 정보",
"messages": "개의 메시지",
"desc": "이 작업은 선택한 메시지를 Bichon 시스템에서 다시 업로드하여 IMAP 서버의 해당 사서함으로 복원합니다.",
"confirm": "복원 실행", "confirm": "복원 실행",
"restore_to_imap": "메 복원", "restore_to_imap": "메시지 복원",
"success": "메시지 복원 완료", "success": "복원 성공",
"successDesc": "선택한 메시지가 IMAP 서버로 성공적으로 복원되었습니다.", "successDesc": "선택한 메시지가 IMAP 서버로 성공적으로 복원되었습니다.",
"failed": "메시지 복원 실패", "failed": "복원 실패",
"failedTitle": "복원 실패" "failedTitle": "복원 실패"
}, },
"time": { "time": {
+12 -7
View File
@@ -1453,14 +1453,19 @@
"confirm": "Uitloggen" "confirm": "Uitloggen"
}, },
"restore_message": { "restore_message": {
"title": "Berichten herstellen", "title": "Enkel bericht herstellen",
"desc": "Deze actie uploadt de geselecteerde berichten van Bichon en herstelt ze in de bijbehorende mailboxen op de IMAP-server.", "bulkTitle": "Meerdere berichten herstellen",
"confirm": "Herstellen", "bulkWarning": "Let op: berichten worden hersteld in hun oorspronkelijke mappen.",
"restore_to_imap": "E-mail herstellen", "accountsInvolved": "Betrokken accounts",
"success": "Berichten hersteld", "summary": "Hersteldetails",
"messages": "berichten",
"desc": "Deze actie zal de geselecteerde berichten opnieuw uploaden vanuit Bichon en ze herstellen in de bijbehorende mailboxen op de IMAP-server.",
"confirm": "Herstel uitvoeren",
"restore_to_imap": "Bericht herstellen",
"success": "Herstel geslaagd",
"successDesc": "De geselecteerde berichten zijn succesvol hersteld op de IMAP-server.", "successDesc": "De geselecteerde berichten zijn succesvol hersteld op de IMAP-server.",
"failed": "Herstellen van berichten mislukt", "failed": "Herstel mislukt",
"failedTitle": "Herstel mislukt" "failedTitle": "Herstelfout"
}, },
"time": { "time": {
"label": "Tijd", "label": "Tijd",
+13 -8
View File
@@ -1453,14 +1453,19 @@
"confirm": "Logg ut" "confirm": "Logg ut"
}, },
"restore_message": { "restore_message": {
"title": "Gjenopprett meldinger", "title": "Gjenopprett enkeltmelding",
"desc": "Denne handlingen vil laste opp de valgte meldingene fra Bichon og gjenopprette dem til deres tilsvarende postbokser på IMAP-serveren.", "bulkTitle": "Gjenopprett flere meldinger",
"confirm": "Gjenopprett", "bulkWarning": "Merk: Meldinger vil bli gjenopprettet til sine opprinnelige mapper.",
"restore_to_imap": "Gjenopprett e-post", "accountsInvolved": "Involverte kontoer",
"success": "Meldinger gjenopprettet", "summary": "Gjenopprettingsdetaljer",
"successDesc": "De valgte meldingene har blitt gjenopprettet til IMAP-serveren.", "messages": "meldinger",
"failed": "Kunne ikke gjenopprette meldinger", "desc": "Denne handlingen vil laste opp valgte meldinger fra Bichon på nytt og gjenopprette dem til de tilsvarende postkassene på IMAP-serveren.",
"failedTitle": "Gjenoppretting mislyktes" "confirm": "Utfør gjenoppretting",
"restore_to_imap": "Gjenopprett melding",
"success": "Gjenoppretting vellykket",
"successDesc": "De valgte meldingene er gjenopprettet til IMAP-serveren.",
"failed": "Gjenoppretting mislykkedes",
"failedTitle": "Feil ved gjenoppretting"
}, },
"time": { "time": {
"label": "Tid", "label": "Tid",
+12 -7
View File
@@ -1453,14 +1453,19 @@
"confirm": "Wyloguj się" "confirm": "Wyloguj się"
}, },
"restore_message": { "restore_message": {
"title": "Przywróć wiadomości", "title": "Przywróć pojedynczą wiadomość",
"desc": "Ta operacja prześle wybrane wiadomości z Bichon i przywróci je do odpowiednich skrzynek pocztowych na serwerze IMAP.", "bulkTitle": "Masowe przywracanie wiadomości",
"confirm": "Przywróć", "bulkWarning": "Uwaga: Wiadomości zostaną przywrócone do ich oryginalnych folderów.",
"restore_to_imap": "Przywróć pocztę", "accountsInvolved": "Zaangażowane konta",
"success": "Wiadomości przywrócone", "summary": "Szczegóły przywracania",
"messages": "wiadomości",
"desc": "Ta operacja spowoduje ponowne przesłanie wybranych wiadomości z systemu Bichon i przywrócenie ich do odpowiednich skrzynek pocztowych na serwerze IMAP.",
"confirm": "Wykonaj przywracanie",
"restore_to_imap": "Przywróć wiadomość",
"success": "Przywracanie zakończone sukcesem",
"successDesc": "Wybrane wiadomości zostały pomyślnie przywrócone na serwer IMAP.", "successDesc": "Wybrane wiadomości zostały pomyślnie przywrócone na serwer IMAP.",
"failed": "Nie udało się przywrócić wiadomości", "failed": "Przywracanie nie powiodło się",
"failedTitle": "Przywracanie nie powiodło się" "failedTitle": "Błąd przywracania"
}, },
"time": { "time": {
"label": "Czas", "label": "Czas",
+12 -7
View File
@@ -1453,13 +1453,18 @@
"confirm": "Sair" "confirm": "Sair"
}, },
"restore_message": { "restore_message": {
"title": "Restaurar mensagens", "title": "Restaurar mensagem individual",
"desc": "Esta ação irá carregar as mensagens selecionadas do Bichon e restaurá-las nas respetivas caixas de correio no servidor IMAP.", "bulkTitle": "Restaurar múltiplas mensagens",
"confirm": "Restaurar", "bulkWarning": "Nota: As mensagens serão restauradas nas suas pastas originais.",
"restore_to_imap": "Restaurar e-mail", "accountsInvolved": "Contas envolvidas",
"success": "Mensagens restauradas", "summary": "Detalhes da restauração",
"successDesc": "As mensagens selecionadas foram restauradas com sucesso para o servidor IMAP.", "messages": "mensagens",
"failed": "Falha ao restaurar mensagens", "desc": "Esta ação irá recarregar as mensagens selecionadas do Bichon e restaurá-las nas pastas correspondentes no servidor IMAP.",
"confirm": "Executar restauração",
"restore_to_imap": "Restaurar mensagem",
"success": "Restauração bem-sucedida",
"successDesc": "As mensagens selecionadas foram restauradas com sucesso no servidor IMAP.",
"failed": "Falha ao restaurar",
"failedTitle": "Falha na restauração" "failedTitle": "Falha na restauração"
}, },
"time": { "time": {
+13 -8
View File
@@ -1453,14 +1453,19 @@
"confirm": "Выйти" "confirm": "Выйти"
}, },
"restore_message": { "restore_message": {
"title": "Восстановить сообщения", "title": "Восстановить одно сообщение",
"desc": "Это действие загрузит выбранные сообщения из Bichon и восстановит их в соответствующих почтовых ящиках на IMAP-сервере.", "bulkTitle": "Массовое восстановление сообщений",
"confirm": "Восстановить", "bulkWarning": "Примечание: сообщения будут восстановлены в исходные папки.",
"restore_to_imap": "Восстановить почту", "accountsInvolved": "Задействованные аккаунты",
"success": "Сообщения восстановлены", "summary": "Детали восстановления",
"successDesc": "Выбранные сообщения были успешно восстановлены на IMAP-сервере.", "messages": "сообщений",
"failed": "Не удалось восстановить сообщения", "desc": "Это действие повторно загрузит выбранные сообщения из Bichon и восстановит их в соответствующих почтовых ящиках на сервере IMAP.",
"failedTitle": "Ошибка восстановления" "confirm": "Выполнить восстановление",
"restore_to_imap": "Восстановить сообщение",
"success": "Восстановление успешно",
"successDesc": "Выбранные сообщения были успешно восстановлены на сервере IMAP.",
"failed": "Ошибка восстановления",
"failedTitle": "Восстановление не удалось"
}, },
"time": { "time": {
"label": "Время", "label": "Время",
+12 -7
View File
@@ -1453,14 +1453,19 @@
"confirm": "Logga ut" "confirm": "Logga ut"
}, },
"restore_message": { "restore_message": {
"title": "Återställ meddelanden", "title": "Återställ enstaka meddelande",
"desc": "Denna åtgärd kommer att ladda upp de valda meddelandena från Bichon och återställa dem till deras motsvarande brevlådor på IMAP-servern.", "bulkTitle": "Återställ flera meddelanden",
"confirm": "Återställ", "bulkWarning": "Obs: Meddelanden kommer att återställas till sina ursprungliga mappar.",
"restore_to_imap": "Återställ e-post", "accountsInvolved": "Inblandade konton",
"success": "Meddelanden återställda", "summary": "Återställningsdetaljer",
"messages": "meddelanden",
"desc": "Denna åtgärd kommer att ladda upp de valda meddelandena igen från Bichon och återställa dem till deras motsvarande inkorgar på IMAP-servern.",
"confirm": "Utför återställning",
"restore_to_imap": "Återställ meddelande",
"success": "Återställning lyckades",
"successDesc": "De valda meddelandena har återställts till IMAP-servern.", "successDesc": "De valda meddelandena har återställts till IMAP-servern.",
"failed": "Misslyckades med att återställa meddelanden", "failed": "Återställning misslyckades",
"failedTitle": "Återställning misslyckades" "failedTitle": "Fel vid återställning"
}, },
"time": { "time": {
"label": "Tid", "label": "Tid",
+8 -3
View File
@@ -1453,12 +1453,17 @@
"confirm": "登出" "confirm": "登出"
}, },
"restore_message": { "restore_message": {
"title": "還原郵件", "title": "還原單個郵件",
"desc": "此操作將把選定的郵件從 Bichon 系統重新上傳並還原到其在 IMAP 伺服器上對應的信箱中。", "bulkTitle": "批量還原多個郵件",
"bulkWarning": "請注意:郵件將被還原至其原始存儲的資料夾中。",
"accountsInvolved": "涉及帳戶",
"summary": "待還原詳情",
"messages": "封郵件",
"desc": "此操作將把選定的郵件從 Bichon 系統重新上傳並恢復到其在 IMAP 服務器上對應的郵箱中。",
"confirm": "執行還原", "confirm": "執行還原",
"restore_to_imap": "還原郵件", "restore_to_imap": "還原郵件",
"success": "郵件還原成功", "success": "郵件還原成功",
"successDesc": "選定的郵件已成功還原到 IMAP 服器。", "successDesc": "選定的郵件已成功恢復到 IMAP 服器。",
"failed": "還原郵件失敗", "failed": "還原郵件失敗",
"failedTitle": "還原失敗" "failedTitle": "還原失敗"
}, },
+6 -1
View File
@@ -1453,7 +1453,12 @@
"confirm": "退出登录" "confirm": "退出登录"
}, },
"restore_message": { "restore_message": {
"title": "还原邮件", "title": "还原单个邮件",
"bulkTitle": "批量还原多个邮件",
"bulkWarning": "请注意:邮件将被还原至其原始存储的文件夹中。",
"accountsInvolved": "涉及账户",
"summary": "待还原详情",
"messages": "封邮件",
"desc": "此操作将把选定的邮件从 Bichon 系统重新上传并恢复到其在 IMAP 服务器上对应的邮箱中。", "desc": "此操作将把选定的邮件从 Bichon 系统重新上传并恢复到其在 IMAP 服务器上对应的邮箱中。",
"confirm": "执行还原", "confirm": "执行还原",
"restore_to_imap": "还原邮件", "restore_to_imap": "还原邮件",