diff --git a/apps/readest-app/public/locales/ar/translation.json b/apps/readest-app/public/locales/ar/translation.json index 65cf1ddb..af63869a 100644 --- a/apps/readest-app/public/locales/ar/translation.json +++ b/apps/readest-app/public/locales/ar/translation.json @@ -1926,5 +1926,11 @@ "Background Image (Reader)": "صورة الخلفية (واجهة القراءة)", "updated metadata for {{n}} book(s)": "تم تحديث البيانات الوصفية لـ {{n}} كتاب", "{{n}} metadata": "{{n}} بيانات وصفية", - "Metadata updated": "تم تحديث البيانات الوصفية" + "Metadata updated": "تم تحديث البيانات الوصفية", + "Filter": "تصفية", + "Sort by": "ترتيب حسب", + "Date modified": "تاريخ التعديل", + "Date created": "تاريخ الإنشاء", + "Sort ascending": "ترتيب تصاعدي", + "Sort descending": "ترتيب تنازلي" } diff --git a/apps/readest-app/public/locales/bn/translation.json b/apps/readest-app/public/locales/bn/translation.json index 946cfef5..f26a38da 100644 --- a/apps/readest-app/public/locales/bn/translation.json +++ b/apps/readest-app/public/locales/bn/translation.json @@ -1794,5 +1794,11 @@ "Background Image (Reader)": "ব্যাকগ্রাউন্ড ছবি (রিডার)", "updated metadata for {{n}} book(s)": "{{n}}টি বইয়ের মেটাডেটা আপডেট হয়েছে", "{{n}} metadata": "{{n}} মেটাডেটা", - "Metadata updated": "মেটাডেটা আপডেট হয়েছে" + "Metadata updated": "মেটাডেটা আপডেট হয়েছে", + "Filter": "ফিল্টার", + "Sort by": "অনুসারে সাজান", + "Date modified": "পরিবর্তনের তারিখ", + "Date created": "তৈরির তারিখ", + "Sort ascending": "ঊর্ধ্বক্রমে সাজান", + "Sort descending": "অধঃক্রমে সাজান" } diff --git a/apps/readest-app/public/locales/de/translation.json b/apps/readest-app/public/locales/de/translation.json index 055c4520..dc180c80 100644 --- a/apps/readest-app/public/locales/de/translation.json +++ b/apps/readest-app/public/locales/de/translation.json @@ -1794,5 +1794,11 @@ "Background Image (Reader)": "Hintergrundbild (Leseansicht)", "updated metadata for {{n}} book(s)": "Metadaten für {{n}} Buch/Bücher aktualisiert", "{{n}} metadata": "{{n}} Metadaten", - "Metadata updated": "Metadaten aktualisiert" + "Metadata updated": "Metadaten aktualisiert", + "Filter": "Filtern", + "Sort by": "Sortieren nach", + "Date modified": "Änderungsdatum", + "Date created": "Erstellungsdatum", + "Sort ascending": "Aufsteigend sortieren", + "Sort descending": "Absteigend sortieren" } diff --git a/apps/readest-app/public/locales/el/translation.json b/apps/readest-app/public/locales/el/translation.json index 0c5f936c..548a4ed5 100644 --- a/apps/readest-app/public/locales/el/translation.json +++ b/apps/readest-app/public/locales/el/translation.json @@ -1794,5 +1794,11 @@ "Background Image (Reader)": "Εικόνα φόντου (Προβολή ανάγνωσης)", "updated metadata for {{n}} book(s)": "ενημερώθηκαν τα μεταδεδομένα για {{n}} βιβλίο/α", "{{n}} metadata": "{{n}} μεταδεδομένα", - "Metadata updated": "Ενημερωμένα μεταδεδομένα" + "Metadata updated": "Ενημερωμένα μεταδεδομένα", + "Filter": "Φιλτράρισμα", + "Sort by": "Ταξινόμηση κατά", + "Date modified": "Ημερομηνία τροποποίησης", + "Date created": "Ημερομηνία δημιουργίας", + "Sort ascending": "Αύξουσα ταξινόμηση", + "Sort descending": "Φθίνουσα ταξινόμηση" } diff --git a/apps/readest-app/public/locales/es/translation.json b/apps/readest-app/public/locales/es/translation.json index d4097ee2..ad84348f 100644 --- a/apps/readest-app/public/locales/es/translation.json +++ b/apps/readest-app/public/locales/es/translation.json @@ -1827,5 +1827,11 @@ "Background Image (Reader)": "Imagen de fondo (Lector)", "updated metadata for {{n}} book(s)": "metadatos actualizados de {{n}} libro(s)", "{{n}} metadata": "{{n}} metadatos", - "Metadata updated": "Metadatos actualizados" + "Metadata updated": "Metadatos actualizados", + "Filter": "Filtrar", + "Sort by": "Ordenar por", + "Date modified": "Fecha de modificación", + "Date created": "Fecha de creación", + "Sort ascending": "Orden ascendente", + "Sort descending": "Orden descendente" } diff --git a/apps/readest-app/public/locales/fa/translation.json b/apps/readest-app/public/locales/fa/translation.json index 825f3134..192826b9 100644 --- a/apps/readest-app/public/locales/fa/translation.json +++ b/apps/readest-app/public/locales/fa/translation.json @@ -1794,5 +1794,11 @@ "Background Image (Reader)": "تصویر پس‌زمینه (صفحه مطالعه)", "updated metadata for {{n}} book(s)": "متادیتای {{n}} کتاب به‌روزرسانی شد", "{{n}} metadata": "{{n}} متادیتا", - "Metadata updated": "متادیتا به‌روزرسانی شد" + "Metadata updated": "متادیتا به‌روزرسانی شد", + "Filter": "فیلتر", + "Sort by": "مرتب‌سازی بر اساس", + "Date modified": "تاریخ تغییر", + "Date created": "تاریخ ایجاد", + "Sort ascending": "مرتب‌سازی صعودی", + "Sort descending": "مرتب‌سازی نزولی" } diff --git a/apps/readest-app/public/locales/fr/translation.json b/apps/readest-app/public/locales/fr/translation.json index 22d4bbe8..b21233da 100644 --- a/apps/readest-app/public/locales/fr/translation.json +++ b/apps/readest-app/public/locales/fr/translation.json @@ -1827,5 +1827,11 @@ "Background Image (Reader)": "Image de Fond (Vue de lecture)", "updated metadata for {{n}} book(s)": "métadonnées mises à jour pour {{n}} livre(s)", "{{n}} metadata": "{{n}} métadonnées", - "Metadata updated": "Métadonnées mises à jour" + "Metadata updated": "Métadonnées mises à jour", + "Filter": "Filtrer", + "Sort by": "Trier par", + "Date modified": "Date de modification", + "Date created": "Date de création", + "Sort ascending": "Tri croissant", + "Sort descending": "Tri décroissant" } diff --git a/apps/readest-app/public/locales/he/translation.json b/apps/readest-app/public/locales/he/translation.json index dffeb5ae..bcd3732b 100644 --- a/apps/readest-app/public/locales/he/translation.json +++ b/apps/readest-app/public/locales/he/translation.json @@ -1827,5 +1827,11 @@ "Background Image (Reader)": "תמונת רקע (מצב קריאה)", "updated metadata for {{n}} book(s)": "המטא-נתונים עודכנו עבור {{n}} ספרים", "{{n}} metadata": "{{n}} מטא-נתונים", - "Metadata updated": "המטא-נתונים עודכנו" + "Metadata updated": "המטא-נתונים עודכנו", + "Filter": "סינון", + "Sort by": "מיין לפי", + "Date modified": "תאריך שינוי", + "Date created": "תאריך יצירה", + "Sort ascending": "מיון עולה", + "Sort descending": "מיון יורד" } diff --git a/apps/readest-app/public/locales/hi/translation.json b/apps/readest-app/public/locales/hi/translation.json index 49f37909..ab866d83 100644 --- a/apps/readest-app/public/locales/hi/translation.json +++ b/apps/readest-app/public/locales/hi/translation.json @@ -1794,5 +1794,11 @@ "Background Image (Reader)": "पृष्ठभूमि छवि (रीडर)", "updated metadata for {{n}} book(s)": "{{n}} पुस्तक(ों) के मेटाडेटा अपडेट किए गए", "{{n}} metadata": "{{n}} मेटाडेटा", - "Metadata updated": "मेटाडेटा अपडेट किया गया" + "Metadata updated": "मेटाडेटा अपडेट किया गया", + "Filter": "फ़िल्टर", + "Sort by": "इसके अनुसार क्रमित करें", + "Date modified": "संशोधन तिथि", + "Date created": "निर्माण तिथि", + "Sort ascending": "आरोही क्रम", + "Sort descending": "अवरोही क्रम" } diff --git a/apps/readest-app/public/locales/hu/translation.json b/apps/readest-app/public/locales/hu/translation.json index cd934cd3..5c435f17 100644 --- a/apps/readest-app/public/locales/hu/translation.json +++ b/apps/readest-app/public/locales/hu/translation.json @@ -1794,5 +1794,11 @@ "Background Image (Reader)": "Háttérkép (Olvasónézet)", "updated metadata for {{n}} book(s)": "{{n}} könyv metaadatai frissítve", "{{n}} metadata": "{{n}} metaadat", - "Metadata updated": "Metaadatok frissítve" + "Metadata updated": "Metaadatok frissítve", + "Filter": "Szűrés", + "Sort by": "Rendezés alapja", + "Date modified": "Módosítás dátuma", + "Date created": "Létrehozás dátuma", + "Sort ascending": "Növekvő sorrend", + "Sort descending": "Csökkenő sorrend" } diff --git a/apps/readest-app/public/locales/id/translation.json b/apps/readest-app/public/locales/id/translation.json index d5aa72c7..dd87e931 100644 --- a/apps/readest-app/public/locales/id/translation.json +++ b/apps/readest-app/public/locales/id/translation.json @@ -1761,5 +1761,11 @@ "Background Image (Reader)": "Gambar Latar Belakang (Tampilan Baca)", "updated metadata for {{n}} book(s)": "metadata diperbarui untuk {{n}} buku", "{{n}} metadata": "{{n}} metadata", - "Metadata updated": "Metadata diperbarui" + "Metadata updated": "Metadata diperbarui", + "Filter": "Filter", + "Sort by": "Urutkan menurut", + "Date modified": "Tanggal diubah", + "Date created": "Tanggal dibuat", + "Sort ascending": "Urutkan menaik", + "Sort descending": "Urutkan menurun" } diff --git a/apps/readest-app/public/locales/it/translation.json b/apps/readest-app/public/locales/it/translation.json index b676b933..65bdc6de 100644 --- a/apps/readest-app/public/locales/it/translation.json +++ b/apps/readest-app/public/locales/it/translation.json @@ -1827,5 +1827,11 @@ "Background Image (Reader)": "Immagine di sfondo (Vista di lettura)", "updated metadata for {{n}} book(s)": "metadati aggiornati per {{n}} libro/i", "{{n}} metadata": "{{n}} metadati", - "Metadata updated": "Metadati aggiornati" + "Metadata updated": "Metadati aggiornati", + "Filter": "Filtra", + "Sort by": "Ordina per", + "Date modified": "Data di modifica", + "Date created": "Data di creazione", + "Sort ascending": "Ordine crescente", + "Sort descending": "Ordine decrescente" } diff --git a/apps/readest-app/public/locales/ja/translation.json b/apps/readest-app/public/locales/ja/translation.json index 96da46d9..9e140f62 100644 --- a/apps/readest-app/public/locales/ja/translation.json +++ b/apps/readest-app/public/locales/ja/translation.json @@ -1761,5 +1761,11 @@ "Background Image (Reader)": "背景画像(リーダー)", "updated metadata for {{n}} book(s)": "{{n}} 冊分のメタデータを更新", "{{n}} metadata": "{{n}} 件メタデータ", - "Metadata updated": "更新したメタデータ" + "Metadata updated": "更新したメタデータ", + "Filter": "フィルター", + "Sort by": "並べ替え", + "Date modified": "更新日", + "Date created": "作成日", + "Sort ascending": "昇順で並べ替え", + "Sort descending": "降順で並べ替え" } diff --git a/apps/readest-app/public/locales/ko/translation.json b/apps/readest-app/public/locales/ko/translation.json index cedba22f..a0ebe4db 100644 --- a/apps/readest-app/public/locales/ko/translation.json +++ b/apps/readest-app/public/locales/ko/translation.json @@ -1761,5 +1761,11 @@ "Background Image (Reader)": "배경 이미지 (리더)", "updated metadata for {{n}} book(s)": "{{n}}권의 메타데이터 업데이트됨", "{{n}} metadata": "{{n}} 메타데이터", - "Metadata updated": "메타데이터 업데이트됨" + "Metadata updated": "메타데이터 업데이트됨", + "Filter": "필터", + "Sort by": "정렬 기준", + "Date modified": "수정한 날짜", + "Date created": "만든 날짜", + "Sort ascending": "오름차순 정렬", + "Sort descending": "내림차순 정렬" } diff --git a/apps/readest-app/public/locales/ms/translation.json b/apps/readest-app/public/locales/ms/translation.json index 366b6ac4..1334e5cd 100644 --- a/apps/readest-app/public/locales/ms/translation.json +++ b/apps/readest-app/public/locales/ms/translation.json @@ -1761,5 +1761,11 @@ "Background Image (Reader)": "Imej Latar Belakang (Paparan Bacaan)", "updated metadata for {{n}} book(s)": "metadata dikemas kini untuk {{n}} buku", "{{n}} metadata": "{{n}} metadata", - "Metadata updated": "Metadata dikemas kini" + "Metadata updated": "Metadata dikemas kini", + "Filter": "Tapis", + "Sort by": "Isih mengikut", + "Date modified": "Tarikh diubah suai", + "Date created": "Tarikh dicipta", + "Sort ascending": "Isih menaik", + "Sort descending": "Isih menurun" } diff --git a/apps/readest-app/public/locales/nl/translation.json b/apps/readest-app/public/locales/nl/translation.json index bdea4d29..db2ef335 100644 --- a/apps/readest-app/public/locales/nl/translation.json +++ b/apps/readest-app/public/locales/nl/translation.json @@ -1794,5 +1794,11 @@ "Background Image (Reader)": "Achtergrondafbeelding (Leesweergave)", "updated metadata for {{n}} book(s)": "metadata bijgewerkt voor {{n}} boek(en)", "{{n}} metadata": "{{n}} metadata", - "Metadata updated": "Metadata bijgewerkt" + "Metadata updated": "Metadata bijgewerkt", + "Filter": "Filteren", + "Sort by": "Sorteren op", + "Date modified": "Wijzigingsdatum", + "Date created": "Aanmaakdatum", + "Sort ascending": "Oplopend sorteren", + "Sort descending": "Aflopend sorteren" } diff --git a/apps/readest-app/public/locales/pl/translation.json b/apps/readest-app/public/locales/pl/translation.json index 098c4b52..29949420 100644 --- a/apps/readest-app/public/locales/pl/translation.json +++ b/apps/readest-app/public/locales/pl/translation.json @@ -1860,5 +1860,11 @@ "Background Image (Reader)": "Obraz tła (Widok czytania)", "updated metadata for {{n}} book(s)": "zaktualizowano metadane dla {{n}} książek", "{{n}} metadata": "{{n}} metadane", - "Metadata updated": "Zaktualizowane metadane" + "Metadata updated": "Zaktualizowane metadane", + "Filter": "Filtruj", + "Sort by": "Sortuj według", + "Date modified": "Data modyfikacji", + "Date created": "Data utworzenia", + "Sort ascending": "Sortuj rosnąco", + "Sort descending": "Sortuj malejąco" } diff --git a/apps/readest-app/public/locales/pt-BR/translation.json b/apps/readest-app/public/locales/pt-BR/translation.json index 04c1465a..e9782ca6 100644 --- a/apps/readest-app/public/locales/pt-BR/translation.json +++ b/apps/readest-app/public/locales/pt-BR/translation.json @@ -1827,5 +1827,11 @@ "Background Image (Reader)": "Imagem de fundo (Vista de leitura)", "updated metadata for {{n}} book(s)": "metadados atualizados de {{n}} livro(s)", "{{n}} metadata": "{{n}} metadados", - "Metadata updated": "Metadados atualizados" + "Metadata updated": "Metadados atualizados", + "Filter": "Filtrar", + "Sort by": "Ordenar por", + "Date modified": "Data de modificação", + "Date created": "Data de criação", + "Sort ascending": "Ordem crescente", + "Sort descending": "Ordem decrescente" } diff --git a/apps/readest-app/public/locales/pt/translation.json b/apps/readest-app/public/locales/pt/translation.json index a1f420b2..8135800e 100644 --- a/apps/readest-app/public/locales/pt/translation.json +++ b/apps/readest-app/public/locales/pt/translation.json @@ -1827,5 +1827,11 @@ "Background Image (Reader)": "Imagem de Fundo (Vista de leitura)", "updated metadata for {{n}} book(s)": "metadados atualizados de {{n}} livro(s)", "{{n}} metadata": "{{n}} metadados", - "Metadata updated": "Metadados atualizados" + "Metadata updated": "Metadados atualizados", + "Filter": "Filtrar", + "Sort by": "Ordenar por", + "Date modified": "Data de modificação", + "Date created": "Data de criação", + "Sort ascending": "Ordem crescente", + "Sort descending": "Ordem decrescente" } diff --git a/apps/readest-app/public/locales/ro/translation.json b/apps/readest-app/public/locales/ro/translation.json index 05c6222a..7eac417e 100644 --- a/apps/readest-app/public/locales/ro/translation.json +++ b/apps/readest-app/public/locales/ro/translation.json @@ -1827,5 +1827,11 @@ "Background Image (Reader)": "Imagine de fundal (Vizualizare de lectură)", "updated metadata for {{n}} book(s)": "metadate actualizate pentru {{n}} carte/cărți", "{{n}} metadata": "{{n}} metadate", - "Metadata updated": "Metadate actualizate" + "Metadata updated": "Metadate actualizate", + "Filter": "Filtrează", + "Sort by": "Sortează după", + "Date modified": "Data modificării", + "Date created": "Data creării", + "Sort ascending": "Sortare crescătoare", + "Sort descending": "Sortare descrescătoare" } diff --git a/apps/readest-app/public/locales/ru/translation.json b/apps/readest-app/public/locales/ru/translation.json index 611041a1..1720d6e9 100644 --- a/apps/readest-app/public/locales/ru/translation.json +++ b/apps/readest-app/public/locales/ru/translation.json @@ -1860,5 +1860,11 @@ "Background Image (Reader)": "Фоновое изображение (Режим чтения)", "updated metadata for {{n}} book(s)": "обновлены метаданные для {{n}} книг", "{{n}} metadata": "{{n}} метаданные", - "Metadata updated": "Метаданные обновлены" + "Metadata updated": "Метаданные обновлены", + "Filter": "Фильтр", + "Sort by": "Сортировать по", + "Date modified": "Дата изменения", + "Date created": "Дата создания", + "Sort ascending": "По возрастанию", + "Sort descending": "По убыванию" } diff --git a/apps/readest-app/public/locales/si/translation.json b/apps/readest-app/public/locales/si/translation.json index 7e3a7bb3..9b69fe26 100644 --- a/apps/readest-app/public/locales/si/translation.json +++ b/apps/readest-app/public/locales/si/translation.json @@ -1794,5 +1794,11 @@ "Background Image (Reader)": "පසුබිම් රූපය (කියවීමේ දසුන)", "updated metadata for {{n}} book(s)": "පොත් {{n}}ක මෙටාඩේටා යාවත්කාලීන කරන ලදී", "{{n}} metadata": "{{n}} මෙටාඩේටා", - "Metadata updated": "මෙටාඩේටා යාවත්කාලීන කරන ලදී" + "Metadata updated": "මෙටාඩේටා යාවත්කාලීන කරන ලදී", + "Filter": "පෙරහන", + "Sort by": "අනුව සකසන්න", + "Date modified": "වෙනස් කළ දිනය", + "Date created": "සෑදූ දිනය", + "Sort ascending": "ආරෝහණව සකසන්න", + "Sort descending": "අවරෝහණව සකසන්න" } diff --git a/apps/readest-app/public/locales/sl/translation.json b/apps/readest-app/public/locales/sl/translation.json index fb449824..42ad8c0a 100644 --- a/apps/readest-app/public/locales/sl/translation.json +++ b/apps/readest-app/public/locales/sl/translation.json @@ -1860,5 +1860,11 @@ "Background Image (Reader)": "Slikovno ozadje (Bralni pogled)", "updated metadata for {{n}} book(s)": "posodobljeni metapodatki za {{n}} knjig", "{{n}} metadata": "{{n}} metapodatki", - "Metadata updated": "Metapodatki posodobljeni" + "Metadata updated": "Metapodatki posodobljeni", + "Filter": "Filtriraj", + "Sort by": "Razvrsti po", + "Date modified": "Datum spremembe", + "Date created": "Datum ustvarjanja", + "Sort ascending": "Naraščajoče razvrščanje", + "Sort descending": "Padajoče razvrščanje" } diff --git a/apps/readest-app/public/locales/sv/translation.json b/apps/readest-app/public/locales/sv/translation.json index 3c5ea89b..a1fdf948 100644 --- a/apps/readest-app/public/locales/sv/translation.json +++ b/apps/readest-app/public/locales/sv/translation.json @@ -1794,5 +1794,11 @@ "Background Image (Reader)": "Bakgrundsbild (Läsvy)", "updated metadata for {{n}} book(s)": "uppdaterade metadata för {{n}} bok/böcker", "{{n}} metadata": "{{n}} metadata", - "Metadata updated": "Metadata uppdaterad" + "Metadata updated": "Metadata uppdaterad", + "Filter": "Filtrera", + "Sort by": "Sortera efter", + "Date modified": "Ändringsdatum", + "Date created": "Skapandedatum", + "Sort ascending": "Stigande sortering", + "Sort descending": "Fallande sortering" } diff --git a/apps/readest-app/public/locales/ta/translation.json b/apps/readest-app/public/locales/ta/translation.json index b761dc0b..7d2bad1b 100644 --- a/apps/readest-app/public/locales/ta/translation.json +++ b/apps/readest-app/public/locales/ta/translation.json @@ -1794,5 +1794,11 @@ "Background Image (Reader)": "பின்னணி படம் (வாசிப்புப் பக்கம்)", "updated metadata for {{n}} book(s)": "{{n}} புத்தகத்தின் மெட்டாடேட்டா புதுப்பிக்கப்பட்டது", "{{n}} metadata": "{{n}} மெட்டாடேட்டா", - "Metadata updated": "மெட்டாடேட்டா புதுப்பிக்கப்பட்டது" + "Metadata updated": "மெட்டாடேட்டா புதுப்பிக்கப்பட்டது", + "Filter": "வடிகட்டு", + "Sort by": "வரிசைப்படுத்து", + "Date modified": "மாற்றிய தேதி", + "Date created": "உருவாக்கிய தேதி", + "Sort ascending": "ஏறுவரிசை", + "Sort descending": "இறங்குவரிசை" } diff --git a/apps/readest-app/public/locales/th/translation.json b/apps/readest-app/public/locales/th/translation.json index aa50894b..406d708a 100644 --- a/apps/readest-app/public/locales/th/translation.json +++ b/apps/readest-app/public/locales/th/translation.json @@ -1761,5 +1761,11 @@ "Background Image (Reader)": "ภาพพื้นหลัง (มุมมองการอ่าน)", "updated metadata for {{n}} book(s)": "อัปเดตเมทาดาตาสำหรับ {{n}} เล่ม", "{{n}} metadata": "{{n}} เมทาดาตา", - "Metadata updated": "อัปเดตเมทาดาตาแล้ว" + "Metadata updated": "อัปเดตเมทาดาตาแล้ว", + "Filter": "กรอง", + "Sort by": "เรียงตาม", + "Date modified": "วันที่แก้ไข", + "Date created": "วันที่สร้าง", + "Sort ascending": "เรียงจากน้อยไปมาก", + "Sort descending": "เรียงจากมากไปน้อย" } diff --git a/apps/readest-app/public/locales/tr/translation.json b/apps/readest-app/public/locales/tr/translation.json index de8b2566..2319e954 100644 --- a/apps/readest-app/public/locales/tr/translation.json +++ b/apps/readest-app/public/locales/tr/translation.json @@ -1794,5 +1794,11 @@ "Background Image (Reader)": "Arka Plan Görüntüsü (Okuma Görünümü)", "updated metadata for {{n}} book(s)": "{{n}} kitabın meta verileri güncellendi", "{{n}} metadata": "{{n}} meta veri", - "Metadata updated": "Meta veriler güncellendi" + "Metadata updated": "Meta veriler güncellendi", + "Filter": "Filtrele", + "Sort by": "Sıralama ölçütü", + "Date modified": "Değiştirilme tarihi", + "Date created": "Oluşturulma tarihi", + "Sort ascending": "Artan sıralama", + "Sort descending": "Azalan sıralama" } diff --git a/apps/readest-app/public/locales/uk/translation.json b/apps/readest-app/public/locales/uk/translation.json index 606ed5e5..9483214c 100644 --- a/apps/readest-app/public/locales/uk/translation.json +++ b/apps/readest-app/public/locales/uk/translation.json @@ -1860,5 +1860,11 @@ "Background Image (Reader)": "Фонове зображення (Режим читання)", "updated metadata for {{n}} book(s)": "оновлено метадані для {{n}} книг", "{{n}} metadata": "{{n}} метадані", - "Metadata updated": "Метадані оновлено" + "Metadata updated": "Метадані оновлено", + "Filter": "Фільтр", + "Sort by": "Сортувати за", + "Date modified": "Дата зміни", + "Date created": "Дата створення", + "Sort ascending": "За зростанням", + "Sort descending": "За спаданням" } diff --git a/apps/readest-app/public/locales/uz/translation.json b/apps/readest-app/public/locales/uz/translation.json index db9cde80..fb7b5d83 100644 --- a/apps/readest-app/public/locales/uz/translation.json +++ b/apps/readest-app/public/locales/uz/translation.json @@ -1794,5 +1794,11 @@ "Background Image (Reader)": "Fon rasmi (Oʻqish koʻrinishi)", "updated metadata for {{n}} book(s)": "{{n}} ta kitob metamaʼlumotlari yangilandi", "{{n}} metadata": "{{n}} metamaʼlumot", - "Metadata updated": "Metamaʼlumotlar yangilandi" + "Metadata updated": "Metamaʼlumotlar yangilandi", + "Filter": "Filtr", + "Sort by": "Saralash tartibi", + "Date modified": "Oʻzgartirilgan sana", + "Date created": "Yaratilgan sana", + "Sort ascending": "Oʻsish boʻyicha", + "Sort descending": "Kamayish boʻyicha" } diff --git a/apps/readest-app/public/locales/vi/translation.json b/apps/readest-app/public/locales/vi/translation.json index 81b6f9eb..7a092e34 100644 --- a/apps/readest-app/public/locales/vi/translation.json +++ b/apps/readest-app/public/locales/vi/translation.json @@ -1761,5 +1761,11 @@ "Background Image (Reader)": "Ảnh nền (Trình đọc)", "updated metadata for {{n}} book(s)": "đã cập nhật siêu dữ liệu cho {{n}} sách", "{{n}} metadata": "{{n}} siêu dữ liệu", - "Metadata updated": "Đã cập nhật siêu dữ liệu" + "Metadata updated": "Đã cập nhật siêu dữ liệu", + "Filter": "Lọc", + "Sort by": "Sắp xếp theo", + "Date modified": "Ngày sửa đổi", + "Date created": "Ngày tạo", + "Sort ascending": "Sắp xếp tăng dần", + "Sort descending": "Sắp xếp giảm dần" } diff --git a/apps/readest-app/public/locales/zh-CN/translation.json b/apps/readest-app/public/locales/zh-CN/translation.json index dc5f87cd..1bc45108 100644 --- a/apps/readest-app/public/locales/zh-CN/translation.json +++ b/apps/readest-app/public/locales/zh-CN/translation.json @@ -1761,5 +1761,11 @@ "Background Image (Reader)": "背景图片(阅读界面)", "updated metadata for {{n}} book(s)": "已更新 {{n}} 本书的元数据", "{{n}} metadata": "{{n}} 元数据", - "Metadata updated": "已更新元数据" + "Metadata updated": "已更新元数据", + "Filter": "筛选", + "Sort by": "排序方式", + "Date modified": "修改日期", + "Date created": "创建日期", + "Sort ascending": "升序排列", + "Sort descending": "降序排列" } diff --git a/apps/readest-app/public/locales/zh-TW/translation.json b/apps/readest-app/public/locales/zh-TW/translation.json index cf287825..7716636f 100644 --- a/apps/readest-app/public/locales/zh-TW/translation.json +++ b/apps/readest-app/public/locales/zh-TW/translation.json @@ -1761,5 +1761,11 @@ "Background Image (Reader)": "背景圖片(閱讀介面)", "updated metadata for {{n}} book(s)": "已更新 {{n}} 本書的中繼資料", "{{n}} metadata": "{{n}} 中繼資料", - "Metadata updated": "已更新中繼資料" + "Metadata updated": "已更新中繼資料", + "Filter": "篩選", + "Sort by": "排序方式", + "Date modified": "修改日期", + "Date created": "建立日期", + "Sort ascending": "升序排列", + "Sort descending": "降序排列" } diff --git a/apps/readest-app/src/__tests__/components/webdav-browse-sort-filter.test.ts b/apps/readest-app/src/__tests__/components/webdav-browse-sort-filter.test.ts new file mode 100644 index 00000000..f70973f7 --- /dev/null +++ b/apps/readest-app/src/__tests__/components/webdav-browse-sort-filter.test.ts @@ -0,0 +1,149 @@ +import { describe, expect, test } from 'vitest'; +import { + filterWebDAVEntries, + sortWebDAVEntries, +} from '@/components/settings/integrations/webdavBrowseUtils'; +import type { WebDAVEntry } from '@/services/sync/providers/webdav/client'; + +/** + * Pure sort/filter helpers backing the WebDAV browser's sort + search + * controls. They operate on already-fetched `WebDAVEntry[]` (the pane + * lists a whole directory in one PROPFIND, so there's no pagination to + * worry about) which keeps them trivially unit-testable here. + */ + +const dir = (name: string, extra: Partial = {}): WebDAVEntry => ({ + name, + path: `/${name}`, + isDirectory: true, + ...extra, +}); + +const file = (name: string, extra: Partial = {}): WebDAVEntry => ({ + name, + path: `/${name}`, + isDirectory: false, + ...extra, +}); + +const names = (entries: WebDAVEntry[]): string[] => entries.map((e) => e.name); + +describe('sortWebDAVEntries', () => { + test('keeps directories grouped before files regardless of field/direction', () => { + const entries = [file('b.epub', { size: 10 }), dir('z-folder'), file('a.epub', { size: 20 })]; + const sorted = sortWebDAVEntries(entries, 'size', false); + // Directories first even though the field is size descending and the + // dir has no size; files follow, ordered by the field. + expect(sorted[0]!.isDirectory).toBe(true); + expect(names(sorted.filter((e) => !e.isDirectory))).toEqual(['a.epub', 'b.epub']); + }); + + test('sorts by name ascending and descending', () => { + const entries = [file('Charlie.epub'), file('alpha.epub'), file('Bravo.epub')]; + expect(names(sortWebDAVEntries(entries, 'name', true))).toEqual([ + 'alpha.epub', + 'Bravo.epub', + 'Charlie.epub', + ]); + expect(names(sortWebDAVEntries(entries, 'name', false))).toEqual([ + 'Charlie.epub', + 'Bravo.epub', + 'alpha.epub', + ]); + }); + + test('sorts by last modified date, newest-first when descending', () => { + const entries = [ + file('old.epub', { lastModified: 'Mon, 01 Jan 2024 00:00:00 GMT' }), + file('new.epub', { lastModified: 'Wed, 01 Jan 2025 00:00:00 GMT' }), + file('mid.epub', { lastModified: 'Sat, 01 Jun 2024 00:00:00 GMT' }), + ]; + expect(names(sortWebDAVEntries(entries, 'modified', false))).toEqual([ + 'new.epub', + 'mid.epub', + 'old.epub', + ]); + expect(names(sortWebDAVEntries(entries, 'modified', true))).toEqual([ + 'old.epub', + 'mid.epub', + 'new.epub', + ]); + }); + + test('sorts by creation date', () => { + const entries = [ + file('second.epub', { created: '2024-05-01T00:00:00Z' }), + file('first.epub', { created: '2024-01-01T00:00:00Z' }), + ]; + expect(names(sortWebDAVEntries(entries, 'created', true))).toEqual([ + 'first.epub', + 'second.epub', + ]); + }); + + test('sorts by size', () => { + const entries = [file('big.epub', { size: 5000 }), file('small.epub', { size: 10 })]; + expect(names(sortWebDAVEntries(entries, 'size', true))).toEqual(['small.epub', 'big.epub']); + expect(names(sortWebDAVEntries(entries, 'size', false))).toEqual(['big.epub', 'small.epub']); + }); + + test('entries missing the sort field sort last in both directions, tie-broken by name', () => { + const entries = [ + file('has-date.epub', { lastModified: 'Wed, 01 Jan 2025 00:00:00 GMT' }), + file('no-date-b.epub'), + file('no-date-a.epub'), + ]; + // Ascending: dated entry first, then the undated pair alphabetically. + expect(names(sortWebDAVEntries(entries, 'modified', true))).toEqual([ + 'has-date.epub', + 'no-date-a.epub', + 'no-date-b.epub', + ]); + // Descending: dated entry still first; undated remain last (not flipped + // to the top) and stay name-ordered. + expect(names(sortWebDAVEntries(entries, 'modified', false))).toEqual([ + 'has-date.epub', + 'no-date-a.epub', + 'no-date-b.epub', + ]); + }); + + test('name sort follows the resolved display name when provided', () => { + // Under Readest/books the entry name is a content hash; the resolver + // maps it to the human title so "sort by name" matches what's shown. + const entries = [dir('hashZ'), dir('hashA')]; + const titles: Record = { hashZ: 'Alpha', hashA: 'Zulu' }; + const getName = (e: WebDAVEntry) => titles[e.name] ?? e.name; + expect(names(sortWebDAVEntries(entries, 'name', true, getName))).toEqual(['hashZ', 'hashA']); + }); + + test('does not mutate the input array', () => { + const entries = [file('b.epub'), file('a.epub')]; + const snapshot = names(entries); + sortWebDAVEntries(entries, 'name', true); + expect(names(entries)).toEqual(snapshot); + }); +}); + +describe('filterWebDAVEntries', () => { + const entries = [file('The Great Gatsby.epub'), file('mobydick.epub'), dir('hash123')]; + + test('returns every entry for an empty/whitespace query', () => { + expect(filterWebDAVEntries(entries, '')).toHaveLength(3); + expect(filterWebDAVEntries(entries, ' ')).toHaveLength(3); + }); + + test('matches file name case-insensitively as a substring', () => { + expect(names(filterWebDAVEntries(entries, 'GREAT'))).toEqual(['The Great Gatsby.epub']); + expect(names(filterWebDAVEntries(entries, 'dick'))).toEqual(['mobydick.epub']); + }); + + test('matches the resolved display title for hashed book directories', () => { + const getName = (e: WebDAVEntry) => (e.name === 'hash123' ? 'Crime and Punishment' : e.name); + expect(names(filterWebDAVEntries(entries, 'punishment', getName))).toEqual(['hash123']); + }); + + test('returns an empty array when nothing matches', () => { + expect(filterWebDAVEntries(entries, 'zzz-nope')).toEqual([]); + }); +}); diff --git a/apps/readest-app/src/__tests__/services/webdav-list-directory.test.ts b/apps/readest-app/src/__tests__/services/webdav-list-directory.test.ts new file mode 100644 index 00000000..a87693dc --- /dev/null +++ b/apps/readest-app/src/__tests__/services/webdav-list-directory.test.ts @@ -0,0 +1,101 @@ +import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; +import { listDirectory, type WebDAVConfig } from '@/services/sync/providers/webdav/client'; + +/** + * Tests for the PROPFIND listing parser, focused on the metadata fields + * that drive the browser's sort/search. `getcontentlength` / + * `getlastmodified` were already parsed; `creationdate` was added so the + * browser can offer "sort by date created". Servers that omit the + * property must still produce a valid entry with `created === undefined`. + */ + +const ORIGINAL_FETCH = globalThis.fetch; + +const config: WebDAVConfig = { + serverUrl: 'https://dav.example.com', + username: 'alice', + password: 'secret', +}; + +const multistatus = (inner: string): string => + ` + + + /books/ + + + HTTP/1.1 200 OK + + + ${inner} +`; + +let fetchMock: ReturnType; + +beforeEach(() => { + fetchMock = vi.fn(); + globalThis.fetch = fetchMock as unknown as typeof fetch; +}); + +afterEach(() => { + globalThis.fetch = ORIGINAL_FETCH; + vi.restoreAllMocks(); +}); + +describe('listDirectory metadata parsing', () => { + test('requests creationdate in the PROPFIND body', async () => { + fetchMock.mockResolvedValueOnce(new Response(multistatus(''), { status: 207 })); + await listDirectory(config, '/books'); + const [, init] = fetchMock.mock.calls[0]!; + expect(init?.method).toBe('PROPFIND'); + expect(String(init?.body)).toContain('creationdate'); + }); + + test('parses creationdate, getlastmodified and getcontentlength for a file', async () => { + const fileResponse = ` + + /books/novel.epub + + + + 1234 + Mon, 15 Jan 2024 10:30:00 GMT + 2023-12-01T08:00:00Z + + HTTP/1.1 200 OK + + `; + fetchMock.mockResolvedValueOnce(new Response(multistatus(fileResponse), { status: 207 })); + + const entries = await listDirectory(config, '/books'); + expect(entries).toHaveLength(1); + const entry = entries[0]!; + expect(entry.name).toBe('novel.epub'); + expect(entry.isDirectory).toBe(false); + expect(entry.size).toBe(1234); + expect(entry.lastModified).toBe('Mon, 15 Jan 2024 10:30:00 GMT'); + expect(entry.created).toBe('2023-12-01T08:00:00Z'); + }); + + test('leaves created undefined when the server omits creationdate', async () => { + const dirResponse = ` + + /books/sub/ + + + + Mon, 15 Jan 2024 10:30:00 GMT + + HTTP/1.1 200 OK + + `; + fetchMock.mockResolvedValueOnce(new Response(multistatus(dirResponse), { status: 207 })); + + const entries = await listDirectory(config, '/books'); + expect(entries).toHaveLength(1); + const entry = entries[0]!; + expect(entry.name).toBe('sub'); + expect(entry.isDirectory).toBe(true); + expect(entry.created).toBeUndefined(); + }); +}); diff --git a/apps/readest-app/src/components/settings/integrations/WebDAVBrowsePane.tsx b/apps/readest-app/src/components/settings/integrations/WebDAVBrowsePane.tsx index 76d8ba02..c2069ffd 100644 --- a/apps/readest-app/src/components/settings/integrations/WebDAVBrowsePane.tsx +++ b/apps/readest-app/src/components/settings/integrations/WebDAVBrowsePane.tsx @@ -9,6 +9,9 @@ import { MdCheck, MdDeleteSweep, MdClose, + MdSearch, + MdArrowUpward, + MdArrowDownward, } from 'react-icons/md'; import { useEnv } from '@/context/EnvContext'; import { useAuth } from '@/context/AuthContext'; @@ -30,15 +33,17 @@ import { buildBasePath, SYNC_BOOKS_DIR } from '@/services/sync/file/layout'; import { createWebDAVProvider } from '@/services/sync/providers/webdav/WebDAVProvider'; import { deleteRemoteBookDir } from '@/services/sync/file/engine'; import { FileSyncError } from '@/services/sync/file/provider'; -import { WebDAVSettings } from '@/types/settings'; +import { WebDAVBrowseSortByType, WebDAVSettings } from '@/types/settings'; import { Book } from '@/types/book'; import { SettingLabel } from '../primitives'; import { + filterWebDAVEntries, formatLastModified, formatShortHash, formatSize, getEntryIcon, isSupportedBookExt, + sortWebDAVEntries, } from './webdavBrowseUtils'; /** @@ -50,9 +55,15 @@ import { */ export interface WebDAVBrowsePaneProps { settings: WebDAVSettings; + /** + * Persist a partial WebDAV settings patch. Used to remember the sort + * field + direction across sessions. Optional so the pane still renders + * (sort/search just stay session-local) when a caller omits it. + */ + onUpdateSettings?: (patch: Partial) => void | Promise; } -const WebDAVBrowsePane: React.FC = ({ settings }) => { +const WebDAVBrowsePane: React.FC = ({ settings, onUpdateSettings }) => { const _ = useTranslation(); const { envConfig } = useEnv(); const { user } = useAuth(); @@ -95,6 +106,15 @@ const WebDAVBrowsePane: React.FC = ({ settings }) => { Record >({}); + // —— Sort & filter —— + // Free-text filter over the current folder (transient: reset on every + // navigation/refresh below). Sort field + direction seed from the + // persisted settings — default name/ascending reproduces the legacy + // directories-first alphabetical order — and write back on change. + const [query, setQuery] = useState(''); + const [sortBy, setSortBy] = useState(settings.browseSortBy ?? 'name'); + const [ascending, setAscending] = useState(settings.browseSortAscending ?? true); + // —— Cleanup mode —— // GC surface for remote orphans (per-hash dirs whose local Book // has `deletedAt` set). When on, the listing is pinned to @@ -148,13 +168,24 @@ const WebDAVBrowsePane: React.FC = ({ settings }) => { return !!bookByHash.get(entry.name)?.deletedAt; }; - // Cleanup mode shows only the orphan rows; the toolbar uses the - // count too, so materialise the filter once here. - const displayedEntries = useMemo( - () => (cleanupMode ? entries.filter(isEntryLocallyDeleted) : entries), + // The listing the UI renders: cleanup mode pins to orphan rows (search + // is hidden there); normal mode applies the free-text filter. Both then + // run through the chosen sort. Sort/search resolve a hashed book dir to + // its library title so they operate on what the user actually sees. + const displayedEntries = useMemo(() => { + const resolveDisplayName = (entry: WebDAVEntry): string => { + if (entry.isDirectory && currentPath === booksDirPath) { + const matched = bookByHash.get(entry.name); + if (matched) return matched.title || entry.name; + } + return entry.name; + }; + const base = cleanupMode + ? entries.filter(isEntryLocallyDeleted) + : filterWebDAVEntries(entries, query, resolveDisplayName); + return sortWebDAVEntries(base, sortBy, ascending, resolveDisplayName); // eslint-disable-next-line react-hooks/exhaustive-deps - [entries, cleanupMode, bookByHash, currentPath, booksDirPath], - ); + }, [entries, cleanupMode, bookByHash, currentPath, booksDirPath, query, sortBy, ascending]); /** * Sequentially DELETE the selected per-hash directories from the @@ -301,8 +332,11 @@ const WebDAVBrowsePane: React.FC = ({ settings }) => { if (!currentPath) return; let cancelled = false; // Reset per-entry download status on every (re)load so stale - // "done" badges don't carry across folders. + // "done" badges don't carry across folders. The filter is + // per-folder too — clear it so a query typed in one directory + // doesn't silently hide rows in the next. setDownloadStatus({}); + setQuery(''); const load = async () => { setIsLoading(true); setLoadError(null); @@ -353,6 +387,18 @@ const WebDAVBrowsePane: React.FC = ({ settings }) => { setReloadTick((n) => n + 1); }; + const handleSortByChange = (e: React.ChangeEvent) => { + const next = e.target.value as WebDAVBrowseSortByType; + setSortBy(next); + void onUpdateSettings?.({ browseSortBy: next }); + }; + + const handleToggleDirection = () => { + const next = !ascending; + setAscending(next); + void onUpdateSettings?.({ browseSortAscending: next }); + }; + /** * Download a single remote file and ingest it into the library. * Streams via tauriDownload to avoid the WebView IPC limit on @@ -495,6 +541,67 @@ const WebDAVBrowsePane: React.FC = ({ settings }) => { + {/* Sort + filter controls. Normal browse only — cleanup keeps its + own focused toolbar. Gated on a non-empty listing so empty / + unloaded folders stay uncluttered; `entries` is the pre-filter + length, so the row stays put (and the box clearable) even when a + query matches nothing. */} + {!cleanupMode && !loadError && entries.length > 0 && ( +
+
+ + setQuery(e.target.value)} + // Keep keystrokes (Esc/Backspace) from bubbling to the + // Settings dialog's key handlers while typing a filter. + onKeyDown={(e) => e.stopPropagation()} + placeholder={_('Filter')} + aria-label={_('Filter')} + className='min-w-0 flex-1 bg-transparent px-2 text-sm focus:outline-none' + /> + {query && ( + + )} +
+ + +
+ )} +
{isLoading ? (
@@ -534,6 +641,11 @@ const WebDAVBrowsePane: React.FC = ({ settings }) => { const rowTitle = isLocallyDeleted ? _('Deleted locally · still on server') : undefined; + // Surface whichever date the active sort orders by, so a + // "Date created" sort is legible at a glance; every other + // sort keeps showing the modified time. Undefined (server + // didn't report it) simply omits the date from the row. + const activeDateRaw = sortBy === 'created' ? entry.created : entry.lastModified; return (
  • = ({ settings }) => { so directories don't render an empty span. */} {(matchedBook || (!entry.isDirectory && typeof entry.size === 'number') || - entry.lastModified) && ( + activeDateRaw) && ( {matchedBook && ( @@ -633,10 +745,8 @@ const WebDAVBrowsePane: React.FC = ({ settings }) => { {!entry.isDirectory && typeof entry.size === 'number' && ( {formatSize(entry.size)} )} - {entry.lastModified && ( - - {formatLastModified(entry.lastModified)} - + {activeDateRaw && ( + {formatLastModified(activeDateRaw)} )} )} diff --git a/apps/readest-app/src/components/settings/integrations/WebDAVForm.tsx b/apps/readest-app/src/components/settings/integrations/WebDAVForm.tsx index ac29b8cf..46466c58 100644 --- a/apps/readest-app/src/components/settings/integrations/WebDAVForm.tsx +++ b/apps/readest-app/src/components/settings/integrations/WebDAVForm.tsx @@ -392,7 +392,7 @@ const WebDAVForm: React.FC = ({ onBack }) => { - +