mirror of
https://github.com/rustmailer/bichon.git
synced 2026-08-03 07:48:34 +02:00
feat: detect legacy tantivy data layout and abort startup with migration hint
This commit is contained in:
@@ -46,6 +46,7 @@ use crate::{
|
|||||||
pub struct DashboardStats {
|
pub struct DashboardStats {
|
||||||
pub account_count: usize, // Number of accounts
|
pub account_count: usize, // Number of accounts
|
||||||
pub email_count: u64, // Total number of emails
|
pub email_count: u64, // Total number of emails
|
||||||
|
pub attachment_count: u64, // Total number of attachments
|
||||||
pub total_size_bytes: u64, // Total size of all emails (in bytes)
|
pub total_size_bytes: u64, // Total size of all emails (in bytes)
|
||||||
pub storage_usage_bytes: u64, // Actual storage used (in bytes)
|
pub storage_usage_bytes: u64, // Actual storage used (in bytes)
|
||||||
pub index_usage_bytes: u64, // Index storage size (in bytes)
|
pub index_usage_bytes: u64, // Index storage size (in bytes)
|
||||||
@@ -89,7 +90,7 @@ impl DashboardStats {
|
|||||||
};
|
};
|
||||||
|
|
||||||
stat.email_count = ENVELOPE_MANAGER.total_emails(&authorized_ids)?;
|
stat.email_count = ENVELOPE_MANAGER.total_emails(&authorized_ids)?;
|
||||||
|
stat.attachment_count = ATTACHMENT_MANAGER.total_attachments(&authorized_ids)?;
|
||||||
if has_all_accounts {
|
if has_all_accounts {
|
||||||
stat.storage_usage_bytes = get_total_size(&DATA_DIR_MANAGER.storage_dir)
|
stat.storage_usage_bytes = get_total_size(&DATA_DIR_MANAGER.storage_dir)
|
||||||
.map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?;
|
.map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?;
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ pub mod import;
|
|||||||
pub mod logger;
|
pub mod logger;
|
||||||
pub mod mailbox;
|
pub mod mailbox;
|
||||||
pub mod message;
|
pub mod message;
|
||||||
|
pub mod migrate;
|
||||||
pub mod oauth2;
|
pub mod oauth2;
|
||||||
pub mod settings;
|
pub mod settings;
|
||||||
pub mod store;
|
pub mod store;
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use crate::settings::cli::SETTINGS;
|
||||||
|
|
||||||
|
pub fn is_tantivy_index_dir(dir: &PathBuf) -> std::io::Result<bool> {
|
||||||
|
if !dir.exists() || !dir.is_dir() {
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
let tantivy_extensions = [".store", ".term", ".idx", ".fieldnorm", ".pos"];
|
||||||
|
let mut match_count = 0;
|
||||||
|
let mut has_meta_json = false;
|
||||||
|
|
||||||
|
for entry in std::fs::read_dir(dir)? {
|
||||||
|
let entry = entry?;
|
||||||
|
let file_name = entry.file_name();
|
||||||
|
let name = file_name.to_string_lossy();
|
||||||
|
|
||||||
|
if name == "meta.json" {
|
||||||
|
has_meta_json = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if tantivy_extensions.iter().any(|ext| name.ends_with(ext)) {
|
||||||
|
match_count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(has_meta_json && match_count >= 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_legacy_data_layout() -> std::io::Result<bool> {
|
||||||
|
let root_dir = PathBuf::from(&SETTINGS.bichon_root_dir);
|
||||||
|
let envelope_dir = if let Some(ref index_dir) = SETTINGS.bichon_index_dir {
|
||||||
|
PathBuf::from(index_dir)
|
||||||
|
} else {
|
||||||
|
root_dir.join("envelope")
|
||||||
|
};
|
||||||
|
|
||||||
|
let eml_dir = if let Some(ref data_dir) = SETTINGS.bichon_data_dir {
|
||||||
|
PathBuf::from(data_dir)
|
||||||
|
} else {
|
||||||
|
root_dir.join("eml")
|
||||||
|
};
|
||||||
|
|
||||||
|
let envelope_result = is_tantivy_index_dir(&envelope_dir)?;
|
||||||
|
let eml_result = is_tantivy_index_dir(&eml_dir)?;
|
||||||
|
Ok(envelope_result || eml_result)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_real_tantivy_dir() {
|
||||||
|
let path = PathBuf::from(r"D:\test-data\envelope");
|
||||||
|
let result = is_tantivy_index_dir(&path).unwrap();
|
||||||
|
println!("is tantivy index dir: {}", result);
|
||||||
|
assert!(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -266,6 +266,30 @@ impl IndexManager {
|
|||||||
Box::new(boolean_query)
|
Box::new(boolean_query)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn total_attachments(&self, accounts: &Option<HashSet<u64>>) -> BichonResult<u64> {
|
||||||
|
let searcher = self.create_searcher()?;
|
||||||
|
|
||||||
|
match accounts {
|
||||||
|
Some(ref ids) if !ids.is_empty() => {
|
||||||
|
let mut subqueries = Vec::new();
|
||||||
|
for &id in ids {
|
||||||
|
let term = Term::from_field_u64(SchemaTools::email_fields().f_account_id, id);
|
||||||
|
subqueries.push((
|
||||||
|
Occur::Should,
|
||||||
|
Box::new(TermQuery::new(term, IndexRecordOption::Basic)) as Box<dyn Query>,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let query = Box::new(BooleanQuery::new(subqueries)) as Box<dyn Query>;
|
||||||
|
let count = searcher
|
||||||
|
.search(&query, &Count)
|
||||||
|
.map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?;
|
||||||
|
Ok(count as u64)
|
||||||
|
}
|
||||||
|
Some(_) => Ok(0),
|
||||||
|
None => Ok(searcher.num_docs()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn filter_query(
|
fn filter_query(
|
||||||
&self,
|
&self,
|
||||||
accounts: Option<HashSet<u64>>,
|
accounts: Option<HashSet<u64>>,
|
||||||
@@ -358,7 +382,7 @@ impl IndexManager {
|
|||||||
if let Some(ref name) = filter.attachment_name {
|
if let Some(ref name) = filter.attachment_name {
|
||||||
let query_parser =
|
let query_parser =
|
||||||
QueryParser::for_index(&self.index, vec![f.f_name_text, f.f_name_exact]);
|
QueryParser::for_index(&self.index, vec![f.f_name_text, f.f_name_exact]);
|
||||||
|
|
||||||
let q = query_parser
|
let q = query_parser
|
||||||
.parse_query(name)
|
.parse_query(name)
|
||||||
.map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InvalidParameter))?;
|
.map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InvalidParameter))?;
|
||||||
|
|||||||
+31
-13
@@ -19,20 +19,20 @@
|
|||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
|
|
||||||
use bichon_core::{
|
use bichon_core::{
|
||||||
bichon_version, raise_error,
|
bichon_version,
|
||||||
{
|
cache::imap::task::SYNC_TASKS,
|
||||||
cache::imap::task::SYNC_TASKS,
|
common::rustls::BichonTls,
|
||||||
common::rustls::BichonTls,
|
context::{executors::BichonContext, Initialize},
|
||||||
context::{executors::BichonContext, Initialize},
|
error::{code::ErrorCode, BichonResult},
|
||||||
error::{code::ErrorCode, BichonResult},
|
logger,
|
||||||
logger,
|
migrate::is_legacy_data_layout,
|
||||||
settings::cli::SETTINGS,
|
raise_error,
|
||||||
store::{
|
settings::cli::SETTINGS,
|
||||||
storage::BLOB_MANAGER,
|
store::{
|
||||||
tantivy::{attachment::ATTACHMENT_MANAGER, envelope::ENVELOPE_MANAGER},
|
storage::BLOB_MANAGER,
|
||||||
},
|
tantivy::{attachment::ATTACHMENT_MANAGER, envelope::ENVELOPE_MANAGER},
|
||||||
tasks::PeriodicTasks,
|
|
||||||
},
|
},
|
||||||
|
tasks::PeriodicTasks,
|
||||||
};
|
};
|
||||||
use bichon_smtp::server::{start_smtp_server, SmtpServer};
|
use bichon_smtp::server::{start_smtp_server, SmtpServer};
|
||||||
use mimalloc::MiMalloc;
|
use mimalloc::MiMalloc;
|
||||||
@@ -69,6 +69,24 @@ async fn main() -> BichonResult<()> {
|
|||||||
info!("Git: [{}]", env!("GIT_HASH"));
|
info!("Git: [{}]", env!("GIT_HASH"));
|
||||||
info!("GitHub: https://github.com/rustmailer/bichon");
|
info!("GitHub: https://github.com/rustmailer/bichon");
|
||||||
|
|
||||||
|
match is_legacy_data_layout() {
|
||||||
|
Ok(true) => {
|
||||||
|
error!("Incompatible data format detected.");
|
||||||
|
error!("Your data was created by an older version of Bichon and must be migrated before use.");
|
||||||
|
error!("Please run: bichon-migrate");
|
||||||
|
error!("Documentation: https://github.com/rustmailer/bichon/wiki/migration");
|
||||||
|
return Err(raise_error!(
|
||||||
|
"Legacy data layout detected".into(),
|
||||||
|
ErrorCode::InternalError
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to check data layout: {:#?}", e);
|
||||||
|
return Err(raise_error!(format!("{:#?}", e), ErrorCode::InternalError));
|
||||||
|
}
|
||||||
|
Ok(false) => {}
|
||||||
|
}
|
||||||
|
|
||||||
if let Err(error) = initialize().await {
|
if let Err(error) = initialize().await {
|
||||||
eprintln!("{:?}", error);
|
eprintln!("{:?}", error);
|
||||||
return Err(error);
|
return Err(error);
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ export const get_notifications = async () => {
|
|||||||
export interface DashboardStats {
|
export interface DashboardStats {
|
||||||
account_count: number; // Number of accounts
|
account_count: number; // Number of accounts
|
||||||
email_count: number; // Total number of emails
|
email_count: number; // Total number of emails
|
||||||
|
attachment_count: number; // Total number of attachments
|
||||||
total_size_bytes: number; // Total size of all emails (in bytes)
|
total_size_bytes: number; // Total size of all emails (in bytes)
|
||||||
storage_usage_bytes: number; // Actual storage used (in bytes)
|
storage_usage_bytes: number; // Actual storage used (in bytes)
|
||||||
index_usage_bytes: number; // Index storage size (in bytes)
|
index_usage_bytes: number; // Index storage size (in bytes)
|
||||||
@@ -60,6 +61,7 @@ export interface DashboardStats {
|
|||||||
export const INITIAL_DASHBOARD_STATS: DashboardStats = {
|
export const INITIAL_DASHBOARD_STATS: DashboardStats = {
|
||||||
account_count: 0,
|
account_count: 0,
|
||||||
email_count: 0,
|
email_count: 0,
|
||||||
|
attachment_count: 0,
|
||||||
total_size_bytes: 0,
|
total_size_bytes: 0,
|
||||||
storage_usage_bytes: 0,
|
storage_usage_bytes: 0,
|
||||||
index_usage_bytes: 0,
|
index_usage_bytes: 0,
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import { AccountTable } from './components/table'
|
|||||||
import AccountProvider, {
|
import AccountProvider, {
|
||||||
type AccountDialogType,
|
type AccountDialogType,
|
||||||
} from './context'
|
} from './context'
|
||||||
import { MoreVertical, Plus } from 'lucide-react'
|
import { Plus } from 'lucide-react'
|
||||||
import Logo from '@/assets/logo.svg'
|
import Logo from '@/assets/logo.svg'
|
||||||
import { AccountDetailDrawer } from './components/account-detail'
|
import { AccountDetailDrawer } from './components/account-detail'
|
||||||
import { AccountModel, list_accounts } from '@/api/account/api'
|
import { AccountModel, list_accounts } from '@/api/account/api'
|
||||||
@@ -39,7 +39,6 @@ import { RunningStateDialog } from './components/running-state-dialog'
|
|||||||
import { FixedHeader } from '@/components/layout/fixed-header'
|
import { FixedHeader } from '@/components/layout/fixed-header'
|
||||||
import { DownloadFoldersDialog } from './components/download-folders'
|
import { DownloadFoldersDialog } from './components/download-folders'
|
||||||
import { NoSyncAccountDialog } from './components/nosync-dialog'
|
import { NoSyncAccountDialog } from './components/nosync-dialog'
|
||||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { AccountAccessAssignmentDialog } from './components/access-assignment-dialog'
|
import { AccountAccessAssignmentDialog } from './components/access-assignment-dialog'
|
||||||
import { useCurrentUser } from '@/hooks/use-current-user'
|
import { useCurrentUser } from '@/hooks/use-current-user'
|
||||||
|
|||||||
@@ -219,21 +219,15 @@ export default function MailArchiveDashboard() {
|
|||||||
<p className="text-xs text-muted-foreground">{t('dashboard.syncedLocally')}</p>
|
<p className="text-xs text-muted-foreground">{t('dashboard.syncedLocally')}</p>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Card className="md:col-span-2 lg:col-span-2">
|
<Card className="md:col-span-2 lg:col-span-2">
|
||||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||||
<CardTitle className="text-xs font-bold uppercase tracking-wider">{t('dashboard.systemVersion')}</CardTitle>
|
<CardTitle className="text-xs font-bold uppercase tracking-wider">{t('dashboard.totalAttachments')}</CardTitle>
|
||||||
|
<Mail className="h-4 w-4 text-muted-foreground" />
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className='text-xl font-bold truncate text-primary tracking-tighter'>
|
<div className="text-xl font-bold">{formatNumber(stats1.attachment_count)}</div>
|
||||||
{stats1.system_version ? (
|
<p className="text-xs text-muted-foreground">{t('dashboard.regularAttachments')}</p>
|
||||||
<a href={`https://github.com/rustmailer/bichon/releases/tag/${stats1.system_version}`} target="_blank" rel="noopener noreferrer" className="hover:underline">
|
|
||||||
{stats1.system_version}
|
|
||||||
</a>
|
|
||||||
) : 'N/A'}
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-1.5 mt-1">
|
|
||||||
<GithubIcon className="h-5 w-5 text-muted-foreground" />
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
<Card className="md:col-span-6 lg:col-span-6">
|
<Card className="md:col-span-6 lg:col-span-6">
|
||||||
@@ -558,7 +552,30 @@ export default function MailArchiveDashboard() {
|
|||||||
</Main>
|
</Main>
|
||||||
|
|
||||||
<div className="mt-auto p-6 text-center text-xs text-muted-foreground border-t">
|
<div className="mt-auto p-6 text-center text-xs text-muted-foreground border-t">
|
||||||
© 2025-2026 <a href="https://rustmailer.com" target="_blank" rel="noopener noreferrer" className="hover:underline">rustmailer.com</a> - Bichon Email Archiving Project
|
<p>
|
||||||
|
© 2025-2026{" "}
|
||||||
|
<a
|
||||||
|
href="https://github.com/rustmailer/bichon"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="hover:underline font-medium"
|
||||||
|
>
|
||||||
|
Bichon Email Archiving Project
|
||||||
|
</a>
|
||||||
|
{stats1.system_version && (
|
||||||
|
<>
|
||||||
|
<span className="mx-2 opacity-50">•</span>
|
||||||
|
<a
|
||||||
|
href={`https://github.com/rustmailer/bichon/releases/tag/${stats1.system_version}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="hover:underline font-mono"
|
||||||
|
>
|
||||||
|
v{stats1.system_version}
|
||||||
|
</a>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -484,6 +484,7 @@
|
|||||||
"noRecentActivity": "لا يوجد نشاط حديث",
|
"noRecentActivity": "لا يوجد نشاط حديث",
|
||||||
"noSendersData": "لا توجد بيانات للمرسلين",
|
"noSendersData": "لا توجد بيانات للمرسلين",
|
||||||
"noSubject": "(لا يوجد موضوع)",
|
"noSubject": "(لا يوجد موضوع)",
|
||||||
|
"regularAttachments": "المرفقات العادية",
|
||||||
"saved": "الموفرة",
|
"saved": "الموفرة",
|
||||||
"sender": "المرسل",
|
"sender": "المرسل",
|
||||||
"size": "الحجم",
|
"size": "الحجم",
|
||||||
@@ -497,6 +498,7 @@
|
|||||||
"top10LargestEmails": "أكبر 10 رسائل بريد إلكتروني",
|
"top10LargestEmails": "أكبر 10 رسائل بريد إلكتروني",
|
||||||
"top10Senders": "أفضل 10 مرسلين",
|
"top10Senders": "أفضل 10 مرسلين",
|
||||||
"topLists": "القوائم الأعلى",
|
"topLists": "القوائم الأعلى",
|
||||||
|
"totalAttachments": "إجمالي المرفقات",
|
||||||
"totalEmailSize": "الحجم الإجمالي لرسائل البريد الإلكتروني",
|
"totalEmailSize": "الحجم الإجمالي لرسائل البريد الإلكتروني",
|
||||||
"totalEmails": "إجمالي رسائل البريد الإلكتروني",
|
"totalEmails": "إجمالي رسائل البريد الإلكتروني",
|
||||||
"withAttachments": "مع مرفقات"
|
"withAttachments": "مع مرفقات"
|
||||||
|
|||||||
@@ -484,6 +484,7 @@
|
|||||||
"noRecentActivity": "Ingen nylig aktivitet",
|
"noRecentActivity": "Ingen nylig aktivitet",
|
||||||
"noSendersData": "Ingen afsenderdata",
|
"noSendersData": "Ingen afsenderdata",
|
||||||
"noSubject": "(Intet emne)",
|
"noSubject": "(Intet emne)",
|
||||||
|
"regularAttachments": "Almindelige vedhæftede filer",
|
||||||
"saved": "Sparet",
|
"saved": "Sparet",
|
||||||
"sender": "Afender",
|
"sender": "Afender",
|
||||||
"size": "Størrelse",
|
"size": "Størrelse",
|
||||||
@@ -497,6 +498,7 @@
|
|||||||
"top10LargestEmails": "Top 10 Største E-mails",
|
"top10LargestEmails": "Top 10 Største E-mails",
|
||||||
"top10Senders": "Top 10 Afsendere",
|
"top10Senders": "Top 10 Afsendere",
|
||||||
"topLists": "Toplister",
|
"topLists": "Toplister",
|
||||||
|
"totalAttachments": "Samlet antal vedhæftede filer",
|
||||||
"totalEmailSize": "Samlet e-mailstørrelse",
|
"totalEmailSize": "Samlet e-mailstørrelse",
|
||||||
"totalEmails": "Samlet antal e-mails",
|
"totalEmails": "Samlet antal e-mails",
|
||||||
"withAttachments": "Med vedhæftninger"
|
"withAttachments": "Med vedhæftninger"
|
||||||
|
|||||||
@@ -484,6 +484,7 @@
|
|||||||
"noRecentActivity": "Keine kürzliche Aktivität",
|
"noRecentActivity": "Keine kürzliche Aktivität",
|
||||||
"noSendersData": "Keine Absenderdaten",
|
"noSendersData": "Keine Absenderdaten",
|
||||||
"noSubject": "(Kein Betreff)",
|
"noSubject": "(Kein Betreff)",
|
||||||
|
"regularAttachments": "Reguläre Anhänge",
|
||||||
"saved": "Eingespart",
|
"saved": "Eingespart",
|
||||||
"sender": "Absender",
|
"sender": "Absender",
|
||||||
"size": "Größe",
|
"size": "Größe",
|
||||||
@@ -497,6 +498,7 @@
|
|||||||
"top10LargestEmails": "Top 10 größte E-Mails",
|
"top10LargestEmails": "Top 10 größte E-Mails",
|
||||||
"top10Senders": "Top 10 Absender",
|
"top10Senders": "Top 10 Absender",
|
||||||
"topLists": "Top-Listen",
|
"topLists": "Top-Listen",
|
||||||
|
"totalAttachments": "Anhänge insgesamt",
|
||||||
"totalEmailSize": "Gesamtgröße der E-Mails",
|
"totalEmailSize": "Gesamtgröße der E-Mails",
|
||||||
"totalEmails": "Gesamt-E-Mails",
|
"totalEmails": "Gesamt-E-Mails",
|
||||||
"withAttachments": "Mit Anhängen"
|
"withAttachments": "Mit Anhängen"
|
||||||
|
|||||||
@@ -484,6 +484,7 @@
|
|||||||
"noRecentActivity": "No recent activity",
|
"noRecentActivity": "No recent activity",
|
||||||
"noSendersData": "No senders data",
|
"noSendersData": "No senders data",
|
||||||
"noSubject": "(No Subject)",
|
"noSubject": "(No Subject)",
|
||||||
|
"regularAttachments": "Regular attachments",
|
||||||
"saved": "Saved",
|
"saved": "Saved",
|
||||||
"sender": "Sender",
|
"sender": "Sender",
|
||||||
"size": "Size",
|
"size": "Size",
|
||||||
@@ -497,6 +498,7 @@
|
|||||||
"top10LargestEmails": "Top 10 Largest Emails",
|
"top10LargestEmails": "Top 10 Largest Emails",
|
||||||
"top10Senders": "Top 10 Senders",
|
"top10Senders": "Top 10 Senders",
|
||||||
"topLists": "Top Lists",
|
"topLists": "Top Lists",
|
||||||
|
"totalAttachments": "Total attachments",
|
||||||
"totalEmailSize": "Total Email Size",
|
"totalEmailSize": "Total Email Size",
|
||||||
"totalEmails": "Total Emails",
|
"totalEmails": "Total Emails",
|
||||||
"withAttachments": "With Attachments"
|
"withAttachments": "With Attachments"
|
||||||
|
|||||||
@@ -484,6 +484,7 @@
|
|||||||
"noRecentActivity": "Sin actividad reciente",
|
"noRecentActivity": "Sin actividad reciente",
|
||||||
"noSendersData": "Sin datos de remitentes",
|
"noSendersData": "Sin datos de remitentes",
|
||||||
"noSubject": "(Sin asunto)",
|
"noSubject": "(Sin asunto)",
|
||||||
|
"regularAttachments": "Adjuntos regulares",
|
||||||
"saved": "Ahorrado",
|
"saved": "Ahorrado",
|
||||||
"sender": "Remitente",
|
"sender": "Remitente",
|
||||||
"size": "Tamaño",
|
"size": "Tamaño",
|
||||||
@@ -497,6 +498,7 @@
|
|||||||
"top10LargestEmails": "Los 10 correos más grandes",
|
"top10LargestEmails": "Los 10 correos más grandes",
|
||||||
"top10Senders": "Los 10 principales remitentes",
|
"top10Senders": "Los 10 principales remitentes",
|
||||||
"topLists": "Listas principales",
|
"topLists": "Listas principales",
|
||||||
|
"totalAttachments": "Total de adjuntos",
|
||||||
"totalEmailSize": "Tamaño total de correos",
|
"totalEmailSize": "Tamaño total de correos",
|
||||||
"totalEmails": "Total de correos",
|
"totalEmails": "Total de correos",
|
||||||
"withAttachments": "Con adjuntos"
|
"withAttachments": "Con adjuntos"
|
||||||
|
|||||||
@@ -484,6 +484,7 @@
|
|||||||
"noRecentActivity": "Ei viimeaikaisia tapahtumia",
|
"noRecentActivity": "Ei viimeaikaisia tapahtumia",
|
||||||
"noSendersData": "Ei lähettäjätietoja",
|
"noSendersData": "Ei lähettäjätietoja",
|
||||||
"noSubject": "(Ei aihetta)",
|
"noSubject": "(Ei aihetta)",
|
||||||
|
"regularAttachments": "Tavalliset liitteet",
|
||||||
"saved": "Säästetty",
|
"saved": "Säästetty",
|
||||||
"sender": "Lähettäjä",
|
"sender": "Lähettäjä",
|
||||||
"size": "Koko",
|
"size": "Koko",
|
||||||
@@ -497,6 +498,7 @@
|
|||||||
"top10LargestEmails": "10 suurinta sähköpostia",
|
"top10LargestEmails": "10 suurinta sähköpostia",
|
||||||
"top10Senders": "10 parasta lähettäjää",
|
"top10Senders": "10 parasta lähettäjää",
|
||||||
"topLists": "Parhaat listat",
|
"topLists": "Parhaat listat",
|
||||||
|
"totalAttachments": "Liitteitä yhteensä",
|
||||||
"totalEmailSize": "Sähköpostien kokonaiskoko",
|
"totalEmailSize": "Sähköpostien kokonaiskoko",
|
||||||
"totalEmails": "Sähköpostien kokonaismäärä",
|
"totalEmails": "Sähköpostien kokonaismäärä",
|
||||||
"withAttachments": "Liitteillä"
|
"withAttachments": "Liitteillä"
|
||||||
|
|||||||
@@ -484,6 +484,7 @@
|
|||||||
"noRecentActivity": "Aucune activité récente",
|
"noRecentActivity": "Aucune activité récente",
|
||||||
"noSendersData": "Aucune donnée d'expéditeurs",
|
"noSendersData": "Aucune donnée d'expéditeurs",
|
||||||
"noSubject": "(Aucun Objet)",
|
"noSubject": "(Aucun Objet)",
|
||||||
|
"regularAttachments": "Pièces jointes standard",
|
||||||
"saved": "Économisé",
|
"saved": "Économisé",
|
||||||
"sender": "Expéditeur",
|
"sender": "Expéditeur",
|
||||||
"size": "Taille",
|
"size": "Taille",
|
||||||
@@ -497,6 +498,7 @@
|
|||||||
"top10LargestEmails": "Top 10 des E-mails les Plus Volumineux",
|
"top10LargestEmails": "Top 10 des E-mails les Plus Volumineux",
|
||||||
"top10Senders": "Top 10 des Expéditeurs",
|
"top10Senders": "Top 10 des Expéditeurs",
|
||||||
"topLists": "Top Listes",
|
"topLists": "Top Listes",
|
||||||
|
"totalAttachments": "Total des pièces jointes",
|
||||||
"totalEmailSize": "Taille totale des e-mails",
|
"totalEmailSize": "Taille totale des e-mails",
|
||||||
"totalEmails": "Total des e-mails",
|
"totalEmails": "Total des e-mails",
|
||||||
"withAttachments": "Avec Pièces Jointes"
|
"withAttachments": "Avec Pièces Jointes"
|
||||||
|
|||||||
@@ -484,6 +484,7 @@
|
|||||||
"noRecentActivity": "Nessuna attività recente",
|
"noRecentActivity": "Nessuna attività recente",
|
||||||
"noSendersData": "Nessun dato sui mittenti",
|
"noSendersData": "Nessun dato sui mittenti",
|
||||||
"noSubject": "(Nessun Oggetto)",
|
"noSubject": "(Nessun Oggetto)",
|
||||||
|
"regularAttachments": "Allegati regolari",
|
||||||
"saved": "Risparmiato",
|
"saved": "Risparmiato",
|
||||||
"sender": "Mittente",
|
"sender": "Mittente",
|
||||||
"size": "Dimensione",
|
"size": "Dimensione",
|
||||||
@@ -497,6 +498,7 @@
|
|||||||
"top10LargestEmails": "Le 10 Email più Grandi",
|
"top10LargestEmails": "Le 10 Email più Grandi",
|
||||||
"top10Senders": "I 10 Mittenti Principali",
|
"top10Senders": "I 10 Mittenti Principali",
|
||||||
"topLists": "Liste Top",
|
"topLists": "Liste Top",
|
||||||
|
"totalAttachments": "Allegati totali",
|
||||||
"totalEmailSize": "Dimensione totale email",
|
"totalEmailSize": "Dimensione totale email",
|
||||||
"totalEmails": "Email totali",
|
"totalEmails": "Email totali",
|
||||||
"withAttachments": "Con Allegati"
|
"withAttachments": "Con Allegati"
|
||||||
|
|||||||
@@ -484,6 +484,7 @@
|
|||||||
"noRecentActivity": "最近のアクティビティなし",
|
"noRecentActivity": "最近のアクティビティなし",
|
||||||
"noSendersData": "送信者データなし",
|
"noSendersData": "送信者データなし",
|
||||||
"noSubject": "(件名なし)",
|
"noSubject": "(件名なし)",
|
||||||
|
"regularAttachments": "通常の添付ファイル",
|
||||||
"saved": "節約済み",
|
"saved": "節約済み",
|
||||||
"sender": "送信者",
|
"sender": "送信者",
|
||||||
"size": "サイズ",
|
"size": "サイズ",
|
||||||
@@ -497,6 +498,7 @@
|
|||||||
"top10LargestEmails": "容量の大きいメールトップ10",
|
"top10LargestEmails": "容量の大きいメールトップ10",
|
||||||
"top10Senders": "送信者トップ10",
|
"top10Senders": "送信者トップ10",
|
||||||
"topLists": "トップリスト",
|
"topLists": "トップリスト",
|
||||||
|
"totalAttachments": "添付ファイル総数",
|
||||||
"totalEmailSize": "メール総容量",
|
"totalEmailSize": "メール総容量",
|
||||||
"totalEmails": "合計メール数",
|
"totalEmails": "合計メール数",
|
||||||
"withAttachments": "添付ファイルあり"
|
"withAttachments": "添付ファイルあり"
|
||||||
|
|||||||
@@ -484,6 +484,7 @@
|
|||||||
"noRecentActivity": "최근 활동 없음",
|
"noRecentActivity": "최근 활동 없음",
|
||||||
"noSendersData": "발신자 데이터 없음",
|
"noSendersData": "발신자 데이터 없음",
|
||||||
"noSubject": "(제목 없음)",
|
"noSubject": "(제목 없음)",
|
||||||
|
"regularAttachments": "일반 첨부 파일",
|
||||||
"saved": "절약됨",
|
"saved": "절약됨",
|
||||||
"sender": "발신자",
|
"sender": "발신자",
|
||||||
"size": "크기",
|
"size": "크기",
|
||||||
@@ -497,6 +498,7 @@
|
|||||||
"top10LargestEmails": "상위 10개 최대 크기 이메일",
|
"top10LargestEmails": "상위 10개 최대 크기 이메일",
|
||||||
"top10Senders": "상위 10명 발신자",
|
"top10Senders": "상위 10명 발신자",
|
||||||
"topLists": "상위 목록",
|
"topLists": "상위 목록",
|
||||||
|
"totalAttachments": "총 첨부 파일",
|
||||||
"totalEmailSize": "총 이메일 크기",
|
"totalEmailSize": "총 이메일 크기",
|
||||||
"totalEmails": "총 이메일 수",
|
"totalEmails": "총 이메일 수",
|
||||||
"withAttachments": "첨부 파일 포함"
|
"withAttachments": "첨부 파일 포함"
|
||||||
|
|||||||
@@ -484,6 +484,7 @@
|
|||||||
"noRecentActivity": "Geen recente activiteit",
|
"noRecentActivity": "Geen recente activiteit",
|
||||||
"noSendersData": "Geen afzendersgegevens",
|
"noSendersData": "Geen afzendersgegevens",
|
||||||
"noSubject": "(Geen Onderwerp)",
|
"noSubject": "(Geen Onderwerp)",
|
||||||
|
"regularAttachments": "Reguliere bijlagen",
|
||||||
"saved": "Bespaard",
|
"saved": "Bespaard",
|
||||||
"sender": "Afzender",
|
"sender": "Afzender",
|
||||||
"size": "Grootte",
|
"size": "Grootte",
|
||||||
@@ -497,6 +498,7 @@
|
|||||||
"top10LargestEmails": "Top 10 Grootste e-mails",
|
"top10LargestEmails": "Top 10 Grootste e-mails",
|
||||||
"top10Senders": "Top 10 Afzenders",
|
"top10Senders": "Top 10 Afzenders",
|
||||||
"topLists": "Toplijsten",
|
"topLists": "Toplijsten",
|
||||||
|
"totalAttachments": "Totaal aantal bijlagen",
|
||||||
"totalEmailSize": "Totale e-mailgrootte",
|
"totalEmailSize": "Totale e-mailgrootte",
|
||||||
"totalEmails": "Totaal aantal e-mails",
|
"totalEmails": "Totaal aantal e-mails",
|
||||||
"withAttachments": "Met Bijlagen"
|
"withAttachments": "Met Bijlagen"
|
||||||
|
|||||||
@@ -484,6 +484,7 @@
|
|||||||
"noRecentActivity": "Ingen nylig aktivitet",
|
"noRecentActivity": "Ingen nylig aktivitet",
|
||||||
"noSendersData": "Ingen avsenderdata",
|
"noSendersData": "Ingen avsenderdata",
|
||||||
"noSubject": "(Uten emne)",
|
"noSubject": "(Uten emne)",
|
||||||
|
"regularAttachments": "Vanlige vedlegg",
|
||||||
"saved": "Spart",
|
"saved": "Spart",
|
||||||
"sender": "Avsender",
|
"sender": "Avsender",
|
||||||
"size": "Størrelse",
|
"size": "Størrelse",
|
||||||
@@ -497,6 +498,7 @@
|
|||||||
"top10LargestEmails": "Topp 10 største e-poster",
|
"top10LargestEmails": "Topp 10 største e-poster",
|
||||||
"top10Senders": "Topp 10 avsendere",
|
"top10Senders": "Topp 10 avsendere",
|
||||||
"topLists": "Topplister",
|
"topLists": "Topplister",
|
||||||
|
"totalAttachments": "Totalt antall vedlegg",
|
||||||
"totalEmailSize": "Total e-poststørrelse",
|
"totalEmailSize": "Total e-poststørrelse",
|
||||||
"totalEmails": "Totalt antall e-poster",
|
"totalEmails": "Totalt antall e-poster",
|
||||||
"withAttachments": "Med vedlegg"
|
"withAttachments": "Med vedlegg"
|
||||||
|
|||||||
@@ -484,6 +484,7 @@
|
|||||||
"noRecentActivity": "Brak aktywności",
|
"noRecentActivity": "Brak aktywności",
|
||||||
"noSendersData": "Brak danych od Nadawcy",
|
"noSendersData": "Brak danych od Nadawcy",
|
||||||
"noSubject": "(brak tematu)",
|
"noSubject": "(brak tematu)",
|
||||||
|
"regularAttachments": "Zwykłe załączniki",
|
||||||
"saved": "Zaoszczędzone",
|
"saved": "Zaoszczędzone",
|
||||||
"sender": "Nadawca",
|
"sender": "Nadawca",
|
||||||
"size": "Rozmiar",
|
"size": "Rozmiar",
|
||||||
@@ -497,6 +498,7 @@
|
|||||||
"top10LargestEmails": "Top 10 największych wiadomości",
|
"top10LargestEmails": "Top 10 największych wiadomości",
|
||||||
"top10Senders": "Top 10 Nadawców",
|
"top10Senders": "Top 10 Nadawców",
|
||||||
"topLists": "Lista TOP",
|
"topLists": "Lista TOP",
|
||||||
|
"totalAttachments": "Łączna liczba załączników",
|
||||||
"totalEmailSize": "Wielkość wszystkich wiadomości",
|
"totalEmailSize": "Wielkość wszystkich wiadomości",
|
||||||
"totalEmails": "Wszystkie wiadomości",
|
"totalEmails": "Wszystkie wiadomości",
|
||||||
"withAttachments": "Z załącznikami"
|
"withAttachments": "Z załącznikami"
|
||||||
|
|||||||
@@ -484,6 +484,7 @@
|
|||||||
"noRecentActivity": "Nenhuma Atividade Recente",
|
"noRecentActivity": "Nenhuma Atividade Recente",
|
||||||
"noSendersData": "Sem Dados de Remetentes",
|
"noSendersData": "Sem Dados de Remetentes",
|
||||||
"noSubject": "(Sem Assunto)",
|
"noSubject": "(Sem Assunto)",
|
||||||
|
"regularAttachments": "Anexos regulares",
|
||||||
"saved": "Economizado",
|
"saved": "Economizado",
|
||||||
"sender": "Remetente",
|
"sender": "Remetente",
|
||||||
"size": "Tamanho",
|
"size": "Tamanho",
|
||||||
@@ -497,6 +498,7 @@
|
|||||||
"top10LargestEmails": "Top 10 Maiores Emails",
|
"top10LargestEmails": "Top 10 Maiores Emails",
|
||||||
"top10Senders": "Top 10 Remetentes",
|
"top10Senders": "Top 10 Remetentes",
|
||||||
"topLists": "Principais Listas",
|
"topLists": "Principais Listas",
|
||||||
|
"totalAttachments": "Total de anexos",
|
||||||
"totalEmailSize": "Tamanho Total do Email",
|
"totalEmailSize": "Tamanho Total do Email",
|
||||||
"totalEmails": "Total de Emails",
|
"totalEmails": "Total de Emails",
|
||||||
"withAttachments": "Com Anexos"
|
"withAttachments": "Com Anexos"
|
||||||
|
|||||||
@@ -484,6 +484,7 @@
|
|||||||
"noRecentActivity": "Нет недавней активности",
|
"noRecentActivity": "Нет недавней активности",
|
||||||
"noSendersData": "Нет данных об отправителях",
|
"noSendersData": "Нет данных об отправителях",
|
||||||
"noSubject": "(Без темы)",
|
"noSubject": "(Без темы)",
|
||||||
|
"regularAttachments": "Обычные вложения",
|
||||||
"saved": "Сэкономлено",
|
"saved": "Сэкономлено",
|
||||||
"sender": "Отправитель",
|
"sender": "Отправитель",
|
||||||
"size": "Размер",
|
"size": "Размер",
|
||||||
@@ -497,6 +498,7 @@
|
|||||||
"top10LargestEmails": "Топ 10 самых больших писем",
|
"top10LargestEmails": "Топ 10 самых больших писем",
|
||||||
"top10Senders": "Топ 10 отправителей",
|
"top10Senders": "Топ 10 отправителей",
|
||||||
"topLists": "Топ списки",
|
"topLists": "Топ списки",
|
||||||
|
"totalAttachments": "Всего вложений",
|
||||||
"totalEmailSize": "Общий размер писем",
|
"totalEmailSize": "Общий размер писем",
|
||||||
"totalEmails": "Всего писем",
|
"totalEmails": "Всего писем",
|
||||||
"withAttachments": "С вложениями"
|
"withAttachments": "С вложениями"
|
||||||
|
|||||||
@@ -484,6 +484,7 @@
|
|||||||
"noRecentActivity": "Ingen nylig aktivitet",
|
"noRecentActivity": "Ingen nylig aktivitet",
|
||||||
"noSendersData": "Inga avsändardata",
|
"noSendersData": "Inga avsändardata",
|
||||||
"noSubject": "(Inget ämne)",
|
"noSubject": "(Inget ämne)",
|
||||||
|
"regularAttachments": "Vanliga bilagor",
|
||||||
"saved": "Sparat",
|
"saved": "Sparat",
|
||||||
"sender": "Avsändare",
|
"sender": "Avsändare",
|
||||||
"size": "Storlek",
|
"size": "Storlek",
|
||||||
@@ -497,6 +498,7 @@
|
|||||||
"top10LargestEmails": "Topp 10 största e-postmeddelanden",
|
"top10LargestEmails": "Topp 10 största e-postmeddelanden",
|
||||||
"top10Senders": "Topp 10 avsändare",
|
"top10Senders": "Topp 10 avsändare",
|
||||||
"topLists": "Topplistor",
|
"topLists": "Topplistor",
|
||||||
|
"totalAttachments": "Totalt antal bilagor",
|
||||||
"totalEmailSize": "Total e-poststorlek",
|
"totalEmailSize": "Total e-poststorlek",
|
||||||
"totalEmails": "Totalt antal e-postmeddelanden",
|
"totalEmails": "Totalt antal e-postmeddelanden",
|
||||||
"withAttachments": "Med bilagor"
|
"withAttachments": "Med bilagor"
|
||||||
|
|||||||
@@ -484,6 +484,7 @@
|
|||||||
"noRecentActivity": "無近期活動",
|
"noRecentActivity": "無近期活動",
|
||||||
"noSendersData": "無寄件人資料",
|
"noSendersData": "無寄件人資料",
|
||||||
"noSubject": "(無主旨)",
|
"noSubject": "(無主旨)",
|
||||||
|
"regularAttachments": "普通附件",
|
||||||
"saved": "節省空間",
|
"saved": "節省空間",
|
||||||
"sender": "寄件人",
|
"sender": "寄件人",
|
||||||
"size": "大小",
|
"size": "大小",
|
||||||
@@ -497,6 +498,7 @@
|
|||||||
"top10LargestEmails": "前 10 封最大郵件",
|
"top10LargestEmails": "前 10 封最大郵件",
|
||||||
"top10Senders": "前 10 名寄件人",
|
"top10Senders": "前 10 名寄件人",
|
||||||
"topLists": "排行榜",
|
"topLists": "排行榜",
|
||||||
|
"totalAttachments": "附件總數",
|
||||||
"totalEmailSize": "郵件總容量",
|
"totalEmailSize": "郵件總容量",
|
||||||
"totalEmails": "郵件總數",
|
"totalEmails": "郵件總數",
|
||||||
"withAttachments": "包含附件"
|
"withAttachments": "包含附件"
|
||||||
|
|||||||
@@ -484,6 +484,7 @@
|
|||||||
"noRecentActivity": "无最近活动",
|
"noRecentActivity": "无最近活动",
|
||||||
"noSendersData": "无发件人数据",
|
"noSendersData": "无发件人数据",
|
||||||
"noSubject": "(无主题)",
|
"noSubject": "(无主题)",
|
||||||
|
"regularAttachments": "普通附件",
|
||||||
"saved": "节省空间",
|
"saved": "节省空间",
|
||||||
"sender": "发件人",
|
"sender": "发件人",
|
||||||
"size": "大小",
|
"size": "大小",
|
||||||
@@ -497,6 +498,7 @@
|
|||||||
"top10LargestEmails": "前10大邮件",
|
"top10LargestEmails": "前10大邮件",
|
||||||
"top10Senders": "前10名发件人",
|
"top10Senders": "前10名发件人",
|
||||||
"topLists": "排行榜",
|
"topLists": "排行榜",
|
||||||
|
"totalAttachments": "附件总数",
|
||||||
"totalEmailSize": "邮件总大小",
|
"totalEmailSize": "邮件总大小",
|
||||||
"totalEmails": "邮件总数",
|
"totalEmails": "邮件总数",
|
||||||
"withAttachments": "包含附件"
|
"withAttachments": "包含附件"
|
||||||
|
|||||||
Reference in New Issue
Block a user