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">
<p className="text-sm text-muted-foreground">
{t(
'restore_message.desc', 'restore_message.desc',
'This action will append the selected messages from Bichon to their corresponding mailboxes on the IMAP server.' '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": "还原邮件",