fix(share): correct recipient import flow and assorted UI polish (#4043)

- ensureSharedBookLocal helper makes sure the local library has both the
  Book entry and the bytes on disk after /import succeeds; navigating
  into the reader before this lands on "Book not found"
- ShareLanding navigates via navigateToReader (path form on web) so the
  reader actually renders instead of hitting the App Router stub and
  going blank
- Loading + progress UI on the landing page while bytes stream in;
  Open-in-app disabled mid-import to avoid races
- UserInfo header: vertically center avatar with name/email, tighter
  mobile gap, and a fillContainer prop on UserAvatar so a parent can
  size the box via classes without the inline style fighting back
- Rename "Share current page" -> "Share reading progress" (and matching
  post-generation hint) and shrink the dialog from 480 to 460px
- Drop the unused Reload Page menu item from SettingsMenu
- Translate the two new i18n keys across 31 locales

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Huang Xin
2026-05-03 22:16:46 +08:00
committed by GitHub
parent 19f2414f42
commit f5657fb3a0
41 changed files with 491 additions and 201 deletions
@@ -306,7 +306,6 @@
"No notes match your search": "لا توجد ملاحظات تطابق بحثك",
"Search notes and excerpts...": "بحث في الملاحظات والمقتطفات...",
"Sign in to Sync": "قم بتسجيل الدخول للمزامنة",
"Synced at {{time}}": "تمت المزامنة في {{time}}",
"Never synced": "لم تتم المزامنة بعد",
"Show Remaining Time": "إظهار الوقت المتبقي",
"{{time}} min left in chapter": "{{time}} دقيقة متبقية حتى نهاية الفصل",
@@ -1275,7 +1274,6 @@
"{{count}} failed_many": "{{count}} فشل",
"{{count}} failed_other": "{{count}} فشل",
"(none)": "(لا شيء)",
"Metadata Hash": "تجزئة البيانات الوصفية",
"Meta Hash": "تجزئة البيانات الوصفية",
"Computed Hash": "التجزئة المحسوبة",
"Identifiers": "المعرفات",
@@ -1337,7 +1335,6 @@
"Uploading book…": "جاري رفع الكتاب…",
"Generating…": "جاري الإنشاء…",
"Generate share link": "إنشاء رابط مشاركة",
"Includes your current page": "يشمل صفحتك الحالية",
"Share URL": "رابط المشاركة",
"Copy link": "نسخ الرابط",
"Copied": "تم النسخ",
@@ -1385,7 +1382,6 @@
"Added to your library": "تمت الإضافة إلى مكتبتك",
"Could not import shared book": "تعذر استيراد الكتاب المشترك",
"Manage Shared Links": "إدارة روابط المشاركة",
"Share current page": "مشاركة الصفحة الحالية",
"Expires in": "تنتهي خلال",
"{{count}} days_zero": "لا توجد أيام",
"{{count}} days_one": "يوم واحد",
@@ -1406,5 +1402,12 @@
"Expires in {{count}} hours_many": "تنتهي خلال {{count}} ساعة",
"Expires in {{count}} hours_other": "تنتهي خلال {{count}} ساعة",
"Open in app": "فتح في التطبيق",
"Shared with you": "تمت مشاركته معك"
"Shared with you": "تمت مشاركته معك",
"Downloading… {{percent}}%": "جارٍ التنزيل… {{percent}}٪",
"Import progress": "تقدم الاستيراد",
"Share reading progress": "مشاركة تقدّم القراءة",
"Includes your reading progress": "يتضمن تقدّم قراءتك",
"Synced {{time}}": "تمت المزامنة {{time}}",
"Sync Info": "معلومات المزامنة",
"Last Synced": "آخر مزامنة"
}
@@ -330,7 +330,6 @@
"{{engine}}: {{count}} voices_one": "{{engine}}: ১টি কণ্ঠস্বর",
"{{engine}}: {{count}} voices_other": "{{engine}}: {{count}}টি কণ্ঠস্বর",
"Sign in to Sync": "সিঙ্কের জন্য সাইন ইন",
"Synced at {{time}}": "{{time}} এ সিঙ্ক হয়েছে",
"Never synced": "কখনো সিঙ্ক হয়নি",
"Reading Progress Synced": "পড়ার অগ্রগতি সিঙ্ক হয়েছে",
"Page {{page}} of {{total}} ({{percentage}}%)": "{{total}} এর {{page}} পৃষ্ঠা ({{percentage}}%)",
@@ -1207,7 +1206,6 @@
"{{count}} failed_one": "{{count}}টি ব্যর্থ",
"{{count}} failed_other": "{{count}}টি ব্যর্থ",
"(none)": "(কোনোটি না)",
"Metadata Hash": "মেটাডেটা হ্যাশ",
"Meta Hash": "মেটা হ্যাশ",
"Computed Hash": "গণিত হ্যাশ",
"Identifiers": "শনাক্তকারী",
@@ -1265,7 +1263,6 @@
"Uploading book…": "বই আপলোড হচ্ছে…",
"Generating…": "তৈরি হচ্ছে…",
"Generate share link": "শেয়ার লিঙ্ক তৈরি করুন",
"Includes your current page": "আপনার বর্তমান পৃষ্ঠা সহ",
"Share URL": "শেয়ার URL",
"Copy link": "লিঙ্ক কপি করুন",
"Copied": "কপি হয়েছে",
@@ -1305,7 +1302,6 @@
"Added to your library": "আপনার লাইব্রেরিতে যোগ করা হয়েছে",
"Could not import shared book": "শেয়ার করা বই আমদানি করা যায়নি",
"Manage Shared Links": "শেয়ার লিঙ্ক পরিচালনা করুন",
"Share current page": "বর্তমান পৃষ্ঠা শেয়ার করুন",
"Expires in": "মেয়াদ শেষ হবে",
"{{count}} days_one": "১ দিন",
"{{count}} days_other": "{{count}} দিন",
@@ -1314,5 +1310,12 @@
"Expires in {{count}} hours_one": "১ ঘণ্টায় মেয়াদ শেষ",
"Expires in {{count}} hours_other": "{{count}} ঘণ্টায় মেয়াদ শেষ",
"Open in app": "অ্যাপে খুলুন",
"Shared with you": "আপনার সাথে শেয়ার করা"
"Shared with you": "আপনার সাথে শেয়ার করা",
"Downloading… {{percent}}%": "ডাউনলোড হচ্ছে… {{percent}}%",
"Import progress": "আমদানি অগ্রগতি",
"Share reading progress": "পড়ার অগ্রগতি শেয়ার করুন",
"Includes your reading progress": "আপনার পড়ার অগ্রগতি অন্তর্ভুক্ত",
"Synced {{time}}": "{{time}} সিঙ্ক হয়েছে",
"Sync Info": "সিঙ্ক তথ্য",
"Last Synced": "শেষ সিঙ্ক"
}
@@ -296,7 +296,6 @@
"No notes match your search": "མཐུན་པའི་ཟིན་བྲིས་མ་རྙེད།",
"Search notes and excerpts...": "ཟིན་བྲིས་འཚོལ་བཤེར།...",
"Sign in to Sync": "ནང་བསྐྱོད་བྱས་རྗེས་སྐད་མཉམ་བྱེད་པ།",
"Synced at {{time}}": "སྐད་མཉམ་བྱས་པའི་དུས་ཚོད། {{time}}",
"Never synced": "ནམ་ཡང་སྐད་མཉམ་བྱས་མ་མྱོང་།",
"Show Remaining Time": "ལྷག་མའི་དུས་ཚོད་མངོན་པ།",
"{{time}} min left in chapter": "ལེའུ་འདིར་ད་དུང་ {{time}} སྐར་མ་ལྷག་ཡོད།",
@@ -1190,7 +1189,6 @@
"Retry all": "ཡོད་ཚད་ཡང་སྐྱར།",
"{{count}} failed_other": "{{count}} ཕམ་ཁ།",
"(none)": "(མེད)",
"Metadata Hash": "དཔྱད་གཞིའི་ཧེཤི",
"Meta Hash": "དཔྱད་གཞིའི་ཧེཤི",
"Computed Hash": "རྩིས་པའི་ཧེཤི",
"Identifiers": "ངོས་འཛིན་བྱེད་མཁན",
@@ -1247,7 +1245,6 @@
"Uploading book…": "དཔེ་ཆ་ཡར་སྐྱེལ་བཞིན་པ…",
"Generating…": "བཟོ་བཞིན་པ…",
"Generate share link": "མཉམ་སྤྱོད་འབྲེལ་མཐུད་བཟོ།",
"Includes your current page": "ཁྱེད་ཀྱི་ད་ལྟའི་ཤོག་ངོས་འདུས།",
"Share URL": "མཉམ་སྤྱོད་གནས་སྡོད།",
"Copy link": "འབྲེལ་མཐུད་པར་སློག",
"Copied": "པར་སློག་གྲུབ།",
@@ -1285,11 +1282,17 @@
"Added to your library": "ཁྱེད་ཀྱི་དཔེ་མཛོད་ལ་སྣོན་ཟིན།",
"Could not import shared book": "མཉམ་སྤྱོད་དཔེ་ཆ་འདྲེན་མ་ཐུབ།",
"Manage Shared Links": "མཉམ་སྤྱོད་འབྲེལ་མཐུད་དོ་དམ།",
"Share current page": "ད་ལྟའི་ཤོག་ངོས་མཉམ་སྤྱོད།",
"Expires in": "དུས་ཟད།",
"{{count}} days_other": "ཉིན་{{count}}",
"Expires in {{count}} days_other": "ཉིན་{{count}}ནང་ཟད།",
"Expires in {{count}} hours_other": "ཆུ་ཚོད་{{count}}ནང་ཟད།",
"Open in app": "ཉེར་སྤྱོད་ནང་ཁ་ཕྱེ།",
"Shared with you": "ཁྱེད་དང་མཉམ་སྤྱོད་བྱས།"
"Shared with you": "ཁྱེད་དང་མཉམ་སྤྱོད་བྱས།",
"Downloading… {{percent}}%": "ཕབ་ལེན་བྱེད་བཞིན་པ… {{percent}}%",
"Import progress": "ནང་འདྲེན་གྱི་ཡར་ཐོན།",
"Share reading progress": "ཀློག་འདོན་གྱི་ཡར་ཐོན་སྤེལ་མཁོ།",
"Includes your reading progress": "ཁྱེད་ཀྱི་ཀློག་འདོན་གྱི་ཡར་ཐོན་འདུས།",
"Synced {{time}}": "{{time}}མཉམ་སྒྲིག་བྱས་པ",
"Sync Info": "མཉམ་སྒྲིག་གྱི་ཆ་འཕྲིན",
"Last Synced": "མཐའ་མའི་མཉམ་སྒྲིག"
}
@@ -298,7 +298,6 @@
"No notes match your search": "Noch keine Notizen gefunden",
"Search notes and excerpts...": "Notizen und Auszüge suchen...",
"Sign in to Sync": "Anmelden zum Synchronisieren",
"Synced at {{time}}": "Synchronisiert um {{time}}",
"Never synced": "Nie synchronisiert",
"Show Remaining Time": "Verbleibende Zeit anzeigen",
"{{time}} min left in chapter": "{{time}} min verbleibend im Kapitel",
@@ -1207,7 +1206,6 @@
"{{count}} failed_one": "{{count}} fehlgeschlagen",
"{{count}} failed_other": "{{count}} fehlgeschlagen",
"(none)": "(keine)",
"Metadata Hash": "Metadaten-Hash",
"Meta Hash": "Meta-Hash",
"Computed Hash": "Berechneter Hash",
"Identifiers": "Bezeichner",
@@ -1265,7 +1263,6 @@
"Uploading book…": "Buch wird hochgeladen…",
"Generating…": "Wird erstellt…",
"Generate share link": "Freigabelink erstellen",
"Includes your current page": "Enthält Ihre aktuelle Seite",
"Share URL": "Freigabelink",
"Copy link": "Link kopieren",
"Copied": "Kopiert",
@@ -1305,7 +1302,6 @@
"Added to your library": "Zu deiner Bibliothek hinzugefügt",
"Could not import shared book": "Geteiltes Buch konnte nicht importiert werden",
"Manage Shared Links": "Freigabelinks verwalten",
"Share current page": "Aktuelle Seite teilen",
"Expires in": "Läuft ab in",
"{{count}} days_one": "1 Tag",
"{{count}} days_other": "{{count}} Tage",
@@ -1314,5 +1310,12 @@
"Expires in {{count}} hours_one": "Läuft in 1 Stunde ab",
"Expires in {{count}} hours_other": "Läuft in {{count}} Stunden ab",
"Open in app": "In der App öffnen",
"Shared with you": "Mit dir geteilt"
"Shared with you": "Mit dir geteilt",
"Downloading… {{percent}}%": "Wird heruntergeladen… {{percent}}%",
"Import progress": "Importfortschritt",
"Share reading progress": "Lesefortschritt teilen",
"Includes your reading progress": "Enthält deinen Lesefortschritt",
"Synced {{time}}": "Synchronisiert {{time}}",
"Sync Info": "Synchronisierungs-Info",
"Last Synced": "Zuletzt synchronisiert"
}
@@ -299,7 +299,6 @@
"No notes match your search": "Καμία σημείωση δεν βρέθηκε",
"Search notes and excerpts...": "Αναζήτηση σημειώσεων...",
"Sign in to Sync": "Σύνδεση για συγχρονισμό",
"Synced at {{time}}": "Συγχρονίστηκε στις {{time}}",
"Never synced": "Ποτέ δεν συγχρονίστηκε",
"Show Remaining Time": "Εμφάνιση υπολειπόμενου χρόνου",
"{{time}} min left in chapter": "{{time}} λεπτά απομένουν στο κεφάλαιο",
@@ -1207,7 +1206,6 @@
"{{count}} failed_one": "{{count}} απέτυχε",
"{{count}} failed_other": "{{count}} απέτυχαν",
"(none)": "(κανένα)",
"Metadata Hash": "Hash Μεταδεδομένων",
"Meta Hash": "Meta Hash",
"Computed Hash": "Υπολογισμένο Hash",
"Identifiers": "Αναγνωριστικά",
@@ -1265,7 +1263,6 @@
"Uploading book…": "Μεταφόρτωση βιβλίου…",
"Generating…": "Δημιουργία…",
"Generate share link": "Δημιουργία συνδέσμου κοινής χρήσης",
"Includes your current page": "Περιλαμβάνει την τρέχουσα σελίδα σας",
"Share URL": "URL κοινής χρήσης",
"Copy link": "Αντιγραφή συνδέσμου",
"Copied": "Αντιγράφηκε",
@@ -1305,7 +1302,6 @@
"Added to your library": "Προστέθηκε στη βιβλιοθήκη σας",
"Could not import shared book": "Δεν ήταν δυνατή η εισαγωγή του κοινόχρηστου βιβλίου",
"Manage Shared Links": "Διαχείριση συνδέσμων κοινής χρήσης",
"Share current page": "Κοινή χρήση τρέχουσας σελίδας",
"Expires in": "Λήγει σε",
"{{count}} days_one": "1 ημέρα",
"{{count}} days_other": "{{count}} ημέρες",
@@ -1314,5 +1310,12 @@
"Expires in {{count}} hours_one": "Λήγει σε 1 ώρα",
"Expires in {{count}} hours_other": "Λήγει σε {{count}} ώρες",
"Open in app": "Άνοιγμα στην εφαρμογή",
"Shared with you": "Κοινοποιήθηκε σε εσάς"
"Shared with you": "Κοινοποιήθηκε σε εσάς",
"Downloading… {{percent}}%": "Λήψη… {{percent}}%",
"Import progress": "Πρόοδος εισαγωγής",
"Share reading progress": "Κοινή χρήση προόδου ανάγνωσης",
"Includes your reading progress": "Περιλαμβάνει την πρόοδο ανάγνωσής σας",
"Synced {{time}}": "Συγχρονίστηκε {{time}}",
"Sync Info": "Πληροφορίες συγχρονισμού",
"Last Synced": "Τελευταίος συγχρονισμός"
}
@@ -328,7 +328,6 @@
"No notes match your search": "No se encontraron notas",
"Search notes and excerpts...": "Buscar notas...",
"Sign in to Sync": "Inicia sesión para sincronizar",
"Synced at {{time}}": "Sincronizado a las {{time}}",
"Never synced": "Nunca sincronizado",
"Show Remaining Time": "Mostrar tiempo restante",
"{{time}} min left in chapter": "{{time}} min restantes en el capítulo",
@@ -1224,7 +1223,6 @@
"{{count}} failed_many": "{{count}} fallidas",
"{{count}} failed_other": "{{count}} fallidas",
"(none)": "(ninguno)",
"Metadata Hash": "Hash de Metadatos",
"Meta Hash": "Meta Hash",
"Computed Hash": "Hash Calculado",
"Identifiers": "Identificadores",
@@ -1283,7 +1281,6 @@
"Uploading book…": "Subiendo libro…",
"Generating…": "Generando…",
"Generate share link": "Generar enlace de compartir",
"Includes your current page": "Incluye tu página actual",
"Share URL": "URL de compartir",
"Copy link": "Copiar enlace",
"Copied": "Copiado",
@@ -1325,7 +1322,6 @@
"Added to your library": "Añadido a tu biblioteca",
"Could not import shared book": "No se pudo importar el libro compartido",
"Manage Shared Links": "Gestionar enlaces compartidos",
"Share current page": "Compartir página actual",
"Expires in": "Caduca en",
"{{count}} days_one": "1 día",
"{{count}} days_many": "{{count}} días",
@@ -1337,5 +1333,12 @@
"Expires in {{count}} hours_many": "Caduca en {{count}} horas",
"Expires in {{count}} hours_other": "Caduca en {{count}} horas",
"Open in app": "Abrir en la app",
"Shared with you": "Compartido contigo"
"Shared with you": "Compartido contigo",
"Downloading… {{percent}}%": "Descargando… {{percent}}%",
"Import progress": "Progreso de la importación",
"Share reading progress": "Compartir el progreso de lectura",
"Includes your reading progress": "Incluye tu progreso de lectura",
"Synced {{time}}": "Sincronizado {{time}}",
"Sync Info": "Información de sincronización",
"Last Synced": "Última sincronización"
}
@@ -298,7 +298,6 @@
"No notes match your search": "هیچ یادداشتی با جستجوی شما مطابقت ندارد",
"Search notes and excerpts...": "جستجوی یادداشت‌ها و گزیده‌ها...",
"Sign in to Sync": "برای همگام‌سازی وارد شوید",
"Synced at {{time}}": "همگام‌سازی شده در {{time}}",
"Never synced": "هرگز همگام‌سازی نشده",
"Show Remaining Time": "نمایش زمان باقی‌مانده",
"{{time}} min left in chapter": "{{time}} دقیقه تا انتهای فصل",
@@ -1207,7 +1206,6 @@
"{{count}} failed_one": "{{count}} ناموفق",
"{{count}} failed_other": "{{count}} ناموفق",
"(none)": "(هیچ‌کدام)",
"Metadata Hash": "هش متادیتا",
"Meta Hash": "متا هش",
"Computed Hash": "هش محاسبه‌شده",
"Identifiers": "شناسه‌ها",
@@ -1265,7 +1263,6 @@
"Uploading book…": "در حال آپلود کتاب…",
"Generating…": "در حال ایجاد…",
"Generate share link": "ایجاد لینک اشتراک‌گذاری",
"Includes your current page": "شامل صفحه فعلی شما",
"Share URL": "آدرس اشتراک‌گذاری",
"Copy link": "کپی لینک",
"Copied": "کپی شد",
@@ -1305,7 +1302,6 @@
"Added to your library": "به کتابخانه شما افزوده شد",
"Could not import shared book": "وارد کردن کتاب اشتراک‌گذاری شده امکان‌پذیر نبود",
"Manage Shared Links": "مدیریت لینک‌های اشتراک‌گذاری",
"Share current page": "اشتراک صفحه فعلی",
"Expires in": "منقضی می‌شود در",
"{{count}} days_one": "۱ روز",
"{{count}} days_other": "{{count}} روز",
@@ -1314,5 +1310,12 @@
"Expires in {{count}} hours_one": "در ۱ ساعت منقضی می‌شود",
"Expires in {{count}} hours_other": "در {{count}} ساعت منقضی می‌شود",
"Open in app": "باز کردن در برنامه",
"Shared with you": "با شما به اشتراک گذاشته شده"
"Shared with you": "با شما به اشتراک گذاشته شده",
"Downloading… {{percent}}%": "در حال دانلود… {{percent}}٪",
"Import progress": "پیشرفت وارد کردن",
"Share reading progress": "اشتراک‌گذاری پیشرفت مطالعه",
"Includes your reading progress": "شامل پیشرفت مطالعهٔ شماست",
"Synced {{time}}": "{{time}} همگام‌سازی شد",
"Sync Info": "اطلاعات همگام‌سازی",
"Last Synced": "آخرین همگام‌سازی"
}
@@ -300,7 +300,6 @@
"No notes match your search": "Aucune note trouvée",
"Search notes and excerpts...": "Rechercher des notes...",
"Sign in to Sync": "Connectez-vous pour synchroniser",
"Synced at {{time}}": "Synchronisé à {{time}}",
"Never synced": "Jamais synchronisé",
"Show Remaining Time": "Afficher le temps restant",
"{{time}} min left in chapter": "{{time}} min restant dans le chapitre",
@@ -1224,7 +1223,6 @@
"{{count}} failed_many": "{{count}} échecs",
"{{count}} failed_other": "{{count}} échecs",
"(none)": "(aucun)",
"Metadata Hash": "Hachage des métadonnées",
"Meta Hash": "Hachage Meta",
"Computed Hash": "Hachage calculé",
"Identifiers": "Identifiants",
@@ -1283,7 +1281,6 @@
"Uploading book…": "Téléchargement du livre…",
"Generating…": "Génération…",
"Generate share link": "Créer un lien de partage",
"Includes your current page": "Inclut votre page actuelle",
"Share URL": "URL de partage",
"Copy link": "Copier le lien",
"Copied": "Copié",
@@ -1325,7 +1322,6 @@
"Added to your library": "Ajouté à votre bibliothèque",
"Could not import shared book": "Impossible d'importer le livre partagé",
"Manage Shared Links": "Gérer les liens de partage",
"Share current page": "Partager la page actuelle",
"Expires in": "Expire dans",
"{{count}} days_one": "1 jour",
"{{count}} days_many": "{{count}} jours",
@@ -1337,5 +1333,12 @@
"Expires in {{count}} hours_many": "Expire dans {{count}} heures",
"Expires in {{count}} hours_other": "Expire dans {{count}} heures",
"Open in app": "Ouvrir dans l'app",
"Shared with you": "Partagé avec vous"
"Shared with you": "Partagé avec vous",
"Downloading… {{percent}}%": "Téléchargement… {{percent}} %",
"Import progress": "Progression de limportation",
"Share reading progress": "Partager la progression de lecture",
"Includes your reading progress": "Inclut votre progression de lecture",
"Synced {{time}}": "Synchronisé {{time}}",
"Sync Info": "Infos de synchronisation",
"Last Synced": "Dernière synchronisation"
}
@@ -130,7 +130,6 @@
"Cloud File Transfers": "העברות קבצים בענן",
"{{activeCount}} active, {{pendingCount}} pending": "{{activeCount}} פעילים, {{pendingCount}} בהמתנה",
"{{failedCount}} failed": "{{failedCount}} נכשלו",
"Synced at {{time}}": "סונכרן ב-{{time}}",
"Never synced": "מעולם לא סונכרן",
"Account": "חשבון",
"Sign In": "התחבר",
@@ -1224,7 +1223,6 @@
"{{count}} failed_two": "{{count}} נכשלו",
"{{count}} failed_other": "{{count}} נכשלו",
"(none)": "(אין)",
"Metadata Hash": "גיבוב מטא-נתונים",
"Meta Hash": "גיבוב מטא",
"Computed Hash": "גיבוב מחושב",
"Identifiers": "מזהים",
@@ -1283,7 +1281,6 @@
"Uploading book…": "מעלה ספר…",
"Generating…": "יוצר…",
"Generate share link": "צור קישור שיתוף",
"Includes your current page": "כולל את העמוד הנוכחי שלך",
"Share URL": "כתובת שיתוף",
"Copy link": "העתק קישור",
"Copied": "הועתק",
@@ -1325,7 +1322,6 @@
"Added to your library": "נוסף לספרייה שלך",
"Could not import shared book": "לא ניתן היה לייבא את הספר המשותף",
"Manage Shared Links": "ניהול קישורי שיתוף",
"Share current page": "שתף את העמוד הנוכחי",
"Expires in": "יפוג בעוד",
"{{count}} days_one": "יום אחד",
"{{count}} days_two": "יומיים",
@@ -1337,5 +1333,12 @@
"Expires in {{count}} hours_two": "יפוג בעוד שעתיים",
"Expires in {{count}} hours_other": "יפוג בעוד {{count}} שעות",
"Open in app": "פתח באפליקציה",
"Shared with you": "שותף איתך"
"Shared with you": "שותף איתך",
"Downloading… {{percent}}%": "מוריד… {{percent}}%",
"Import progress": "התקדמות הייבוא",
"Share reading progress": "שיתוף התקדמות הקריאה",
"Includes your reading progress": "כולל את התקדמות הקריאה שלך",
"Synced {{time}}": "סונכרן {{time}}",
"Sync Info": "פרטי סנכרון",
"Last Synced": "סונכרן לאחרונה"
}
@@ -299,7 +299,6 @@
"No notes match your search": "कोई नोट्स नहीं मिले",
"Search notes and excerpts...": "नोट्स खोजें...",
"Sign in to Sync": "सिंक करने के लिए साइन इन करें",
"Synced at {{time}}": "{{time}} पर सिंक किया गया",
"Never synced": "कभी सिंक नहीं किया गया",
"Show Remaining Time": "शेष समय दिखाएं",
"{{time}} min left in chapter": "{{time}} मिनट शेष अध्याय में",
@@ -1207,7 +1206,6 @@
"{{count}} failed_one": "{{count}} असफल",
"{{count}} failed_other": "{{count}} असफल",
"(none)": "(कोई नहीं)",
"Metadata Hash": "मेटाडेटा हैश",
"Meta Hash": "मेटा हैश",
"Computed Hash": "परिकलित हैश",
"Identifiers": "पहचानकर्ता",
@@ -1265,7 +1263,6 @@
"Uploading book…": "पुस्तक अपलोड हो रही है…",
"Generating…": "बना रहा है…",
"Generate share link": "साझा लिंक बनाएँ",
"Includes your current page": "आपका वर्तमान पृष्ठ शामिल है",
"Share URL": "साझा URL",
"Copy link": "लिंक कॉपी करें",
"Copied": "कॉपी किया गया",
@@ -1305,7 +1302,6 @@
"Added to your library": "आपकी लाइब्रेरी में जोड़ा गया",
"Could not import shared book": "साझा पुस्तक आयात नहीं की जा सकी",
"Manage Shared Links": "साझा लिंक प्रबंधित करें",
"Share current page": "वर्तमान पृष्ठ साझा करें",
"Expires in": "समाप्त होगा",
"{{count}} days_one": "1 दिन",
"{{count}} days_other": "{{count}} दिन",
@@ -1314,5 +1310,12 @@
"Expires in {{count}} hours_one": "1 घंटे में समाप्त",
"Expires in {{count}} hours_other": "{{count}} घंटों में समाप्त",
"Open in app": "ऐप में खोलें",
"Shared with you": "आपके साथ साझा किया गया"
"Shared with you": "आपके साथ साझा किया गया",
"Downloading… {{percent}}%": "डाउनलोड हो रहा है… {{percent}}%",
"Import progress": "आयात प्रगति",
"Share reading progress": "पढ़ने की प्रगति साझा करें",
"Includes your reading progress": "आपकी पढ़ने की प्रगति शामिल है",
"Synced {{time}}": "{{time}} सिंक हुआ",
"Sync Info": "सिंक जानकारी",
"Last Synced": "अंतिम सिंक"
}
@@ -146,7 +146,6 @@
"Cloud File Transfers": "Felhő fájlátvitelek",
"{{activeCount}} active, {{pendingCount}} pending": "{{activeCount}} aktív, {{pendingCount}} függőben",
"{{failedCount}} failed": "{{failedCount}} sikertelen",
"Synced at {{time}}": "Szinkronizálva: {{time}}",
"Never synced": "Még nem szinkronizált",
"Account": "Fiók",
"Sign In": "Bejelentkezés",
@@ -1207,7 +1206,6 @@
"{{count}} failed_one": "{{count}} sikertelen",
"{{count}} failed_other": "{{count}} sikertelen",
"(none)": "(nincs)",
"Metadata Hash": "Metaadat-hash",
"Meta Hash": "Meta-hash",
"Computed Hash": "Számított hash",
"Identifiers": "Azonosítók",
@@ -1265,7 +1263,6 @@
"Uploading book…": "Könyv feltöltése…",
"Generating…": "Létrehozás…",
"Generate share link": "Megosztási link létrehozása",
"Includes your current page": "Tartalmazza az aktuális oldalt",
"Share URL": "Megosztási URL",
"Copy link": "Link másolása",
"Copied": "Másolva",
@@ -1305,7 +1302,6 @@
"Added to your library": "Hozzáadva a könyvtáradhoz",
"Could not import shared book": "A megosztott könyv importálása nem sikerült",
"Manage Shared Links": "Megosztási linkek kezelése",
"Share current page": "Aktuális oldal megosztása",
"Expires in": "Lejár",
"{{count}} days_one": "1 nap",
"{{count}} days_other": "{{count}} nap",
@@ -1314,5 +1310,12 @@
"Expires in {{count}} hours_one": "Lejár 1 óra múlva",
"Expires in {{count}} hours_other": "Lejár {{count}} óra múlva",
"Open in app": "Megnyitás az alkalmazásban",
"Shared with you": "Megosztva veled"
"Shared with you": "Megosztva veled",
"Downloading… {{percent}}%": "Letöltés… {{percent}}%",
"Import progress": "Importálás folyamata",
"Share reading progress": "Olvasási folyamat megosztása",
"Includes your reading progress": "Tartalmazza az olvasási folyamatot",
"Synced {{time}}": "Szinkronizálva {{time}}",
"Sync Info": "Szinkronizálási információk",
"Last Synced": "Utolsó szinkronizálás"
}
@@ -297,7 +297,6 @@
"No notes match your search": "Tidak ada catatan ditemukan",
"Search notes and excerpts...": "Cari catatan...",
"Sign in to Sync": "Masuk untuk Sinkronisasi",
"Synced at {{time}}": "Disinkronkan pada {{time}}",
"Never synced": "Belum pernah disinkronkan",
"Show Remaining Time": "Perlihatkan Waktu Tersisa",
"{{time}} min left in chapter": "{{time}} menit tersisa di bab",
@@ -1190,7 +1189,6 @@
"Retry all": "Coba lagi semua",
"{{count}} failed_other": "{{count}} gagal",
"(none)": "(tidak ada)",
"Metadata Hash": "Hash Metadata",
"Meta Hash": "Meta Hash",
"Computed Hash": "Hash Terhitung",
"Identifiers": "Pengenal",
@@ -1247,7 +1245,6 @@
"Uploading book…": "Mengunggah buku…",
"Generating…": "Membuat…",
"Generate share link": "Buat tautan berbagi",
"Includes your current page": "Termasuk halaman Anda saat ini",
"Share URL": "URL Berbagi",
"Copy link": "Salin tautan",
"Copied": "Disalin",
@@ -1285,11 +1282,17 @@
"Added to your library": "Ditambahkan ke pustaka Anda",
"Could not import shared book": "Tidak dapat mengimpor buku yang dibagikan",
"Manage Shared Links": "Kelola tautan berbagi",
"Share current page": "Bagikan halaman saat ini",
"Expires in": "Kedaluwarsa dalam",
"{{count}} days_other": "{{count}} hari",
"Expires in {{count}} days_other": "Kedaluwarsa dalam {{count}} hari",
"Expires in {{count}} hours_other": "Kedaluwarsa dalam {{count}} jam",
"Open in app": "Buka di aplikasi",
"Shared with you": "Dibagikan dengan Anda"
"Shared with you": "Dibagikan dengan Anda",
"Downloading… {{percent}}%": "Mengunduh… {{percent}}%",
"Import progress": "Kemajuan impor",
"Share reading progress": "Bagikan progres baca",
"Includes your reading progress": "Termasuk progres baca Anda",
"Synced {{time}}": "Disinkronkan {{time}}",
"Sync Info": "Info sinkronisasi",
"Last Synced": "Sinkronisasi terakhir"
}
@@ -301,7 +301,6 @@
"No notes match your search": "Nessuna nota trovata",
"Search notes and excerpts...": "Cerca nelle note...",
"Sign in to Sync": "Accedi per sincronizzare",
"Synced at {{time}}": "Sincronizzato alle {{time}}",
"Never synced": "Mai sincronizzato",
"Show Remaining Time": "Mostra tempo rimanente",
"{{time}} min left in chapter": "{{time}} min rimasti nel capitolo",
@@ -1224,7 +1223,6 @@
"{{count}} failed_many": "{{count}} non riusciti",
"{{count}} failed_other": "{{count}} non riusciti",
"(none)": "(nessuno)",
"Metadata Hash": "Hash Metadati",
"Meta Hash": "Meta Hash",
"Computed Hash": "Hash Calcolato",
"Identifiers": "Identificatori",
@@ -1283,7 +1281,6 @@
"Uploading book…": "Caricamento libro…",
"Generating…": "Generazione…",
"Generate share link": "Genera link di condivisione",
"Includes your current page": "Include la tua pagina attuale",
"Share URL": "URL di condivisione",
"Copy link": "Copia link",
"Copied": "Copiato",
@@ -1325,7 +1322,6 @@
"Added to your library": "Aggiunto alla tua libreria",
"Could not import shared book": "Impossibile importare il libro condiviso",
"Manage Shared Links": "Gestisci link di condivisione",
"Share current page": "Condividi pagina attuale",
"Expires in": "Scade tra",
"{{count}} days_one": "1 giorno",
"{{count}} days_many": "{{count}} giorni",
@@ -1337,5 +1333,12 @@
"Expires in {{count}} hours_many": "Scade tra {{count}} ore",
"Expires in {{count}} hours_other": "Scade tra {{count}} ore",
"Open in app": "Apri nell'app",
"Shared with you": "Condiviso con te"
"Shared with you": "Condiviso con te",
"Downloading… {{percent}}%": "Download in corso… {{percent}}%",
"Import progress": "Avanzamento dell'importazione",
"Share reading progress": "Condividi i progressi di lettura",
"Includes your reading progress": "Include i tuoi progressi di lettura",
"Synced {{time}}": "Sincronizzato {{time}}",
"Sync Info": "Info di sincronizzazione",
"Last Synced": "Ultima sincronizzazione"
}
@@ -297,7 +297,6 @@
"No notes match your search": "ノートが見つかりません",
"Search notes and excerpts...": "ノートを検索...",
"Sign in to Sync": "同期するにはサインイン",
"Synced at {{time}}": "{{time}} に同期済み",
"Never synced": "まだ同期されていません",
"Show Remaining Time": "残り時間を表示",
"{{time}} min left in chapter": "{{time}}分残り",
@@ -1190,7 +1189,6 @@
"Retry all": "すべて再試行",
"{{count}} failed_other": "{{count}}件失敗",
"(none)": "(なし)",
"Metadata Hash": "メタデータハッシュ",
"Meta Hash": "メタハッシュ",
"Computed Hash": "計算されたハッシュ",
"Identifiers": "識別子",
@@ -1247,7 +1245,6 @@
"Uploading book…": "本をアップロード中…",
"Generating…": "生成中…",
"Generate share link": "共有リンクを作成",
"Includes your current page": "現在のページを含む",
"Share URL": "共有URL",
"Copy link": "リンクをコピー",
"Copied": "コピーしました",
@@ -1285,11 +1282,17 @@
"Added to your library": "ライブラリに追加しました",
"Could not import shared book": "共有された本を取り込めませんでした",
"Manage Shared Links": "共有リンクを管理",
"Share current page": "現在のページを共有",
"Expires in": "有効期限",
"{{count}} days_other": "{{count}}日",
"Expires in {{count}} days_other": "{{count}}日で期限切れ",
"Expires in {{count}} hours_other": "{{count}}時間で期限切れ",
"Open in app": "アプリで開く",
"Shared with you": "あなたへの共有"
"Shared with you": "あなたへの共有",
"Downloading… {{percent}}%": "ダウンロード中… {{percent}}%",
"Import progress": "インポートの進行状況",
"Share reading progress": "読書の進捗を共有",
"Includes your reading progress": "読書の進捗を含みます",
"Synced {{time}}": "{{time}}に同期",
"Sync Info": "同期情報",
"Last Synced": "最終同期"
}
@@ -297,7 +297,6 @@
"No notes match your search": "일치하는 노트가 없습니다",
"Search notes and excerpts...": "노트 검색...",
"Sign in to Sync": "동기화를 위해 로그인하세요",
"Synced at {{time}}": "{{time}}에 동기화됨",
"Never synced": "동기화된 적 없음",
"Show Remaining Time": "남은 시간 표시",
"{{time}} min left in chapter": "{{time}}분 남음",
@@ -1190,7 +1189,6 @@
"Retry all": "모두 재시도",
"{{count}} failed_other": "{{count}}개 실패",
"(none)": "(없음)",
"Metadata Hash": "메타데이터 해시",
"Meta Hash": "메타 해시",
"Computed Hash": "계산된 해시",
"Identifiers": "식별자",
@@ -1247,7 +1245,6 @@
"Uploading book…": "책 업로드 중…",
"Generating…": "생성 중…",
"Generate share link": "공유 링크 만들기",
"Includes your current page": "현재 페이지 포함",
"Share URL": "공유 URL",
"Copy link": "링크 복사",
"Copied": "복사됨",
@@ -1285,11 +1282,17 @@
"Added to your library": "라이브러리에 추가되었습니다",
"Could not import shared book": "공유된 책을 가져올 수 없습니다",
"Manage Shared Links": "공유 링크 관리",
"Share current page": "현재 페이지 공유",
"Expires in": "만료",
"{{count}} days_other": "{{count}}일",
"Expires in {{count}} days_other": "{{count}}일 후 만료",
"Expires in {{count}} hours_other": "{{count}}시간 후 만료",
"Open in app": "앱에서 열기",
"Shared with you": "회원님과 공유됨"
"Shared with you": "회원님과 공유됨",
"Downloading… {{percent}}%": "다운로드 중… {{percent}}%",
"Import progress": "가져오기 진행률",
"Share reading progress": "읽기 진행률 공유",
"Includes your reading progress": "읽기 진행률 포함됨",
"Synced {{time}}": "{{time}} 동기화됨",
"Sync Info": "동기화 정보",
"Last Synced": "마지막 동기화"
}
@@ -315,7 +315,6 @@
"Separate Cover Page": "Halaman Kulit Berasingan",
"Scrolled Mode": "Mod Tatal",
"Sign in to Sync": "Log masuk untuk Segerak",
"Synced at {{time}}": "Disegerak pada {{time}}",
"Never synced": "Tidak pernah disegerak",
"Invert Image In Dark Mode": "Songsangkan Imej Dalam Mod Gelap",
"Failed to auto-save book cover for lock screen: {{error}}": "Gagal auto-simpan kulit buku untuk skrin kunci: {{error}}",
@@ -1190,7 +1189,6 @@
"Retry all": "Cuba lagi semua",
"{{count}} failed_other": "{{count}} gagal",
"(none)": "(tiada)",
"Metadata Hash": "Cincang Metadata",
"Meta Hash": "Cincang Meta",
"Computed Hash": "Cincang Dikira",
"Identifiers": "Pengenal",
@@ -1247,7 +1245,6 @@
"Uploading book…": "Memuat naik buku…",
"Generating…": "Menjana…",
"Generate share link": "Jana pautan kongsi",
"Includes your current page": "Termasuk halaman semasa anda",
"Share URL": "URL Kongsi",
"Copy link": "Salin pautan",
"Copied": "Disalin",
@@ -1285,11 +1282,17 @@
"Added to your library": "Ditambah ke pustaka anda",
"Could not import shared book": "Tidak dapat mengimport buku yang dikongsi",
"Manage Shared Links": "Urus pautan kongsi",
"Share current page": "Kongsi halaman semasa",
"Expires in": "Tamat dalam",
"{{count}} days_other": "{{count}} hari",
"Expires in {{count}} days_other": "Tamat dalam {{count}} hari",
"Expires in {{count}} hours_other": "Tamat dalam {{count}} jam",
"Open in app": "Buka dalam app",
"Shared with you": "Dikongsi dengan anda"
"Shared with you": "Dikongsi dengan anda",
"Downloading… {{percent}}%": "Memuat turun… {{percent}}%",
"Import progress": "Kemajuan import",
"Share reading progress": "Kongsi kemajuan bacaan",
"Includes your reading progress": "Termasuk kemajuan bacaan anda",
"Synced {{time}}": "Disegerakkan {{time}}",
"Sync Info": "Maklumat penyegerakan",
"Last Synced": "Penyegerakan terakhir"
}
@@ -299,7 +299,6 @@
"No notes match your search": "Geen notities gevonden",
"Search notes and excerpts...": "Zoek notities...",
"Sign in to Sync": "Meld je aan om te synchroniseren",
"Synced at {{time}}": "Gesynchroniseerd om {{time}}",
"Never synced": "Nooit gesynchroniseerd",
"Show Remaining Time": "Toon resterende tijd",
"{{time}} min left in chapter": "Nog {{time}} min in hoofdstuk",
@@ -1207,7 +1206,6 @@
"{{count}} failed_one": "{{count}} mislukt",
"{{count}} failed_other": "{{count}} mislukt",
"(none)": "(geen)",
"Metadata Hash": "Metadata-hash",
"Meta Hash": "Meta-hash",
"Computed Hash": "Berekende hash",
"Identifiers": "Identificatoren",
@@ -1265,7 +1263,6 @@
"Uploading book…": "Boek uploaden…",
"Generating…": "Genereren…",
"Generate share link": "Deellink maken",
"Includes your current page": "Bevat uw huidige pagina",
"Share URL": "Deel-URL",
"Copy link": "Koppeling kopiëren",
"Copied": "Gekopieerd",
@@ -1305,7 +1302,6 @@
"Added to your library": "Toegevoegd aan uw bibliotheek",
"Could not import shared book": "Kan gedeeld boek niet importeren",
"Manage Shared Links": "Deelkoppelingen beheren",
"Share current page": "Huidige pagina delen",
"Expires in": "Verloopt over",
"{{count}} days_one": "1 dag",
"{{count}} days_other": "{{count}} dagen",
@@ -1314,5 +1310,12 @@
"Expires in {{count}} hours_one": "Verloopt over 1 uur",
"Expires in {{count}} hours_other": "Verloopt over {{count}} uur",
"Open in app": "Openen in app",
"Shared with you": "Met u gedeeld"
"Shared with you": "Met u gedeeld",
"Downloading… {{percent}}%": "Downloaden… {{percent}}%",
"Import progress": "Voortgang import",
"Share reading progress": "Leesvoortgang delen",
"Includes your reading progress": "Inclusief je leesvoortgang",
"Synced {{time}}": "Gesynchroniseerd {{time}}",
"Sync Info": "Synchronisatie-info",
"Last Synced": "Laatst gesynchroniseerd"
}
@@ -303,7 +303,6 @@
"No notes match your search": "Nie znaleziono notatek",
"Search notes and excerpts...": "Szukaj notatek...",
"Sign in to Sync": "Zaloguj się",
"Synced at {{time}}": "Sync: {{time}}",
"Never synced": "Brak synchronizacji",
"Show Remaining Time": "Pokazuj pozostały czas",
"{{time}} min left in chapter": "{{time}} min pozostało do końca rozdziału",
@@ -1241,7 +1240,6 @@
"{{count}} failed_many": "{{count}} nieudanych",
"{{count}} failed_other": "{{count}} nieudanych",
"(none)": "(brak)",
"Metadata Hash": "Skrót metadanych",
"Meta Hash": "Meta-skrót",
"Computed Hash": "Obliczony skrót",
"Identifiers": "Identyfikatory",
@@ -1301,7 +1299,6 @@
"Uploading book…": "Przesyłanie książki…",
"Generating…": "Generowanie…",
"Generate share link": "Utwórz link udostępniania",
"Includes your current page": "Zawiera Twoją bieżącą stronę",
"Share URL": "Adres udostępniania",
"Copy link": "Kopiuj link",
"Copied": "Skopiowano",
@@ -1345,7 +1342,6 @@
"Added to your library": "Dodano do biblioteki",
"Could not import shared book": "Nie można zaimportować udostępnionej książki",
"Manage Shared Links": "Zarządzaj linkami udostępniania",
"Share current page": "Udostępnij bieżącą stronę",
"Expires in": "Wygasa za",
"{{count}} days_one": "1 dzień",
"{{count}} days_few": "{{count}} dni",
@@ -1360,5 +1356,12 @@
"Expires in {{count}} hours_many": "Wygasa za {{count}} godz.",
"Expires in {{count}} hours_other": "Wygasa za {{count}} godz.",
"Open in app": "Otwórz w aplikacji",
"Shared with you": "Udostępnione Tobie"
"Shared with you": "Udostępnione Tobie",
"Downloading… {{percent}}%": "Pobieranie… {{percent}}%",
"Import progress": "Postęp importu",
"Share reading progress": "Udostępnij postęp czytania",
"Includes your reading progress": "Zawiera Twój postęp czytania",
"Synced {{time}}": "Zsynchronizowano {{time}}",
"Sync Info": "Informacje o synchronizacji",
"Last Synced": "Ostatnia synchronizacja"
}
@@ -301,7 +301,6 @@
"No notes match your search": "Nenhuma nota encontrada",
"Search notes and excerpts...": "Buscar notas...",
"Sign in to Sync": "Entrar",
"Synced at {{time}}": "Sincronizado às {{time}}",
"Never synced": "Nunca sincronizado",
"Show Remaining Time": "Mostrar Tempo Restante",
"{{time}} min left in chapter": "{{time}} min restantes no capítulo",
@@ -1224,7 +1223,6 @@
"{{count}} failed_many": "{{count}} falhadas",
"{{count}} failed_other": "{{count}} falhadas",
"(none)": "(nenhum)",
"Metadata Hash": "Hash de Metadados",
"Meta Hash": "Meta Hash",
"Computed Hash": "Hash Calculado",
"Identifiers": "Identificadores",
@@ -1283,7 +1281,6 @@
"Uploading book…": "Enviando livro…",
"Generating…": "Gerando…",
"Generate share link": "Gerar link de compartilhamento",
"Includes your current page": "Inclui sua página atual",
"Share URL": "URL de compartilhamento",
"Copy link": "Copiar link",
"Copied": "Copiado",
@@ -1325,7 +1322,6 @@
"Added to your library": "Adicionado à sua biblioteca",
"Could not import shared book": "Não foi possível importar o livro compartilhado",
"Manage Shared Links": "Gerenciar links de compartilhamento",
"Share current page": "Compartilhar página atual",
"Expires in": "Expira em",
"{{count}} days_one": "1 dia",
"{{count}} days_many": "{{count}} dias",
@@ -1337,5 +1333,12 @@
"Expires in {{count}} hours_many": "Expira em {{count}} horas",
"Expires in {{count}} hours_other": "Expira em {{count}} horas",
"Open in app": "Abrir no app",
"Shared with you": "Compartilhado com você"
"Shared with you": "Compartilhado com você",
"Downloading… {{percent}}%": "Baixando… {{percent}}%",
"Import progress": "Progresso da importação",
"Share reading progress": "Compartilhar progresso de leitura",
"Includes your reading progress": "Inclui seu progresso de leitura",
"Synced {{time}}": "Sincronizado {{time}}",
"Sync Info": "Informações de sincronização",
"Last Synced": "Última sincronização"
}
@@ -150,7 +150,6 @@
"Cloud File Transfers": "Transferuri de fișiere în cloud",
"{{activeCount}} active, {{pendingCount}} pending": "{{activeCount}} activ, {{pendingCount}} în așteptare",
"{{failedCount}} failed": "{{failedCount}} a eșuat",
"Synced at {{time}}": "Sincronizat la {{time}}",
"Never synced": "Nu s-a sincronizat niciodată",
"Account": "Cont",
"Sign In": "Conectare",
@@ -1224,7 +1223,6 @@
"{{count}} failed_few": "{{count}} eșuate",
"{{count}} failed_other": "{{count}} eșuate",
"(none)": "(niciunul)",
"Metadata Hash": "Hash metadate",
"Meta Hash": "Meta hash",
"Computed Hash": "Hash calculat",
"Identifiers": "Identificatori",
@@ -1283,7 +1281,6 @@
"Uploading book…": "Se încarcă cartea…",
"Generating…": "Se generează…",
"Generate share link": "Generează link de partajare",
"Includes your current page": "Include pagina ta curentă",
"Share URL": "URL de partajare",
"Copy link": "Copiază linkul",
"Copied": "Copiat",
@@ -1325,7 +1322,6 @@
"Added to your library": "Adăugat în biblioteca ta",
"Could not import shared book": "Nu s-a putut importa cartea partajată",
"Manage Shared Links": "Gestionează linkurile de partajare",
"Share current page": "Partajează pagina curentă",
"Expires in": "Expiră în",
"{{count}} days_one": "1 zi",
"{{count}} days_few": "{{count}} zile",
@@ -1337,5 +1333,12 @@
"Expires in {{count}} hours_few": "Expiră în {{count}} ore",
"Expires in {{count}} hours_other": "Expiră în {{count}} de ore",
"Open in app": "Deschide în aplicație",
"Shared with you": "Partajat cu tine"
"Shared with you": "Partajat cu tine",
"Downloading… {{percent}}%": "Se descarcă… {{percent}}%",
"Import progress": "Progresul importului",
"Share reading progress": "Partajează progresul lecturii",
"Includes your reading progress": "Include progresul tău de lectură",
"Synced {{time}}": "Sincronizat {{time}}",
"Sync Info": "Informații sincronizare",
"Last Synced": "Ultima sincronizare"
}
@@ -303,7 +303,6 @@
"No notes match your search": "Записи не найдены",
"Search notes and excerpts...": "Искать записи...",
"Sign in to Sync": "Войти",
"Synced at {{time}}": "Синхр. в {{time}}",
"Never synced": "Не синхр.",
"Show Remaining Time": "Показать оставшееся время",
"{{time}} min left in chapter": "{{time}} мин осталось в главе",
@@ -1241,7 +1240,6 @@
"{{count}} failed_many": "{{count}} ошибок",
"{{count}} failed_other": "{{count}} ошибок",
"(none)": "(нет)",
"Metadata Hash": "Хеш метаданных",
"Meta Hash": "Мета-хеш",
"Computed Hash": "Вычисленный хеш",
"Identifiers": "Идентификаторы",
@@ -1301,7 +1299,6 @@
"Uploading book…": "Загрузка книги…",
"Generating…": "Создание…",
"Generate share link": "Создать ссылку",
"Includes your current page": "Включает вашу текущую страницу",
"Share URL": "URL для общего доступа",
"Copy link": "Копировать ссылку",
"Copied": "Скопировано",
@@ -1345,7 +1342,6 @@
"Added to your library": "Добавлено в вашу библиотеку",
"Could not import shared book": "Не удалось импортировать книгу",
"Manage Shared Links": "Управление ссылками",
"Share current page": "Поделиться текущей страницей",
"Expires in": "Истекает через",
"{{count}} days_one": "{{count}} день",
"{{count}} days_few": "{{count}} дня",
@@ -1360,5 +1356,12 @@
"Expires in {{count}} hours_many": "Истекает через {{count}} часов",
"Expires in {{count}} hours_other": "Истекает через {{count}} часов",
"Open in app": "Открыть в приложении",
"Shared with you": "Поделились с вами"
"Shared with you": "Поделились с вами",
"Downloading… {{percent}}%": "Загрузка… {{percent}}%",
"Import progress": "Ход импорта",
"Share reading progress": "Поделиться прогрессом чтения",
"Includes your reading progress": "Включает ваш прогресс чтения",
"Synced {{time}}": "Синхронизировано {{time}}",
"Sync Info": "Информация о синхронизации",
"Last Synced": "Последняя синхронизация"
}
@@ -330,7 +330,6 @@
"{{engine}}: {{count}} voices_one": "{{engine}}: හඬ 1",
"{{engine}}: {{count}} voices_other": "{{engine}}: හඬ {{count}}",
"Sign in to Sync": "සමමුහුර්ත කිරීම සඳහා ඇතුල් වන්න",
"Synced at {{time}}": "{{time}} වේලාවේ සමමුහුර්ත විය",
"Never synced": "කිසි විටෙක සමමුහුර්ත නොවිණි",
"Reading Progress Synced": "කියවීමේ ප්‍රගතිය සමමුහුර්ත විය",
"Page {{page}} of {{total}} ({{percentage}}%)": "{{total}} න් {{page}} පිටුව ({{percentage}}%)",
@@ -1207,7 +1206,6 @@
"{{count}} failed_one": "{{count}} අසාර්ථකයි",
"{{count}} failed_other": "{{count}} අසාර්ථකයි",
"(none)": "(කිසිවක් නැත)",
"Metadata Hash": "පාරදත්ත හෑෂ්",
"Meta Hash": "මෙටා හෑෂ්",
"Computed Hash": "ගණනය කළ හෑෂ්",
"Identifiers": "හඳුනාගැනීම්",
@@ -1265,7 +1263,6 @@
"Uploading book…": "පොත උඩුගත කරමින්…",
"Generating…": "සාදමින්…",
"Generate share link": "බෙදාගැනීමේ සබැඳිය සාදන්න",
"Includes your current page": "ඔබගේ වර්තමාන පිටුව ඇතුළත්",
"Share URL": "බෙදාගැනීමේ URL",
"Copy link": "සබැඳිය පිටපත් කරන්න",
"Copied": "පිටපත් කළා",
@@ -1305,7 +1302,6 @@
"Added to your library": "ඔබගේ පුස්තකාලයට එක් කළා",
"Could not import shared book": "බෙදාගත් පොත ආයාත කළ නොහැක",
"Manage Shared Links": "බෙදාගැනීමේ සබැඳි කළමනාකරණය",
"Share current page": "වර්තමාන පිටුව බෙදාගන්න",
"Expires in": "කල් ඉකුත් වේ",
"{{count}} days_one": "දින 1",
"{{count}} days_other": "දින {{count}}",
@@ -1314,5 +1310,12 @@
"Expires in {{count}} hours_one": "පැය 1කින් කල් ඉකුත් වේ",
"Expires in {{count}} hours_other": "පැය {{count}}කින් කල් ඉකුත් වේ",
"Open in app": "යෙදුමෙන් විවෘත කරන්න",
"Shared with you": "ඔබ සමඟ බෙදාගත්"
"Shared with you": "ඔබ සමඟ බෙදාගත්",
"Downloading… {{percent}}%": "බාගත කරමින්… {{percent}}%",
"Import progress": "ආයාත ප්‍රගතිය",
"Share reading progress": "කියවීමේ ප්‍රගතිය බෙදාගන්න",
"Includes your reading progress": "ඔබේ කියවීමේ ප්‍රගතිය ඇතුළත්",
"Synced {{time}}": "{{time}} සමමුහුර්ත කළේ",
"Sync Info": "සමමුහුර්ත තොරතුරු",
"Last Synced": "අවසන් සමමුහුර්තය"
}
@@ -133,7 +133,6 @@
"Cloud File Transfers": "Prenosi datotek v oblaku",
"{{activeCount}} active, {{pendingCount}} pending": "{{activeCount}} aktivnih, {{pendingCount}} na čakanju",
"{{failedCount}} failed": "{{failedCount}} spodletelih",
"Synced at {{time}}": "Sinhronizirano ob {{time}}",
"Never synced": "Nikoli sinhronizirano",
"Account": "Račun",
"Sign In": "Prijava",
@@ -1241,7 +1240,6 @@
"{{count}} failed_few": "{{count}} niso uspele",
"{{count}} failed_other": "{{count}} jih ni uspelo",
"(none)": "(brez)",
"Metadata Hash": "Hash metapodatkov",
"Meta Hash": "Meta hash",
"Computed Hash": "Izračunani hash",
"Identifiers": "Identifikatorji",
@@ -1301,7 +1299,6 @@
"Uploading book…": "Prenašanje knjige…",
"Generating…": "Ustvarjanje…",
"Generate share link": "Ustvari povezavo za deljenje",
"Includes your current page": "Vključuje vašo trenutno stran",
"Share URL": "URL za deljenje",
"Copy link": "Kopiraj povezavo",
"Copied": "Kopirano",
@@ -1345,7 +1342,6 @@
"Added to your library": "Dodano v vašo knjižnico",
"Could not import shared book": "Deljene knjige ni bilo mogoče uvoziti",
"Manage Shared Links": "Upravljanje povezav za deljenje",
"Share current page": "Deli trenutno stran",
"Expires in": "Poteče čez",
"{{count}} days_one": "{{count}} dan",
"{{count}} days_two": "{{count}} dneva",
@@ -1360,5 +1356,12 @@
"Expires in {{count}} hours_few": "Poteče čez {{count}} ure",
"Expires in {{count}} hours_other": "Poteče čez {{count}} ur",
"Open in app": "Odpri v aplikaciji",
"Shared with you": "Deljeno z vami"
"Shared with you": "Deljeno z vami",
"Downloading… {{percent}}%": "Prenašanje… {{percent}} %",
"Import progress": "Napredek uvoza",
"Share reading progress": "Deli napredek branja",
"Includes your reading progress": "Vključuje vaš napredek branja",
"Synced {{time}}": "Sinhronizirano {{time}}",
"Sync Info": "Informacije o sinhronizaciji",
"Last Synced": "Zadnja sinhronizacija"
}
@@ -301,7 +301,6 @@
"Separate Cover Page": "Separat omslagssida",
"Scrolled Mode": "Rullningsläge",
"Sign in to Sync": "Logga in för synk",
"Synced at {{time}}": "Synkad {{time}}",
"Never synced": "Aldrig synkad",
"Invert Image In Dark Mode": "Invertera bild i mörkt läge",
"Reading Progress Synced": "Läsframsteg synkat",
@@ -1207,7 +1206,6 @@
"{{count}} failed_one": "{{count}} misslyckades",
"{{count}} failed_other": "{{count}} misslyckades",
"(none)": "(ingen)",
"Metadata Hash": "Metadata-hash",
"Meta Hash": "Meta-hash",
"Computed Hash": "Beräknat hash",
"Identifiers": "Identifierare",
@@ -1265,7 +1263,6 @@
"Uploading book…": "Laddar upp bok…",
"Generating…": "Genererar…",
"Generate share link": "Skapa delningslänk",
"Includes your current page": "Inkluderar din nuvarande sida",
"Share URL": "Delnings-URL",
"Copy link": "Kopiera länk",
"Copied": "Kopierad",
@@ -1305,7 +1302,6 @@
"Added to your library": "Tillagd i ditt bibliotek",
"Could not import shared book": "Det gick inte att importera den delade boken",
"Manage Shared Links": "Hantera delningslänkar",
"Share current page": "Dela nuvarande sida",
"Expires in": "Upphör om",
"{{count}} days_one": "1 dag",
"{{count}} days_other": "{{count}} dagar",
@@ -1314,5 +1310,12 @@
"Expires in {{count}} hours_one": "Upphör om 1 timme",
"Expires in {{count}} hours_other": "Upphör om {{count}} timmar",
"Open in app": "Öppna i appen",
"Shared with you": "Delat med dig"
"Shared with you": "Delat med dig",
"Downloading… {{percent}}%": "Laddar ner… {{percent}} %",
"Import progress": "Importförlopp",
"Share reading progress": "Dela läsförloppet",
"Includes your reading progress": "Inkluderar ditt läsförlopp",
"Synced {{time}}": "Synkroniserades {{time}}",
"Sync Info": "Synkroniseringsinformation",
"Last Synced": "Senast synkroniserad"
}
@@ -330,7 +330,6 @@
"{{engine}}: {{count}} voices_one": "{{engine}}: 1 குரல்",
"{{engine}}: {{count}} voices_other": "{{engine}}: {{count}} குரல்கள்",
"Sign in to Sync": "ஒத்திசைக்க உள்நுழையவும்",
"Synced at {{time}}": "{{time}} இல் ஒத்திசைக்கப்பட்டது",
"Never synced": "ஒத்திசைக்கப்படவில்லை",
"Reading Progress Synced": "வாசிப்பு முன்னேற்றம் ஒத்திசைக்கப்பட்டது",
"Page {{page}} of {{total}} ({{percentage}}%)": "{{total}} இல் {{page}} பக்கம் ({{percentage}}%)",
@@ -1207,7 +1206,6 @@
"{{count}} failed_one": "{{count}} தோல்வி",
"{{count}} failed_other": "{{count}} தோல்விகள்",
"(none)": "(இல்லை)",
"Metadata Hash": "மெட்டாடேட்டா ஹாஷ்",
"Meta Hash": "மெட்டா ஹாஷ்",
"Computed Hash": "கணக்கிடப்பட்ட ஹாஷ்",
"Identifiers": "அடையாளங்காட்டிகள்",
@@ -1265,7 +1263,6 @@
"Uploading book…": "புத்தகத்தை பதிவேற்றுகிறது…",
"Generating…": "உருவாக்குகிறது…",
"Generate share link": "பகிர்வு இணைப்பை உருவாக்கு",
"Includes your current page": "உங்கள் தற்போதைய பக்கத்தை உள்ளடக்கியது",
"Share URL": "பகிர்வு URL",
"Copy link": "இணைப்பை நகலெடு",
"Copied": "நகலெடுக்கப்பட்டது",
@@ -1305,7 +1302,6 @@
"Added to your library": "உங்கள் நூலகத்தில் சேர்க்கப்பட்டது",
"Could not import shared book": "பகிரப்பட்ட புத்தகத்தை இறக்குமதி செய்ய முடியவில்லை",
"Manage Shared Links": "பகிர்வு இணைப்புகளை நிர்வகி",
"Share current page": "தற்போதைய பக்கத்தைப் பகிர்",
"Expires in": "காலாவதியாகும்",
"{{count}} days_one": "1 நாள்",
"{{count}} days_other": "{{count}} நாட்கள்",
@@ -1314,5 +1310,12 @@
"Expires in {{count}} hours_one": "1 மணிநேரத்தில் காலாவதியாகும்",
"Expires in {{count}} hours_other": "{{count}} மணிநேரத்தில் காலாவதியாகும்",
"Open in app": "பயன்பாட்டில் திற",
"Shared with you": "உங்களுடன் பகிரப்பட்டது"
"Shared with you": "உங்களுடன் பகிரப்பட்டது",
"Downloading… {{percent}}%": "பதிவிறக்கம்… {{percent}}%",
"Import progress": "இறக்குமதி முன்னேற்றம்",
"Share reading progress": "வாசிப்பு முன்னேற்றத்தைப் பகிரவும்",
"Includes your reading progress": "உங்கள் வாசிப்பு முன்னேற்றத்தை உள்ளடக்கியது",
"Synced {{time}}": "{{time}} ஒத்திசைக்கப்பட்டது",
"Sync Info": "ஒத்திசைவு தகவல்",
"Last Synced": "கடைசி ஒத்திசைவு"
}
@@ -277,7 +277,6 @@
"Fast": "เร็ว",
"{{engine}}: {{count}} voices_other": "{{engine}}: {{count}} เสียง",
"Sign in to Sync": "เข้าสู่ระบบเพื่อซิงค์",
"Synced at {{time}}": "ซิงค์เมื่อ {{time}}",
"Never synced": "ไม่เคยซิงค์",
"Reading Progress Synced": "ซิงค์ความคืบหน้าแล้ว",
"Delete Your Account?": "ลบบัญชีของคุณ?",
@@ -1190,7 +1189,6 @@
"Retry all": "ลองอีกครั้งทั้งหมด",
"{{count}} failed_other": "{{count}} ล้มเหลว",
"(none)": "(ไม่มี)",
"Metadata Hash": "แฮชเมตาดาต้า",
"Meta Hash": "แฮชเมตา",
"Computed Hash": "แฮชที่คำนวณ",
"Identifiers": "ตัวระบุ",
@@ -1247,7 +1245,6 @@
"Uploading book…": "กำลังอัปโหลดหนังสือ…",
"Generating…": "กำลังสร้าง…",
"Generate share link": "สร้างลิงก์แชร์",
"Includes your current page": "รวมหน้าปัจจุบันของคุณ",
"Share URL": "URL แชร์",
"Copy link": "คัดลอกลิงก์",
"Copied": "คัดลอกแล้ว",
@@ -1285,11 +1282,17 @@
"Added to your library": "เพิ่มลงในคลังของคุณแล้ว",
"Could not import shared book": "ไม่สามารถนำเข้าหนังสือที่แชร์ได้",
"Manage Shared Links": "จัดการลิงก์แชร์",
"Share current page": "แชร์หน้าปัจจุบัน",
"Expires in": "หมดอายุใน",
"{{count}} days_other": "{{count}} วัน",
"Expires in {{count}} days_other": "หมดอายุใน {{count}} วัน",
"Expires in {{count}} hours_other": "หมดอายุใน {{count}} ชั่วโมง",
"Open in app": "เปิดในแอป",
"Shared with you": "แชร์กับคุณ"
"Shared with you": "แชร์กับคุณ",
"Downloading… {{percent}}%": "กำลังดาวน์โหลด… {{percent}}%",
"Import progress": "ความคืบหน้าการนำเข้า",
"Share reading progress": "แชร์ความคืบหน้าการอ่าน",
"Includes your reading progress": "รวมถึงความคืบหน้าการอ่านของคุณ",
"Synced {{time}}": "ซิงค์เมื่อ {{time}}",
"Sync Info": "ข้อมูลการซิงค์",
"Last Synced": "ซิงค์ล่าสุด"
}
@@ -299,7 +299,6 @@
"No notes match your search": "Eşleşen not bulunamadı",
"Search notes and excerpts...": "Notlarda ara...",
"Sign in to Sync": "Giriş yap",
"Synced at {{time}}": "{{time}}'de eşzlendi",
"Never synced": "Hiç eşzlenmedi",
"Show Remaining Time": "Kalan Süreyi Göster",
"{{time}} min left in chapter": "{{time}} dakika bölümde kaldı",
@@ -1207,7 +1206,6 @@
"{{count}} failed_one": "{{count}} başarısız",
"{{count}} failed_other": "{{count}} başarısız",
"(none)": "(yok)",
"Metadata Hash": "Üst Veri Hash'i",
"Meta Hash": "Meta Hash",
"Computed Hash": "Hesaplanan Hash",
"Identifiers": "Tanımlayıcılar",
@@ -1265,7 +1263,6 @@
"Uploading book…": "Kitap yükleniyor…",
"Generating…": "Oluşturuluyor…",
"Generate share link": "Paylaşım bağlantısı oluştur",
"Includes your current page": "Mevcut sayfanızı içerir",
"Share URL": "Paylaşım URLsi",
"Copy link": "Bağlantıyı kopyala",
"Copied": "Kopyalandı",
@@ -1305,7 +1302,6 @@
"Added to your library": "Kütüphanenize eklendi",
"Could not import shared book": "Paylaşılan kitap içe aktarılamadı",
"Manage Shared Links": "Paylaşım bağlantılarını yönet",
"Share current page": "Mevcut sayfayı paylaş",
"Expires in": "Sona erer",
"{{count}} days_one": "1 gün",
"{{count}} days_other": "{{count}} gün",
@@ -1314,5 +1310,12 @@
"Expires in {{count}} hours_one": "1 saat içinde sona erer",
"Expires in {{count}} hours_other": "{{count}} saat içinde sona erer",
"Open in app": "Uygulamada aç",
"Shared with you": "Sizinle paylaşıldı"
"Shared with you": "Sizinle paylaşıldı",
"Downloading… {{percent}}%": "İndiriliyor… %{{percent}}",
"Import progress": "İçe aktarma ilerlemesi",
"Share reading progress": "Okuma ilerlemesini paylaş",
"Includes your reading progress": "Okuma ilerlemenizi içerir",
"Synced {{time}}": "{{time}} senkronize edildi",
"Sync Info": "Senkronizasyon bilgisi",
"Last Synced": "Son senkronizasyon"
}
@@ -303,7 +303,6 @@
"No notes match your search": "Нотаток не знайдено",
"Search notes and excerpts...": "Шукати нотатки...",
"Sign in to Sync": "Увійти",
"Synced at {{time}}": "Синхр. о {{time}}",
"Never synced": "Ніколи не синхр.",
"Show Remaining Time": "Показати залишок часу",
"{{time}} min left in chapter": "{{time}} хв до кінця розділу",
@@ -1241,7 +1240,6 @@
"{{count}} failed_many": "{{count}} помилок",
"{{count}} failed_other": "{{count}} помилок",
"(none)": "(немає)",
"Metadata Hash": "Хеш метаданих",
"Meta Hash": "Мета-хеш",
"Computed Hash": "Обчислений хеш",
"Identifiers": "Ідентифікатори",
@@ -1301,7 +1299,6 @@
"Uploading book…": "Завантаження книги…",
"Generating…": "Створення…",
"Generate share link": "Створити посилання",
"Includes your current page": "Включає вашу поточну сторінку",
"Share URL": "URL для поширення",
"Copy link": "Копіювати посилання",
"Copied": "Скопійовано",
@@ -1345,7 +1342,6 @@
"Added to your library": "Додано до вашої бібліотеки",
"Could not import shared book": "Не вдалося імпортувати поширену книгу",
"Manage Shared Links": "Керування посиланнями",
"Share current page": "Поділитися поточною сторінкою",
"Expires in": "Закінчується через",
"{{count}} days_one": "{{count}} день",
"{{count}} days_few": "{{count}} дні",
@@ -1360,5 +1356,12 @@
"Expires in {{count}} hours_many": "Дійсне ще {{count}} годин",
"Expires in {{count}} hours_other": "Дійсне ще {{count}} годин",
"Open in app": "Відкрити в застосунку",
"Shared with you": "Поділилися з вами"
"Shared with you": "Поділилися з вами",
"Downloading… {{percent}}%": "Завантаження… {{percent}}%",
"Import progress": "Хід імпорту",
"Share reading progress": "Поділитися прогресом читання",
"Includes your reading progress": "Містить ваш прогрес читання",
"Synced {{time}}": "Синхронізовано {{time}}",
"Sync Info": "Інформація про синхронізацію",
"Last Synced": "Остання синхронізація"
}
@@ -297,7 +297,6 @@
"No notes match your search": "Không tìm thấy ghi chú nào",
"Search notes and excerpts...": "Tìm ghi chú...",
"Sign in to Sync": "Đăng nhập",
"Synced at {{time}}": "Đã đồng bộ lúc {{time}}",
"Never synced": "Chưa từng đồng bộ",
"Show Remaining Time": "Hiển thị thời gian còn lại",
"{{time}} min left in chapter": "{{time}} phút còn lại trong chương",
@@ -1190,7 +1189,6 @@
"Retry all": "Thử lại tất cả",
"{{count}} failed_other": "{{count}} thất bại",
"(none)": "(không có)",
"Metadata Hash": "Hash Siêu Dữ Liệu",
"Meta Hash": "Meta Hash",
"Computed Hash": "Hash Đã Tính",
"Identifiers": "Định Danh",
@@ -1247,7 +1245,6 @@
"Uploading book…": "Đang tải lên sách…",
"Generating…": "Đang tạo…",
"Generate share link": "Tạo liên kết chia sẻ",
"Includes your current page": "Bao gồm trang hiện tại của bạn",
"Share URL": "URL chia sẻ",
"Copy link": "Sao chép liên kết",
"Copied": "Đã sao chép",
@@ -1285,11 +1282,17 @@
"Added to your library": "Đã thêm vào thư viện của bạn",
"Could not import shared book": "Không thể nhập sách được chia sẻ",
"Manage Shared Links": "Quản lý liên kết chia sẻ",
"Share current page": "Chia sẻ trang hiện tại",
"Expires in": "Hết hạn sau",
"{{count}} days_other": "{{count}} ngày",
"Expires in {{count}} days_other": "Hết hạn sau {{count}} ngày",
"Expires in {{count}} hours_other": "Hết hạn sau {{count}} giờ",
"Open in app": "Mở trong ứng dụng",
"Shared with you": "Được chia sẻ với bạn"
"Shared with you": "Được chia sẻ với bạn",
"Downloading… {{percent}}%": "Đang tải xuống… {{percent}}%",
"Import progress": "Tiến độ nhập",
"Share reading progress": "Chia sẻ tiến độ đọc",
"Includes your reading progress": "Bao gồm tiến độ đọc của bạn",
"Synced {{time}}": "Đã đồng bộ {{time}}",
"Sync Info": "Thông tin đồng bộ",
"Last Synced": "Đồng bộ lần cuối"
}
@@ -298,7 +298,6 @@
"No notes match your search": "未找到匹配的笔记",
"Search notes and excerpts...": "搜索笔记...",
"Sign in to Sync": "登录后同步",
"Synced at {{time}}": "同步于:{{time}}",
"Never synced": "从未同步",
"Show Remaining Time": "显示剩余时间",
"{{time}} min left in chapter": "本章剩余 {{time}} 分钟",
@@ -1190,7 +1189,6 @@
"Retry all": "全部重试",
"{{count}} failed_other": "{{count}} 个失败",
"(none)": "(无)",
"Metadata Hash": "元数据哈希",
"Meta Hash": "元哈希",
"Computed Hash": "计算的哈希",
"Identifiers": "标识符",
@@ -1247,7 +1245,6 @@
"Uploading book…": "正在上传书籍…",
"Generating…": "正在生成…",
"Generate share link": "生成分享链接",
"Includes your current page": "包含你当前的页面",
"Share URL": "分享 URL",
"Copy link": "复制链接",
"Copied": "已复制",
@@ -1285,11 +1282,17 @@
"Added to your library": "已加入你的书库",
"Could not import shared book": "无法导入共享书籍",
"Manage Shared Links": "管理分享链接",
"Share current page": "分享当前页",
"Expires in": "有效期",
"{{count}} days_other": "{{count}} 天",
"Expires in {{count}} days_other": "{{count}} 天后到期",
"Expires in {{count}} hours_other": "{{count}} 小时后到期",
"Open in app": "在应用中打开",
"Shared with you": "与你分享"
"Shared with you": "与你分享",
"Downloading… {{percent}}%": "下载中… {{percent}}%",
"Import progress": "导入进度",
"Share reading progress": "分享阅读进度",
"Includes your reading progress": "包含你的阅读进度",
"Synced {{time}}": "{{time}}已同步",
"Sync Info": "同步信息",
"Last Synced": "上一次同步"
}
@@ -297,7 +297,6 @@
"No notes match your search": "找不到符合的筆記",
"Search notes and excerpts...": "搜尋筆記...",
"Sign in to Sync": "登入以同步",
"Synced at {{time}}": "同步於:{{time}}",
"Never synced": "從未同步",
"Show Remaining Time": "顯示剩餘時間",
"{{time}} min left in chapter": "本章剩餘 {{time}} 分鐘",
@@ -1190,7 +1189,6 @@
"Retry all": "全部重試",
"{{count}} failed_other": "{{count}} 個失敗",
"(none)": "(無)",
"Metadata Hash": "中繼資料雜湊",
"Meta Hash": "中繼雜湊",
"Computed Hash": "計算所得雜湊",
"Identifiers": "識別碼",
@@ -1247,7 +1245,6 @@
"Uploading book…": "正在上傳書籍…",
"Generating…": "正在產生…",
"Generate share link": "產生分享連結",
"Includes your current page": "包含您目前的頁面",
"Share URL": "分享 URL",
"Copy link": "複製連結",
"Copied": "已複製",
@@ -1285,11 +1282,17 @@
"Added to your library": "已加入您的書庫",
"Could not import shared book": "無法匯入分享書籍",
"Manage Shared Links": "管理分享連結",
"Share current page": "分享目前頁面",
"Expires in": "有效期",
"{{count}} days_other": "{{count}} 天",
"Expires in {{count}} days_other": "{{count}} 天後到期",
"Expires in {{count}} hours_other": "{{count}} 小時後到期",
"Open in app": "在應用中開啟",
"Shared with you": "與您分享"
"Shared with you": "與您分享",
"Downloading… {{percent}}%": "下載中… {{percent}}%",
"Import progress": "匯入進度",
"Share reading progress": "分享閱讀進度",
"Includes your reading progress": "包含你的閱讀進度",
"Synced {{time}}": "{{time}}已同步",
"Sync Info": "同步資訊",
"Last Synced": "上一次同步"
}
@@ -27,7 +27,7 @@ import { setMigrateDataDirDialogVisible } from '@/app/library/components/Migrate
import { requestStoragePermission } from '@/utils/permission';
import { saveSysSettings } from '@/helpers/settings';
import { selectDirectory } from '@/utils/bridge';
import { formatLocaleDateTime } from '@/utils/book';
import dayjs from 'dayjs';
import UserAvatar from '@/components/UserAvatar';
import MenuItem from '@/components/MenuItem';
import Quota from '@/components/Quota';
@@ -101,11 +101,6 @@ const SettingsMenu: React.FC<SettingsMenuProps> = ({ onPullLibrary, setIsDropdow
setThemeMode(nextMode);
};
const handleReloadPage = () => {
window.location.reload();
setIsDropdownOpen?.(false);
};
const handleFullScreen = () => {
tauriHandleToggleFullScreen();
setIsDropdownOpen?.(false);
@@ -319,8 +314,8 @@ const SettingsMenu: React.FC<SettingsMenuProps> = ({ onPullLibrary, setIsDropdow
<MenuItem
label={
settings.lastSyncedAtBooks
? _('Synced at {{time}}', {
time: formatLocaleDateTime(settings.lastSyncedAtBooks),
? _('Synced {{time}}', {
time: dayjs(settings.lastSyncedAtBooks).fromNow(),
})
: _('Never synced')
}
@@ -398,7 +393,6 @@ const SettingsMenu: React.FC<SettingsMenuProps> = ({ onPullLibrary, setIsDropdow
onClick={toggleAlwaysInForeground}
/>
)}
<MenuItem label={_('Reload Page')} onClick={handleReloadPage} />
<MenuItem
label={themeModeLabel}
Icon={themeMode === 'dark' ? PiMoon : themeMode === 'light' ? PiSun : TbSunMoon}
@@ -222,7 +222,7 @@ const ShareBookDialog: React.FC<ShareBookDialogProps> = ({ isOpen, book, cfi, on
isOpen={isOpen}
title={_('Share Book')}
onClose={onClose}
boxClassName='sm:min-w-[480px] sm:max-w-[480px] sm:h-auto sm:max-h-[90%]'
boxClassName='sm:min-w-[460px] sm:max-w-[460px] sm:h-auto sm:max-h-[90%]'
contentClassName='!px-6 !py-4'
>
<div className='flex flex-col gap-5 pt-2'>
@@ -278,7 +278,7 @@ const ShareBookDialog: React.FC<ShareBookDialogProps> = ({ isOpen, book, cfi, on
{cfi && (
<label className='flex min-h-12 cursor-pointer select-none items-center justify-between gap-3 px-4 py-2'>
<span className='text-base-content text-sm font-medium'>
{_('Share current page')}
{_('Share reading progress')}
</span>
<input
type='checkbox'
@@ -325,7 +325,7 @@ const ShareBookDialog: React.FC<ShareBookDialogProps> = ({ isOpen, book, cfi, on
{created.hasCfi && (
<div className='text-primary inline-flex items-center gap-1.5 self-start text-xs font-medium'>
<IoCheckmarkCircle className='h-4 w-4' aria-hidden='true' />
{_('Includes your current page')}
{_('Includes your reading progress')}
</div>
)}
@@ -30,7 +30,7 @@ import NotebookToggler from './NotebookToggler';
import SettingsToggler from './SettingsToggler';
import TranslationToggler from './TranslationToggler';
import ViewMenu from './ViewMenu';
import MetaHashInfoDialog from './MetaHashInfoDialog';
import SyncInfoDialog from './SyncInfoDialog';
interface HeaderBarProps {
bookKey: string;
@@ -64,9 +64,12 @@ const HeaderBar: React.FC<HeaderBarProps> = ({
const { isDarkMode, systemUIVisible, statusBarHeight } = useThemeStore();
const { isSideBarVisible, getIsSideBarVisible } = useSidebarStore();
const { getView, getViewSettings, setHoveredBookKey } = useReaderStore();
const { getBookData } = useBookDataStore();
const { getBookData, getConfig } = useBookDataStore();
const viewSettings = getViewSettings(bookKey);
const bookData = getBookData(bookKey);
const bookConfig = getConfig(bookKey);
const lastSyncedAt =
Math.max(bookConfig?.lastSyncedAtConfig || 0, bookConfig?.lastSyncedAtNotes || 0) || undefined;
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
const [isMetaHashDialogOpen, setIsMetaHashDialogOpen] = useState(false);
@@ -295,10 +298,11 @@ const HeaderBar: React.FC<HeaderBarProps> = ({
</Dropdown>
{isMetaHashDialogOpen && (
<ModalPortal showOverlay={false}>
<MetaHashInfoDialog
<SyncInfoDialog
isOpen={isMetaHashDialogOpen}
metadata={bookData?.bookDoc?.metadata ?? bookData?.book?.metadata}
storedMetaHash={bookData?.book?.metaHash}
lastSyncedAt={lastSyncedAt}
onClose={() => setIsMetaHashDialogOpen(false)}
/>
</ModalPortal>
@@ -2,28 +2,31 @@ import React from 'react';
import Dialog from '@/components/Dialog';
import { useTranslation } from '@/hooks/useTranslation';
import { BookMetadata } from '@/libs/document';
import { getMetadataHashInfo } from '@/utils/book';
import { formatLocaleDateTime, getMetadataHashInfo } from '@/utils/book';
interface MetaHashInfoDialogProps {
interface SyncInfoDialogProps {
isOpen: boolean;
metadata: BookMetadata | null | undefined;
storedMetaHash?: string;
/** Most recent sync timestamp across pull + push of config and notes. */
lastSyncedAt?: number;
onClose: () => void;
}
const Row: React.FC<{ label: string; value: React.ReactNode }> = ({ label, value }) => (
<div className='flex flex-col gap-1'>
<span className='text-base-content/60 text-xs uppercase tracking-wide'>{label}</span>
<div className='bg-base-200 text-base-content/90 break-all rounded-md p-2 font-mono text-xs'>
<span className='text-base-content/60 text-sm uppercase tracking-wide sm:text-xs'>{label}</span>
<div className='bg-base-200 text-base-content/90 break-all rounded-md p-2 font-mono text-sm sm:text-xs'>
{value}
</div>
</div>
);
const MetaHashInfoDialog: React.FC<MetaHashInfoDialogProps> = ({
const SyncInfoDialog: React.FC<SyncInfoDialogProps> = ({
isOpen,
metadata,
storedMetaHash,
lastSyncedAt,
onClose,
}) => {
const _ = useTranslation();
@@ -31,12 +34,14 @@ const MetaHashInfoDialog: React.FC<MetaHashInfoDialogProps> = ({
const displayHash = storedMetaHash || info?.metaHash || '';
const hashesMatch = !info || !storedMetaHash || storedMetaHash === info.metaHash;
const placeholder = _('(none)');
const lastSyncedLabel = lastSyncedAt ? formatLocaleDateTime(lastSyncedAt) : _('Never synced');
return (
<Dialog
isOpen={isOpen}
onClose={onClose}
title={_('Metadata Hash')}
snapHeight={0.7}
title={_('Sync Info')}
boxClassName='sm:!min-w-[520px] sm:h-auto'
>
{isOpen && (
@@ -52,10 +57,11 @@ const MetaHashInfoDialog: React.FC<MetaHashInfoDialogProps> = ({
label={_('Identifiers')}
value={info && info.identifiers.length > 0 ? info.identifiers.join(', ') : placeholder}
/>
<Row label={_('Last Synced')} value={lastSyncedLabel} />
</div>
)}
</Dialog>
);
};
export default MetaHashInfoDialog;
export default SyncInfoDialog;
@@ -23,7 +23,7 @@ import { getStyles } from '@/utils/style';
import { navigateToLogin } from '@/utils/nav';
import { eventDispatcher } from '@/utils/event';
import { getMaxInlineSize } from '@/utils/config';
import { formatLocaleDateTime } from '@/utils/book';
import dayjs from 'dayjs';
import { saveViewSettings } from '@/helpers/settings';
import { tauriHandleToggleFullScreen } from '@/utils/window';
import MenuItem from '@/components/MenuItem';
@@ -327,8 +327,8 @@ const ViewMenu: React.FC<ViewMenuProps> = ({
!user
? _('Sign in to Sync')
: lastSyncTime
? _('Synced at {{time}}', {
time: formatLocaleDateTime(lastSyncTime),
? _('Synced {{time}}', {
time: dayjs(lastSyncTime).fromNow(),
})
: _('Never synced')
}
@@ -337,8 +337,8 @@ const ViewMenu: React.FC<ViewMenuProps> = ({
onClick={handleSync}
siblings={
<button
aria-label={_('Metadata Hash')}
title={_('Metadata Hash')}
aria-label={_('Sync Info')}
title={_('Sync Info')}
className='hover:bg-base-300 text-base-content/70 mx-1 rounded-md px-2'
onClick={() => {
setIsDropdownOpen?.(false);
+56 -10
View File
@@ -12,11 +12,14 @@ import {
import { DOWNLOAD_READEST_URL } from '@/services/constants';
import { useTranslation, type TranslationFunc } from '@/hooks/useTranslation';
import { useAuth } from '@/context/AuthContext';
import { useEnv } from '@/context/EnvContext';
import { BrandHeader } from '@/components/landing/BrandHeader';
import { Card } from '@/components/landing/Card';
import { PageFooter } from '@/components/landing/PageFooter';
import { getShare, importShare, type ShareMetadata } from '@/libs/share';
import { ensureSharedBookLocal } from '@/libs/shareImport';
import { formatBytes } from '@/utils/book';
import { navigateToReader } from '@/utils/nav';
const formatExpiry = (iso: string, _: TranslationFunc): string => {
const ms = new Date(iso).getTime() - Date.now();
@@ -33,6 +36,7 @@ const ShareLanding = () => {
const searchParams = useSearchParams();
const pathname = usePathname();
const { user } = useAuth();
const { appService } = useEnv();
// Resolve the token from either the rewritten query (?token=) or the pretty
// path (/s/{token}). The next.config.mjs rewrite handles the web build; the
@@ -50,6 +54,7 @@ const ShareLanding = () => {
const [meta, setMeta] = useState<ShareMetadata | null>(null);
const [loadError, setLoadError] = useState<{ status: number; message: string } | null>(null);
const [importing, setImporting] = useState(false);
const [importProgress, setImportProgress] = useState<number | null>(null);
const [importError, setImportError] = useState<string | null>(null);
useEffect(() => {
@@ -80,19 +85,32 @@ const ShareLanding = () => {
const appHref = `readest://share/${encodeURIComponent(token)}`;
const handleAddToLibrary = async () => {
if (!token || importing) return;
if (!token || importing || !appService) return;
setImporting(true);
setImportProgress(null);
setImportError(null);
try {
const result = await importShare(token);
// The reader resolves `ids` via getBookByHash, so pass the book hash —
// not the `files.id` UUID. Same identifier used by /reader/:hash links.
const params = new URLSearchParams();
params.set('ids', result.bookHash);
if (result.cfi) params.set('cfi', result.cfi);
router.push(`/reader?${params.toString()}`);
// /import only mutates server state (R2 byte-copy + files row). The
// local library is unchanged, so the reader's getBookByHash would miss.
// Pull bytes + create the local Book entry before navigating; meta is
// already in state from the initial getShare call so no extra round-trip.
await ensureSharedBookLocal({
token,
importResult: result,
appService,
meta: meta ?? undefined,
onProgress: setImportProgress,
});
// navigateToReader routes web to /reader/{hash} (Pages Router page that
// actually renders) and Tauri to /reader?ids={hash}. Building the URL
// by hand here lands on the App Router /reader page instead, which is
// a stub for this flow and renders blank.
const queryParams = result.cfi ? `cfi=${encodeURIComponent(result.cfi)}` : undefined;
navigateToReader(router, [result.bookHash], queryParams);
} catch (err) {
setImporting(false);
setImportProgress(null);
const message = err instanceof Error ? err.message : _('Could not add to your library');
setImportError(message);
}
@@ -227,14 +245,42 @@ const ShareLanding = () => {
type='button'
onClick={handleAddToLibrary}
disabled={importing}
aria-busy={importing}
className='btn btn-primary btn-block flex-nowrap gap-2 whitespace-nowrap rounded-xl'
>
<IoLibraryOutline className='h-5 w-5' aria-hidden='true' />
{importing ? _('Adding…') : _('Add to my library')}
{importing ? (
<span className='loading loading-spinner loading-sm' aria-hidden='true' />
) : (
<IoLibraryOutline className='h-5 w-5' aria-hidden='true' />
)}
{importing
? importProgress !== null
? _('Downloading… {{percent}}%', { percent: importProgress })
: _('Adding…')
: _('Add to my library')}
</button>
{/* Live progress bar while bytes are streaming in. Stays at
the indeterminate striped state until we get the first
progress event from the byte transfer. */}
{importing && (
<progress
className='progress progress-primary w-full'
value={importProgress ?? undefined}
max={100}
aria-label={_('Import progress')}
/>
)}
<a
href={appHref}
className='btn btn-ghost btn-block flex-nowrap gap-2 whitespace-nowrap rounded-xl'
aria-disabled={importing}
onClick={(e) => {
if (importing) e.preventDefault();
}}
className={
importing
? 'btn btn-ghost btn-block btn-disabled flex-nowrap gap-2 whitespace-nowrap rounded-xl'
: 'btn btn-ghost btn-block flex-nowrap gap-2 whitespace-nowrap rounded-xl'
}
>
<IoOpenOutline className='h-5 w-5' aria-hidden='true' />
{_('Open in app')}
@@ -13,18 +13,23 @@ interface UserInfoProps {
const UserInfo: React.FC<UserInfoProps> = ({ avatarUrl, userFullName, userEmail, planDetails }) => {
const _ = useTranslation();
return (
<div className='flex flex-col items-center gap-x-6 gap-y-4 md:flex-row md:items-start'>
<div className='flex-shrink-0'>
<div className='flex flex-col items-center gap-x-6 gap-y-2 md:flex-row md:items-center'>
{/* Lock the avatar box to a square via classes so it can't go oval
across breakpoints. fillContainer drops UserAvatar's inline
width/height so the child stretches to this wrapper instead of
fighting it. */}
<div className='aspect-square h-16 w-16 flex-shrink-0 md:h-24 md:w-24'>
{avatarUrl ? (
<UserAvatar
url={avatarUrl}
size={window.innerWidth < 640 ? 64 : 128}
size={128}
DefaultIcon={PiUserCircle}
className='h-16 w-16 md:h-24 md:w-24'
className='h-full w-full'
borderClassName='border-base-100 border-4'
fillContainer
/>
) : (
<PiUserCircle className='h-16 w-16 md:h-24 md:w-24' />
<PiUserCircle className='h-full w-full' />
)}
</div>
+11 -1
View File
@@ -9,6 +9,15 @@ interface UserAvatarProps {
DefaultIcon: IconType;
className?: string;
borderClassName?: string;
/**
* When true, the root box stretches via h-full/w-full instead of getting
* the explicit `size` pinned via inline style. Use this when a parent
* constrains the avatar with its own (responsive) classes otherwise the
* inline style overrides the parent box and the avatar can overflow on
* smaller breakpoints. `size` is still used as the intrinsic dimension
* for next/image and the fallback icon.
*/
fillContainer?: boolean;
}
const getStorageKey = (url: string) => {
@@ -21,6 +30,7 @@ const UserAvatar: React.FC<UserAvatarProps> = ({
className,
borderClassName,
DefaultIcon,
fillContainer,
}) => {
const [cachedImageUrl, setCachedImageUrl] = useState<string | null>(() => {
if (!url) return null;
@@ -64,7 +74,7 @@ const UserAvatar: React.FC<UserAvatarProps> = ({
return (
<div
className='relative flex h-full w-full items-center justify-center rounded-full'
style={{ width: size, height: size }}
style={fillContainer ? undefined : { width: size, height: size }}
>
{url ? (
<div>
@@ -8,6 +8,7 @@ import { eventDispatcher } from '@/utils/event';
import { useAuth } from '@/context/AuthContext';
import { navigateToReader } from '@/utils/nav';
import { ShareApiError, confirmDownload, importShare } from '@/libs/share';
import { ensureSharedBookLocal } from '@/libs/shareImport';
import { parseShareDeepLink, type ShareDeepLink } from '@/utils/share';
import { useTranslation } from './useTranslation';
@@ -57,13 +58,18 @@ export function useOpenShareLink() {
});
return;
}
if (!appService) return;
try {
const result = await importShare(token);
// The /import endpoint only creates rows + R2 bytes server-side; the
// local library is unchanged. Make sure the local library has both the
// Book entry and the bytes on disk before navigating, otherwise
// `getBookByHash` returns undefined and the reader throws "Book not
// found". See src/libs/shareImport.ts for the three branches.
await ensureSharedBookLocal({ token, importResult: result, appService });
// Best-effort analytics ping; doesn't affect UX.
confirmDownload(token);
// The reader resolves IDs via getBookByHash, so navigate with the
// book hash (not the `files.id` UUID).
const queryParams = result.cfi ? `cfi=${encodeURIComponent(result.cfi)}` : undefined;
navigateToReader(router, [result.bookHash], queryParams);
@@ -86,7 +92,7 @@ export function useOpenShareLink() {
});
}
},
[_, router, user],
[_, router, user, appService],
);
useEffect(() => {
+126
View File
@@ -0,0 +1,126 @@
import type { Book } from '@/types/book';
import type { AppService } from '@/types/system';
import { useLibraryStore } from '@/store/libraryStore';
import { getAPIBaseUrl } from '@/services/environment';
import { ShareApiError, getShare, type ImportShareResponse, type ShareMetadata } from './share';
interface EnsureSharedBookLocalArgs {
token: string;
importResult: ImportShareResponse;
appService: AppService;
/** Optional cached share metadata to avoid an extra GET on the new-import branch. */
meta?: ShareMetadata;
/** Progress callback for byte transfers (0-100). */
onProgress?: (percent: number) => void;
}
/**
* After `importShare(token)` succeeds (server-side R2 byte-copy done), make
* sure the local library has BOTH the Book entry AND the bytes on local fs,
* so opening the reader at the shared book works without a "Book not found"
* error.
*
* Three branches:
* - Book is in library and bytes are present on fs no-op, return book.
* - Book is in library but bytes are missing locally (e.g. previously deleted
* or imported from another device) `appService.downloadBook` pulls from
* the recipient's R2 namespace, which the import endpoint just populated.
* - Book is NOT in the local library fetch bytes via the public share
* download endpoint, then run `appService.importBook` so a proper local
* Book entry is created (metadata, cover extraction, dir layout). Mark
* `uploadedAt` + `coverDownloadedAt` so transferManager doesn't re-upload
* bytes the server already has, and so the cover-pull doesn't redo what
* `importBook` just extracted from the book file.
*/
export const ensureSharedBookLocal = async ({
token,
importResult,
appService,
meta,
onProgress,
}: EnsureSharedBookLocalArgs): Promise<Book> => {
const storeState = useLibraryStore.getState();
const { setLibrary } = storeState;
// When the share landing runs this helper, `libraryLoaded` is false because
// /s/[token] doesn't mount useLibrary(). We load fresh from disk and only
// push the result back into the store if the store had already been hydrated
// by useLibrary somewhere else (e.g. /library, /reader, /opds). Otherwise we
// *must not* set libraryLoaded ourselves: useLibrary's init block loads BOTH
// the library AND `settings.globalReadSettings` in one go, and skips the
// whole block when libraryLoaded is already true. Setting it prematurely
// here leaves settings unloaded, and the Reader gate at Reader.tsx:167
// (`libraryLoaded && settings.globalReadSettings`) renders the empty
// fallback — exactly the blank-page symptom.
const wasLibraryLoaded = storeState.libraryLoaded;
const library = wasLibraryLoaded ? storeState.library : await appService.loadLibraryBooks();
const findByHash = (hash: string): Book | undefined =>
wasLibraryLoaded ? storeState.getBookByHash(hash) : library.find((b) => b.hash === hash);
const existing = findByHash(importResult.bookHash);
const persistLibrary = async () => {
await appService.saveLibraryBooks(library);
if (wasLibraryLoaded) setLibrary(library);
};
const reportProgress = onProgress
? (prog: { progress: number; total: number }) => {
if (prog.total > 0) onProgress(Math.floor((prog.progress / prog.total) * 100));
}
: undefined;
if (existing) {
const bytesPresent = !!existing.downloadedAt && (await appService.isBookAvailable(existing));
if (bytesPresent) return existing;
// Pull from recipient's namespace — the import endpoint already byte-copied
// both the book and the cover there. downloadBook handles missing-cover
// gracefully (covers may not exist) and sets downloadedAt internally.
await appService.downloadBook(existing, false, false, reportProgress);
// cloudService.downloadBook is silent on failure: if the cloud path
// mismatches the local Book's filename (e.g. share-import wrote bytes at
// the sharer's title, recipient's local Book has a different title) the
// function resolves without touching downloadedAt and the bytes are still
// absent. Verify before claiming success — otherwise the recipient
// navigates into the reader and hits "Book file not found".
if (!(await appService.isBookAvailable(existing))) {
throw new Error('Could not download shared book');
}
if (!existing.downloadedAt) existing.downloadedAt = Date.now();
existing.updatedAt = Date.now();
await persistLibrary();
return existing;
}
// Book is not in the local library yet. Fetch the bytes via the public share
// download endpoint (302 → presigned URL; fetch follows redirects), then
// hand them to importBook which knows how to create the proper local Book.
const shareMeta = meta ?? (await getShare(token));
const downloadUrl = `${getAPIBaseUrl()}/share/${encodeURIComponent(token)}/download`;
const response = await fetch(downloadUrl);
if (!response.ok) {
throw new ShareApiError(
response.status,
undefined,
response.statusText || 'Could not download shared book',
);
}
const blob = await response.blob();
const filename = `${shareMeta.title}.${shareMeta.format.toLowerCase()}`;
const file = new File([blob], filename, { type: blob.type || 'application/octet-stream' });
const imported = await appService.importBook(file, library);
if (!imported) {
throw new Error('Could not import shared book');
}
// The server already holds the book + cover bytes (R2 byte-copy in the
// import endpoint), so we tag uploadedAt/coverDownloadedAt to skip work
// transferManager and the cloud cover-pull would otherwise repeat.
const now = Date.now();
imported.uploadedAt = now;
if (!imported.downloadedAt) imported.downloadedAt = now;
imported.coverDownloadedAt = now;
await persistLibrary();
return imported;
};