From 787bbf2103bdcbb820c9d62ce2a5468c0a20063d Mon Sep 17 00:00:00 2001 From: Huang Xin Date: Sat, 16 May 2026 01:57:33 +0800 Subject: [PATCH] feat(reader): custom hardware-button page turning (#4177) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(reader): add custom hardware-button page turning (#4139) Lets users bind hardware remote keys (media keys, D-pad/arrow keys) to previous/next page via a learn-mode capture UI in reader settings — an accessibility feature for page-turner remotes. - New global hardwarePageTurner system setting (enabled + key bindings). - hardwareKeys.ts: key normalization, matching, and page-turn resolution. - deviceStore: reference-counted media-key interception + learn mode. - usePagination: flips pages from bound media keys (native bridge) and D-pad/keyboard keys (DOM keydown), scoped to the active book and suppressed while the toolbar is visible. - Page Turner settings section on all platforms; web/desktop bind keys via DOM keydown only, native media-key interception stays mobile-only. - Android: intercept media + learn-mode keys in dispatchKeyEvent. - iOS: forward media keys via MPRemoteCommandCenter. Co-Authored-By: Claude Opus 4.7 (1M context) * chore(i18n): add and translate hardware page turner strings (#4139) Co-Authored-By: Claude Opus 4.7 (1M context) * feat(reader): refine hardware page turner (#4139) - Handle book-iframe key events (iframe-keydown messages) so custom bindings work as soon as a book is open, not only after the settings panel has been shown. - Add Previous/Next Section bindings alongside the page bindings. - Rename the hardwareKeys util to keybinding. - Wire the Page Turner section into the settings Reset action. - Drop the focus ring on the capture buttons; BoxedList gains an optional description. Co-Authored-By: Claude Opus 4.7 (1M context) * chore(i18n): translate page turner section and key strings (#4139) Co-Authored-By: Claude Opus 4.7 (1M context) --------- Co-authored-by: Claude Opus 4.7 (1M context) --- .../public/locales/ar/translation.json | 26 ++- .../public/locales/bn/translation.json | 26 ++- .../public/locales/bo/translation.json | 26 ++- .../public/locales/de/translation.json | 26 ++- .../public/locales/el/translation.json | 26 ++- .../public/locales/es/translation.json | 26 ++- .../public/locales/fa/translation.json | 26 ++- .../public/locales/fr/translation.json | 26 ++- .../public/locales/he/translation.json | 26 ++- .../public/locales/hi/translation.json | 26 ++- .../public/locales/hu/translation.json | 26 ++- .../public/locales/id/translation.json | 26 ++- .../public/locales/it/translation.json | 26 ++- .../public/locales/ja/translation.json | 26 ++- .../public/locales/ko/translation.json | 26 ++- .../public/locales/ms/translation.json | 26 ++- .../public/locales/nl/translation.json | 26 ++- .../public/locales/pl/translation.json | 26 ++- .../public/locales/pt-BR/translation.json | 26 ++- .../public/locales/pt/translation.json | 26 ++- .../public/locales/ro/translation.json | 26 ++- .../public/locales/ru/translation.json | 26 ++- .../public/locales/si/translation.json | 26 ++- .../public/locales/sl/translation.json | 26 ++- .../public/locales/sv/translation.json | 26 ++- .../public/locales/ta/translation.json | 26 ++- .../public/locales/th/translation.json | 26 ++- .../public/locales/tr/translation.json | 26 ++- .../public/locales/uk/translation.json | 26 ++- .../public/locales/uz/translation.json | 26 ++- .../public/locales/vi/translation.json | 26 ++- .../public/locales/zh-CN/translation.json | 26 ++- .../public/locales/zh-TW/translation.json | 26 ++- .../com/bilingify/readest/MainActivity.kt | 55 ++++- .../src/main/java/NativeBridgePlugin.kt | 19 +- .../ios/Sources/NativeBridgePlugin.swift | 93 ++++++++ .../src/__tests__/services/constants.test.ts | 10 + .../src/__tests__/store/device-store.test.ts | 69 ++++++ .../src/__tests__/utils/hardwareKeys.test.ts | 103 +++++++++ .../src/app/reader/hooks/usePagination.ts | 122 ++++++++++- .../src/components/settings/ControlPanel.tsx | 35 +-- .../settings/PageTurnerSettings.tsx | 201 ++++++++++++++++++ .../settings/primitives/BoxedList.tsx | 10 +- apps/readest-app/src/services/constants.ts | 4 + apps/readest-app/src/store/deviceStore.ts | 48 ++++- apps/readest-app/src/types/settings.ts | 20 ++ apps/readest-app/src/utils/bridge.ts | 4 + apps/readest-app/src/utils/keybinding.ts | 72 +++++++ 48 files changed, 1569 insertions(+), 154 deletions(-) create mode 100644 apps/readest-app/src/__tests__/utils/hardwareKeys.test.ts create mode 100644 apps/readest-app/src/components/settings/PageTurnerSettings.tsx create mode 100644 apps/readest-app/src/utils/keybinding.ts diff --git a/apps/readest-app/public/locales/ar/translation.json b/apps/readest-app/public/locales/ar/translation.json index 55be9a52..b8f0319e 100644 --- a/apps/readest-app/public/locales/ar/translation.json +++ b/apps/readest-app/public/locales/ar/translation.json @@ -227,7 +227,6 @@ "CJK Font": "خط CJK", "Clear Search": "مسح البحث", "Header & Footer": "الترويسة والتذييل", - "Apply also in Scrolled Mode": "تطبيق أيضًا في وضع التمرير", "A new version of Readest is available!": "يتوفر إصدار جديد من Readest!", "Readest {{newVersion}} is available (installed version {{currentVersion}}).": "إصدار {{newVersion}} من ريديست متاح للتنزيل (الإصدار المثبت حاليًا {{currentVersion}}).", "Download and install now?": "هل ترغب في تنزيله وتثبيته الآن؟", @@ -251,7 +250,6 @@ "Grid": "شبكة", "(from 'As You Like It', Act II)": "(من 'كما تحب'، الفصل الثاني)", "Link Color": "لون الرابط", - "Volume Keys for Page Flip": "مفاتيح الصوت لتقليب الصفحات", "Screen": "الشاشة", "Orientation": "الاتجاه", "Portrait": "عمودي", @@ -1512,5 +1510,27 @@ "Start your library": "ابدأ مكتبتك", "Pick a book from your device to add it to your library.": "اختر كتابًا من جهازك لإضافته إلى مكتبتك.", "Drop a book anywhere on this window, or pick one from your computer.": "أفلِت كتابًا في أي مكان على هذه النافذة، أو اختر واحدًا من حاسوبك.", - "Sign in to sync your library": "سجّل الدخول لمزامنة مكتبتك" + "Sign in to sync your library": "سجّل الدخول لمزامنة مكتبتك", + "Clear": "مسح", + "Listening…": "يستمع…", + "Set key": "تعيين مفتاح", + "Page Turner": "قالب الصفحات", + "Media Next": "وسائط التالي", + "Media Previous": "وسائط السابق", + "Media Play/Pause": "وسائط تشغيل/إيقاف مؤقت", + "Media Fast Forward": "وسائط تقديم سريع", + "Media Rewind": "وسائط إرجاع", + "Volume Up": "رفع الصوت", + "Volume Down": "خفض الصوت", + "Arrow Left": "سهم يسار", + "Arrow Right": "سهم يمين", + "Arrow Up": "سهم أعلى", + "Arrow Down": "سهم أسفل", + "Page Up": "صفحة للأعلى", + "Page Down": "صفحة للأسفل", + "Space": "مسافة", + "Enter": "إدخال", + "Press a button on your remote controller or keyboard after tapping \"Set key\".": "اضغط على زر في جهاز التحكم عن بُعد أو لوحة المفاتيح بعد النقر على \"تعيين مفتاح\".", + "Use Volume Keys": "استخدام مفاتيح الصوت", + "Custom Page Turner": "قلّاب الصفحات المخصص" } diff --git a/apps/readest-app/public/locales/bn/translation.json b/apps/readest-app/public/locales/bn/translation.json index 29c857af..f6055652 100644 --- a/apps/readest-app/public/locales/bn/translation.json +++ b/apps/readest-app/public/locales/bn/translation.json @@ -195,7 +195,6 @@ "Scrolled Mode": "স্ক্রল মোড", "Overlap Pixels": "ওভারল্যাপ পিক্সেল", "Disable Double Click": "ডাবল ক্লিক নিষ্ক্রিয়", - "Volume Keys for Page Flip": "পৃষ্ঠা উল্টানোর জন্য ভলিউম কী", "Animation": "অ্যানিমেশন", "Paging Animation": "পৃষ্ঠা অ্যানিমেশন", "Security": "নিরাপত্তা", @@ -269,7 +268,6 @@ "Reading Progress Style": "পড়ার অগ্রগতির স্টাইল", "Page Number": "পৃষ্ঠা নম্বর", "Percentage": "শতাংশ", - "Apply also in Scrolled Mode": "স্ক্রল মোডেও প্রয়োগ করুন", "Screen": "স্ক্রিন", "Orientation": "দিকবিন্যাস", "Portrait": "পোর্ট্রেট", @@ -1412,5 +1410,27 @@ "Start your library": "আপনার লাইব্রেরি শুরু করুন", "Pick a book from your device to add it to your library.": "আপনার লাইব্রেরিতে যোগ করতে ডিভাইস থেকে একটি বই বেছে নিন।", "Drop a book anywhere on this window, or pick one from your computer.": "এই উইন্ডোর যেকোনো জায়গায় একটি বই ছাড়ুন, অথবা আপনার কম্পিউটার থেকে একটি বেছে নিন।", - "Sign in to sync your library": "আপনার লাইব্রেরি সিঙ্ক করতে সাইন ইন করুন" + "Sign in to sync your library": "আপনার লাইব্রেরি সিঙ্ক করতে সাইন ইন করুন", + "Clear": "মুছুন", + "Listening…": "শুনছে…", + "Set key": "কী সেট করুন", + "Page Turner": "পেজ টার্নার", + "Media Next": "মিডিয়া পরবর্তী", + "Media Previous": "মিডিয়া পূর্ববর্তী", + "Media Play/Pause": "মিডিয়া প্লে/পজ", + "Media Fast Forward": "মিডিয়া ফাস্ট ফরওয়ার্ড", + "Media Rewind": "মিডিয়া রিওয়াইন্ড", + "Volume Up": "ভলিউম বাড়ান", + "Volume Down": "ভলিউম কমান", + "Arrow Left": "বাম তীর", + "Arrow Right": "ডান তীর", + "Arrow Up": "উপরের তীর", + "Arrow Down": "নিচের তীর", + "Page Up": "পেজ আপ", + "Page Down": "পেজ ডাউন", + "Space": "স্পেস", + "Enter": "এন্টার", + "Press a button on your remote controller or keyboard after tapping \"Set key\".": "\"কী সেট করুন\" ট্যাপ করার পর আপনার রিমোট কন্ট্রোলার বা কীবোর্ডের একটি বোতাম চাপুন।", + "Use Volume Keys": "ভলিউম কী ব্যবহার করুন", + "Custom Page Turner": "কাস্টম পেজ টার্নার" } diff --git a/apps/readest-app/public/locales/bo/translation.json b/apps/readest-app/public/locales/bo/translation.json index ba2a7f56..102ab75b 100644 --- a/apps/readest-app/public/locales/bo/translation.json +++ b/apps/readest-app/public/locales/bo/translation.json @@ -227,7 +227,6 @@ "CJK Font": "རྒྱ་ཡིག་གི་ཡིག་གཟུགས།", "Clear Search": "འཚོལ་བཤེར་བསལ་བ།", "Header & Footer": "ཤོག་ངོས་ཀྱི་མགོ་དང་མཇུག", - "Apply also in Scrolled Mode": "འཁོར་རྒྱུག་རྣམ་པར་སྤྱོད་པ།", "A new version of Readest is available!": "Readest ཐོན་རིམ་གསར་པ་བསྐྱར་བརྗེ་བྱ་རྒྱུ་ཡོད།", "Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} བསྐྱར་བརྗེ་བྱ་རྒྱུ་ཡོད།(བསྒྲིགས་ཟིན་པའི་ཐོན་རིམ {{currentVersion}})", "Download and install now?": "ད་ལྟ་ཕབ་ལེན་དང་བསྒྲིགས་དགོས་སམ།", @@ -251,7 +250,6 @@ "Grid": "དྲྭ་རྡལ།", "(from 'As You Like It', Act II)": "《ཐམས་ཅད་དགའ་བ》ནས་དྲངས་པ། ལེའུ་གཉིས་པ།", "Link Color": "སྦྲེལ་ཐག་གི་ཚོན་མདོག", - "Volume Keys for Page Flip": "སྒྲ་ཚད་ལྡེ་མིག་གིས་ཤོག་ལྷེ་བསྒྱུར་བ།", "Screen": "བརྡ་ཁྱབ་ངོས།", "Orientation": "བརྡ་ཁྱབ་ངོས་ཀྱི་ཁ་ཕྱོགས།", "Portrait": "ཀྲིང་ངོས།", @@ -1387,5 +1385,27 @@ "Start your library": "ཁྱེད་ཀྱི་དཔེ་མཛོད་འགོ་འཛུགས།", "Pick a book from your device to add it to your library.": "ཁྱེད་ཀྱི་སྒྲིག་ཆས་ནས་དཔེ་དེབ་ཅིག་འདེམས་ནས་ཁྱེད་ཀྱི་དཔེ་མཛོད་ལ་ཁ་སྣོན་གྱིས།", "Drop a book anywhere on this window, or pick one from your computer.": "སྒེའུ་ཁུང་འདིའི་གང་སར་དཔེ་དེབ་ཅིག་འཇོག་པའམ་ཡང་ན་ཁྱེད་ཀྱི་གློག་ཀླད་ནས་འདེམས་རོགས།", - "Sign in to sync your library": "ཁྱེད་ཀྱི་དཔེ་མཛོད་མཉམ་སྦྱོར་གྱི་ཆེད་ནང་བསྐྱོད་གནང་རོགས།" + "Sign in to sync your library": "ཁྱེད་ཀྱི་དཔེ་མཛོད་མཉམ་སྦྱོར་གྱི་ཆེད་ནང་བསྐྱོད་གནང་རོགས།", + "Clear": "སུབ་པ།", + "Listening…": "ཉན་གྱི་ཡོད…", + "Set key": "མཐེབ་གཞི་གཏན་འཁེལ།", + "Page Turner": "ཤོག་གྲུ་འཕར་མཁན།", + "Media Next": "མི་ཡིའི་རྗེས་མ།", + "Media Previous": "མི་ཡིའི་སྔོན་མ།", + "Media Play/Pause": "མི་ཡི་བཏང་/བཀག", + "Media Fast Forward": "མི་ཡི་མྱུར་མགྱོགས་སྔར་བསྐྱོད།", + "Media Rewind": "མི་ཡི་ཕྱིར་བསྐྱོད།", + "Volume Up": "སྒྲ་སྐད་མཐོར་གཏོང་།", + "Volume Down": "སྒྲ་སྐད་དམའར་གཏོང་།", + "Arrow Left": "གཡོན་མདའ།", + "Arrow Right": "གཡས་མདའ།", + "Arrow Up": "ཡར་མདའ།", + "Arrow Down": "མར་མདའ།", + "Page Up": "ཤོག་ལྷེ་ཡར།", + "Page Down": "ཤོག་ལྷེ་མར།", + "Space": "བར་ཐག", + "Enter": "Enter", + "Press a button on your remote controller or keyboard after tapping \"Set key\".": "\"མཐེབ་གཞི་གཏན་འཁེལ།\" ལ་རེག་རྗེས། ཁྱེད་ཀྱི་རྒྱང་འཐེན་སྒྲིག་ཆས་སམ་མཐེབ་གཞི་གང་རུང་གི་ཐོག་མཐེབ་གཅིག་གནོན་རོགས།", + "Use Volume Keys": "སྒྲ་སྐད་མཐེབ་གཞི་སྤྱོད་པ།", + "Custom Page Turner": "རང་སྒྲུབ་ཤོག་ལྷེ་སྤོ་བའི་ལམ་ལུགས།" } diff --git a/apps/readest-app/public/locales/de/translation.json b/apps/readest-app/public/locales/de/translation.json index 9bd1e726..35518454 100644 --- a/apps/readest-app/public/locales/de/translation.json +++ b/apps/readest-app/public/locales/de/translation.json @@ -227,7 +227,6 @@ "CJK Font": "CJK-Schriftart", "Clear Search": "Suche löschen", "Header & Footer": "Kopf- & Fußzeile", - "Apply also in Scrolled Mode": "Auch im Scroll-Modus anwenden", "A new version of Readest is available!": "Eine neue Version von Readest ist verfügbar!", "Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} ist verfügbar (installierte Version {{currentVersion}}).", "Download and install now?": "Jetzt herunterladen und installieren?", @@ -251,7 +250,6 @@ "Grid": "Raster", "(from 'As You Like It', Act II)": "(aus 'Wie es euch gefällt', Akt II)", "Link Color": "Linkfarbe", - "Volume Keys for Page Flip": "Volumen-Tasten für Seitenwechsel", "Screen": "Bildschirm", "Orientation": "Orientierung", "Portrait": "Hochformat", @@ -1412,5 +1410,27 @@ "Start your library": "Starten Sie Ihre Bibliothek", "Pick a book from your device to add it to your library.": "Wählen Sie ein Buch von Ihrem Gerät, um es Ihrer Bibliothek hinzuzufügen.", "Drop a book anywhere on this window, or pick one from your computer.": "Ziehen Sie ein Buch an eine beliebige Stelle dieses Fensters oder wählen Sie eines von Ihrem Computer aus.", - "Sign in to sync your library": "Melden Sie sich an, um Ihre Bibliothek zu synchronisieren" + "Sign in to sync your library": "Melden Sie sich an, um Ihre Bibliothek zu synchronisieren", + "Clear": "Löschen", + "Listening…": "Warte auf Eingabe…", + "Set key": "Taste festlegen", + "Page Turner": "Seitenblätterer", + "Media Next": "Medien: Weiter", + "Media Previous": "Medien: Zurück", + "Media Play/Pause": "Medien: Wiedergabe/Pause", + "Media Fast Forward": "Medien: Schnellvorlauf", + "Media Rewind": "Medien: Rücklauf", + "Volume Up": "Lautstärke erhöhen", + "Volume Down": "Lautstärke verringern", + "Arrow Left": "Pfeil links", + "Arrow Right": "Pfeil rechts", + "Arrow Up": "Pfeil hoch", + "Arrow Down": "Pfeil runter", + "Page Up": "Bild auf", + "Page Down": "Bild ab", + "Space": "Leertaste", + "Enter": "Eingabe", + "Press a button on your remote controller or keyboard after tapping \"Set key\".": "Drücken Sie eine Taste auf Ihrer Fernbedienung oder Tastatur, nachdem Sie auf \"Taste festlegen\" getippt haben.", + "Use Volume Keys": "Lautstärketasten verwenden", + "Custom Page Turner": "Benutzerdefinierter Seitenumblätterer" } diff --git a/apps/readest-app/public/locales/el/translation.json b/apps/readest-app/public/locales/el/translation.json index 410cabad..f58a8e8d 100644 --- a/apps/readest-app/public/locales/el/translation.json +++ b/apps/readest-app/public/locales/el/translation.json @@ -228,7 +228,6 @@ "CJK Font": "Γραμματοσειρά CJK", "Clear Search": "Καθαρισμός αναζήτησης", "Header & Footer": "Κεφαλίδα & Υποσέλιδο", - "Apply also in Scrolled Mode": "Εφαρμογή και σε λειτουργία κύλισης", "A new version of Readest is available!": "Μια νέα έκδοση του Readest είναι διαθέσιμη!", "Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Η έκδοση Readest {{newVersion}} είναι διαθέσιμη (έκδοση που είναι εγκατεστημένη {{currentVersion}}).", "Download and install now?": "Θέλετε να την κατεβάσετε και να την εγκαταστήσετε τώρα;", @@ -252,7 +251,6 @@ "Grid": "Πλέγμα", "(from 'As You Like It', Act II)": "('Όπως σας αρέσει', Πράξη II)", "Link Color": "Χρώμα συνδέσμου", - "Volume Keys for Page Flip": "Ανατροπή με πλήκτρα έντασης", "Screen": "Οθόνη", "Orientation": "Προσανατολισμός", "Portrait": "Πορτραίτο", @@ -1412,5 +1410,27 @@ "Start your library": "Ξεκινήστε τη βιβλιοθήκη σας", "Pick a book from your device to add it to your library.": "Επιλέξτε ένα βιβλίο από τη συσκευή σας για να το προσθέσετε στη βιβλιοθήκη σας.", "Drop a book anywhere on this window, or pick one from your computer.": "Αφήστε ένα βιβλίο οπουδήποτε σε αυτό το παράθυρο ή επιλέξτε ένα από τον υπολογιστή σας.", - "Sign in to sync your library": "Συνδεθείτε για να συγχρονίσετε τη βιβλιοθήκη σας" + "Sign in to sync your library": "Συνδεθείτε για να συγχρονίσετε τη βιβλιοθήκη σας", + "Clear": "Εκκαθάριση", + "Listening…": "Αναμονή…", + "Set key": "Ορισμός πλήκτρου", + "Page Turner": "Στροφέας σελίδων", + "Media Next": "Πολυμέσα: Επόμενο", + "Media Previous": "Πολυμέσα: Προηγούμενο", + "Media Play/Pause": "Πολυμέσα: Αναπαραγωγή/Παύση", + "Media Fast Forward": "Πολυμέσα: Γρήγορη προώθηση", + "Media Rewind": "Πολυμέσα: Επαναφορά", + "Volume Up": "Αύξηση έντασης", + "Volume Down": "Μείωση έντασης", + "Arrow Left": "Βέλος αριστερά", + "Arrow Right": "Βέλος δεξιά", + "Arrow Up": "Βέλος πάνω", + "Arrow Down": "Βέλος κάτω", + "Page Up": "Page Up", + "Page Down": "Page Down", + "Space": "Κενό", + "Enter": "Enter", + "Press a button on your remote controller or keyboard after tapping \"Set key\".": "Πατήστε ένα κουμπί στο τηλεχειριστήριο ή το πληκτρολόγιό σας αφού πατήσετε «Ορισμός πλήκτρου».", + "Use Volume Keys": "Χρήση πλήκτρων έντασης", + "Custom Page Turner": "Προσαρμοσμένος αναλτήρας σελίδων" } diff --git a/apps/readest-app/public/locales/es/translation.json b/apps/readest-app/public/locales/es/translation.json index 7bac2728..7be56b70 100644 --- a/apps/readest-app/public/locales/es/translation.json +++ b/apps/readest-app/public/locales/es/translation.json @@ -250,7 +250,6 @@ "CJK Font": "Fuente CJK", "Clear Search": "Limpiar búsqueda", "Header & Footer": "Encabezado y pie de página", - "Apply also in Scrolled Mode": "Aplicar también en modo desplazamiento", "A new version of Readest is available!": "¡Una nueva versión de Readest está disponible!", "Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} está disponible (versión instalada {{currentVersion}}).", "Download and install now?": "Descargar e instalar ahora?", @@ -274,7 +273,6 @@ "Grid": "Cuadrícula", "(from 'As You Like It', Act II)": "(de 'Como gustéis', Acto II)", "Link Color": "Color del enlace", - "Volume Keys for Page Flip": "Botones de volumen para pasar página", "Screen": "Pantalla", "Orientation": "Orientación", "Portrait": "Vertical", @@ -1437,5 +1435,27 @@ "Start your library": "Empieza tu biblioteca", "Pick a book from your device to add it to your library.": "Elige un libro de tu dispositivo para añadirlo a tu biblioteca.", "Drop a book anywhere on this window, or pick one from your computer.": "Suelta un libro en cualquier parte de esta ventana o elige uno desde tu computadora.", - "Sign in to sync your library": "Inicia sesión para sincronizar tu biblioteca" + "Sign in to sync your library": "Inicia sesión para sincronizar tu biblioteca", + "Clear": "Borrar", + "Listening…": "Escuchando…", + "Set key": "Asignar tecla", + "Page Turner": "Pasador de páginas", + "Media Next": "Multimedia: Siguiente", + "Media Previous": "Multimedia: Anterior", + "Media Play/Pause": "Multimedia: Reproducir/Pausa", + "Media Fast Forward": "Multimedia: Avance rápido", + "Media Rewind": "Multimedia: Retroceso", + "Volume Up": "Subir volumen", + "Volume Down": "Bajar volumen", + "Arrow Left": "Flecha izquierda", + "Arrow Right": "Flecha derecha", + "Arrow Up": "Flecha arriba", + "Arrow Down": "Flecha abajo", + "Page Up": "Re Pág", + "Page Down": "Av Pág", + "Space": "Espacio", + "Enter": "Intro", + "Press a button on your remote controller or keyboard after tapping \"Set key\".": "Pulse un botón en su mando a distancia o teclado después de tocar \"Asignar tecla\".", + "Use Volume Keys": "Usar teclas de volumen", + "Custom Page Turner": "Pasador de páginas personalizado" } diff --git a/apps/readest-app/public/locales/fa/translation.json b/apps/readest-app/public/locales/fa/translation.json index f7ba6339..03ac5ff1 100644 --- a/apps/readest-app/public/locales/fa/translation.json +++ b/apps/readest-app/public/locales/fa/translation.json @@ -227,7 +227,6 @@ "CJK Font": "فونت CJK", "Clear Search": "پاک کردن جستجو", "Header & Footer": "سربرگ و پاورقی", - "Apply also in Scrolled Mode": "همچنین در حالت پیمایشی اِعمال شود", "A new version of Readest is available!": "نسخه‌ی جدیدی از Readest در دسترس است!", "Readest {{newVersion}} is available (installed version {{currentVersion}}).": "نسخه‌ی {{newVersion}} Readest در دسترس است (نسخه‌ی نصب‌شده: {{currentVersion}}).", "Download and install now?": "الان دانلود و نصب شود؟", @@ -251,7 +250,6 @@ "Grid": "جدول", "(from 'As You Like It', Act II)": "(از «همان‌طور که دوست دارید»، پرده‌ی دوم)", "Link Color": "رنگ لینک", - "Volume Keys for Page Flip": "استفاده از کلیدهای صدا برای ورق زدن صفحه", "Screen": "صفحه", "Orientation": "جهت", "Portrait": "عمودی", @@ -1412,5 +1410,27 @@ "Start your library": "کتابخانه‌ی خود را آغاز کنید", "Pick a book from your device to add it to your library.": "از دستگاه خود کتابی انتخاب کنید تا به کتابخانه‌تان افزوده شود.", "Drop a book anywhere on this window, or pick one from your computer.": "کتابی را در هر جای این پنجره رها کنید، یا یکی را از رایانه‌ی خود انتخاب کنید.", - "Sign in to sync your library": "برای همگام‌سازی کتابخانه‌ی خود وارد شوید" + "Sign in to sync your library": "برای همگام‌سازی کتابخانه‌ی خود وارد شوید", + "Clear": "پاک کردن", + "Listening…": "در حال گوش دادن…", + "Set key": "تنظیم کلید", + "Page Turner": "ورق‌زن صفحه", + "Media Next": "رسانه: بعدی", + "Media Previous": "رسانه: قبلی", + "Media Play/Pause": "رسانه: پخش/مکث", + "Media Fast Forward": "رسانه: جلو بردن سریع", + "Media Rewind": "رسانه: برگشت سریع", + "Volume Up": "افزایش صدا", + "Volume Down": "کاهش صدا", + "Arrow Left": "فلش چپ", + "Arrow Right": "فلش راست", + "Arrow Up": "فلش بالا", + "Arrow Down": "فلش پایین", + "Page Up": "صفحه بالا", + "Page Down": "صفحه پایین", + "Space": "فاصله", + "Enter": "اینتر", + "Press a button on your remote controller or keyboard after tapping \"Set key\".": "پس از ضربه زدن به «تنظیم کلید»، دکمه‌ای روی کنترلر یا صفحه‌کلید خود فشار دهید.", + "Use Volume Keys": "استفاده از کلیدهای صدا", + "Custom Page Turner": "صفحه‌گردان سفارشی" } diff --git a/apps/readest-app/public/locales/fr/translation.json b/apps/readest-app/public/locales/fr/translation.json index 96d1ed09..f6d7fa4d 100644 --- a/apps/readest-app/public/locales/fr/translation.json +++ b/apps/readest-app/public/locales/fr/translation.json @@ -227,7 +227,6 @@ "CJK Font": "Police CJK", "Clear Search": "Effacer la recherche", "Header & Footer": "En-tête et pied de page", - "Apply also in Scrolled Mode": "Appliquer également en mode défilement", "A new version of Readest is available!": "Avis de mise à jour", "Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} est disponible (version installée {{currentVersion}}).", "Download and install now?": "Télécharger et installer maintenant ?", @@ -251,7 +250,6 @@ "Grid": "Grille", "(from 'As You Like It', Act II)": "(de 'Comme il vous plaira', Acte II)", "Link Color": "Couleur du lien", - "Volume Keys for Page Flip": "Boutons volume pour tourner la page", "Screen": "Écran", "Orientation": "Orientation", "Portrait": "Portrait", @@ -1437,5 +1435,27 @@ "Start your library": "Commencez votre bibliothèque", "Pick a book from your device to add it to your library.": "Choisissez un livre depuis votre appareil à ajouter à votre bibliothèque.", "Drop a book anywhere on this window, or pick one from your computer.": "Déposez un livre n'importe où sur cette fenêtre, ou choisissez-en un depuis votre ordinateur.", - "Sign in to sync your library": "Connectez-vous pour synchroniser votre bibliothèque" + "Sign in to sync your library": "Connectez-vous pour synchroniser votre bibliothèque", + "Clear": "Effacer", + "Listening…": "En attente…", + "Set key": "Définir la touche", + "Page Turner": "Tourne-page", + "Media Next": "Média : Suivant", + "Media Previous": "Média : Précédent", + "Media Play/Pause": "Média : Lecture/Pause", + "Media Fast Forward": "Média : Avance rapide", + "Media Rewind": "Média : Retour arrière", + "Volume Up": "Volume +", + "Volume Down": "Volume -", + "Arrow Left": "Flèche gauche", + "Arrow Right": "Flèche droite", + "Arrow Up": "Flèche haut", + "Arrow Down": "Flèche bas", + "Page Up": "Page préc.", + "Page Down": "Page suiv.", + "Space": "Espace", + "Enter": "Entrée", + "Press a button on your remote controller or keyboard after tapping \"Set key\".": "Appuyez sur un bouton de votre télécommande ou de votre clavier après avoir appuyé sur « Définir la touche ».", + "Use Volume Keys": "Utiliser les touches de volume", + "Custom Page Turner": "Tourne-page personnalisé" } diff --git a/apps/readest-app/public/locales/he/translation.json b/apps/readest-app/public/locales/he/translation.json index d7e82c37..a561e228 100644 --- a/apps/readest-app/public/locales/he/translation.json +++ b/apps/readest-app/public/locales/he/translation.json @@ -850,7 +850,6 @@ "Swap Click Sides": "החלף צדדי לחיצה", "Disable Double Tap": "השבת הקשה כפולה", "Disable Double Click": "השבת לחיצה כפולה", - "Volume Keys for Page Flip": "מקשי עוצמת קול לדפדוף", "Show Page Navigation Buttons": "הצג כפתורי ניווט בעמוד", "Annotation Tools": "כלי הערות", "Enable Quick Actions": "הפעל פעולות מהירות", @@ -938,7 +937,6 @@ "Maximum Number of Columns": "מספר עמודות מקסימלי", "Maximum Column Height": "גובה עמודה מקסימלי", "Maximum Column Width": "רוחב עמודה מקסימלי", - "Apply also in Scrolled Mode": "החל גם במצב גלילה", "Header & Footer": "כותרת עליונה ותחתונה", "Show Header": "הצג כותרת עליונה", "Show Footer": "הצג כותרת תחתונה", @@ -1437,5 +1435,27 @@ "Start your library": "התחילו את הספרייה שלכם", "Pick a book from your device to add it to your library.": "בחרו ספר מהמכשיר שלכם כדי להוסיף אותו לספרייה.", "Drop a book anywhere on this window, or pick one from your computer.": "שחררו ספר בכל מקום בחלון הזה, או בחרו אחד מהמחשב שלכם.", - "Sign in to sync your library": "התחברו לסנכרון הספרייה שלכם" + "Sign in to sync your library": "התחברו לסנכרון הספרייה שלכם", + "Clear": "נקה", + "Listening…": "מאזין…", + "Set key": "הגדר מקש", + "Page Turner": "מהפך עמודים", + "Media Next": "מדיה הבא", + "Media Previous": "מדיה הקודם", + "Media Play/Pause": "מדיה ניגון/השהייה", + "Media Fast Forward": "מדיה הרצה מהירה", + "Media Rewind": "מדיה הרצה לאחור", + "Volume Up": "הגבר עוצמה", + "Volume Down": "הנמך עוצמה", + "Arrow Left": "חץ שמאלה", + "Arrow Right": "חץ ימינה", + "Arrow Up": "חץ למעלה", + "Arrow Down": "חץ למטה", + "Page Up": "עמוד קודם", + "Page Down": "עמוד הבא", + "Space": "רווח", + "Enter": "אנטר", + "Press a button on your remote controller or keyboard after tapping \"Set key\".": "לחץ על כפתור בשלט הרחוק או במקלדת לאחר הקשה על \"הגדר מקש\".", + "Use Volume Keys": "השתמש במקשי עוצמת קול", + "Custom Page Turner": "מהפך עמודים מותאם אישית" } diff --git a/apps/readest-app/public/locales/hi/translation.json b/apps/readest-app/public/locales/hi/translation.json index e3e67e79..66988f25 100644 --- a/apps/readest-app/public/locales/hi/translation.json +++ b/apps/readest-app/public/locales/hi/translation.json @@ -228,7 +228,6 @@ "CJK Font": "CJK फ़ॉन्ट", "Clear Search": "खोज साफ करें", "Header & Footer": "हेडर और फ़ुटर", - "Apply also in Scrolled Mode": "स्क्रॉल मोड में भी लागू करें", "A new version of Readest is available!": "Readest का एक नया संस्करण उपलब्ध है!", "Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} उपलब्ध है (स्थापित संस्करण {{currentVersion}})।", "Download and install now?": "अब डाउनलोड और इंस्टॉल करें?", @@ -252,7 +251,6 @@ "Grid": "ग्रिड", "(from 'As You Like It', Act II)": "('जैसा आप इसे पसंद करते हैं', अधिनियम II)", "Link Color": "लिंक रंग", - "Volume Keys for Page Flip": "पृष्ठ फ़्लिप के लिए वॉल्यूम कुंजियाँ", "Screen": "स्क्रीन", "Orientation": "अवयव", "Portrait": "पोर्ट्रेट", @@ -1412,5 +1410,27 @@ "Start your library": "अपनी लाइब्रेरी शुरू करें", "Pick a book from your device to add it to your library.": "अपनी लाइब्रेरी में जोड़ने के लिए अपने डिवाइस से एक किताब चुनें।", "Drop a book anywhere on this window, or pick one from your computer.": "इस विंडो में कहीं भी एक किताब छोड़ें, या अपने कंप्यूटर से कोई एक चुनें।", - "Sign in to sync your library": "अपनी लाइब्रेरी सिंक करने के लिए साइन इन करें" + "Sign in to sync your library": "अपनी लाइब्रेरी सिंक करने के लिए साइन इन करें", + "Clear": "साफ़ करें", + "Listening…": "सुन रहे हैं…", + "Set key": "कुंजी सेट करें", + "Page Turner": "पेज टर्नर", + "Media Next": "मीडिया अगला", + "Media Previous": "मीडिया पिछला", + "Media Play/Pause": "मीडिया प्ले/पॉज़", + "Media Fast Forward": "मीडिया फ़ास्ट फ़ॉरवर्ड", + "Media Rewind": "मीडिया रिवाइंड", + "Volume Up": "वॉल्यूम बढ़ाएं", + "Volume Down": "वॉल्यूम घटाएं", + "Arrow Left": "बाएं तीर", + "Arrow Right": "दाएं तीर", + "Arrow Up": "ऊपर तीर", + "Arrow Down": "नीचे तीर", + "Page Up": "पेज ऊपर", + "Page Down": "पेज नीचे", + "Space": "स्पेस", + "Enter": "एंटर", + "Press a button on your remote controller or keyboard after tapping \"Set key\".": "\"कुंजी सेट करें\" दबाने के बाद अपने रिमोट कंट्रोलर या कीबोर्ड पर एक बटन दबाएं।", + "Use Volume Keys": "वॉल्यूम कुंजियों का उपयोग करें", + "Custom Page Turner": "कस्टम पेज टर्नर" } diff --git a/apps/readest-app/public/locales/hu/translation.json b/apps/readest-app/public/locales/hu/translation.json index 72623418..9abf4c7d 100644 --- a/apps/readest-app/public/locales/hu/translation.json +++ b/apps/readest-app/public/locales/hu/translation.json @@ -930,7 +930,6 @@ "Swap Click Sides": "Kattintási oldalak cseréje", "Disable Double Tap": "Dupla koppintás letiltása", "Disable Double Click": "Dupla kattintás letiltása", - "Volume Keys for Page Flip": "Hangerőgombok a lapozáshoz", "Show Page Navigation Buttons": "Oldalnavigációs gombok megjelenítése", "Annotation Tools": "Jegyzetelési eszközök", "Enable Quick Actions": "Gyorsműveletek engedélyezése", @@ -1018,7 +1017,6 @@ "Maximum Number of Columns": "Oszlopok maximális száma", "Maximum Column Height": "Maximális oszlopmagasság", "Maximum Column Width": "Maximális oszlopszélesség", - "Apply also in Scrolled Mode": "Alkalmazás görgetett módban is", "Header & Footer": "Fejléc és lábléc", "Show Header": "Fejléc megjelenítése", "Show Footer": "Lábléc megjelenítése", @@ -1412,5 +1410,27 @@ "Start your library": "Indítsa el a könyvtárát", "Pick a book from your device to add it to your library.": "Válasszon egy könyvet az eszközéről, hogy hozzáadja a könyvtárához.", "Drop a book anywhere on this window, or pick one from your computer.": "Húzzon egy könyvet bárhová ezen az ablakon, vagy válasszon egyet a számítógépéről.", - "Sign in to sync your library": "Jelentkezzen be a könyvtár szinkronizálásához" + "Sign in to sync your library": "Jelentkezzen be a könyvtár szinkronizálásához", + "Clear": "Törlés", + "Listening…": "Figyelés…", + "Set key": "Billentyű beállítása", + "Page Turner": "Lapozó eszköz", + "Media Next": "Média következő", + "Media Previous": "Média előző", + "Media Play/Pause": "Média lejátszás/szünet", + "Media Fast Forward": "Média gyors előre", + "Media Rewind": "Média visszatekerés", + "Volume Up": "Hangerő növelése", + "Volume Down": "Hangerő csökkentése", + "Arrow Left": "Bal nyíl", + "Arrow Right": "Jobb nyíl", + "Arrow Up": "Fel nyíl", + "Arrow Down": "Le nyíl", + "Page Up": "Page Up", + "Page Down": "Page Down", + "Space": "Szóköz", + "Enter": "Enter", + "Press a button on your remote controller or keyboard after tapping \"Set key\".": "Nyomjon meg egy gombot a távirányítón vagy a billentyűzeten a „Billentyű beállítása” érintése után.", + "Use Volume Keys": "Hangerőgombok használata", + "Custom Page Turner": "Egyéni lapozó eszköz" } diff --git a/apps/readest-app/public/locales/id/translation.json b/apps/readest-app/public/locales/id/translation.json index f7c72e89..fa825b47 100644 --- a/apps/readest-app/public/locales/id/translation.json +++ b/apps/readest-app/public/locales/id/translation.json @@ -228,7 +228,6 @@ "CJK Font": "Font CJK", "Clear Search": "Hapus Pencarian", "Header & Footer": "Header & Footer", - "Apply also in Scrolled Mode": "Terapkan juga di Mode Gulir", "A new version of Readest is available!": "Versi baru Readest Tersedia!", "Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} tersedia (versi terinstal {{currentVersion}}).", "Download and install now?": "Unduh dan instal sekarang?", @@ -252,7 +251,6 @@ "Grid": "Jala", "(from 'As You Like It', Act II)": "(dari 'Seperti yang Anda Inginkan', Bab II)", "Link Color": "Warna Tautan", - "Volume Keys for Page Flip": "Volume Keys untuk Pembalikan Halaman", "Screen": "Screen", "Orientation": "Orientasi", "Portrait": "Potret", @@ -1387,5 +1385,27 @@ "Start your library": "Mulai perpustakaan Anda", "Pick a book from your device to add it to your library.": "Pilih buku dari perangkat Anda untuk ditambahkan ke perpustakaan Anda.", "Drop a book anywhere on this window, or pick one from your computer.": "Jatuhkan buku di mana saja pada jendela ini, atau pilih satu dari komputer Anda.", - "Sign in to sync your library": "Masuk untuk menyinkronkan perpustakaan Anda" + "Sign in to sync your library": "Masuk untuk menyinkronkan perpustakaan Anda", + "Clear": "Hapus", + "Listening…": "Mendengarkan…", + "Set key": "Atur tombol", + "Page Turner": "Pembalik Halaman", + "Media Next": "Media Berikutnya", + "Media Previous": "Media Sebelumnya", + "Media Play/Pause": "Media Putar/Jeda", + "Media Fast Forward": "Media Maju Cepat", + "Media Rewind": "Media Mundur", + "Volume Up": "Volume Naik", + "Volume Down": "Volume Turun", + "Arrow Left": "Panah Kiri", + "Arrow Right": "Panah Kanan", + "Arrow Up": "Panah Atas", + "Arrow Down": "Panah Bawah", + "Page Up": "Halaman Atas", + "Page Down": "Halaman Bawah", + "Space": "Spasi", + "Enter": "Enter", + "Press a button on your remote controller or keyboard after tapping \"Set key\".": "Tekan tombol pada remote controller atau keyboard Anda setelah mengetuk \"Atur tombol\".", + "Use Volume Keys": "Gunakan Tombol Volume", + "Custom Page Turner": "Pembalik Halaman Kustom" } diff --git a/apps/readest-app/public/locales/it/translation.json b/apps/readest-app/public/locales/it/translation.json index e3020459..d74faf6b 100644 --- a/apps/readest-app/public/locales/it/translation.json +++ b/apps/readest-app/public/locales/it/translation.json @@ -228,7 +228,6 @@ "CJK Font": "Font CJK", "Clear Search": "Cancella ricerca", "Header & Footer": "Intestazione e piè di pagina", - "Apply also in Scrolled Mode": "Applica anche in modalità scorrimento", "A new version of Readest is available!": "È disponibile una nuova versione di Readest!", "Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} è disponibile (versione installata {{currentVersion}}).", "Download and install now?": "Scarica e installa ora?", @@ -252,7 +251,6 @@ "Grid": "Griglia", "(from 'As You Like It', Act II)": "(da 'Come vi piace', Atto II)", "Link Color": "Colore link", - "Volume Keys for Page Flip": "Tasti volume per girare pagina", "Screen": "Schermo", "Orientation": "Orientamento", "Portrait": "Ritratto", @@ -1437,5 +1435,27 @@ "Start your library": "Inizia la tua biblioteca", "Pick a book from your device to add it to your library.": "Scegli un libro dal tuo dispositivo per aggiungerlo alla tua biblioteca.", "Drop a book anywhere on this window, or pick one from your computer.": "Trascina un libro in qualsiasi punto di questa finestra, o sceglilo dal tuo computer.", - "Sign in to sync your library": "Accedi per sincronizzare la tua biblioteca" + "Sign in to sync your library": "Accedi per sincronizzare la tua biblioteca", + "Clear": "Cancella", + "Listening…": "In ascolto…", + "Set key": "Imposta tasto", + "Page Turner": "Giratore di pagine", + "Media Next": "Multimediale: Successivo", + "Media Previous": "Multimediale: Precedente", + "Media Play/Pause": "Multimediale: Riproduci/Pausa", + "Media Fast Forward": "Multimediale: Avanzamento rapido", + "Media Rewind": "Multimediale: Riavvolgimento", + "Volume Up": "Volume su", + "Volume Down": "Volume giù", + "Arrow Left": "Freccia sinistra", + "Arrow Right": "Freccia destra", + "Arrow Up": "Freccia su", + "Arrow Down": "Freccia giù", + "Page Up": "Pag su", + "Page Down": "Pag giù", + "Space": "Spazio", + "Enter": "Invio", + "Press a button on your remote controller or keyboard after tapping \"Set key\".": "Premi un pulsante sul telecomando o sulla tastiera dopo aver toccato \"Imposta tasto\".", + "Use Volume Keys": "Usa i tasti del volume", + "Custom Page Turner": "Giratore di pagine personalizzato" } diff --git a/apps/readest-app/public/locales/ja/translation.json b/apps/readest-app/public/locales/ja/translation.json index 4587b8f3..bd352e58 100644 --- a/apps/readest-app/public/locales/ja/translation.json +++ b/apps/readest-app/public/locales/ja/translation.json @@ -228,7 +228,6 @@ "CJK Font": "CJKフォント", "Clear Search": "検索をクリア", "Header & Footer": "ヘッダーとフッター", - "Apply also in Scrolled Mode": "スクロールモードでも適用", "A new version of Readest is available!": "新しいバージョンのReadestが利用可能です!", "Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}}が利用可能です(インストールされたバージョン{{currentVersion}})。", "Download and install now?": "今すぐダウンロードしてインストールしますか?", @@ -252,7 +251,6 @@ "Grid": "グリッド", "(from 'As You Like It', Act II)": "(「お気に召すまま」第2幕)", "Link Color": "リンクカラー", - "Volume Keys for Page Flip": "音量キーでページめくり", "Screen": "画面", "Orientation": "向き", "Portrait": "縦", @@ -1387,5 +1385,27 @@ "Start your library": "ライブラリーを作成しましょう", "Pick a book from your device to add it to your library.": "デバイスから本を選んでライブラリーに追加しましょう。", "Drop a book anywhere on this window, or pick one from your computer.": "このウィンドウの好きな場所に本をドロップするか、コンピューターから選んでください。", - "Sign in to sync your library": "サインインしてライブラリーを同期" + "Sign in to sync your library": "サインインしてライブラリーを同期", + "Clear": "クリア", + "Listening…": "待機中…", + "Set key": "キーを設定", + "Page Turner": "ページターナー", + "Media Next": "メディア: 次へ", + "Media Previous": "メディア: 前へ", + "Media Play/Pause": "メディア: 再生/一時停止", + "Media Fast Forward": "メディア: 早送り", + "Media Rewind": "メディア: 巻き戻し", + "Volume Up": "音量を上げる", + "Volume Down": "音量を下げる", + "Arrow Left": "左矢印", + "Arrow Right": "右矢印", + "Arrow Up": "上矢印", + "Arrow Down": "下矢印", + "Page Up": "ページアップ", + "Page Down": "ページダウン", + "Space": "スペース", + "Enter": "エンター", + "Press a button on your remote controller or keyboard after tapping \"Set key\".": "「キーを設定」をタップした後、リモコンまたはキーボードのボタンを押してください。", + "Use Volume Keys": "音量キーを使用", + "Custom Page Turner": "カスタムページターナー" } diff --git a/apps/readest-app/public/locales/ko/translation.json b/apps/readest-app/public/locales/ko/translation.json index 200cd1e0..77dbe3eb 100644 --- a/apps/readest-app/public/locales/ko/translation.json +++ b/apps/readest-app/public/locales/ko/translation.json @@ -228,7 +228,6 @@ "CJK Font": "CJK 글꼴", "Clear Search": "검색 지우기", "Header & Footer": "헤더 및 푸터", - "Apply also in Scrolled Mode": "스크롤 모드에서도 적용", "A new version of Readest is available!": "Readest의 새 버전이 있습니다!", "Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}}가 사용 가능 (설치된 버전 {{currentVersion}}).", "Download and install now?": "지금 다운로드하고 설치하시겠습니까?", @@ -252,7 +251,6 @@ "Grid": "격자", "(from 'As You Like It', Act II)": "(당신이 좋아하는 대로, 2막)", "Link Color": "링크 색상", - "Volume Keys for Page Flip": "페이지 넘김을 위한 볼륨 키", "Screen": "화면", "Orientation": "방향", "Portrait": "세로", @@ -1387,5 +1385,27 @@ "Start your library": "서재 만들기", "Pick a book from your device to add it to your library.": "기기에서 책을 골라 서재에 추가하세요.", "Drop a book anywhere on this window, or pick one from your computer.": "이 창의 아무 곳에나 책을 놓거나, 컴퓨터에서 골라 보세요.", - "Sign in to sync your library": "로그인하여 서재 동기화" + "Sign in to sync your library": "로그인하여 서재 동기화", + "Clear": "지우기", + "Listening…": "대기 중…", + "Set key": "키 설정", + "Page Turner": "페이지 터너", + "Media Next": "미디어: 다음", + "Media Previous": "미디어: 이전", + "Media Play/Pause": "미디어: 재생/일시정지", + "Media Fast Forward": "미디어: 빨리 감기", + "Media Rewind": "미디어: 되감기", + "Volume Up": "볼륨 높이기", + "Volume Down": "볼륨 낮추기", + "Arrow Left": "왼쪽 화살표", + "Arrow Right": "오른쪽 화살표", + "Arrow Up": "위쪽 화살표", + "Arrow Down": "아래쪽 화살표", + "Page Up": "Page Up", + "Page Down": "Page Down", + "Space": "스페이스", + "Enter": "Enter", + "Press a button on your remote controller or keyboard after tapping \"Set key\".": "\"키 설정\"을 탭한 후 리모컨 또는 키보드의 버튼을 누르세요.", + "Use Volume Keys": "볼륨 키 사용", + "Custom Page Turner": "사용자 지정 페이지 넘기기" } diff --git a/apps/readest-app/public/locales/ms/translation.json b/apps/readest-app/public/locales/ms/translation.json index 05a3a088..889db88e 100644 --- a/apps/readest-app/public/locales/ms/translation.json +++ b/apps/readest-app/public/locales/ms/translation.json @@ -502,7 +502,6 @@ "Swap Click Sides": "Tukar Sisi Klik", "Disable Double Tap": "Lumpuhkan Ketuk Dua Kali", "Disable Double Click": "Lumpuhkan Klik Dua Kali", - "Volume Keys for Page Flip": "Kekunci Volum untuk Balik Halaman", "Animation": "Animasi", "Paging Animation": "Animasi Halaman", "Device": "Peranti", @@ -584,7 +583,6 @@ "Maximum Number of Columns": "Bilangan Maksimum Lajur", "Maximum Column Height": "Tinggi Maksimum Lajur", "Maximum Column Width": "Lebar Maksimum Lajur", - "Apply also in Scrolled Mode": "Gunakan juga dalam Mod Tatal", "Header & Footer": "Pengepala & Pengaki", "Show Header": "Tunjuk Pengepala", "Show Footer": "Tunjuk Pengaki", @@ -1387,5 +1385,27 @@ "Start your library": "Mulakan perpustakaan anda", "Pick a book from your device to add it to your library.": "Pilih buku dari peranti anda untuk ditambahkan ke perpustakaan anda.", "Drop a book anywhere on this window, or pick one from your computer.": "Lepaskan buku di mana-mana pada tetingkap ini, atau pilih satu dari komputer anda.", - "Sign in to sync your library": "Daftar masuk untuk menyegerakkan perpustakaan anda" + "Sign in to sync your library": "Daftar masuk untuk menyegerakkan perpustakaan anda", + "Clear": "Kosongkan", + "Listening…": "Mendengar…", + "Set key": "Tetapkan kekunci", + "Page Turner": "Pembalik Halaman", + "Media Next": "Media Seterusnya", + "Media Previous": "Media Sebelumnya", + "Media Play/Pause": "Media Main/Jeda", + "Media Fast Forward": "Media Maju Laju", + "Media Rewind": "Media Undur", + "Volume Up": "Naikkan Kelantangan", + "Volume Down": "Turunkan Kelantangan", + "Arrow Left": "Anak Panah Kiri", + "Arrow Right": "Anak Panah Kanan", + "Arrow Up": "Anak Panah Atas", + "Arrow Down": "Anak Panah Bawah", + "Page Up": "Halaman Atas", + "Page Down": "Halaman Bawah", + "Space": "Ruang", + "Enter": "Enter", + "Press a button on your remote controller or keyboard after tapping \"Set key\".": "Tekan butang pada pengawal jauh atau papan kekunci anda selepas mengetuk \"Tetapkan kekunci\".", + "Use Volume Keys": "Guna Kekunci Kelantangan", + "Custom Page Turner": "Pembalik Halaman Tersuai" } diff --git a/apps/readest-app/public/locales/nl/translation.json b/apps/readest-app/public/locales/nl/translation.json index 260d1487..041f2e3a 100644 --- a/apps/readest-app/public/locales/nl/translation.json +++ b/apps/readest-app/public/locales/nl/translation.json @@ -168,7 +168,6 @@ "Header & Footer": "Koptekst & voettekst", "Show Header": "Koptekst weergeven", "Show Footer": "Voettekst weergeven", - "Apply also in Scrolled Mode": "Ook toepassen in scroll-modus", "Auto": "Automatisch", "Language": "Taal", "Animation": "Animatie", @@ -178,7 +177,6 @@ "Portrait": "Portret", "Landscape": "Landschap", "Behavior": "Gedrag", - "Volume Keys for Page Flip": "Volumetoetsen voor pagina omslaan", "Apply": "Toepassen", "Font": "Lettertype", "Layout": "Opmaak", @@ -1412,5 +1410,27 @@ "Start your library": "Begin je bibliotheek", "Pick a book from your device to add it to your library.": "Kies een boek van je apparaat om aan je bibliotheek toe te voegen.", "Drop a book anywhere on this window, or pick one from your computer.": "Sleep een boek ergens in dit venster, of kies er een van je computer.", - "Sign in to sync your library": "Meld je aan om je bibliotheek te synchroniseren" + "Sign in to sync your library": "Meld je aan om je bibliotheek te synchroniseren", + "Clear": "Wissen", + "Listening…": "Luisteren…", + "Set key": "Toets instellen", + "Page Turner": "Pagina-omslaan", + "Media Next": "Media: Volgende", + "Media Previous": "Media: Vorige", + "Media Play/Pause": "Media: Afspelen/Pauzeren", + "Media Fast Forward": "Media: Snel vooruitspoelen", + "Media Rewind": "Media: Terugspoelen", + "Volume Up": "Volume omhoog", + "Volume Down": "Volume omlaag", + "Arrow Left": "Pijl links", + "Arrow Right": "Pijl rechts", + "Arrow Up": "Pijl omhoog", + "Arrow Down": "Pijl omlaag", + "Page Up": "Page Up", + "Page Down": "Page Down", + "Space": "Spatie", + "Enter": "Enter", + "Press a button on your remote controller or keyboard after tapping \"Set key\".": "Druk op een knop op uw afstandsbediening of toetsenbord nadat u op \"Toets instellen\" hebt getikt.", + "Use Volume Keys": "Volumetoetsen gebruiken", + "Custom Page Turner": "Aangepaste paginaknop" } diff --git a/apps/readest-app/public/locales/pl/translation.json b/apps/readest-app/public/locales/pl/translation.json index ff23df8e..0ff51aaa 100644 --- a/apps/readest-app/public/locales/pl/translation.json +++ b/apps/readest-app/public/locales/pl/translation.json @@ -228,7 +228,6 @@ "CJK Font": "Czcionka CJK", "Clear Search": "Wyczyść wyszukiwanie", "Header & Footer": "Nagłówek i stopka", - "Apply also in Scrolled Mode": "Zastosuj również w trybie przewijania", "A new version of Readest is available!": "Nowa wersja Readest jest dostępna!", "Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} jest dostępny (zainstalowana wersja {{currentVersion}}).", "Download and install now?": "Pobierz i zainstaluj teraz?", @@ -252,7 +251,6 @@ "Grid": "Siatka", "(from 'As You Like It', Act II)": "(z „Jak wam się podoba”, Akt II)", "Link Color": "Kolor linku", - "Volume Keys for Page Flip": "Klucze głośności do przewracania stron", "Screen": "Ekran", "Orientation": "Orientacja", "Portrait": "Portret", @@ -1462,5 +1460,27 @@ "Start your library": "Rozpocznij swoją bibliotekę", "Pick a book from your device to add it to your library.": "Wybierz książkę z urządzenia, aby dodać ją do swojej biblioteki.", "Drop a book anywhere on this window, or pick one from your computer.": "Upuść książkę w dowolnym miejscu tego okna lub wybierz ją z komputera.", - "Sign in to sync your library": "Zaloguj się, aby zsynchronizować bibliotekę" + "Sign in to sync your library": "Zaloguj się, aby zsynchronizować bibliotekę", + "Clear": "Wyczyść", + "Listening…": "Nasłuchiwanie…", + "Set key": "Ustaw klawisz", + "Page Turner": "Przewijacz stron", + "Media Next": "Media: Następny", + "Media Previous": "Media: Poprzedni", + "Media Play/Pause": "Media: Odtwórz/Pauza", + "Media Fast Forward": "Media: Przewiń do przodu", + "Media Rewind": "Media: Przewiń do tyłu", + "Volume Up": "Głośność+", + "Volume Down": "Głośność−", + "Arrow Left": "Strzałka w lewo", + "Arrow Right": "Strzałka w prawo", + "Arrow Up": "Strzałka w górę", + "Arrow Down": "Strzałka w dół", + "Page Up": "Page Up", + "Page Down": "Page Down", + "Space": "Spacja", + "Enter": "Enter", + "Press a button on your remote controller or keyboard after tapping \"Set key\".": "Naciśnij przycisk na pilocie lub klawiaturze po dotknięciu \"Ustaw klawisz\".", + "Use Volume Keys": "Używaj klawiszy głośności", + "Custom Page Turner": "Własne przełączanie stron" } diff --git a/apps/readest-app/public/locales/pt-BR/translation.json b/apps/readest-app/public/locales/pt-BR/translation.json index f047c30c..3c113e1f 100644 --- a/apps/readest-app/public/locales/pt-BR/translation.json +++ b/apps/readest-app/public/locales/pt-BR/translation.json @@ -1046,7 +1046,6 @@ "Swap Click Sides": "Trocar lados do clique", "Disable Double Tap": "Desativar toque duplo", "Disable Double Click": "Desativar clique duplo", - "Volume Keys for Page Flip": "Teclas de volume para virar página", "Show Page Navigation Buttons": "Botões de navegação", "Annotation Tools": "Ferramentas de anotação", "Enable Quick Actions": "Ativar ações rápidas", @@ -1167,7 +1166,6 @@ "Maximum Number of Columns": "Número máximo de colunas", "Maximum Column Height": "Altura máxima da coluna", "Maximum Column Width": "Largura máxima da coluna", - "Apply also in Scrolled Mode": "Aplicar também no modo de rolagem", "Header & Footer": "Cabeçalho e rodapé", "Show Header": "Mostrar cabeçalho", "Show Footer": "Mostrar rodapé", @@ -1437,5 +1435,27 @@ "Start your library": "Comece sua biblioteca", "Pick a book from your device to add it to your library.": "Escolha um livro do seu dispositivo para adicionar à sua biblioteca.", "Drop a book anywhere on this window, or pick one from your computer.": "Solte um livro em qualquer parte desta janela, ou escolha um do seu computador.", - "Sign in to sync your library": "Entre para sincronizar sua biblioteca" + "Sign in to sync your library": "Entre para sincronizar sua biblioteca", + "Clear": "Limpar", + "Listening…": "Aguardando…", + "Set key": "Definir tecla", + "Page Turner": "Virador de páginas", + "Media Next": "Mídia: Próxima", + "Media Previous": "Mídia: Anterior", + "Media Play/Pause": "Mídia: Reproduzir/Pausar", + "Media Fast Forward": "Mídia: Avançar", + "Media Rewind": "Mídia: Retroceder", + "Volume Up": "Volume para cima", + "Volume Down": "Volume para baixo", + "Arrow Left": "Seta para esquerda", + "Arrow Right": "Seta para direita", + "Arrow Up": "Seta para cima", + "Arrow Down": "Seta para baixo", + "Page Up": "Page Up", + "Page Down": "Page Down", + "Space": "Espaço", + "Enter": "Enter", + "Press a button on your remote controller or keyboard after tapping \"Set key\".": "Pressione um botão no controle remoto ou teclado após tocar em \"Definir tecla\".", + "Use Volume Keys": "Usar teclas de volume", + "Custom Page Turner": "Virador de páginas personalizado" } diff --git a/apps/readest-app/public/locales/pt/translation.json b/apps/readest-app/public/locales/pt/translation.json index a75e7d78..2559fc3c 100644 --- a/apps/readest-app/public/locales/pt/translation.json +++ b/apps/readest-app/public/locales/pt/translation.json @@ -228,7 +228,6 @@ "CJK Font": "Fonte CJK", "Clear Search": "Limpar Pesquisa", "Header & Footer": "Cabeçalho e Rodapé", - "Apply also in Scrolled Mode": "Aplicar também no Modo Rolado", "A new version of Readest is available!": "Uma nova versão do Readest está disponível!", "Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} está disponível (versão instalada {{currentVersion}}).", "Download and install now?": "Baixar e instalar agora?", @@ -252,7 +251,6 @@ "Grid": "Grade", "(from 'As You Like It', Act II)": "(de 'Como Gostais', Ato II)", "Link Color": "Cor do Link", - "Volume Keys for Page Flip": "Teclas de Volume para Virar Página", "Screen": "Tela", "Orientation": "Orientação", "Portrait": "Retrato", @@ -1437,5 +1435,27 @@ "Start your library": "Comece a sua biblioteca", "Pick a book from your device to add it to your library.": "Escolha um livro do seu dispositivo para adicionar à sua biblioteca.", "Drop a book anywhere on this window, or pick one from your computer.": "Largue um livro em qualquer parte desta janela, ou escolha um do seu computador.", - "Sign in to sync your library": "Inicie sessão para sincronizar a sua biblioteca" + "Sign in to sync your library": "Inicie sessão para sincronizar a sua biblioteca", + "Clear": "Limpar", + "Listening…": "A aguardar…", + "Set key": "Definir tecla", + "Page Turner": "Virador de páginas", + "Media Next": "Multimédia: Seguinte", + "Media Previous": "Multimédia: Anterior", + "Media Play/Pause": "Multimédia: Reproduzir/Pausar", + "Media Fast Forward": "Multimédia: Avançar rapidamente", + "Media Rewind": "Multimédia: Retroceder", + "Volume Up": "Volume+", + "Volume Down": "Volume−", + "Arrow Left": "Seta esquerda", + "Arrow Right": "Seta direita", + "Arrow Up": "Seta cima", + "Arrow Down": "Seta baixo", + "Page Up": "Page Up", + "Page Down": "Page Down", + "Space": "Espaço", + "Enter": "Enter", + "Press a button on your remote controller or keyboard after tapping \"Set key\".": "Pressione um botão no seu controle remoto ou teclado após tocar em \"Definir tecla\".", + "Use Volume Keys": "Usar teclas de volume", + "Custom Page Turner": "Virador de página personalizado" } diff --git a/apps/readest-app/public/locales/ro/translation.json b/apps/readest-app/public/locales/ro/translation.json index c92ecc6d..a801c37d 100644 --- a/apps/readest-app/public/locales/ro/translation.json +++ b/apps/readest-app/public/locales/ro/translation.json @@ -918,7 +918,6 @@ "Swap Click Sides": "Schimbă părțile de clic", "Disable Double Tap": "Dezactivează dubla apăsare", "Disable Double Click": "Dezactivează dublu clic", - "Volume Keys for Page Flip": "Taste de volum pentru schimbarea paginii", "Show Page Navigation Buttons": "Afișează butoanele de navigare pe pagină", "Annotation Tools": "Instrumente de adnotare", "Enable Quick Actions": "Activează acțiunile rapide", @@ -1006,7 +1005,6 @@ "Maximum Number of Columns": "Număr maxim de coloane", "Maximum Column Height": "Înălțimea maximă a coloanei", "Maximum Column Width": "Lățimea maximă a coloanei", - "Apply also in Scrolled Mode": "Aplică și în modul Derulare", "Header & Footer": "Antet și Subsol", "Show Header": "Afișează antetul", "Show Footer": "Afișează subsolul", @@ -1437,5 +1435,27 @@ "Start your library": "Începe-ți biblioteca", "Pick a book from your device to add it to your library.": "Alege o carte de pe dispozitiv pentru a o adăuga în biblioteca ta.", "Drop a book anywhere on this window, or pick one from your computer.": "Lasă o carte oriunde în această fereastră sau alege una de pe computer.", - "Sign in to sync your library": "Conectează-te pentru a-ți sincroniza biblioteca" + "Sign in to sync your library": "Conectează-te pentru a-ți sincroniza biblioteca", + "Clear": "Șterge", + "Listening…": "Ascultă…", + "Set key": "Setează tasta", + "Page Turner": "Dispozitiv de întoarcere pagini", + "Media Next": "Media Următor", + "Media Previous": "Media Anterior", + "Media Play/Pause": "Media Redare/Pauză", + "Media Fast Forward": "Media Derulare înainte", + "Media Rewind": "Media Derulare înapoi", + "Volume Up": "Volum mai mare", + "Volume Down": "Volum mai mic", + "Arrow Left": "Săgeată stânga", + "Arrow Right": "Săgeată dreapta", + "Arrow Up": "Săgeată sus", + "Arrow Down": "Săgeată jos", + "Page Up": "Page Up", + "Page Down": "Page Down", + "Space": "Spațiu", + "Enter": "Enter", + "Press a button on your remote controller or keyboard after tapping \"Set key\".": "Apăsați un buton pe telecomandă sau tastatură după ce atingeți \"Setează tasta\".", + "Use Volume Keys": "Folosiți tastele de volum", + "Custom Page Turner": "Dispozitiv personalizat de întoarcere pagini" } diff --git a/apps/readest-app/public/locales/ru/translation.json b/apps/readest-app/public/locales/ru/translation.json index fe1cc786..4e7893cb 100644 --- a/apps/readest-app/public/locales/ru/translation.json +++ b/apps/readest-app/public/locales/ru/translation.json @@ -228,7 +228,6 @@ "CJK Font": "CJK Шрифт", "Clear Search": "Очистить поиск", "Header & Footer": "Заголовок и нижний колонтитул", - "Apply also in Scrolled Mode": "Применить также в прокручиваемом режиме", "A new version of Readest is available!": "Доступна новая версия Readest!", "Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} доступен (установленная версия {{currentVersion}}).", "Download and install now?": "Скачать и установить сейчас?", @@ -252,7 +251,6 @@ "Grid": "Сетка", "(from 'As You Like It', Act II)": "(из 'Как вам это понравится', Акт II)", "Link Color": "Цвет ссылки", - "Volume Keys for Page Flip": "Клавиши громкости для перелистывания страниц", "Screen": "Экран", "Orientation": "Ориентация", "Portrait": "Портрет", @@ -1462,5 +1460,27 @@ "Start your library": "Создайте свою библиотеку", "Pick a book from your device to add it to your library.": "Выберите книгу с устройства, чтобы добавить её в библиотеку.", "Drop a book anywhere on this window, or pick one from your computer.": "Перетащите книгу в любое место этого окна или выберите её на компьютере.", - "Sign in to sync your library": "Войдите, чтобы синхронизировать библиотеку" + "Sign in to sync your library": "Войдите, чтобы синхронизировать библиотеку", + "Clear": "Очистить", + "Listening…": "Ожидание…", + "Set key": "Назначить клавишу", + "Page Turner": "Листалка страниц", + "Media Next": "Медиа: Следующий", + "Media Previous": "Медиа: Предыдущий", + "Media Play/Pause": "Медиа: Воспроизведение/Пауза", + "Media Fast Forward": "Медиа: Перемотка вперёд", + "Media Rewind": "Медиа: Перемотка назад", + "Volume Up": "Громкость+", + "Volume Down": "Громкость−", + "Arrow Left": "Стрелка влево", + "Arrow Right": "Стрелка вправо", + "Arrow Up": "Стрелка вверх", + "Arrow Down": "Стрелка вниз", + "Page Up": "Page Up", + "Page Down": "Page Down", + "Space": "Пробел", + "Enter": "Enter", + "Press a button on your remote controller or keyboard after tapping \"Set key\".": "Нажмите кнопку на пульте дистанционного управления или клавиатуре после нажатия \"Назначить клавишу\".", + "Use Volume Keys": "Использовать клавиши громкости", + "Custom Page Turner": "Пользовательское перелистывание страниц" } diff --git a/apps/readest-app/public/locales/si/translation.json b/apps/readest-app/public/locales/si/translation.json index 7d924ee9..28d9fbd2 100644 --- a/apps/readest-app/public/locales/si/translation.json +++ b/apps/readest-app/public/locales/si/translation.json @@ -195,7 +195,6 @@ "Scrolled Mode": "අනුචලන ආකාරය", "Overlap Pixels": "අතිච්ඡාදන පික්සල්", "Disable Double Click": "ද්විත්ව ක්ලික් අබල කරන්න", - "Volume Keys for Page Flip": "පිටු පෙරැළීම සඳහා ශබ්ද යතුර", "Animation": "සජීවිකරණය", "Paging Animation": "පිටු සජීවිකරණය", "Security": "ආරක්ෂාව", @@ -269,7 +268,6 @@ "Reading Progress Style": "කියවීමේ ප්‍රගති ශෛලිය", "Page Number": "පිටු අංකය", "Percentage": "ප්‍රතිශතය", - "Apply also in Scrolled Mode": "අනුචලන ආකාරයේදීද අනුගත කරන්න", "Screen": "තිරය", "Orientation": "දිශානතිය", "Portrait": "සිරස්", @@ -1412,5 +1410,27 @@ "Start your library": "ඔබේ පුස්තකාලය ආරම්භ කරන්න", "Pick a book from your device to add it to your library.": "ඔබේ පුස්තකාලයට එක් කිරීමට ඔබේ උපාංගයෙන් පොතක් තෝරන්න.", "Drop a book anywhere on this window, or pick one from your computer.": "මෙම කවුළුවේ ඕනෑම තැනක පොතක් දමන්න, නැතහොත් ඔබේ පරිගණකයෙන් එකක් තෝරන්න.", - "Sign in to sync your library": "ඔබේ පුස්තකාලය සමමුහුර්ත කිරීමට පුරනය වන්න" + "Sign in to sync your library": "ඔබේ පුස්තකාලය සමමුහුර්ත කිරීමට පුරනය වන්න", + "Clear": "හිස් කරන්න", + "Listening…": "සවන් දෙමින්…", + "Set key": "යතුර සකසන්න", + "Page Turner": "පිටු හරවනය", + "Media Next": "මාධ්‍ය ඊළඟ", + "Media Previous": "මාධ්‍ය පෙර", + "Media Play/Pause": "මාධ්‍ය ධාවනය/විරාමය", + "Media Fast Forward": "මාධ්‍ය වේගයෙන් ඉදිරියට", + "Media Rewind": "මාධ්‍ය ආපසු", + "Volume Up": "ශබ්දය වැඩි කරන්න", + "Volume Down": "ශබ්දය අඩු කරන්න", + "Arrow Left": "වම් ඊතලය", + "Arrow Right": "දකුණු ඊතලය", + "Arrow Up": "ඉහළ ඊතලය", + "Arrow Down": "පහළ ඊතලය", + "Page Up": "පිටුව ඉහළට", + "Page Down": "පිටුව පහළට", + "Space": "අවකාශය", + "Enter": "Enter", + "Press a button on your remote controller or keyboard after tapping \"Set key\".": "\"යතුර සකසන්න\" තට්ටු කිරීමෙන් පසු ඔබේ දුරස්ථ පාලකයේ හෝ යතුරු පුවරුවේ බොත්තමක් එබෙන්න.", + "Use Volume Keys": "ශබ්ද කතා යතුරු භාවිතා කරන්න", + "Custom Page Turner": "අභිරුචි පිටු හරවනය" } diff --git a/apps/readest-app/public/locales/sl/translation.json b/apps/readest-app/public/locales/sl/translation.json index 6e649761..a25c8790 100644 --- a/apps/readest-app/public/locales/sl/translation.json +++ b/apps/readest-app/public/locales/sl/translation.json @@ -889,7 +889,6 @@ "Swap Click Sides": "Zamenjaj strani za klike", "Disable Double Tap": "Onemogoči dvojni tap", "Disable Double Click": "Onemogoči dvojni klik", - "Volume Keys for Page Flip": "Tipke za glasnost za obračanje strani", "Show Page Navigation Buttons": "Prikaži gumbe za navigacijo po straneh", "Annotation Tools": "Orodja za opombe", "Enable Quick Actions": "Omogoči hitra dejanja", @@ -978,7 +977,6 @@ "Maximum Number of Columns": "Največje število stolpcev", "Maximum Column Height": "Največja višina stolpca", "Maximum Column Width": "Največja širina stolpca", - "Apply also in Scrolled Mode": "Uporabi tudi v načinu pomikanja", "Header & Footer": "Glava in noga", "Show Header": "Prikaži glavo", "Show Footer": "Prikaži nogo", @@ -1462,5 +1460,27 @@ "Start your library": "Začnite svojo knjižnico", "Pick a book from your device to add it to your library.": "Izberite knjigo iz svoje naprave, da jo dodate v svojo knjižnico.", "Drop a book anywhere on this window, or pick one from your computer.": "Spustite knjigo kjer koli v tem oknu ali jo izberite iz svojega računalnika.", - "Sign in to sync your library": "Prijavite se za sinhronizacijo svoje knjižnice" + "Sign in to sync your library": "Prijavite se za sinhronizacijo svoje knjižnice", + "Clear": "Počisti", + "Listening…": "Poslušanje…", + "Set key": "Nastavi tipko", + "Page Turner": "Obračalnik strani", + "Media Next": "Mediji: Naslednje", + "Media Previous": "Mediji: Prejšnje", + "Media Play/Pause": "Mediji: Predvajaj/Premor", + "Media Fast Forward": "Mediji: Hitro naprej", + "Media Rewind": "Mediji: Previj nazaj", + "Volume Up": "Glasnost+", + "Volume Down": "Glasnost−", + "Arrow Left": "Puščica levo", + "Arrow Right": "Puščica desno", + "Arrow Up": "Puščica gor", + "Arrow Down": "Puščica dol", + "Page Up": "Page Up", + "Page Down": "Page Down", + "Space": "Preslednica", + "Enter": "Enter", + "Press a button on your remote controller or keyboard after tapping \"Set key\".": "Pritisnite gumb na daljinskem upravljalniku ali tipkovnici po tapkanju na \"Nastavi tipko\".", + "Use Volume Keys": "Uporabi tipki za glasnost", + "Custom Page Turner": "Tipka za obračanje strani po meri" } diff --git a/apps/readest-app/public/locales/sv/translation.json b/apps/readest-app/public/locales/sv/translation.json index e9c93f12..593bd40e 100644 --- a/apps/readest-app/public/locales/sv/translation.json +++ b/apps/readest-app/public/locales/sv/translation.json @@ -441,7 +441,6 @@ "Scroll": "Rullning", "Overlap Pixels": "Överlappande pixlar", "Disable Double Click": "Inaktivera dubbelklick", - "Volume Keys for Page Flip": "Volymknappar för sidbyte", "Animation": "Animation", "Paging Animation": "Sidanimation", "Security": "Säkerhet", @@ -511,7 +510,6 @@ "Reading Progress Style": "Läsframstegsstil", "Page Number": "Sidnummer", "Percentage": "Procent", - "Apply also in Scrolled Mode": "Tillämpa även i rullningsläge", "Screen": "Skärm", "Orientation": "Orientering", "Portrait": "Porträtt", @@ -1412,5 +1410,27 @@ "Start your library": "Starta ditt bibliotek", "Pick a book from your device to add it to your library.": "Välj en bok från din enhet att lägga till i ditt bibliotek.", "Drop a book anywhere on this window, or pick one from your computer.": "Släpp en bok var som helst på det här fönstret, eller välj en från din dator.", - "Sign in to sync your library": "Logga in för att synkronisera ditt bibliotek" + "Sign in to sync your library": "Logga in för att synkronisera ditt bibliotek", + "Clear": "Rensa", + "Listening…": "Lyssnar…", + "Set key": "Ange tangent", + "Page Turner": "Sidvändare", + "Media Next": "Media: Nästa", + "Media Previous": "Media: Föregående", + "Media Play/Pause": "Media: Spela/Pausa", + "Media Fast Forward": "Media: Snabbspolning framåt", + "Media Rewind": "Media: Snabbspolning bakåt", + "Volume Up": "Volym upp", + "Volume Down": "Volym ned", + "Arrow Left": "Pil vänster", + "Arrow Right": "Pil höger", + "Arrow Up": "Pil upp", + "Arrow Down": "Pil ned", + "Page Up": "Page Up", + "Page Down": "Page Down", + "Space": "Mellanslag", + "Enter": "Enter", + "Press a button on your remote controller or keyboard after tapping \"Set key\".": "Tryck på en knapp på din fjärrkontroll eller tangentbord efter att ha tryckt på \"Ange tangent\".", + "Use Volume Keys": "Använd volymknappar", + "Custom Page Turner": "Anpassad sidvändare" } diff --git a/apps/readest-app/public/locales/ta/translation.json b/apps/readest-app/public/locales/ta/translation.json index f02bccf2..cc0f8574 100644 --- a/apps/readest-app/public/locales/ta/translation.json +++ b/apps/readest-app/public/locales/ta/translation.json @@ -195,7 +195,6 @@ "Scrolled Mode": "உருட்டல் பயன்முறை", "Overlap Pixels": "ஒன்றுடன் ஒன்று பிக்சல்கள்", "Disable Double Click": "இரட்டை கிளிக்கை முடக்கவும்", - "Volume Keys for Page Flip": "பக்கம் புரட்டுவதற்கு ஒலி விசைகள்", "Animation": "அனிமேஷன்", "Paging Animation": "பக்க அனிமேஷன்", "Security": "பாதுகாப்பு", @@ -269,7 +268,6 @@ "Reading Progress Style": "வாசிப்பு முன்னேற்ற பாணி", "Page Number": "பக்க எண்", "Percentage": "சதவீதம்", - "Apply also in Scrolled Mode": "உருட்டல் பயன்முறையிலும் பொருந்தும்", "Screen": "திரை", "Orientation": "திசைநிலை", "Portrait": "நிலைமான்", @@ -1412,5 +1410,27 @@ "Start your library": "உங்கள் நூலகத்தைத் தொடங்குங்கள்", "Pick a book from your device to add it to your library.": "உங்கள் நூலகத்தில் சேர்க்க உங்கள் சாதனத்திலிருந்து ஒரு புத்தகத்தைத் தேர்ந்தெடுக்கவும்.", "Drop a book anywhere on this window, or pick one from your computer.": "இந்த சாளரத்தில் எங்கேனும் ஒரு புத்தகத்தை விடவும், அல்லது உங்கள் கணினியிலிருந்து ஒன்றைத் தேர்ந்தெடுக்கவும்.", - "Sign in to sync your library": "உங்கள் நூலகத்தை ஒத்திசைக்க உள்நுழையவும்" + "Sign in to sync your library": "உங்கள் நூலகத்தை ஒத்திசைக்க உள்நுழையவும்", + "Clear": "அழி", + "Listening…": "கேட்கிறது…", + "Set key": "விசை அமை", + "Page Turner": "பக்க திருப்பி", + "Media Next": "மீடியா அடுத்தது", + "Media Previous": "மீடியா முந்தையது", + "Media Play/Pause": "மீடியா இயக்கு/இடைநிறுத்து", + "Media Fast Forward": "மீடியா வேகமாக முன்னேறு", + "Media Rewind": "மீடியா பின்னோக்கி", + "Volume Up": "ஒலி அளவு அதிகரி", + "Volume Down": "ஒலி அளவு குறை", + "Arrow Left": "இடது அம்பு", + "Arrow Right": "வலது அம்பு", + "Arrow Up": "மேல் அம்பு", + "Arrow Down": "கீழ் அம்பு", + "Page Up": "பக்கம் மேலே", + "Page Down": "பக்கம் கீழே", + "Space": "இடைவெளி", + "Enter": "என்டர்", + "Press a button on your remote controller or keyboard after tapping \"Set key\".": "\"விசை அமை\" தட்டிய பிறகு உங்கள் ரிமோட் கன்ட்ரோலர் அல்லது கீபோர்டில் ஒரு பட்டனை அழுத்தவும்.", + "Use Volume Keys": "ஒலி அளவு விசைகளைப் பயன்படுத்து", + "Custom Page Turner": "தனிப்பயன் பக்க திருப்பி" } diff --git a/apps/readest-app/public/locales/th/translation.json b/apps/readest-app/public/locales/th/translation.json index 9a290b19..a0fc66d1 100644 --- a/apps/readest-app/public/locales/th/translation.json +++ b/apps/readest-app/public/locales/th/translation.json @@ -164,7 +164,6 @@ "Scroll": "เลื่อน", "Scrolled Mode": "โหมดเลื่อน", "Overlap Pixels": "พิกเซลซ้อน", - "Volume Keys for Page Flip": "ปุ่มเสียงเพื่อพลิกหน้า", "Animation": "ภาพเคลื่อนไหว", "Paging Animation": "ภาพเคลื่อนไหวพลิกหน้า", "Security": "ความปลอดภัย", @@ -227,7 +226,6 @@ "Show Footer": "แสดงท้ายกระดาษ", "Show Remaining Time": "แสดงเวลาที่เหลือ", "Show Remaining Pages": "แสดงหน้าที่เหลือ", - "Apply also in Scrolled Mode": "ใช้ในโหมดเลื่อนด้วย", "Screen": "หน้าจอ", "Orientation": "การวางแนว", "Portrait": "แนวตั้ง", @@ -1387,5 +1385,27 @@ "Start your library": "เริ่มต้นห้องสมุดของคุณ", "Pick a book from your device to add it to your library.": "เลือกหนังสือจากอุปกรณ์ของคุณเพื่อเพิ่มลงในห้องสมุดของคุณ", "Drop a book anywhere on this window, or pick one from your computer.": "วางหนังสือที่ใดก็ได้บนหน้าต่างนี้ หรือเลือกจากคอมพิวเตอร์ของคุณ", - "Sign in to sync your library": "ลงชื่อเข้าใช้เพื่อซิงค์ห้องสมุดของคุณ" + "Sign in to sync your library": "ลงชื่อเข้าใช้เพื่อซิงค์ห้องสมุดของคุณ", + "Clear": "ล้าง", + "Listening…": "กำลังฟัง…", + "Set key": "ตั้งปุ่ม", + "Page Turner": "ตัวพลิกหน้า", + "Media Next": "มีเดียถัดไป", + "Media Previous": "มีเดียก่อนหน้า", + "Media Play/Pause": "มีเดียเล่น/หยุดชั่วคราว", + "Media Fast Forward": "มีเดียเดินหน้าเร็ว", + "Media Rewind": "มีเดียย้อนกลับ", + "Volume Up": "เพิ่มระดับเสียง", + "Volume Down": "ลดระดับเสียง", + "Arrow Left": "ลูกศรซ้าย", + "Arrow Right": "ลูกศรขวา", + "Arrow Up": "ลูกศรขึ้น", + "Arrow Down": "ลูกศรลง", + "Page Up": "หน้าขึ้น", + "Page Down": "หน้าลง", + "Space": "สเปซบาร์", + "Enter": "Enter", + "Press a button on your remote controller or keyboard after tapping \"Set key\".": "กดปุ่มบนรีโมทคอนโทรลหรือคีย์บอร์ดหลังจากแตะ \"ตั้งปุ่ม\"", + "Use Volume Keys": "ใช้ปุ่มระดับเสียง", + "Custom Page Turner": "ปุ่มพลิกหน้าแบบกำหนดเอง" } diff --git a/apps/readest-app/public/locales/tr/translation.json b/apps/readest-app/public/locales/tr/translation.json index 03fba9ff..958a71d9 100644 --- a/apps/readest-app/public/locales/tr/translation.json +++ b/apps/readest-app/public/locales/tr/translation.json @@ -228,7 +228,6 @@ "CJK Font": "CJK Yazı Tipi", "Clear Search": "Aramayı Temizle", "Header & Footer": "Başlık ve Altbilgi", - "Apply also in Scrolled Mode": "Kaydırılmış Modda da Uygula", "A new version of Readest is available!": "Yeni bir Readest sürümü mevcut!", "Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} mevcut (yüklü sürüm {{currentVersion}}).", "Download and install now?": "Şimdi indirip yükleyelim mi?", @@ -252,7 +251,6 @@ "Grid": "Izgara", "(from 'As You Like It', Act II)": "(As You Like It, 2. Perde)", "Link Color": "Bağlantı Rengi", - "Volume Keys for Page Flip": "Sayfa Çevirme için Ses Seviyesi Tuşları", "Screen": "Ekran", "Orientation": "Yönlendirme", "Portrait": "Portre", @@ -1412,5 +1410,27 @@ "Start your library": "Kütüphanenizi başlatın", "Pick a book from your device to add it to your library.": "Kütüphanenize eklemek için cihazınızdan bir kitap seçin.", "Drop a book anywhere on this window, or pick one from your computer.": "Bu pencerede herhangi bir yere bir kitap bırakın veya bilgisayarınızdan birini seçin.", - "Sign in to sync your library": "Kütüphanenizi senkronize etmek için oturum açın" + "Sign in to sync your library": "Kütüphanenizi senkronize etmek için oturum açın", + "Clear": "Temizle", + "Listening…": "Dinleniyor…", + "Set key": "Tuş ayarla", + "Page Turner": "Sayfa Çevirici", + "Media Next": "Medya: Sonraki", + "Media Previous": "Medya: Önceki", + "Media Play/Pause": "Medya: Oynat/Duraklat", + "Media Fast Forward": "Medya: Hızlı İleri", + "Media Rewind": "Medya: Geri Sar", + "Volume Up": "Ses Yükselt", + "Volume Down": "Ses Kıs", + "Arrow Left": "Sol Ok", + "Arrow Right": "Sağ Ok", + "Arrow Up": "Yukarı Ok", + "Arrow Down": "Aşağı Ok", + "Page Up": "Page Up", + "Page Down": "Page Down", + "Space": "Boşluk", + "Enter": "Enter", + "Press a button on your remote controller or keyboard after tapping \"Set key\".": "\"Tuş ayarla\" düğmesine dokunduktan sonra uzaktan kumandanızdaki veya klavyenizdeki bir düğmeye basın.", + "Use Volume Keys": "Ses Tuşlarını Kullan", + "Custom Page Turner": "Özel Sayfa Çevirici" } diff --git a/apps/readest-app/public/locales/uk/translation.json b/apps/readest-app/public/locales/uk/translation.json index a27ac4ac..0453e22d 100644 --- a/apps/readest-app/public/locales/uk/translation.json +++ b/apps/readest-app/public/locales/uk/translation.json @@ -228,7 +228,6 @@ "CJK Font": "CJK шрифт", "Clear Search": "Очистити пошук", "Header & Footer": "Заголовок і нижній колонтитул", - "Apply also in Scrolled Mode": "Застосувати також у прокрученому режимі", "A new version of Readest is available!": "Доступна нова версія Readest!", "Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Нова версія Readest {{newVersion}} доступна. (Поточна {{currentVersion}}).", "Download and install now?": "Завантажити та встановити зараз?", @@ -252,7 +251,6 @@ "Grid": "Сітка", "(from 'As You Like It', Act II)": "(із 'Як вам це подобається', Дія II)", "Link Color": "Колір посилання", - "Volume Keys for Page Flip": "Перегортати сторінки клавішами гучності", "Screen": "Екран", "Orientation": "Орієнтація", "Portrait": "Портрет", @@ -1462,5 +1460,27 @@ "Start your library": "Створіть свою бібліотеку", "Pick a book from your device to add it to your library.": "Виберіть книжку з вашого пристрою, щоб додати її до бібліотеки.", "Drop a book anywhere on this window, or pick one from your computer.": "Перетягніть книжку будь-куди у це вікно або виберіть її з комп’ютера.", - "Sign in to sync your library": "Увійдіть, щоб синхронізувати бібліотеку" + "Sign in to sync your library": "Увійдіть, щоб синхронізувати бібліотеку", + "Clear": "Очистити", + "Listening…": "Очікування…", + "Set key": "Призначити клавішу", + "Page Turner": "Перегортач сторінок", + "Media Next": "Медіа: Наступний", + "Media Previous": "Медіа: Попередній", + "Media Play/Pause": "Медіа: Відтворення/Пауза", + "Media Fast Forward": "Медіа: Перемотування вперед", + "Media Rewind": "Медіа: Перемотування назад", + "Volume Up": "Гучність+", + "Volume Down": "Гучність−", + "Arrow Left": "Стрілка ліворуч", + "Arrow Right": "Стрілка праворуч", + "Arrow Up": "Стрілка вгору", + "Arrow Down": "Стрілка вниз", + "Page Up": "Сторінка вгору", + "Page Down": "Сторінка вниз", + "Space": "Пробіл", + "Enter": "Enter", + "Press a button on your remote controller or keyboard after tapping \"Set key\".": "Натисніть кнопку на пульті або клавіатурі після натискання \"Призначити клавішу\".", + "Use Volume Keys": "Використовувати клавіші гучності", + "Custom Page Turner": "Власне перегортання сторінок" } diff --git a/apps/readest-app/public/locales/uz/translation.json b/apps/readest-app/public/locales/uz/translation.json index 80938e46..ba72eb3e 100644 --- a/apps/readest-app/public/locales/uz/translation.json +++ b/apps/readest-app/public/locales/uz/translation.json @@ -1026,7 +1026,6 @@ "Swap Click Sides": "Bosish tomonlarini almashtirish", "Disable Double Tap": "Ikki marta bosishni oʻchirish", "Disable Double Click": "Ikki marta bosishni oʻchirish", - "Volume Keys for Page Flip": "Sahifani aylantirish uchun ovoz tugmalari", "Show Page Navigation Buttons": "Sahifalararo navigatsiya tugmalarini koʻrsatish", "Annotation Tools": "Izoh vositalari", "Enable Quick Actions": "Tezkor amallarni yoqish", @@ -1146,7 +1145,6 @@ "Maximum Number of Columns": "Maksimal ustunlar soni", "Maximum Column Height": "Maksimal ustun balandligi", "Maximum Column Width": "Maksimal ustun kengligi", - "Apply also in Scrolled Mode": "Aylantirish rejimida ham qoʻllash", "Header & Footer": "Yuqori va pastki kolontitul", "Show Header": "Yuqori kolontitulni koʻrsatish", "Show Footer": "Pastki kolontitulni koʻrsatish", @@ -1412,5 +1410,27 @@ "Start your library": "Kutubxonangizni boshlang", "Pick a book from your device to add it to your library.": "Kutubxonangizga qo‘shish uchun qurilmangizdan kitob tanlang.", "Drop a book anywhere on this window, or pick one from your computer.": "Ushbu oynaning istalgan joyiga kitob tashlang yoki kompyuteringizdan birini tanlang.", - "Sign in to sync your library": "Kutubxonangizni sinxronlash uchun tizimga kiring" + "Sign in to sync your library": "Kutubxonangizni sinxronlash uchun tizimga kiring", + "Clear": "Tozalash", + "Listening…": "Tinglanmoqda…", + "Set key": "Tugmani o'rnatish", + "Page Turner": "Sahifa buruvchi", + "Media Next": "Media keyingi", + "Media Previous": "Media oldingi", + "Media Play/Pause": "Media ijro/to'xtatish", + "Media Fast Forward": "Media tez oldinga", + "Media Rewind": "Media orqaga", + "Volume Up": "Ovoz balandligi oshirish", + "Volume Down": "Ovoz balandligi kamaytirish", + "Arrow Left": "Chap o'q", + "Arrow Right": "O'ng o'q", + "Arrow Up": "Yuqori o'q", + "Arrow Down": "Pastki o'q", + "Page Up": "Sahifa yuqori", + "Page Down": "Sahifa pastga", + "Space": "Bo'sh joy", + "Enter": "Enter", + "Press a button on your remote controller or keyboard after tapping \"Set key\".": "\"Tugmani o'rnatish\" tugmasini bosgandan so'ng masofadan boshqarish pultingiz yoki klaviaturangizdagi tugmani bosing.", + "Use Volume Keys": "Ovoz balandligi tugmalarini ishlatish", + "Custom Page Turner": "Maxsus sahifa buruvchi" } diff --git a/apps/readest-app/public/locales/vi/translation.json b/apps/readest-app/public/locales/vi/translation.json index f0b3d097..76cc36a9 100644 --- a/apps/readest-app/public/locales/vi/translation.json +++ b/apps/readest-app/public/locales/vi/translation.json @@ -228,7 +228,6 @@ "CJK Font": "Phông chữ CJK", "Clear Search": "Xóa tìm kiếm", "Header & Footer": "Đầu trang & Chân trang", - "Apply also in Scrolled Mode": "Áp dụng cũng trong chế độ cuộn", "A new version of Readest is available!": "Đã có phiên bản mới của Readest!", "Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} đã có sẵn (phiên bản đã cài đặt {{currentVersion}}).", "Download and install now?": "Tải xuống và cài đặt ngay bây giờ?", @@ -252,7 +251,6 @@ "Grid": "Lưới", "(from 'As You Like It', Act II)": "(từ 'Như bạn thích', Hành động II)", "Link Color": "Màu liên kết", - "Volume Keys for Page Flip": "Phím âm lượng để lật trang", "Screen": "Màn hình", "Orientation": "Định hướng", "Portrait": "Chân dung", @@ -1387,5 +1385,27 @@ "Start your library": "Bắt đầu thư viện của bạn", "Pick a book from your device to add it to your library.": "Chọn một cuốn sách từ thiết bị để thêm vào thư viện của bạn.", "Drop a book anywhere on this window, or pick one from your computer.": "Thả một cuốn sách vào bất kỳ đâu trên cửa sổ này, hoặc chọn một cuốn từ máy tính của bạn.", - "Sign in to sync your library": "Đăng nhập để đồng bộ thư viện của bạn" + "Sign in to sync your library": "Đăng nhập để đồng bộ thư viện của bạn", + "Clear": "Xóa", + "Listening…": "Đang lắng nghe…", + "Set key": "Đặt phím", + "Page Turner": "Bộ Lật Trang", + "Media Next": "Media Tiếp Theo", + "Media Previous": "Media Trước", + "Media Play/Pause": "Media Phát/Tạm Dừng", + "Media Fast Forward": "Media Tua Nhanh", + "Media Rewind": "Media Tua Lại", + "Volume Up": "Tăng Âm Lượng", + "Volume Down": "Giảm Âm Lượng", + "Arrow Left": "Mũi Tên Trái", + "Arrow Right": "Mũi Tên Phải", + "Arrow Up": "Mũi Tên Lên", + "Arrow Down": "Mũi Tên Xuống", + "Page Up": "Trang Trên", + "Page Down": "Trang Dưới", + "Space": "Dấu Cách", + "Enter": "Enter", + "Press a button on your remote controller or keyboard after tapping \"Set key\".": "Nhấn một nút trên bộ điều khiển từ xa hoặc bàn phím sau khi chạm \"Đặt phím\".", + "Use Volume Keys": "Dùng Phím Âm Lượng", + "Custom Page Turner": "Nút Lật Trang Tùy Chỉnh" } diff --git a/apps/readest-app/public/locales/zh-CN/translation.json b/apps/readest-app/public/locales/zh-CN/translation.json index d1a1e2fc..ec350194 100644 --- a/apps/readest-app/public/locales/zh-CN/translation.json +++ b/apps/readest-app/public/locales/zh-CN/translation.json @@ -229,7 +229,6 @@ "CJK Font": "中文字体", "Clear Search": "清除搜索", "Header & Footer": "页眉页脚", - "Apply also in Scrolled Mode": "应用到滚动模式", "A new version of Readest is available!": "有新版本的 Readest 可更新!", "Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} 可更新(已安装版本 {{currentVersion}})。", "Download and install now?": "立即下载并安装?", @@ -253,7 +252,6 @@ "Grid": "网格", "(from 'As You Like It', Act II)": "出自《皆大欢喜》,第二幕", "Link Color": "链接颜色", - "Volume Keys for Page Flip": "音量键翻页", "Screen": "屏幕", "Orientation": "屏幕方向", "Portrait": "竖屏", @@ -1387,5 +1385,27 @@ "Start your library": "开启你的书库", "Pick a book from your device to add it to your library.": "从设备中挑一本书加入书库吧。", "Drop a book anywhere on this window, or pick one from your computer.": "把书拖到窗口任意位置,或从电脑中挑选一本。", - "Sign in to sync your library": "登录以同步书库" + "Sign in to sync your library": "登录以同步书库", + "Clear": "清除", + "Listening…": "监听中…", + "Set key": "设置按键", + "Page Turner": "翻页器", + "Media Next": "媒体下一首", + "Media Previous": "媒体上一首", + "Media Play/Pause": "媒体播放/暂停", + "Media Fast Forward": "媒体快进", + "Media Rewind": "媒体快退", + "Volume Up": "音量增大", + "Volume Down": "音量减小", + "Arrow Left": "左箭头", + "Arrow Right": "右箭头", + "Arrow Up": "上箭头", + "Arrow Down": "下箭头", + "Page Up": "向上翻页", + "Page Down": "向下翻页", + "Space": "空格键", + "Enter": "回车键", + "Press a button on your remote controller or keyboard after tapping \"Set key\".": "点击「设置按键」后,按下遥控器或键盘上的按钮。", + "Use Volume Keys": "使用音量键", + "Custom Page Turner": "自定义翻页器" } diff --git a/apps/readest-app/public/locales/zh-TW/translation.json b/apps/readest-app/public/locales/zh-TW/translation.json index 6b0ff16a..5d6f39ac 100644 --- a/apps/readest-app/public/locales/zh-TW/translation.json +++ b/apps/readest-app/public/locales/zh-TW/translation.json @@ -228,7 +228,6 @@ "CJK Font": "中文字體", "Clear Search": "清除搜索", "Header & Footer": "頁眉頁腳", - "Apply also in Scrolled Mode": "滾動模式同樣適用", "A new version of Readest is available!": "有新版本的 Readest 可更新!", "Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} 可更新(已安裝版本 {{currentVersion}})。", "Download and install now?": "立即下載並安裝?", @@ -252,7 +251,6 @@ "Grid": "網格", "(from 'As You Like It', Act II)": "(出自《皆大歡喜》,第二幕)", "Link Color": "連結顏色", - "Volume Keys for Page Flip": "音量鍵翻頁", "Screen": "螢幕", "Orientation": "螢幕方向", "Portrait": "豎屏", @@ -1387,5 +1385,27 @@ "Start your library": "開啟你的書庫", "Pick a book from your device to add it to your library.": "從裝置中挑一本書加入書庫吧。", "Drop a book anywhere on this window, or pick one from your computer.": "把書拖到視窗任一處,或從電腦中挑選一本。", - "Sign in to sync your library": "登入以同步書庫" + "Sign in to sync your library": "登入以同步書庫", + "Clear": "清除", + "Listening…": "監聽中…", + "Set key": "設定按鍵", + "Page Turner": "翻頁器", + "Media Next": "媒體下一首", + "Media Previous": "媒體上一首", + "Media Play/Pause": "媒體播放/暫停", + "Media Fast Forward": "媒體快進", + "Media Rewind": "媒體快退", + "Volume Up": "音量增大", + "Volume Down": "音量減小", + "Arrow Left": "左箭頭", + "Arrow Right": "右箭頭", + "Arrow Up": "上箭頭", + "Arrow Down": "下箭頭", + "Page Up": "向上翻頁", + "Page Down": "向下翻頁", + "Space": "空白鍵", + "Enter": "Enter 鍵", + "Press a button on your remote controller or keyboard after tapping \"Set key\".": "點擊「設定按鍵」後,按下遙控器或鍵盤上的按鈕。", + "Use Volume Keys": "使用音量鍵", + "Custom Page Turner": "自訂翻頁器" } diff --git a/apps/readest-app/src-tauri/gen/android/app/src/main/java/com/bilingify/readest/MainActivity.kt b/apps/readest-app/src-tauri/gen/android/app/src/main/java/com/bilingify/readest/MainActivity.kt index 63601f96..bbc93836 100644 --- a/apps/readest-app/src-tauri/gen/android/app/src/main/java/com/bilingify/readest/MainActivity.kt +++ b/apps/readest-app/src-tauri/gen/android/app/src/main/java/com/bilingify/readest/MainActivity.kt @@ -27,6 +27,8 @@ class MainActivity : TauriActivity(), KeyDownInterceptor { private var wv: WebView? = null private var interceptVolumeKeysEnabled = false private var interceptBackKeyEnabled = false + private var interceptPageTurnerKeysEnabled = false + private var keyLearnModeEnabled = false override fun onWebViewCreate(webView: WebView) { wv = webView @@ -38,6 +40,24 @@ class MainActivity : TauriActivity(), KeyDownInterceptor { KeyEvent.KEYCODE_VOLUME_UP to "VolumeUp" ) + private val mediaKeyMap = mapOf( + KeyEvent.KEYCODE_MEDIA_NEXT to "MediaNext", + KeyEvent.KEYCODE_MEDIA_PREVIOUS to "MediaPrevious", + KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE to "MediaPlayPause", + KeyEvent.KEYCODE_MEDIA_FAST_FORWARD to "MediaFastForward", + KeyEvent.KEYCODE_MEDIA_REWIND to "MediaRewind" + ) + + private fun keyNameFor(keyCode: Int): String = + keyEventMap[keyCode] ?: mediaKeyMap[keyCode] ?: "Keycode$keyCode" + + private fun forwardKeyToWebView(keyName: String, keyCode: Int) { + wv?.evaluateJavascript( + """try { window.onNativeKeyDown("$keyName", $keyCode); } catch (_) {}""", + null + ) + } + override fun interceptVolumeKeys(enabled: Boolean) { Log.d("MainActivity", "Intercept volume keys: $enabled") interceptVolumeKeysEnabled = enabled @@ -48,6 +68,16 @@ class MainActivity : TauriActivity(), KeyDownInterceptor { interceptBackKeyEnabled = enabled } + override fun interceptPageTurnerKeys(enabled: Boolean) { + Log.d("MainActivity", "Intercept page turner keys: $enabled") + interceptPageTurnerKeysEnabled = enabled + } + + override fun setKeyLearnMode(enabled: Boolean) { + Log.d("MainActivity", "Key learn mode: $enabled") + keyLearnModeEnabled = enabled + } + override fun dispatchTouchEvent(event: MotionEvent): Boolean { val action = when (event.actionMasked) { MotionEvent.ACTION_DOWN -> "touchstart" @@ -93,22 +123,31 @@ class MainActivity : TauriActivity(), KeyDownInterceptor { override fun dispatchKeyEvent(event: KeyEvent): Boolean { if (event.action == KeyEvent.ACTION_DOWN) { val keyCode = event.keyCode - val keyName = keyEventMap[keyCode] + // Learn mode: forward and consume every key so the settings UI + // can capture whatever the remote sends. + if (keyLearnModeEnabled && keyCode != KeyEvent.KEYCODE_BACK) { + forwardKeyToWebView(keyNameFor(keyCode), keyCode) + return true + } + + // Hardware page turner: intercept media keys when enabled. + if (interceptPageTurnerKeysEnabled && mediaKeyMap.containsKey(keyCode)) { + forwardKeyToWebView(mediaKeyMap[keyCode]!!, keyCode) + return true + } + + val keyName = keyEventMap[keyCode] if (keyName != null) { val shouldIntercept = when (keyCode) { KeyEvent.KEYCODE_BACK -> interceptBackKeyEnabled - KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_VOLUME_DOWN -> interceptVolumeKeysEnabled + KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_VOLUME_DOWN -> + interceptVolumeKeysEnabled else -> false } if (shouldIntercept) { - wv?.evaluateJavascript( - """ - try { window.onNativeKeyDown("$keyName", $keyCode); } catch (_) {} - """.trimIndent(), - null - ) + forwardKeyToWebView(keyName, keyCode) return true } } diff --git a/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/android/src/main/java/NativeBridgePlugin.kt b/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/android/src/main/java/NativeBridgePlugin.kt index 9ddf13a7..3d3d4f75 100644 --- a/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/android/src/main/java/NativeBridgePlugin.kt +++ b/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/android/src/main/java/NativeBridgePlugin.kt @@ -66,6 +66,8 @@ class SetSystemUIVisibilityRequestArgs { class InterceptKeysRequestArgs { var volumeKeys: Boolean? = null var backKey: Boolean? = null + var pageTurnerKeys: Boolean? = null + var learnMode: Boolean? = null } @InvokeArg @@ -115,6 +117,8 @@ data class PurchaseData( interface KeyDownInterceptor { fun interceptVolumeKeys(enabled: Boolean) fun interceptBackKey(enabled: Boolean) + fun interceptPageTurnerKeys(enabled: Boolean) + fun setKeyLearnMode(enabled: Boolean) } @TauriPlugin( @@ -401,16 +405,11 @@ class NativeBridgePlugin(private val activity: Activity): Plugin(activity) { fun intercept_keys(invoke: Invoke) { val args = invoke.parseArgs(InterceptKeysRequestArgs::class.java) if (activity is KeyDownInterceptor) { - when (args.backKey) { - true -> (activity as KeyDownInterceptor).interceptBackKey(true) - false -> (activity as KeyDownInterceptor).interceptBackKey(false) - else -> {} - } - when (args.volumeKeys) { - true -> (activity as KeyDownInterceptor).interceptVolumeKeys(true) - false -> (activity as KeyDownInterceptor).interceptVolumeKeys(false) - else -> {} - } + val interceptor = activity as KeyDownInterceptor + args.backKey?.let { interceptor.interceptBackKey(it) } + args.volumeKeys?.let { interceptor.interceptVolumeKeys(it) } + args.pageTurnerKeys?.let { interceptor.interceptPageTurnerKeys(it) } + args.learnMode?.let { interceptor.setKeyLearnMode(it) } } else { Log.e("NativeBridgePlugin", "Activity does not implement KeyDownInterceptor") } diff --git a/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/ios/Sources/NativeBridgePlugin.swift b/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/ios/Sources/NativeBridgePlugin.swift index e4e16a54..59f4a100 100644 --- a/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/ios/Sources/NativeBridgePlugin.swift +++ b/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/ios/Sources/NativeBridgePlugin.swift @@ -43,6 +43,8 @@ class SetSystemUIVisibilityRequestArgs: Decodable { class InterceptKeysRequestArgs: Decodable { let backKey: Bool? let volumeKeys: Bool? + let pageTurnerKeys: Bool? + let learnMode: Bool? } class LockScreenOrientationRequestArgs: Decodable { @@ -192,6 +194,44 @@ class VolumeKeyHandler: NSObject { } } +class MediaKeyHandler { + private weak var webView: WKWebView? + private var registered = false + private let commandCenter = MPRemoteCommandCenter.shared() + + func start(webView: WKWebView) { + self.webView = webView + if registered { return } + registered = true + commandCenter.nextTrackCommand.isEnabled = true + commandCenter.previousTrackCommand.isEnabled = true + commandCenter.nextTrackCommand.addTarget { [weak self] _ in + self?.forward("MediaNext") + return .success + } + commandCenter.previousTrackCommand.addTarget { [weak self] _ in + self?.forward("MediaPrevious") + return .success + } + logger.log("MediaKeyHandler: started") + } + + func stop() { + if !registered { return } + registered = false + commandCenter.nextTrackCommand.removeTarget(nil) + commandCenter.previousTrackCommand.removeTarget(nil) + logger.log("MediaKeyHandler: stopped") + } + + private func forward(_ name: String) { + DispatchQueue.main.async { [weak self] in + self?.webView?.evaluateJavaScript( + "try { window.onNativeKeyDown('\(name)', 0); } catch (_) {}", completionHandler: nil) + } + } +} + class WebViewLifecycleManager: NSObject { private weak var webView: WKWebView? private var originalNavigationDelegate: WKNavigationDelegate? @@ -503,6 +543,8 @@ class NativeBridgePlugin: Plugin { private struct AssociatedKeys { static var volumeKeyHandler = "volumeKeyHandler" static var interceptingVolumeKeys = "interceptingVolumeKeys" + static var mediaKeyHandler = "mediaKeyHandler" + static var mediaKeyState = "mediaKeyState" } private var volumeKeyHandler: VolumeKeyHandler? { @@ -526,6 +568,44 @@ class NativeBridgePlugin: Plugin { } } + private var mediaKeyHandler: MediaKeyHandler? { + get { + return objc_getAssociatedObject(self, &AssociatedKeys.mediaKeyHandler) as? MediaKeyHandler + } + set { + objc_setAssociatedObject( + self, &AssociatedKeys.mediaKeyHandler, newValue, .OBJC_ASSOCIATION_RETAIN) + } + } + + // Bit 0 = pageTurnerKeys interception, bit 1 = learn mode. + private var mediaKeyState: Int { + get { + return objc_getAssociatedObject(self, &AssociatedKeys.mediaKeyState) as? Int ?? 0 + } + set { + objc_setAssociatedObject( + self, &AssociatedKeys.mediaKeyState, newValue, .OBJC_ASSOCIATION_RETAIN) + } + } + + private func updateMediaKeyHandler() { + let shouldRun = mediaKeyState != 0 + if shouldRun { + if mediaKeyHandler == nil { + mediaKeyHandler = MediaKeyHandler() + } + if let webView = self.webView { + mediaKeyHandler?.start(webView: webView) + } else { + logger.warning("Cannot start media key handler: webView is nil") + } + } else { + mediaKeyHandler?.stop() + mediaKeyHandler = nil + } + } + @objc public func use_background_audio(_ invoke: Invoke) { do { let args = try invoke.parseArgs(UseBackgroundAudioRequestArgs.self) @@ -633,6 +713,19 @@ class NativeBridgePlugin: Plugin { } } } + + if let pageTurnerKeys = args.pageTurnerKeys { + mediaKeyState = pageTurnerKeys ? (mediaKeyState | 1) : (mediaKeyState & ~1) + } + if let learnMode = args.learnMode { + mediaKeyState = learnMode ? (mediaKeyState | 2) : (mediaKeyState & ~2) + } + if args.pageTurnerKeys != nil || args.learnMode != nil { + DispatchQueue.main.async { [weak self] in + self?.updateMediaKeyHandler() + } + } + invoke.resolve() } catch { invoke.reject(error.localizedDescription) diff --git a/apps/readest-app/src/__tests__/services/constants.test.ts b/apps/readest-app/src/__tests__/services/constants.test.ts index c5ee3485..13d2dbee 100644 --- a/apps/readest-app/src/__tests__/services/constants.test.ts +++ b/apps/readest-app/src/__tests__/services/constants.test.ts @@ -293,6 +293,16 @@ describe('services/constants', () => { expect(Array.isArray(DEFAULT_SYSTEM_SETTINGS.lastOpenBooks)).toBe(true); expect(DEFAULT_SYSTEM_SETTINGS.lastOpenBooks!.length).toBe(0); }); + + it('has a disabled hardwarePageTurner with empty bindings', () => { + const hw = DEFAULT_SYSTEM_SETTINGS.hardwarePageTurner!; + expect(hw).toBeDefined(); + expect(hw.enabled).toBe(false); + expect(hw.bindings.pagePrev).toBeNull(); + expect(hw.bindings.pageNext).toBeNull(); + expect(hw.bindings.sectionPrev).toBeNull(); + expect(hw.bindings.sectionNext).toBeNull(); + }); }); describe('DEFAULT_MOBILE_SYSTEM_SETTINGS', () => { diff --git a/apps/readest-app/src/__tests__/store/device-store.test.ts b/apps/readest-app/src/__tests__/store/device-store.test.ts index ccc8e368..8da0e89a 100644 --- a/apps/readest-app/src/__tests__/store/device-store.test.ts +++ b/apps/readest-app/src/__tests__/store/device-store.test.ts @@ -22,6 +22,8 @@ beforeEach(() => { backKeyIntercepted: false, volumeKeysInterceptionCount: 0, backKeyInterceptionCount: 0, + pageTurnerKeysIntercepted: false, + pageTurnerKeysInterceptionCount: 0, }); vi.clearAllMocks(); // Clean up window handlers @@ -224,4 +226,71 @@ describe('deviceStore', () => { expect(state.backKeyInterceptionCount).toBe(0); }); }); + + // ── Page turner key interception ─────────────────────────────── + describe('page turner key interception', () => { + test('acquire sets state and intercepts page turner keys', async () => { + const { interceptKeys } = await import('@/utils/bridge'); + useDeviceControlStore.getState().acquirePageTurnerKeyInterception(); + + expect(useDeviceControlStore.getState().pageTurnerKeysIntercepted).toBe(true); + expect(useDeviceControlStore.getState().pageTurnerKeysInterceptionCount).toBe(1); + expect(interceptKeys).toHaveBeenCalledWith({ pageTurnerKeys: true }); + }); + + test('release after single acquire stops interception', async () => { + const { interceptKeys } = await import('@/utils/bridge'); + useDeviceControlStore.getState().acquirePageTurnerKeyInterception(); + useDeviceControlStore.getState().releasePageTurnerKeyInterception(); + + expect(useDeviceControlStore.getState().pageTurnerKeysIntercepted).toBe(false); + expect(useDeviceControlStore.getState().pageTurnerKeysInterceptionCount).toBe(0); + expect(interceptKeys).toHaveBeenCalledWith({ pageTurnerKeys: false }); + }); + + test('second acquire increments count without re-intercepting', async () => { + const { interceptKeys } = await import('@/utils/bridge'); + useDeviceControlStore.getState().acquirePageTurnerKeyInterception(); + useDeviceControlStore.getState().acquirePageTurnerKeyInterception(); + + expect(useDeviceControlStore.getState().pageTurnerKeysInterceptionCount).toBe(2); + expect(interceptKeys).toHaveBeenCalledTimes(1); + }); + + test('setKeyLearnMode toggles learn mode through the bridge', async () => { + const { interceptKeys } = await import('@/utils/bridge'); + useDeviceControlStore.getState().setKeyLearnMode(true); + expect(interceptKeys).toHaveBeenCalledWith({ learnMode: true }); + + useDeviceControlStore.getState().setKeyLearnMode(false); + expect(interceptKeys).toHaveBeenCalledWith({ learnMode: false }); + }); + }); + + // ── Native key forwarding ────────────────────────────────────── + describe('handleNativeKeyDown forwarding', () => { + test('forwards a media key through the native-key-down event', async () => { + const { eventDispatcher } = await import('@/utils/event'); + useDeviceControlStore.getState().acquirePageTurnerKeyInterception(); + + window.onNativeKeyDown?.('MediaNext', 87); + + expect(eventDispatcher.dispatch).toHaveBeenCalledWith('native-key-down', { + keyName: 'MediaNext', + keyCode: 87, + }); + }); + + test('forwards the Back key synchronously', async () => { + const { eventDispatcher } = await import('@/utils/event'); + useDeviceControlStore.getState().acquirePageTurnerKeyInterception(); + + window.onNativeKeyDown?.('Back', 4); + + expect(eventDispatcher.dispatchSync).toHaveBeenCalledWith('native-key-down', { + keyName: 'Back', + keyCode: 4, + }); + }); + }); }); diff --git a/apps/readest-app/src/__tests__/utils/hardwareKeys.test.ts b/apps/readest-app/src/__tests__/utils/hardwareKeys.test.ts new file mode 100644 index 00000000..ce069ddc --- /dev/null +++ b/apps/readest-app/src/__tests__/utils/hardwareKeys.test.ts @@ -0,0 +1,103 @@ +import { describe, test, expect } from 'vitest'; +import { + normalizeNativeKey, + normalizeDomKeyEvent, + matchesBinding, + resolvePageTurn, +} from '@/utils/keybinding'; +import { HardwarePageTurnerSettings } from '@/types/settings'; + +describe('normalizeNativeKey', () => { + test('maps a known native key to a friendly label', () => { + expect(normalizeNativeKey('MediaNext')).toEqual({ + source: 'native', + id: 'MediaNext', + label: 'Media Next', + }); + }); + + test('falls back to the raw id for an unknown native key', () => { + expect(normalizeNativeKey('Keycode99')).toEqual({ + source: 'native', + id: 'Keycode99', + label: 'Keycode99', + }); + }); +}); + +describe('normalizeDomKeyEvent', () => { + test('uses event.code and a friendly label for a known key', () => { + const event = { code: 'ArrowLeft', key: 'ArrowLeft' } as KeyboardEvent; + expect(normalizeDomKeyEvent(event)).toEqual({ + source: 'dom', + id: 'ArrowLeft', + label: 'Arrow Left', + }); + }); + + test('falls back to event.key when event.code is empty', () => { + const event = { code: '', key: 'MediaTrackNext' } as KeyboardEvent; + expect(normalizeDomKeyEvent(event)).toEqual({ + source: 'dom', + id: 'MediaTrackNext', + label: 'Media Next', + }); + }); +}); + +describe('matchesBinding', () => { + const binding = { source: 'native' as const, id: 'MediaNext', label: 'Media Next' }; + + test('matches same source and id', () => { + expect(matchesBinding(binding, { source: 'native', id: 'MediaNext' })).toBe(true); + }); + + test('rejects different id', () => { + expect(matchesBinding(binding, { source: 'native', id: 'MediaPrevious' })).toBe(false); + }); + + test('rejects different source', () => { + expect(matchesBinding(binding, { source: 'dom', id: 'MediaNext' })).toBe(false); + }); + + test('rejects a null binding', () => { + expect(matchesBinding(null, { source: 'native', id: 'MediaNext' })).toBe(false); + }); +}); + +describe('resolvePageTurn', () => { + const settings: HardwarePageTurnerSettings = { + enabled: true, + bindings: { + pagePrev: { source: 'native', id: 'MediaPrevious', label: 'Media Previous' }, + pageNext: { source: 'native', id: 'MediaNext', label: 'Media Next' }, + sectionPrev: { source: 'dom', id: 'PageUp', label: 'Page Up' }, + sectionNext: { source: 'dom', id: 'PageDown', label: 'Page Down' }, + }, + }; + + test('returns "pagePrev" for the pagePrev binding', () => { + expect(resolvePageTurn(settings, { source: 'native', id: 'MediaPrevious' })).toBe('pagePrev'); + }); + + test('returns "pageNext" for the pageNext binding', () => { + expect(resolvePageTurn(settings, { source: 'native', id: 'MediaNext' })).toBe('pageNext'); + }); + + test('returns "sectionPrev" for the sectionPrev binding', () => { + expect(resolvePageTurn(settings, { source: 'dom', id: 'PageUp' })).toBe('sectionPrev'); + }); + + test('returns "sectionNext" for the sectionNext binding', () => { + expect(resolvePageTurn(settings, { source: 'dom', id: 'PageDown' })).toBe('sectionNext'); + }); + + test('returns null for an unbound key', () => { + expect(resolvePageTurn(settings, { source: 'native', id: 'MediaPlayPause' })).toBeNull(); + }); + + test('returns null when the feature is disabled', () => { + const disabled = { ...settings, enabled: false }; + expect(resolvePageTurn(disabled, { source: 'native', id: 'MediaNext' })).toBeNull(); + }); +}); diff --git a/apps/readest-app/src/app/reader/hooks/usePagination.ts b/apps/readest-app/src/app/reader/hooks/usePagination.ts index 2f77a546..65aaee61 100644 --- a/apps/readest-app/src/app/reader/hooks/usePagination.ts +++ b/apps/readest-app/src/app/reader/hooks/usePagination.ts @@ -5,7 +5,10 @@ import { ViewSettings } from '@/types/book'; import { useReaderStore } from '@/store/readerStore'; import { useBookDataStore } from '@/store/bookDataStore'; import { useDeviceControlStore } from '@/store/deviceStore'; +import { useSettingsStore } from '@/store/settingsStore'; +import { useSidebarStore } from '@/store/sidebarStore'; import { eventDispatcher } from '@/utils/event'; +import { resolvePageTurn, normalizeDomKeyEvent, KeyCandidate } from '@/utils/keybinding'; import { isTauriAppPlatform } from '@/services/environment'; import { tauriGetWindowLogicalPosition } from '@/utils/window'; import { getReadingRulerMoveDirection } from '../utils/readingRuler'; @@ -109,7 +112,15 @@ export const usePagination = ( const { getBookData } = useBookDataStore(); const { getViewSettings, getViewState } = useReaderStore(); const { hoveredBookKey, setHoveredBookKey } = useReaderStore(); - const { acquireVolumeKeyInterception, releaseVolumeKeyInterception } = useDeviceControlStore(); + const { + acquireVolumeKeyInterception, + releaseVolumeKeyInterception, + acquirePageTurnerKeyInterception, + releasePageTurnerKeyInterception, + } = useDeviceControlStore(); + // Reactive subscription: drives the effect dependency array below. The + // handlers themselves re-read via getState() to avoid stale closures. + const hardwarePageTurner = useSettingsStore((s) => s.settings.hardwarePageTurner); const handlePageFlip = async ( msg: MessageEvent | CustomEvent | React.MouseEvent, @@ -247,6 +258,76 @@ export const usePagination = ( } }; + // Hardware page turner: media keys arrive via the `native-key-down` + // event; D-pad / keyboard keys arrive either as a top-window `keydown` + // or — when focus is inside a book iframe — as an `iframe-keydown` + // postMessage (mirroring useShortcuts' unified window + iframe handling). + // All resolve through the shared binding registry. Suppressed while the + // toolbar is visible so D-pad keys keep driving toolbar spatial navigation. + const handleHardwarePageTurn = (candidate: KeyCandidate): boolean => { + const settings = useSettingsStore.getState().settings.hardwarePageTurner; + if (!settings?.enabled) return false; + if (useReaderStore.getState().hoveredBookKey) return false; + + // Only the active book (the one driving the sidebar) responds, so a + // single key press doesn't flip every book open in a parallel view. + if (useSidebarStore.getState().sideBarBookKey !== bookKey) return false; + + const viewState = getViewState(bookKey); + if (!viewState?.inited) return false; + + const action = resolvePageTurn(settings, candidate); + if (!action) return false; + + const viewSettings = getViewSettings(bookKey); + const side = action === 'pagePrev' || action === 'sectionPrev' ? 'up' : 'down'; + const mode = action === 'sectionPrev' || action === 'sectionNext' ? 'section' : 'page'; + setHoveredBookKey(''); + if ( + mode === 'page' && + viewSettings?.readingRulerEnabled && + eventDispatcher.dispatchSync('reading-ruler-move', { + bookKey, + direction: getReadingRulerMoveDirection(side, viewRef.current?.book.dir), + }) + ) { + return true; + } + viewPagination(viewRef.current, viewSettings, side, mode); + return true; + }; + + const handleHardwareNativeKey = (msg: CustomEvent) => { + const keyName = msg.detail?.keyName; + if (typeof keyName !== 'string') return; + handleHardwarePageTurn({ source: 'native', id: keyName }); + }; + + const handleHardwareDomKey = (event: KeyboardEvent | MessageEvent) => { + let candidate: KeyCandidate; + if (event instanceof KeyboardEvent) { + if (event.repeat) return; + const target = event.target as HTMLElement | null; + if (target?.isContentEditable || /^(INPUT|TEXTAREA|SELECT)$/.test(target?.tagName ?? '')) { + return; + } + candidate = normalizeDomKeyEvent(event); + } else if (event.data?.type === 'iframe-keydown' && event.data.bookKey === bookKey) { + const id = event.data.code || event.data.key; + if (typeof id !== 'string' || !id) return; + candidate = { source: 'dom', id }; + } else { + return; + } + + if (handleHardwarePageTurn(candidate)) { + // Stop `useShortcuts` from also paging on this key — capture-phase + // for the window keydown, registration order for the iframe message. + if (event instanceof KeyboardEvent) event.preventDefault(); + event.stopImmediatePropagation(); + } + }; + useEffect(() => { if (!appService?.isMobileApp) return; @@ -265,6 +346,45 @@ export const usePagination = ( // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + // Hardware page turner: native-key + DOM-key listeners and native + // media-key interception, re-evaluated whenever the setting changes. + useEffect(() => { + const hasNativeBinding = + hardwarePageTurner?.bindings.pagePrev?.source === 'native' || + hardwarePageTurner?.bindings.pageNext?.source === 'native' || + hardwarePageTurner?.bindings.sectionPrev?.source === 'native' || + hardwarePageTurner?.bindings.sectionNext?.source === 'native'; + const needsNativeInterception = + !!appService?.isMobileApp && !!hardwarePageTurner?.enabled && hasNativeBinding; + + if (needsNativeInterception) { + acquirePageTurnerKeyInterception(); + } + if (hasNativeBinding) { + eventDispatcher.on('native-key-down', handleHardwareNativeKey); + } + window.addEventListener('keydown', handleHardwareDomKey, true); + window.addEventListener('message', handleHardwareDomKey); + + return () => { + if (needsNativeInterception) { + releasePageTurnerKeyInterception(); + } + if (hasNativeBinding) { + eventDispatcher.off('native-key-down', handleHardwareNativeKey); + } + window.removeEventListener('keydown', handleHardwareDomKey, true); + window.removeEventListener('message', handleHardwareDomKey); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ + hardwarePageTurner?.enabled, + hardwarePageTurner?.bindings.pagePrev?.source, + hardwarePageTurner?.bindings.pageNext?.source, + hardwarePageTurner?.bindings.sectionPrev?.source, + hardwarePageTurner?.bindings.sectionNext?.source, + ]); + // Touch swipe page flip for fixed-layout books — registered as a touch interceptor // so it participates in the priority-based consumption chain. useTouchInterceptor( diff --git a/apps/readest-app/src/components/settings/ControlPanel.tsx b/apps/readest-app/src/components/settings/ControlPanel.tsx index bc2399f7..4bf3e75a 100644 --- a/apps/readest-app/src/components/settings/ControlPanel.tsx +++ b/apps/readest-app/src/components/settings/ControlPanel.tsx @@ -1,7 +1,6 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { useEnv } from '@/context/EnvContext'; import { useReaderStore } from '@/store/readerStore'; -import { useDeviceControlStore } from '@/store/deviceStore'; import { useTranslation } from '@/hooks/useTranslation'; import { useBookDataStore } from '@/store/bookDataStore'; import { useSettingsStore } from '@/store/settingsStore'; @@ -14,6 +13,7 @@ import { SettingsPanelPanelProp } from './SettingsDialog'; import { annotationToolQuickActions } from '@/app/reader/components/annotator/AnnotationTools'; import { BoxedList, SettingsRow, SettingsSelect, SettingsSwitchRow } from './primitives'; import NumberInput from './NumberInput'; +import PageTurnerSettings from './PageTurnerSettings'; const ControlPanel: React.FC = ({ bookKey, onRegisterReset }) => { const _ = useTranslation(); @@ -22,7 +22,6 @@ const ControlPanel: React.FC = ({ bookKey, onRegisterRes const { getBookData } = useBookDataStore(); const { settings } = useSettingsStore(); const { applyEinkMode } = useEinkMode(); - const { acquireVolumeKeyInterception, releaseVolumeKeyInterception } = useDeviceControlStore(); const bookData = getBookData(bookKey); const viewSettings = getViewSettings(bookKey) || settings.globalViewSettings; @@ -30,7 +29,6 @@ const ControlPanel: React.FC = ({ bookKey, onRegisterRes const [noContinuousScroll, setNoContinuousScroll] = useState(viewSettings.noContinuousScroll); const [scrollingOverlap, setScrollingOverlap] = useState(viewSettings.scrollingOverlap); const [hideScrollbar, setHideScrollbar] = useState(viewSettings.hideScrollbar || false); - const [volumeKeysToFlip, setVolumeKeysToFlip] = useState(viewSettings.volumeKeysToFlip); const [showPaginationButtons, setShowPaginationButtons] = useState( viewSettings.showPaginationButtons, ); @@ -53,6 +51,7 @@ const ControlPanel: React.FC = ({ bookKey, onRegisterRes const [allowScript, setAllowScript] = useState(viewSettings.allowScript); const resetToDefaults = useResetViewSettings(); + const pageTurnerResetRef = useRef<() => void>(() => {}); const handleReset = () => { resetToDefaults({ @@ -60,7 +59,6 @@ const ControlPanel: React.FC = ({ bookKey, onRegisterRes noContinuousScroll: setNoContinuousScroll, scrollingOverlap: setScrollingOverlap, hideScrollbar: setHideScrollbar, - volumeKeysToFlip: setVolumeKeysToFlip, showPaginationButtons: setShowPaginationButtons, disableClick: setIsDisableClick, swapClickArea: setSwapClickArea, @@ -72,6 +70,7 @@ const ControlPanel: React.FC = ({ bookKey, onRegisterRes enableAnnotationQuickActions: setEnableAnnotationQuickActions, copyToNotebook: setCopyToNotebook, }); + pageTurnerResetRef.current(); }; useEffect(() => { @@ -113,18 +112,6 @@ const ControlPanel: React.FC = ({ bookKey, onRegisterRes // eslint-disable-next-line react-hooks/exhaustive-deps }, [scrollingOverlap]); - useEffect(() => { - saveViewSettings(envConfig, bookKey, 'volumeKeysToFlip', volumeKeysToFlip, false, false); - if (appService?.isMobileApp) { - if (volumeKeysToFlip) { - acquireVolumeKeyInterception(); - } else { - releaseVolumeKeyInterception(); - } - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [volumeKeysToFlip]); - useEffect(() => { saveViewSettings( envConfig, @@ -300,13 +287,6 @@ const ControlPanel: React.FC = ({ bookKey, onRegisterRes onChange={() => setIsDisableDoubleClick(!isDisableDoubleClick)} data-setting-id='settings.control.disableDoubleClick' /> - {appService?.isMobileApp && ( - setVolumeKeysToFlip(!volumeKeysToFlip)} - /> - )} = ({ bookKey, onRegisterRes /> + { + pageTurnerResetRef.current = fn; + }} + /> + void) => void; +} + +const PageTurnerSettings: React.FC = ({ bookKey, onRegisterReset }) => { + const _ = useTranslation(); + const { envConfig, appService } = useEnv(); + const { getViewSettings } = useReaderStore(); + const { setKeyLearnMode, acquireVolumeKeyInterception, releaseVolumeKeyInterception } = + useDeviceControlStore(); + const { settings } = useSettingsStore(); + const viewSettings = getViewSettings(bookKey) || settings.globalViewSettings; + const resetToDefaults = useResetViewSettings(); + + const [volumeKeysToFlip, setVolumeKeysToFlip] = useState(viewSettings.volumeKeysToFlip); + const [config, setConfig] = useState(settings.hardwarePageTurner); + const configRef = useRef(config); + configRef.current = config; + const [listening, setListening] = useState(null); + const timeoutRef = useRef | null>(null); + + const persist = (next: HardwarePageTurnerSettings) => { + setConfig(next); + saveSysSettings(envConfig, 'hardwarePageTurner', next); + }; + + // Native key interception exists only on mobile; on web and desktop + // learn mode relies on standard DOM keydown events alone. + const setNativeLearnMode = (enabled: boolean) => { + if (appService?.isMobileApp) setKeyLearnMode(enabled); + }; + + const stopListening = () => { + if (timeoutRef.current) clearTimeout(timeoutRef.current); + timeoutRef.current = null; + setNativeLearnMode(false); + setListening(null); + }; + + const captureBinding = (slot: Slot, binding: KeyBinding) => { + const current = configRef.current; + const bindings = { ...current.bindings, [slot]: binding }; + // A key can drive only one action — clear it from every other slot. + for (const other of PAGE_TURN_ACTIONS) { + if ( + other !== slot && + bindings[other]?.source === binding.source && + bindings[other]?.id === binding.id + ) { + bindings[other] = null; + } + } + persist({ ...current, bindings }); + stopListening(); + }; + + useEffect(() => { + if (!listening) return; + + const onNativeKey = (msg: CustomEvent) => { + const keyName = msg.detail?.keyName; + if (typeof keyName !== 'string') return; + // Back, and volume keys (which have their own dedicated page-flip + // toggle), are not bindable here — binding them would be inert or + // would double-fire alongside the volume-keys handler. + if (keyName === 'Back' || keyName === 'VolumeUp' || keyName === 'VolumeDown') return; + captureBinding(listening, normalizeNativeKey(keyName)); + }; + const onDomKey = (event: KeyboardEvent) => { + if (event.repeat) return; + event.preventDefault(); + event.stopImmediatePropagation(); + captureBinding(listening, normalizeDomKeyEvent(event)); + }; + + setNativeLearnMode(true); + eventDispatcher.on('native-key-down', onNativeKey); + window.addEventListener('keydown', onDomKey, true); + timeoutRef.current = setTimeout(stopListening, LEARN_TIMEOUT_MS); + + return () => { + eventDispatcher.off('native-key-down', onNativeKey); + window.removeEventListener('keydown', onDomKey, true); + if (timeoutRef.current) clearTimeout(timeoutRef.current); + timeoutRef.current = null; + setNativeLearnMode(false); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [listening]); + + useEffect(() => { + saveViewSettings(envConfig, bookKey, 'volumeKeysToFlip', volumeKeysToFlip, false, false); + if (appService?.isMobileApp) { + if (volumeKeysToFlip) { + acquireVolumeKeyInterception(); + } else { + releaseVolumeKeyInterception(); + } + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [volumeKeysToFlip]); + + useEffect(() => { + onRegisterReset?.(() => resetToDefaults({ volumeKeysToFlip: setVolumeKeysToFlip })); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + useEffect(() => { + if (!config.enabled && listening) stopListening(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [config.enabled]); + + const renderSlot = (slot: Slot, label: string) => { + const binding = config.bindings[slot]; + const isListening = listening === slot; + return ( + +
+ {binding && !isListening && ( + + )} + +
+
+ ); + }; + + return ( +
+ + {appService?.isMobileApp && ( + setVolumeKeysToFlip(!volumeKeysToFlip)} + /> + )} + persist({ ...config, enabled: !config.enabled })} + /> + {renderSlot('pagePrev', _('Previous Page'))} + {renderSlot('pageNext', _('Next Page'))} + {renderSlot('sectionPrev', _('Previous Section'))} + {renderSlot('sectionNext', _('Next Section'))} + +
+ ); +}; + +export default PageTurnerSettings; diff --git a/apps/readest-app/src/components/settings/primitives/BoxedList.tsx b/apps/readest-app/src/components/settings/primitives/BoxedList.tsx index 71c0bbb7..8a8c1f32 100644 --- a/apps/readest-app/src/components/settings/primitives/BoxedList.tsx +++ b/apps/readest-app/src/components/settings/primitives/BoxedList.tsx @@ -42,14 +42,14 @@ const BoxedList: React.FC = ({ return (
{title && {title}} - {description && ( -

- {description} -

- )}
{children}
+ {description && ( +

+ {description} +

+ )}
); }; diff --git a/apps/readest-app/src/services/constants.ts b/apps/readest-app/src/services/constants.ts index 5147493d..127bb473 100644 --- a/apps/readest-app/src/services/constants.ts +++ b/apps/readest-app/src/services/constants.ts @@ -94,6 +94,10 @@ export const DEFAULT_SYSTEM_SETTINGS: Partial = { screenWakeLock: false, screenBrightness: -1, // -1~100, -1 for system default autoScreenBrightness: true, + hardwarePageTurner: { + enabled: false, + bindings: { pagePrev: null, pageNext: null, sectionPrev: null, sectionNext: null }, + }, openLastBooks: false, lastOpenBooks: [], autoImportBooksOnOpen: false, diff --git a/apps/readest-app/src/store/deviceStore.ts b/apps/readest-app/src/store/deviceStore.ts index 678aacb0..a206edb6 100644 --- a/apps/readest-app/src/store/deviceStore.ts +++ b/apps/readest-app/src/store/deviceStore.ts @@ -5,19 +5,19 @@ import { NativeTouchEventType } from '@/types/system'; declare global { interface Window { - onNativeKeyDown?: (keyName: string) => void; + onNativeKeyDown?: (keyName: string, keyCode?: number) => void; onNativeTouch?: (event: NativeTouchEventType) => void; } } -const handleNativeKeyDown = (keyName: string) => { - if (keyName === 'VolumeUp' || keyName === 'VolumeDown') { - return eventDispatcher.dispatch('native-key-down', { keyName }); - } +const handleNativeKeyDown = (keyName: string, keyCode?: number) => { + // Back is handled synchronously so dialogs can consume it before it + // bubbles; every other key (volume, media, learn-mode captures) is + // dispatched asynchronously through the same channel. if (keyName === 'Back') { - return eventDispatcher.dispatchSync('native-key-down', { keyName }); + return eventDispatcher.dispatchSync('native-key-down', { keyName, keyCode }); } - return false; + return eventDispatcher.dispatch('native-key-down', { keyName, keyCode }); }; type DeviceControlState = { @@ -31,6 +31,11 @@ type DeviceControlState = { releaseVolumeKeyInterception: () => void; acquireBackKeyInterception: () => void; releaseBackKeyInterception: () => void; + pageTurnerKeysIntercepted: boolean; + pageTurnerKeysInterceptionCount: number; + acquirePageTurnerKeyInterception: () => void; + releasePageTurnerKeyInterception: () => void; + setKeyLearnMode: (enabled: boolean) => void; listenToNativeTouchEvents: () => void; }; @@ -39,6 +44,8 @@ export const useDeviceControlStore = create((set, get) => ({ backKeyIntercepted: false, volumeKeysInterceptionCount: 0, backKeyInterceptionCount: 0, + pageTurnerKeysIntercepted: false, + pageTurnerKeysInterceptionCount: 0, acquireVolumeKeyInterception: () => { const { volumeKeysInterceptionCount } = get(); @@ -80,6 +87,33 @@ export const useDeviceControlStore = create((set, get) => ({ } }, + acquirePageTurnerKeyInterception: () => { + const { pageTurnerKeysInterceptionCount } = get(); + if (pageTurnerKeysInterceptionCount == 0) { + window.onNativeKeyDown = handleNativeKeyDown; + interceptKeys({ pageTurnerKeys: true }); + set({ pageTurnerKeysIntercepted: true }); + } + set({ pageTurnerKeysInterceptionCount: pageTurnerKeysInterceptionCount + 1 }); + }, + + releasePageTurnerKeyInterception: () => { + const { pageTurnerKeysInterceptionCount } = get(); + if (pageTurnerKeysInterceptionCount <= 1) { + interceptKeys({ pageTurnerKeys: false }); + set({ pageTurnerKeysIntercepted: false, pageTurnerKeysInterceptionCount: 0 }); + } else { + set({ pageTurnerKeysInterceptionCount: pageTurnerKeysInterceptionCount - 1 }); + } + }, + + // Learn mode is a stateless UI toggle (used while capturing a binding), + // not reference-counted like the acquire/release interception actions. + setKeyLearnMode: (enabled: boolean) => { + window.onNativeKeyDown = handleNativeKeyDown; + interceptKeys({ learnMode: enabled }); + }, + listenToNativeTouchEvents: () => { window.onNativeTouch = (event: NativeTouchEventType) => { return eventDispatcher.dispatch('native-touch', event); diff --git a/apps/readest-app/src/types/settings.ts b/apps/readest-app/src/types/settings.ts index e5874fb7..52514e29 100644 --- a/apps/readest-app/src/types/settings.ts +++ b/apps/readest-app/src/types/settings.ts @@ -111,6 +111,25 @@ export const SYNC_CATEGORIES: readonly SyncCategory[] = [ 'credentials', ] as const; +export interface KeyBinding { + /** `native` = media keys forwarded by the OS bridge; `dom` = keyboard/D-pad keys. */ + source: 'native' | 'dom'; + /** Native key name (e.g. `MediaNext`) or DOM `event.code` (e.g. `ArrowLeft`). */ + id: string; + /** Human-readable label shown in settings. */ + label: string; +} + +export interface HardwarePageTurnerSettings { + enabled: boolean; + bindings: { + pagePrev: KeyBinding | null; + pageNext: KeyBinding | null; + sectionPrev: KeyBinding | null; + sectionNext: KeyBinding | null; + }; +} + export interface SystemSettings { version: number; migrationVersion: number; @@ -125,6 +144,7 @@ export interface SystemSettings { screenWakeLock: boolean; screenBrightness: number; autoScreenBrightness: boolean; + hardwarePageTurner: HardwarePageTurnerSettings; alwaysShowStatusBar: boolean; alwaysInForeground: boolean; openLastBooks: boolean; diff --git a/apps/readest-app/src/utils/bridge.ts b/apps/readest-app/src/utils/bridge.ts index 6fcea6b9..df17bf6f 100644 --- a/apps/readest-app/src/utils/bridge.ts +++ b/apps/readest-app/src/utils/bridge.ts @@ -46,6 +46,10 @@ export interface GetSystemFontsListResponse { export interface InterceptKeysRequest { volumeKeys?: boolean; backKey?: boolean; + /** Intercept media keys (next/previous/play-pause) for the hardware page turner. */ + pageTurnerKeys?: boolean; + /** Forward every key press to JS so the settings UI can capture a binding. */ + learnMode?: boolean; } export interface LockScreenRequest { diff --git a/apps/readest-app/src/utils/keybinding.ts b/apps/readest-app/src/utils/keybinding.ts new file mode 100644 index 00000000..896e11c0 --- /dev/null +++ b/apps/readest-app/src/utils/keybinding.ts @@ -0,0 +1,72 @@ +import { HardwarePageTurnerSettings, KeyBinding } from '@/types/settings'; +import { stubTranslation as _ } from '@/utils/misc'; + +export type KeyCandidate = { source: 'native' | 'dom'; id: string }; +export type PageTurnAction = 'pagePrev' | 'pageNext' | 'sectionPrev' | 'sectionNext'; + +export const PAGE_TURN_ACTIONS: PageTurnAction[] = [ + 'pagePrev', + 'pageNext', + 'sectionPrev', + 'sectionNext', +]; + +const NATIVE_KEY_LABELS: Record = { + MediaNext: _('Media Next'), + MediaPrevious: _('Media Previous'), + MediaPlayPause: _('Media Play/Pause'), + MediaFastForward: _('Media Fast Forward'), + MediaRewind: _('Media Rewind'), + VolumeUp: _('Volume Up'), + VolumeDown: _('Volume Down'), +}; + +const DOM_KEY_LABELS: Record = { + ArrowLeft: _('Arrow Left'), + ArrowRight: _('Arrow Right'), + ArrowUp: _('Arrow Up'), + ArrowDown: _('Arrow Down'), + PageUp: _('Page Up'), + PageDown: _('Page Down'), + Space: _('Space'), + Enter: _('Enter'), + MediaTrackNext: _('Media Next'), + MediaTrackPrevious: _('Media Previous'), + MediaPlayPause: _('Media Play/Pause'), +}; + +/** Normalize a native key name (from the OS bridge) into a `KeyBinding`. */ +export const normalizeNativeKey = (name: string): KeyBinding => ({ + source: 'native', + id: name, + label: NATIVE_KEY_LABELS[name] ?? name, +}); + +/** Normalize a DOM `KeyboardEvent` into a `KeyBinding`. */ +export const normalizeDomKeyEvent = (event: KeyboardEvent): KeyBinding => { + const id = event.code || event.key; + return { + source: 'dom', + id, + label: DOM_KEY_LABELS[id] ?? id, + }; +}; + +/** True when `candidate` is the key described by `binding`. */ +export const matchesBinding = (binding: KeyBinding | null, candidate: KeyCandidate): boolean => + !!binding && binding.source === candidate.source && binding.id === candidate.id; + +/** + * Decide which page-turn action an incoming key triggers. Returns the + * action, or `null` when the feature is disabled or the key is unbound. + */ +export const resolvePageTurn = ( + settings: HardwarePageTurnerSettings, + candidate: KeyCandidate, +): PageTurnAction | null => { + if (!settings.enabled) return null; + for (const action of PAGE_TURN_ACTIONS) { + if (matchesBinding(settings.bindings[action], candidate)) return action; + } + return null; +};