diff --git a/lib/I18n/translations/belarusian.yaml b/lib/I18n/translations/belarusian.yaml index 11b36192..70228655 100644 --- a/lib/I18n/translations/belarusian.yaml +++ b/lib/I18n/translations/belarusian.yaml @@ -298,6 +298,11 @@ STR_SLEEP_TIMER_VALUE_FORMAT: "%u хв" STR_SLEEP_NEVER: "Ніколі" STR_STEP_HINT_FRONT: "Пярэднія кнопкі:" STR_STEP_HINT_SIDE: "Бакавыя кнопкі:" +STR_OPDS_DOWNLOAD_FOLDER: "Папка для спампоўкі" +STR_OPDS_FILENAME_FORMAT: "Фармат назвы файла" +STR_FMT_AUTHOR_TITLE: "Аўтар - Назва" +STR_FMT_TITLE_AUTHOR: "Назва - Аўтар" +STR_FMT_TITLE: "Назва" STR_AUTO_TURN_ENABLED: "Аўтаперагортванне: " STR_AUTO_TURN_PAGES_PER_MIN: "Аўтаперагортванне (старонак за хвіліну)" STR_TILT_PAGE_TURN: "Перагортванне нахілам" diff --git a/lib/I18n/translations/catalan.yaml b/lib/I18n/translations/catalan.yaml index ff5db58a..776dbb67 100644 --- a/lib/I18n/translations/catalan.yaml +++ b/lib/I18n/translations/catalan.yaml @@ -347,6 +347,11 @@ STR_SERVER_NAME: "Nom del servidor" STR_NO_SERVERS: "No hi ha servidors OPDS configurats" STR_DELETE_SERVER: "Suprimeix el servidor" STR_OPDS_SERVERS: "Servidors OPDS" +STR_OPDS_DOWNLOAD_FOLDER: "Carpeta de baixades" +STR_OPDS_FILENAME_FORMAT: "Format del nom de fitxer" +STR_FMT_AUTHOR_TITLE: "Autor - Títol" +STR_FMT_TITLE_AUTHOR: "Títol - Autor" +STR_FMT_TITLE: "Títol" STR_MANAGE_FONTS: "Gestiona les fonts" STR_FONT_BROWSER: "Navegador de fonts" STR_LOADING_FONT_LIST: "S'està carregant la llista de fonts..." diff --git a/lib/I18n/translations/czech.yaml b/lib/I18n/translations/czech.yaml index f77cd325..774d286e 100644 --- a/lib/I18n/translations/czech.yaml +++ b/lib/I18n/translations/czech.yaml @@ -270,6 +270,11 @@ STR_BOOK_S_STYLE: "Styl knihy" STR_EMBEDDED_STYLE: "Vložený styl" STR_FOCUS_READING: "Soustředěné čtení" STR_OPDS_SERVER_URL: "URL serveru OPDS" +STR_OPDS_DOWNLOAD_FOLDER: "Složka pro stahování" +STR_OPDS_FILENAME_FORMAT: "Formát názvu souboru" +STR_FMT_AUTHOR_TITLE: "Autor - Název" +STR_FMT_TITLE_AUTHOR: "Název - Autor" +STR_FMT_TITLE: "Název" STR_SCREENSHOT_BUTTON: "Udělat snímek obrazovky" STR_SLEEP_TIMER_VALUE_FORMAT: "%u min" STR_SLEEP_NEVER: "Nikdy" diff --git a/lib/I18n/translations/danish.yaml b/lib/I18n/translations/danish.yaml index cf6d7d21..cf3041e7 100644 --- a/lib/I18n/translations/danish.yaml +++ b/lib/I18n/translations/danish.yaml @@ -300,6 +300,11 @@ STR_SLEEP_TIMER_VALUE_FORMAT: "%u min" STR_SLEEP_NEVER: "Aldrig" STR_STEP_HINT_FRONT: "Frontknapper:" STR_STEP_HINT_SIDE: "Sideknapper:" +STR_OPDS_DOWNLOAD_FOLDER: "Downloadmappe" +STR_OPDS_FILENAME_FORMAT: "Filnavnsformat" +STR_FMT_AUTHOR_TITLE: "Forfatter - Titel" +STR_FMT_TITLE_AUTHOR: "Titel - Forfatter" +STR_FMT_TITLE: "Titel" STR_SCREENSHOT_BUTTON: "Tag skærmbillede" STR_AUTO_TURN_ENABLED: "Automatisk sidevendning aktiveret: " STR_AUTO_TURN_PAGES_PER_MIN: "Automatisk vending (sider per minut)" diff --git a/lib/I18n/translations/dutch.yaml b/lib/I18n/translations/dutch.yaml index 08ae4e1f..b254c1fa 100644 --- a/lib/I18n/translations/dutch.yaml +++ b/lib/I18n/translations/dutch.yaml @@ -300,6 +300,11 @@ STR_SLEEP_TIMER_VALUE_FORMAT: "%u min" STR_SLEEP_NEVER: "Nooit" STR_STEP_HINT_FRONT: "Voorknoppen:" STR_STEP_HINT_SIDE: "Zijknoppen:" +STR_OPDS_DOWNLOAD_FOLDER: "Downloadmap" +STR_OPDS_FILENAME_FORMAT: "Bestandsnaamformaat" +STR_FMT_AUTHOR_TITLE: "Auteur - Titel" +STR_FMT_TITLE_AUTHOR: "Titel - Auteur" +STR_FMT_TITLE: "Titel" STR_SCREENSHOT_BUTTON: "Screenshot maken" STR_AUTO_TURN_ENABLED: "Automatisch omslaan ingeschakeld: " STR_AUTO_TURN_PAGES_PER_MIN: "Autom. omslaan (pagina's per minuut)" diff --git a/lib/I18n/translations/english.yaml b/lib/I18n/translations/english.yaml index aac1ea0c..447e8497 100644 --- a/lib/I18n/translations/english.yaml +++ b/lib/I18n/translations/english.yaml @@ -347,6 +347,12 @@ STR_SERVER_NAME: "Server Name" STR_NO_SERVERS: "No OPDS servers configured" STR_DELETE_SERVER: "Delete Server" STR_OPDS_SERVERS: "OPDS Servers" +STR_OPDS_DOWNLOAD_FOLDER: "Download folder" +STR_OPDS_FILENAME_FORMAT: "Filename format" +STR_FMT_AUTHOR_TITLE: "Author - Title" +STR_FMT_TITLE_AUTHOR: "Title - Author" +STR_FMT_TITLE: "Title" +STR_OPDS_SD_ROOT: "SD root" STR_AUTO_TURN_ENABLED: "Auto Turn Enabled: " STR_AUTO_TURN_PAGES_PER_MIN: "Auto Turn (Pages Per Minute)" STR_MANAGE_FONTS: "Manage Fonts" diff --git a/lib/I18n/translations/finnish.yaml b/lib/I18n/translations/finnish.yaml index aa9b7064..f8872ba1 100644 --- a/lib/I18n/translations/finnish.yaml +++ b/lib/I18n/translations/finnish.yaml @@ -268,6 +268,11 @@ STR_BOOK_S_STYLE: "Kirjan tyyli" STR_EMBEDDED_STYLE: "Upotettu tyyli" STR_FOCUS_READING: "Keskittynyt lukeminen" STR_OPDS_SERVER_URL: "OPDS-palvelimen osoite" +STR_OPDS_DOWNLOAD_FOLDER: "Latauskansio" +STR_OPDS_FILENAME_FORMAT: "Tiedostonimen muoto" +STR_FMT_AUTHOR_TITLE: "Tekijä - Nimi" +STR_FMT_TITLE_AUTHOR: "Nimi - Tekijä" +STR_FMT_TITLE: "Nimi" STR_SCREENSHOT_BUTTON: "Ota kuvakaappaus" STR_SLEEP_TIMER_VALUE_FORMAT: "%u min" STR_SLEEP_NEVER: "Ei koskaan" diff --git a/lib/I18n/translations/french.yaml b/lib/I18n/translations/french.yaml index 0ddce253..098f3ee3 100644 --- a/lib/I18n/translations/french.yaml +++ b/lib/I18n/translations/french.yaml @@ -301,6 +301,11 @@ STR_SLEEP_TIMER_VALUE_FORMAT: "%u min" STR_SLEEP_NEVER: "Jamais" STR_STEP_HINT_FRONT: "Boutons avant :" STR_STEP_HINT_SIDE: "Boutons latéraux :" +STR_OPDS_DOWNLOAD_FOLDER: "Dossier de téléchargement" +STR_OPDS_FILENAME_FORMAT: "Format du nom de fichier" +STR_FMT_AUTHOR_TITLE: "Auteur - Titre" +STR_FMT_TITLE_AUTHOR: "Titre - Auteur" +STR_FMT_TITLE: "Titre" STR_SCREENSHOT_BUTTON: "Capture d'écran" STR_AUTO_TURN_ENABLED: "Tourne-page auto : " STR_AUTO_TURN_PAGES_PER_MIN: "Tourne-page auto (pages par minute)" diff --git a/lib/I18n/translations/german.yaml b/lib/I18n/translations/german.yaml index 06078b85..b0b499c1 100644 --- a/lib/I18n/translations/german.yaml +++ b/lib/I18n/translations/german.yaml @@ -329,6 +329,11 @@ STR_SERVER_NAME: "Servername" STR_NO_SERVERS: "Keine OPDS-Server konfiguriert" STR_DELETE_SERVER: "Server entfernen" STR_OPDS_SERVERS: "OPDS-Server" +STR_OPDS_DOWNLOAD_FOLDER: "Download-Ordner" +STR_OPDS_FILENAME_FORMAT: "Dateinamenformat" +STR_FMT_AUTHOR_TITLE: "Autor - Titel" +STR_FMT_TITLE_AUTHOR: "Titel - Autor" +STR_FMT_TITLE: "Titel" STR_AUTO_TURN_ENABLED: "Auto-Umblättern aktiv: " STR_AUTO_TURN_PAGES_PER_MIN: "Auto-Umblättern (Seiten/Min.)" STR_IMAGES: "Bilder" diff --git a/lib/I18n/translations/hebrew.yaml b/lib/I18n/translations/hebrew.yaml index da9bbc30..d0b20e1b 100644 --- a/lib/I18n/translations/hebrew.yaml +++ b/lib/I18n/translations/hebrew.yaml @@ -306,6 +306,11 @@ STR_NO_SERVERS: "לא הוגדרו שרתי OPDS" STR_DELETE_SERVER: "מחק שרת" STR_DELETE_CONFIRM: "למחוק שרת זה?" STR_OPDS_SERVERS: "שרתי OPDS" +STR_OPDS_DOWNLOAD_FOLDER: "תיקיית הורדות" +STR_OPDS_FILENAME_FORMAT: "תבנית שם קובץ" +STR_FMT_AUTHOR_TITLE: "מחבר - כותרת" +STR_FMT_TITLE_AUTHOR: "כותרת - מחבר" +STR_FMT_TITLE: "כותרת" STR_AUTO_TURN_ENABLED: "דפדוף אוטומטי פועל: " STR_AUTO_TURN_PAGES_PER_MIN: "דפדוף אוטומטי (דפים בדקה)" STR_MANAGE_FONTS: "ניהול גופנים" diff --git a/lib/I18n/translations/hungarian.yaml b/lib/I18n/translations/hungarian.yaml index b2909e84..c67f58e4 100644 --- a/lib/I18n/translations/hungarian.yaml +++ b/lib/I18n/translations/hungarian.yaml @@ -299,6 +299,11 @@ STR_SLEEP_TIMER_VALUE_FORMAT: "%u perc" STR_SLEEP_NEVER: "Soha" STR_STEP_HINT_FRONT: "Elülső gombok:" STR_STEP_HINT_SIDE: "Oldalsó gombok:" +STR_OPDS_DOWNLOAD_FOLDER: "Letöltési mappa" +STR_OPDS_FILENAME_FORMAT: "Fájlnév formátuma" +STR_FMT_AUTHOR_TITLE: "Szerző - Cím" +STR_FMT_TITLE_AUTHOR: "Cím - Szerző" +STR_FMT_TITLE: "Cím" STR_SCREENSHOT_BUTTON: "Képernyőkép készítése" STR_AUTO_TURN_ENABLED: "Automatikus lapozás bekapcsolva: " STR_AUTO_TURN_PAGES_PER_MIN: "Automatikus lapozás (oldal/perc)" diff --git a/lib/I18n/translations/italian.yaml b/lib/I18n/translations/italian.yaml index eee6767e..35d20e12 100644 --- a/lib/I18n/translations/italian.yaml +++ b/lib/I18n/translations/italian.yaml @@ -315,6 +315,11 @@ STR_SERVER_NAME: "Nome server" STR_NO_SERVERS: "Nessun server OPDS configurato" STR_DELETE_SERVER: "Elimina server" STR_OPDS_SERVERS: "Server OPDS" +STR_OPDS_DOWNLOAD_FOLDER: "Cartella download" +STR_OPDS_FILENAME_FORMAT: "Formato nome file" +STR_FMT_AUTHOR_TITLE: "Autore - Titolo" +STR_FMT_TITLE_AUTHOR: "Titolo - Autore" +STR_FMT_TITLE: "Titolo" STR_AUTO_TURN_ENABLED: "Volta pagina automatico: " STR_AUTO_TURN_PAGES_PER_MIN: "Volta pagina automatico (pag/min)" STR_MANAGE_FONTS: "Gestisci font" diff --git a/lib/I18n/translations/kazakh.yaml b/lib/I18n/translations/kazakh.yaml index 6abbba5c..cce8ce98 100644 --- a/lib/I18n/translations/kazakh.yaml +++ b/lib/I18n/translations/kazakh.yaml @@ -296,6 +296,11 @@ STR_SLEEP_TIMER_VALUE_FORMAT: "%u мин" STR_SLEEP_NEVER: "Ешқашан" STR_STEP_HINT_FRONT: "Алдыңғы түймелер:" STR_STEP_HINT_SIDE: "Бүйір түймелері:" +STR_OPDS_DOWNLOAD_FOLDER: "Жүктеп алу қалтасы" +STR_OPDS_FILENAME_FORMAT: "Файл атауының пішімі" +STR_FMT_AUTHOR_TITLE: "Автор - Атауы" +STR_FMT_TITLE_AUTHOR: "Атауы - Автор" +STR_FMT_TITLE: "Атауы" STR_SCREENSHOT_BUTTON: "Скриншот түсіру" STR_AUTO_TURN_ENABLED: "Автоматты бет аудару қосулы: " STR_AUTO_TURN_PAGES_PER_MIN: "Автоматты бет аудару (минутына бет саны)" diff --git a/lib/I18n/translations/lithuanian.yaml b/lib/I18n/translations/lithuanian.yaml index 7626d6b7..30b40174 100644 --- a/lib/I18n/translations/lithuanian.yaml +++ b/lib/I18n/translations/lithuanian.yaml @@ -297,6 +297,11 @@ STR_SLEEP_TIMER_VALUE_FORMAT: "%u min." STR_SLEEP_NEVER: "Niekada" STR_STEP_HINT_FRONT: "Priekiniai mygtukai:" STR_STEP_HINT_SIDE: "Šoniniai mygtukai:" +STR_OPDS_DOWNLOAD_FOLDER: "Atsisiuntimų aplankas" +STR_OPDS_FILENAME_FORMAT: "Failo pavadinimo formatas" +STR_FMT_AUTHOR_TITLE: "Autorius - Pavadinimas" +STR_FMT_TITLE_AUTHOR: "Pavadinimas - Autorius" +STR_FMT_TITLE: "Pavadinimas" STR_SCREENSHOT_BUTTON: "Ekrano nuotrauka" STR_AUTO_TURN_ENABLED: "Auto-vertimas: " STR_AUTO_TURN_PAGES_PER_MIN: "Auto-vertimas (psl/min)" diff --git a/lib/I18n/translations/polish.yaml b/lib/I18n/translations/polish.yaml index e1336e45..4767815e 100644 --- a/lib/I18n/translations/polish.yaml +++ b/lib/I18n/translations/polish.yaml @@ -316,6 +316,11 @@ STR_SERVER_NAME: "Nazwa serwera" STR_NO_SERVERS: "Brak skonfigurowanych serwerów OPDS" STR_DELETE_SERVER: "Usuń serwer" STR_OPDS_SERVERS: "Serwery OPDS" +STR_OPDS_DOWNLOAD_FOLDER: "Folder pobierania" +STR_OPDS_FILENAME_FORMAT: "Format nazwy pliku" +STR_FMT_AUTHOR_TITLE: "Autor - Tytuł" +STR_FMT_TITLE_AUTHOR: "Tytuł - Autor" +STR_FMT_TITLE: "Tytuł" STR_AUTO_TURN_ENABLED: "Auto-kartkowanie: " STR_AUTO_TURN_PAGES_PER_MIN: "Auto-kartkowanie (str./min)" STR_DOWNLOAD_FONTS: "Pobierz czcionki" diff --git a/lib/I18n/translations/portuguese-BR.yaml b/lib/I18n/translations/portuguese-BR.yaml index 1668eb7b..f25b2ffe 100644 --- a/lib/I18n/translations/portuguese-BR.yaml +++ b/lib/I18n/translations/portuguese-BR.yaml @@ -339,6 +339,11 @@ STR_SERVER_NAME: "Nome do Servidor" STR_NO_SERVERS: "Nenhum servidor OPDS configurado" STR_DELETE_SERVER: "Excluir Servidor" STR_OPDS_SERVERS: "Servidores OPDS" +STR_OPDS_DOWNLOAD_FOLDER: "Pasta de downloads" +STR_OPDS_FILENAME_FORMAT: "Formato do nome do arquivo" +STR_FMT_AUTHOR_TITLE: "Autor - Título" +STR_FMT_TITLE_AUTHOR: "Título - Autor" +STR_FMT_TITLE: "Título" STR_AUTO_TURN_ENABLED: "Virada automática ativada: " STR_AUTO_TURN_PAGES_PER_MIN: "Virada automática (páginas por minuto)" STR_MANAGE_FONTS: "Gerenciar fontes" diff --git a/lib/I18n/translations/romanian.yaml b/lib/I18n/translations/romanian.yaml index 827175ad..111394d5 100644 --- a/lib/I18n/translations/romanian.yaml +++ b/lib/I18n/translations/romanian.yaml @@ -300,6 +300,11 @@ STR_SLEEP_TIMER_VALUE_FORMAT: "%u min" STR_SLEEP_NEVER: "Niciodată" STR_STEP_HINT_FRONT: "Butoane frontale:" STR_STEP_HINT_SIDE: "Butoane laterale:" +STR_OPDS_DOWNLOAD_FOLDER: "Dosar descărcări" +STR_OPDS_FILENAME_FORMAT: "Format nume fișier" +STR_FMT_AUTHOR_TITLE: "Autor - Titlu" +STR_FMT_TITLE_AUTHOR: "Titlu - Autor" +STR_FMT_TITLE: "Titlu" STR_SCREENSHOT_BUTTON: "Captură ecran" STR_AUTO_TURN_ENABLED: "Răsfoire automată: " STR_AUTO_TURN_PAGES_PER_MIN: "Pagini pe minut" diff --git a/lib/I18n/translations/russian.yaml b/lib/I18n/translations/russian.yaml index 6db7d4b2..f8e96197 100644 --- a/lib/I18n/translations/russian.yaml +++ b/lib/I18n/translations/russian.yaml @@ -339,6 +339,11 @@ STR_SERVER_NAME: "Имя сервера" STR_NO_SERVERS: "Нет настроенных серверов OPDS" STR_DELETE_SERVER: "Удалить сервер" STR_OPDS_SERVERS: "Серверы OPDS" +STR_OPDS_DOWNLOAD_FOLDER: "Папка загрузок" +STR_OPDS_FILENAME_FORMAT: "Формат имени файла" +STR_FMT_AUTHOR_TITLE: "Автор - Название" +STR_FMT_TITLE_AUTHOR: "Название - Автор" +STR_FMT_TITLE: "Название" STR_AUTO_TURN_ENABLED: "Автоперелистывание: " STR_AUTO_TURN_PAGES_PER_MIN: "Автоперелистывание (стр./мин)" STR_MANAGE_FONTS: "Управление шрифтами" diff --git a/lib/I18n/translations/slovak.yaml b/lib/I18n/translations/slovak.yaml index fce33cbf..2513fa02 100644 --- a/lib/I18n/translations/slovak.yaml +++ b/lib/I18n/translations/slovak.yaml @@ -335,6 +335,11 @@ STR_SERVER_NAME: "Názov servera" STR_NO_SERVERS: "Nie sú nakonfigurované žiadne OPDS servery" STR_DELETE_SERVER: "Odstrániť server" STR_OPDS_SERVERS: "OPDS servery" +STR_OPDS_DOWNLOAD_FOLDER: "Priečinok sťahovania" +STR_OPDS_FILENAME_FORMAT: "Formát názvu súboru" +STR_FMT_AUTHOR_TITLE: "Autor - Názov" +STR_FMT_TITLE_AUTHOR: "Názov - Autor" +STR_FMT_TITLE: "Názov" STR_AUTO_TURN_ENABLED: "Automatické otáčanie strán zapnuté: " STR_AUTO_TURN_PAGES_PER_MIN: "Automatické otáčanie (strán za minútu)" STR_MANAGE_FONTS: "Správa písiem" diff --git a/lib/I18n/translations/slovenian.yaml b/lib/I18n/translations/slovenian.yaml index 09780706..821504b8 100644 --- a/lib/I18n/translations/slovenian.yaml +++ b/lib/I18n/translations/slovenian.yaml @@ -297,6 +297,11 @@ STR_SLEEP_TIMER_VALUE_FORMAT: "%u min" STR_SLEEP_NEVER: "Nikoli" STR_STEP_HINT_FRONT: "Sprednji gumbi:" STR_STEP_HINT_SIDE: "Stranski gumbi:" +STR_OPDS_DOWNLOAD_FOLDER: "Mapa za prenose" +STR_OPDS_FILENAME_FORMAT: "Oblika imena datoteke" +STR_FMT_AUTHOR_TITLE: "Avtor - Naslov" +STR_FMT_TITLE_AUTHOR: "Naslov - Avtor" +STR_FMT_TITLE: "Naslov" STR_SCREENSHOT_BUTTON: "Naredi posnetek zaslona" STR_AUTO_TURN_ENABLED: "Samodejno obračanje: " STR_AUTO_TURN_PAGES_PER_MIN: "Samodejno obračanje (strani na minuto)" diff --git a/lib/I18n/translations/spanish.yaml b/lib/I18n/translations/spanish.yaml index d2720471..67fce2f7 100644 --- a/lib/I18n/translations/spanish.yaml +++ b/lib/I18n/translations/spanish.yaml @@ -345,6 +345,11 @@ STR_SERVER_NAME: "Nombre de servidor" STR_NO_SERVERS: "No se configuraron servidores OPDS" STR_DELETE_SERVER: "Borrar servidor" STR_OPDS_SERVERS: "Servidores OPDS" +STR_OPDS_DOWNLOAD_FOLDER: "Carpeta de descargas" +STR_OPDS_FILENAME_FORMAT: "Formato del nombre de archivo" +STR_FMT_AUTHOR_TITLE: "Autor - Título" +STR_FMT_TITLE_AUTHOR: "Título - Autor" +STR_FMT_TITLE: "Título" STR_AUTO_TURN_ENABLED: "Avance activado: " STR_AUTO_TURN_PAGES_PER_MIN: "Avance auto. (pág./min)" STR_MANAGE_FONTS: "Gestionar tipografías" diff --git a/lib/I18n/translations/swedish.yaml b/lib/I18n/translations/swedish.yaml index ba34feb0..81a5593e 100644 --- a/lib/I18n/translations/swedish.yaml +++ b/lib/I18n/translations/swedish.yaml @@ -342,6 +342,11 @@ STR_SERVER_NAME: "Servernamn" STR_NO_SERVERS: "Inga OPDS-servrar konfigurerade" STR_DELETE_SERVER: "Ta bort server" STR_OPDS_SERVERS: "OPDS-servrar" +STR_OPDS_DOWNLOAD_FOLDER: "Hämtningsmapp" +STR_OPDS_FILENAME_FORMAT: "Filnamnsformat" +STR_FMT_AUTHOR_TITLE: "Författare - Titel" +STR_FMT_TITLE_AUTHOR: "Titel - Författare" +STR_FMT_TITLE: "Titel" STR_AUTO_TURN_ENABLED: "Automatisk vändning aktiverad: " STR_AUTO_TURN_PAGES_PER_MIN: "Automatisk vändning (sidor per minut)" STR_MANAGE_FONTS: "Hantera teckensnitt" diff --git a/lib/I18n/translations/turkish.yaml b/lib/I18n/translations/turkish.yaml index 9f8d47c1..cea580c6 100644 --- a/lib/I18n/translations/turkish.yaml +++ b/lib/I18n/translations/turkish.yaml @@ -291,6 +291,11 @@ STR_SLEEP_TIMER_VALUE_FORMAT: "%u dak" STR_SLEEP_NEVER: "Asla" STR_STEP_HINT_FRONT: "Ön tuşlar:" STR_STEP_HINT_SIDE: "Yan tuşlar:" +STR_OPDS_DOWNLOAD_FOLDER: "İndirme klasörü" +STR_OPDS_FILENAME_FORMAT: "Dosya adı biçimi" +STR_FMT_AUTHOR_TITLE: "Yazar - Başlık" +STR_FMT_TITLE_AUTHOR: "Başlık - Yazar" +STR_FMT_TITLE: "Başlık" STR_NO_FILES_FOUND: "Dosya bulunamadı" STR_NO_FOOTNOTES: "Bu sayfada dipnot yok" STR_PREVIEW: "Önizleme" diff --git a/lib/I18n/translations/ukrainian.yaml b/lib/I18n/translations/ukrainian.yaml index 1d47c835..3274b63d 100644 --- a/lib/I18n/translations/ukrainian.yaml +++ b/lib/I18n/translations/ukrainian.yaml @@ -336,6 +336,11 @@ STR_SERVER_NAME: "Назва сервера" STR_NO_SERVERS: "Не налаштовано жодного сервера OPDS" STR_DELETE_SERVER: "Видалити сервер" STR_OPDS_SERVERS: "Сервери OPDS" +STR_OPDS_DOWNLOAD_FOLDER: "Папка завантажень" +STR_OPDS_FILENAME_FORMAT: "Формат імені файлу" +STR_FMT_AUTHOR_TITLE: "Автор - Назва" +STR_FMT_TITLE_AUTHOR: "Назва - Автор" +STR_FMT_TITLE: "Назва" STR_AUTO_TURN_ENABLED: "Автоперегортання увімк: " STR_AUTO_TURN_PAGES_PER_MIN: "Автоперегортання (ст/хв)" STR_MANAGE_FONTS: "Керування шрифтами" diff --git a/lib/I18n/translations/valencian.yaml b/lib/I18n/translations/valencian.yaml index 2c5ad86e..e1b3a129 100644 --- a/lib/I18n/translations/valencian.yaml +++ b/lib/I18n/translations/valencian.yaml @@ -348,6 +348,11 @@ STR_SERVER_NAME: "Nom del servidor" STR_NO_SERVERS: "No hi ha servidors OPDS configurats" STR_DELETE_SERVER: "Elimina el servidor" STR_OPDS_SERVERS: "Servidors OPDS" +STR_OPDS_DOWNLOAD_FOLDER: "Carpeta de baixades" +STR_OPDS_FILENAME_FORMAT: "Format del nom de fitxer" +STR_FMT_AUTHOR_TITLE: "Autor - Títol" +STR_FMT_TITLE_AUTHOR: "Títol - Autor" +STR_FMT_TITLE: "Títol" STR_MANAGE_FONTS: "Gestiona les fonts" STR_FONT_BROWSER: "Navegador de fonts" STR_LOADING_FONT_LIST: "S'està carregant la llista de fonts..." diff --git a/lib/I18n/translations/vietnamese.yaml b/lib/I18n/translations/vietnamese.yaml index 3ce82416..19753a12 100644 --- a/lib/I18n/translations/vietnamese.yaml +++ b/lib/I18n/translations/vietnamese.yaml @@ -335,6 +335,11 @@ STR_SERVER_NAME: "Tên máy chủ" STR_NO_SERVERS: "Chưa cấu hình máy chủ OPDS" STR_DELETE_SERVER: "Xóa máy chủ" STR_OPDS_SERVERS: "Máy chủ OPDS" +STR_OPDS_DOWNLOAD_FOLDER: "Thư mục tải xuống" +STR_OPDS_FILENAME_FORMAT: "Định dạng tên tệp" +STR_FMT_AUTHOR_TITLE: "Tác giả - Tựa đề" +STR_FMT_TITLE_AUTHOR: "Tựa đề - Tác giả" +STR_FMT_TITLE: "Tựa đề" STR_AUTO_TURN_ENABLED: "Tự lật trang: " STR_AUTO_TURN_PAGES_PER_MIN: "Tự lật (số trang mỗi phút)" STR_MANAGE_FONTS: "Quản lý phông chữ" diff --git a/src/CrossPointSettings.h b/src/CrossPointSettings.h index 5df277c6..3fc3e32c 100644 --- a/src/CrossPointSettings.h +++ b/src/CrossPointSettings.h @@ -238,6 +238,14 @@ class CrossPointSettings { // Reader screen margin settings uint8_t screenMargin = 5; + // OPDS download destination folder ("" = SD root). Global; edited from the + // OPDS server list. Persisted via a category-less SettingInfo::String in + // SettingsList.h, so it stays out of the on-device Settings screen. + char opdsDownloadFolder[64] = ""; + // On-disk filename format for OPDS downloads (0=Author-Title default, 1=Title-Author, + // 2=Title). See OpdsFilenameFormat. Persisted via a category-less SettingInfo::Enum, + // edited from the OPDS server list; hidden from the on-device Settings screen. + uint8_t opdsFilenameFormat = 0; // Hide battery percentage uint8_t hideBatteryPercentage = HIDE_NEVER; // Long-press page turn button behavior diff --git a/src/SettingsList.h b/src/SettingsList.h index c171cc5f..f2efbc9e 100644 --- a/src/SettingsList.h +++ b/src/SettingsList.h @@ -194,6 +194,16 @@ inline std::vector getSettingsList(const SdCardFontRegistry* regist SettingInfo::Toggle(StrId::STR_MOVE_FINISHED_TO_READ, &CrossPointSettings::moveFinishedToReadFolder, "moveFinishedToReadFolder", StrId::STR_CAT_SYSTEM), + // OPDS download folder: persisted + web-exposed, but category-less so it + // is hidden from the on-device Settings screen (edited via OPDS UI). + SettingInfo::String(StrId::STR_OPDS_DOWNLOAD_FOLDER, &SETTINGS.opdsDownloadFolder[0], + sizeof(SETTINGS.opdsDownloadFolder), "opdsDownloadFolder"), + // OPDS download filename format: persisted + web-exposed, category-less so it + // is hidden from the on-device Settings screen (cycled from the OPDS UI). + SettingInfo::Enum(StrId::STR_OPDS_FILENAME_FORMAT, &CrossPointSettings::opdsFilenameFormat, + {StrId::STR_FMT_AUTHOR_TITLE, StrId::STR_FMT_TITLE_AUTHOR, StrId::STR_FMT_TITLE}, + "opdsFilenameFormat"), + // --- KOReader Sync (web-only, uses KOReaderCredentialStore) --- SettingInfo::DynamicString( StrId::STR_KOREADER_USERNAME, [] { return KOREADER_STORE.getUsername(); }, diff --git a/src/activities/browser/OpdsBookBrowserActivity.cpp b/src/activities/browser/OpdsBookBrowserActivity.cpp index b2da2bdc..f24da28d 100644 --- a/src/activities/browser/OpdsBookBrowserActivity.cpp +++ b/src/activities/browser/OpdsBookBrowserActivity.cpp @@ -2,11 +2,13 @@ #include #include +#include #include #include #include #include +#include "CrossPointSettings.h" #include "MappedInputManager.h" #include "SilentRestart.h" #include "activities/network/WifiSelectionActivity.h" @@ -15,6 +17,7 @@ #include "fontIds.h" #include "network/HttpDownloader.h" #include "util/BookCacheUtils.h" +#include "util/OpdsFilename.h" #include "util/StringUtils.h" #include "util/UrlUtils.h" @@ -279,8 +282,26 @@ void OpdsBookBrowserActivity::downloadBook(const OpdsEntry& book) { // Build full download URL relative to the current feed, not the root server URL const std::string feedUrl = UrlUtils::buildUrl(server.url, currentPath); std::string downloadUrl = UrlUtils::buildUrl(feedUrl, book.href); - std::string filename = - "/" + StringUtils::sanitizeFilename((book.author.empty() ? "" : book.author + " - ") + book.title) + ".epub"; + // opdsDownloadFolder is already a null-terminated char[64]; use it directly — + // no std::string copy. exists()/mkdir() take const char*. + const char* folder = SETTINGS.opdsDownloadFolder; // "" => SD root + bool haveFolder = folder[0] != '\0'; + if (haveFolder && !Storage.exists(folder) && !Storage.mkdir(folder)) { + // exists()-guard first: mkdir's return-on-existing is unconfirmed, and every + // existing caller checks exists() before mkdir. On real failure, fall back + // to SD root so the download is never lost. + LOG_ERR("OPDS", "mkdir failed for %s, using SD root", folder); + haveFolder = false; + } + + // downloadToFile() needs a std::string, and titles are unbounded (a fixed + // char[] would truncate). Cold path (a multi-second download follows), so one + // reserve'd, in-place-appended owning string is the right call. + std::string filename; + filename.reserve(96); + if (haveFolder) filename += folder; + filename += '/'; + filename += opdsBookFilename(book.author, book.title, static_cast(SETTINGS.opdsFilenameFormat)); LOG_DBG("OPDS", "Downloading: %s -> %s", downloadUrl.c_str(), filename.c_str()); int lastRenderedPercent = -1; diff --git a/src/activities/settings/OpdsServerListActivity.cpp b/src/activities/settings/OpdsServerListActivity.cpp index 30557703..345c175d 100644 --- a/src/activities/settings/OpdsServerListActivity.cpp +++ b/src/activities/settings/OpdsServerListActivity.cpp @@ -3,19 +3,51 @@ #include #include +#include + +#include "CrossPointSettings.h" #include "MappedInputManager.h" #include "OpdsServerStore.h" #include "OpdsSettingsActivity.h" #include "activities/ActivityManager.h" #include "activities/browser/OpdsBookBrowserActivity.h" +#include "activities/util/KeyboardEntryActivity.h" #include "components/UITheme.h" #include "fontIds.h" +#include "util/OpdsFilename.h" + +namespace { +// Normalizes a user-typed folder: trims spaces, "" => SD root, otherwise a +// single leading '/' and no trailing '/'. Cold path (runs once per edit). +std::string normalizeFolder(std::string v) { + while (!v.empty() && (v.front() == ' ' || v.front() == '\t')) v.erase(v.begin()); + while (!v.empty() && (v.back() == ' ' || v.back() == '\t')) v.pop_back(); + if (v.empty()) return ""; + if (v.front() != '/') v.insert(v.begin(), '/'); + while (v.size() > 1 && v.back() == '/') v.pop_back(); + if (v == "/") return ""; // a bare slash is SD root, same as empty + return v; +} + +// Label shown for the current OPDS filename format in the list subtitle. +StrId opdsFormatLabel(uint8_t format) { + switch (format) { + case static_cast(OpdsFilenameFormat::TitleAuthor): + return StrId::STR_FMT_TITLE_AUTHOR; + case static_cast(OpdsFilenameFormat::TitleOnly): + return StrId::STR_FMT_TITLE; + default: + return StrId::STR_FMT_AUTHOR_TITLE; + } +} +} // namespace int OpdsServerListActivity::getItemCount() const { int count = static_cast(OPDS_STORE.getCount()); - // In settings mode, append a virtual "Add Server" item; in picker mode, only show real servers + // Settings mode appends three virtual items: "Add Server", "Download folder" + // and "Filename format". if (!pickerMode) { - count++; + count += 3; } return count; } @@ -74,6 +106,34 @@ void OpdsServerListActivity::handleSelection() { return; } + // Index layout: [servers 0..serverCount-1], [Add Server], [Download folder], [Filename format]. + if (selectedIndex == serverCount + 1) { + auto folderHandler = [this](const ActivityResult& result) { + if (!result.isCancelled) { + const auto& kb = std::get(result.data); + const std::string norm = normalizeFolder(kb.text); + strncpy(SETTINGS.opdsDownloadFolder, norm.c_str(), sizeof(SETTINGS.opdsDownloadFolder) - 1); + SETTINGS.opdsDownloadFolder[sizeof(SETTINGS.opdsDownloadFolder) - 1] = '\0'; + SETTINGS.saveToFile(); + requestUpdate(); + } + }; + startActivityForResult( + std::make_unique(renderer, mappedInput, tr(STR_OPDS_DOWNLOAD_FOLDER), + std::string(SETTINGS.opdsDownloadFolder), 63, InputType::Text), + folderHandler); + return; + } + + // "Filename format": tap cycles through the available formats. + if (selectedIndex == serverCount + 2) { + SETTINGS.opdsFilenameFormat = + static_cast((SETTINGS.opdsFilenameFormat + 1) % static_cast(OpdsFilenameFormat::Count)); + SETTINGS.saveToFile(); + requestUpdate(); + return; + } + // Settings mode: open editor for selected server, or create a new one auto resultHandler = [this](const ActivityResult&) { // Reload server list when returning from editor @@ -111,17 +171,30 @@ void OpdsServerListActivity::render(RenderLock&&) { // Secondary label: server URL (shown as subtitle when name is set). GUI.drawList( renderer, Rect{0, contentTop, pageWidth, contentHeight}, itemCount, selectedIndex, - [&servers, serverCount](int index) { + [&servers, serverCount](int index) -> std::string { if (index < serverCount) { const auto& server = servers[index]; return server.name.empty() ? server.url : server.name; } - return std::string(I18n::getInstance().get(StrId::STR_ADD_SERVER)); + if (index == serverCount) { + return std::string(I18n::getInstance().get(StrId::STR_ADD_SERVER)); + } + if (index == serverCount + 1) { + return std::string(I18n::getInstance().get(StrId::STR_OPDS_DOWNLOAD_FOLDER)); + } + return std::string(I18n::getInstance().get(StrId::STR_OPDS_FILENAME_FORMAT)); }, - [&servers, serverCount](int index) { + [&servers, serverCount](int index) -> std::string { if (index < serverCount && !servers[index].name.empty()) { return servers[index].url; } + if (index == serverCount + 1) { + const char* f = SETTINGS.opdsDownloadFolder; + return f[0] ? std::string(f) : std::string(I18n::getInstance().get(StrId::STR_OPDS_SD_ROOT)); + } + if (index == serverCount + 2) { + return std::string(I18n::getInstance().get(opdsFormatLabel(SETTINGS.opdsFilenameFormat))); + } return std::string(""); }); } diff --git a/src/util/OpdsFilename.cpp b/src/util/OpdsFilename.cpp new file mode 100644 index 00000000..507730a7 --- /dev/null +++ b/src/util/OpdsFilename.cpp @@ -0,0 +1,23 @@ +#include "OpdsFilename.h" + +#include "StringUtils.h" + +std::string opdsBookFilename(const std::string& author, const std::string& title, OpdsFilenameFormat format) { + std::string base; + switch (format) { + case OpdsFilenameFormat::TitleAuthor: + base = author.empty() ? title : title + " - " + author; + break; + case OpdsFilenameFormat::TitleOnly: + base = title; + break; + case OpdsFilenameFormat::AuthorTitle: + default: + base = author.empty() ? title : author + " - " + title; + break; + } + // sanitizeFilename caps at 100 bytes and never returns empty (falls back to + // "book"); ".epub" is appended after so the extension is never truncated — + // identical treatment to the previous inline construction. + return StringUtils::sanitizeFilename(base) + ".epub"; +} diff --git a/src/util/OpdsFilename.h b/src/util/OpdsFilename.h new file mode 100644 index 00000000..33d3e424 --- /dev/null +++ b/src/util/OpdsFilename.h @@ -0,0 +1,18 @@ +#pragma once +#include +#include + +// On-disk filename format for books downloaded from an OPDS server. Stored as a +// uint8_t in CrossPointSettings; cast to this enum at the call sites. `Count` is +// the number of selectable formats (used to cycle the setting in the UI). +enum class OpdsFilenameFormat : uint8_t { + AuthorTitle = 0, // "Author - Title.epub" (default; matches legacy behaviour) + TitleAuthor = 1, // "Title - Author.epub" + TitleOnly = 2, // "Title.epub" + Count = 3, +}; + +// Composes and sanitizes the on-disk filename (including the ".epub" extension) +// for a downloaded OPDS book, according to `format`. When the author is empty, +// every format collapses to just the sanitized title. Pure: no I/O, no globals. +std::string opdsBookFilename(const std::string& author, const std::string& title, OpdsFilenameFormat format); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index db3a3f35..5fb011e1 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -43,5 +43,6 @@ add_subdirectory(release_json_parser) add_subdirectory(differential_rounding) add_subdirectory(hyphenation_eval) add_subdirectory(utf8_compose) +add_subdirectory(opds_filename) add_subdirectory(minibidi_arabic) add_subdirectory(combining_marks) diff --git a/test/opds_filename/CMakeLists.txt b/test/opds_filename/CMakeLists.txt new file mode 100644 index 00000000..f4842ff5 --- /dev/null +++ b/test/opds_filename/CMakeLists.txt @@ -0,0 +1,18 @@ +add_executable(OpdsFilenameTest + OpdsFilenameTest.cpp + ${REPO_ROOT}/src/util/OpdsFilename.cpp + ${REPO_ROOT}/src/util/StringUtils.cpp + ${REPO_ROOT}/lib/Utf8/Utf8.cpp +) + +target_include_directories(OpdsFilenameTest PRIVATE + ${REPO_ROOT}/src/util + ${REPO_ROOT}/lib/Utf8 +) + +target_link_libraries(OpdsFilenameTest PRIVATE + crosspoint_test_common + GTest::gtest_main +) + +gtest_discover_tests(OpdsFilenameTest) diff --git a/test/opds_filename/OpdsFilenameTest.cpp b/test/opds_filename/OpdsFilenameTest.cpp new file mode 100644 index 00000000..b1f3b616 --- /dev/null +++ b/test/opds_filename/OpdsFilenameTest.cpp @@ -0,0 +1,52 @@ +#include + +#include + +#include "OpdsFilename.h" + +namespace { + +TEST(OpdsFilename, AuthorTitleIsDefaultOrder) { + EXPECT_EQ(opdsBookFilename("J. Doe", "My Book", OpdsFilenameFormat::AuthorTitle), "J. Doe - My Book.epub"); +} + +TEST(OpdsFilename, TitleAuthorSwapsOrder) { + EXPECT_EQ(opdsBookFilename("J. Doe", "My Book", OpdsFilenameFormat::TitleAuthor), "My Book - J. Doe.epub"); +} + +TEST(OpdsFilename, TitleOnlyIgnoresAuthor) { + EXPECT_EQ(opdsBookFilename("J. Doe", "My Book", OpdsFilenameFormat::TitleOnly), "My Book.epub"); +} + +TEST(OpdsFilename, EmptyAuthorCollapsesToTitleForEveryFormat) { + EXPECT_EQ(opdsBookFilename("", "My Book", OpdsFilenameFormat::AuthorTitle), "My Book.epub"); + EXPECT_EQ(opdsBookFilename("", "My Book", OpdsFilenameFormat::TitleAuthor), "My Book.epub"); + EXPECT_EQ(opdsBookFilename("", "My Book", OpdsFilenameFormat::TitleOnly), "My Book.epub"); +} + +TEST(OpdsFilename, IllegalCharactersAreSanitized) { + // '/' ':' '*' '?' etc. are replaced with '_' by sanitizeFilename. + EXPECT_EQ(opdsBookFilename("A/B", "C:D*E?", OpdsFilenameFormat::AuthorTitle), "A_B - C_D_E_.epub"); +} + +TEST(OpdsFilename, EmptyAuthorAndTitleFallsBackToBook) { + // sanitizeFilename returns "book" when nothing usable remains. + EXPECT_EQ(opdsBookFilename("", "", OpdsFilenameFormat::AuthorTitle), "book.epub"); + EXPECT_EQ(opdsBookFilename("", "", OpdsFilenameFormat::TitleOnly), "book.epub"); +} + +TEST(OpdsFilename, LongNameIsTruncatedToByteBudgetBeforeExtension) { + // sanitizeFilename caps the base at 100 bytes; ".epub" is appended after. + const std::string longTitle(200, 'a'); + const std::string result = opdsBookFilename("", longTitle, OpdsFilenameFormat::TitleOnly); + EXPECT_EQ(result, std::string(100, 'a') + ".epub"); + EXPECT_EQ(result.size(), 105u); +} + +TEST(OpdsFilename, UnknownFormatValueFallsBackToAuthorTitle) { + // Defensive: a persisted value outside the enum still yields a valid name. + const auto bogus = static_cast(99); + EXPECT_EQ(opdsBookFilename("J. Doe", "My Book", bogus), "J. Doe - My Book.epub"); +} + +} // namespace