diff --git a/app/src/main/res/values-ar/plurals.xml b/app/src/main/res/values-ar/plurals.xml
index a73d4c4..4732792 100644
--- a/app/src/main/res/values-ar/plurals.xml
+++ b/app/src/main/res/values-ar/plurals.xml
@@ -2,23 +2,23 @@
لا كتب
- كتاب واحد
+ %1$d كتابكتابان%1$d كتب%1$d كتابًا%1$d كتاب
- لا كتب
- كتاب واحد
+ كتب
+ كتابكتابان
- %1$d كتب
- %1$d كتابًا
- %1$d كتاب
+ كتب
+ كتاب
+ كتبلا توجد رفوف
- رف واحد
+ %1$d رفرفان%1$d رفوف%1$d رفاً
@@ -26,23 +26,23 @@
لم يتم العثور على نتائج
- تم العثور على نتيجة واحدة
+ تم العثور على %1$d نتيجةتم العثور على نتيجتينتم العثور على %1$d نتائجتم العثور على %1$d نتيجةتم العثور على %1$d نتيجة
- لا توجد ملفات لحذفها نهائياً
- حذف الملف نهائياً
- حذف الملفين نهائياً
- حذف الـ %1$d ملفات نهائياً
- حذف الـ %1$d ملفاً نهائياً
- حذف الـ %1$d ملف نهائياً
+ حذف الملفات نهائيًا
+ حذف الملف نهائيًا
+ حذف الملفين نهائيًا
+ حذف الملفات نهائيًا
+ حذف الملفات نهائيًا
+ حذف الملفات نهائيًالا توجد ملفات مختارة.
- هل تريد حذف الملف المختار نهائياً من جهازك؟ لا يمكن التراجع عن هذا الإجراء.
+ هل تريد حذف %1$d ملف محدد نهائيًا من جهازك؟ لا يمكن التراجع عن هذا الإجراء.هل تريد حذف الملفين المختارين نهائياً من جهازك؟ لا يمكن التراجع عن هذا الإجراء.هل تريد حذف الـ %1$d ملفات المختارة نهائياً من جهازك؟ لا يمكن التراجع عن هذا الإجراء.هل تريد حذف الـ %1$d ملفاً مختاراً نهائياً من جهازك؟ لا يمكن التراجع عن هذا الإجراء.
@@ -50,7 +50,7 @@
لا توجد ملفات مختارة لإزالتها من القائمة.
- هل تريد إزالة الملف المختار من قائمة الملفات الأخيرة؟ سيظهر مجدداً إذا فتحته من المكتبة.
+ هل تريد إزالة %1$d ملف محدد من قائمة الملفات الأخيرة؟ سيظهر مرة أخرى إذا فتحته من المكتبة.هل تريد إزالة الملفين المختارين من قائمة الملفات الأخيرة؟ سيظهران مجدداً إذا فتحتهما من المكتبة.هل تريد إزالة الـ %1$d ملفات المختارة من قائمة الملفات الأخيرة؟ ستظهر مجدداً إذا فتحتها من المكتبة.هل تريد إزالة الـ %1$d ملفاً مختاراً من قائمة الملفات الأخيرة؟ ستظهر مجدداً إذا فتحتها من المكتبة.
@@ -58,7 +58,7 @@
لا توجد كتب مختارة لإزالتها من الرف.
- هل أنت متأكد من أنك تريد إزالة كتاب واحد من رف \'%2$s\'؟ سيبقى الكتاب في مكتبتك وسيظهر ضمن القسم \"غير مصنف\".
+ هل أنت متأكد من أنك تريد إزالة %1$d كتاب من رف \'%2$s\'؟ سيبقى الكتاب في مكتبتك وسيظهر ضمن القسم "غير مصنف".هل أنت متأكد من أنك تريد إزالة كتابين من رف \'%2$s\'؟ سيبقى الكتابان في مكتبتك وسيظهران ضمن القسم \"غير مصنف\".هل أنت متأكد من أنك تريد إزالة %1$d كتب من رف \'%2$s\'؟ ستبقى الكتب في مكتبتك وستظهر ضمن القسم \"غير مصنف\".هل أنت متأكد من أنك تريد إزالة %1$d كتاباً من رف \'%2$s\'؟ ستبقى الكتب في مكتبتك وستظهر ضمن القسم \"غير مصنف\".
@@ -66,10 +66,210 @@
لم يتم إزالة أي كتب من المكتبة.
- تم إزالة كتاب واحد من المكتبة.
+ تمت إزالة %1$d كتاب من المكتبة.تم إزالة كتابين من المكتبة.تم إزالة %1$d كتب من المكتبة.تم إزالة %1$d كتاباً من المكتبة.تم إزالة %1$d كتاب من المكتبة.
+
+ %1$d تم العثور على التطابقات
+ %1$d تم العثور على تطابق
+ %1$d تم العثور على التطابقات
+ %1$d تم العثور على التطابقات
+ %1$d تم العثور على التطابقات
+ %1$d تم العثور على التطابقات
+
+
+ استيراد %1$d الكتب... ستظهر في مكتبتك قريبًا.
+ استيراد %1$d كتاب... سيظهر في مكتبتك قريبا.
+ استيراد %1$d الكتب... ستظهر في مكتبتك قريبًا.
+ استيراد %1$d الكتب... ستظهر في مكتبتك قريبًا.
+ استيراد %1$d الكتب... ستظهر في مكتبتك قريبًا.
+ استيراد %1$d الكتب... ستظهر في مكتبتك قريبًا.
+
+
+ مستورد %1$d كتب. يمكنك العثور عليها في علامة التبويب "المكتبة".
+ مستورد %1$d كتاب. يمكنك العثور عليه في علامة التبويب "المكتبة".
+ مستورد %1$d كتب. يمكنك العثور عليها في علامة التبويب "المكتبة".
+ مستورد %1$d كتب. يمكنك العثور عليها في علامة التبويب "المكتبة".
+ مستورد %1$d كتب. يمكنك العثور عليها في علامة التبويب "المكتبة".
+ مستورد %1$d كتب. يمكنك العثور عليها في علامة التبويب "المكتبة".
+
+
+ %1$d الكتب المضافة إلى الرف.
+ %1$d تمت إضافة الكتاب إلى الرف.
+ %1$d الكتب المضافة إلى الرف.
+ %1$d الكتب المضافة إلى الرف.
+ %1$d الكتب المضافة إلى الرف.
+ %1$d الكتب المضافة إلى الرف.
+
+
+ %1$d الكتب الموسومة ب "%2$s".
+ %1$d كتاب ذو علامة "%2$s".
+ %1$d الكتب الموسومة ب "%2$s".
+ %1$d الكتب الموسومة ب "%2$s".
+ %1$d الكتب الموسومة ب "%2$s".
+ %1$d الكتب الموسومة ب "%2$s".
+
+
+ تمت إزالة المجلد "%1$s" و %2$d الكتب من التطبيق.
+ تمت إزالة المجلد "%1$s" و %2$d كتاب من التطبيق.
+ تمت إزالة المجلد "%1$s" و %2$d الكتب من التطبيق.
+ تمت إزالة المجلد "%1$s" و %2$d الكتب من التطبيق.
+ تمت إزالة المجلد "%1$s" و %2$d الكتب من التطبيق.
+ تمت إزالة المجلد "%1$s" و %2$d الكتب من التطبيق.
+
+
+ %1$d المجلدات
+ %1$d المجلد
+ %1$d المجلدات
+ %1$d المجلدات
+ %1$d المجلدات
+ %1$d المجلدات
+
+
+ %1$d ملفات
+ %1$d ملف
+ %1$d ملفات
+ %1$d ملفات
+ %1$d ملفات
+ %1$d ملفات
+
+
+ أفلت للاستيراد %1$d ملفات
+ أفلت للاستيراد %1$d file
+ أفلت للاستيراد %1$d ملفات
+ أفلت للاستيراد %1$d ملفات
+ أفلت للاستيراد %1$d ملفات
+ أفلت للاستيراد %1$d ملفات
+
+
+ %1$d سيتم تخطي الملفات غير المدعومة.
+ %1$d سيتم تخطي الملف غير المدعوم.
+ %1$d سيتم تخطي الملفات غير المدعومة.
+ %1$d سيتم تخطي الملفات غير المدعومة.
+ %1$d سيتم تخطي الملفات غير المدعومة.
+ %1$d سيتم تخطي الملفات غير المدعومة.
+
+
+ استيراد %1$d ملفات…
+ استيراد %1$d ملف…
+ استيراد %1$d ملفات…
+ استيراد %1$d ملفات…
+ استيراد %1$d ملفات…
+ استيراد %1$d ملفات…
+
+
+ مستورد %1$d ملفات.
+ مستورد %1$d ملف.
+ مستورد %1$d ملفات.
+ مستورد %1$d ملفات.
+ مستورد %1$d ملفات.
+ مستورد %1$d ملفات.
+
+
+ مستورد %1$d ملفات. يأتي دعم القارئ لاحقًا.
+ مستورد %1$d ملف. يأتي دعم القارئ لاحقًا.
+ مستورد %1$d ملفات. يأتي دعم القارئ لاحقًا.
+ مستورد %1$d ملفات. يأتي دعم القارئ لاحقًا.
+ مستورد %1$d ملفات. يأتي دعم القارئ لاحقًا.
+ مستورد %1$d ملفات. يأتي دعم القارئ لاحقًا.
+
+
+ تعذر استيراد %1$d ملفات.
+ تعذر استيراد %1$d ملف.
+ تعذر استيراد %1$d ملفات.
+ تعذر استيراد %1$d ملفات.
+ تعذر استيراد %1$d ملفات.
+ تعذر استيراد %1$d ملفات.
+
+
+ تم تخطي %1$d ملفات.
+ تم تخطي %1$d ملف.
+ تم تخطي %1$d ملفات.
+ تم تخطي %1$d ملفات.
+ تم تخطي %1$d ملفات.
+ تم تخطي %1$d ملفات.
+
+
+ إزالة "%1$s" و %2$d الكتب من التطبيق؟ لن يتم حذف الملفات الموجودة على القرص.
+ إزالة "%1$s" و %2$d كتاب من التطبيق؟ لن يتم حذف الملفات الموجودة على القرص.
+ إزالة "%1$s" و %2$d الكتب من التطبيق؟ لن يتم حذف الملفات الموجودة على القرص.
+ إزالة "%1$s" و %2$d الكتب من التطبيق؟ لن يتم حذف الملفات الموجودة على القرص.
+ إزالة "%1$s" و %2$d الكتب من التطبيق؟ لن يتم حذف الملفات الموجودة على القرص.
+ إزالة "%1$s" و %2$d الكتب من التطبيق؟ لن يتم حذف الملفات الموجودة على القرص.
+
+
+ فشلت مزامنة المجلد لـ %1$d المجلدات.
+ فشلت مزامنة المجلد لـ %1$d المجلد.
+ فشلت مزامنة المجلد لـ %1$d المجلدات.
+ فشلت مزامنة المجلد لـ %1$d المجلدات.
+ فشلت مزامنة المجلد لـ %1$d المجلدات.
+ فشلت مزامنة المجلد لـ %1$d المجلدات.
+
+
+ انتهت مزامنة المجلد مع %1$d تم تخطي المجلدات.
+ انتهت مزامنة المجلد مع %1$d تم تخطي المجلد.
+ انتهت مزامنة المجلد مع %1$d تم تخطي المجلدات.
+ انتهت مزامنة المجلد مع %1$d تم تخطي المجلدات.
+ انتهت مزامنة المجلد مع %1$d تم تخطي المجلدات.
+ انتهت مزامنة المجلد مع %1$d تم تخطي المجلدات.
+
+
+ تمت الإزالة %1$d متدفقة OPDS الكتب من هذا الكتالوج.
+ تمت الإزالة %1$d متدفقة OPDS كتاب من هذا الكتالوج.
+ تمت الإزالة %1$d متدفقة OPDS الكتب من هذا الكتالوج.
+ تمت الإزالة %1$d متدفقة OPDS الكتب من هذا الكتالوج.
+ تمت الإزالة %1$d متدفقة OPDS الكتب من هذا الكتالوج.
+ تمت الإزالة %1$d متدفقة OPDS الكتب من هذا الكتالوج.
+
+
+ %1$d العلامات
+ %1$d علامة
+ %1$d العلامات
+ %1$d العلامات
+ %1$d العلامات
+ %1$d العلامات
+
+
+ جميع الكتب %1$d
+ جميع الكتب %1$d
+ جميع الكتب %1$d
+ جميع الكتب %1$d
+ جميع الكتب %1$d
+ جميع الكتب %1$d
+
+
+ رفوف %1$d
+ رفوف %1$d
+ رفوف %1$d
+ رفوف %1$d
+ رفوف %1$d
+ رفوف %1$d
+
+
+ العلامات %1$d
+ العلامات %1$d
+ العلامات %1$d
+ العلامات %1$d
+ العلامات %1$d
+ العلامات %1$d
+
+
+ المجلدات %1$d
+ المجلدات %1$d
+ المجلدات %1$d
+ المجلدات %1$d
+ المجلدات %1$d
+ المجلدات %1$d
+
+
+ (%1$d قطع)
+ (%1$d قطعة)
+ (%1$d قطع)
+ (%1$d قطع)
+ (%1$d قطع)
+ (%1$d قطع)
+
diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml
index 03b05ae..f393f2f 100644
--- a/app/src/main/res/values-ar/strings.xml
+++ b/app/src/main/res/values-ar/strings.xml
@@ -657,4 +657,859 @@
Nederlands (الهولندية)Українська (الأوكرانية)Bahasa Indonesia (الإندونيسية)
+ تمكين
+ خطأ: %1$s
+ العودة
+ إظهار علامات التبويب في شريط التطبيقات العلوي
+ دعم المشروع
+ تعطيل المزامنة المحلية
+ تمكين المزامنة المحلية
+ تم تعطيل المزامنة المحلية
+ هل تريد تعطيل مزامنة المجلد المحلي؟
+ Episteme سيتوقف عن فحص هذا المجلد ويتوقف عن الكتابة JSON مزامنة الملفات. قم بإزالة %1$s مجلد من هذا المجلد أيضا؟
+ احتفظ ببيانات المزامنة
+ إزالة بيانات المزامنة
+ Google الخطوط
+ تصفح Google الخطوط
+ ابحث عن أكثر من 1900 خط...
+ الاختيارات الشعبية
+ لم يتم العثور على خطوط مطابقة \'%1$s\'
+ تم تنزيلها بالفعل
+ هل تريد حذف الخطوط؟
+ هل أنت متأكد أنك تريد حذف %1$d الخطوط المختارة؟ سيؤدي هذا إلى إزالتها من جميع أجهزتك إذا كانت المزامنة قيد التشغيل.
+ دعم المشروع
+ ساعد في الحفاظ على Episteme تتحرك
+ دعمكم يساعدني في الحفاظ على Episteme للجميع!!!
+ الراعي على جيثب
+ دعم التطوير مباشرة من خلال رعاة GitHub. كتعبير عن شكرك، ستحصل على صيحة README في مستودع المشروع.
+ انضم على باتريون
+ كشكر لك على دعم التطبيق، يحصل مؤيدو Patreon على محتوى وفوائد إضافية: نظرة خاطفة على ما أعمل عليه، ولقطات شاشة وتحديثات مبكرة، وأصوات تساعد في تشكيل الشكل الذي يجب أن تبدو عليه الميزات الجديدة وتعمل، وصيحة README في مستودع المشروع.
+ لم يتم تمكين مزامنة أي مجلدات محلية.
+ تم تعطيل مزامنة المجلد المحلي.
+ تم تعطيل مزامنة المجلد المحلي. تمت إزالة مجلد بيانات المزامنة.
+ تم تعطيل مزامنة المجلد المحلي، ولكن لا يمكن إزالة مجلد بيانات المزامنة.
+ تم تمكين مزامنة المجلد المحلي.
+ نوع الملف هذا غير مدعوم.
+ جارٍ إنشاء الملخص...
+ نسخ الموضوع
+ لا يمكن إنشاء أي ملخص.
+ التفكير…
+ AI لا يمكن تقديم تعريف.
+ السؤال AI حول \'%1$s\'…
+ السحابة (المقر الرئيسي)
+ الإعدادات المسبقة محكم
+ بلدي المواضيع محكم
+ لا توجد سمات مزخرفة مخصصة حتى الآن.
+ موضوع محكم جديد
+ الملمس
+ لا شيء
+ تحميل
+ شفافية الملمس
+ النص فارغ.
+ AI عاد تعريف فارغ.
+ لا يمكن الحصول على التعريف.
+ حدث خطأ غير معروف في الخادم.
+ خطأ في الشبكة. تحقق من الاتصال.
+ لا يوجد سياق كافٍ للتلخيص.
+ فشل تحليل الملخّص.
+ خطأ في الشبكة أثناء إنشاء الملخّص.
+ محتوى الكتاب فارغ.
+ فشل تحليل الملخص من استجابة الخادم.
+ تعذر جلب الملخص.
+ خطأ: %1$d. %2$s
+ خطأ في الشبكة. يرجى التحقق من الاتصال وحالة الخادم.
+ تحليل الفصل %1$d...
+ قراءة الوضع الحالي...
+ جارٍ إنشاء الملخّص...
+ ملخص الفصل
+ ملخص القصة (تجريبي)
+ فتح تلخيص الفصل
+ احصل على ملخصات موجزة لأي فصل باستخدام Episteme Pro. قم بالترقية لبدء استخدام هذه الميزة.
+ فتح القاموس الذكي
+ يعد تحديد العبارات والفقرات بأكملها حتى 2000 حرفًا ميزة احترافية. قم بالترقية للحصول على تعريفات فورية لأي نص محدد.
+ عمودي (عرض ويب)
+ عمودي (إصدار تجريبي أصلي)
+ اتجاه الشاشة
+ تغيير وضع القراءة
+ مرقّم (من اليمين إلى اليسار)
+ TTS الإعدادات
+ TTS استبدال الكلمات
+ كتاب استبدال الكلمات
+ شارك أو احفظ أو اطبع
+ خلاصة (تجريبية)
+ السابق TTS قطعة
+ التالي TTS قطعة
+ إنقاص
+ زيادة
+ علامات التبويب
+ الصفحات
+ الصور
+ قم بتوسيع الكل
+ طي الكل
+ تحديد موقع
+ لم يتم العثور على صور.
+ تحميل الصورة
+ عنوان جديد
+ تم الحفظ %1$s
+ لا يمكن حفظ الصورة.
+ تخطيط الصفحة
+ PDF انتشار الصفحة
+ صفحة واحدة
+ صفحتين
+ الصفحة الأولى وحدها
+ يبدأ الحيزات المواجهة للصفحة بعد صفحة الغلاف.
+ إزالة الفجوة بين الصفحات
+ ينطبق على القراءة الرأسية والفروق المكونة من صفحتين.
+ إخفاء تراكب رقم الصفحة
+ إزالة تسمية عدد الصفحات الصغيرة من كل صفحة.
+ اتجاه الشاشة
+ اختر ما إذا كان القارئ يتبع اتجاه النظام أو يفضل الوضع الرأسي أو الأفقي عند Android يسمح بذلك.
+ الموقف
+ السطوع
+ استخدام سطوع النظام
+ يتبع إعداد سطوع الجهاز.
+ سطوع مخصص
+ ينطبق عندما تكون شاشة القارئ مفتوحة.
+ %1$d%%
+ تم إنشاء الرف "%1$s".
+ تم إنشاء رف ذكي "%1$s".
+ تمت إعادة تسمية الرف إلى "%1$s".
+ الرف المحذوف "%1$s".
+ تم التحديث "%1$s".
+ هذه الملفات موجودة بالفعل في المكتبة.
+ %1$s - %2$s
+ حفظ
+ حفظ التعليق
+ أضف تعليق
+ رد
+ إضافة تعليق…
+ التعليقات
+ تحرير التعليق
+ الرد على %1$s
+ حجم الصورة
+ الهامش الأفقي
+ الهامش العمودي
+ لا شيء
+ حدد عائلة الخطوط
+ حدد حجم الخط
+ خلفية الخط
+ جريئة
+ مائل
+ تسطير
+ يتوسطه خط
+ إدراج مربع نص
+ تمكين القراءة متعددة علامات التبويب
+ استخدم عامل تصفية الملفات الصارمة
+ استخدم PDF أسماء الملفات
+ اللغة
+ كشف ML في لوحة الاختبار
+ اختبار اكتشاف فقاعة الكلام ML
+ سجلات التصدير (آخر %1$d الأسطر)
+ تمكين عامل تصفية الملفات الصارمة
+ If you enable this, some supported file types like AZW3, CB7, and FB2 might not show up depending on your file manager.\n\nAre you sure you want to enable this filter?
+ الإنجليزية (افتراضي)
+ العربية (العربية)
+ الألمانية (الألمانية)
+ التركية (التركية)
+ الفرنسية (الفرنسية)
+ Русский (الروسية)
+ موضوع التطبيق
+ المظهر
+ التباين
+ سطوع النص
+ نظام الألوان
+ ديناميكي
+ إنشاء سمة التطبيق
+ موضوع مخصص
+ المحيط
+ نعناع
+ روز
+ بني داكن
+ الجمشت
+ العنبر
+ الياقوت
+ إضافة موضوع مخصص
+ موضوع التطبيق
+ أيقونة التطبيق
+ الجهاز
+ درج مفتوح
+ صورة الملف الشخصي
+ الملف الشخصي
+ ميزة برو
+ تصفية
+ فرز
+ إغلاق البحث
+ مسح الاستعلام
+ رف البحث
+ %1$s غطاء الرف
+ الحفاظ على ألوان الصورة
+ احتفظ بألوان الصورة الأصلية عندما يتغير المظهر
+ غير على الرف
+ جميع الكتب
+ نظام
+ ضوء
+ مظلم
+ معيار
+ واسطة
+ عالي
+ مؤخرًا
+ العنوان من الألف إلى الياء
+ المؤلف من الألف إلى الياء
+ النسبة المئوية الكاملة 0-100
+ النسبة المئوية كاملة 100-0
+ الحجم (الأصغر)
+ الحجم (الأكبر)
+ الجميع
+ غير مقروءة
+ في تَقَدم
+ مكتمل
+ العلامات: %1$s
+ تصفح حسب العلامة
+ العلامات
+ المجلدات
+ ملفات
+ تحويل النص إلى كلام
+ ضوابط التشغيل لتحويل النص إلى كلام.
+ إعداد النص إلى الكلام
+ تحضير: %1$s
+ نشط TTS محرك
+ سحابة AI
+ الجهاز أصلي
+ أصوات السحابة
+ أصوات الجهاز
+ ذاكرة التخزين المؤقت السحابية
+ حدد صوت سحابي عالي الجودة
+ عينات واضحة
+ الصوت الافتراضي للنظام
+ يستخدم إعدادات الجهاز
+ مرشح اللغة
+ متصل
+ غير متصل
+ مرشح الصوت
+ لا يوجد صوت مخزن مؤقتًا لهذا الصوت.
+ مسح ذاكرة التخزين المؤقت لـ %1$s
+ هذه عينة صوتية
+ AI سمات
+ ملخص
+ خلاصة
+ مخبأ
+ لا يوجد ملخص لـ %1$s حتى الآن.
+ إنشاء ملخص لـ %1$s
+ احصل على ملخص للقصة حتى موقعك الحالي.
+ إنشاء خلاصة القصة
+ خلاصة القصة
+ ضرب ذاكرة التخزين المؤقت • مجانا
+ تم الإنشاء • مجاني (%1$d/10 متبقي)
+ تم الإنشاء • التكلفة: %1$s الاعتمادات
+ التوليد... • التكلفة: الحساب
+ AI الإخراج
+ تجديد
+ لا توجد ملخصات مخبأة لهذا الكتاب.
+ الاعتمادات
+ AI والائتمانات السحابية
+ الاعتمادات المتاحة
+ %1$d الاعتمادات
+ توزيع التكلفة المقدرة
+ سحابة TTS
+ Cost: ~3–4 credits per minute of audio generated.\nTo enable: Reader Screen > More > TTS Voice Settings.
+ AI ملخصات وخلاصة
+ Cost: ~1–4 credits per request based on chapter length.\nPro Users get 10 free summaries daily.
+ عن طريق الشراء،
+ نفاد الاعتمادات
+ ليس لديك\'ليس لديك أرصدة كافية. احصل على Episteme Pro للحصول على 10 ملخصات مجانية يوميًا، أو أضف المزيد من الاعتمادات لاستخدام الملخصات، السحابة TTS وملخص القصة.
+ احصل على Pro / أضف الاعتمادات
+ فتح تلخيص الصفحة
+ احصل على ملخصات موجزة لأي صفحة باستخدام Episteme Pro. قم بالترقية لبدء استخدام هذه الميزة.
+ قم بتنزيل نموذج Bubble Zoom
+ لاستخدام ميزة Bubble Zoom، استخدم AI يجب تنزيل النموذج (حوالي 134 ميجابايت). هل تريد تحميله الآن؟
+ ترجمة
+ العودة إلى صفحة %1$d
+ الصفحة %1$d
+ النتيجة %1$d / %2$d
+ فشل التحميل PDF.
+ جارٍ تنزيل نموذج Bubble Zoom… %1$d%%
+ الخروج من شريط التمرير
+ القفز للخلف
+ القفز إلى الأمام
+ قم بالتمرير إلى صفحة القراءة
+ صفحة مشروحة
+ إغلاق الصورة
+ تبديل أبرز البحث
+ اسحب لتحريك مربع النص
+ لا يوجد أيقونة الملفات
+ نسخ %1$s
+ علامة
+ علامة عنصر القائمة
+ إعادة ضبط التكبير
+ إنشاء التعليقات التوضيحية التجريبية
+ التعليقات التوضيحية
+ افتح ملعب القلم
+ علامة تبويب جديدة
+ تسليط الضوء على كل النص
+ تبديل وضع التحرير
+ التكبير الهزلي الذكي
+ تخصيص النقاط البارزة
+ الإنجليزية، الإسبانية، الفرنسية، الخ
+ الهندية، الماراثية، السنسكريتية + الإنجليزية
+ الصينية + الإنجليزية
+ اليابانية + الإنجليزية
+ الكورية + الإنجليزية
+ وثيقة
+ تم إنشاؤها
+ %1$s (عرض النص)
+ %1$s (إنحسر)
+ إضافة / تحرير
+ لم يتم تعيين أي علامات.
+ تطبيق العلامات
+ بحث أو إنشاء علامة...
+ إنشاء \"%1$s\"
+ سحب المسافة لتغيير الفصل
+ قصير
+ طويل
+ السرعة: %1$sx
+ الملعب: %1$sx
+ تشغيل/إيقاف مؤقت
+ إعادة ضبط السرعة
+ إعادة تعيين الملعب
+ حدد اللون
+ هذا الكتاب ليس لديه محتوى لعرضه.
+ الرابط المنسوخ
+ النص المنسوخ
+ جدول المحتويات
+ إشارة مرجعية
+ انتقل إلى الصفحة %1$d
+ العودة إلى الصفحة السابقة
+ الخروج من التكبير الذكي
+ التكبير الهزلي الذكي
+ تبديل التكبير الهزلي الذكي
+ حماية التقاط الشاشة
+ حماية التقاط الشاشة قيد التشغيل
+ تم إيقاف حماية التقاط الشاشة
+ الإعدادات
+ تحرير
+ استعادة
+ %1$s مختارة
+ افتراضيات القارئ
+ PDF-الخاصة بـ OCR والتعليقات التوضيحية وإعدادات الأداة تظل في PDF قارئ.
+ AI التعريف
+ الفصل %1$d
+ الموقع
+ الخط المخصص
+ حدث خطأ: %1$s
+ حدث خطأ أثناء تحميل المستند: %1$s
+ OCR لم يتم العثور على نص في هذه الصفحة.
+ تبدو الصفحة فارغة أو النص غير قابل للاستخراج.
+ لا يمكن تلخيص صفحة فارغة.
+ لم يتم تحميل المستند.
+ AI الميزات غير متوفرة في وضع عدم الاتصال OSS يبني.
+ تم حظره لأسباب تتعلق بالسلامة.
+ اختر نموذجًا لـ %1$s في AI إعدادات المفتاح والنموذج.
+ أضف %1$s API أدخل AI إعدادات المفتاح والنموذج.
+ AI أعاد الموفر استجابة فارغة.
+ AI خطأ في الموفر: %1$d. %2$s
+ يحتاج هذا الملخص إلى Gemini لأن نماذج Groq المحددة لا تدعم PDF/إدخال الصورة.
+ يجب عليك تسجيل الدخول لاستخدام التعليقات.
+ يجب عليك تسجيل الدخول لإرسال الملاحظات.
+ الحد الأقصى المسموح به هو 3 صور للتذاكر.
+ الحد الأقصى المسموح به هو 5 صور لكل رسالة.
+ تتجاوز صورة واحدة أو أكثر الحد المسموح به وهو 5 ميغابايت.
+ فشل إنشاء تذكرة: %1$s
+ فشل الإرسال: %1$s
+ فشل تحميل الخلاصة: %1$s
+ جسد فارغ
+ فشل التنزيل: %1$s
+ خطأ في التحميل: %1$s
+ فشل الشراء: %1$s
+ تعذر الاتصال بخدمة الفوترة.
+ لم يتم العثور على المنتجات.
+ فشل الاستعلام عن المنتجات
+ غير متوفر في النسخة مفتوحة المصدر
+ لا يوجد نص للقراءة.
+ حدث خطأ أثناء بدء التشغيل.
+ فشل تحميل الصوت.
+ خطأ في التشغيل: %1$s
+ سحابة TTS لم يتم تكوينه.
+ كتاب غير معروف
+ AI المفاتيح والنماذج
+ المفاتيح المحفوظة
+ إضافة أو استبدال المفتاح
+ مزود
+ API مفتاح
+ حفظ المفتاح
+ استخدم نموذجًا واحدًا لجميع الميزات
+ عند إيقاف التشغيل، كل قارئ AI تستخدم الميزة النموذج المحدد الخاص بها.
+ الكل AI سمات
+ يستخدم القاموس الذكي والملخصات والملخصات هذا النموذج.
+ القاموس الذكي
+ يستخدم عند تحديد الكلمات أو العبارات المحددة.
+ ملخصات
+ يستخدم لـ EPUB ملخصات و PDF ملخصات الصفحة. PDF/تحتاج ملخصات الصور إلى Gemini.
+ ملخصات
+ تستخدم لتوليد ملخص القصة.
+ يستخدم Gemini المحفوظة مفتاح. فقط %1$s مدعوم في الوقت الحالي.
+ حفظ %1$s مفتاح؟
+ بعد الحفظ، ستكون الأحرف الثلاثة الأولى والأخيرة مرئية فقط. لتغييره لاحقًا، قم باستبداله أو حذفه.
+ حذف %1$s مفتاح؟
+ ستتوقف الميزات التي تستخدم هذا الموفر عن العمل حتى يتم حفظ مفتاح جديد.
+ لم يتم حفظ أي مفتاح
+ حذف %1$s مفتاح
+ نموذج
+ لم يتم تحديد أي نموذج
+ عرض AI في القارئ
+ إخفاء AI في القارئ
+ الألوان الصلبة
+ محكم
+ مخصص الصلبة
+ مخصص محكم
+ حدد نسيج مخصص
+ سطوع النص (الضوء)
+ سطوع النص (داكن)
+ تقصير
+ غادر
+ يمين
+ يبرر
+ اعرض دائمًا
+ المزامنة مع القوائم
+ إخفاء دائما
+ قمة
+ قاع
+ هل تريد استعادة البيانات الوصفية الأصلية؟
+ سيؤدي هذا إلى كتابة العنوان الأصلي والمؤلف والسلسلة والملخص مرة أخرى في EPUB ملف. لن يتغير تقدم القراءة والعلامات والملاحظات.
+ EPUB تم تحرير البيانات الوصفية
+ بيانات التعريف من EPUB ملف
+ تم تغيير اسم العرض في التطبيق
+ البيانات الوصفية من الملف
+ البيانات الوصفية
+ ملف
+ عنوان
+ مسلسل
+ قراءة
+ اسم الملف
+ معدل
+ ملخص
+ علامات المكتبة
+ البيانات الوصفية القابلة للتحرير
+ اسم العرض
+ الاسم الموضح في القارئ
+ الملف الأصلي: %1$s
+ الشريط العلوي
+ شريط أسفل
+ الأدوات المخفية
+ المزيد من القائمة
+ الأدوات المخفية
+ قم بإسقاط الأدوات هنا
+ اسحب لإعادة الترتيب
+ التطبيقات الخارجية
+ شريط تمرير التنقل
+ السطوع
+ الشريط الجانبي
+ قم بتمييز النص القابل للتحديد
+ وضع التحرير
+ TTS الضوابط
+ وضع القراءة
+ إدارة الصفحة
+ عرض النص (إعادة التدفق)
+ الكتاب الحالي
+ عالمي
+ هذا الكتاب
+ تمكين البدائل
+ تنطبق القواعد هنا على كل كتاب ما لم يتم تعطيله لعنوان محدد.
+ أضف القاعدة
+ إضافة قواعد الكتاب
+ لا توجد قواعد استبدال عالمية حتى الآن.
+ لا توجد قواعد خاصة بالكتاب حتى الآن.
+ استخدم القواعد العالمية هنا
+ قم بإيقاف تشغيل هذا عندما يحتاج الكتاب إلى خيارات النطق الخاصة به.
+ تمكين قواعد الكتاب
+ القواعد المحلية تعمل بعد القواعد العالمية.
+ القواعد العالمية الموروثة
+ لا توجد قواعد عالمية للميراث.
+ المسموح به في هذا الكتاب
+ معطل لهذا الكتاب
+ اقتراحات
+ بديل جديد
+ تحرير الاستبدال
+ استبدال
+ تحدث كما
+ ممكّن
+ كلمة كاملة
+ حالة المباراة
+ معاينة الإدخال
+ القواعد
+ الصمت
+ نص عادي
+ حساس لحالة الأحرف
+ الكتاب الحالي
+ أضف القاعدة
+ لا توجد قواعد بديلة لهذا الكتاب حتى الآن.
+ بديل جديد
+ تحرير الاستبدال
+ مع
+ نص فارغ
+ حول
+ قارئ سطح المكتب
+ الوصول إلى سطح المكتب
+ الحساب
+ الحساب والائتمانات
+ نظرة عامة على الحساب
+ AI hub
+ يستخدم لـ EPUB ملخصات و PDF ملخصات الصفحة.
+ Episteme oss
+ نص المؤلف
+ ذاكرة التخزين المؤقت: %1$s
+ مخبأة
+ ملخص مخبأة
+ اختر Gemini الصوت المستخدم للقراءة السحابية بصوت عالٍ.
+ احذف كتاب سطح المكتب الذي تم إنشاؤه وEPUB ملفات ذاكرة التخزين المؤقت لترقيم الصفحات؟ سيتم إعادة إنشائها في المرة القادمة التي يتم فيها فتح الكتب.
+ مسح ذاكرة التخزين المؤقت الصوتية
+ إغلاق الأدوات
+ المزامنة السحابية
+ سحابة TTS الاحتياجات Gemini
+ سحابة TTS يحتاج إلى اعتمادات مسجلة
+ سحابة TTS مستعد
+ سحابة TTS إعدادات
+ سحابة TTS غير متاح
+ سحابة TTS صوت
+ يتضمن
+ حساب التكلفة
+ قم بإنشاء خلاصة تصل إلى موقعك الحالي.
+ إنشاء الرف الذكي
+ %1$d الاعتمادات المتاحة
+ %1$s الاعتمادات
+ الخطوط المستوردة للقارئ
+ حذف الخط
+ حذف %1$s؟ ستعود الكتب التي تستخدمه إلى الخط الافتراضي.
+ حذف \"%1$s\"؟ الكتب تبقى في مكتبتك.
+ حذف الملخص
+ عاجز
+ قم بإسقاط الملفات لاستيرادها
+ قم بإسقاط الملفات المدعومة لاستيرادها
+ اتصل بنا مباشرة عن طريق البريد الإلكتروني لأي شيء آخر.
+ يساوي
+ إضافات
+ تعليق
+ مجال
+ مسار المجلد
+ من هنا
+ مسح كامل
+ مجاني، %1$d غادر
+ إنشاء خلاصة
+ إنشاء ملخص
+ قم بالإبلاغ عن الأخطاء أو طلب الميزات أو الاتصال بالدعم مباشرة.
+ رعاة جيثب
+ دعم التطوير من خلال رعاة GitHub.
+ Google لم يتم تكوين تسجيل الدخول لإصدار سطح المكتب هذا.
+ أعظم من
+ يساعد
+ تقارير الأخطاء وطلبات الميزات والدعم
+ يخفي
+ استيراد الملفات
+ مشاكل
+ افتح أداة تعقب المشكلات لمعرفة الأخطاء وطلبات الميزات.
+ أقل من
+ المكتبة والقارئ
+ أي
+ إجراءات المكتبة
+ أكثر
+ لا توجد ملخصات مخبأة لهذا الكتاب حتى الآن.
+ قم باستيراد ملفات TTF أو OTF أو WOFF2 لاستخدامها في الكتب.
+ لم يتم العثور على خطوط مطابقة \"%1$s\"
+ لا Google الحساب متصل.
+ لا يوجد ملخص مخبأ لهذا القسم.
+ قارئ سطح المكتب غير متصل
+ فتح القراء
+ الافتتاح %1$s
+ فتح مكتبتك
+ المشغل
+ صفحة
+ محمية بكلمة مرور PDF
+ باتريون
+ ادعم المشروع على باتريون.
+ متوقف مؤقتًا
+ %1$s يتطلب كلمة مرور قبل أن يتم فتحه.
+ كلمة المرور مطلوبة أو غير صحيحة.
+ كلمة المرور هذه لم تفتح %1$s. أدخل PDF كلمة المرور وحاول مرة أخرى.
+ نسبة مئوية
+ يخطط
+ تحضير الصوت
+ التفضيلات
+ الحساب والائتمانات
+ الحساب والائتمانات
+ لم يتم فتح Pro لهذا الحساب.
+ لا يمكن شراء Pro والأرصدة إلا من Android برنامج. يتحقق سطح المكتب من نفس الحساب الذي تم تسجيل الدخول إليه ويستخدم تلك الاعتمادات للسحابة TTS والملخصات والملخصات وغيرها من المبالغ المدفوعة AI سمات.
+ قم بتسجيل الدخول للتحقق من حالة حسابك على سطح المكتب.
+ تم فتح Pro لهذا الحساب.
+ تقدم
+ مشروع
+ قارئ
+ علامات تبويب القارئ معطلة
+ علامات تبويب القارئ على
+ ينعش
+ الافراج عن إضافة إلى مكتبتك.
+ تخزين المفاتيح الآمنة غير متوفر في نظام التشغيل هذا. سيتم استخدام المفاتيح التي تم إدخالها هنا لهذه الجلسة ولكن لن تستمر.
+ مركز الإعدادات
+ يطابق Android إخفاء التبديل للقاموس الذكي والملخصات والملخصات.
+ مزامنة الحساب، Pro، والائتمانات
+ تم تسجيل الدخول
+ كود المصدر
+ تصفح مصدر المشروع على GitHub.
+ التوقف عن القراءة لتغيير الأصوات.
+ يدعم
+ الدعم Episteme
+ تساعد المساهمات في الحفاظ على تحسن القارئ عبر Android وسطح المكتب.
+ طرق دعم Episteme تطوير
+ مزامنة المجلدات
+ مزامنة البيانات التعريفية
+ اسم العلامة
+ وضع علامة على الكتب المختارة
+ نص العنوان
+ أدوات
+ إعدادات الاستيراد والمزامنة والتطبيق
+ اكتب، على سبيل المثال. PDF
+ منظر
+ ذاكرة التخزين المؤقت الصوتية
+ جارٍ تحضير عرض الويب المضمن…
+ إعداد عرض الويب المضمن المجمع %1$d%%
+ تم تثبيت عرض الويب المضمن. إعادة تشغيل Episteme لإنهاء الإعداد.
+ تعذر بدء عرض الويب المضمن: %1$s
+ عمل…
+ مساحة العمل
+ أضف إلى الرف
+ أنشئ رفًا أولاً، ثم أضف الكتب المحددة إليه.
+ إنشاء موضوع
+ موجود: %1$s
+ لقد قمت بالنقر فوق رابط خارجي.
+ تحرير EPUB البيانات الوصفية
+ أقل
+ …المزيد
+ لا توجد مواضيع مخصصة حتى الآن
+ إعادة تسمية في التطبيق
+ العلامات، مفصولة بفواصل
+ غير معروف
+ تحديد
+ تعليق توضيحي
+ خيارات التعليقات التوضيحية
+ أدوات التعليقات التوضيحية
+ مساعدة
+ اختر أي PDF للحفظ.
+ مسح تاريخ القفز
+ سحابة TTS فشل.
+ أضف Gemini مفتاح وحدد Gemini سحابة TTS في AI المفاتيح والنماذج.
+ سحابة TTS لم يتم تكوينه لبناء سطح المكتب هذا.
+ قم بتسجيل الدخول باستخدام Google لاستخدام السحابة TTS.
+ سحابة TTS يحتاج إلى حساب مسجل الدخول مع الاعتمادات. لا يمكن شراء Pro والأرصدة إلا من Android برنامج.
+ اللون
+ خيارات التعليق
+ مخصص
+ يؤدي هذا إلى إزالة التعليق التوضيحي من PDF.
+ هل تريد حذف التعليق التوضيحي؟
+ نص الوثيقة
+ مضمن PDF تعليق
+ فشل عرض الصفحة.
+ الميزة غير متاحة
+ انتهى
+ قلم حبر
+ إخفاء نتائج البحث
+ لون التمييز %1$d
+ لوحة تمييز
+ التفاعل
+ الفهرسة %1$d/%2$d الصفحات
+ العلامات
+ %1$d مباريات
+ %1$d المباريات حتى الآن
+ الصفحة التالية
+ نتيجة البحث التالية
+ لا يوجد تعليقات توضيحية حتى الآن
+ لا توجد إشارات مرجعية حتى الآن
+ لا تعليق
+ لا توجد مباريات
+ لا توجد تطابقات في الصفحات المفهرسة حتى الآن
+ لا يوجد جدول محتويات
+ لا يوجد نص هنا للقراءة.
+ لا يوجد نص في هذه الصفحة للقراءة.
+ لا يوجد نص لتلخيصه.
+ فتح التعليق
+ نفاد الاعتمادات. لا يمكن شراء Pro والأرصدة إلا من Android برنامج.
+ باستخدام السحابة TTS يحتاج إلى اعتمادات على سطح المكتب. لا يمكن شراء Pro والأرصدة إلا من Android برنامج.
+ يحتاج استخدام هذه الميزة إلى أرصدة على سطح المكتب. لا يمكن شراء Pro والأرصدة إلا من Android برنامج.
+ يحتاج استخدام الملخّصات إلى أرصدة على سطح المكتب. لا يمكن شراء Pro والأرصدة إلا من Android برنامج.
+ استخدام الملخصات يحتاج إلى اعتمادات على سطح المكتب. لا يمكن شراء Pro والأرصدة إلا من Android برنامج.
+ مِقلاة
+ PDF فشل الإجراء
+ PDF لا يمكن إكمال الإجراء.
+ PDF تعليق
+ ص. %1$d
+ PDF الصفحة %1$d
+ الصفحة %1$d - %2$s
+ الصفحة %1$s من %2$d
+ الصفحات %1$s من %2$d
+ PDF أنقذ
+ PDF أدوات
+ قلم رصاص
+ تحضير الاختيار
+ تحضير %1$s
+ الصفحة السابقة
+ نتيجة البحث السابقة
+ انتهى مربع حوار الطباعة.
+ مطلوب برو
+ هذه الميزة تتطلب برو. يمكن شراء Pro فقط من Android التطبيق، فسيستخدم سطح المكتب الحساب الذي تمت ترقيته بعد تسجيل الدخول.
+ يتطلب القاموس الذكي متعدد الكلمات Pro. يمكن شراء Pro فقط من Android التطبيق، فسيستخدم سطح المكتب الحساب الذي تمت ترقيته بعد تسجيل الدخول.
+ القارئ AI الميزات مخفية.
+ سطح المكتب AI لم يتم تكوينه لهذا البناء.
+ ينطبق على القراءة الرأسية والفروق المكونة من صفحتين.
+ هايلايتر دائري
+ تم الحفظ في %1$s
+ قم بالتمرير
+ البحث في PDF
+ حدد النص
+ تم التحديد %1$s
+ عرض نتائج البحث
+ قم بتسجيل الدخول باستخدام Google لاستخدام هذه الميزة على سطح المكتب.
+ قم بتسجيل الدخول باستخدام Google لاستخدام القاموس الذكي متعدد الكلمات على سطح المكتب.
+ قم بتسجيل الدخول باستخدام Google لاستخدام الملخّصات على سطح المكتب.
+ قم بتسجيل الدخول باستخدام Google لاستخدام الملخصات على سطح المكتب.
+ توقف
+ ملاحظة نصية
+ ملاحظة نصية
+ نمط النص
+ سمك %1$s
+ جدول المحتويات
+ اكتب للبحث في هذا PDF
+ بدون عنوان
+ عرض الحساب والائتمانات
+ تم مسح ذاكرة التخزين المؤقت الصوتية
+ تكبير
+ تكبير
+ تصغير
+ يختار
+ مواصلة القراءة
+ رفض
+ تحت
+ أعلى
+ AI
+ مركز
+ المؤلفون
+ العودة إلى المكتبة
+ كتاب الإجراءات
+ مجلد
+ تصفح
+ فئات
+ الفصل. %1$d
+ يتحول الفصل
+ اختر الخط
+ اختر نسيج القارئ
+ مسح أنواع الملفات
+ مسح التعليقات التوضيحية للصفحة
+ مصادر واضحة
+ حالة واضحة
+ مسح العلامات
+ إغلاق القارئ
+ مستمر
+ يغطي
+ ألوان مخصصة
+ معاينة الموضوع المخصص
+ إنقاص %1$s
+ تحديد الصفحة
+ يؤدي هذا إلى إزالة التمييز وملاحظته.
+ أدخل ملء الشاشة
+ الخروج من وضع ملء الشاشة
+ بحث خارجي
+ كتب
+ كاريكاتير
+ المستندات
+ أخرى
+ النص والويب
+ ملء
+ مظهر تخطيط ثابت
+ المجلد فارغ
+ لا تتوفر هنا أي ملفات أو مجلدات فرعية مدعومة.
+ %1$s, %2$s
+ %1$s - %2$s
+ إخفاء المرشحات
+ إخفاء أدوات القارئ
+ اضغط على إحدى الفتحات، ثم اختر لونًا.
+ مواصلة القراءة والكتب الأخيرة
+ استيراد الكتب
+ مجلد الاستيراد
+ الخطوط المستوردة
+ %1$s %2$s
+ زيادة %1$s
+ القفز على التاريخ
+ التخطيط والتباعد
+ قم باستيراد الملفات إلى مساحة تخزين التطبيق أو قم بإضافة مجلد لقراءة الملفات الموجودة في مكانها.
+ تصفح مجموعتك
+ AI مفاتيح
+ ذكي %1$d
+ غير مقروء %1$d
+ قيد التقدم %1$d
+ أكمل %1$d
+ قائمة
+ الملاحة
+ لا يوجد كتاب مفتوح
+ أضف مجلدًا لقراءة الملفات من هذا المجلد في مكانه.
+ لا توجد مجلدات حتى الآن
+ لا توجد عناصر التنقل
+ لا يوجد محتوى الصفحة
+ لم يتم العثور على إعدادات
+ ستظهر هنا الرفوف اليدوية ومجموعات السلسلة.
+ لا يوجد رفوف بعد
+ إنشاء رفوف ذكية لجمع الكتب حسب القواعد.
+ لا توجد أرفف ذكية حتى الآن
+ ستظهر هنا العلامات المضافة إلى الكتب.
+ لا توجد علامات حتى الآن
+ لم يتم استيراد أي ملفات مدعومة.
+ كتالوج
+ هل تريد حذف "%1$s"؟ قد يتوقف فتح الكتب المتدفقة من هذا الكتالوج إذا تغيرت بيانات الاعتماد لاحقًا.
+ لا توجد كتالوجات
+ أضف OPDS كتالوج لتصفح الكتب عن بعد.
+ تصفح الكتالوجات، والتدفقات، والتنزيلات
+ كتاب مفتوح
+ افتح المجلد
+ فتح PDF
+ ألوان الصفحة والنص
+ معلومات الصفحة
+ عرض الصفحة
+ تنطبق هذه الإعدادات الافتراضية عندما يدعم النظام الأساسي PDF المشترك مظهر. لكل كتاب PDF تبقى التجاوزات في PDF قارئ.
+ PDF إجراءات الملف
+ PDF هيغليغتر
+ المحفوظة مع القارئ تسليط الضوء على الشفافية.
+ دبوس
+ إدارة القارئ PDF أدوات
+ التمرير التلقائي، OCR، الإعدادات الافتراضية للتعليقات التوضيحية، وPDF- تتم إدارة رؤية الأداة فقط داخل PDF قارئ.
+ %1$s %2$s من %3$d (%4$d%%)
+ تتم إدارة الإعدادات الافتراضية لشريط أدوات القارئ من القارئ على هذا النظام الأساسي.
+ أدوات القارئ
+ حفظ الصورة
+ البحث: %1$s
+ البحث في القارئ
+ إعدادات البحث
+ اختيار
+ مقبض نهاية التحديد
+ مقبض بداية التحديد
+ أضف رفوفًا أو علامات أو بيانات تعريف المجلدات لتنظيم مكتبتك.
+ المجموعات والسلاسل والعلامات والمجلدات
+ إظهار أدوات القارئ
+ مجلد
+ ذكي
+ صلب
+ سرعة
+ بدء التمرير التلقائي
+ إيقاف التمرير التلقائي
+ توقف عن القراءة بصوت عالٍ
+ قوة الملمس
+ اكتب للبحث في هذا الكتاب
+ الطباعة
+ التراجع عن التعليق التوضيحي
+ إزالة التثبيت
+ استخدم المظهر الداكن
+ استخدام موضوع الضوء
+ كثرة الوحيدات
+ بلا
+ شريف
+ ابحث في الكتب أو المؤلفين أو العلامات
+ لا توجد أدوات
+ مرئي
+ استبدل فقط ما يتم التحدث به
+ يظل نص القارئ والإبرازات والمواقع دون تغيير.
+ %1$s -> %2$s
diff --git a/app/src/main/res/values-be/plurals.xml b/app/src/main/res/values-be/plurals.xml
index d44076d..cabb6d3 100644
--- a/app/src/main/res/values-be/plurals.xml
+++ b/app/src/main/res/values-be/plurals.xml
@@ -78,4 +78,130 @@
(%1$d фрагментаў)(%1$d фрагмента)
+
+ Імпарт %1$d кніга... Хутка яна з\'явіцца ў вашай бібліятэцы.
+ Імпарт %1$d кнігі... Хутка яны з\'явяцца ў вашай бібліятэцы.
+ Імпарт %1$d кнігі... Хутка яны з\'явяцца ў вашай бібліятэцы.
+ Імпарт %1$d кнігі... Хутка яны з\'явяцца ў вашай бібліятэцы.
+
+
+ Імпартавана %1$d кніга. Вы можаце знайсці яго на ўкладцы «Бібліятэка».
+ Імпартавана %1$d кнігі. Вы можаце знайсці іх на ўкладцы «Бібліятэка».
+ Імпартавана %1$d кнігі. Вы можаце знайсці іх на ўкладцы «Бібліятэка».
+ Імпартавана %1$d кнігі. Вы можаце знайсці іх на ўкладцы «Бібліятэка».
+
+
+ %1$d кніга дададзена на паліцу.
+ %1$d кніг дададзена на паліцу.
+ %1$d кніг дададзена на паліцу.
+ %1$d кніг дададзена на паліцу.
+
+
+ %1$d кніга з тэгам «%2$s».
+ %1$d кнігі з тэгам «%2$s».
+ %1$d кнігі з тэгам «%2$s».
+ %1$d кнігі з тэгам «%2$s».
+
+
+ Выдалена папка "%1$s" і %2$d кніга з дадатку.
+ Выдалена папка "%1$s" і %2$d кнігі з прыкладання.
+ Выдалена папка "%1$s" і %2$d кнігі з прыкладання.
+ Выдалена папка "%1$s" і %2$d кнігі з прыкладання.
+
+
+ %1$d файл
+ %1$d файлы
+ %1$d файлы
+ %1$d файлы
+
+
+ Адпусціце, каб імпартаваць %1$d файл
+ Адпусціце, каб імпартаваць %1$d файлы
+ Адпусціце, каб імпартаваць %1$d файлы
+ Адпусціце, каб імпартаваць %1$d файлы
+
+
+ %1$d файл, які не падтрымліваецца, будзе прапушчаны.
+ %1$d файлы, якія не падтрымліваюцца, будуць прапушчаны.
+ %1$d файлы, якія не падтрымліваюцца, будуць прапушчаны.
+ %1$d файлы, якія не падтрымліваюцца, будуць прапушчаны.
+
+
+ Імпарт %1$d файл…
+ Імпарт %1$d файлы…
+ Імпарт %1$d файлы…
+ Імпарт %1$d файлы…
+
+
+ Імпартавана %1$d файл.
+ Імпартавана %1$d файлы.
+ Імпартавана %1$d файлы.
+ Імпартавана %1$d файлы.
+
+
+ Імпартавана %1$d файл. Падтрымка Reader з\'явіцца пазней.
+ Імпартавана %1$d файлы. Падтрымка Reader з\'явіцца пазней.
+ Імпартавана %1$d файлы. Падтрымка Reader з\'явіцца пазней.
+ Імпартавана %1$d файлы. Падтрымка Reader з\'явіцца пазней.
+
+
+ Не атрымалася імпартаваць %1$d файл.
+ Не атрымалася імпартаваць %1$d файлы.
+ Не атрымалася імпартаваць %1$d файлы.
+ Не атрымалася імпартаваць %1$d файлы.
+
+
+ Прапушчана %1$d файл.
+ Прапушчана %1$d файлы.
+ Прапушчана %1$d файлы.
+ Прапушчана %1$d файлы.
+
+
+ Выдаліць "%1$s" і яго %2$d кніга з прыкладання? Файлы на дыску не будуць выдалены.
+ Выдаліць "%1$s" і яго %2$d кнігі з прыкладання? Файлы на дыску не будуць выдалены.
+ Выдаліць "%1$s" і яго %2$d кнігі з прыкладання? Файлы на дыску не будуць выдалены.
+ Выдаліць "%1$s" і яго %2$d кнігі з прыкладання? Файлы на дыску не будуць выдалены.
+
+
+ Збой сінхранізацыі папкі для %1$d папка.
+ Збой сінхранізацыі папкі для %1$d папкі.
+ Збой сінхранізацыі папкі для %1$d папкі.
+ Збой сінхранізацыі папкі для %1$d папкі.
+
+
+ Сінхранізацыя папкі завершана з %1$d папка прапушчана.
+ Сінхранізацыя папкі завершана з %1$d тэчкі прапушчаны.
+ Сінхранізацыя папкі завершана з %1$d тэчкі прапушчаны.
+ Сінхранізацыя папкі завершана з %1$d тэчкі прапушчаны.
+
+
+ Выдалена %1$d струменевае OPDS кнігу з гэтага каталога.
+ Выдалена %1$d струменевае OPDS кнігі з гэтага каталога.
+ Выдалена %1$d струменевае OPDS кнігі з гэтага каталога.
+ Выдалена %1$d струменевае OPDS кнігі з гэтага каталога.
+
+
+ Усе кнігі %1$d
+ Усе кнігі %1$d
+ Усе кнігі %1$d
+ Усе кнігі %1$d
+
+
+ Паліцы %1$d
+ Паліцы %1$d
+ Паліцы %1$d
+ Паліцы %1$d
+
+
+ Тэгі %1$d
+ Тэгі %1$d
+ Тэгі %1$d
+ Тэгі %1$d
+
+
+ Тэчкі %1$d
+ Тэчкі %1$d
+ Тэчкі %1$d
+ Тэчкі %1$d
+
diff --git a/app/src/main/res/values-be/strings.xml b/app/src/main/res/values-be/strings.xml
index f3b50bf..c40352c 100644
--- a/app/src/main/res/values-be/strings.xml
+++ b/app/src/main/res/values-be/strings.xml
@@ -1105,4 +1105,411 @@
Выкарыстоўваць назвы файлаў PDFЯркасцьПаказваць укладкі на верхняй панэлі
+ Адключыць лакальную сінхранізацыю
+ Уключыць лакальную сінхранізацыю
+ Лакальная сінхранізацыя адключана
+ Адключыць лакальную сінхранізацыю папак?
+ Episteme спыніць сканаванне гэтай папкі і спыніць запіс JSON сінхранізаваць файлы. Выдаліце %1$s тэчку з гэтай папкі таксама?
+ Захоўваць сінхранізацыю дадзеных
+ Выдаліць дадзеныя сінхранізацыі
+ Выдаліць шрыфты?
+ Вы ўпэўнены, што хочаце выдаліць %1$d выбраныя шрыфты? Гэта выдаліць іх з усіх вашых прылад, калі сінхранізацыя ўключана.
+ Ні ў адной лакальнай папцы не ўключана сінхранізацыя.
+ Сінхранізацыя лакальнай папкі адключана.
+ Сінхранізацыя лакальнай папкі адключана. Папка дадзеных сінхранізацыі выдалена.
+ Сінхранізацыя лакальнай папкі адключана, але папку дадзеных сінхранізацыі нельга выдаліць.
+ Сінхранізацыя лакальных папак уключана.
+ Вертыкальны (WebView)
+ Вертыкальны (родная бэта-версія)
+ Кніга Замены слоў
+ Папярэдняя TTS кавалак
+ Далей TTS кавалак
+ Малюнкі
+ Выяў не знойдзена.
+ Спампаваць малюнак
+ Захавана %1$s
+ Не ўдалося захаваць малюнак.
+ PDF разварот старонкі
+ Адна старонка
+ Дзве старонкі
+ Першая старонка адна
+ Пачынае развароты пасля тытульнай старонкі.
+ Яркасць
+ Выкарыстоўвайце яркасць сістэмы
+ Адпавядае наладзе яркасці прылады.
+ Карыстальніцкая яркасць
+ Прымяняецца, калі экран чытача адкрыты.
+ Актуальная кніга
+ Дадаць правіла
+ Для гэтай кнігі пакуль няма правілаў замены.
+ Новая замена
+ Рэдагаваць замену
+ з
+ пусты тэкст
+ Аб
+ Чыталка для працоўнага стала
+ Доступ да працоўнага стала
+ Рахунак
+ Рахунак і крэдыты
+ Агляд акаўнта
+ AI хаб
+ Выкарыстоўваецца для EPUB зводкі і PDF зводкі старонак.
+ Episteme ас
+ Аўтарскі тэкст
+ Кэш: %1$s
+ У кэшы
+ Рэзюмэ ў кэшы
+ Выберыце Gemini голас, які выкарыстоўваецца для чытання ўслых у воблаку.
+ Выдаліць згенераваную настольную кнігу і EPUB файлы кэша пагінацыі? Яны будуць адноўлены пры наступным адкрыцці кніг.
+ Ачысціць галасавы кэш
+ Закрыць інструменты
+ Воблачная сінхранізацыя
+ Воблака TTS патрэбы Gemini
+ Воблака TTS патрабуе ўваходу ў крэдыты
+ Воблака TTS гатовы
+ Воблака TTS налады
+ Воблака TTS недаступны
+ Воблака TTS голас
+ Змяшчае
+ Разлік кошту
+ Стварыце рэзюмэ да вашай бягучай пазіцыі.
+ Ствары разумную паліцу
+ %1$d даступныя крэдыты
+ %1$s крэдыты
+ Імпартаваныя шрыфты для чыталкі
+ Выдаліць шрыфт
+ Выдаліць %1$s? Кнігі, якія выкарыстоўваюць яго, вернуцца да шрыфта па змаўчанні.
+ Выдаліць \"%1$s\"? Кнігі застаюцца ў вашай бібліятэцы.
+ Выдаліць зводку
+ Інваліды
+ Перацягніце файлы для імпарту
+ Адпусціце падтрымоўваныя файлы для імпарту
+ Звязвайцеся з намі непасрэдна па электроннай пошце для чаго-небудзь яшчэ.
+ Роўнае
+ Дадаткова
+ Зваротная сувязь
+ Палявы
+ Шлях да папкі
+ Адсюль
+ Поўнае сканаванне
+ Бясплатна, %1$d злева
+ Стварыце рэзюмэ
+ Сфармаваць рэзюмэ
+ Паведамляйце пра памылкі, запытвайце функцыі або звяртайцеся ў службу падтрымкі.
+ Спонсары GitHub
+ Падтрымка распрацоўкі праз спонсараў GitHub.
+ Google уваход не наладжаны для гэтай зборкі працоўнага стала.
+ Больш, чым
+ Даведка
+ Справаздачы пра памылкі, запыты функцый і падтрымка
+ Схаваць
+ Імпарт файлаў
+ Праблемы
+ Адкрыйце праграму адсочвання памылак і запытаў функцый.
+ Менш чым
+ Бібліятэка і чытач
+ Любая
+ Бібліятэчныя акцыі
+ больш
+ Кэшаваных зводак для гэтай кнігі пакуль няма.
+ Імпартуйце файлы TTF, OTF або WOFF2, каб выкарыстоўваць іх у кнігах.
+ Шрыфты, якія адпавядаюць \"%1$s\"
+ Няма Google уліковы запіс падключаны.
+ Для гэтага раздзела няма зводкі ў кэшы.
+ Аўтаномная праграма для чытання на працоўным стале
+ Адкрытыя чытачы
+ Адкрыццё %1$s
+ Адкрыццё вашай бібліятэкі
+ Аператар
+ старонка
+ Абаронена паролем PDF
+ патрэон
+ Падтрымайце праект на Patreon.
+ Прыпынена
+ %1$s перад адкрыццём патрабуецца пароль.
+ Пароль патрабуецца або няправільны.
+ Гэты пароль не адкрыў %1$s. Увядзіце PDF пароль і паспрабуйце яшчэ раз.
+ Працэнт
+ План
+ Падрыхтоўка аўдыё
+ Прэферэнцыі
+ Рахунак і крэдыты
+ Рахунак і крэдыты
+ Pro не разблакіраваны для гэтага ўліковага запісу.
+ Pro і крэдыты можна набыць толькі ў Android дадатак. Desktop правярае той жа ўліковы запіс, у які ўвайшоў, і выкарыстоўвае гэтыя крэдыты для воблака TTS, зводак, рэзюмэ і іншых платных AI асаблівасці.
+ Увайдзіце, каб праверыць стан свайго ўліковага запісу на працоўным стале.
+ Pro разблакіравана для гэтага ўліковага запісу.
+ Прагрэс
+ Праект
+ Чытач
+ Укладкі Reader выключаны
+ Укладкі для чытання
+ Абнавіць
+ Адпусціце, каб дадаць у сваю бібліятэку.
+ Бяспечнае сховішча ключоў недаступнае ў гэтай аперацыйнай сістэме. Ключы, уведзеныя тут, будуць выкарыстоўвацца для гэтага сеансу, але не будуць захоўвацца.
+ Цэнтр налад
+ Супадае з Android схаваць пераключальнік для разумнага слоўніка, рэзюмэ і рэзюмэ.
+ Сінхранізацыя ўліковага запісу, Pro і крэдытаў
+ Выкананы ўваход
+ Зыходны код
+ Праглядзіце зыходны код праекта на GitHub.
+ Каб змяніць галасы, спыніце чытанне.
+ Падтрымка
+ Падтрымка Episteme
+ Уклад дапамагае чытачу паляпшацца ў Android і працоўны стол.
+ Спосабы падтрымкі Episteme развіццё
+ Сінхранізацыя тэчак
+ Сінхранізацыя метададзеных
+ Імя тэга
+ Пазначыць выбраныя кнігі
+ Тэкст загалоўка
+ інструменты
+ Імпарт, сінхранізацыя і налады праграм
+ Тып, напр. PDF
+ Выгляд
+ Галасавы кэш
+ Падрыхтоўка ўбудаванага вэб-прагляду…
+ Падрыхтоўка ўбудаванага вэб-прагляду %1$d%%
+ Убудаваны вэб-прагляд усталяваны. Перазапусціць Episteme каб завяршыць наладку.
+ Не ўдалося запусціць убудаваны вэб-прагляд: %1$s
+ Працуе...
+ Працоўная прастора
+ Дадаць на паліцу
+ Спачатку стварыце паліцу, а потым дадайце на яе выбраныя кнігі.
+ Стварыць тэму
+ Існуючы: %1$s
+ Вы націснулі на знешнюю спасылку.
+ Рэдагаваць EPUB метададзеныя
+ Менш
+ …больш
+ Карыстальніцкіх тэм пакуль няма
+ Перайменаваць у дадатку
+ Тэгі, падзеленыя коскамі
+ Невядомы
+ Вызначце
+ Анатацыя
+ Параметры анатацыі
+ Інструменты анатавання
+ Дапамога
+ Выберыце, які PDF каб захаваць.
+ Ачысціць гісторыю скачкоў
+ Воблака TTS не атрымалася.
+ Дадайце Gemini і выберыце Gemini воблака TTS у AI ключы і мадэлі.
+ Воблака TTS не наладжаны для гэтай зборкі працоўнага стала.
+ Увайдзіце з Google выкарыстоўваць воблака TTS.
+ Воблака TTS патрабуе ўваходу ва ўліковы запіс з крэдытамі. Pro і крэдыты можна набыць толькі ў Android дадатак.
+ Колер
+ Параметры каментарыяў
+ Прыстасаваныя
+ Гэта выдаляе анатацыю з гэтага PDF.
+ Выдаліць анатацыю?
+ Тэкст дакумента
+ Убудаваны PDF каментар
+ Не ўдалося адлюстраваць старонку.
+ Функцыя недаступная
+ Скончана
+ Аўтаручка
+ Схаваць вынікі пошуку
+ Колер выдзялення %1$d
+ Палітра хайлайтер
+ Узаемадзеянне
+ Індэксацыя %1$d/%2$d старонкі
+ Разметка
+ %1$d запалкі
+ %1$d матчаў да гэтага часу
+ Наступная старонка
+ Наступны вынік пошуку
+ Пакуль няма анатацый
+ Закладак пакуль няма
+ Без каментароў
+ Супадзенняў няма
+ Супадзенняў на праіндэксаваных старонках пакуль няма
+ Няма зместу
+ Тут няма тэксту для чытання.
+ На гэтай старонцы няма тэксту для чытання.
+ Няма тэксту для абагульнення.
+ Адкрыты каментар
+ Скончыліся крэдыты. Pro і крэдыты можна набыць толькі ў Android дадатак.
+ Выкарыстанне воблака TTS патрэбны крэдыты на працоўны стол. Pro і крэдыты можна набыць толькі ў Android дадатак.
+ Для выкарыстання гэтай функцыі патрэбны крэдыты на працоўным стале. Pro і крэдыты можна набыць толькі ў Android дадатак.
+ Для выкарыстання рэзюмэ патрэбны крэдыты на працоўным стале. Pro і крэдыты можна набыць толькі ў Android дадатак.
+ Для выкарыстання рэзюмэ патрэбны крэдыты на працоўным стале. Pro і крэдыты можна набыць толькі ў Android дадатак.
+ Пан
+ PDF дзеянне не атрымалася
+ PDF дзеянне не можа быць завершана.
+ PDF каментар
+ стар. %1$d
+ PDF старонка %1$d
+ Старонка %1$d - %2$s
+ Старонка %1$s з %2$d
+ Старонкі %1$s з %2$d
+ PDF захаваны
+ PDF інструменты
+ Аловак
+ Рыхтуем выбар
+ Падрыхтоўка %1$s
+ Папярэдняя старонка
+ Папярэдні вынік пошуку
+ Дыялогавае акно друку завершана.
+ Патрабуецца прафесіянал
+ Для гэтай функцыі патрабуецца Pro. Pro можна набыць толькі ў Android пасля ўваходу ў сістэму працоўны стол будзе выкарыстоўваць абноўлены ўліковы запіс.
+ Шматслоўны разумны слоўнік патрабуе Pro. Pro можна набыць толькі ў Android пасля ўваходу ў сістэму працоўны стол будзе выкарыстоўваць абноўлены ўліковы запіс.
+ Чытач AI функцыі схаваныя.
+ Працоўны стол AI не настроены для гэтай зборкі.
+ Ужываецца для вертыкальнага чытання і двухстаронкавых разваротаў.
+ Круглы хайлайтер
+ Захавана ў %1$s
+ Скрутак
+ Шукаць у PDF
+ Вылучыце тэкст
+ Выбраны %1$s
+ Паказаць вынікі пошуку
+ Увайдзіце з Google каб выкарыстоўваць гэтую функцыю на працоўным стале.
+ Увайдзіце з Google выкарыстоўваць шматслоўны разумны слоўнік на працоўным стале.
+ Увайдзіце з Google выкарыстоўваць recaps на працоўным стале.
+ Увайдзіце з Google выкарыстоўваць зводкі на працоўным стале.
+ Спыніліся
+ Тэкставая нататка
+ тэкставая нататка
+ Стыль тэксту
+ Таўшчыня %1$s
+ TOC
+ Увядзіце для пошуку PDF
+ Без назвы
+ Прагляд рахунку і крэдытаў
+ Галасавы кэш ачышчаны
+ маштабаванне
+ Павялічыць
+ Паменшыць маштаб
+ Выбірай
+ Працягвайце чытаць
+ Звольніць
+ Уніз
+ Уверх
+ AI
+ Цэнтр
+ Аўтары
+ Вярнуцца да бібліятэкі
+ Кніжныя акцыі
+ Папка
+ Праглядзіце
+ Катэгорыі
+ гл. %1$d
+ Раздзел паваротаў
+ Выберыце шрыфт
+ Выберыце тэкстуру чытача
+ Выдаліць тыпы файлаў
+ Ачысціць анатацыі старонкі
+ Чыстыя крыніцы
+ Выразны статус
+ Ачысціць тэгі
+ Закрыць чытач
+ Бесперапынны
+ Вокладкі
+ Індывідуальныя колеры
+ Папярэдні прагляд карыстальніцкай тэмы
+ Паменшыць %1$s
+ Вызначыць старонку
+ Гэта выдаляе вылучэнне і яго ноту.
+ Перайсці на ўвесь экран
+ Выйсці з поўнаэкраннага рэжыму
+ Знешні пошук
+ Кнігі
+ Коміксы
+ Дакументы
+ Іншае
+ Тэкст і вэб
+ Запоўніць
+ Знешні выгляд фіксаванага макета
+ Тэчка пустая
+ Тут няма падтрымоўваных файлаў і падтэчак.
+ %1$s, %2$s
+ %1$s - %2$s
+ Схаваць фільтры
+ Схаваць інструменты для чытання
+ Дакраніцеся да слота і выберыце колер.
+ Працягвайце чытаць і апошнія кнігі
+ Імпартаваць кнігі
+ Тэчка імпарту
+ Імпартаваныя шрыфты
+ %1$s %2$s
+ Павелічэнне %1$s
+ Гісторыя скачкоў
+ Макет і інтэрвал
+ Імпартуйце файлы ў сховішча праграмы або дадайце папку для чытання файлаў на месцы.
+ Праглядзіце сваю калекцыю
+ AI ключы
+ Разумны %1$d
+ Непрачытанае %1$d
+ Выконваецца %1$d
+ Завяршыць %1$d
+ Спіс
+ Навігацыя
+ Няма адкрытай кнігі
+ Дадайце папку для чытання файлаў з гэтай папкі на месцы.
+ Папак пакуль няма
+ Няма элементаў навігацыі
+ Няма зместу старонкі
+ Налады не знойдзены
+ Тут з\'явяцца ручныя паліцы і калекцыі серый.
+ Паліц пакуль няма
+ Стварыце разумныя паліцы, каб збіраць кнігі па правілах.
+ Разумных паліц пакуль няма
+ Тэгі, дададзеныя да кніг, з\'явяцца тут.
+ Тэгаў пакуль няма
+ Файлы, якія падтрымліваюцца, не былі імпартаваныя.
+ Каталог
+ Выдаліць "%1$s"? Патокавыя кнігі з гэтага каталога могуць перастаць адкрывацца, калі ўліковыя даныя зменяцца пазней.
+ Ніякіх каталогаў
+ Дадайце OPDS каталог для прагляду выдаленых кніг.
+ Праглядайце каталогі, патокі і загрузкі
+ Адкрытая кніга
+ Адкрыць тэчку
+ Адкрыць PDF
+ Колеры старонкі і тэксту
+ Інфармацыя пра старонку
+ Шырыня старонкі
+ Гэтыя значэнні па змаўчанні прымяняюцца, калі платформа падтрымлівае агульны PDF знешні выгляд. За кнігу PDF перавызначае знаходжанне ў PDF чытач.
+ PDF дзеянні з файламі
+ PDF хайлайтер
+ Захавана з празрыстасцю вылучэння чытача.
+ Pin
+ Кіраваны чытачом PDF інструменты
+ Аўтаматычная пракрутка, OCR, анатацыі па змаўчанні і PDF-толькі бачнасць інструмента кіруюцца ўнутры актыўнага PDF чытач.
+ %1$s %2$s з %3$d (%4$d%%)
+ Налады панэлі інструментаў Reader па змаўчанні кіруюцца з праграмы Reader на гэтай платформе.
+ Інструменты чытача
+ Захаваць малюнак
+ Пошук: %1$s
+ Пошук у чыталцы
+ Налады пошуку
+ Выбар
+ Маркёр канца выбару
+ Маркер пачатку выбару
+ Дадайце паліцы, тэгі або метаданыя тэчак, каб упарадкаваць сваю бібліятэку.
+ Калекцыі, серыі, тэгі і папкі
+ Паказаць інструменты для чытання
+ Папка
+ Разумны
+ Цвёрдая
+ хуткасць
+ Запусціць аўтаматычную пракрутку
+ Спыніць аўтаматычную пракрутку
+ Перастаньце чытаць услых
+ Трываласць тэкстуры
+ Увядзіце для пошуку ў гэтай кнізе
+ Тыпаграфіка
+ Адмяніць анатацыю
+ Адмацаваць
+ Выкарыстоўвайце цёмную тэму
+ Выкарыстоўвайце светлую тэму
+ Мона
+ Без
+ Засечкі
+ Пошук кніг, аўтараў або тэгаў
+ Без інструментаў
+ Бачны
+ Замяняйце толькі сказанае
+ Тэкст Reader, вылучэнні і месцы застаюцца нязменнымі.
+ %1$s -> %2$s
diff --git a/app/src/main/res/values-de/plurals.xml b/app/src/main/res/values-de/plurals.xml
index ca3511a..76bd90b 100644
--- a/app/src/main/res/values-de/plurals.xml
+++ b/app/src/main/res/values-de/plurals.xml
@@ -52,4 +52,88 @@
(%1$d Stück)(%1$d Stücke)
+
+ Import %1$d Buch… Es wird in Kürze in Ihrer Bibliothek erscheinen.
+ Import %1$d Bücher… Sie werden in Kürze in Ihrer Bibliothek erscheinen.
+
+
+ Importiert %1$d Buch. Sie finden es auf der Registerkarte „Bibliothek“.
+ Importiert %1$d Bücher. Sie finden sie auf der Registerkarte „Bibliothek“.
+
+
+ %1$d Buch zum Regal hinzugefügt.
+ %1$d Bücher zum Regal hinzugefügt.
+
+
+ %1$d Buch mit dem Tag „%2$s“.
+ %1$d Bücher mit dem Tag „%2$s“.
+
+
+ Ordner „%1$s“ entfernt und %2$d Buchen Sie über die App.
+ Ordner „%1$s“ entfernt und %2$d Bücher aus der App.
+
+
+ %1$d Datei
+ %1$d Dateien
+
+
+ Zum Importieren %1$d ablegen Datei
+ Zum Importieren %1$d ablegen Dateien
+
+
+ %1$d Nicht unterstützte Dateien werden übersprungen.
+ %1$d Nicht unterstützte Dateien werden übersprungen.
+
+
+ Import %1$d Datei…
+ Import %1$d Dateien…
+
+
+ Importiert %1$d Datei.
+ Importiert %1$d Dateien.
+
+
+ Importiert %1$d Datei. Die Reader-Unterstützung kommt später.
+ Importiert %1$d Dateien. Die Reader-Unterstützung kommt später.
+
+
+ %1$d konnte nicht importiert werden Datei.
+ %1$d konnte nicht importiert werden Dateien.
+
+
+ Übersprungen %1$d Datei.
+ Übersprungen %1$d Dateien.
+
+
+ Entfernen Sie „%1$s“ und sein %2$d über die App buchen? Dateien auf der Festplatte werden nicht gelöscht.
+ Entfernen Sie „%1$s“ und sein %2$d Bücher aus der App? Dateien auf der Festplatte werden nicht gelöscht.
+
+
+ Ordnersynchronisierung für %1$d fehlgeschlagen Ordner.
+ Ordnersynchronisierung für %1$d fehlgeschlagen Ordner.
+
+
+ Ordnersynchronisierung mit %1$d abgeschlossen Ordner übersprungen.
+ Ordnersynchronisierung mit %1$d abgeschlossen Ordner übersprungen.
+
+
+ Entfernt %1$d gestreamt OPDS Buch aus diesem Katalog.
+ Entfernt %1$d gestreamt OPDS Bücher aus diesem Katalog.
+
+
+ Alle Bücher %1$d
+ Alle Bücher %1$d
+
+
+ Regale %1$d
+ Regale %1$d
+
+
+ Schlagworte %1$d
+ Schlagworte %1$d
+
+
+ Ordner %1$d
+ Ordner %1$d
+
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 6e159ac..53b1a00 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -1047,4 +1047,469 @@
Bubble Zoom Modell herunterladenUm die Bubble Zoom Funktion zu nutzen, muss ein KI Modell heruntergeladen werden (~134 MB). Jetzt herunterladen?Bubble Zoom Modell herunterladen... %1$d%%
+ Deaktivieren Sie die lokale Synchronisierung
+ Aktivieren Sie die lokale Synchronisierung
+ Lokale Synchronisierung deaktiviert
+ Lokale Ordnersynchronisierung deaktivieren?
+ Episteme stoppt das Scannen dieses Ordners und stoppt das Schreiben von JSON Dateien synchronisieren. Entfernen Sie den %1$s Ordner aus diesem Ordner auch?
+ Synchronisieren Sie die Daten
+ Synchronisierungsdaten entfernen
+ Schriftarten löschen?
+ Sind Sie sicher, dass Sie %1$d löschen möchten? ausgewählte Schriftarten? Dadurch werden sie von allen Ihren Geräten entfernt, wenn die Synchronisierung aktiviert ist.
+ Für keine lokalen Ordner ist die Synchronisierung aktiviert.
+ Synchronisierung lokaler Ordner deaktiviert.
+ Synchronisierung lokaler Ordner deaktiviert. Synchronisierungsdatenordner entfernt.
+ Die Synchronisierung lokaler Ordner wurde deaktiviert, der Synchronisierungsdatenordner konnte jedoch nicht entfernt werden.
+ Lokale Ordnersynchronisierung aktiviert.
+ Vertikal (WebView)
+ Vertikal (Native Beta)
+ Wortersetzungen in Büchern
+ Symbol „Keine Dateien“.
+ Listenelementmarkierung
+ Demo-Anmerkungen generieren
+ Demo-Anmerkungen
+ Öffnen Sie den Pen Playground
+ Intelligenter Comic-Zoom
+ Passen Sie Highlights an
+ %1$s (Textansicht)
+ %1$s (Reflow)
+ Erstellen Sie \"%1$s\"
+ Ziehen Sie die Distanz, um das Kapitel zu wechseln
+ Der Bildschirmaufnahmeschutz ist aktiviert
+ Der Bildschirmaufnahmeschutz ist deaktiviert
+ PDF-spezifische OCR-, Anmerkungs- und Werkzeugeinstellungen bleiben im PDF Leser.
+ AI Funktionen sind im Offline-Modus OSS nicht verfügbar bauen.
+ Wählen Sie ein Modell für %1$s in AI Schlüssel- und Modelleinstellungen.
+ Fügen Sie einen %1$s hinzu API Geben Sie AI ein Schlüssel- und Modelleinstellungen.
+ Der AI Der Anbieter hat eine leere Antwort zurückgegeben.
+ AI Anbieterfehler: %1$d. %2$s
+ Diese Zusammenfassung benötigt einen Gemini Modell, da die ausgewählten Groq-Modelle die Eingabe PDF/image nicht unterstützen.
+ Sie müssen angemeldet sein, um Feedback nutzen zu können.
+ Sie müssen angemeldet sein, um Feedback abzugeben.
+ Für Tickets sind maximal 3 Bilder zulässig.
+ Pro Nachricht sind maximal 5 Bilder zulässig.
+ Ein oder mehrere Bilder überschreiten die 5-MB-Grenze.
+ Ticket konnte nicht erstellt werden: %1$s
+ Senden fehlgeschlagen: %1$s
+ Feed konnte nicht geladen werden: %1$s
+ Download-Fehler: %1$s
+ Der Kauf ist fehlgeschlagen: %1$s
+ Es konnte keine Verbindung zum Abrechnungsdienst hergestellt werden.
+ Die Produktabfrage ist fehlgeschlagen
+ Audio konnte nicht geladen werden.
+ Wiedergabefehler: %1$s
+ Verwenden Sie ein Modell für alle Funktionen
+ Im ausgeschalteten Zustand ist jeder Leser AI Die Funktion verwendet ihr eigenes ausgewähltes Modell.
+ Intelligente Wörterbücher, Zusammenfassungen und Rückblicke nutzen alle dieses Modell.
+ Intelligentes Wörterbuch
+ Wird beim Definieren ausgewählter Wörter oder Phrasen verwendet.
+ Zusammenfassungen
+ Wird für EPUB verwendet Zusammenfassungen und PDF Seitenzusammenfassungen. PDF/Bildzusammenfassungen benötigen Gemini.
+ Zusammenfassungen
+ Wird für die Generierung von Story-Zusammenfassungen verwendet.
+ Verwendet den gespeicherten Gemini Schlüssel. Nur %1$s wird vorerst unterstützt.
+ Sparen Sie %1$s Schlüssel?
+ Nach dem Speichern sind nur die ersten 3 und die letzten 3 Zeichen sichtbar. Um es später zu ändern, ersetzen oder löschen Sie es.
+ Funktionen, die diesen Anbieter nutzen, funktionieren erst, wenn ein neuer Schlüssel gespeichert wird.
+ Lesen
+ Mehr Menü
+ Legen Sie hier Werkzeuge ab
+ Textansicht (Reflow)
+ Die hier aufgeführten Regeln gelten für jedes Buch, sofern sie nicht für einen bestimmten Titel deaktiviert sind.
+ Noch keine globalen Ersatzregeln.
+ Noch keine buchspezifischen Regeln.
+ Nutzen Sie hier globale Regeln
+ Deaktivieren Sie diese Option, wenn ein Buch eigene Ausspracheoptionen benötigt.
+ Buchregeln aktivieren
+ Lokale Regeln werden nach globalen Regeln ausgeführt.
+ Geerbte globale Regeln
+ Keine globalen Regeln zum Vererben.
+ In diesem Buch erlaubt
+ Für dieses Buch deaktiviert
+ Vorschläge
+ Neuer Ersatz
+ Ersetzung bearbeiten
+ Ersetzen
+ Sprechen Sie als
+ Aktiviert
+ Ganzes Wort
+ Streichholzetui
+ Vorschau der Eingabe
+ Regeln
+ Stille
+ Klartext
+ Groß- und Kleinschreibung beachten
+ Aktuelles Buch
+ Regel hinzufügen
+ Für dieses Buch gibt es noch keine Ersatzregeln.
+ Neuer Ersatz
+ Ersetzung bearbeiten
+ Mit
+ leerer Text
+ Über
+ Desktop-Reader
+ Desktop-Zugriff
+ Konto
+ Konto & Guthaben
+ Kontoübersicht
+ AI Nabe
+ Wird für EPUB verwendet Zusammenfassungen und PDF Seitenzusammenfassungen.
+ Episteme oss
+ Autorentext
+ Cache: %1$s
+ Zwischengespeichert
+ Zwischengespeicherte Zusammenfassung
+ Wählen Sie Gemini Stimme, die zum Vorlesen in der Cloud verwendet wird.
+ Löschen Sie das generierte Desktop-Buch und EPUB Paginierungs-Cache-Dateien? Sie werden beim nächsten Öffnen von Büchern neu erstellt.
+ Sprachcache löschen
+ Werkzeuge schließen
+ Cloud-Synchronisierung
+ Wolke TTS braucht Gemini
+ Wolke TTS benötigt angemeldete Credits
+ Wolke TTS fertig
+ Wolke TTS Einstellungen
+ Wolke TTS nicht verfügbar
+ Wolke TTS Stimme
+ Enthält
+ Kostenberechnung
+ Erstellen Sie eine Zusammenfassung bis zu Ihrer aktuellen Position.
+ Erstellen Sie ein intelligentes Regal
+ %1$d Credits verfügbar
+ %1$s Credits
+ Importierte Schriftarten für den Leser
+ Schriftart löschen
+ %1$s löschen? Bücher, die diese Schriftart verwenden, greifen auf die Standardschriftart zurück.
+ \"%1$s\" löschen? Bücher bleiben in Ihrer Bibliothek.
+ Zusammenfassung löschen
+ Deaktiviert
+ Legen Sie die zu importierenden Dateien ab
+ Löschen Sie unterstützte Dateien zum Importieren
+ Kontaktieren Sie uns für alles Weitere direkt per E-Mail.
+ Gleich
+ Extras
+ Rückmeldung
+ Feld
+ Ordnerpfad
+ Von hier
+ Vollständiger Scan
+ Kostenlos, %1$d links
+ Zusammenfassung erstellen
+ Zusammenfassung erstellen
+ Melden Sie Fehler, fordern Sie Funktionen an oder wenden Sie sich direkt an den Support.
+ GitHub-Sponsoren
+ Unterstützen Sie die Entwicklung durch GitHub-Sponsoren.
+ Google Die Anmeldung ist für diesen Desktop-Build nicht konfiguriert.
+ Größer als
+ Hilfe
+ Fehlerberichte, Funktionsanfragen und Support
+ Verstecken
+ Dateien importieren
+ Probleme
+ Öffnen Sie den Issue-Tracker für Fehler und Funktionsanfragen.
+ Weniger als
+ Bibliothek und Leser
+ Irgendein
+ Bibliotheksaktionen
+ Mehr
+ Für dieses Buch wurden noch keine Zusammenfassungen zwischengespeichert.
+ Importieren Sie TTF-, OTF- oder WOFF2-Dateien, um sie in Büchern zu verwenden.
+ Keine passenden Schriftarten für \"%1$s\" gefunden
+ Nein Google Konto ist verbunden.
+ Für diesen Abschnitt wurde keine Zusammenfassung zwischengespeichert.
+ Offline-Desktop-Reader
+ Offene Leser
+ Eröffnung %1$s
+ Öffnen Sie Ihre Bibliothek
+ Betreiber
+ Seite
+ Passwortgeschützt PDF
+ Patreon
+ Unterstützen Sie das Projekt auf Patreon.
+ Angehalten
+ %1$s erfordert ein Passwort, bevor es geöffnet werden kann.
+ Das Passwort ist erforderlich oder falsch.
+ Mit diesem Passwort konnte %1$s nicht geöffnet werden. Geben Sie PDF ein Geben Sie Ihr Passwort ein und versuchen Sie es erneut.
+ Prozent
+ Planen
+ Audio vorbereiten
+ Präferenzen
+ Konto & Guthaben
+ Konto & Guthaben
+ Pro ist für dieses Konto nicht freigeschaltet.
+ Pro- und Credits können nur über Android erworben werden App. Desktop überprüft dasselbe angemeldete Konto und verwendet diese Credits für Cloud TTS, Zusammenfassungen, Rückblicke und andere kostenpflichtige AI Merkmale.
+ Melden Sie sich an, um Ihren Kontostatus auf dem Desktop zu überprüfen.
+ Pro ist für dieses Konto freigeschaltet.
+ Fortschritt
+ Projekt
+ Leser
+ Reader-Tabs aus
+ Reader-Tabs an
+ Aktualisieren
+ Zum Hinzufügen zu Ihrer Bibliothek freigeben.
+ Eine sichere Schlüsselspeicherung ist auf diesem Betriebssystem nicht verfügbar. Die hier eingegebenen Schlüssel werden für diese Sitzung verwendet, aber nicht gespeichert.
+ Einstellungs-Hub
+ Entspricht Android Blenden Sie den Schalter für intelligentes Wörterbuch, Zusammenfassungen und Rückblicke aus.
+ Konto, Pro und Credits synchronisieren
+ Angemeldet
+ Quellcode
+ Durchsuchen Sie die Projektquelle auf GitHub.
+ Hören Sie auf zu lesen, um die Stimmen zu ändern.
+ Unterstützung
+ Unterstützung Episteme
+ Beiträge tragen dazu bei, dass sich der Leser im gesamten Android weiter verbessert und Desktop.
+ Möglichkeiten zur Unterstützung von Episteme Entwicklung
+ Ordner synchronisieren
+ Metadaten synchronisieren
+ Tag-Name
+ Markieren Sie ausgewählte Bücher
+ Titeltext
+ Werkzeuge
+ Import-, Synchronisierungs- und App-Einstellungen
+ Typ, z.B. PDF
+ Ansicht
+ Sprachcache
+ Eingebettete Webansicht wird vorbereitet…
+ Vorbereiten der gebündelten eingebetteten Webansicht %1$d%%
+ Eingebettete Webansicht installiert. Starten Sie Episteme neu um die Einrichtung abzuschließen.
+ Eingebettete Webansicht konnte nicht gestartet werden: %1$s
+ Arbeiten…
+ Arbeitsbereich
+ Zum Regal hinzufügen
+ Erstellen Sie zunächst ein Regal und fügen Sie dann ausgewählte Bücher hinzu.
+ Thema erstellen
+ Vorhanden: %1$s
+ Sie haben auf einen externen Link geklickt.
+ Bearbeiten EPUB Metadaten
+ Weniger
+ …mehr
+ Noch keine benutzerdefinierten Designs
+ In App umbenennen
+ Tags, durch Kommas getrennt
+ Unbekannt
+ Definieren
+ Anmerkung
+ Anmerkungsoptionen
+ Anmerkungswerkzeuge
+ Assist
+ Wählen Sie aus, welches PDF zu retten.
+ Sprungverlauf löschen
+ Wolke TTS fehlgeschlagen.
+ Fügen Sie einen Gemini hinzu Taste und wählen Sie Gemini Wolke TTS in AI Schlüssel und Modelle.
+ Wolke TTS ist für diesen Desktop-Build nicht konfiguriert.
+ Melden Sie sich mit Google an um die Cloud TTS zu verwenden.
+ Wolke TTS benötigt ein angemeldetes Konto mit Guthaben. Pro- und Credits können nur über Android erworben werden App.
+ Farbe
+ Kommentarmöglichkeiten
+ Benutzerdefiniert
+ Dadurch wird die Anmerkung von diesem PDF entfernt.
+ Anmerkung löschen?
+ Dokumenttext
+ Eingebettet PDF Kommentar
+ Seite konnte nicht gerendert werden.
+ Funktion nicht verfügbar
+ Fertig
+ Füllfederhalter
+ Suchergebnisse ausblenden
+ Hervorhebungsfarbe %1$d
+ Highlighter-Palette
+ Interaktion
+ Indizierung %1$d/%2$d Seiten
+ Markup
+ %1$d Streichhölzer
+ %1$d Spiele bisher
+ Nächste Seite
+ Nächstes Suchergebnis
+ Noch keine Anmerkungen
+ Noch keine Lesezeichen
+ Kein Kommentar
+ Keine Übereinstimmungen
+ Noch keine Übereinstimmungen auf indizierten Seiten
+ Kein Inhaltsverzeichnis
+ Hier gibt es keinen Text zum Lesen.
+ Auf dieser Seite gibt es keinen Text zum Lesen.
+ Es gibt keinen zusammenfassenden Text.
+ Kommentar öffnen
+ Keine Credits mehr. Pro- und Credits können nur über Android erworben werden App.
+ Verwenden der Cloud TTS benötigt Credits auf dem Desktop. Pro- und Credits können nur über Android erworben werden App.
+ Für die Nutzung dieser Funktion sind Credits auf dem Desktop erforderlich. Pro- und Credits können nur über Android erworben werden App.
+ Für die Verwendung von Zusammenfassungen sind Credits auf dem Desktop erforderlich. Pro- und Credits können nur über Android erworben werden App.
+ Für die Verwendung von Zusammenfassungen sind Credits auf dem Desktop erforderlich. Pro- und Credits können nur über Android erworben werden App.
+ Pfanne
+ PDF Aktion fehlgeschlagen
+ Der PDF Aktion konnte nicht abgeschlossen werden.
+ PDF Kommentar
+ P. %1$d
+ PDF Seite %1$d
+ Seite %1$d - %2$s
+ Seite %1$s von %2$d
+ Seiten %1$s von %2$d
+ PDF gespeichert
+ PDF Werkzeuge
+ Bleistift
+ Auswahl vorbereiten
+ Vorbereiten %1$s
+ Vorherige Seite
+ Vorheriges Suchergebnis
+ Der Druckdialog ist beendet.
+ Profi erforderlich
+ Für diese Funktion ist Pro erforderlich. Pro kann nur bei Android erworben werden App, dann verwendet der Desktop nach der Anmeldung das aktualisierte Konto.
+ Für das intelligente Wörterbuch mit mehreren Wörtern ist Pro erforderlich. Pro kann nur bei Android erworben werden App, dann verwendet der Desktop nach der Anmeldung das aktualisierte Konto.
+ Leser AI Funktionen sind ausgeblendet.
+ Desktop AI ist für diesen Build nicht konfiguriert.
+ Gilt für vertikales Lesen und Doppelseiten.
+ Runder Textmarker
+ Gespeichert unter %1$s
+ Scrollen
+ Suche in PDF
+ Text auswählen
+ Ausgewählt %1$s
+ Suchergebnisse anzeigen
+ Melden Sie sich mit Google an um diese Funktion auf dem Desktop zu verwenden.
+ Melden Sie sich mit Google an um das intelligente Wörterbuch mit mehreren Wörtern auf dem Desktop zu verwenden.
+ Melden Sie sich mit Google an um Zusammenfassungen auf dem Desktop zu verwenden.
+ Melden Sie sich mit Google an um Zusammenfassungen auf dem Desktop zu verwenden.
+ Angehalten
+ Textnotiz
+ Textnotiz
+ Textstil
+ Dicke %1$s
+ Inhaltsverzeichnis
+ Geben Sie Folgendes ein, um nach diesem PDF zu suchen
+ Ohne Titel
+ Konto und Guthaben anzeigen
+ Sprachcache geleert
+ Zoomen
+ Vergrößern
+ Herauszoomen
+ Wählen Sie
+ Lesen Sie weiter
+ Entlassen
+ Runter
+ Auf
+ AI
+ Mitte
+ Autoren
+ Zurück zur Bibliothek
+ Aktionen buchen
+ Ordner
+ Durchsuchen
+ Kategorien
+ Kap. %1$d
+ Kapitelwechsel
+ Wählen Sie eine Schriftart
+ Wählen Sie die Textur des Lesers
+ Dateitypen löschen
+ Seitenanmerkungen löschen
+ Klare Quellen
+ Klarer Status
+ Tags löschen
+ Leser schließen
+ Kontinuierlich
+ Abdeckungen
+ Benutzerdefinierte Farben
+ Vorschau des benutzerdefinierten Themes
+ %1$s verringern
+ Seite definieren
+ Dadurch werden die Hervorhebung und ihre Notiz entfernt.
+ Geben Sie den Vollbildmodus ein
+ Verlassen Sie den Vollbildmodus
+ Externe Suche
+ Bücher
+ Comics
+ Dokumente
+ Andere
+ Text und Web
+ Füllen
+ Erscheinungsbild mit festem Layout
+ Der Ordner ist leer
+ Hier sind keine unterstützten Dateien oder Unterordner verfügbar.
+ %1$s, %2$s
+ %1$s - %2$s
+ Filter ausblenden
+ Lesetools ausblenden
+ Tippen Sie auf einen Steckplatz und wählen Sie dann eine Farbe aus.
+ Lesen Sie weiter und aktuelle Bücher
+ Bücher importieren
+ Ordner importieren
+ Importierte Schriftarten
+ %1$s %2$s
+ %1$s erhöhen
+ Sprunggeschichte
+ Layout und Abstand
+ Importieren Sie Dateien in den App-Speicher oder fügen Sie einen Ordner hinzu, um Dateien direkt zu lesen.
+ Durchsuchen Sie Ihre Sammlung
+ AI Schlüssel
+ Smart %1$d
+ Ungelesen %1$d
+ In Bearbeitung %1$d
+ Vervollständigen Sie %1$d
+ Liste
+ Navigation
+ Kein Buch geöffnet
+ Fügen Sie einen Ordner hinzu, um Dateien aus diesem Ordner direkt zu lesen.
+ Noch keine Ordner
+ Keine Navigationselemente
+ Kein Seiteninhalt
+ Keine Einstellungen gefunden
+ Hier erscheinen manuelle Regale und Seriensammlungen.
+ Noch keine Regale
+ Erstellen Sie intelligente Regale, um Bücher nach Regeln zu sammeln.
+ Noch keine smarten Regale
+ Den Büchern hinzugefügte Tags werden hier angezeigt.
+ Noch keine Tags
+ Es wurden keine unterstützten Dateien importiert.
+ Katalog
+ „%1$s“ löschen? Gestreamte Bücher aus diesem Katalog werden möglicherweise nicht mehr geöffnet, wenn sich die Anmeldeinformationen später ändern.
+ Keine Kataloge
+ Fügen Sie einen OPDS hinzu Katalog zum Durchsuchen entfernter Bücher.
+ Durchsuchen Sie Kataloge, Streams und Downloads
+ Offenes Buch
+ Ordner öffnen
+ Öffnen Sie PDF
+ Seiten- und Textfarben
+ Seiteninformationen
+ Seitenbreite
+ Diese Standardeinstellungen gelten, wenn die Plattform gemeinsam genutzte PDF unterstützt Aussehen. Pro Buch PDF Überschreibungen bleiben im PDF Leser.
+ PDF Dateiaktionen
+ PDF Textmarker
+ Gespeichert mit Leser-Highlight-Transparenz.
+ Pin
+ Vom Leser verwaltet PDF Werkzeuge
+ Automatischer Bildlauf, OCR, Anmerkungsstandardwerte und nur PDF-Werkzeugsichtbarkeit werden innerhalb des aktiven PDF verwaltet Leser.
+ %1$s %2$s von %3$d (%4$d%%)
+ Die Standardeinstellungen der Reader-Symbolleiste werden vom Reader auf dieser Plattform verwaltet.
+ Reader-Tools
+ Bild speichern
+ Suche: %1$s
+ Im Reader suchen
+ Sucheinstellungen
+ Auswahl
+ Auswahl-Endgriff
+ Starthandle für die Auswahl
+ Fügen Sie Regale, Tags oder Ordnermetadaten hinzu, um Ihre Bibliothek zu organisieren.
+ Sammlungen, Serien, Tags und Ordner
+ Lesetools anzeigen
+ Ordner
+ Clever
+ Solide
+ Geschwindigkeit
+ Automatisches Scrollen starten
+ Automatisches Scrollen stoppen
+ Hören Sie auf, laut vorzulesen
+ Texturstärke
+ Geben Sie ein, um dieses Buch zu durchsuchen
+ Typografie
+ Anmerkung rückgängig machen
+ Lösen
+ Verwenden Sie ein dunkles Thema
+ Verwenden Sie ein helles Thema
+ Mono
+ Ohne
+ Serife
+ Suchen Sie nach Büchern, Autoren oder Tags
+ Keine Werkzeuge
+ Sichtbar
+ Ersetzen Sie nur das Gesprochene
+ Lesertext, Hervorhebungen und Standorte bleiben unverändert.
+ %1$s -> %2$s
diff --git a/app/src/main/res/values-es/plurals.xml b/app/src/main/res/values-es/plurals.xml
index fbd9e9a..f9d77c7 100644
--- a/app/src/main/res/values-es/plurals.xml
+++ b/app/src/main/res/values-es/plurals.xml
@@ -52,4 +52,88 @@
(%1$d fragmento)(%1$d fragmentos)
+
+ Importando %1$d libro... Aparecerá en tu biblioteca en breve.
+ Importando %1$d libros... Aparecerán en tu biblioteca en breve.
+
+
+ Importado %1$d libro. Puede encontrarlo en la pestaña Biblioteca.
+ Importado %1$d libros. Puede encontrarlos en la pestaña Biblioteca.
+
+
+ %1$d libro agregado al estante.
+ %1$d libros agregados al estante.
+
+
+ %1$d libro etiquetado con "%2$s".
+ %1$d libros etiquetados con "%2$s".
+
+
+ Carpeta eliminada "%1$s" y %2$d reservar desde la aplicación.
+ Carpeta eliminada "%1$s" y %2$d libros desde la aplicación.
+
+
+ %1$d archivo
+ %1$d archivos
+
+
+ Soltar para importar %1$d archivo
+ Soltar para importar %1$d archivos
+
+
+ %1$d Se omitirá el archivo no compatible.
+ %1$d Se omitirán los archivos no compatibles.
+
+
+ Importando %1$d archivo…
+ Importando %1$d archivos…
+
+
+ Importado %1$d archivo.
+ Importado %1$d archivos.
+
+
+ Importado %1$d archivo. El soporte para lectores llegará más tarde.
+ Importado %1$d archivos. El soporte para lectores llegará más tarde.
+
+
+ No se pudo importar %1$d archivo.
+ No se pudo importar %1$d archivos.
+
+
+ Saltado %1$d archivo.
+ Saltado %1$d archivos.
+
+
+ Eliminar "%1$s" y su %2$d reservar desde la aplicación? Los archivos en el disco no se eliminarán.
+ Eliminar "%1$s" y su %2$d libros de la aplicación? Los archivos en el disco no se eliminarán.
+
+
+ Error de sincronización de carpetas para %1$d carpeta.
+ Error de sincronización de carpetas para %1$d carpetas.
+
+
+ Sincronización de carpetas finalizada con %1$d carpeta omitida.
+ Sincronización de carpetas finalizada con %1$d carpetas omitidas.
+
+
+ Eliminado %1$d transmitido OPDS libro de ese catálogo.
+ Eliminado %1$d transmitido OPDS libros de ese catálogo.
+
+
+ Todos los libros %1$d
+ Todos los libros %1$d
+
+
+ Estantes %1$d
+ Estantes %1$d
+
+
+ Etiquetas %1$d
+ Etiquetas %1$d
+
+
+ Carpetas %1$d
+ Carpetas %1$d
+
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index 0fac6be..a443664 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -1086,4 +1086,430 @@
Nederlands (neerlandés)Українська (ucraniano)Bahasa Indonesia (indonesio)
+ Mostrar pestañas en la barra superior de aplicaciones
+ Deshabilitar la sincronización local
+ Habilitar sincronización local
+ Sincronización local deshabilitada
+ ¿Desactivar la sincronización de carpetas locales?
+ Episteme dejará de escanear esta carpeta y dejará de escribir JSON sincronizar archivos. Retire el %1$s carpeta de esta carpeta también?
+ Mantener datos sincronizados
+ Eliminar datos de sincronización
+ ¿Eliminar fuentes?
+ ¿Estás seguro de que deseas eliminar %1$d fuentes seleccionadas? Esto los eliminará de todos sus dispositivos si la sincronización está activada.
+ Ninguna carpeta local tiene la sincronización habilitada.
+ Sincronización de carpeta local deshabilitada.
+ Sincronización de carpeta local deshabilitada. Se eliminó la carpeta de datos de sincronización.
+ La sincronización de la carpeta local está deshabilitada, pero no se pudo eliminar la carpeta de datos de sincronización.
+ Sincronización de carpeta local habilitada.
+ Vertical (vista web)
+ Vertical (Beta nativa)
+ Reemplazos de palabras de libros
+ Anterior TTS pedazo
+ Siguiente TTS pedazo
+ Imágenes
+ No se encontraron imágenes.
+ Descargar imagen
+ Guardado %1$s
+ No se pudo guardar la imagen.
+ PDF extensión de página
+ Una sola página
+ dos paginas
+ Primera pagina sola
+ Comienza a desplegar páginas opuestas después de la portada.
+ Brillo
+ Usar el brillo del sistema
+ Sigue la configuración de brillo del dispositivo.
+ Brillo personalizado
+ Se aplica mientras una pantalla de lector está abierta.
+ %1$d%%
+ Estante creado "%1$s".
+ Estante inteligente creado "%1$s".
+ Estante renombrado a "%1$s".
+ Estante eliminado "%1$s".
+ Actualizado "%1$s".
+ Esos archivos ya están en la biblioteca.
+ %1$s - %2$s
+ Ahorrar
+ Guardar comentario
+ Agregar comentario
+ Responder
+ Añade un comentario…
+ Comentarios
+ Editando comentario
+ Respondiendo a %1$s
+ Uso PDF Nombres de archivos
+ Brillo
+ libro actual
+ Agregar regla
+ Aún no hay reglas de reemplazo para este libro.
+ Nuevo reemplazo
+ Editar reemplazo
+ Con
+ texto vacío
+ Acerca de
+ Lector de escritorio
+ Acceso al escritorio
+ Cuenta
+ Cuenta y créditos
+ Descripción general de la cuenta
+ AI centro
+ Utilizado para EPUB resúmenes y PDF resúmenes de páginas.
+ Episteme oss
+ Texto del autor
+ Caché: %1$s
+ En caché
+ Resumen en caché
+ Elija el Gemini voz utilizada para la lectura en la nube en voz alta.
+ Eliminar el libro de escritorio generado y EPUB archivos de caché de paginación? Se recrearán la próxima vez que se abran los libros.
+ Borrar caché de voz
+ Cerrar herramientas
+ Sincronización en la nube
+ Nube TTS necesidades Gemini
+ Nube TTS necesita créditos registrados
+ Nube TTS listo
+ Nube TTS ajustes
+ Nube TTS indisponible
+ Nube TTS voz
+ Contiene
+ cálculo de costos
+ Cree un resumen hasta su posición actual.
+ Crear estante inteligente
+ %1$d créditos disponibles
+ %1$s créditos
+ Fuentes importadas para el lector.
+ Eliminar fuente
+ Eliminar %1$s? Los libros que lo utilicen volverán a la fuente predeterminada.
+ ¿Eliminar \"%1$s\"? Los libros permanecen en tu biblioteca.
+ Eliminar resumen
+ Desactivado
+ Soltar archivos para importar
+ Eliminar archivos compatibles para importar
+ Contáctenos directamente por correo electrónico para cualquier otra cosa.
+ igual
+ Extras
+ Comentario
+ Campo
+ Ruta de la carpeta
+ Desde aquí
+ escaneo completo
+ Gratis, %1$d izquierda
+ Generar resumen
+ Generar resumen
+ Informe errores, solicite funciones o comuníquese con el soporte directamente.
+ Patrocinadores de GitHub
+ Apoyar el desarrollo a través de patrocinadores de GitHub.
+ Google El inicio de sesión no está configurado para esta compilación de escritorio.
+ Más que
+ Ayuda
+ Informes de errores, solicitudes de funciones y soporte
+ Esconder
+ Importar archivos
+ Asuntos
+ Abra el rastreador de problemas para detectar errores y solicitudes de funciones.
+ Menos que
+ Biblioteca y lector
+ Cualquier
+ Acciones de biblioteca
+ Más
+ Aún no hay resúmenes almacenados en caché para este libro.
+ Importe archivos TTF, OTF o WOFF2 para usarlos en libros.
+ No se encontraron fuentes que coincidan con \"%1$s\"
+ No Google La cuenta está conectada.
+ No hay ningún resumen almacenado en caché para esta sección.
+ Lector de escritorio sin conexión
+ Lectores abiertos
+ Apertura %1$s
+ Abriendo tu biblioteca
+ Operador
+ Página
+ Protegido con contraseña PDF
+ Patreón
+ Apoya el proyecto en Patreon.
+ En pausa
+ %1$s requiere una contraseña antes de poder abrirse.
+ La contraseña es obligatoria o incorrecta.
+ Esa contraseña no se abrió %1$s. Ingrese el PDF contraseña y vuelva a intentarlo.
+ Por ciento
+ Plan
+ Preparando audio
+ Preferencias
+ Cuenta y créditos
+ Cuenta y créditos
+ Pro no está desbloqueado para esta cuenta.
+ Pro y créditos solo se pueden comprar en Android aplicación. Desktop verifica la misma cuenta iniciada y usa esos créditos para la nube TTS, resúmenes, resúmenes y otros pagos AI características.
+ Inicie sesión para verificar el estado de su cuenta en el escritorio.
+ Pro está desbloqueado para esta cuenta.
+ Progreso
+ Proyecto
+ Lector
+ Pestañas del lector desactivadas
+ Pestañas del lector en
+ Refrescar
+ Suelte para agregarlo a su biblioteca.
+ El almacenamiento seguro de claves no está disponible en este sistema operativo. Las claves ingresadas aquí se usarán para esta sesión pero no se conservarán.
+ Centro de configuración
+ Coincide con el Android Ocultar alternar para diccionario inteligente, resúmenes y resúmenes.
+ Sincronizar cuenta, Pro y créditos
+ Iniciado sesión
+ código fuente
+ Explore el código fuente del proyecto en GitHub.
+ Deja de leer para cambiar de voz.
+ Apoyo
+ Soporte Episteme
+ Las contribuciones ayudan a que el lector siga mejorando en Android y escritorio.
+ Formas de apoyar Episteme desarrollo
+ Sincronizar carpetas
+ Sincronizar metadatos
+ Nombre de etiqueta
+ Etiquetar libros seleccionados
+ Texto del título
+ Herramientas
+ Importación, sincronización y configuración de aplicaciones
+ Tipo, p.e. PDF
+ Vista
+ Caché de voz
+ Preparando vista web integrada...
+ Preparando la vista web integrada incluida %1$d%%
+ Vista web integrada instalada. Reiniciar Episteme para finalizar la configuración.
+ La vista web integrada no pudo iniciarse: %1$s
+ Laboral…
+ Espacio de trabajo
+ Añadir al estante
+ Primero cree un estante y luego agréguele los libros seleccionados.
+ Crear tema
+ Existente: %1$s
+ Hiciste clic en un enlace externo.
+ Editar EPUB metadatos
+ Menos
+ …más
+ Aún no hay temas personalizados
+ Cambiar nombre en la aplicación
+ Etiquetas, separadas por comas
+ Desconocido
+ Definir
+ Anotación
+ Opciones de anotación
+ Herramientas de anotación
+ Asistir
+ Elige cuál PDF para ahorrar.
+ Borrar historial de saltos
+ Nube TTS fallido.
+ Agregar un Gemini y seleccione Gemini nube TTS en AI claves y modelos.
+ Nube TTS no está configurado para esta versión de escritorio.
+ Inicia sesión con Google utilizar la nube TTS.
+ Nube TTS necesita una cuenta registrada con créditos. Pro y créditos solo se pueden comprar en Android aplicación.
+ Color
+ Opciones de comentarios
+ Costumbre
+ Esto elimina la anotación de este PDF.
+ ¿Eliminar anotación?
+ Texto del documento
+ Integrado PDF comentario
+ No se pudo renderizar la página.
+ Característica no disponible
+ Finalizado
+ Pluma estilográfica
+ Ocultar resultados de búsqueda
+ Color de resaltado %1$d
+ paleta de resaltadores
+ Interacción
+ Indexación %1$d/%2$d paginas
+ Margen
+ %1$d partidos
+ %1$d partidos hasta ahora
+ Página siguiente
+ Siguiente resultado de búsqueda
+ Aún no hay anotaciones
+ Aún no hay marcadores
+ Sin comentarios
+ No hay coincidencias
+ Aún no hay coincidencias en las páginas indexadas
+ Sin tabla de contenidos
+ No hay texto aquí para leer.
+ No hay texto en esta página para leer.
+ No hay texto para resumir.
+ Abrir comentario
+ Sin créditos. Pro y créditos solo se pueden comprar en Android aplicación.
+ Usando la nube TTS necesita créditos en el escritorio. Pro y créditos solo se pueden comprar en Android aplicación.
+ Para utilizar esta función se necesitan créditos en el escritorio. Pro y créditos solo se pueden comprar en Android aplicación.
+ El uso de resúmenes necesita créditos en el escritorio. Pro y créditos solo se pueden comprar en Android aplicación.
+ El uso de resúmenes necesita créditos en el escritorio. Pro y créditos solo se pueden comprar en Android aplicación.
+ Cacerola
+ PDF la acción falló
+ El PDF la acción no se pudo completar.
+ PDF comentario
+ pag. %1$d
+ PDF página %1$d
+ Página %1$d - %2$s
+ Página %1$s de %2$d
+ Páginas %1$s de %2$d
+ PDF salvado
+ PDF herramientas
+ Lápiz
+ Preparando la selección
+ Preparando %1$s
+ Pagina anterior
+ Resultado de búsqueda anterior
+ El cuadro de diálogo de impresión ha finalizado.
+ Se requiere profesional
+ Esta característica requiere Pro. Pro solo se puede comprar en Android aplicación, luego el escritorio usará la cuenta actualizada después de iniciar sesión.
+ El diccionario inteligente de varias palabras requiere Pro. Pro solo se puede comprar en Android aplicación, luego el escritorio usará la cuenta actualizada después de iniciar sesión.
+ Lector AI Las características están ocultas.
+ Escritorio AI no está configurado para esta compilación.
+ Se aplica a lectura vertical y pliegos de dos páginas.
+ resaltador redondo
+ Guardado en %1$s
+ Voluta
+ Buscar en PDF
+ Seleccionar texto
+ Seleccionado %1$s
+ Mostrar resultados de búsqueda
+ Inicia sesión con Google para utilizar esta función en el escritorio.
+ Inicia sesión con Google para utilizar el diccionario inteligente de varias palabras en el escritorio.
+ Inicia sesión con Google para usar resúmenes en el escritorio.
+ Inicia sesión con Google para usar resúmenes en el escritorio.
+ Interrumpido
+ Nota de texto
+ nota de texto
+ Estilo de texto
+ Espesor %1$s
+ TOC
+ Escribe para buscar esto PDF
+ Intitulado
+ Ver cuenta y créditos
+ Caché de voz borrado
+ Zoom
+ Dar un golpe de zoom
+ alejar
+ Elegir
+ Continuar leyendo
+ Despedir
+ Abajo
+ Arriba
+ AI
+ Centro
+ Autores
+ volver a la biblioteca
+ Acciones del libro
+ Carpeta
+ Navegar
+ Categorías
+ Cap. %1$d
+ Turnos de capítulo
+ Elige fuente
+ Elige la textura del lector
+ Borrar tipos de archivos
+ Borrar anotaciones de página
+ Fuentes claras
+ Borrar estado
+ Borrar etiquetas
+ Lector cercano
+ Continuo
+ Cubiertas
+ Colores personalizados
+ Vista previa del tema personalizado
+ Disminución %1$s
+ Definir página
+ Esto elimina el resaltado y su nota.
+ Entrar en pantalla completa
+ Salir de pantalla completa
+ búsqueda externa
+ Libros
+ Historietas
+ Documentos
+ Otro
+ Texto y web
+ Llenar
+ Apariencia de diseño fijo
+ La carpeta está vacía
+ No hay archivos ni subcarpetas compatibles disponibles aquí.
+ %1$s, %2$s
+ %1$s - %2$s
+ Ocultar filtros
+ Ocultar herramientas de lectura
+ Toca una ranura y luego elige un color.
+ Continuar leyendo y libros recientes
+ importar libros
+ Carpeta de importación
+ Fuentes importadas
+ %1$s %2$s
+ Incrementar %1$s
+ Historial de saltos
+ Diseño y espaciado
+ Importe archivos al almacenamiento de aplicaciones o agregue una carpeta para leer archivos en su lugar.
+ Explora tu colección
+ AI llaves
+ Inteligente %1$d
+ No leído %1$d
+ En curso %1$d
+ Completo %1$d
+ Lista
+ Navegación
+ ningún libro abierto
+ Agregue una carpeta para leer archivos de esa carpeta en su lugar.
+ Aún no hay carpetas
+ Sin elementos de navegación
+ Sin contenido de página
+ No se encontraron configuraciones
+ Aquí aparecerán estanterías manuales y colecciones de series.
+ Aún no hay estantes
+ Crea estanterías inteligentes para recoger libros según reglas.
+ Aún no hay estantes inteligentes
+ Las etiquetas agregadas a los libros aparecerán aquí.
+ Aún no hay etiquetas
+ No se importaron archivos compatibles.
+ Catalogar
+ ¿Eliminar "%1$s"? Los libros transmitidos desde este catálogo pueden dejar de abrirse si las credenciales cambian más adelante.
+ Sin catálogos
+ Agregar un OPDS catálogo para buscar libros remotos.
+ Explorar catálogos, transmisiones y descargas
+ Libro abierto
+ Abrir carpeta
+ Abierto PDF
+ Colores de página y texto
+ Información de la página
+ Ancho de página
+ Estos valores predeterminados se aplican cuando la plataforma admite compartido PDF apariencia. Por libro PDF anula la permanencia en el PDF lector.
+ PDF acciones de archivo
+ PDF resaltador
+ Guardado con transparencia resaltada del lector.
+ Alfiler
+ Gestionado por lector PDF herramientas
+ El desplazamiento automático, OCR, los valores predeterminados de anotación y la visibilidad de la herramienta PDF-solo se administran dentro del PDF activo. lector.
+ %1$s %2$s de %3$d (%4$d%%)
+ Los valores predeterminados de la barra de herramientas del lector se administran desde el lector en esta plataforma.
+ Herramientas de lectura
+ Salvar el imagen
+ Buscar: %1$s
+ Buscar en lector
+ Configuración de búsqueda
+ Selección
+ Mango final de selección
+ Controlador de inicio de selección
+ Agregue estantes, etiquetas o metadatos de carpetas para organizar su biblioteca.
+ Colecciones, series, etiquetas y carpetas.
+ Mostrar herramientas de lectura
+ Carpeta
+ Elegante
+ Sólido
+ Velocidad
+ Iniciar desplazamiento automático
+ Detener el desplazamiento automático
+ dejar de leer en voz alta
+ Fuerza de la textura
+ Escribe para buscar este libro
+ Tipografía
+ Deshacer anotación
+ Desprender
+ Usar tema oscuro
+ Usar tema claro
+ Mononucleosis infecciosa
+ sin
+ Serifa
+ Buscar libros, autores o etiquetas
+ Sin herramientas
+ Visible
+ Reemplazar solo lo que se habla
+ El texto del lector, los aspectos destacados y las ubicaciones permanecen sin cambios.
+ %1$s -> %2$s
diff --git a/app/src/main/res/values-et/plurals.xml b/app/src/main/res/values-et/plurals.xml
index d3af106..abf02c6 100644
--- a/app/src/main/res/values-et/plurals.xml
+++ b/app/src/main/res/values-et/plurals.xml
@@ -20,4 +20,120 @@
Kustuta fail jäädavaltKustuta failid jäädavalt
+
+ %1$d riiul
+ %1$d riiulid
+
+
+ Kas soovite %1$d jäädavalt kustutada valitud faili oma seadmest? Seda toimingut ei saa tagasi võtta.
+ Kas soovite %1$d jäädavalt kustutada teie seadmest valitud failid? Seda toimingut ei saa tagasi võtta.
+
+
+ Kas soovite eemaldada %1$d valitud faili viimaste failide loendist? See kuvatakse uuesti, kui avate selle uuesti raamatukogust.
+ Kas soovite eemaldada %1$d valitud failid viimaste failide loendist? See kuvatakse uuesti, kui avate selle uuesti raamatukogust.
+
+
+ Kas soovite kindlasti eemaldada %1$d raamat \'%2$s\' riiul? Raamat jääb teie kogusse ja kuvatakse jaotises Riiulita.
+ Kas soovite kindlasti eemaldada %1$d raamatud \'%2$s\' riiul? Raamatud jäävad teie kogusse ja kuvatakse jaotises Riiulita.
+
+
+ %1$d raamat raamatukogust eemaldatud.
+ %1$d raamatud raamatukogust eemaldatud.
+
+
+ Importimine %1$d raamat… See ilmub peagi teie teegis.
+ Importimine %1$d raamatud… Need ilmuvad peagi teie kogusse.
+
+
+ Imporditud %1$d raamat. Selle leiate vahekaardilt Raamatukogu.
+ Imporditud %1$d raamatuid. Leiate need vahekaardilt Raamatukogu.
+
+
+ %1$d raamat lisatud riiulile.
+ %1$d raamatud lisatud riiulile.
+
+
+ %1$d raamat sildiga "%2$s".
+ %1$d raamatud sildiga "%2$s".
+
+
+ Eemaldatud kaust "%1$s" ja %2$d raamat rakendusest.
+ Eemaldatud kaust "%1$s" ja %2$d raamatud rakendusest.
+
+
+ %1$d kausta
+ %1$d kaustad
+
+
+ %1$d faili
+ %1$d failid
+
+
+ Langetage import %1$d faili
+ Langetage import %1$d failid
+
+
+ %1$d toetamata fail jäetakse vahele.
+ %1$d toetamata failid jäetakse vahele.
+
+
+ Importimine %1$d fail…
+ Importimine %1$d failid…
+
+
+ Imporditud %1$d faili.
+ Imporditud %1$d failid.
+
+
+ Imporditud %1$d faili. Lugeja tugi tuleb hiljem.
+ Imporditud %1$d failid. Lugeja tugi tuleb hiljem.
+
+
+ Ei saanud importida %1$d faili.
+ Ei saanud importida %1$d failid.
+
+
+ Vahele jäetud %1$d faili.
+ Vahele jäetud %1$d failid.
+
+
+ Eemalda "%1$s" ja selle %2$d raamatut rakendusest? Ketta faile ei kustutata.
+ Eemalda "%1$s" ja selle %2$d raamatud rakendusest? Ketta faile ei kustutata.
+
+
+ Kausta sünkroonimine ebaõnnestus %1$d kausta.
+ Kausta sünkroonimine ebaõnnestus %1$d kaustad.
+
+
+ Kausta sünkroonimine on lõpetatud %1$d kaust jäi vahele.
+ Kausta sünkroonimine on lõpetatud %1$d kaustad vahele jäetud.
+
+
+ Eemaldatud %1$d voogesitatud OPDS raamat sellest kataloogist.
+ Eemaldatud %1$d voogesitatud OPDS raamatud sellest kataloogist.
+
+
+ %1$d tag
+ %1$d sildid
+
+
+ Kõik raamatud %1$d
+ Kõik raamatud %1$d
+
+
+ Riiulid %1$d
+ Riiulid %1$d
+
+
+ Sildid %1$d
+ Sildid %1$d
+
+
+ Kaustad %1$d
+ Kaustad %1$d
+
+
+ (%1$d tükk)
+ (%1$d tükid)
+
diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml
index 68327c6..c9f585f 100644
--- a/app/src/main/res/values-et/strings.xml
+++ b/app/src/main/res/values-et/strings.xml
@@ -8,4 +8,1508 @@
SulgeLisaMuuda nime
+ Tagasi
+ Otsi
+ Selge
+ Rakenda
+ Luba
+ Viga: %1$s
+ Mine tagasi
+ Tasuta
+ Aktiivsed vahelehed
+ Kuva vahekaardid ülemisel rakenduseribal
+ Sule vahekaart
+ Sulgege kõik vahelehed
+ Kas sulgeda kõik vahelehed?
+ Kas olete kindel, et soovite sulgeda kõik aktiivsed vahelehed?
+ %1$s nõustute meie %2$s ja kinnitage, et olete lugenud meie %3$s.
+ Kasutustingimused
+ Privaatsuspoliitika
+ Litsentsid
+ %1$d valitud
+ Tühjenda valik
+ Kinnita/vabasta
+ Info
+ Valige Kõik
+ Eemalda hiljutiste hulgast
+ Warning: Some selected items are synced from a local folder. Proceeding will delete the actual files from your device storage.\n\nThis action cannot be undone.
+ Faili teave
+ Raamatu nimi
+ Kopeeri nimi
+ Algne nimi: %1$s
+ Taastage originaal
+ Faili nimi: %1$s
+ Autor
+ Vorming
+ Suurus
+ Lisatud
+ Asukoht
+ Allikas: OPDS Voog
+ Rakendusesisene salvestusruum
+ Sisemälu
+ Umbes Episteme
+ Versioon: %1$s (Järgmine: %2$d)
+ Valige fail
+ Kas kustutada kõik sünkroonitud andmed?
+ Kas olete kindel, et soovite kõik oma raamatuandmed pilvest jäädavalt kustutada? See kustutab uuesti sünkroonimise vältimiseks ka teie kohaliku raamatukogu. Seda toimingut ei saa tagasi võtta.
+ KUSTUTA KÕIK ANDMED
+ Kodu
+ Raamatukogu
+ Viimased failid
+ Teie raamatukogu on tühi
+ Valige lugemiseks fail või sünkroonige kohalik kaust, et raamatuid automaatselt importida.
+ Viimaseid faile pole
+ Avage fail oma teegist, et seda siin näha.
+ Kausta sünkroonimise seadistamine
+ Sünkrooni kaust
+ Kohalik kaust
+ Kinnitatud
+ %1$d%% täielik
+ Pole kohapeal saadaval
+ Logige sisse rakendusega Google
+ Sisse logides
+ Episteme Pro
+ Uuenda versioonile Episteme Pro
+ Sünkrooni raamatukogu
+ Pilvesünkroonimine kohalike kaustade jaoks
+ Laadige raamatud oma sünkroonitud kaustadest üles kausta Google Drive.
+ Kohandatud fondid
+ Toetage projekti
+ Abi ja tagasiside
+ Logi välja
+ Viimaste failide limiit
+ Piiramata
+ %1$d failid
+ Tühjenda raamatu vahemälu
+ Tühjendage reflow vahemälu
+ Raamatukogu
+ Otsi pealkirja või autorit…
+ Tüübid: %1$s
+ Kaustad: %1$d
+ Olek: %1$s
+ Kõik raamatud
+ Riiulid
+ Kaustad
+ Kataloogid
+ Päringule \"%1$s\" ei leitud tulemusi
+ Valige PDF, EPUB, MOBI või AZW3 alustamiseks oma seadmest faili.
+ Lisa fail
+ Uus riiul
+ Looge uus riiul
+ Riiuli nimi
+ Loo
+ Nimetage riiul ümber
+ Kustuta riiul
+ Lisage raamatuid
+ See riiul on tühi
+ Lisa %1$s
+ LISA (%1$d)
+ Pole ühtegi riiulita raamatut, mida lisada
+ Kõik raamatud on juba sellel riiulil
+ Nimetage riiul ümber
+ Kas kustutada riiul?
+ Kas soovite kindlasti kustutada faili \'%1$s\' riiul? Kõik raamatud teisaldatakse riiulitele.
+ Kas eemaldada riiulist?
+ Kustuta %1$s?
+ Kas soovite kindlasti kustutada %1$d valitud %2$s? Kõik sees olevad raamatud teisaldatakse jaotisesse Riiulita.
+ Sünkroonige kohalikud kaustad
+ Reaalajas teegi loomiseks ühendage kohalikud kaustad. Episteme jälgib faile ja sünkroonimise edenemist.
+ Lisa kaust
+ Skanni kõik
+ Skannimine…
+ Sünkrooni meta
+ VIIMANE SÜNKROON
+ RAAMATUD
+ Redigeeri filtreid
+ Eemalda kaust
+ Keela kohalik sünkroonimine
+ Luba kohalik sünkroonimine
+ Kohalik sünkroonimine on keelatud
+ Kas keelata kohaliku kausta sünkroonimine?
+ Episteme lõpetab selle kausta skannimise ja kirjutamise JSON failide sünkroonimine. Eemaldage %1$s kaust ka sellest kaustast?
+ Sünkrooni andmed
+ Eemaldage sünkroonimisandmed
+ Filtreeri failitüübid
+ Valige failitüübid, mida soovite sellest kaustast sünkroonida:
+ Filtri raamatukogu
+ Faili tüüp
+ Allikakaust
+ Loe olekut
+ Kustuta kõik
+ Rakendusesisene salvestusruum
+ Kas salvestada fail?
+ Do you want to save this external file in the app\'s library? If not, it will be removed.\n\n(You can change this default behavior anytime from the Home Screen > More Options > External File Behavior).
+ Ära\'ära küsi uuesti
+ Hoidke raamatukogus
+ Eemalda
+ Küsi iga kord
+ Hoidke alati
+ Eemalda alati
+ Välise faili käitumine
+ Lisa kataloog
+ Otsi kataloogist…
+ See voog on tühi.
+ Allalaadimine…
+ Laadimine…
+ Voog
+ Pole saadaval
+ Laadi alla
+ Laadi alla vorming
+ Voogesitage kohe
+ Lugege
+ Toetatud vorminguid pole saadaval.
+ VÄLJAANDJA
+ AVALDATUD
+ KEEL
+ Sisukokkuvõte
+ Redigeeri kataloogi
+ Lisa OPDS Kataloog
+ Kataloogi nimi
+ URL
+ Autentimine (valikuline)
+ Kasutajanimi
+ Parool
+ Kustuta kataloog
+ Kas soovite kindlasti kustutada \'%1$s\'?
+ Selle kataloogi kustutamisel eemaldatakse jäädavalt ka %1$d sellega seotud raamatute voogesitamine teie kogust.
+ Eelseadistatud
+ Tasuta plaan
+ Igavesti tasuta
+ Mitu vormingut
+ Toed PDF, EPUB, MOBI, AZW3
+ Android Tekst kõneks
+ Kuulake oma raamatuid sisseehitatud TTS
+ Põhisõnastik
+ Otsige kiiresti üles üksikud sõnad
+ Praegune plaan
+ Hinna laadimine…
+ Ühekordne makse
+ Eluaegne juurdepääs
+ Varajase juurdepääsu müük
+ Omadused:
+ Pilvesünkroonimine seadmete vahel
+ Hoidke kogu oma kogu, sealhulgas raamatufailid ja lugemised, sünkroonituna kuni neljas seadmes.
+ Kokkuvõte
+ Saate päevas 10 tasuta kokkuvõtet peatükkide või lehtede kohta
+ Nutikas sõnastik
+ Otsige fraase ja isegi lõike, mitte ainult üksikuid sõnu
+ Prioriteetsete funktsioonide taotlused
+ Teie ettepanekud seatakse prioriteediks
+ Pro funktsioonid on lukustamata!
+ Sisselogimine Nõutav
+ Ostu kinnitamine…
+ Olemasolev ost leitud
+ Hankige eluaegne juurdepääs
+ Uuendamine pole praegu saadaval. Kontrollige oma Internetti ja proovige uuesti.
+ Logige sisse oma Google konto ostmiseks Episteme Pro.
+ Logige sisse oma Google konto krediidi ostmiseks.
+ See võib võtta mõne hetke. Teie Pro staatust värskendatakse automaatselt.
+ Sellel seadmel on juba Pro-ost, kuid see\' on lingitud teise kontoga. Pro funktsioonide taastamiseks logige sisse kontole, mida kasutati algsel ostul.
+ Te\'toodate Episteme Pro meie varase juurdepääsu perioodil erisoodushinnaga! See on piiratud aja pakkumine.
+ Logige sisse oma Google konto ostmiseks Episteme Pro ja avage kõik esmaklassilised funktsioonid.
+ Mitte praegu
+ Selge!
+ Kohandatud fondid
+ Importi font
+ Google Fondid
+ Sirvi Google Fondid
+ Otsige üle 1900 fondi…
+ Populaarsed valikud
+ No fonts found matching \'%1$s\'
+ Juba alla laaditud
+ Kohandatud fonte pole
+ Importige TTF- või OTF-faile, et neid oma raamatutes kasutada.
+ Eelvaade pole saadaval (kehtetu fondifail)
+ Kas kustutada font?
+ Kas soovite kindlasti kustutada \'%1$s\'? Kui sünkroonimine on sisse lülitatud, eemaldatakse see kõigist teie seadmetest.
+ Kas kustutada fondid?
+ Kas soovite kindlasti kustutada %1$d valitud fonte? Kui sünkroonimine on sisse lülitatud, eemaldatakse need kõigist teie seadmetest.
+ Võtke ühendust
+ Kas leidsite vea, teil on funktsioonitaotlus või soovite lihtsalt tere öelda? Andke meile teada GitHubis või saatke meile e-kiri.
+ GitHubi probleemid
+ Teatage vigadest, taotlege funktsioone ja jälgige arenduse edenemist.
+ Meili tugi
+ Muude päringute korral võtke meiega otse e-posti teel ühendust.
+ Toetage projekti
+ Aidake hoida Episteme liigub
+ Teie tugi aitab mul hoida ja täiustada Episteme kõigile!!!
+ Sponsor GitHubis
+ Toetage arendust otse GitHubi sponsorite kaudu. Tänutäheks saate projekti repos README hüüdlause.
+ Liituge Patreoniga
+ Tänutäheks rakenduse toetamise eest saavad Patreoni toetajad lisasisu ja -hüvesid: pilkupüüre sellest, millega ma töötan, varasemaid ekraanipilte ja värskendusi, hääli, mis aitavad kujundada, kuidas uued funktsioonid peaksid välja nägema ja töötama, ning README-hüüde projekti repos.
+ Avage Episteme Pro
+ Seadmetevaheline sünkroonimine on Pro funktsioon. Avage kõik professionaalsed funktsioonid ühe ühekordse ostuga.
+ Uuendage
+ Kinnitage väljalogimine
+ Kas olete kindel, et soovite välja logida?
+ Seadme limiit on saavutatud
+ Kasutamiseks Episteme Pro selles seadmes eemaldage üks oma olemasolevatest registreeritud seadmetest.
+ Viimati nähtud: %1$s
+ Kinnitage hävitav tegevus
+ See kustutab jäädavalt kõik teie raamatud ja lugemise edenemine sellest seadmest JA teie seadmest Google Drive konto. Seda toimingut ei saa tagasi võtta. Oled sa kindel?
+ Tühjenda raamatu vahemälu
+ See kustutab kõik töödeldud lehed lehekülgede muutmise režiimis. See aitab lahendada paigutusprobleeme, kuid järgmisel korral tuleb raamatute avamisel uuesti töödelda.
+ Kinnita ja kustuta
+ Tühjendage reflow vahemälu
+ See kustutab kõik loodud \'Tekstivaade\' PDF-ide versioonid ja tühjendage nendega seotud pildid/HTML-i vahemälu. Teie algsed PDF-id jäävad puutumata.
+ Tagasi
+ Sõnastik
+ Rohkem valikuid
+ Lehekülje liugur
+ Sisukord
+ Tekstivorming
+ Otsi
+ AI Omadused
+ Käivitage kõneks muutmine
+ Peatage kõneks muutmine
+ Paus
+ Jätka
+ Luba tume režiim
+ Keela tume režiim
+ Lukusta panoraam
+ Avage panoraam
+ Täisekraan
+ Kuva esiletõstmised
+ Peida esiletõstmised
+ Märkuste režiim
+ Välju annotatsioonirežiimist
+ Sule otsing
+ Tühjenda otsing
+ Näita tulemusi
+ Peida tulemused
+ Eelmine tulemus
+ Järgmine tulemus
+ Väljuge lugejast ja naaske avakuvale
+ Valige sõnade otsimiseks eelistatud rakendus
+ Juurdepääs lugemisrežiimile, järjehoidjatele ja täpsematele seadetele
+ Lohistage, et hüpata kiiresti dokumendi mis tahes lehele
+ Sirvige peatükke ja navigeerige mis tahes jaotisesse
+ Reguleerige fonti, suurust, rea kõrgust, joondamist ja kohandatud fonte
+ Otsige sellest raamatust üles mis tahes sõna või fraas
+ Tehke praegusest peatükist või leheküljest kokkuvõte, kasutades AI
+ Lugege raamatut ette oma seadme\'s häälemootori abil
+ Peatage praegune ettelugemise seanss
+ Peatage praegune etteloetud taasesitus
+ Jätkake peatatud ettelugemisega taasesitust
+ Inverteerida PDF värvid tumeda režiimi jaoks
+ Keela tume režiim ja taasta originaal PDF värvid
+ Lukustage lehel horisontaalne panoraam
+ Avage panoraam, et uuesti lubada suumimiseks ja lohistamiseks kokkusurutud liigutused
+ Peitke kõik kasutajaliidese juhtnupud, et näha kaasahaaravat ja häireteta lugemisvaadet
+ Märkige visuaalselt valitud tekstipiirkonnad praegusel lehel
+ Eemaldage lehelt valitav tekstiülekate
+ Lisage tinti või tekstimärkusi
+ Lõpetage redigeerimine ja naaske tavalisse lugemisvaatesse
+ Väljuge otsingust ja minge tagasi lugeja juurde
+ Kustutage praegune otsingupäring ja alustage otsast peale
+ Laiendage paneeli, et näha kõiki otsingu vasteid
+ Ahendage otsingutulemuste paneel
+ Hüppa dokumendis eelmisele otsingu vastele
+ Hüppa dokumendis järgmise otsingu vaste juurde
+ Logi sisse
+ Valige kaust
+ Valige
+ Privaatsuspoliitika • Kasutustingimused • Litsentsid
+ Teie seade\' ei toeta kaustade valikut. Saate endiselt faile ükshaaval importida.
+ Failihaldurit ei leitud. Installige failihalduri rakendus.
+ Allalaaditud %1$s
+ %1$s: %2$s
+ Mitte kunagi
+ %1$s: %2$d
+ Eemaldatud %1$d voogesituse raamatud.
+ Fondi importimine ebaõnnestus: %1$s
+ Tekstivaade kustutatud.
+ Sünkroonimiseks on vaja Google Drive luba.
+ Ostmisel ilmnes viga.
+ Uuendamine õnnestus! Tere tulemast Pro-sse.
+ Ostu kinnitamine ebaõnnestus. Kui teilt võeti tasu, võtke ühendust klienditoega.
+ See seade eemaldati teie kontolt.
+ Seda seadet ei saanud kinnitada. Palun kontrollige oma ühendust.
+ Seadmete värskendamine ebaõnnestus. Palun proovi uuesti.
+ Säästmine PDF…
+ PDF edukalt salvestatud.
+ Faili avamine salvestamiseks ebaõnnestus.
+ Viga salvestamisel PDF: %1$s
+ Originaali salvestamine PDF…
+ Originaal PDF edukalt salvestatud.
+ Jagamine: %1$s
+ Jaga PDF
+ Jagamine ebaõnnestus: %1$s
+ Limiit saavutatud: maksimaalne %1$d kaustad lubatud.
+ See kaust on juba sünkroonitud.
+ Lisatud kaust: %1$s
+ Juurdepääs kausta lubadele ebaõnnestus.
+ Kaust eemaldatud.
+ Kaustade sünkroonimine: metaandmete värskendamine…
+ Kausta skannimine uute raamatute jaoks…
+ Kausta sünkroonimine: skannimine on lõpetatud.
+ Sünkroonimine ebaõnnestus.
+ Üheski kohalikus kaustas pole sünkroonimine lubatud.
+ Kohaliku kausta sünkroonimine on keelatud.
+ Kohaliku kausta sünkroonimine on keelatud. Sünkroonimisandmete kaust on eemaldatud.
+ Kohaliku kausta sünkroonimine on keelatud, kuid sünkroonimisandmete kausta ei saanud eemaldada.
+ Kohaliku kausta sünkroonimine on lubatud.
+ Failide allalaadimiseks lubage sünkroonimine.
+ Allalaadimine ebaõnnestus %1$s.
+ Pilveandmete kustutamiseks lubage sünkroonimine.
+ Pole sisse logitud, ei saa pilvandmeid kustutada.
+ Kõigi pilve- ja kohalike andmete kustutamine…
+ Kõik pilve- ja kohalikud andmed kustutati edukalt.
+ Viga: kõigi andmete kustutamine ebaõnnestus.
+ Kõik kohalikud andmed kustutati.
+ Sisselogimine ebaõnnestus. Palun proovi uuesti.
+ Ei leitud Google konto. See võib juhtuda värske installi korral, proovige mõne aja pärast uuesti.
+ Sisselogimisel ilmnes viga. Kontrollige oma Interneti-ühendust.
+ Seadmehalduse testimiseks logige sisse.
+ Sünkroonimine on Episteme Pro funktsiooni.
+ Pole sisse logitud, ei saa sünkroonida.
+ Pilvesünkroonimine: värskenduste otsimine…
+ Pilvesünkroonimine: lõpetatud.
+ Teegi sünkroonimine ebaõnnestus.
+ Viimast üksust ei leitud.
+ Faili importimine ebaõnnestus.
+ Faili asukohta ei leitud.
+ Seda failitüüpi ei toetata.
+ Loodud tekstivaate laadimine ebaõnnestus.
+ Tekstivaate genereerimine ebaõnnestus.
+ FB2 laadimine ebaõnnestus: %1$s
+ Faili laadimine ebaõnnestus: %1$s
+ Laadimine ebaõnnestus MOBI: %1$s
+ Laadimine ebaõnnestus EPUB: %1$s
+ Fail kustutati kaustast. Teegist eemaldatud.
+ Selle nimega riiul on juba olemas.
+ Kustutamine kõigist seadmetest…
+ Kustutamine lõpetatud.
+ Pilvesünkroonimine ebaõnnestus, kustutati kohapeal.
+ Vahemälu ja loodud tekstivaated on kustutatud.
+ Otsi raamatust…
+ Tulemusi ei leitud.
+ Kokkuvõtte genereerimine…
+ Peatus
+ Lugege ette
+ Kopeeri
+ Kopeeri lõim
+ Kokkuvõtet ei saanud luua.
+ Mõeldes…
+ Ava sõnastikurakenduses
+ AI ei osanud definitsiooni anda.
+ Küsib AI umbes \'%1$s\'…
+ Kõnesünteesi seaded
+ Seadete muutmiseks peatage taasesitus.
+ Sünteesirežiim
+ Seadmes
+ Pilv (HQ)
+ Hääle valik
+ Esita näidis
+ Seadme hääleseaded
+ Sulgege seaded
+ Süsteemi vaikeseade
+ Vastab teie Android süsteemi seaded
+ Valitud
+ Häälte laadimine…
+ Selles seadmes pole hääli saadaval.
+ Konkreetsed hääled
+ Saadaolevad hääled (%1$d)
+ Selle keele jaoks ei leitud ühtegi häält.
+ Variant: %1$s
+ See on näidis %1$s.
+ Lugemise teemad
+ Eelseaded
+ Tekstuuriga eelseaded
+ Minu teemad
+ Minu tekstureeritud teemad
+ Kohandatud teemasid veel pole. Puudutage \'+\' ühe loomiseks.
+ Kohandatud tekstuuriga teemasid pole veel.
+ Uus teema
+ Uus tekstureeritud teema
+ Redigeeri teemat
+ Teema nimi
+ Nii palju raamatuid, nii vähe aega.
+ ⚠️ Madal kontrast! See võib põhjustada silmade väsimust.
+ Lehekülje värv
+ Teksti värv
+ Tekstuur
+ Mitte ühtegi
+ Laadi üles
+ Tekstuuri läbipaistvus
+ Reaalajas eelvaade
+ Lugemine on unistamine.
+ Tekst on tühi.
+ AI tagastas tühja definitsiooni.
+ Ei saanud definitsiooni.
+ Ilmnes tundmatu serveriviga.
+ Võrgu viga. Kontrollige ühendust.
+ Kokkuvõtteks ei piisa kontekstist.
+ Kokkuvõtte sõelumine ebaõnnestus.
+ Võrguviga kokkuvõtte loomisel.
+ Otsi seaded
+ Sõnaraamat Mootor
+ Nutikas (AI)
+ Väline rakendus
+ Kasutab AI määratluste jaoks. Võrguühenduseta või kui valitud fraas on liiga pikk, naaseb allolevasse välisrakendusse.
+ Kasutab valitud rakendust sõnastikust otsimiseks.
+ Varurakendus
+ Sõnastiku rakendus
+ Valige rakendus
+ Tõlgi
+ Rakendus, mida kasutatakse valitud teksti tõlkimiseks.
+ Otsi rakendust
+ Rakendus, mida kasutatakse veebiotsinguteks.
+ Mitte ühtegi
+ Raamatu sisu on tühi.
+ Serveri vastuse kokkuvõtte sõelumine ebaõnnestus.
+ Kokkuvõtet ei õnnestunud tuua.
+ Viga: %1$d. %2$s
+ Võrgu viga. Kontrollige ühendust ja serveri olekut.
+ Peatüki analüüsimine %1$d…
+ Praeguse asukoha lugemine…
+ Kokkuvõtte genereerimine…
+ Peatüki kokkuvõte
+ Loo kokkuvõte (beeta)
+ Avage peatüki kokkuvõte
+ Saate mis tahes peatüki lühikokkuvõtteid kasutades Episteme Pro. Selle funktsiooni kasutamise alustamiseks uuendage.
+ Lisateave
+ Avage nutikas sõnaraamat
+ Tervete fraaside ja lõikude määratlemine kuni 2000 tähemärgini on Pro funktsioon. Täiendage, et saada mis tahes valitud teksti jaoks kohesed määratlused.
+ Järjehoidja
+ Valitud pesa
+ Kohandage palett
+ Puudutage muutmiseks pesa:
+ Valige pesa värv:
+ See peatükk on tühi.
+ Peatükki ei leitud
+ Viga peatüki laadimisel
+ Sõnastiku sätted
+ Teema
+ Teema seaded
+ Rohkem valikuid
+ Vaata originaali PDF
+ Kustuta tekstivaade
+ Vertikaalne
+ Vertikaalne (veebivaade)
+ Vertikaalne (native beeta)
+ Leheküljed (vasakult paremale)
+ Lubatud
+ Eemalda järjehoidja
+ Lisa see leht järjehoidjatesse
+ Lehekülgede pööramiseks puudutage
+ Helitugevuse nupu kerimine
+ Helitugevuse nupp Lehekülje pööramine
+ Realistlikud leheküljepöörded
+ Hoidke ekraan sees
+ Visuaalsed valikud
+ Ekraani suund
+ Muutke lugemisrežiimi
+ Leheküljed (paremalt vasakule)
+ Automaatne kerimine
+ TTS Seaded
+ TTS Hääleseaded
+ TTS Sõnade asendused
+ Sõnaasenduste raamat
+ Jagage, salvestage või printige
+ TTS Seaded (silumine)
+ Liikuge liuguriga
+ Peatükkide menüü
+ Teksti vormindamine
+ Peatükk Kokkuvõte
+ Kokkuvõte (beeta)
+ Peatus TTS
+ Alusta TTS
+ Paus TTS
+ Jätka TTS
+ Eelmine TTS tükk
+ Järgmine TTS tükk
+ Välju liuguriga navigeerimisest
+ Avalehe pisipilt
+ Laienda
+ Ahenda
+ Mängi
+ Kohalik kiirus
+ Globaalne kiirus
+ Valige Režiim
+ Kehtib kõikidele failidele
+ Salvestatud ainult selle faili jaoks
+ Keela muusiku režiim
+ Lubage muusiku režiim
+ Vahetage juhtnuppe
+ Min
+ Max
+ Aeglasem
+ Kiiremini
+ Vähendada
+ Suurendada
+ Lehekülg %1$d of %2$d
+ Peatükid
+ Vahekaardid
+ Järjehoidjad
+ Esiletõstmised
+ Leheküljed
+ Pildid
+ Laienda kõik
+ Ahenda kõik
+ Otsige üles
+ You haven\'t added any bookmarks yet.
+ Pilte ei leitud.
+ Laadige pilt alla
+ Rohkem valikuid järjehoidja jaoks
+ Nimeta järjehoidja ümber
+ Uus nimi
+ Uus pealkiri
+ Kas kustutada järjehoidja?
+ Kas olete kindel, et soovite selle järjehoidja jäädavalt kustutada?
+ Esiletõsteid veel pole.
+ Tundmatu peatükk
+ Valikud
+ Kas kustutada esiletõst?
+ Kas olete kindel, et soovite selle esiletõstmise jäädavalt kustutada?
+ Salvestatud %1$s
+ Pilti ei saanud salvestada.
+ Originaal PDF ei leitud.
+ Viga: raamatu sisu ei leitud. Tee: %1$s
+ Valige esmalt sõnastikurakendus.
+ Valige esmalt tõlkerakendus.
+ Valige esmalt otsingurakendus.
+ Selle raamatu jaoks pole peatükke saadaval.
+ Navigeerimine asukohta…
+ Nõutav luba
+ Taasesituse juhtnuppude kuvamiseks, kui rakendus töötab taustal, andke teatise luba.
+ Jätka
+ Põhjendatud teksti piirang
+ Põhjendatud joonduse kasutamine leheküljelises režiimis võib küljenduse piirangute tõttu muuta teksti valiku ja esiletõstmised ebatäpseks.
+ ma saan aru
+ Peatükki navigeerimine…
+ Valige esmalt võrguühenduseta sõnastik.
+ Raamat pole veel lehekülgedega varustatud.
+ Oodake, kuni raamat on täielikult laaditud.
+ Eelmise peatüki väljalase
+ Väljalase järgmise peatüki jaoks
+ Tõmmake edasi… (%1$d%%)
+ Peatükk
+ Peatüki sisu ei õnnestunud hankida.
+ Praegust peatükki ei õnnestunud määrata.
+ WebView pole saadaval.
+ Lehekülg %1$d/%2$d
+ Kohalik formaat
+ Globaalne formaat
+ Lähtesta
+ Suurus
+ Vahekaugus
+ Valige Font
+ Eelseaded
+ Imporditud
+ Import failidest
+ Pole veel imporditud fonte.
+ Visuaalsed valikud
+ Lehekülje paigutus
+ PDF leht laiali
+ Üks leht
+ Kaks lehte
+ Esimene leht üksi
+ Alustab esikülje laialivalgumist pärast kaanelehte.
+ Eemaldage lehtede vahe
+ Kehtib vertikaalsel lugemisel ja kaheleheküljelistel laialitel.
+ Peida lehenumbri ülekate
+ Eemaldab igalt lehelt väikese lehekülgede arvu sildi.
+ Süsteemi kasutajaliides (oleku- ja navigeerimisribad)
+ Kontrollige seadme\'-süsteemi ribade nähtavust.
+ Ekraani suund
+ Valige, kas lugeja järgib süsteemi orientatsiooni või eelistab vertikaalset või horisontaalset, kui Android lubab seda.
+ Edenemisriba
+ Lugemise edenemise ja peatüki indikaator lugemisekraanil.
+ positsioon
+ Peatükkide sujuv üleminek
+ Laadige lõpust mööda kerides kohe järgmine/eelmine peatükk, ilma tõmmake värskendamiseks animatsioonita.
+ Eemaldage serva polsterdus
+ Eemaldab horisontaalse vahe vasakust ja paremast servast.
+ Heledus
+ Kasutage süsteemi heledust
+ Järgib seadme heleduse seadistust.
+ Kohandatud heledus
+ Kehtib, kui lugejaekraan on avatud.
+ %1$d%%
+ Otsi
+ Viga sõnastiku avamisel
+ Tõlkerakenduse avamisel tekkis viga
+ Viga otsingurakenduse avamisel
+ Avatud lähtekoodiga versioon
+ Playstore\'i versioon
+ Versioon %1$s
+ Ehitamine %1$s
+ Sirvige lähtekoodi, tärniga, kahvliga ja teatage probleemidest.
+ Kuidas me teie andmeid käsitleme.
+ Kasutustingimused.
+ Kasutatud avatud lähtekoodiga teegid.
+ Importimine %1$d raamatud… Need ilmuvad peagi teie kogusse.
+ Loodi riiul "%1$s".
+ Loodi nutikas riiul "%1$s".
+ Riiul nimetati ümber "%1$s".
+ Kustutatud riiul "%1$s".
+ Uuendatud "%1$s".
+ Need failid on juba raamatukogus.
+ %1$s - %2$s
+ Väline link
+ You clicked on an external link:\n\n%1$s\n\nWhat would you like to do?
+ Avatud
+ Lingi avamiseks ei leitud ühtegi brauserit.
+ Salvesta märkus
+ Salvesta
+ Salvesta kommentaar
+ Lisa kommentaar
+ Vasta
+ Lisa märge…
+ Lisa kommentaar…
+ Dikt
+ Rääkige
+ Märkus
+ Kommentaarid
+ Kommentaari redigeerimine
+ Vastamine %1$s
+ Muuda
+ FONT JA JOONDAMINE
+ PAIGUTUS JA VAHUMUS
+ Fondi suurus
+ Joone kõrgus
+ Lõigu lünk
+ Pildi suurus
+ Horisontaalne marginaal
+ Vertikaalne veeris
+ Mitte ühtegi
+ Orig
+ Hääle reguleerimine
+ Kiirus (%1$sx)
+ Kõrgus (%1$sx)
+ Nii kõlavad teie praegused hääleseaded.
+ Peata raamat
+ Jätkamise raamat
+ Süsteemi hääle/mootori sätted
+ Globaalne kiirus
+ Kohalik kiirus
+ Kehtib kõikidele failidele
+ Salvestatud ainult selle faili jaoks
+ Kerige üles
+ Kohandage tööriistariba
+ Valige tööriistad, mida soovite nähtavana hoida. Tööriista märke tühistamine peidab selle kasutajaliidese eest, et anda teile tähelepanu kõrvalejuhtimiseta lugemisruumi.
+ Märkused
+ Kõik
+ Märkmetega
+ Lisa märkus
+ Redigeeri märkust
+ %1$d / %2$d
+ Lehekülg %1$d of %2$d
+ Sulgege redigeerimisrežiim
+ Lülitage nähtavus sisse
+ Ainult pliiatsi režiim
+ Pliiats
+ Esiletõstja
+ Tekst
+ Kustutuskumm
+ Võta tagasi
+ Tee uuesti
+ Näita dokki
+ Valige Fontide perekond
+ Valige Fondi suurus
+ Fondi taust
+ Paks
+ Kursiiv
+ Allajoonimine
+ Läbikriipsutatud
+ Sisesta tekstikast
+ OCR valiku viga: %1$s
+ Valiku viga: %1$s
+ Viga lehe töötlemisel: %1$s
+ Lehte %1$d ei saa kuvada.
+ Prindisätteid ei saanud avada
+ Laadimine PDF…
+ Viga laadimisel PDF
+ Lehe laadimine…
+ PDF Vaataja
+ Pliiatsi mänguväljak
+ Impordi SVG
+ Imporditud %1$d SVG lööki!
+ SVG importimine ebaõnnestus või tühi.
+ OCR Keel
+ Sisesta tühi leht
+ Kustuta leht
+ Tekib… %1$d%%
+ Avage tekstivaade
+ Loo tekstivaade
+ Jaga
+ Salvesta koopia seadmesse
+ Prindi
+ Tekstivaate genereerimine…
+ Lehtede indekseerimine… %1$d%% tehtud. Otsingutulemused värskendatakse automaatselt.
+ Tulemused leitud %1$d+ lehekülgedel
+ Tulemus %1$d / %2$d
+ %1$d+ Lehekülgi
+ Lehe kokkuvõte (lehekülg %1$d)
+ Allalaadimine %1$s keelepakett…
+ Valige OCR Keel
+ Paremate tekstituvastustulemuste saamiseks valige selle dokumendi esmane keel/skript.
+ Saate seda hiljem muuta jaotises Rohkem valikuid > OCR Keel.
+ Kas dokument uuesti indekseerida?
+ You are changing the OCR script to %1$s.\n\nTo ensure search accuracy, we need to clear the existing index and re-scan pages that require OCR using this new language.\n\nThis will happen in the background.
+ Indekseeri uuesti
+ Parooliga kaitstud
+ See dokument on krüpteeritud. Selle vaatamiseks sisestage parool.
+ Parool
+ Vale parool
+ Peida parool
+ Näita parooli
+ You are about to navigate to:\n%1$s
+ Külastage
+ Salvesta seadmesse
+ Valige salvestamiseks vorming:
+ Koos märkustega
+ Originaal
+ Valige jagamiseks vorming:
+ Ettevalmistus PDF…
+ Lisa PDF vahekaardile
+ Teisi PDF-e teie teegist ei leitud.
+ PDF on tühi või seda ei saa kuvada.
+ Leht lisatud aadressil %1$d
+ Leht kustutatud
+ Lisaleht eemaldatud
+ Peatükid pole selle raamatu jaoks saadaval.
+ Esiletõstetud jaotis
+ Sirge joon
+ Spekter
+ Valmis
+ Punane
+ Roheline
+ Sinine
+ Süsteemi vaikefont
+ Import
+ Ühtegi fonti ei imporditud
+ Fondi värv
+ Tõstke esile
+ Mitme vahekaardi lugemise lubamine
+ Kasutage ranget failifiltrit
+ Kasutage PDF Failinimed
+ Keel
+ Testpaneeli ML tuvastamine
+ Testige kõnemulli ML tuvastamist
+ Ekspordi logid (viimased %1$d read)
+ Luba range failifilter
+ If you enable this, some supported file types like AZW3, CB7, and FB2 might not show up depending on your file manager.\n\nAre you sure you want to enable this filter?
+ Süsteemi vaikeseade
+ inglise keel
+ inglise keel (vaikimisi)
+ العربية (araabia)
+ saksa (saksa)
+ türkçe (türgi)
+ Français (prantsuse)
+ Русский (vene)
+ Беларуская (valgevene keel)
+ español (hispaania)
+ portugali keel (Brasiilia)
+ itaalia keel (itaalia)
+ polski (poola)
+ Tiếng Việt (vietnami)
+ 日本語 (jaapani keel)
+ 한국어 (korea)
+ हिन्दी (hindi)
+ 简体中文 (hiina, lihtsustatud)
+ Rakenduse teema
+ Välimus
+ Kontrast
+ Teksti heledus
+ Värviskeem
+ Dünaamiline
+ Loo rakenduse teema
+ Kohandatud teema
+ Ookean
+ Mint
+ Roos
+ Seepia
+ Ametüst
+ Merevaik
+ Safiir
+ Lisa kohandatud teema
+ Rakenduse teema
+ Rakenduse ikoon
+ Seade
+ Avage sahtel
+ Profiilipilt
+ Profiil
+ Pro funktsioon
+ Filter
+ Sorteeri
+ Sule otsing
+ Kustuta päring
+ Otsi riiul
+ %1$s riiuli kate
+ Säilitage pildi värvid
+ Teema muutumisel säilitage pildi algsed värvid
+ Riiulita
+ Kõik raamatud
+ Süsteem
+ Valgus
+ Tume
+ Standardne
+ Keskmine
+ Kõrge
+ Viimased
+ Pealkiri A-Z
+ Autor A-Z
+ Valmisoleku protsent 0–100
+ Valmisoleku protsent 100–0
+ Suurus (väikseim)
+ Suurus (suurim)
+ Kõik
+ Lugemata
+ Käimas
+ Lõpetatud
+ Sildid: %1$s
+ Sirvige sildi järgi
+ Sildid
+ Kaustad
+ Failid
+ Tekst kõneks
+ Taasesituse juhtnupud teksti kõneks muutmiseks.
+ Teksti kõneks ettevalmistamine
+ Ettevalmistus: %1$s
+ Aktiivne TTS Mootor
+ Pilv AI
+ Seadme algseade
+ Pilve hääled
+ Seadme hääled
+ Pilve vahemälu
+ Valige Kvaliteetne pilvehääl
+ Puhasta proovid
+ Süsteemi vaikehääl
+ Kasutab seadme sätteid
+ Keelefilter
+ Internetis
+ Võrguühenduseta
+ Häälefilter
+ Selle hääle jaoks pole vahemällu salvestatud heli.
+ Tühjenda vahemälu %1$s
+ See on häälenäidis.
+ AI Omadused
+ Kokkuvõte
+ Kokkuvõte
+ Vahemälu
+ Puudub kokkuvõte %1$s veel.
+ Loo kokkuvõte: %1$s
+ Tehke loo kokkuvõte kuni oma praeguse positsioonini.
+ Loo kokkuvõte
+ Loo kokkuvõte
+ Vahemälu tabamus • Tasuta
+ Loodud • Tasuta (%1$d/10 jäänud)
+ Tekitatud • Kulud: %1$s krediiti
+ Loomine… • Maksumus: arvutamine
+ AI Väljund
+ Regenereerida
+ Selle raamatu kohta pole vahemällu salvestatud kokkuvõtteid.
+ Krediidid
+ AI & Cloud Credits
+ Krediidid on saadaval
+ %1$d Krediidid
+ Hinnanguline kulude jaotus
+ Pilv TTS
+ Cost: ~3–4 credits per minute of audio generated.\nTo enable: Reader Screen > More > TTS Voice Settings.
+ AI Kokkuvõtted ja kokkuvõte
+ Cost: ~1–4 credits per request based on chapter length.\nPro Users get 10 free summaries daily.
+ Ostes,
+ Krediidid otsas
+ Teil ei ole piisavalt krediiti\' Hangi Episteme Pro 10 tasuta kokkuvõtet päevas või lisage krediiti, et kasutada kokkuvõtteid, Cloud TTS ja loo kokkuvõte.
+ Hankige Pro / lisage krediiti
+ Avage lehe kokkuvõte
+ Hankige täpseid kokkuvõtteid mis tahes lehekülje kohta, millel on Episteme Pro. Selle funktsiooni kasutamise alustamiseks uuendage.
+ Laadige alla Bubble Zoom mudel
+ Funktsiooni Bubble Zoom kasutamiseks kasutage AI mudel tuleb alla laadida (~134 MB). Kas soovite selle kohe alla laadida?
+ Tõlgi
+ Tagasi lk %1$d
+ Lehekülg %1$d
+ Tulemus %1$d / %2$d
+ Laadimine ebaõnnestus PDF.
+ Bubble Zoom mudeli allalaadimine… %1$d%%
+ Välju liuguriga navigeerimisest
+ Hüppa tagasi
+ Hüppa edasi
+ Kerige lugemislehele
+ Märkustega leht
+ Sule pilt
+ Otsingu esiletõstude sisse- ja väljalülitamine
+ Tekstikasti teisaldamiseks lohistage
+ Failide ikoon puudub
+ Kopeeri %1$s
+ Tag
+ Loendi üksuse marker
+ Lähtestage suum
+ Loo demomärkusi
+ Demo annotatsioonid
+ Avage pliiatsi mänguväljak
+ Uus vaheleht
+ Tõstke esile kogu tekst
+ Lülitage redigeerimisrežiim sisse
+ Nutikas koomiksisuum
+ Esiletõstete kohandamine
+ inglise, hispaania, prantsuse jne.
+ hindi, marati, sanskriti + inglise keel
+ hiina + inglise keel
+ jaapani + inglise keel
+ Korea + inglise keel
+ Leht pole saadaval
+ Dokument
+ Loodud
+ %1$s (Tekstivaade)
+ %1$s (Taasvoolamine)
+ Lisa / Muuda
+ Silte pole määratud.
+ Rakenda sildid
+ Otsige või looge silt…
+ Loo \"%1$s\"
+ Peatüki muutmiseks tõmmake kaugust
+ Lühike
+ Pikk
+ Kiirus: %1$sx
+ Kõrgus: %1$sx
+ Esita/Paus
+ Lähtestage kiirus
+ Lähtesta helikõrgus
+ Valige Värv
+ Sellel raamatul pole kuvatavat sisu.
+ Kopeeritud link
+ Kopeeritud tekst
+ Sisukord
+ Järjehoidja
+ Hüppa tagasi lehele %1$d
+ Tagasi eelmisele lehele
+ Välju Smart Zoom
+ Nutikas koomiksisuum
+ Lülitage nutikas koomiksisuum sisse
+ Ekraani jäädvustamise kaitse
+ Ekraani jäädvustamise kaitse on sisse lülitatud
+ Ekraani jäädvustamise kaitse on välja lülitatud
+ Seaded
+ Muuda
+ Taasta
+ %1$s valitud
+ Lugeja vaikeseaded
+ PDF-spetsiifiline OCR, annotatsioon ja tööriista seaded jäävad PDF lugeja.
+ AI Definitsioon
+ Peatükk %1$d
+ Asukoht
+ Kohandatud font
+ Ilmnes viga: %1$s
+ Viga dokumendi laadimisel: %1$s
+ OCR sellelt lehelt teksti ei leitud.
+ Leht näib olevat tühi või teksti ei saa välja tõmmata.
+ Tühja lehe kokkuvõtet ei saa teha.
+ Dokumenti ei laaditud.
+ AI funktsioonid pole võrguühenduseta saadaval OSS ehitada.
+ Turvakaalutlustel blokeeritud.
+ Valige mudel %1$s aastal AI võtme ja mudeli sätted.
+ Lisage a %1$s API sisestage AI võtme ja mudeli sätted.
+ AI pakkuja andis tühja vastuse.
+ AI pakkuja viga: %1$d. %2$s
+ See kokkuvõte vajab Gemini mudelit, kuna valitud Groqi mudelid ei toeta PDF/pildisisendit.
+ Tagasiside kasutamiseks peate olema sisse logitud.
+ Tagasiside saatmiseks peate olema sisse logitud.
+ Piletite jaoks on lubatud kuni 3 pilti.
+ Ühe sõnumi kohta on lubatud kuni 5 pilti.
+ Üks või mitu pilti ületavad 5 MB piirangut.
+ Pileti loomine ebaõnnestus: %1$s
+ Saatmine ebaõnnestus: %1$s
+ Voo laadimine ebaõnnestus: %1$s
+ Tühi keha
+ Allalaadimine ebaõnnestus: %1$s
+ Allalaadimisviga: %1$s
+ Ost ebaõnnestus: %1$s
+ Arveldusteenusega ei õnnestunud ühendust luua.
+ Tooteid ei leitud.
+ Toodete päringu esitamine ebaõnnestus
+ Pole saadaval avatud lähtekoodiga versioonis
+ Teksti pole lugeda.
+ Viga taasesituse alustamisel.
+ Heli laadimine ebaõnnestus.
+ Taasesituse viga: %1$s
+ Pilv TTS ei ole konfigureeritud.
+ Tundmatu raamat
+ AI võtmed ja mudelid
+ Salvestatud võtmed
+ Lisa või asenda võti
+ Pakkuja
+ API võti
+ Salvesta võti
+ Kasutage kõigi funktsioonide jaoks ühte mudelit
+ Kui see on välja lülitatud, siis iga lugeja AI funktsioon kasutab oma valitud mudelit.
+ Kõik AI funktsioonid
+ Seda mudelit kasutavad nutikas sõnastik, kokkuvõtted ja kokkuvõtted.
+ Nutikas sõnastik
+ Kasutatakse valitud sõnade või fraaside määratlemisel.
+ Kokkuvõtted
+ Kasutatud EPUB kokkuvõtted ja PDF lehe kokkuvõtted. PDF/vajavad pildikokkuvõtted Gemini.
+ Kokkuvõtted
+ Kasutatakse lugude kokkuvõtete genereerimiseks.
+ Kasutab salvestatud Gemini võti. Ainult %1$s on praegu toetatud.
+ Salvesta %1$s võti?
+ Pärast salvestamist on nähtavad ainult esimesed 3 ja 3 viimast tähemärki. Kui soovite seda hiljem muuta, asendage see või kustutage see.
+ Kustuta %1$s võti?
+ Seda teenusepakkujat kasutavad funktsioonid lakkavad töötamast kuni uue võtme salvestamiseni.
+ Võti pole salvestatud
+ Kustuta %1$s võti
+ Mudel
+ Ühtegi mudelit pole valitud
+ Näita AI lugejas
+ Peida AI lugejas
+ Ühevärvilised
+ Tekstuuriga
+ Kohandatud tahke
+ Kohandatud tekstuuriga
+ Valige Kohandatud tekstuur
+ Teksti heledus (hele)
+ Teksti heledus (tume)
+ Vaikimisi
+ Vasakule
+ Õige
+ Põhjenda
+ Näita alati
+ Sünkrooni menüüdega
+ Peida alati
+ Üles
+ Altpoolt
+ Kas taastada algsed metaandmed?
+ See kirjutab algse pealkirja, autori, sarja ja kokkuvõtte tagasi EPUB faili. Lugemise edenemine, sildid ja märkmed ei muutu.
+ EPUB metaandmeid muudetud
+ Metaandmed saidilt EPUB faili
+ Rakenduses kuvatav nimi muudetud
+ Metaandmed failist
+ Metaandmed
+ Fail
+ Pealkiri
+ seeria
+ Lugemine
+ Faili nimi
+ Muudetud
+ Kokkuvõte
+ Raamatukogu sildid
+ Redigeeritavad metaandmed
+ Kuvatav nimi
+ Lugejas kuvatud nimi
+ Algne fail: %1$s
+ Ülemine riba
+ Alumine riba
+ Peidetud tööriistad
+ Rohkem menüüd
+ Varjatud tööriistad
+ Pange tööriistad siia
+ Lohistage ümberjärjestamiseks
+ Välised rakendused
+ Navigeerimisliugur
+ Heledus
+ Külgriba
+ Tõstke esile valitav tekst
+ Redigeerimisrežiim
+ TTS Juhtnupud
+ Lugemisrežiim
+ Lehekülje haldamine
+ Tekstivaade (ümbervoolamine)
+ Praegune raamat
+ Globaalne
+ See raamat
+ Luba asendused
+ Siinsed reeglid kehtivad iga raamatu kohta, välja arvatud juhul, kui need on teatud pealkirja puhul keelatud.
+ Lisa reegel
+ Lisa raamatu reegel
+ Globaalseid asendusreegleid pole veel.
+ Raamatupõhiseid reegleid veel pole.
+ Kasutage siin globaalseid reegleid
+ Lülitage see välja, kui raamat vajab oma hääldusvalikuid.
+ Luba raamatureeglid
+ Kohalikud reeglid järgivad globaalseid reegleid.
+ Päritud globaalsed reeglid
+ Pärimiseks pole globaalseid reegleid.
+ Selles raamatus lubatud
+ Selle raamatu jaoks keelatud
+ Ettepanekud
+ Uus asendus
+ Redigeeri asendust
+ Asenda
+ Räägi nagu
+ Lubatud
+ Terve sõna
+ Matši juhtum
+ Sisestuse eelvaade
+ Reeglid
+ vaikus
+ Lihttekst
+ tõstutundlik
+ Praegune raamat
+ Lisa reegel
+ Selle raamatu jaoks pole veel asendusreegleid.
+ Uus asendus
+ Redigeeri asendust
+ Koos
+ tühi tekst
+ Holland (hollandi)
+ Українська (ukraina)
+ indoneesia (indoneesia)
+ Umbes
+ Lauaarvuti lugeja
+ Juurdepääs töölauale
+ konto
+ Konto ja krediidid
+ Konto ülevaade
+ AI jaotur
+ Kasutatud EPUB kokkuvõtted ja PDF lehe kokkuvõtted.
+ Episteme oss
+ Autortekst
+ Vahemälu: %1$s
+ Vahemällu salvestatud
+ Vahemällu salvestatud kokkuvõte
+ Valige Gemini hääl, mida kasutatakse pilve ettelugemiseks.
+ Kustutage loodud töölauaraamat ja EPUB lehekülgede vahemälu failid? Järgmisel raamatute avamisel luuakse need uuesti.
+ Tühjendage hääle vahemälu
+ Sulgege tööriistad
+ Pilvesünkroonimine
+ Pilv TTS vajab Gemini
+ Pilv TTS vajab sisselogitud krediiti
+ Pilv TTS valmis
+ Pilv TTS seaded
+ Pilv TTS kättesaamatu
+ Pilv TTS hääl
+ Sisaldab
+ Kulude arvutamine
+ Tehke kokkuvõte kuni oma praeguse positsioonini.
+ Loo nutikas riiul
+ %1$d krediiti saadaval
+ %1$s krediiti
+ Imporditud fondid lugeja jaoks
+ Kustuta font
+ Kustuta %1$s? Seda kasutavad raamatud naasevad vaikefondile.
+ Kas kustutada \"%1$s\"? Raamatud jäävad teie raamatukogusse.
+ Kustuta kokkuvõte
+ Keelatud
+ Pukseerige failid importimiseks
+ Eemaldage importimiseks toetatud failid
+ Kui soovite midagi muud, võtke meiega otse e-posti teel ühendust.
+ Võrdub
+ Lisad
+ Tagasiside
+ Väli
+ Kausta tee
+ Siit
+ Täielik skannimine
+ Tasuta, %1$d vasakule
+ Loo kokkuvõte
+ Loo kokkuvõte
+ Teatage vigadest, taotlege funktsioone või võtke otse ühendust toega.
+ GitHubi sponsorid
+ Toetage arengut GitHubi sponsorite kaudu.
+ Google sisselogimine pole selle töölauajärgu jaoks konfigureeritud.
+ Suurem kui
+ Abi
+ Veaaruanded, funktsioonitaotlused ja tugi
+ Peida
+ Importige faile
+ Probleemid
+ Avage probleemide jälgija vigade ja funktsioonitaotluste jaoks.
+ Vähem kui
+ Raamatukogu ja lugeja
+ Ükskõik milline
+ Raamatukogu tegevused
+ Rohkem
+ Selle raamatu kohta pole veel vahemällu salvestatud kokkuvõtteid.
+ Importige TTF-, OTF- või WOFF2-faile, et neid raamatutes kasutada.
+ Ei leitud fonte, mis vastavad \"%1$s\"
+ Nr Google konto on ühendatud.
+ Selle jaotise kohta pole vahemällu salvestatud kokkuvõtet.
+ Võrguühenduseta lauaarvuti lugeja
+ Avatud lugejad
+ Avamine %1$s
+ Teie raamatukogu avamine
+ Operaator
+ Lehekülg
+ Parooliga kaitstud PDF
+ Patreon
+ Toetage projekti Patreonis.
+ Peatatud
+ %1$s nõuab enne avamist parooli.
+ Parool on nõutav või vale.
+ See parool ei avanenud %1$s. Sisestage PDF parool ja proovige uuesti.
+ protsenti
+ Plaan
+ Heli ettevalmistamine
+ Eelistused
+ Konto ja krediidid
+ Konto ja krediidid
+ Pro ei ole selle konto jaoks avatud.
+ Pro and credits can only be purchased from the Android rakendus. Desktop checks the same signed-in account and uses those credits for cloud TTS, summaries, recaps, and other paid AI funktsioonid.
+ Sign in to check your account status on desktop.
+ Pro on selle konto jaoks lukustamata.
+ Edusammud
+ Projekt
+ Lugeja
+ Lugeja vahelehed välja lülitatud
+ Lugeja vahelehed sisse lülitatud
+ Värskenda
+ Teeki lisamiseks vabastage.
+ Secure key storage is unavailable on this operating system. Keys entered here will be used for this session but will not be persisted.
+ Seadete keskus
+ Vastab Android hide toggle for smart dictionary, summaries, and recaps.
+ Sünkrooni konto, Pro ja krediit
+ Sisse logitud
+ Lähtekood
+ Sirvige projekti allikat GitHubis.
+ Stop reading to change voices.
+ Toetus
+ Tugi Episteme
+ Contributions help keep the reader improving across Android ja töölaud.
+ Toetamise viisid Episteme arengut
+ Sünkrooni kaustad
+ Sünkrooni metaandmed
+ Sildi nimi
+ Märgistage valitud raamatud
+ Pealkirja tekst
+ Tööriistad
+ Import, sync, and app settings
+ Tüüp, nt. PDF
+ Vaade
+ Hääle vahemälu
+ Manustatud veebivaate ettevalmistamine…
+ Preparing bundled embedded webview %1$d%%
+ Manustatud veebivaade installitud. Taaskäivitage Episteme seadistamise lõpetamiseks.
+ Embedded webview could not start: %1$s
+ Töötab…
+ Tööruum
+ Lisa riiulile
+ Create a shelf first, then add selected books to it.
+ Loo teema
+ Olemasolev: %1$s
+ Klõpsasite välisel lingil.
+ Redigeeri EPUB metaandmed
+ Vähem
+ …veel
+ Kohandatud teemasid pole veel
+ Nimeta rakenduses ümber
+ Sildid, komadega eraldatud
+ Tundmatu
+ Defineeri
+ Annotatsioon
+ Märkuste valikud
+ Märkuste tegemise tööriistad
+ Abi
+ Valige, milline PDF päästa.
+ Tühjenda hüppeajalugu
+ Pilv TTS ebaõnnestunud.
+ Lisage a Gemini klahvi ja valige Gemini pilv TTS aastal AI võtmed ja mudelid.
+ Pilv TTS pole selle töölaua järgu jaoks konfigureeritud.
+ Logige sisse rakendusega Google to use cloud TTS.
+ Pilv TTS needs a signed-in account with credits. Pro ja krediite saab osta ainult veebisaidilt Android rakendus.
+ Värv
+ Kommentaaride valikud
+ Kohandatud
+ See eemaldab märkuse sellelt PDF.
+ Kas kustutada märkus?
+ Dokumendi tekst
+ Manustatud PDF kommenteerida
+ Lehe renderdamine ebaõnnestus.
+ Funktsioon pole saadaval
+ Valmis
+ Täitesulepea
+ Peida otsingutulemused
+ Tõstke esile värv %1$d
+ Highlighteri palett
+ Interaktsioon
+ Indekseerimine %1$d/%2$d lehekülgi
+ Märgistus
+ %1$d tikud
+ %1$d seniseid vasteid
+ Järgmine leht
+ Järgmine otsingutulemus
+ Märkusi veel pole
+ Järjehoidjaid pole veel
+ Ei mingit kommentaari
+ Vasteid pole
+ Indekseeritud lehtedel pole veel vasteid
+ Sisukorda pole
+ There is no text here to read.
+ Sellel lehel pole lugemiseks teksti.
+ There is no text to summarize.
+ Ava kommentaar
+ Krediidid otsas. Pro ja krediite saab osta ainult Android rakendus.
+ Pilve kasutamine TTS needs credits on desktop. Pro ja krediite saab osta ainult veebisaidilt Android rakendus.
+ Using this feature needs credits on desktop. Pro ja krediite saab osta ainult Android rakendus.
+ Using recaps needs credits on desktop. Pro ja krediite saab osta ainult Android rakendus.
+ Using summaries needs credits on desktop. Pro ja krediite saab osta ainult Android rakendus.
+ Pan
+ PDF tegevus ebaõnnestus
+ PDF action could not be completed.
+ PDF kommenteerida
+ lk. %1$d
+ PDF leht %1$d
+ Lehekülg %1$d - %2$s
+ Lehekülg %1$s of %2$d
+ Lehekülgi %1$s of %2$d
+ PDF salvestatud
+ PDF tööriistad
+ Pliiats
+ Valiku ettevalmistamine
+ Ettevalmistus %1$s
+ Eelmine leht
+ Eelmine otsingutulemus
+ Printimisdialoog on lõppenud.
+ Nõutav pro
+ See funktsioon nõuab Pro. Pro saab osta ainult ettevõttelt Android rakendust, siis kasutab töölaud pärast sisselogimist täiendatud kontot.
+ Mitmesõnaline nutikas sõnastik nõuab Pro-d. Pro saab osta ainult ettevõttelt Android rakendust, siis kasutab töölaud pärast sisselogimist täiendatud kontot.
+ Lugeja AI funktsioonid on peidetud.
+ Lauaarvuti AI pole selle järgu jaoks konfigureeritud.
+ Kehtib vertikaalsel lugemisel ja kaheleheküljelistel laialitel.
+ Ümmargune highlighter
+ Salvestatud asukohta %1$s
+ Kerige
+ Otsi: PDF
+ Valige tekst
+ Valitud %1$s
+ Kuva otsingutulemused
+ Logige sisse rakendusega Google selle funktsiooni kasutamiseks töölaual.
+ Logige sisse rakendusega Google mitmesõnalise nutika sõnastiku kasutamiseks töölaual.
+ Logige sisse rakendusega Google töölaual kokkuvõtete kasutamiseks.
+ Logige sisse rakendusega Google töölaual kokkuvõtete kasutamiseks.
+ Peatatud
+ Tekstimärkus
+ tekstimärkus
+ Teksti stiil
+ Paksus %1$s
+ TOC
+ Sisestage selle otsimiseks PDF
+ Pealkirjata
+ Vaadake kontot ja krediite
+ Hääle vahemälu tühjendatud
+ Suumi
+ Suumi sisse
+ Suumi välja
+ Vali
+ Jätka lugemist
+ Loobu
+ Alla
+ Üles
+ AI
+ Keskus
+ Autorid
+ Tagasi raamatukogusse
+ Raamatutoimingud
+ Kaust
+ Sirvige
+ Kategooriad
+ Ch. %1$d
+ Peatükk Pöörded
+ Valige font
+ Valige lugeja tekstuur
+ Kustuta failitüübid
+ Lehekülje märkuste kustutamine
+ Selged allikad
+ Tühjenda olek
+ Tühjenda sildid
+ Sule lugeja
+ Pidev
+ Kaaned
+ Kohandatud värvid
+ Kohandatud teema eelvaade
+ Vähendamine %1$s
+ Määrake leht
+ See eemaldab esiletõstmise ja selle märkuse.
+ Sisenege täisekraanil
+ Välju täisekraanilt
+ Väline otsing
+ Raamatud
+ Koomiksid
+ Dokumendid
+ muud
+ Tekst ja veeb
+ Täida
+ Fikseeritud paigutusega välimus
+ Kaust on tühi
+ Siin pole toetatud faile ega alamkaustu saadaval.
+ %1$s, %2$s
+ %1$s - %2$s
+ Peida filtrid
+ Peida lugeja tööriistad
+ Puudutage pesa ja seejärel valige värv.
+ Jätkake lugemist ja hiljutisi raamatuid
+ Importige raamatuid
+ Impordi kaust
+ Imporditud fondid
+ %1$s %2$s
+ Suurendada %1$s
+ Hüppe ajalugu
+ Paigutus ja vahekaugus
+ Importige failid rakenduste salvestusruumi või lisage failide lugemiseks kaust.
+ Sirvige oma kollektsiooni
+ AI võtmed
+ Nutikas %1$d
+ Lugemata %1$d
+ Pooleli %1$d
+ Täitke %1$d
+ Nimekiri
+ Navigeerimine
+ Avatud pole ühtegi raamatut
+ Lisage kaust selle kausta failide lugemiseks.
+ Pole veel kaustu
+ Navigeerimisüksusi pole
+ Lehe sisu puudub
+ Seadeid ei leitud
+ Siin ilmuvad käsitsi riiulid ja seeriakollektsioonid.
+ Riiuleid veel pole
+ Looge nutikad riiulid raamatute kogumiseks reeglite järgi.
+ Nutikaid riiuleid veel pole
+ Siin kuvatakse raamatutele lisatud sildid.
+ Silte pole veel
+ Toetatud faile ei imporditud.
+ Kataloog
+ Kas kustutada "%1$s"? Sellest kataloogist voogesitatud raamatute avamine võib peatada, kui volikirjad hiljem muutuvad.
+ Kataloogid puuduvad
+ Lisage OPDS kataloog kaugraamatute sirvimiseks.
+ Sirvige katalooge, vooge ja allalaaditavaid faile
+ Ava raamat
+ Ava kaust
+ Avatud PDF
+ Lehekülje ja teksti värvid
+ Lehekülje teave
+ Lehekülje laius
+ Need vaikeseaded kehtivad, kui platvorm toetab jagatud PDF välimus. Raamatu kohta PDF alistab jääda PDF lugeja.
+ PDF failitoimingud
+ PDF marker
+ Salvestatud lugeja esiletõstmise läbipaistvusega.
+ Pin
+ Lugeja hallatav PDF tööriistad
+ Automaatne kerimine, OCR, märkuste vaikeseaded ja PDF-ainult tööriista nähtavust hallatakse aktiivses PDF lugeja.
+ %1$s %2$s of %3$d (%4$d%%)
+ Lugeja tööriistariba vaikeseadeid hallatakse sellel platvormil olevast lugejast.
+ Lugeja tööriistad
+ Salvesta pilt
+ Otsing: %1$s
+ Otsi lugejast
+ Otsinguseaded
+ Valik
+ Valiku otsa käepide
+ Valiku käivituskäepide
+ Teegi korrastamiseks lisage riiuleid, silte või kausta metaandmeid.
+ Kogud, sarjad, sildid ja kaustad
+ Näita lugeja tööriistu
+ Kaust
+ Nutikas
+ Tahke
+ Kiirus
+ Käivitage automaatne kerimine
+ Peatage automaatne kerimine
+ Lõpetage ette lugemine
+ Tekstuuri tugevus
+ Selle raamatu otsimiseks tippige
+ Tüpograafia
+ Võta märkus tagasi
+ Vabastage
+ Kasutage tumedat teemat
+ Kasutage heledat teemat
+ Mono
+ Sans
+ Serif
+ Otsige raamatuid, autoreid või silte
+ Tööriistad puuduvad
+ Nähtav
+ Asendage ainult see, mida räägitakse
+ Lugeja tekst, esiletõstmised ja asukohad jäävad muutumatuks.
+ %1$s -> %2$s
diff --git a/app/src/main/res/values-fr/plurals.xml b/app/src/main/res/values-fr/plurals.xml
index b9367a0..f997d91 100644
--- a/app/src/main/res/values-fr/plurals.xml
+++ b/app/src/main/res/values-fr/plurals.xml
@@ -60,4 +60,114 @@
(%1$d morceaux)(%1$d morceaux)
+
+ %1$d correspondance trouvée
+ %1$d correspondances trouvées
+ %1$d correspondances trouvées
+
+
+ Importation %1$d livre… Il apparaîtra bientôt dans votre bibliothèque.
+ Importation %1$d livres… Ils apparaîtront bientôt dans votre bibliothèque.
+ Importation %1$d livres… Ils apparaîtront bientôt dans votre bibliothèque.
+
+
+ Importé %1$d livre. Vous pouvez le trouver dans l\'onglet Bibliothèque.
+ Importé %1$d livres. Vous pouvez les trouver dans l\'onglet Bibliothèque.
+ Importé %1$d livres. Vous pouvez les trouver dans l\'onglet Bibliothèque.
+
+
+ %1$d livre ajouté à l\'étagère.
+ %1$d livres ajoutés à l\'étagère.
+ %1$d livres ajoutés à l\'étagère.
+
+
+ %1$d livre étiqueté "%2$s".
+ %1$d livres étiquetés avec "%2$s".
+ %1$d livres étiquetés avec "%2$s".
+
+
+ Dossier supprimé "%1$s" et %2$d réserver depuis l\'application.
+ Dossier supprimé "%1$s" et %2$d livres depuis l\'application.
+ Dossier supprimé "%1$s" et %2$d livres depuis l\'application.
+
+
+ %1$d fichier
+ %1$d fichiers
+ %1$d fichiers
+
+
+ Déposer pour importer %1$d fichier
+ Déposer pour importer %1$d fichiers
+ Déposer pour importer %1$d fichiers
+
+
+ %1$d les fichiers non pris en charge seront ignorés.
+ %1$d les fichiers non pris en charge seront ignorés.
+ %1$d les fichiers non pris en charge seront ignorés.
+
+
+ Importation %1$d fichier…
+ Importation %1$d fichiers…
+ Importation %1$d fichiers…
+
+
+ Importé %1$d déposer.
+ Importé %1$d fichiers.
+ Importé %1$d fichiers.
+
+
+ Importé %1$d déposer. Le support des lecteurs vient plus tard.
+ Importé %1$d fichiers. Le support des lecteurs vient plus tard.
+ Importé %1$d fichiers. Le support des lecteurs vient plus tard.
+
+
+ Impossible d\'importer %1$d déposer.
+ Impossible d\'importer %1$d fichiers.
+ Impossible d\'importer %1$d fichiers.
+
+
+ Ignoré %1$d déposer.
+ Ignoré %1$d fichiers.
+ Ignoré %1$d fichiers.
+
+
+ Supprimer "%1$s" et son %2$d réserver depuis l\'application ? Les fichiers sur le disque ne seront pas supprimés.
+ Supprimer "%1$s" et son %2$d des livres depuis l\'application ? Les fichiers sur le disque ne seront pas supprimés.
+ Supprimer "%1$s" et son %2$d des livres depuis l\'application ? Les fichiers sur le disque ne seront pas supprimés.
+
+
+ La synchronisation du dossier a échoué pour %1$d dossier.
+ La synchronisation du dossier a échoué pour %1$d dossiers.
+ La synchronisation du dossier a échoué pour %1$d dossiers.
+
+
+ Synchronisation des dossiers terminée avec %1$d dossier ignoré.
+ Synchronisation des dossiers terminée avec %1$d dossiers ignorés.
+ Synchronisation des dossiers terminée avec %1$d dossiers ignorés.
+
+
+ Supprimé %1$d diffusé OPDS livre de ce catalogue.
+ Supprimé %1$d diffusé OPDS livres de ce catalogue.
+ Supprimé %1$d diffusé OPDS livres de ce catalogue.
+
+
+ Tous les livres %1$d
+ Tous les livres %1$d
+ Tous les livres %1$d
+
+
+ Étagères %1$d
+ Étagères %1$d
+ Étagères %1$d
+
+
+ Balises %1$d
+ Balises %1$d
+ Balises %1$d
+
+
+ Dossiers %1$d
+ Dossiers %1$d
+ Dossiers %1$d
+
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 4b10e44..f21da19 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -753,4 +753,763 @@
Transparence de la textureRécapitulation de l\'histoire (Bêta)Débloquer la récapitulation des chapitres
+ Désactiver la synchronisation locale
+ Activer la synchronisation locale
+ Synchronisation locale désactivée
+ Désactiver la synchronisation des dossiers locaux ?
+ Episteme arrêtera d\'analyser ce dossier et arrêtera d\'écrire JSON synchroniser les fichiers. Supprimez le %1$s dossier de ce dossier également ?
+ Conserver les données de synchronisation
+ Supprimer les données de synchronisation
+ Supprimer les polices ?
+ Etes-vous sûr de vouloir supprimer %1$d polices sélectionnées ? Cela les supprimera de tous vos appareils si la synchronisation est activée.
+ Aucun dossier local n\'a la synchronisation activée.
+ Synchronisation des dossiers locaux désactivée.
+ Synchronisation des dossiers locaux désactivée. Dossier de données de synchronisation supprimé.
+ La synchronisation du dossier local est désactivée, mais le dossier de données de synchronisation n\'a pas pu être supprimé.
+ Synchronisation des dossiers locaux activée.
+ Obtenez des résumés concis de n’importe quel chapitre avec Episteme Pro. Effectuez la mise à niveau pour commencer à utiliser cette fonctionnalité.
+ Débloquez le dictionnaire intelligent
+ Définir des phrases entières et des paragraphes jusqu\'à 2 000 caractères est une fonctionnalité Pro. Mettez à niveau pour obtenir des définitions instantanées pour tout texte sélectionné.
+ Verticale (WebView)
+ Vertical (bêta native)
+ Orientation de l\'écran
+ Changer le mode de lecture
+ Paginé (de droite à gauche)
+ TTS Paramètres
+ TTS Remplacements de mots
+ Remplacements de mots de livres
+ Partager, enregistrer ou imprimer
+ Récapitulatif (bêta)
+ Précédent TTS morceau
+ Suivant TTS morceau
+ Onglets
+ Images
+ Aucune image trouvée.
+ Télécharger l\'image
+ Enregistré %1$s
+ Impossible d\'enregistrer l\'image.
+ Mise en page
+ PDF page étendue
+ Page unique
+ Deux pages
+ Première page seule
+ Commence les pages en regard après la page de garde.
+ Supprimer l\'espace entre les pages
+ S\'applique à la lecture verticale et aux doubles pages.
+ Masquer la superposition des numéros de page
+ Supprime la petite étiquette de nombre de pages de chaque page.
+ Orientation de l\'écran
+ Choisissez si le lecteur suit l\'orientation du système ou préfère le portrait ou le paysage lorsque Android le permet.
+ Luminosité
+ Utiliser la luminosité du système
+ Suit le réglage de la luminosité de l\'appareil.
+ Luminosité personnalisée
+ S\'applique lorsqu\'un écran de lecteur est ouvert.
+ %1$d%%
+ Étagère créée "%1$s".
+ Création d\'une étagère intelligente "%1$s".
+ Étagère renommée "%1$s".
+ Étagère supprimée "%1$s".
+ Mise à jour de "%1$s".
+ Ces fichiers sont déjà dans la bibliothèque.
+ %1$s - %2$s
+ Enregistrer
+ Enregistrer le commentaire
+ Ajouter un commentaire
+ Répondre
+ Ajouter un commentaire…
+ Commentaires
+ Modification du commentaire
+ Répondre à %1$s
+ Fond de police
+ Utiliser un filtre de fichiers strict
+ Utiliser PDF Noms de fichiers
+ Détection ML du panneau de test
+ Tester la détection ML des bulles vocales
+ Activer le filtre de fichiers strict
+ Thème de l\'application
+ Apparence
+ Schéma de couleurs
+ Dynamique
+ Créer un thème d\'application
+ Thème personnalisé
+ Ajouter un thème personnalisé
+ Thème de l\'application
+ Tiroir ouvert
+ Fonctionnalité Pro
+ Trier
+ Fermer la recherche
+ Effacer la requête
+ Étagère de recherche
+ %1$s couverture d\'étagère
+ Préserver les couleurs de l\'image
+ Conserver les couleurs d\'origine de l\'image lorsque le thème change
+ Non mis en rayon
+ Tous les livres
+ Système
+ Lumière
+ Sombre
+ Norme
+ Moyen
+ Élevé
+ Récent
+ Titre A à Z
+ Auteur A-Z
+ Pourcentage terminé 0 à 100
+ Pourcentage terminé 100-0
+ Taille (la plus petite)
+ Taille (la plus grande)
+ Tout
+ Non lu
+ En cours
+ Terminé
+ Étiquettes : %1$s
+ Rechercher par balise
+ Balises
+ Dossiers
+ Fichiers
+ Synthèse vocale
+ Commandes de lecture pour la synthèse vocale.
+ Préparation de la synthèse vocale
+ Préparation : %1$s
+ Actif TTS Moteur
+ Nuage AI
+ Appareil natif
+ Voix dans le cloud
+ Voix de l\'appareil
+ Cache cloud
+ Sélectionnez une voix cloud de haute qualité
+ Effacer les échantillons
+ Voix par défaut du système
+ Utilise les paramètres de l\'appareil
+ Filtre de langue
+ En ligne
+ Hors ligne
+ Filtre vocal
+ Aucun audio mis en cache pour cette voix.
+ Vider le cache pour %1$s
+ Ceci est un échantillon de voix.
+ AI Caractéristiques
+ Résumé
+ Récapitulatif
+ Cache
+ Pas de résumé pour %1$s encore.
+ Générer un résumé pour %1$s
+ Obtenez un récapitulatif de l\'histoire jusqu\'à votre position actuelle.
+ Générer un récapitulatif de l\'histoire
+ Récapitulatif de l\'histoire
+ Accès au cache • Gratuit
+ Généré • Gratuit (%1$d/10 restant)
+ Généré • Coût : %1$s crédits
+ Générer… • Coût : Calculer
+ AI Sortie
+ Régénérer
+ Aucun résumé en cache pour ce livre.
+ Crédits
+ AI & Crédits Cloud
+ Crédits disponibles
+ %1$d Crédits
+ Répartition des coûts estimés
+ Nuage TTS
+ Cost: ~3–4 credits per minute of audio generated.\nTo enable: Reader Screen > More > TTS Voice Settings.
+ AI Résumés et récapitulation
+ Cost: ~1–4 credits per request based on chapter length.\nPro Users get 10 free summaries daily.
+ En achetant,
+ Plus de crédits
+ Vous n\'avez pas\'pas assez de crédits. Obtenez Episteme Pro pour 10 résumés gratuits par jour, ou ajoutez plus de crédits pour utiliser les résumés, Cloud TTS et Récapitulatif de l\'histoire.
+ Devenir Pro / Ajouter des crédits
+ Déverrouiller le résumé de la page
+ Obtenez des résumés concis de n’importe quelle page avec Episteme Pro. Effectuez la mise à niveau pour commencer à utiliser cette fonctionnalité.
+ Télécharger le modèle de zoom à bulles
+ Pour utiliser la fonction Bubble Zoom, un AI le modèle doit être téléchargé (~ 134 Mo). Voulez-vous le télécharger maintenant ?
+ Traduire
+ Retour à la page %1$d
+ Page %1$d
+ Résultat %1$d / %2$d
+ Échec du chargement de PDF.
+ Téléchargement du modèle Bubble Zoom… %1$d%%
+ Quitter la navigation par curseur
+ Revenir en arrière
+ Aller en avant
+ Faites défiler jusqu\'à la page de lecture
+ Page annotée
+ Fermer l\'image
+ Basculer les points forts de la recherche
+ Faites glisser pour déplacer la zone de texte
+ Aucune icône de fichiers
+ Copier %1$s
+ Étiquette
+ Marqueur d\'élément de liste
+ Réinitialiser le zoom
+ Générer des annotations de démonstration
+ Annotations de démonstration
+ Aire de jeux à stylo ouvert
+ Nouvel onglet
+ Surligner tout le texte
+ Basculer le mode d\'édition
+ Zoom intelligent sur la bande dessinée
+ Personnaliser les faits saillants
+ Anglais, espagnol, français, etc.
+ Hindi, Marathi, Sanskrit + Anglais
+ Chinois + Anglais
+ Japonais + Anglais
+ Coréen + Anglais
+ Documenter
+ Généré
+ %1$s (Vue texte)
+ %1$s (Refusion)
+ Ajouter/Modifier
+ Aucune balise attribuée.
+ Appliquer les balises
+ Rechercher ou créer une balise…
+ Créer \"%1$s\"
+ Tirez la distance pour changer de chapitre
+ Court
+ Longue
+ Vitesse : %1$sx
+ Pas : %1$sx
+ Lecture/Pause
+ Réinitialiser la vitesse
+ Réinitialiser le pas
+ Sélectionnez la couleur
+ Ce livre n\'a aucun contenu à afficher.
+ Lien copié
+ Texte copié
+ Table des matières
+ Marque-page
+ Revenir à la page %1$d
+ Retour à la page précédente
+ Quitter le zoom intelligent
+ Zoom intelligent sur la bande dessinée
+ Activer le zoom intelligent de la bande dessinée
+ Protection contre les captures d\'écran
+ La protection contre les captures d\'écran est activée
+ La protection contre les captures d\'écran est désactivée
+ Paramètres
+ Modifier
+ Restaurer
+ %1$s sélectionné
+ Paramètres par défaut du lecteur
+ PDF-spécifiques OCR, les annotations et les paramètres d\'outils restent dans PDF lecteur.
+ AI Définition
+ Chapitre %1$d
+ Emplacement
+ Police personnalisée
+ Une erreur s\'est produite : %1$s
+ Erreur lors du chargement du document : %1$s
+ OCR n\'a trouvé aucun texte sur cette page.
+ La page semble vide ou le texte ne peut pas être extrait.
+ Impossible de résumer une page blanche.
+ Document non chargé.
+ AI les fonctionnalités ne sont pas disponibles dans le mode hors ligne OSS construire.
+ Bloqué pour des raisons de sécurité.
+ Choisissez un modèle pour %1$s dans AI paramètres de clé et de modèle.
+ Ajouter un %1$s API saisir AI paramètres de clé et de modèle.
+ Le AI Le fournisseur a renvoyé une réponse vide.
+ AI erreur du fournisseur : %1$d. %2$s
+ Ce résumé nécessite un Gemini modèle car les modèles Groq sélectionnés ne prennent pas en charge l\'entrée PDF/image.
+ Vous devez être connecté pour utiliser les commentaires.
+ Vous devez être connecté pour soumettre des commentaires.
+ Max 3 images autorisées pour les billets.
+ Max 5 images autorisées par message.
+ Une ou plusieurs images dépassent la limite de 5 Mo.
+ Échec de la création du ticket : %1$s
+ Échec de l\'envoi : %1$s
+ Échec du chargement du flux : %1$s
+ Corps vide
+ Échec du téléchargement : %1$s
+ Erreur de téléchargement : %1$s
+ Échec de l\'achat : %1$s
+ Impossible de se connecter au service de facturation.
+ Produits introuvables.
+ Échec de l\'interrogation des produits
+ Non disponible en version Open Source
+ Aucun texte à lire.
+ Erreur lors du démarrage de la lecture.
+ Échec du chargement de l\'audio.
+ Erreur de lecture : %1$s
+ Nuage TTS n’est pas configuré.
+ Livre inconnu
+ AI clés et modèles
+ Clés enregistrées
+ Ajouter ou remplacer une clé
+ Fournisseur
+ API clé
+ Enregistrer la clé
+ Utiliser un seul modèle pour toutes les fonctionnalités
+ Lorsqu\'il est éteint, chaque lecteur AI La fonctionnalité utilise son propre modèle sélectionné.
+ Tous AI fonctionnalités
+ Le dictionnaire intelligent, les résumés et les récapitulatifs utilisent tous ce modèle.
+ Dictionnaire intelligent
+ Utilisé lors de la définition de mots ou d\'expressions sélectionnés.
+ Résumés
+ Utilisé pour EPUB résumés et PDF résumés de pages. PDF/les résumés d\'images nécessitent Gemini.
+ Récapitulatifs
+ Utilisé pour la génération de récapitulatifs d’histoire.
+ Utilise le Gemini enregistré clé. Uniquement %1$s est pris en charge pour l\'instant.
+ Enregistrer %1$s clé?
+ Après la sauvegarde, seuls les 3 premiers et 3 derniers caractères seront visibles. Pour le modifier ultérieurement, remplacez-le ou supprimez-le.
+ Supprimer %1$s clé?
+ Les fonctionnalités utilisant ce fournisseur cesseront de fonctionner jusqu\'à ce qu\'une nouvelle clé soit enregistrée.
+ Aucune clé enregistrée
+ Supprimer %1$s clé
+ Modèle
+ Aucun modèle sélectionné
+ Afficher AI dans le lecteur
+ Masquer AI dans le lecteur
+ Couleurs unies
+ Texturé
+ Solide personnalisé
+ Texturé personnalisé
+ Sélectionnez une texture personnalisée
+ Luminosité du texte (Lumière)
+ Luminosité du texte (foncé)
+ Par défaut
+ Gauche
+ À droite
+ Justifier
+ Toujours afficher
+ Synchroniser avec les menus
+ Toujours se cacher
+ Haut
+ En bas
+ Restaurer les métadonnées d\'origine ?
+ Cela réécrira le titre original, l\'auteur, la série et le résumé dans le EPUB déposer. La progression de la lecture, les balises et les notes ne changeront pas.
+ EPUB métadonnées modifiées
+ Métadonnées de EPUB fichier
+ Nom d\'affichage modifié dans l\'application
+ Métadonnées du fichier
+ Métadonnées
+ Fichier
+ Titre
+ Série
+ Lecture
+ Nom du fichier
+ Modifié
+ Résumé
+ Balises de la bibliothèque
+ Métadonnées modifiables
+ Nom d\'affichage
+ Nom affiché dans Reader
+ Fichier d\'origine : %1$s
+ Barre supérieure
+ Barre inférieure
+ Outils cachés
+ Plus de menu
+ Outils cachés
+ Déposez les outils ici
+ Faites glisser pour réorganiser
+ Applications externes
+ Curseur de navigation
+ Luminosité
+ Barre latérale
+ Mettre en surbrillance le texte sélectionnable
+ Mode édition
+ TTS Contrôles
+ Mode de lecture
+ Gestion des pages
+ Vue texte (redistribution)
+ Livre actuel
+ Mondial
+ Ce livre
+ Activer les remplacements
+ Les règles ici s\'appliquent à chaque livre, sauf si elles sont désactivées pour un titre spécifique.
+ Ajouter une règle
+ Ajouter une règle du livre
+ Aucune règle de remplacement globale pour l\'instant.
+ Pas encore de règles spécifiques au livre.
+ Utilisez les règles globales ici
+ Désactivez cette option lorsqu\'un livre a besoin de ses propres choix de prononciation.
+ Activer les règles du livre
+ Les règles locales s\'exécutent après les règles globales.
+ Règles globales héritées
+ Aucune règle globale à hériter.
+ Autorisé dans ce livre
+ Désactivé pour ce livre
+ Suggestions
+ Nouveau remplacement
+ Modifier le remplacement
+ Remplacer
+ Parlez comme
+ Activé
+ Mot entier
+ Étui de correspondance
+ Aperçu de la saisie
+ Règles
+ silence
+ Texte brut
+ sensible à la casse
+ Livre actuel
+ Ajouter une règle
+ Il n\'y a pas encore de règles de remplacement pour ce livre.
+ Nouveau remplacement
+ Modifier le remplacement
+ Avec
+ texte vide
+ À propos
+ Lecteur de bureau
+ Accès au bureau
+ Compte
+ Compte et crédits
+ Aperçu du compte
+ AI moyeu
+ Utilisé pour EPUB résumés et PDF résumés de pages.
+ Episteme OSS
+ Texte de l\'auteur
+ Cache : %1$s
+ En cache
+ Résumé en cache
+ Choisissez le Gemini voix utilisée pour la lecture à haute voix dans le cloud.
+ Supprimez le livre de bureau généré et EPUB fichiers de cache de pagination ? Ils seront recréés lors de la prochaine ouverture des livres.
+ Effacer le cache vocal
+ Fermer les outils
+ Synchronisation dans le cloud
+ Nuage TTS besoins Gemini
+ Nuage TTS a besoin de crédits de connexion
+ Nuage TTS prêt
+ Nuage TTS paramètres
+ Nuage TTS indisponible
+ Nuage TTS voix
+ Contient
+ Calcul des coûts
+ Créez un récapitulatif jusqu\'à votre position actuelle.
+ Créer une étagère intelligente
+ %1$d crédits disponibles
+ %1$s crédits
+ Polices importées pour le lecteur
+ Supprimer la police
+ Supprimer %1$s? Les livres qui l\'utilisent reviendront à la police par défaut.
+ Supprimer \"%1$s\" ? Les livres restent dans votre bibliothèque.
+ Supprimer le résumé
+ Désactivé
+ Déposez les fichiers à importer
+ Supprimez les fichiers pris en charge à importer
+ Contactez-nous directement par email pour toute autre chose.
+ Égal
+ Suppléments
+ Commentaires
+ Champ
+ Chemin du dossier
+ D\'ici
+ Analyse complète
+ Gratuit, %1$d à gauche
+ Générer un récapitulatif
+ Générer un résumé
+ Signalez des bogues, demandez des fonctionnalités ou contactez directement l’assistance.
+ Commanditaires GitHub
+ Soutenir le développement via les sponsors GitHub.
+ Google la connexion n’est pas configurée pour cette version de bureau.
+ Plus grand que
+ Aide
+ Rapports de bogues, demandes de fonctionnalités et assistance
+ Masquer
+ Importer des fichiers
+ Problèmes
+ Ouvrez le suivi des problèmes pour les bogues et les demandes de fonctionnalités.
+ Moins de
+ Bibliothèque et lecteur
+ N\'importe lequel
+ Actions de la bibliothèque
+ Plus
+ Aucun résumé en cache pour ce livre pour l\'instant.
+ Importez des fichiers TTF, OTF ou WOFF2 pour les utiliser dans des livres.
+ Aucune police trouvée correspondant à \"%1$s\"
+ Non Google le compte est connecté.
+ Aucun résumé mis en cache pour cette section.
+ Lecteur de bureau hors ligne
+ Lecteurs ouverts
+ Ouverture %1$s
+ Ouvrir votre bibliothèque
+ Opérateur
+ Pages
+ Protégé par mot de passe PDF
+ Patréon
+ Soutenez le projet sur Patreon.
+ En pause
+ %1$s nécessite un mot de passe avant de pouvoir être ouvert.
+ Le mot de passe est requis ou incorrect.
+ Ce mot de passe ne s\'est pas ouvert %1$s. Entrez le PDF mot de passe et réessayez.
+ Pourcentage
+ Planifier
+ Préparation du son
+ Préférences
+ Compte et crédits
+ Compte et crédits
+ Pro n\'est pas débloqué pour ce compte.
+ Les pro et les crédits ne peuvent être achetés qu\'à partir du Android application. Desktop vérifie le même compte connecté et utilise ces crédits pour le cloud TTS, les résumés, les récapitulatifs et autres AI payants. caractéristiques.
+ Connectez-vous pour vérifier l\'état de votre compte sur le bureau.
+ Pro est débloqué pour ce compte.
+ Progrès
+ Projet
+ Lecteur
+ Le lecteur onglets désactivés
+ Onglets du lecteur sur
+ Actualiser
+ Relâchez pour ajouter à votre bibliothèque.
+ Le stockage sécurisé des clés n\'est pas disponible sur ce système d\'exploitation. Les clés saisies ici seront utilisées pour cette session mais ne seront pas conservées.
+ Centre de paramètres
+ Correspond au Android masquer la bascule pour un dictionnaire intelligent, des résumés et des récapitulations.
+ Synchroniser le compte, Pro et les crédits
+ Connecté
+ Code source
+ Parcourez la source du projet sur GitHub.
+ Arrêtez de lire pour changer de voix.
+ Assistance
+ Prise en charge Episteme
+ Les contributions aident le lecteur à s\'améliorer sur Android et ordinateur de bureau.
+ Façons de soutenir Episteme développement
+ Synchroniser les dossiers
+ Synchroniser les métadonnées
+ Nom de la balise
+ Marquer les livres sélectionnés
+ Texte du titre
+ Outils
+ Paramètres d\'importation, de synchronisation et d\'application
+ Tapez, par ex. PDF
+ Voir
+ Cache vocal
+ Préparation de la vue Web intégrée…
+ Préparation de la vue Web intégrée groupée %1$d%%
+ Vue Web intégrée installée. Redémarrer Episteme pour terminer la configuration.
+ La vue Web intégrée n\'a pas pu démarrer : %1$s
+ Travailler…
+ Espace de travail
+ Ajouter à l\'étagère
+ Créez d’abord une étagère, puis ajoutez-y les livres sélectionnés.
+ Créer un thème
+ Existant : %1$s
+ Vous avez cliqué sur un lien externe.
+ Modifier EPUB métadonnées
+ Moins
+ …plus
+ Pas encore de thèmes personnalisés
+ Renommer dans l\'application
+ Balises, séparées par des virgules
+ Inconnu
+ Définir
+ Annotations
+ Options d\'annotation
+ Outils d\'annotation
+ Aider
+ Choisissez lequel PDF pour sauvegarder.
+ Effacer l\'historique des sauts
+ Nuage TTS échoué.
+ Ajouter un Gemini et sélectionnez Gemini nuage TTS dans AI clés et modèles.
+ Nuage TTS n\'est pas configuré pour cette version de bureau.
+ Connectez-vous avec Google pour utiliser le cloud TTS.
+ Nuage TTS a besoin d\'un compte connecté avec des crédits. Les pro et les crédits ne peuvent être achetés qu\'à partir du Android application.
+ Couleur
+ Options de commentaires
+ Personnalisé
+ Cela supprime l\'annotation de ce PDF.
+ Supprimer l\'annotation ?
+ Texte du document
+ Intégré PDF commentaire
+ Échec du rendu de la page.
+ Fonctionnalité indisponible
+ Fini
+ Stylo plume
+ Masquer les résultats de recherche
+ Couleur de surbrillance %1$d
+ Palette de surligneurs
+ Interactions
+ Indexation %1$d/%2$d pages
+ Balisage
+ %1$d matchs
+ %1$d matchs jusqu\'à présent
+ Page suivante
+ Résultat de recherche suivant
+ Aucune annotation pour l\'instant
+ Pas encore de favoris
+ Pas de commentaire
+ Aucune correspondance
+ Aucune correspondance dans les pages indexées pour le moment
+ Pas de table des matières
+ Il n\'y a pas de texte ici à lire.
+ Il n\'y a pas de texte à lire sur cette page.
+ Il n\'y a pas de texte pour résumer.
+ Ouvrir le commentaire
+ À court de crédits. Les pro et les crédits ne peuvent être achetés qu\'à partir du Android application.
+ Utilisation du cloud TTS a besoin de crédits sur le bureau. Les pro et les crédits ne peuvent être achetés qu\'à partir du Android application.
+ L\'utilisation de cette fonctionnalité nécessite des crédits sur le bureau. Les pro et les crédits ne peuvent être achetés qu\'à partir du Android application.
+ L\'utilisation des récapitulatifs nécessite des crédits sur le bureau. Les pro et les crédits ne peuvent être achetés qu\'à partir du Android application.
+ L\'utilisation des résumés nécessite des crédits sur le bureau. Les pro et les crédits ne peuvent être achetés qu\'à partir du Android application.
+ Poêle
+ PDF l\'action a échoué
+ Le PDF l\'action n\'a pas pu être complétée.
+ PDF commentaire
+ p. %1$d
+ PDF %1$d
+ Page %1$d - %2$s
+ Page %1$s de %2$d
+ Pages %1$s de %2$d
+ PDF enregistré
+ PDF outils
+ Crayon
+ Préparation de la sélection
+ Préparation %1$s
+ Page précédente
+ Résultat de la recherche précédente
+ La boîte de dialogue d\'impression est terminée.
+ Professionnel requis
+ Cette fonctionnalité nécessite Pro. Pro ne peut être acheté qu\'à partir du Android application, le bureau utilisera le compte mis à niveau après la connexion.
+ Le dictionnaire intelligent multi-mots nécessite Pro. Pro ne peut être acheté qu\'à partir du Android application, le bureau utilisera le compte mis à niveau après la connexion.
+ Lecteur AI les fonctionnalités sont masquées.
+ Bureau AI n\'est pas configuré pour cette version.
+ S\'applique à la lecture verticale et aux doubles pages.
+ Surligneur rond
+ Enregistré dans %1$s
+ Faire défiler
+ Rechercher dans PDF
+ Sélectionnez le texte
+ Sélectionné %1$s
+ Afficher les résultats de la recherche
+ Connectez-vous avec Google pour utiliser cette fonctionnalité sur le bureau.
+ Connectez-vous avec Google pour utiliser un dictionnaire intelligent multi-mots sur le bureau.
+ Connectez-vous avec Google pour utiliser les récapitulatifs sur le bureau.
+ Connectez-vous avec Google pour utiliser les résumés sur le bureau.
+ Arrêté
+ Note textuelle
+ note de texte
+ Style de texte
+ Épaisseur %1$s
+ Table des matières
+ Tapez pour rechercher ceci PDF
+ Sans titre
+ Afficher le compte et les crédits
+ Cache vocal vidé
+ Zoomer
+ Zoomer
+ Zoom arrière
+ Choisissez
+ Continuer la lecture
+ Rejeter
+ Vers le bas
+ Vers le haut
+ AI
+ Centre
+ Auteurs
+ Retour à la bibliothèque
+ Actions du livre
+ Dossier
+ Parcourir
+ Catégories
+ Ch. %1$d
+ Tour de chapitre
+ Choisir la police
+ Choisir la texture du lecteur
+ Effacer les types de fichiers
+ Effacer les annotations de page
+ Sources claires
+ Effacer le statut
+ Effacer les balises
+ Fermer le lecteur
+ Continu
+ Couvertures
+ Couleurs personnalisées
+ Aperçu du thème personnalisé
+ Diminuer %1$s
+ Définir la page
+ Cela supprime la surbrillance et sa note.
+ Entrer en plein écran
+ Quitter le plein écran
+ Recherche externe
+ Livres
+ Bandes dessinées
+ Documents
+ Autre
+ Texte et web
+ Remplir
+ Apparence de mise en page fixe
+ Le dossier est vide
+ Aucun fichier ou sous-dossier pris en charge n\'est disponible ici.
+ %1$s, %2$s
+ %1$s - %2$s
+ Masquer les filtres
+ Masquer les outils de lecture
+ Appuyez sur un emplacement, puis choisissez une couleur.
+ Continuer la lecture et les livres récents
+ Importer des livres
+ Dossier d\'importation
+ Polices importées
+ %1$s %2$s
+ Augmenter %1$s
+ Historique des sauts
+ Disposition et espacement
+ Importez des fichiers dans le stockage de l\'application ou ajoutez un dossier pour lire les fichiers sur place.
+ Parcourez votre collection
+ AI clés
+ Intelligent %1$d
+ Non lu %1$d
+ En cours %1$d
+ Terminé %1$d
+ Liste
+ Navigation
+ Aucun livre ouvert
+ Ajoutez un dossier pour lire les fichiers de ce dossier en place.
+ Aucun dossier pour l\'instant
+ Aucun élément de navigation
+ Aucun contenu de page
+ Aucun paramètre trouvé
+ Les étagères manuelles et les collections de séries apparaîtront ici.
+ Pas encore d\'étagères
+ Créez des étagères intelligentes pour collecter des livres selon des règles.
+ Pas encore d\'étagères intelligentes
+ Les balises ajoutées aux livres apparaîtront ici.
+ Pas encore de balises
+ Aucun fichier pris en charge n\'a été importé.
+ Catalogue
+ Supprimer "%1$s" ? Les livres diffusés en streaming à partir de ce catalogue peuvent cesser de s\'ouvrir si les informations d\'identification changent ultérieurement.
+ Aucun catalogue
+ Ajoutez un OPDS catalogue pour parcourir les livres distants.
+ Parcourez les catalogues, les flux et les téléchargements
+ Livre ouvert
+ Ouvrir le dossier
+ Ouvrir PDF
+ Couleurs des pages et du texte
+ Informations sur la page
+ Largeur de page
+ Ces valeurs par défaut s\'appliquent lorsque la plate-forme prend en charge le partage PDF apparence. Par livre PDF les remplacements restent dans le PDF lecteur.
+ PDF actions sur les fichiers
+ PDF surligneur
+ Enregistré avec la transparence du lecteur.
+ Épingle
+ Géré par les lecteurs PDF outils
+ Le défilement automatique, OCR, les valeurs par défaut des annotations et PDF-la visibilité des outils uniquement sont gérés à l\'intérieur du PDF actif. lecteur.
+ %1$s %2$s de %3$d (%4$d%%)
+ Les paramètres par défaut de la barre d\'outils du lecteur sont gérés à partir du lecteur sur cette plateforme.
+ Outils de lecture
+ Enregistrer l\'image
+ Rechercher : %1$s
+ Rechercher dans le lecteur
+ Paramètres de recherche
+ Sélection
+ Poignée de fin de sélection
+ Poignée de début de sélection
+ Ajoutez des étagères, des balises ou des métadonnées de dossier pour organiser votre bibliothèque.
+ Collections, séries, balises et dossiers
+ Afficher les outils de lecture
+ Dossier
+ Intelligent
+ Solide
+ Vitesse
+ Démarrer le défilement automatique
+ Arrêter le défilement automatique
+ Arrêtez de lire à haute voix
+ Résistance de la texture
+ Tapez pour rechercher ce livre
+ Typographie
+ Annuler l\'annotation
+ Désépingler
+ Utiliser un thème sombre
+ Utiliser le thème clair
+ Mono
+ Sans
+ Serif
+ Rechercher des livres, des auteurs ou des tags
+ Aucun outil
+ Visible
+ Remplacez uniquement ce qui est dit
+ Le texte du lecteur, les surlignages et les emplacements restent inchangés.
+ %1$s -> %2$s
diff --git a/app/src/main/res/values-hi/plurals.xml b/app/src/main/res/values-hi/plurals.xml
index bd76dac..da92a55 100644
--- a/app/src/main/res/values-hi/plurals.xml
+++ b/app/src/main/res/values-hi/plurals.xml
@@ -52,4 +52,88 @@
(%1$d खंड)(%1$d खंड)
+
+ आयात हो रहा है %1$d पुस्तक... यह शीघ्र ही आपकी लाइब्रेरी में दिखाई देगी।
+ आयात हो रहा है %1$d पुस्तकें... वे शीघ्र ही आपकी लाइब्रेरी में दिखाई देंगी।
+
+
+ आयातित %1$d किताब। आप इसे लाइब्रेरी टैब में पा सकते हैं।
+ आयातित %1$d किताबें. आप उन्हें लाइब्रेरी टैब में पा सकते हैं।
+
+
+ %1$d पुस्तक शेल्फ में जोड़ी गई.
+ %1$d पुस्तकें शेल्फ में जोड़ी गईं।
+
+
+ %1$d पुस्तक को "%2$s" के साथ टैग किया गया है।
+ %1$d "%2$s" टैग वाली पुस्तकें।
+
+
+ हटाया गया फ़ोल्डर "%1$s" और %2$d ऐप से बुक करें.
+ हटाया गया फ़ोल्डर "%1$s" और %2$d ऐप से किताबें।
+
+
+ %1$d फ़ाइल
+ %1$d फ़ाइलें
+
+
+ आयात करने के लिए छोड़ें %1$d फ़ाइल
+ आयात करने के लिए छोड़ें %1$d फ़ाइलें
+
+
+ %1$d असमर्थित फ़ाइल छोड़ दी जाएगी.
+ %1$d असमर्थित फ़ाइलें छोड़ दी जाएंगी.
+
+
+ आयात हो रहा है %1$d फ़ाइल...
+ आयात हो रहा है %1$d फ़ाइलें...
+
+
+ आयातित %1$d फ़ाइल।
+ आयातित %1$d फ़ाइलें.
+
+
+ आयातित %1$d फ़ाइल। पाठक का समर्थन बाद में आता है।
+ आयातित %1$d फ़ाइलें. पाठक का समर्थन बाद में आता है।
+
+
+ आयात नहीं किया जा सका %1$d फ़ाइल।
+ आयात नहीं किया जा सका %1$d फ़ाइलें.
+
+
+ छोड़ दिया गया %1$d फ़ाइल।
+ छोड़ दिया गया %1$d फ़ाइलें.
+
+
+ "%1$s" हटाएं और इसका %2$d ऐप से बुक करें? डिस्क पर फ़ाइलें हटाई नहीं जाएंगी.
+ "%1$s" हटाएं और इसका %2$d ऐप से किताबें? डिस्क पर फ़ाइलें हटाई नहीं जाएंगी.
+
+
+ %1$d के लिए फ़ोल्डर सिंक विफल रहा फ़ोल्डर.
+ %1$d के लिए फ़ोल्डर सिंक विफल रहा फ़ोल्डर्स.
+
+
+ फ़ोल्डर सिंक %1$d के साथ समाप्त हुआ फ़ोल्डर छोड़ दिया गया.
+ फ़ोल्डर सिंक %1$d के साथ समाप्त हुआ फ़ोल्डर्स छोड़ दिए गए.
+
+
+ हटा दिया गया %1$d स्ट्रीम किया गया OPDS उस कैटलॉग से पुस्तक.
+ हटा दिया गया %1$d स्ट्रीम किया गया OPDS उस कैटलॉग से पुस्तकें.
+
+
+ सभी पुस्तकें %1$d
+ सभी पुस्तकें %1$d
+
+
+ अलमारियां %1$d
+ अलमारियां %1$d
+
+
+ टैग %1$d
+ टैग %1$d
+
+
+ फ़ोल्डर्स %1$d
+ फ़ोल्डर्स %1$d
+
diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml
index 2f1d080..d704c1b 100644
--- a/app/src/main/res/values-hi/strings.xml
+++ b/app/src/main/res/values-hi/strings.xml
@@ -1086,4 +1086,430 @@
Nederlands (डच)Українська (यूक्रेनी)Bahasa Indonesia (इंडोनेशियाई)
+ शीर्ष ऐप बार में टैब दिखाएं
+ स्थानीय सिंक अक्षम करें
+ स्थानीय सिंक सक्षम करें
+ स्थानीय समन्वयन अक्षम
+ स्थानीय फ़ोल्डर सिंक अक्षम करें?
+ Episteme इस फ़ोल्डर को स्कैन करना बंद कर देगा और लिखना बंद कर देगा JSON फ़ाइलें सिंक करें. %1$s हटाएं इस फ़ोल्डर से भी फ़ोल्डर?
+ डेटा सिंक रखें
+ सिंक डेटा हटाएँ
+ फ़ॉन्ट हटाएँ?
+ क्या आप वाकई हटाना चाहते हैं %1$d चयनित फ़ॉन्ट? यदि सिंक चालू है तो यह उन्हें आपके सभी डिवाइस से हटा देगा।
+ किसी भी स्थानीय फ़ोल्डर में सिंक सक्षम नहीं है.
+ स्थानीय फ़ोल्डर समन्वयन अक्षम.
+ स्थानीय फ़ोल्डर समन्वयन अक्षम. सिंक डेटा फ़ोल्डर हटा दिया गया.
+ स्थानीय फ़ोल्डर सिंक अक्षम है, लेकिन सिंक डेटा फ़ोल्डर हटाया नहीं जा सका।
+ स्थानीय फ़ोल्डर समन्वयन सक्षम.
+ लंबवत (वेबव्यू)
+ कार्यक्षेत्र (मूल बीटा)
+ शब्द प्रतिस्थापन बुक करें
+ पिछला TTS टुकड़ा
+ अगला TTS टुकड़ा
+ छवियाँ
+ कोई चित्र नहीं मिला.
+ छवि डाउनलोड करें
+ सहेजा गया %1$s
+ छवि सहेजी नहीं जा सकी.
+ PDF पृष्ठ प्रसार
+ एकल पृष्ठ
+ दो पन्ने
+ पहला पेज अकेला
+ कवर पेज के बाद फेसिंग पेज स्प्रेड शुरू होता है।
+ चमक
+ सिस्टम चमक का प्रयोग करें
+ डिवाइस की चमक सेटिंग का अनुसरण करता है।
+ कस्टम चमक
+ रीडर स्क्रीन खुली होने पर लागू होता है।
+ %1$d%%
+ शेल्फ़ बनाया गया "%1$s"।
+ स्मार्ट शेल्फ "%1$s" बनाया गया।
+ शेल्फ़ का नाम बदलकर "%1$s" कर दिया गया।
+ हटाई गई शेल्फ़ "%1$s"।
+ अपडेट किया गया "%1$s"।
+ वे फ़ाइलें पहले से ही लाइब्रेरी में हैं.
+ %1$s - %2$s
+ सहेजें
+ टिप्पणी सहेजें
+ टिप्पणी जोड़ें
+ उत्तर
+ एक टिप्पणी जोड़ें...
+ टिप्पणियाँ
+ टिप्पणी संपादित करना
+ %1$s को उत्तर दिया जा रहा है
+ PDF का प्रयोग करें फ़ाइलनाम
+ चमक
+ वर्तमान पुस्तक
+ नियम जोड़ें
+ इस पुस्तक के लिए अभी तक कोई प्रतिस्थापन नियम नहीं हैं।
+ नया प्रतिस्थापन
+ प्रतिस्थापन संपादित करें
+ साथ
+ खाली पाठ
+ के बारे में
+ डेस्कटॉप रीडर
+ डेस्कटॉप पहुंच
+ खाता
+ खाता एवं क्रेडिट
+ खाता अवलोकन
+ AI हब
+ EPUB के लिए उपयोग किया जाता है सारांश और PDF पृष्ठ सारांश.
+ Episteme ओस्स
+ लेखक पाठ
+ कैश: %1$s
+ कैश्ड
+ कैश्ड सारांश
+ Gemini चुनें क्लाउड को जोर से पढ़ने के लिए इस्तेमाल की जाने वाली आवाज।
+ जेनरेट की गई डेस्कटॉप बुक और EPUB को हटाएं पेजिनेशन कैश फ़ाइलें? अगली बार किताबें खोले जाने पर उन्हें फिर से बनाया जाएगा।
+ वॉइस कैश साफ़ करें
+ उपकरण बंद करें
+ क्लाउड सिंक
+ बादल TTS आवश्यकताएँ Gemini
+ बादल TTS साइन-इन क्रेडिट की आवश्यकता है
+ बादल TTS तैयार
+ बादल TTS सेटिंग्स
+ बादल TTS अनुपलब्ध
+ बादल TTS आवाज
+ शामिल है
+ लागत की गणना
+ अपनी वर्तमान स्थिति का पुनर्कथन करें।
+ स्मार्ट शेल्फ बनाएं
+ %1$d क्रेडिट उपलब्ध है
+ %1$s श्रेय
+ पाठक के लिए आयातित फ़ॉन्ट
+ फ़ॉन्ट हटाएँ
+ हटाएं %1$s? इसका उपयोग करने वाली पुस्तकें डिफ़ॉल्ट फ़ॉन्ट पर वापस आ जाएंगी।
+ \"%1$s\" हटाएं? किताबें आपकी लाइब्रेरी में रहती हैं.
+ सारांश हटाएँ
+ विकलांग
+ आयात करने के लिए फ़ाइलें छोड़ें
+ आयात करने के लिए समर्थित फ़ाइलें छोड़ें
+ किसी भी अन्य चीज़ के लिए सीधे ईमेल द्वारा हमसे संपर्क करें।
+ बराबर
+ अतिरिक्त
+ प्रतिक्रिया
+ मैदान
+ फ़ोल्डर पथ
+ यहाँ से
+ पूर्ण स्कैन
+ मुफ़्त, %1$d बायां
+ पुनर्कथन उत्पन्न करें
+ सारांश उत्पन्न करें
+ बग की रिपोर्ट करें, सुविधाओं का अनुरोध करें, या सीधे समर्थन से संपर्क करें।
+ गिटहब प्रायोजक
+ GitHub प्रायोजकों के माध्यम से विकास का समर्थन करें।
+ Google इस डेस्कटॉप बिल्ड के लिए साइन-इन कॉन्फ़िगर नहीं किया गया है।
+ से भी बड़ा
+ मदद
+ बग रिपोर्ट, सुविधा अनुरोध और समर्थन
+ छिपाओ
+ फ़ाइलें आयात करें
+ मुद्दे
+ बग और सुविधा अनुरोधों के लिए समस्या ट्रैकर खोलें।
+ से भी कम
+ पुस्तकालय और पाठक
+ कोई भी
+ पुस्तकालय क्रियाएँ
+ अधिक
+ इस पुस्तक के लिए अभी तक कोई संचित सारांश नहीं है।
+ पुस्तकों में उपयोग करने के लिए TTF, OTF, या WOFF2 फ़ाइलें आयात करें।
+ \"%1$s\" से मेल खाता कोई फ़ॉन्ट नहीं मिला
+ नहीं Google खाता जुड़ा हुआ है.
+ इस अनुभाग के लिए कोई सारांश कैश नहीं किया गया.
+ ऑफ़लाइन डेस्कटॉप रीडर
+ पाठकों को खोलें
+ खुल रहा है %1$s
+ अपनी लाइब्रेरी खोल रहा हूँ
+ संचालिका
+ पेज
+ पासवर्ड सुरक्षित PDF
+ पैट्रियन
+ पैट्रियन पर परियोजना का समर्थन करें।
+ रुका हुआ
+ %1$s इसे खोलने से पहले एक पासवर्ड की आवश्यकता होती है।
+ पासवर्ड आवश्यक है या ग़लत है.
+ वह पासवर्ड नहीं खुला %1$s PDF दर्ज करें पासवर्ड और पुनः प्रयास करें.
+ प्रतिशत
+ योजना
+ ऑडियो तैयार हो रहा है
+ प्राथमिकताएँ
+ खाता एवं क्रेडिट
+ खाता एवं क्रेडिट
+ इस खाते के लिए प्रो अनलॉक नहीं है.
+ प्रो और क्रेडिट केवल Android से खरीदे जा सकते हैं अनुप्रयोग। डेस्कटॉप उसी साइन-इन खाते की जाँच करता है और उन क्रेडिट का उपयोग क्लाउड TTS, सारांश, पुनर्कथन और अन्य भुगतान AI के लिए करता है। विशेषताएँ।
+ डेस्कटॉप पर अपने खाते की स्थिति जांचने के लिए साइन इन करें।
+ इस खाते के लिए प्रो अनलॉक है.
+ प्रगति
+ प्रोजेक्ट
+ पाठक
+ रीडर टैब बंद
+ पाठक टैब चालू करता है
+ ताज़ा करें
+ अपनी लाइब्रेरी में जोड़ने के लिए रिलीज़ करें।
+ इस ऑपरेटिंग सिस्टम पर सुरक्षित कुंजी भंडारण उपलब्ध नहीं है। यहां दर्ज की गई कुंजियाँ इस सत्र के लिए उपयोग की जाएंगी लेकिन कायम नहीं रहेंगी।
+ सेटिंग्स हब
+ Android से मेल खाता है स्मार्ट शब्दकोश, सारांश और पुनर्कथन के लिए टॉगल छिपाएँ।
+ खाता, प्रो और क्रेडिट सिंक करें
+ साइन इन किया गया
+ स्रोत कोड
+ GitHub पर प्रोजेक्ट स्रोत ब्राउज़ करें।
+ आवाज़ें बदलने के लिए पढ़ना बंद करें।
+ समर्थन
+ समर्थन Episteme
+ योगदान पाठक को Android में सुधार लाने में मदद करता है और डेस्कटॉप.
+ समर्थन करने के तरीके Episteme विकास
+ फ़ोल्डर सिंक करें
+ मेटाडेटा सिंक करें
+ टैग नाम
+ चयनित पुस्तकों को टैग करें
+ शीर्षक पाठ
+ उपकरण
+ आयात, समन्वयन और ऐप सेटिंग
+ प्रकार, उदा. PDF
+ देखें
+ वॉयस कैश
+ एम्बेडेड वेबव्यू तैयार किया जा रहा है...
+ बंडल एम्बेडेड वेबव्यू तैयार किया जा रहा है %1$d%%
+ एंबेडेड वेबव्यू स्थापित किया गया. पुनरारंभ करें Episteme सेटअप समाप्त करने के लिए.
+ एंबेडेड वेबव्यू प्रारंभ नहीं हो सका: %1$s
+ काम कर रहा हूँ...
+ कार्यक्षेत्र
+ शेल्फ़ में जोड़ें
+ पहले एक शेल्फ बनाएं, फिर उसमें चयनित पुस्तकें जोड़ें।
+ थीम बनाएं
+ मौजूदा: %1$s
+ आपने एक बाहरी लिंक पर क्लिक किया.
+ संपादित करें EPUB मेटाडेटा
+ कम
+ …और अधिक
+ अभी तक कोई कस्टम थीम नहीं है
+ ऐप में नाम बदलें
+ टैग, अल्पविराम से अलग
+ अज्ञात
+ परिभाषित करें
+ एनोटेशन
+ एनोटेशन विकल्प
+ एनोटेशन उपकरण
+ सहायता करें
+ कौन सा चुनें PDF बचाने के लिए।
+ स्पष्ट छलांग इतिहास
+ बादल TTS असफल।
+ एक Gemini जोड़ें कुंजी और चयन करें Gemini बादल TTS में AI चाबियाँ और मॉडल.
+ बादल TTS इस डेस्कटॉप बिल्ड के लिए कॉन्फ़िगर नहीं किया गया है.
+ Google के साथ साइन इन करें क्लाउड का उपयोग करने के लिए TTS
+ बादल TTS क्रेडिट के साथ एक साइन-इन खाते की आवश्यकता है। प्रो और क्रेडिट केवल Android से खरीदे जा सकते हैं अनुप्रयोग।
+ रंग
+ टिप्पणी विकल्प
+ कस्टम
+ यह इस PDF से एनोटेशन हटा देता है।
+ एनोटेशन हटाएँ?
+ दस्तावेज़ पाठ
+ एंबेडेड PDF टिप्पणी
+ पेज प्रस्तुत करने में विफल.
+ सुविधा अनुपलब्ध
+ समाप्त
+ फाउंटेन पेन
+ खोज परिणाम छिपाएँ
+ रंग हाइलाइट करें %1$d
+ हाइलाइटर पैलेट
+ इंटरेक्शन
+ अनुक्रमण %1$d/%2$d पन्ने
+ मार्कअप
+ %1$d मेल खाता है
+ %1$d अब तक के मैच
+ अगला पेज
+ अगला खोज परिणाम
+ अभी तक कोई एनोटेशन नहीं
+ अभी तक कोई बुकमार्क नहीं
+ कोई टिप्पणी नहीं
+ कोई मेल नहीं
+ अनुक्रमित पृष्ठों में अभी तक कोई मिलान नहीं है
+ सामग्री की कोई तालिका नहीं
+ यहां पढ़ने के लिए कोई पाठ नहीं है.
+ इस पृष्ठ पर पढ़ने के लिए कोई पाठ नहीं है।
+ संक्षेप में बताने के लिए कोई पाठ नहीं है।
+ टिप्पणी खोलें
+ क्रेडिट से बाहर. प्रो और क्रेडिट केवल Android से खरीदे जा सकते हैं अनुप्रयोग।
+ क्लाउड का उपयोग करना TTS डेस्कटॉप पर क्रेडिट की आवश्यकता है. प्रो और क्रेडिट केवल Android से खरीदे जा सकते हैं अनुप्रयोग।
+ इस सुविधा का उपयोग करने के लिए डेस्कटॉप पर क्रेडिट की आवश्यकता होती है। प्रो और क्रेडिट केवल Android से खरीदे जा सकते हैं अनुप्रयोग।
+ रीकैप्स का उपयोग करने के लिए डेस्कटॉप पर क्रेडिट की आवश्यकता होती है। प्रो और क्रेडिट केवल Android से खरीदे जा सकते हैं अनुप्रयोग।
+ सारांशों का उपयोग करने के लिए डेस्कटॉप पर क्रेडिट की आवश्यकता होती है। प्रो और क्रेडिट केवल Android से खरीदे जा सकते हैं अनुप्रयोग।
+ पैन
+ PDF कार्रवाई विफल रही
+ PDF कार्रवाई पूरी नहीं हो सकी.
+ PDF टिप्पणी
+ पी। %1$d
+ PDF पेज %1$d
+ पेज %1$d - %2$s
+ पेज %1$s का %2$d
+ पेज %1$s का %2$d
+ PDF बचाया
+ PDF उपकरण
+ पेंसिल
+ चयन की तैयारी
+ तैयारी %1$s
+ पिछला पृष्ठ
+ पिछला खोज परिणाम
+ मुद्रण संवाद समाप्त हो गया है.
+ प्रो की आवश्यकता है
+ इस सुविधा के लिए प्रो की आवश्यकता है. प्रो को केवल Android से खरीदा जा सकता है ऐप, फिर डेस्कटॉप साइन-इन के बाद अपग्रेड किए गए खाते का उपयोग करेगा।
+ मल्टी-वर्ड स्मार्ट डिक्शनरी के लिए प्रो की आवश्यकता है। प्रो को केवल Android से खरीदा जा सकता है ऐप, फिर डेस्कटॉप साइन-इन के बाद अपग्रेड किए गए खाते का उपयोग करेगा।
+ पाठक AI विशेषताएं छुपी हुई हैं.
+ डेस्कटॉप AI इस निर्माण के लिए कॉन्फ़िगर नहीं किया गया है.
+ वर्टिकल रीडिंग और दो-पेज स्प्रेड पर लागू होता है।
+ गोल हाइलाइटर
+ %1$s में सहेजा गया
+ स्क्रॉल करें
+ PDF में खोजें
+ पाठ का चयन करें
+ चयनित %1$s
+ खोज परिणाम दिखाएँ
+ Google के साथ साइन इन करें डेस्कटॉप पर इस सुविधा का उपयोग करने के लिए.
+ Google के साथ साइन इन करें डेस्कटॉप पर मल्टी-वर्ड स्मार्ट डिक्शनरी का उपयोग करने के लिए।
+ Google के साथ साइन इन करें डेस्कटॉप पर रीकैप्स का उपयोग करने के लिए.
+ Google के साथ साइन इन करें डेस्कटॉप पर सारांशों का उपयोग करने के लिए.
+ रुक गया
+ टेक्स्ट नोट
+ पाठ नोट
+ पाठ शैली
+ मोटाई %1$s
+ टीओसी
+ इसे खोजने के लिए टाइप करें PDF
+ शीर्षकहीन
+ खाता और क्रेडिट देखें
+ वॉइस कैश साफ़ किया गया
+ ज़ूम करें
+ ज़ूम इन करें
+ ज़ूम आउट
+ चुनें
+ पढ़ना जारी रखें
+ ख़ारिज करें
+ नीचे
+ ऊपर
+ AI
+ केंद्र
+ लेखक
+ लाइब्रेरी में वापस जाएँ
+ पुस्तक क्रियाएँ
+ फ़ोल्डर
+ ब्राउज़ करें
+ श्रेणियाँ
+ चौ. %1$d
+ अध्याय बदल जाता है
+ फ़ॉन्ट चुनें
+ पाठक बनावट चुनें
+ फ़ाइल प्रकार साफ़ करें
+ पृष्ठ एनोटेशन साफ़ करें
+ स्पष्ट स्रोत
+ स्पष्ट स्थिति
+ टैग साफ़ करें
+ निकट पाठक
+ निरंतर
+ कवर
+ कस्टम रंग
+ कस्टम थीम पूर्वावलोकन
+ कमी %1$s
+ पेज को परिभाषित करें
+ इससे हाइलाइट और उसका नोट हट जाता है.
+ पूर्ण स्क्रीन दर्ज करें
+ पूर्ण स्क्रीन से बाहर निकलें
+ बाहरी खोज
+ किताबें
+ कॉमिक्स
+ दस्तावेज़
+ अन्य
+ पाठ और वेब
+ भरें
+ निश्चित-लेआउट उपस्थिति
+ फ़ोल्डर खाली है
+ यहां कोई समर्थित फ़ाइल या सबफ़ोल्डर उपलब्ध नहीं है।
+ %1$s, %2$s
+ %1$s - %2$s
+ फ़िल्टर छिपाएँ
+ पाठक उपकरण छिपाएँ
+ एक स्लॉट टैप करें, फिर एक रंग चुनें।
+ पढ़ना जारी रखें और हाल की किताबें
+ पुस्तकें आयात करें
+ फ़ोल्डर आयात करें
+ आयातित फ़ॉन्ट
+ %1$s %2$s
+ बढ़ोतरी %1$s
+ छलांग का इतिहास
+ लेआउट और रिक्ति
+ ऐप स्टोरेज में फ़ाइलें आयात करें या फ़ाइलों को पढ़ने के लिए एक फ़ोल्डर जोड़ें।
+ अपना संग्रह ब्राउज़ करें
+ AI चाबियाँ
+ स्मार्ट %1$d
+ अपठित %1$d
+ प्रगति पर %1$d
+ पूर्ण %1$d
+ सूची
+ नेविगेशन
+ कोई किताब खुली नहीं
+ उस फ़ोल्डर से फ़ाइलों को पढ़ने के लिए एक फ़ोल्डर जोड़ें।
+ अभी तक कोई फ़ोल्डर नहीं
+ कोई नेविगेशन आइटम नहीं
+ कोई पृष्ठ सामग्री नहीं
+ कोई सेटिंग नहीं मिली
+ मैनुअल अलमारियाँ और श्रृंखला संग्रह यहां दिखाई देंगे।
+ अभी तक कोई शेल्फ़ नहीं
+ नियमों के अनुसार किताबें एकत्र करने के लिए स्मार्ट अलमारियां बनाएं।
+ अभी तक कोई स्मार्ट शेल्फ़ नहीं
+ पुस्तकों में जोड़े गए टैग यहां दिखाई देंगे.
+ अभी तक कोई टैग नहीं
+ कोई समर्थित फ़ाइल आयात नहीं की गई.
+ कैटलॉग
+ "%1$s" हटाएं? यदि क्रेडेंशियल बाद में बदलते हैं तो इस कैटलॉग से स्ट्रीम की गई पुस्तकें खुलना बंद हो सकती हैं।
+ कोई कैटलॉग नहीं
+ एक OPDS जोड़ें दूरस्थ पुस्तकें ब्राउज़ करने के लिए कैटलॉग।
+ कैटलॉग, स्ट्रीम और डाउनलोड ब्राउज़ करें
+ किताब खोलें
+ फ़ोल्डर खोलें
+ खोलें PDF
+ पृष्ठ और पाठ रंग
+ पृष्ठ जानकारी
+ पृष्ठ की चौड़ाई
+ ये डिफ़ॉल्ट वहां लागू होते हैं जहां प्लेटफ़ॉर्म साझा PDF का समर्थन करता है उपस्थिति। प्रति-पुस्तक PDF ओवरराइड्स PDF में रहते हैं पाठक.
+ PDF फ़ाइल कार्रवाई
+ PDF हाइलाइटर
+ रीडर हाइलाइट पारदर्शिता के साथ सहेजा गया।
+ पिन
+ पाठक-प्रबंधित PDF उपकरण
+ ऑटो-स्क्रॉल, OCR, एनोटेशन डिफ़ॉल्ट, और PDF-केवल टूल दृश्यता को सक्रिय PDF के अंदर प्रबंधित किया जाता है। पाठक.
+ %1$s %2$s का %3$d (%4$d%%)
+ इस प्लेटफ़ॉर्म पर रीडर टूलबार डिफॉल्ट को रीडर से प्रबंधित किया जाता है।
+ पाठक उपकरण
+ छवि सहेजें
+ खोजें: %1$s
+ पाठक में खोजें
+ सेटिंग खोजें
+ चयन
+ चयन अंत हैंडल
+ चयन प्रारंभ हैंडल
+ अपनी लाइब्रेरी को व्यवस्थित करने के लिए शेल्फ़, टैग या फ़ोल्डर मेटाडेटा जोड़ें।
+ संग्रह, श्रृंखला, टैग और फ़ोल्डर्स
+ पाठक उपकरण दिखाएँ
+ फ़ोल्डर
+ होशियार
+ ठोस
+ गति
+ ऑटो स्क्रॉल प्रारंभ करें
+ ऑटो स्क्रॉल बंद करो
+ ज़ोर से पढ़ना बंद करो
+ बनावट की ताकत
+ इस पुस्तक को खोजने के लिए टाइप करें
+ टाइपोग्राफी
+ एनोटेशन पूर्ववत करें
+ अनपिन करें
+ डार्क थीम का प्रयोग करें
+ लाइट थीम का प्रयोग करें
+ मोनो
+ सं
+ सेरिफ़
+ पुस्तकें, लेखक या टैग खोजें
+ कोई उपकरण नहीं
+ दर्शनीय
+ जो बोला गया है उसे ही बदलें
+ पाठक पाठ, हाइलाइट्स और स्थान अपरिवर्तित रहते हैं।
+ %1$s -> %2$s
diff --git a/app/src/main/res/values-in/plurals.xml b/app/src/main/res/values-in/plurals.xml
index 3406b88..9a36cfc 100644
--- a/app/src/main/res/values-in/plurals.xml
+++ b/app/src/main/res/values-in/plurals.xml
@@ -2,41 +2,138 @@
%1$d buku
+ %1$d bukubuku
+ buku%1$d rak
+ %1$d rak%1$d ditemukan
+ %1$d hasil ditemukan%1$d cocok
+ %1$d kecocokan ditemukanBerkas dihapus permanen
+ Hapus File Secara PermanenAnda ingin menghapus secara permanen %1$d file yang dipilih dari perangkat Anda? Tindakan ini tidak dapat dibatalkan.
+ Apakah Anda ingin menghapus %1$d secara permanen file yang dipilih dari perangkat Anda? Tindakan ini tidak dapat dibatalkan.Apakah Anda ingin menghapus %1$d file yang dipilih dari daftar file terbaru? File tersebut akan muncul kembali jika Anda membukanya lagi dari perpustakaan.
+ Apakah Anda ingin menghapus %1$d file yang dipilih dari daftar file terbaru? Ini akan muncul kembali jika Anda membukanya lagi dari perpustakaan.Apakah Anda yakin ingin mengeluarkan %1$d buku dari rak \'%2$s\'? Buku-buku tersebut akan tetap berada di perpustakaan Anda dan muncul di bawah Tanpa rak.
+ Apakah Anda yakin ingin menghapus %1$d buku dari \'%2$s\' rak? Buku tersebut akan tetap ada di perpustakaan Anda dan muncul di bawah Unshelved.%1$d buku dikeluarkan dari perpustakaan.
+ %1$d buku dihapus dari perpustakaan.%1$d folder
+ %1$d map%1$d tagar
+ %1$d menandai(%1$d Potongan)
+ (%1$d potongan)
+
+
+ Mengimpor %1$d buku… Buku itu akan segera muncul di Perpustakaan Anda.
+ Mengimpor %1$d buku… Ini akan segera muncul di Perpustakaan Anda.
+
+
+ Impor %1$d buku. Anda dapat menemukannya di tab Perpustakaan.
+ Impor %1$d buku. Anda dapat menemukannya di tab Perpustakaan.
+
+
+ %1$d buku ditambahkan ke rak.
+ %1$d buku ditambahkan ke rak.
+
+
+ %1$d buku yang diberi tag "%2$s".
+ %1$d buku yang diberi tag "%2$s".
+
+
+ Folder "%1$s" dihapus dan %2$d buku dari aplikasi.
+ Folder "%1$s" dihapus dan %2$d buku dari aplikasi.
+
+
+ %1$d file
+ %1$d mengajukan
+
+
+ Jatuhkan untuk mengimpor %1$d file
+ Jatuhkan untuk mengimpor %1$d mengajukan
+
+
+ %1$d file yang tidak didukung akan dilewati.
+ %1$d file yang tidak didukung akan dilewati.
+
+
+ Mengimpor %1$d file…
+ Mengimpor %1$d mengajukan…
+
+
+ Impor %1$d file.
+ Impor %1$d mengajukan.
+
+
+ Impor %1$d file. Dukungan pembaca datang kemudian.
+ Impor %1$d mengajukan. Dukungan pembaca datang kemudian.
+
+
+ Tidak dapat mengimpor %1$d file.
+ Tidak dapat mengimpor %1$d mengajukan.
+
+
+ Dilewati %1$d file.
+ Dilewati %1$d mengajukan.
+
+
+ Hapus "%1$s" dan %2$dnya buku dari aplikasi? File di disk tidak akan dihapus.
+ Hapus "%1$s" dan %2$dnya pesan dari aplikasi? File di disk tidak akan dihapus.
+
+
+ Sinkronisasi folder gagal untuk %1$d folder.
+ Sinkronisasi folder gagal untuk %1$d map.
+
+
+ Sinkronisasi folder selesai dengan %1$d folder dilewati.
+ Sinkronisasi folder selesai dengan %1$d folder dilewati.
+
+
+ Dihapus %1$d streaming OPDS buku dari katalog itu.
+ Dihapus %1$d streaming OPDS buku dari katalog itu.
+
+
+ Semua Buku %1$d
+ Semua Buku %1$d
+
+
+ Rak %1$d
+ Rak %1$d
+
+
+ Tag %1$d
+ Tag %1$d
+
+
+ Folder %1$d
+ Folder %1$d
diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml
index 27bdc18..1b98e63 100644
--- a/app/src/main/res/values-in/strings.xml
+++ b/app/src/main/res/values-in/strings.xml
@@ -1474,4 +1474,42 @@
Ganti hanya yang dibacakanTeks pembaca, sorotan, dan posisi tetap tidak berubah.%1$s -> %2$s
+ Nonaktifkan sinkronisasi lokal
+ Aktifkan sinkronisasi lokal
+ Sinkronisasi lokal dinonaktifkan
+ Nonaktifkan sinkronisasi folder lokal?
+ Episteme akan berhenti memindai folder ini dan berhenti menulis JSON menyinkronkan file. Hapus %1$s folder dari folder ini juga?
+ Simpan data sinkronisasi
+ Hapus data sinkronisasi
+ Hapus Font?
+ Apakah Anda yakin ingin menghapus %1$d font yang dipilih? Ini akan menghapusnya dari semua perangkat Anda jika sinkronisasi aktif.
+ Tidak ada folder lokal yang sinkronisasinya diaktifkan.
+ Sinkronisasi folder lokal dinonaktifkan.
+ Sinkronisasi folder lokal dinonaktifkan. Folder data sinkronisasi dihapus.
+ Sinkronisasi folder lokal dinonaktifkan, namun folder data sinkronisasi tidak dapat dihapus.
+ Sinkronisasi folder lokal diaktifkan.
+ Vertikal (Tampilan Web)
+ Vertikal (Beta Asli)
+ Penggantian Kata Buku
+ Buku saat ini
+ Tambahkan aturan
+ Belum ada aturan pengganti untuk buku ini.
+ Pengganti baru
+ Edit penggantinya
+ Dengan
+ teks kosong
+ Akun & kredit
+ Ikhtisar akun
+ Episteme oss
+ Sinkronisasi awan
+ Bantuan
+ Tindakan perpustakaan
+ Lebih lanjut
+ Pembaca desktop offline
+ Rencana
+ Preferensi
+ Tab pembaca mati
+ Tab pembaca aktif
+ Sinkronkan akun, Pro, dan kredit
+ AI kunci
diff --git a/app/src/main/res/values-it/plurals.xml b/app/src/main/res/values-it/plurals.xml
index b3a8206..5c523bc 100644
--- a/app/src/main/res/values-it/plurals.xml
+++ b/app/src/main/res/values-it/plurals.xml
@@ -52,4 +52,88 @@
(%1$d segmento)(%1$d segmenti)
+
+ Importazione %1$d libro… Apparirà a breve nella tua Libreria.
+ Importazione %1$d libri… Appariranno a breve nella tua Libreria.
+
+
+ Importato %1$d libro. Puoi trovarlo nella scheda Libreria.
+ Importato %1$d libri. Li puoi trovare nella scheda Libreria.
+
+
+ %1$d libro aggiunto allo scaffale.
+ %1$d libri aggiunti allo scaffale.
+
+
+ %1$d libro contrassegnato con "%2$s".
+ %1$d libri contrassegnati con "%2$s".
+
+
+ Cartella rimossa "%1$s" e %2$d prenotare dall\'app.
+ Cartella rimossa "%1$s" e %2$d libri dall\'app.
+
+
+ %1$d file
+ %1$d file
+
+
+ Rilascia per importare %1$d file
+ Rilascia per importare %1$d file
+
+
+ %1$d il file non supportato verrà ignorato.
+ %1$d i file non supportati verranno ignorati.
+
+
+ Importazione %1$d cartella…
+ Importazione %1$d file...
+
+
+ Importato %1$d file.
+ Importato %1$d file.
+
+
+ Importato %1$d file. Il supporto del lettore arriva più tardi.
+ Importato %1$d file. Il supporto del lettore arriva più tardi.
+
+
+ Impossibile importare %1$d file.
+ Impossibile importare %1$d file.
+
+
+ Saltato %1$d file.
+ Saltato %1$d file.
+
+
+ Rimuovi "%1$s" e il relativo %2$d prenotare dall\'app? I file sul disco non verranno eliminati.
+ Rimuovi "%1$s" e il relativo %2$d libri dall\'app? I file sul disco non verranno eliminati.
+
+
+ Sincronizzazione della cartella non riuscita per %1$d cartella.
+ Sincronizzazione della cartella non riuscita per %1$d cartelle.
+
+
+ Sincronizzazione della cartella terminata con %1$d cartella saltata.
+ Sincronizzazione della cartella terminata con %1$d cartelle saltate.
+
+
+ Rimosso %1$d trasmesso in streaming OPDS libro da quel catalogo.
+ Rimosso %1$d trasmesso in streaming OPDS libri da quel catalogo.
+
+
+ Tutti i libri %1$d
+ Tutti i libri %1$d
+
+
+ Ripiani %1$d
+ Ripiani %1$d
+
+
+ Tag %1$d
+ Tag %1$d
+
+
+ Cartelle %1$d
+ Cartelle %1$d
+
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index 22ebf05..7de301b 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -1086,4 +1086,430 @@
Nederlands (olandese)Українська (ucraino)Bahasa Indonesia (indonesiano)
+ Mostra le schede nella barra dell\'app in alto
+ Disabilita la sincronizzazione locale
+ Abilita la sincronizzazione locale
+ Sincronizzazione locale disabilitata
+ Disabilitare la sincronizzazione delle cartelle locali?
+ Episteme interromperà la scansione di questa cartella e smetterà di scrivere JSON sincronizzare i file. Rimuovere %1$s cartella anche da questa cartella?
+ Conserva i dati sincronizzati
+ Rimuovi i dati di sincronizzazione
+ Eliminare i caratteri?
+ Sei sicuro di voler eliminare %1$d caratteri selezionati? Ciò li rimuoverà da tutti i tuoi dispositivi se la sincronizzazione è attiva.
+ Nessuna cartella locale ha la sincronizzazione abilitata.
+ Sincronizzazione delle cartelle locali disabilitata.
+ Sincronizzazione delle cartelle locali disabilitata. Cartella dati di sincronizzazione rimossa.
+ Sincronizzazione della cartella locale disabilitata, ma non è stato possibile rimuovere la cartella dei dati di sincronizzazione.
+ Sincronizzazione delle cartelle locali abilitata.
+ Verticale (Visualizzazione Web)
+ Verticale (Beta nativa)
+ Sostituzioni di parole del libro
+ Precedente TTS pezzo
+ Successivo TTS pezzo
+ Immagini
+ Nessuna immagine trovata.
+ Scarica l\'immagine
+ Salvato %1$s
+ Impossibile salvare l\'immagine.
+ PDF pagina diffusa
+ Pagina singola
+ Due pagine
+ Solo la prima pagina
+ Inizia le pagine affiancate dopo la copertina.
+ Luminosità
+ Utilizza la luminosità del sistema
+ Segue l\'impostazione della luminosità del dispositivo.
+ Luminosità personalizzata
+ Si applica mentre la schermata di un lettore è aperta.
+ %1$d%%
+ Scaffale creato "%1$s".
+ Creato scaffale intelligente "%1$s".
+ Scaffale rinominato in "%1$s".
+ Scaffale "%1$s" eliminato.
+ Aggiornato "%1$s".
+ Quei file sono già nella libreria.
+ %1$s - %2$s
+ Salva
+ Salva commento
+ Aggiungi commento
+ Rispondi
+ Aggiungi un commento...
+ Commenti
+ Modifica del commento
+ Rispondendo a %1$s
+ Utilizzare PDF Nomi dei file
+ Luminosità
+ Libro attuale
+ Aggiungi regola
+ Nessuna regola sostitutiva per questo libro ancora.
+ Nuova sostituzione
+ Modifica sostituzione
+ Con
+ testo vuoto
+ Circa
+ Lettore da tavolo
+ Accesso al desktop
+ Conto
+ Conto e crediti
+ Panoramica del conto
+ AI mozzo
+ Utilizzato per EPUB riepiloghi e PDF riepiloghi delle pagine.
+ Episteme oss
+ Testo dell\'autore
+ Cache: %1$s
+ Memorizzato nella cache
+ Riepilogo memorizzato nella cache
+ Scegliere Gemini voce utilizzata per la lettura ad alta voce nel cloud.
+ Elimina il libro desktop generato e EPUB file della cache di impaginazione? Verranno ricreati alla successiva apertura dei libri.
+ Cancella cache vocale
+ Chiudi strumenti
+ Sincronizzazione sul cloud
+ Nuvola TTS esigenze Gemini
+ Nuvola TTS necessita di crediti registrati
+ Nuvola TTS pronto
+ Nuvola TTS impostazioni
+ Nuvola TTS non disponibile
+ Nuvola TTS voce
+ Contiene
+ Calcolo dei costi
+ Crea un riepilogo fino alla tua posizione attuale.
+ Crea uno scaffale intelligente
+ %1$d crediti disponibili
+ %1$s crediti
+ Caratteri importati per il lettore
+ Elimina carattere
+ Elimina %1$s? I libri che lo utilizzano torneranno al carattere predefinito.
+ Eliminare \"%1$s\"? I libri rimangono nella tua libreria.
+ Elimina riepilogo
+ Disabilitato
+ Rilascia i file da importare
+ Rilascia i file supportati da importare
+ Contattaci direttamente via email per qualsiasi altra cosa.
+ Uguali
+ Extra
+ Feedback
+ Campo
+ Percorso della cartella
+ Da qui
+ Scansione completa
+ Gratuito, %1$d sinistra
+ Genera riepilogo
+ Genera riepilogo
+ Segnala bug, richiedi funzionalità o contatta direttamente l\'assistenza.
+ Sponsor di GitHub
+ Supportare lo sviluppo tramite gli sponsor GitHub.
+ Google l\'accesso non è configurato per questa build desktop.
+ Maggiore di
+ Aiuto
+ Segnalazioni di bug, richieste di funzionalità e supporto
+ Nasconditi
+ Importa file
+ Problemi
+ Apri il tracker dei problemi per bug e richieste di funzionalità.
+ Meno di
+ Biblioteca e lettore
+ Qualunque
+ Azioni della biblioteca
+ Di più
+ Nessun riepilogo ancora memorizzato nella cache per questo libro.
+ Importa file TTF, OTF o WOFF2 per usarli nei libri.
+ Nessun carattere trovato corrispondente a \"%1$s\"
+ No Google l\'account è collegato.
+ Nessun riepilogo memorizzato nella cache per questa sezione.
+ Lettore desktop offline
+ Lettori aperti
+ Apertura %1$s
+ Apertura della tua libreria
+ Operatore
+ Pagina
+ Protetto da password PDF
+ Patreon
+ Sostieni il progetto su Patreon.
+ In pausa
+ %1$s richiede una password prima di poter essere aperto.
+ La password è richiesta o errata.
+ Quella password non ha aperto %1$s. Immettere PDF password e riprovare.
+ Percentuale
+ Piano
+ Preparazione dell\'audio
+ Preferenze
+ Conto e crediti
+ Conto e crediti
+ Pro non è sbloccato per questo account.
+ Pro e crediti possono essere acquistati solo da Android app. Desktop controlla lo stesso account a cui è stato effettuato l\'accesso e utilizza tali crediti per il cloud TTS, riepiloghi, riepiloghi e altri pagamenti AI caratteristiche.
+ Accedi per verificare lo stato del tuo account sul desktop.
+ La versione Pro è sbloccata per questo account.
+ Progresso
+ Progetto
+ Lettore
+ Il lettore si spegne
+ Schede del lettore attive
+ Aggiorna
+ Rilascia per aggiungere alla tua libreria.
+ L\'archiviazione sicura delle chiavi non è disponibile su questo sistema operativo. Le chiavi immesse qui verranno utilizzate per questa sessione ma non verranno mantenute.
+ Hub delle impostazioni
+ Corrisponde a Android nascondi l\'interruttore per dizionario intelligente, riepiloghi e riepiloghi.
+ Sincronizza account, Pro e crediti
+ Effettuato l\'accesso
+ Codice sorgente
+ Sfoglia l\'origine del progetto su GitHub.
+ Smetti di leggere per cambiare voce.
+ Supporto
+ Supporto Episteme
+ I contributi aiutano il lettore a migliorare attraverso Android e desktop.
+ Modi per sostenere Episteme sviluppo
+ Sincronizza cartelle
+ Sincronizza i metadati
+ Nome dell\'etichetta
+ Tagga i libri selezionati
+ Testo del titolo
+ Strumenti
+ Importa, sincronizza e imposta le app
+ Digitare, ad es. PDF
+ Visualizza
+ Cache vocale
+ Preparazione della visualizzazione Web incorporata…
+ Preparazione della visualizzazione Web incorporata in bundle %1$d%%
+ Visualizzazione Web incorporata installata. Riavvia Episteme per completare la configurazione.
+ Impossibile avviare la visualizzazione Web incorporata: %1$s
+ Lavorando…
+ Spazio di lavoro
+ Aggiungi allo scaffale
+ Crea prima uno scaffale, quindi aggiungivi i libri selezionati.
+ Crea tema
+ Esistente: %1$s
+ Hai fatto clic su un collegamento esterno.
+ Modifica EPUB metadati
+ Meno
+ …di più
+ Nessun tema personalizzato ancora
+ Rinomina nell\'app
+ Tag separati da virgole
+ Sconosciuto
+ Definire
+ Annotazione
+ Opzioni di annotazione
+ Strumenti di annotazione
+ Assistere
+ Scegli quale PDF salvare.
+ Cancella la cronologia dei salti
+ Nuvola TTS fallito.
+ Aggiungi un Gemini e selezionare Gemini nuvola TTS in AI chiavi e modelli.
+ Nuvola TTS non è configurato per questa build desktop.
+ Accedi con Google utilizzare il cloud TTS.
+ Nuvola TTS necessita di un account registrato con crediti. Pro e crediti possono essere acquistati solo da Android app.
+ Colore
+ Opzioni di commento
+ Personalizzato
+ Ciò rimuove l\'annotazione da questo PDF.
+ Eliminare l\'annotazione?
+ Testo del documento
+ Incorporato PDF commento
+ Impossibile eseguire il rendering della pagina.
+ Funzionalità non disponibile
+ Finito
+ Penna stilografica
+ Nascondi i risultati della ricerca
+ Colore evidenziazione %1$d
+ Tavolozza di evidenziatori
+ Interazione
+ Indicizzazione %1$d/%2$d pagine
+ Markup
+ %1$d partite
+ %1$d partite finora
+ Pagina successiva
+ Risultato della ricerca successivo
+ Nessuna annotazione ancora
+ Nessun segnalibro ancora
+ Nessun commento
+ Nessuna corrispondenza
+ Ancora nessuna corrispondenza nelle pagine indicizzate
+ Nessun sommario
+ Non c\'è testo qui da leggere.
+ Non c\'è testo da leggere in questa pagina.
+ Non c\'è testo da riassumere.
+ Apri commento
+ Crediti esauriti. Pro e crediti possono essere acquistati solo da Android app.
+ Utilizzo del cloud TTS necessita di crediti sul desktop. Pro e crediti possono essere acquistati solo da Android app.
+ L\'utilizzo di questa funzionalità richiede crediti sul desktop. Pro e crediti possono essere acquistati solo da Android app.
+ L\'utilizzo dei riepiloghi richiede crediti sul desktop. Pro e crediti possono essere acquistati solo da Android app.
+ L\'utilizzo dei riepiloghi richiede crediti sul desktop. Pro e crediti possono essere acquistati solo da Android app.
+ Pan
+ PDF azione fallita
+ Il PDF Impossibile completare l\'azione.
+ PDF commento
+ P. %1$d
+ PDF pagina %1$d
+ Pagina %1$d - %2$s
+ Pagina %1$s di %2$d
+ Pagine %1$s di %2$d
+ PDF salvato
+ PDF strumenti
+ Matita
+ Preparazione della selezione
+ Preparazione %1$s
+ Pagina precedente
+ Risultato della ricerca precedente
+ La finestra di dialogo di stampa è terminata.
+ Pro richiesto
+ Questa funzionalità richiede Pro. Pro può essere acquistato solo da Android app, il desktop utilizzerà l\'account aggiornato dopo l\'accesso.
+ Il dizionario intelligente di più parole richiede Pro. Pro può essere acquistato solo da Android app, il desktop utilizzerà l\'account aggiornato dopo l\'accesso.
+ Lettore AI le funzionalità sono nascoste.
+ Desktop AI non è configurato per questa build.
+ Si applica alla lettura verticale e alle pagine doppie.
+ Evidenziatore rotondo
+ Salvato in %1$s
+ Scorri
+ Cerca in PDF
+ Seleziona il testo
+ Selezionato %1$s
+ Mostra i risultati della ricerca
+ Accedi con Google per utilizzare questa funzionalità sul desktop.
+ Accedi con Google per utilizzare il dizionario intelligente di più parole sul desktop.
+ Accedi con Google per utilizzare i riepiloghi sul desktop.
+ Accedi con Google per utilizzare i riepiloghi sul desktop.
+ Fermato
+ Nota di testo
+ nota di testo
+ Stile del testo
+ Spessore %1$s
+ SOMMARIO
+ Digita per cercare questo PDF
+ Senza titolo
+ Visualizza account e crediti
+ Cache vocale cancellata
+ Zoom
+ Ingrandisci
+ Rimpicciolisci
+ Scegli
+ Continua a leggere
+ Ignora
+ Giù
+ Su
+ AI
+ Centro
+ Autori
+ Ritorno in biblioteca
+ Azioni del libro
+ Cartella
+ Sfoglia
+ Categorie
+ cap. %1$d
+ Il capitolo gira
+ Scegli il carattere
+ Scegli la trama del lettore
+ Cancella tipi di file
+ Cancellare le annotazioni della pagina
+ Fonti chiare
+ Stato chiaro
+ Cancella tag
+ Lettore vicino
+ Continuo
+ Copertine
+ Colori personalizzati
+ Anteprima del tema personalizzato
+ Diminuisci %1$s
+ Definire la pagina
+ Ciò rimuove l\'evidenziazione e la relativa nota.
+ Entra a schermo intero
+ Esci dallo schermo intero
+ Ricerca esterna
+ Libri
+ Fumetti
+ Documenti
+ Altro
+ Testo e web
+ Riempi
+ Aspetto con layout fisso
+ La cartella è vuota
+ Qui non sono disponibili file o sottocartelle supportati.
+ %1$s, %2$s
+ %1$s - %2$s
+ Nascondi filtri
+ Nascondi gli strumenti di lettura
+ Tocca uno slot, quindi scegli un colore.
+ Continua a leggere e libri recenti
+ Importa libri
+ Cartella di importazione
+ Caratteri importati
+ %1$s %2$s
+ Aumenta %1$s
+ Salta la cronologia
+ Disposizione e spaziatura
+ Importa file nell\'archivio app o aggiungi una cartella per leggere i file sul posto.
+ Sfoglia la tua collezione
+ AI chiavi
+ Intelligente %1$d
+ Non letto %1$d
+ In corso %1$d
+ Completa %1$d
+ Elenco
+ Navigazione
+ Nessun libro aperto
+ Aggiungi una cartella per leggere i file da quella cartella sul posto.
+ Nessuna cartella ancora
+ Nessun elemento di navigazione
+ Nessun contenuto della pagina
+ Nessuna impostazione trovata
+ Gli scaffali manuali e le raccolte di serie appariranno qui.
+ Ancora nessuno scaffale
+ Crea scaffali intelligenti per raccogliere libri secondo regole.
+ Non ci sono ancora scaffali intelligenti
+ I tag aggiunti ai libri verranno visualizzati qui.
+ Nessun tag ancora
+ Non è stato importato alcun file supportato.
+ Catalogo
+ Eliminare "%1$s"? I libri trasmessi in streaming da questo catalogo potrebbero interrompersi se le credenziali cambiano in seguito.
+ Nessun cataloghi
+ Aggiungi un OPDS catalogo per sfogliare libri a distanza.
+ Sfoglia cataloghi, stream e download
+ Libro aperto
+ Apri cartella
+ Apri PDF
+ Colori della pagina e del testo
+ Informazioni sulla pagina
+ Larghezza della pagina
+ Queste impostazioni predefinite si applicano laddove la piattaforma supporta PDF condiviso aspetto. Per libro PDF sostituisce il soggiorno in PDF lettore.
+ PDF azioni sui file
+ PDF evidenziatore
+ Salvato con la trasparenza dell\'evidenziazione del lettore.
+ Perno
+ Gestito dal lettore PDF strumenti
+ Lo scorrimento automatico, OCR, le impostazioni predefinite delle annotazioni e la visibilità dello strumento solo PDF sono gestiti all\'interno del PDF attivo. lettore.
+ %1$s %2$s di %3$d (%4$d%%)
+ Le impostazioni predefinite della barra degli strumenti del lettore sono gestite dal lettore su questa piattaforma.
+ Strumenti di lettura
+ Salva immagine
+ Cerca: %1$s
+ Cerca nel lettore
+ Impostazioni di ricerca
+ Selezione
+ Maniglia fine selezione
+ Maniglia di inizio selezione
+ Aggiungi scaffali, tag o metadati di cartelle per organizzare la tua libreria.
+ Raccolte, serie, tag e cartelle
+ Mostra gli strumenti di lettura
+ Cartella
+ Intelligente
+ Solido
+ Velocità
+ Avvia lo scorrimento automatico
+ Arresta lo scorrimento automatico
+ Smetti di leggere ad alta voce
+ Forza della trama
+ Digita per cercare in questo libro
+ Tipografia
+ Annulla annotazione
+ Sblocca
+ Utilizza il tema scuro
+ Usa il tema chiaro
+ Mono
+ Sans
+ Serif
+ Cerca libri, autori o tag
+ Nessuno strumento
+ Visibile
+ Sostituisci solo ciò che viene detto
+ Il testo, le evidenziazioni e le posizioni del lettore rimangono invariati.
+ %1$s -> %2$s
diff --git a/app/src/main/res/values-ja/plurals.xml b/app/src/main/res/values-ja/plurals.xml
index be50ff1..87f3ae2 100644
--- a/app/src/main/res/values-ja/plurals.xml
+++ b/app/src/main/res/values-ja/plurals.xml
@@ -2,41 +2,138 @@
%1$d冊の本
+ %1$d本本
+ 本%1$d個の本棚
+ %1$d棚%1$d件の結果が見つかりました
+ %1$d結果が見つかりました%1$d件の一致が見つかりました
+ %1$d一致が見つかりましたファイルを完全に削除
+ ファイルを完全に削除選択した%1$d個のファイルをデバイスから完全に削除しますか?この操作は元に戻せません。
+ %1$d を完全に削除しますか?デバイスからファイルを選択しましたか?この操作は元に戻すことができません。選択した%1$d個のファイルを最近使ったファイルから削除しますか?ライブラリからもう一度開くと再表示されます。
+ %1$d を削除しますか?最近使用したファイルのリストからファイルを選択しましたか?ライブラリから再度開くと再び表示されます。%1$d冊の本を本棚「%2$s」から削除しますか?本はライブラリに残り、本棚未設定に表示されます。
+ %1$d 削除してもよろしいですか? \'%2$s\' の本棚?この本はライブラリに残り、「未棚」の下に表示されます。%1$d冊の本をライブラリから削除しました。
+ %1$d図書館から削除された本。%1$d個のフォルダー
+ %1$dフォルダ%1$d個のタグ
+ %1$dタグ(%1$dチャンク)
+ (%1$d チャンク)
+
+
+ インポート中 %1$d本… すぐにライブラリに表示されます。
+ インポート中 %1$d本…まもなくライブラリに表示されます。
+
+
+ 輸入品 %1$d本。これらは「ライブラリ」タブで見つけることができます。
+ 輸入品 %1$d本。 [ライブラリ]タブで見つけることができます。
+
+
+ %1$d本が棚に追加されました。
+ %1$d本が棚に追加されました。
+
+
+ %1$d 「%2$s」のタグが付けられた本。
+ %1$d 「%2$s」のタグが付けられた本。
+
+
+ フォルダー「%1$s」を削除しましたそして %2$dアプリから本を。
+ フォルダー「%1$s」を削除しましたそして %2$dアプリから予約します。
+
+
+ %1$dファイル
+ %1$dファイル
+
+
+ ドロップしてインポート %1$dファイル
+ ドロップしてインポート %1$dファイル
+
+
+ %1$dサポートされていないファイルはスキップされます。
+ %1$dサポートされていないファイルはスキップされます。
+
+
+ インポート中 %1$dファイル…
+ インポート中 %1$dファイル…
+
+
+ 輸入品 %1$dファイル。
+ 輸入品 %1$dファイル。
+
+
+ 輸入品 %1$dファイル。読者サポートは後で提供されます。
+ 輸入品 %1$dファイル。読者サポートは後で提供されます。
+
+
+ %1$d をインポートできませんでしたファイル。
+ %1$d をインポートできませんでしたファイル。
+
+
+ スキップされました %1$dファイル。
+ スキップされました %1$dファイル。
+
+
+ 「%1$s」を削除しますそしてその %2$dアプリから本を?ディスク上のファイルは削除されません。
+ 「%1$s」を削除しますそしてその %2$dアプリから予約しますか?ディスク上のファイルは削除されません。
+
+
+ %1$d のフォルダー同期が失敗しましたフォルダー。
+ %1$d のフォルダー同期が失敗しましたフォルダ。
+
+
+ フォルダーの同期は %1$d で終了しましたフォルダーはスキップされました。
+ フォルダーの同期は %1$d で終了しましたフォルダがスキップされました。
+
+
+ 削除されました %1$dストリーミング OPDSそのカタログからの本。
+ 削除されました %1$dストリーミング OPDSそのカタログからの本。
+
+
+ すべての書籍 %1$d
+ すべての書籍 %1$d
+
+
+ 棚 %1$d
+ 棚 %1$d
+
+
+ タグ %1$d
+ タグ %1$d
+
+
+ フォルダー %1$d
+ フォルダー %1$d
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index 08a442f..51631a6 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -1086,4 +1086,430 @@
Nederlands (オランダ語)Українська (ウクライナ語)Bahasa Indonesia (インドネシア語)
+ トップアプリバーにタブを表示
+ ローカル同期を無効にする
+ ローカル同期を有効にする
+ ローカル同期が無効になっています
+ ローカルフォルダーの同期を無効にしますか?
+ Epistemeこのフォルダのスキャンを停止し、書き込みを停止します JSONファイルを同期します。 %1$s を取り外します。このフォルダからもフォルダを作成しますか?
+ 同期データを維持する
+ 同期データを削除する
+ フォントを削除しますか?
+ %1$d 削除してもよろしいですか?選択されたフォント?同期がオンになっている場合、これによりすべてのデバイスからそれらが削除されます。
+ 同期が有効になっているローカル フォルダーはありません。
+ ローカルフォルダーの同期が無効になっています。
+ ローカルフォルダーの同期が無効になっています。同期データフォルダーが削除されました。
+ ローカル フォルダーの同期は無効になっていますが、同期データ フォルダーを削除できませんでした。
+ ローカルフォルダーの同期が有効になりました。
+ 縦型 (WebView)
+ 垂直 (ネイティブ ベータ版)
+ 書籍の単語の置換
+ 前 TTSかたまり
+ 次へ TTSかたまり
+ 画像
+ 画像が見つかりませんでした。
+ 画像をダウンロード
+ 保存しました %1$s
+ 画像を保存できませんでした。
+ PDF見開きページ
+ 単一ページ
+ 2ページ
+ 最初のページだけ
+ 表紙の後に見開きページが始まります。
+ 輝度
+ システムの明るさを使用する
+ デバイスの明るさ設定に従います。
+ カスタムの明るさ
+ リーダー画面が開いているときに適用されます。
+ %1$d%%
+ シェルフ「%1$s」を作成しました。
+ スマートシェルフ「%1$s」を作成しました。
+ シェルフの名前を「%1$s」に変更しました。
+ シェルフ「%1$s」を削除しました。
+ 「%1$s」を更新しました。
+ これらのファイルはすでにライブラリにあります。
+ %1$s - %2$s
+ 保存
+ コメントの保存
+ コメントの追加
+ 返事
+ コメントを追加…
+ コメント
+ コメントの編集
+ %1$s に返信します
+ PDF を使用しますファイル名
+ 輝度
+ 現在の本
+ ルールの追加
+ この本の代替ルールはまだありません。
+ 新しい交換品
+ 置換の編集
+ と
+ 空のテキスト
+ について
+ デスクトップリーダー
+ デスクトップアクセス
+ アカウント
+ アカウントとクレジット
+ アカウントの概要
+ AIハブ
+ EPUB に使用概要と PDFページの概要。
+ Epistemeオス
+ 著者のテキスト
+ キャッシュ: %1$s
+ キャッシュされた
+ キャッシュされた概要
+ Gemini を選択してくださいクラウドの読み上げに使用される音声。
+ 生成されたデスクトップ ブックと EPUB を削除します。ページネーションキャッシュファイル?次回本を開いたときに再作成されます。
+ 音声キャッシュをクリアする
+ ツールを閉じる
+ クラウド同期
+ クラウド TTS Gemini が必要です
+ クラウド TTSサインインしたクレジットが必要です
+ クラウド TTS準備ができて
+ クラウド TTS設定
+ クラウド TTS利用不可
+ クラウド TTS声
+ 含まれています
+ コスト計算
+ 現在の立場までの要約を作成します。
+ スマートシェルフを作成する
+ %1$d利用可能なクレジット
+ %1$sクレジット
+ リーダー用にインポートされたフォント
+ フォントの削除
+ %1$s を削除しますか?これを使用する書籍はデフォルトのフォントに戻ります。
+ 「%1$s\」を削除しますか?本はあなたの図書館に残ります。
+ 概要の削除
+ 無効
+ インポートするファイルをドロップします
+ サポートされているファイルをドロップしてインポートする
+ それ以外のことについては、メールで直接お問い合わせください。
+ 等しい
+ エクストラ
+ フィードバック
+ 分野
+ フォルダーパス
+ ここから
+ フルスキャン
+ 無料、%1$d左
+ 要約を生成する
+ 概要の生成
+ バグを報告したり、機能をリクエストしたり、サポートに直接連絡したりできます。
+ GitHub スポンサー
+ GitHub スポンサーを通じて開発をサポートします。
+ Googleこのデスクトップ ビルドにはサインインが構成されていません。
+ より大きい
+ ヘルプ
+ バグレポート、機能リクエスト、サポート
+ 隠れる
+ ファイルをインポートする
+ 問題
+ バグや機能リクエストについては、問題トラッカーを開いてください。
+ 未満
+ ライブラリとリーダー
+ どれでも
+ ライブラリのアクション
+ もっと
+ この本のキャッシュされた要約はまだありません。
+ TTF、OTF、または WOFF2 ファイルをインポートして書籍で使用します。
+ 「%1$s\」に一致するフォントが見つかりませんでした
+ いいえ Googleアカウントが接続されています。
+ このセクションの要約はキャッシュされていません。
+ オフラインデスクトップリーダー
+ オープンリーダー
+ オープニング %1$s
+ ライブラリを開く
+ オペレーター
+ ページ
+ パスワードで保護されています PDF
+ パトレオン
+ Patreon でプロジェクトをサポートしてください。
+ 一時停止中
+ %1$s開く前にパスワードが必要です。
+ パスワードが必要か、間違っています。
+ そのパスワードは %1$s を開けませんでした。 PDF を入力します。パスワードを入力して再試行してください。
+ パーセント
+ プラン
+ 音声の準備中
+ 設定
+ アカウントとクレジット
+ アカウントとクレジット
+ このアカウントでは Pro のロックが解除されていません。
+ Pro とクレジットは Android からのみ購入できます。アプリ。デスクトップは同じサインイン アカウントをチェックし、それらのクレジットをクラウド TTS、概要、要約、その他の有料 AI に使用します。特徴。
+ サインインしてデスクトップでアカウントのステータスを確認します。
+ このアカウントでは Pro のロックが解除されています。
+ 進捗
+ プロジェクト
+ リーダー
+ リーダーのタブがオフになっています
+ リーダータブ
+ リフレッシュ
+ リリースしてライブラリに追加します。
+ このオペレーティング システムでは安全なキー ストレージを利用できません。ここに入力されたキーはこのセッションで使用されますが、永続化されません。
+ 設定ハブ
+ Android と一致しますスマート辞書、要約、要約のトグルを非表示にします。
+ アカウント、Pro、クレジットを同期する
+ サインインしました
+ ソースコード
+ GitHub でプロジェクトのソースを参照します。
+ 声を変えるには読むのをやめてください。
+ サポート
+ サポート Episteme
+ 貢献は Android 全体で読者の向上を維持するのに役立ちます。そしてデスクトップ。
+ Episteme をサポートする方法発達
+ フォルダーを同期する
+ メタデータを同期する
+ タグ名
+ 選択した書籍にタグを付ける
+ タイトルテキスト
+ ツール
+ インポート、同期、アプリ設定
+ タイプ: 例: PDF
+ ビュー
+ 音声キャッシュ
+ 埋め込み Web ビューを準備しています…
+ バンドルされた埋め込み Web ビューを準備しています %1$d%%
+ 埋め込みWebViewがインストールされています。再起動 Epistemeセットアップを終了します。
+ 埋め込み Web ビューを開始できませんでした: %1$s
+ 働く…
+ ワークスペース
+ 棚に追加
+ まず棚を作成し、選択した本をそこに追加します。
+ テーマの作成
+ 既存: %1$s
+ 外部リンクをクリックしました。
+ 編集 EPUBメタデータ
+ 少ない
+ …もっと
+ カスタムテーマはまだありません
+ アプリ内で名前を変更する
+ タグ、カンマ区切り
+ 未知
+ 定義する
+ 注釈
+ 注釈オプション
+ 注釈ツール
+ アシスト
+ PDF どちらを選択してください保存するために。
+ ジャンプ履歴をクリアする
+ クラウド TTS失敗した。
+ Gemini を追加しますキーを押して Gemini を選択します。クラウド TTS AI でキーとモデル。
+ クラウド TTSはこのデスクトップ ビルド用に構成されていません。
+ Google でサインインしますクラウド TTS を使用します。
+ クラウド TTSクレジットのあるサインインしたアカウントが必要です。 Pro とクレジットは Android からのみ購入できます。アプリ。
+ 色
+ コメントオプション
+ カスタム
+ これにより、この PDF から注釈が削除されます。
+ 注釈を削除しますか?
+ 文書テキスト
+ 埋め込み型 PDFコメント
+ ページのレンダリングに失敗しました。
+ 利用できない機能
+ 終了した
+ 万年筆
+ 検索結果を非表示にする
+ ハイライトカラー %1$d
+ ハイライトパレット
+ 交流
+ インデックス作成 %1$d/%2$dページ
+ マークアップ
+ %1$dマッチ
+ %1$dこれまでの試合
+ 次のページ
+ 次の検索結果
+ まだ注釈はありません
+ まだブックマークはありません
+ ノーコメント
+ 一致しません
+ インデックスされたページに一致するものはまだありません
+ 目次なし
+ ここには読むべきテキストはありません。
+ このページには読むべきテキストはありません。
+ 要約するテキストはありません。
+ コメントを開く
+ クレジットが不足しています。 Pro とクレジットは Android からのみ購入できます。アプリ。
+ クラウドの使用 TTSデスクトップにクレジットが必要です。 Pro とクレジットは Android からのみ購入できます。アプリ。
+ この機能を使用するには、デスクトップにクレジットが必要です。 Pro とクレジットは Android からのみ購入できます。アプリ。
+ Recaps を使用するには、デスクトップにクレジットが必要です。 Pro とクレジットは Android からのみ購入できます。アプリ。
+ 概要を使用するには、デスクトップにクレジットが必要です。 Pro とクレジットは Android からのみ購入できます。アプリ。
+ パン
+ PDFアクションが失敗しました
+ PDFアクションを完了できませんでした。
+ PDFコメント
+ p. %1$d
+ PDFページ %1$d
+ ページ %1$d - %2$s
+ ページ %1$s %2$d
+ ページ %1$s %2$d
+ PDF保存されました
+ PDFツール
+ 鉛筆
+ 選択範囲を準備しています
+ 準備中 %1$s
+ 前のページへ
+ 前回の検索結果
+ 印刷ダイアログが終了しました。
+ プロが必要
+ この機能には Pro が必要です。 Pro は Android からのみ購入できます。アプリの場合、デスクトップではサインイン後にアップグレードされたアカウントが使用されます。
+ 複数の単語のスマート辞書には Pro が必要です。 Pro は Android からのみ購入できます。アプリの場合、デスクトップではサインイン後にアップグレードされたアカウントが使用されます。
+ リーダー AI特徴が隠されています。
+ デスクトップ AIはこのビルド用に構成されていません。
+ 縦読みおよび見開きに適用されます。
+ ラウンドハイライター
+ %1$s に保存されました
+ スクロール
+ PDF で検索します
+ テキストを選択
+ 選択済み %1$s
+ 検索結果を表示する
+ Google でサインインしますこの機能をデスクトップで使用するには、
+ Google でサインインしますデスクトップで複数単語のスマート辞書を使用するには。
+ Google でサインインしますデスクトップで要約を使用するには。
+ Google でサインインしますデスクトップで概要を使用します。
+ 停止しました
+ テキストメモ
+ テキストメモ
+ テキストスタイル
+ 厚さ %1$s
+ 目次
+ PDF と入力して検索します
+ 無題
+ アカウントとクレジットを表示する
+ 音声キャッシュがクリアされました
+ ズーム
+ ズームイン
+ ズームアウト
+ 選ぶ
+ 続きを読む
+ 却下する
+ 下
+ 上
+ AI
+ 中心
+ 著者
+ ライブラリに戻る
+ 予約アクション
+ フォルダ
+ ブラウズ
+ カテゴリー
+ Ch. %1$d
+ 章の変わり目
+ フォントを選択してください
+ リーダーのテクスチャを選択する
+ クリアファイルの種類
+ ページの注釈をクリアする
+ 出典を明確にする
+ クリアステータス
+ タグをクリアする
+ 読者を閉じる
+ 継続的
+ カバー
+ カスタムカラー
+ カスタムテーマのプレビュー
+ 減少 %1$s
+ ページの定義
+ これにより、ハイライトとそのメモが削除されます。
+ 全画面表示に入る
+ 全画面表示を終了する
+ 外部ルックアップ
+ 本
+ 漫画
+ 書類
+ 他の
+ テキストとウェブ
+ 埋める
+ 固定レイアウトの外観
+ フォルダーが空です
+ ここでは、サポートされているファイルやサブフォルダーは利用できません。
+ %1$s、%2$s
+ %1$s - %2$s
+ フィルターを非表示にする
+ リーダーツールを非表示にする
+ スロットをタップして、色を選択します。
+ 続きを読むと最近の本
+ 本を輸入する
+ インポートフォルダー
+ インポートされたフォント
+ %1$s %2$s
+ 増加 %1$s
+ ジャンプ履歴
+ レイアウトと間隔
+ ファイルをアプリのストレージにインポートするか、フォルダーを追加してファイルを所定の場所に読み込みます。
+ コレクションを閲覧する
+ AIキー
+ スマート %1$d
+ 未読 %1$d
+ 進行中 %1$d
+ 完了 %1$d
+ リスト
+ ナビゲーション
+ 本が開いていない
+ 適切な場所にフォルダーを追加して、そのフォルダーからファイルを読み取ります。
+ まだフォルダがありません
+ ナビゲーション項目はありません
+ ページコンテンツがありません
+ 設定が見つかりません
+ 手動棚やシリーズコレクションがここに表示されます。
+ まだ棚がありません
+ ルールに従って本を収集するためのスマートな棚を作成します。
+ スマートシェルフはまだありません
+ 書籍に追加されたタグがここに表示されます。
+ まだタグがありません
+ サポートされているファイルはインポートされませんでした。
+ カタログ
+ 「%1$s」を削除しますか?資格情報が後で変更されると、このカタログからストリーミングされた書籍が開かなくなる可能性があります。
+ カタログなし
+ OPDS を追加しますリモートの書籍を閲覧するためのカタログ。
+ カタログ、ストリーム、ダウンロードを閲覧する
+ 開いた本
+ フォルダーを開く
+ 開く PDF
+ ページとテキストの色
+ ページ情報
+ ページ幅
+ これらのデフォルトは、プラットフォームが共有 PDF をサポートする場合に適用されます。外観。本ごと PDFオーバーライドは PDF に残ります。リーダー。
+ PDFファイルアクション
+ PDFハイライター
+ リーダーのハイライト透明度を使用して保存されます。
+ ピン
+ リーダー管理 PDFツール
+ 自動スクロール、OCR、注釈のデフォルト、および PDF のみのツールの表示設定は、アクティブな PDF 内で管理されます。リーダー。
+ %1$s %2$s %3$d (%4$d%%)
+ リーダー ツールバーのデフォルトは、このプラットフォームのリーダーから管理されます。
+ リーダーツール
+ 画像の保存
+ 検索: %1$s
+ リーダー内で検索
+ 検索設定
+ 選択
+ 選択終了ハンドル
+ 選択開始ハンドル
+ シェルフ、タグ、またはフォルダーのメタデータを追加して、ライブラリを整理します。
+ コレクション、シリーズ、タグ、フォルダー
+ リーダーツールを表示
+ フォルダ
+ 頭いい
+ 固体
+ スピード
+ 自動スクロールを開始します
+ 自動スクロールを停止する
+ 読み上げをやめる
+ 質感の強さ
+ この本を検索するには入力してください
+ タイポグラフィ
+ 注釈を元に戻す
+ 固定を解除する
+ ダークテーマを使用する
+ ライトテーマを使用する
+ 単核症
+ サンズ
+ セリフ
+ 書籍、著者、またはタグを検索する
+ 工具なし
+ 見える
+ 話されている内容だけを置き換える
+ リーダーのテキスト、ハイライト、位置は変更されません。
+ %1$s -> %2$s
diff --git a/app/src/main/res/values-ko/plurals.xml b/app/src/main/res/values-ko/plurals.xml
index 23ccbcf..77df96c 100644
--- a/app/src/main/res/values-ko/plurals.xml
+++ b/app/src/main/res/values-ko/plurals.xml
@@ -2,41 +2,138 @@
책 %1$d권
+ %1$d 책책
+ 책책장 %1$d개
+ %1$d 선반결과 %1$d개 발견
+ %1$d 결과를 찾았습니다일치 항목 %1$d개 발견
+ %1$d 일치하는 항목을 찾았습니다.파일 영구 삭제
+ 파일을 영구적으로 삭제선택한 파일 %1$d개를 기기에서 영구적으로 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다.
+ %1$d을(를) 영구적으로 삭제하시겠습니까? 장치에서 파일을 선택하셨나요? 이 작업은 취소할 수 없습니다.선택한 파일 %1$d개를 최근 파일 목록에서 제거하시겠습니까? 라이브러리에서 다시 열면 다시 표시됩니다.
+ %1$d을(를) 제거하시겠습니까? 최근 파일 목록에서 선택한 파일은 무엇입니까? 라이브러리에서 다시 열면 다시 나타납니다.책 %1$d권을 “%2$s” 책장에서 제거하시겠습니까? 책은 라이브러리에 남아 미분류로 표시됩니다.
+ %1$d을(를) 제거하시겠습니까? \'%2$s\' 선반? 책은 라이브러리에 남아 있으며 Unshelved 아래에 표시됩니다.책 %1$d권을 라이브러리에서 제거했습니다.
+ %1$d 도서관에서 책이 제거되었습니다.폴더 %1$d개
+ %1$d 폴더태그 %1$d개
+ %1$d 태그(%1$d개 청크)
+ (%1$d 청크)
+
+
+ 가져오는 중 %1$d 책… 곧 라이브러리에 표시됩니다.
+ 가져오는 중 %1$d 책… 곧 라이브러리에 표시됩니다.
+
+
+ 가져옴 %1$d 서적. 라이브러리 탭에서 찾을 수 있습니다.
+ 가져옴 %1$d 책. 라이브러리 탭에서 찾을 수 있습니다.
+
+
+ %1$d 책장에 책이 추가되었습니다.
+ %1$d 책이 서가에 추가되었습니다.
+
+
+ %1$d "%2$s" 태그가 붙은 책.
+ %1$d "%2$s" 태그가 붙은 책입니다.
+
+
+ "%1$s" 폴더가 제거되었습니다. 그리고 %2$d 앱에서 책을 읽습니다.
+ "%1$s" 폴더가 제거되었습니다. 그리고 %2$d 앱에서 예약하세요.
+
+
+ %1$d 파일
+ %1$d 파일
+
+
+ 가져오기로 드롭 %1$d 파일
+ 가져오기로 드롭 %1$d 파일
+
+
+ %1$d 지원되지 않는 파일은 건너뜁니다.
+ %1$d 지원되지 않는 파일은 건너뜁니다.
+
+
+ 가져오는 중 %1$d 파일…
+ 가져오는 중 %1$d 파일…
+
+
+ 가져옴 %1$d 파일.
+ 가져옴 %1$d 파일.
+
+
+ 가져옴 %1$d 파일. 독자 지원은 나중에 제공됩니다.
+ 가져옴 %1$d 파일. 독자 지원은 나중에 제공됩니다.
+
+
+ %1$d가져올 수 없습니다. 파일.
+ %1$d가져올 수 없습니다. 파일.
+
+
+ 건너뛰었습니다 %1$d 파일.
+ 건너뛰었습니다 %1$d 파일.
+
+
+ "%1$s" 제거 그리고 %2$d 앱에서 책을? 디스크에 있는 파일은 삭제되지 않습니다.
+ "%1$s" 제거 그리고 %2$d 앱에서 예약하시겠어요? 디스크에 있는 파일은 삭제되지 않습니다.
+
+
+ %1$d에 대한 폴더 동기화가 실패했습니다. 폴더.
+ %1$d에 대한 폴더 동기화가 실패했습니다. 접는 사람.
+
+
+ %1$d로 폴더 동기화가 완료되었습니다. 폴더를 건너뛰었습니다.
+ %1$d로 폴더 동기화가 완료되었습니다. 폴더를 건너뛰었습니다.
+
+
+ 제거됨 %1$d 스트리밍됨 OPDS 그 카탈로그의 책들.
+ 제거됨 %1$d 스트리밍됨 OPDS 그 카탈로그에서 책을 읽으세요.
+
+
+ 모든 책 %1$d
+ 모든 책 %1$d
+
+
+ 선반 %1$d
+ 선반 %1$d
+
+
+ 태그 %1$d
+ 태그 %1$d
+
+
+ 폴더 %1$d
+ 폴더 %1$d
diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml
index 4445a1c..4691082 100644
--- a/app/src/main/res/values-ko/strings.xml
+++ b/app/src/main/res/values-ko/strings.xml
@@ -1086,4 +1086,430 @@
Nederlands (네덜란드어)Українська (우크라이나어)Bahasa Indonesia (인도네시아어)
+ 상단 앱 바에 탭 표시
+ 로컬 동기화 비활성화
+ 로컬 동기화 활성화
+ 로컬 동기화가 비활성화되었습니다.
+ 로컬 폴더 동기화를 비활성화하시겠습니까?
+ Episteme 이 폴더 스캔을 중지하고 쓰기를 중지합니다 JSON 동기화 파일. %1$s를 제거하세요. 이 폴더에도 있나요?
+ 동기화 데이터 유지
+ 동기화 데이터 삭제
+ 글꼴을 삭제하시겠습니까?
+ 정말로 %1$d을(를) 삭제하시겠습니까? 선택한 글꼴? 동기화가 켜져 있으면 모든 기기에서 해당 항목이 제거됩니다.
+ 동기화가 활성화된 로컬 폴더가 없습니다.
+ 로컬 폴더 동기화가 비활성화되었습니다.
+ 로컬 폴더 동기화가 비활성화되었습니다. 동기화 데이터 폴더가 제거되었습니다.
+ 로컬 폴더 동기화가 비활성화되었지만 동기화 데이터 폴더를 제거할 수 없습니다.
+ 로컬 폴더 동기화가 활성화되었습니다.
+ 수직(WebView)
+ 수직(네이티브 베타)
+ 책 단어 교체
+ 이전 TTS 덩어리
+ 다음 TTS 덩어리
+ 이미지
+ 이미지를 찾을 수 없습니다.
+ 이미지 다운로드
+ 저장됨 %1$s
+ 이미지를 저장할 수 없습니다.
+ PDF 페이지 펼치기
+ 단일 페이지
+ 두 페이지
+ 첫 페이지만
+ 표지 이후 양면 페이지 펼치기를 시작합니다.
+ 밝기
+ 시스템 밝기 사용
+ 장치 밝기 설정을 따릅니다.
+ 맞춤 밝기
+ 리더 화면이 열려 있는 동안 적용됩니다.
+ %1$d%%
+ "%1$s" 서가를 생성했습니다.
+ 스마트 선반 \'%1$s\'을(를) 생성했습니다.
+ 선반 이름을 "%1$s"로 변경했습니다.
+ "%1$s" 서가를 삭제했습니다.
+ "%1$s"을 업데이트했습니다.
+ 해당 파일은 이미 라이브러리에 있습니다.
+ %1$s - %2$s
+ 저장
+ 댓글 저장
+ 댓글 추가
+ 답장하다
+ 댓글 추가…
+ 댓글
+ 댓글 편집 중
+ %1$s에 응답합니다.
+ PDF 사용 파일 이름
+ 밝기
+ 현재 도서
+ 규칙 추가
+ 이 책에는 아직 대체 규칙이 없습니다.
+ 새로운 교체
+ 교체 수정
+ 와
+ 빈 텍스트
+ 소개
+ 데스크탑 리더
+ 데스크톱 액세스
+ 계정
+ 계정 및 크레딧
+ 계정 개요
+ AI 허브
+ EPUB에 사용됩니다. 요약 및 PDF 페이지 요약.
+ Episteme 오스
+ 작성자 텍스트
+ 캐시: %1$s
+ 캐시됨
+ 캐시된 요약
+ Gemini을 선택하세요. 클라우드 읽기에 사용되는 음성입니다.
+ 생성된 데스크탑 북을 삭제하고 EPUB 페이지 매김 캐시 파일? 다음에 책을 열 때 다시 만들어집니다.
+ 음성 캐시 지우기
+ 도구 닫기
+ 클라우드 동기화
+ 클라우드 TTS 필요 Gemini
+ 클라우드 TTS 로그인 크레딧이 필요합니다
+ 클라우드 TTS 준비
+ 클라우드 TTS 설정
+ 클라우드 TTS 이용할 수 없음
+ 클라우드 TTS 목소리
+ 포함
+ 비용 계산
+ 현재 위치까지 요약을 작성하십시오.
+ 스마트 선반 만들기
+ %1$d 사용 가능한 크레딧
+ %1$s 크레딧
+ 독자를 위해 가져온 글꼴
+ 글꼴 삭제
+ %1$s을(를) 삭제하시겠습니까? 이를 사용하는 책은 기본 글꼴로 돌아갑니다.
+ \"%1$s\"을(를) 삭제하시겠습니까? 책은 도서관에 보관됩니다.
+ 요약 삭제
+ 장애인
+ 가져올 파일을 삭제하세요.
+ 가져올 지원되는 파일을 삭제하세요.
+ 그 밖의 사항은 이메일로 직접 문의해 주세요.
+ 같음
+ 엑스트라
+ 피드백
+ 필드
+ 폴더 경로
+ 여기에서
+ 전체 스캔
+ 무료, %1$d 왼쪽
+ 요약 생성
+ 요약 생성
+ 버그를 신고하고 기능을 요청하거나 지원팀에 직접 문의하세요.
+ GitHub 후원자
+ GitHub 후원자를 통해 개발을 지원합니다.
+ Google 이 데스크톱 빌드에는 로그인이 구성되어 있지 않습니다.
+ 보다 큼
+ 도움말
+ 버그 보고서, 기능 요청 및 지원
+ 숨기기
+ 파일 가져오기
+ 문제
+ 버그 및 기능 요청에 대한 문제 추적기를 엽니다.
+ 미만
+ 도서관과 독자
+ 모두
+ 도서관 활동
+ 더
+ 아직 이 책에 대해 캐시된 요약이 없습니다.
+ TTF, OTF, WOFF2 파일을 가져와서 책에 사용하세요.
+ \"%1$s\"와 일치하는 글꼴을 찾을 수 없습니다.
+ 아니요 Google 계정이 연결되었습니다.
+ 이 섹션에 대해 캐시된 요약이 없습니다.
+ 오프라인 데스크톱 리더
+ 열린 독자
+ 여는 중 %1$s
+ 라이브러리 열기
+ 운영자
+ 페이지
+ 비밀번호로 보호됨 PDF
+ 패트리온
+ Patreon에서 프로젝트를 지원하세요.
+ 일시중지됨
+ %1$s 열려면 비밀번호가 필요합니다.
+ 비밀번호가 필요하거나 올바르지 않습니다.
+ 해당 비밀번호는 %1$s을 열지 못했습니다. PDF 비밀번호를 입력하고 다시 시도하세요.
+ 퍼센트
+ 계획
+ 오디오 준비 중
+ 환경설정
+ 계정 및 크레딧
+ 계정 및 크레딧
+ 이 계정에서는 Pro가 잠금 해제되지 않았습니다.
+ Pro 및 크레딧은 Android에서만 구매할 수 있습니다. 앱. 데스크톱은 동일한 로그인 계정을 확인하고 해당 크레딧을 클라우드 TTS, 요약, 요약 및 기타 유료 AI에 사용합니다. 특징.
+ 데스크톱에서 계정 상태를 확인하려면 로그인하세요.
+ 이 계정에 대해 Pro가 잠금 해제되었습니다.
+ 진행상황
+ 프로젝트
+ 리더
+ 리더 탭 꺼짐
+ 리더 탭 켜짐
+ 새로고침
+ 라이브러리에 추가하려면 손을 떼세요.
+ 이 운영 체제에서는 보안 키 저장소를 사용할 수 없습니다. 여기에 입력한 키는 이 세션에 사용되지만 유지되지는 않습니다.
+ 설정 허브
+ Android와 일치합니다. 스마트 사전, 요약, 요약 토글을 숨깁니다.
+ 계정, Pro 및 크레딧 동기화
+ 로그인됨
+ 소스 코드
+ GitHub에서 프로젝트 소스를 찾아보세요.
+ 목소리를 바꾸려면 읽기를 중단하세요.
+ 지원
+ 지원 Episteme
+ 기여는 Android 전반에 걸쳐 독자의 발전을 유지하는 데 도움이 됩니다. 그리고 데스크탑.
+ 지원 방법 Episteme 개발
+ 폴더 동기화
+ 메타데이터 동기화
+ 태그 이름
+ 선택한 책에 태그 지정
+ 제목 텍스트
+ 도구
+ 가져오기, 동기화 및 앱 설정
+ 유형을 입력하세요. PDF
+ 보기
+ 음성 캐시
+ 삽입된 웹뷰 준비 중…
+ 번들로 포함된 웹뷰 준비 중 %1$d%%
+ 임베디드 웹뷰가 설치되었습니다. 다시 시작 Episteme 설정을 마치려면
+ 내장된 WebView를 시작할 수 없습니다: %1$s
+ 일하는 중…
+ 작업공간
+ 선반에 추가
+ 먼저 서가를 만든 다음 선택한 책을 서가에 추가하세요.
+ 테마 만들기
+ 기존: %1$s
+ 외부 링크를 클릭하셨습니다.
+ 편집 EPUB 메타데이터
+ 덜
+ …더 보기
+ 아직 맞춤 테마가 없습니다.
+ 앱에서 이름 바꾸기
+ 태그, 쉼표로 구분
+ 알 수 없음
+ 정의
+ 주석
+ 주석 옵션
+ 주석 도구
+ 어시스트
+ PDF 저장합니다.
+ 점프 기록 지우기
+ 클라우드 TTS 실패한.
+ Gemini 추가 키를 누르고 Gemini 클라우드 TTS AI 열쇠와 모델.
+ 클라우드 TTS 이 데스크탑 빌드에 대해 구성되지 않았습니다.
+ Google로 로그인 클라우드를 사용하려면 TTS.
+ 클라우드 TTS 크레딧이 있는 로그인된 계정이 필요합니다. Pro 및 크레딧은 Android에서만 구매할 수 있습니다. 앱.
+ 색상
+ 댓글 옵션
+ 맞춤
+ 그러면 PDF에서 주석이 제거됩니다.
+ 주석을 삭제하시겠습니까?
+ 문서 텍스트
+ 임베디드 PDF 코멘트
+ 페이지를 렌더링하지 못했습니다.
+ 사용할 수 없는 기능
+ 완료
+ 만년필
+ 검색결과 숨기기
+ 강조 색상 %1$d
+ 하이라이터 팔레트
+ 상호작용
+ 인덱싱 %1$d/%2$d 페이지
+ 마크업
+ %1$d 성냥
+ %1$d 지금까지의 경기
+ 다음 페이지
+ 다음 검색결과
+ 아직 주석이 없습니다.
+ 아직 북마크가 없습니다.
+ 댓글 없음
+ 일치하는 항목 없음
+ 아직 색인이 생성된 페이지에 일치하는 항목이 없습니다.
+ 목차 없음
+ 여기에 읽을 텍스트가 없습니다.
+ 이 페이지에는 읽을 텍스트가 없습니다.
+ 요약할 텍스트가 없습니다.
+ 댓글 열기
+ 크레딧이 부족합니다. Pro 및 크레딧은 Android에서만 구매할 수 있습니다. 앱.
+ 클라우드 사용 TTS 데스크톱에는 크레딧이 필요합니다. Pro 및 크레딧은 Android에서만 구매할 수 있습니다. 앱.
+ 이 기능을 사용하려면 데스크톱에서 크레딧이 필요합니다. Pro 및 크레딧은 Android에서만 구매할 수 있습니다. 앱.
+ 요약을 사용하려면 데스크톱에서 크레딧이 필요합니다. Pro 및 크레딧은 Android에서만 구매할 수 있습니다. 앱.
+ 요약을 사용하려면 데스크톱에서 크레딧이 필요합니다. Pro 및 크레딧은 Android에서만 구매할 수 있습니다. 앱.
+ 팬
+ PDF 작업 실패
+ PDF 작업을 완료할 수 없습니다.
+ PDF 코멘트
+ 피. %1$d
+ PDF 페이지 %1$d
+ 페이지 %1$d - %2$s
+ 페이지 %1$s %2$d
+ 페이지 %1$s %2$d
+ PDF 저장됨
+ PDF 도구
+ 연필
+ 선택 준비 중
+ 준비 중 %1$s
+ 이전 페이지
+ 이전 검색결과
+ 인쇄 대화 상자가 완료되었습니다.
+ 프로 필수
+ 이 기능을 사용하려면 Pro가 필요합니다. Pro는 Android에서만 구매할 수 있습니다. 앱을 사용하면 로그인 후 데스크톱에서 업그레이드된 계정을 사용합니다.
+ 다중 단어 스마트 사전에는 Pro가 필요합니다. Pro는 Android에서만 구매할 수 있습니다. 앱을 사용하면 로그인 후 데스크톱에서 업그레이드된 계정을 사용합니다.
+ 리더 AI 기능이 숨겨져 있습니다.
+ 데스크탑 AI 이 빌드에 대해 구성되지 않았습니다.
+ 세로 읽기 및 두 페이지 보기에 적용됩니다.
+ 원형 하이라이터
+ %1$s에 저장되었습니다.
+ 스크롤
+ PDF에서 검색
+ 텍스트 선택
+ 선택됨 %1$s
+ 검색결과 표시
+ Google로 로그인 데스크톱에서 이 기능을 사용하려면
+ Google로 로그인 데스크탑에서 다중 단어 스마트 사전을 사용하려면
+ Google로 로그인 데스크톱에서 요약을 사용합니다.
+ Google로 로그인 데스크톱에서 요약을 사용하려면
+ 중지됨
+ 텍스트 메모
+ 텍스트 메모
+ 텍스트 스타일
+ 두께 %1$s
+ 목차
+ 검색하려면 입력하세요 PDF
+ 제목 없음
+ 계정 및 크레딧 보기
+ 음성 캐시가 삭제되었습니다.
+ 줌
+ 확대
+ 축소
+ 선택
+ 계속 읽기
+ 닫기
+ 아래로
+ 위로
+ AI
+ 센터
+ 저자
+ 도서관으로 돌아가기
+ 도서 작업
+ 폴더
+ 찾아보기
+ 카테고리
+ Ch. %1$d
+ 챕터 턴
+ 글꼴 선택
+ 리더 텍스처 선택
+ 파일 형식 지우기
+ 페이지 주석 지우기
+ 소스 지우기
+ 상태 지우기
+ 태그 지우기
+ 독자 닫기
+ 연속
+ 커버
+ 맞춤 색상
+ 맞춤 테마 미리보기
+ 감소 %1$s
+ 페이지 정의
+ 그러면 강조 표시와 해당 메모가 제거됩니다.
+ 전체 화면으로 전환
+ 전체 화면 종료
+ 외부 조회
+ 책
+ 만화
+ 문서
+ 기타
+ 텍스트와 웹
+ 채우기
+ 고정 레이아웃 모양
+ 폴더가 비어 있습니다.
+ 여기서는 지원되는 파일이나 하위 폴더를 사용할 수 없습니다.
+ %1$s, %2$s
+ %1$s - %2$s
+ 필터 숨기기
+ 리더 도구 숨기기
+ 슬롯을 탭한 후 색상을 선택하세요.
+ 계속 읽기 및 최근 도서
+ 도서 가져오기
+ 폴더 가져오기
+ 가져온 글꼴
+ %1$s %2$s
+ 증가 %1$s
+ 점프 이력
+ 레이아웃 및 간격
+ 파일을 앱 저장소로 가져오거나 폴더를 추가하여 파일을 읽을 수 있습니다.
+ 컬렉션 찾아보기
+ AI 열쇠
+ 스마트 %1$d
+ 읽지 않음 %1$d
+ 진행 중 %1$d
+ 완료 %1$d
+ 목록
+ 네비게이션
+ 열린 책 없음
+ 해당 폴더의 파일을 읽을 수 있도록 폴더를 추가하세요.
+ 아직 폴더가 없습니다.
+ 탐색 항목 없음
+ 페이지 콘텐츠 없음
+ 설정을 찾을 수 없습니다.
+ 수동 서가와 시리즈 컬렉션이 여기에 표시됩니다.
+ 아직 선반이 없습니다.
+ 스마트 선반을 만들어 규칙에 따라 책을 모아보세요.
+ 아직 스마트 선반이 없습니다.
+ 책에 추가된 태그가 여기에 표시됩니다.
+ 아직 태그가 없습니다.
+ 지원되는 파일을 가져오지 않았습니다.
+ 카탈로그
+ "%1$s"을 삭제하시겠습니까? 나중에 자격 증명이 변경되면 이 카탈로그의 스트리밍된 책이 열리지 않을 수 있습니다.
+ 카탈로그 없음
+ OPDS 추가 원격 도서를 검색할 수 있는 카탈로그입니다.
+ 카탈로그, 스트림, 다운로드 찾아보기
+ 오픈북
+ 폴더 열기
+ 열기 PDF
+ 페이지 및 텍스트 색상
+ 페이지 정보
+ 페이지 너비
+ 이러한 기본값은 플랫폼이 공유 PDF를 지원하는 경우 적용됩니다. 모습. 도서별 PDF 재정의는 PDF 리더.
+ PDF 파일 작업
+ PDF 형광펜
+ 리더 하이라이트 투명도와 함께 저장되었습니다.
+ 핀
+ 리더 관리 PDF 도구
+ 자동 스크롤, OCR, 주석 기본값 및 PDF 전용 도구 가시성은 활성 PDF 리더.
+ %1$s %2$s %3$d (%4$d%%)
+ 리더 도구 모음 기본값은 이 플랫폼의 리더에서 관리됩니다.
+ 리더 도구
+ 이미지 저장
+ 검색: %1$s
+ 리더에서 검색
+ 검색 설정
+ 선택
+ 선택 끝 핸들
+ 선택 시작 핸들
+ 서가, 태그 또는 폴더 메타데이터를 추가하여 라이브러리를 정리하세요.
+ 컬렉션, 시리즈, 태그 및 폴더
+ 리더 도구 표시
+ 폴더
+ 스마트
+ 솔리드
+ 속도
+ 자동 스크롤 시작
+ 자동 스크롤 중지
+ 소리내어 읽기 중지
+ 질감 강도
+ 이 책을 검색하려면 입력하세요.
+ 타이포그래피
+ 주석 실행 취소
+ 고정 해제
+ 어두운 테마 사용
+ 밝은 테마 사용
+ 모노
+ 샌즈
+ 세리프
+ 도서, 저자 또는 태그 검색
+ 도구 없음
+ 보이는
+ 말한 내용만 바꾸기
+ 리더 텍스트, 하이라이트, 위치는 변경되지 않습니다.
+ %1$s -> %2$s
diff --git a/app/src/main/res/values-nl/plurals.xml b/app/src/main/res/values-nl/plurals.xml
index d1d6423..1c39ab0 100644
--- a/app/src/main/res/values-nl/plurals.xml
+++ b/app/src/main/res/values-nl/plurals.xml
@@ -52,4 +52,88 @@
(%1$d segment)(%1$d segmenten)
+
+ %1$d importeren boek... Het zal binnenkort in uw bibliotheek verschijnen.
+ %1$d importeren boeken... Ze verschijnen binnenkort in uw bibliotheek.
+
+
+ Geïmporteerd %1$d boek. Je vindt het onder het tabblad Bibliotheek.
+ Geïmporteerd %1$d boeken. Je vindt ze onder het tabblad Bibliotheek.
+
+
+ %1$d boek toegevoegd aan de plank.
+ %1$d boeken toegevoegd aan de plank.
+
+
+ %1$d boek getagd met "%2$s".
+ %1$d boeken getagd met "%2$s".
+
+
+ Map "%1$s" verwijderd en %2$d boek vanuit de app.
+ Map "%1$s" verwijderd en %2$d boeken uit de app.
+
+
+ %1$d bestand
+ %1$d bestanden
+
+
+ Ga naar import %1$d bestand
+ Ga naar import %1$d bestanden
+
+
+ %1$d niet-ondersteund bestand wordt overgeslagen.
+ %1$d niet-ondersteunde bestanden worden overgeslagen.
+
+
+ %1$d importeren bestand…
+ %1$d importeren bestanden…
+
+
+ Geïmporteerd %1$d bestand.
+ Geïmporteerd %1$d bestanden.
+
+
+ Geïmporteerd %1$d bestand. Ondersteuning voor lezers komt later.
+ Geïmporteerd %1$d bestanden. Ondersteuning voor lezers komt later.
+
+
+ Kan %1$d niet importeren bestand.
+ Kan %1$d niet importeren bestanden.
+
+
+ %1$d overgeslagen bestand.
+ %1$d overgeslagen bestanden.
+
+
+ Verwijder "%1$s" en zijn %2$d boeken vanuit de app? Bestanden op schijf worden niet verwijderd.
+ Verwijder "%1$s" en zijn %2$d boeken uit de app? Bestanden op schijf worden niet verwijderd.
+
+
+ Mapsynchronisatie mislukt voor %1$d map.
+ Mapsynchronisatie mislukt voor %1$d mappen.
+
+
+ Mapsynchronisatie voltooid met %1$d map overgeslagen.
+ Mapsynchronisatie voltooid met %1$d mappen overgeslagen.
+
+
+ %1$d verwijderd gestreamd OPDS boek uit die catalogus.
+ %1$d verwijderd gestreamd OPDS boeken uit die catalogus.
+
+
+ Alle boeken %1$d
+ Alle boeken %1$d
+
+
+ Planken %1$d
+ Planken %1$d
+
+
+ Labels %1$d
+ Labels %1$d
+
+
+ Mappen %1$d
+ Mappen %1$d
+
diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml
index 4640cd3..2075356 100644
--- a/app/src/main/res/values-nl/strings.xml
+++ b/app/src/main/res/values-nl/strings.xml
@@ -1086,4 +1086,430 @@
NederlandsУкраїнська (Oekraïens)Bahasa Indonesia (Indonesisch)
+ Toon tabbladen in de bovenste app-balk
+ Schakel lokale synchronisatie uit
+ Schakel lokale synchronisatie in
+ Lokale synchronisatie uitgeschakeld
+ Synchronisatie van lokale mappen uitschakelen?
+ Episteme stopt met het scannen van deze map en stopt met schrijven JSON bestanden synchroniseren. Verwijder de %1$s map ook uit deze map?
+ Bewaar synchronisatiegegevens
+ Synchronisatiegegevens verwijderen
+ Lettertypen verwijderen?
+ Weet u zeker dat u %1$d wilt verwijderen geselecteerde lettertypen? Hierdoor worden ze van al uw apparaten verwijderd als de synchronisatie is ingeschakeld.
+ Er zijn geen lokale mappen waarvoor synchronisatie is ingeschakeld.
+ Synchronisatie van lokale mappen uitgeschakeld.
+ Synchronisatie van lokale mappen uitgeschakeld. Map voor synchronisatiegegevens verwijderd.
+ Synchronisatie van lokale mappen uitgeschakeld, maar de map met synchronisatiegegevens kan niet worden verwijderd.
+ Synchronisatie van lokale mappen ingeschakeld.
+ Verticaal (WebView)
+ Verticaal (native bèta)
+ Boekwoordvervangingen
+ Vorige TTS stuk
+ Volgende TTS stuk
+ Afbeeldingen
+ Geen afbeeldingen gevonden.
+ Afbeelding downloaden
+ Opgeslagen %1$s
+ Kan afbeelding niet opslaan.
+ PDF pagina verspreid
+ Enkele pagina
+ Twee pagina\'s
+ Alleen al de eerste pagina
+ Begint met tegenover elkaar liggende pagina\'s na het voorblad.
+ Helderheid
+ Gebruik systeemhelderheid
+ Volgt de helderheidsinstelling van het apparaat.
+ Aangepaste helderheid
+ Van toepassing terwijl een lezerscherm geopend is.
+ %1$d%%
+ Plank "%1$s" gemaakt.
+ Slimme plank "%1$s" gemaakt.
+ Hernoemde plank naar "%1$s".
+ Plank "%1$s" verwijderd.
+ Bijgewerkt "%1$s".
+ Deze bestanden staan al in de bibliotheek.
+ %1$s - %2$s
+ Opslaan
+ Opmerking opslaan
+ Commentaar toevoegen
+ Antwoord
+ Voeg een reactie toe…
+ Opmerkingen
+ Commentaar bewerken
+ Reageert op %1$s
+ Gebruik PDF Bestandsnamen
+ Helderheid
+ Huidig boek
+ Regel toevoegen
+ Er zijn nog geen vervangingsregels voor dit boek.
+ Nieuwe vervanging
+ Vervanging bewerken
+ Met
+ lege tekst
+ Over
+ Desktoplezer
+ Desktoptoegang
+ Rekening
+ Account & tegoeden
+ Rekeningoverzicht
+ AI naaf
+ Gebruikt voor EPUB samenvattingen en PDF pagina samenvattingen.
+ Episteme oss
+ Auteur tekst
+ Cache: %1$s
+ In cache opgeslagen
+ Samenvatting in cache
+ Kies de Gemini stem gebruikt voor voorlezen in de cloud.
+ Verwijder het gegenereerde bureaubladboek en EPUB paginering cachebestanden? De volgende keer dat er boeken worden geopend, worden ze opnieuw gemaakt.
+ Spraakcache wissen
+ Gereedschap sluiten
+ Cloudsynchronisatie
+ Wolk TTS heeft Gemini nodig
+ Wolk TTS heeft ingelogde credits nodig
+ Wolk TTS klaar
+ Wolk TTS instellingen
+ Wolk TTS niet beschikbaar
+ Wolk TTS stem
+ Bevat
+ Kosten berekenen
+ Maak een samenvatting van uw huidige positie.
+ Creëer een slimme plank
+ %1$d beschikbare kredieten
+ %1$s tegoeden
+ Geïmporteerde lettertypen voor de lezer
+ Lettertype verwijderen
+ %1$s verwijderen? Boeken die dit gebruiken, vallen terug op het standaardlettertype.
+ \"%1$s\" verwijderen? Boeken blijven in uw bibliotheek.
+ Samenvatting verwijderen
+ Uitgeschakeld
+ Zet bestanden neer om te importeren
+ Zet ondersteunde bestanden neer om te importeren
+ Neem voor al het andere rechtstreeks contact met ons op via e-mail.
+ Gelijk aan
+ Extra\'s
+ Feedback
+ Veld
+ Mappad
+ Vanaf hier
+ Volledige scan
+ Gratis, %1$d links
+ Samenvatting genereren
+ Samenvatting genereren
+ Rapporteer bugs, vraag functies aan of neem rechtstreeks contact op met de ondersteuning.
+ GitHub-sponsors
+ Ondersteun de ontwikkeling via GitHub-sponsors.
+ Google aanmelden is niet geconfigureerd voor deze desktopbuild.
+ Groter dan
+ Hulp
+ Bugrapporten, functieverzoeken en ondersteuning
+ Verbergen
+ Importeer bestanden
+ Problemen
+ Open de issuetracker voor bugs en functieverzoeken.
+ Minder dan
+ Bibliotheek en lezer
+ Elke
+ Bibliotheekacties
+ Meer
+ Er zijn nog geen samenvattingen in de cache voor dit boek.
+ Importeer TTF-, OTF- of WOFF2-bestanden om ze in boeken te gebruiken.
+ Geen lettertypen gevonden die overeenkomen met \"%1$s\"
+ Nee Google rekening is verbonden.
+ Er is geen samenvatting in de cache opgeslagen voor deze sectie.
+ Offline desktoplezer
+ Open lezers
+ Openen %1$s
+ Uw bibliotheek openen
+ Exploitant
+ Pagina
+ Wachtwoord beveiligd PDF
+ Patreon
+ Steun het project op Patreon.
+ Gepauzeerd
+ %1$s vereist een wachtwoord voordat het kan worden geopend.
+ Wachtwoord is vereist of onjuist.
+ Dat wachtwoord kan %1$s niet openen. Voer de PDF in wachtwoord en probeer het opnieuw.
+ Procent
+ Plannen
+ Audio voorbereiden
+ Voorkeuren
+ Account & tegoeden
+ Account & tegoeden
+ Pro is niet ontgrendeld voor dit account.
+ Pro en credits kunnen alleen worden gekocht bij Android app. Desktop controleert hetzelfde ingelogde account en gebruikt deze credits voor cloud TTS, samenvattingen, samenvattingen en andere betaalde AI functies.
+ Meld u aan om uw accountstatus op uw desktop te controleren.
+ Pro is ontgrendeld voor dit account.
+ Vooruitgang
+ Project
+ Lezer
+ De lezer wordt uitgeschakeld
+ Lezertabbladen ingeschakeld
+ Vernieuwen
+ Geef vrij om toe te voegen aan uw bibliotheek.
+ Veilige sleutelopslag is niet beschikbaar op dit besturingssysteem. Sleutels die hier worden ingevoerd, worden voor deze sessie gebruikt, maar worden niet bewaard.
+ Instellingen-hub
+ Komt overeen met Android verberg schakelaar voor slim woordenboek, samenvattingen en samenvattingen.
+ Synchroniseer account, Pro en tegoeden
+ Ingelogd
+ Broncode
+ Blader door de projectbron op GitHub.
+ Stop met lezen om van stem te veranderen.
+ Ondersteuning
+ Ondersteuning Episteme
+ Bijdragen zorgen ervoor dat de lezer zich blijft verbeteren in Android en bureaublad.
+ Manieren om Episteme te ondersteunen ontwikkeling
+ Synchroniseer mappen
+ Synchroniseer metagegevens
+ Tagnaam
+ Tag geselecteerde boeken
+ Titel tekst
+ Gereedschap
+ Import-, synchronisatie- en app-instellingen
+ Typ bijv. PDF
+ Bekijk
+ Spraakcache
+ Ingesloten webweergave voorbereiden…
+ Gebundelde ingebedde webweergave voorbereiden %1$d%%
+ Ingebouwde webview geïnstalleerd. Herstart Episteme om de installatie te voltooien.
+ Ingesloten webweergave kan niet starten: %1$s
+ Werken…
+ Werkruimte
+ Toevoegen aan plank
+ Maak eerst een plank en voeg er vervolgens geselecteerde boeken aan toe.
+ Maak een thema
+ Bestaand: %1$s
+ U heeft op een externe link geklikt.
+ Bewerken EPUB metagegevens
+ Minder
+ …meer
+ Nog geen aangepaste thema\'s
+ Naam wijzigen in app
+ Tags, door komma\'s gescheiden
+ Onbekend
+ Definieer
+ Annotatie
+ Annotatie-opties
+ Annotatiehulpmiddelen
+ Assisteren
+ Kies welke PDF om op te slaan.
+ Wis de spronggeschiedenis
+ Wolk TTS mislukt.
+ Voeg een Gemini toe en selecteer Gemini wolk TTS in AI sleutels en modellen.
+ Wolk TTS is niet geconfigureerd voor deze desktopbuild.
+ Meld u aan met Google om cloud TTS te gebruiken.
+ Wolk TTS heeft een ingelogd account met credits nodig. Pro en credits kunnen alleen worden gekocht via Android app.
+ Kleur
+ Commentaaropties
+ Aangepast
+ Hierdoor wordt de annotatie van deze PDF verwijderd.
+ Annotatie verwijderen?
+ Documenttekst
+ Ingesloten PDF commentaar
+ Kan de pagina niet weergeven.
+ Functie niet beschikbaar
+ Klaar
+ Vulpen
+ Zoekresultaten verbergen
+ Markeerkleur %1$d
+ Markeerstiftpalet
+ Interactie
+ Indexering %1$d/%2$d pagina\'s
+ Opmaak
+ %1$d wedstrijden
+ %1$d wedstrijden tot nu toe
+ Volgende pagina
+ Volgende zoekresultaat
+ Nog geen aantekeningen
+ Nog geen bladwijzers
+ Geen commentaar
+ Geen overeenkomsten
+ Er zijn nog geen overeenkomsten op geïndexeerde pagina\'s
+ Geen inhoudsopgave
+ Er is hier geen tekst om te lezen.
+ Er is geen tekst op deze pagina om te lezen.
+ Er is geen tekst om samen te vatten.
+ Opmerking openen
+ Geen krediet meer. Pro en credits kunnen alleen worden gekocht bij Android app.
+ Met behulp van de cloud TTS heeft credits nodig op de desktop. Pro en credits kunnen alleen worden gekocht via Android app.
+ Voor het gebruik van deze functie zijn credits op de desktop nodig. Pro en credits kunnen alleen worden gekocht bij Android app.
+ Voor het gebruik van samenvattingen zijn credits nodig op de desktop. Pro en credits kunnen alleen worden gekocht bij Android app.
+ Voor het gebruik van samenvattingen zijn credits op de desktop nodig. Pro en credits kunnen alleen worden gekocht bij Android app.
+ Pan
+ PDF actie mislukt
+ De PDF actie kon niet worden voltooid.
+ PDF commentaar
+ P. %1$d
+ PDF pagina %1$d
+ Pagina %1$d - %2$s
+ Pagina %1$s van %2$d
+ Pagina\'s %1$s van %2$d
+ PDF opgeslagen
+ PDF gereedschap
+ Potlood
+ Selectie voorbereiden
+ %1$s voorbereiden
+ Vorige pagina
+ Vorig zoekresultaat
+ Het afdrukdialoogvenster is voltooid.
+ Pro vereist
+ Voor deze functie is Pro vereist. Pro kan alleen worden gekocht bij de Android app, waarna de desktop het geüpgradede account gebruikt na het inloggen.
+ Voor een slim woordenboek met meerdere woorden is Pro vereist. Pro kan alleen worden gekocht bij de Android app, waarna de desktop het geüpgradede account gebruikt na het inloggen.
+ Lezer AI functies zijn verborgen.
+ Bureaublad AI is niet geconfigureerd voor deze build.
+ Geldt voor verticaal lezen en spreads van twee pagina\'s.
+ Ronde markeerstift
+ Opgeslagen in %1$s
+ Blader
+ Zoek in PDF
+ Selecteer tekst
+ Geselecteerd %1$s
+ Toon zoekresultaten
+ Meld u aan met Google om deze functie op het bureaublad te gebruiken.
+ Meld u aan met Google om een slim woordenboek met meerdere woorden op het bureaublad te gebruiken.
+ Meld u aan met Google om samenvattingen op het bureaublad te gebruiken.
+ Meld u aan met Google om samenvattingen op het bureaublad te gebruiken.
+ Gestopt
+ Tekstnotitie
+ tekst notitie
+ Tekststijl
+ Dikte %1$s
+ TOC
+ Typ om dit te zoeken PDF
+ Zonder titel
+ Bekijk account en tegoeden
+ Spraakcache gewist
+ Zoomen
+ Zoom in
+ Uitzoomen
+ Kies
+ Lees verder
+ Negeren
+ Naar beneden
+ Op
+ AI
+ Centrum
+ Auteurs
+ Terug naar bibliotheek
+ Boek acties
+ Map
+ Blader
+ Categorieën
+ Ch. %1$d
+ Hoofdstuk beurten
+ Kies lettertype
+ Kies lezertextuur
+ Bestandstypen wissen
+ Paginaannotaties wissen
+ Duidelijke bronnen
+ Duidelijke status
+ Duidelijke tags
+ Dichte lezer
+ Continu
+ Hoezen
+ Aangepaste kleuren
+ Aangepast themavoorbeeld
+ Verlaag %1$s
+ Pagina definiëren
+ Hierdoor worden de markering en de bijbehorende noot verwijderd.
+ Ga naar volledig scherm
+ Sluit het volledige scherm af
+ Externe zoekopdracht
+ Boeken
+ Strips
+ Documenten
+ Anders
+ Tekst en internet
+ Vullen
+ Uiterlijk met vaste lay-out
+ Map is leeg
+ Er zijn hier geen ondersteunde bestanden of submappen beschikbaar.
+ %1$s, %2$s
+ %1$s - %2$s
+ Verberg filters
+ Lezerstools verbergen
+ Tik op een slot en kies vervolgens een kleur.
+ Lees verder en recente boeken
+ Boeken importeren
+ Map importeren
+ Geïmporteerde lettertypen
+ %1$s %2$s
+ Verhoog %1$s
+ Geschiedenis springen
+ Indeling en afstand
+ Importeer bestanden in de app-opslag of voeg een map toe om bestanden ter plekke te lezen.
+ Blader door uw collectie
+ AI sleutels
+ Slim %1$d
+ Ongelezen %1$d
+ In uitvoering %1$d
+ Voltooi %1$d
+ Lijst
+ Navigatie
+ Geen boek geopend
+ Voeg een map toe om bestanden uit die map te lezen.
+ Nog geen mappen
+ Geen navigatie-items
+ Geen pagina-inhoud
+ Geen instellingen gevonden
+ Hier verschijnen handmatige planken en seriecollecties.
+ Nog geen planken
+ Creëer slimme planken om boeken volgens regels te verzamelen.
+ Nog geen slimme planken
+ Tags die aan boeken zijn toegevoegd, worden hier weergegeven.
+ Nog geen tags
+ Er zijn geen ondersteunde bestanden geïmporteerd.
+ Catalogus
+ "%1$s" verwijderen? Gestreamde boeken uit deze catalogus worden mogelijk niet meer geopend als de inloggegevens later veranderen.
+ Geen catalogi
+ Voeg een OPDS toe catalogus om door externe boeken te bladeren.
+ Blader door catalogi, streams en downloads
+ Boek openen
+ Map openen
+ Open PDF
+ Pagina- en tekstkleuren
+ Pagina-info
+ Paginabreedte
+ Deze standaardwaarden zijn van toepassing als het platform gedeelde PDF ondersteunt verschijning. Per boek PDF overschrijvingen blijven in PDF lezer.
+ PDF bestandsacties
+ PDF markeerstift
+ Opgeslagen met transparantie van de lezermarkering.
+ Vastzetten
+ Door lezer beheerd PDF gereedschap
+ Automatisch scrollen, OCR, standaardinstellingen voor annotaties en PDF-only toolzichtbaarheid worden beheerd binnen de actieve PDF lezer.
+ %1$s %2$s van %3$d (%4$d%%)
+ De standaardinstellingen van de werkbalk van de lezer worden beheerd vanuit de lezer op dit platform.
+ Lezershulpmiddelen
+ Afbeelding opslaan
+ Zoeken: %1$s
+ Zoek in lezer
+ Zoekinstellingen
+ Selectie
+ Selectie eindgreep
+ Startgreep selectie
+ Voeg planken, tags of mapmetagegevens toe om uw bibliotheek te ordenen.
+ Collecties, series, tags en mappen
+ Toon leeshulpmiddelen
+ Map
+ Slim
+ Solide
+ Snelheid
+ Start automatisch scrollen
+ Stop automatisch scrollen
+ Houd op met voorlezen
+ Textuursterkte
+ Typ om dit boek te doorzoeken
+ Typografie
+ Annotatie ongedaan maken
+ Losmaken
+ Gebruik een donker thema
+ Gebruik een licht thema
+ Mono
+ San
+ Serif
+ Zoek naar boeken, auteurs of tags
+ Geen gereedschap
+ Zichtbaar
+ Vervang alleen wat er wordt gesproken
+ Lezertekst, hoogtepunten en locaties blijven ongewijzigd.
+ %1$s -> %2$s
diff --git a/app/src/main/res/values-pl/plurals.xml b/app/src/main/res/values-pl/plurals.xml
index 1f7a4d7..cf142fc 100644
--- a/app/src/main/res/values-pl/plurals.xml
+++ b/app/src/main/res/values-pl/plurals.xml
@@ -78,4 +78,130 @@
(%1$d fragmentów)(%1$d fragmentu)
+
+ Importowanie %1$d książka… Wkrótce pojawi się w Twojej bibliotece.
+ Importowanie %1$d książki… Wkrótce pojawią się w Twojej bibliotece.
+ Importowanie %1$d książki… Wkrótce pojawią się w Twojej bibliotece.
+ Importowanie %1$d książki… Wkrótce pojawią się w Twojej bibliotece.
+
+
+ Zaimportowano %1$d książka. Znajdziesz ją w zakładce Biblioteka.
+ Zaimportowano %1$d książki. Znajdziesz je w zakładce Biblioteka.
+ Zaimportowano %1$d książki. Znajdziesz je w zakładce Biblioteka.
+ Zaimportowano %1$d książki. Znajdziesz je w zakładce Biblioteka.
+
+
+ %1$d książka dodana do półki.
+ %1$d książki dodane do półki.
+ %1$d książki dodane do półki.
+ %1$d książki dodane do półki.
+
+
+ %1$d książka oznaczona tagiem „%2$s”.
+ %1$d książki oznaczone tagiem „%2$s”.
+ %1$d książki oznaczone tagiem „%2$s”.
+ %1$d książki oznaczone tagiem „%2$s”.
+
+
+ Usunięto folder „%1$s” i %2$d zarezerwuj z aplikacji.
+ Usunięto folder „%1$s” i %2$d książki z aplikacji.
+ Usunięto folder „%1$s” i %2$d książki z aplikacji.
+ Usunięto folder „%1$s” i %2$d książki z aplikacji.
+
+
+ %1$d plik
+ %1$d akta
+ %1$d akta
+ %1$d akta
+
+
+ Przejdź do importu %1$d plik
+ Przejdź do importu %1$d akta
+ Przejdź do importu %1$d akta
+ Przejdź do importu %1$d akta
+
+
+ %1$d nieobsługiwany plik zostanie pominięty.
+ %1$d nieobsługiwane pliki zostaną pominięte.
+ %1$d nieobsługiwane pliki zostaną pominięte.
+ %1$d nieobsługiwane pliki zostaną pominięte.
+
+
+ Importowanie %1$d plik…
+ Importowanie %1$d akta…
+ Importowanie %1$d akta…
+ Importowanie %1$d akta…
+
+
+ Zaimportowano %1$d plik.
+ Zaimportowano %1$d akta.
+ Zaimportowano %1$d akta.
+ Zaimportowano %1$d akta.
+
+
+ Zaimportowano %1$d plik. Wsparcie czytelników pojawi się później.
+ Zaimportowano %1$d akta. Wsparcie czytelników pojawi się później.
+ Zaimportowano %1$d akta. Wsparcie czytelników pojawi się później.
+ Zaimportowano %1$d akta. Wsparcie czytelników pojawi się później.
+
+
+ Nie można zaimportować %1$d plik.
+ Nie można zaimportować %1$d akta.
+ Nie można zaimportować %1$d akta.
+ Nie można zaimportować %1$d akta.
+
+
+ Pominięte %1$d plik.
+ Pominięte %1$d akta.
+ Pominięte %1$d akta.
+ Pominięte %1$d akta.
+
+
+ Usuń „%1$s” i jego %2$d zarezerwować z aplikacji? Pliki na dysku nie zostaną usunięte.
+ Usuń „%1$s” i jego %2$d książki z aplikacji? Pliki na dysku nie zostaną usunięte.
+ Usuń „%1$s” i jego %2$d książki z aplikacji? Pliki na dysku nie zostaną usunięte.
+ Usuń „%1$s” i jego %2$d książki z aplikacji? Pliki na dysku nie zostaną usunięte.
+
+
+ Synchronizacja folderu nie powiodła się dla %1$d falcówka.
+ Synchronizacja folderu nie powiodła się dla %1$d lornetka składana.
+ Synchronizacja folderu nie powiodła się dla %1$d lornetka składana.
+ Synchronizacja folderu nie powiodła się dla %1$d lornetka składana.
+
+
+ Synchronizacja folderów zakończona %1$d folder pominięty.
+ Synchronizacja folderów zakończona %1$d foldery pominięte.
+ Synchronizacja folderów zakończona %1$d foldery pominięte.
+ Synchronizacja folderów zakończona %1$d foldery pominięte.
+
+
+ Usunięto %1$d przesyłane strumieniowo OPDS książka z tego katalogu.
+ Usunięto %1$d przesyłane strumieniowo OPDS książki z tego katalogu.
+ Usunięto %1$d przesyłane strumieniowo OPDS książki z tego katalogu.
+ Usunięto %1$d przesyłane strumieniowo OPDS książki z tego katalogu.
+
+
+ Wszystkie książki %1$d
+ Wszystkie książki %1$d
+ Wszystkie książki %1$d
+ Wszystkie książki %1$d
+
+
+ Półki %1$d
+ Półki %1$d
+ Półki %1$d
+ Półki %1$d
+
+
+ Tagi %1$d
+ Tagi %1$d
+ Tagi %1$d
+ Tagi %1$d
+
+
+ Foldery %1$d
+ Foldery %1$d
+ Foldery %1$d
+ Foldery %1$d
+
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 340b6a5..7c5cd44 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -1086,4 +1086,430 @@
Nederlands (niderlandzki)Українська (ukraiński)Bahasa Indonesia (indonezyjski)
+ Pokaż karty na górnym pasku aplikacji
+ Wyłącz synchronizację lokalną
+ Włącz synchronizację lokalną
+ Synchronizacja lokalna wyłączona
+ Wyłączyć synchronizację folderów lokalnych?
+ Episteme przestanie skanować ten folder i przestanie zapisywać JSON synchronizować pliki. Usuń %1$s folder też z tego folderu?
+ Zachowaj dane synchronizacji
+ Usuń dane synchronizacji
+ Usunąć czcionki?
+ Czy na pewno chcesz usunąć %1$d wybrane czcionki? Spowoduje to usunięcie ich ze wszystkich Twoich urządzeń, jeśli synchronizacja jest włączona.
+ Żaden folder lokalny nie ma włączonej synchronizacji.
+ Synchronizacja folderów lokalnych wyłączona.
+ Synchronizacja folderów lokalnych wyłączona. Usunięto folder danych synchronizacji.
+ Synchronizacja folderów lokalnych jest wyłączona, ale nie można usunąć folderu danych synchronizacji.
+ Włączono synchronizację folderów lokalnych.
+ Pionowo (WebView)
+ Pion (natywna wersja beta)
+ Książka Zamienniki słów
+ Poprzedni TTS kawałek
+ Dalej TTS kawałek
+ Obrazy
+ Nie znaleziono obrazów.
+ Pobierz obraz
+ Zapisano %1$s
+ Nie można zapisać obrazu.
+ PDF rozkład strony
+ Pojedyncza strona
+ Dwie strony
+ Sama pierwsza strona
+ Rozpoczyna rozkładówkę po stronie tytułowej.
+ Jasność
+ Użyj jasności systemu
+ Podąża za ustawieniem jasności urządzenia.
+ Niestandardowa jasność
+ Obowiązuje, gdy ekran czytnika jest otwarty.
+ %1$d%%
+ Utworzono półkę „%1$s”.
+ Utworzono inteligentną półkę „%1$s”.
+ Zmieniono nazwę półki na „%1$s”.
+ Usunięto półkę „%1$s”.
+ Zaktualizowano „%1$s”.
+ Te pliki są już w bibliotece.
+ %1$s - %2$s
+ Zapisz
+ Zapisz komentarz
+ Dodaj komentarz
+ Odpowiedz
+ Dodaj komentarz…
+ Komentarze
+ Edytowanie komentarza
+ Odpowiadanie na %1$s
+ Użyj PDF Nazwy plików
+ Jasność
+ Aktualna książka
+ Dodaj regułę
+ Nie ma jeszcze zasad wymiany tej książki.
+ Nowy zamiennik
+ Edytuj zamiennik
+ Z
+ pusty tekst
+ O
+ Czytnik stacjonarny
+ Dostęp do pulpitu
+ Konto
+ Konto i kredyty
+ Przegląd konta
+ AI centrum
+ Używany do EPUB podsumowania i PDF podsumowania stron.
+ Episteme os
+ Tekst autora
+ Pamięć podręczna: %1$s
+ Buforowane
+ Podsumowanie w pamięci podręcznej
+ Wybierz Gemini głos używany do głośnego czytania w chmurze.
+ Usuń wygenerowaną książkę komputerową i EPUB pliki pamięci podręcznej stronicowania? Zostaną odtworzone przy następnym otwarciu książek.
+ Wyczyść pamięć podręczną głosu
+ Zamknij narzędzia
+ Synchronizacja w chmurze
+ Chmura TTS potrzeby Gemini
+ Chmura TTS wymaga zalogowanych kredytów
+ Chmura TTS gotowy
+ Chmura TTS ustawienia
+ Chmura TTS nie płynny
+ Chmura TTS głos
+ Zawiera
+ Obliczanie kosztów
+ Utwórz podsumowanie aż do aktualnej pozycji.
+ Stwórz inteligentną półkę
+ %1$d dostępne kredyty
+ %1$s kredyty
+ Zaimportowane czcionki dla czytnika
+ Usuń czcionkę
+ Usunąć %1$s? Książki korzystające z tej czcionki powrócą do domyślnej czcionki.
+ Usunąć \"%1$s\"? Książki pozostają w Twojej bibliotece.
+ Usuń podsumowanie
+ Wyłączony
+ Upuść pliki do zaimportowania
+ Upuść obsługiwane pliki, aby je zaimportować
+ Skontaktuj się z nami bezpośrednio przez e-mail, aby uzyskać więcej informacji.
+ Równe
+ Dodatki
+ Informacja zwrotna
+ Pole
+ Ścieżka folderu
+ Stąd
+ Pełne skanowanie
+ Bezpłatnie, %1$d lewy
+ Wygeneruj podsumowanie
+ Wygeneruj podsumowanie
+ Zgłaszaj błędy, żądaj funkcji lub skontaktuj się bezpośrednio z pomocą techniczną.
+ Sponsorzy GitHuba
+ Wspieraj rozwój za pośrednictwem sponsorów GitHub.
+ Google logowanie nie jest skonfigurowane dla tej kompilacji komputerów stacjonarnych.
+ Większy niż
+ Pomoc
+ Raporty o błędach, prośby o funkcje i wsparcie
+ Ukrywać
+ Importuj pliki
+ Kwestie
+ Otwórz narzędzie do śledzenia problemów, aby znaleźć błędy i prośby o funkcje.
+ Mniej niż
+ Biblioteka i czytelnik
+ Każdy
+ Działania biblioteczne
+ Więcej
+ Nie ma jeszcze żadnych podsumowań tej książki w pamięci podręcznej.
+ Importuj pliki TTF, OTF lub WOFF2, aby używać ich w książkach.
+ Nie znaleziono czcionek pasujących do \"%1$s\"
+ Nie Google konto jest połączone.
+ Brak podsumowania w pamięci podręcznej dla tej sekcji.
+ Czytnik stacjonarny offline
+ Otwórzcie czytelników
+ Otwarcie %1$s
+ Otwieram bibliotekę
+ Operator
+ Strona
+ Chroniony hasłem PDF
+ Patreona
+ Wesprzyj projekt na Patreonie.
+ Wstrzymano
+ %1$s wymaga hasła, zanim będzie można je otworzyć.
+ Hasło jest wymagane lub nieprawidłowe.
+ To hasło nie otworzyło się %1$s. Wprowadź PDF hasło i spróbuj ponownie.
+ Procent
+ Plan
+ Przygotowanie dźwięku
+ Preferencje
+ Konto i kredyty
+ Konto i kredyty
+ Wersja Pro nie jest odblokowana dla tego konta.
+ Pro i kredyty można kupić wyłącznie u Android aplikacja. Desktop sprawdza to samo zalogowane konto i wykorzystuje te środki do chmury TTS, podsumowań, podsumowań i innych płatnych AI cechy.
+ Zaloguj się, aby sprawdzić stan swojego konta na komputerze.
+ Pro jest odblokowane dla tego konta.
+ Postęp
+ Projekt
+ Czytelnik
+ Zakładki czytnika wyłączone
+ Zakładki czytnika włączone
+ Odświeżać
+ Zwolnij, aby dodać do swojej biblioteki.
+ Bezpieczne przechowywanie kluczy jest niedostępne w tym systemie operacyjnym. Wpisane tutaj klucze zostaną użyte w tej sesji, ale nie zostaną utrwalone.
+ Centrum ustawień
+ Pasuje do Android ukryj przełącznik inteligentnego słownika, podsumowań i podsumowań.
+ Zsynchronizuj konto, Pro i kredyty
+ Zalogowano
+ Kod źródłowy
+ Przeglądaj źródło projektu w serwisie GitHub.
+ Przestań czytać, aby zmienić głosy.
+ Wsparcie
+ Wsparcie Episteme
+ Wkłady pomagają czytelnikowi w ulepszaniu Android i komputer stacjonarny.
+ Sposoby wsparcia Episteme rozwój
+ Synchronizuj foldery
+ Synchronizuj metadane
+ Nazwa znacznika
+ Oznacz wybrane książki
+ Tekst tytułu
+ Narzędzia
+ Importuj, synchronizuj i ustawienia aplikacji
+ Wpisz np. PDF
+ Pogląd
+ Pamięć podręczna głosowa
+ Przygotowuję osadzony widok internetowy…
+ Przygotowanie wbudowanego widoku internetowego %1$d%%
+ Zainstalowany wbudowany podgląd sieciowy. Uruchom ponownie Episteme aby zakończyć konfigurację.
+ Nie można uruchomić wbudowanego widoku internetowego: %1$s
+ Pracujący…
+ Obszar roboczy
+ Dodaj do półki
+ Najpierw utwórz półkę, a następnie dodaj do niej wybrane książki.
+ Utwórz motyw
+ Istniejące: %1$s
+ Kliknąłeś link zewnętrzny.
+ Edytuj EPUB metadane
+ Mniej
+ …więcej
+ Nie ma jeszcze niestandardowych motywów
+ Zmień nazwę w aplikacji
+ Tagi, oddzielone przecinkami
+ Nieznany
+ Określić
+ Adnotacja
+ Opcje adnotacji
+ Narzędzia do adnotacji
+ Wspierać
+ Wybierz który PDF zapisać.
+ Wyczyść historię skoków
+ Chmura TTS przegrany.
+ Dodaj Gemini klawisz i wybierz Gemini chmura TTS w AI klucze i modele.
+ Chmura TTS nie jest skonfigurowany dla tej kompilacji komputera stacjonarnego.
+ Zaloguj się za pomocą Google korzystać z chmury TTS.
+ Chmura TTS wymaga zalogowanego konta z kredytami. Pro i kredyty można kupić wyłącznie u Android aplikacja.
+ Kolor
+ Opcje komentarzy
+ Zwyczaj
+ Spowoduje to usunięcie adnotacji z tego PDF.
+ Usunąć adnotację?
+ Tekst dokumentu
+ Wbudowany PDF komentarz
+ Nie udało się wyrenderować strony.
+ Funkcja niedostępna
+ Gotowy
+ Wieczne pióro
+ Ukryj wyniki wyszukiwania
+ Kolor podświetlenia %1$d
+ Paleta rozświetlaczy
+ Wzajemne oddziaływanie
+ Indeksowanie %1$d/%2$d strony
+ Oznaczenia
+ %1$d zapałki
+ %1$d mecze jak dotąd
+ Następna strona
+ Następny wynik wyszukiwania
+ Nie ma jeszcze żadnych adnotacji
+ Nie ma jeszcze żadnych zakładek
+ Bez komentarza
+ Brak dopasowań
+ Brak dopasowań na zaindeksowanych stronach
+ Brak spisu treści
+ Nie ma tu tekstu do przeczytania.
+ Na tej stronie nie ma tekstu do przeczytania.
+ Nie ma tekstu do podsumowania.
+ Otwórz komentarz
+ Brak kredytów. Pro i kredyty można kupić wyłącznie u Android aplikacja.
+ Korzystanie z chmury TTS potrzebuje kredytów na komputerze. Pro i kredyty można kupić wyłącznie u Android aplikacja.
+ Korzystanie z tej funkcji wymaga kredytów na komputerze. Pro i kredyty można kupić wyłącznie u Android aplikacja.
+ Korzystanie z podsumowań wymaga kredytów na komputerze. Pro i kredyty można kupić wyłącznie u Android aplikacja.
+ Korzystanie z podsumowań wymaga kredytów na komputerze. Pro i kredyty można kupić wyłącznie u Android aplikacja.
+ Patelnia
+ PDF akcja nie powiodła się
+ PDF akcja nie mogła zostać ukończona.
+ PDF komentarz
+ P. %1$d
+ PDF strona %1$d
+ Strona %1$d - %2$s
+ Strona %1$s z %2$d
+ Strony %1$s z %2$d
+ PDF zapisane
+ PDF narzędzia
+ Ołówek
+ Przygotowanie selekcji
+ Przygotowanie %1$s
+ Poprzednia strona
+ Poprzedni wynik wyszukiwania
+ Okno drukowania zostało zakończone.
+ Wymagany profesjonalista
+ Ta funkcja wymaga wersji Pro. Pro można kupić wyłącznie w sklepie Android app, komputer stacjonarny użyje uaktualnionego konta po zalogowaniu.
+ Inteligentny słownik wielowyrazowy wymaga wersji Pro. Pro można kupić wyłącznie w sklepie Android app, komputer stacjonarny użyje uaktualnionego konta po zalogowaniu.
+ Czytnik AI funkcje są ukryte.
+ Pulpit AI nie jest skonfigurowany dla tej kompilacji.
+ Dotyczy czytania pionowego i rozkładówek dwustronicowych.
+ Okrągły rozświetlacz
+ Zapisano w %1$s
+ Zwój
+ Szukaj w PDF
+ Wybierz tekst
+ Wybrano %1$s
+ Pokaż wyniki wyszukiwania
+ Zaloguj się za pomocą Google aby korzystać z tej funkcji na komputerze.
+ Zaloguj się za pomocą Google aby korzystać z inteligentnego słownika wielowyrazowego na komputerze.
+ Zaloguj się za pomocą Google aby korzystać z podsumowań na komputerze.
+ Zaloguj się za pomocą Google aby korzystać z podsumowań na komputerze.
+ Zatrzymany
+ Notatka tekstowa
+ notatka tekstowa
+ Styl tekstu
+ Grubość %1$s
+ Spis treści
+ Wpisz, aby wyszukać PDF
+ Nieuprawny
+ Zobacz konto i środki
+ Pamięć podręczna głosu została wyczyszczona
+ Brzęczenie
+ Powiększ
+ Pomniejsz
+ Wybierz
+ Kontynuuj czytanie
+ Odrzuć
+ W dół
+ W górę
+ AI
+ Centrum
+ Autorzy
+ Powrót do biblioteki
+ Akcje książkowe
+ Folder
+ Przeglądaj
+ Kategorie
+ Ch. %1$d
+ Rozdział Zakręty
+ Wybierz czcionkę
+ Wybierz teksturę czytnika
+ Wyczyść typy plików
+ Wyczyść adnotacje na stronie
+ Wyczyść źródła
+ Wyczyść stan
+ Wyczyść tagi
+ Zamknij czytelnika
+ Ciągłe
+ Okładki
+ Kolory niestandardowe
+ Niestandardowy podgląd motywu
+ Zmniejsz %1$s
+ Zdefiniuj stronę
+ Spowoduje to usunięcie wyróżnienia i przypisanej mu notatki.
+ Wejdź na pełny ekran
+ Wyjdź z pełnego ekranu
+ Wyszukiwanie zewnętrzne
+ Książki
+ Komiksy
+ Dokumenty
+ Inne
+ Tekst i sieć
+ Wypełnij
+ Wygląd o stałym układzie
+ Folder jest pusty
+ Nie są tu dostępne żadne obsługiwane pliki ani podfoldery.
+ %1$s, %2$s
+ %1$s - %2$s
+ Ukryj filtry
+ Ukryj narzędzia czytnika
+ Kliknij miejsce, a następnie wybierz kolor.
+ Kontynuuj czytanie i najnowsze książki
+ Importuj książki
+ Importuj folder
+ Importowane czcionki
+ %1$s %2$s
+ Zwiększ %1$s
+ Historia skoków
+ Układ i odstępy
+ Zaimportuj pliki do magazynu aplikacji lub dodaj folder, aby czytać pliki na miejscu.
+ Przeglądaj swoją kolekcję
+ AI klucze
+ Inteligentne %1$d
+ Nieprzeczytane %1$d
+ W toku %1$d
+ Kompletny %1$d
+ Lista
+ Nawigacja
+ Żadna książka nie jest otwarta
+ Dodaj folder, aby odczytywać pliki z tego folderu na miejscu.
+ Nie ma jeszcze folderów
+ Brak elementów nawigacyjnych
+ Brak zawartości strony
+ Nie znaleziono ustawień
+ Pojawią się tutaj półki ręczne i kolekcje seryjne.
+ Nie ma jeszcze półek
+ Twórz inteligentne półki, aby gromadzić książki według zasad.
+ Nie ma jeszcze inteligentnych półek
+ Tutaj pojawią się tagi dodane do książek.
+ Nie ma jeszcze tagów
+ Nie zaimportowano żadnych obsługiwanych plików.
+ Katalog
+ Usunąć „%1$s”? Książki przesyłane strumieniowo z tego katalogu mogą przestać się otwierać, jeśli dane uwierzytelniające zmienią się później.
+ Żadnych katalogów
+ Dodaj OPDS katalog do przeglądania zdalnych książek.
+ Przeglądaj katalogi, strumienie i pliki do pobrania
+ Otwórz książkę
+ Otwórz folder
+ Otwórz PDF
+ Kolory strony i tekstu
+ Informacje o stronie
+ Szerokość strony
+ Te wartości domyślne mają zastosowanie, gdy platforma obsługuje współdzielone PDF wygląd. Za książkę PDF zastępuje pobyt w PDF czytelnik.
+ PDF akcje plików
+ PDF zakreślacz
+ Zapisano z przezroczystością podświetlenia czytnika.
+ Szpilka
+ Zarządzane przez czytnik PDF narzędzia
+ Automatyczne przewijanie, OCR, domyślne ustawienia adnotacji i PDF-tylko widoczność narzędzi są zarządzane w aktywnym PDF czytelnik.
+ %1$s %2$s z %3$d (%4$d%%)
+ Domyślnymi ustawieniami paska narzędzi czytnika zarządza się z poziomu czytnika na tej platformie.
+ Narzędzia czytnika
+ Zapisz obraz
+ Szukaj: %1$s
+ Szukaj w czytniku
+ Ustawienia wyszukiwania
+ Wybór
+ Wybór uchwytu końcowego
+ Uchwyt początkowy zaznaczenia
+ Dodaj półki, znaczniki lub metadane folderów, aby uporządkować swoją bibliotekę.
+ Kolekcje, serie, tagi i foldery
+ Pokaż narzędzia czytnika
+ Falcówka
+ Mądry
+ Solidny
+ Prędkość
+ Rozpocznij automatyczne przewijanie
+ Zatrzymaj automatyczne przewijanie
+ Przestań czytać na głos
+ Siła tekstury
+ Wpisz, aby przeszukać tę książkę
+ Typografia
+ Cofnij adnotację
+ Odpiąć
+ Użyj ciemnego motywu
+ Użyj jasnego motywu
+ Mononukleoza
+ Bez
+ Szeryf
+ Wyszukaj książki, autorów lub tagi
+ Żadnych narzędzi
+ Widoczny
+ Zastąp tylko to, co zostało powiedziane
+ Tekst czytnika, wyróżnienia i lokalizacje pozostają niezmienione.
+ %1$s -> %2$s
diff --git a/app/src/main/res/values-pt-rBR/plurals.xml b/app/src/main/res/values-pt-rBR/plurals.xml
index a533b7f..b6f47f9 100644
--- a/app/src/main/res/values-pt-rBR/plurals.xml
+++ b/app/src/main/res/values-pt-rBR/plurals.xml
@@ -52,4 +52,88 @@
(%1$d trecho)(%1$d trechos)
+
+ Importando %1$d livro… Ele aparecerá em sua Biblioteca em breve.
+ Importando %1$d livros… Eles aparecerão em sua Biblioteca em breve.
+
+
+ Importado %1$d livro. Você pode encontrá-lo na guia Biblioteca.
+ Importado %1$d livros. Você pode encontrá-los na guia Biblioteca.
+
+
+ %1$d livro adicionado à estante.
+ %1$d livros adicionados à estante.
+
+
+ %1$d livro marcado com "%2$s".
+ %1$d livros marcados com "%2$s".
+
+
+ Pasta removida "%1$s" e %2$d reserve no aplicativo.
+ Pasta removida "%1$s" e %2$d livros do aplicativo.
+
+
+ %1$d arquivo
+ %1$d arquivos
+
+
+ Solte para importar %1$d arquivo
+ Solte para importar %1$d arquivos
+
+
+ %1$d arquivo não suportado será ignorado.
+ %1$d arquivos não suportados serão ignorados.
+
+
+ Importando %1$d arquivo…
+ Importando %1$d arquivos…
+
+
+ Importado %1$d arquivo.
+ Importado %1$d arquivos.
+
+
+ Importado %1$d arquivo. O suporte ao leitor vem mais tarde.
+ Importado %1$d arquivos. O suporte ao leitor vem mais tarde.
+
+
+ Não foi possível importar %1$d arquivo.
+ Não foi possível importar %1$d arquivos.
+
+
+ Ignorado %1$d arquivo.
+ Ignorado %1$d arquivos.
+
+
+ Remover "%1$s" e seu %2$d reservar pelo aplicativo? Os arquivos no disco não serão excluídos.
+ Remover "%1$s" e seu %2$d livros do aplicativo? Os arquivos no disco não serão excluídos.
+
+
+ Falha na sincronização da pasta para %1$d pasta.
+ Falha na sincronização da pasta para %1$d pastas.
+
+
+ Sincronização de pasta concluída com %1$d pasta ignorada.
+ Sincronização de pasta concluída com %1$d pastas ignoradas.
+
+
+ Removido %1$d transmitido OPDS livro desse catálogo.
+ Removido %1$d transmitido OPDS livros desse catálogo.
+
+
+ Todos os livros %1$d
+ Todos os livros %1$d
+
+
+ Prateleiras %1$d
+ Prateleiras %1$d
+
+
+ Etiquetas %1$d
+ Etiquetas %1$d
+
+
+ Pastas %1$d
+ Pastas %1$d
+
diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml
index 9105605..c882207 100644
--- a/app/src/main/res/values-pt-rBR/strings.xml
+++ b/app/src/main/res/values-pt-rBR/strings.xml
@@ -1097,4 +1097,419 @@
Página únicaDuas páginasApenas a primeira página
+ Desativar sincronização local
+ Habilitar sincronização local
+ Sincronização local desativada
+ Desativar a sincronização de pastas locais?
+ Episteme irá parar de verificar esta pasta e parar de escrever JSON sincronizar arquivos. Remova o %1$s pasta desta pasta também?
+ Manter dados sincronizados
+ Remover dados sincronizados
+ Excluir fontes?
+ Tem certeza de que deseja excluir %1$d fontes selecionadas? Isso os removerá de todos os seus dispositivos se a sincronização estiver ativada.
+ Nenhuma pasta local tem sincronização ativada.
+ Sincronização de pasta local desativada.
+ Sincronização de pasta local desativada. Pasta de dados de sincronização removida.
+ A sincronização da pasta local foi desativada, mas não foi possível remover a pasta de dados de sincronização.
+ Sincronização de pasta local habilitada.
+ Verticais (WebView)
+ Vertical (beta nativo)
+ Substituições de palavras de livros
+ PDF propagação da página
+ Inicia páginas espelhadas após a página de rosto.
+ Brilho
+ Use o brilho do sistema
+ Segue a configuração de brilho do dispositivo.
+ Brilho personalizado
+ Aplica-se enquanto uma tela do leitor está aberta.
+ %1$d%%
+ Prateleira criada "%1$s".
+ Criada prateleira inteligente "%1$s".
+ Prateleira renomeada para "%1$s".
+ Prateleira excluída "%1$s".
+ "%1$s" atualizado.
+ Esses arquivos já estão na biblioteca.
+ %1$s - %2$s
+ Salvar
+ Salvar comentário
+ Adicionar comentário
+ Responder
+ Adicione um comentário…
+ Comentários
+ Editando comentário
+ Respondendo a %1$s
+ Usar PDF Nomes de arquivos
+ Brilho
+ Livro atual
+ Adicionar regra
+ Ainda não há regras de substituição para este livro.
+ Nova substituição
+ Editar substituição
+ Com
+ texto vazio
+ Sobre
+ Leitor de mesa
+ Acesso à área de trabalho
+ Conta
+ Conta e créditos
+ Visão geral da conta
+ AI centro
+ Usado para EPUB resumos e PDF resumos de páginas.
+ Episteme oss
+ Texto do autor
+ Cache: %1$s
+ Em cache
+ Resumo em cache
+ Escolha o Gemini voz usada para nuvem lida em voz alta.
+ Exclua o livro de desktop gerado e EPUB arquivos de cache de paginação? Eles serão recriados na próxima vez que os livros forem abertos.
+ Limpar cache de voz
+ Fechar ferramentas
+ Sincronização na nuvem
+ Nuvem TTS necessidades Gemini
+ Nuvem TTS precisa de créditos de login
+ Nuvem TTS pronto
+ Nuvem TTS configurações
+ Nuvem TTS indisponível
+ Nuvem TTS voz
+ Contém
+ Cálculo de custos
+ Crie uma recapitulação da sua posição atual.
+ Crie uma prateleira inteligente
+ %1$d créditos disponíveis
+ %1$s créditos
+ Fontes importadas para o leitor
+ Excluir fonte
+ Excluir %1$s? Os livros que o utilizam voltarão à fonte padrão.
+ Excluir \"%1$s\"? Os livros ficam na sua biblioteca.
+ Excluir resumo
+ Desativado
+ Solte arquivos para importar
+ Solte os arquivos suportados para importar
+ Contate-nos diretamente por e-mail para qualquer outra coisa.
+ Igual
+ Extras
+ Comentários
+ Campo
+ Caminho da pasta
+ Daqui
+ Verificação completa
+ Grátis, %1$d esquerda
+ Gerar recapitulação
+ Gerar resumo
+ Relate bugs, solicite recursos ou entre em contato diretamente com o suporte.
+ Patrocinadores do GitHub
+ Apoie o desenvolvimento por meio de patrocinadores do GitHub.
+ Google o login não está configurado para esta versão de desktop.
+ Maior que
+ Ajuda
+ Relatórios de bugs, solicitações de recursos e suporte
+ Esconder
+ Importar arquivos
+ Problemas
+ Abra o rastreador de problemas para bugs e solicitações de recursos.
+ Menos que
+ Biblioteca e leitor
+ Qualquer
+ Ações da biblioteca
+ Mais
+ Ainda não há resumos em cache para este livro.
+ Importe arquivos TTF, OTF ou WOFF2 para usá-los em livros.
+ Nenhuma fonte encontrada correspondente a \"%1$s\"
+ Não Google conta está conectada.
+ Nenhum resumo armazenado em cache para esta seção.
+ Leitor de desktop off-line
+ Leitores abertos
+ Abertura %1$s
+ Abrindo sua biblioteca
+ Operador
+ Página
+ Protegido por senha PDF
+ Patreon
+ Apoie o projeto no Patreon.
+ Pausado
+ %1$s requer uma senha antes de poder ser aberto.
+ A senha é obrigatória ou está incorreta.
+ Essa senha não abriu %1$s. Digite o PDF senha e tente novamente.
+ Porcentagem
+ Plano
+ Preparando áudio
+ Preferências
+ Conta e créditos
+ Conta e créditos
+ O Pro não está desbloqueado para esta conta.
+ Pro e créditos só podem ser adquiridos no Android aplicativo. O Desktop verifica a mesma conta conectada e usa esses créditos para nuvem TTS, resumos, recapitulações e outros AI pagos características.
+ Faça login para verificar o status da sua conta no desktop.
+ O Pro está desbloqueado para esta conta.
+ Progresso
+ Projeto
+ Leitor
+ Guias do leitor desativadas
+ Guias do leitor ativadas
+ Atualizar
+ Solte para adicionar à sua biblioteca.
+ O armazenamento seguro de chaves não está disponível neste sistema operacional. As chaves inseridas aqui serão usadas para esta sessão, mas não serão persistidas.
+ Centro de configurações
+ Corresponde ao Android ocultar a alternância para dicionário inteligente, resumos e recapitulações.
+ Sincronizar conta, Pro e créditos
+ Conectado
+ Código fonte
+ Navegue pela fonte do projeto no GitHub.
+ Pare de ler para mudar de voz.
+ Suporte
+ Suporte Episteme
+ As contribuições ajudam a manter o leitor melhorando em Android e área de trabalho.
+ Maneiras de apoiar Episteme desenvolvimento
+ Sincronizar pastas
+ Sincronizar metadados
+ Nome da etiqueta
+ Marcar livros selecionados
+ Texto do título
+ Ferramentas
+ Importar, sincronizar e configurações de aplicativos
+ Digite, por ex. PDF
+ Ver
+ Cache de voz
+ Preparando webview incorporado…
+ Preparando webview integrado incluído %1$d%%
+ Webview incorporado instalado. Reinicie Episteme para concluir a configuração.
+ A visualização da web incorporada não pôde ser iniciada: %1$s
+ Trabalhando…
+ Espaço de trabalho
+ Adicionar à estante
+ Crie primeiro uma estante e depois adicione os livros selecionados a ela.
+ Criar tema
+ Existente: %1$s
+ Você clicou em um link externo.
+ Editar EPUB metadados
+ Menos
+ …mais
+ Ainda não há temas personalizados
+ Renomear no aplicativo
+ Tags, separadas por vírgula
+ Desconhecido
+ Definir
+ Anotação
+ Opções de anotação
+ Ferramentas de anotação
+ Ajuda
+ Escolha qual PDF para salvar.
+ Limpar histórico de saltos
+ Nuvem TTS fracassado.
+ Adicione um Gemini e selecione Gemini nuvem TTS em AI chaves e modelos.
+ Nuvem TTS não está configurado para esta compilação de desktop.
+ Faça login com Google usar nuvem TTS.
+ Nuvem TTS precisa de uma conta conectada com créditos. Pro e créditos só podem ser adquiridos no Android aplicativo.
+ Cor
+ Opções de comentários
+ Personalizado
+ Isso remove a anotação deste PDF.
+ Excluir anotação?
+ Texto do documento
+ Incorporado PDF comentar
+ Falha ao renderizar a página.
+ Recurso indisponível
+ Concluído
+ Caneta-tinteiro
+ Ocultar resultados da pesquisa
+ Cor de destaque %1$d
+ Paleta de realce
+ Interação
+ Indexação %1$d/%2$d páginas
+ Marcação
+ %1$d partidas
+ %1$d jogos até agora
+ Próxima página
+ Próximo resultado da pesquisa
+ Ainda não há anotações
+ Ainda não há favoritos
+ Sem comentários
+ Nenhuma correspondência
+ Ainda não há correspondências nas páginas indexadas
+ Sem índice
+ Não há texto aqui para ler.
+ Não há texto nesta página para ler.
+ Não há texto para resumir.
+ Abrir comentário
+ Fora dos créditos. Pro e créditos só podem ser adquiridos no Android aplicativo.
+ Usando nuvem TTS precisa de créditos no desktop. Pro e créditos só podem ser adquiridos no Android aplicativo.
+ O uso deste recurso requer créditos no desktop. Pro e créditos só podem ser adquiridos no Android aplicativo.
+ O uso de recapitulações precisa de créditos no desktop. Pro e créditos só podem ser adquiridos no Android aplicativo.
+ O uso de resumos requer créditos no desktop. Pro e créditos só podem ser adquiridos no Android aplicativo.
+ Panela
+ PDF ação falhou
+ O PDF a ação não pôde ser concluída.
+ PDF comentar
+ pág. %1$d
+ PDF página %1$d
+ Página %1$d - %2$s
+ Página %1$s de %2$d
+ Páginas %1$s de %2$d
+ PDF salvo
+ PDF ferramentas
+ Lápis
+ Preparando seleção
+ Preparando %1$s
+ Página anterior
+ Resultado da pesquisa anterior
+ A caixa de diálogo de impressão foi concluída.
+ Profissional obrigatório
+ Este recurso requer Pro. O Pro só pode ser adquirido no Android app, o desktop usará a conta atualizada após o login.
+ O dicionário inteligente de várias palavras requer Pro. O Pro só pode ser adquirido no Android app, o desktop usará a conta atualizada após o login.
+ Leitor AI recursos estão ocultos.
+ Computador de mesa AI não está configurado para esta compilação.
+ Aplica-se à leitura vertical e páginas espelhadas de duas páginas.
+ Marcador redondo
+ Salvo em %1$s
+ Rolar
+ Pesquise em PDF
+ Selecione o texto
+ Selecionado %1$s
+ Mostrar resultados de pesquisa
+ Faça login com Google para usar esse recurso na área de trabalho.
+ Faça login com Google para usar o dicionário inteligente de várias palavras no desktop.
+ Faça login com Google para usar recapitulações no desktop.
+ Faça login com Google para usar resumos no desktop.
+ Parado
+ Nota de texto
+ nota de texto
+ Estilo de texto
+ Espessura %1$s
+ Índice
+ Digite para pesquisar isso PDF
+ Sem título
+ Ver conta e créditos
+ Cache de voz limpo
+ Zoom
+ Ampliar
+ Diminuir zoom
+ Escolha
+ Continuar lendo
+ Dispensar
+ Para baixo
+ Acima
+ AI
+ Centro
+ Autores
+ De volta à biblioteca
+ Reservar ações
+ Pasta
+ Navegar
+ Categorias
+ Cap. %1$d
+ Turnos de Capítulo
+ Escolha a fonte
+ Escolha a textura do leitor
+ Limpar tipos de arquivo
+ Limpar anotações da página
+ Fontes claras
+ Limpar status
+ Limpar tags
+ Fechar leitor
+ Contínuo
+ Capas
+ Cores personalizadas
+ Pré-visualização do tema personalizado
+ Diminuir %1$s
+ Definir página
+ Isso remove o destaque e sua nota.
+ Entrar em tela cheia
+ Sair da tela inteira
+ Pesquisa externa
+ Livros
+ Quadrinhos
+ Documentos
+ Outro
+ Texto e web
+ Preencher
+ Aparência de layout fixo
+ A pasta está vazia
+ Nenhum arquivo ou subpasta compatível está disponível aqui.
+ %1$s, %2$s
+ %1$s - %2$s
+ Ocultar filtros
+ Ocultar ferramentas de leitura
+ Toque em um slot e escolha uma cor.
+ Continue lendo e livros recentes
+ Importar livros
+ Pasta de importação
+ Fontes importadas
+ %1$s %2$s
+ Aumentar %1$s
+ Histórico de saltos
+ Layout e espaçamento
+ Importe arquivos para o armazenamento do aplicativo ou adicione uma pasta para ler os arquivos no local.
+ Navegue pela sua coleção
+ AI chaves
+ Inteligente %1$d
+ Não lido %1$d
+ Em andamento %1$d
+ Concluído %1$d
+ Lista
+ Navegação
+ Nenhum livro aberto
+ Adicione uma pasta para ler os arquivos dessa pasta.
+ Ainda não há pastas
+ Nenhum item de navegação
+ Nenhum conteúdo da página
+ Nenhuma configuração encontrada
+ Estantes manuais e coleções de séries aparecerão aqui.
+ Ainda não há prateleiras
+ Crie estantes inteligentes para coletar livros de acordo com regras.
+ Ainda não há prateleiras inteligentes
+ As tags adicionadas aos livros aparecerão aqui.
+ Ainda não há tags
+ Nenhum arquivo compatível foi importado.
+ Catálogo
+ Excluir "%1$s"? Os livros transmitidos deste catálogo poderão parar de abrir se as credenciais forem alteradas posteriormente.
+ Sem catálogos
+ Adicione um OPDS catálogo para navegar em livros remotos.
+ Navegue por catálogos, streams e downloads
+ Livro aberto
+ Abrir pasta
+ Abra PDF
+ Cores da página e do texto
+ Informações da página
+ Largura da página
+ Esses padrões se aplicam onde a plataforma suporta PDF aparência. Por livro PDF as substituições permanecem no PDF leitor.
+ PDF ações de arquivo
+ PDF marcador
+ Salvo com transparência de destaque do leitor.
+ Fixar
+ Gerenciado por leitor PDF ferramentas
+ Rolagem automática, OCR, padrões de anotação e visibilidade da ferramenta somente PDF são gerenciados dentro do PDF ativo leitor.
+ %1$s %2$s de %3$d (%4$d%%)
+ Os padrões da barra de ferramentas do leitor são gerenciados pelo leitor nesta plataforma.
+ Ferramentas de leitura
+ Salvar imagem
+ Pesquisar: %1$s
+ Pesquisar no leitor
+ Configurações de pesquisa
+ Seleção
+ Alça final de seleção
+ Alça inicial de seleção
+ Adicione estantes, tags ou metadados de pasta para organizar sua biblioteca.
+ Coleções, séries, tags e pastas
+ Mostrar ferramentas do leitor
+ Pasta
+ Inteligente
+ Sólido
+ Velocidade
+ Iniciar rolagem automática
+ Parar a rolagem automática
+ Pare de ler em voz alta
+ Força da textura
+ Digite para pesquisar este livro
+ Tipografia
+ Desfazer anotação
+ Liberar
+ Usar tema escuro
+ Use tema claro
+ Mono
+ Sem
+ Serif
+ Pesquise livros, autores ou tags
+ Sem ferramentas
+ Visível
+ Substitua apenas o que é falado
+ O texto do leitor, os destaques e os locais permanecem inalterados.
+ %1$s -> %2$s
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 379bd45..3b9ab7b 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -1474,4 +1474,42 @@
Заменять только произносимый текстТекст в читалке, выделения и позиции остаются без изменений.%1$s → %2$s
+ Отключить локальную синхронизацию
+ Включить локальную синхронизацию
+ Локальная синхронизация отключена
+ Отключить синхронизацию локальных папок?
+ Episteme прекратит сканирование этой папки и перестанет писать JSON синхронизировать файлы. Удалите %1$s папка из этой папки тоже?
+ Сохранять данные синхронизации
+ Удалить данные синхронизации
+ Удалить шрифты?
+ Вы уверены, что хотите удалить %1$d выбранные шрифты? Они будут удалены со всех ваших устройств, если синхронизация включена.
+ Ни для одной локальной папки не включена синхронизация.
+ Синхронизация локальных папок отключена.
+ Синхронизация локальных папок отключена. Папка с данными синхронизации удалена.
+ Синхронизация локальной папки отключена, но папку данных синхронизации удалить не удалось.
+ Синхронизация локальных папок включена.
+ Вертикальный (WebView)
+ Вертикальная (собственная бета-версия)
+ Замены слов в книге
+ Текущая книга
+ Добавить правило
+ Для этой книги пока нет правил замены.
+ Новая замена
+ Изменить замену
+ С
+ пустой текст
+ Счет и кредиты
+ Обзор аккаунта
+ Episteme ОСС
+ Облачная синхронизация
+ Помощь
+ Действия библиотеки
+ Подробнее
+ Автономная настольная читалка
+ План
+ Предпочтения
+ Вкладки Reader отключены
+ Вкладки Reader включены
+ Синхронизация учетной записи, Pro и кредитов
+ AI ключи
diff --git a/app/src/main/res/values-tr/plurals.xml b/app/src/main/res/values-tr/plurals.xml
index 33f8480..3036ba2 100644
--- a/app/src/main/res/values-tr/plurals.xml
+++ b/app/src/main/res/values-tr/plurals.xml
@@ -21,15 +21,15 @@
Dosyaları Kalıcı Olarak Sil
- Seçilmiş dosyayı cihazınızdan kalıcı olarak silmek istediğinize emin misiniz? Bu işlem geri alınamaz.
+ %1$d seçilmiş dosyayı cihazınızdan kalıcı olarak silmek istediğinize emin misiniz? Bu işlem geri alınamaz.%1$d seçilmiş dosyayı cihazınızdan kalıcı olarak silmek istediğinize emin misiniz? Bu işlem geri alınamaz.
- Seçilmiş dosyayı son dosyalar listesinden silmek istediğinize emin misiniz? Dosyayı kütüphaneden tekrar açarsanız yeniden burada gözükecektir.
+ %1$d seçilmiş dosyayı son dosyalar listesinden silmek istediğinize emin misiniz? Dosyayı kütüphaneden tekrar açarsanız yeniden burada gözükecektir.%1$d seçilmiş dosyayı son dosyalar listesinden silmek istediğinize emin misiniz? Dosyayı kütüphaneden tekrar açarsanız yeniden burada gözükecektir.
- Kitabı \'%2$s\' rafından kaldırmak istediğinize emin misiniz? Kitap, kütüphanenizde kalmaya devam edecek ve Rafta Değil olarak gözükecektir.
+ %1$d kitabı \'%2$s\' rafından kaldırmak istediğinize emin misiniz? Kitap, kütüphanenizde kalmaya devam edecek ve Rafta Değil olarak gözükecektir.%1$d kitabı \'%2$s\' rafından kaldırmak istediğinize emin misiniz? Kitap, kütüphanenizde kalmaya devam edecek ve Rafta Değil olarak gözükecektir.
@@ -52,4 +52,88 @@
(%1$d yığın)(%1$d yığın)
+
+ İçe aktarılıyor %1$d kitap… Kısa süre içinde Kütüphanenizde görünecek.
+ İçe aktarılıyor %1$d kitaplar… Kısa süre içinde Kitaplığınızda görünecekler.
+
+
+ İthal %1$d kitap. Kütüphane sekmesinde bulabilirsiniz.
+ İthal %1$d kitaplar. Bunları Kitaplık sekmesinde bulabilirsiniz.
+
+
+ %1$d kitap rafa eklendi.
+ %1$d Kitaplar rafa eklendi.
+
+
+ %1$d kitap "%2$s" ile etiketlendi.
+ %1$d "%2$s" ile etiketlenen kitaplar.
+
+
+ "%1$s" klasörü kaldırıldı ve %2$d uygulamadan rezervasyon yapın.
+ "%1$s" klasörü kaldırıldı ve %2$d Uygulamadan kitaplar.
+
+
+ %1$d dosya
+ %1$d dosyalar
+
+
+ İçe aktarmak için bırakın %1$d dosya
+ İçe aktarmak için bırakın %1$d dosyalar
+
+
+ %1$d desteklenmeyen dosya atlanacak.
+ %1$d desteklenmeyen dosyalar atlanacak.
+
+
+ İçe aktarılıyor %1$d dosya…
+ İçe aktarılıyor %1$d dosyalar…
+
+
+ İthal %1$d dosya.
+ İthal %1$d dosyalar.
+
+
+ İthal %1$d dosya. Okuyucu desteği daha sonra gelir.
+ İthal %1$d dosyalar. Okuyucu desteği daha sonra gelir.
+
+
+ %1$d içe aktarılamadı dosya.
+ %1$d içe aktarılamadı dosyalar.
+
+
+ Atlandı %1$d dosya.
+ Atlandı %1$d dosyalar.
+
+
+ "%1$s"\'yi kaldır ve %2$d uygulamadan rezervasyon yaptırılsın mı? Diskteki dosyalar silinmeyecektir.
+ "%1$s"\'yi kaldır ve %2$d uygulamadan kitaplar? Diskteki dosyalar silinmeyecektir.
+
+
+ %1$d için klasör senkronizasyonu başarısız oldu dosya.
+ %1$d için klasör senkronizasyonu başarısız oldu klasörler.
+
+
+ Klasör senkronizasyonu %1$d ile tamamlandı klasör atlandı.
+ Klasör senkronizasyonu %1$d ile tamamlandı klasörler atlandı.
+
+
+ Kaldırıldı %1$d akış OPDS o katalogdan kitap.
+ Kaldırıldı %1$d akış OPDS o katalogdaki kitaplar.
+
+
+ Tüm Kitaplar %1$d
+ Tüm Kitaplar %1$d
+
+
+ Raflar %1$d
+ Raflar %1$d
+
+
+ Etiketler %1$d
+ Etiketler %1$d
+
+
+ Klasörler %1$d
+ Klasörler %1$d
+
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index a4a032b..78be013 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -106,7 +106,7 @@
\'%1$s\' rafını silmek istediğinize emin misiniz? Tüm kitaplar \'Rafta Değil\'e taşınacaktır.Raftan Kaldırılsın mı?%1$s silinsin mi?
- %1$d rafı silmek istediğinize emin misiniz? Tüm kitaplar \'Rafta Değil\'e taşınacaktır.
+ %1$d seçili %2$s silmek istediğinize emin misiniz? Tüm kitaplar \'Rafta Değil\'e taşınacaktır.Yerel Klasörleri EşitleCanlı kitaplık oluşturmak için yerel klasörleri bağlayın. Episteme, dosyaları ve eşitleme ilerlemesini gözetecektir.Klasör Ekle
@@ -809,4 +809,707 @@
Nederlands (Felemenkçe)Українська (Ukraynaca)Bahasa Indonesia (Endonezce)
+ Üst uygulama çubuğunda sekmeleri göster
+ Projeyi Destekleyin
+ Yerel senkronizasyonu devre dışı bırak
+ Yerel senkronizasyonu etkinleştir
+ Yerel senkronizasyon devre dışı bırakıldı
+ Yerel klasör senkronizasyonu devre dışı bırakılsın mı?
+ Episteme bu klasörü taramayı ve yazmayı durduracak JSON dosyaları senkronize edin. %1$s öğesini çıkarın Bu klasördeki klasör de mi?
+ Verileri senkronize et
+ Senkronizasyon verilerini kaldır
+ Yazı Tipleri Silinsin mi?
+ %1$d silmek istediğinizden emin misiniz? seçilen yazı tipleri? Senkronizasyon açıksa bu, onları tüm cihazlarınızdan kaldıracaktır.
+ Projeyi Destekleyin
+ Episteme\'yi korumaya yardım edin hareketli
+ Desteğiniz bakımı sürdürmeme ve Geliştirmeme yardımcı oluyor Episteme herkes için!!!
+ GitHub\'da sponsor
+ Geliştirmeyi doğrudan GitHub Sponsorları aracılığıyla destekleyin. Teşekkür olarak proje deposunda README notu alırsınız.
+ Patreon\'a katılın
+ Uygulamayı desteklediğiniz için bir teşekkür olarak Patreon destekçileri ekstra içerik ve avantajlar elde ediyor: üzerinde çalıştığım şeye kısa bakışlar, erken ekran görüntüleri ve güncellemeler, yeni özelliklerin nasıl görünmesi ve çalışması gerektiğini şekillendirmeye yardımcı olan oylar ve proje deposunda bir README duyurusu.
+ Hiçbir yerel klasörde senkronizasyon etkin değil.
+ Yerel klasör senkronizasyonu devre dışı bırakıldı.
+ Yerel klasör senkronizasyonu devre dışı bırakıldı. Senkronizasyon veri klasörü kaldırıldı.
+ Yerel klasör senkronizasyonu devre dışı bırakıldı ancak senkronizasyon verileri klasörü kaldırılamadı.
+ Yerel klasör senkronizasyonu etkinleştirildi.
+ Bu dosya türü desteklenmiyor.
+ Dokulu Hazır Ayarlar
+ Dokulu Temalarım
+ Henüz özel dokulu tema yok.
+ Yeni Dokulu Tema
+ Doku
+ Yok
+ Yükle
+ Doku Şeffaflığı
+ Dikey (Web Görünümü)
+ Dikey (Yerel Beta)
+ Ekran Yönü
+ Okuma Modunu Değiştir
+ Sayfalandırılmış (sağdan sola)
+ TTS Ayarlar
+ TTS Kelime Değiştirmeleri
+ Kitap Kelime Değiştirmeleri
+ Paylaşın, Kaydedin veya Yazdırın
+ Önceki TTS yığın
+ Sonraki TTS yığın
+ Sekmeler
+ Görseller
+ Resim bulunamadı.
+ Resmi indir
+ Kaydedildi %1$s
+ Resim kaydedilemedi.
+ Sayfa düzeni
+ PDF sayfa yayılması
+ Tek sayfa
+ İki sayfa
+ Yalnız ilk sayfa
+ Kapak sayfasından sonra karşılıklı sayfa yayılmalarını başlatır.
+ Sayfalar arasındaki boşluğu kaldırın
+ Dikey okuma ve iki sayfaya yayılma için geçerlidir.
+ Sayfa numarası yer paylaşımını gizle
+ Her sayfadan küçük sayfa sayısı etiketini kaldırır.
+ Ekran Yönü
+ Android olduğunda okuyucunun sistem yönelimini mi izleyeceğini yoksa dikey mi yoksa yatay mı tercih edeceğini seçin. buna izin veriyor.
+ Pozisyon
+ Parlaklık
+ Sistem parlaklığını kullan
+ Cihazın parlaklık ayarını takip eder.
+ Özel parlaklık
+ Okuyucu ekranı açıkken uygulanır.
+ %1$d%%
+ "%1$s" rafı oluşturuldu.
+ Akıllı raf "%1$s" oluşturuldu.
+ Raf "%1$s" olarak yeniden adlandırıldı.
+ "%1$s" rafı silindi.
+ "%1$s" güncellendi.
+ Bu dosyalar zaten kütüphanede.
+ %1$s - %2$s
+ Kaydet
+ Yorumu Kaydet
+ Yorum Ekle
+ Yanıtla
+ Yorum ekleyin…
+ Yorumlar
+ Yorum düzenleniyor
+ %1$s\'e yanıt veriliyor
+ Dikey Kenar Boşluğu
+ PDF kullanın Dosya adları
+ Metinden konuşmaya
+ Metinden konuşmaya yönelik oynatma kontrolleri.
+ Metni konuşmaya hazırlama
+ Hazırlanıyor: %1$s
+ Önbellek İsabeti • Ücretsiz
+ Oluşturuldu • Ücretsiz (%1$d/10 kaldı)
+ Oluşturulan • Maliyet: %1$s kredi
+ Üretiliyor... • Maliyet: Hesaplanıyor
+ AI Çıkış
+ Yenile
+ Bu kitap için önbelleğe alınmış özet yok.
+ Kredi
+ AI ve Bulut Kredileri
+ Mevcut Krediler
+ %1$d Kredi
+ Tahmini Maliyet Dağılımı
+ Bulut TTS
+ Cost: ~3–4 credits per minute of audio generated.\nTo enable: Reader Screen > More > TTS Voice Settings.
+ AI Özetler ve Özet
+ Cost: ~1–4 credits per request based on chapter length.\nPro Users get 10 free summaries daily.
+ Satın alarak,
+ Kredi Bitti
+ \'yeterli krediniz yok. Episteme Pro alın Günde 10 ücretsiz Özet karşılığında veya Özetleri kullanmak için daha fazla kredi ekleyin, Bulut TTS ve Hikaye Özeti.
+ Pro\'yu Alın / Kredi Ekleyin
+ Sayfa Özetlemenin Kilidini Aç
+ Episteme Pro ile herhangi bir sayfanın kısa özetlerini alın. Bu özelliği kullanmaya başlamak için yükseltme yapın.
+ Kabarcık Yakınlaştırma Modelini İndirin
+ Kabarcık Yakınlaştırma özelliğini kullanmak için AI modelin indirilmesi gerekiyor (~134 MB). Şimdi indirmek istiyor musunuz?
+ Çevir
+ Pg\'ye Geri Dön %1$d
+ Sayfa %1$d
+ Sonuç %1$d / %2$d
+ PDF yüklenemedi.
+ Bubble Zoom modeli indiriliyor… %1$d%%
+ Kaydırıcı navigasyonundan çık
+ Geri Atla
+ İleri Atla
+ Okuma sayfasına ilerleyin
+ Açıklamalı Sayfa
+ Resmi Kapat
+ Arama vurgularını değiştir
+ Metin kutusunu taşımak için sürükleyin
+ Dosya simgesi yok
+ Kopyala %1$s
+ Etiket
+ Liste öğesi işaretçisi
+ Yakınlaştırmayı Sıfırla
+ Demo Ek Açıklamaları Oluşturun
+ Demo Ek Açıklamaları
+ Açık Kalem Oyun Alanı
+ Yeni Sekme
+ Tüm metni vurgula
+ Düzenleme Modunu Değiştir
+ Akıllı Komik Yakınlaştırma
+ Öne Çıkanları Özelleştir
+ İngilizce, İspanyolca, Fransızca vb.
+ Hintçe, Marathi, Sanskritçe + İngilizce
+ Çince + İngilizce
+ Japonca + İngilizce
+ Korece + İngilizce
+ Belge
+ Oluşturuldu
+ %1$s (Metin Görünümü)
+ %1$s (Yeniden akıtma)
+ Ekle / Düzenle
+ Hiçbir etiket atanmadı.
+ Etiketleri Uygula
+ Etiket arayın veya oluşturun…
+ \"%1$s\" oluştur
+ Bölümü Değiştirmek İçin Çekme Mesafesi
+ Kısa
+ Uzun
+ Hız: %1$sx
+ Aralık: %1$sx
+ Oynat/Duraklat
+ Hızı Sıfırla
+ Perdeyi Sıfırla
+ Renk Seç
+ Bu kitabın görüntülenecek içeriği yok.
+ Kopyalanan Bağlantı
+ Kopyalanan Metin
+ İçindekiler
+ Yer imi
+ Sayfaya Geri Dön %1$d
+ Önceki sayfaya dön
+ Akıllı Yakınlaştırmadan Çık
+ Akıllı Komik Yakınlaştırma
+ Akıllı Komik Yakınlaştırmayı Değiştir
+ Ekran yakalama koruması
+ Ekran yakalama koruması açık
+ Ekran yakalama koruması kapalı
+ Ayarlar
+ Düzenle
+ Geri yükle
+ %1$s seçilmiş
+ Okuyucu varsayılanları
+ PDF-özel OCR, ek açıklama ve araç ayarları PDF okuyucu.
+ AI Tanım
+ Bölüm %1$d
+ Konum
+ Özel Yazı Tipi
+ Bir hata oluştu: %1$s
+ Belge yüklenirken hata oluştu: %1$s
+ OCR bu sayfada metin bulunamadı.
+ Sayfa boş görünüyor veya metin çıkarılamıyor.
+ Boş bir sayfa özetlenemez.
+ Belge yüklenmedi.
+ AI özellikler çevrimdışı olarak kullanılamaz OSS inşa etmek.
+ Güvenlik nedeniyle engellendi.
+ %1$s için bir model seçin AI anahtar ve model ayarları.
+ %1$s ekleyin API AI girin anahtar ve model ayarları.
+ AI sağlayıcı boş bir yanıt döndürdü.
+ AI sağlayıcı hatası: %1$d. %2$s
+ Bu özetin Gemini olması gerekiyor model çünkü seçilen Groq modelleri PDF/görüntü girişini desteklemiyor.
+ Geri bildirimi kullanmak için oturum açmalısınız.
+ Geri bildirim göndermek için oturum açmalısınız.
+ Biletler için maksimum 3 görsele izin verilir.
+ Mesaj başına maksimum 5 görsele izin verilir.
+ Bir veya daha fazla resim 5 MB sınırını aşıyor.
+ Bilet oluşturulamadı: %1$s
+ Gönderilemedi: %1$s
+ Özet akışı yüklenemedi: %1$s
+ Boş gövde
+ İndirme başarısız oldu: %1$s
+ İndirme hatası: %1$s
+ Satın alma başarısız oldu: %1$s
+ Faturalandırma hizmetine bağlanılamadı.
+ Ürünler bulunamadı.
+ Ürünler sorgulanamadı
+ Açık Kaynak sürümünde mevcut değil
+ Okunacak metin yok.
+ Oynatma başlatılırken hata oluştu.
+ Ses yüklenemedi.
+ Oynatma hatası: %1$s
+ Bulut TTS yapılandırılmamış.
+ Bilinmeyen Kitap
+ AI anahtarlar ve modeller
+ Kayıtlı anahtarlar
+ Anahtar ekle veya değiştir
+ sağlayıcı
+ API anahtar
+ Anahtarı kaydet
+ Tüm özellikler için tek bir model kullanın
+ Kapalıyken, her okuyucu AI özellik kendi seçilmiş modelini kullanır.
+ Hepsi AI özellikler
+ Akıllı sözlük, özetler ve özetlerin tümü bu modeli kullanır.
+ Akıllı sözlük
+ Seçilen kelimeleri veya cümleleri tanımlarken kullanılır.
+ Özetler
+ EPUB için kullanılır özetler ve PDF sayfa özetleri. PDF/resim özetleri için Gemini gerekir.
+ Özetler
+ Hikaye özeti oluşturmak için kullanılır.
+ Kaydedilen Gemini\'yi kullanır anahtar. Yalnızca %1$s şimdilik destekleniyor.
+ Kaydet %1$s anahtar?
+ Kaydettikten sonra yalnızca ilk 3 ve son 3 karakter görünecektir. Daha sonra değiştirmek için değiştirin veya silin.
+ Sil %1$s anahtar?
+ Bu sağlayıcıyı kullanan özellikler, yeni bir anahtar kaydedilene kadar çalışmayı durduracaktır.
+ Anahtar kaydedilmedi
+ Sil %1$s anahtar
+ Modeli
+ Hiçbir model seçilmedi
+ Göster AI okuyucuda
+ Gizle AI okuyucuda
+ Katı Renkler
+ Dokulu
+ Özel Katı
+ Özel Dokulu
+ Özel Dokuyu Seçin
+ Metin Parlaklığı (Işık)
+ Metin Parlaklığı (Koyu)
+ Varsayılan
+ Sol
+ Sağ
+ Gerekçelendir
+ Her Zaman Göster
+ Menülerle Senkronizasyon
+ Her Zaman Gizle
+ Üst
+ Alt
+ Orijinal meta veriler geri yüklensin mi?
+ Bu, orijinal başlığı, yazarı, seriyi ve özeti EPUB dosyasına geri yazacaktır. dosya. Okuma ilerleme durumu, etiketler ve notlar değişmeyecektir.
+ EPUB meta veriler düzenlendi
+ EPUB meta verisi dosya
+ Uygulamada görünen ad değiştirildi
+ Dosyadaki meta veriler
+ Meta veriler
+ Dosya
+ Başlık
+ Serisi
+ Okuma
+ Dosya adı
+ Değiştirildi
+ Özet
+ Kitaplık etiketleri
+ Düzenlenebilir meta veriler
+ Görünen ad
+ Reader\'da gösterilen ad
+ Orijinal dosya: %1$s
+ Üst Çubuk
+ Alt Çubuk
+ Gizli Araçlar
+ Daha fazla menü
+ Gizli araçlar
+ Araçları buraya bırakın
+ Yeniden sıralamak için sürükleyin
+ Harici Uygulamalar
+ Gezinme Kaydırıcısı
+ Parlaklık
+ Kenar çubuğu
+ Seçilebilir metni vurgula
+ Düzenleme Modu
+ TTS Kontroller
+ Okuma Modu
+ Sayfa Yönetimi
+ Metin Görünümü (Yeniden Akıtma)
+ Güncel kitap
+ Küresel
+ Bu kitap
+ Değişiklikleri etkinleştir
+ Buradaki kurallar, belirli bir başlık için devre dışı bırakılmadığı sürece her kitap için geçerlidir.
+ Kural ekle
+ Kitap kuralı ekle
+ Henüz küresel değiştirme kuralı yok.
+ Henüz kitaba özel kural yok.
+ Burada genel kuralları kullanın
+ Bir kitabın kendi telaffuz seçeneklerine ihtiyacı olduğunda bunu kapatın.
+ Kitap kurallarını etkinleştir
+ Yerel kurallar küresel kuralların ardından yürür.
+ Devralınan küresel kurallar
+ Devralılacak küresel kural yok.
+ Bu kitapta izin veriliyor
+ Bu kitap için devre dışı bırakıldı
+ Öneriler
+ Yeni değiştirme
+ Değiştirmeyi düzenle
+ Değiştir
+ Olarak konuş
+ Etkin
+ Tüm kelime
+ Maç durumu
+ Girişi önizleyin
+ Kurallar
+ sessizlik
+ Düz metin
+ büyük/küçük harfe duyarlı
+ Güncel kitap
+ Kural ekle
+ Bu kitap için henüz değiştirme kuralı yok.
+ Yeni değiştirme
+ Değiştirmeyi düzenle
+ ile
+ boş metin
+ Hakkında
+ Masaüstü okuyucu
+ Masaüstü erişimi
+ Hesap
+ Hesap ve krediler
+ Hesaba genel bakış
+ AI merkez
+ EPUB için kullanılır özetler ve PDF sayfa özetleri.
+ Episteme oss
+ Yazar metni
+ Önbellek: %1$s
+ Önbelleğe alındı
+ Önbelleğe alınmış özet
+ Gemini\'yi seçin Bulutun yüksek sesle okunması için kullanılan ses.
+ Oluşturulan masaüstü kitabını ve EPUB sayfalandırma önbellek dosyaları? Kitaplar bir sonraki açıldığında yeniden oluşturulacak.
+ Ses önbelleğini temizle
+ Araçları kapat
+ Bulut senkronizasyonu
+ Bulut TTS ihtiyaçlar Gemini
+ Bulut TTS oturum açılmış kredilere ihtiyaç var
+ Bulut TTS hazır
+ Bulut TTS ayarlar
+ Bulut TTS müsait değil
+ Bulut TTS ses
+ İçerir
+ Maliyet hesaplama
+ Mevcut konumunuza ilişkin bir özet oluşturun.
+ Akıllı raf oluştur
+ %1$d kredi mevcut
+ %1$s kredi
+ Okuyucu için içe aktarılan yazı tipleri
+ Yazı tipini sil
+ %1$s silinsin mi? Bunu kullanan kitaplar varsayılan yazı tipine geri dönecektir.
+ \"%1$s\" silinsin mi? Kitaplar kütüphanenizde kalır.
+ Özeti sil
+ Devre dışı
+ İçe aktarılacak dosyaları bırakın
+ Desteklenen dosyaları içe aktarılacak şekilde bırakın
+ Başka bir şey için doğrudan e-posta yoluyla bizimle iletişime geçin.
+ Eşittir
+ Ekstralar
+ Geribildirim
+ Alan
+ Klasör yolu
+ Buradan
+ Tam tarama
+ Ücretsiz, %1$d sol
+ Özet oluştur
+ Özet oluştur
+ Hataları bildirin, özellik isteyin veya doğrudan destek ekibiyle iletişime geçin.
+ GitHub Sponsorları
+ GitHub Sponsorları aracılığıyla gelişimi destekleyin.
+ Google oturum açma bu masaüstü yapısı için yapılandırılmamış.
+ Şundan büyük:
+ Yardım
+ Hata raporları, özellik istekleri ve destek
+ Gizle
+ Dosyaları içe aktar
+ Sorunlar
+ Hatalar ve özellik istekleri için sorun izleyiciyi açın.
+ Şundan az:
+ Kütüphane ve okuyucu
+ Herhangi biri
+ Kitaplık eylemleri
+ Daha Fazla
+ Bu kitap için henüz önbelleğe alınmış özet yok.
+ Kitaplarda kullanmak için TTF, OTF veya WOFF2 dosyalarını içe aktarın.
+ \"%1$s\" ile eşleşen yazı tipi bulunamadı
+ Hayır Google hesap bağlandı.
+ Bu bölüm için önbelleğe alınmış özet yok.
+ Çevrimdışı masaüstü okuyucu
+ Okuyucuları aç
+ Açılış %1$s
+ Kitaplığınızı açma
+ Operatör
+ Sayfa
+ Şifre korumalı PDF
+ Patreon
+ Projeyi Patreon\'da destekleyin.
+ Duraklatıldı
+ %1$s açılmadan önce bir şifre gerektirir.
+ Şifre gerekli veya yanlış.
+ Bu şifre %1$s\'yi açmadı. PDF girin şifreyi girin ve tekrar deneyin.
+ Yüzde
+ Planı
+ Ses hazırlanıyor
+ Tercihler
+ Hesap ve krediler
+ Hesap ve krediler
+ Bu hesap için Pro\'nun kilidi açık değil.
+ Pro ve krediler yalnızca Android\'den satın alınabilir. uygulama. Masaüstü aynı oturum açılmış hesabı kontrol eder ve bu kredileri bulut TTS, özetler, özetler ve diğer ücretli AI için kullanır. özellikler.
+ Hesap durumunuzu masaüstünde kontrol etmek için oturum açın.
+ Bu hesap için Pro\'nun kilidi açıldı.
+ İlerleme
+ Proje
+ Okuyucu
+ Okuyucu sekmeleri kapalı
+ Okuyucu sekmeleri açık
+ Yenile
+ Kitaplığınıza eklemek için bırakın.
+ Güvenli anahtar depolama bu işletim sisteminde kullanılamıyor. Buraya girilen anahtarlar bu oturum için kullanılacak ancak kalıcı olmayacaktır.
+ Ayarlar merkezi
+ Android ile eşleşir Akıllı sözlük, özetler ve özetler için geçişi gizleyin.
+ Hesabı, Pro\'yu ve kredileri senkronize et
+ Oturum açıldı
+ Kaynak kodu
+ GitHub\'daki proje kaynağına göz atın.
+ Sesleri değiştirmek için okumayı bırakın.
+ Destek
+ Destek Episteme
+ Katkılar okuyucunun Android genelinde gelişmesine yardımcı olur ve masaüstü.
+ Destekleme yolları Episteme gelişme
+ Klasörleri senkronize et
+ Meta verileri senkronize et
+ Etiket adı
+ Seçilen kitapları etiketle
+ Başlık metni
+ Araçlar
+ İçe aktarma, senkronize etme ve uygulama ayarları
+ Tür, ör. PDF
+ Görüntüle
+ Ses önbelleği
+ Gömülü web görünümü hazırlanıyor…
+ Paketlenmiş gömülü web görünümü hazırlanıyor %1$d%%
+ Gömülü web görünümü yüklendi. Yeniden başlat Episteme Kurulumu bitirmek için.
+ Katıştırılmış web görünümü başlatılamadı: %1$s
+ Çalışıyor…
+ Çalışma alanı
+ Rafa ekle
+ Önce bir raf oluşturun, ardından seçilen kitapları buna ekleyin.
+ Tema oluştur
+ Mevcut: %1$s
+ Harici bir bağlantıya tıkladınız.
+ Düzenle EPUB meta veri
+ Daha az
+ …devamı
+ Henüz özel tema yok
+ Uygulamada yeniden adlandır
+ Etiketler, virgülle ayrılmış
+ Bilinmiyor
+ Tanımla
+ Ek açıklama
+ Ek açıklama seçenekleri
+ Ek açıklama araçları
+ Yardım
+ Hangisini seçin PDF kurtarmak için.
+ Atlama geçmişini temizle
+ Bulut TTS arızalı.
+ Gemini ekleyin tuşuna basın ve Gemini öğesini seçin bulut TTS AI anahtarlar ve modeller.
+ Bulut TTS bu masaüstü yapısı için yapılandırılmamış.
+ Google ile oturum açın bulutu kullanmak için TTS.
+ Bulut TTS kredili, oturum açmış bir hesaba ihtiyacı var. Pro ve krediler yalnızca Android adresinden satın alınabilir. uygulama.
+ Renk
+ Yorum seçenekleri
+ Özel
+ Bu, PDF\'deki ek açıklamayı kaldırır.
+ Ek açıklama silinsin mi?
+ Belge metni
+ Gömülü PDF yorum
+ Sayfa oluşturulamadı.
+ Özellik kullanılamıyor
+ Bitti
+ Dolma kalem
+ Arama sonuçlarını gizle
+ Rengi vurgula %1$d
+ Vurgulayıcı paleti
+ Etkileşim
+ İndeksleme %1$d/%2$d sayfalar
+ İşaretleme
+ %1$d maçlar
+ %1$d şu ana kadarki maçlar
+ Sonraki sayfa
+ Sonraki arama sonucu
+ Henüz ek açıklama yok
+ Henüz yer işareti yok
+ Yorum yok
+ Eşleşme yok
+ Dizine eklenen sayfalarda henüz eşleşme yok
+ İçindekiler tablosu yok
+ Burada okunacak bir metin yok.
+ Bu sayfada okunacak metin yok.
+ Özetlenecek bir metin yok.
+ Yorumu aç
+ Krediler bitti. Pro ve krediler yalnızca Android\'den satın alınabilir. uygulama.
+ Bulut kullanma TTS masaüstünde krediye ihtiyacı var. Pro ve krediler yalnızca Android adresinden satın alınabilir. uygulama.
+ Bu özelliğin kullanılması masaüstü bilgisayar için kredi gerektirir. Pro ve krediler yalnızca Android\'den satın alınabilir. uygulama.
+ Özetleri kullanmak için masaüstünde kredi gerekir. Pro ve krediler yalnızca Android\'den satın alınabilir. uygulama.
+ Özetleri kullanmak için masaüstünde kredi gerekir. Pro ve krediler yalnızca Android\'den satın alınabilir. uygulama.
+ Tava
+ PDF eylem başarısız oldu
+ PDF eylem tamamlanamadı.
+ PDF yorum
+ P. %1$d
+ PDF sayfa %1$d
+ Sayfa %1$d - %2$s
+ Sayfa %1$s %2$d
+ Sayfalar %1$s %2$d
+ PDF kaydedildi
+ PDF aletler
+ Kalem
+ Seçim hazırlanıyor
+ Hazırlanıyor %1$s
+ Önceki sayfa
+ Önceki arama sonucu
+ Yazdırma iletişim kutusu tamamlandı.
+ Profesyonel gerekli
+ Bu özellik Pro gerektirir. Pro yalnızca Android adresinden satın alınabilir uygulamasını kullanırsanız, oturum açıldıktan sonra masaüstü yükseltilmiş hesabı kullanacaktır.
+ Çok kelimeli akıllı sözlük Pro gerektirir. Pro yalnızca Android adresinden satın alınabilir uygulamasını kullanırsanız, oturum açıldıktan sonra masaüstü yükseltilmiş hesabı kullanacaktır.
+ Okuyucu AI özellikler gizlenmiştir.
+ Masaüstü AI bu yapı için yapılandırılmamış.
+ Dikey okuma ve iki sayfaya yayılma için geçerlidir.
+ Yuvarlak vurgulayıcı
+ %1$s konumuna kaydedildi
+ Kaydırma
+ PDF kategorisinde ara
+ Metni seçin
+ Seçildi %1$s
+ Arama sonuçlarını göster
+ Google ile oturum açın Bu özelliği masaüstünde kullanmak için.
+ Google ile oturum açın masaüstünde çok kelimeli akıllı sözlüğü kullanmak için.
+ Google ile oturum açın Özetleri masaüstünde kullanmak için.
+ Google ile oturum açın Özetleri masaüstünde kullanmak için.
+ Durduruldu
+ Metin notu
+ metin notu
+ Metin stili
+ Kalınlık %1$s
+ İçindekiler
+ Bunu aramak için yazın PDF
+ İsimsiz
+ Hesabı ve kredileri görüntüle
+ Ses önbelleği temizlendi
+ Yakınlaştır
+ Yakınlaştır
+ Uzaklaştır
+ Seç
+ Okumaya devam et
+ Reddet
+ Aşağı
+ Yukarı
+ AI
+ Merkez
+ Yazarlar
+ Kütüphaneye geri dön
+ Rezervasyon işlemleri
+ Klasör
+ Göz at
+ Kategoriler
+ Ch. %1$d
+ Bölüm Dönüşleri
+ Yazı tipini seç
+ Okuyucu dokusunu seçin
+ Dosya türlerini temizle
+ Sayfa açıklamalarını temizle
+ Kaynakları temizle
+ Durumu temizle
+ Etiketleri temizle
+ Okuyucuyu kapat
+ Sürekli
+ Kapaklar
+ Özel renkler
+ Özel tema önizlemesi
+ %1$s\'i azalt
+ Sayfayı tanımla
+ Bu, vurguyu ve notunu kaldırır.
+ Tam ekrana girin
+ Tam ekrandan çık
+ Harici arama
+ Kitaplar
+ çizgi roman
+ Belgeler
+ Diğer
+ Metin ve web
+ Doldur
+ Sabit düzen görünümü
+ Klasör boş
+ Burada desteklenen dosya veya alt klasör yok.
+ %1$s, %2$s
+ %1$s - %2$s
+ Filtreleri gizle
+ Okuyucu araçlarını gizle
+ Bir yuvaya dokunun, ardından bir renk seçin.
+ Okumaya devam edin ve yeni kitaplar
+ Kitapları içe aktar
+ Klasörü içe aktar
+ İçe aktarılan yazı tipleri
+ %1$s %2$s
+ %1$s\'yi artırın
+ Atlama geçmişi
+ Düzen ve Aralık
+ Dosyaları uygulama depolama alanına aktarın veya dosyaları yerinde okumak için bir klasör ekleyin.
+ Koleksiyonunuza göz atın
+ AI anahtarlar
+ Akıllı %1$d
+ Okunmamış %1$d
+ Devam ediyor %1$d
+ Tamamlandı %1$d
+ Liste
+ Navigasyon
+ Kitap açık değil
+ Bu klasördeki dosyaları yerinde okumak için bir klasör ekleyin.
+ Henüz klasör yok
+ Gezinme öğesi yok
+ Sayfa içeriği yok
+ Hiçbir ayar bulunamadı
+ Manuel raflar ve seri koleksiyonlar burada görünecek.
+ Henüz raf yok
+ Kitapları kurallara göre toplamak için akıllı raflar oluşturun.
+ Henüz akıllı raf yok
+ Kitaplara eklenen etiketler burada görünecektir.
+ Henüz etiket yok
+ Desteklenen hiçbir dosya içe aktarılmadı.
+ Katalog
+ "%1$s" silinsin mi? Kimlik bilgileri daha sonra değişirse bu katalogdan yayınlanan kitapların açılması durdurulabilir.
+ Katalog yok
+ OPDS ekleyin Uzaktaki kitaplara göz atmak için katalog.
+ Kataloglara, akışlara ve indirmelere göz atın
+ Kitabı Aç
+ Klasörü aç
+ Aç PDF
+ Sayfa ve metin renkleri
+ Sayfa Bilgisi
+ Sayfa genişliği
+ Bu varsayılanlar, platformun paylaşımlı PDF\'yi desteklediği durumlarda geçerlidir. dış görünüş. Kitap başına PDF geçersiz kılmalar PDF okuyucu.
+ PDF dosya eylemleri
+ PDF vurgulayıcı
+ Okuyucunun vurguladığı şeffaflıkla kaydedildi.
+ Sabitle
+ Okuyucu tarafından yönetilen PDF araçlar
+ Otomatik kaydırma, OCR, açıklama varsayılanları ve PDFyalnızca araç görünürlüğü, etkin PDF içinde yönetilir. okuyucu.
+ %1$s %2$s %3$d (%4$d%%)
+ Okuyucu araç çubuğu varsayılanları bu platformdaki okuyucudan yönetilir.
+ Okuyucu araçları
+ Resmi kaydet
+ Arama: %1$s
+ Okuyucuda ara
+ Arama ayarları
+ Seçim
+ Seçim bitiş tutamacı
+ Seçim başlatma tutamacı
+ Kitaplığınızı düzenlemek için raflar, etiketler veya klasör meta verileri ekleyin.
+ Koleksiyonlar, seriler, etiketler ve klasörler
+ Okuyucu araçlarını göster
+ Klasör
+ Akıllı
+ Katı
+ Hız
+ Otomatik kaydırmayı başlat
+ Otomatik kaydırmayı durdur
+ Yüksek sesle okumayı bırak
+ Doku gücü
+ Bu kitabı aramak için yazın
+ Tipografi
+ Ek açıklamayı geri al
+ Sabitlemeyi kaldır
+ Koyu temayı kullan
+ Açık temayı kullan
+ Mono
+ sans
+ Serif
+ Kitapları, yazarları veya etiketleri arayın
+ Alet yok
+ Görünür
+ Yalnızca konuşulanları değiştirin
+ Okuyucu metni, vurgulamalar ve konumlar değişmeden kalır.
+ %1$s -> %2$s
diff --git a/app/src/main/res/values-uk/plurals.xml b/app/src/main/res/values-uk/plurals.xml
index 643772d..5d05bc7 100644
--- a/app/src/main/res/values-uk/plurals.xml
+++ b/app/src/main/res/values-uk/plurals.xml
@@ -78,4 +78,130 @@
(%1$d фрагментів)(%1$d фрагментів)
+
+ Імпорт %1$d книга… Незабаром вона з’явиться у вашій бібліотеці.
+ Імпорт %1$d книги… Вони незабаром з’являться у вашій бібліотеці.
+ Імпорт %1$d книги… Вони незабаром з’являться у вашій бібліотеці.
+ Імпорт %1$d книги… Вони незабаром з’являться у вашій бібліотеці.
+
+
+ Імпортований %1$d книга. Ви можете знайти його на вкладці «Бібліотека».
+ Імпортований %1$d книги. Ви можете знайти їх у вкладці «Бібліотека».
+ Імпортований %1$d книги. Ви можете знайти їх у вкладці «Бібліотека».
+ Імпортований %1$d книги. Ви можете знайти їх у вкладці «Бібліотека».
+
+
+ %1$d книгу додано на полицю.
+ %1$d книг додано на полицю.
+ %1$d книг додано на полицю.
+ %1$d книг додано на полицю.
+
+
+ %1$d книга з тегом "%2$s".
+ %1$d книги з тегом «%2$s».
+ %1$d книги з тегом «%2$s».
+ %1$d книги з тегом «%2$s».
+
+
+ Видалено папку "%1$s" та %2$d книга з додатку.
+ Видалено папку "%1$s" та %2$d книги з програми.
+ Видалено папку "%1$s" та %2$d книги з програми.
+ Видалено папку "%1$s" та %2$d книги з програми.
+
+
+ %1$d файл
+ %1$d файли
+ %1$d файли
+ %1$d файли
+
+
+ Відпустіть, щоб імпортувати %1$d файл
+ Відпустіть, щоб імпортувати %1$d файли
+ Відпустіть, щоб імпортувати %1$d файли
+ Відпустіть, щоб імпортувати %1$d файли
+
+
+ %1$d непідтримуваний файл буде пропущено.
+ %1$d непідтримувані файли будуть пропущені.
+ %1$d непідтримувані файли будуть пропущені.
+ %1$d непідтримувані файли будуть пропущені.
+
+
+ Імпорт %1$d файл…
+ Імпорт %1$d файли…
+ Імпорт %1$d файли…
+ Імпорт %1$d файли…
+
+
+ Імпортований %1$d файл.
+ Імпортований %1$d файли.
+ Імпортований %1$d файли.
+ Імпортований %1$d файли.
+
+
+ Імпортований %1$d файл. Підтримка Reader з’явиться пізніше.
+ Імпортований %1$d файли. Підтримка Reader з’явиться пізніше.
+ Імпортований %1$d файли. Підтримка Reader з’явиться пізніше.
+ Імпортований %1$d файли. Підтримка Reader з’явиться пізніше.
+
+
+ Не вдалося імпортувати %1$d файл.
+ Не вдалося імпортувати %1$d файли.
+ Не вдалося імпортувати %1$d файли.
+ Не вдалося імпортувати %1$d файли.
+
+
+ Пропущено %1$d файл.
+ Пропущено %1$d файли.
+ Пропущено %1$d файли.
+ Пропущено %1$d файли.
+
+
+ Видалити "%1$s" та його %2$d книга з програми? Файли на диску не будуть видалені.
+ Видалити "%1$s" та його %2$d книги з програми? Файли на диску не будуть видалені.
+ Видалити "%1$s" та його %2$d книги з програми? Файли на диску не будуть видалені.
+ Видалити "%1$s" та його %2$d книги з програми? Файли на диску не будуть видалені.
+
+
+ Помилка синхронізації папки для %1$d папку.
+ Помилка синхронізації папки для %1$d папки.
+ Помилка синхронізації папки для %1$d папки.
+ Помилка синхронізації папки для %1$d папки.
+
+
+ Синхронізацію папки завершено з %1$d папку пропущено.
+ Синхронізацію папки завершено з %1$d папок пропущено.
+ Синхронізацію папки завершено з %1$d папок пропущено.
+ Синхронізацію папки завершено з %1$d папок пропущено.
+
+
+ Видалено %1$d потокове OPDS книга з цього каталогу.
+ Видалено %1$d потокове OPDS книг з цього каталогу.
+ Видалено %1$d потокове OPDS книг з цього каталогу.
+ Видалено %1$d потокове OPDS книг з цього каталогу.
+
+
+ Усі книги %1$d
+ Усі книги %1$d
+ Усі книги %1$d
+ Усі книги %1$d
+
+
+ Полиці %1$d
+ Полиці %1$d
+ Полиці %1$d
+ Полиці %1$d
+
+
+ Теги %1$d
+ Теги %1$d
+ Теги %1$d
+ Теги %1$d
+
+
+ Папки %1$d
+ Папки %1$d
+ Папки %1$d
+ Папки %1$d
+
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index daa9aab..35110e0 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -1086,4 +1086,430 @@
Nederlands (нідерландська)УкраїнськаBahasa Indonesia (індонезійська)
+ Показати вкладки на верхній панелі програми
+ Вимкнути локальну синхронізацію
+ Увімкнути локальну синхронізацію
+ Локальну синхронізацію вимкнено
+ Вимкнути синхронізацію локальних папок?
+ Episteme припинить сканування цієї папки та припинить запис JSON синхронізувати файли. Видаліть %1$s папку з цієї папки теж?
+ Тримайте дані синхронізації
+ Видалити дані синхронізації
+ Видалити шрифти?
+ Ви впевнені, що хочете видалити %1$d вибрані шрифти? Це видалить їх з усіх ваших пристроїв, якщо синхронізацію ввімкнено.
+ Для жодної локальної папки не ввімкнено синхронізацію.
+ Синхронізацію локальної папки вимкнено.
+ Синхронізацію локальної папки вимкнено. Папку даних синхронізації видалено.
+ Синхронізацію локальної папки вимкнено, але папку даних синхронізації не вдалося видалити.
+ Синхронізацію локальної папки ввімкнено.
+ Вертикальний (WebView)
+ Вертикальний (власна бета-версія)
+ Книга Заміни слів
+ Попередній TTS шматок
+ Далі TTS шматок
+ Зображення
+ Зображення не знайдено.
+ Завантажити зображення
+ Збережено %1$s
+ Не вдалося зберегти зображення.
+ PDF розворот сторінки
+ Одна сторінка
+ Дві сторінки
+ Лише перша сторінка
+ Починає розвороти після титульної сторінки.
+ Яскравість
+ Використовуйте системну яскравість
+ Слідкує за налаштуванням яскравості пристрою.
+ Власна яскравість
+ Застосовується, коли відкритий екран читача.
+ %1$d%%
+ Створена полиця "%1$s".
+ Створено розумну полицю "%1$s".
+ Полицю перейменовано на "%1$s".
+ Видалена полиця "%1$s".
+ Оновлено "%1$s".
+ Ці файли вже є в бібліотеці.
+ %1$s - %2$s
+ зберегти
+ Зберегти коментар
+ Додати коментар
+ Відповісти
+ Додати коментар…
+ Коментарі
+ Редагування коментаря
+ Відповідь на %1$s
+ Використовуйте PDF Імена файлів
+ Яскравість
+ Актуальна книга
+ Додайте правило
+ Для цієї книги ще немає правил заміни.
+ Нова заміна
+ Редагувати заміну
+ с
+ порожній текст
+ про
+ Настільний рідер
+ Доступ до робочого столу
+ Обліковий запис
+ Рахунок і кредити
+ Огляд облікового запису
+ AI хаб
+ Використовується для EPUB резюме та PDF резюме сторінок.
+ Episteme осс
+ Авторський текст
+ Кеш: %1$s
+ Кешовано
+ Кешований підсумок
+ Виберіть Gemini голос, який використовується для читання вголос у хмарі.
+ Видалити згенеровану робочу книгу та EPUB файли кешу сторінки? Їх буде відтворено під час наступного відкриття книг.
+ Очистити голосовий кеш
+ Закрити інструменти
+ Хмарна синхронізація
+ Хмара TTS потребує Gemini
+ Хмара TTS потребує авторизації кредитів
+ Хмара TTS готовий
+ Хмара TTS налаштування
+ Хмара TTS недоступний
+ Хмара TTS голос
+ Містить
+ Розрахунок вартості
+ Створіть підсумок до вашої поточної посади.
+ Створіть розумну полицю
+ %1$d доступні кредити
+ %1$s кредити
+ Імпортовані шрифти для читалки
+ Видалити шрифт
+ Видалити %1$s? Книги, які використовують його, повернуться до шрифту за умовчанням.
+ Видалити \"%1$s\"? Книги залишаються у вашій бібліотеці.
+ Видалити резюме
+ Вимкнено
+ Перетягніть файли для імпорту
+ Перетягніть підтримувані файли для імпорту
+ Зв’яжіться з нами безпосередньо електронною поштою для будь-яких інших питань.
+ Дорівнює
+ Додатково
+ Зворотній зв\'язок
+ Поле
+ Шлях до папки
+ Звідси
+ Повне сканування
+ Безкоштовно, %1$d зліва
+ Згенерувати підсумок
+ Створити резюме
+ Повідомляйте про помилки, надсилайте запити на функції або звертайтеся безпосередньо до служби підтримки.
+ Спонсори GitHub
+ Підтримуйте розробку через спонсорів GitHub.
+ Google вхід не налаштовано для цієї збірки робочого столу.
+ Більше ніж
+ Довідка
+ Звіти про помилки, запити на функції та підтримка
+ Сховати
+ Імпорт файлів
+ Питання
+ Відкрийте програму відстеження помилок і запитів на функції.
+ Менше ніж
+ Бібліотека і читач
+ Будь-який
+ Бібліотечні акції
+ більше
+ Кешованих підсумків для цієї книги ще немає.
+ Імпортуйте файли TTF, OTF або WOFF2, щоб використовувати їх у книгах.
+ Не знайдено шрифтів, що відповідають \"%1$s\"
+ Ні Google обліковий запис підключено.
+ Для цього розділу не збережено зведення.
+ Офлайн настільна програма для читання
+ Відкриті читачі
+ Відкриття %1$s
+ Відкриття вашої бібліотеки
+ Оператор
+ Сторінка
+ Захищено паролем PDF
+ Patreon
+ Підтримайте проект на Patreon.
+ Призупинено
+ %1$s потрібен пароль, перш ніж його можна буде відкрити.
+ Пароль необхідний або неправильний.
+ Цей пароль не відкрив %1$s. Введіть PDF пароль і спробуйте ще раз.
+ Відсоток
+ План
+ Підготовка аудіо
+ Уподобання
+ Рахунок і кредити
+ Рахунок і кредити
+ Pro не розблоковано для цього облікового запису.
+ Pro та кредити можна придбати лише в Android додаток Desktop перевіряє той самий обліковий запис, у який ви ввійшли, і використовує ці кредити для хмарних TTS, підсумків, підсумків та інших платних AI особливості.
+ Увійдіть, щоб перевірити стан свого облікового запису на комп’ютері.
+ Pro для цього облікового запису розблоковано.
+ Прогрес
+ Проект
+ читач
+ Вкладки Reader вимкнено
+ Увімкнено вкладки Reader
+ Оновити
+ Відпустіть, щоб додати до своєї бібліотеки.
+ Безпечне зберігання ключів недоступне в цій операційній системі. Введені тут ключі використовуватимуться для цього сеансу, але не зберігатимуться.
+ Центр налаштувань
+ Відповідає Android сховати перемикач для інтелектуального словника, підсумків і підсумків.
+ Синхронізація облікового запису, Pro та кредитів
+ Ви ввійшли
+ Вихідний код
+ Перегляньте джерело проекту на GitHub.
+ Припиніть читати, щоб змінити голоси.
+ Підтримка
+ Підтримка Episteme
+ Внески допомагають читачеві вдосконалюватись у Android і робочий стіл.
+ Способи підтримки Episteme розвитку
+ Синхронізація папок
+ Синхронізація метаданих
+ Назва тега
+ Позначте тегами вибрані книги
+ Текст заголовка
+ Інструменти
+ Імпорт, синхронізація та налаштування програми
+ Тип, напр. PDF
+ Переглянути
+ Голосовий кеш
+ Підготовка вбудованого веб-перегляду…
+ Підготовка вбудованого веб-перегляду %1$d%%
+ Вбудований веб-перегляд встановлено. Перезапустіть Episteme щоб завершити налаштування.
+ Не вдалося запустити вбудований веб-перегляд: %1$s
+ Робота…
+ Робоча область
+ Додати на полицю
+ Спочатку створіть полицю, а потім додайте на неї вибрані книги.
+ Створити тему
+ Існуючі: %1$s
+ Ви натиснули зовнішнє посилання.
+ Редагувати EPUB метадані
+ менше
+ … більше
+ Спеціальних тем ще немає
+ Перейменувати в додатку
+ Теги, розділені комами
+ Невідомий
+ Визначити
+ Анотація
+ Параметри анотації
+ Інструменти анотування
+ асист
+ Виберіть який PDF зберегти.
+ Очистити історію переходів
+ Хмара TTS не вдалося.
+ Додайте Gemini і виберіть Gemini хмара TTS в AI ключі та моделі.
+ Хмара TTS не налаштовано для цієї збірки робочого столу.
+ Увійдіть за допомогою Google використовувати хмару TTS.
+ Хмара TTS потрібен обліковий запис із кредитами. Pro та кредити можна придбати лише в Android додаток
+ Колір
+ Варіанти коментарів
+ Custom
+ Це видаляє анотацію з цього PDF.
+ Видалити анотацію?
+ Текст документа
+ Вбудований PDF коментар
+ Не вдалося відобразити сторінку.
+ Функція недоступна
+ Готово
+ Перова ручка
+ Приховати результати пошуку
+ Колір виділення %1$d
+ Палітра хайлайтерів
+ Взаємодія
+ Індексація %1$d/%2$d сторінки
+ Розмітка
+ %1$d сірники
+ %1$d матчів на даний момент
+ Наступна сторінка
+ Наступний результат пошуку
+ Анотацій ще немає
+ Закладок ще немає
+ Без коментарів
+ Немає збігів
+ Збігів на проіндексованих сторінках ще немає
+ Немає змісту
+ Тут немає тексту для читання.
+ На цій сторінці немає тексту для читання.
+ Немає тексту для підсумовування.
+ Відкрити коментар
+ Закінчилися кредити. Pro та кредити можна придбати лише в Android додаток
+ Використання хмари TTS потрібні кредити на робочому столі. Pro та кредити можна придбати лише в Android додаток
+ Для використання цієї функції потрібні кредити на комп’ютері. Pro та кредити можна придбати лише в Android додаток
+ Для використання recaps потрібні кредити на комп’ютері. Pro та кредити можна придбати лише в Android додаток
+ Для використання резюме потрібні кредити на робочому столі. Pro та кредити можна придбати лише в Android додаток
+ Пан
+ PDF дія не вдалася
+ PDF дію не вдалося завершити.
+ PDF коментар
+ стор. %1$d
+ PDF сторінка %1$d
+ Сторінка %1$d - %2$s
+ Сторінка %1$s %2$d
+ Сторінки %1$s %2$d
+ PDF збережено
+ PDF інструменти
+ Олівець
+ Готується вибір
+ Підготовка %1$s
+ Попередня сторінка
+ Попередній результат пошуку
+ Діалог друку завершено.
+ Потрібен професіонал
+ Для цієї функції потрібен Pro. Pro можна придбати лише в Android після входу в систему комп’ютер використовуватиме оновлений обліковий запис.
+ Багатослівний розумний словник вимагає Pro. Pro можна придбати лише в Android після входу в систему комп’ютер використовуватиме оновлений обліковий запис.
+ Зчитувач AI функції приховані.
+ Робочий стіл AI не налаштовано для цієї збірки.
+ Застосовується для вертикального читання та двосторінкових розворотів.
+ Круглий хайлайтер
+ Збережено в %1$s
+ Прокрутка
+ Пошук у PDF
+ Виберіть текст
+ Вибрано %1$s
+ Показати результати пошуку
+ Увійдіть за допомогою Google щоб використовувати цю функцію на робочому столі.
+ Увійдіть за допомогою Google використовувати багатослівний розумний словник на робочому столі.
+ Увійдіть за допомогою Google використовувати рекапс на робочому столі.
+ Увійдіть за допомогою Google використовувати резюме на робочому столі.
+ Зупинився
+ Текстова примітка
+ текстова примітка
+ Стиль тексту
+ Товщина %1$s
+ TOC
+ Введіть для пошуку це PDF
+ Без назви
+ Переглянути рахунок і кредити
+ Голосовий кеш очищено
+ Збільшити
+ Збільшити
+ Зменшити масштаб
+ Виберіть
+ Читайте далі
+ Відхилити
+ вниз
+ вгору
+ AI
+ центр
+ Автори
+ Назад до бібліотеки
+ Книжкові акції
+ Папка
+ переглядати
+ Категорії
+ гл. %1$d
+ Повороти глави
+ Виберіть шрифт
+ Виберіть текстуру читача
+ Очистити типи файлів
+ Очистити анотації сторінки
+ Чисті джерела
+ Очистити статус
+ Очистити теги
+ Закрити читання
+ Безперервний
+ Обкладинки
+ Нестандартні кольори
+ Попередній перегляд спеціальної теми
+ Зменшити %1$s
+ Визначити сторінку
+ Це видалить виділення та його ноту.
+ Перейти на весь екран
+ Вийти з повноекранного режиму
+ Зовнішній пошук
+ Книги
+ Комікси
+ Документи
+ інше
+ Текст і Інтернет
+ Заповнити
+ Зовнішній вигляд фіксованого макета
+ Папка порожня
+ Тут немає підтримуваних файлів або вкладених папок.
+ %1$s, %2$s
+ %1$s - %2$s
+ Сховати фільтри
+ Приховати інструменти читання
+ Торкніться слота та виберіть колір.
+ Продовжуйте читати останні книги
+ Імпорт книг
+ Імпорт папки
+ Імпортовані шрифти
+ %1$s %2$s
+ Збільшити %1$s
+ Історія стрибків
+ Макет і інтервали
+ Імпортуйте файли в сховище програми або додайте папку для читання файлів на місці.
+ Перегляньте свою колекцію
+ AI ключі
+ Розумний %1$d
+ Непрочитаний %1$d
+ Виконується %1$d
+ Завершити %1$d
+ Список
+ Навігація
+ Немає відкритої книги
+ Додайте папку, щоб читати файли з цієї папки на місці.
+ Папок ще немає
+ Немає елементів навігації
+ Немає вмісту сторінки
+ Налаштувань не знайдено
+ Тут з’являться ручні полиці та колекції серій.
+ Ще немає полиць
+ Створіть розумні полиці, щоб збирати книги за правилами.
+ Розумних полиць ще немає
+ Тут з’являться теги, додані до книг.
+ Тегів ще немає
+ Не було імпортовано жодного підтримуваного файлу.
+ Каталог
+ Видалити "%1$s"? Потокові книги з цього каталогу можуть перестати відкриватися, якщо пізніше облікові дані зміняться.
+ Без каталогів
+ Додайте OPDS каталог для перегляду віддалених книг.
+ Переглядайте каталоги, потоки та завантаження
+ Відкрита книга
+ Відкрити папку
+ Відкрити PDF
+ Кольори сторінки та тексту
+ Інформація про сторінку
+ Ширина сторінки
+ Ці стандартні параметри застосовуються, якщо платформа підтримує спільний PDF зовнішній вигляд. За книгу PDF перекриває перебування в PDF читач.
+ PDF дії з файлами
+ PDF хайлайтер
+ Збережено з прозорістю підсвічування читача.
+ Pin
+ Керований читачем PDF інструменти
+ Автоматичне прокручування, OCR, анотації за замовчуванням і PDF-лише видимість інструменту керуються всередині активного PDF читач.
+ %1$s %2$s %3$d (%4$d%%)
+ Параметри панелі інструментів Reader за замовчуванням керуються з Reader на цій платформі.
+ Інструменти читача
+ Зберегти зображення
+ Пошук: %1$s
+ Шукати в читалці
+ Налаштування пошуку
+ Вибір
+ Ручка кінця вибору
+ Початковий маркер вибору
+ Додайте полиці, теги або метадані папок, щоб упорядкувати свою бібліотеку.
+ Колекції, серії, теги та папки
+ Показати засоби читання
+ Папка
+ Розумний
+ Твердий
+ швидкість
+ Почати автоматичне прокручування
+ Зупинити автоматичне прокручування
+ Перестаньте читати вголос
+ Міцність текстури
+ Введіть для пошуку в цій книзі
+ Типографіка
+ Скасувати анотацію
+ Відкріпити
+ Використовуйте темну тему
+ Використовуйте світлу тему
+ Моно
+ Sans
+ Засічки
+ Шукайте книги, авторів або теги
+ Без інструментів
+ Видно
+ Замінювати тільки сказане
+ Текст Reader, виділення та розташування залишаються незмінними.
+ %1$s -> %2$s
diff --git a/app/src/main/res/values-vi/plurals.xml b/app/src/main/res/values-vi/plurals.xml
index 29316d5..93e9d5f 100644
--- a/app/src/main/res/values-vi/plurals.xml
+++ b/app/src/main/res/values-vi/plurals.xml
@@ -2,41 +2,138 @@
%1$d sách
+ %1$d cuốn sáchsách
+ cuốn sách%1$d kệ sách
+ %1$d kệTìm thấy %1$d kết quả
+ %1$d kết quả được tìm thấyTìm thấy %1$d kết quả khớp
+ %1$d trận đấu được tìm thấyXóa vĩnh viễn tệp
+ Xóa tập tin vĩnh viễnBạn có muốn xóa vĩnh viễn %1$d tệp đã chọn khỏi thiết bị không? Không thể hoàn tác thao tác này.
+ Bạn có muốn xóa vĩnh viễn %1$d tập tin đã chọn từ thiết bị của bạn? Không thể hoàn tác hành động này.Bạn có muốn xóa %1$d tệp đã chọn khỏi danh sách tệp gần đây không? Tệp sẽ xuất hiện lại nếu bạn mở lại từ thư viện.
+ Bạn có muốn xóa %1$d tập tin đã chọn từ danh sách tập tin gần đây? Nó sẽ xuất hiện lại nếu bạn mở lại từ thư viện.Bạn có chắc muốn xóa %1$d sách khỏi kệ \"%2$s\" không? Sách vẫn nằm trong thư viện và xuất hiện trong Chưa xếp kệ.
+ Bạn có chắc chắn muốn xóa %1$d cuốn sách từ \'%2$s\' cái kệ? Sách sẽ vẫn còn trong thư viện của bạn và xuất hiện dưới mục Chưa được lưu trữ.Đã xóa %1$d sách khỏi thư viện.
+ %1$d cuốn sách bị xóa khỏi thư viện.%1$d thư mục
+ %1$d thư mục%1$d thẻ
+ %1$d gắn thẻ(%1$d đoạn)
+ (%1$d đoạn)
+
+
+ Đang nhập %1$d sách… Chúng sẽ sớm xuất hiện trong Thư viện của bạn.
+ Đang nhập %1$d sách… Nó sẽ sớm xuất hiện trong Thư viện của bạn.
+
+
+ Nhập khẩu %1$d sách. Bạn có thể tìm thấy chúng trong tab Thư viện.
+ Nhập khẩu %1$d sách. Bạn có thể tìm thấy nó trong tab Thư viện.
+
+
+ %1$d sách được thêm vào kệ.
+ %1$d cuốn sách được thêm vào kệ.
+
+
+ %1$d sách được gắn thẻ "%2$s".
+ %1$d cuốn sách được gắn thẻ "%2$s".
+
+
+ Đã xóa thư mục "%1$s" và %2$d sách từ ứng dụng.
+ Đã xóa thư mục "%1$s" và %2$d cuốn sách từ ứng dụng.
+
+
+ %1$d tập tin
+ %1$d tập tin
+
+
+ Thả để nhập %1$d tập tin
+ Thả để nhập %1$d tập tin
+
+
+ %1$d các tập tin không được hỗ trợ sẽ bị bỏ qua.
+ %1$d tập tin không được hỗ trợ sẽ bị bỏ qua.
+
+
+ Đang nhập %1$d tập tin…
+ Đang nhập %1$d tập tin…
+
+
+ Nhập khẩu %1$d tập tin.
+ Nhập khẩu %1$d tài liệu.
+
+
+ Nhập khẩu %1$d tập tin. Hỗ trợ người đọc đến sau.
+ Nhập khẩu %1$d tài liệu. Hỗ trợ người đọc đến sau.
+
+
+ Không thể nhập %1$d tập tin.
+ Không thể nhập %1$d tài liệu.
+
+
+ Đã bỏ qua %1$d tập tin.
+ Đã bỏ qua %1$d tài liệu.
+
+
+ Xóa "%1$s" và %2$d của nó sách từ ứng dụng? Các tập tin trên đĩa sẽ không bị xóa.
+ Xóa "%1$s" và %2$d của nó cuốn sách từ ứng dụng? Các tập tin trên đĩa sẽ không bị xóa.
+
+
+ Đồng bộ hóa thư mục không thành công cho %1$d thư mục.
+ Đồng bộ hóa thư mục không thành công cho %1$d thư mục.
+
+
+ Đồng bộ hóa thư mục đã hoàn tất với %1$d các thư mục bị bỏ qua.
+ Đồng bộ hóa thư mục đã hoàn tất với %1$d thư mục bị bỏ qua.
+
+
+ Đã xóa %1$d đã phát trực tuyến OPDS sách từ danh mục đó.
+ Đã xóa %1$d đã phát trực tuyến OPDS cuốn sách từ danh mục đó.
+
+
+ Tất cả sách %1$d
+ Tất cả sách %1$d
+
+
+ Kệ %1$d
+ Kệ %1$d
+
+
+ Thẻ %1$d
+ Thẻ %1$d
+
+
+ Thư mục %1$d
+ Thư mục %1$d
diff --git a/app/src/main/res/values-zh-rCN/plurals.xml b/app/src/main/res/values-zh-rCN/plurals.xml
index 06c09ae..e578ab6 100644
--- a/app/src/main/res/values-zh-rCN/plurals.xml
+++ b/app/src/main/res/values-zh-rCN/plurals.xml
@@ -2,41 +2,138 @@
%1$d 本书
+ %1$d书本书
+ 书%1$d 个书架
+ %1$d架子找到 %1$d 个结果
+ %1$d找到结果找到 %1$d 个匹配项
+ %1$d找到匹配项永久删除文件
+ 永久删除文件确定要从设备中永久删除选中的 %1$d 个文件吗?此操作无法撤销。
+ 您想永久删除 %1$d从您的设备中选择文件?此操作无法撤消。要从最近文件列表中移除选中的 %1$d 个文件吗?如果再次从书库打开,它们会重新显示。
+ 您要删除 %1$d从最近的文件列表中选择文件?如果您再次从库中打开它,它会重新出现。确定要从“%2$s”书架中移除 %1$d 本书吗?这些书仍会保留在你的书库中,并显示在未归架下。
+ 您确定要删除 %1$d本书来自\'%2$s\'架子?该书将保留在您的图书馆中并显示在“未上架”下。已从书库移除 %1$d 本书。
+ %1$d书已从图书馆删除。%1$d 个文件夹
+ %1$d文件夹%1$d 个标签
+ %1$d标签(%1$d 个片段)
+ (%1$d 块)
+
+
+ 导入%1$d书籍……它们很快就会出现在您的图书馆中。
+ 导入%1$d书...它很快就会出现在您的图书馆中。
+
+
+ 进口%1$d图书。您可以在“库”选项卡中找到它们。
+ 进口%1$d书。您可以在“库”选项卡中找到它。
+
+
+ %1$d书籍已添加至书架。
+ %1$d书已添加到书架。
+
+
+ %1$d标有“%2$s”的书籍。
+ %1$d标有“%2$s”的书。
+
+
+ 已删除文件夹“%1$s”和%2$d来自应用程序的书籍。
+ 已删除文件夹“%1$s”和%2$d从应用程序预订。
+
+
+ %1$d文件
+ %1$d文件
+
+
+ 拖拽导入%1$d文件
+ 拖拽导入%1$d文件
+
+
+ %1$d将跳过不支持的文件。
+ %1$d不支持的文件将被跳过。
+
+
+ 导入%1$d文件…
+ 导入%1$d文件…
+
+
+ 进口%1$d文件。
+ 进口%1$d文件。
+
+
+ 进口%1$d文件。读者支持稍后提供。
+ 进口%1$d文件。读者支持稍后提供。
+
+
+ 无法导入%1$d文件。
+ 无法导入%1$d文件。
+
+
+ 已跳过 %1$d文件。
+ 已跳过 %1$d文件。
+
+
+ 删除“%1$s”及其%2$d来自应用程序的书籍?磁盘上的文件不会被删除。
+ 删除“%1$s”及其%2$d从应用程序预订?磁盘上的文件不会被删除。
+
+
+ %1$d 文件夹同步失败文件夹。
+ %1$d 文件夹同步失败文件夹。
+
+
+ 文件夹同步已完成 %1$d跳过的文件夹。
+ 文件夹同步已完成 %1$d文件夹已跳过。
+
+
+ 已删除%1$d流式传输OPDS该目录中的书籍。
+ 已删除%1$d流式传输OPDS该目录中的书。
+
+
+ 所有书籍%1$d
+ 所有书籍%1$d
+
+
+ 货架%1$d
+ 货架%1$d
+
+
+ 标签%1$d
+ 标签%1$d
+
+
+ 文件夹%1$d
+ 文件夹%1$d
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index 59528ba..48d1574 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -1086,4 +1086,430 @@
Nederlands(荷兰语)Українська(乌克兰语)Bahasa Indonesia(印度尼西亚语)
+ 在顶部应用栏中显示选项卡
+ 禁用本地同步
+ 启用本地同步
+ 本地同步已禁用
+ 禁用本地文件夹同步?
+ Episteme将停止扫描此文件夹并停止写入 JSON同步文件。删除 %1$s文件夹也来自这个文件夹吗?
+ 保持同步数据
+ 删除同步数据
+ 删除字体?
+ 您确定要删除 %1$d选定的字体?如果同步已打开,这会将它们从您的所有设备中删除。
+ 没有本地文件夹启用同步。
+ 本地文件夹同步已禁用。
+ 本地文件夹同步已禁用。同步数据文件夹已删除。
+ 本地文件夹同步已禁用,但无法删除同步数据文件夹。
+ 本地文件夹同步已启用。
+ 垂直(Web 视图)
+ 垂直(原生测试版)
+ 书本单词替换
+ 上一页 TTS块
+ 下一页 TTS块
+ 图片
+ 没有找到图像。
+ 下载图片
+ 已保存%1$s
+ 无法保存图像。
+ PDF页面跨度
+ 单页
+ 两页
+ 单独第一页
+ 在封面页之后开始跨页。
+ 亮度
+ 使用系统亮度
+ 遵循设备亮度设置。
+ 自定义亮度
+ 在阅读器屏幕打开时适用。
+ %1$d%%
+ 创建架子“%1$s”。
+ 创建智能货架“%1$s”。
+ 将架子重命名为“%1$s”。
+ 已删除架子“%1$s”。
+ 更新了“%1$s”。
+ 这些文件已经在库中。
+ %1$s - %2$s
+ 保存
+ 保存评论
+ 添加评论
+ 回复
+ 添加评论...
+ 评论
+ 编辑评论
+ 回复%1$s
+ 使用PDF文件名
+ 亮度
+ 当前书籍
+ 添加规则
+ 本书尚无替换规则。
+ 新的替代品
+ 编辑替换
+ 与
+ 空文本
+ 关于
+ 桌面阅读器
+ 桌面访问
+ 账户
+ 账户和积分
+ 账户概览
+ AI枢纽
+ 用于EPUB摘要和 PDF页面摘要。
+ Episteme操作系统
+ 作者文字
+ 缓存:%1$s
+ 缓存
+ 缓存摘要
+ 选择Gemini用于云朗读的语音。
+ 删除生成的桌面书籍和 EPUB分页缓存文件?它们将在下次打开书籍时重新创建。
+ 清除语音缓存
+ 关闭工具
+ 云同步
+ 云TTS需要Gemini
+ 云TTS需要登录积分
+ 云TTS准备好了
+ 云TTS设置
+ 云TTS不可用
+ 云TTS声音
+ 包含
+ 成本计算
+ 对您当前的职位进行回顾。
+ 打造智能货架
+ %1$d可用学分
+ %1$s学分
+ 为读者导入的字体
+ 删除字体
+ 删除%1$s?使用它的书籍将恢复为默认字体。
+ 删除\“%1$s\”?书籍留在您的图书馆中。
+ 删除摘要
+ 残疾人
+ 拖放要导入的文件
+ 删除要导入的受支持文件
+ 如需其他任何信息,请直接通过电子邮件联系我们。
+ 等于
+ 附加功能
+ 反馈意见
+ 领域
+ 文件夹路径
+ 从这里
+ 全盘扫描
+ 免费,%1$d左
+ 生成回顾
+ 生成摘要
+ 报告错误、请求功能或直接联系支持人员。
+ GitHub 赞助商
+ 通过 GitHub 赞助商支持开发。
+ Google未为此桌面版本配置登录。
+ 大于
+ 帮助
+ 错误报告、功能请求和支持
+ 隐藏
+ 导入文件
+ 问题
+ 打开问题跟踪器以查找错误和功能请求。
+ 小于
+ 图书馆和读者
+ 任意
+ 图书馆行动
+ 更多的
+ 还没有这本书的缓存摘要。
+ 导入 TTF、OTF 或 WOFF2 文件以在书籍中使用它们。
+ 未找到与“%1$s\”匹配的字体
+ 否 Google帐户已连接。
+ 没有为此部分缓存摘要。
+ 离线桌面阅读器
+ 开放读者
+ 开幕%1$s
+ 打开你的图书馆
+ 操作员
+ 页面
+ 密码保护PDF
+ 帕特隆
+ 支持 Patreon 上的项目。
+ 已暂停
+ %1$s需要输入密码才能打开。
+ 密码为必填项或不正确。
+ 该密码打不开%1$s。输入 PDF密码并重试。
+ 百分比
+ 计划
+ 准备音频
+ 偏好设置
+ 账户和积分
+ 账户和积分
+ 此帐户未解锁 Pro。
+ 专业版和积分只能从 Android 购买应用程序。 Desktop 检查相同的登录帐户,并将这些积分用于云 TTS、摘要、回顾和其他付费 AI特征。
+ 登录并在桌面上检查您的帐户状态。
+ 此帐户已解锁 Pro。
+ 进展
+ 项目
+ 读者
+ 阅读器关闭标签
+ 阅读器选项卡打开
+ 刷新
+ 发布以添加到您的库中。
+ 安全密钥存储在此操作系统上不可用。此处输入的密钥将用于此会话,但不会保留。
+ 设置中心
+ 匹配Android隐藏智能词典、摘要和回顾的切换。
+ 同步帐户、Pro 和积分
+ 已登录
+ 源代码
+ 浏览 GitHub 上的项目源。
+ 停止阅读以改变声音。
+ 支持
+ 支持Episteme
+ 贡献有助于读者不断提高 Android和桌面。
+ 支持方式Episteme发展
+ 同步文件夹
+ 同步元数据
+ 标签名称
+ 标记选定的书籍
+ 标题文字
+ 工具
+ 导入、同步和应用程序设置
+ 类型,例如PDF
+ 查看
+ 语音缓存
+ 正在准备嵌入的网络视图...
+ 准备捆绑的嵌入式 webview %1$d%%
+ 安装了嵌入式 webview。重新启动Episteme完成设置。
+ 嵌入式 webview 无法启动:%1$s
+ 工作…
+ 工作空间
+ 添加到货架
+ 首先创建一个书架,然后将选定的书籍添加到其中。
+ 创建主题
+ 现有:%1$s
+ 您点击了外部链接。
+ 编辑EPUB元数据
+ 少
+ …更多
+ 还没有自定义主题
+ 在应用程序中重命名
+ 标签,逗号分隔
+ 未知
+ 定义
+ 注释
+ 注释选项
+ 注释工具
+ 协助
+ 选择哪个PDF保存。
+ 清除跳转历史记录
+ 云TTS失败的。
+ 添加 Gemini键并选择 Gemini云TTS在AI钥匙和型号。
+ 云TTS未针对此桌面版本进行配置。
+ 使用 Google 登录使用云TTS。
+ 云TTS需要一个有积分的登录帐户。专业版和积分只能从 Android 购买应用程序。
+ 颜色
+ 评论选项
+ 定制
+ 这将从 PDF 中删除注释。
+ 删除注释?
+ 文档文本
+ 嵌入式PDF评论
+ 无法呈现页面。
+ 功能不可用
+ 完成
+ 钢笔
+ 隐藏搜索结果
+ 突出显示颜色 %1$d
+ 荧光笔调色板
+ 互动
+ 索引 %1$d/%2$d页面
+ 标记
+ %1$d比赛
+ %1$d到目前为止的比赛
+ 下一页
+ 下一个搜索结果
+ 还没有注释
+ 还没有书签
+ 暂无评论
+ 没有匹配项
+ 索引页面中尚无匹配项
+ 没有目录
+ 这里没有文字可供阅读。
+ 此页上没有可供阅读的文字。
+ 没有文字可以概括。
+ 打开评论
+ 积分用完。专业版和积分只能从 Android 购买应用程序。
+ 使用云TTS桌面上需要积分。专业版和积分只能从 Android 购买应用程序。
+ 使用此功能需要桌面上的积分。专业版和积分只能从 Android 购买应用程序。
+ 使用 recaps 需要桌面上的学分。专业版和积分只能从 Android 购买应用程序。
+ 使用摘要需要在桌面上使用积分。专业版和积分只能从 Android 购买应用程序。
+ 潘
+ PDF操作失败
+ PDF操作无法完成。
+ PDF评论
+ p。 %1$d
+ PDF页 %1$d
+ 页%1$d - %2$s
+ 页%1$s %2$d
+ 页数%1$s %2$d
+ PDF已保存
+ PDF工具
+ 铅笔
+ 准备选择
+ 准备%1$s
+ 上一页
+ 上一个搜索结果
+ 打印对话框已完成。
+ 需要专业版
+ 此功能需要专业版。专业版只能从 Android 购买app,则桌面登录后将使用升级后的帐户。
+ 多词智能词典需要Pro。专业版只能从 Android 购买app,则桌面登录后将使用升级后的帐户。
+ 读者AI功能被隐藏。
+ 桌面AI没有为此版本配置。
+ 适用于垂直阅读和两页跨页。
+ 圆形荧光笔
+ 保存到%1$s
+ 滚动
+ 搜索 PDF
+ 选择文本
+ 已选择%1$s
+ 显示搜索结果
+ 使用 Google 登录在桌面上使用此功能。
+ 使用 Google 登录在桌面上使用多词智能词典。
+ 使用 Google 登录在桌面上使用 recaps。
+ 使用 Google 登录在桌面上使用摘要。
+ 已停止
+ 文字注释
+ 文字注释
+ 文字样式
+ 厚度%1$s
+ 总有机碳
+ 输入搜索此 PDF
+ 无题
+ 查看帐户和积分
+ 语音缓存已清除
+ 变焦
+ 放大
+ 缩小
+ 选择
+ 继续阅读
+ 解雇
+ 向下
+ 向上
+ AI
+ 中心
+ 作者
+ 返回图书馆
+ 预订行动
+ 文件夹
+ 浏览
+ 类别
+ 章。 %1$d
+ 章转
+ 选择字体
+ 选择读卡器纹理
+ 清除文件类型
+ 清除页面注释
+ 来源清晰
+ 状态清晰
+ 清除标签
+ 关闭读者
+ 连续
+ 封面
+ 定制颜色
+ 自定义主题预览
+ 降低%1$s
+ 定义页面
+ 这将删除突出显示及其注释。
+ 进入全屏
+ 退出全屏
+ 外部查找
+ 书籍
+ 漫画
+ 文件
+ 其他
+ 文本和网络
+ 填充
+ 固定布局外观
+ 文件夹为空
+ 此处不提供受支持的文件或子文件夹。
+ %1$s,%2$s
+ %1$s - %2$s
+ 隐藏过滤器
+ 隐藏阅读器工具
+ 点击一个插槽,然后选择一种颜色。
+ 继续阅读和最近的书籍
+ 进口书籍
+ 导入文件夹
+ 导入字体
+ %1$s %2$s
+ 增加%1$s
+ 跳转历史记录
+ 布局和间距
+ 将文件导入应用程序存储或添加文件夹以就地读取文件。
+ 浏览您的收藏
+ AI键
+ 智能%1$d
+ 未读%1$d
+ 进行中%1$d
+ 完整%1$d
+ 列表
+ 导航
+ 没有书打开
+ 添加一个文件夹以从该文件夹中读取文件。
+ 还没有文件夹
+ 没有导航项目
+ 没有页面内容
+ 没有找到设置
+ 手动货架和系列收藏品都会出现在这里。
+ 还没有货架
+ 创建智能书架,按规则收集书籍。
+ 还没有智能货架
+ 添加到书籍的标签将显示在此处。
+ 还没有标签
+ 未导入受支持的文件。
+ 目录
+ 删除“%1$s”?如果稍后凭据发生更改,此目录中的流式图书可能会停止打开。
+ 没有目录
+ 添加OPDS目录以浏览远程书籍。
+ 浏览目录、流和下载
+ 打开书本
+ 打开文件夹
+ 打开PDF
+ 页面和文字颜色
+ 页面信息
+ 页宽
+ 这些默认值适用于平台支持共享 PDF 的地方外貌。每本书PDF覆盖保留在PDF读者。
+ PDF文件操作
+ PDF荧光笔
+ 与阅读器一起保存突出显示透明度。
+ 销
+ 读者管理 PDF工具
+ 自动滚动、OCR、注释默认值和 PDF-仅限工具可见性在活动 PDF 内进行管理读者。
+ %1$s %2$s %3$d (%4$d%%)
+ 阅读器工具栏默认值由该平台上的阅读器管理。
+ 读者工具
+ 保存图像
+ 搜索:%1$s
+ 在阅读器中搜索
+ 搜索设置
+ 选择
+ 选择结束手柄
+ 选择开始手柄
+ 添加书架、标签或文件夹元数据来组织您的图书馆。
+ 集合、系列、标签和文件夹
+ 显示读者工具
+ 文件夹
+ 智能
+ 固体
+ 速度
+ 开始自动滚动
+ 停止自动滚动
+ 停止大声朗读
+ 质感强度
+ 输入搜索这本书
+ 版式
+ 撤消注释
+ 取消固定
+ 使用深色主题
+ 使用浅色主题
+ 单声道
+ 桑斯
+ 衬线
+ 搜索书籍、作者或标签
+ 没有工具
+ 可见
+ 仅替换所说内容
+ 阅读器文本、突出显示和位置保持不变。
+ %1$s -> %2$s
diff --git a/desktopApp/src/desktopMain/kotlin/com/aryan/reader/desktop/DesktopAiByokStore.kt b/desktopApp/src/desktopMain/kotlin/com/aryan/reader/desktop/DesktopAiByokStore.kt
index 1b33f50..09d0be7 100644
--- a/desktopApp/src/desktopMain/kotlin/com/aryan/reader/desktop/DesktopAiByokStore.kt
+++ b/desktopApp/src/desktopMain/kotlin/com/aryan/reader/desktop/DesktopAiByokStore.kt
@@ -3,6 +3,7 @@ package com.aryan.reader.desktop
import com.aryan.reader.shared.DEFAULT_CLOUD_TTS_SPEAKER_ID
import com.aryan.reader.shared.GEMINI_CLOUD_TTS_MODEL_ID
import com.aryan.reader.shared.ReaderAiByokSettings
+import com.sun.jna.Library
import com.sun.jna.Memory
import com.sun.jna.Native
import com.sun.jna.Pointer
@@ -18,6 +19,15 @@ import java.util.concurrent.TimeUnit
private const val WINDOWS_CRED_TYPE_GENERIC = 1
private const val WINDOWS_CRED_PERSIST_LOCAL_MACHINE = 2
private const val WINDOWS_ERROR_NOT_FOUND = 1168
+private const val LINUX_SECRET_SCHEMA_DONT_MATCH_NAME = 2
+private const val LINUX_SECRET_SCHEMA_ATTRIBUTE_STRING = 0
+private const val LINUX_SECRET_SCHEMA_NAME = "com.aryan.reader.Secret"
+private const val LINUX_SECRET_COLLECTION_DEFAULT = "default"
+private const val LINUX_SECRET_SERVICE_APPLICATION_ATTRIBUTE = "application"
+private const val LINUX_SECRET_SERVICE_APPLICATION_VALUE = "Episteme.Reader"
+private const val LINUX_SECRET_SERVICE_KEY_ATTRIBUTE = "key"
+private const val LINUX_LIBSECRET_PREFIX = "linux-libsecret:"
+private const val LINUX_SECRET_TOOL_PREFIX = "secret-tool:"
internal class DesktopAiByokStore(
private val settingsFile: File = defaultSettingsFile(),
@@ -174,7 +184,7 @@ internal interface DesktopSecretCodec {
val osName = System.getProperty("os.name").orEmpty()
val codec = when {
osName.startsWith("Windows", ignoreCase = true) -> WindowsSecretCodec
- osName.contains("Linux", ignoreCase = true) -> LinuxSecretToolCodec()
+ osName.contains("Linux", ignoreCase = true) -> LinuxSecretServiceCodec()
else -> UnavailableDesktopSecretCodec
}
logDesktopTts("settings_platform os=\"${osName.desktopTtsPreview()}\" codec=${codec.name}")
@@ -241,6 +251,329 @@ private object DesktopProcessSecretCommandRunner : DesktopSecretCommandRunner {
}
}
+internal class LinuxSecretServiceCodec(
+ private val libsecretCodec: DesktopSecretCodec = LinuxLibsecretCodec(),
+ private val secretToolCodec: DesktopSecretCodec = LinuxSecretToolCodec()
+) : DesktopSecretCodec {
+ private val codecs: List = listOf(libsecretCodec, secretToolCodec)
+
+ private val selectedCodec: DesktopSecretCodec? by lazy {
+ codecs.firstOrNull { codec ->
+ runCatching { codec.isAvailable }
+ .onFailure { error ->
+ logDesktopTts("settings_linux_secret_service_probe_failed codec=${codec.name} error=\"${error.desktopTtsSummary()}\"")
+ }
+ .getOrDefault(false)
+ }
+ }
+
+ override val name: String = "linux-secret-service"
+
+ override val isAvailable: Boolean
+ get() = selectedCodec != null
+
+ override fun protect(value: String): String {
+ return protect("secret", value)
+ }
+
+ override fun unprotect(value: String): String {
+ return unprotect("secret", value)
+ }
+
+ override fun protect(keyName: String, value: String): String {
+ val codec = selectedCodec ?: throw IllegalStateException(
+ "Linux Secret Service is unavailable. Install gnome-keyring or another Secret Service provider."
+ )
+ return codec.protect(keyName, value)
+ }
+
+ override fun unprotect(keyName: String, value: String): String {
+ val orderedCodecs = when {
+ value.startsWith(LINUX_LIBSECRET_PREFIX) -> listOf(libsecretCodec, secretToolCodec)
+ value.startsWith(LINUX_SECRET_TOOL_PREFIX) -> listOf(libsecretCodec, secretToolCodec)
+ else -> codecs
+ }
+ for (codec in orderedCodecs) {
+ val secret = runCatching {
+ if (!codec.isAvailable) "" else codec.unprotect(keyName, value)
+ }.onFailure { error ->
+ logDesktopTts(
+ "settings_linux_secret_service_read_failed codec=${codec.name} key=$keyName " +
+ "error=\"${error.desktopTtsSummary()}\""
+ )
+ }.getOrDefault("")
+ if (secret.isNotBlank()) return secret
+ }
+ return ""
+ }
+
+ override fun delete(keyName: String) {
+ codecs.forEach { codec ->
+ runCatching { codec.delete(keyName) }
+ .onFailure { error ->
+ logDesktopTts(
+ "settings_linux_secret_service_delete_failed codec=${codec.name} key=$keyName " +
+ "error=\"${error.desktopTtsSummary()}\""
+ )
+ }
+ }
+ }
+}
+
+internal interface LinuxSecretServiceClient {
+ val isAvailable: Boolean
+ fun store(key: String, label: String, password: String)
+ fun lookup(key: String): String?
+ fun clear(key: String)
+}
+
+internal class LinuxLibsecretCodec(
+ private val client: LinuxSecretServiceClient = JnaLinuxSecretServiceClient
+) : DesktopSecretCodec {
+ override val name: String = "linux-libsecret"
+
+ override val isAvailable: Boolean by lazy {
+ val available = if (!client.isAvailable) {
+ false
+ } else {
+ val probeKey = linuxSecretKey("probe")
+ val probeSecret = "episteme-linux-libsecret-probe"
+ runCatching {
+ client.store(probeKey, "Episteme secure storage probe", probeSecret)
+ client.lookup(probeKey) == probeSecret
+ }.onFailure { error ->
+ logDesktopTts("settings_linux_libsecret_unavailable error=\"${error.desktopTtsSummary()}\"")
+ }.also {
+ runCatching { client.clear(probeKey) }
+ }.getOrDefault(false)
+ }
+ logDesktopTts("settings_linux_libsecret_available available=$available")
+ available
+ }
+
+ override fun protect(value: String): String {
+ return protect("secret", value)
+ }
+
+ override fun unprotect(value: String): String {
+ return unprotect("secret", value)
+ }
+
+ override fun protect(keyName: String, value: String): String {
+ if (!isAvailable) {
+ throw IllegalStateException(
+ "Linux Secret Service is unavailable. Install gnome-keyring or another Secret Service provider."
+ )
+ }
+ val key = linuxSecretKey(keyName)
+ logDesktopTts("settings_linux_libsecret_write_start key=$keyName valueChars=${value.length}")
+ client.store(key, "Episteme $keyName", value)
+ logDesktopTts("settings_linux_libsecret_write_result key=$keyName")
+ return LINUX_LIBSECRET_PREFIX + key
+ }
+
+ override fun unprotect(keyName: String, value: String): String {
+ if (!isAvailable) return ""
+ val key = linuxSecretReferenceKey(keyName, value)
+ logDesktopTts("settings_linux_libsecret_read_start key=$keyName")
+ val secret = client.lookup(key).orEmpty()
+ logDesktopTts("settings_linux_libsecret_read_result key=$keyName chars=${secret.length}")
+ return secret
+ }
+
+ override fun delete(keyName: String) {
+ if (!client.isAvailable) return
+ val key = linuxSecretKey(keyName)
+ runCatching { client.clear(key) }
+ .onFailure { error ->
+ logDesktopTts("settings_linux_libsecret_delete_failed key=$keyName error=\"${error.desktopTtsSummary()}\"")
+ }
+ }
+}
+
+private object JnaLinuxSecretServiceClient : LinuxSecretServiceClient {
+ override val isAvailable: Boolean by lazy {
+ runCatching {
+ LinuxLibsecretNative.INSTANCE
+ LinuxGlibNative.INSTANCE
+ true
+ }.onFailure { error ->
+ logDesktopTts("settings_linux_libsecret_load_failed error=\"${error.desktopTtsSummary()}\"")
+ }.getOrDefault(false)
+ }
+
+ private val schema: Pointer by lazy {
+ LinuxLibsecretNative.INSTANCE.secret_schema_new(
+ LINUX_SECRET_SCHEMA_NAME,
+ LINUX_SECRET_SCHEMA_DONT_MATCH_NAME,
+ LINUX_SECRET_SERVICE_APPLICATION_ATTRIBUTE,
+ LINUX_SECRET_SCHEMA_ATTRIBUTE_STRING,
+ LINUX_SECRET_SERVICE_KEY_ATTRIBUTE,
+ LINUX_SECRET_SCHEMA_ATTRIBUTE_STRING,
+ null
+ ) ?: throw IllegalStateException("Linux Secret Service schema creation failed.")
+ }
+
+ override fun store(key: String, label: String, password: String) {
+ val error = PointerByReference()
+ val stored = LinuxLibsecretNative.INSTANCE.secret_password_store_sync(
+ schema,
+ LINUX_SECRET_COLLECTION_DEFAULT,
+ label,
+ password,
+ null,
+ error,
+ LINUX_SECRET_SERVICE_APPLICATION_ATTRIBUTE,
+ LINUX_SECRET_SERVICE_APPLICATION_VALUE,
+ LINUX_SECRET_SERVICE_KEY_ATTRIBUTE,
+ key,
+ null
+ )
+ takeLibsecretError(error)?.let { message ->
+ throw IllegalStateException("Linux Secret Service write failed: $message")
+ }
+ if (!stored) {
+ throw IllegalStateException("Linux Secret Service write failed: libsecret returned false.")
+ }
+ }
+
+ override fun lookup(key: String): String? {
+ val error = PointerByReference()
+ val passwordPointer = LinuxLibsecretNative.INSTANCE.secret_password_lookup_sync(
+ schema,
+ null,
+ error,
+ LINUX_SECRET_SERVICE_APPLICATION_ATTRIBUTE,
+ LINUX_SECRET_SERVICE_APPLICATION_VALUE,
+ LINUX_SECRET_SERVICE_KEY_ATTRIBUTE,
+ key,
+ null
+ )
+ val errorMessage = takeLibsecretError(error)
+ if (errorMessage != null) {
+ passwordPointer?.let { pointer -> LinuxLibsecretNative.INSTANCE.secret_password_free(pointer) }
+ throw IllegalStateException("Linux Secret Service read failed: $errorMessage")
+ }
+ return passwordPointer?.let { pointer ->
+ try {
+ pointer.getString(0, Charsets.UTF_8.name())
+ } finally {
+ LinuxLibsecretNative.INSTANCE.secret_password_free(pointer)
+ }
+ }
+ }
+
+ override fun clear(key: String) {
+ val error = PointerByReference()
+ LinuxLibsecretNative.INSTANCE.secret_password_clear_sync(
+ schema,
+ null,
+ error,
+ LINUX_SECRET_SERVICE_APPLICATION_ATTRIBUTE,
+ LINUX_SECRET_SERVICE_APPLICATION_VALUE,
+ LINUX_SECRET_SERVICE_KEY_ATTRIBUTE,
+ key,
+ null
+ )
+ takeLibsecretError(error)?.let { message ->
+ throw IllegalStateException("Linux Secret Service delete failed: $message")
+ }
+ }
+
+ private fun takeLibsecretError(error: PointerByReference): String? {
+ val errorPointer = error.value ?: return null
+ return try {
+ LinuxGError(errorPointer).message
+ ?.getString(0, Charsets.UTF_8.name())
+ ?.ifBlank { null }
+ ?: "unknown libsecret error"
+ } finally {
+ LinuxGlibNative.INSTANCE.g_error_free(errorPointer)
+ }
+ }
+}
+
+private interface LinuxLibsecretNative : Library {
+ fun secret_schema_new(name: String, flags: Int, vararg attributes: Any?): Pointer?
+
+ fun secret_password_store_sync(
+ schema: Pointer,
+ collection: String,
+ label: String,
+ password: String,
+ cancellable: Pointer?,
+ error: PointerByReference,
+ vararg attributes: Any?
+ ): Boolean
+
+ fun secret_password_lookup_sync(
+ schema: Pointer,
+ cancellable: Pointer?,
+ error: PointerByReference,
+ vararg attributes: Any?
+ ): Pointer?
+
+ fun secret_password_clear_sync(
+ schema: Pointer,
+ cancellable: Pointer?,
+ error: PointerByReference,
+ vararg attributes: Any?
+ ): Boolean
+
+ fun secret_password_free(password: Pointer?)
+
+ companion object {
+ val INSTANCE: LinuxLibsecretNative by lazy {
+ loadLinuxNativeLibrary(
+ LinuxLibsecretNative::class.java,
+ "secret-1",
+ "libsecret-1.so.0"
+ )
+ }
+ }
+}
+
+private interface LinuxGlibNative : Library {
+ fun g_error_free(error: Pointer?)
+
+ companion object {
+ val INSTANCE: LinuxGlibNative by lazy {
+ loadLinuxNativeLibrary(
+ LinuxGlibNative::class.java,
+ "glib-2.0",
+ "libglib-2.0.so.0"
+ )
+ }
+ }
+}
+
+@Structure.FieldOrder("domain", "code", "message")
+internal class LinuxGError(pointer: Pointer) : Structure(pointer) {
+ @JvmField
+ var domain: Int = 0
+
+ @JvmField
+ var code: Int = 0
+
+ @JvmField
+ var message: Pointer? = null
+
+ init {
+ read()
+ }
+}
+
+private fun loadLinuxNativeLibrary(type: Class, vararg names: String): T {
+ var lastError: Throwable? = null
+ for (name in names) {
+ val loaded = runCatching { Native.load(name, type) as T }
+ .onFailure { error -> lastError = error }
+ .getOrNull()
+ if (loaded != null) return loaded
+ }
+ throw IllegalStateException("Could not load Linux native library ${names.joinToString(" or ")}.", lastError)
+}
+
internal class LinuxSecretToolCodec(
private val commandRunner: DesktopSecretCommandRunner = DesktopProcessSecretCommandRunner
) : DesktopSecretCodec {
@@ -277,9 +610,9 @@ internal class LinuxSecretToolCodec(
"store",
"--label",
"Episteme $keyName",
- SecretToolApplicationAttribute,
- SecretToolApplicationValue,
- SecretToolKeyAttribute,
+ LINUX_SECRET_SERVICE_APPLICATION_ATTRIBUTE,
+ LINUX_SECRET_SERVICE_APPLICATION_VALUE,
+ LINUX_SECRET_SERVICE_KEY_ATTRIBUTE,
key
),
input = value,
@@ -289,20 +622,20 @@ internal class LinuxSecretToolCodec(
if (!result.isSuccess) {
throw IllegalStateException("Linux Secret Service write failed: ${result.errorSummary}")
}
- return Prefix + key
+ return LINUX_SECRET_TOOL_PREFIX + key
}
override fun unprotect(keyName: String, value: String): String {
if (!isAvailable) return ""
- val key = value.removePrefix(Prefix).takeIf { value.startsWith(Prefix) } ?: linuxSecretKey(keyName)
+ val key = linuxSecretReferenceKey(keyName, value)
logDesktopTts("settings_linux_secret_tool_read_start key=$keyName")
val result = commandRunner.run(
command = listOf(
SecretToolCommand,
"lookup",
- SecretToolApplicationAttribute,
- SecretToolApplicationValue,
- SecretToolKeyAttribute,
+ LINUX_SECRET_SERVICE_APPLICATION_ATTRIBUTE,
+ LINUX_SECRET_SERVICE_APPLICATION_VALUE,
+ LINUX_SECRET_SERVICE_KEY_ATTRIBUTE,
key
),
timeoutMillis = 8_000L
@@ -321,9 +654,9 @@ internal class LinuxSecretToolCodec(
command = listOf(
SecretToolCommand,
"clear",
- SecretToolApplicationAttribute,
- SecretToolApplicationValue,
- SecretToolKeyAttribute,
+ LINUX_SECRET_SERVICE_APPLICATION_ATTRIBUTE,
+ LINUX_SECRET_SERVICE_APPLICATION_VALUE,
+ LINUX_SECRET_SERVICE_KEY_ATTRIBUTE,
key
),
timeoutMillis = 8_000L
@@ -333,19 +666,23 @@ internal class LinuxSecretToolCodec(
}
}
- private fun linuxSecretKey(keyName: String): String {
- return "Episteme.Reader.$keyName"
- }
-
private companion object {
- const val Prefix = "secret-tool:"
const val SecretToolCommand = "secret-tool"
- const val SecretToolApplicationAttribute = "application"
- const val SecretToolApplicationValue = "Episteme.Reader"
- const val SecretToolKeyAttribute = "key"
}
}
+private fun linuxSecretKey(keyName: String): String {
+ return "Episteme.Reader.$keyName"
+}
+
+private fun linuxSecretReferenceKey(keyName: String, reference: String): String {
+ return when {
+ reference.startsWith(LINUX_LIBSECRET_PREFIX) -> reference.removePrefix(LINUX_LIBSECRET_PREFIX)
+ reference.startsWith(LINUX_SECRET_TOOL_PREFIX) -> reference.removePrefix(LINUX_SECRET_TOOL_PREFIX)
+ else -> ""
+ }.ifBlank { linuxSecretKey(keyName) }
+}
+
private object WindowsSecretCodec : DesktopSecretCodec {
override val name: String = "windows"
diff --git a/desktopApp/src/desktopMain/kotlin/com/aryan/reader/desktop/DesktopAppHost.kt b/desktopApp/src/desktopMain/kotlin/com/aryan/reader/desktop/DesktopAppHost.kt
index babe5ae..735aee4 100644
--- a/desktopApp/src/desktopMain/kotlin/com/aryan/reader/desktop/DesktopAppHost.kt
+++ b/desktopApp/src/desktopMain/kotlin/com/aryan/reader/desktop/DesktopAppHost.kt
@@ -38,6 +38,9 @@ import androidx.compose.ui.window.application
import androidx.compose.ui.window.rememberWindowState
import com.aryan.reader.shared.AppContrastOption
import com.aryan.reader.shared.AppThemeMode
+import com.aryan.reader.shared.reader.ReaderReadingMode
+import com.aryan.reader.shared.reader.ReaderSettings
+import com.aryan.reader.shared.reader.SharedJvmBookLoadSemanticMode
import com.aryan.reader.shared.ui.SharedAppTheme
import com.aryan.reader.shared.ui.readerString
import kotlinx.coroutines.Dispatchers
@@ -629,6 +632,26 @@ internal fun desktopEpubWebViewUsesWebView2(
return desktopEpubWebViewBackend(platform) == DesktopEpubWebViewBackend.WINDOWS_WEBVIEW2
}
+internal fun desktopShouldUseNativeVerticalEpubReader(
+ platform: DesktopPlatform = currentDesktopPlatform()
+): Boolean {
+ return platform.os == DesktopOperatingSystem.LINUX
+}
+
+internal fun desktopEpubBookLoadSemanticMode(
+ settings: ReaderSettings,
+ platform: DesktopPlatform = currentDesktopPlatform()
+): SharedJvmBookLoadSemanticMode {
+ return if (
+ settings.readingMode == ReaderReadingMode.VERTICAL &&
+ !desktopShouldUseNativeVerticalEpubReader(platform)
+ ) {
+ SharedJvmBookLoadSemanticMode.SKIP
+ } else {
+ SharedJvmBookLoadSemanticMode.FULL
+ }
+}
+
internal fun desktopEpubWebViewCanRender(
state: DesktopWebViewRuntimeState,
platform: DesktopPlatform = currentDesktopPlatform()
diff --git a/desktopApp/src/desktopMain/kotlin/com/aryan/reader/desktop/DesktopFirebaseAuthRepository.kt b/desktopApp/src/desktopMain/kotlin/com/aryan/reader/desktop/DesktopFirebaseAuthRepository.kt
index 21126ed..1c82cb5 100644
--- a/desktopApp/src/desktopMain/kotlin/com/aryan/reader/desktop/DesktopFirebaseAuthRepository.kt
+++ b/desktopApp/src/desktopMain/kotlin/com/aryan/reader/desktop/DesktopFirebaseAuthRepository.kt
@@ -370,17 +370,34 @@ internal class DesktopAuthStore(
}
fun save(session: DesktopAuthSession) {
- val protectedRefreshToken = protectRequired(RefreshTokenKey, session.refreshToken)
- val protectedGoogleRefreshToken = session.googleRefreshToken
- .takeIf { it.isNotBlank() }
- ?.let { protectRequired(GoogleRefreshTokenKey, it) }
+ if (session.refreshToken.isBlank()) {
+ throw IllegalArgumentException("Cannot save a desktop account without a refresh token.")
+ }
+ val protectedTokens = runCatching {
+ ProtectedDesktopAuthTokens(
+ refreshToken = protectRequired(RefreshTokenKey, session.refreshToken),
+ googleRefreshToken = session.googleRefreshToken
+ .takeIf { it.isNotBlank() }
+ ?.let { protectRequired(GoogleRefreshTokenKey, it) }
+ )
+ }.getOrElse { error ->
+ if (secretCodec.isAvailable) {
+ throw error
+ }
+ logDesktopCloudSync {
+ "desktop.auth.persist_skipped reason=secure_storage_unavailable codec=${secretCodec.name} " +
+ "error=\"${error.desktopTtsSummary()}\""
+ }
+ clear()
+ return
+ }
val properties = Properties().apply {
setProperty("uid", session.user.uid)
setProperty("displayName", session.user.displayName.orEmpty())
setProperty("photoUrl", session.user.photoUrl.orEmpty())
setProperty("email", session.user.email.orEmpty())
- setProperty(RefreshTokenKey, protectedRefreshToken)
- protectedGoogleRefreshToken?.let { setProperty(GoogleRefreshTokenKey, it) }
+ setProperty(RefreshTokenKey, protectedTokens.refreshToken)
+ protectedTokens.googleRefreshToken?.let { setProperty(GoogleRefreshTokenKey, it) }
}
settingsFile.storePropertiesAtomically(properties, "Episteme desktop account")
}
@@ -396,10 +413,12 @@ internal class DesktopAuthStore(
const val GoogleRefreshTokenKey = "googleRefreshTokenProtected"
}
+ private data class ProtectedDesktopAuthTokens(
+ val refreshToken: String,
+ val googleRefreshToken: String?
+ )
+
private fun protectRequired(keyName: String, value: String): String {
- if (value.isBlank()) {
- throw IllegalArgumentException("Cannot save a desktop account without a refresh token.")
- }
val protectedValue = secretCodec.protect(keyName, value)
if (protectedValue.isBlank()) {
throw IllegalStateException("Desktop secure key storage returned an empty value for $keyName.")
diff --git a/desktopApp/src/desktopMain/kotlin/com/aryan/reader/desktop/DesktopPdfChromeUi.kt b/desktopApp/src/desktopMain/kotlin/com/aryan/reader/desktop/DesktopPdfChromeUi.kt
index 909c59e..ee21b77 100644
--- a/desktopApp/src/desktopMain/kotlin/com/aryan/reader/desktop/DesktopPdfChromeUi.kt
+++ b/desktopApp/src/desktopMain/kotlin/com/aryan/reader/desktop/DesktopPdfChromeUi.kt
@@ -84,72 +84,74 @@ internal fun DesktopPdfFullscreenBottomChrome(
val chromeContent = MaterialTheme.colorScheme.onSurface
val sliderActive = MaterialTheme.colorScheme.primary
val sliderInactive = chromeContent.copy(alpha = if (chromeBackground.luminance() > 0.5f) 0.44f else 0.52f)
- Surface(
- modifier = Modifier
- .fillMaxWidth(),
- shape = RoundedCornerShape(0.dp),
- color = chromeBackground,
- contentColor = chromeContent,
- tonalElevation = 0.dp,
- shadowElevation = 1.dp,
- border = BorderStroke(1.dp, MaterialTheme.colorScheme.outlineVariant)
- ) {
- Column(modifier = Modifier.fillMaxWidth()) {
- extraContent()
- val hasJumpTargets = jumpBackPage != null || jumpForwardPage != null
- DesktopPdfJumpHistoryControls(
- visible = showJumpHistory,
- backPage = jumpBackPage,
- forwardPage = jumpForwardPage,
- onBack = onJumpBack,
- onForward = onJumpForward,
- onClear = onClearJumpHistory
- )
- if (showJumpHistory && hasJumpTargets) {
- HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant)
- }
- Row(
- modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp, vertical = 4.dp),
- horizontalArrangement = Arrangement.spacedBy(8.dp),
- verticalAlignment = Alignment.CenterVertically
- ) {
- ReaderTooltipIconButton(
- tooltip = readerString("desktop_previous_page", "Previous page"),
- onClick = onPrevious,
- enabled = canGoPrevious
- ) {
- Icon(
- Icons.AutoMirrored.Filled.NavigateBefore,
- contentDescription = readerString("desktop_previous_page", "Previous page"),
- tint = chromeContent.copy(alpha = if (canGoPrevious) 0.78f else 0.32f)
- )
+ Column(modifier = Modifier.fillMaxWidth()) {
+ extraContent()
+ Surface(
+ modifier = Modifier
+ .fillMaxWidth(),
+ shape = RoundedCornerShape(0.dp),
+ color = chromeBackground,
+ contentColor = chromeContent,
+ tonalElevation = 0.dp,
+ shadowElevation = 1.dp,
+ border = BorderStroke(1.dp, MaterialTheme.colorScheme.outlineVariant)
+ ) {
+ Column(modifier = Modifier.fillMaxWidth()) {
+ val hasJumpTargets = jumpBackPage != null || jumpForwardPage != null
+ DesktopPdfJumpHistoryControls(
+ visible = showJumpHistory,
+ backPage = jumpBackPage,
+ forwardPage = jumpForwardPage,
+ onBack = onJumpBack,
+ onForward = onJumpForward,
+ onClear = onClearJumpHistory
+ )
+ if (showJumpHistory && hasJumpTargets) {
+ HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant)
}
- Text(
- pageLabel,
- style = MaterialTheme.typography.labelSmall,
- color = chromeContent.copy(alpha = 0.72f)
- )
- ReaderMinimalSlider(
- value = pageIndex.toFloat(),
- onValueChange = onPageScrub,
- onValueChangeFinished = onPageScrubFinished,
- valueRange = 0f..(pageCount - 1).coerceAtLeast(0).toFloat(),
- enabled = pageCount > 1,
- activeColor = sliderActive,
- inactiveColor = sliderInactive,
- thumbColor = sliderActive,
- modifier = Modifier.weight(1f)
- )
- ReaderTooltipIconButton(
- tooltip = readerString("desktop_next_page", "Next page"),
- onClick = onNext,
- enabled = canGoNext
+ Row(
+ modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp, vertical = 4.dp),
+ horizontalArrangement = Arrangement.spacedBy(8.dp),
+ verticalAlignment = Alignment.CenterVertically
) {
- Icon(
- Icons.AutoMirrored.Filled.NavigateNext,
- contentDescription = readerString("desktop_next_page", "Next page"),
- tint = chromeContent.copy(alpha = if (canGoNext) 0.78f else 0.32f)
+ ReaderTooltipIconButton(
+ tooltip = readerString("desktop_previous_page", "Previous page"),
+ onClick = onPrevious,
+ enabled = canGoPrevious
+ ) {
+ Icon(
+ Icons.AutoMirrored.Filled.NavigateBefore,
+ contentDescription = readerString("desktop_previous_page", "Previous page"),
+ tint = chromeContent.copy(alpha = if (canGoPrevious) 0.78f else 0.32f)
+ )
+ }
+ Text(
+ pageLabel,
+ style = MaterialTheme.typography.labelSmall,
+ color = chromeContent.copy(alpha = 0.72f)
)
+ ReaderMinimalSlider(
+ value = pageIndex.toFloat(),
+ onValueChange = onPageScrub,
+ onValueChangeFinished = onPageScrubFinished,
+ valueRange = 0f..(pageCount - 1).coerceAtLeast(0).toFloat(),
+ enabled = pageCount > 1,
+ activeColor = sliderActive,
+ inactiveColor = sliderInactive,
+ thumbColor = sliderActive,
+ modifier = Modifier.weight(1f)
+ )
+ ReaderTooltipIconButton(
+ tooltip = readerString("desktop_next_page", "Next page"),
+ onClick = onNext,
+ enabled = canGoNext
+ ) {
+ Icon(
+ Icons.AutoMirrored.Filled.NavigateNext,
+ contentDescription = readerString("desktop_next_page", "Next page"),
+ tint = chromeContent.copy(alpha = if (canGoNext) 0.78f else 0.32f)
+ )
+ }
}
}
}
@@ -180,78 +182,80 @@ internal fun DesktopPdfBottomChrome(
val chromeContent = MaterialTheme.colorScheme.onSurface
val sliderActive = MaterialTheme.colorScheme.primary
val sliderInactive = chromeContent.copy(alpha = if (chromeBackground.luminance() > 0.5f) 0.44f else 0.52f)
- Surface(
- modifier = Modifier.fillMaxWidth(),
- shape = RoundedCornerShape(0.dp),
- color = chromeBackground,
- contentColor = chromeContent,
- tonalElevation = 0.dp,
- shadowElevation = 1.dp,
- border = BorderStroke(1.dp, MaterialTheme.colorScheme.outlineVariant)
- ) {
- Column(modifier = Modifier.fillMaxWidth()) {
- extraContent()
- DesktopPdfJumpHistoryControls(
- visible = showJumpHistory,
- backPage = jumpBackPage,
- forwardPage = jumpForwardPage,
- onBack = onJumpBack,
- onForward = onJumpForward,
- onClear = onClearJumpHistory
- )
- if (showJumpHistory && (jumpBackPage != null || jumpForwardPage != null)) {
- HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant)
- }
- Row(
- modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp, vertical = 4.dp),
- horizontalArrangement = Arrangement.spacedBy(8.dp),
- verticalAlignment = Alignment.CenterVertically
- ) {
- ReaderTooltipIconButton(
- tooltip = readerString("desktop_previous_page", "Previous page"),
- onClick = onPrevious,
- enabled = canGoPrevious
- ) {
- Icon(
- Icons.AutoMirrored.Filled.NavigateBefore,
- contentDescription = readerString("desktop_previous_page", "Previous page"),
- tint = chromeContent.copy(alpha = if (canGoPrevious) 0.78f else 0.32f)
- )
- }
- Text(
- pageLabel,
- style = MaterialTheme.typography.labelSmall,
- color = chromeContent.copy(alpha = 0.72f)
+ Column(modifier = Modifier.fillMaxWidth()) {
+ extraContent()
+ Surface(
+ modifier = Modifier.fillMaxWidth(),
+ shape = RoundedCornerShape(0.dp),
+ color = chromeBackground,
+ contentColor = chromeContent,
+ tonalElevation = 0.dp,
+ shadowElevation = 1.dp,
+ border = BorderStroke(1.dp, MaterialTheme.colorScheme.outlineVariant)
+ ) {
+ Column(modifier = Modifier.fillMaxWidth()) {
+ DesktopPdfJumpHistoryControls(
+ visible = showJumpHistory,
+ backPage = jumpBackPage,
+ forwardPage = jumpForwardPage,
+ onBack = onJumpBack,
+ onForward = onJumpForward,
+ onClear = onClearJumpHistory
)
- if (pageCount > 1) {
- ReaderMinimalSlider(
- value = pageIndex.toFloat(),
- onValueChange = onPageScrub,
- onValueChangeFinished = onPageScrubFinished,
- valueRange = 0f..(pageCount - 1).toFloat(),
- activeColor = sliderActive,
- inactiveColor = sliderInactive,
- thumbColor = sliderActive,
- modifier = Modifier.weight(1f)
- )
- } else {
- Spacer(Modifier.weight(1f))
+ if (showJumpHistory && (jumpBackPage != null || jumpForwardPage != null)) {
+ HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant)
}
- Text(
- "${progressPercent.toInt()}%",
- style = MaterialTheme.typography.labelSmall,
- color = chromeContent.copy(alpha = 0.72f)
- )
- ReaderTooltipIconButton(
- tooltip = readerString("desktop_next_page", "Next page"),
- onClick = onNext,
- enabled = canGoNext
+ Row(
+ modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp, vertical = 4.dp),
+ horizontalArrangement = Arrangement.spacedBy(8.dp),
+ verticalAlignment = Alignment.CenterVertically
) {
- Icon(
- Icons.AutoMirrored.Filled.NavigateNext,
- contentDescription = readerString("desktop_next_page", "Next page"),
- tint = chromeContent.copy(alpha = if (canGoNext) 0.78f else 0.32f)
+ ReaderTooltipIconButton(
+ tooltip = readerString("desktop_previous_page", "Previous page"),
+ onClick = onPrevious,
+ enabled = canGoPrevious
+ ) {
+ Icon(
+ Icons.AutoMirrored.Filled.NavigateBefore,
+ contentDescription = readerString("desktop_previous_page", "Previous page"),
+ tint = chromeContent.copy(alpha = if (canGoPrevious) 0.78f else 0.32f)
+ )
+ }
+ Text(
+ pageLabel,
+ style = MaterialTheme.typography.labelSmall,
+ color = chromeContent.copy(alpha = 0.72f)
)
+ if (pageCount > 1) {
+ ReaderMinimalSlider(
+ value = pageIndex.toFloat(),
+ onValueChange = onPageScrub,
+ onValueChangeFinished = onPageScrubFinished,
+ valueRange = 0f..(pageCount - 1).toFloat(),
+ activeColor = sliderActive,
+ inactiveColor = sliderInactive,
+ thumbColor = sliderActive,
+ modifier = Modifier.weight(1f)
+ )
+ } else {
+ Spacer(Modifier.weight(1f))
+ }
+ Text(
+ "${progressPercent.toInt()}%",
+ style = MaterialTheme.typography.labelSmall,
+ color = chromeContent.copy(alpha = 0.72f)
+ )
+ ReaderTooltipIconButton(
+ tooltip = readerString("desktop_next_page", "Next page"),
+ onClick = onNext,
+ enabled = canGoNext
+ ) {
+ Icon(
+ Icons.AutoMirrored.Filled.NavigateNext,
+ contentDescription = readerString("desktop_next_page", "Next page"),
+ tint = chromeContent.copy(alpha = if (canGoNext) 0.78f else 0.32f)
+ )
+ }
}
}
}
diff --git a/desktopApp/src/desktopMain/kotlin/com/aryan/reader/desktop/DesktopReaderScreen.kt b/desktopApp/src/desktopMain/kotlin/com/aryan/reader/desktop/DesktopReaderScreen.kt
index 1515dc3..9b0721d 100644
--- a/desktopApp/src/desktopMain/kotlin/com/aryan/reader/desktop/DesktopReaderScreen.kt
+++ b/desktopApp/src/desktopMain/kotlin/com/aryan/reader/desktop/DesktopReaderScreen.kt
@@ -62,6 +62,7 @@ import com.aryan.reader.shared.ui.DesktopEpubNativeImage
import com.aryan.reader.shared.ui.ReaderContentRenderPlan
import com.aryan.reader.shared.ui.SharedNativePaginatedReader
import com.aryan.reader.shared.ui.SharedNativeReaderSelectionAction
+import com.aryan.reader.shared.ui.SharedNativeVerticalReader
import com.aryan.reader.shared.ui.SharedReaderScreen
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
@@ -627,6 +628,7 @@ internal fun DesktopReaderScreen(
},
readerCustomTextureIds = readerCustomTextureIds,
onImportReaderTexture = onImportReaderTexture,
+ preferNativeVerticalReader = desktopShouldUseNativeVerticalEpubReader(),
bottomChromeExtraContent = bottomChromeExtraContent,
useDetachedChromeLayer = useDetachedChromeLayer,
useDetachedPanelLayer = useDetachedPanelLayer
@@ -634,7 +636,7 @@ internal fun DesktopReaderScreen(
val renderPlanModeKey = renderPlan.desktopReaderSurfaceModeKey()
val readerSurfaceKey = renderPlan.desktopReaderSurfaceContentKey(paginatedLayoutReady)
val readerModeSwitchLayoutModifier =
- if (renderPlan is ReaderContentRenderPlan.NativePaginatedPages) {
+ if (renderPlan.desktopReaderUsesNativeComposeSurface()) {
Modifier.onGloballyPositioned { coordinates ->
val bounds = coordinates.boundsInWindow()
logReaderModeSwitch(
@@ -713,7 +715,7 @@ internal fun DesktopReaderScreen(
"htmlChars=${(renderPlan as? ReaderContentRenderPlan.WebDocument)?.html?.length ?: 0} " +
"paginatedReady=$paginatedLayoutReady"
}
- if (renderPlan is ReaderContentRenderPlan.NativePaginatedPages) {
+ if (renderPlan.desktopReaderUsesNativeComposeSurface()) {
cleanupRetiredDesktopWebView2InteropHosts(
readerAwtWindow,
"native_surface_state_ready_$paginatedLayoutReady"
@@ -900,6 +902,67 @@ internal fun DesktopReaderScreen(
)
}
}
+ is ReaderContentRenderPlan.NativeVerticalPages -> {
+ LaunchedEffect(renderPlan.book.id, renderPlan.pages, renderPlan.currentPageIndex) {
+ logReaderModeSwitch(
+ "native_vertical_reader_render currentPage=${renderPlan.currentPageIndex + 1} " +
+ "pages=${renderPlan.pages.size} chapters=${renderPlan.book.chapters.size} " +
+ "semanticBlocks=${renderPlan.book.chapters.sumOf { it.semanticBlocks.size }} " +
+ "background=${renderPlan.background} foreground=${renderPlan.foreground}"
+ )
+ }
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .onGloballyPositioned { coordinates ->
+ val bounds = coordinates.boundsInWindow()
+ logReaderModeSwitch(
+ "native_vertical_reader_content_layout size=${coordinates.size.width}x${coordinates.size.height} " +
+ "windowBounds=${bounds.left.formatLogFloat()},${bounds.top.formatLogFloat()} " +
+ "${bounds.width.formatLogFloat()}x${bounds.height.formatLogFloat()} " +
+ "currentPage=${renderPlan.currentPageIndex + 1}"
+ )
+ }
+ ) {
+ SharedNativeVerticalReader(
+ renderPlan = renderPlan,
+ readerFontFamily = renderPlan.settings.toDesktopReaderFontFamily(),
+ searchHighlight = MaterialTheme.colorScheme.tertiaryContainer.copy(alpha = 0.72f),
+ onVisiblePageChanged = onVisiblePageChanged,
+ enabledSelectionActions = nativeSelectionActions,
+ onCopyText = { text -> clipboardManager.setText(AnnotatedString(text)) },
+ onSelectionAction = handleNativeSelectionAction,
+ onOpenHighlightPaletteManager = onOpenHighlightPaletteManager,
+ onHighlightCreated = { highlight ->
+ logDesktopHighlightMap(
+ "native_vertical_state_reduce_start id=${highlight.id.logPreview(80)} before=${session.highlights.size} " +
+ "color=${highlight.color.id} chapter=${highlight.chapterIndex} " +
+ "page=${highlight.locator.pageIndex} offsets=${highlight.locator.startOffset}..${highlight.locator.endOffset} " +
+ "block=${highlight.locator.blockIndex} char=${highlight.locator.charOffset} " +
+ "textChars=${highlight.text.length} cfi=\"${highlight.cfi.logPreview(160)}\""
+ )
+ val nextSession = session.reduce(ReaderAction.HighlightCreated(highlight), readerEngine)
+ logDesktopHighlightMap(
+ "native_vertical_state_reduce_done id=${highlight.id.logPreview(80)} after=${nextSession.highlights.size} " +
+ "contains=${nextSession.highlights.any { it.id == highlight.id }}"
+ )
+ onSessionChange(nextSession)
+ },
+ onHighlightSelected = onHighlightSelected,
+ onLinkClicked = { link ->
+ handleDesktopEpubLinkClicked(link.toDesktopEpubLinkClick())
+ },
+ onReaderTap = onChromeActivity,
+ imageContent = { image, imageModifier ->
+ DesktopEpubNativeImage(
+ image = image,
+ modifier = imageModifier
+ )
+ },
+ modifier = Modifier.fillMaxSize()
+ )
+ }
+ }
}
}
}
@@ -933,6 +996,7 @@ private fun ReaderContentRenderPlan.desktopReaderSurfaceModeKey(): String {
return when (this) {
is ReaderContentRenderPlan.WebDocument -> "desktop-reader-web"
is ReaderContentRenderPlan.NativePaginatedPages -> "desktop-reader-native"
+ is ReaderContentRenderPlan.NativeVerticalPages -> "desktop-reader-native-vertical"
}
}
@@ -940,6 +1004,7 @@ private fun ReaderContentRenderPlan.desktopReaderSurfaceModeLabel(): String {
return when (this) {
is ReaderContentRenderPlan.WebDocument -> "web"
is ReaderContentRenderPlan.NativePaginatedPages -> "native"
+ is ReaderContentRenderPlan.NativeVerticalPages -> "native-vertical"
}
}
@@ -948,9 +1013,15 @@ private fun ReaderContentRenderPlan.desktopReaderSurfaceContentKey(paginatedLayo
is ReaderContentRenderPlan.WebDocument -> "desktop-reader-web"
is ReaderContentRenderPlan.NativePaginatedPages ->
"desktop-reader-native-${if (paginatedLayoutReady) "ready" else "preparing"}"
+ is ReaderContentRenderPlan.NativeVerticalPages -> "desktop-reader-native-vertical"
}
}
+private fun ReaderContentRenderPlan.desktopReaderUsesNativeComposeSurface(): Boolean {
+ return this is ReaderContentRenderPlan.NativePaginatedPages ||
+ this is ReaderContentRenderPlan.NativeVerticalPages
+}
+
private fun Window?.requestDesktopReaderModeSwitchRepaint(reason: String) {
val targetWindow = this
EventQueue.invokeLater {
diff --git a/desktopApp/src/desktopMain/kotlin/com/aryan/reader/desktop/Main.kt b/desktopApp/src/desktopMain/kotlin/com/aryan/reader/desktop/Main.kt
index c3b94e9..4a4bca3 100644
--- a/desktopApp/src/desktopMain/kotlin/com/aryan/reader/desktop/Main.kt
+++ b/desktopApp/src/desktopMain/kotlin/com/aryan/reader/desktop/Main.kt
@@ -80,7 +80,6 @@ import com.aryan.reader.shared.opds.SharedOpdsStreamUri
import com.aryan.reader.shared.pdf.SharedPdfReaderViewport
import com.aryan.reader.shared.reader.ReaderEngine
import com.aryan.reader.shared.reader.ReaderImageReference
-import com.aryan.reader.shared.reader.ReaderReadingMode
import com.aryan.reader.shared.reader.ReaderSessionState
import com.aryan.reader.shared.reader.ReaderSettings
import com.aryan.reader.shared.reader.SharedEpubMetadataEditor
@@ -3472,11 +3471,7 @@ internal fun EpistemeDesktopApp(
val readerFile = File(path)
val settingsStartedAt = System.nanoTime()
val restoredSettings = resolvedDesktopReaderSettings(book, readerDefaultSettings)
- val semanticMode = if (restoredSettings.readingMode == ReaderReadingMode.VERTICAL) {
- SharedJvmBookLoadSemanticMode.SKIP
- } else {
- SharedJvmBookLoadSemanticMode.FULL
- }
+ val semanticMode = desktopEpubBookLoadSemanticMode(restoredSettings)
val preparedHtmlChapterRange = if (semanticMode == SharedJvmBookLoadSemanticMode.SKIP) {
val initialChapter = book.readerPosition?.chapterIndex?.takeIf { it >= 0 } ?: 0
(initialChapter - DesktopVerticalInitialPreparedHtmlChapterRadius).coerceAtLeast(0)..
diff --git a/desktopApp/src/desktopTest/kotlin/com/aryan/reader/desktop/DesktopAuthStoreTest.kt b/desktopApp/src/desktopTest/kotlin/com/aryan/reader/desktop/DesktopAuthStoreTest.kt
index fe001ba..f5d4042 100644
--- a/desktopApp/src/desktopTest/kotlin/com/aryan/reader/desktop/DesktopAuthStoreTest.kt
+++ b/desktopApp/src/desktopTest/kotlin/com/aryan/reader/desktop/DesktopAuthStoreTest.kt
@@ -32,12 +32,25 @@ class DesktopAuthStoreTest {
}
@Test
- fun `save fails without leaving a partial account file when secure storage is unavailable`() {
+ fun `save falls back to session only without leaving a partial account file when secure storage is unavailable`() {
val settingsFile = Files.createTempDirectory("reader-auth-store-unavailable")
.resolve("auth.properties")
.toFile()
val store = DesktopAuthStore(settingsFile, ThrowingSecretCodec)
+ store.save(testSession())
+
+ assertFalse(settingsFile.exists())
+ assertEquals(null, store.load())
+ }
+
+ @Test
+ fun `save still surfaces secure storage write failures when storage is available`() {
+ val settingsFile = Files.createTempDirectory("reader-auth-store-available-write-failure")
+ .resolve("auth.properties")
+ .toFile()
+ val store = DesktopAuthStore(settingsFile, AvailableThrowingSecretCodec)
+
assertFailsWith {
store.save(testSession())
}
@@ -82,4 +95,14 @@ class DesktopAuthStoreTest {
override fun unprotect(value: String): String = ""
}
+
+ private object AvailableThrowingSecretCodec : DesktopSecretCodec {
+ override val isAvailable: Boolean = true
+
+ override fun protect(value: String): String {
+ throw IllegalStateException("Secure storage write failed")
+ }
+
+ override fun unprotect(value: String): String = ""
+ }
}
diff --git a/desktopApp/src/desktopTest/kotlin/com/aryan/reader/desktop/DesktopStartupTest.kt b/desktopApp/src/desktopTest/kotlin/com/aryan/reader/desktop/DesktopStartupTest.kt
index a605f49..2509680 100644
--- a/desktopApp/src/desktopTest/kotlin/com/aryan/reader/desktop/DesktopStartupTest.kt
+++ b/desktopApp/src/desktopTest/kotlin/com/aryan/reader/desktop/DesktopStartupTest.kt
@@ -1,6 +1,9 @@
package com.aryan.reader.desktop
import com.aryan.reader.shared.SharedReaderScreenState
+import com.aryan.reader.shared.reader.ReaderReadingMode
+import com.aryan.reader.shared.reader.ReaderSettings
+import com.aryan.reader.shared.reader.SharedJvmBookLoadSemanticMode
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
@@ -59,6 +62,33 @@ class DesktopStartupTest {
assertFalse(desktopEpubWebViewCanRender(DesktopWebViewRuntimeState(initialized = true), other))
}
+ @Test
+ fun `desktop vertical epub native reader is Linux only`() {
+ val windows = DesktopPlatform(DesktopOperatingSystem.WINDOWS, DesktopArchitecture.X64)
+ val linux = DesktopPlatform(DesktopOperatingSystem.LINUX, DesktopArchitecture.X64)
+ val macos = DesktopPlatform(DesktopOperatingSystem.MACOS, DesktopArchitecture.ARM64)
+ val other = DesktopPlatform(DesktopOperatingSystem.OTHER, DesktopArchitecture.X64)
+
+ assertTrue(desktopShouldUseNativeVerticalEpubReader(linux))
+ assertFalse(desktopShouldUseNativeVerticalEpubReader(windows))
+ assertFalse(desktopShouldUseNativeVerticalEpubReader(macos))
+ assertFalse(desktopShouldUseNativeVerticalEpubReader(other))
+ }
+
+ @Test
+ fun `desktop vertical epub load keeps semantic blocks for Linux native reader`() {
+ val windows = DesktopPlatform(DesktopOperatingSystem.WINDOWS, DesktopArchitecture.X64)
+ val linux = DesktopPlatform(DesktopOperatingSystem.LINUX, DesktopArchitecture.X64)
+ val macos = DesktopPlatform(DesktopOperatingSystem.MACOS, DesktopArchitecture.ARM64)
+ val verticalSettings = ReaderSettings(readingMode = ReaderReadingMode.VERTICAL)
+ val paginatedSettings = ReaderSettings(readingMode = ReaderReadingMode.PAGINATED)
+
+ assertEquals(SharedJvmBookLoadSemanticMode.FULL, desktopEpubBookLoadSemanticMode(verticalSettings, linux))
+ assertEquals(SharedJvmBookLoadSemanticMode.SKIP, desktopEpubBookLoadSemanticMode(verticalSettings, windows))
+ assertEquals(SharedJvmBookLoadSemanticMode.SKIP, desktopEpubBookLoadSemanticMode(verticalSettings, macos))
+ assertEquals(SharedJvmBookLoadSemanticMode.FULL, desktopEpubBookLoadSemanticMode(paginatedSettings, windows))
+ }
+
@Test
fun `native webview unavailable messages point to the platform runtime`() {
val windowsMessage = desktopNativeWebViewUnavailableMessage(
diff --git a/desktopApp/src/desktopTest/kotlin/com/aryan/reader/desktop/LinuxSecretToolCodecTest.kt b/desktopApp/src/desktopTest/kotlin/com/aryan/reader/desktop/LinuxSecretToolCodecTest.kt
index 91974ef..532360b 100644
--- a/desktopApp/src/desktopTest/kotlin/com/aryan/reader/desktop/LinuxSecretToolCodecTest.kt
+++ b/desktopApp/src/desktopTest/kotlin/com/aryan/reader/desktop/LinuxSecretToolCodecTest.kt
@@ -2,9 +2,44 @@ package com.aryan.reader.desktop
import kotlin.test.Test
import kotlin.test.assertEquals
+import kotlin.test.assertFalse
import kotlin.test.assertTrue
class LinuxSecretToolCodecTest {
+ @Test
+ fun `libsecret codec stores looks up legacy secret tool references and clears secrets by key`() {
+ val client = FakeLinuxSecretServiceClient()
+ val codec = LinuxLibsecretCodec(client)
+
+ assertTrue(codec.isAvailable)
+ val reference = codec.protect("geminiKeyProtected", "linux_gemini_key")
+
+ assertTrue(reference.startsWith("linux-libsecret:"))
+ assertEquals("linux_gemini_key", codec.unprotect("geminiKeyProtected", reference))
+ assertEquals(
+ "linux_gemini_key",
+ codec.unprotect("geminiKeyProtected", "secret-tool:Episteme.Reader.geminiKeyProtected")
+ )
+ codec.delete("geminiKeyProtected")
+ assertTrue(client.storedSecrets.isEmpty())
+ }
+
+ @Test
+ fun `linux secret service codec falls back to secret tool when libsecret is unavailable`() {
+ val runner = FakeSecretCommandRunner()
+ val codec = LinuxSecretServiceCodec(
+ libsecretCodec = LinuxLibsecretCodec(FakeLinuxSecretServiceClient(available = false)),
+ secretToolCodec = LinuxSecretToolCodec(runner)
+ )
+
+ assertTrue(codec.isAvailable)
+ val reference = codec.protect("geminiKeyProtected", "linux_gemini_key")
+
+ assertTrue(reference.startsWith("secret-tool:"))
+ assertEquals("linux_gemini_key", codec.unprotect("geminiKeyProtected", reference))
+ assertFalse(runner.storedSecrets.isEmpty())
+ }
+
@Test
fun `secret tool codec stores looks up and clears secrets by key`() {
val runner = FakeSecretCommandRunner()
@@ -18,6 +53,31 @@ class LinuxSecretToolCodecTest {
assertTrue(runner.storedSecrets.isEmpty())
}
+ private class FakeLinuxSecretServiceClient(
+ private val available: Boolean = true
+ ) : LinuxSecretServiceClient {
+ val storedSecrets = linkedMapOf()
+
+ override val isAvailable: Boolean
+ get() = available
+
+ override fun store(key: String, label: String, password: String) {
+ check(available) { "libsecret unavailable" }
+ storedSecrets[key] = password
+ }
+
+ override fun lookup(key: String): String? {
+ check(available) { "libsecret unavailable" }
+ return storedSecrets[key]
+ }
+
+ override fun clear(key: String) {
+ if (available) {
+ storedSecrets.remove(key)
+ }
+ }
+ }
+
private class FakeSecretCommandRunner : DesktopSecretCommandRunner {
val storedSecrets = linkedMapOf()
diff --git a/docs/EPISTEME.png b/docs/EPISTEME.png
index d154bdd..2cce3ca 100644
Binary files a/docs/EPISTEME.png and b/docs/EPISTEME.png differ
diff --git a/docs/EPISTEME_desktop.png b/docs/EPISTEME_desktop.png
new file mode 100644
index 0000000..d27a66c
Binary files /dev/null and b/docs/EPISTEME_desktop.png differ
diff --git a/shared/src/commonMain/kotlin/com/aryan/reader/shared/pdf/PdfInteractionModels.kt b/shared/src/commonMain/kotlin/com/aryan/reader/shared/pdf/PdfInteractionModels.kt
index d51bfae..1de9902 100644
--- a/shared/src/commonMain/kotlin/com/aryan/reader/shared/pdf/PdfInteractionModels.kt
+++ b/shared/src/commonMain/kotlin/com/aryan/reader/shared/pdf/PdfInteractionModels.kt
@@ -194,7 +194,7 @@ object SharedPdfAnnotationDefaults {
0xFFFFFFFF.toInt()
)
- val highlighterPalette: List = SharedPdfAndroidHighlightColors.palette.take(4)
+ val highlighterPalette: List = SharedPdfAndroidHighlightColors.palette.take(5)
fun configFor(tool: PdfInkTool): PdfToolConfig {
return when (tool) {
@@ -235,7 +235,7 @@ data class SharedPdfHighlighterPalette(
companion object {
const val DefaultAlpha: Int = 0x8C
- const val MaxColors: Int = 4
+ const val MaxColors: Int = 5
val defaultColors: List
get() = SharedPdfAnnotationDefaults.highlighterPalette
.take(MaxColors)
diff --git a/shared/src/commonMain/kotlin/com/aryan/reader/shared/reader/SharedReaderTextAlignment.kt b/shared/src/commonMain/kotlin/com/aryan/reader/shared/reader/SharedReaderTextAlignment.kt
new file mode 100644
index 0000000..63f3527
--- /dev/null
+++ b/shared/src/commonMain/kotlin/com/aryan/reader/shared/reader/SharedReaderTextAlignment.kt
@@ -0,0 +1,54 @@
+package com.aryan.reader.shared.reader
+
+import androidx.compose.ui.text.style.TextAlign
+
+internal fun resolveSharedReaderTextAlign(
+ cssTextAlign: TextAlign,
+ fallbackTextAlign: TextAlign
+): TextAlign {
+ return when {
+ fallbackTextAlign.isExplicitSharedReaderTextAlign() -> fallbackTextAlign
+ cssTextAlign == TextAlign.Unspecified -> fallbackTextAlign
+ cssTextAlign == TextAlign.Justify -> TextAlign.Left
+ else -> cssTextAlign
+ }
+}
+
+private fun TextAlign.isExplicitSharedReaderTextAlign(): Boolean {
+ return this != TextAlign.Start &&
+ this != TextAlign.Left &&
+ this != TextAlign.Unspecified
+}
+
+internal fun resolveSharedReaderFontFeatureSettings(
+ existingSettings: String?,
+ fontVariantNumeric: String?
+): String? {
+ val existing = existingSettings
+ ?.trim()
+ ?.takeIf { it.isNotBlank() }
+ val numericFeatures = fontVariantNumeric
+ ?.lowercase()
+ ?.split(Regex("\\s+"))
+ ?.mapNotNull { token ->
+ when (token) {
+ "lining-nums" -> "lnum"
+ "oldstyle-nums" -> "onum"
+ "proportional-nums" -> "pnum"
+ "tabular-nums" -> "tnum"
+ "diagonal-fractions" -> "frac"
+ "stacked-fractions" -> "afrc"
+ "ordinal" -> "ordn"
+ "slashed-zero" -> "zero"
+ else -> null
+ }
+ }
+ ?.distinct()
+ ?.map { feature -> "\"$feature\" on" }
+ ?.takeIf { it.isNotEmpty() }
+ ?.joinToString(", ")
+
+ return listOfNotNull(existing, numericFeatures)
+ .joinToString(", ")
+ .takeIf { it.isNotBlank() }
+}
diff --git a/shared/src/commonMain/kotlin/com/aryan/reader/shared/ui/ReaderContentRenderPlan.kt b/shared/src/commonMain/kotlin/com/aryan/reader/shared/ui/ReaderContentRenderPlan.kt
index 2b1237f..3545e52 100644
--- a/shared/src/commonMain/kotlin/com/aryan/reader/shared/ui/ReaderContentRenderPlan.kt
+++ b/shared/src/commonMain/kotlin/com/aryan/reader/shared/ui/ReaderContentRenderPlan.kt
@@ -8,6 +8,7 @@ import com.aryan.reader.shared.reader.ReaderPage
import com.aryan.reader.shared.reader.ReaderReadingMode
import com.aryan.reader.shared.reader.ReaderSearchOptions
import com.aryan.reader.shared.reader.ReaderSettings
+import com.aryan.reader.shared.reader.SharedEpubBook
data class ReaderContentNavigationTarget(
val locator: ReaderLocator?,
@@ -44,4 +45,18 @@ sealed interface ReaderContentRenderPlan {
override val navigationTarget: ReaderContentNavigationTarget,
override val highlights: List
) : ReaderContentRenderPlan
+
+ data class NativeVerticalPages(
+ val book: SharedEpubBook,
+ val pages: List,
+ val currentPageIndex: Int,
+ val settings: ReaderSettings,
+ val searchQuery: String,
+ val searchOptions: ReaderSearchOptions,
+ val highlightPalette: ReaderHighlightPalette,
+ override val background: Color,
+ override val foreground: Color,
+ override val navigationTarget: ReaderContentNavigationTarget,
+ override val highlights: List
+ ) : ReaderContentRenderPlan
}
diff --git a/shared/src/commonMain/kotlin/com/aryan/reader/shared/ui/SharedNativePaginatedReader.kt b/shared/src/commonMain/kotlin/com/aryan/reader/shared/ui/SharedNativePaginatedReader.kt
index 8425017..2946c9b 100644
--- a/shared/src/commonMain/kotlin/com/aryan/reader/shared/ui/SharedNativePaginatedReader.kt
+++ b/shared/src/commonMain/kotlin/com/aryan/reader/shared/ui/SharedNativePaginatedReader.kt
@@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
+import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
@@ -27,7 +28,11 @@ import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.horizontalScroll
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.itemsIndexed
+import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.rememberScrollState
+import androidx.compose.ui.draw.drawBehind
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
@@ -42,6 +47,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.setValue
+import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
@@ -49,29 +55,48 @@ import androidx.compose.ui.graphics.TransformOrigin
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.geometry.CornerRadius
+import androidx.compose.ui.geometry.RoundRect
+import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Path
+import androidx.compose.ui.graphics.PathEffect
import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.drawscope.Fill
+import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.graphics.isSpecified
+import androidx.compose.ui.graphics.luminance
+import androidx.compose.ui.geometry.toRect
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.vector.PathParser
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.boundsInWindow
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.layout.positionInRoot
import androidx.compose.ui.platform.LocalViewConfiguration
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.ParagraphStyle
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.TextLayoutResult
+import androidx.compose.ui.text.TextMeasurer
+import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.buildAnnotatedString
+import androidx.compose.ui.text.drawText
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.rememberTextMeasurer
+import androidx.compose.ui.text.withStyle
+import androidx.compose.ui.text.style.BaselineShift
+import androidx.compose.ui.text.style.Hyphens
import androidx.compose.ui.text.style.LineBreak
import androidx.compose.ui.text.style.LineHeightStyle
import androidx.compose.ui.text.style.TextAlign
-import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.style.TextDecoration
+import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
@@ -80,6 +105,9 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.isSpecified
import androidx.compose.ui.unit.sp
import com.aryan.reader.paginatedreader.CssStyle
+import com.aryan.reader.paginatedreader.BlockStyle
+import com.aryan.reader.paginatedreader.BorderStyle
+import com.aryan.reader.paginatedreader.CssParser
import com.aryan.reader.paginatedreader.SemanticBlock
import com.aryan.reader.paginatedreader.SemanticFlexContainer
import com.aryan.reader.paginatedreader.SemanticHeader
@@ -99,8 +127,17 @@ import com.aryan.reader.shared.reader.ReaderPage
import com.aryan.reader.shared.reader.ReaderReadingMode
import com.aryan.reader.shared.reader.ReaderSettings
import com.aryan.reader.shared.reader.SharedEpubCutoffDiagnosticsTag
+import com.aryan.reader.shared.reader.SharedEpubBook
+import com.aryan.reader.shared.reader.SharedEpubChapter
import com.aryan.reader.shared.reader.SharedReaderTextAlign
import com.aryan.reader.shared.reader.logSharedReaderDiagnostic
+import com.aryan.reader.shared.reader.resolveSharedReaderFontFeatureSettings
+import com.aryan.reader.shared.reader.resolveSharedReaderTextAlign
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlin.io.encoding.Base64
+import kotlin.io.encoding.ExperimentalEncodingApi
+import kotlin.math.abs
import kotlin.math.roundToInt
enum class SharedNativeReaderSelectionAction {
@@ -115,6 +152,20 @@ data class SharedNativeReaderLinkClick(
val text: String?
)
+internal enum class SharedNativeVerticalFlowItemKind {
+ CHAPTER_GAP,
+ BLOCK,
+ TEXT_PAGE,
+ EMPTY_CHAPTER
+}
+
+internal data class SharedNativeVerticalFlowItem(
+ val key: String,
+ val kind: SharedNativeVerticalFlowItemKind,
+ val page: ReaderPage,
+ val block: SemanticBlock? = null
+)
+
internal data class SharedNativeReaderTextSelection(
val chapterIndex: Int,
val pageIndex: Int,
@@ -265,7 +316,7 @@ fun SharedNativePaginatedReader(
var readerCoordinates by remember(renderPlan.navigationTarget.requestId) {
mutableStateOf(null)
}
- val density = LocalDensity.current
+ val readerDensity = LocalDensity.current
LaunchedEffect(visiblePageIndices) {
val selection = activeSelection
if (selection != null && selection.pageIndex !in visiblePageIndices) {
@@ -315,7 +366,7 @@ fun SharedNativePaginatedReader(
val availableContentWidth = (maxWidth - (horizontalMargin * 2f)).coerceAtLeast(1.dp)
minOf(availableContentWidth, configuredContentWidth) + (horizontalMargin * 2f)
}
- val pageRenderGeometry = with(density) {
+ val pageRenderGeometry = with(readerDensity) {
val contentWidth = (pageOuterWidth - (horizontalMargin * 2f)).coerceAtLeast(1.dp)
val contentHeight = (maxHeight - (renderPlan.settings.resolvedVerticalMargin.dp * 2f)).coerceAtLeast(1.dp)
SharedNativePageRenderGeometry(
@@ -412,6 +463,304 @@ fun SharedNativePaginatedReader(
},
onOpenHighlightPaletteManager = onOpenHighlightPaletteManager,
onDismiss = { updateActiveSelection(null) },
+ modifier = Modifier
+ .align(Alignment.TopStart)
+ .offset {
+ sharedNativeSelectionMenuOffset(
+ selection = selection,
+ readerCoordinates = readerCoordinates,
+ density = readerDensity,
+ highlightPaletteSize = highlightPalette.size,
+ actionCount = enabledSelectionActions.size + 2
+ )
+ }
+ )
+ }
+ }
+ }
+}
+
+@Composable
+fun SharedNativeVerticalReader(
+ renderPlan: ReaderContentRenderPlan.NativeVerticalPages,
+ readerFontFamily: FontFamily,
+ searchHighlight: Color,
+ onVisiblePageChanged: (Int, ReaderLocator?) -> Unit,
+ modifier: Modifier = Modifier,
+ enabledSelectionActions: Set = emptySet(),
+ onCopyText: (String) -> Unit = {},
+ onSelectionAction: (SharedNativeReaderSelectionAction, String, ReaderLocator?) -> Unit = { _, _, _ -> },
+ onOpenHighlightPaletteManager: () -> Unit = {},
+ onHighlightCreated: (UserHighlight) -> Unit = {},
+ onHighlightSelected: (String) -> Unit = {},
+ onLinkClicked: (SharedNativeReaderLinkClick) -> Unit = {},
+ onReaderTap: () -> Unit = {},
+ imageContent: (@Composable (SemanticImage, Modifier) -> Unit)? = null
+) {
+ val flowItems = remember(renderPlan.book, renderPlan.pages) {
+ buildSharedNativeVerticalFlowItems(
+ book = renderPlan.book,
+ pages = renderPlan.pages
+ )
+ }
+ val listState = rememberLazyListState()
+ var activeSelection by remember(renderPlan.navigationTarget.requestId) {
+ mutableStateOf(null)
+ }
+ var selectionGestureActive by remember(renderPlan.navigationTarget.requestId) {
+ mutableStateOf(false)
+ }
+ var selectionHandleDragging by remember(renderPlan.navigationTarget.requestId) {
+ mutableStateOf(false)
+ }
+ fun updateActiveSelection(selection: SharedNativeReaderTextSelection?) {
+ activeSelection = selection
+ if (selection == null) {
+ selectionGestureActive = false
+ selectionHandleDragging = false
+ }
+ }
+ val selectionLayouts = remember(renderPlan.navigationTarget.requestId, renderPlan.book.id) {
+ mutableStateMapOf()
+ }
+ var readerCoordinates by remember(renderPlan.navigationTarget.requestId) {
+ mutableStateOf(null)
+ }
+ var didInitialScroll by remember(renderPlan.book.id) {
+ mutableStateOf(false)
+ }
+ val density = LocalDensity.current
+ val selectionHighlight = MaterialTheme.colorScheme.primary.copy(alpha = 0.28f)
+
+ LaunchedEffect(flowItems, renderPlan.navigationTarget.requestId) {
+ if (flowItems.isEmpty()) return@LaunchedEffect
+ val navigationIndex = renderPlan.navigationTarget.locator
+ ?.let { locator -> flowItems.sharedNativeVerticalItemIndexForLocator(locator) }
+ val initialIndex = if (!didInitialScroll) {
+ flowItems.sharedNativeVerticalItemIndexForPage(renderPlan.currentPageIndex)
+ } else {
+ null
+ }
+ val targetIndex = navigationIndex ?: initialIndex
+ if (targetIndex != null) {
+ listState.scrollToItem(targetIndex.coerceIn(0, flowItems.lastIndex))
+ }
+ didInitialScroll = true
+ }
+
+ LaunchedEffect(flowItems, listState) {
+ if (flowItems.isEmpty()) return@LaunchedEffect
+ snapshotFlow { listState.firstVisibleItemIndex }
+ .distinctUntilChanged()
+ .collectLatest { itemIndex ->
+ val item = flowItems.getOrNull(itemIndex) ?: return@collectLatest
+ onVisiblePageChanged(item.page.pageIndex, item.toNativeVerticalLocator())
+ }
+ }
+
+ if (flowItems.isEmpty()) {
+ Box(modifier = modifier, contentAlignment = Alignment.Center) {
+ Text(readerString("desktop_no_page_content", "No page content"), color = renderPlan.foreground.copy(alpha = 0.68f))
+ }
+ return
+ }
+
+ Box(
+ modifier = modifier
+ .readerChromeTapTogglePointerInput {
+ if (activeSelection == null) {
+ onReaderTap()
+ }
+ }
+ .onGloballyPositioned { readerCoordinates = it }
+ ) {
+ BoxWithConstraints(
+ modifier = Modifier
+ .fillMaxSize()
+ .background(renderPlan.background)
+ ) {
+ val requestedHorizontalPadding = renderPlan.settings.resolvedHorizontalMargin.dp
+ val requestedVerticalPadding = renderPlan.settings.resolvedVerticalMargin.dp
+ val minReadableWidth = minOf(96.dp, maxWidth)
+ val minReadableHeight = minOf(160.dp, maxHeight)
+ val horizontalPadding = requestedHorizontalPadding.coerceAtMost(
+ ((maxWidth - minReadableWidth) / 2f).coerceAtLeast(0.dp)
+ )
+ val verticalPadding = requestedVerticalPadding.coerceAtMost(
+ ((maxHeight - minReadableHeight) / 2f).coerceAtLeast(0.dp)
+ )
+ val chapterBoundaryGap = (
+ 44f * (renderPlan.settings.resolvedVerticalMargin / 48f)
+ ).coerceIn(32f, 112f).dp
+ val fallbackTextAlign = renderPlan.settings.textAlign.toComposeTextAlign()
+
+ LazyColumn(
+ state = listState,
+ modifier = Modifier.fillMaxSize(),
+ contentPadding = PaddingValues(top = verticalPadding, bottom = verticalPadding)
+ ) {
+ itemsIndexed(
+ items = flowItems,
+ key = { _, item -> item.key }
+ ) { _, item ->
+ when (item.kind) {
+ SharedNativeVerticalFlowItemKind.CHAPTER_GAP -> {
+ Spacer(
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(chapterBoundaryGap)
+ )
+ }
+
+ SharedNativeVerticalFlowItemKind.EMPTY_CHAPTER -> {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = horizontalPadding)
+ .height(72.dp),
+ contentAlignment = Alignment.CenterStart
+ ) {
+ Text(
+ text = item.page.chapterTitle.ifBlank { readerString("reader_chapter", "Chapter") },
+ color = renderPlan.foreground.copy(alpha = 0.54f),
+ style = MaterialTheme.typography.bodyMedium
+ )
+ }
+ }
+
+ SharedNativeVerticalFlowItemKind.TEXT_PAGE -> {
+ val page = item.page
+ val visibleHighlights = renderPlan.highlights.visibleInPage(page)
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = horizontalPadding)
+ ) {
+ SharedNativeInteractiveText(
+ text = page.text.toReaderAnnotatedString(
+ searchQuery = renderPlan.searchQuery,
+ searchHighlight = searchHighlight,
+ chapterIndex = page.chapterIndex,
+ pageIndex = page.pageIndex,
+ absoluteStartOffset = page.startOffset,
+ highlights = visibleHighlights,
+ activeSelection = activeSelection,
+ selectionHighlight = selectionHighlight
+ ),
+ page = page,
+ textBlock = SharedNativeTextBlockDescriptor(
+ chapterIndex = page.chapterIndex,
+ pageIndex = page.pageIndex,
+ blockIndex = -1,
+ blockCharOffset = page.startOffset,
+ baseCfi = null,
+ textStartOffset = page.startOffset,
+ text = page.text
+ ),
+ textStartOffset = page.startOffset,
+ color = renderPlan.foreground,
+ textAlign = fallbackTextAlign,
+ style = MaterialTheme.typography.bodyLarge.copy(
+ fontSize = renderPlan.settings.fontSize.sp,
+ lineHeight = (renderPlan.settings.fontSize * renderPlan.settings.lineSpacing).sp,
+ fontFamily = readerFontFamily
+ ).withAndroidPaginationTextMetrics(),
+ activeSelection = activeSelection,
+ onReaderTap = onReaderTap,
+ onSelectionChange = ::updateActiveSelection,
+ onSelectionGestureActiveChange = { selectionGestureActive = it },
+ onHighlightSelected = onHighlightSelected,
+ onLinkClicked = onLinkClicked,
+ selectionLayouts = selectionLayouts
+ )
+ }
+ }
+
+ SharedNativeVerticalFlowItemKind.BLOCK -> {
+ val page = item.page
+ val block = item.block
+ if (block != null) {
+ val visibleHighlights = renderPlan.highlights.visibleInPage(page)
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = horizontalPadding)
+ .background(renderPlan.background)
+ ) {
+ SharedSemanticBlockStack(
+ blocks = listOf(block),
+ page = page,
+ background = renderPlan.background,
+ foreground = renderPlan.foreground,
+ searchQuery = renderPlan.searchQuery,
+ searchHighlight = searchHighlight,
+ highlights = visibleHighlights,
+ activeSelection = activeSelection,
+ selectionHighlight = selectionHighlight,
+ fallbackTextAlign = fallbackTextAlign,
+ fallbackFontFamily = readerFontFamily,
+ settings = renderPlan.settings,
+ includeTrailingBottomMargin = true,
+ onReaderTap = onReaderTap,
+ onSelectionChange = ::updateActiveSelection,
+ onSelectionGestureActiveChange = { selectionGestureActive = it },
+ onHighlightSelected = onHighlightSelected,
+ onLinkClicked = onLinkClicked,
+ selectionLayouts = selectionLayouts,
+ imageContent = imageContent
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ activeSelection?.let { selection ->
+ arrayOf(SharedNativeSelectionHandle.START, SharedNativeSelectionHandle.END).forEach { handle ->
+ SharedNativeSelectionHandleView(
+ selection = selection,
+ handle = handle,
+ selectionLayouts = selectionLayouts.values,
+ readerCoordinates = readerCoordinates,
+ onDragActiveChange = { selectionHandleDragging = it },
+ onDrag = { windowPosition ->
+ val currentSelection = activeSelection
+ if (currentSelection != null) {
+ sharedNativeSelectionWithHandleMoved(
+ selection = currentSelection,
+ handle = handle,
+ windowPosition = windowPosition,
+ layouts = selectionLayouts.values
+ )?.let(::updateActiveSelection)
+ }
+ },
+ modifier = Modifier.align(Alignment.TopStart)
+ )
+ }
+ if (!selectionGestureActive && !selectionHandleDragging) {
+ val highlightPalette = renderPlan.highlightPalette.sanitized().colors
+ SharedNativeSelectionMenu(
+ selection = selection,
+ highlightPalette = highlightPalette,
+ enabledSelectionActions = enabledSelectionActions,
+ background = renderPlan.background,
+ foreground = renderPlan.foreground,
+ onCopy = {
+ onCopyText(selection.text)
+ updateActiveSelection(null)
+ },
+ onSelectionAction = { action ->
+ onSelectionAction(action, selection.text, selection.toReaderLocator())
+ updateActiveSelection(null)
+ },
+ onHighlight = { color ->
+ onHighlightCreated(sharedNativeReaderHighlightForSelection(selection, color))
+ updateActiveSelection(null)
+ },
+ onOpenHighlightPaletteManager = onOpenHighlightPaletteManager,
+ onDismiss = { updateActiveSelection(null) },
modifier = Modifier
.align(Alignment.TopStart)
.offset {
@@ -651,6 +1000,7 @@ private fun SharedNativePaginatedPage(
SharedSemanticBlockStack(
blocks = blocks,
page = page,
+ background = renderPlan.background,
foreground = renderPlan.foreground,
searchQuery = renderPlan.searchQuery,
searchHighlight = searchHighlight,
@@ -1206,6 +1556,173 @@ private fun SharedNativeInteractiveText(
)
}
+internal fun buildSharedNativeVerticalFlowItems(
+ book: SharedEpubBook,
+ pages: List
+): List {
+ val pagesByChapter = pages.groupBy { it.chapterIndex }
+ return book.chapters.flatMapIndexed { chapterIndex, chapter ->
+ val chapterPages = pagesByChapter[chapterIndex].orEmpty()
+ val fallbackPage = chapterPages.firstOrNull()
+ ?: chapter.toSharedNativeVerticalSyntheticPage(chapterIndex, pages.size + chapterIndex)
+ val boundary = if (chapterIndex > 0) {
+ listOf(
+ SharedNativeVerticalFlowItem(
+ key = "chapter-$chapterIndex-gap",
+ kind = SharedNativeVerticalFlowItemKind.CHAPTER_GAP,
+ page = fallbackPage
+ )
+ )
+ } else {
+ emptyList()
+ }
+ when {
+ chapter.semanticBlocks.isNotEmpty() -> {
+ boundary + chapter.semanticBlocks.mapIndexed { ordinal, block ->
+ val pageIndex = chapterPages.findSharedNativeVerticalPageIndexForBlock(block)
+ ?: fallbackPage.pageIndex
+ val range = block.sharedNativeVerticalTextRange()
+ ?: (0 to chapter.plainText.length)
+ SharedNativeVerticalFlowItem(
+ key = "chapter-$chapterIndex-block-$ordinal-${block.blockIndex}",
+ kind = SharedNativeVerticalFlowItemKind.BLOCK,
+ page = ReaderPage(
+ pageIndex = pageIndex,
+ chapterIndex = chapterIndex,
+ chapterTitle = chapter.title,
+ text = block.sharedNativeVerticalText().ifBlank { chapter.plainText },
+ startOffset = range.first,
+ endOffset = range.second.coerceAtLeast(range.first),
+ semanticBlocks = listOf(block)
+ ),
+ block = block
+ )
+ }
+ }
+
+ chapterPages.isNotEmpty() -> {
+ boundary + chapterPages.map { page ->
+ SharedNativeVerticalFlowItem(
+ key = "chapter-$chapterIndex-page-${page.pageIndex}",
+ kind = SharedNativeVerticalFlowItemKind.TEXT_PAGE,
+ page = page
+ )
+ }
+ }
+
+ chapter.plainText.isNotBlank() -> {
+ boundary + SharedNativeVerticalFlowItem(
+ key = "chapter-$chapterIndex-text",
+ kind = SharedNativeVerticalFlowItemKind.TEXT_PAGE,
+ page = chapter.toSharedNativeVerticalSyntheticPage(chapterIndex, fallbackPage.pageIndex)
+ )
+ }
+
+ else -> {
+ boundary + SharedNativeVerticalFlowItem(
+ key = "chapter-$chapterIndex-empty",
+ kind = SharedNativeVerticalFlowItemKind.EMPTY_CHAPTER,
+ page = fallbackPage
+ )
+ }
+ }
+ }
+}
+
+private fun SharedEpubChapter.toSharedNativeVerticalSyntheticPage(
+ chapterIndex: Int,
+ pageIndex: Int
+): ReaderPage {
+ return ReaderPage(
+ pageIndex = pageIndex.coerceAtLeast(0),
+ chapterIndex = chapterIndex,
+ chapterTitle = title,
+ text = plainText,
+ startOffset = 0,
+ endOffset = plainText.length,
+ semanticBlocks = emptyList()
+ )
+}
+
+private fun List.findSharedNativeVerticalPageIndexForBlock(block: SemanticBlock): Int? {
+ val range = block.sharedNativeVerticalTextRange()
+ val blockIndex = block.blockIndex
+ if (range != null) {
+ val (start, end) = range
+ firstOrNull { page ->
+ if (start == end) {
+ page.containsNativeCollapsedOffset(start)
+ } else {
+ start < page.endOffset && end > page.startOffset
+ }
+ }?.let { return it.pageIndex }
+ }
+ firstOrNull { page ->
+ page.semanticBlocks.flattenNativeSemanticBlocks().any { it.blockIndex == blockIndex }
+ }?.let { return it.pageIndex }
+ return firstOrNull()?.pageIndex
+}
+
+private fun List.sharedNativeVerticalItemIndexForLocator(
+ locator: ReaderLocator
+): Int? {
+ locator.pageIndex?.let { pageIndex ->
+ val samePage = indexOfFirst { item -> item.page.pageIndex == pageIndex }
+ if (samePage >= 0) return samePage
+ }
+ val chapterIndex = locator.chapterIndex
+ if (chapterIndex != null) {
+ locator.blockIndex?.let { blockIndex ->
+ val sameBlock = indexOfFirst { item ->
+ item.page.chapterIndex == chapterIndex &&
+ item.page.semanticBlocks.flattenNativeSemanticBlocks().any { block -> block.blockIndex == blockIndex }
+ }
+ if (sameBlock >= 0) return sameBlock
+ }
+ val targetOffset = locator.charOffset ?: locator.startOffset
+ if (targetOffset != null) {
+ val sameOffset = indexOfFirst { item ->
+ item.page.chapterIndex == chapterIndex &&
+ item.page.containsNativeCollapsedOffset(targetOffset)
+ }
+ if (sameOffset >= 0) return sameOffset
+ }
+ val sameChapter = indexOfFirst { item -> item.page.chapterIndex == chapterIndex }
+ if (sameChapter >= 0) return sameChapter
+ }
+ return null
+}
+
+private fun List.sharedNativeVerticalItemIndexForPage(pageIndex: Int): Int? {
+ val samePage = indexOfFirst { item -> item.page.pageIndex == pageIndex }
+ if (samePage >= 0) return samePage
+ val nearestPage = filter { item -> item.kind != SharedNativeVerticalFlowItemKind.CHAPTER_GAP }
+ .minByOrNull { item -> kotlin.math.abs(item.page.pageIndex - pageIndex) }
+ return nearestPage?.let { indexOf(it) }
+}
+
+private fun SharedNativeVerticalFlowItem.toNativeVerticalLocator(): ReaderLocator {
+ return page.toNativeReaderLocator().withFallbacks(
+ blockIndex = block?.blockIndex,
+ cfi = block?.cfi
+ )
+}
+
+private fun SemanticBlock.sharedNativeVerticalTextRange(): Pair? {
+ val textBlocks = flattenNativeSemanticBlock().filterIsInstance()
+ if (textBlocks.isEmpty()) return null
+ val start = textBlocks.minOf { it.startCharOffsetInSource }
+ val end = textBlocks.maxOf { it.startCharOffsetInSource + it.text.length }
+ return start to end.coerceAtLeast(start)
+}
+
+private fun SemanticBlock.sharedNativeVerticalText(): String {
+ return flattenNativeSemanticBlock()
+ .filterIsInstance()
+ .joinToString(separator = "\n") { it.text }
+ .trim()
+}
+
private fun ReaderPage.toNativeReaderLocator(): ReaderLocator {
val blockPosition = firstNativeLocatorBlockPosition()
return ReaderLocator(
@@ -1318,6 +1835,7 @@ private fun String.toReaderAnnotatedString(
private fun SharedSemanticBlockStack(
blocks: List,
page: ReaderPage,
+ background: Color,
foreground: Color,
searchQuery: String,
searchHighlight: Color,
@@ -1343,6 +1861,7 @@ private fun SharedSemanticBlockStack(
SharedSemanticBlockView(
block = block,
page = page,
+ background = background,
foreground = foreground,
searchQuery = searchQuery,
searchHighlight = searchHighlight,
@@ -1377,6 +1896,7 @@ private fun SharedSemanticBlockStack(
private fun SharedSemanticBlockView(
block: SemanticBlock,
page: ReaderPage,
+ background: Color,
foreground: Color,
searchQuery: String,
searchHighlight: Color,
@@ -1399,27 +1919,27 @@ private fun SharedSemanticBlockView(
onTextLaidOut: ((SharedNativeTextFit) -> Unit)? = null,
onBlockLaidOut: ((SharedNativeBlockFit) -> Unit)? = null
) {
+ val blockStyle = block.style.blockStyle.sharedNativeThemeBlockStyle(
+ isDarkTheme = settings.darkMode,
+ background = background,
+ foreground = foreground
+ )
val modifier = Modifier
.fillMaxWidth()
.padding(
- start = block.style.blockStyle.margin.left.safeDp(),
+ start = blockStyle.margin.left.safeDp(),
top = marginTop,
- end = block.style.blockStyle.margin.right.safeDp(),
+ end = blockStyle.margin.right.safeDp(),
bottom = marginBottom
)
- .then(
- if (block.style.blockStyle.backgroundColor.isSpecified) {
- Modifier.background(block.style.blockStyle.backgroundColor, RoundedCornerShape(4.dp))
- } else {
- Modifier
- }
- )
+ .sharedNativeCssBox(blockStyle)
.padding(
- start = block.style.blockStyle.padding.left.safeDp(),
- top = block.style.blockStyle.padding.top.safeDp(),
- end = block.style.blockStyle.padding.right.safeDp(),
- bottom = block.style.blockStyle.padding.bottom.safeDp()
+ start = blockStyle.padding.left.safeDp(),
+ top = blockStyle.padding.top.safeDp(),
+ end = blockStyle.padding.right.safeDp(),
+ bottom = blockStyle.padding.bottom.safeDp()
)
+ .sharedNativeVisibility(blockStyle)
val measuredModifier = if (layoutIndex != null && onBlockLaidOut != null) {
Modifier
.onGloballyPositioned { coordinates ->
@@ -1430,151 +1950,139 @@ private fun SharedSemanticBlockView(
modifier
}
- when (block) {
- is SemanticHeader -> {
- SharedSemanticTextView(
- block = block,
- page = page,
- modifier = measuredModifier,
- foreground = foreground,
- searchQuery = searchQuery,
- searchHighlight = searchHighlight,
- highlights = highlights,
- activeSelection = activeSelection,
- selectionHighlight = selectionHighlight,
- fallbackTextAlign = block.style.paragraphStyle.textAlign.takeUnless { it == TextAlign.Unspecified } ?: fallbackTextAlign,
- fallbackFontFamily = fallbackFontFamily,
- settings = settings,
- fontWeight = FontWeight.Bold,
- onReaderTap = onReaderTap,
- onSelectionChange = onSelectionChange,
- onSelectionGestureActiveChange = onSelectionGestureActiveChange,
- onHighlightSelected = onHighlightSelected,
- onLinkClicked = onLinkClicked,
- selectionLayouts = selectionLayouts,
- onTextLaidOut = onTextLaidOut
- )
- }
+ SharedNativeCssBlockContainer(
+ modifier = measuredModifier,
+ blockStyle = blockStyle,
+ blockIndex = block.blockIndex,
+ imageContent = imageContent
+ ) { contentModifier ->
+ when (block) {
+ is SemanticHeader -> {
+ SharedSemanticTextView(
+ block = block,
+ page = page,
+ modifier = contentModifier,
+ background = background,
+ foreground = foreground,
+ searchQuery = searchQuery,
+ searchHighlight = searchHighlight,
+ highlights = highlights,
+ activeSelection = activeSelection,
+ selectionHighlight = selectionHighlight,
+ fallbackTextAlign = fallbackTextAlign,
+ fallbackFontFamily = fallbackFontFamily,
+ settings = settings,
+ fontWeight = FontWeight.Bold,
+ onReaderTap = onReaderTap,
+ onSelectionChange = onSelectionChange,
+ onSelectionGestureActiveChange = onSelectionGestureActiveChange,
+ onHighlightSelected = onHighlightSelected,
+ onLinkClicked = onLinkClicked,
+ selectionLayouts = selectionLayouts,
+ onTextLaidOut = onTextLaidOut
+ )
+ }
- is SemanticParagraph -> SharedSemanticTextView(block, page, measuredModifier, foreground, searchQuery, searchHighlight, highlights, activeSelection, selectionHighlight, fallbackTextAlign, fallbackFontFamily, settings, onReaderTap = onReaderTap, onSelectionChange = onSelectionChange, onSelectionGestureActiveChange = onSelectionGestureActiveChange, onHighlightSelected = onHighlightSelected, onLinkClicked = onLinkClicked, selectionLayouts = selectionLayouts, onTextLaidOut = onTextLaidOut)
- is SemanticListItem -> SharedSemanticTextView(block, page, measuredModifier, foreground, searchQuery, searchHighlight, highlights, activeSelection, selectionHighlight, fallbackTextAlign, fallbackFontFamily, settings, onReaderTap = onReaderTap, onSelectionChange = onSelectionChange, onSelectionGestureActiveChange = onSelectionGestureActiveChange, onHighlightSelected = onHighlightSelected, onLinkClicked = onLinkClicked, selectionLayouts = selectionLayouts, onTextLaidOut = onTextLaidOut)
- is SemanticTextBlock -> SharedSemanticTextView(block, page, measuredModifier, foreground, searchQuery, searchHighlight, highlights, activeSelection, selectionHighlight, fallbackTextAlign, fallbackFontFamily, settings, onReaderTap = onReaderTap, onSelectionChange = onSelectionChange, onSelectionGestureActiveChange = onSelectionGestureActiveChange, onHighlightSelected = onHighlightSelected, onLinkClicked = onLinkClicked, selectionLayouts = selectionLayouts, onTextLaidOut = onTextLaidOut)
+ is SemanticParagraph -> SharedSemanticTextView(block, page, contentModifier, background, foreground, searchQuery, searchHighlight, highlights, activeSelection, selectionHighlight, fallbackTextAlign, fallbackFontFamily, settings, onReaderTap = onReaderTap, onSelectionChange = onSelectionChange, onSelectionGestureActiveChange = onSelectionGestureActiveChange, onHighlightSelected = onHighlightSelected, onLinkClicked = onLinkClicked, selectionLayouts = selectionLayouts, onTextLaidOut = onTextLaidOut)
+ is SemanticListItem -> SharedSemanticTextView(block, page, contentModifier, background, foreground, searchQuery, searchHighlight, highlights, activeSelection, selectionHighlight, fallbackTextAlign, fallbackFontFamily, settings, onReaderTap = onReaderTap, onSelectionChange = onSelectionChange, onSelectionGestureActiveChange = onSelectionGestureActiveChange, onHighlightSelected = onHighlightSelected, onLinkClicked = onLinkClicked, selectionLayouts = selectionLayouts, onTextLaidOut = onTextLaidOut)
+ is SemanticTextBlock -> SharedSemanticTextView(block, page, contentModifier, background, foreground, searchQuery, searchHighlight, highlights, activeSelection, selectionHighlight, fallbackTextAlign, fallbackFontFamily, settings, onReaderTap = onReaderTap, onSelectionChange = onSelectionChange, onSelectionGestureActiveChange = onSelectionGestureActiveChange, onHighlightSelected = onHighlightSelected, onLinkClicked = onLinkClicked, selectionLayouts = selectionLayouts, onTextLaidOut = onTextLaidOut)
- is SemanticList -> {
- Column(modifier = measuredModifier, verticalArrangement = Arrangement.Top) {
- var previous: SemanticBlock? = null
- block.items.forEachIndexed { index, item ->
- Row(
- modifier = Modifier.padding(
- top = item.collapsedTopMarginDp(previous, settings),
- bottom = if (index == block.items.lastIndex) item.effectiveBottomMarginDp(settings) else 0.dp
- ),
- verticalAlignment = Alignment.Top
- ) {
- val markerModifier = Modifier
- .width(SharedNativeListItemMarkerAreaWidthDp.dp)
- .padding(end = SharedNativeListItemMarkerEndPaddingDp.dp)
- Text(
- text = if (block.isOrdered) "${index + 1}." else "\u2022",
- color = foreground,
- modifier = markerModifier,
- textAlign = TextAlign.End,
- style = item.renderedTextStyle(
- settings = settings,
+ is SemanticList -> {
+ Column(modifier = contentModifier, verticalArrangement = Arrangement.Top) {
+ var previous: SemanticBlock? = null
+ block.items.forEachIndexed { index, item ->
+ Row(
+ modifier = Modifier.padding(
+ top = item.collapsedTopMarginDp(previous, settings),
+ bottom = if (index == block.items.lastIndex) item.effectiveBottomMarginDp(settings) else 0.dp
+ ),
+ verticalAlignment = Alignment.Top
+ ) {
+ val markerModifier = Modifier
+ .width(SharedNativeListItemMarkerAreaWidthDp.dp)
+ .padding(end = SharedNativeListItemMarkerEndPaddingDp.dp)
+ val markerImage = item.itemMarkerImage
+ ?: block.style.blockStyle.listStyleImage?.takeIf { it.isNotBlank() }
+ if (markerImage != null && imageContent != null) {
+ val markerSize = with(LocalDensity.current) { (settings.fontSize * 0.85f).sp.toDp() }
+ Box(
+ modifier = markerModifier,
+ contentAlignment = Alignment.CenterEnd
+ ) {
+ imageContent(
+ SemanticImage(
+ path = markerImage,
+ altText = "",
+ intrinsicWidth = null,
+ intrinsicHeight = null,
+ style = CssStyle(),
+ elementId = null,
+ cfi = null,
+ blockIndex = item.blockIndex
+ ),
+ Modifier.size(markerSize)
+ )
+ }
+ } else {
+ Text(
+ text = sharedNativeListMarker(
+ index = index,
+ isOrdered = block.isOrdered,
+ listStyleType = block.style.blockStyle.listStyleType
+ ),
+ color = foreground,
+ modifier = markerModifier,
+ textAlign = TextAlign.End,
+ style = item.renderedTextStyle(
+ settings = settings,
+ fallbackFontFamily = fallbackFontFamily,
+ fallbackTextAlign = TextAlign.End,
+ background = background,
+ foreground = foreground
+ )
+ )
+ }
+ SharedSemanticTextView(
+ block = item,
+ page = page,
+ modifier = Modifier.weight(1f),
+ background = background,
+ foreground = foreground,
+ searchQuery = searchQuery,
+ searchHighlight = searchHighlight,
+ highlights = highlights,
+ activeSelection = activeSelection,
+ selectionHighlight = selectionHighlight,
+ fallbackTextAlign = fallbackTextAlign,
fallbackFontFamily = fallbackFontFamily,
- fallbackTextAlign = TextAlign.End
+ settings = settings,
+ onReaderTap = onReaderTap,
+ onSelectionChange = onSelectionChange,
+ onSelectionGestureActiveChange = onSelectionGestureActiveChange,
+ onHighlightSelected = onHighlightSelected,
+ onLinkClicked = onLinkClicked,
+ selectionLayouts = selectionLayouts,
+ onTextLaidOut = onTextLaidOut
)
- )
- SharedSemanticTextView(
- block = item,
- page = page,
- modifier = Modifier.weight(1f),
- foreground = foreground,
- searchQuery = searchQuery,
- searchHighlight = searchHighlight,
- highlights = highlights,
- activeSelection = activeSelection,
- selectionHighlight = selectionHighlight,
- fallbackTextAlign = fallbackTextAlign,
- fallbackFontFamily = fallbackFontFamily,
- settings = settings,
- onReaderTap = onReaderTap,
- onSelectionChange = onSelectionChange,
- onSelectionGestureActiveChange = onSelectionGestureActiveChange,
- onHighlightSelected = onHighlightSelected,
- onLinkClicked = onLinkClicked,
- selectionLayouts = selectionLayouts,
- onTextLaidOut = onTextLaidOut
- )
+ }
+ previous = item
}
- previous = item
}
}
- }
- is SemanticFlexContainer -> {
- Column(modifier = measuredModifier, verticalArrangement = Arrangement.Top) {
- SharedSemanticBlockStack(
- blocks = block.children,
- page = page,
- foreground = foreground,
- searchQuery = searchQuery,
- searchHighlight = searchHighlight,
- highlights = highlights,
- activeSelection = activeSelection,
- selectionHighlight = selectionHighlight,
- fallbackTextAlign = fallbackTextAlign,
- fallbackFontFamily = fallbackFontFamily,
- settings = settings,
- includeTrailingBottomMargin = true,
- onReaderTap = onReaderTap,
- onSelectionChange = onSelectionChange,
- onSelectionGestureActiveChange = onSelectionGestureActiveChange,
- onHighlightSelected = onHighlightSelected,
- onLinkClicked = onLinkClicked,
- selectionLayouts = selectionLayouts,
- imageContent = imageContent,
- onTextLaidOut = onTextLaidOut
- )
- }
- }
-
- is SemanticWrappingBlock -> {
- Column(modifier = measuredModifier, verticalArrangement = Arrangement.Top) {
- SharedSemanticBlockStack(
- blocks = listOf(block.floatedImage) + block.paragraphsToWrap,
- page = page,
- foreground = foreground,
- searchQuery = searchQuery,
- searchHighlight = searchHighlight,
- highlights = highlights,
- activeSelection = activeSelection,
- selectionHighlight = selectionHighlight,
- fallbackTextAlign = fallbackTextAlign,
- fallbackFontFamily = fallbackFontFamily,
- settings = settings,
- includeTrailingBottomMargin = true,
- onReaderTap = onReaderTap,
- onSelectionChange = onSelectionChange,
- onSelectionGestureActiveChange = onSelectionGestureActiveChange,
- onHighlightSelected = onHighlightSelected,
- onLinkClicked = onLinkClicked,
- selectionLayouts = selectionLayouts,
- imageContent = imageContent,
- onTextLaidOut = onTextLaidOut
- )
- }
- }
-
- is SemanticTable -> {
- Column(modifier = measuredModifier, verticalArrangement = Arrangement.Top) {
- block.rows.forEach { row ->
- Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
- row.forEach { cell ->
- Column(modifier = Modifier.weight(cell.colspan.toFloat().coerceAtLeast(1f))) {
+ is SemanticFlexContainer -> {
+ if (blockStyle.flexDirection == "row") {
+ Row(
+ modifier = contentModifier.fillMaxWidth(),
+ horizontalArrangement = blockStyle.sharedNativeFlexHorizontalArrangement(),
+ verticalAlignment = blockStyle.sharedNativeFlexVerticalAlignment()
+ ) {
+ block.children.forEach { child ->
+ Box(modifier = Modifier.weight(1f, fill = false)) {
SharedSemanticBlockStack(
- blocks = cell.content,
+ blocks = listOf(child),
page = page,
+ background = background,
foreground = foreground,
searchQuery = searchQuery,
searchHighlight = searchHighlight,
@@ -1597,30 +2105,548 @@ private fun SharedSemanticBlockView(
}
}
}
+ } else {
+ Column(
+ modifier = contentModifier.fillMaxWidth(),
+ verticalArrangement = blockStyle.sharedNativeFlexVerticalArrangement(),
+ horizontalAlignment = blockStyle.sharedNativeFlexHorizontalAlignment()
+ ) {
+ SharedSemanticBlockStack(
+ blocks = block.children,
+ page = page,
+ background = background,
+ foreground = foreground,
+ searchQuery = searchQuery,
+ searchHighlight = searchHighlight,
+ highlights = highlights,
+ activeSelection = activeSelection,
+ selectionHighlight = selectionHighlight,
+ fallbackTextAlign = fallbackTextAlign,
+ fallbackFontFamily = fallbackFontFamily,
+ settings = settings,
+ includeTrailingBottomMargin = true,
+ onReaderTap = onReaderTap,
+ onSelectionChange = onSelectionChange,
+ onSelectionGestureActiveChange = onSelectionGestureActiveChange,
+ onHighlightSelected = onHighlightSelected,
+ onLinkClicked = onLinkClicked,
+ selectionLayouts = selectionLayouts,
+ imageContent = imageContent,
+ onTextLaidOut = onTextLaidOut
+ )
+ }
}
}
- }
- is SemanticImage -> {
- SharedNativeImageBlock(
- block = block,
+ is SemanticWrappingBlock -> {
+ SharedNativeWrappingBlock(
+ block = block,
+ page = page,
+ modifier = contentModifier,
+ background = background,
+ foreground = foreground,
+ searchQuery = searchQuery,
+ searchHighlight = searchHighlight,
+ highlights = highlights,
+ activeSelection = activeSelection,
+ selectionHighlight = selectionHighlight,
+ fallbackTextAlign = fallbackTextAlign,
+ fallbackFontFamily = fallbackFontFamily,
+ settings = settings,
+ imageContent = imageContent,
+ onReaderTap = onReaderTap,
+ onSelectionChange = onSelectionChange,
+ onHighlightSelected = onHighlightSelected,
+ onLinkClicked = onLinkClicked
+ )
+ }
+
+ is SemanticTable -> {
+ Column(modifier = contentModifier, verticalArrangement = Arrangement.Top) {
+ block.rows.forEach { row ->
+ val hasFixedWidths = row.any { cell -> cell.style.blockStyle.width.isPositiveSpecified() }
+ val cellGap = if (blockStyle.borderCollapse == "collapse") {
+ 0.dp
+ } else {
+ blockStyle.borderSpacing.takeIfPositiveSpecified() ?: 8.dp
+ }
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(IntrinsicSize.Min),
+ horizontalArrangement = Arrangement.spacedBy(cellGap)
+ ) {
+ row.forEach { cell ->
+ val cellBlockStyle = cell.style.blockStyle.sharedNativeThemeBlockStyle(
+ isDarkTheme = settings.darkMode,
+ background = background,
+ foreground = foreground
+ )
+ val cellModifier = Modifier
+ .fillMaxHeight()
+ .then(
+ if (hasFixedWidths && cellBlockStyle.width.isPositiveSpecified()) {
+ Modifier.width(cellBlockStyle.width)
+ } else {
+ Modifier.weight(cell.colspan.toFloat().coerceAtLeast(1f), fill = true)
+ }
+ )
+ .sharedNativeCssBox(cellBlockStyle)
+ .padding(
+ start = cellBlockStyle.padding.left.safeDp(),
+ top = cellBlockStyle.padding.top.safeDp(),
+ end = cellBlockStyle.padding.right.safeDp(),
+ bottom = cellBlockStyle.padding.bottom.safeDp()
+ )
+ .sharedNativeVisibility(cellBlockStyle)
+ val cellAlignment = when (cell.style.paragraphStyle.textAlign) {
+ TextAlign.Center -> Alignment.CenterHorizontally
+ TextAlign.End,
+ TextAlign.Right -> Alignment.End
+ else -> Alignment.Start
+ }
+ SharedNativeCssBlockContainer(
+ modifier = cellModifier,
+ blockStyle = cellBlockStyle,
+ blockIndex = cell.content.firstOrNull()?.blockIndex ?: block.blockIndex,
+ imageContent = imageContent
+ ) { cellContentModifier ->
+ Column(
+ modifier = cellContentModifier,
+ horizontalAlignment = cellAlignment
+ ) {
+ SharedSemanticBlockStack(
+ blocks = cell.content,
+ page = page,
+ background = background,
+ foreground = foreground,
+ searchQuery = searchQuery,
+ searchHighlight = searchHighlight,
+ highlights = highlights,
+ activeSelection = activeSelection,
+ selectionHighlight = selectionHighlight,
+ fallbackTextAlign = fallbackTextAlign,
+ fallbackFontFamily = fallbackFontFamily,
+ settings = settings,
+ includeTrailingBottomMargin = true,
+ onReaderTap = onReaderTap,
+ onSelectionChange = onSelectionChange,
+ onSelectionGestureActiveChange = onSelectionGestureActiveChange,
+ onHighlightSelected = onHighlightSelected,
+ onLinkClicked = onLinkClicked,
+ selectionLayouts = selectionLayouts,
+ imageContent = imageContent,
+ onTextLaidOut = onTextLaidOut
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ is SemanticImage -> {
+ SharedNativeImageBlock(
+ block = block,
+ foreground = foreground,
+ settings = settings,
+ imageContent = imageContent,
+ modifier = contentModifier
+ )
+ }
+
+ is SemanticMath -> {
+ SharedNativeMathBlock(
+ block = block,
+ foreground = foreground,
+ settings = settings,
+ imageContent = imageContent,
+ modifier = contentModifier
+ )
+ }
+
+ is SemanticSpacer -> Spacer(contentModifier.height(if (block.isExplicitLineBreak) 8.dp else 16.dp))
+ }
+ }
+}
+
+@Composable
+private fun SharedNativeCssBlockContainer(
+ modifier: Modifier,
+ blockStyle: BlockStyle,
+ blockIndex: Int,
+ imageContent: (@Composable (SemanticImage, Modifier) -> Unit)?,
+ content: @Composable (Modifier) -> Unit
+) {
+ Box(modifier = modifier) {
+ val backgroundImage = remember(
+ blockStyle.backgroundImage,
+ blockStyle.objectFit,
+ blockStyle.objectPosition,
+ blockStyle.filter,
+ blockIndex
+ ) {
+ blockStyle.toSharedNativeBackgroundImage(blockIndex)
+ }
+ if (backgroundImage != null && imageContent != null) {
+ imageContent(backgroundImage, Modifier.matchParentSize())
+ }
+ content(Modifier.fillMaxWidth())
+ }
+}
+
+internal fun BlockStyle.toSharedNativeBackgroundImage(blockIndex: Int): SemanticImage? {
+ val path = sharedNativeBackgroundImagePath() ?: return null
+ return SemanticImage(
+ path = path,
+ altText = "",
+ intrinsicWidth = null,
+ intrinsicHeight = null,
+ style = CssStyle(
+ blockStyle = BlockStyle(
+ objectFit = objectFit,
+ objectPosition = objectPosition,
+ filter = filter
+ )
+ ),
+ elementId = null,
+ cfi = null,
+ blockIndex = blockIndex
+ )
+}
+
+internal fun BlockStyle.sharedNativeBackgroundImagePath(): String? {
+ val raw = backgroundImage?.trim()?.takeIf { it.isNotBlank() } ?: return null
+ if (raw.contains("gradient(", ignoreCase = true)) return null
+ val value = SharedNativeCssUrlRegex.find(raw)?.groupValues?.getOrNull(2)?.takeIf { it.isNotBlank() } ?: raw
+ return value
+ .trim()
+ .removeSurrounding("\"")
+ .removeSurrounding("'")
+ .takeIf { it.isNotBlank() && !it.equals("none", ignoreCase = true) }
+}
+
+private data class SharedNativeWrappedTextLine(
+ val text: AnnotatedString,
+ val layout: TextLayoutResult,
+ val topLeft: Offset
+)
+
+private data class SharedNativeWrappedParagraph(
+ val text: AnnotatedString,
+ val style: TextStyle
+)
+
+@Composable
+private fun SharedNativeWrappingBlock(
+ block: SemanticWrappingBlock,
+ page: ReaderPage,
+ modifier: Modifier,
+ background: Color,
+ foreground: Color,
+ searchQuery: String,
+ searchHighlight: Color,
+ highlights: List,
+ activeSelection: SharedNativeReaderTextSelection?,
+ selectionHighlight: Color,
+ fallbackTextAlign: TextAlign,
+ fallbackFontFamily: FontFamily,
+ settings: ReaderSettings,
+ imageContent: (@Composable (SemanticImage, Modifier) -> Unit)?,
+ onReaderTap: () -> Unit,
+ onSelectionChange: (SharedNativeReaderTextSelection?) -> Unit,
+ onHighlightSelected: (String) -> Unit,
+ onLinkClicked: (SharedNativeReaderLinkClick) -> Unit
+) {
+ val textMeasurer = rememberTextMeasurer()
+ val density = LocalDensity.current
+ var wrappedLines by remember(block, page.pageIndex) {
+ mutableStateOf>(emptyList())
+ }
+ val latestWrappedLines = rememberUpdatedState(wrappedLines)
+ val latestActiveSelection = rememberUpdatedState(activeSelection)
+ val fullText = remember(block.paragraphsToWrap) {
+ block.paragraphsToWrap.joinToString(separator = "\n\n") { it.text }
+ }
+ val wrappedParagraphs = mutableListOf()
+ for (paragraph in block.paragraphsToWrap) {
+ val textStyle = paragraph.renderedTextStyle(
+ settings = settings,
+ fallbackFontFamily = fallbackFontFamily,
+ fallbackTextAlign = fallbackTextAlign,
+ background = background,
+ foreground = foreground
+ )
+ wrappedParagraphs += SharedNativeWrappedParagraph(
+ text = paragraph.toAnnotatedString(
+ query = searchQuery,
+ highlightColor = searchHighlight,
+ highlights = highlights,
+ activeSelection = activeSelection,
+ selectionHighlight = selectionHighlight,
+ fallbackTextAlign = fallbackTextAlign,
+ blockFontSizeSp = textStyle.fontSize.value,
+ chapterIndex = page.chapterIndex,
+ pageIndex = page.pageIndex,
+ blockCfi = paragraph.cfi,
+ blockIndex = paragraph.blockIndex,
+ blockCharOffset = paragraph.startCharOffsetInSource,
+ background = background,
foreground = foreground,
- settings = settings,
- imageContent = imageContent,
- modifier = measuredModifier
+ isDarkTheme = settings.darkMode
+ ),
+ style = textStyle
+ )
+ }
+
+ Layout(
+ content = {
+ if (imageContent != null) {
+ imageContent(block.floatedImage, Modifier.fillMaxSize())
+ } else {
+ Text(
+ text = block.floatedImage.altText?.takeIf { it.isNotBlank() }
+ ?: block.floatedImage.path.substringAfterLast('/').substringAfterLast('\\'),
+ color = foreground.copy(alpha = 0.7f),
+ style = MaterialTheme.typography.bodySmall
+ )
+ }
+ },
+ modifier = modifier
+ .drawBehind {
+ wrappedLines.forEach { line ->
+ drawText(line.layout, topLeft = line.topLeft)
+ }
+ }
+ .pointerInput(block.blockIndex, wrappedLines) {
+ detectTapGestures(
+ onTap = { offset ->
+ latestWrappedLines.value.firstNotNullOfOrNull { line ->
+ line.hitAnnotation(offset)
+ }?.let { annotation ->
+ when (annotation.tag) {
+ ReaderNativeAnnotationUrl -> {
+ onSelectionChange(null)
+ onLinkClicked(
+ SharedNativeReaderLinkClick(
+ href = annotation.item,
+ chapterIndex = page.chapterIndex,
+ text = fullText
+ )
+ )
+ return@detectTapGestures
+ }
+
+ ReaderNativeAnnotationHighlight -> {
+ onSelectionChange(null)
+ onHighlightSelected(annotation.item)
+ return@detectTapGestures
+ }
+ }
+ }
+ if (latestActiveSelection.value == null) {
+ onReaderTap()
+ }
+ onSelectionChange(null)
+ }
+ )
+ }
+ ) { measurables, constraints ->
+ val maxWidthPx = constraints.maxWidth.coerceAtLeast(0)
+ val imageScale = settings.imageScale.coerceIn(0.5f, 2f)
+ val imageSize = sharedNativeImageRenderSizePxOrFallback(
+ block = block.floatedImage,
+ density = density,
+ maxWidthPx = maxWidthPx.toFloat(),
+ imageScale = imageScale,
+ settings = settings
+ )
+ val imageWidthPx = imageSize.first.roundToInt().coerceIn(0, maxWidthPx)
+ val imageHeightPx = imageSize.second.roundToInt().coerceAtLeast(0)
+ val imagePlaceable = if (imageWidthPx > 0 && imageHeightPx > 0 && measurables.isNotEmpty()) {
+ measurables.first().measure(Constraints.fixed(imageWidthPx, imageHeightPx))
+ } else {
+ null
+ }
+ val effectiveImageWidth = imagePlaceable?.width ?: 0
+ val effectiveImageHeight = imagePlaceable?.height ?: 0
+ val floatLeft = block.floatedImage.style.blockStyle.float != "right"
+
+ var currentY = 0f
+ val lines = mutableListOf()
+ wrappedParagraphs.forEachIndexed { paragraphIndex, paragraph ->
+ currentY = layoutSharedNativeWrappedLines(
+ text = paragraph.text,
+ style = paragraph.style,
+ textMeasurer = textMeasurer,
+ constraints = constraints,
+ currentY = currentY,
+ effectiveImageWidth = effectiveImageWidth,
+ effectiveImageHeight = effectiveImageHeight,
+ floatLeft = floatLeft,
+ lines = lines
)
+ if (paragraphIndex < block.paragraphsToWrap.lastIndex) {
+ currentY += block.paragraphsToWrap.sharedNativeCollapsedParagraphGapPx(
+ index = paragraphIndex,
+ settings = settings,
+ density = density
+ )
+ }
+ }
+ if (wrappedLines.sharedNativeWrappedLineSignature() != lines.sharedNativeWrappedLineSignature()) {
+ wrappedLines = lines
+ }
+ val height = maxOf(currentY.roundToInt(), effectiveImageHeight).coerceAtLeast(0)
+ layout(maxWidthPx, height) {
+ imagePlaceable?.let { placeable ->
+ val x = if (floatLeft) 0 else maxWidthPx - placeable.width
+ placeable.placeRelative(x = x.coerceAtLeast(0), y = 0)
+ }
+ }
+ }
+}
+
+private fun layoutSharedNativeWrappedLines(
+ text: AnnotatedString,
+ style: TextStyle,
+ textMeasurer: TextMeasurer,
+ constraints: Constraints,
+ currentY: Float,
+ effectiveImageWidth: Int,
+ effectiveImageHeight: Int,
+ floatLeft: Boolean,
+ lines: MutableList
+): Float {
+ var y = currentY
+ var textOffset = 0
+ while (textOffset < text.length) {
+ val isBesideImage = y < effectiveImageHeight
+ val currentMaxWidth = if (isBesideImage) {
+ (constraints.maxWidth - effectiveImageWidth).coerceAtLeast(0)
+ } else {
+ constraints.maxWidth
+ }
+ if (currentMaxWidth <= 0) {
+ if (isBesideImage) {
+ y = effectiveImageHeight.toFloat()
+ continue
+ }
+ break
}
- is SemanticMath -> {
- Text(
- text = block.altText ?: "Equation",
- color = foreground,
- modifier = measuredModifier,
- style = MaterialTheme.typography.bodyMedium
- )
+ val lineConstraints = constraints.copy(minWidth = 0, maxWidth = currentMaxWidth)
+ val remainingText = text.subSequence(textOffset, text.length)
+ val measuredRemaining = textMeasurer.measure(
+ text = remainingText,
+ style = style,
+ constraints = lineConstraints
+ )
+ val firstLineEndOffset = measuredRemaining.getLineEnd(0, visibleEnd = true)
+ if (firstLineEndOffset == 0 && remainingText.length > 0) {
+ textOffset++
+ continue
}
+ if (firstLineEndOffset == 0) break
- is SemanticSpacer -> Spacer(measuredModifier.height(if (block.isExplicitLineBreak) 8.dp else 16.dp))
+ val lineText = remainingText.subSequence(0, firstLineEndOffset)
+ val lineLayout = textMeasurer.measure(
+ text = lineText,
+ style = style,
+ constraints = lineConstraints
+ )
+ val x = if (isBesideImage && floatLeft) effectiveImageWidth.toFloat() else 0f
+ lines += SharedNativeWrappedTextLine(
+ text = lineText,
+ layout = lineLayout,
+ topLeft = Offset(x, y)
+ )
+ y += lineLayout.size.height
+ textOffset += firstLineEndOffset
+ while (textOffset < text.length && text.text[textOffset].isWhitespace()) {
+ textOffset++
+ }
+ }
+ return y
+}
+
+private data class SharedNativeWrappedAnnotation(
+ val tag: String,
+ val item: String
+)
+
+private fun List.sharedNativeWrappedLineSignature(): String {
+ return joinToString(separator = "|") { line ->
+ "${line.text.hashCode()}:${line.layout.size.width}x${line.layout.size.height}@" +
+ "${line.topLeft.x.roundToInt()},${line.topLeft.y.roundToInt()}"
+ }
+}
+
+private fun SharedNativeWrappedTextLine.hitAnnotation(offset: Offset): SharedNativeWrappedAnnotation? {
+ val local = Offset(offset.x - topLeft.x, offset.y - topLeft.y)
+ if (
+ local.x < 0f ||
+ local.y < 0f ||
+ local.x > layout.size.width.toFloat() ||
+ local.y > layout.size.height.toFloat()
+ ) {
+ return null
+ }
+ val textOffset = layout.getOffsetForPosition(local).coerceIn(0, text.length)
+ return text.stringAnnotationAt(ReaderNativeAnnotationUrl, textOffset)?.let { href ->
+ SharedNativeWrappedAnnotation(ReaderNativeAnnotationUrl, href)
+ } ?: text.stringAnnotationAt(ReaderNativeAnnotationHighlight, textOffset)?.let { highlightId ->
+ SharedNativeWrappedAnnotation(ReaderNativeAnnotationHighlight, highlightId)
+ }
+}
+
+private fun List.sharedNativeCollapsedParagraphGapPx(
+ index: Int,
+ settings: ReaderSettings,
+ density: Density
+): Float {
+ val current = getOrNull(index) ?: return 0f
+ val next = getOrNull(index + 1) ?: return 0f
+ val explicitGap = maxOf(
+ current.style.blockStyle.margin.bottom.safeDp(),
+ next.style.blockStyle.margin.top.safeDp()
+ )
+ return with(density) {
+ if (explicitGap != 0.dp) {
+ explicitGap.toPx()
+ } else {
+ (settings.fontSize * settings.paragraphSpacing).sp.toPx()
+ }
+ }
+}
+
+@Composable
+private fun SharedNativeMathBlock(
+ block: SemanticMath,
+ foreground: Color,
+ settings: ReaderSettings,
+ imageContent: (@Composable (SemanticImage, Modifier) -> Unit)?,
+ modifier: Modifier = Modifier
+) {
+ val image = remember(block.svgContent, block.svgWidth, block.svgHeight, block.svgViewBox, block.style) {
+ block.toSharedNativeSvgImage()
+ }
+ if (image != null && imageContent != null) {
+ SharedNativeImageBlock(
+ block = image,
+ foreground = foreground,
+ settings = settings,
+ imageContent = imageContent,
+ modifier = modifier
+ )
+ } else {
+ Text(
+ text = block.altText ?: "Equation",
+ color = foreground,
+ modifier = modifier,
+ style = MaterialTheme.typography.bodyMedium
+ )
}
}
@@ -1650,6 +2676,50 @@ private fun SharedNativeImageBlock(
}
}
+@OptIn(ExperimentalEncodingApi::class)
+private fun SemanticMath.toSharedNativeSvgImage(): SemanticImage? {
+ val svg = svgContent?.takeIf { it.isNotBlank() } ?: return null
+ val viewBoxSize = svgViewBox.sharedNativeSvgViewBoxSize()
+ return SemanticImage(
+ path = "data:image/svg+xml;base64,${Base64.Default.encode(svg.encodeToByteArray())}",
+ altText = altText ?: "SVG Image",
+ intrinsicWidth = svgWidth.sharedNativeSvgDimensionPx() ?: viewBoxSize?.first,
+ intrinsicHeight = svgHeight.sharedNativeSvgDimensionPx() ?: viewBoxSize?.second,
+ style = style,
+ elementId = elementId,
+ cfi = cfi,
+ blockIndex = blockIndex
+ )
+}
+
+private fun String?.sharedNativeSvgDimensionPx(): Float? {
+ val trimmed = this?.trim()?.takeIf { it.isNotBlank() } ?: return null
+ val numeric = trimmed
+ .removeSuffix("px")
+ .removeSuffix("PX")
+ .toFloatOrNull()
+ ?.takeIf { it > 0f }
+ if (numeric != null) return numeric
+ val pointValue = trimmed
+ .removeSuffix("pt")
+ .removeSuffix("PT")
+ .toFloatOrNull()
+ ?.takeIf { it > 0f }
+ return pointValue?.let { it * 1.3333334f }
+}
+
+private fun String?.sharedNativeSvgViewBoxSize(): Pair? {
+ val parts = this
+ ?.trim()
+ ?.split(Regex("[,\\s]+"))
+ ?.mapNotNull { it.toFloatOrNull() }
+ ?: return null
+ if (parts.size < 4) return null
+ val width = parts[2]
+ val height = parts[3]
+ return if (width > 0f && height > 0f) width to height else null
+}
+
private fun SemanticImage.imageContentAlignment(): Alignment {
val style = style.blockStyle
return when {
@@ -1711,6 +2781,25 @@ private fun sharedNativeImageRenderSizeDp(
maxWidth: Dp,
imageScale: Float
): Pair? {
+ val maxWidthPx = with(density) { maxWidth.toPx() }
+ return sharedNativeImageRenderSizePx(
+ block = block,
+ density = density,
+ maxWidthPx = maxWidthPx,
+ imageScale = imageScale
+ )?.let { (widthPx, heightPx) ->
+ with(density) {
+ widthPx.toDp() to heightPx.toDp()
+ }
+ }
+}
+
+private fun sharedNativeImageRenderSizePx(
+ block: SemanticImage,
+ density: Density,
+ maxWidthPx: Float,
+ imageScale: Float
+): Pair? {
val intrinsicWidth = block.intrinsicWidth
val intrinsicHeight = block.intrinsicHeight
if (intrinsicWidth == null || intrinsicHeight == null || intrinsicWidth <= 0f || intrinsicHeight <= 0f) {
@@ -1719,9 +2808,8 @@ private fun sharedNativeImageRenderSizeDp(
val style = block.style.blockStyle
val aspectRatio = intrinsicHeight / intrinsicWidth
- val maxWidthPx = with(density) { maxWidth.toPx() }
val baseWidthPx = with(density) {
- if (style.width.isPositiveSpecified()) style.width.toPx() else maxWidth.toPx()
+ if (style.width.isPositiveSpecified()) style.width.toPx() else maxWidthPx
}
var scaledWidthPx = baseWidthPx * imageScale
@@ -1730,9 +2818,34 @@ private fun sharedNativeImageRenderSizeDp(
}
scaledWidthPx = scaledWidthPx.coerceAtMost(maxWidthPx)
- return with(density) {
- scaledWidthPx.toDp() to (scaledWidthPx * aspectRatio).toDp()
- }
+ return scaledWidthPx to (scaledWidthPx * aspectRatio)
+}
+
+private fun sharedNativeImageRenderSizePxOrFallback(
+ block: SemanticImage,
+ density: Density,
+ maxWidthPx: Float,
+ imageScale: Float,
+ settings: ReaderSettings
+): Pair {
+ sharedNativeImageRenderSizePx(
+ block = block,
+ density = density,
+ maxWidthPx = maxWidthPx,
+ imageScale = imageScale
+ )?.let { return it }
+ val style = block.style.blockStyle
+ val widthPx = with(density) {
+ when {
+ style.width.isPositiveSpecified() -> style.width.toPx()
+ style.maxWidth.isPositiveSpecified() -> maxWidthPx.coerceAtMost(style.maxWidth.toPx())
+ else -> maxWidthPx
+ }
+ }.coerceAtLeast(1f)
+ val heightPx = with(density) {
+ (style.height.takeIfPositiveSpecified() ?: (settings.fontSize * 8f).sp.toDp()).toPx()
+ }.coerceAtLeast(1f)
+ return widthPx to heightPx
}
private data class SharedNativeContentFit(
@@ -1947,6 +3060,7 @@ private fun SharedSemanticTextView(
block: SemanticTextBlock,
page: ReaderPage,
modifier: Modifier,
+ background: Color,
foreground: Color,
searchQuery: String,
searchHighlight: Color,
@@ -1969,21 +3083,28 @@ private fun SharedSemanticTextView(
settings = settings,
fallbackFontFamily = fallbackFontFamily,
fallbackTextAlign = fallbackTextAlign,
- fontWeight = fontWeight
+ fontWeight = fontWeight,
+ background = background,
+ foreground = foreground
)
+ val textColor = textStyle.color.takeIf { it.isSpecified } ?: foreground
SharedNativeInteractiveText(
text = block.toAnnotatedString(
query = searchQuery,
- highlightColor = searchHighlight,
- highlights = highlights,
- activeSelection = activeSelection,
- selectionHighlight = selectionHighlight,
- blockFontSizeSp = textStyle.fontSize.value,
- chapterIndex = page.chapterIndex,
+ highlightColor = searchHighlight,
+ highlights = highlights,
+ activeSelection = activeSelection,
+ selectionHighlight = selectionHighlight,
+ fallbackTextAlign = fallbackTextAlign,
+ blockFontSizeSp = textStyle.fontSize.value,
+ chapterIndex = page.chapterIndex,
pageIndex = page.pageIndex,
blockCfi = block.cfi,
blockIndex = block.blockIndex,
- blockCharOffset = block.startCharOffsetInSource
+ blockCharOffset = block.startCharOffsetInSource,
+ background = background,
+ foreground = foreground,
+ isDarkTheme = settings.darkMode
),
page = page,
textBlock = SharedNativeTextBlockDescriptor(
@@ -1996,9 +3117,12 @@ private fun SharedSemanticTextView(
text = block.text
),
textStartOffset = block.startCharOffsetInSource,
- color = foreground,
+ color = textColor,
modifier = modifier,
- textAlign = block.style.paragraphStyle.textAlign.takeUnless { it == TextAlign.Unspecified } ?: fallbackTextAlign,
+ textAlign = resolveSharedReaderTextAlign(
+ cssTextAlign = block.style.paragraphStyle.textAlign,
+ fallbackTextAlign = fallbackTextAlign
+ ),
style = textStyle,
activeSelection = activeSelection,
onReaderTap = onReaderTap,
@@ -2023,7 +3147,9 @@ private fun SemanticTextBlock.renderedTextStyle(
settings: ReaderSettings,
fallbackFontFamily: FontFamily,
fallbackTextAlign: TextAlign,
- fontWeight: FontWeight? = null
+ fontWeight: FontWeight? = null,
+ background: Color,
+ foreground: Color
): TextStyle {
val fontSize = (style.fontSize.takeIfSpecified()
?: style.spanStyle.fontSize.takeIfSpecified())
@@ -2036,11 +3162,24 @@ private fun SemanticTextBlock.renderedTextStyle(
?.resolveLineHeightSp(fontSize.value)
?: (fontSize.value * settings.lineSpacing).sp
return MaterialTheme.typography.bodyLarge.copy(
+ color = style.spanStyle.color.sharedNativeThemeColor(
+ isDarkTheme = settings.darkMode,
+ isBackground = false,
+ background = background,
+ foreground = foreground
+ ).takeIf { it.isSpecified } ?: foreground,
fontSize = fontSize,
lineHeight = lineHeight,
fontFamily = fallbackFontFamily,
- fontWeight = fontWeight ?: if (this is SemanticHeader) FontWeight.Bold else MaterialTheme.typography.bodyLarge.fontWeight,
- textAlign = style.paragraphStyle.textAlign.takeUnless { it == TextAlign.Unspecified } ?: fallbackTextAlign
+ fontWeight = fontWeight
+ ?: style.spanStyle.fontWeight
+ ?: if (this is SemanticHeader) FontWeight.Bold else MaterialTheme.typography.bodyLarge.fontWeight,
+ fontStyle = style.spanStyle.fontStyle ?: MaterialTheme.typography.bodyLarge.fontStyle,
+ textDecoration = style.spanStyle.textDecoration ?: MaterialTheme.typography.bodyLarge.textDecoration,
+ textAlign = resolveSharedReaderTextAlign(
+ cssTextAlign = style.paragraphStyle.textAlign,
+ fallbackTextAlign = fallbackTextAlign
+ )
).withAndroidPaginationTextMetrics()
}
@@ -2061,22 +3200,56 @@ private fun SemanticTextBlock.toAnnotatedString(
highlights: List,
activeSelection: SharedNativeReaderTextSelection?,
selectionHighlight: Color,
+ fallbackTextAlign: TextAlign,
blockFontSizeSp: Float,
chapterIndex: Int,
pageIndex: Int,
blockCfi: String?,
blockIndex: Int,
- blockCharOffset: Int
+ blockCharOffset: Int,
+ background: Color,
+ foreground: Color,
+ isDarkTheme: Boolean
): AnnotatedString {
val normalized = query.trim()
return buildAnnotatedString {
- append(text)
+ withStyle(sharedNativeParagraphStyle(fallbackTextAlign)) {
+ append(text)
+ }
spans.forEach { span ->
val start = span.start.coerceIn(0, text.length)
val end = span.end.coerceIn(start, text.length)
if (start < end) {
- addStyle(span.style.toRenderedSpanStyle(blockFontSizeSp), start, end)
- span.linkHref?.takeIf { it.isNotBlank() }?.let { href ->
+ val linkHref = span.linkHref?.takeIf { it.isNotBlank() }
+ val spanStyle = span.style.toRenderedSpanStyle(blockFontSizeSp, span.tag)
+ .sharedNativeThemeSpanStyle(
+ isDarkTheme = isDarkTheme,
+ background = background,
+ foreground = foreground
+ )
+ .let { renderedStyle ->
+ if (linkHref != null) {
+ renderedStyle.withSharedNativeReaderLinkStyle(
+ isDarkTheme = isDarkTheme,
+ themeBackgroundColor = background,
+ themeTextColor = foreground
+ )
+ } else {
+ renderedStyle
+ }
+ }
+ addStyle(
+ spanStyle,
+ start,
+ end
+ )
+ addSharedNativeWordSpacing(
+ text = text,
+ start = start,
+ end = end,
+ wordSpacing = span.style.wordSpacing
+ )
+ linkHref?.let { href ->
addStringAnnotation(ReaderNativeAnnotationUrl, href, start, end)
}
}
@@ -2115,6 +3288,48 @@ private fun SemanticTextBlock.toAnnotatedString(
}
}
+private fun SemanticTextBlock.sharedNativeParagraphStyle(fallbackTextAlign: TextAlign): ParagraphStyle {
+ return ParagraphStyle(
+ textAlign = resolveSharedReaderTextAlign(
+ cssTextAlign = style.paragraphStyle.textAlign,
+ fallbackTextAlign = fallbackTextAlign
+ ),
+ textIndent = style.paragraphStyle.textIndent,
+ lineBreak = LineBreak.Paragraph,
+ hyphens = style.sharedNativeHyphens()
+ )
+}
+
+private fun AnnotatedString.Builder.addSharedNativeWordSpacing(
+ text: String,
+ start: Int,
+ end: Int,
+ wordSpacing: TextUnit
+) {
+ if (!wordSpacing.isSpecified || wordSpacing.value == 0f) return
+ for (index in start until end) {
+ if (text[index] == ' ') {
+ addStyle(SpanStyle(letterSpacing = wordSpacing), index, index + 1)
+ }
+ }
+}
+
+private fun CssStyle.sharedNativeHyphens(): Hyphens {
+ return if (hyphens == "auto") Hyphens.Auto else Hyphens.None
+}
+
+internal fun BlockStyle.isSharedNativeVisibilityHidden(): Boolean {
+ return visibility == "hidden"
+}
+
+private fun Modifier.sharedNativeVisibility(blockStyle: BlockStyle): Modifier {
+ return if (blockStyle.isSharedNativeVisibilityHidden()) {
+ graphicsLayer(alpha = 0f)
+ } else {
+ this
+ }
+}
+
private fun TextUnit.takeIfSpecified(): TextUnit? = if (isSpecified) this else null
private fun Color.blendWith(other: Color, foregroundWeight: Float): Color {
@@ -2142,13 +3357,348 @@ private fun TextUnit.resolveLineHeightSp(fontSizeSp: Float): TextUnit {
}
}
-private fun CssStyle.toRenderedSpanStyle(parentFontSizeSp: Float): SpanStyle {
+private fun Modifier.sharedNativeCssBox(blockStyle: BlockStyle): Modifier = drawBehind {
+ val borderTop = blockStyle.borderTop
+ val borderRight = blockStyle.borderRight
+ val borderBottom = blockStyle.borderBottom
+ val borderLeft = blockStyle.borderLeft
+ val topWidth = borderTop?.width?.toPx() ?: 0f
+ val rightWidth = borderRight?.width?.toPx() ?: 0f
+ val bottomWidth = borderBottom?.width?.toPx() ?: 0f
+ val leftWidth = borderLeft?.width?.toPx() ?: 0f
+
+ val topLeftRadius = blockStyle.borderTopLeftRadius.toPx()
+ val topRightRadius = blockStyle.borderTopRightRadius.toPx()
+ val bottomRightRadius = blockStyle.borderBottomRightRadius.toPx()
+ val bottomLeftRadius = blockStyle.borderBottomLeftRadius.toPx()
+
+ if (blockStyle.backgroundColor.isSpecified && blockStyle.backgroundColor != Color.Transparent) {
+ val backgroundPath = Path().apply {
+ addRoundRect(
+ RoundRect(
+ rect = size.toRect(),
+ topLeft = CornerRadius(topLeftRadius, topLeftRadius),
+ topRight = CornerRadius(topRightRadius, topRightRadius),
+ bottomRight = CornerRadius(bottomRightRadius, bottomRightRadius),
+ bottomLeft = CornerRadius(bottomLeftRadius, bottomLeftRadius)
+ )
+ )
+ }
+ drawPath(backgroundPath, color = blockStyle.backgroundColor, style = Fill)
+ }
+
+ fun pathEffect(style: String?, width: Float): PathEffect? {
+ return when (style) {
+ "dashed" -> PathEffect.dashPathEffect(floatArrayOf(width * 3f, width * 2f), 0f)
+ "dotted" -> PathEffect.dashPathEffect(floatArrayOf(width, width), 0f)
+ else -> null
+ }
+ }
+
+ if (topWidth > 0f && borderTop != null) {
+ drawLine(
+ color = borderTop.color,
+ start = Offset(if (topLeftRadius > 0f) topLeftRadius else 0f, topWidth / 2f),
+ end = Offset(if (topRightRadius > 0f) size.width - topRightRadius else size.width, topWidth / 2f),
+ strokeWidth = topWidth,
+ pathEffect = pathEffect(borderTop.style, topWidth)
+ )
+ }
+
+ if (bottomWidth > 0f && borderBottom != null) {
+ val y = size.height - (bottomWidth / 2f)
+ drawLine(
+ color = borderBottom.color,
+ start = Offset(if (bottomLeftRadius > 0f) bottomLeftRadius else 0f, y),
+ end = Offset(if (bottomRightRadius > 0f) size.width - bottomRightRadius else size.width, y),
+ strokeWidth = bottomWidth,
+ pathEffect = pathEffect(borderBottom.style, bottomWidth)
+ )
+ }
+
+ if (leftWidth > 0f && borderLeft != null) {
+ drawLine(
+ color = borderLeft.color,
+ start = Offset(leftWidth / 2f, if (topLeftRadius > 0f) topLeftRadius else 0f),
+ end = Offset(leftWidth / 2f, if (bottomLeftRadius > 0f) size.height - bottomLeftRadius else size.height),
+ strokeWidth = leftWidth,
+ pathEffect = pathEffect(borderLeft.style, leftWidth)
+ )
+ }
+
+ if (rightWidth > 0f && borderRight != null) {
+ val x = size.width - (rightWidth / 2f)
+ drawLine(
+ color = borderRight.color,
+ start = Offset(x, if (topRightRadius > 0f) topRightRadius else 0f),
+ end = Offset(x, if (bottomRightRadius > 0f) size.height - bottomRightRadius else size.height),
+ strokeWidth = rightWidth,
+ pathEffect = pathEffect(borderRight.style, rightWidth)
+ )
+ }
+
+ if (topLeftRadius > 0f && topWidth > 0f && leftWidth > 0f && borderTop != null) {
+ drawArc(
+ color = borderTop.color,
+ startAngle = 180f,
+ sweepAngle = 90f,
+ useCenter = false,
+ topLeft = Offset(leftWidth / 2f, topWidth / 2f),
+ size = Size(topLeftRadius * 2 - leftWidth, topLeftRadius * 2 - topWidth),
+ style = Stroke(width = topWidth)
+ )
+ }
+
+ if (topRightRadius > 0f && topWidth > 0f && rightWidth > 0f && borderTop != null) {
+ drawArc(
+ color = borderTop.color,
+ startAngle = 270f,
+ sweepAngle = 90f,
+ useCenter = false,
+ topLeft = Offset(size.width - (topRightRadius * 2) + (rightWidth / 2f), topWidth / 2f),
+ size = Size(topRightRadius * 2 - rightWidth, topRightRadius * 2 - topWidth),
+ style = Stroke(width = topWidth)
+ )
+ }
+
+ if (bottomRightRadius > 0f && bottomWidth > 0f && rightWidth > 0f && borderBottom != null) {
+ drawArc(
+ color = borderBottom.color,
+ startAngle = 0f,
+ sweepAngle = 90f,
+ useCenter = false,
+ topLeft = Offset(
+ size.width - (bottomRightRadius * 2) + (rightWidth / 2f),
+ size.height - (bottomRightRadius * 2) + (bottomWidth / 2f)
+ ),
+ size = Size(bottomRightRadius * 2 - rightWidth, bottomRightRadius * 2 - bottomWidth),
+ style = Stroke(width = bottomWidth)
+ )
+ }
+
+ if (bottomLeftRadius > 0f && bottomWidth > 0f && leftWidth > 0f && borderBottom != null) {
+ drawArc(
+ color = borderBottom.color,
+ startAngle = 90f,
+ sweepAngle = 90f,
+ useCenter = false,
+ topLeft = Offset(leftWidth / 2f, size.height - (bottomLeftRadius * 2) + (bottomWidth / 2f)),
+ size = Size(bottomLeftRadius * 2 - leftWidth, bottomLeftRadius * 2 - bottomWidth),
+ style = Stroke(width = bottomWidth)
+ )
+ }
+}
+
+private fun BlockStyle.sharedNativeThemeBlockStyle(
+ isDarkTheme: Boolean,
+ background: Color,
+ foreground: Color
+): BlockStyle {
+ return copy(
+ backgroundColor = backgroundColor.sharedNativeThemeColor(
+ isDarkTheme = isDarkTheme,
+ isBackground = true,
+ background = background,
+ foreground = foreground
+ ),
+ borderTop = borderTop?.sharedNativeThemeBorderStyle(isDarkTheme, background, foreground),
+ borderRight = borderRight?.sharedNativeThemeBorderStyle(isDarkTheme, background, foreground),
+ borderBottom = borderBottom?.sharedNativeThemeBorderStyle(isDarkTheme, background, foreground),
+ borderLeft = borderLeft?.sharedNativeThemeBorderStyle(isDarkTheme, background, foreground)
+ )
+}
+
+private fun BorderStyle.sharedNativeThemeBorderStyle(
+ isDarkTheme: Boolean,
+ background: Color,
+ foreground: Color
+): BorderStyle {
+ return copy(
+ color = color.sharedNativeThemeColor(
+ isDarkTheme = isDarkTheme,
+ isBackground = false,
+ background = background,
+ foreground = foreground
+ )
+ )
+}
+
+private fun SpanStyle.sharedNativeThemeSpanStyle(
+ isDarkTheme: Boolean,
+ background: Color,
+ foreground: Color
+): SpanStyle {
+ return copy(
+ color = color.sharedNativeThemeColor(
+ isDarkTheme = isDarkTheme,
+ isBackground = false,
+ background = background,
+ foreground = foreground
+ ),
+ background = this.background.sharedNativeThemeColor(
+ isDarkTheme = isDarkTheme,
+ isBackground = true,
+ background = background,
+ foreground = foreground
+ )
+ )
+}
+
+internal fun sharedNativeReaderLinkSpanStyle(
+ isDarkTheme: Boolean,
+ themeBackgroundColor: Color,
+ themeTextColor: Color,
+ existingDecoration: TextDecoration? = null
+): SpanStyle {
+ val background = themeBackgroundColor.takeIf { it.isSpecified }
+ ?: if (isDarkTheme) Color.Black else Color.White
+ val text = themeTextColor.takeIf { it.isSpecified }
+ ?: if (isDarkTheme) Color.White else Color.Black
+ val linkColor = sharedNativeReaderLinkColorForTheme(isDarkTheme, background, text)
+ val backgroundAlpha = if (background.sharedNativeSafeLuminance() < 0.45f) 0.24f else 0.16f
+ return SpanStyle(
+ color = linkColor,
+ background = linkColor.copy(alpha = backgroundAlpha),
+ textDecoration = existingDecoration.withSharedNativeUnderline()
+ )
+}
+
+private fun SpanStyle.withSharedNativeReaderLinkStyle(
+ isDarkTheme: Boolean,
+ themeBackgroundColor: Color,
+ themeTextColor: Color
+): SpanStyle {
+ val linkStyle = sharedNativeReaderLinkSpanStyle(
+ isDarkTheme = isDarkTheme,
+ themeBackgroundColor = themeBackgroundColor,
+ themeTextColor = themeTextColor,
+ existingDecoration = textDecoration
+ )
+ return copy(
+ color = linkStyle.color,
+ background = linkStyle.background,
+ textDecoration = linkStyle.textDecoration
+ )
+}
+
+private fun sharedNativeReaderLinkColorForTheme(
+ isDarkTheme: Boolean,
+ background: Color,
+ text: Color
+): Color {
+ val backgroundLuminance = background.sharedNativeSafeLuminance()
+ val textLuminance = text.sharedNativeSafeLuminance()
+ val candidates = if (isDarkTheme || backgroundLuminance < 0.45f) {
+ listOf(
+ Color(0xFF7DD3FC),
+ Color(0xFF5EEAD4),
+ Color(0xFFA5B4FC),
+ Color(0xFFFDE68A),
+ Color.White
+ )
+ } else {
+ listOf(
+ Color(0xFF005FCC),
+ Color(0xFF006D75),
+ Color(0xFF7A1E52),
+ Color(0xFF4A148C),
+ Color(0xFF111827)
+ )
+ }
+ return candidates.firstOrNull { candidate ->
+ candidate.sharedNativeContrastRatio(background) >= 4.5f &&
+ abs(candidate.sharedNativeSafeLuminance() - textLuminance) >= 0.08f
+ } ?: candidates.maxByOrNull { it.sharedNativeContrastRatio(background) }
+ ?: if (isDarkTheme) Color(0xFF7DD3FC) else Color(0xFF005FCC)
+}
+
+private fun TextDecoration?.withSharedNativeUnderline(): TextDecoration {
+ val current = this ?: TextDecoration.None
+ val decorations = mutableListOf()
+ if (current.contains(TextDecoration.LineThrough)) decorations += TextDecoration.LineThrough
+ decorations += TextDecoration.Underline
+ return TextDecoration.combine(decorations)
+}
+
+private fun Color.sharedNativeContrastRatio(other: Color): Float {
+ val lighter = maxOf(sharedNativeSafeLuminance(), other.sharedNativeSafeLuminance())
+ val darker = minOf(sharedNativeSafeLuminance(), other.sharedNativeSafeLuminance())
+ return (lighter + 0.05f) / (darker + 0.05f)
+}
+
+private fun Color.sharedNativeSafeLuminance(): Float {
+ return if (isSpecified) luminance() else 0f
+}
+
+private fun BlockStyle.sharedNativeFlexHorizontalArrangement(): Arrangement.Horizontal {
+ return when (justifyContent) {
+ "center" -> Arrangement.Center
+ "flex-end" -> Arrangement.End
+ "space-between" -> Arrangement.SpaceBetween
+ "space-around" -> Arrangement.SpaceAround
+ else -> Arrangement.Start
+ }
+}
+
+private fun BlockStyle.sharedNativeFlexVerticalArrangement(): Arrangement.Vertical {
+ return when (justifyContent) {
+ "center" -> Arrangement.Center
+ "flex-end" -> Arrangement.Bottom
+ "space-between" -> Arrangement.SpaceBetween
+ "space-around" -> Arrangement.SpaceAround
+ else -> Arrangement.Top
+ }
+}
+
+private fun BlockStyle.sharedNativeFlexVerticalAlignment(): Alignment.Vertical {
+ return when (alignItems) {
+ "center" -> Alignment.CenterVertically
+ "flex-end" -> Alignment.Bottom
+ else -> Alignment.Top
+ }
+}
+
+private fun BlockStyle.sharedNativeFlexHorizontalAlignment(): Alignment.Horizontal {
+ return when (alignItems) {
+ "center" -> Alignment.CenterHorizontally
+ "flex-end" -> Alignment.End
+ else -> Alignment.Start
+ }
+}
+
+private fun Color.sharedNativeThemeColor(
+ isDarkTheme: Boolean,
+ isBackground: Boolean,
+ background: Color,
+ foreground: Color
+): Color {
+ return CssParser.adaptColorForTheme(
+ color = this,
+ isDarkTheme = isDarkTheme,
+ isBackground = isBackground,
+ themeBackground = background,
+ themeText = foreground
+ )
+}
+
+private fun CssStyle.toRenderedSpanStyle(parentFontSizeSp: Float, tag: String? = null): SpanStyle {
val resolvedFontSize = (spanStyle.fontSize.takeIfSpecified() ?: fontSize.takeIfSpecified())
?.resolveFontSizeSp(parentFontSizeSp)
- return if (resolvedFontSize == null) {
- spanStyle
- } else {
- spanStyle.copy(fontSize = resolvedFontSize)
+ return spanStyle.copy(
+ fontSize = resolvedFontSize ?: spanStyle.fontSize,
+ fontFeatureSettings = resolveSharedReaderFontFeatureSettings(
+ existingSettings = spanStyle.fontFeatureSettings,
+ fontVariantNumeric = fontVariantNumeric
+ ),
+ baselineShift = spanStyle.baselineShift ?: sharedNativeBaselineShift(tag, verticalAlign)
+ )
+}
+
+private fun sharedNativeBaselineShift(tag: String?, verticalAlign: String?): BaselineShift? {
+ return when (tag?.lowercase() ?: verticalAlign?.lowercase()) {
+ "sub", "subscript" -> BaselineShift.Subscript
+ "sup", "super", "superscript" -> BaselineShift.Superscript
+ else -> null
}
}
@@ -3265,6 +4815,67 @@ private fun headerScale(level: Int): Float {
}
}
+private fun sharedNativeListMarker(index: Int, isOrdered: Boolean, listStyleType: String?): String {
+ val normalized = listStyleType?.lowercase().orEmpty()
+ if (!isOrdered) {
+ return when (normalized) {
+ "circle" -> "\u25e6"
+ "square" -> "\u25aa"
+ "none" -> ""
+ else -> "\u2022"
+ }
+ }
+
+ val number = index + 1
+ return when (normalized) {
+ "lower-alpha", "lower-latin" -> "${sharedNativeAlphaMarker(number, uppercase = false)}."
+ "upper-alpha", "upper-latin" -> "${sharedNativeAlphaMarker(number, uppercase = true)}."
+ "lower-roman" -> "${sharedNativeRomanMarker(number).lowercase()}."
+ "upper-roman" -> "${sharedNativeRomanMarker(number)}."
+ "none" -> ""
+ else -> "$number."
+ }
+}
+
+private fun sharedNativeAlphaMarker(number: Int, uppercase: Boolean): String {
+ var value = number.coerceAtLeast(1)
+ val chars = ArrayDeque()
+ while (value > 0) {
+ value -= 1
+ chars.addFirst(('a'.code + (value % 26)).toChar())
+ value /= 26
+ }
+ val marker = chars.joinToString("")
+ return if (uppercase) marker.uppercase() else marker
+}
+
+private fun sharedNativeRomanMarker(number: Int): String {
+ var value = number.coerceIn(1, 3999)
+ val parts = listOf(
+ 1000 to "M",
+ 900 to "CM",
+ 500 to "D",
+ 400 to "CD",
+ 100 to "C",
+ 90 to "XC",
+ 50 to "L",
+ 40 to "XL",
+ 10 to "X",
+ 9 to "IX",
+ 5 to "V",
+ 4 to "IV",
+ 1 to "I"
+ )
+ return buildString {
+ parts.forEach { (amount, marker) ->
+ while (value >= amount) {
+ append(marker)
+ value -= amount
+ }
+ }
+ }
+}
+
private fun Dp.safeDp(): Dp = if (isSpecified) this else 0.dp
private fun Dp.isPositiveSpecified(): Boolean = isSpecified && this > 0.dp
@@ -3323,3 +4934,4 @@ private const val EpubCutoffLogTag = SharedEpubCutoffDiagnosticsTag
private const val EpubPageFitTailBlockCount = 4
private const val SharedNativeListItemMarkerAreaWidthDp = 32
private const val SharedNativeListItemMarkerEndPaddingDp = 8
+private val SharedNativeCssUrlRegex = Regex("""url\(\s*(['"]?)(.*?)\1\s*\)""", RegexOption.IGNORE_CASE)
diff --git a/shared/src/commonMain/kotlin/com/aryan/reader/shared/ui/SharedPdfAnnotationUi.kt b/shared/src/commonMain/kotlin/com/aryan/reader/shared/ui/SharedPdfAnnotationUi.kt
index 259d0bc..ec98bcc 100644
--- a/shared/src/commonMain/kotlin/com/aryan/reader/shared/ui/SharedPdfAnnotationUi.kt
+++ b/shared/src/commonMain/kotlin/com/aryan/reader/shared/ui/SharedPdfAnnotationUi.kt
@@ -63,17 +63,20 @@ import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.geometry.RoundRect
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Path
+import androidx.compose.ui.graphics.PathMeasure
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.graphics.StrokeJoin
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.drawscope.Fill
import androidx.compose.ui.graphics.drawscope.Stroke
+import androidx.compose.ui.graphics.drawscope.clipPath
import androidx.compose.ui.graphics.drawscope.translate
import androidx.compose.ui.graphics.luminance
import androidx.compose.ui.graphics.toArgb
@@ -700,13 +703,11 @@ private fun SharedPdfAnnotationToolSettingsPanel(
penPalette.ifEmpty { SharedPdfAnnotationDefaults.penPalette }
}
val selectedPaletteIndex = remember(activePalette, selectedColor, isHighlighter) {
- activePalette.indexOfFirst { paletteColor ->
- if (isHighlighter) {
- Color(paletteColor).copy(alpha = 1f) == Color(selectedColor).copy(alpha = 1f)
- } else {
- paletteColor == selectedColor
- }
- }
+ sharedPdfSettingsSelectedPaletteIndex(
+ activePalette = activePalette,
+ selectedColor = selectedColor,
+ matchRgbOnly = isHighlighter
+ )
}
fun colorPickerPalette(): List {
@@ -810,6 +811,8 @@ private fun SharedPdfAnnotationToolSettingsPanel(
}
}
+ Spacer(Modifier.height(16.dp))
+
if (isHighlighter) {
Row(
modifier = Modifier.fillMaxWidth(),
@@ -978,6 +981,20 @@ private fun SharedPdfAnnotationToolSettingsPanel(
}
}
+internal fun sharedPdfSettingsSelectedPaletteIndex(
+ activePalette: List,
+ selectedColor: Int,
+ matchRgbOnly: Boolean
+): Int {
+ return activePalette.indexOfFirst { paletteColor ->
+ if (matchRgbOnly) {
+ (paletteColor and 0x00FFFFFF) == (selectedColor and 0x00FFFFFF)
+ } else {
+ paletteColor == selectedColor
+ }
+ }
+}
+
@Composable
private fun SharedPdfHighlighterPalettePreview(
colors: List,
@@ -1076,10 +1093,7 @@ private fun SharedPdfStyledPropertySlider(
thumbColor: Color,
activeColor: Color
) {
- val displayValue = remember(value, valueRange) {
- val fraction = (value - valueRange.start) / (valueRange.endInclusive - valueRange.start)
- (fraction * 100f).roundToInt().coerceIn(1, 100)
- }
+ val displayValue = remember(value, valueRange) { sharedPdfSettingsDisplayPercent(value, valueRange) }
val onePercentDelta = (valueRange.endInclusive - valueRange.start) / 100f
val canDecrease = value > valueRange.start + 0.0001f
val canIncrease = value < valueRange.endInclusive - 0.0001f
@@ -1097,7 +1111,7 @@ private fun SharedPdfStyledPropertySlider(
contentAlignment = Alignment.Center
) {
Text(
- text = "-",
+ text = "\u2014",
color = if (canDecrease) Color.White else Color.White.copy(alpha = 0.3f),
fontSize = 18.sp,
fontWeight = FontWeight.Bold
@@ -1137,36 +1151,66 @@ private fun SharedPdfStyledPropertySlider(
}
}
},
- track = { sliderState ->
- val range = sliderState.valueRange.endInclusive - sliderState.valueRange.start
- val fraction = if (range == 0f) {
- 0f
- } else {
- ((sliderState.value - sliderState.valueRange.start) / range).coerceIn(0f, 1f)
- }
- val activeTrackColor = when {
- isOpacity -> activeColor.copy(alpha = 1f)
- activeColor.luminance() < 0.18f -> Color.White.copy(alpha = 0.88f)
- else -> activeColor.copy(alpha = 0.95f)
- }
- val inactiveTrackColor = if (isOpacity) {
- Color.White.copy(alpha = 0.24f)
- } else {
- trackColor.copy(alpha = 0.85f)
- }
- Box(
+ track = { _ ->
+ Canvas(
modifier = Modifier
.fillMaxWidth()
- .height(4.dp)
- .background(inactiveTrackColor, RoundedCornerShape(2.dp)),
- contentAlignment = Alignment.CenterStart
+ .height(16.dp)
) {
- Box(
- modifier = Modifier
- .fillMaxWidth(fraction)
- .height(4.dp)
- .background(activeTrackColor, RoundedCornerShape(2.dp))
- )
+ val trackHeight = size.height
+ val cornerRadius = CornerRadius(trackHeight / 2f)
+ if (isOpacity) {
+ drawRoundRect(
+ color = Color.Gray,
+ size = size,
+ cornerRadius = cornerRadius
+ )
+ val roundedClipPath = Path().apply {
+ addRoundRect(
+ RoundRect(
+ rect = Rect(Offset.Zero, size),
+ cornerRadius = cornerRadius
+ )
+ )
+ }
+ clipPath(roundedClipPath) {
+ val boxSize = 12f
+ val columns = (size.width / boxSize).toInt() + 1
+ val rows = (size.height / boxSize).toInt() + 1
+ for (column in 0 until columns) {
+ for (row in 0 until rows) {
+ drawRect(
+ color = if ((column + row) % 2 == 0) {
+ Color(0xFF555555)
+ } else {
+ Color(0xFF333333)
+ },
+ topLeft = Offset(column * boxSize, row * boxSize),
+ size = Size(boxSize, boxSize)
+ )
+ }
+ }
+ drawRect(color = activeColor)
+ }
+ } else {
+ drawRoundRect(
+ color = trackColor.copy(alpha = 0.5f),
+ size = size,
+ cornerRadius = cornerRadius
+ )
+ val dotRadius = 1.5.dp.toPx()
+ val padding = trackHeight / 2f
+ val availableWidth = size.width - (padding * 2f)
+ val dotCount = 8
+ val spacing = availableWidth / (dotCount - 1)
+ for (index in 0 until dotCount) {
+ drawCircle(
+ color = Color.White.copy(alpha = 0.2f),
+ radius = dotRadius,
+ center = Offset(padding + (index * spacing), size.height / 2f)
+ )
+ }
+ }
}
}
)
@@ -1192,6 +1236,15 @@ private fun SharedPdfStyledPropertySlider(
}
}
+internal fun sharedPdfSettingsDisplayPercent(
+ value: Float,
+ valueRange: ClosedFloatingPointRange
+): Int {
+ val range = valueRange.endInclusive - valueRange.start
+ val fraction = if (range == 0f) 0f else (value - valueRange.start) / range
+ return (fraction * 100f).roundToInt().coerceIn(1, 100)
+}
+
@Composable
private fun SharedPdfInkColorPalette(
colors: List,
@@ -2174,7 +2227,7 @@ private fun SharedPdfPenIcon(
val animatedInkColor by animateColorAsState(targetValue = inkColor, label = "shared_ink_color")
val inkProgress by animateFloatAsState(
targetValue = if (isSelected) 1f else 0f,
- animationSpec = tween(durationMillis = 450, easing = LinearEasing),
+ animationSpec = tween(durationMillis = 600, easing = LinearEasing),
label = "shared_ink_progress"
)
@@ -2211,12 +2264,18 @@ private fun SharedPdfPenIcon(
}
if (inkProgress > 0.01f) {
+ val tipY = when (tool) {
+ PdfInkTool.HIGHLIGHTER -> topPadding
+ PdfInkTool.HIGHLIGHTER_ROUND -> topPadding + tipHeight * 0.15f
+ else -> topPadding
+ }
drawInkPreview(
tool = tool,
color = animatedInkColor,
progress = inkProgress,
- startPoint = Offset(size.width / 2f, topPadding - 1f),
- strokeWidth = strokeWidth
+ startPoint = Offset(size.width / 2f, tipY),
+ strokeWidth = strokeWidth,
+ isStraight = showHighlighterSnap
)
}
if (showHighlighterSnap && tool.isDesktopHighlighter) {
@@ -2545,23 +2604,59 @@ private fun DrawScope.drawInkPreview(
color: Color,
progress: Float,
startPoint: Offset,
- strokeWidth: Float
+ strokeWidth: Float,
+ isStraight: Boolean = false
) {
+ val x = startPoint.x
+ val y = startPoint.y - 2f
val path = Path().apply {
- moveTo(startPoint.x, startPoint.y)
+ moveTo(x, y)
if (tool.isHighlighter) {
- val waveWidth = 46f
- cubicTo(startPoint.x + waveWidth * 0.35f, startPoint.y - 12f, startPoint.x + waveWidth * 0.65f, startPoint.y + 12f, startPoint.x + waveWidth, startPoint.y)
+ val waveWidth = 70f
+ if (isStraight) {
+ lineTo(x + waveWidth, y)
+ } else {
+ val amplitude = 20f
+ cubicTo(
+ x + waveWidth * 0.35f,
+ y - amplitude,
+ x + waveWidth * 0.65f,
+ y + amplitude,
+ x + waveWidth,
+ y
+ )
+ }
} else {
- cubicTo(startPoint.x + 22f, startPoint.y - 24f, startPoint.x - 22f, startPoint.y - 52f, startPoint.x - 9f, startPoint.y - 28f)
- cubicTo(startPoint.x - 3f, startPoint.y - 8f, startPoint.x + 32f, startPoint.y - 16f, startPoint.x + 44f, startPoint.y - 34f)
+ cubicTo(
+ x + 16f,
+ y - 18f,
+ x - 18f,
+ y - 34f,
+ x - 7f,
+ y - 21f
+ )
+ cubicTo(
+ x - 2f,
+ y - 5f,
+ x + 22f,
+ y - 11f,
+ x + 31f,
+ y - 26f
+ )
}
}
+ val revealProgress = sharedPdfInkPreviewRevealProgress(progress)
+ val pathMeasure = PathMeasure()
+ pathMeasure.setPath(path, false)
+ val revealedPath = Path()
+ val targetLength = pathMeasure.length * revealProgress
+ if (targetLength <= 0f || !pathMeasure.getSegment(0f, targetLength, revealedPath, true)) return
+
val width = SharedPdfInkRenderer.effectiveStrokeWidthPx(strokeWidth, pageWidthPx = 700f)
.coerceIn(if (tool.isHighlighter) 5f else 1.2f, if (tool.isHighlighter) 16f else 5f)
drawPath(
- path = path,
- color = color.copy(alpha = color.alpha * progress),
+ path = revealedPath,
+ color = color,
style = Stroke(
width = width,
cap = if (tool == PdfInkTool.HIGHLIGHTER) StrokeCap.Butt else StrokeCap.Round,
@@ -2571,6 +2666,10 @@ private fun DrawScope.drawInkPreview(
)
}
+internal fun sharedPdfInkPreviewRevealProgress(progress: Float): Float {
+ return progress.coerceIn(0f, 1f)
+}
+
private val PdfInkTool.isDesktopPenTool: Boolean
get() = this == PdfInkTool.FOUNTAIN_PEN || this == PdfInkTool.PEN || this == PdfInkTool.PENCIL
diff --git a/shared/src/commonMain/kotlin/com/aryan/reader/shared/ui/SharedReaderChrome.kt b/shared/src/commonMain/kotlin/com/aryan/reader/shared/ui/SharedReaderChrome.kt
index e0f60de..238832f 100644
--- a/shared/src/commonMain/kotlin/com/aryan/reader/shared/ui/SharedReaderChrome.kt
+++ b/shared/src/commonMain/kotlin/com/aryan/reader/shared/ui/SharedReaderChrome.kt
@@ -242,6 +242,7 @@ fun SharedReaderScreen(
readerTexturePreviewContent: (@Composable (String, Modifier) -> Unit)? = null,
readerCustomTextureIds: List = emptyList(),
onImportReaderTexture: ((ReaderSettings) -> ReaderSettings?)? = null,
+ preferNativeVerticalReader: Boolean = false,
bottomChromeExtraContent: @Composable ColumnScope.() -> Unit = {},
useDetachedChromeLayer: Boolean = true,
useDetachedPanelLayer: Boolean = true,
@@ -597,6 +598,21 @@ fun SharedReaderScreen(
ttsRequestId = ttsRequestId
)
val renderPlan = if (settings.readingMode == ReaderReadingMode.VERTICAL) {
+ if (preferNativeVerticalReader) {
+ ReaderContentRenderPlan.NativeVerticalPages(
+ book = readerState.book,
+ pages = readerState.pages,
+ currentPageIndex = readerState.currentPageIndex,
+ settings = settings,
+ searchQuery = session.searchQuery,
+ searchOptions = session.searchOptions,
+ highlightPalette = highlightPalette,
+ background = background,
+ foreground = foreground,
+ navigationTarget = navigationTarget,
+ highlights = session.highlights
+ )
+ } else {
val lastChapterIndex = readerState.book.chapters.lastIndex
val activeChapterIndex = if (lastChapterIndex >= 0) {
readerState.currentPage?.chapterIndex?.takeIf { it in 0..lastChapterIndex }
@@ -761,6 +777,7 @@ fun SharedReaderScreen(
navigationTarget = navigationTarget,
highlights = session.highlights
)
+ }
} else {
ReaderContentRenderPlan.NativePaginatedPages(
visiblePages = readerState.visiblePages,
diff --git a/shared/src/commonTest/kotlin/com/aryan/reader/shared/ReaderAppearanceModelsTest.kt b/shared/src/commonTest/kotlin/com/aryan/reader/shared/ReaderAppearanceModelsTest.kt
index fa8f6f1..97bca5d 100644
--- a/shared/src/commonTest/kotlin/com/aryan/reader/shared/ReaderAppearanceModelsTest.kt
+++ b/shared/src/commonTest/kotlin/com/aryan/reader/shared/ReaderAppearanceModelsTest.kt
@@ -78,9 +78,9 @@ class ReaderAppearanceModelsTest {
@Test
fun `pdf highlighter defaults follow android pdf highlight slots`() {
- val expectedPdfColors = SharedPdfAndroidHighlightColors.palette.take(4)
+ val expectedPdfColors = SharedPdfAndroidHighlightColors.palette.take(5)
- assertEquals(4, SharedPdfHighlighterPalette.MaxColors)
+ assertEquals(5, SharedPdfHighlighterPalette.MaxColors)
assertEquals(expectedPdfColors, SharedPdfHighlighterPalette.defaultColors)
assertEquals(expectedPdfColors[0], SharedPdfAnnotationDefaults.configFor(PdfInkTool.HIGHLIGHTER).colorArgb)
assertEquals(expectedPdfColors[1], SharedPdfAnnotationDefaults.configFor(PdfInkTool.HIGHLIGHTER_ROUND).colorArgb)
@@ -88,7 +88,7 @@ class ReaderAppearanceModelsTest {
val custom = SharedPdfHighlighterPalette(
colors = expectedPdfColors + listOf(0xFFFF00FF.toInt())
).sanitized()
- assertEquals(4, custom.colors.size)
+ assertEquals(5, custom.colors.size)
assertEquals(expectedPdfColors, custom.colors)
}
diff --git a/shared/src/commonTest/kotlin/com/aryan/reader/shared/ui/SharedNativeVerticalReaderFlowTest.kt b/shared/src/commonTest/kotlin/com/aryan/reader/shared/ui/SharedNativeVerticalReaderFlowTest.kt
new file mode 100644
index 0000000..4ee7ec6
--- /dev/null
+++ b/shared/src/commonTest/kotlin/com/aryan/reader/shared/ui/SharedNativeVerticalReaderFlowTest.kt
@@ -0,0 +1,324 @@
+package com.aryan.reader.shared.ui
+
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.isSpecified
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.text.style.TextDecoration
+import androidx.compose.ui.unit.dp
+import com.aryan.reader.paginatedreader.BlockStyle
+import com.aryan.reader.paginatedreader.BorderStyle
+import com.aryan.reader.paginatedreader.CssStyle
+import com.aryan.reader.paginatedreader.SemanticImage
+import com.aryan.reader.paginatedreader.SemanticMath
+import com.aryan.reader.paginatedreader.SemanticParagraph
+import com.aryan.reader.paginatedreader.SemanticWrappingBlock
+import com.aryan.reader.shared.reader.ReaderPage
+import com.aryan.reader.shared.reader.SharedEpubBook
+import com.aryan.reader.shared.reader.SharedEpubChapter
+import com.aryan.reader.shared.reader.resolveSharedReaderFontFeatureSettings
+import com.aryan.reader.shared.reader.resolveSharedReaderTextAlign
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertIs
+import kotlin.test.assertNotNull
+
+class SharedNativeVerticalReaderFlowTest {
+ @Test
+ fun `shared native visibility hides css hidden blocks`() {
+ assertEquals(true, BlockStyle(visibility = "hidden").isSharedNativeVisibilityHidden())
+ assertEquals(false, BlockStyle(visibility = "visible").isSharedNativeVisibilityHidden())
+ assertEquals(false, BlockStyle().isSharedNativeVisibilityHidden())
+ }
+
+ @Test
+ fun `shared native background image extracts usable image paths`() {
+ assertEquals(
+ "images/paper.png",
+ BlockStyle(backgroundImage = """url("images/paper.png")""").sharedNativeBackgroundImagePath()
+ )
+ assertEquals(
+ "images/paper.png",
+ BlockStyle(backgroundImage = """url( "images/paper.png" )""").sharedNativeBackgroundImagePath()
+ )
+ assertEquals(
+ "data:image/png;base64,abc",
+ BlockStyle(backgroundImage = "data:image/png;base64,abc").sharedNativeBackgroundImagePath()
+ )
+ assertEquals(
+ null,
+ BlockStyle(backgroundImage = "NONE").sharedNativeBackgroundImagePath()
+ )
+ assertEquals(
+ null,
+ BlockStyle(backgroundImage = "linear-gradient(red, blue)").sharedNativeBackgroundImagePath()
+ )
+ }
+
+ @Test
+ fun `shared native background image keeps fit and position styles`() {
+ val image = BlockStyle(
+ backgroundImage = """url("images/paper.png")""",
+ objectFit = "cover",
+ objectPosition = "left top",
+ filter = "invert(100%)"
+ ).toSharedNativeBackgroundImage(blockIndex = 9)
+
+ assertNotNull(image)
+ assertEquals("images/paper.png", image.path)
+ assertEquals("", image.altText)
+ assertEquals("cover", image.style.blockStyle.objectFit)
+ assertEquals("left top", image.style.blockStyle.objectPosition)
+ assertEquals("invert(100%)", image.style.blockStyle.filter)
+ assertEquals(9, image.blockIndex)
+ }
+
+ @Test
+ fun `shared native link style makes links visible`() {
+ val style = sharedNativeReaderLinkSpanStyle(
+ isDarkTheme = false,
+ themeBackgroundColor = Color.White,
+ themeTextColor = Color.Black
+ )
+
+ assertEquals(true, style.color.isSpecified)
+ assertEquals(true, style.background.isSpecified)
+ assertEquals(true, style.textDecoration?.contains(TextDecoration.Underline))
+ }
+
+ @Test
+ fun `css justify downgrades unless shared setting explicitly forces alignment`() {
+ assertEquals(
+ TextAlign.Left,
+ resolveSharedReaderTextAlign(
+ cssTextAlign = TextAlign.Justify,
+ fallbackTextAlign = TextAlign.Start
+ )
+ )
+ assertEquals(
+ TextAlign.Justify,
+ resolveSharedReaderTextAlign(
+ cssTextAlign = TextAlign.Justify,
+ fallbackTextAlign = TextAlign.Justify
+ )
+ )
+ assertEquals(
+ TextAlign.Right,
+ resolveSharedReaderTextAlign(
+ cssTextAlign = TextAlign.Center,
+ fallbackTextAlign = TextAlign.Right
+ )
+ )
+ }
+
+ @Test
+ fun `css numeric font variants map to shared native font features`() {
+ assertEquals(
+ """"tnum" on, "zero" on""",
+ resolveSharedReaderFontFeatureSettings(
+ existingSettings = null,
+ fontVariantNumeric = "tabular-nums slashed-zero"
+ )
+ )
+ assertEquals(
+ """"smcp" on, "onum" on, "pnum" on""",
+ resolveSharedReaderFontFeatureSettings(
+ existingSettings = """"smcp" on""",
+ fontVariantNumeric = "oldstyle-nums proportional-nums"
+ )
+ )
+ }
+
+ @Test
+ fun `semantic images and styles stay in native vertical flow`() {
+ val paragraphStyle = CssStyle(
+ blockStyle = BlockStyle(
+ backgroundColor = Color(0xFFEFEFEF),
+ borderTop = BorderStyle(width = 1.dp, color = Color.Red)
+ )
+ )
+ val paragraph = SemanticParagraph(
+ text = "Styled paragraph",
+ spans = emptyList(),
+ style = paragraphStyle,
+ elementId = "p1",
+ cfi = "/4/2",
+ startCharOffsetInSource = 0,
+ blockIndex = 1
+ )
+ val imageStyle = CssStyle(
+ blockStyle = BlockStyle(
+ width = 120.dp,
+ height = 80.dp,
+ objectFit = "cover"
+ )
+ )
+ val image = SemanticImage(
+ path = "data:image/png;base64,iVBORw0KGgo=",
+ altText = "cover",
+ intrinsicWidth = 120f,
+ intrinsicHeight = 80f,
+ style = imageStyle,
+ elementId = "img1",
+ cfi = "/4/4",
+ blockIndex = 2
+ )
+ val book = SharedEpubBook(
+ id = "book",
+ fileName = "book.epub",
+ title = "Book",
+ chapters = listOf(
+ SharedEpubChapter(
+ id = "chapter_0",
+ title = "Chapter",
+ plainText = "Styled paragraph",
+ semanticBlocks = listOf(paragraph, image)
+ )
+ )
+ )
+
+ val items = buildSharedNativeVerticalFlowItems(book, pages = emptyList())
+
+ assertEquals(
+ listOf(SharedNativeVerticalFlowItemKind.BLOCK, SharedNativeVerticalFlowItemKind.BLOCK),
+ items.map { it.kind }
+ )
+ val imageItem = items.single { it.block is SemanticImage }
+ val flowImage = assertIs(imageItem.block)
+ assertEquals("data:image/png;base64,iVBORw0KGgo=", flowImage.path)
+ assertEquals("cover", flowImage.style.blockStyle.objectFit)
+ assertEquals(2, imageItem.page.semanticBlocks.single().blockIndex)
+ val paragraphItem = assertNotNull(items.first().block)
+ assertEquals(Color(0xFFEFEFEF), paragraphItem.style.blockStyle.backgroundColor)
+ assertEquals(Color.Red, paragraphItem.style.blockStyle.borderTop?.color)
+ }
+
+ @Test
+ fun `svg math blocks stay in native vertical flow`() {
+ val math = SemanticMath(
+ svgContent = """""",
+ altText = "Equation",
+ svgWidth = "24",
+ svgHeight = "12",
+ svgViewBox = "0 0 24 12",
+ isFromMathJax = false,
+ style = CssStyle(),
+ elementId = "eq1",
+ cfi = "/4/6",
+ blockIndex = 3
+ )
+ val book = SharedEpubBook(
+ id = "book",
+ fileName = "book.epub",
+ title = "Book",
+ chapters = listOf(
+ SharedEpubChapter(
+ id = "chapter_0",
+ title = "Chapter",
+ plainText = "Equation",
+ semanticBlocks = listOf(math)
+ )
+ )
+ )
+
+ val items = buildSharedNativeVerticalFlowItems(book, pages = emptyList())
+
+ assertEquals(1, items.size)
+ assertEquals(SharedNativeVerticalFlowItemKind.BLOCK, items.single().kind)
+ val flowMath = assertIs(items.single().block)
+ assertEquals("""""", flowMath.svgContent)
+ assertEquals(3, items.single().page.semanticBlocks.single().blockIndex)
+ }
+
+ @Test
+ fun `floated image wrapping blocks stay grouped in native vertical flow`() {
+ val image = SemanticImage(
+ path = "cover.png",
+ altText = "Cover",
+ intrinsicWidth = 120f,
+ intrinsicHeight = 180f,
+ style = CssStyle(blockStyle = BlockStyle(float = "left")),
+ elementId = "img1",
+ cfi = "/4/2",
+ blockIndex = 4
+ )
+ val firstParagraph = SemanticParagraph(
+ text = "Wrapped first",
+ spans = emptyList(),
+ style = CssStyle(),
+ elementId = "p1",
+ cfi = "/4/4",
+ startCharOffsetInSource = 0,
+ blockIndex = 5
+ )
+ val secondParagraph = SemanticParagraph(
+ text = "Wrapped second",
+ spans = emptyList(),
+ style = CssStyle(),
+ elementId = "p2",
+ cfi = "/4/6",
+ startCharOffsetInSource = 14,
+ blockIndex = 6
+ )
+ val wrappingBlock = SemanticWrappingBlock(
+ floatedImage = image,
+ paragraphsToWrap = listOf(firstParagraph, secondParagraph),
+ style = CssStyle(),
+ elementId = "wrap1",
+ cfi = "/4/2",
+ blockIndex = 7
+ )
+ val book = SharedEpubBook(
+ id = "book",
+ fileName = "book.epub",
+ title = "Book",
+ chapters = listOf(
+ SharedEpubChapter(
+ id = "chapter_0",
+ title = "Chapter",
+ plainText = "Wrapped first\nWrapped second",
+ semanticBlocks = listOf(wrappingBlock)
+ )
+ )
+ )
+
+ val items = buildSharedNativeVerticalFlowItems(book, pages = emptyList())
+
+ assertEquals(1, items.size)
+ assertEquals(SharedNativeVerticalFlowItemKind.BLOCK, items.single().kind)
+ val flowWrappingBlock = assertIs(items.single().block)
+ assertEquals("cover.png", flowWrappingBlock.floatedImage.path)
+ assertEquals(listOf(5, 6), flowWrappingBlock.paragraphsToWrap.map { it.blockIndex })
+ assertEquals(listOf(7), items.single().page.semanticBlocks.map { it.blockIndex })
+ assertEquals("Wrapped first\nWrapped second", items.single().page.text)
+ }
+
+ @Test
+ fun `plain text pages are used when a chapter has no semantic blocks`() {
+ val page = ReaderPage(
+ pageIndex = 0,
+ chapterIndex = 0,
+ chapterTitle = "Chapter",
+ text = "Plain text",
+ startOffset = 0,
+ endOffset = 10
+ )
+ val book = SharedEpubBook(
+ id = "book",
+ fileName = "book.txt",
+ title = "Book",
+ chapters = listOf(
+ SharedEpubChapter(
+ id = "chapter_0",
+ title = "Chapter",
+ plainText = "Plain text"
+ )
+ )
+ )
+
+ val items = buildSharedNativeVerticalFlowItems(book, pages = listOf(page))
+
+ assertEquals(1, items.size)
+ assertEquals(SharedNativeVerticalFlowItemKind.TEXT_PAGE, items.single().kind)
+ assertEquals(page, items.single().page)
+ }
+}
diff --git a/shared/src/commonTest/kotlin/com/aryan/reader/shared/ui/SharedPdfAnnotationUiTest.kt b/shared/src/commonTest/kotlin/com/aryan/reader/shared/ui/SharedPdfAnnotationUiTest.kt
index 4922df4..1af0158 100644
--- a/shared/src/commonTest/kotlin/com/aryan/reader/shared/ui/SharedPdfAnnotationUiTest.kt
+++ b/shared/src/commonTest/kotlin/com/aryan/reader/shared/ui/SharedPdfAnnotationUiTest.kt
@@ -74,4 +74,43 @@ class SharedPdfAnnotationUiTest {
sharedPdfInteractionDockItems(tools = listOf(PdfInkTool.TEXT))
)
}
+
+ @Test
+ fun `tool settings palette matches highlighter colors by rgb`() {
+ val paletteColor = Color(0xFFFFEB3B).copy(alpha = 0.55f).toArgb()
+ val selectedColor = Color(0xFFFFEB3B).copy(alpha = 0.25f).toArgb()
+
+ assertEquals(
+ 0,
+ sharedPdfSettingsSelectedPaletteIndex(
+ activePalette = listOf(paletteColor),
+ selectedColor = selectedColor,
+ matchRgbOnly = true
+ )
+ )
+ assertEquals(
+ -1,
+ sharedPdfSettingsSelectedPaletteIndex(
+ activePalette = listOf(paletteColor),
+ selectedColor = selectedColor,
+ matchRgbOnly = false
+ )
+ )
+ }
+
+ @Test
+ fun `tool settings slider display percent clamps like android popup`() {
+ val range = 0.01f..0.06f
+
+ assertEquals(1, sharedPdfSettingsDisplayPercent(0.0f, range))
+ assertEquals(50, sharedPdfSettingsDisplayPercent(0.035f, range))
+ assertEquals(100, sharedPdfSettingsDisplayPercent(0.10f, range))
+ }
+
+ @Test
+ fun `ink preview reveal progress clamps to animation range`() {
+ assertEquals(0f, sharedPdfInkPreviewRevealProgress(-0.5f))
+ assertEquals(0.45f, sharedPdfInkPreviewRevealProgress(0.45f))
+ assertEquals(1f, sharedPdfInkPreviewRevealProgress(1.5f))
+ }
}
diff --git a/shared/src/desktopMain/kotlin/com/aryan/reader/shared/ui/DesktopEpubNativeImage.desktop.kt b/shared/src/desktopMain/kotlin/com/aryan/reader/shared/ui/DesktopEpubNativeImage.desktop.kt
index 64e4e2f..c5bd2e3 100644
--- a/shared/src/desktopMain/kotlin/com/aryan/reader/shared/ui/DesktopEpubNativeImage.desktop.kt
+++ b/shared/src/desktopMain/kotlin/com/aryan/reader/shared/ui/DesktopEpubNativeImage.desktop.kt
@@ -1,6 +1,7 @@
package com.aryan.reader.shared.ui
import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Spacer
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@@ -9,6 +10,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.ColorMatrix
@@ -18,11 +20,17 @@ import androidx.compose.ui.layout.ContentScale
import com.aryan.reader.paginatedreader.SemanticImage
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
+import org.jetbrains.skia.Data
+import org.jetbrains.skia.Surface
+import org.jetbrains.skia.svg.SVGDOM
+import org.jetbrains.skia.svg.SVGLengthContext
+import org.jetbrains.skia.Color as SkiaColor
import org.jetbrains.skia.Image as SkiaImage
import java.io.ByteArrayInputStream
import java.io.File
import java.util.Base64
import javax.imageio.ImageIO
+import kotlin.math.roundToInt
@Composable
fun DesktopEpubNativeImage(
@@ -42,14 +50,20 @@ fun DesktopEpubNativeImage(
}
val currentBitmap = bitmap
+ val isDecorative = image.altText != null && image.altText.isBlank()
if (currentBitmap != null) {
Image(
bitmap = currentBitmap,
- contentDescription = image.altText ?: "Image from EPUB",
+ contentDescription = image.altText
+ ?.takeIf { it.isNotBlank() }
+ ?: if (isDecorative) null else "Image from EPUB",
modifier = modifier,
- contentScale = ContentScale.Fit,
+ contentScale = image.readerImageContentScale(),
+ alignment = desktopEpubImageContentAlignment(image.style.blockStyle.objectPosition),
colorFilter = image.readerImageColorFilter()
)
+ } else if (isDecorative) {
+ Spacer(modifier = modifier)
} else {
Text(
text = image.altText?.takeIf { it.isNotBlank() } ?: image.path.substringAfterLast('/').substringAfterLast('\\'),
@@ -108,6 +122,9 @@ private object DesktopEpubNativeImageCache {
private fun decode(source: DesktopEpubImageSource): ImageBitmap? {
val bytes = source.bytes() ?: return null
+ if (source.isSvg) {
+ decodeSvg(bytes)?.let { return it }
+ }
runCatching {
ImageIO.read(ByteArrayInputStream(bytes))?.toComposeImageBitmap()
}.getOrNull()?.let { return it }
@@ -116,19 +133,60 @@ private object DesktopEpubNativeImageCache {
SkiaImage.makeFromEncoded(bytes).toComposeImageBitmap()
}.getOrNull()
}
+
+ private fun decodeSvg(bytes: ByteArray): ImageBitmap? {
+ var data: Data? = null
+ var dom: SVGDOM? = null
+ var surface: Surface? = null
+ return runCatching {
+ data = Data.makeFromBytes(bytes)
+ dom = SVGDOM(data!!)
+ val root = dom?.root
+ val viewBox = root?.viewBox
+ val intrinsic = root?.getIntrinsicSize(SVGLengthContext(DefaultSvgViewportPx, DefaultSvgViewportPx))
+ val width = (intrinsic?.x?.takeIf { it.isFinite() && it > 0f }
+ ?: viewBox?.width?.takeIf { it.isFinite() && it > 0f }
+ ?: DefaultSvgViewportPx)
+ .roundToInt()
+ .coerceIn(1, MaxSvgRasterDimensionPx)
+ val height = (intrinsic?.y?.takeIf { it.isFinite() && it > 0f }
+ ?: viewBox?.height?.takeIf { it.isFinite() && it > 0f }
+ ?: DefaultSvgViewportPx)
+ .roundToInt()
+ .coerceIn(1, MaxSvgRasterDimensionPx)
+ dom?.setContainerSize(width.toFloat(), height.toFloat())
+ surface = Surface.makeRasterN32Premul(width, height)
+ val canvas = surface!!.canvas
+ canvas.clear(SkiaColor.TRANSPARENT)
+ dom?.render(canvas)
+ surface!!.makeImageSnapshot().toComposeImageBitmap()
+ }.getOrNull().also {
+ surface?.close()
+ dom?.close()
+ data?.close()
+ }
+ }
}
private sealed class DesktopEpubImageSource(
val key: String,
val length: Long?,
- val lastModified: Long?
+ val lastModified: Long?,
+ val mimeType: String?
) {
abstract fun bytes(): ByteArray?
+ val isSvg: Boolean
+ get() = mimeType.equals("image/svg+xml", ignoreCase = true) ||
+ key.substringBefore('?').substringBefore('#').endsWith(".svg", ignoreCase = true)
+
data class FileSource(private val file: File) : DesktopEpubImageSource(
key = file.absolutePath,
length = file.length(),
- lastModified = file.lastModified()
+ lastModified = file.lastModified(),
+ mimeType = file.extension
+ .takeIf { it.equals("svg", ignoreCase = true) }
+ ?.let { "image/svg+xml" }
) {
override fun bytes(): ByteArray? = runCatching { file.readBytes() }.getOrNull()
}
@@ -136,7 +194,11 @@ private sealed class DesktopEpubImageSource(
data class DataUriSource(private val path: String) : DesktopEpubImageSource(
key = path,
length = path.length.toLong(),
- lastModified = null
+ lastModified = null,
+ mimeType = path.substringAfter("data:", missingDelimiterValue = "")
+ .substringBefore(';')
+ .substringBefore(',')
+ .takeIf { it.isNotBlank() }
) {
override fun bytes(): ByteArray? {
val marker = "base64,"
@@ -159,6 +221,9 @@ private sealed class DesktopEpubImageSource(
}
}
+private const val DefaultSvgViewportPx = 512f
+private const val MaxSvgRasterDimensionPx = 4096
+
private fun SemanticImage.readerImageColorFilter(): ColorFilter? {
if (style.blockStyle.filter != "invert(100%)") return null
return ColorFilter.colorMatrix(
@@ -172,3 +237,90 @@ private fun SemanticImage.readerImageColorFilter(): ColorFilter? {
)
)
}
+
+private fun SemanticImage.readerImageContentScale(): ContentScale {
+ return when (style.blockStyle.objectFit) {
+ "cover" -> ContentScale.Crop
+ "fill" -> ContentScale.FillBounds
+ "contain", "scale-down" -> ContentScale.Fit
+ else -> ContentScale.Fit
+ }
+}
+
+internal fun desktopEpubImageContentAlignment(objectPosition: String?): Alignment {
+ val tokens = objectPosition
+ ?.lowercase()
+ ?.split(Regex("\\s+"))
+ ?.map { it.trim() }
+ ?.filter { it.isNotBlank() }
+ ?: return Alignment.Center
+
+ val orderedHorizontal = tokens.getOrNull(0)?.toDesktopObjectPositionHorizontal()
+ val orderedVertical = tokens.getOrNull(1)?.toDesktopObjectPositionVertical()
+ val horizontal = orderedHorizontal
+ ?: tokens.firstNotNullOfOrNull { it.toDesktopObjectPositionHorizontalKeyword() }
+ ?: DesktopObjectPositionAxis.CENTER
+ val vertical = orderedVertical
+ ?: tokens.firstNotNullOfOrNull { it.toDesktopObjectPositionVerticalKeyword() }
+ ?: DesktopObjectPositionAxis.CENTER
+
+ return when (vertical) {
+ DesktopObjectPositionAxis.START -> when (horizontal) {
+ DesktopObjectPositionAxis.START -> Alignment.TopStart
+ DesktopObjectPositionAxis.END -> Alignment.TopEnd
+ else -> Alignment.TopCenter
+ }
+ DesktopObjectPositionAxis.END -> when (horizontal) {
+ DesktopObjectPositionAxis.START -> Alignment.BottomStart
+ DesktopObjectPositionAxis.END -> Alignment.BottomEnd
+ else -> Alignment.BottomCenter
+ }
+ DesktopObjectPositionAxis.CENTER -> when (horizontal) {
+ DesktopObjectPositionAxis.START -> Alignment.CenterStart
+ DesktopObjectPositionAxis.END -> Alignment.CenterEnd
+ else -> Alignment.Center
+ }
+ }
+}
+
+private enum class DesktopObjectPositionAxis {
+ START,
+ CENTER,
+ END
+}
+
+private fun String.toDesktopObjectPositionHorizontal(): DesktopObjectPositionAxis? {
+ return when (this) {
+ "left", "0%" -> DesktopObjectPositionAxis.START
+ "center", "50%" -> DesktopObjectPositionAxis.CENTER
+ "right", "100%" -> DesktopObjectPositionAxis.END
+ else -> null
+ }
+}
+
+private fun String.toDesktopObjectPositionVertical(): DesktopObjectPositionAxis? {
+ return when (this) {
+ "top", "0%" -> DesktopObjectPositionAxis.START
+ "center", "50%" -> DesktopObjectPositionAxis.CENTER
+ "bottom", "100%" -> DesktopObjectPositionAxis.END
+ else -> null
+ }
+}
+
+private fun String.toDesktopObjectPositionHorizontalKeyword(): DesktopObjectPositionAxis? {
+ return when (this) {
+ "left" -> DesktopObjectPositionAxis.START
+ "center" -> DesktopObjectPositionAxis.CENTER
+ "right" -> DesktopObjectPositionAxis.END
+ else -> null
+ }
+}
+
+private fun String.toDesktopObjectPositionVerticalKeyword(): DesktopObjectPositionAxis? {
+ return when (this) {
+ "top" -> DesktopObjectPositionAxis.START
+ "center" -> DesktopObjectPositionAxis.CENTER
+ "bottom" -> DesktopObjectPositionAxis.END
+ else -> null
+ }
+}
diff --git a/shared/src/desktopTest/kotlin/com/aryan/reader/shared/reader/SharedMeasuredEpubPaginatorTest.kt b/shared/src/desktopTest/kotlin/com/aryan/reader/shared/reader/SharedMeasuredEpubPaginatorTest.kt
index 012a363..e056b20 100644
--- a/shared/src/desktopTest/kotlin/com/aryan/reader/shared/reader/SharedMeasuredEpubPaginatorTest.kt
+++ b/shared/src/desktopTest/kotlin/com/aryan/reader/shared/reader/SharedMeasuredEpubPaginatorTest.kt
@@ -1,6 +1,8 @@
package com.aryan.reader.shared.reader
import androidx.compose.ui.text.ParagraphStyle
+import androidx.compose.ui.text.style.Hyphens
+import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextIndent
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@@ -138,6 +140,33 @@ class SharedMeasuredEpubPaginatorTest {
assertEquals(0.dp, split.second.style.blockStyle.margin.top)
}
+ @Test
+ fun `measurement paragraph style keeps css indent hyphenation and android justify rule`() {
+ val paragraph = SemanticParagraph(
+ text = "Indented paragraph",
+ spans = emptyList(),
+ style = CssStyle(
+ paragraphStyle = ParagraphStyle(
+ textAlign = TextAlign.Justify,
+ textIndent = TextIndent(firstLine = 20.sp, restLine = 4.sp)
+ ),
+ hyphens = "auto"
+ ),
+ elementId = null,
+ cfi = null,
+ startCharOffsetInSource = 0,
+ blockIndex = 1
+ )
+
+ val defaultAlignStyle = paragraph.toMeasurementParagraphStyleForPagination(TextAlign.Start)
+ val forcedJustifyStyle = paragraph.toMeasurementParagraphStyleForPagination(TextAlign.Justify)
+
+ assertEquals(TextAlign.Left, defaultAlignStyle.textAlign)
+ assertEquals(TextAlign.Justify, forcedJustifyStyle.textAlign)
+ assertEquals(TextIndent(firstLine = 20.sp, restLine = 4.sp), defaultAlignStyle.textIndent)
+ assertEquals(Hyphens.Auto, defaultAlignStyle.hyphens)
+ }
+
@Test
fun `pagination stack collapses adjacent margins and can ignore trailing bottom margin`() {
val items = listOf(
diff --git a/shared/src/desktopTest/kotlin/com/aryan/reader/shared/ui/DesktopEpubNativeImageTest.kt b/shared/src/desktopTest/kotlin/com/aryan/reader/shared/ui/DesktopEpubNativeImageTest.kt
new file mode 100644
index 0000000..e113441
--- /dev/null
+++ b/shared/src/desktopTest/kotlin/com/aryan/reader/shared/ui/DesktopEpubNativeImageTest.kt
@@ -0,0 +1,16 @@
+package com.aryan.reader.shared.ui
+
+import androidx.compose.ui.Alignment
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class DesktopEpubNativeImageTest {
+ @Test
+ fun `desktop native epub image maps css object position to compose alignment`() {
+ assertEquals(Alignment.TopStart, desktopEpubImageContentAlignment("left top"))
+ assertEquals(Alignment.TopStart, desktopEpubImageContentAlignment("top left"))
+ assertEquals(Alignment.BottomStart, desktopEpubImageContentAlignment("0% 100%"))
+ assertEquals(Alignment.CenterEnd, desktopEpubImageContentAlignment("right center"))
+ assertEquals(Alignment.Center, desktopEpubImageContentAlignment(null))
+ }
+}
diff --git a/shared/src/readerJvmMain/kotlin/com/aryan/reader/shared/reader/SharedMeasuredEpubPaginator.kt b/shared/src/readerJvmMain/kotlin/com/aryan/reader/shared/reader/SharedMeasuredEpubPaginator.kt
index 5cc580a..1d62cc6 100644
--- a/shared/src/readerJvmMain/kotlin/com/aryan/reader/shared/reader/SharedMeasuredEpubPaginator.kt
+++ b/shared/src/readerJvmMain/kotlin/com/aryan/reader/shared/reader/SharedMeasuredEpubPaginator.kt
@@ -1,6 +1,7 @@
package com.aryan.reader.shared.reader
import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.ParagraphStyle
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.TextMeasurer
@@ -8,6 +9,8 @@ import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.withStyle
+import androidx.compose.ui.text.style.Hyphens
import androidx.compose.ui.text.style.LineBreak
import androidx.compose.ui.text.style.LineHeightStyle
import androidx.compose.ui.text.style.TextAlign
@@ -401,13 +404,7 @@ class SharedMeasuredEpubPaginator(
settings = settings,
includeTrailingBottomMargin = true
)
- is SemanticWrappingBlock -> measureBlockStack(
- blocks = listOf(block.floatedImage) + block.paragraphsToWrap,
- geometry = geometry,
- baseStyle = baseStyle,
- settings = settings,
- includeTrailingBottomMargin = true
- )
+ is SemanticWrappingBlock -> measureWrapping(block, geometry, baseStyle, settings)
is SemanticImage -> measureImage(block, geometry, settings)
is SemanticMath -> measureMath(block, geometry, baseStyle, settings)
is SemanticSpacer -> if (block.isExplicitLineBreak) 8 else 16
@@ -445,7 +442,7 @@ class SharedMeasuredEpubPaginator(
): Int {
currentCoroutineContext().ensureActive()
val style = block.textStyle(baseStyle, settings)
- val annotated = block.toAnnotatedString(style.fontSize.value)
+ val annotated = block.toAnnotatedString(style.fontSize.value, style.textAlign)
val minimumLineHeight = style.lineHeight.takeIfSpecified()
?.let { lineHeight -> with(density) { lineHeight.toPx().roundToInt() } }
?: with(density) { (settings.fontSize * settings.lineSpacing).sp.toPx().roundToInt() }
@@ -494,6 +491,110 @@ class SharedMeasuredEpubPaginator(
}
}
+ private suspend fun measureWrapping(
+ block: SemanticWrappingBlock,
+ geometry: MeasuredPageGeometry,
+ baseStyle: TextStyle,
+ settings: ReaderSettings
+ ): Int {
+ val contentWidth = block.measuredTextContentWidthPx(geometry)
+ val imageSize = measureImageSize(block.floatedImage, geometry, settings, maxWidthPx = contentWidth)
+ if (imageSize.first <= 0 || imageSize.second <= 0) {
+ return measureBlockStack(
+ blocks = block.paragraphsToWrap,
+ geometry = geometry,
+ baseStyle = baseStyle,
+ settings = settings,
+ includeTrailingBottomMargin = true
+ )
+ }
+
+ val wrappingWidth = (contentWidth - imageSize.first).coerceAtLeast(0)
+ if (wrappingWidth <= 0) {
+ val paragraphHeight = measureBlockStack(
+ blocks = block.paragraphsToWrap,
+ geometry = geometry,
+ baseStyle = baseStyle,
+ settings = settings,
+ includeTrailingBottomMargin = true
+ )
+ return (imageSize.second + paragraphHeight).coerceAtLeast(1)
+ }
+
+ var currentY = 0f
+ block.paragraphsToWrap.forEachIndexed { index, paragraph ->
+ val style = paragraph.textStyle(baseStyle, settings)
+ val annotated = paragraph.toAnnotatedString(style.fontSize.value, style.textAlign)
+ currentY = measureWrappedParagraphLines(
+ text = annotated,
+ style = style,
+ fullWidthPx = contentWidth,
+ wrappingWidthPx = wrappingWidth,
+ currentY = currentY,
+ imageHeightPx = imageSize.second
+ )
+ if (index < block.paragraphsToWrap.lastIndex) {
+ currentY += block.paragraphsToWrap.measuredCollapsedParagraphGapPx(index, settings)
+ }
+ }
+ return maxOf(currentY.roundToInt(), imageSize.second).coerceAtLeast(1)
+ }
+
+ private suspend fun measureWrappedParagraphLines(
+ text: AnnotatedString,
+ style: TextStyle,
+ fullWidthPx: Int,
+ wrappingWidthPx: Int,
+ currentY: Float,
+ imageHeightPx: Int
+ ): Float {
+ if (text.text.isBlank()) {
+ return currentY + (style.lineHeight.takeIfSpecified()?.let { with(density) { it.toPx() } } ?: 1f)
+ }
+ var y = currentY
+ var textOffset = 0
+ while (textOffset < text.length) {
+ currentCoroutineContext().ensureActive()
+ val isBesideImage = y < imageHeightPx
+ val currentMaxWidth = if (isBesideImage) wrappingWidthPx else fullWidthPx
+ if (currentMaxWidth <= 0) {
+ if (isBesideImage) {
+ y = imageHeightPx.toFloat()
+ continue
+ }
+ break
+ }
+ val remaining = text.subSequence(textOffset, text.length)
+ val measuredRemaining = measureTextLayout(remaining, style, currentMaxWidth)
+ val firstLineEndOffset = measuredRemaining.getLineEnd(0, visibleEnd = true)
+ if (firstLineEndOffset == 0 && remaining.length > 0) {
+ textOffset++
+ continue
+ }
+ if (firstLineEndOffset == 0) break
+ val lineText = remaining.subSequence(0, firstLineEndOffset)
+ y += measureTextLayout(lineText, style, currentMaxWidth).size.height
+ textOffset += firstLineEndOffset
+ while (textOffset < text.length && text.text[textOffset].isWhitespace()) {
+ textOffset++
+ }
+ }
+ return y
+ }
+
+ private fun List.measuredCollapsedParagraphGapPx(
+ index: Int,
+ settings: ReaderSettings
+ ): Int {
+ val current = getOrNull(index) ?: return 0
+ val next = getOrNull(index + 1) ?: return 0
+ val explicitGap = maxOf(
+ current.style.blockStyle.margin.bottom.toPxIfSpecified(),
+ next.style.blockStyle.margin.top.toPxIfSpecified()
+ )
+ return explicitGap.takeIf { it != 0 } ?: settings.renderedDefaultBlockSpacingPx()
+ }
+
private suspend fun measureMath(
block: SemanticMath,
geometry: MeasuredPageGeometry,
@@ -518,13 +619,23 @@ class SharedMeasuredEpubPaginator(
}
private fun measureImage(block: SemanticImage, geometry: MeasuredPageGeometry, settings: ReaderSettings): Int {
+ return measureImageSize(block, geometry, settings, maxWidthPx = geometry.pageWidthPx)
+ .second
+ }
+
+ private fun measureImageSize(
+ block: SemanticImage,
+ geometry: MeasuredPageGeometry,
+ settings: ReaderSettings,
+ maxWidthPx: Int
+ ): Pair {
val width = block.intrinsicWidth?.takeIf { it > 0f }
val height = block.intrinsicHeight?.takeIf { it > 0f }
val imageScale = settings.imageScale.coerceIn(0.5f, 2.0f)
- val measured = when {
+ when {
width != null && height != null -> {
val style = block.style.blockStyle
- val contentMaxWidth = geometry.pageWidthPx.toFloat()
+ val contentMaxWidth = maxWidthPx.toFloat()
val baseWidth = if (style.width.isSpecified && style.width > 0.dp) {
style.width.toPxInt().toFloat()
} else {
@@ -535,12 +646,30 @@ class SharedMeasuredEpubPaginator(
scaledWidth = scaledWidth.coerceAtMost(style.maxWidth.toPxInt() * imageScale)
}
scaledWidth = scaledWidth.coerceAtMost(contentMaxWidth)
- (scaledWidth * (height / width)).roundToInt()
+ val measuredWidth = scaledWidth.roundToInt().coerceAtLeast(1)
+ val measuredHeight = (scaledWidth * (height / width)).roundToInt()
+ return measuredWidth to measuredHeight.coerceIn(
+ 24,
+ (geometry.pageHeightPx * 0.86f).roundToInt().coerceAtLeast(24)
+ )
}
- block.style.blockStyle.height.isSpecified && block.style.blockStyle.height > 0.dp -> block.style.blockStyle.height.toPxInt()
- else -> with(density) { (settings.fontSize * 8f).sp.toPx().roundToInt() }
}
- return measured.coerceIn(24, (geometry.pageHeightPx * 0.86f).roundToInt().coerceAtLeast(24))
+ val style = block.style.blockStyle
+ val measuredWidth = when {
+ style.width.isSpecified && style.width > 0.dp -> style.width.toPxInt()
+ style.maxWidth.isSpecified && style.maxWidth > 0.dp -> minOf(maxWidthPx, style.maxWidth.toPxInt())
+ else -> maxWidthPx
+ }.coerceAtLeast(1)
+ val measuredHeight = if (style.height.isSpecified && style.height > 0.dp) {
+ style.height.toPxInt()
+ } else {
+ with(density) { (settings.fontSize * 8f).sp.toPx().roundToInt() }
+ }
+ val coercedHeight = measuredHeight.coerceIn(
+ 24,
+ (geometry.pageHeightPx * 0.86f).roundToInt().coerceAtLeast(24)
+ )
+ return measuredWidth to coercedHeight
}
private suspend fun splitBlock(
@@ -579,7 +708,7 @@ class SharedMeasuredEpubPaginator(
if (availableTextHeight <= 0) return null
val layoutResult = measureTextLayout(
- text = block.toAnnotatedString(style.fontSize.value),
+ text = block.toAnnotatedString(style.fontSize.value, style.textAlign),
style = style,
widthPx = contentWidth
)
@@ -598,7 +727,7 @@ class SharedMeasuredEpubPaginator(
val remaining = splitSemanticTextBlockAtOffsetForPagination(block, splitOffset)?.second
if (remaining != null && remaining.text.isNotBlank()) {
val remainingLayout = measureTextLayout(
- text = remaining.toAnnotatedString(style.fontSize.value),
+ text = remaining.toAnnotatedString(style.fontSize.value, style.textAlign),
style = style,
widthPx = contentWidth
)
@@ -957,19 +1086,60 @@ private fun SemanticBlock.textBlocks(): List {
}
}
-private fun SemanticTextBlock.toAnnotatedString(blockFontSizeSp: Float): AnnotatedString {
+private fun SemanticTextBlock.toAnnotatedString(
+ blockFontSizeSp: Float,
+ fallbackTextAlign: TextAlign
+): AnnotatedString {
return buildAnnotatedString {
- append(text)
+ withStyle(toMeasurementParagraphStyleForPagination(fallbackTextAlign)) {
+ append(text)
+ }
spans.forEach { span ->
val start = span.start.coerceIn(0, text.length)
val end = span.end.coerceIn(start, text.length)
if (start < end) {
addStyle(span.style.toMeasurementSpanStyle(blockFontSizeSp), start, end)
+ addMeasurementWordSpacing(
+ text = text,
+ start = start,
+ end = end,
+ wordSpacing = span.style.wordSpacing
+ )
}
}
}
}
+internal fun SemanticTextBlock.toMeasurementParagraphStyleForPagination(fallbackTextAlign: TextAlign): ParagraphStyle {
+ return ParagraphStyle(
+ textAlign = resolveSharedReaderTextAlign(
+ cssTextAlign = style.paragraphStyle.textAlign,
+ fallbackTextAlign = fallbackTextAlign
+ ),
+ textIndent = style.paragraphStyle.textIndent,
+ lineBreak = LineBreak.Paragraph,
+ hyphens = style.toMeasurementHyphens()
+ )
+}
+
+private fun AnnotatedString.Builder.addMeasurementWordSpacing(
+ text: String,
+ start: Int,
+ end: Int,
+ wordSpacing: TextUnit
+) {
+ if (!wordSpacing.isSpecified || wordSpacing.value == 0f) return
+ for (index in start until end) {
+ if (text[index] == ' ') {
+ addStyle(SpanStyle(letterSpacing = wordSpacing), index, index + 1)
+ }
+ }
+}
+
+private fun CssStyle.toMeasurementHyphens(): Hyphens {
+ return if (hyphens == "auto") Hyphens.Auto else Hyphens.None
+}
+
private fun SemanticTextBlock.textStyle(baseStyle: TextStyle, settings: ReaderSettings): TextStyle {
val fontSize = (style.fontSize.takeIfSpecified()
?: style.spanStyle.fontSize.takeIfSpecified())
@@ -989,7 +1159,10 @@ private fun SemanticTextBlock.textStyle(baseStyle: TextStyle, settings: ReaderSe
fontSize = fontSize,
lineHeight = lineHeight,
fontWeight = if (this is SemanticHeader) FontWeight.Bold else baseStyle.fontWeight,
- textAlign = style.paragraphStyle.textAlign.takeUnless { it == TextAlign.Unspecified } ?: baseStyle.textAlign
+ textAlign = resolveSharedReaderTextAlign(
+ cssTextAlign = style.paragraphStyle.textAlign,
+ fallbackTextAlign = baseStyle.textAlign
+ )
).withAndroidPaginationTextMetrics()
}
@@ -1023,11 +1196,13 @@ private fun TextUnit.resolveLineHeightSp(fontSizeSp: Float): TextUnit {
private fun CssStyle.toMeasurementSpanStyle(parentFontSizeSp: Float): SpanStyle {
val resolvedFontSize = (spanStyle.fontSize.takeIfSpecified() ?: fontSize.takeIfSpecified())
?.resolveFontSizeSp(parentFontSizeSp)
- return if (resolvedFontSize == null) {
- spanStyle
- } else {
- spanStyle.copy(fontSize = resolvedFontSize)
- }
+ return spanStyle.copy(
+ fontSize = resolvedFontSize ?: spanStyle.fontSize,
+ fontFeatureSettings = resolveSharedReaderFontFeatureSettings(
+ existingSettings = spanStyle.fontFeatureSettings,
+ fontVariantNumeric = fontVariantNumeric
+ )
+ )
}
private fun headerScale(level: Int): Float {