feat(library): add Manage Cache to advanced settings (#4359)
Add a "Manage Cache" item to the library Advanced Settings menu (native mobile apps only) that opens a modern dialog showing the combined size and file count of the app's reclaimable storage, with a confirm-gated clear that reports per-file progress. - iOS clears Cache + Temp + Documents/Inbox; Android clears Cache + Temp. - Multi-source helper (getCacheEntries/getCacheStats/clearCacheEntries) with unit tests; per-file failures are counted, never abort the run. - Dialog uses the centered-hero + btn-contrast design language, theme-neutral progress, and is e-ink correct. - i18n: new strings translated across all locales (+ en plural forms). Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1807,5 +1807,22 @@
|
||||
"No bookmark yet": "لا توجد إشارات مرجعية بعد",
|
||||
"Importing...": "جاري الاستيراد...",
|
||||
"Swipe for Brightness": "اسحب لضبط السطوع",
|
||||
"Slide along the left edge": "اسحب على طول الحافة اليسرى"
|
||||
"Slide along the left edge": "اسحب على طول الحافة اليسرى",
|
||||
"Failed to read cache": "فشل قراءة ذاكرة التخزين المؤقت",
|
||||
"Failed to clear cache": "فشل مسح ذاكرة التخزين المؤقت",
|
||||
"Calculating cache size...": "جارٍ حساب حجم ذاكرة التخزين المؤقت…",
|
||||
"Cache cleared": "تم مسح ذاكرة التخزين المؤقت",
|
||||
"{{count}} files_zero": "{{count}} ملف",
|
||||
"{{count}} files_one": "ملف واحد",
|
||||
"{{count}} files_two": "ملفان",
|
||||
"{{count}} files_few": "{{count}} ملفات",
|
||||
"{{count}} files_many": "{{count}} ملفًا",
|
||||
"{{count}} files_other": "{{count}} ملف",
|
||||
"Manage Cache": "إدارة ذاكرة التخزين المؤقت",
|
||||
"Deleting: {{file}}": "جارٍ الحذف: {{file}}",
|
||||
"Clearing cache...": "جارٍ مسح ذاكرة التخزين المؤقت…",
|
||||
"This will delete all cached files. This cannot be undone.": "سيؤدي هذا إلى حذف جميع الملفات المخزنة مؤقتًا. لا يمكن التراجع عن هذا الإجراء.",
|
||||
"Confirm Clear": "تأكيد المسح",
|
||||
"Clearing...": "جارٍ المسح…",
|
||||
"Clear Cache": "مسح ذاكرة التخزين المؤقت"
|
||||
}
|
||||
|
||||
@@ -1679,5 +1679,18 @@
|
||||
"No bookmark yet": "এখনও কোনো বুকমার্ক নেই",
|
||||
"Importing...": "আমদানি হচ্ছে...",
|
||||
"Swipe for Brightness": "উজ্জ্বলতার জন্য সোয়াইপ করুন",
|
||||
"Slide along the left edge": "বাম প্রান্ত বরাবর স্লাইড করুন"
|
||||
"Slide along the left edge": "বাম প্রান্ত বরাবর স্লাইড করুন",
|
||||
"Failed to read cache": "ক্যাশ পড়া ব্যর্থ হয়েছে",
|
||||
"Failed to clear cache": "ক্যাশ সাফ করা ব্যর্থ হয়েছে",
|
||||
"Calculating cache size...": "ক্যাশের আকার গণনা করা হচ্ছে…",
|
||||
"Cache cleared": "ক্যাশ সাফ করা হয়েছে",
|
||||
"{{count}} files_one": "{{count}}টি ফাইল",
|
||||
"{{count}} files_other": "{{count}}টি ফাইল",
|
||||
"Manage Cache": "ক্যাশ পরিচালনা করুন",
|
||||
"Deleting: {{file}}": "মুছে ফেলা হচ্ছে: {{file}}",
|
||||
"Clearing cache...": "ক্যাশ সাফ করা হচ্ছে…",
|
||||
"This will delete all cached files. This cannot be undone.": "এটি সমস্ত ক্যাশ করা ফাইল মুছে ফেলবে। এটি পূর্বাবস্থায় ফেরানো যাবে না।",
|
||||
"Confirm Clear": "সাফ করা নিশ্চিত করুন",
|
||||
"Clearing...": "সাফ করা হচ্ছে…",
|
||||
"Clear Cache": "ক্যাশ সাফ করুন"
|
||||
}
|
||||
|
||||
@@ -1647,5 +1647,17 @@
|
||||
"No bookmark yet": "ད་དུང་དཔེ་རྟགས་མེད།",
|
||||
"Importing...": "ནང་འདྲེན་བྱེད་བཞིན་པ...",
|
||||
"Swipe for Brightness": "འོད་གདངས་ཆེད་འཐེན།",
|
||||
"Slide along the left edge": "གཡོན་ངོས་ཟུར་ནས་འཐེན།"
|
||||
"Slide along the left edge": "གཡོན་ངོས་ཟུར་ནས་འཐེན།",
|
||||
"Failed to read cache": "གསོག་ཉར་ཀློག་མ་ཐུབ།",
|
||||
"Failed to clear cache": "གསོག་ཉར་བསལ་མ་ཐུབ།",
|
||||
"Calculating cache size...": "གསོག་ཉར་གྱི་ཆེ་ཆུང་རྩིས་བཞིན་པ།...",
|
||||
"Cache cleared": "གསོག་ཉར་བསལ་ཟིན།",
|
||||
"{{count}} files_other": "ཡིག་ཆ་ {{count}}",
|
||||
"Manage Cache": "གསོག་ཉར་དོ་དམ།",
|
||||
"Deleting: {{file}}": "བསུབ་བཞིན་པ། {{file}}",
|
||||
"Clearing cache...": "གསོག་ཉར་བསལ་བཞིན་པ།...",
|
||||
"This will delete all cached files. This cannot be undone.": "འདིས་གསོག་ཉར་གྱི་ཡིག་ཆ་ཚང་མ་བསུབ་ངེས་ཡིན། འདི་སྔར་བཞིན་བཟོ་མི་ཐུབ།",
|
||||
"Confirm Clear": "བསལ་བ་གཏན་འཁེལ།",
|
||||
"Clearing...": "བསལ་བཞིན་པ།...",
|
||||
"Clear Cache": "གསོག་ཉར་བསལ་བ།"
|
||||
}
|
||||
|
||||
@@ -1679,5 +1679,18 @@
|
||||
"No bookmark yet": "Noch kein Lesezeichen",
|
||||
"Importing...": "Wird importiert...",
|
||||
"Swipe for Brightness": "Wischen für Helligkeit",
|
||||
"Slide along the left edge": "Am linken Rand entlang wischen"
|
||||
"Slide along the left edge": "Am linken Rand entlang wischen",
|
||||
"Failed to read cache": "Cache konnte nicht gelesen werden",
|
||||
"Failed to clear cache": "Cache konnte nicht geleert werden",
|
||||
"Calculating cache size...": "Cache-Größe wird berechnet …",
|
||||
"Cache cleared": "Cache geleert",
|
||||
"{{count}} files_one": "{{count}} Datei",
|
||||
"{{count}} files_other": "{{count}} Dateien",
|
||||
"Manage Cache": "Cache verwalten",
|
||||
"Deleting: {{file}}": "Wird gelöscht: {{file}}",
|
||||
"Clearing cache...": "Cache wird geleert …",
|
||||
"This will delete all cached files. This cannot be undone.": "Dadurch werden alle zwischengespeicherten Dateien gelöscht. Dies kann nicht rückgängig gemacht werden.",
|
||||
"Confirm Clear": "Leeren bestätigen",
|
||||
"Clearing...": "Wird geleert …",
|
||||
"Clear Cache": "Cache leeren"
|
||||
}
|
||||
|
||||
@@ -1679,5 +1679,18 @@
|
||||
"No bookmark yet": "Δεν υπάρχει ακόμη σελιδοδείκτης",
|
||||
"Importing...": "Εισαγωγή...",
|
||||
"Swipe for Brightness": "Σάρωση για φωτεινότητα",
|
||||
"Slide along the left edge": "Σύρετε κατά μήκος της αριστερής άκρης"
|
||||
"Slide along the left edge": "Σύρετε κατά μήκος της αριστερής άκρης",
|
||||
"Failed to read cache": "Αποτυχία ανάγνωσης της προσωρινής μνήμης",
|
||||
"Failed to clear cache": "Αποτυχία εκκαθάρισης της προσωρινής μνήμης",
|
||||
"Calculating cache size...": "Υπολογισμός μεγέθους προσωρινής μνήμης…",
|
||||
"Cache cleared": "Η προσωρινή μνήμη εκκαθαρίστηκε",
|
||||
"{{count}} files_one": "{{count}} αρχείο",
|
||||
"{{count}} files_other": "{{count}} αρχεία",
|
||||
"Manage Cache": "Διαχείριση προσωρινής μνήμης",
|
||||
"Deleting: {{file}}": "Διαγραφή: {{file}}",
|
||||
"Clearing cache...": "Εκκαθάριση προσωρινής μνήμης…",
|
||||
"This will delete all cached files. This cannot be undone.": "Αυτό θα διαγράψει όλα τα προσωρινά αποθηκευμένα αρχεία. Αυτή η ενέργεια δεν μπορεί να αναιρεθεί.",
|
||||
"Confirm Clear": "Επιβεβαίωση εκκαθάρισης",
|
||||
"Clearing...": "Εκκαθάριση…",
|
||||
"Clear Cache": "Εκκαθάριση προσωρινής μνήμης"
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
"{{count}} pages left in book_other": "<0>{{count}}</0><1> pages left in book</1>",
|
||||
"Deleted {{count}} file(s)_one": "Deleted {{count}} file",
|
||||
"Deleted {{count}} file(s)_other": "Deleted {{count}} files",
|
||||
"{{count}} files_one": "{{count}} file",
|
||||
"{{count}} files_other": "{{count}} files",
|
||||
"Failed to delete {{count}} file(s)_one": "Failed to delete {{count}} file",
|
||||
"Failed to delete {{count}} file(s)_other": "Failed to delete {{count}} files",
|
||||
"Failed to import {{count}} books_one": "Failed to import {{count}} book",
|
||||
|
||||
@@ -1711,5 +1711,19 @@
|
||||
"No bookmark yet": "Aún no hay marcadores",
|
||||
"Importing...": "Importando...",
|
||||
"Swipe for Brightness": "Deslizar para el brillo",
|
||||
"Slide along the left edge": "Desliza por el borde izquierdo"
|
||||
"Slide along the left edge": "Desliza por el borde izquierdo",
|
||||
"Failed to read cache": "No se pudo leer la caché",
|
||||
"Failed to clear cache": "No se pudo borrar la caché",
|
||||
"Calculating cache size...": "Calculando el tamaño de la caché…",
|
||||
"Cache cleared": "Caché borrada",
|
||||
"{{count}} files_one": "{{count}} archivo",
|
||||
"{{count}} files_many": "{{count}} archivos",
|
||||
"{{count}} files_other": "{{count}} archivos",
|
||||
"Manage Cache": "Administrar caché",
|
||||
"Deleting: {{file}}": "Eliminando: {{file}}",
|
||||
"Clearing cache...": "Borrando caché…",
|
||||
"This will delete all cached files. This cannot be undone.": "Esto eliminará todos los archivos en caché. Esta acción no se puede deshacer.",
|
||||
"Confirm Clear": "Confirmar borrado",
|
||||
"Clearing...": "Borrando…",
|
||||
"Clear Cache": "Borrar caché"
|
||||
}
|
||||
|
||||
@@ -1679,5 +1679,18 @@
|
||||
"No bookmark yet": "هنوز نشانکی وجود ندارد",
|
||||
"Importing...": "در حال وارد کردن...",
|
||||
"Swipe for Brightness": "کشیدن برای روشنایی",
|
||||
"Slide along the left edge": "در امتداد لبه چپ بکشید"
|
||||
"Slide along the left edge": "در امتداد لبه چپ بکشید",
|
||||
"Failed to read cache": "خواندن حافظهٔ پنهان ناموفق بود",
|
||||
"Failed to clear cache": "پاک کردن حافظهٔ پنهان ناموفق بود",
|
||||
"Calculating cache size...": "در حال محاسبهٔ اندازهٔ حافظهٔ پنهان…",
|
||||
"Cache cleared": "حافظهٔ پنهان پاک شد",
|
||||
"{{count}} files_one": "{{count}} فایل",
|
||||
"{{count}} files_other": "{{count}} فایل",
|
||||
"Manage Cache": "مدیریت حافظهٔ پنهان",
|
||||
"Deleting: {{file}}": "در حال حذف: {{file}}",
|
||||
"Clearing cache...": "در حال پاک کردن حافظهٔ پنهان…",
|
||||
"This will delete all cached files. This cannot be undone.": "این کار همهٔ فایلهای ذخیرهشده در حافظهٔ پنهان را حذف میکند. این عمل قابل بازگشت نیست.",
|
||||
"Confirm Clear": "تأیید پاک کردن",
|
||||
"Clearing...": "در حال پاک کردن…",
|
||||
"Clear Cache": "پاک کردن حافظهٔ پنهان"
|
||||
}
|
||||
|
||||
@@ -1711,5 +1711,19 @@
|
||||
"No bookmark yet": "Aucun signet pour le moment",
|
||||
"Importing...": "Importation...",
|
||||
"Swipe for Brightness": "Balayer pour la luminosité",
|
||||
"Slide along the left edge": "Glissez le long du bord gauche"
|
||||
"Slide along the left edge": "Glissez le long du bord gauche",
|
||||
"Failed to read cache": "Échec de la lecture du cache",
|
||||
"Failed to clear cache": "Échec du vidage du cache",
|
||||
"Calculating cache size...": "Calcul de la taille du cache…",
|
||||
"Cache cleared": "Cache vidé",
|
||||
"{{count}} files_one": "{{count}} fichier",
|
||||
"{{count}} files_many": "{{count}} fichiers",
|
||||
"{{count}} files_other": "{{count}} fichiers",
|
||||
"Manage Cache": "Gérer le cache",
|
||||
"Deleting: {{file}}": "Suppression : {{file}}",
|
||||
"Clearing cache...": "Vidage du cache…",
|
||||
"This will delete all cached files. This cannot be undone.": "Cette action supprimera tous les fichiers mis en cache. Elle est irréversible.",
|
||||
"Confirm Clear": "Confirmer le vidage",
|
||||
"Clearing...": "Vidage…",
|
||||
"Clear Cache": "Vider le cache"
|
||||
}
|
||||
|
||||
@@ -1711,5 +1711,19 @@
|
||||
"No bookmark yet": "אין סימניות עדיין",
|
||||
"Importing...": "מייבא...",
|
||||
"Swipe for Brightness": "החלקה לכוונון בהירות",
|
||||
"Slide along the left edge": "החליקו לאורך הקצה השמאלי"
|
||||
"Slide along the left edge": "החליקו לאורך הקצה השמאלי",
|
||||
"Failed to read cache": "קריאת המטמון נכשלה",
|
||||
"Failed to clear cache": "ניקוי המטמון נכשל",
|
||||
"Calculating cache size...": "מחשב את גודל המטמון…",
|
||||
"Cache cleared": "המטמון נוקה",
|
||||
"{{count}} files_one": "קובץ אחד",
|
||||
"{{count}} files_two": "{{count}} קבצים",
|
||||
"{{count}} files_other": "{{count}} קבצים",
|
||||
"Manage Cache": "ניהול מטמון",
|
||||
"Deleting: {{file}}": "מוחק: {{file}}",
|
||||
"Clearing cache...": "מנקה מטמון…",
|
||||
"This will delete all cached files. This cannot be undone.": "פעולה זו תמחק את כל הקבצים השמורים במטמון. לא ניתן לבטל פעולה זו.",
|
||||
"Confirm Clear": "אישור ניקוי",
|
||||
"Clearing...": "מנקה…",
|
||||
"Clear Cache": "נקה מטמון"
|
||||
}
|
||||
|
||||
@@ -1679,5 +1679,18 @@
|
||||
"No bookmark yet": "अभी तक कोई बुकमार्क नहीं",
|
||||
"Importing...": "आयात हो रहा है...",
|
||||
"Swipe for Brightness": "चमक के लिए स्वाइप करें",
|
||||
"Slide along the left edge": "बाएँ किनारे पर स्लाइड करें"
|
||||
"Slide along the left edge": "बाएँ किनारे पर स्लाइड करें",
|
||||
"Failed to read cache": "कैश पढ़ने में विफल",
|
||||
"Failed to clear cache": "कैश साफ़ करने में विफल",
|
||||
"Calculating cache size...": "कैश का आकार परिकलित किया जा रहा है…",
|
||||
"Cache cleared": "कैश साफ़ कर दिया गया",
|
||||
"{{count}} files_one": "{{count}} फ़ाइल",
|
||||
"{{count}} files_other": "{{count}} फ़ाइलें",
|
||||
"Manage Cache": "कैश प्रबंधित करें",
|
||||
"Deleting: {{file}}": "हटाया जा रहा है: {{file}}",
|
||||
"Clearing cache...": "कैश साफ़ किया जा रहा है…",
|
||||
"This will delete all cached files. This cannot be undone.": "इससे सभी कैश की गई फ़ाइलें हट जाएँगी. इसे पूर्ववत नहीं किया जा सकता.",
|
||||
"Confirm Clear": "साफ़ करने की पुष्टि करें",
|
||||
"Clearing...": "साफ़ किया जा रहा है…",
|
||||
"Clear Cache": "कैश साफ़ करें"
|
||||
}
|
||||
|
||||
@@ -1679,5 +1679,18 @@
|
||||
"No bookmark yet": "Még nincs könyvjelző",
|
||||
"Importing...": "Importálás...",
|
||||
"Swipe for Brightness": "Húzás a fényerőhöz",
|
||||
"Slide along the left edge": "Húzza a bal szél mentén"
|
||||
"Slide along the left edge": "Húzza a bal szél mentén",
|
||||
"Failed to read cache": "Nem sikerült olvasni a gyorsítótárat",
|
||||
"Failed to clear cache": "Nem sikerült törölni a gyorsítótárat",
|
||||
"Calculating cache size...": "Gyorsítótár méretének kiszámítása…",
|
||||
"Cache cleared": "Gyorsítótár törölve",
|
||||
"{{count}} files_one": "{{count}} fájl",
|
||||
"{{count}} files_other": "{{count}} fájl",
|
||||
"Manage Cache": "Gyorsítótár kezelése",
|
||||
"Deleting: {{file}}": "Törlés: {{file}}",
|
||||
"Clearing cache...": "Gyorsítótár törlése…",
|
||||
"This will delete all cached files. This cannot be undone.": "Ez törli az összes gyorsítótárazott fájlt. A művelet nem vonható vissza.",
|
||||
"Confirm Clear": "Törlés megerősítése",
|
||||
"Clearing...": "Törlés…",
|
||||
"Clear Cache": "Gyorsítótár törlése"
|
||||
}
|
||||
|
||||
@@ -1647,5 +1647,17 @@
|
||||
"No bookmark yet": "Belum ada penanda",
|
||||
"Importing...": "Mengimpor...",
|
||||
"Swipe for Brightness": "Geser untuk kecerahan",
|
||||
"Slide along the left edge": "Geser di sepanjang tepi kiri"
|
||||
"Slide along the left edge": "Geser di sepanjang tepi kiri",
|
||||
"Failed to read cache": "Gagal membaca cache",
|
||||
"Failed to clear cache": "Gagal membersihkan cache",
|
||||
"Calculating cache size...": "Menghitung ukuran cache…",
|
||||
"Cache cleared": "Cache dibersihkan",
|
||||
"{{count}} files_other": "{{count}} file",
|
||||
"Manage Cache": "Kelola Cache",
|
||||
"Deleting: {{file}}": "Menghapus: {{file}}",
|
||||
"Clearing cache...": "Membersihkan cache…",
|
||||
"This will delete all cached files. This cannot be undone.": "Tindakan ini akan menghapus semua file cache. Tindakan ini tidak dapat dibatalkan.",
|
||||
"Confirm Clear": "Konfirmasi Pembersihan",
|
||||
"Clearing...": "Membersihkan…",
|
||||
"Clear Cache": "Bersihkan Cache"
|
||||
}
|
||||
|
||||
@@ -1711,5 +1711,19 @@
|
||||
"No bookmark yet": "Nessun segnalibro ancora",
|
||||
"Importing...": "Importazione...",
|
||||
"Swipe for Brightness": "Scorri per la luminosità",
|
||||
"Slide along the left edge": "Scorri lungo il bordo sinistro"
|
||||
"Slide along the left edge": "Scorri lungo il bordo sinistro",
|
||||
"Failed to read cache": "Impossibile leggere la cache",
|
||||
"Failed to clear cache": "Impossibile svuotare la cache",
|
||||
"Calculating cache size...": "Calcolo della dimensione della cache…",
|
||||
"Cache cleared": "Cache svuotata",
|
||||
"{{count}} files_one": "{{count}} file",
|
||||
"{{count}} files_many": "{{count}} file",
|
||||
"{{count}} files_other": "{{count}} file",
|
||||
"Manage Cache": "Gestisci cache",
|
||||
"Deleting: {{file}}": "Eliminazione: {{file}}",
|
||||
"Clearing cache...": "Svuotamento della cache…",
|
||||
"This will delete all cached files. This cannot be undone.": "Questa operazione eliminerà tutti i file memorizzati nella cache. L’azione non può essere annullata.",
|
||||
"Confirm Clear": "Conferma svuotamento",
|
||||
"Clearing...": "Svuotamento…",
|
||||
"Clear Cache": "Svuota cache"
|
||||
}
|
||||
|
||||
@@ -1647,5 +1647,17 @@
|
||||
"No bookmark yet": "ブックマークはまだありません",
|
||||
"Importing...": "インポートしています...",
|
||||
"Swipe for Brightness": "スワイプで明るさ調整",
|
||||
"Slide along the left edge": "左端をスライド"
|
||||
"Slide along the left edge": "左端をスライド",
|
||||
"Failed to read cache": "キャッシュの読み込みに失敗しました",
|
||||
"Failed to clear cache": "キャッシュの削除に失敗しました",
|
||||
"Calculating cache size...": "キャッシュサイズを計算中…",
|
||||
"Cache cleared": "キャッシュを削除しました",
|
||||
"{{count}} files_other": "{{count}} 件のファイル",
|
||||
"Manage Cache": "キャッシュの管理",
|
||||
"Deleting: {{file}}": "削除中: {{file}}",
|
||||
"Clearing cache...": "キャッシュを削除中…",
|
||||
"This will delete all cached files. This cannot be undone.": "すべてのキャッシュファイルを削除します。この操作は取り消せません。",
|
||||
"Confirm Clear": "削除を確認",
|
||||
"Clearing...": "削除中…",
|
||||
"Clear Cache": "キャッシュを削除"
|
||||
}
|
||||
|
||||
@@ -1647,5 +1647,17 @@
|
||||
"No bookmark yet": "아직 북마크가 없습니다",
|
||||
"Importing...": "가져오는 중...",
|
||||
"Swipe for Brightness": "밝기 조절 스와이프",
|
||||
"Slide along the left edge": "왼쪽 가장자리를 따라 슬라이드"
|
||||
"Slide along the left edge": "왼쪽 가장자리를 따라 슬라이드",
|
||||
"Failed to read cache": "캐시를 읽지 못했습니다",
|
||||
"Failed to clear cache": "캐시를 지우지 못했습니다",
|
||||
"Calculating cache size...": "캐시 크기 계산 중…",
|
||||
"Cache cleared": "캐시가 지워졌습니다",
|
||||
"{{count}} files_other": "파일 {{count}}개",
|
||||
"Manage Cache": "캐시 관리",
|
||||
"Deleting: {{file}}": "삭제 중: {{file}}",
|
||||
"Clearing cache...": "캐시 지우는 중…",
|
||||
"This will delete all cached files. This cannot be undone.": "캐시된 모든 파일을 삭제합니다. 이 작업은 되돌릴 수 없습니다.",
|
||||
"Confirm Clear": "지우기 확인",
|
||||
"Clearing...": "지우는 중…",
|
||||
"Clear Cache": "캐시 지우기"
|
||||
}
|
||||
|
||||
@@ -1647,5 +1647,17 @@
|
||||
"No bookmark yet": "Belum ada tandabuku",
|
||||
"Importing...": "Mengimport...",
|
||||
"Swipe for Brightness": "Leret untuk kecerahan",
|
||||
"Slide along the left edge": "Leret di sepanjang tepi kiri"
|
||||
"Slide along the left edge": "Leret di sepanjang tepi kiri",
|
||||
"Failed to read cache": "Gagal membaca cache",
|
||||
"Failed to clear cache": "Gagal mengosongkan cache",
|
||||
"Calculating cache size...": "Mengira saiz cache…",
|
||||
"Cache cleared": "Cache dikosongkan",
|
||||
"{{count}} files_other": "{{count}} fail",
|
||||
"Manage Cache": "Urus Cache",
|
||||
"Deleting: {{file}}": "Memadam: {{file}}",
|
||||
"Clearing cache...": "Mengosongkan cache…",
|
||||
"This will delete all cached files. This cannot be undone.": "Tindakan ini akan memadam semua fail cache. Tindakan ini tidak boleh diundurkan.",
|
||||
"Confirm Clear": "Sahkan Pengosongan",
|
||||
"Clearing...": "Mengosongkan…",
|
||||
"Clear Cache": "Kosongkan Cache"
|
||||
}
|
||||
|
||||
@@ -1679,5 +1679,18 @@
|
||||
"No bookmark yet": "Nog geen bladwijzer",
|
||||
"Importing...": "Bezig met importeren...",
|
||||
"Swipe for Brightness": "Veeg voor helderheid",
|
||||
"Slide along the left edge": "Veeg langs de linkerrand"
|
||||
"Slide along the left edge": "Veeg langs de linkerrand",
|
||||
"Failed to read cache": "Kan cache niet lezen",
|
||||
"Failed to clear cache": "Kan cache niet wissen",
|
||||
"Calculating cache size...": "Cachegrootte berekenen…",
|
||||
"Cache cleared": "Cache gewist",
|
||||
"{{count}} files_one": "{{count}} bestand",
|
||||
"{{count}} files_other": "{{count}} bestanden",
|
||||
"Manage Cache": "Cache beheren",
|
||||
"Deleting: {{file}}": "Verwijderen: {{file}}",
|
||||
"Clearing cache...": "Cache wissen…",
|
||||
"This will delete all cached files. This cannot be undone.": "Hiermee worden alle gecachte bestanden verwijderd. Dit kan niet ongedaan worden gemaakt.",
|
||||
"Confirm Clear": "Wissen bevestigen",
|
||||
"Clearing...": "Bezig met wissen…",
|
||||
"Clear Cache": "Cache wissen"
|
||||
}
|
||||
|
||||
@@ -1743,5 +1743,20 @@
|
||||
"No bookmark yet": "Brak zakładek",
|
||||
"Importing...": "Importowanie...",
|
||||
"Swipe for Brightness": "Przesuń, aby zmienić jasność",
|
||||
"Slide along the left edge": "Przesuń wzdłuż lewej krawędzi"
|
||||
"Slide along the left edge": "Przesuń wzdłuż lewej krawędzi",
|
||||
"Failed to read cache": "Nie udało się odczytać pamięci podręcznej",
|
||||
"Failed to clear cache": "Nie udało się wyczyścić pamięci podręcznej",
|
||||
"Calculating cache size...": "Obliczanie rozmiaru pamięci podręcznej…",
|
||||
"Cache cleared": "Wyczyszczono pamięć podręczną",
|
||||
"{{count}} files_one": "{{count}} plik",
|
||||
"{{count}} files_few": "{{count}} pliki",
|
||||
"{{count}} files_many": "{{count}} plików",
|
||||
"{{count}} files_other": "{{count}} pliku",
|
||||
"Manage Cache": "Zarządzaj pamięcią podręczną",
|
||||
"Deleting: {{file}}": "Usuwanie: {{file}}",
|
||||
"Clearing cache...": "Czyszczenie pamięci podręcznej…",
|
||||
"This will delete all cached files. This cannot be undone.": "Spowoduje to usunięcie wszystkich plików z pamięci podręcznej. Tej operacji nie można cofnąć.",
|
||||
"Confirm Clear": "Potwierdź czyszczenie",
|
||||
"Clearing...": "Czyszczenie…",
|
||||
"Clear Cache": "Wyczyść pamięć podręczną"
|
||||
}
|
||||
|
||||
@@ -1711,5 +1711,19 @@
|
||||
"No bookmark yet": "Nenhum marcador ainda",
|
||||
"Importing...": "Importando...",
|
||||
"Swipe for Brightness": "Deslizar para o brilho",
|
||||
"Slide along the left edge": "Deslize pela borda esquerda"
|
||||
"Slide along the left edge": "Deslize pela borda esquerda",
|
||||
"Failed to read cache": "Falha ao ler o cache",
|
||||
"Failed to clear cache": "Falha ao limpar o cache",
|
||||
"Calculating cache size...": "Calculando o tamanho do cache…",
|
||||
"Cache cleared": "Cache limpo",
|
||||
"{{count}} files_one": "{{count}} arquivo",
|
||||
"{{count}} files_many": "{{count}} arquivos",
|
||||
"{{count}} files_other": "{{count}} arquivos",
|
||||
"Manage Cache": "Gerenciar cache",
|
||||
"Deleting: {{file}}": "Excluindo: {{file}}",
|
||||
"Clearing cache...": "Limpando o cache…",
|
||||
"This will delete all cached files. This cannot be undone.": "Isso excluirá todos os arquivos em cache. Esta ação não pode ser desfeita.",
|
||||
"Confirm Clear": "Confirmar limpeza",
|
||||
"Clearing...": "Limpando…",
|
||||
"Clear Cache": "Limpar cache"
|
||||
}
|
||||
|
||||
@@ -1711,5 +1711,19 @@
|
||||
"No bookmark yet": "Ainda não há marcadores",
|
||||
"Importing...": "Importando...",
|
||||
"Swipe for Brightness": "Deslizar para o brilho",
|
||||
"Slide along the left edge": "Deslize pela borda esquerda"
|
||||
"Slide along the left edge": "Deslize pela borda esquerda",
|
||||
"Failed to read cache": "Falha ao ler a cache",
|
||||
"Failed to clear cache": "Falha ao limpar a cache",
|
||||
"Calculating cache size...": "A calcular o tamanho da cache…",
|
||||
"Cache cleared": "Cache limpa",
|
||||
"{{count}} files_one": "{{count}} ficheiro",
|
||||
"{{count}} files_many": "{{count}} ficheiros",
|
||||
"{{count}} files_other": "{{count}} ficheiros",
|
||||
"Manage Cache": "Gerir cache",
|
||||
"Deleting: {{file}}": "A eliminar: {{file}}",
|
||||
"Clearing cache...": "A limpar a cache…",
|
||||
"This will delete all cached files. This cannot be undone.": "Isto irá eliminar todos os ficheiros em cache. Esta ação não pode ser anulada.",
|
||||
"Confirm Clear": "Confirmar limpeza",
|
||||
"Clearing...": "A limpar…",
|
||||
"Clear Cache": "Limpar cache"
|
||||
}
|
||||
|
||||
@@ -1711,5 +1711,19 @@
|
||||
"No bookmark yet": "Niciun marcaj încă",
|
||||
"Importing...": "Se importă...",
|
||||
"Swipe for Brightness": "Glisează pentru luminozitate",
|
||||
"Slide along the left edge": "Glisează de-a lungul marginii stângi"
|
||||
"Slide along the left edge": "Glisează de-a lungul marginii stângi",
|
||||
"Failed to read cache": "Citirea memoriei cache a eșuat",
|
||||
"Failed to clear cache": "Golirea memoriei cache a eșuat",
|
||||
"Calculating cache size...": "Se calculează dimensiunea memoriei cache…",
|
||||
"Cache cleared": "Memoria cache a fost golită",
|
||||
"{{count}} files_one": "{{count}} fișier",
|
||||
"{{count}} files_few": "{{count}} fișiere",
|
||||
"{{count}} files_other": "{{count}} de fișiere",
|
||||
"Manage Cache": "Gestionează memoria cache",
|
||||
"Deleting: {{file}}": "Se șterge: {{file}}",
|
||||
"Clearing cache...": "Se golește memoria cache…",
|
||||
"This will delete all cached files. This cannot be undone.": "Această acțiune va șterge toate fișierele din memoria cache. Acțiunea nu poate fi anulată.",
|
||||
"Confirm Clear": "Confirmă golirea",
|
||||
"Clearing...": "Se golește…",
|
||||
"Clear Cache": "Golește memoria cache"
|
||||
}
|
||||
|
||||
@@ -1743,5 +1743,20 @@
|
||||
"No bookmark yet": "Пока нет закладок",
|
||||
"Importing...": "Импорт...",
|
||||
"Swipe for Brightness": "Свайп для яркости",
|
||||
"Slide along the left edge": "Проведите вдоль левого края"
|
||||
"Slide along the left edge": "Проведите вдоль левого края",
|
||||
"Failed to read cache": "Не удалось прочитать кэш",
|
||||
"Failed to clear cache": "Не удалось очистить кэш",
|
||||
"Calculating cache size...": "Вычисление размера кэша…",
|
||||
"Cache cleared": "Кэш очищен",
|
||||
"{{count}} files_one": "{{count}} файл",
|
||||
"{{count}} files_few": "{{count}} файла",
|
||||
"{{count}} files_many": "{{count}} файлов",
|
||||
"{{count}} files_other": "{{count}} файла",
|
||||
"Manage Cache": "Управление кэшем",
|
||||
"Deleting: {{file}}": "Удаление: {{file}}",
|
||||
"Clearing cache...": "Очистка кэша…",
|
||||
"This will delete all cached files. This cannot be undone.": "Это удалит все файлы из кэша. Это действие нельзя отменить.",
|
||||
"Confirm Clear": "Подтвердить очистку",
|
||||
"Clearing...": "Очистка…",
|
||||
"Clear Cache": "Очистить кэш"
|
||||
}
|
||||
|
||||
@@ -1679,5 +1679,18 @@
|
||||
"No bookmark yet": "තවම පොත් සලකුණක් නැත",
|
||||
"Importing...": "ආයාත වෙමින්...",
|
||||
"Swipe for Brightness": "දීප්තිය සඳහා ස්වයිප් කරන්න",
|
||||
"Slide along the left edge": "වම් කෙළවර දිගේ අදින්න"
|
||||
"Slide along the left edge": "වම් කෙළවර දිගේ අදින්න",
|
||||
"Failed to read cache": "හැඹිලිය කියවීමට අසමත් විය",
|
||||
"Failed to clear cache": "හැඹිලිය හිස් කිරීමට අසමත් විය",
|
||||
"Calculating cache size...": "හැඹිලි ප්රමාණය ගණනය කරමින්…",
|
||||
"Cache cleared": "හැඹිලිය හිස් කරන ලදී",
|
||||
"{{count}} files_one": "ගොනු {{count}}",
|
||||
"{{count}} files_other": "ගොනු {{count}}",
|
||||
"Manage Cache": "හැඹිලිය කළමනාකරණය",
|
||||
"Deleting: {{file}}": "මකමින්: {{file}}",
|
||||
"Clearing cache...": "හැඹිලිය හිස් කරමින්…",
|
||||
"This will delete all cached files. This cannot be undone.": "මෙය සියලුම හැඹිලිගත ගොනු මකා දමයි. මෙය පෙරළිය නොහැක.",
|
||||
"Confirm Clear": "හිස් කිරීම තහවුරු කරන්න",
|
||||
"Clearing...": "හිස් කරමින්…",
|
||||
"Clear Cache": "හැඹිලිය හිස් කරන්න"
|
||||
}
|
||||
|
||||
@@ -1743,5 +1743,20 @@
|
||||
"No bookmark yet": "Še ni zaznamka",
|
||||
"Importing...": "Uvažanje ...",
|
||||
"Swipe for Brightness": "Poteg za svetlost",
|
||||
"Slide along the left edge": "Povlecite ob levem robu"
|
||||
"Slide along the left edge": "Povlecite ob levem robu",
|
||||
"Failed to read cache": "Predpomnilnika ni bilo mogoče prebrati",
|
||||
"Failed to clear cache": "Predpomnilnika ni bilo mogoče počistiti",
|
||||
"Calculating cache size...": "Računanje velikosti predpomnilnika…",
|
||||
"Cache cleared": "Predpomnilnik počiščen",
|
||||
"{{count}} files_one": "{{count}} datoteka",
|
||||
"{{count}} files_two": "{{count}} datoteki",
|
||||
"{{count}} files_few": "{{count}} datoteke",
|
||||
"{{count}} files_other": "{{count}} datotek",
|
||||
"Manage Cache": "Upravljanje predpomnilnika",
|
||||
"Deleting: {{file}}": "Brisanje: {{file}}",
|
||||
"Clearing cache...": "Čiščenje predpomnilnika…",
|
||||
"This will delete all cached files. This cannot be undone.": "To bo izbrisalo vse predpomnjene datoteke. Tega dejanja ni mogoče razveljaviti.",
|
||||
"Confirm Clear": "Potrdi čiščenje",
|
||||
"Clearing...": "Čiščenje…",
|
||||
"Clear Cache": "Počisti predpomnilnik"
|
||||
}
|
||||
|
||||
@@ -1679,5 +1679,18 @@
|
||||
"No bookmark yet": "Inget bokmärke än",
|
||||
"Importing...": "Importerar...",
|
||||
"Swipe for Brightness": "Svep för ljusstyrka",
|
||||
"Slide along the left edge": "Dra längs vänsterkanten"
|
||||
"Slide along the left edge": "Dra längs vänsterkanten",
|
||||
"Failed to read cache": "Det gick inte att läsa cachen",
|
||||
"Failed to clear cache": "Det gick inte att rensa cachen",
|
||||
"Calculating cache size...": "Beräknar cachestorlek…",
|
||||
"Cache cleared": "Cachen rensad",
|
||||
"{{count}} files_one": "{{count}} fil",
|
||||
"{{count}} files_other": "{{count}} filer",
|
||||
"Manage Cache": "Hantera cache",
|
||||
"Deleting: {{file}}": "Tar bort: {{file}}",
|
||||
"Clearing cache...": "Rensar cache…",
|
||||
"This will delete all cached files. This cannot be undone.": "Detta tar bort alla cachade filer. Åtgärden kan inte ångras.",
|
||||
"Confirm Clear": "Bekräfta rensning",
|
||||
"Clearing...": "Rensar…",
|
||||
"Clear Cache": "Rensa cache"
|
||||
}
|
||||
|
||||
@@ -1679,5 +1679,18 @@
|
||||
"No bookmark yet": "இன்னும் புக்மார்க் இல்லை",
|
||||
"Importing...": "இறக்குமதி செய்கிறது...",
|
||||
"Swipe for Brightness": "ஒளிர்வுக்கு ஸ்வைப் செய்யவும்",
|
||||
"Slide along the left edge": "இடது விளிம்பில் இழுக்கவும்"
|
||||
"Slide along the left edge": "இடது விளிம்பில் இழுக்கவும்",
|
||||
"Failed to read cache": "தற்காலிக சேமிப்பைப் படிக்க முடியவில்லை",
|
||||
"Failed to clear cache": "தற்காலிக சேமிப்பை அழிக்க முடியவில்லை",
|
||||
"Calculating cache size...": "தற்காலிக சேமிப்பின் அளவு கணக்கிடப்படுகிறது…",
|
||||
"Cache cleared": "தற்காலிக சேமிப்பு அழிக்கப்பட்டது",
|
||||
"{{count}} files_one": "{{count}} கோப்பு",
|
||||
"{{count}} files_other": "{{count}} கோப்புகள்",
|
||||
"Manage Cache": "தற்காலிக சேமிப்பை நிர்வகி",
|
||||
"Deleting: {{file}}": "நீக்கப்படுகிறது: {{file}}",
|
||||
"Clearing cache...": "தற்காலிக சேமிப்பு அழிக்கப்படுகிறது…",
|
||||
"This will delete all cached files. This cannot be undone.": "இது தற்காலிகச் சேமிப்பில் உள்ள அனைத்துக் கோப்புகளையும் நீக்கும். இதை மீட்டெடுக்க முடியாது.",
|
||||
"Confirm Clear": "அழிப்பதை உறுதிப்படுத்து",
|
||||
"Clearing...": "அழிக்கப்படுகிறது…",
|
||||
"Clear Cache": "தற்காலிக சேமிப்பை அழி"
|
||||
}
|
||||
|
||||
@@ -1647,5 +1647,17 @@
|
||||
"No bookmark yet": "ยังไม่มีบุ๊กมาร์ก",
|
||||
"Importing...": "กำลังนำเข้า...",
|
||||
"Swipe for Brightness": "ปัดเพื่อปรับความสว่าง",
|
||||
"Slide along the left edge": "เลื่อนตามขอบด้านซ้าย"
|
||||
"Slide along the left edge": "เลื่อนตามขอบด้านซ้าย",
|
||||
"Failed to read cache": "อ่านแคชไม่สำเร็จ",
|
||||
"Failed to clear cache": "ล้างแคชไม่สำเร็จ",
|
||||
"Calculating cache size...": "กำลังคำนวณขนาดแคช…",
|
||||
"Cache cleared": "ล้างแคชแล้ว",
|
||||
"{{count}} files_other": "{{count}} ไฟล์",
|
||||
"Manage Cache": "จัดการแคช",
|
||||
"Deleting: {{file}}": "กำลังลบ: {{file}}",
|
||||
"Clearing cache...": "กำลังล้างแคช…",
|
||||
"This will delete all cached files. This cannot be undone.": "การดำเนินการนี้จะลบไฟล์แคชทั้งหมด ไม่สามารถยกเลิกได้",
|
||||
"Confirm Clear": "ยืนยันการล้าง",
|
||||
"Clearing...": "กำลังล้าง…",
|
||||
"Clear Cache": "ล้างแคช"
|
||||
}
|
||||
|
||||
@@ -1679,5 +1679,18 @@
|
||||
"No bookmark yet": "Henüz yer imi yok",
|
||||
"Importing...": "İçe aktarılıyor...",
|
||||
"Swipe for Brightness": "Parlaklık için kaydır",
|
||||
"Slide along the left edge": "Sol kenar boyunca kaydır"
|
||||
"Slide along the left edge": "Sol kenar boyunca kaydır",
|
||||
"Failed to read cache": "Önbellek okunamadı",
|
||||
"Failed to clear cache": "Önbellek temizlenemedi",
|
||||
"Calculating cache size...": "Önbellek boyutu hesaplanıyor…",
|
||||
"Cache cleared": "Önbellek temizlendi",
|
||||
"{{count}} files_one": "{{count}} dosya",
|
||||
"{{count}} files_other": "{{count}} dosya",
|
||||
"Manage Cache": "Önbelleği Yönet",
|
||||
"Deleting: {{file}}": "Siliniyor: {{file}}",
|
||||
"Clearing cache...": "Önbellek temizleniyor…",
|
||||
"This will delete all cached files. This cannot be undone.": "Bu, önbelleğe alınmış tüm dosyaları siler. Bu işlem geri alınamaz.",
|
||||
"Confirm Clear": "Temizlemeyi Onayla",
|
||||
"Clearing...": "Temizleniyor…",
|
||||
"Clear Cache": "Önbelleği Temizle"
|
||||
}
|
||||
|
||||
@@ -1743,5 +1743,20 @@
|
||||
"No bookmark yet": "Поки немає закладок",
|
||||
"Importing...": "Імпорт...",
|
||||
"Swipe for Brightness": "Свайп для яскравості",
|
||||
"Slide along the left edge": "Проведіть уздовж лівого краю"
|
||||
"Slide along the left edge": "Проведіть уздовж лівого краю",
|
||||
"Failed to read cache": "Не вдалося прочитати кеш",
|
||||
"Failed to clear cache": "Не вдалося очистити кеш",
|
||||
"Calculating cache size...": "Обчислення розміру кешу…",
|
||||
"Cache cleared": "Кеш очищено",
|
||||
"{{count}} files_one": "{{count}} файл",
|
||||
"{{count}} files_few": "{{count}} файли",
|
||||
"{{count}} files_many": "{{count}} файлів",
|
||||
"{{count}} files_other": "{{count}} файлу",
|
||||
"Manage Cache": "Керування кешем",
|
||||
"Deleting: {{file}}": "Видалення: {{file}}",
|
||||
"Clearing cache...": "Очищення кешу…",
|
||||
"This will delete all cached files. This cannot be undone.": "Це видалить усі файли з кешу. Цю дію не можна скасувати.",
|
||||
"Confirm Clear": "Підтвердити очищення",
|
||||
"Clearing...": "Очищення…",
|
||||
"Clear Cache": "Очистити кеш"
|
||||
}
|
||||
|
||||
@@ -1679,5 +1679,18 @@
|
||||
"No bookmark yet": "Hali xatchoʻp yoʻq",
|
||||
"Importing...": "Import qilinmoqda...",
|
||||
"Swipe for Brightness": "Yorqinlik uchun suring",
|
||||
"Slide along the left edge": "Chap chekka bo'ylab suring"
|
||||
"Slide along the left edge": "Chap chekka bo'ylab suring",
|
||||
"Failed to read cache": "Keshni o‘qib bo‘lmadi",
|
||||
"Failed to clear cache": "Keshni tozalab bo‘lmadi",
|
||||
"Calculating cache size...": "Kesh hajmi hisoblanmoqda…",
|
||||
"Cache cleared": "Kesh tozalandi",
|
||||
"{{count}} files_one": "{{count}} ta fayl",
|
||||
"{{count}} files_other": "{{count}} ta fayl",
|
||||
"Manage Cache": "Keshni boshqarish",
|
||||
"Deleting: {{file}}": "O‘chirilmoqda: {{file}}",
|
||||
"Clearing cache...": "Kesh tozalanmoqda…",
|
||||
"This will delete all cached files. This cannot be undone.": "Bu barcha keshlangan fayllarni o‘chiradi. Buni qaytarib bo‘lmaydi.",
|
||||
"Confirm Clear": "Tozalashni tasdiqlash",
|
||||
"Clearing...": "Tozalanmoqda…",
|
||||
"Clear Cache": "Keshni tozalash"
|
||||
}
|
||||
|
||||
@@ -1647,5 +1647,17 @@
|
||||
"No bookmark yet": "Chưa có dấu trang",
|
||||
"Importing...": "Đang nhập...",
|
||||
"Swipe for Brightness": "Vuốt để chỉnh độ sáng",
|
||||
"Slide along the left edge": "Vuốt dọc theo cạnh trái"
|
||||
"Slide along the left edge": "Vuốt dọc theo cạnh trái",
|
||||
"Failed to read cache": "Không thể đọc bộ nhớ đệm",
|
||||
"Failed to clear cache": "Không thể xóa bộ nhớ đệm",
|
||||
"Calculating cache size...": "Đang tính kích thước bộ nhớ đệm…",
|
||||
"Cache cleared": "Đã xóa bộ nhớ đệm",
|
||||
"{{count}} files_other": "{{count}} tệp",
|
||||
"Manage Cache": "Quản lý bộ nhớ đệm",
|
||||
"Deleting: {{file}}": "Đang xóa: {{file}}",
|
||||
"Clearing cache...": "Đang xóa bộ nhớ đệm…",
|
||||
"This will delete all cached files. This cannot be undone.": "Thao tác này sẽ xóa tất cả tệp trong bộ nhớ đệm. Không thể hoàn tác.",
|
||||
"Confirm Clear": "Xác nhận xóa",
|
||||
"Clearing...": "Đang xóa…",
|
||||
"Clear Cache": "Xóa bộ nhớ đệm"
|
||||
}
|
||||
|
||||
@@ -1647,5 +1647,17 @@
|
||||
"No bookmark yet": "尚无书签",
|
||||
"Importing...": "正在导入...",
|
||||
"Swipe for Brightness": "滑动调节亮度",
|
||||
"Slide along the left edge": "沿左侧边缘滑动"
|
||||
"Slide along the left edge": "沿左侧边缘滑动",
|
||||
"Failed to read cache": "读取缓存失败",
|
||||
"Failed to clear cache": "清除缓存失败",
|
||||
"Calculating cache size...": "正在计算缓存大小…",
|
||||
"Cache cleared": "缓存已清除",
|
||||
"{{count}} files_other": "{{count}} 个文件",
|
||||
"Manage Cache": "管理缓存",
|
||||
"Deleting: {{file}}": "正在删除:{{file}}",
|
||||
"Clearing cache...": "正在清除缓存…",
|
||||
"This will delete all cached files. This cannot be undone.": "这将删除所有缓存文件,此操作无法撤销。",
|
||||
"Confirm Clear": "确认清除",
|
||||
"Clearing...": "正在清除…",
|
||||
"Clear Cache": "清除缓存"
|
||||
}
|
||||
|
||||
@@ -1647,5 +1647,17 @@
|
||||
"No bookmark yet": "尚無書籤",
|
||||
"Importing...": "正在匯入...",
|
||||
"Swipe for Brightness": "滑動調整亮度",
|
||||
"Slide along the left edge": "沿左側邊緣滑動"
|
||||
"Slide along the left edge": "沿左側邊緣滑動",
|
||||
"Failed to read cache": "讀取快取失敗",
|
||||
"Failed to clear cache": "清除快取失敗",
|
||||
"Calculating cache size...": "正在計算快取大小…",
|
||||
"Cache cleared": "快取已清除",
|
||||
"{{count}} files_other": "{{count}} 個檔案",
|
||||
"Manage Cache": "管理快取",
|
||||
"Deleting: {{file}}": "正在刪除:{{file}}",
|
||||
"Clearing cache...": "正在清除快取…",
|
||||
"This will delete all cached files. This cannot be undone.": "這將刪除所有快取檔案,此操作無法復原。",
|
||||
"Confirm Clear": "確認清除",
|
||||
"Clearing...": "正在清除…",
|
||||
"Clear Cache": "清除快取"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { AppService, FileItem } from '@/types/system';
|
||||
import {
|
||||
clearCacheEntries,
|
||||
getCacheEntries,
|
||||
getCacheStats,
|
||||
CacheClearProgress,
|
||||
CacheEntry,
|
||||
} from '@/utils/cache';
|
||||
|
||||
const makeFiles = (...names: string[]): FileItem[] =>
|
||||
names.map((path, i) => ({ path, size: (i + 1) * 10 }));
|
||||
|
||||
describe('getCacheStats', () => {
|
||||
it('sums file count and byte size', () => {
|
||||
const entries: CacheEntry[] = [
|
||||
{ base: 'Cache', path: 'a', size: 10 },
|
||||
{ base: 'Cache', path: 'b', size: 20 },
|
||||
{ base: 'None', path: '/Inbox/c', size: 30 },
|
||||
];
|
||||
expect(getCacheStats(entries)).toEqual({ count: 3, size: 60 });
|
||||
});
|
||||
|
||||
it('returns zeros for an empty cache', () => {
|
||||
expect(getCacheStats([])).toEqual({ count: 0, size: 0 });
|
||||
});
|
||||
});
|
||||
|
||||
describe('getCacheEntries', () => {
|
||||
it('reads the Cache base root with base-relative paths', async () => {
|
||||
const readDirectory = vi.fn().mockResolvedValue(makeFiles('x.json', 'y.epub'));
|
||||
const appService = { readDirectory } as unknown as AppService;
|
||||
|
||||
const entries = await getCacheEntries(appService, [{ base: 'Cache', dir: '' }]);
|
||||
|
||||
expect(readDirectory).toHaveBeenCalledWith('', 'Cache');
|
||||
expect(entries).toEqual([
|
||||
{ base: 'Cache', path: 'x.json', size: 10 },
|
||||
{ base: 'Cache', path: 'y.epub', size: 20 },
|
||||
]);
|
||||
});
|
||||
|
||||
it('prefixes a non-root source dir so paths are directly deletable (iOS Inbox)', async () => {
|
||||
const readDirectory = vi.fn().mockResolvedValue(makeFiles('book.epub'));
|
||||
const appService = { readDirectory } as unknown as AppService;
|
||||
|
||||
const entries = await getCacheEntries(appService, [
|
||||
{ base: 'None', dir: '/var/mobile/.../Documents/Inbox' },
|
||||
]);
|
||||
|
||||
expect(readDirectory).toHaveBeenCalledWith('/var/mobile/.../Documents/Inbox', 'None');
|
||||
expect(entries).toEqual([
|
||||
{ base: 'None', path: '/var/mobile/.../Documents/Inbox/book.epub', size: 10 },
|
||||
]);
|
||||
});
|
||||
|
||||
it('merges multiple sources and skips unreadable ones', async () => {
|
||||
const readDirectory = vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce(makeFiles('cache.json'))
|
||||
.mockRejectedValueOnce(new Error('no inbox'));
|
||||
const appService = { readDirectory } as unknown as AppService;
|
||||
|
||||
const entries = await getCacheEntries(appService, [
|
||||
{ base: 'Cache', dir: '' },
|
||||
{ base: 'None', dir: '/Inbox' },
|
||||
]);
|
||||
|
||||
expect(entries).toEqual([{ base: 'Cache', path: 'cache.json', size: 10 }]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('clearCacheEntries', () => {
|
||||
it('deletes every entry with its own base and reports progress', async () => {
|
||||
const entries: CacheEntry[] = [
|
||||
{ base: 'Cache', path: 'a.json', size: 10 },
|
||||
{ base: 'Cache', path: 'b.epub', size: 20 },
|
||||
{ base: 'None', path: '/Inbox/c.epub', size: 30 },
|
||||
];
|
||||
const deleteFile = vi.fn().mockResolvedValue(undefined);
|
||||
const appService = { deleteFile } as unknown as AppService;
|
||||
const progress: CacheClearProgress[] = [];
|
||||
|
||||
const result = await clearCacheEntries(appService, entries, (p) => progress.push(p));
|
||||
|
||||
expect(result).toEqual({ deleted: 3, failed: 0 });
|
||||
expect(deleteFile).toHaveBeenCalledWith('a.json', 'Cache');
|
||||
expect(deleteFile).toHaveBeenCalledWith('/Inbox/c.epub', 'None');
|
||||
expect(progress).toEqual([
|
||||
{ current: 1, total: 3, currentFile: 'a.json' },
|
||||
{ current: 2, total: 3, currentFile: 'b.epub' },
|
||||
{ current: 3, total: 3, currentFile: '/Inbox/c.epub' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('counts failures without aborting the loop', async () => {
|
||||
const entries: CacheEntry[] = [
|
||||
{ base: 'Cache', path: 'a', size: 1 },
|
||||
{ base: 'Cache', path: 'b', size: 1 },
|
||||
{ base: 'Cache', path: 'c', size: 1 },
|
||||
];
|
||||
const deleteFile = vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce(undefined)
|
||||
.mockRejectedValueOnce(new Error('locked'))
|
||||
.mockResolvedValueOnce(undefined);
|
||||
const appService = { deleteFile } as unknown as AppService;
|
||||
|
||||
const result = await clearCacheEntries(appService, entries);
|
||||
|
||||
expect(result).toEqual({ deleted: 2, failed: 1 });
|
||||
expect(deleteFile).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,261 @@
|
||||
import clsx from 'clsx';
|
||||
import { useEffect, useState } from 'react';
|
||||
import {
|
||||
RiDatabase2Line,
|
||||
RiCheckboxCircleFill,
|
||||
RiErrorWarningFill,
|
||||
RiLoader2Line,
|
||||
} from 'react-icons/ri';
|
||||
import { documentDir, join } from '@tauri-apps/api/path';
|
||||
import { useEnv } from '@/context/EnvContext';
|
||||
import { useTranslation } from '@/hooks/useTranslation';
|
||||
import { formatBytes } from '@/utils/book';
|
||||
import {
|
||||
clearCacheEntries,
|
||||
getCacheEntries,
|
||||
getCacheStats,
|
||||
CacheClearProgress,
|
||||
CacheSource,
|
||||
} from '@/utils/cache';
|
||||
import { AppService } from '@/types/system';
|
||||
import Dialog from '@/components/Dialog';
|
||||
|
||||
export const setCacheManagerDialogVisible = (visible: boolean) => {
|
||||
const dialog = document.getElementById('cache_manager_window');
|
||||
if (dialog) {
|
||||
const event = new CustomEvent('setDialogVisibility', {
|
||||
detail: { visible },
|
||||
});
|
||||
dialog.dispatchEvent(event);
|
||||
}
|
||||
};
|
||||
|
||||
type CacheStatus = 'scanning' | 'idle' | 'confirming' | 'clearing' | 'done' | 'error';
|
||||
|
||||
/**
|
||||
* Locations Manage Cache clears (mobile only). Both iOS and Android clear the
|
||||
* app Cache and Temp bases; iOS additionally clears the `Documents/Inbox`
|
||||
* folder, where "Open in Readest" leaves already-imported book copies that
|
||||
* otherwise linger forever.
|
||||
*/
|
||||
const getCacheSources = async (appService: AppService): Promise<CacheSource[]> => {
|
||||
const sources: CacheSource[] = [
|
||||
{ base: 'Cache', dir: '' },
|
||||
{ base: 'Temp', dir: '' },
|
||||
];
|
||||
if (appService.isIOSApp) {
|
||||
try {
|
||||
const inboxDir = await join(await documentDir(), 'Inbox');
|
||||
sources.push({ base: 'None', dir: inboxDir });
|
||||
} catch {
|
||||
// Documents dir unavailable — skip the inbox source.
|
||||
}
|
||||
}
|
||||
return sources;
|
||||
};
|
||||
|
||||
export const CacheManagerWindow = () => {
|
||||
const _ = useTranslation();
|
||||
const { appService } = useEnv();
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [status, setStatus] = useState<CacheStatus>('scanning');
|
||||
const [count, setCount] = useState(0);
|
||||
const [size, setSize] = useState(0);
|
||||
const [progress, setProgress] = useState<CacheClearProgress>({ current: 0, total: 0 });
|
||||
const [errorMessage, setErrorMessage] = useState('');
|
||||
|
||||
const scanCache = async () => {
|
||||
if (!appService) return;
|
||||
setStatus('scanning');
|
||||
setErrorMessage('');
|
||||
try {
|
||||
const entries = await getCacheEntries(appService, await getCacheSources(appService));
|
||||
const stats = getCacheStats(entries);
|
||||
setCount(stats.count);
|
||||
setSize(stats.size);
|
||||
setStatus('idle');
|
||||
} catch (error) {
|
||||
console.error('Error scanning cache:', error);
|
||||
setErrorMessage(_('Failed to read cache'));
|
||||
setStatus('error');
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const handleCustomEvent = (event: CustomEvent) => {
|
||||
setIsOpen(event.detail.visible);
|
||||
if (event.detail.visible) {
|
||||
scanCache();
|
||||
}
|
||||
};
|
||||
|
||||
const el = document.getElementById('cache_manager_window');
|
||||
if (el) {
|
||||
el.addEventListener('setDialogVisibility', handleCustomEvent as EventListener);
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (el) {
|
||||
el.removeEventListener('setDialogVisibility', handleCustomEvent as EventListener);
|
||||
}
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [appService]);
|
||||
|
||||
const handleClearClick = () => {
|
||||
if (status === 'idle' && count > 0) {
|
||||
setStatus('confirming');
|
||||
}
|
||||
};
|
||||
|
||||
const handleConfirmClear = async () => {
|
||||
if (!appService) return;
|
||||
setStatus('clearing');
|
||||
setProgress({ current: 0, total: 0 });
|
||||
try {
|
||||
const entries = await getCacheEntries(appService, await getCacheSources(appService));
|
||||
await clearCacheEntries(appService, entries, setProgress);
|
||||
await scanCache();
|
||||
setStatus('done');
|
||||
} catch (error) {
|
||||
console.error('Error clearing cache:', error);
|
||||
setErrorMessage(_('Failed to clear cache'));
|
||||
setStatus('error');
|
||||
}
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
if (status === 'clearing') {
|
||||
return;
|
||||
}
|
||||
setIsOpen(false);
|
||||
setStatus('scanning');
|
||||
setProgress({ current: 0, total: 0 });
|
||||
setErrorMessage('');
|
||||
};
|
||||
|
||||
const progressPercentage =
|
||||
progress.total > 0 ? Math.round((progress.current / progress.total) * 100) : 0;
|
||||
|
||||
const primaryBtn = 'btn btn-contrast h-11 min-h-0 rounded-xl text-sm font-medium';
|
||||
const ghostBtn = clsx(
|
||||
'eink-bordered flex h-11 items-center justify-center rounded-xl border border-transparent',
|
||||
'text-base-content hover:bg-base-200 text-sm font-medium transition-colors',
|
||||
'focus-visible:ring-base-content/15 focus-visible:outline-none focus-visible:ring-2',
|
||||
'disabled:opacity-40',
|
||||
);
|
||||
|
||||
const heroIcon =
|
||||
status === 'scanning' || status === 'clearing' ? (
|
||||
<RiLoader2Line className='h-7 w-7 animate-spin' />
|
||||
) : status === 'done' ? (
|
||||
<RiCheckboxCircleFill className='text-success h-8 w-8' />
|
||||
) : status === 'error' ? (
|
||||
<RiErrorWarningFill className='text-error h-7 w-7' />
|
||||
) : (
|
||||
<RiDatabase2Line className='h-7 w-7' />
|
||||
);
|
||||
|
||||
const heroCaption =
|
||||
status === 'scanning'
|
||||
? _('Calculating cache size...')
|
||||
: status === 'done'
|
||||
? _('Cache cleared')
|
||||
: status === 'error'
|
||||
? errorMessage
|
||||
: _('{{count}} files', { count });
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
id='cache_manager_window'
|
||||
isOpen={isOpen}
|
||||
title={_('Manage Cache')}
|
||||
onClose={handleClose}
|
||||
boxClassName='sm:!w-[440px] sm:!max-w-screen-sm sm:h-auto'
|
||||
>
|
||||
{isOpen && (
|
||||
<div className='cache-manager-content flex flex-col gap-7 px-2 pb-2 pt-3'>
|
||||
{/* Hero stat */}
|
||||
<div className='flex flex-col items-center gap-3 text-center'>
|
||||
<div
|
||||
className='eink-bordered bg-base-200 text-base-content/80 flex h-16 w-16 items-center justify-center rounded-full'
|
||||
aria-hidden='true'
|
||||
>
|
||||
{heroIcon}
|
||||
</div>
|
||||
<div className='flex flex-col items-center gap-1'>
|
||||
<span className='text-base-content text-3xl font-bold tracking-tight tabular-nums'>
|
||||
{status === 'scanning' ? '—' : formatBytes(size)}
|
||||
</span>
|
||||
<span className='text-base-content/60 line-clamp-2 text-sm'>{heroCaption}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Clearing progress */}
|
||||
{status === 'clearing' && (
|
||||
<div className='space-y-2'>
|
||||
<div className='bg-base-200 h-1.5 w-full overflow-hidden rounded-full'>
|
||||
<div
|
||||
className='bg-base-content h-full rounded-full transition-all duration-300'
|
||||
style={{ width: `${progressPercentage}%` }}
|
||||
/>
|
||||
</div>
|
||||
<div className='text-base-content/55 flex items-center justify-between gap-3 text-xs'>
|
||||
<span
|
||||
className='overflow-hidden font-mono'
|
||||
style={{
|
||||
direction: 'rtl',
|
||||
textAlign: 'left',
|
||||
whiteSpace: 'nowrap',
|
||||
textOverflow: 'ellipsis',
|
||||
}}
|
||||
>
|
||||
{progress.currentFile
|
||||
? _('Deleting: {{file}}', { file: progress.currentFile })
|
||||
: _('Clearing cache...')}
|
||||
</span>
|
||||
<span className='shrink-0 tabular-nums'>{progressPercentage}%</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Confirm notice */}
|
||||
{status === 'confirming' && (
|
||||
<p className='text-base-content/60 flex items-center justify-center gap-1.5 text-center text-[13px] leading-relaxed'>
|
||||
<RiErrorWarningFill className='text-warning h-4 w-4 shrink-0' aria-hidden='true' />
|
||||
{_('This will delete all cached files. This cannot be undone.')}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{/* Action buttons */}
|
||||
{status === 'done' ? (
|
||||
<button className={primaryBtn} onClick={handleClose}>
|
||||
{_('Close')}
|
||||
</button>
|
||||
) : (
|
||||
<div className='grid grid-cols-2 gap-2.5'>
|
||||
<button className={ghostBtn} onClick={handleClose} disabled={status === 'clearing'}>
|
||||
{_('Cancel')}
|
||||
</button>
|
||||
{status === 'confirming' ? (
|
||||
<button className={primaryBtn} onClick={handleConfirmClear}>
|
||||
{_('Confirm Clear')}
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
className={primaryBtn}
|
||||
onClick={handleClearClick}
|
||||
disabled={status !== 'idle' || count === 0}
|
||||
>
|
||||
{status === 'clearing' ? _('Clearing...') : _('Clear Cache')}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default CacheManagerWindow;
|
||||
@@ -10,6 +10,7 @@ import { invoke, PermissionState } from '@tauri-apps/api/core';
|
||||
import { isTauriAppPlatform, isWebAppPlatform } from '@/services/environment';
|
||||
import { DOWNLOAD_READEST_URL } from '@/services/constants';
|
||||
import { setBackupDialogVisible } from '@/app/library/components/BackupWindow';
|
||||
import { setCacheManagerDialogVisible } from '@/app/library/components/CacheManagerWindow';
|
||||
import { useAuth } from '@/context/AuthContext';
|
||||
import { useEnv } from '@/context/EnvContext';
|
||||
import { useThemeStore } from '@/store/themeStore';
|
||||
@@ -192,6 +193,11 @@ const SettingsMenu: React.FC<SettingsMenuProps> = ({ onPullLibrary, setIsDropdow
|
||||
setBackupDialogVisible(true);
|
||||
};
|
||||
|
||||
const handleManageCache = () => {
|
||||
setIsDropdownOpen?.(false);
|
||||
setCacheManagerDialogVisible(true);
|
||||
};
|
||||
|
||||
const handleRefreshMetadata = async () => {
|
||||
if (!appService || isRefreshingMetadata) return;
|
||||
setIsRefreshingMetadata(true);
|
||||
@@ -426,6 +432,9 @@ const SettingsMenu: React.FC<SettingsMenuProps> = ({ onPullLibrary, setIsDropdow
|
||||
onClick={handleRefreshMetadata}
|
||||
disabled={isRefreshingMetadata}
|
||||
/>
|
||||
{appService?.isMobileApp && (
|
||||
<MenuItem label={_('Manage Cache')} onClick={handleManageCache} />
|
||||
)}
|
||||
{!isPinEnabled && (
|
||||
<MenuItem
|
||||
label={_('Set PIN…')}
|
||||
|
||||
@@ -67,6 +67,7 @@ import { UpdaterWindow } from '@/components/UpdaterWindow';
|
||||
import { CatalogDialog } from './components/OPDSDialog';
|
||||
import { MigrateDataWindow } from './components/MigrateDataWindow';
|
||||
import { BackupWindow } from './components/BackupWindow';
|
||||
import { CacheManagerWindow } from './components/CacheManagerWindow';
|
||||
import { useDragDropImport } from './hooks/useDragDropImport';
|
||||
import { useTransferQueue } from '@/hooks/useTransferQueue';
|
||||
import { useAppRouter } from '@/hooks/useAppRouter';
|
||||
@@ -1468,6 +1469,7 @@ const LibraryPageContent = ({ searchParams }: { searchParams: ReadonlyURLSearchP
|
||||
<UpdaterWindow />
|
||||
<MigrateDataWindow />
|
||||
<BackupWindow onPullLibrary={pullLibrary} />
|
||||
<CacheManagerWindow />
|
||||
{isSettingsDialogOpen && <SettingsDialog bookKey={''} />}
|
||||
{showCatalogManager && <CatalogDialog onClose={handleDismissOPDSDialog} />}
|
||||
{failedImportsModal && (
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
import { AppService, BaseDir } from '@/types/system';
|
||||
|
||||
export interface CacheClearProgress {
|
||||
current: number;
|
||||
total: number;
|
||||
currentFile?: string;
|
||||
}
|
||||
|
||||
export interface CacheClearResult {
|
||||
deleted: number;
|
||||
failed: number;
|
||||
}
|
||||
|
||||
/** A cache location to scan and clear. */
|
||||
export interface CacheSource {
|
||||
base: BaseDir;
|
||||
/** Directory within `base` to scan; '' for the base root. */
|
||||
dir: string;
|
||||
}
|
||||
|
||||
/** A single deletable file, with a path usable directly by deleteFile(path, base). */
|
||||
export interface CacheEntry {
|
||||
base: BaseDir;
|
||||
path: string;
|
||||
size: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* List every file under the given cache sources as deletable entries. A source
|
||||
* that can't be read (e.g. an Inbox that doesn't exist yet) simply contributes
|
||||
* nothing instead of failing the whole scan.
|
||||
*/
|
||||
export const getCacheEntries = async (
|
||||
appService: AppService,
|
||||
sources: CacheSource[],
|
||||
): Promise<CacheEntry[]> => {
|
||||
const entries: CacheEntry[] = [];
|
||||
for (const source of sources) {
|
||||
try {
|
||||
const files = await appService.readDirectory(source.dir, source.base);
|
||||
for (const file of files) {
|
||||
entries.push({
|
||||
base: source.base,
|
||||
path: source.dir ? `${source.dir}/${file.path}` : file.path,
|
||||
size: file.size || 0,
|
||||
});
|
||||
}
|
||||
} catch {
|
||||
// Missing or unreadable source — skip it.
|
||||
}
|
||||
}
|
||||
return entries;
|
||||
};
|
||||
|
||||
/** Total file count and byte size for a set of cache entries. */
|
||||
export const getCacheStats = (entries: CacheEntry[]): { count: number; size: number } => ({
|
||||
count: entries.length,
|
||||
size: entries.reduce((acc, entry) => acc + entry.size, 0),
|
||||
});
|
||||
|
||||
/**
|
||||
* Delete the given cache entries one at a time, reporting progress before each
|
||||
* deletion. Individual failures are counted but never abort the loop, so a
|
||||
* single locked file can't leave the cache half-cleared without feedback.
|
||||
*/
|
||||
export const clearCacheEntries = async (
|
||||
appService: AppService,
|
||||
entries: CacheEntry[],
|
||||
onProgress?: (progress: CacheClearProgress) => void,
|
||||
): Promise<CacheClearResult> => {
|
||||
let deleted = 0;
|
||||
let failed = 0;
|
||||
for (let i = 0; i < entries.length; i++) {
|
||||
const entry = entries[i]!;
|
||||
onProgress?.({ current: i + 1, total: entries.length, currentFile: entry.path });
|
||||
try {
|
||||
await appService.deleteFile(entry.path, entry.base);
|
||||
deleted++;
|
||||
} catch {
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
return { deleted, failed };
|
||||
};
|
||||
Reference in New Issue
Block a user