feat: detect legacy tantivy data layout and abort startup with migration hint

This commit is contained in:
rustmailer
2026-05-08 16:28:11 +08:00
parent 174d56e7b4
commit 66b595908c
26 changed files with 189 additions and 29 deletions
+2 -1
View File
@@ -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))?;
+1
View File
@@ -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;
+62
View File
@@ -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);
}
}
+25 -1
View File
@@ -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
View File
@@ -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);
+2
View File
@@ -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,
+1 -2
View File
@@ -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'
+29 -12
View File
@@ -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>
</> </>
); );
+2
View File
@@ -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": "مع مرفقات"
+2
View File
@@ -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"
+2
View File
@@ -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"
+2
View File
@@ -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"
+2
View File
@@ -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"
+2
View File
@@ -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ä"
+2
View File
@@ -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"
+2
View File
@@ -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"
+2
View File
@@ -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": "添付ファイルあり"
+2
View File
@@ -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": "첨부 파일 포함"
+2
View File
@@ -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"
+2
View File
@@ -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"
+2
View File
@@ -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"
+2
View File
@@ -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"
+2
View File
@@ -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": "С вложениями"
+2
View File
@@ -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"
+2
View File
@@ -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": "包含附件"
+2
View File
@@ -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": "包含附件"