mirror of
https://github.com/rustmailer/bichon.git
synced 2026-08-03 07:48:34 +02:00
feat: add manual download and cancel download for email accounts
This commit is contained in:
Generated
+1
@@ -529,6 +529,7 @@ dependencies = [
|
||||
"ring",
|
||||
"rustls",
|
||||
"rustls-pki-types",
|
||||
"scopeguard",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"snafu",
|
||||
|
||||
@@ -70,3 +70,4 @@ tracing-log.workspace = true
|
||||
tokio-util.workspace = true
|
||||
whichlang = "0.1.1"
|
||||
deunicode = "1.6.2"
|
||||
scopeguard = "1.2.0"
|
||||
|
||||
@@ -318,7 +318,7 @@ impl AccountV4 {
|
||||
|
||||
if matches!(cloned.account_type, AccountType::IMAP) {
|
||||
DOWNLOAD_CONTROLLER
|
||||
.trigger_start(cloned.id, cloned.email.clone())
|
||||
.trigger_schedule(cloned.id, cloned.email.clone())
|
||||
.await;
|
||||
}
|
||||
Ok(cloned)
|
||||
|
||||
+25
-19
@@ -17,6 +17,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{
|
||||
utc_now,
|
||||
{
|
||||
account::{
|
||||
migration::AccountModel,
|
||||
@@ -24,7 +25,6 @@ use crate::{
|
||||
},
|
||||
error::BichonResult,
|
||||
},
|
||||
utc_now,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
@@ -34,27 +34,33 @@ pub enum DownloadTask {
|
||||
Idle,
|
||||
}
|
||||
|
||||
pub async fn decide_next_download_task(account: &AccountModel) -> BichonResult<DownloadTask> {
|
||||
Ok(match DownloadState::get(account.id).await? {
|
||||
Some(state) => {
|
||||
let should_trigger = should_trigger_next_download(
|
||||
state.last_trigger_at,
|
||||
state.last_finished_at.unwrap_or(0),
|
||||
account.download_interval_min.unwrap(),
|
||||
);
|
||||
|
||||
if should_trigger {
|
||||
DownloadState::start_new_session(account.id, TriggerType::Scheduled).await?;
|
||||
DownloadTask::TraceFetch
|
||||
} else {
|
||||
DownloadTask::Idle
|
||||
}
|
||||
}
|
||||
pub async fn decide_next_download_task(
|
||||
account: &AccountModel,
|
||||
trigger_type: TriggerType,
|
||||
) -> BichonResult<DownloadTask> {
|
||||
let state = match DownloadState::get(account.id).await? {
|
||||
None => {
|
||||
DownloadState::init(account.id).await?;
|
||||
DownloadTask::FullFetch
|
||||
return Ok(DownloadTask::FullFetch);
|
||||
}
|
||||
})
|
||||
Some(s) => s,
|
||||
};
|
||||
|
||||
let should_start = match trigger_type {
|
||||
TriggerType::Manual => true,
|
||||
TriggerType::Scheduled => should_trigger_next_download(
|
||||
state.last_trigger_at,
|
||||
state.last_finished_at.unwrap_or(0),
|
||||
account.download_interval_min.unwrap_or(60),
|
||||
),
|
||||
};
|
||||
|
||||
if should_start {
|
||||
DownloadState::start_new_session(account.id, trigger_type).await?;
|
||||
Ok(DownloadTask::TraceFetch)
|
||||
} else {
|
||||
Ok(DownloadTask::Idle)
|
||||
}
|
||||
}
|
||||
|
||||
fn should_trigger_next_download(
|
||||
|
||||
+8
-7
@@ -19,33 +19,34 @@
|
||||
use crate::{
|
||||
account::{
|
||||
migration::{AccountModel, AccountType},
|
||||
state::{DownloadState, DownloadStatus},
|
||||
state::{DownloadState, DownloadStatus, TriggerType},
|
||||
},
|
||||
cache::imap::{mailbox::MailBox, download::flow::FetchDirection},
|
||||
cache::imap::{download::flow::FetchDirection, mailbox::MailBox},
|
||||
error::BichonResult,
|
||||
imap::executor::ImapExecutor,
|
||||
};
|
||||
use download_folders::get_download_folders;
|
||||
use download_type::{decide_next_download_task, DownloadTask};
|
||||
use flow::reconcile_mailboxes;
|
||||
use rebuild::{rebuild_cache, rebuild_cache_by_date};
|
||||
use std::time::Instant;
|
||||
use download_folders::get_download_folders;
|
||||
use download_type::{decide_next_download_task, DownloadTask};
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tracing::{debug, warn};
|
||||
|
||||
pub mod flow;
|
||||
pub mod rebuild;
|
||||
pub mod download_folders;
|
||||
pub mod download_type;
|
||||
pub mod flow;
|
||||
pub mod rebuild;
|
||||
|
||||
pub async fn process_imap_download(
|
||||
account: &AccountModel,
|
||||
token: CancellationToken,
|
||||
trigger_type: TriggerType,
|
||||
) -> BichonResult<()> {
|
||||
assert_eq!(account.account_type, AccountType::IMAP);
|
||||
let start_time = Instant::now();
|
||||
let account_id = account.id;
|
||||
let download_task = decide_next_download_task(account).await?;
|
||||
let download_task = decide_next_download_task(account, trigger_type).await?;
|
||||
if matches!(download_task, DownloadTask::Idle) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Vendored
+121
-8
@@ -17,37 +17,56 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::account::entity::AuthType;
|
||||
use crate::account::state::DownloadState;
|
||||
use crate::account::state::{DownloadState, TriggerType};
|
||||
use crate::cache::imap::download::process_imap_download;
|
||||
use crate::common::periodic::{PeriodicTask, TaskHandle};
|
||||
use crate::error::code::ErrorCode;
|
||||
use crate::oauth2::token::OAuth2AccessToken;
|
||||
use crate::utc_now;
|
||||
use crate::{account::migration::AccountModel, error::BichonResult};
|
||||
use std::collections::HashMap;
|
||||
use crate::{raise_error, utc_now};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::sync::atomic::{AtomicI64, Ordering};
|
||||
use std::{sync::LazyLock, time::Duration};
|
||||
use tokio::sync::Mutex;
|
||||
use tokio::task::JoinHandle;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tracing::{error, info, warn};
|
||||
|
||||
static _DESCRIPTION: &str = "This task periodically synchronizes mailbox data for a specified account, ensuring that all local data is up-to-date.";
|
||||
const TASK_INTERVAL: Duration = Duration::from_secs(10);
|
||||
pub static SYNC_TASKS: LazyLock<AccountSyncTask> = LazyLock::new(AccountSyncTask::new);
|
||||
pub static SYNC_TASKS: LazyLock<AccountDownTask> = LazyLock::new(AccountDownTask::new);
|
||||
static LAST_WARN_TIME: AtomicI64 = AtomicI64::new(0);
|
||||
const WARN_INTERVAL_MS: i64 = 600_000;
|
||||
|
||||
pub struct AccountSyncTask {
|
||||
pub struct AccountDownTask {
|
||||
tasks: Mutex<Option<HashMap<u64, (TaskHandle, CancellationToken)>>>,
|
||||
manual_tasks: Mutex<HashMap<u64, (JoinHandle<()>, CancellationToken)>>,
|
||||
busy_accounts: Mutex<HashSet<u64>>,
|
||||
}
|
||||
|
||||
impl AccountSyncTask {
|
||||
impl AccountDownTask {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
tasks: Mutex::new(Some(HashMap::new())),
|
||||
manual_tasks: Mutex::new(HashMap::new()),
|
||||
busy_accounts: Mutex::new(HashSet::new()),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn start_account_download_task(&self, account_id: u64, email: String) {
|
||||
async fn set_busy(&self, account_id: u64, is_busy: bool) {
|
||||
let mut guard = self.busy_accounts.lock().await;
|
||||
if is_busy {
|
||||
guard.insert(account_id);
|
||||
} else {
|
||||
guard.remove(&account_id);
|
||||
}
|
||||
}
|
||||
|
||||
async fn is_busy(&self, account_id: u64) -> bool {
|
||||
self.busy_accounts.lock().await.contains(&account_id)
|
||||
}
|
||||
|
||||
pub async fn start_download_task(&self, account_id: u64, email: String) {
|
||||
let task_name = format!("account-download-task-{}-{}", account_id, &email);
|
||||
let periodic_task = PeriodicTask::new(&task_name);
|
||||
|
||||
@@ -58,6 +77,28 @@ impl AccountSyncTask {
|
||||
let account_id = param.unwrap();
|
||||
let internal_token = task_token.clone();
|
||||
Box::pin(async move {
|
||||
if SYNC_TASKS.is_manual_running(account_id).await {
|
||||
info!(
|
||||
"Account {}: Scheduled task skipped (Manual task is running).",
|
||||
account_id
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if SYNC_TASKS.is_busy(account_id).await {
|
||||
warn!(
|
||||
"Account {}: Scheduled task skipped (Previous sync still active).",
|
||||
account_id
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
SYNC_TASKS.set_busy(account_id, true).await;
|
||||
let _busy_guard = scopeguard::guard(account_id, |id| {
|
||||
tokio::spawn(async move {
|
||||
SYNC_TASKS.set_busy(id, false).await;
|
||||
});
|
||||
});
|
||||
let account = AccountModel::async_get(account_id).await.ok();
|
||||
match account {
|
||||
Some(account) => {
|
||||
@@ -82,7 +123,13 @@ impl AccountSyncTask {
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Err(e) = process_imap_download(&account, internal_token).await {
|
||||
if let Err(e) = process_imap_download(
|
||||
&account,
|
||||
internal_token,
|
||||
TriggerType::Scheduled,
|
||||
)
|
||||
.await
|
||||
{
|
||||
DownloadState::append_session_error(
|
||||
account.id,
|
||||
format!("error in account download task: {:#?}", e),
|
||||
@@ -150,4 +197,70 @@ impl AccountSyncTask {
|
||||
info!("Shutdown: All download tasks processed.");
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn start_manual_task(&self, account_id: u64) -> BichonResult<()> {
|
||||
{
|
||||
if self.is_manual_running(account_id).await {
|
||||
return Err(raise_error!(
|
||||
"Manual task already running.".into(),
|
||||
ErrorCode::Forbidden
|
||||
));
|
||||
}
|
||||
if self.is_busy(account_id).await {
|
||||
return Err(raise_error!(
|
||||
"The background synchronization is currently active. Please try again in a few seconds.".into(),
|
||||
ErrorCode::Forbidden
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let cancel_token = CancellationToken::new();
|
||||
let token_clone = cancel_token.clone();
|
||||
let handle = tokio::spawn(async move {
|
||||
SYNC_TASKS.set_busy(account_id, true).await;
|
||||
let _cleanup = scopeguard::guard(account_id, |id| {
|
||||
tokio::spawn(async move {
|
||||
SYNC_TASKS.set_busy(id, false).await;
|
||||
let mut guard = SYNC_TASKS.manual_tasks.lock().await;
|
||||
guard.remove(&id);
|
||||
});
|
||||
});
|
||||
if token_clone.is_cancelled() {
|
||||
return;
|
||||
}
|
||||
let account = match AccountModel::async_get(account_id).await {
|
||||
Ok(acc) => acc,
|
||||
Err(e) => {
|
||||
error!("Failed to fetch account {}: {:?}", account_id, e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(e) = process_imap_download(&account, token_clone, TriggerType::Manual).await
|
||||
{
|
||||
error!("Manual download failed for {}: {:?}", account_id, e);
|
||||
let error_msg = format!("error in account download task: {:#?}", e);
|
||||
let _ = DownloadState::append_session_error(account.id, error_msg).await;
|
||||
}
|
||||
});
|
||||
{
|
||||
let mut guard = self.manual_tasks.lock().await;
|
||||
guard.insert(account_id, (handle, cancel_token));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn cancel_manual_task(&self, account_id: u64) {
|
||||
let mut guard = self.manual_tasks.lock().await;
|
||||
if let Some((handle, token)) = guard.remove(&account_id) {
|
||||
token.cancel();
|
||||
let _ = handle.await;
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn is_manual_running(&self, account_id: u64) -> bool {
|
||||
let guard = self.manual_tasks.lock().await;
|
||||
guard.contains_key(&account_id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,11 +35,10 @@ impl DownloadController {
|
||||
tokio::spawn(async move {
|
||||
while let Some((account_id, email)) = rx.recv().await {
|
||||
match Self::start_download(account_id, email.clone()).await {
|
||||
Ok(Some(_)) => {}
|
||||
Ok(None) => {}
|
||||
Ok(_) => {}
|
||||
Err(err) => {
|
||||
error!(
|
||||
"Failed to prepare and start download of account {{{}-{}}}, error: {:#?}",
|
||||
"Failed to prepare and start scheduled download of account {{{}-{}}}, error: {:#?}",
|
||||
&account_id, &email, err
|
||||
);
|
||||
}
|
||||
@@ -51,7 +50,7 @@ impl DownloadController {
|
||||
}
|
||||
|
||||
/// Trigger synchronization for a specific account
|
||||
pub async fn trigger_start(&self, account_id: u64, email: String) {
|
||||
pub async fn trigger_schedule(&self, account_id: u64, email: String) {
|
||||
if let Err(e) = self.channel.send((account_id, email)).await {
|
||||
error!(
|
||||
"Failed to trigger download for account={{{}}}, error: {:?}",
|
||||
@@ -60,13 +59,13 @@ impl DownloadController {
|
||||
}
|
||||
}
|
||||
|
||||
async fn start_download(account_id: u64, email: String) -> BichonResult<Option<()>> {
|
||||
async fn start_download(account_id: u64, email: String) -> BichonResult<()> {
|
||||
info!(
|
||||
"Account download starting for account: {}-{}.",
|
||||
account_id, email
|
||||
);
|
||||
SYNC_TASKS.start_account_download_task(account_id, email).await;
|
||||
SYNC_TASKS.start_download_task(account_id, email).await;
|
||||
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||
Ok(Some(()))
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ pub struct BichonContext {
|
||||
|
||||
impl Initialize for BichonContext {
|
||||
async fn initialize() -> BichonResult<()> {
|
||||
BICHON_CONTEXT.start_account_syncers().await
|
||||
BICHON_CONTEXT.start_account_downloader().await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ impl BichonContext {
|
||||
utc_now!() - self.start_at
|
||||
}
|
||||
|
||||
pub async fn start_account_syncers(&self) -> BichonResult<()> {
|
||||
pub async fn start_account_downloader(&self) -> BichonResult<()> {
|
||||
let accounts = AccountModel::list_all().await?;
|
||||
let active_accounts: Vec<AccountModel> = accounts
|
||||
.into_iter()
|
||||
@@ -66,7 +66,7 @@ impl BichonContext {
|
||||
);
|
||||
for account in active_accounts {
|
||||
DOWNLOAD_CONTROLLER
|
||||
.trigger_start(account.id, account.email)
|
||||
.trigger_schedule(account.id, account.email)
|
||||
.await
|
||||
}
|
||||
|
||||
|
||||
@@ -20,14 +20,17 @@ use crate::common::auth::WrappedContext;
|
||||
use crate::rest::api::ApiTags;
|
||||
use crate::rest::ApiResult;
|
||||
use bichon_core::account::grant::BatchAccountRoleRequest;
|
||||
use bichon_core::account::migration::AccountModel;
|
||||
use bichon_core::account::migration::{AccountModel, AccountType};
|
||||
use bichon_core::account::payload::{
|
||||
filter_accessible_accounts, AccountCreateRequest, AccountUpdateRequest, MinimalAccount,
|
||||
};
|
||||
use bichon_core::account::state::DownloadState;
|
||||
use bichon_core::account::stats::AccountStats;
|
||||
use bichon_core::account::view::AccountResp;
|
||||
use bichon_core::cache::imap::task::SYNC_TASKS;
|
||||
use bichon_core::common::paginated::{paginate_vec, DataPage};
|
||||
use bichon_core::error::code::ErrorCode;
|
||||
use bichon_core::raise_error;
|
||||
use bichon_core::store::tantivy::envelope::ENVELOPE_MANAGER;
|
||||
use bichon_core::users::permissions::Permission;
|
||||
use bichon_core::users::UserModel;
|
||||
@@ -204,6 +207,68 @@ impl AccountApi {
|
||||
Ok(Json(state))
|
||||
}
|
||||
|
||||
/// Start a manual download task for an account
|
||||
#[oai(
|
||||
path = "/accounts/:account_id/start-download",
|
||||
method = "post",
|
||||
operation_id = "accounts_start_download"
|
||||
)]
|
||||
async fn accounts_start_download(
|
||||
&self,
|
||||
/// The account ID to start download for
|
||||
account_id: Path<u64>,
|
||||
context: WrappedContext,
|
||||
) -> ApiResult<()> {
|
||||
let account_id = account_id.0;
|
||||
let account = AccountModel::check_account_exists(account_id).await?;
|
||||
if !matches!(account.account_type, AccountType::IMAP) {
|
||||
return Err(raise_error!(
|
||||
format!("Manual download is not supported for '{:#?}' accounts. Only IMAP accounts are supported.", account.account_type),
|
||||
ErrorCode::InvalidParameter
|
||||
))?;
|
||||
}
|
||||
context
|
||||
.require_permission(Some(account_id), Permission::ACCOUNT_MANAGE)
|
||||
.await?;
|
||||
SYNC_TASKS.start_manual_task(account_id).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Cancel a running manual download task
|
||||
#[oai(
|
||||
path = "/accounts/:account_id/cancel-download",
|
||||
method = "post",
|
||||
operation_id = "accounts_cancel_download"
|
||||
)]
|
||||
async fn accounts_cancel_download(
|
||||
&self,
|
||||
/// The account ID to cancel download for
|
||||
account_id: Path<u64>,
|
||||
context: WrappedContext,
|
||||
) -> ApiResult<()> {
|
||||
let account_id = account_id.0;
|
||||
let account = AccountModel::check_account_exists(account_id).await?;
|
||||
|
||||
if !matches!(account.account_type, AccountType::IMAP) {
|
||||
return Err(raise_error!(
|
||||
"This operation is only supported for IMAP accounts.".into(),
|
||||
ErrorCode::InvalidParameter
|
||||
))?;
|
||||
}
|
||||
context
|
||||
.require_permission(Some(account_id), Permission::ACCOUNT_MANAGE)
|
||||
.await?;
|
||||
|
||||
if !SYNC_TASKS.is_manual_running(account_id).await {
|
||||
return Err(raise_error!(
|
||||
"No running manual task found for this account.".into(),
|
||||
ErrorCode::ResourceNotFound
|
||||
))?;
|
||||
}
|
||||
SYNC_TASKS.cancel_manual_task(account_id).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the stats of an account
|
||||
#[oai(
|
||||
path = "/accounts/:account_id/stats",
|
||||
|
||||
@@ -170,6 +170,17 @@ export const remove_account = async (account_id: number) => {
|
||||
return response.data;
|
||||
};
|
||||
|
||||
|
||||
export const start_account_download = async (account_id: number) => {
|
||||
const response = await axiosInstance.post(`api/v1/accounts/${account_id}/start-download`);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const cancel_account_download = async (account_id: number) => {
|
||||
const response = await axiosInstance.post(`api/v1/accounts/${account_id}/cancel-download`);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export interface AutoConfigResult {
|
||||
imap: ServerConfig;
|
||||
oauth2?: OAuth2Config;
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
import { DotsHorizontalIcon } from '@radix-ui/react-icons'
|
||||
import { Row } from '@tanstack/react-table'
|
||||
import { IconEdit, IconShieldLock, IconTrash } from '@tabler/icons-react'
|
||||
import { IconEdit, IconPlayerPlay, IconPlayerStop, IconShieldLock, IconTrash } from '@tabler/icons-react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
DropdownMenu,
|
||||
@@ -33,7 +33,8 @@ import { useAccountContext } from '../context'
|
||||
import { Mailbox, MessageSquareMore } from 'lucide-react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useCurrentUser } from '@/hooks/use-current-user'
|
||||
import { AccountModel } from '@/api/account/api'
|
||||
import { AccountModel, cancel_account_download, start_account_download } from '@/api/account/api'
|
||||
import { toast } from '@/hooks/use-toast'
|
||||
|
||||
interface DataTableRowActionsProps {
|
||||
row: Row<AccountModel>
|
||||
@@ -55,6 +56,35 @@ export function DataTableRowActions({ row }: DataTableRowActionsProps) {
|
||||
(account_type === 'IMAP' && hasPermission) ||
|
||||
(account_type === 'IMAP' && hasReadPermission);
|
||||
|
||||
const showDownload = 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
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const handleCancelDownload = async () => {
|
||||
try {
|
||||
await cancel_account_download(row.original.id);
|
||||
toast({ title: t('accounts.downloadCancelled') });
|
||||
} catch (error: any) {
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: t('accounts.cancelFailed'),
|
||||
description: error.response?.data?.message || error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<DropdownMenu modal={false}>
|
||||
@@ -68,6 +98,26 @@ export function DataTableRowActions({ row }: DataTableRowActionsProps) {
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align='end' className='w-[220px]'>
|
||||
|
||||
{showDownload && (
|
||||
<DropdownMenuItem onClick={handleStartDownload}>
|
||||
{t('accounts.startDownload')}
|
||||
<DropdownMenuShortcut>
|
||||
<IconPlayerPlay size={16} />
|
||||
</DropdownMenuShortcut>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
|
||||
|
||||
{showDownload && (
|
||||
<DropdownMenuItem onClick={handleCancelDownload}>
|
||||
{t('accounts.cancelDownload')}
|
||||
<DropdownMenuShortcut>
|
||||
<IconPlayerStop size={16} />
|
||||
</DropdownMenuShortcut>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{showDownload && <DropdownMenuSeparator />}
|
||||
{hasPermission && <DropdownMenuItem
|
||||
onClick={() => {
|
||||
setCurrentRow(row.original)
|
||||
|
||||
@@ -298,7 +298,7 @@ export function RunningStateDialog({ currentRow, open, onOpenChange }: Props) {
|
||||
{format(new Date(h.start_time), 'yyyy-MM-dd HH:mm:ss')}
|
||||
</div>
|
||||
<StatusBadge status={h.status} />
|
||||
<div className="hidden xs:block"><TriggerBadge trigger={h.trigger} /></div>
|
||||
<div className="xs:block"><TriggerBadge trigger={h.trigger} /></div>
|
||||
</div>
|
||||
<span className="text-[10px] font-bold text-muted-foreground bg-muted px-2 py-0.5 rounded-full self-start sm:self-auto">
|
||||
{Object.keys(h.folder_details).length} {t('accounts.runningState.folders')}
|
||||
|
||||
@@ -104,6 +104,8 @@
|
||||
"autoConfiguring": "جارٍ التكوين التلقائي...",
|
||||
"beforeRelative": "تنزيل رسائل البريد الإلكتروني القديمة فقط",
|
||||
"beforeRelativeValue": "تنزيل رسائل البريد الإلكتروني قبل {{value}} {{unit}} مضت",
|
||||
"cancelDownload": "إلغاء التنزيل",
|
||||
"cancelFailed": "فشل إلغاء مهمة التنزيل",
|
||||
"capabilities": "الإمكانيات",
|
||||
"chooseAuthMethod": "اختر طريقة المصادقة لـ IMAP.",
|
||||
"chooseEncryptionMethod": "اختر طريقة التشفير لـ IMAP.",
|
||||
@@ -127,10 +129,13 @@
|
||||
"downloadAll": "تنزيل جميع رسائل البريد الإلكتروني",
|
||||
"downloadBatchSize": "حجم دفعة التنزيل",
|
||||
"downloadBatchSizeDescription": "عدد الرسائل التي يتم جلبها لكل طلب IMAP",
|
||||
"downloadCancelled": "تم إلغاء المهمة",
|
||||
"downloadFailed": "فشل بدء مهمة التنزيل",
|
||||
"downloadInterval": "دورة التنزيل (بالدقائق)",
|
||||
"downloadIntervalPlaceholder": "أدخل الدقائق",
|
||||
"downloadScope": "استراتيجية التنزيل",
|
||||
"downloadScopeDescription": "اختر رسائل البريد الإلكتروني التي يجب فهرستها وتنزيلها.",
|
||||
"downloadStarted": "بدأت مهمة التنزيل",
|
||||
"duration": "المدة",
|
||||
"edit": "تعديل",
|
||||
"email": "البريد الإلكتروني",
|
||||
@@ -255,6 +260,7 @@
|
||||
"sinceFixed": "منذ تاريخ محدد",
|
||||
"sinceRelative": "تنزيل رسائل البريد الإلكتروني الأخيرة فقط",
|
||||
"sinceRelativeValue": "تنزيل رسائل البريد الإلكتروني من آخر",
|
||||
"startDownload": "بدء التنزيل",
|
||||
"state": "الحالة",
|
||||
"status": "الحالة",
|
||||
"step": "الخطوة {{index}}",
|
||||
|
||||
@@ -104,6 +104,8 @@
|
||||
"autoConfiguring": "Konfigurerer automatisk...",
|
||||
"beforeRelative": "Download kun gamle e-mails",
|
||||
"beforeRelativeValue": "Download e-mails før {{value}} {{unit}} siden",
|
||||
"cancelDownload": "Annuller download",
|
||||
"cancelFailed": "Kunne ikke annullere download-opgave",
|
||||
"capabilities": "Funktioner",
|
||||
"chooseAuthMethod": "Vælg godkendelsesmetode til IMAP.",
|
||||
"chooseEncryptionMethod": "Vælg krypteringsmetode til IMAP.",
|
||||
@@ -127,10 +129,13 @@
|
||||
"downloadAll": "Download alle e-mails",
|
||||
"downloadBatchSize": "Download batchstørrelse",
|
||||
"downloadBatchSizeDescription": "Antal beskeder hentet pr. IMAP-anmodning",
|
||||
"downloadCancelled": "Opgave annulleret",
|
||||
"downloadFailed": "Kunne ikke starte download-opgave",
|
||||
"downloadInterval": "Downloadinterval (minutter)",
|
||||
"downloadIntervalPlaceholder": "Indtast minutter",
|
||||
"downloadScope": "Downloadstrategi",
|
||||
"downloadScopeDescription": "Vælg hvilke e-mails der skal indekseres og downloades.",
|
||||
"downloadStarted": "Download-opgave startet",
|
||||
"duration": "Varighed",
|
||||
"edit": "Rediger",
|
||||
"email": "E-mail",
|
||||
@@ -255,6 +260,7 @@
|
||||
"sinceFixed": "Siden specifik dato",
|
||||
"sinceRelative": "Download kun seneste e-mails",
|
||||
"sinceRelativeValue": "Download e-mails fra de sidste",
|
||||
"startDownload": "Start download",
|
||||
"state": "Tilstand",
|
||||
"status": "Status",
|
||||
"step": "Trin {{index}}",
|
||||
|
||||
@@ -104,6 +104,8 @@
|
||||
"autoConfiguring": "Automatische Konfiguration läuft...",
|
||||
"beforeRelative": "Nur alte E-Mails herunterladen",
|
||||
"beforeRelativeValue": "E-Mails von vor {{value}} {{unit}} herunterladen",
|
||||
"cancelDownload": "Download abbrechen",
|
||||
"cancelFailed": "Download-Aufgabe konnte nicht abgebrochen werden",
|
||||
"capabilities": "Funktionen",
|
||||
"chooseAuthMethod": "Wählen Sie die Authentifizierungsmethode für IMAP.",
|
||||
"chooseEncryptionMethod": "Wählen Sie die Verschlüsselungsmethode für IMAP.",
|
||||
@@ -127,10 +129,13 @@
|
||||
"downloadAll": "Alle E-Mails herunterladen",
|
||||
"downloadBatchSize": "Download-Batch-Größe",
|
||||
"downloadBatchSizeDescription": "Anzahl der pro IMAP-Anfrage abgerufenen Nachrichten",
|
||||
"downloadCancelled": "Aufgabe abgebrochen",
|
||||
"downloadFailed": "Download-Aufgabe konnte nicht gestartet werden",
|
||||
"downloadInterval": "Download-Intervall (Minuten)",
|
||||
"downloadIntervalPlaceholder": "Minuten eingeben",
|
||||
"downloadScope": "Download-Strategie",
|
||||
"downloadScopeDescription": "Wählen Sie aus, welche E-Mails indiziert und heruntergeladen werden sollen.",
|
||||
"downloadStarted": "Download-Aufgabe gestartet",
|
||||
"duration": "Dauer",
|
||||
"edit": "Bearbeiten",
|
||||
"email": "E-Mail",
|
||||
@@ -255,6 +260,7 @@
|
||||
"sinceFixed": "Seit einem bestimmten Datum",
|
||||
"sinceRelative": "Nur aktuelle E-Mails herunterladen",
|
||||
"sinceRelativeValue": "E-Mails der letzten Zeit herunterladen",
|
||||
"startDownload": "Download starten",
|
||||
"state": "Zustand",
|
||||
"status": "Status",
|
||||
"step": "Schritt {{index}}",
|
||||
|
||||
@@ -104,6 +104,8 @@
|
||||
"autoConfiguring": "Auto-configuring...",
|
||||
"beforeRelative": "Download Old Emails Only",
|
||||
"beforeRelativeValue": "Download emails before {{value}} {{unit}} ago",
|
||||
"cancelDownload": "Cancel download",
|
||||
"cancelFailed": "Failed to cancel download task",
|
||||
"capabilities": "Capabilities",
|
||||
"chooseAuthMethod": "Choose the authentication method for IMAP.",
|
||||
"chooseEncryptionMethod": "Choose the encryption method for IMAP.",
|
||||
@@ -127,10 +129,13 @@
|
||||
"downloadAll": "Download All Emails",
|
||||
"downloadBatchSize": "Download batch size",
|
||||
"downloadBatchSizeDescription": "Number of messages fetched per IMAP request",
|
||||
"downloadCancelled": "Task cancelled",
|
||||
"downloadFailed": "Failed to start download task",
|
||||
"downloadInterval": "Download Interval (minutes)",
|
||||
"downloadIntervalPlaceholder": "Enter minutes",
|
||||
"downloadScope": "Download Strategy",
|
||||
"downloadScopeDescription": "Choose which emails should be indexed and downloaded.",
|
||||
"downloadStarted": "Download task started",
|
||||
"duration": "Duration",
|
||||
"edit": "Edit",
|
||||
"email": "Email",
|
||||
@@ -255,6 +260,7 @@
|
||||
"sinceFixed": "Since Specific Date",
|
||||
"sinceRelative": "Download Recent Emails Only",
|
||||
"sinceRelativeValue": "Download emails from the last",
|
||||
"startDownload": "Start download",
|
||||
"state": "State",
|
||||
"status": "Status",
|
||||
"step": "Step {{index}}",
|
||||
|
||||
@@ -104,6 +104,8 @@
|
||||
"autoConfiguring": "Autoconfigurando...",
|
||||
"beforeRelative": "Descargar solo correos antiguos",
|
||||
"beforeRelativeValue": "Descargar correos de hace {{value}} {{unit}}",
|
||||
"cancelDownload": "Cancelar descarga",
|
||||
"cancelFailed": "Error al cancelar la tarea de descarga",
|
||||
"capabilities": "Capacidades",
|
||||
"chooseAuthMethod": "Elige el método de autenticación a utilizar para IMAP.",
|
||||
"chooseEncryptionMethod": "Elige el método de cifrado a utilizar para IMAP.",
|
||||
@@ -127,10 +129,13 @@
|
||||
"downloadAll": "Descargar todos los correos",
|
||||
"downloadBatchSize": "Tamaño del lote de descarga",
|
||||
"downloadBatchSizeDescription": "Número de mensajes obtenidos por solicitud IMAP",
|
||||
"downloadCancelled": "Tarea cancelada",
|
||||
"downloadFailed": "Error al iniciar la tarea de descarga",
|
||||
"downloadInterval": "Intervalo de descarga (minutos)",
|
||||
"downloadIntervalPlaceholder": "Ingresa los minutos",
|
||||
"downloadScope": "Estrategia de descarga",
|
||||
"downloadScopeDescription": "Elija qué correos electrónicos deben indexarse y descargarse.",
|
||||
"downloadStarted": "Tarea de descarga iniciada",
|
||||
"duration": "Duración",
|
||||
"edit": "Editar",
|
||||
"email": "Correo electrónico",
|
||||
@@ -255,6 +260,7 @@
|
||||
"sinceFixed": "Desde una fecha específica",
|
||||
"sinceRelative": "Descargar solo correos recientes",
|
||||
"sinceRelativeValue": "Descargar correos de los últimos",
|
||||
"startDownload": "Iniciar descarga",
|
||||
"state": "Estado",
|
||||
"status": "Estado",
|
||||
"step": "Paso {{index}}",
|
||||
|
||||
@@ -104,6 +104,8 @@
|
||||
"autoConfiguring": "Automaattinen määritys...",
|
||||
"beforeRelative": "Lataa vain vanhat sähköpostit",
|
||||
"beforeRelativeValue": "Lataa sähköpostit {{value}} {{unit}} sitten",
|
||||
"cancelDownload": "Peruuta lataus",
|
||||
"cancelFailed": "Lataustehtävän peruuttaminen epäonnistui",
|
||||
"capabilities": "Ominaisuudet",
|
||||
"chooseAuthMethod": "Valitse IMAP:lle käytettävä todennusmenetelmä.",
|
||||
"chooseEncryptionMethod": "Valitse IMAP:lle käytettävä salausmenetelmä.",
|
||||
@@ -127,10 +129,13 @@
|
||||
"downloadAll": "Lataa kaikki sähköpostit",
|
||||
"downloadBatchSize": "Latauserän koko",
|
||||
"downloadBatchSizeDescription": "IMAP-pyyntöä kohden noudettujen viestien määrä",
|
||||
"downloadCancelled": "Tehtävä peruutettu",
|
||||
"downloadFailed": "Lataustehtävän aloittaminen epäonnistui",
|
||||
"downloadInterval": "Latausväli (minuuttia)",
|
||||
"downloadIntervalPlaceholder": "Syötä minuutit",
|
||||
"downloadScope": "Latausstrategia",
|
||||
"downloadScopeDescription": "Valitse, mitkä sähköpostit indeksoidaan ja ladataan.",
|
||||
"downloadStarted": "Lataustehtävä aloitettu",
|
||||
"duration": "Kesto",
|
||||
"edit": "Muokkaa",
|
||||
"email": "Sähköposti",
|
||||
@@ -255,6 +260,7 @@
|
||||
"sinceFixed": "Tietystä päivämäärästä lähtien",
|
||||
"sinceRelative": "Lataa vain viimeisimmät sähköpostit",
|
||||
"sinceRelativeValue": "Lataa sähköpostit viimeisimmiltä",
|
||||
"startDownload": "Aloita lataus",
|
||||
"state": "Tila",
|
||||
"status": "Tila",
|
||||
"step": "Vaihe {{index}}",
|
||||
|
||||
@@ -104,6 +104,8 @@
|
||||
"autoConfiguring": "Configuration automatique...",
|
||||
"beforeRelative": "Télécharger uniquement les anciens e-mails",
|
||||
"beforeRelativeValue": "Télécharger les e-mails d'il y a {{value}} {{unit}}",
|
||||
"cancelDownload": "Annuler le téléchargement",
|
||||
"cancelFailed": "Échec de l'annulation de la tâche de téléchargement",
|
||||
"capabilities": "Capacités",
|
||||
"chooseAuthMethod": "Choisissez la méthode d'authentification à utiliser pour IMAP.",
|
||||
"chooseEncryptionMethod": "Choisissez la méthode de chiffrement à utiliser pour IMAP.",
|
||||
@@ -127,10 +129,13 @@
|
||||
"downloadAll": "Télécharger tous les e-mails",
|
||||
"downloadBatchSize": "Taille du lot de téléchargement",
|
||||
"downloadBatchSizeDescription": "Nombre de messages récupérés par requête IMAP",
|
||||
"downloadCancelled": "Tâche annulée",
|
||||
"downloadFailed": "Échec du lancement de la tâche de téléchargement",
|
||||
"downloadInterval": "Intervalle de téléchargement (minutes)",
|
||||
"downloadIntervalPlaceholder": "Entrer les minutes",
|
||||
"downloadScope": "Stratégie de téléchargement",
|
||||
"downloadScopeDescription": "Choisissez les e-mails à indexer et à télécharger.",
|
||||
"downloadStarted": "Tâche de téléchargement lancée",
|
||||
"duration": "Durée",
|
||||
"edit": "Modifier",
|
||||
"email": "E-mail",
|
||||
@@ -255,6 +260,7 @@
|
||||
"sinceFixed": "Depuis une date spécifique",
|
||||
"sinceRelative": "Télécharger uniquement les e-mails récents",
|
||||
"sinceRelativeValue": "Télécharger les e-mails des derniers",
|
||||
"startDownload": "Lancer le téléchargement",
|
||||
"state": "État",
|
||||
"status": "Statut",
|
||||
"step": "Étape {{index}}",
|
||||
|
||||
@@ -104,6 +104,8 @@
|
||||
"autoConfiguring": "Configurazione automatica...",
|
||||
"beforeRelative": "Scarica solo le vecchie email",
|
||||
"beforeRelativeValue": "Scarica email da {{value}} {{unit}} fa",
|
||||
"cancelDownload": "Annulla download",
|
||||
"cancelFailed": "Annullamento attività di download non riuscito",
|
||||
"capabilities": "Capacità",
|
||||
"chooseAuthMethod": "Scegli il metodo di autenticazione per IMAP.",
|
||||
"chooseEncryptionMethod": "Scegli il metodo di crittografia per IMAP.",
|
||||
@@ -127,10 +129,13 @@
|
||||
"downloadAll": "Scarica tutte le email",
|
||||
"downloadBatchSize": "Dimensione del lotto di download",
|
||||
"downloadBatchSizeDescription": "Numero di messaggi recuperati per richiesta IMAP",
|
||||
"downloadCancelled": "Attività annullata",
|
||||
"downloadFailed": "Avvio attività di download non riuscito",
|
||||
"downloadInterval": "Intervallo di download (minuti)",
|
||||
"downloadIntervalPlaceholder": "Inserisci i minuti",
|
||||
"downloadScope": "Strategia di download",
|
||||
"downloadScopeDescription": "Scegli quali email indicizzare e scaricare.",
|
||||
"downloadStarted": "Attività di download avviata",
|
||||
"duration": "Durata",
|
||||
"edit": "Modifica",
|
||||
"email": "Email",
|
||||
@@ -255,6 +260,7 @@
|
||||
"sinceFixed": "Da una data specifica",
|
||||
"sinceRelative": "Scarica solo le email recenti",
|
||||
"sinceRelativeValue": "Scarica email degli ultimi",
|
||||
"startDownload": "Avvia download",
|
||||
"state": "Stato",
|
||||
"status": "Stato",
|
||||
"step": "Passo {{index}}",
|
||||
|
||||
@@ -104,6 +104,8 @@
|
||||
"autoConfiguring": "自動設定中...",
|
||||
"beforeRelative": "古いメールのみダウンロード",
|
||||
"beforeRelativeValue": "{{value}} {{unit}}前より古いメールをダウンロード",
|
||||
"cancelDownload": "ダウンロードをキャンセル",
|
||||
"cancelFailed": "ダウンロードタスクのキャンセルに失敗しました",
|
||||
"capabilities": "機能",
|
||||
"chooseAuthMethod": "IMAPの認証方式を選択してください。",
|
||||
"chooseEncryptionMethod": "IMAPの暗号化方式を選択してください。",
|
||||
@@ -127,10 +129,13 @@
|
||||
"downloadAll": "すべてのメールをダウンロード",
|
||||
"downloadBatchSize": "ダウンロードバッチサイズ",
|
||||
"downloadBatchSizeDescription": "IMAPリクエストごとに取得されるメッセージ数",
|
||||
"downloadCancelled": "タスクをキャンセルしました",
|
||||
"downloadFailed": "ダウンロードタスクの起動に失敗しました",
|
||||
"downloadInterval": "ダウンロード間隔 (分)",
|
||||
"downloadIntervalPlaceholder": "分を入力してください",
|
||||
"downloadScope": "ダウンロード戦略",
|
||||
"downloadScopeDescription": "インデックスとダウンロードの対象となるメールを選択してください。",
|
||||
"downloadStarted": "ダウンロードタスクを開始しました",
|
||||
"duration": "期間",
|
||||
"edit": "編集",
|
||||
"email": "メールアドレス",
|
||||
@@ -255,6 +260,7 @@
|
||||
"sinceFixed": "指定した日付以降",
|
||||
"sinceRelative": "最近のメールのみダウンロード",
|
||||
"sinceRelativeValue": "直近の期間のメールをダウンロード",
|
||||
"startDownload": "ダウンロードを開始",
|
||||
"state": "状態",
|
||||
"status": "ステータス",
|
||||
"step": "ステップ {{index}}",
|
||||
|
||||
@@ -104,6 +104,8 @@
|
||||
"autoConfiguring": "자동 구성 중...",
|
||||
"beforeRelative": "이전 이메일만 다운로드",
|
||||
"beforeRelativeValue": "{{value}} {{unit}} 전의 이메일 다운로드",
|
||||
"cancelDownload": "다운로드 취소",
|
||||
"cancelFailed": "다운로드 작업 취소 실패",
|
||||
"capabilities": "기능",
|
||||
"chooseAuthMethod": "IMAP에 사용할 인증 방법을 선택하십시오.",
|
||||
"chooseEncryptionMethod": "IMAP에 사용할 암호화 방법을 선택하십시오.",
|
||||
@@ -127,10 +129,13 @@
|
||||
"downloadAll": "모든 이메일 다운로드",
|
||||
"downloadBatchSize": "다운로드 일괄 처리 크기",
|
||||
"downloadBatchSizeDescription": "IMAP 요청당 가져온 메시지 수",
|
||||
"downloadCancelled": "작업 취소됨",
|
||||
"downloadFailed": "다운로드 작업 시작 실패",
|
||||
"downloadInterval": "다운로드 주기 (분)",
|
||||
"downloadIntervalPlaceholder": "분 단위 입력",
|
||||
"downloadScope": "다운로드 전략",
|
||||
"downloadScopeDescription": "색인화 및 다운로드할 이메일을 선택하십시오.",
|
||||
"downloadStarted": "다운로드 작업 시작됨",
|
||||
"duration": "기간",
|
||||
"edit": "편집",
|
||||
"email": "이메일",
|
||||
@@ -255,6 +260,7 @@
|
||||
"sinceFixed": "특정 날짜 이후",
|
||||
"sinceRelative": "최근 이메일만 다운로드",
|
||||
"sinceRelativeValue": "최근 기간의 이메일 다운로드",
|
||||
"startDownload": "다운로드 시작",
|
||||
"state": "상태",
|
||||
"status": "상태",
|
||||
"step": "단계 {{index}}",
|
||||
|
||||
@@ -104,6 +104,8 @@
|
||||
"autoConfiguring": "Automatisch configureren...",
|
||||
"beforeRelative": "Download alleen oude e-mails",
|
||||
"beforeRelativeValue": "Download e-mails van {{value}} {{unit}} geleden",
|
||||
"cancelDownload": "Download annuleren",
|
||||
"cancelFailed": "Downloadtaak annuleren mislukt",
|
||||
"capabilities": "Mogelijkheden",
|
||||
"chooseAuthMethod": "Kies de authenticatiemethode voor IMAP.",
|
||||
"chooseEncryptionMethod": "Kies de versleutelingsmethode voor IMAP.",
|
||||
@@ -127,10 +129,13 @@
|
||||
"downloadAll": "Download alle e-mails",
|
||||
"downloadBatchSize": "Download batchgrootte",
|
||||
"downloadBatchSizeDescription": "Aantal berichten opgehaald per IMAP-verzoek",
|
||||
"downloadCancelled": "Taak geannuleerd",
|
||||
"downloadFailed": "Downloadtaak starten mislukt",
|
||||
"downloadInterval": "Download-interval (minuten)",
|
||||
"downloadIntervalPlaceholder": "Voer minuten in",
|
||||
"downloadScope": "Downloadstrategie",
|
||||
"downloadScopeDescription": "Kies welke e-mails moeten worden geïndexeerd en gedownload.",
|
||||
"downloadStarted": "Downloadtaak gestart",
|
||||
"duration": "Duur",
|
||||
"edit": "Bewerken",
|
||||
"email": "E-mail",
|
||||
@@ -255,6 +260,7 @@
|
||||
"sinceFixed": "Sinds een specifieke datum",
|
||||
"sinceRelative": "Download alleen recente e-mails",
|
||||
"sinceRelativeValue": "Download e-mails van de laatste",
|
||||
"startDownload": "Download starten",
|
||||
"state": "Status",
|
||||
"status": "Status",
|
||||
"step": "Stap {{index}}",
|
||||
|
||||
@@ -104,6 +104,8 @@
|
||||
"autoConfiguring": "Konfigurerer automatisk...",
|
||||
"beforeRelative": "Last ned kun gamle e-poster",
|
||||
"beforeRelativeValue": "Last ned e-poster fra {{value}} {{unit}} siden",
|
||||
"cancelDownload": "Avbryt nedlasting",
|
||||
"cancelFailed": "Kunne ikke avbryte nedlastingsoppgave",
|
||||
"capabilities": "Funksjoner",
|
||||
"chooseAuthMethod": "Velg autentiseringsmetoden for IMAP.",
|
||||
"chooseEncryptionMethod": "Velg krypteringsmetoden for IMAP.",
|
||||
@@ -127,10 +129,13 @@
|
||||
"downloadAll": "Last ned alle e-poster",
|
||||
"downloadBatchSize": "Nedlastingsbatchstørrelse",
|
||||
"downloadBatchSizeDescription": "Antall meldinger hentet per IMAP-forespørsel",
|
||||
"downloadCancelled": "Oppgave avbrutt",
|
||||
"downloadFailed": "Kunne ikke starte nedlastingsoppgave",
|
||||
"downloadInterval": "Nedlastingsintervall (minutter)",
|
||||
"downloadIntervalPlaceholder": "Skriv inn minutter",
|
||||
"downloadScope": "Nedlastingsstrategi",
|
||||
"downloadScopeDescription": "Velg hvilke e-poster som skal indekseres og lastes ned.",
|
||||
"downloadStarted": "Nedlastingsoppgave startet",
|
||||
"duration": "Varighet",
|
||||
"edit": "Rediger",
|
||||
"email": "E-post",
|
||||
@@ -255,6 +260,7 @@
|
||||
"sinceFixed": "Siden spesifikk dato",
|
||||
"sinceRelative": "Last ned kun nylige e-poster",
|
||||
"sinceRelativeValue": "Last ned e-poster fra de siste",
|
||||
"startDownload": "Start nedlasting",
|
||||
"state": "Tilstand",
|
||||
"status": "Status",
|
||||
"step": "Trinn {{index}}",
|
||||
|
||||
@@ -104,6 +104,8 @@
|
||||
"autoConfiguring": "Auto konfiguracja...",
|
||||
"beforeRelative": "Pobierz tylko stare e-maile",
|
||||
"beforeRelativeValue": "Pobierz e-maile sprzed {{value}} {{unit}}",
|
||||
"cancelDownload": "Anuluj pobieranie",
|
||||
"cancelFailed": "Nie udało się anulować zadania pobierania",
|
||||
"capabilities": "Możliwości",
|
||||
"chooseAuthMethod": "Wybierz metodę uwierzytelniania IMAP.",
|
||||
"chooseEncryptionMethod": "Wybierz metodę szyfrowania dla IMAP.",
|
||||
@@ -127,10 +129,13 @@
|
||||
"downloadAll": "Pobierz wszystkie e-maile",
|
||||
"downloadBatchSize": "Rozmiar partii pobierania",
|
||||
"downloadBatchSizeDescription": "Liczba wiadomości pobieranych na żądanie IMAP",
|
||||
"downloadCancelled": "Zadanie anulowane",
|
||||
"downloadFailed": "Nie udało się uruchomić zadania pobierania",
|
||||
"downloadInterval": "Cykl pobierania (minuty)",
|
||||
"downloadIntervalPlaceholder": "Wprowadź minuty",
|
||||
"downloadScope": "Strategia pobierania",
|
||||
"downloadScopeDescription": "Wybierz, które wiadomości e-mail mają być indeksowane i pobierane.",
|
||||
"downloadStarted": "Uruchomiono zadanie pobierania",
|
||||
"duration": "Czas trwania",
|
||||
"edit": "Edytuj",
|
||||
"email": "Email",
|
||||
@@ -255,6 +260,7 @@
|
||||
"sinceFixed": "Od konkretnej daty",
|
||||
"sinceRelative": "Pobierz tylko ostatnie e-maile",
|
||||
"sinceRelativeValue": "Pobierz e-maile z ostatnich",
|
||||
"startDownload": "Uruchom pobieranie",
|
||||
"state": "Status",
|
||||
"status": "Status",
|
||||
"step": "Krok {{index}}",
|
||||
|
||||
@@ -104,6 +104,8 @@
|
||||
"autoConfiguring": "Configurando Automaticamente...",
|
||||
"beforeRelative": "Baixar apenas e-mails antigos",
|
||||
"beforeRelativeValue": "Baixar e-mails de {{value}} {{unit}} atrás",
|
||||
"cancelDownload": "Cancelar download",
|
||||
"cancelFailed": "Falha ao cancelar tarefa de download",
|
||||
"capabilities": "Capacidades",
|
||||
"chooseAuthMethod": "Por favor, escolha o método de autenticação para IMAP.",
|
||||
"chooseEncryptionMethod": "Por favor, escolha o método de criptografia para IMAP.",
|
||||
@@ -127,10 +129,13 @@
|
||||
"downloadAll": "Baixar todos os e-mails",
|
||||
"downloadBatchSize": "Tamanho do lote de download",
|
||||
"downloadBatchSizeDescription": "Número de mensagens recuperadas por solicitação IMAP",
|
||||
"downloadCancelled": "Tarefa cancelada",
|
||||
"downloadFailed": "Falha ao iniciar tarefa de download",
|
||||
"downloadInterval": "Intervalo de download (minutos)",
|
||||
"downloadIntervalPlaceholder": "Insira os minutos",
|
||||
"downloadScope": "Estratégia de download",
|
||||
"downloadScopeDescription": "Escolha quais e-mails devem ser indexados e baixados.",
|
||||
"downloadStarted": "Tarefa de download iniciada",
|
||||
"duration": "Duração",
|
||||
"edit": "Editar",
|
||||
"email": "Email",
|
||||
@@ -255,6 +260,7 @@
|
||||
"sinceFixed": "Desde uma data específica",
|
||||
"sinceRelative": "Baixar apenas e-mails recentes",
|
||||
"sinceRelativeValue": "Baixar e-mails dos últimos",
|
||||
"startDownload": "Iniciar download",
|
||||
"state": "Estado",
|
||||
"status": "Status",
|
||||
"step": "Passo {{index}}",
|
||||
|
||||
@@ -104,6 +104,8 @@
|
||||
"autoConfiguring": "Автонастройка...",
|
||||
"beforeRelative": "Скачать только старые письма",
|
||||
"beforeRelativeValue": "Скачать письма за {{value}} {{unit}} назад",
|
||||
"cancelDownload": "Отменить загрузку",
|
||||
"cancelFailed": "Не удалось отменить задачу загрузки",
|
||||
"capabilities": "Возможности",
|
||||
"chooseAuthMethod": "Выберите метод авторизации для IMAP.",
|
||||
"chooseEncryptionMethod": "Выберите метод шифрования для IMAP.",
|
||||
@@ -127,10 +129,13 @@
|
||||
"downloadAll": "Скачать все письма",
|
||||
"downloadBatchSize": "Размер пакета загрузки",
|
||||
"downloadBatchSizeDescription": "Количество сообщений, получаемых за один IMAP-запрос",
|
||||
"downloadCancelled": "Задача отменена",
|
||||
"downloadFailed": "Не удалось запустить задачу загрузки",
|
||||
"downloadInterval": "Интервал загрузки (мин.)",
|
||||
"downloadIntervalPlaceholder": "Введите минуты",
|
||||
"downloadScope": "Стратегия загрузки",
|
||||
"downloadScopeDescription": "Выберите, какие электронные письма должны быть проиндексированы и скачаны.",
|
||||
"downloadStarted": "Задача загрузки запущена",
|
||||
"duration": "Продолжительность",
|
||||
"edit": "Ред.",
|
||||
"email": "Email",
|
||||
@@ -255,6 +260,7 @@
|
||||
"sinceFixed": "С определенной даты",
|
||||
"sinceRelative": "Скачать только недавние письма",
|
||||
"sinceRelativeValue": "Скачать письма за последние",
|
||||
"startDownload": "Запустить загрузку",
|
||||
"state": "Состояние",
|
||||
"status": "Статус",
|
||||
"step": "Шаг {{index}}",
|
||||
|
||||
@@ -104,6 +104,8 @@
|
||||
"autoConfiguring": "Konfigurerar automatiskt...",
|
||||
"beforeRelative": "Ladda ner endast gamla e-postmeddelanden",
|
||||
"beforeRelativeValue": "Ladda ner e-post från {{value}} {{unit}} sedan",
|
||||
"cancelDownload": "Avbryt hämtning",
|
||||
"cancelFailed": "Misslyckades med att avbryta hämtningsuppgift",
|
||||
"capabilities": "Funktioner",
|
||||
"chooseAuthMethod": "Välj autentiseringsmetod för IMAP.",
|
||||
"chooseEncryptionMethod": "Välj krypteringsmetod för IMAP.",
|
||||
@@ -127,10 +129,13 @@
|
||||
"downloadAll": "Ladda ner alla e-postmeddelanden",
|
||||
"downloadBatchSize": "Batchstorlek för nedladdning",
|
||||
"downloadBatchSizeDescription": "Antal meddelanden som hämtas per IMAP-begäran",
|
||||
"downloadCancelled": "Uppgiften avbruten",
|
||||
"downloadFailed": "Misslyckades med att starta hämtningsuppgift",
|
||||
"downloadInterval": "Nedladdningsintervall (minuter)",
|
||||
"downloadIntervalPlaceholder": "Ange minuter",
|
||||
"downloadScope": "Nedladdningsstrategi",
|
||||
"downloadScopeDescription": "Välj vilka e-postmeddelanden som ska indexeras och laddas ner.",
|
||||
"downloadStarted": "Hämtningsuppgift startad",
|
||||
"duration": "Varaktighet",
|
||||
"edit": "Redigera",
|
||||
"email": "E-post",
|
||||
@@ -255,6 +260,7 @@
|
||||
"sinceFixed": "Sedan specifikt datum",
|
||||
"sinceRelative": "Ladda ner endast senaste e-postmeddelanden",
|
||||
"sinceRelativeValue": "Ladda ner e-post från de senaste",
|
||||
"startDownload": "Starta hämtning",
|
||||
"state": "Tillstånd",
|
||||
"status": "Status",
|
||||
"step": "Steg {{index}}",
|
||||
|
||||
@@ -104,6 +104,8 @@
|
||||
"autoConfiguring": "正在自動設定...",
|
||||
"beforeRelative": "僅下載舊郵件",
|
||||
"beforeRelativeValue": "下載 {{value}} {{unit}} 之前的郵件",
|
||||
"cancelDownload": "取消下載",
|
||||
"cancelFailed": "取消下載任務失敗",
|
||||
"capabilities": "功能",
|
||||
"chooseAuthMethod": "請選擇 IMAP 的驗證方法。",
|
||||
"chooseEncryptionMethod": "請選擇 IMAP 的加密方法。",
|
||||
@@ -127,10 +129,13 @@
|
||||
"downloadAll": "下載所有郵件",
|
||||
"downloadBatchSize": "下載批量大小",
|
||||
"downloadBatchSizeDescription": "每個 IMAP 請求獲取的郵件數量",
|
||||
"downloadCancelled": "下載任務已取消",
|
||||
"downloadFailed": "啟動下載任務失敗",
|
||||
"downloadInterval": "下載週期 (分鐘)",
|
||||
"downloadIntervalPlaceholder": "請輸入分鐘數",
|
||||
"downloadScope": "下載策略",
|
||||
"downloadScopeDescription": "選擇哪些郵件應被索引和下載。",
|
||||
"downloadStarted": "下載任務已啟動",
|
||||
"duration": "時長",
|
||||
"edit": "編輯",
|
||||
"email": "電子郵件",
|
||||
@@ -255,6 +260,7 @@
|
||||
"sinceFixed": "自特定日期起",
|
||||
"sinceRelative": "僅下載最近郵件",
|
||||
"sinceRelativeValue": "下載最近一段時期的郵件",
|
||||
"startDownload": "啟動下載",
|
||||
"state": "狀態",
|
||||
"status": "狀態",
|
||||
"step": "步驟 {{index}}",
|
||||
|
||||
@@ -104,6 +104,8 @@
|
||||
"autoConfiguring": "自动配置中...",
|
||||
"beforeRelative": "仅下载旧邮件",
|
||||
"beforeRelativeValue": "下载 {{value}} {{unit}} 之前的邮件",
|
||||
"cancelDownload": "取消下载",
|
||||
"cancelFailed": "取消下载任务失败",
|
||||
"capabilities": "功能",
|
||||
"chooseAuthMethod": "选择 IMAP 的认证方法。",
|
||||
"chooseEncryptionMethod": "选择 IMAP 的加密方法。",
|
||||
@@ -127,10 +129,13 @@
|
||||
"downloadAll": "下载所有邮件",
|
||||
"downloadBatchSize": "下载批量大小",
|
||||
"downloadBatchSizeDescription": "每个 IMAP 请求获取的邮件数量",
|
||||
"downloadCancelled": "下载任务已取消",
|
||||
"downloadFailed": "启动下载任务失败",
|
||||
"downloadInterval": "下载周期 (分钟)",
|
||||
"downloadIntervalPlaceholder": "请输入分钟数",
|
||||
"downloadScope": "下载策略",
|
||||
"downloadScopeDescription": "选择哪些邮件应被索引和下载。",
|
||||
"downloadStarted": "下载任务已启动",
|
||||
"duration": "时长",
|
||||
"edit": "编辑",
|
||||
"email": "邮箱",
|
||||
@@ -255,6 +260,7 @@
|
||||
"sinceFixed": "自特定日期起",
|
||||
"sinceRelative": "仅下载最近邮件",
|
||||
"sinceRelativeValue": "下载最近一段时期的邮件",
|
||||
"startDownload": "启动下载",
|
||||
"state": "状态",
|
||||
"status": "状态",
|
||||
"step": "步骤 {{index}}",
|
||||
|
||||
Reference in New Issue
Block a user