feat(imap): gap-fill missing-mail repair with live progress UI

This commit is contained in:
rustmailer
2026-08-06 01:18:20 +08:00
parent 75d345b742
commit ed02c642c8
31 changed files with 1589 additions and 62 deletions

View File

@@ -67,6 +67,35 @@ export interface AccountError {
error: string;
}
export interface GapFillFolderStats {
downloaded: number;
failed: number;
candidate_count: number;
message?: string | null;
}
export enum GapFillStatus {
Running = "Running",
Success = "Success",
Failed = "Failed",
Cancelled = "Cancelled",
}
export interface GapFillRun {
started_at: number;
finished_at: number | null;
status: GapFillStatus;
folders: Record<string, GapFillFolderStats>;
downloaded: number;
failed: number;
}
export interface GapFillState {
account_id: number;
active: GapFillRun | null;
history: GapFillRun[];
}
export interface DownloadSession {
start_time: number;
end_time: number | null;
@@ -168,6 +197,11 @@ export const download_state = async (account_id: number) => {
return response.data;
};
export const gap_fill_state = async (account_id: number) => {
const response = await axiosInstance.get<GapFillState>(`api/v1/accounts/${account_id}/gap-fill-stats`);
return response.data;
};
export const create_account = async (data: Record<string, any>) => {
const response = await axiosInstance.post("api/v1/account", data);
return response.data;
@@ -189,8 +223,8 @@ export const remove_account = async (account_id: number) => {
};
export const start_account_download = async (account_id: number) => {
const response = await axiosInstance.post(`api/v1/accounts/${account_id}/start-download`);
export const start_account_download = async (account_id: number, run_gap_fill = false) => {
const response = await axiosInstance.post(`api/v1/accounts/${account_id}/start-download`, { run_gap_fill });
return response.data;
};

View File

@@ -17,6 +17,7 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import { useState } from 'react'
import { DotsHorizontalIcon } from '@radix-ui/react-icons'
import { Row } from '@tanstack/react-table'
import { IconEdit, IconPlayerPlay, IconPlayerStop, IconShieldLock, IconTrash } from '@tabler/icons-react'
@@ -33,9 +34,10 @@ import { useAccountContext } from '../context'
import { Mailbox, MessageSquareMore, Settings } from 'lucide-react'
import { useTranslation } from 'react-i18next'
import { useCurrentUser } from '@/hooks/use-current-user'
import { AccountModel, cancel_account_download, start_account_download } from '@/api/account/api'
import { AccountModel, cancel_account_download } from '@/api/account/api'
import { toast } from '@/hooks/use-toast'
import { useNavigate } from '@tanstack/react-router'
import { StartDownloadDialog } from './start-download-dialog'
interface DataTableRowActionsProps {
row: Row<AccountModel>
@@ -45,6 +47,7 @@ export function DataTableRowActions({ row }: DataTableRowActionsProps) {
const { t } = useTranslation()
const { setOpen, setCurrentRow } = useAccountContext()
const navigate = useNavigate()
const [startDialogOpen, setStartDialogOpen] = useState(false)
const account_type = row.original.account_type;
const { require_any_permission } = useCurrentUser()
@@ -64,16 +67,7 @@ export function DataTableRowActions({ row }: DataTableRowActionsProps) {
const showDownload = !isDeleting && account_type === 'IMAP' && hasPermission;
const handleStartDownload = async () => {
try {
await start_account_download(row.original.id);
toast({ title: t('accounts.downloadStarted') });
} catch (error: any) {
toast({
variant: "destructive",
title: t('accounts.downloadFailed'),
description: error.response?.data?.message || error.message
});
}
setStartDialogOpen(true)
}
@@ -198,6 +192,11 @@ export function DataTableRowActions({ row }: DataTableRowActionsProps) {
</DropdownMenuItem>}
</DropdownMenuContent>
</DropdownMenu>
<StartDownloadDialog
row={row.original}
open={startDialogOpen}
onOpenChange={setStartDialogOpen}
/>
</>
)
}

View File

@@ -27,7 +27,7 @@ import {
import { Button } from '@/components/ui/button'
import { useQuery } from '@tanstack/react-query'
import { useEffect, useState } from 'react'
import { download_state, DownloadStatus, AccountModel, FolderProgress } from '@/api/account/api'
import { download_state, gap_fill_state, DownloadStatus, AccountModel, FolderProgress, GapFillRun, GapFillStatus } from '@/api/account/api'
import { format } from 'date-fns'
import { ScrollArea } from '@/components/ui/scroll-area'
import { Badge } from '@/components/ui/badge'
@@ -136,6 +136,62 @@ function FolderDetailItem({ f, t }: { f: FolderProgress, t: (key: string) => str
)
}
function GapFillRunDetail({ run, t }: { run: GapFillRun, t: (key: string) => string }) {
const folderEntries = Object.entries(run.folders)
if (folderEntries.length === 0) {
return (
<div className="text-center py-8 text-muted-foreground italic text-xs">
{t('accounts.runningState.empty.no_gap_fill_folders')}
</div>
)
}
const isActive = run.status === GapFillStatus.Running
return (
<div className="space-y-3">
{folderEntries.map(([name, stats]) => {
const pct = stats.candidate_count > 0
? Math.min(100, Math.round((stats.downloaded / stats.candidate_count) * 100))
: 0
return (
<div key={name} className="py-1.5 border-b border-border/50 last:border-b-0">
<div className="flex items-center justify-between gap-2">
<span className="text-xs font-bold text-foreground truncate">{name}</span>
<span className="text-[10px] font-bold text-muted-foreground whitespace-nowrap">
{isActive && stats.candidate_count > 0 ? (
<span className="text-blue-600">
{stats.downloaded} <span className="opacity-50">/</span> {stats.candidate_count}
</span>
) : (
<>
<span className="text-blue-600">{stats.downloaded} {t('accounts.runningState.gap_fill_downloaded_suffix')}</span>
{stats.failed > 0 && (
<>
<span className="mx-1 opacity-30">·</span>
<span className="text-destructive">{stats.failed} {t('accounts.runningState.gap_fill_failed_suffix')}</span>
</>
)}
</>
)}
</span>
</div>
{isActive && stats.candidate_count > 0 && (
<div className="mt-1.5 h-1.5 w-full rounded-full bg-muted overflow-hidden">
<div className="h-full rounded-full bg-blue-500 transition-all duration-500" style={{ width: `${pct}%` }} />
</div>
)}
{stats.message && (
<div className="mt-1.5 flex items-start gap-1.5">
<AlertTriangle className="w-3 h-3 text-amber-600 mt-0.5 shrink-0" />
<p className="text-[10px] font-medium text-amber-700 leading-relaxed">{stats.message}</p>
</div>
)}
</div>
)
})}
</div>
)
}
export function RunningStateDialog({ currentRow, open, onOpenChange }: Props) {
const { t } = useTranslation();
@@ -149,6 +205,16 @@ export function RunningStateDialog({ currentRow, open, onOpenChange }: Props) {
enabled: open && !!currentRow.id,
})
const { data: gapFillData } = useQuery({
queryKey: ['gap-fill-state', currentRow.id],
queryFn: () => gap_fill_state(currentRow.id),
refetchInterval: (query) => {
const a = query.state.data?.active
return a && a.status === GapFillStatus.Running ? 5000 : false
},
enabled: open && !!currentRow.id,
})
const session = state?.active_session
const history = state?.history || []
const isRunning = !!session && session.status === DownloadStatus.Running
@@ -199,6 +265,10 @@ export function RunningStateDialog({ currentRow, open, onOpenChange }: Props) {
{t('accounts.runningState.tabs.history')}
<Badge variant="secondary" className="ml-2 h-4 px-1 text-[10px] font-bold">{history.length}</Badge>
</TabsTrigger>
<TabsTrigger value="gapfill" className="whitespace-nowrap data-[state=active]:border-b-2 data-[state=active]:border-primary rounded-none h-full bg-transparent shadow-none px-0 text-xs sm:text-sm font-bold">
{t('accounts.runningState.tabs.gap_fill')}
{gapFillData?.active && <Badge variant="secondary" className="ml-2 h-4 px-1 text-[10px] font-bold animate-pulse">{t('accounts.runningState.syncing')}</Badge>}
</TabsTrigger>
</TabsList>
</div>
@@ -439,6 +509,55 @@ export function RunningStateDialog({ currentRow, open, onOpenChange }: Props) {
</div>
</ScrollArea>
</TabsContent>
<TabsContent value="gapfill" className="h-full m-0 data-[state=active]:flex flex-col">
<ScrollArea className="flex-1">
<div className="p-4 sm:p-6 space-y-4">
{gapFillData?.active && (
<div className="rounded-xl border bg-card shadow-sm p-4">
<div className="flex items-center justify-between mb-3">
<p className="text-[10px] font-bold text-muted-foreground uppercase">{t('accounts.runningState.gap_fill_active')}</p>
<StatusBadge status={gapFillData.active.status} />
</div>
<GapFillRunDetail run={gapFillData.active} t={t} />
</div>
)}
{(!gapFillData?.history || gapFillData.history.length === 0) ? (
<div className="text-center py-20 text-muted-foreground italic text-sm">
{t('accounts.runningState.empty.no_gap_fill_history')}
</div>
) : (
<Accordion type="single" collapsible className="space-y-3">
{[...gapFillData.history].reverse().map((run, i) => (
<AccordionItem key={i} value={`gapfill-${i}`} className="border rounded-xl bg-card shadow-sm px-4 border-border overflow-hidden">
<AccordionTrigger className="hover:no-underline py-4">
<div className="flex flex-col sm:flex-row sm:items-center justify-between w-full pr-4 gap-2">
<div className="flex items-center gap-3">
<div className="text-xs sm:text-xs font-bold font-mono text-foreground">
{format(new Date(run.started_at), 'yyyy-MM-dd HH:mm:ss')}
</div>
<StatusBadge status={run.status} />
</div>
<span className="text-[10px] font-bold text-muted-foreground bg-muted px-2 py-0.5 rounded-full self-start sm:self-auto">
<span className="text-blue-600">{run.downloaded} {t('accounts.runningState.gap_fill_downloaded_suffix')}</span>
{run.failed > 0 && (
<>
<span className="mx-1 opacity-30">·</span>
<span className="text-destructive">{run.failed} {t('accounts.runningState.gap_fill_failed_suffix')}</span>
</>
)}
</span>
</div>
</AccordionTrigger>
<AccordionContent className="pb-4 border-t pt-4 mt-1 border-border">
<GapFillRunDetail run={run} t={t} />
</AccordionContent>
</AccordionItem>
))}
</Accordion>
)}
</div>
</ScrollArea>
</TabsContent>
</>
)}
</div>

View File

@@ -0,0 +1,88 @@
//
// Copyright (c) 2025-2026 rustmailer.com (https://rustmailer.com)
//
// This file is part of the Bichon Email Archiving Project
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import { useEffect, useState } from 'react'
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'
import { Button } from '@/components/ui/button'
import { Checkbox } from '@/components/ui/checkbox'
import { Label } from '@/components/ui/label'
import { useTranslation } from 'react-i18next'
import { toast } from '@/hooks/use-toast'
import { start_account_download, AccountModel } from '@/api/account/api'
interface Props {
row: AccountModel
open: boolean
onOpenChange: (open: boolean) => void
}
export function StartDownloadDialog({ row, open, onOpenChange }: Props) {
const { t } = useTranslation()
const [runGapFill, setRunGapFill] = useState(false)
const [submitting, setSubmitting] = useState(false)
useEffect(() => {
if (open) {
setRunGapFill(false)
setSubmitting(false)
}
}, [open])
const handleConfirm = async () => {
setSubmitting(true)
try {
await start_account_download(row.id, runGapFill)
toast({ title: t('accounts.downloadStarted') })
onOpenChange(false)
} catch (error: any) {
toast({
variant: 'destructive',
title: t('accounts.downloadFailed'),
description: error.response?.data?.message || error.message,
})
} finally {
setSubmitting(false)
}
}
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent>
<DialogHeader>
<DialogTitle>{t('accounts.startDownload')}</DialogTitle>
<DialogDescription>
{t('accounts.startDownloadConfirmDesc')}
</DialogDescription>
</DialogHeader>
<div className="flex items-center gap-2 py-2">
<Checkbox id="run-gap-fill" checked={runGapFill} onCheckedChange={(v) => setRunGapFill(!!v)} />
<Label htmlFor="run-gap-fill">{t('accounts.runGapFill')}</Label>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => onOpenChange(false)} disabled={submitting}>
{t('common.cancel')}
</Button>
<Button onClick={handleConfirm} disabled={submitting}>
{t('accounts.startDownload')}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)
}

View File

@@ -240,6 +240,7 @@
"imapPortPlaceholder": "مثال: 993",
"imapProxy": "استخدم وكيل SOCKS5 لاتصالات IMAP.",
"incDownload": "الفاصل",
"incSync": "فترة التزامن",
"lastSync": "آخر مزامنة",
"leaveEmptyToKeepExisting": "اترك فارغًا للاحتفاظ بكلمة المرور الحالية، أو أدخل كلمة مرور جديدة لتحديثها.",
"leaveEmptyToKeepPassword": "اترك فارغًا للاحتفاظ بكلمة المرور الحالية",
@@ -271,6 +272,7 @@
"refreshToken": "رمز التحديث",
"refreshTokenCopiedToClipboard": "تم نسخ رمز التحديث إلى الحافظة",
"relative": "نسبي",
"runGapFill": "فحص البريد الجديد وتنزيله، مع ملء الرسائل القديمة المفقودة محلياً تلقائياً",
"runningState": {
"account": {
"id": "معرّف الحساب"
@@ -283,10 +285,15 @@
"no_active_download": "لا يوجد تنزيل نشط",
"no_errors_current": "لا توجد أخطاء في الجلسة الحالية",
"no_errors_session": "لا توجد أخطاء في هذه الجلسة",
"no_gap_fill_folders": "لا توجد مجلدات لمزامنة الرسائل المفقودة",
"no_gap_fill_history": "لا يوجد سجل لاستكمال الرسائل",
"no_global_errors": "لا توجد أخطاء عامة",
"no_history": "لا يوجد سجل"
},
"folders": "صناديق البريد",
"gap_fill_active": "عملية الاستكمال قيد التشغيل",
"gap_fill_downloaded_suffix": "تم تنزيلها",
"gap_fill_failed_suffix": "فشلت",
"latest": "الأحدث",
"loading": {
"fetching_account_state": "جارٍ تحميل حالة الحساب..."
@@ -305,6 +312,7 @@
"active_session": "الجلسة النشطة",
"errors": "أخطاء",
"folders": "صناديق البريد",
"gap_fill": "استكمال الرسائل الناقصة",
"history": "السجل"
}
},
@@ -345,6 +353,7 @@
"sinceRelativeDesc": "تنزيل رسائل الفترة الأخيرة فقط (مثل آخر 3 أشهر). يتحرك تاريخ البدء تلقائياً مع مرور الوقت.",
"sinceRelativeValue": "تنزيل رسائل البريد الإلكتروني من آخر",
"startDownload": "بدء التنزيل",
"startDownloadConfirmDesc": "بدء تنزيل بيانات الحسابات المحددة؟",
"state": "الحالة",
"status": "الحالة",
"step": "الخطوة {{index}}",

View File

@@ -240,6 +240,7 @@
"imapPortPlaceholder": "f.eks. 993",
"imapProxy": "Brug en SOCKS5-proxy til IMAP-forbindelser.",
"incDownload": "Interval",
"incSync": "Synk-interval",
"lastSync": "Sidste synk.",
"leaveEmptyToKeepExisting": "Lad stå tomt for at beholde den eksisterende adgangskode, eller indtast en ny for at opdatere den.",
"leaveEmptyToKeepPassword": "Lad stå tomt for at beholde nuværende adgangskode",
@@ -271,6 +272,7 @@
"refreshToken": "Opdateringstoken",
"refreshTokenCopiedToClipboard": "Opdateringstoken kopieret til udklipsholderen",
"relative": "Relativ",
"runGapFill": "Tjek for nye e-mails og fyld automatisk op på ældre manglende e-mails",
"runningState": {
"account": {
"id": "Konto-ID"
@@ -283,10 +285,15 @@
"no_active_download": "Ingen aktiv download",
"no_errors_current": "Ingen fejl i nuværende session",
"no_errors_session": "Ingen fejl i denne session",
"no_gap_fill_folders": "Ingen mapper med manglende e-mails",
"no_gap_fill_history": "Ingen historik over backfill",
"no_global_errors": "Ingen globale fejl",
"no_history": "Ingen historik"
},
"folders": "postkasser",
"gap_fill_active": "Kørende backfill-kørsel",
"gap_fill_downloaded_suffix": "downloadet",
"gap_fill_failed_suffix": "misllykkedes",
"latest": "SENESTE",
"loading": {
"fetching_account_state": "Henter kontostatus..."
@@ -305,6 +312,7 @@
"active_session": "Aktiv session",
"errors": "Fejl",
"folders": "Postkasser",
"gap_fill": "Udfyld manglende e-mails",
"history": "Historik"
}
},
@@ -345,6 +353,7 @@
"sinceRelativeDesc": "Download kun e-mails fra den seneste periode (f.eks. de seneste 3 måneder). Startdatoen flyttes automatisk fremad.",
"sinceRelativeValue": "Download e-mails fra de sidste",
"startDownload": "Start download",
"startDownloadConfirmDesc": "Start download for valgte konti?",
"state": "Tilstand",
"status": "Status",
"step": "Trin {{index}}",

View File

@@ -240,6 +240,7 @@
"imapPortPlaceholder": "z.B. 993",
"imapProxy": "SOCKS5-Proxy für IMAP-Verbindungen verwenden.",
"incDownload": "Intervall",
"incSync": "Synch.-Intervall",
"lastSync": "Letzte Synchronisierung",
"leaveEmptyToKeepExisting": "Leer lassen, um das bestehende Passwort beizubehalten, oder einen neuen Wert eingeben, um es zu aktualisieren.",
"leaveEmptyToKeepPassword": "Leer lassen, um das aktuelle Passwort beizubehalten",
@@ -271,6 +272,7 @@
"refreshToken": "Aktualisierungstoken",
"refreshTokenCopiedToClipboard": "Aktualisierungstoken in die Zwischenablage kopiert",
"relative": "Relativ",
"runGapFill": "Neue Mails prüfen und ältere, lokal fehlende Mails automatisch nachladen",
"runningState": {
"account": {
"id": "Konto-ID"
@@ -283,10 +285,15 @@
"no_active_download": "Kein aktiver Download",
"no_errors_current": "Keine Fehler in der aktuellen Sitzung",
"no_errors_session": "Keine Fehler in dieser Sitzung",
"no_gap_fill_folders": "Keine Ordner für den Abgleich fehlender Mails",
"no_gap_fill_history": "Kein Backfill-Verlauf vorhanden",
"no_global_errors": "Keine globalen Fehler",
"no_history": "Kein Verlauf vorhanden"
},
"folders": "Postfächer",
"gap_fill_active": "Laufender Backfill-Prozess",
"gap_fill_downloaded_suffix": "heruntergeladen",
"gap_fill_failed_suffix": "fehlgeschlagen",
"latest": "NEU",
"loading": {
"fetching_account_state": "Kontostatus wird geladen..."
@@ -305,6 +312,7 @@
"active_session": "Aktive Sitzung",
"errors": "Fehler",
"folders": "Postfächer",
"gap_fill": "Lückenfüllung",
"history": "Verlauf"
}
},
@@ -345,6 +353,7 @@
"sinceRelativeDesc": "Nur E-Mails aus dem jüngsten Zeitraum herunterladen (z. B. letzte 3 Monate). Das Startdatum verschiebt sich automatisch.",
"sinceRelativeValue": "E-Mails der letzten Zeit herunterladen",
"startDownload": "Download starten",
"startDownloadConfirmDesc": "E-Mail-Download für gewählte Konten starten?",
"state": "Zustand",
"status": "Status",
"step": "Schritt {{index}}",

View File

@@ -242,6 +242,7 @@
"imapPortPlaceholder": "e.g 993",
"imapProxy": "Use a proxy (http/socks5) for IMAP connections.",
"incDownload": "Interval",
"incSync": "Sync interval",
"lastSync": "Last Sync",
"leaveEmptyToKeepExisting": "Leave empty to keep the existing password, or enter a new password to update it.",
"leaveEmptyToKeepPassword": "Leave empty to keep current password",
@@ -273,6 +274,7 @@
"refreshToken": "Refresh Token",
"refreshTokenCopiedToClipboard": "Refresh token copied to clipboard",
"relative": "Relative",
"runGapFill": "Check new emails and automatically backfill older missing emails",
"runningState": {
"account": {
"id": "Account ID"
@@ -285,10 +287,15 @@
"no_active_download": "No download in progress",
"no_errors_current": "No errors in current session",
"no_errors_session": "No errors in this session",
"no_gap_fill_folders": "No folders syncing missing messages",
"no_gap_fill_history": "No backfill history",
"no_global_errors": "No global errors",
"no_history": "No historical records found"
},
"folders": "mailboxes",
"gap_fill_active": "Running backfill task",
"gap_fill_downloaded_suffix": "downloaded",
"gap_fill_failed_suffix": "failed",
"latest": "LATEST",
"loading": {
"fetching_account_state": "Fetching account state..."
@@ -307,6 +314,7 @@
"active_session": "Active Session",
"errors": "Errors",
"folders": "Mailboxes",
"gap_fill": "Backfill missing emails",
"history": "History"
}
},
@@ -355,6 +363,7 @@
"sinceRelativeDesc": "Only download emails from the recent period (e.g. last 3 months). The start date automatically moves forward over time.",
"sinceRelativeValue": "Download emails from the last",
"startDownload": "Start download",
"startDownloadConfirmDesc": "Start downloading mail data for selected accounts?",
"state": "State",
"status": "Status",
"step": "Step {{index}}",

View File

@@ -240,6 +240,7 @@
"imapPortPlaceholder": "ej. 993",
"imapProxy": "Usar proxy SOCKS5 para conexiones IMAP.",
"incDownload": "Intervalo",
"incSync": "Intervalo de sinc.",
"lastSync": "Última sincronización",
"leaveEmptyToKeepExisting": "Deja vacío para mantener la contraseña existente, o introduce un nuevo valor para actualizarla.",
"leaveEmptyToKeepPassword": "Deja vacío para mantener la contraseña actual",
@@ -271,6 +272,7 @@
"refreshToken": "Token de actualización",
"refreshTokenCopiedToClipboard": "Token de actualización copiado al portapapeles",
"relative": "Relativa",
"runGapFill": "Verificar correos nuevos y rellenar automáticamente los faltantes antiguos",
"runningState": {
"account": {
"id": "ID de cuenta"
@@ -283,10 +285,15 @@
"no_active_download": "No hay descargas en curso",
"no_errors_current": "Sin errores en la sesión actual",
"no_errors_session": "Sin errores en esta sesión",
"no_gap_fill_folders": "No hay carpetas sincronizando mensajes faltantes",
"no_gap_fill_history": "Sin historial de backfill",
"no_global_errors": "Sin errores globales",
"no_history": "Sin historial"
},
"folders": "buzones",
"gap_fill_active": "Tarea de backfill en ejecución",
"gap_fill_downloaded_suffix": "descargados",
"gap_fill_failed_suffix": "fallidos",
"latest": "RECIENTE",
"loading": {
"fetching_account_state": "Obteniendo estado de la cuenta..."
@@ -305,6 +312,7 @@
"active_session": "Sesión activa",
"errors": "Errores",
"folders": "Buzones",
"gap_fill": "Completar mensajes faltantes",
"history": "Historial"
}
},
@@ -345,6 +353,7 @@
"sinceRelativeDesc": "Solo descargar correos del período reciente (ej. últimos 3 meses). La fecha de inicio avanza automáticamente.",
"sinceRelativeValue": "Descargar correos de los últimos",
"startDownload": "Iniciar descarga",
"startDownloadConfirmDesc": "¿Iniciar descarga para las cuentas seleccionadas?",
"state": "Estado",
"status": "Estado",
"step": "Paso {{index}}",

View File

@@ -240,6 +240,7 @@
"imapPortPlaceholder": "esim. 993",
"imapProxy": "Käytä SOCKS5-välityspalvelinta IMAP-yhteyksiin.",
"incDownload": "Väli",
"incSync": "Synkronointiväli",
"lastSync": "Viimeisin synkronointi",
"leaveEmptyToKeepExisting": "Jätä tyhjäksi säilyttääksesi olemassa olevan salasanan, tai syötä uusi päivittääksesi sen.",
"leaveEmptyToKeepPassword": "Jätä tyhjäksi säilyttääksesi nykyisen salasanan",
@@ -271,6 +272,7 @@
"refreshToken": "Virheettömyystunnus",
"refreshTokenCopiedToClipboard": "Virheettömyystunnus kopioitu leikepöydälle",
"relative": "Suhteellinen",
"runGapFill": "Tarkista uudet sähköpostit ja täydennä vanhat puuttuvat viestit automaattisesti",
"runningState": {
"account": {
"id": "Tilin ID"
@@ -283,10 +285,15 @@
"no_active_download": "Ei aktiivista latausta",
"no_errors_current": "Ei virheitä nykyisessä istunnossa",
"no_errors_session": "Ei virheitä tässä istunnossa",
"no_gap_fill_folders": "Ei kansioita puuttuvien viestien täydennykseen",
"no_gap_fill_history": "Ei täydennyshistoriaa",
"no_global_errors": "Ei yleisiä virheitä",
"no_history": "Ei historiaa"
},
"folders": "postilaatikot",
"gap_fill_active": "Käynnissä oleva täydennys",
"gap_fill_downloaded_suffix": "ladattu",
"gap_fill_failed_suffix": "epäonnistui",
"latest": "UUSIN",
"loading": {
"fetching_account_state": "Haetaan tilan tietoja..."
@@ -305,6 +312,7 @@
"active_session": "Aktiivinen istunto",
"errors": "Virheet",
"folders": "Postilaatikot",
"gap_fill": "Puuttuvien täydennys",
"history": "Historia"
}
},
@@ -345,6 +353,7 @@
"sinceRelativeDesc": "Lataa vain viimeaikaiset sähköpostit (esim. viimeiset 3 kuukautta). Aloituspäivämäärä siirtyy automaattisesti eteenpäin.",
"sinceRelativeValue": "Lataa sähköpostit viimeisimmiltä",
"startDownload": "Aloita lataus",
"startDownloadConfirmDesc": "Aloitetaanko valittujen tilien lataus?",
"state": "Tila",
"status": "Tila",
"step": "Vaihe {{index}}",

View File

@@ -240,6 +240,7 @@
"imapPortPlaceholder": "ex. 993",
"imapProxy": "Utiliser un proxy SOCKS5 pour les connexions IMAP.",
"incDownload": "Intervalle",
"incSync": "Intervalle de synchro",
"lastSync": "Dernière Synchronisation",
"leaveEmptyToKeepExisting": "Laissez vide pour conserver le mot de passe existant, ou entrez-en un nouveau pour le mettre à jour.",
"leaveEmptyToKeepPassword": "Laisser vide pour conserver le mot de passe actuel",
@@ -271,6 +272,7 @@
"refreshToken": "Jeton de Rafraîchissement",
"refreshTokenCopiedToClipboard": "Jeton de rafraîchissement copié dans le presse-papiers",
"relative": "Relative",
"runGapFill": "Vérifier les nouveaux e-mails et rattraper automatiquement les anciens messages manquants",
"runningState": {
"account": {
"id": "ID du compte"
@@ -283,10 +285,15 @@
"no_active_download": "Aucun téléchargement en cours",
"no_errors_current": "Aucune erreur dans la session actuelle",
"no_errors_session": "Aucune erreur dans cette session",
"no_gap_fill_folders": "Aucun dossier en cours de synchronisation des messages manquants",
"no_gap_fill_history": "Aucun historique de rattrapage",
"no_global_errors": "Aucune erreur globale",
"no_history": "Aucun historique disponible"
},
"folders": "boîtes mail",
"gap_fill_active": "Tâche de rattrapage en cours",
"gap_fill_downloaded_suffix": "téléchargés",
"gap_fill_failed_suffix": "échec(s)",
"latest": "RÉCENT",
"loading": {
"fetching_account_state": "Chargement de l'état du compte..."
@@ -305,6 +312,7 @@
"active_session": "Session active",
"errors": "Erreurs",
"folders": "Boîtes mail",
"gap_fill": "Rattrapage des messages",
"history": "Historique"
}
},
@@ -345,6 +353,7 @@
"sinceRelativeDesc": "Télécharger uniquement les e-mails récents (ex. 3 derniers mois). La date de début avance automatiquement.",
"sinceRelativeValue": "Télécharger les e-mails des derniers",
"startDownload": "Lancer le téléchargement",
"startDownloadConfirmDesc": "Démarrer le téléchargement pour les comptes sélectionnés ?",
"state": "État",
"status": "Statut",
"step": "Étape {{index}}",

View File

@@ -240,6 +240,7 @@
"imapPortPlaceholder": "es. 993",
"imapProxy": "Usa un proxy SOCKS5 per le connessioni IMAP.",
"incDownload": "Intervallo",
"incSync": "Intervallo sinc.",
"lastSync": "Ultima Sincronizzazione",
"leaveEmptyToKeepExisting": "Lascia vuoto per mantenere la password esistente, o inseriscine una nuova per aggiornarla.",
"leaveEmptyToKeepPassword": "Lascia vuoto per mantenere la password attuale",
@@ -271,6 +272,7 @@
"refreshToken": "Token di Refresh",
"refreshTokenCopiedToClipboard": "Token di refresh copiato negli appunti",
"relative": "Relativa",
"runGapFill": "Controlla le nuove email e recupera automaticamente i vecchi messaggi mancanti",
"runningState": {
"account": {
"id": "ID account"
@@ -283,10 +285,15 @@
"no_active_download": "Nessun download in corso",
"no_errors_current": "Nessun errore nella sessione corrente",
"no_errors_session": "Nessun errore in questa sessione",
"no_gap_fill_folders": "Nessuna cartella con messaggi mancanti da scaricare",
"no_gap_fill_history": "Nessuna cronologia di backfill",
"no_global_errors": "Nessun errore globale",
"no_history": "Nessuna cronologia disponibile"
},
"folders": "caselle di posta",
"gap_fill_active": "Attività di backfill in esecuzione",
"gap_fill_downloaded_suffix": "scaricati",
"gap_fill_failed_suffix": "non riusciti",
"latest": "RECENTE",
"loading": {
"fetching_account_state": "Caricamento stato account..."
@@ -305,6 +312,7 @@
"active_session": "Sessione attiva",
"errors": "Errori",
"folders": "Caselle di posta",
"gap_fill": "Recupero messaggi mancanti",
"history": "Cronologia"
}
},
@@ -345,6 +353,7 @@
"sinceRelativeDesc": "Scarica solo le email del periodo recente (es. ultimi 3 mesi). La data di inizio si aggiorna automaticamente.",
"sinceRelativeValue": "Scarica email degli ultimi",
"startDownload": "Avvia download",
"startDownloadConfirmDesc": "Avviare il download per gli account selezionati?",
"state": "Stato",
"status": "Stato",
"step": "Passo {{index}}",

View File

@@ -240,6 +240,7 @@
"imapPortPlaceholder": "例: 993",
"imapProxy": "IMAP接続にSOCKS5プロキシを使用します。",
"incDownload": "間隔",
"incSync": "同期間隔",
"lastSync": "最終同期",
"leaveEmptyToKeepExisting": "既存のパスワードを保持する場合は空欄にしてください。更新する場合は新しいパスワードを入力してください。",
"leaveEmptyToKeepPassword": "現在のパスワードを保持する場合は空欄にしてください",
@@ -271,6 +272,7 @@
"refreshToken": "リフレッシュトークン",
"refreshTokenCopiedToClipboard": "リフレッシュトークンをクリップボードにコピーしました",
"relative": "相対",
"runGapFill": "新着メールを確認してダウンロードし、過去の未取得メールも自動的に差分補填します",
"runningState": {
"account": {
"id": "アカウントID"
@@ -283,10 +285,15 @@
"no_active_download": "現在ダウンロード中のタスクはありません",
"no_errors_current": "現在のタスクにエラーはありません",
"no_errors_session": "このタスクにエラーはありません",
"no_gap_fill_folders": "未取得メールを同期中のフォルダーはありません",
"no_gap_fill_history": "差分補填の履歴はありません",
"no_global_errors": "全体エラーはありません",
"no_history": "履歴がありません"
},
"folders": "メールボックス",
"gap_fill_active": "実行中の差分補填タスク",
"gap_fill_downloaded_suffix": "件ダウンロード済み",
"gap_fill_failed_suffix": "件失敗",
"latest": "最新",
"loading": {
"fetching_account_state": "アカウント状態を取得中..."
@@ -305,6 +312,7 @@
"active_session": "実行中タスク",
"errors": "エラー",
"folders": "メールボックス",
"gap_fill": "差分メール補填",
"history": "履歴"
}
},
@@ -345,6 +353,7 @@
"sinceRelativeDesc": "直近の期間過去3ヶ月のメールのみをダウンロードします。開始日は時間経過に伴い自動的に更新されます。",
"sinceRelativeValue": "直近の期間のメールをダウンロード",
"startDownload": "ダウンロードを開始",
"startDownloadConfirmDesc": "選択したアカウントのメールデータをダウンロードしますか?",
"state": "状態",
"status": "ステータス",
"step": "ステップ {{index}}",

View File

@@ -240,6 +240,7 @@
"imapPortPlaceholder": "예: 993",
"imapProxy": "IMAP 연결에 SOCKS5 프록시를 사용합니다.",
"incDownload": "간격",
"incSync": "동기화 간격",
"lastSync": "최종 동기화",
"leaveEmptyToKeepExisting": "기존 비밀번호를 유지하려면 비워 두십시오. 업데이트할 경우에만 새 비밀번호를 입력하십시오.",
"leaveEmptyToKeepPassword": "현재 비밀번호를 유지하려면 비워 두십시오",
@@ -271,6 +272,7 @@
"refreshToken": "새로 고침 토큰",
"refreshTokenCopiedToClipboard": "새로 고침 토큰이 클립보드에 복사되었습니다",
"relative": "상대적",
"runGapFill": "새 메일을 확인하여 다운로드하고, 누락된 과거 메일도 자동으로 백필합니다",
"runningState": {
"account": {
"id": "계정 ID"
@@ -283,10 +285,15 @@
"no_active_download": "진행 중인 다운로드가 없습니다",
"no_errors_current": "현재 작업에 오류가 없습니다",
"no_errors_session": "이 작업에 오류가 없습니다",
"no_gap_fill_folders": "누락된 메일을 동기화 중인 메일함이 없습니다",
"no_gap_fill_history": "백필 기록 없음",
"no_global_errors": "전체 오류가 없습니다",
"no_history": "기록이 없습니다"
},
"folders": "메일함",
"gap_fill_active": "실행 중인 백필 작업",
"gap_fill_downloaded_suffix": "개 다운로드됨",
"gap_fill_failed_suffix": "개 실패",
"latest": "최신",
"loading": {
"fetching_account_state": "계정 상태를 불러오는 중..."
@@ -305,6 +312,7 @@
"active_session": "현재 작업",
"errors": "오류",
"folders": "메일함",
"gap_fill": "누락 메일 백필",
"history": "기록"
}
},
@@ -345,6 +353,7 @@
"sinceRelativeDesc": "최근 기간(예: 지난 3개월)의 이메일만 다운로드합니다. 시작 날짜는 시간이 지남에 따라 자동으로 이동합니다.",
"sinceRelativeValue": "최근 기간의 이메일 다운로드",
"startDownload": "다운로드 시작",
"startDownloadConfirmDesc": "선택한 계정의 메일 데이터를 다운로드할까요?",
"state": "상태",
"status": "상태",
"step": "단계 {{index}}",

View File

@@ -240,6 +240,7 @@
"imapPortPlaceholder": "bv. 993",
"imapProxy": "Gebruik een SOCKS5-proxy voor IMAP-verbindingen.",
"incDownload": "Interval",
"incSync": "Synch.-interval",
"lastSync": "Laatste Sync",
"leaveEmptyToKeepExisting": "Laat leeg om het bestaande wachtwoord te behouden, of voer een nieuw wachtwoord in om het bij te werken.",
"leaveEmptyToKeepPassword": "Laat leeg om huidig wachtwoord te behouden",
@@ -271,6 +272,7 @@
"refreshToken": "Vernieuwingstoken (Refresh Token)",
"refreshTokenCopiedToClipboard": "Vernieuwingstoken naar klembord gekopieerd",
"relative": "Relatief",
"runGapFill": "Controleer op nieuwe e-mails en vul oudere ontbrekende e-mails automatisch aan",
"runningState": {
"account": {
"id": "Account-ID"
@@ -283,10 +285,15 @@
"no_active_download": "Geen actieve download",
"no_errors_current": "Geen fouten in huidige sessie",
"no_errors_session": "Geen fouten in deze sessie",
"no_gap_fill_folders": "Geen mappen die ontbrekende berichten synchroniseren",
"no_gap_fill_history": "Geen backfill-geschiedenis",
"no_global_errors": "Geen globale fouten",
"no_history": "Geen geschiedenis beschikbaar"
},
"folders": "mailboxen",
"gap_fill_active": "Lopende backfill-taak",
"gap_fill_downloaded_suffix": "Gedownload",
"gap_fill_failed_suffix": "mislukt",
"latest": "RECENT",
"loading": {
"fetching_account_state": "Accountstatus ophalen..."
@@ -305,6 +312,7 @@
"active_session": "Actieve sessie",
"errors": "Fouten",
"folders": "Mailboxen",
"gap_fill": "Ontbrekende berichten aanvullen",
"history": "Geschiedenis"
}
},
@@ -345,6 +353,7 @@
"sinceRelativeDesc": "Download alleen e-mails uit de afgelopen periode (bijv. laatste 3 maanden). De startdatum verschuift automatisch mee.",
"sinceRelativeValue": "Download e-mails van de laatste",
"startDownload": "Download starten",
"startDownloadConfirmDesc": "Download starten voor geselecteerde accounts?",
"state": "Status",
"status": "Status",
"step": "Stap {{index}}",

View File

@@ -240,6 +240,7 @@
"imapPortPlaceholder": "f.eks. 993",
"imapProxy": "Bruk en SOCKS5-proxy for IMAP-tilkoblinger.",
"incDownload": "Intervall",
"incSync": "Synk-intervall",
"lastSync": "Siste synkronisering",
"leaveEmptyToKeepExisting": "La stå tomt for å beholde det eksisterende passordet, eller skriv inn et nytt passord for å oppdatere det.",
"leaveEmptyToKeepPassword": "La stå tomt for å beholde nåværende passord",
@@ -271,6 +272,7 @@
"refreshToken": "Oppfriskningstoken",
"refreshTokenCopiedToClipboard": "Oppfriskningstoken kopiert til utklippstavlen",
"relative": "Relativ",
"runGapFill": "Sjekk etter nye e-poster og fyll automatisk inn eldre manglende e-poster",
"runningState": {
"account": {
"id": "Konto-ID"
@@ -283,10 +285,15 @@
"no_active_download": "Ingen aktiv nedlasting",
"no_errors_current": "Ingen feil i gjeldende økt",
"no_errors_session": "Ingen feil i denne økten",
"no_gap_fill_folders": "Ingen mapper med manglende e-poster",
"no_gap_fill_history": "Ingen backfill-historikk",
"no_global_errors": "Ingen globale feil",
"no_history": "Ingen historikk"
},
"folders": "postbokser",
"gap_fill_active": "Kjørende backfill-oppgave",
"gap_fill_downloaded_suffix": "lastet ned",
"gap_fill_failed_suffix": "mislyktes",
"latest": "NYEST",
"loading": {
"fetching_account_state": "Henter kontostatus..."
@@ -305,6 +312,7 @@
"active_session": "Aktiv økt",
"errors": "Feil",
"folders": "Postbokser",
"gap_fill": "Fyll inn manglende e-poster",
"history": "Historikk"
}
},
@@ -345,6 +353,7 @@
"sinceRelativeDesc": "Last bare ned e-poster fra den siste perioden (f.eks. siste 3 måneder). Startdatoen flyttes automatisk fremover.",
"sinceRelativeValue": "Last ned e-poster fra de siste",
"startDownload": "Start nedlasting",
"startDownloadConfirmDesc": "Start nedlasting for valgte kontoer?",
"state": "Tilstand",
"status": "Status",
"step": "Trinn {{index}}",

View File

@@ -240,6 +240,7 @@
"imapPortPlaceholder": "np. 993 (szyfrowany TLS/SSL) lub 143 (bez szyfrowania)",
"imapProxy": "Użyj proxy gniazda SOCKS5 dla połączeń IMAP.",
"incDownload": "Interwał",
"incSync": "Interwał synch.",
"lastSync": "OStatnia synchronizacja",
"leaveEmptyToKeepExisting": "Pozostaw to pole puste, jeśli chcesz zachować dotychczasowe hasło lub wpisz nowe, aby zaktualizować.",
"leaveEmptyToKeepPassword": "Pozostaw to pole puste, jeśli chcesz zachować dotychczasowe hasło",
@@ -271,6 +272,7 @@
"refreshToken": "Odśwież token",
"refreshTokenCopiedToClipboard": "Token odśwież skopiowany do schowka",
"relative": "Wzglednie",
"runGapFill": "Sprawdzaj nowe e-maile i automatycznie uzupełniaj starsze, brakujące wiadomości",
"runningState": {
"account": {
"id": "ID konta"
@@ -283,10 +285,15 @@
"no_active_download": "Brak aktywnego pobierania",
"no_errors_current": "Brak błędów w bieżącej sesji",
"no_errors_session": "Brak błędów w tej sesji",
"no_gap_fill_folders": "Brak folderów z brakującymi wiadomościami do pobrania",
"no_gap_fill_history": "Brak historii uzupełniania",
"no_global_errors": "Brak błędów globalnych",
"no_history": "Brak historii"
},
"folders": "skrzynki",
"gap_fill_active": "Trwające uzupełnianie",
"gap_fill_downloaded_suffix": "pobrano",
"gap_fill_failed_suffix": "niepowodzenie",
"latest": "NAJNOWSZE",
"loading": {
"fetching_account_state": "Pobieranie stanu konta..."
@@ -305,6 +312,7 @@
"active_session": "Aktywna sesja",
"errors": "Błędy",
"folders": "Skrzynki",
"gap_fill": "Uzupełnianie braków",
"history": "Historia"
}
},
@@ -345,6 +353,7 @@
"sinceRelativeDesc": "Pobieraj tylko wiadomości z ostatniego okresu (np. ostatnie 3 miesiące). Data początkowa automatycznie przesuwa się w czasie.",
"sinceRelativeValue": "Pobierz e-maile z ostatnich",
"startDownload": "Uruchom pobieranie",
"startDownloadConfirmDesc": "Rozpocząć pobieranie dla wybranych kont?",
"state": "Status",
"status": "Status",
"step": "Krok {{index}}",

View File

@@ -240,6 +240,7 @@
"imapPortPlaceholder": "Ex: 993",
"imapProxy": "Usar proxy SOCKS5 para conexão IMAP.",
"incDownload": "Intervalo",
"incSync": "Intervalo de sinc.",
"lastSync": "Última Sincronização",
"leaveEmptyToKeepExisting": "Deixe vazio para manter a senha existente. Insira a nova senha apenas se estiver atualizando.",
"leaveEmptyToKeepPassword": "Deixe vazio para manter a senha atual",
@@ -271,6 +272,7 @@
"refreshToken": "Token de Atualização",
"refreshTokenCopiedToClipboard": "Token de atualização copiado para a área de transferência",
"relative": "Relativo",
"runGapFill": "Verifique novos e-mails e preencha automaticamente e-mails antigos ausentes",
"runningState": {
"account": {
"id": "ID da conta"
@@ -283,10 +285,15 @@
"no_active_download": "Nenhum download em andamento",
"no_errors_current": "Sem erros na sessão atual",
"no_errors_session": "Sem erros nesta sessão",
"no_gap_fill_folders": "Nenhuma pasta sincronizando mensagens ausentes",
"no_gap_fill_history": "Nenhum histórico de backfill",
"no_global_errors": "Sem erros globais",
"no_history": "Sem histórico"
},
"folders": "caixas de correio",
"gap_fill_active": "Tarefa de backfill em execução",
"gap_fill_downloaded_suffix": "baixados",
"gap_fill_failed_suffix": "falharam",
"latest": "RECENTE",
"loading": {
"fetching_account_state": "Obtendo estado da conta..."
@@ -305,6 +312,7 @@
"active_session": "Sessão ativa",
"errors": "Erros",
"folders": "Caixas de correio",
"gap_fill": "Preenchimento de lacunas",
"history": "Histórico"
}
},
@@ -345,6 +353,7 @@
"sinceRelativeDesc": "Baixar apenas e-mails do período recente (ex: últimos 3 meses). A data de início avança automaticamente com o tempo.",
"sinceRelativeValue": "Baixar e-mails dos últimos",
"startDownload": "Iniciar download",
"startDownloadConfirmDesc": "Iniciar download para as contas selecionadas?",
"state": "Estado",
"status": "Status",
"step": "Passo {{index}}",

View File

@@ -240,6 +240,7 @@
"imapPortPlaceholder": "например, 993",
"imapProxy": "Использовать SOCKS5 прокси для соединений IMAP.",
"incDownload": "Интервал",
"incSync": "Интервал синхр.",
"lastSync": "Посл. синхр.",
"leaveEmptyToKeepExisting": "Оставьте пустым, чтобы сохранить существующий пароль, или введите новый для обновления.",
"leaveEmptyToKeepPassword": "Оставьте пустым, чтобы сохранить текущий пароль",
@@ -271,6 +272,7 @@
"refreshToken": "Refresh Token",
"refreshTokenCopiedToClipboard": "Refresh token скопирован в буфер обмена",
"relative": "Относительная",
"runGapFill": "Проверять новые письма и автоматически восполнять старые недостающие сообщения",
"runningState": {
"account": {
"id": "ID аккаунта"
@@ -283,10 +285,15 @@
"no_active_download": "Нет активных загрузок",
"no_errors_current": "Нет ошибок в текущей сессии",
"no_errors_session": "Нет ошибок в этой сессии",
"no_gap_fill_folders": "Нет папок для загрузки недостающих писем",
"no_gap_fill_history": "История восполнения отсутствует",
"no_global_errors": "Нет глобальных ошибок",
"no_history": "Нет истории"
},
"folders": "почтовые ящики",
"gap_fill_active": "Выполняемый запуск восполнения",
"gap_fill_downloaded_suffix": "скачано",
"gap_fill_failed_suffix": "ошибок",
"latest": "ПОСЛЕДНЕЕ",
"loading": {
"fetching_account_state": "Загрузка состояния аккаунта..."
@@ -305,6 +312,7 @@
"active_session": "Активная сессия",
"errors": "Ошибки",
"folders": "Почтовые ящики",
"gap_fill": "Восполнение пропусков",
"history": "История"
}
},
@@ -345,6 +353,7 @@
"sinceRelativeDesc": "Скачивать письма только за последний период (напр., за 3 месяца). Дата начала автоматически сдвигается со временем.",
"sinceRelativeValue": "Скачать письма за последние",
"startDownload": "Запустить загрузку",
"startDownloadConfirmDesc": "Начать загрузку почты для выбранных аккаунтов?",
"state": "Состояние",
"status": "Статус",
"step": "Шаг {{index}}",

View File

@@ -240,6 +240,7 @@
"imapPortPlaceholder": "t.ex. 993",
"imapProxy": "Använd en SOCKS5-proxy för IMAP-anslutningar.",
"incDownload": "Intervall",
"incSync": "Synkintervall",
"lastSync": "Senaste synk",
"leaveEmptyToKeepExisting": "Lämna tomt för att behålla det befintliga lösenordet, eller ange ett nytt för att uppdatera det.",
"leaveEmptyToKeepPassword": "Lämna tomt för att behålla nuvarande lösenord",
@@ -271,6 +272,7 @@
"refreshToken": "Uppdateringstoken",
"refreshTokenCopiedToClipboard": "Uppdateringstoken kopierad till urklipp",
"relative": "Relativ",
"runGapFill": "Kontrollera nya e-postmeddelanden och komplettera automatiskt äldre saknade meddelanden",
"runningState": {
"account": {
"id": "Kontots ID"
@@ -283,10 +285,15 @@
"no_active_download": "Ingen aktiv nedladdning",
"no_errors_current": "Inga fel i aktuell session",
"no_errors_session": "Inga fel i denna session",
"no_gap_fill_folders": "Inga mappar för synkning av saknade meddelanden",
"no_gap_fill_history": "Ingen kompletteringshistorik",
"no_global_errors": "Inga globala fel",
"no_history": "Ingen historik"
},
"folders": "postlådor",
"gap_fill_active": "Pågående kompletteringskörning",
"gap_fill_downloaded_suffix": "nedladdade",
"gap_fill_failed_suffix": "misslyckades",
"latest": "SENASTE",
"loading": {
"fetching_account_state": "Hämtar kontostatus..."
@@ -305,6 +312,7 @@
"active_session": "Aktiv session",
"errors": "Fel",
"folders": "Postlådor",
"gap_fill": "Komplettera saknade meddelanden",
"history": "Historik"
}
},
@@ -345,6 +353,7 @@
"sinceRelativeDesc": "Ladda endast ner e-postmeddelanden från den senaste perioden (t.ex. senaste 3 månaderna). Startdatumet flyttas automatiskt framåt.",
"sinceRelativeValue": "Ladda ner e-post från de senaste",
"startDownload": "Starta hämtning",
"startDownloadConfirmDesc": "Starta nedladdning för valda konton?",
"state": "Tillstånd",
"status": "Status",
"step": "Steg {{index}}",

View File

@@ -240,6 +240,7 @@
"imapPortPlaceholder": "例如993",
"imapProxy": "使用 SOCKS5 代理進行 IMAP 連線。",
"incDownload": "間隔",
"incSync": "同步間隔",
"lastSync": "上次同步",
"leaveEmptyToKeepExisting": "保留現有密碼請留空。若要更新,請輸入新密碼。",
"leaveEmptyToKeepPassword": "保留現有密碼請留空",
@@ -271,6 +272,7 @@
"refreshToken": "更新權杖 (Refresh Token)",
"refreshTokenCopiedToClipboard": "更新權杖已複製到剪貼簿",
"relative": "相對",
"runGapFill": "檢查並下載新郵件,同時自動補全本地缺失的歷史舊郵件",
"runningState": {
"account": {
"id": "帳戶 ID"
@@ -283,10 +285,15 @@
"no_active_download": "目前沒有下載任務",
"no_errors_current": "目前任務沒有錯誤",
"no_errors_session": "此任務沒有錯誤",
"no_gap_fill_folders": "暫無需要補充缺失郵件的郵件資料夾",
"no_gap_fill_history": "暫無查漏補缺歷史記錄",
"no_global_errors": "沒有全域錯誤",
"no_history": "沒有歷史記錄"
},
"folders": "個郵件夾",
"gap_fill_active": "執行中的查漏補缺任務",
"gap_fill_downloaded_suffix": "已下載",
"gap_fill_failed_suffix": "失敗",
"latest": "最新",
"loading": {
"fetching_account_state": "正在取得帳戶狀態..."
@@ -305,6 +312,7 @@
"active_session": "目前任務",
"errors": "錯誤",
"folders": "郵件夾",
"gap_fill": "缺失郵件查漏補缺",
"history": "歷史記錄"
}
},
@@ -345,6 +353,7 @@
"sinceRelativeDesc": "僅下載最近一段時間(如過去 3 個月)的郵件。開始日期會隨時間推移自動向前滾動。",
"sinceRelativeValue": "下載最近一段時期的郵件",
"startDownload": "啟動下載",
"startDownloadConfirmDesc": "確定開始下載所選帳號的郵件資料?",
"state": "狀態",
"status": "狀態",
"step": "步驟 {{index}}",

View File

@@ -242,6 +242,7 @@
"imapPortPlaceholder": "例如:993",
"imapProxy": "为 IMAP 连接使用 SOCKS5 代理。",
"incDownload": "间隔",
"incSync": "同步间隔",
"lastSync": "最后同步",
"leaveEmptyToKeepExisting": "留空以保持现有密码,或输入新密码进行更新。",
"leaveEmptyToKeepPassword": "留空以保持当前密码",
@@ -273,6 +274,7 @@
"refreshToken": "刷新令牌",
"refreshTokenCopiedToClipboard": "刷新令牌已复制到剪贴板",
"relative": "相对",
"runGapFill": "检查并下载新邮件,同时自动补全本地缺失的历史老邮件",
"runningState": {
"account": {
"id": "账户 ID"
@@ -285,10 +287,15 @@
"no_active_download": "当前没有下载任务",
"no_errors_current": "当前任务无错误",
"no_errors_session": "该任务无错误",
"no_gap_fill_folders": "暂无需要补充缺失邮件的邮件夹",
"no_gap_fill_history": "暂无查漏补缺历史记录",
"no_global_errors": "暂无全局错误",
"no_history": "暂无历史记录"
},
"folders": "个文件夹",
"gap_fill_active": "运行中的查漏补缺任务",
"gap_fill_downloaded_suffix": "已下载",
"gap_fill_failed_suffix": "失败",
"latest": "最新",
"loading": {
"fetching_account_state": "正在获取账户状态..."
@@ -307,6 +314,7 @@
"active_session": "当前任务",
"errors": "错误",
"folders": "邮件夹",
"gap_fill": "缺失邮件查漏补缺",
"history": "历史记录"
}
},
@@ -355,6 +363,7 @@
"sinceRelativeDesc": "仅下载最近一段时间(如过去 3 个月)的邮件。开始日期会随时间推移自动向后滚动。",
"sinceRelativeValue": "下载最近一段时期的邮件",
"startDownload": "启动下载",
"startDownloadConfirmDesc": "确定开始下载所选账号的邮件数据?",
"state": "状态",
"status": "状态",
"step": "步骤 {{index}}",