From 05d0f12608a3083eaae446606fd649a74f2ea44e Mon Sep 17 00:00:00 2001 From: Iris Wildthyme <46726098+wildthyme@users.noreply.github.com> Date: Tue, 2 Apr 2019 18:52:45 -0400 Subject: [PATCH 01/21] redid comic importing support to handle metadata properly using comicapi from comictagger. needs work to automate installation --- .gitignore | 4 ++++ cps/comic.py | 65 ++++++++++++++++++++++++++++------------------------ 2 files changed, 39 insertions(+), 30 deletions(-) mode change 100644 => 100755 cps/comic.py diff --git a/.gitignore b/.gitignore index 09bf3faa..b3c3d439 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,7 @@ gdrive_credentials vendor client_secrets.json + +# External dependencies - need proper integrating +comicapi +libunrar diff --git a/cps/comic.py b/cps/comic.py old mode 100644 new mode 100755 index 91cb325d..cc1b0600 --- a/cps/comic.py +++ b/cps/comic.py @@ -17,33 +17,21 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import zipfile -import tarfile import os import uploader - +from comicapi.comicarchive import * +from comicapi.issuestring import * +from iso639 import languages as isoLanguages def extractCover(tmp_file_name, original_file_extension): + archive = ComicArchive(tmp_file_name) cover_data = None - if original_file_extension.upper() == '.CBZ': - cf = zipfile.ZipFile(tmp_file_name) - for name in cf.namelist(): - ext = os.path.splitext(name) - if len(ext) > 1: - extension = ext[1].lower() - if extension == '.jpg': - cover_data = cf.read(name) - break - elif original_file_extension.upper() == '.CBT': - cf = tarfile.TarFile(tmp_file_name) - for name in cf.getnames(): - ext = os.path.splitext(name) - if len(ext) > 1: - extension = ext[1].lower() - if extension == '.jpg': - cover_data = cf.extractfile(name).read() - break - + ext = os.path.splitext(archive.getPageName(0)) + if len(ext) > 1: + extension = ext[1].lower() + if extension == '.jpg' or extension == '.jpeg': + cover_data = archive.getPage(0) + print(archive.getPageName(0)) prefix = os.path.dirname(tmp_file_name) if cover_data: tmp_cover_name = prefix + '/cover' + extension @@ -56,17 +44,34 @@ def extractCover(tmp_file_name, original_file_extension): def get_comic_info(tmp_file_path, original_file_name, original_file_extension): + archive = ComicArchive(tmp_file_path) + if archive.seemsToBeAComicArchive(): + if archive.hasMetadata(MetaDataStyle.CIX): + style = MetaDataStyle.CIX + elif archive.hasMetadata(MetaDataStyle.CBI): + style = MetaDataStyle.CBI + else: + style = None - coverfile = extractCover(tmp_file_path, original_file_extension) + if style is not None: + loadedMetadata = archive.readMetadata(style) + + lang = loadedMetadata.language + if len(lang) == 2: + loadedMetadata.language = isoLanguages.get(part1=lang).name + elif len(lang) == 3: + loadedMetadata.language = isoLanguages.get(part3=lang).name + else: + loadedMetadata.language = "" return uploader.BookMeta( file_path=tmp_file_path, extension=original_file_extension, - title=original_file_name, - author=u"Unknown", - cover=coverfile, - description="", + title=loadedMetadata.title or original_file_name, + author=" & ".join([credit["person"] for credit in loadedMetadata.credits if credit["role"] == "Writer"]) or u"Unknown", + cover=extractCover(tmp_file_path, original_file_extension), + description=loadedMetadata.comments or "", tags="", - series="", - series_id="", - languages="") + series=loadedMetadata.series or "", + series_id=loadedMetadata.issue or "", + languages=loadedMetadata.language) From cbdc9876b2b39b15e2081d0c71fc102d722d10a2 Mon Sep 17 00:00:00 2001 From: Iris Wildthyme <46726098+wildthyme@users.noreply.github.com> Date: Tue, 2 Apr 2019 20:58:23 -0400 Subject: [PATCH 02/21] comicapi dependency now pippable --- .gitignore | 1 - cps/comic.py | 4 +--- requirements.txt | 1 + 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index b3c3d439..ee415c42 100644 --- a/.gitignore +++ b/.gitignore @@ -31,5 +31,4 @@ vendor client_secrets.json # External dependencies - need proper integrating -comicapi libunrar diff --git a/cps/comic.py b/cps/comic.py index cc1b0600..7622a072 100755 --- a/cps/comic.py +++ b/cps/comic.py @@ -19,8 +19,7 @@ import os import uploader -from comicapi.comicarchive import * -from comicapi.issuestring import * +from comicapi.comicarchive import ComicArchive, MetaDataStyle from iso639 import languages as isoLanguages def extractCover(tmp_file_name, original_file_extension): @@ -31,7 +30,6 @@ def extractCover(tmp_file_name, original_file_extension): extension = ext[1].lower() if extension == '.jpg' or extension == '.jpeg': cover_data = archive.getPage(0) - print(archive.getPageName(0)) prefix = os.path.dirname(tmp_file_name) if cover_data: tmp_cover_name = prefix + '/cover' + extension diff --git a/requirements.txt b/requirements.txt index 3fb23ea3..b2561849 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,3 +13,4 @@ SQLAlchemy>=1.1.0 tornado>=4.1 Wand>=0.4.4 unidecode>=0.04.19 +git+https://github.com/wildthyme/comicapi.git@c57bda958e56cf1ae23bec1cde974b4c424b2eb4#egg=comicapi From b7e30644abe80e508f68df895efcab06997c879a Mon Sep 17 00:00:00 2001 From: Iris W <46726098+wildthyme@users.noreply.github.com> Date: Wed, 3 Apr 2019 15:36:05 -0400 Subject: [PATCH 03/21] =?UTF-8?q?updated=20comicapi=20requirement=E2=80=94?= =?UTF-8?q?libunrar=20no=20longer=20necessary=20(CBR=20parsing=20support?= =?UTF-8?q?=20was=20always=20disabled=20anyway)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 --- requirements.txt | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index ee415c42..09bf3faa 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,3 @@ gdrive_credentials vendor client_secrets.json - -# External dependencies - need proper integrating -libunrar diff --git a/requirements.txt b/requirements.txt index b2561849..55a67a3e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,4 +13,4 @@ SQLAlchemy>=1.1.0 tornado>=4.1 Wand>=0.4.4 unidecode>=0.04.19 -git+https://github.com/wildthyme/comicapi.git@c57bda958e56cf1ae23bec1cde974b4c424b2eb4#egg=comicapi +git+https://github.com/wildthyme/comicapi.git@0f36fdd81bd03d6a979bebf03f907a1405d992c4#egg=comicapi From 029d299067422246e553edc096f16c05ed5961b8 Mon Sep 17 00:00:00 2001 From: Iris W <46726098+wildthyme@users.noreply.github.com> Date: Tue, 9 Apr 2019 15:22:03 -0400 Subject: [PATCH 04/21] updated comicapi version to fix installation error --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 55a67a3e..7f2776d7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,4 +13,4 @@ SQLAlchemy>=1.1.0 tornado>=4.1 Wand>=0.4.4 unidecode>=0.04.19 -git+https://github.com/wildthyme/comicapi.git@0f36fdd81bd03d6a979bebf03f907a1405d992c4#egg=comicapi +git+https://github.com/wildthyme/comicapi.git@cb279168f9c5cec742b5a05ac8326b9c168a8a91#egg=comicapi From 8b6d165d64b480f177f51ebad4f8ec41239a9a0e Mon Sep 17 00:00:00 2001 From: subdiox Date: Sun, 21 Apr 2019 17:53:40 +0900 Subject: [PATCH 05/21] Fix Japanese translation --- cps/translations/ja/LC_MESSAGES/messages.mo | Bin 49886 -> 37153 bytes cps/translations/ja/LC_MESSAGES/messages.po | 862 +++++++++----------- 2 files changed, 396 insertions(+), 466 deletions(-) diff --git a/cps/translations/ja/LC_MESSAGES/messages.mo b/cps/translations/ja/LC_MESSAGES/messages.mo index a89da6d2f37f56ae8f002d2c5040795dadae648c..df5fa48610c0ec82306a5b8b640650efaebc692c 100644 GIT binary patch literal 37153 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 %(rating)s" -msgstr "" +msgstr "評価 >= %(rating)s" #: cps/web.py:2022 cps/web.py:2031 msgid "search" @@ -359,7 +359,7 @@ msgstr "検索" #: cps/templates/index.xml:47 cps/templates/index.xml:51 #: cps/templates/layout.html:148 cps/web.py:2099 msgid "Read Books" -msgstr "既読の本" +msgstr "読んだ本" #: cps/templates/index.xml:55 cps/templates/index.xml:59 #: cps/templates/layout.html:150 cps/web.py:2102 @@ -381,28 +381,28 @@ msgstr "登録" #: cps/web.py:2247 cps/web.py:3365 msgid "An unknown error occurred. Please try again later." -msgstr "" +msgstr "不明なエラーが発生しました。あとで再試行してください。" #: cps/web.py:2250 msgid "Your e-mail is not allowed to register" -msgstr "" +msgstr "このメールアドレスは登録が許可されていません" #: cps/web.py:2253 msgid "Confirmation e-mail was send to your e-mail account." -msgstr "" +msgstr "確認メールがこのメールアドレスに送信されました。" #: cps/web.py:2256 msgid "This username or e-mail address is already in use." -msgstr "" +msgstr "このユーザ名またはメールアドレスはすでに使われています。" #: cps/web.py:2273 cps/web.py:2369 #, python-format msgid "you are now logged in as: '%(nickname)s'" -msgstr "%(nickname)s としてログインします" +msgstr "%(nickname)s としてログイン中" #: cps/web.py:2278 msgid "Wrong Username or Password" -msgstr "ユーザ名またはパスワードは間違いました" +msgstr "ユーザ名またはパスワードが違います" #: cps/web.py:2284 cps/web.py:2305 msgid "login" @@ -410,153 +410,153 @@ msgstr "ログイン" #: cps/web.py:2317 cps/web.py:2348 msgid "Token not found" -msgstr "トークンは見つかりません" +msgstr "トークンが見つかりません" #: cps/web.py:2325 cps/web.py:2356 msgid "Token has expired" -msgstr "トークンは失効されました" +msgstr "トークンが無効です" #: cps/web.py:2333 msgid "Success! Please return to your device" -msgstr "成功しまた!端末に戻ってください" +msgstr "成功です!端末に戻ってください" #: cps/web.py:2383 msgid "Please configure the SMTP mail settings first..." -msgstr "SMTPメールをまず設定してください" +msgstr "初めにSMTPメールの設定をしてください" #: cps/web.py:2388 #, python-format msgid "Book successfully queued for sending to %(kindlemail)s" -msgstr "" +msgstr "本の %(kindlemail)s への送信がキューに追加されました" #: cps/web.py:2392 #, python-format msgid "There was an error sending this book: %(res)s" -msgstr "%(res)s を送信する際にエーラが発生しました" +msgstr "%(res)s を送信中にエラーが発生しました" #: cps/web.py:2394 cps/web.py:3199 msgid "Please configure your kindle e-mail address first..." -msgstr "" +msgstr "初めにKindleのメールアドレスを設定してください" #: cps/web.py:2405 cps/web.py:2457 msgid "Invalid shelf specified" -msgstr "" +msgstr "指定された本棚は無効です" #: cps/web.py:2412 #, python-format msgid "Sorry you are not allowed to add a book to the the shelf: %(shelfname)s" -msgstr "" +msgstr "申し訳ありませんが、あなたは %(shelfname)s に本を追加することが許可されていません" #: cps/web.py:2420 msgid "You are not allowed to edit public shelves" -msgstr "" +msgstr "みんなの本棚を編集することが許可されていません" #: cps/web.py:2429 #, python-format msgid "Book is already part of the shelf: %(shelfname)s" -msgstr "" +msgstr "この本は %(shelfname)s にすでに追加されています" #: cps/web.py:2443 #, python-format msgid "Book has been added to shelf: %(sname)s" -msgstr "本 %(sname)s を書架に追加されました" +msgstr "本を %(sname)s に追加しました" #: cps/web.py:2462 #, python-format msgid "You are not allowed to add a book to the the shelf: %(name)s" -msgstr "" +msgstr "%(name)s に本を追加することが許可されていません" #: cps/web.py:2467 msgid "User is not allowed to edit public shelves" -msgstr "" +msgstr "みんなの本棚を編集することが許可されていません" #: cps/web.py:2485 #, python-format msgid "Books are already part of the shelf: %(name)s" -msgstr "" +msgstr "これらの本は %(name)s にすでに追加されています" #: cps/web.py:2499 #, python-format msgid "Books have been added to shelf: %(sname)s" -msgstr "" +msgstr "本が %(sname)s に追加されました" #: cps/web.py:2501 #, python-format msgid "Could not add books to shelf: %(sname)s" -msgstr "" +msgstr "%(sname)s に本を追加できません" #: cps/web.py:2538 #, python-format msgid "Book has been removed from shelf: %(sname)s" -msgstr "本 %(sname)s を書架から除去されました" +msgstr "本が %(sname)s から削除されました" #: cps/web.py:2544 #, python-format msgid "Sorry you are not allowed to remove a book from this shelf: %(sname)s" -msgstr "" +msgstr "申し訳ありませんが、%(sname)s から本を削除することが許可されていません" #: cps/web.py:2565 cps/web.py:2589 #, python-format msgid "A shelf with the name '%(title)s' already exists." -msgstr "名前を使った書架 '%(title)s' は既に存在しました" +msgstr "'%(title)s'は既に存在します" #: cps/web.py:2570 #, python-format msgid "Shelf %(title)s created" -msgstr "書架%(title)s は作成されました" +msgstr "%(title)s を作成しました" #: cps/web.py:2572 cps/web.py:2600 msgid "There was an error" -msgstr "エーラが発生しました" +msgstr "エラーが発生しました" #: cps/web.py:2573 cps/web.py:2575 msgid "create a shelf" -msgstr "書架を作成する" +msgstr "本棚を作成する" #: cps/web.py:2598 #, python-format msgid "Shelf %(title)s changed" -msgstr "書架 %(title)s 変わりました" +msgstr "%(title)s を変更しました" #: cps/web.py:2601 cps/web.py:2603 msgid "Edit a shelf" -msgstr "書架を編集する" +msgstr "本棚を編集する" #: cps/web.py:2624 #, python-format msgid "successfully deleted shelf %(name)s" -msgstr "%(name)s の書架を削除されました" +msgstr "%(name)s を削除しました" #: cps/web.py:2651 #, python-format msgid "Shelf: '%(name)s'" -msgstr "書架: '%(name)s'" +msgstr "本棚: '%(name)s'" #: cps/web.py:2654 msgid "Error opening shelf. Shelf does not exist or is not accessible" -msgstr "書架を開けません。書架は存在しないまたはアクセスできません" +msgstr "本棚を開けません。この本棚は存在しないかアクセスできません" #: cps/web.py:2685 #, python-format msgid "Change order of Shelf: '%(name)s'" -msgstr "'%(name)s' の書架の順番を入れ替える" +msgstr "'%(name)s' 内の本の順番を変更する" #: cps/web.py:2714 cps/web.py:3152 msgid "E-mail is not from valid domain" -msgstr "" +msgstr "このメールは有効なドメインからのものではありません" #: cps/web.py:2716 cps/web.py:2758 cps/web.py:2761 #, python-format msgid "%(name)s's profile" -msgstr "%(name)sのプロファイル" +msgstr "%(name)s のプロフィール" #: cps/web.py:2756 msgid "Found an existing account for this e-mail address." -msgstr "" +msgstr "このメールアドレスで登録されたアカウントがあります" #: cps/web.py:2759 msgid "Profile updated" -msgstr "プロファイルが更新されました" +msgstr "プロフィールを更新しました" #: cps/web.py:2790 msgid "Admin page" @@ -564,23 +564,23 @@ msgstr "管理者ページ" #: cps/web.py:2875 cps/web.py:3055 msgid "Calibre-Web configuration updated" -msgstr "Calibre-Web 設定を更新されました" +msgstr "Calibre-Web の設定を更新しました" #: cps/templates/admin.html:100 cps/web.py:2889 msgid "UI Configuration" -msgstr "" +msgstr "UI設定" #: cps/web.py:2907 msgid "Import of optional Google Drive requirements missing" -msgstr "" +msgstr "Googleドライブ用のOptional Requirementsがインストールされていません" #: cps/web.py:2910 msgid "client_secrets.json is missing or not readable" -msgstr "" +msgstr "client_secrets.json が存在しないか読み込めません" #: cps/web.py:2915 cps/web.py:2944 msgid "client_secrets.json is not configured for web application" -msgstr "" +msgstr "client_secrets.json がWebアプリ用に設定されていません" #: cps/templates/admin.html:99 cps/web.py:2947 cps/web.py:2973 cps/web.py:2985 #: cps/web.py:3030 cps/web.py:3045 cps/web.py:3064 cps/web.py:3072 @@ -590,19 +590,19 @@ msgstr "基本設定" #: cps/web.py:2970 msgid "Keyfile location is not valid, please enter correct path" -msgstr "" +msgstr "キーファイルが無効です。正しいパスを入力してください" #: cps/web.py:2982 msgid "Certfile location is not valid, please enter correct path" -msgstr "" +msgstr "証明書が無効です。正しいパスを入力してください" #: cps/web.py:3027 msgid "Logfile location is not valid, please enter correct path" -msgstr "ログファイルの場所は不適切です。正しい場所を入力してください" +msgstr "ログファイルが無効です。正しいパスを入力してください" #: cps/web.py:3068 msgid "DB location is not valid, please enter correct path" -msgstr "データベースの場所は不適切です。正しい場所を入力してください" +msgstr "データベースが無効です。正しいパスを入力してください" #: cps/templates/admin.html:33 cps/web.py:3148 cps/web.py:3154 cps/web.py:3170 msgid "Add new user" @@ -611,99 +611,99 @@ msgstr "新規ユーザ追加" #: cps/web.py:3160 #, python-format msgid "User '%(user)s' created" -msgstr "ユーザ '%(user)s' が作成されました" +msgstr "ユーザ '%(user)s' を作成しました" #: cps/web.py:3164 msgid "Found an existing account for this e-mail address or nickname." -msgstr "" +msgstr "このメールアドレスかニックネームで登録されたアカウントが見つかりました" #: cps/web.py:3194 #, python-format msgid "Test e-mail successfully send to %(kindlemail)s" -msgstr "" +msgstr "テストメールが %(kindlemail)s に送信されました" #: cps/web.py:3197 #, python-format msgid "There was an error sending the Test e-mail: %(res)s" -msgstr "" +msgstr "テストメールを %(res)s に送信中にエラーが発生しました" #: cps/web.py:3201 msgid "E-mail server settings updated" -msgstr "" +msgstr "メールサーバの設定を更新しました" #: cps/web.py:3202 msgid "Edit e-mail server settings" -msgstr "" +msgstr "メールサーバの設定を編集" #: cps/web.py:3227 #, python-format msgid "User '%(nick)s' deleted" -msgstr "ユーザ '%(nick)s' 削除されました" +msgstr "ユーザ '%(nick)s' を削除しました" #: cps/web.py:3340 #, python-format msgid "User '%(nick)s' updated" -msgstr "ユーザ '%(nick)s' 更新されました" +msgstr "ユーザ '%(nick)s' を更新しました" #: cps/web.py:3343 msgid "An unknown error occured." -msgstr "不明のエーラが発生しました" +msgstr "不明なエラーが発生しました。" #: cps/web.py:3345 #, python-format msgid "Edit User %(nick)s" -msgstr "ユーザ編集 %(nick)s" +msgstr "%(nick)s を編集" #: cps/web.py:3362 #, python-format msgid "Password for user %(user)s reset" -msgstr "" +msgstr "%(user)s 用のパスワードをリセット" #: cps/web.py:3376 cps/web.py:3582 msgid "Error opening eBook. File does not exist or file is not accessible" -msgstr "電子本を開けません。ファイルは存在しないまたはアクセスできません" +msgstr "電子書籍を開けません。ファイルが存在しないかアクセスできません" #: cps/web.py:3404 msgid "edit metadata" -msgstr "メタデータを編集します" +msgstr "メタデータを編集" #: cps/web.py:3497 cps/web.py:3743 #, python-format msgid "File extension '%(ext)s' is not allowed to be uploaded to this server" -msgstr "ファイル拡張子 '%(ext)s' をこのサーバにアップロードする許可はありません" +msgstr "ファイル拡張子 '%(ext)s' をこのサーバにアップロードすることは許可されていません" #: cps/web.py:3501 cps/web.py:3746 msgid "File to be uploaded must have an extension" -msgstr "ファイルをアップロードするために拡張子が必要です" +msgstr "アップロードするファイルには拡張子が必要です" #: cps/web.py:3513 cps/web.py:3765 #, python-format msgid "Failed to create path %(path)s (Permission denied)." -msgstr "場所 %(path)s の作成を失敗しました (許可拒否)" +msgstr "%(path)s の作成に失敗しました (Permission denied)。" #: cps/web.py:3518 #, python-format msgid "Failed to store file %(file)s." -msgstr "フアイル %(file)s の保存を失敗しました" +msgstr "%(file)s を保存できません。" #: cps/web.py:3535 #, python-format msgid "File format %(ext)s added to %(book)s" -msgstr "" +msgstr "ファイル形式 %(ext)s が %(book)s に追加されました" #: cps/web.py:3553 #, python-format msgid "Failed to create path for cover %(path)s (Permission denied)." -msgstr "" +msgstr "カバー画像 %(path)s の作成に失敗しました (Permission denied)。" #: cps/web.py:3561 #, python-format msgid "Failed to store cover-file %(cover)s." -msgstr "" +msgstr "カバー画像 %(cover)s の保存に失敗しました。" #: cps/web.py:3564 msgid "Cover-file is not a valid image file" -msgstr "" +msgstr "カバー画像が無効な画像ファイルです" #: cps/web.py:3594 cps/web.py:3603 msgid "unknown" @@ -711,71 +711,71 @@ msgstr "不明" #: cps/web.py:3635 msgid "Cover is not a jpg file, can't save" -msgstr "" +msgstr "カバー画像がjpgファイルでないため、保存できません" #: cps/web.py:3683 #, python-format msgid "%(langname)s is not a valid language" -msgstr "" +msgstr "%(langname)s は有効な言語ではありません" #: cps/web.py:3714 msgid "Metadata successfully updated" -msgstr "" +msgstr "メタデータを更新しました" #: cps/web.py:3723 msgid "Error editing book, please check logfile for details" -msgstr "" +msgstr "本の編集でエラーが発生しました。詳細はログファイルを確認してください" #: cps/web.py:3769 #, python-format msgid "Failed to store file %(file)s (Permission denied)." -msgstr "ファイル %(file)s の保存を失敗しました (許可拒否)" +msgstr "ファイル %(file)s の保存に失敗しました (Permission denied)。" #: cps/web.py:3774 #, python-format msgid "Failed to delete file %(file)s (Permission denied)." -msgstr "ファイル %(file)s の削除を失敗しました (許可拒否)" +msgstr "ファイル %(file)s の削除に失敗しました (Permission denied)。" #: cps/web.py:3857 #, python-format msgid "File %(title)s" -msgstr "" +msgstr "ファイル %(title)s" #: cps/web.py:3886 msgid "Source or destination format for conversion missing" -msgstr "" +msgstr "変換元の形式または変換後の形式が指定されていません" #: cps/web.py:3896 #, python-format msgid "Book successfully queued for converting to %(book_format)s" -msgstr "" +msgstr "本の %(book_format)s への変換がキューに追加されました" #: cps/web.py:3900 #, python-format msgid "There was an error converting this book: %(res)s" -msgstr "" +msgstr "この本の変換中にエラーが発生しました: %(res)s" #: cps/worker.py:305 #, python-format msgid "Ebook-converter failed: %(error)s" -msgstr "" +msgstr "Ebook-converter が失敗しました: %(error)s" #: cps/worker.py:316 #, python-format msgid "Kindlegen failed with Error %(error)s. Message: %(message)s" -msgstr "Kindlegen 失敗しました、エーラ %(error)s. メッセージ: %(message)s" +msgstr "Kindlegen が失敗しました。エラー: %(error)s, メッセージ: %(message)s" #: cps/templates/admin.html:6 msgid "User list" -msgstr "ユーザリスト" +msgstr "ユーザ一覧" #: cps/templates/admin.html:9 msgid "Nickname" -msgstr "通称" +msgstr "ニックネーム" #: cps/templates/admin.html:10 msgid "E-mail" -msgstr "" +msgstr "メールアドレス" #: cps/templates/admin.html:11 msgid "Kindle" @@ -804,7 +804,7 @@ msgstr "編集" #: cps/templates/admin.html:39 msgid "SMTP e-mail server settings" -msgstr "" +msgstr "SMTPメールサーバ設定" #: cps/templates/admin.html:42 cps/templates/email_edit.html:11 msgid "SMTP hostname" @@ -820,7 +820,7 @@ msgstr "SSL" #: cps/templates/admin.html:45 cps/templates/email_edit.html:27 msgid "SMTP login" -msgstr "SMTP ログイン" +msgstr "SMTPログイン" #: cps/templates/admin.html:46 msgid "From mail" @@ -828,7 +828,7 @@ msgstr "メールから" #: cps/templates/admin.html:56 msgid "Change SMTP settings" -msgstr "SMTP設定を変更する" +msgstr "SMTP設定を変更" #: cps/templates/admin.html:62 msgid "Configuration" @@ -836,35 +836,35 @@ msgstr "設定" #: cps/templates/admin.html:65 msgid "Calibre DB dir" -msgstr "Calibre データベースの場所" +msgstr "Calibreデータベースのあるフォルダ" #: cps/templates/admin.html:69 msgid "Log level" -msgstr "" +msgstr "ログレベル" #: cps/templates/admin.html:73 msgid "Port" -msgstr "ポート" +msgstr "ポート番号" #: cps/templates/admin.html:79 cps/templates/config_view_edit.html:23 msgid "Books per page" -msgstr "本数毎ページ" +msgstr "1ページに表示する本の冊数" #: cps/templates/admin.html:83 msgid "Uploading" -msgstr "アップロード中" +msgstr "アップロード機能" #: cps/templates/admin.html:87 msgid "Anonymous browsing" -msgstr "" +msgstr "匿名で閲覧" #: cps/templates/admin.html:91 msgid "Public registration" -msgstr "公的登録" +msgstr "誰でも新規登録可能" #: cps/templates/admin.html:95 cps/templates/remote_login.html:4 msgid "Remote login" -msgstr "遠距離ログイン" +msgstr "リモートログイン" #: cps/templates/admin.html:106 msgid "Administration" @@ -872,43 +872,43 @@ msgstr "管理" #: cps/templates/admin.html:107 msgid "Reconnect to Calibre DB" -msgstr "Calibreデータベースに再接続します" +msgstr "Calibreデータベースに再接続" #: cps/templates/admin.html:108 msgid "Restart Calibre-Web" -msgstr "Calibre-Webを再起動します" +msgstr "Calibre-Webを再起動" #: cps/templates/admin.html:109 msgid "Stop Calibre-Web" -msgstr "Calibre-Webを停止します" +msgstr "Calibre-Webを停止" #: cps/templates/admin.html:115 msgid "Update" -msgstr "" +msgstr "アップデート" #: cps/templates/admin.html:119 msgid "Version" -msgstr "" +msgstr "バージョン" #: cps/templates/admin.html:120 msgid "Details" -msgstr "" +msgstr "詳細" #: cps/templates/admin.html:126 msgid "Current version" -msgstr "" +msgstr "現在のバージョン" #: cps/templates/admin.html:132 msgid "Check for update" -msgstr "更新を確認します" +msgstr "更新を確認" #: cps/templates/admin.html:133 msgid "Perform Update" -msgstr "更新を実行します" +msgstr "更新を実行" #: cps/templates/admin.html:145 msgid "Do you really want to restart Calibre-Web?" -msgstr "Calibre-Webを再起動します。宜しいですか?" +msgstr "Calibre-Webを再起動します。よろしいですか?" #: cps/templates/admin.html:150 cps/templates/admin.html:164 #: cps/templates/admin.html:184 cps/templates/shelf.html:73 @@ -927,11 +927,11 @@ msgstr "戻る" #: cps/templates/admin.html:163 msgid "Do you really want to stop Calibre-Web?" -msgstr "Calibre-Webを停止します。宜しいですか?" +msgstr "Calibre-Webを停止します。よろしいですか?" #: cps/templates/admin.html:175 msgid "Updating, please do not reload page" -msgstr "更新中、ページ再読み込みしないでください" +msgstr "更新中です。ページ再読み込みしないでください" #: cps/templates/author.html:15 msgid "via" @@ -939,18 +939,18 @@ msgstr "経由" #: cps/templates/author.html:23 msgid "In Library" -msgstr "図書館の中" +msgstr "ライブラリ内" #: cps/templates/author.html:50 cps/templates/author.html:97 #: cps/templates/discover.html:28 cps/templates/index.html:31 #: cps/templates/index.html:86 cps/templates/search.html:55 #: cps/templates/shelf.html:37 msgid "reduce" -msgstr "" +msgstr "減らす" #: cps/templates/author.html:81 msgid "More by" -msgstr "もっと多い" +msgstr "" #: cps/templates/book_edit.html:16 msgid "Delete Book" @@ -958,7 +958,7 @@ msgstr "本を削除" #: cps/templates/book_edit.html:19 msgid "Delete formats:" -msgstr "" +msgstr "削除する形式:" #: cps/templates/book_edit.html:22 cps/templates/book_edit.html:199 #: cps/templates/email_edit.html:73 cps/templates/email_edit.html:74 @@ -967,23 +967,23 @@ msgstr "削除" #: cps/templates/book_edit.html:30 msgid "Convert book format:" -msgstr "" +msgstr "変換する形式:" #: cps/templates/book_edit.html:34 msgid "Convert from:" -msgstr "" +msgstr "変換元:" #: cps/templates/book_edit.html:36 cps/templates/book_edit.html:43 msgid "select an option" -msgstr "" +msgstr "選択肢を選ぶ" #: cps/templates/book_edit.html:41 msgid "Convert to:" -msgstr "" +msgstr "変換先:" #: cps/templates/book_edit.html:50 msgid "Convert book" -msgstr "" +msgstr "本を変換" #: cps/templates/book_edit.html:59 cps/templates/search_form.html:6 msgid "Book Title" @@ -1006,11 +1006,11 @@ msgstr "タグ" #: cps/templates/book_edit.html:75 cps/templates/layout.html:159 #: cps/templates/search_form.html:53 msgid "Series" -msgstr "叢書" +msgstr "シリーズ" #: cps/templates/book_edit.html:79 msgid "Series id" -msgstr "叢書番号" +msgstr "シリーズID" #: cps/templates/book_edit.html:83 msgid "Rating" @@ -1018,11 +1018,11 @@ msgstr "評価" #: cps/templates/book_edit.html:87 msgid "Cover URL (jpg, cover is downloaded and stored in database, field is afterwards empty again)" -msgstr "" +msgstr "カバー画像のURL (カバー画像はjpg形式でダウンロードしてデータベースに保存され、ここは再度空欄になります)" #: cps/templates/book_edit.html:91 msgid "Upload Cover from local drive" -msgstr "" +msgstr "カバー画像をローカルからアップロード" #: cps/templates/book_edit.html:96 cps/templates/detail.html:148 msgid "Publishing date" @@ -1048,26 +1048,26 @@ msgstr "いいえ" #: cps/templates/book_edit.html:164 msgid "Upload format" -msgstr "アップロード拡張子" +msgstr "アップロードする形式" #: cps/templates/book_edit.html:173 msgid "view book after edit" -msgstr "編集してから本を表示します" +msgstr "編集後に本を表示" #: cps/templates/book_edit.html:176 cps/templates/book_edit.html:212 msgid "Get metadata" -msgstr "メタデータを取得します" +msgstr "メタデータを取得" #: cps/templates/book_edit.html:177 cps/templates/config_edit.html:224 #: cps/templates/config_view_edit.html:178 cps/templates/login.html:20 #: cps/templates/search_form.html:150 cps/templates/shelf_edit.html:17 #: cps/templates/user_edit.html:147 msgid "Submit" -msgstr "提出" +msgstr "決定" #: cps/templates/book_edit.html:191 msgid "Are you really sure?" -msgstr "宜しいですか?" +msgstr "よろしいですか?" #: cps/templates/book_edit.html:194 msgid "Book will be deleted from Calibre database" @@ -1075,7 +1075,7 @@ msgstr "この本はCalibreデータベースから削除されます" #: cps/templates/book_edit.html:195 msgid "and from hard disk" -msgstr "とハードディクスから" +msgstr "" #: cps/templates/book_edit.html:215 msgid "Keyword" @@ -1083,15 +1083,15 @@ msgstr "キーワード" #: cps/templates/book_edit.html:216 msgid " Search keyword " -msgstr "キーワードを検索します" +msgstr "キーワードを検索" #: cps/templates/book_edit.html:218 cps/templates/layout.html:47 msgid "Go!" -msgstr "行く" +msgstr "決定" #: cps/templates/book_edit.html:222 msgid "Click the cover to load metadata to the form" -msgstr "メタデータをフォームに読み込むためにカバーをクリックしてください" +msgstr "カバー画像をクリックしてメタデータをフォームに読み込んでください" #: cps/templates/book_edit.html:234 cps/templates/book_edit.html:274 msgid "Loading..." @@ -1108,15 +1108,15 @@ msgstr "ソース" #: cps/templates/book_edit.html:275 msgid "Search error!" -msgstr "検索エーラ!" +msgstr "検索エラー" #: cps/templates/book_edit.html:276 msgid "No Result(s) found! Please try aonther keyword." -msgstr "" +msgstr "検索結果が見つかりません。別のキーワードで検索してみてください。" #: cps/templates/config_edit.html:12 msgid "Library Configuration" -msgstr "" +msgstr "ライブラリ設定" #: cps/templates/config_edit.html:19 msgid "Location of Calibre database" @@ -1124,27 +1124,27 @@ msgstr "Calibreデータベースの場所" #: cps/templates/config_edit.html:24 msgid "Use Google Drive?" -msgstr "Googleドライブを利用します?" +msgstr "Googleドライブを利用しますか?" #: cps/templates/config_edit.html:30 msgid "Google Drive config problem" -msgstr "" +msgstr "Googleドライブ設定の問題" #: cps/templates/config_edit.html:36 msgid "Authenticate Google Drive" -msgstr "" +msgstr "Googleドライブを認証" #: cps/templates/config_edit.html:40 msgid "Please hit submit to continue with setup" -msgstr "" +msgstr "決定を押して設定を続けてください" #: cps/templates/config_edit.html:43 msgid "Please finish Google Drive setup after login" -msgstr "" +msgstr "ログイン後にGoogleドライブの設定を完了してください" #: cps/templates/config_edit.html:48 msgid "Google Drive Calibre folder" -msgstr "" +msgstr "Googleドライブ上のCalibreフォルダ" #: cps/templates/config_edit.html:56 msgid "Metadata Watch Channel ID" @@ -1152,47 +1152,47 @@ msgstr "" #: cps/templates/config_edit.html:59 msgid "Revoke" -msgstr "" +msgstr "取り消す" #: cps/templates/config_edit.html:78 msgid "Server Configuration" -msgstr "" +msgstr "サーバ設定" #: cps/templates/config_edit.html:85 msgid "Server Port" -msgstr "サーバポート" +msgstr "ポート" #: cps/templates/config_edit.html:89 msgid "SSL certfile location (leave it empty for non-SSL Servers)" -msgstr "" +msgstr "SSL証明書の場所 (非SSLサーバでは空欄にしてください)" #: cps/templates/config_edit.html:93 msgid "SSL Keyfile location (leave it empty for non-SSL Servers)" -msgstr "" +msgstr "SSL鍵ファイルの場所 (非SSLサーバでは空欄にしてください)" #: cps/templates/config_edit.html:97 msgid "Update channel" -msgstr "" +msgstr "チャンネルを更新" #: cps/templates/config_edit.html:99 msgid "Stable" -msgstr "" +msgstr "安定" #: cps/templates/config_edit.html:100 msgid "Stable (Automatic)" -msgstr "" +msgstr "安定 (自動)" #: cps/templates/config_edit.html:101 msgid "Nightly" -msgstr "" +msgstr "最新" #: cps/templates/config_edit.html:102 msgid "Nightly (Automatic)" -msgstr "" +msgstr "最新 (自動)" #: cps/templates/config_edit.html:113 msgid "Logfile Configuration" -msgstr "" +msgstr "ログファイルの設定" #: cps/templates/config_edit.html:120 msgid "Log Level" @@ -1200,27 +1200,27 @@ msgstr "ログレベル" #: cps/templates/config_edit.html:129 msgid "Location and name of logfile (calibre-web.log for no entry)" -msgstr "" +msgstr "ログファイル名 (空欄の場合はcalibre-web.log)" #: cps/templates/config_edit.html:140 msgid "Feature Configuration" -msgstr "" +msgstr "機能設定" #: cps/templates/config_edit.html:148 msgid "Enable uploading" -msgstr "アップロードを 有効する" +msgstr "アップロードを有効にする" #: cps/templates/config_edit.html:152 msgid "Enable anonymous browsing" -msgstr "匿名ブラウジングを有効する" +msgstr "匿名での閲覧を有効にする" #: cps/templates/config_edit.html:156 msgid "Enable public registration" -msgstr "公的登録を有効する" +msgstr "誰でも新規登録を可能にする" #: cps/templates/config_edit.html:160 msgid "Enable remote login (\"magic link\")" -msgstr "遠距離ログインを有効する ('マジックリンク')" +msgstr "リモートログインを有効する (\"マジックリンク\")" #: cps/templates/config_edit.html:165 msgid "Use" @@ -1228,7 +1228,7 @@ msgstr "使う" #: cps/templates/config_edit.html:166 msgid "Obtain an API Key" -msgstr "APIキーを取得する" +msgstr "APIキーを取得" #: cps/templates/config_edit.html:170 msgid "Goodreads API Key" @@ -1236,35 +1236,35 @@ msgstr "GoodreadsのAPIキー" #: cps/templates/config_edit.html:174 msgid "Goodreads API Secret" -msgstr "GoodreadsのAPI秘密" +msgstr "GoodreadsのAPIシークレット" #: cps/templates/config_edit.html:187 msgid "External binaries" -msgstr "" +msgstr "外部バイナリ" #: cps/templates/config_edit.html:195 msgid "No converter" -msgstr "" +msgstr "変換ソフトなし" #: cps/templates/config_edit.html:197 msgid "Use Kindlegen" -msgstr "" +msgstr "Kindlegenを使う" #: cps/templates/config_edit.html:199 msgid "Use calibre's ebook converter" -msgstr "" +msgstr "calibreのebook converterを使う" #: cps/templates/config_edit.html:203 msgid "E-Book converter settings" -msgstr "" +msgstr "E-Book converterの設定" #: cps/templates/config_edit.html:207 msgid "Path to convertertool" -msgstr "" +msgstr "convertertoolのパス" #: cps/templates/config_edit.html:213 msgid "Location of Unrar binary" -msgstr "" +msgstr "Unrarバイナリのパス" #: cps/templates/config_edit.html:229 cps/templates/layout.html:84 #: cps/templates/login.html:4 @@ -1273,7 +1273,7 @@ msgstr "ログイン" #: cps/templates/config_view_edit.html:12 msgid "View Configuration" -msgstr "" +msgstr "表示設定" #: cps/templates/config_view_edit.html:19 cps/templates/layout.html:135 #: cps/templates/layout.html:136 cps/templates/shelf_edit.html:7 @@ -1282,35 +1282,35 @@ msgstr "タイトル" #: cps/templates/config_view_edit.html:27 msgid "No. of random books to show" -msgstr "任意本を表示するの数" +msgstr "ランダムで表示する本の冊数" #: cps/templates/config_view_edit.html:31 msgid "No. of authors to show before hiding (0=disable hiding)" -msgstr "" +msgstr "非表示にする前に表示する著者の人数 (0の場合は常に表示)" #: cps/templates/config_view_edit.html:35 cps/templates/readcbr.html:108 msgid "Theme" -msgstr "" +msgstr "テーマ" #: cps/templates/config_view_edit.html:37 msgid "Standard Theme" -msgstr "" +msgstr "通常テーマ" #: cps/templates/config_view_edit.html:38 msgid "caliBlur! Dark Theme" -msgstr "" +msgstr "caliBlur! ダークテーマ" #: cps/templates/config_view_edit.html:42 msgid "Regular expression for ignoring columns" -msgstr "列を無視するの正規表現" +msgstr "本を非表示にする際の正規表現" #: cps/templates/config_view_edit.html:46 msgid "Link read/unread status to Calibre column" -msgstr "" +msgstr "Calibre上のデータと既読/未読のステータスを紐付ける" #: cps/templates/config_view_edit.html:55 msgid "Regular expression for title sorting" -msgstr "タイトルを並び替えの正規表現" +msgstr "タイトルでソートする際の正規表現" #: cps/templates/config_view_edit.html:59 msgid "Tags for Mature Content" @@ -1318,87 +1318,87 @@ msgstr "成人向けのタグ" #: cps/templates/config_view_edit.html:73 msgid "Default settings for new users" -msgstr "新規ユーザにデフォルト設定を設定する" +msgstr "新規ユーザのデフォルト設定" #: cps/templates/config_view_edit.html:81 cps/templates/user_edit.html:104 msgid "Admin user" -msgstr "管理ユーザ" +msgstr "管理者ユーザ" #: cps/templates/config_view_edit.html:85 cps/templates/user_edit.html:113 msgid "Allow Downloads" -msgstr "ダウンロードを有効する" +msgstr "ダウンロードを許可" #: cps/templates/config_view_edit.html:89 cps/templates/user_edit.html:117 msgid "Allow Uploads" -msgstr "アップロードを有効する" +msgstr "アップロードを許可" #: cps/templates/config_view_edit.html:93 cps/templates/user_edit.html:121 msgid "Allow Edit" -msgstr "編集を有効する" +msgstr "編集を許可" #: cps/templates/config_view_edit.html:97 cps/templates/user_edit.html:125 msgid "Allow Delete books" -msgstr "本削除を有効する" +msgstr "本の削除を許可" #: cps/templates/config_view_edit.html:101 cps/templates/user_edit.html:130 msgid "Allow Changing Password" -msgstr "パスワード変更を有効する" +msgstr "パスワード変更を許可" #: cps/templates/config_view_edit.html:105 cps/templates/user_edit.html:134 msgid "Allow Editing Public Shelfs" -msgstr "公的叢書の編集を有効する" +msgstr "みんなの本棚の編集を許可" #: cps/templates/config_view_edit.html:115 msgid "Default visibilities for new users" -msgstr "新規ユーザにデフォルト可視性を設定する" +msgstr "新規ユーザのデフォルト表示設定" #: cps/templates/config_view_edit.html:123 cps/templates/user_edit.html:50 msgid "Show random books" -msgstr "任意本を表示する" +msgstr "ランダムで本を表示" #: cps/templates/config_view_edit.html:127 cps/templates/user_edit.html:54 msgid "Show recent books" -msgstr "最近の本を表示する" +msgstr "最近追加された本を表示" #: cps/templates/config_view_edit.html:131 cps/templates/user_edit.html:58 msgid "Show sorted books" -msgstr "整列された本を表示する" +msgstr "ソートされた本を表示" #: cps/templates/config_view_edit.html:135 cps/templates/user_edit.html:62 msgid "Show hot books" -msgstr "有名な本を表示する" +msgstr "人気な本を表示" #: cps/templates/config_view_edit.html:139 cps/templates/user_edit.html:66 msgid "Show best rated books" -msgstr "最高評価の本を表示する" +msgstr "評価が高い本を表示" #: cps/templates/config_view_edit.html:143 cps/templates/user_edit.html:70 msgid "Show language selection" -msgstr "言語選択を表示する" +msgstr "言語選択を表示" #: cps/templates/config_view_edit.html:147 cps/templates/user_edit.html:74 msgid "Show series selection" -msgstr "奏者選択を表示する" +msgstr "シリーズ選択を表示" #: cps/templates/config_view_edit.html:151 cps/templates/user_edit.html:78 msgid "Show category selection" -msgstr "カテゴリー選択を表示する" +msgstr "カテゴリ選択を表示" #: cps/templates/config_view_edit.html:155 cps/templates/user_edit.html:82 msgid "Show author selection" -msgstr "著者選択を表示する" +msgstr "著者選択を表示" #: cps/templates/config_view_edit.html:159 cps/templates/user_edit.html:86 msgid "Show publisher selection" -msgstr "" +msgstr "出版社選択を表示" #: cps/templates/config_view_edit.html:163 cps/templates/user_edit.html:91 msgid "Show read and unread" -msgstr "既読と未読の本を表示する" +msgstr "既読の本と未読の本を表示" #: cps/templates/config_view_edit.html:167 cps/templates/user_edit.html:96 msgid "Show random books in detail view" -msgstr "任意の本を詳細閲覧で表示する" +msgstr "詳細画面でランダムで本を表示" #: cps/templates/config_view_edit.html:171 cps/templates/user_edit.html:109 msgid "Show mature content" @@ -1414,7 +1414,7 @@ msgstr "本" #: cps/templates/detail.html:100 msgid "of" -msgstr "から" +msgstr "の" #: cps/templates/detail.html:106 msgid "language" @@ -1422,15 +1422,15 @@ msgstr "言語" #: cps/templates/detail.html:185 msgid "Mark As Unread" -msgstr "" +msgstr "未読に設定" #: cps/templates/detail.html:185 msgid "Mark As Read" -msgstr "" +msgstr "既読に設定" #: cps/templates/detail.html:186 msgid "Read" -msgstr "読む" +msgstr "読んだ" #: cps/templates/detail.html:196 msgid "Description:" @@ -1438,15 +1438,15 @@ msgstr "詳細:" #: cps/templates/detail.html:209 cps/templates/search.html:14 msgid "Add to shelf" -msgstr "書架に追加" +msgstr "本棚に追加" #: cps/templates/detail.html:271 msgid "Edit metadata" -msgstr "メタデータを編集する" +msgstr "メタデータを編集" #: cps/templates/email_edit.html:15 msgid "SMTP port (usually 25 for plain SMTP and 465 for SSL and 587 for STARTTLS)" -msgstr "SMTPポート(基本的にplain SMTPは25、SSLは465、STARTTLSは587)" +msgstr "SMTPポート (多くの場合plain SMTPは25、SSLは465、STARTTLSは587)" #: cps/templates/email_edit.html:19 msgid "Encryption" @@ -1466,39 +1466,39 @@ msgstr "SSL/TLS" #: cps/templates/email_edit.html:31 msgid "SMTP password" -msgstr "SMTP パスワード" +msgstr "SMTPパスワード" #: cps/templates/email_edit.html:35 msgid "From e-mail" -msgstr "メールより" +msgstr "メールから" #: cps/templates/email_edit.html:38 msgid "Save settings" -msgstr "設定を保存する" +msgstr "設定を保存" #: cps/templates/email_edit.html:39 msgid "Save settings and send Test E-Mail" -msgstr "設定を保存するとテストメールを送信する" +msgstr "設定を保存してテストメールを送信する" #: cps/templates/email_edit.html:43 msgid "Allowed domains for registering" -msgstr "" +msgstr "登録を許可されたドメイン" #: cps/templates/email_edit.html:47 msgid "Enter domainname" -msgstr "" +msgstr "ドメイン名を入力" #: cps/templates/email_edit.html:55 msgid "Add Domain" -msgstr "" +msgstr "ドメインを追加" #: cps/templates/email_edit.html:58 msgid "Add" -msgstr "" +msgstr "追加" #: cps/templates/email_edit.html:72 msgid "Do you really want to delete this domain rule?" -msgstr "" +msgstr "このドメインルールを削除してもよろしいですか?" #: cps/templates/feed.xml:21 cps/templates/layout.html:210 msgid "Next" @@ -1511,11 +1511,11 @@ msgstr "検索" #: cps/templates/http_error.html:23 msgid "Back to home" -msgstr "" +msgstr "ホームに戻る" #: cps/templates/index.html:5 msgid "Discover (Random Books)" -msgstr "発見 (任意の本)" +msgstr "本を見つける (ランダムで表示)" #: cps/templates/index.xml:6 msgid "Start" @@ -1523,31 +1523,31 @@ msgstr "開始" #: cps/templates/index.xml:18 cps/templates/layout.html:141 msgid "Hot Books" -msgstr "最新の本" +msgstr "人気の本" #: cps/templates/index.xml:22 msgid "Popular publications from this catalog based on Downloads." -msgstr "ダウンロードによりカタログの有名な出版" +msgstr "ダウンロード数に基づいた、この出版社が出している有名な本" #: cps/templates/index.xml:25 cps/templates/layout.html:144 msgid "Best rated Books" -msgstr "最高評価の本" +msgstr "高評価の本" #: cps/templates/index.xml:29 msgid "Popular publications from this catalog based on Rating." -msgstr "評価によりカタログの有名な出版" +msgstr "評価に基づいた、この出版社が出している有名な本" #: cps/templates/index.xml:32 msgid "New Books" -msgstr "新しい本" +msgstr "新着本" #: cps/templates/index.xml:36 msgid "The latest Books" -msgstr "最近の本" +msgstr "最新の本" #: cps/templates/index.xml:43 msgid "Show Random Books" -msgstr "任意の本を表示する" +msgstr "ランダムで本を表示" #: cps/templates/index.xml:62 cps/templates/layout.html:162 msgid "Authors" @@ -1555,43 +1555,43 @@ msgstr "著者" #: cps/templates/index.xml:66 msgid "Books ordered by Author" -msgstr "著者の名前で並び替える" +msgstr "著者名順" #: cps/templates/index.xml:69 cps/templates/layout.html:165 msgid "Publishers" -msgstr "" +msgstr "出版社" #: cps/templates/index.xml:73 msgid "Books ordered by publisher" -msgstr "" +msgstr "出版社順" #: cps/templates/index.xml:80 msgid "Books ordered by category" -msgstr "カテゴリーで並び替える" +msgstr "カテゴリ順" #: cps/templates/index.xml:87 msgid "Books ordered by series" -msgstr "叢書で並び替える" +msgstr "シリーズ順" #: cps/templates/index.xml:90 cps/templates/layout.html:171 msgid "Public Shelves" -msgstr "公的の叢書" +msgstr "みんなの本棚" #: cps/templates/index.xml:94 msgid "Books organized in public shelfs, visible to everyone" -msgstr "公的の叢書に選び分ける、みんなに見える" +msgstr "みんなの本棚に入れた本棚は、他の人からも見えます" #: cps/templates/index.xml:98 cps/templates/layout.html:175 msgid "Your Shelves" -msgstr "あなたの叢書" +msgstr "あなたの本棚" #: cps/templates/index.xml:102 msgid "User's own shelfs, only visible to the current user himself" -msgstr "ユーザ自身の叢書、自分しか見えない" +msgstr "ユーザ自身の本棚は、自分にのみ見えます" #: cps/templates/layout.html:28 msgid "Home" -msgstr "" +msgstr "ホーム" #: cps/templates/layout.html:34 msgid "Toggle navigation" @@ -1608,11 +1608,11 @@ msgstr "設定" #: cps/templates/layout.html:78 msgid "Account" -msgstr "" +msgstr "アカウント" #: cps/templates/layout.html:80 msgid "Logout" -msgstr "ロクアウト" +msgstr "ログアウト" #: cps/templates/layout.html:85 cps/templates/register.html:14 msgid "Register" @@ -1620,36 +1620,36 @@ msgstr "登録" #: cps/templates/layout.html:111 cps/templates/layout.html:257 msgid "Uploading..." -msgstr "" +msgstr "アップロード中..." #: cps/templates/layout.html:112 msgid "please don't refresh the page" -msgstr "" +msgstr "ページを更新しないでください" #: cps/templates/layout.html:122 msgid "Browse" -msgstr "ブラウズ" +msgstr "閲覧" #: cps/templates/layout.html:124 msgid "Recently Added" -msgstr "最近追加" +msgstr "最近追加した本" #: cps/templates/layout.html:129 msgid "Sorted Books" -msgstr "整列した本" +msgstr "本をソート" #: cps/templates/layout.html:133 cps/templates/layout.html:134 #: cps/templates/layout.html:135 cps/templates/layout.html:136 msgid "Sort By" -msgstr "整列" +msgstr "ソート" #: cps/templates/layout.html:133 msgid "Newest" -msgstr "最新" +msgstr "新着順" #: cps/templates/layout.html:134 msgid "Oldest" -msgstr "最古" +msgstr "投稿順" #: cps/templates/layout.html:135 msgid "Ascending" @@ -1661,11 +1661,11 @@ msgstr "降順" #: cps/templates/layout.html:153 msgid "Discover" -msgstr "発見" +msgstr "見つける" #: cps/templates/layout.html:156 msgid "Categories" -msgstr "カテゴリー" +msgstr "カテゴリ" #: cps/templates/layout.html:168 cps/templates/search_form.html:74 msgid "Languages" @@ -1673,7 +1673,7 @@ msgstr "言語" #: cps/templates/layout.html:180 msgid "Create a Shelf" -msgstr "叢書を作成する" +msgstr "本棚を作成" #: cps/templates/layout.html:181 cps/templates/stats.html:3 msgid "About" @@ -1689,11 +1689,11 @@ msgstr "本の詳細" #: cps/templates/layout.html:256 msgid "Upload done, processing, please wait..." -msgstr "" +msgstr "アップロード完了。現在処理中ですのでお待ち下さい..." #: cps/templates/layout.html:259 msgid "Error" -msgstr "" +msgstr "エラー" #: cps/templates/login.html:8 cps/templates/login.html:9 #: cps/templates/register.html:7 cps/templates/user_edit.html:8 @@ -1707,15 +1707,15 @@ msgstr "パスワード" #: cps/templates/login.html:17 msgid "Remember me" -msgstr "" +msgstr "記憶する" #: cps/templates/login.html:22 msgid "Log in with magic link" -msgstr "マジックリンクでログインする" +msgstr "マジックリンクでログイン" #: cps/templates/osd.xml:5 msgid "Calibre-Web ebook catalog" -msgstr "" +msgstr "Calibre-Web 電子書籍カタログ" #: cps/templates/read.html:74 msgid "Reflow text when sidebars are open." @@ -1723,99 +1723,111 @@ msgstr "" #: cps/templates/readcbr.html:84 msgid "Keyboard Shortcuts" -msgstr "" +msgstr "キーボードショートカット" #: cps/templates/readcbr.html:87 msgid "Previous Page" -msgstr "" +msgstr "前のページ" #: cps/templates/readcbr.html:88 msgid "Next Page" -msgstr "" +msgstr "次のページ" #: cps/templates/readcbr.html:89 msgid "Scale to Best" -msgstr "" +msgstr "最適なサイズにする" #: cps/templates/readcbr.html:90 msgid "Scale to Width" -msgstr "" +msgstr "横に合わせる" #: cps/templates/readcbr.html:91 msgid "Scale to Height" -msgstr "" +msgstr "縦に合わせる" #: cps/templates/readcbr.html:92 msgid "Scale to Native" -msgstr "" +msgstr "オリジナルのサイズにする" #: cps/templates/readcbr.html:93 msgid "Rotate Right" -msgstr "" +msgstr "右に回転する" #: cps/templates/readcbr.html:94 msgid "Rotate Left" -msgstr "" +msgstr "左に回転する" #: cps/templates/readcbr.html:95 msgid "Flip Image" -msgstr "" +msgstr "画像を反転する" #: cps/templates/readcbr.html:111 msgid "Light" -msgstr "" +msgstr "ライト" #: cps/templates/readcbr.html:112 msgid "Dark" -msgstr "" +msgstr "ダーク" #: cps/templates/readcbr.html:117 msgid "Scale" -msgstr "" +msgstr "サイズ" #: cps/templates/readcbr.html:120 msgid "Best" -msgstr "" +msgstr "最適" #: cps/templates/readcbr.html:121 msgid "Width" -msgstr "" +msgstr "横に合わせる" #: cps/templates/readcbr.html:122 msgid "Height" -msgstr "" +msgstr "縦に合わせる" #: cps/templates/readcbr.html:123 msgid "Native" -msgstr "" +msgstr "オリジナル" #: cps/templates/readcbr.html:128 msgid "Rotate" -msgstr "" +msgstr "回転" #: cps/templates/readcbr.html:139 msgid "Flip" -msgstr "" +msgstr "反転" #: cps/templates/readcbr.html:142 msgid "Horizontal" -msgstr "" +msgstr "水平方向" #: cps/templates/readcbr.html:143 msgid "Vertical" -msgstr "" +msgstr "垂直方向" + +#: cps/templates/readcbr.html:148 +msgid "Direction" +msgstr "読む方向" + +#: cps/templates/readcbr.html:151 +msgid "Left to Right" +msgstr "左から右" + +#: cps/templates/readcbr.html:152 +msgid "Right to Left" +msgstr "右から左" #: cps/templates/readpdf.html:29 msgid "PDF.js viewer" -msgstr "" +msgstr "PDF.js ビューア" #: cps/templates/readpdf.html:418 msgid "Preparing document for printing..." -msgstr "" +msgstr "印刷用にドキュメントを準備しています..." #: cps/templates/readtxt.html:6 msgid "Basic txt Reader" -msgstr "" +msgstr "テキストリーダ" #: cps/templates/register.html:4 msgid "Register a new account" @@ -1823,11 +1835,11 @@ msgstr "新規アカウントを登録する" #: cps/templates/register.html:8 msgid "Choose a username" -msgstr "ユーザ名" +msgstr "ユーザ名を入力してください" #: cps/templates/register.html:11 cps/templates/user_edit.html:13 msgid "E-mail address" -msgstr "" +msgstr "メールアドレス" #: cps/templates/register.html:12 msgid "Your email address" @@ -1839,11 +1851,11 @@ msgstr "他の端末を使っています" #: cps/templates/remote_login.html:6 msgid "and log in" -msgstr "とログイン" +msgstr "ログイン" #: cps/templates/remote_login.html:9 msgid "Once you do so, you will automatically get logged in on this device." -msgstr "やってたら、自動的にこの端末にログインします" +msgstr "一度実行すれば、自動的にこの端末でログインします" #: cps/templates/search.html:5 msgid "No Results for:" @@ -1859,11 +1871,11 @@ msgstr "結果:" #: cps/templates/search_form.html:19 msgid "Publishing date from" -msgstr "" +msgstr "これ以前の出版日:" #: cps/templates/search_form.html:26 msgid "Publishing date to" -msgstr "" +msgstr "これ以降の出版日:" #: cps/templates/search_form.html:43 msgid "Exclude Tags" @@ -1871,7 +1883,7 @@ msgstr "タグを除外" #: cps/templates/search_form.html:63 msgid "Exclude Series" -msgstr "叢書を除外" +msgstr "シリーズを除外" #: cps/templates/search_form.html:84 msgid "Exclude Languages" @@ -1879,67 +1891,67 @@ msgstr "言語を除外" #: cps/templates/search_form.html:97 msgid "Rating bigger than" -msgstr "" +msgstr "これ以上の評価:" #: cps/templates/search_form.html:101 msgid "Rating less than" -msgstr "" +msgstr "これ未満の評価:" #: cps/templates/shelf.html:7 msgid "Delete this Shelf" -msgstr "この叢書を削除" +msgstr "この本棚を削除" #: cps/templates/shelf.html:8 msgid "Edit Shelf" -msgstr "" +msgstr "本棚を編集" #: cps/templates/shelf.html:9 cps/templates/shelf_order.html:11 msgid "Change order" -msgstr "順番を変更する" +msgstr "順番を変更" #: cps/templates/shelf.html:68 msgid "Do you really want to delete the shelf?" -msgstr "書架を削除します。宜しいですか?" +msgstr "この本棚を削除してもよろしいですか?" #: cps/templates/shelf.html:71 msgid "Shelf will be lost for everybody and forever!" -msgstr "書架は誰にも見えなくなり永遠なくなります" +msgstr "この本棚は誰にも見えなくなり、完全に消滅します" #: cps/templates/shelf_edit.html:13 msgid "should the shelf be public?" -msgstr "叢書を公的に表示しますか?" +msgstr "この本棚を他の人にも表示しますか?" #: cps/templates/shelf_order.html:5 msgid "Drag 'n drop to rearrange order" -msgstr "ドラッグドロップで並び替えます" +msgstr "ドラッグ&ドロップで並び替える" #: cps/templates/stats.html:7 msgid "Calibre library statistics" -msgstr "Calibre図書館の統計" +msgstr "Calibreライブラリの統計" #: cps/templates/stats.html:12 msgid "Books in this Library" -msgstr "この図書館の本" +msgstr "このライブラリ内の本" #: cps/templates/stats.html:16 msgid "Authors in this Library" -msgstr "図書館内の著者" +msgstr "このライブラリ内の著者" #: cps/templates/stats.html:20 msgid "Categories in this Library" -msgstr "図書館内のカテゴリー" +msgstr "このライブラリ内のカテゴリ" #: cps/templates/stats.html:24 msgid "Series in this Library" -msgstr "図書館内の叢書" +msgstr "このライブラリ内のシリーズ" #: cps/templates/stats.html:28 msgid "Linked libraries" -msgstr "リンク付き図書館" +msgstr "紐付けられたライブラリ" #: cps/templates/stats.html:32 msgid "Program library" -msgstr "プログラム図書館" +msgstr "プログラムのライブラリ" #: cps/templates/stats.html:33 msgid "Installed Version" @@ -1947,51 +1959,51 @@ msgstr "インストールされたバージョン" #: cps/templates/tasks.html:7 msgid "Tasks list" -msgstr "" +msgstr "タスク一覧" #: cps/templates/tasks.html:12 msgid "User" -msgstr "" +msgstr "ユーザ" #: cps/templates/tasks.html:14 msgid "Task" -msgstr "" +msgstr "タスク" #: cps/templates/tasks.html:15 msgid "Status" -msgstr "" +msgstr "ステータス" #: cps/templates/tasks.html:16 msgid "Progress" -msgstr "" +msgstr "進捗" #: cps/templates/tasks.html:17 msgid "Runtime" -msgstr "" +msgstr "実行時間" #: cps/templates/tasks.html:18 msgid "Starttime" -msgstr "" +msgstr "開始時間" #: cps/templates/tasks.html:24 msgid "Delete finished tasks" -msgstr "" +msgstr "終了したタスクを削除する" #: cps/templates/tasks.html:25 msgid "Hide all tasks" -msgstr "" +msgstr "すべてのタスクを非表示にする" #: cps/templates/user_edit.html:18 msgid "Reset user Password" -msgstr "" +msgstr "ユーザパスワードをリセット" #: cps/templates/user_edit.html:27 msgid "Kindle E-Mail" -msgstr "Kindleメール" +msgstr "Kindleのメールアドレス" #: cps/templates/user_edit.html:39 msgid "Show books with language" -msgstr "言語で本を表示する" +msgstr "この言語で本を表示" #: cps/templates/user_edit.html:41 msgid "Show all" @@ -1999,90 +2011,8 @@ msgstr "全て表示" #: cps/templates/user_edit.html:141 msgid "Delete this user" -msgstr "このユーザを削除する" +msgstr "このユーザを削除" #: cps/templates/user_edit.html:156 msgid "Recent Downloads" -msgstr "最近ダウンロード" - -#~ msgid "%s: %s" -#~ msgstr "" - -#~ msgid "E-Mail: %(book)s" -#~ msgstr "" - -#~ msgid "Rename title from: '%(src)s' to '%(dest)s' failed with error: %(error)s" -#~ msgstr "タイトルを'%(src)s'から'%(dest)s'の改名は失敗しました。エーラ: %(error)s" - -#~ msgid "Rename author from: '%(src)s' to '%(dest)s' failed with error: %(error)s" -#~ msgstr "著者を'%(src)s'から'%(dest)s'の改名は失敗しました。エーラ:%(error)s" - -#~ msgid "Password for user %(user)s reset" -#~ msgstr "" - -#~ msgid "Password for user %s reset" -#~ msgstr "" - -#~ msgid "Rename title from: '%(src)s' to '%(src)s' failed with error: %(error)s" -#~ msgstr "" - -#~ msgid "Rename author from: '%(src)s' to '%(src)s' failed with error: %(error)s" -#~ msgstr "" - -#~ msgid "Failed to create path for cover %(cover)s (Permission denied)." -#~ msgstr "" - -#~ msgid "File extension '%s' is not allowed to be uploaded to this server" -#~ msgstr "" - -#~ msgid "File extension \"%(ext)s\" is not allowed to be uploaded to this server" -#~ msgstr "ファイル拡張子 \"%(ext)s\" をこのサーバにアップロードする許可はありません" - -#~ msgid "Current commit timestamp" -#~ msgstr "現在コミットのタイムスタンプ" - -#~ msgid "Newest commit timestamp" -#~ msgstr "最新コミットのタイムスタンプ" - -#~ msgid "Convert: %(book)s" -#~ msgstr "" - -#~ msgid "Convert to %(format)s: %(book)s" -#~ msgstr "" - -#~ msgid "Files are replaced" -#~ msgstr "ファイルを書き換えました" - -#~ msgid "Server is stopped" -#~ msgstr "サーバがシャットダウンされました" - -#~ msgid "Convertertool %(converter)s not found" -#~ msgstr "" - -#~ msgid "Choose a password" -#~ msgstr "パスワード" - -#~ msgid "Could not find any formats suitable for sending by e-mail" -#~ msgstr "" - -#~ msgid "Author list" -#~ msgstr "著者リスト" - -#~ msgid "File %(file)s uploaded" -#~ msgstr "" - -#~ msgid "Update done" -#~ msgstr "更新完了" - -#~ msgid "Stable (Automatic))" -#~ msgstr "" - -#~ msgid "Nightly (Automatic))" -#~ msgstr "" - -#~ msgid "A new update is available. Click on the button below to update to version: " -#~ msgstr "" - -#~ msgid "A new update is available. Click on the button below to update to version: %(version)s" -#~ msgstr "" - +msgstr "最近のダウンロード" From 204de4aef62a0b19e6d3aee957c872a97df09f94 Mon Sep 17 00:00:00 2001 From: subdiox Date: Sun, 21 Apr 2019 18:02:02 +0900 Subject: [PATCH 06/21] Fix an issue that fullscreen doens't work on some browsers --- cps/static/js/libs/screenfull.min.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cps/static/js/libs/screenfull.min.js b/cps/static/js/libs/screenfull.min.js index e7a33a42..70b732e9 100644 --- a/cps/static/js/libs/screenfull.min.js +++ b/cps/static/js/libs/screenfull.min.js @@ -1,7 +1,7 @@ /*! * screenfull -* v3.3.0 - 2017-07-06 +* v4.2.0 - 2019-04-01 * (c) Sindre Sorhus; MIT License */ -!function(){"use strict";var a="undefined"!=typeof window&&void 0!==window.document?window.document:{},b="undefined"!=typeof module&&module.exports,c="undefined"!=typeof Element&&"ALLOW_KEYBOARD_INPUT"in Element,d=function(){for(var b,c=[["requestFullscreen","exitFullscreen","fullscreenElement","fullscreenEnabled","fullscreenchange","fullscreenerror"],["webkitRequestFullscreen","webkitExitFullscreen","webkitFullscreenElement","webkitFullscreenEnabled","webkitfullscreenchange","webkitfullscreenerror"],["webkitRequestFullScreen","webkitCancelFullScreen","webkitCurrentFullScreenElement","webkitCancelFullScreen","webkitfullscreenchange","webkitfullscreenerror"],["mozRequestFullScreen","mozCancelFullScreen","mozFullScreenElement","mozFullScreenEnabled","mozfullscreenchange","mozfullscreenerror"],["msRequestFullscreen","msExitFullscreen","msFullscreenElement","msFullscreenEnabled","MSFullscreenChange","MSFullscreenError"]],d=0,e=c.length,f={};d Date: Sun, 21 Apr 2019 18:20:15 +0900 Subject: [PATCH 07/21] Add reading direction settings to readcbr page --- cps/static/css/kthoom.css | 4 ++-- cps/static/js/kthoom.js | 43 ++++++++++++++++++++++++++------------ cps/templates/readcbr.html | 35 +++++++++++++++++++++++++------ 3 files changed, 61 insertions(+), 21 deletions(-) diff --git a/cps/static/css/kthoom.css b/cps/static/css/kthoom.css index 770b94a2..6dfb9967 100644 --- a/cps/static/css/kthoom.css +++ b/cps/static/css/kthoom.css @@ -152,11 +152,11 @@ body { max-width: 70%; } -#prev { +#left { left: 40px; } -#next { +#right { right: 40px; } diff --git a/cps/static/js/kthoom.js b/cps/static/js/kthoom.js index 6ab25ad7..d7d97369 100644 --- a/cps/static/js/kthoom.js +++ b/cps/static/js/kthoom.js @@ -66,7 +66,8 @@ var settings = { vflip: false, rotateTimes: 0, fitMode: kthoom.Key.B, - theme: "light" + theme: "light", + direction: 0 // 0 = Left to Right, 1 = Right to Left }; kthoom.saveSettings = function() { @@ -367,6 +368,22 @@ function setImage(url) { } } +function showLeftPage() { + if (settings.direction === 0) { + showPrevPage() + } else { + showNextPage() + } +} + +function showRightPage() { + if (settings.direction === 0) { + showNextPage() + } else { + showPrevPage() + } +} + function showPrevPage() { currentImage--; if (currentImage < 0) { @@ -421,11 +438,11 @@ function keyHandler(evt) { switch (evt.keyCode) { case kthoom.Key.LEFT: if (hasModifier) break; - showPrevPage(); + showLeftPage(); break; case kthoom.Key.RIGHT: if (hasModifier) break; - showNextPage(); + showRightPage(); break; case kthoom.Key.L: if (hasModifier) break; @@ -486,11 +503,11 @@ function keyHandler(evt) { if (evt.shiftKey && atTop) { evt.preventDefault(); // If it's Shift + Space and the container is at the top of the page - showPrevPage(); + showLeftPage(); } else if (!evt.shiftKey && atBottom) { evt.preventDefault(); // If you're at the bottom of the page and you only pressed space - showNextPage(); + showRightPage(); container.scrollTop(0); } break; @@ -621,25 +638,25 @@ function init(filename) { // Determine if the user clicked/tapped the left side or the // right side of the page. - var clickedPrev = false; + var clickedLeft = false; switch (settings.rotateTimes) { case 0: - clickedPrev = clickX < (comicWidth / 2); + clickedLeft = clickX < (comicWidth / 2); break; case 1: - clickedPrev = clickY < (comicHeight / 2); + clickedLeft = clickY < (comicHeight / 2); break; case 2: - clickedPrev = clickX > (comicWidth / 2); + clickedLeft = clickX > (comicWidth / 2); break; case 3: - clickedPrev = clickY > (comicHeight / 2); + clickedLeft = clickY > (comicHeight / 2); break; } - if (clickedPrev) { - showPrevPage(); + if (clickedLeft) { + showLeftPage(); } else { - showNextPage(); + showRightPage(); } }); } diff --git a/cps/templates/readcbr.html b/cps/templates/readcbr.html index bdc2a3ea..9fdce0f8 100644 --- a/cps/templates/readcbr.html +++ b/cps/templates/readcbr.html @@ -17,9 +17,19 @@ @@ -70,8 +80,8 @@ - - +
+ + + {{_('Direction')}}: + +
+ + +
+ + @@ -152,6 +171,10 @@
- + From f941908f73a8602d72df652271bdb7578776b79f Mon Sep 17 00:00:00 2001 From: Ozzieisaacs Date: Tue, 23 Apr 2019 21:32:48 +0200 Subject: [PATCH 08/21] Workaround for #889 --- cps/web.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/cps/web.py b/cps/web.py index 095020af..b69f83c5 100644 --- a/cps/web.py +++ b/cps/web.py @@ -407,9 +407,14 @@ def mimetype_filter(val): @app.template_filter('formatdate') def formatdate_filter(val): - conformed_timestamp = re.sub(r"[:]|([-](?!((\d{2}[:]\d{2})|(\d{4}))$))", '', val) - formatdate = datetime.datetime.strptime(conformed_timestamp[:15], "%Y%m%d %H%M%S") - return format_date(formatdate, format='medium', locale=get_locale()) + try: + conformed_timestamp = re.sub(r"[:]|([-](?!((\d{2}[:]\d{2})|(\d{4}))$))", '', val) + formatdate = datetime.datetime.strptime(conformed_timestamp[:15], "%Y%m%d %H%M%S") + return format_date(formatdate, format='medium', locale=get_locale()) + except AttributeError as e: + app.logger.error('Babel error: %s, Current user locale: %s, Current User: %s' % (e, current_user.locale, current_user.nickname)) + return formatdate + @app.template_filter('formatdateinput') From b80bfa52605e21574f4525af081b41436bb0fce9 Mon Sep 17 00:00:00 2001 From: Ozzieisaacs Date: Mon, 29 Apr 2019 19:01:30 +0200 Subject: [PATCH 09/21] Improvement for #897 --- cps/web.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cps/web.py b/cps/web.py index b69f83c5..cdfd5f26 100644 --- a/cps/web.py +++ b/cps/web.py @@ -2168,7 +2168,7 @@ def read_book(book_id, book_format): flash(_(u"Error opening eBook. File does not exist or file is not accessible:"), category="error") return redirect(url_for("index")) - # check if book was downloaded before + # check if book has bookmark lbookmark = None if current_user.is_authenticated: lbookmark = ub.session.query(ub.Bookmark).filter(ub.and_(ub.Bookmark.user_id == int(current_user.id), @@ -2203,6 +2203,9 @@ def read_book(book_id, book_format): extension=fileext, title=_(u"Read a Book"), book=book) flash(_(u"Error opening eBook. File does not exist or file is not accessible."), category="error") return redirect(url_for("index"))''' + flash(_(u"Error opening eBook. Fileformat is not supported."), category="error") + return redirect(url_for("index")) + @app.route("/download//") From 55bb8d45903c6aa9e6dfbfb2d83ec3882a42d96f Mon Sep 17 00:00:00 2001 From: Ozzieisaacs Date: Wed, 1 May 2019 17:47:54 +0200 Subject: [PATCH 10/21] Fix #900 --- cps/web.py | 2 +- optional-requirements.txt | 5 ++++- requirements.txt | 1 - 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/cps/web.py b/cps/web.py index cdfd5f26..61a66e81 100644 --- a/cps/web.py +++ b/cps/web.py @@ -704,7 +704,7 @@ def feed_search(term): def render_xml_template(*args, **kwargs): #ToDo: return time in current timezone similar to %z currtime = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%S+00:00") - xml = render_template(current_time=currtime, *args, **kwargs) + xml = render_template(current_time=currtime, instance=config.config_calibre_web_title, *args, **kwargs) response = make_response(xml) response.headers["Content-Type"] = "application/atom+xml; charset=utf-8" return response diff --git a/optional-requirements.txt b/optional-requirements.txt index 154612b3..dd478553 100644 --- a/optional-requirements.txt +++ b/optional-requirements.txt @@ -14,7 +14,10 @@ six==1.10.0 # goodreads goodreads>=0.3.2 python-Levenshtein>=0.12.0 -# other +#extracting metadata lxml>=3.8.0 +Pillow>=4.0.0 rarfile>=2.7 +# other natsort>=2.2.0 + diff --git a/requirements.txt b/requirements.txt index 84ffdd7c..3fb23ea3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,4 +13,3 @@ SQLAlchemy>=1.1.0 tornado>=4.1 Wand>=0.4.4 unidecode>=0.04.19 -Pillow>=5.4.0 \ No newline at end of file From a2c7741e2183d4434b9e7d345074a389c4e910b4 Mon Sep 17 00:00:00 2001 From: Marvin Marx Date: Sun, 5 May 2019 17:43:38 +0200 Subject: [PATCH 11/21] Fix "Internal Server Error" on advanced search Custom boolean columns return that error if calibre does not have a custom_column_1 in the DB, as this is queried in the removed line. However the value is completely unused anyway -> removing. --- cps/web.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cps/web.py b/cps/web.py index cdfd5f26..321ad15c 100644 --- a/cps/web.py +++ b/cps/web.py @@ -2025,7 +2025,6 @@ def advanced_search(): custom_query = request.args.get('custom_column_' + str(c.id)) if custom_query: if c.datatype == 'bool': - getattr(db.Books, 'custom_column_1') q = q.filter(getattr(db.Books, 'custom_column_'+str(c.id)).any( db.cc_classes[c.id].value == (custom_query== "True") )) elif c.datatype == 'int': From a42ebdc0962ce604e14f4b43623e772440451606 Mon Sep 17 00:00:00 2001 From: Ozzieisaacs Date: Tue, 7 May 2019 19:54:13 +0200 Subject: [PATCH 12/21] Fix for #897 --- cps/helper.py | 2 +- cps/templates/readpdf.html | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/cps/helper.py b/cps/helper.py index c11c6909..e5874822 100644 --- a/cps/helper.py +++ b/cps/helper.py @@ -177,7 +177,7 @@ def check_send_to_kindle(entry): # Check if a reader is existing for any of the book formats, if not, return empty list, otherwise return # list with supported formats def check_read_formats(entry): - EXTENSIONS_READER = {'TXT', 'PDF', 'EPUB', 'ZIP', 'CBZ', 'TAR', 'CBT', 'RAR', 'CBR'} + EXTENSIONS_READER = {'TXT', 'PDF', 'EPUB', 'CBZ', 'CBT', 'CBR'} bookformats = list() if len(entry.data): for ele in iter(entry.data): diff --git a/cps/templates/readpdf.html b/cps/templates/readpdf.html index 5f59a062..a49576f2 100644 --- a/cps/templates/readpdf.html +++ b/cps/templates/readpdf.html @@ -34,20 +34,18 @@ See https://github.com/adobe-type-tools/cmap-resources - - + From 479b4b7d82f87290c2bacbbda8aef7382be02eea Mon Sep 17 00:00:00 2001 From: Ozzieisaacs Date: Wed, 8 May 2019 20:24:10 +0200 Subject: [PATCH 13/21] Update translations, integrated update of Italian translation --- cps/translations/de/LC_MESSAGES/messages.mo | Bin 48431 -> 48329 bytes cps/translations/de/LC_MESSAGES/messages.po | 428 +++---- cps/translations/es/LC_MESSAGES/messages.mo | Bin 47833 -> 48145 bytes cps/translations/es/LC_MESSAGES/messages.po | 426 +++---- cps/translations/fr/LC_MESSAGES/messages.mo | Bin 49918 -> 49829 bytes cps/translations/fr/LC_MESSAGES/messages.po | 426 +++---- cps/translations/hu/LC_MESSAGES/messages.mo | Bin 48746 -> 48657 bytes cps/translations/hu/LC_MESSAGES/messages.po | 419 +++---- cps/translations/it/LC_MESSAGES/messages.mo | Bin 45765 -> 47969 bytes cps/translations/it/LC_MESSAGES/messages.po | 1058 +++++++++-------- cps/translations/ja/LC_MESSAGES/messages.mo | Bin 37153 -> 52473 bytes cps/translations/ja/LC_MESSAGES/messages.po | 421 +++---- cps/translations/km/LC_MESSAGES/messages.mo | Bin 58110 -> 58406 bytes cps/translations/km/LC_MESSAGES/messages.po | 424 +++---- cps/translations/nl/LC_MESSAGES/messages.mo | Bin 46973 -> 47296 bytes cps/translations/nl/LC_MESSAGES/messages.po | 426 +++---- cps/translations/pl/LC_MESSAGES/messages.mo | Bin 46437 -> 46799 bytes cps/translations/pl/LC_MESSAGES/messages.po | 424 +++---- cps/translations/ru/LC_MESSAGES/messages.mo | Bin 58826 -> 59026 bytes cps/translations/ru/LC_MESSAGES/messages.po | 426 +++---- cps/translations/sv/LC_MESSAGES/messages.mo | Bin 47395 -> 47724 bytes cps/translations/sv/LC_MESSAGES/messages.po | 426 +++---- cps/translations/uk/LC_MESSAGES/messages.mo | Bin 56164 -> 56594 bytes cps/translations/uk/LC_MESSAGES/messages.po | 426 +++---- .../zh_Hans_CN/LC_MESSAGES/messages.mo | Bin 46152 -> 46504 bytes .../zh_Hans_CN/LC_MESSAGES/messages.po | 426 +++---- messages.pot | 409 ++++--- 27 files changed, 3368 insertions(+), 3197 deletions(-) diff --git a/cps/translations/de/LC_MESSAGES/messages.mo b/cps/translations/de/LC_MESSAGES/messages.mo index 1abbf666db9e1e157e15f05dbfa6834b734419c9..2c5679ecf4fcf7836f74b79be00d4ef98225982b 100644 GIT binary patch delta 13439 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 %(rating)s" msgstr "Bewertung >= %(rating)s" -#: cps/web.py:2022 cps/web.py:2031 +#: cps/web.py:2042 cps/web.py:2051 msgid "search" msgstr "Suche" #: cps/templates/index.xml:47 cps/templates/index.xml:51 -#: cps/templates/layout.html:148 cps/web.py:2099 +#: cps/templates/layout.html:148 cps/web.py:2122 msgid "Read Books" msgstr "Gelesene Bücher" #: cps/templates/index.xml:55 cps/templates/index.xml:59 -#: cps/templates/layout.html:150 cps/web.py:2102 +#: cps/templates/layout.html:150 cps/web.py:2125 msgid "Unread Books" msgstr "Ungelesene Bücher" -#: cps/web.py:2150 cps/web.py:2152 cps/web.py:2154 cps/web.py:2166 +#: cps/web.py:2177 cps/web.py:2179 cps/web.py:2181 cps/web.py:2193 msgid "Read a Book" msgstr "Lese ein Buch" -#: cps/web.py:2225 cps/web.py:3146 +#: cps/web.py:2205 +msgid "Error opening eBook. Fileformat is not supported." +msgstr "Fehler beim Öffnen des Buches, das Format wird nicht unterstützt" + +#: cps/web.py:2255 cps/web.py:3176 msgid "Please fill out all fields!" msgstr "Bitte alle Felder ausfüllen!" -#: cps/web.py:2226 cps/web.py:2248 cps/web.py:2252 cps/web.py:2257 -#: cps/web.py:2259 +#: cps/web.py:2256 cps/web.py:2278 cps/web.py:2282 cps/web.py:2287 +#: cps/web.py:2289 msgid "register" msgstr "Registieren" -#: cps/web.py:2247 cps/web.py:3365 +#: cps/web.py:2277 cps/web.py:3395 msgid "An unknown error occurred. Please try again later." msgstr "Es ist ein unbekannter Fehler aufgetreten. Bitte später erneut versuchen." -#: cps/web.py:2250 +#: cps/web.py:2280 msgid "Your e-mail is not allowed to register" msgstr "Diese E-Mail ist nicht für die Registrierung zugelassen" -#: cps/web.py:2253 +#: cps/web.py:2283 msgid "Confirmation e-mail was send to your e-mail account." msgstr "Eine Bestätigungs E-Mail wurde an den E-Mail Account versendet." -#: cps/web.py:2256 +#: cps/web.py:2286 msgid "This username or e-mail address is already in use." msgstr "Benutzername oder E-Mailadresse ist bereits in Verwendung." -#: cps/web.py:2273 cps/web.py:2369 +#: cps/web.py:2303 cps/web.py:2399 #, python-format msgid "you are now logged in as: '%(nickname)s'" msgstr "Du bist nun eingeloggt als '%(nickname)s'" -#: cps/web.py:2278 +#: cps/web.py:2308 msgid "Wrong Username or Password" msgstr "Falscher Benutzername oder Passwort" -#: cps/web.py:2284 cps/web.py:2305 +#: cps/web.py:2314 cps/web.py:2335 msgid "login" msgstr "Login" -#: cps/web.py:2317 cps/web.py:2348 +#: cps/web.py:2347 cps/web.py:2378 msgid "Token not found" msgstr "Token wurde nicht gefunden" -#: cps/web.py:2325 cps/web.py:2356 +#: cps/web.py:2355 cps/web.py:2386 msgid "Token has expired" msgstr "Das Token ist abgelaufen" -#: cps/web.py:2333 +#: cps/web.py:2363 msgid "Success! Please return to your device" msgstr "Erfolg! Bitte zum Gerät zurückkehren" -#: cps/web.py:2383 +#: cps/web.py:2413 msgid "Please configure the SMTP mail settings first..." msgstr "Bitte zuerst die SMTP Mail Einstellung konfigurieren ..." -#: cps/web.py:2388 +#: cps/web.py:2418 #, python-format msgid "Book successfully queued for sending to %(kindlemail)s" msgstr "Buch erfolgreich zum Senden an %(kindlemail)s eingereiht" -#: cps/web.py:2392 +#: cps/web.py:2422 #, python-format msgid "There was an error sending this book: %(res)s" msgstr "Beim Senden des Buchs trat ein Fehler auf: %(res)s" -#: cps/web.py:2394 cps/web.py:3199 +#: cps/web.py:2424 cps/web.py:3229 msgid "Please configure your kindle e-mail address first..." msgstr "Bitte zuerst die Kindle E-Mailadresse konfigurieren..." -#: cps/web.py:2405 cps/web.py:2457 +#: cps/web.py:2435 cps/web.py:2487 msgid "Invalid shelf specified" msgstr "Ungültiges Bücherregal angegeben" -#: cps/web.py:2412 +#: cps/web.py:2442 #, python-format msgid "Sorry you are not allowed to add a book to the the shelf: %(shelfname)s" msgstr "Keine Erlaubnis ein Buch zum Bücherregale %(shelfname)s hinzuzufügen vorhanden" -#: cps/web.py:2420 +#: cps/web.py:2450 msgid "You are not allowed to edit public shelves" msgstr "Keine Erlaubnis öffentliche Bücherregale zu editieren vorhanden" -#: cps/web.py:2429 +#: cps/web.py:2459 #, python-format msgid "Book is already part of the shelf: %(shelfname)s" msgstr "Buch ist bereits Teil des Bücherregals %(shelfname)s" -#: cps/web.py:2443 +#: cps/web.py:2473 #, python-format msgid "Book has been added to shelf: %(sname)s" msgstr "Das Buch wurde dem Bücherregal: %(sname)s hinzugefügt" -#: cps/web.py:2462 +#: cps/web.py:2492 #, python-format msgid "You are not allowed to add a book to the the shelf: %(name)s" msgstr "Keine Erlaubnis ein Buch zum Bücherregal %(name)s hinzuzufügen" -#: cps/web.py:2467 +#: cps/web.py:2497 msgid "User is not allowed to edit public shelves" msgstr "Benutzer hat keine Erlaubnis öffentliche Bücherregale zu editieren" -#: cps/web.py:2485 +#: cps/web.py:2515 #, python-format msgid "Books are already part of the shelf: %(name)s" msgstr "Bücher sind bereits Teil des Bücherregals %(name)s" -#: cps/web.py:2499 +#: cps/web.py:2529 #, python-format msgid "Books have been added to shelf: %(sname)s" msgstr "Bücher wurden zum Bücherregal %(sname)s hinzugefügt" -#: cps/web.py:2501 +#: cps/web.py:2531 #, python-format msgid "Could not add books to shelf: %(sname)s" msgstr "Bücher konnten nicht zum Bücherregal %(sname)s hinzugefügt werden" -#: cps/web.py:2538 +#: cps/web.py:2568 #, python-format msgid "Book has been removed from shelf: %(sname)s" msgstr "Das Buch wurde aus dem Bücherregal: %(sname)s entfernt" -#: cps/web.py:2544 +#: cps/web.py:2574 #, python-format msgid "Sorry you are not allowed to remove a book from this shelf: %(sname)s" msgstr "Keine Erlaubnis das Buch aus dem Bücherregal %(sname)s zu entfernen" -#: cps/web.py:2565 cps/web.py:2589 +#: cps/web.py:2595 cps/web.py:2619 #, python-format msgid "A shelf with the name '%(title)s' already exists." msgstr "Es existiert bereits ein Bücheregal mit dem Titel '%(title)s'." -#: cps/web.py:2570 +#: cps/web.py:2600 #, python-format msgid "Shelf %(title)s created" msgstr "Bücherregal %(title)s erzeugt" -#: cps/web.py:2572 cps/web.py:2600 +#: cps/web.py:2602 cps/web.py:2630 msgid "There was an error" msgstr "Es trat ein Fehler auf" -#: cps/web.py:2573 cps/web.py:2575 +#: cps/web.py:2603 cps/web.py:2605 msgid "create a shelf" msgstr "Bücherregal erzeugen" -#: cps/web.py:2598 +#: cps/web.py:2628 #, python-format msgid "Shelf %(title)s changed" msgstr "Bücherregal %(title)s verändert" -#: cps/web.py:2601 cps/web.py:2603 +#: cps/web.py:2631 cps/web.py:2633 msgid "Edit a shelf" msgstr "Bücherregal editieren" -#: cps/web.py:2624 +#: cps/web.py:2654 #, python-format msgid "successfully deleted shelf %(name)s" msgstr "Bücherregal %(name)s erfolgreich gelöscht" -#: cps/web.py:2651 +#: cps/web.py:2681 #, python-format msgid "Shelf: '%(name)s'" msgstr "Bücherregal: '%(name)s'" -#: cps/web.py:2654 +#: cps/web.py:2684 msgid "Error opening shelf. Shelf does not exist or is not accessible" msgstr "Fehler beim Öffnen. Bücherregel exisitert nicht oder ist nicht zugänglich" -#: cps/web.py:2685 +#: cps/web.py:2715 #, python-format msgid "Change order of Shelf: '%(name)s'" msgstr "Reihenfolge in Bücherregal '%(name)s' verändern" -#: cps/web.py:2714 cps/web.py:3152 +#: cps/web.py:2744 cps/web.py:3182 msgid "E-mail is not from valid domain" msgstr "E-Mail ist nicht Teil einer gültigen Domain" -#: cps/web.py:2716 cps/web.py:2758 cps/web.py:2761 +#: cps/web.py:2746 cps/web.py:2788 cps/web.py:2791 #, python-format msgid "%(name)s's profile" msgstr "%(name)s's Profil" -#: cps/web.py:2756 +#: cps/web.py:2786 msgid "Found an existing account for this e-mail address." msgstr "Es existiert bereits ein Benutzer für diese E-Mailadresse." -#: cps/web.py:2759 +#: cps/web.py:2789 msgid "Profile updated" msgstr "Profil aktualisiert" -#: cps/web.py:2790 +#: cps/web.py:2820 msgid "Admin page" msgstr "Admin Seite" -#: cps/web.py:2875 cps/web.py:3055 +#: cps/web.py:2905 cps/web.py:3085 msgid "Calibre-Web configuration updated" msgstr "Calibre-Web Konfiguration wurde aktualisiert" -#: cps/templates/admin.html:100 cps/web.py:2889 +#: cps/templates/admin.html:100 cps/web.py:2919 msgid "UI Configuration" msgstr "Konfiguration Benutzeroberfläche" -#: cps/web.py:2907 +#: cps/web.py:2937 msgid "Import of optional Google Drive requirements missing" msgstr "Optionale Abhängigkeiten für Google Drive fehlen" -#: cps/web.py:2910 +#: cps/web.py:2940 msgid "client_secrets.json is missing or not readable" msgstr "client_secrets.json nicht vorhanden, oder nicht lesbar" -#: cps/web.py:2915 cps/web.py:2944 +#: cps/web.py:2945 cps/web.py:2974 msgid "client_secrets.json is not configured for web application" msgstr "client_secrets.json nicht als Webapplication konfiguriert" -#: cps/templates/admin.html:99 cps/web.py:2947 cps/web.py:2973 cps/web.py:2985 -#: cps/web.py:3030 cps/web.py:3045 cps/web.py:3064 cps/web.py:3072 -#: cps/web.py:3088 +#: cps/templates/admin.html:99 cps/web.py:2977 cps/web.py:3003 cps/web.py:3015 +#: cps/web.py:3060 cps/web.py:3075 cps/web.py:3094 cps/web.py:3102 +#: cps/web.py:3118 msgid "Basic Configuration" msgstr "Basis Konfiguration" -#: cps/web.py:2970 +#: cps/web.py:3000 msgid "Keyfile location is not valid, please enter correct path" msgstr "SSL-Keydatei Speicherort ist ungültig, bitte gültigen Pfad angeben" -#: cps/web.py:2982 +#: cps/web.py:3012 msgid "Certfile location is not valid, please enter correct path" msgstr "SSL-Certdatei Speicherort ist ungültig, bitte gültigen Pfad angeben" -#: cps/web.py:3027 +#: cps/web.py:3057 msgid "Logfile location is not valid, please enter correct path" msgstr "Speicherort Logdatei ist ungültig, bitte Pfad korrigieren" -#: cps/web.py:3068 +#: cps/web.py:3098 msgid "DB location is not valid, please enter correct path" msgstr "DB Speicherort ist ungültig, bitte Pfad korrigieren" -#: cps/templates/admin.html:33 cps/web.py:3148 cps/web.py:3154 cps/web.py:3170 +#: cps/templates/admin.html:33 cps/web.py:3178 cps/web.py:3184 cps/web.py:3200 msgid "Add new user" msgstr "Neuen Benutzer hinzufügen" -#: cps/web.py:3160 +#: cps/web.py:3190 #, python-format msgid "User '%(user)s' created" msgstr "Benutzer '%(user)s' angelegt" -#: cps/web.py:3164 +#: cps/web.py:3194 msgid "Found an existing account for this e-mail address or nickname." msgstr "Es existiert bereits ein Account für diese E-Mailadresse oder Benutzernamen." -#: cps/web.py:3194 +#: cps/web.py:3224 #, python-format msgid "Test e-mail successfully send to %(kindlemail)s" msgstr "Test E-Mail wurde erfolgreich an %(kindlemail)s versendet" -#: cps/web.py:3197 +#: cps/web.py:3227 #, python-format msgid "There was an error sending the Test e-mail: %(res)s" msgstr "Es trat ein Fehler beim Versenden der Test E-Mail auf: %(res)s" -#: cps/web.py:3201 +#: cps/web.py:3231 msgid "E-mail server settings updated" msgstr "E-Mail Server Einstellungen aktualisiert" -#: cps/web.py:3202 +#: cps/web.py:3232 msgid "Edit e-mail server settings" msgstr "E-Mail Server Einstellungen bearbeiten" -#: cps/web.py:3227 +#: cps/web.py:3257 #, python-format msgid "User '%(nick)s' deleted" msgstr "Benutzer '%(nick)s' gelöscht" -#: cps/web.py:3340 +#: cps/web.py:3370 #, python-format msgid "User '%(nick)s' updated" msgstr "Benutzer '%(nick)s' aktualisiert" -#: cps/web.py:3343 +#: cps/web.py:3373 msgid "An unknown error occured." msgstr "Es ist ein unbekannter Fehler aufgetreten." -#: cps/web.py:3345 +#: cps/web.py:3375 #, python-format msgid "Edit User %(nick)s" msgstr "Benutzer %(nick)s bearbeiten" -#: cps/web.py:3362 +#: cps/web.py:3392 #, python-format msgid "Password for user %(user)s reset" msgstr "Passwort für Benutzer %(user)s wurde zurückgesetzt" -#: cps/web.py:3376 cps/web.py:3582 +#: cps/web.py:3406 cps/web.py:3598 msgid "Error opening eBook. File does not exist or file is not accessible" msgstr "Buch öffnen fehlgeschlagen. Datei existiert nicht, oder ist nicht zugänglich" -#: cps/web.py:3404 +#: cps/web.py:3434 msgid "edit metadata" msgstr "Metadaten editieren" -#: cps/web.py:3497 cps/web.py:3743 +#: cps/web.py:3527 cps/web.py:3760 #, python-format msgid "File extension '%(ext)s' is not allowed to be uploaded to this server" msgstr "Dateiendung '%(ext)s' kann nicht auf diesen Server hochgeladen werden" -#: cps/web.py:3501 cps/web.py:3746 +#: cps/web.py:3531 cps/web.py:3763 msgid "File to be uploaded must have an extension" msgstr "Dateien müssen eine Erweiterung haben, um hochgeladen zu werden" -#: cps/web.py:3513 cps/web.py:3765 +#: cps/web.py:3543 cps/web.py:3782 #, python-format msgid "Failed to create path %(path)s (Permission denied)." msgstr "Fehler beim Erzeugen des Pfads %(path)s (Zugriff verweigert)" -#: cps/web.py:3518 +#: cps/web.py:3548 #, python-format msgid "Failed to store file %(file)s." msgstr "Fehler beim speichern der Datei %(file)s." -#: cps/web.py:3535 +#: cps/web.py:3565 #, python-format msgid "File format %(ext)s added to %(book)s" msgstr "Dateiformat %(ext)s zu %(book)s hinzugefügt" -#: cps/web.py:3553 -#, python-format -msgid "Failed to create path for cover %(path)s (Permission denied)." -msgstr "Fehler beim Erzeugen des Pfads für das Cover %(path)s (Zugriff verweigert)" - -#: cps/web.py:3561 -#, python-format -msgid "Failed to store cover-file %(cover)s." -msgstr "Fehler beim Speichern des Covers %(cover)s." - -#: cps/web.py:3564 -msgid "Cover-file is not a valid image file" -msgstr "Cover-Datei ist keine gültige Bilddatei" +#: cps/web.py:3579 cps/web.py:3652 +msgid "Cover is not a supported imageformat (jpg/png/webp), can't save" +msgstr "Cover ist keine unterstütztes Bildformat (jpg/png/webp) und kann nicht abgespeichert werden" -#: cps/web.py:3594 cps/web.py:3603 +#: cps/web.py:3611 cps/web.py:3620 msgid "unknown" msgstr "Unbekannt" -#: cps/web.py:3635 -msgid "Cover is not a jpg file, can't save" -msgstr "Cover ist keine JPG Datei, konnte nicht gespeichert werden" - -#: cps/web.py:3683 +#: cps/web.py:3700 #, python-format msgid "%(langname)s is not a valid language" msgstr "%(langname)s ist keine gültige Sprache" -#: cps/web.py:3714 +#: cps/web.py:3731 msgid "Metadata successfully updated" msgstr "Metadaten wurden erfolgreich aktualisiert" -#: cps/web.py:3723 +#: cps/web.py:3740 msgid "Error editing book, please check logfile for details" msgstr "Fehler beim Editieren des Buchs, Details im Logfile" -#: cps/web.py:3769 +#: cps/web.py:3786 #, python-format msgid "Failed to store file %(file)s (Permission denied)." msgstr "Fehler beim speichern der Datei %(file)s (Zugriff verweigert)" -#: cps/web.py:3774 +#: cps/web.py:3791 #, python-format msgid "Failed to delete file %(file)s (Permission denied)." msgstr "Fehler beim Löschen von Datei %(file)s (Zugriff verweigert)" -#: cps/web.py:3857 +#: cps/web.py:3873 #, python-format msgid "File %(title)s" msgstr "Datei %(title)s" -#: cps/web.py:3886 +#: cps/web.py:3902 msgid "Source or destination format for conversion missing" msgstr "Quell- oder Zielformat für Konvertierung fehlt" -#: cps/web.py:3896 +#: cps/web.py:3912 #, python-format msgid "Book successfully queued for converting to %(book_format)s" msgstr "Buch wurde erfolgreich für die Konvertierung in das %(book_format)s Format eingereiht" -#: cps/web.py:3900 +#: cps/web.py:3916 #, python-format msgid "There was an error converting this book: %(res)s" msgstr "Es trat ein Fehlker beim Konvertieren des Buches auf: %(res)s" @@ -1288,7 +1279,7 @@ msgstr "Anzahl Anzeige zufällige Bücher" msgid "No. of authors to show before hiding (0=disable hiding)" msgstr "Anzahl Autoren in Übersicht (0=deaktivieren)" -#: cps/templates/config_view_edit.html:35 cps/templates/readcbr.html:108 +#: cps/templates/config_view_edit.html:35 cps/templates/readcbr.html:118 msgid "Theme" msgstr "Theme" @@ -1602,7 +1593,7 @@ msgid "Advanced Search" msgstr "Erweiterte Suche" #: cps/templates/layout.html:76 cps/templates/read.html:71 -#: cps/templates/readcbr.html:79 cps/templates/readcbr.html:103 +#: cps/templates/readcbr.html:89 cps/templates/readcbr.html:113 msgid "Settings" msgstr "Einstellungen" @@ -1721,98 +1712,106 @@ msgstr "Calibre-Web E-Book Katalog" msgid "Reflow text when sidebars are open." msgstr "Text umbrechen wenn Seitenleiste geöffnet ist." -#: cps/templates/readcbr.html:84 +#: cps/templates/readcbr.html:94 msgid "Keyboard Shortcuts" msgstr "Tastatur Kürzel" -#: cps/templates/readcbr.html:87 +#: cps/templates/readcbr.html:97 msgid "Previous Page" msgstr "Vorherige Seite" -#: cps/templates/readcbr.html:88 +#: cps/templates/readcbr.html:98 msgid "Next Page" msgstr "Nächste Seite" -#: cps/templates/readcbr.html:89 +#: cps/templates/readcbr.html:99 msgid "Scale to Best" msgstr "Optimale Skalierung" -#: cps/templates/readcbr.html:90 +#: cps/templates/readcbr.html:100 msgid "Scale to Width" msgstr "Skaliere auf Breite" -#: cps/templates/readcbr.html:91 +#: cps/templates/readcbr.html:101 msgid "Scale to Height" msgstr "Skaliere auf Höhe" -#: cps/templates/readcbr.html:92 +#: cps/templates/readcbr.html:102 msgid "Scale to Native" msgstr "Skaliere 1:1" -#: cps/templates/readcbr.html:93 +#: cps/templates/readcbr.html:103 msgid "Rotate Right" msgstr "Rechts rotieren" -#: cps/templates/readcbr.html:94 +#: cps/templates/readcbr.html:104 msgid "Rotate Left" msgstr "Links rotieren" -#: cps/templates/readcbr.html:95 +#: cps/templates/readcbr.html:105 msgid "Flip Image" msgstr "Bild umdrehen" -#: cps/templates/readcbr.html:111 +#: cps/templates/readcbr.html:121 msgid "Light" msgstr "Hell" -#: cps/templates/readcbr.html:112 +#: cps/templates/readcbr.html:122 msgid "Dark" msgstr "Dunkel" -#: cps/templates/readcbr.html:117 +#: cps/templates/readcbr.html:127 msgid "Scale" msgstr "Skalierung" -#: cps/templates/readcbr.html:120 +#: cps/templates/readcbr.html:130 msgid "Best" msgstr "Beste" -#: cps/templates/readcbr.html:121 +#: cps/templates/readcbr.html:131 msgid "Width" msgstr "Breite" -#: cps/templates/readcbr.html:122 +#: cps/templates/readcbr.html:132 msgid "Height" msgstr "Höhe" -#: cps/templates/readcbr.html:123 +#: cps/templates/readcbr.html:133 msgid "Native" msgstr "1:1" -#: cps/templates/readcbr.html:128 +#: cps/templates/readcbr.html:138 msgid "Rotate" msgstr "Rotieren" -#: cps/templates/readcbr.html:139 +#: cps/templates/readcbr.html:149 msgid "Flip" msgstr "Umdrehen" -#: cps/templates/readcbr.html:142 +#: cps/templates/readcbr.html:152 msgid "Horizontal" msgstr "Horizontal" -#: cps/templates/readcbr.html:143 +#: cps/templates/readcbr.html:153 msgid "Vertical" msgstr "Vertikal" +#: cps/templates/readcbr.html:158 +msgid "Direction" +msgstr "Leserichtung" + +#: cps/templates/readcbr.html:161 +msgid "Left to Right" +msgstr "Links nach rechts" + +#: cps/templates/readcbr.html:162 +msgid "Right to Left" +msgstr "Rechts nach links" + #: cps/templates/readpdf.html:29 msgid "PDF.js viewer" msgstr "PDF.js Viewer" -#: cps/templates/readpdf.html:418 -msgid "Preparing document for printing..." -msgstr "Bereite Dokument zum Ducken vor..." - #: cps/templates/readtxt.html:6 msgid "Basic txt Reader" msgstr "Basis Txt Reader" @@ -2005,3 +2004,18 @@ msgstr "Benutzer löschen" msgid "Recent Downloads" msgstr "Letzte Downloads" +#~ msgid "Failed to create path for cover %(path)s (Permission denied)." +#~ msgstr "Fehler beim Erzeugen des Pfads für das Cover %(path)s (Zugriff verweigert)" + +#~ msgid "Failed to store cover-file %(cover)s." +#~ msgstr "Fehler beim Speichern des Covers %(cover)s." + +#~ msgid "Cover-file is not a valid image file" +#~ msgstr "Cover-Datei ist keine gültige Bilddatei" + +#~ msgid "Cover is not a jpg file, can't save" +#~ msgstr "Cover ist keine JPG Datei, konnte nicht gespeichert werden" + +#~ msgid "Preparing document for printing..." +#~ msgstr "Bereite Dokument zum Ducken vor..." + diff --git a/cps/translations/es/LC_MESSAGES/messages.mo b/cps/translations/es/LC_MESSAGES/messages.mo index 87d90d014cb534c94b97059dbd7ba6ce83455f05..5c2ddd295e5429c7707575bda32fc87b8ac905a3 100644 GIT binary patch delta 15126 zcmb8$XLwdsy2kOHMhhtu+>d4PE%Rg4L|5$jb@L7e)BX@c z(7%o2l)&=nkJV7?d9XC=JJA$0aZ~Hi!8&v`d!iQVkCkvRYNr!W3(Q3Y_zEg{8!-sq z!WwuM)$a#n$Ifq91q0eTP7~I5YEV#wy-^bnu=*HO%JNVXO}FQ-p(fgc8h04W<6Eem zeTo`)&FZ(!zo8cX1vM@_p8RW~suZ-dni!64P#t=rp7*u-U^5*9X&;SB=_FJJpGO5S z4>fL?S%}KO2CMHwt#dS<{OfX^qd^N^M@@JOmD0bW&hjVJgaHZe^B~l?FtZjafM%!w zI--s&*-S;v+YdF*K-30?Cy;+V$e}?K_O*mTO0rupPF*DOex( zpeFv@yp4^h{~e<-vc2O}z!X%*)4UXPCR4B~7TAMb*qr)lR0bZSA`k4~1{{vcSY@*Y zYUg!PXCI4t-WoNo11clkQS+y~A(FymR3L?@$o69zevG=!F&*8B;!zpuf|}qd z)bmlO%R3ph-~w!o1*pKznio-*^*WMquk(OHBn>{D+<~=FXWIZZFy8EH&wHBvP&*ld z+VNOxpM>S8dr{+;T6-bt=r*7-aUO&8{(nZH84Wj33s>&!ezBsF2%Ukbh)1G!IvsV! z3s4KMM5TH?YUf)~3-7@wJc2dxYaE3BT^xtgbkZ=G^_}$;{BaK|vID5s-fX}^Ph7}3=Ypr#pZ#-TsYTVf52#|RvR3TPU7wZIw*+QAmo&UT@0=}}Z57f>nv z3YDSzs5{`-&FvqI%2ZWU2BOSZ44~c;m8k^OdWonzmfDT{D@7w|h`}YO*XS)&`yEsu zVTo>l^-vSUqAp)6)XttpEtF?Y!6&GDt^OKn+(y(T-+?9YbRzlJB|1xkCi()EqQ|HK z)w;Vou8q2+%~2`skIKLx)UD4%Wnd~+!#SwCvJusPFRI@`)WT=5Bwp}RP(+teJGzfL zo1i3j;W*Sn9Z>^%pcd$3=Aw>dAu0o}p)T`AtcaJ9d+ppq?w`{n*}dEok#CaY-AX|N zZ)0iv0ZZY}sLSP~xDorKG82fp3-wW%i%0D|#oC`i?Qj(8l8!SsV-xCEQ1g`S!JEeV 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 J5PI~?e*wf1Z_xk% delta 14831 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 ckpDNQM^;9K27W6V5fzxcVb+6R088=a6eCjbBd diff --git a/cps/translations/es/LC_MESSAGES/messages.po b/cps/translations/es/LC_MESSAGES/messages.po index c3803984..cb5773e8 100644 --- a/cps/translations/es/LC_MESSAGES/messages.po +++ b/cps/translations/es/LC_MESSAGES/messages.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Calibre-Web\n" "Report-Msgid-Bugs-To: https://github.com/janeczku/Calibre-Web\n" -"POT-Creation-Date: 2019-03-10 08:03+0100\n" +"POT-Creation-Date: 2019-05-08 20:23+0200\n" "PO-Revision-Date: 2018-10-05 11:27+0100\n" "Last-Translator: victorhck \n" "Language: es\n" @@ -18,8 +18,9 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.6.0\n" -#: cps/book_formats.py:152 cps/book_formats.py:153 cps/book_formats.py:157 -#: cps/book_formats.py:161 cps/converter.py:29 cps/converter.py:45 +#: cps/book_formats.py:199 cps/book_formats.py:200 cps/book_formats.py:204 +#: cps/book_formats.py:208 cps/book_formats.py:212 cps/converter.py:29 +#: cps/converter.py:45 msgid "not installed" msgstr "No instalado" @@ -31,133 +32,133 @@ msgstr "Permisos de ejecución ausentes" msgid "not configured" msgstr "" -#: cps/helper.py:72 +#: cps/helper.py:79 #, python-format msgid "%(format)s format not found for book id: %(book)d" msgstr "%(format)s formato no encontrado para el id del libro: %(book)d" -#: cps/helper.py:84 +#: cps/helper.py:91 #, python-format msgid "%(format)s not found on Google Drive: %(fn)s" msgstr "%(format)s no encontrado en Google Drive: %(fn)s" -#: cps/helper.py:91 cps/helper.py:199 cps/templates/detail.html:45 +#: cps/helper.py:98 cps/helper.py:204 cps/templates/detail.html:45 #: cps/templates/detail.html:49 msgid "Send to Kindle" msgstr "Enviar a Kindle" -#: cps/helper.py:92 cps/helper.py:110 cps/helper.py:201 +#: cps/helper.py:99 cps/helper.py:117 cps/helper.py:206 msgid "This e-mail has been sent via Calibre-Web." msgstr "Este correo electrónico ha sido enviado por Calibre-Web." -#: cps/helper.py:103 +#: cps/helper.py:110 #, python-format msgid "%(format)s not found: %(fn)s" msgstr "%(format)s no encontrado: %(fn)s" -#: cps/helper.py:108 +#: cps/helper.py:115 msgid "Calibre-Web test e-mail" msgstr "Calibre-Web comprobar correo electrónico" -#: cps/helper.py:109 +#: cps/helper.py:116 msgid "Test e-mail" msgstr "Comprobar correo electrónico" -#: cps/helper.py:125 +#: cps/helper.py:132 msgid "Get Started with Calibre-Web" msgstr "Primeros pasos con Calibre-Web" -#: cps/helper.py:126 +#: cps/helper.py:133 #, python-format msgid "Registration e-mail for user: %(name)s" msgstr "Registrar un correo electrónico para el usuario: %(name)s" -#: cps/helper.py:139 cps/helper.py:141 cps/helper.py:143 cps/helper.py:145 -#: cps/helper.py:151 cps/helper.py:153 cps/helper.py:155 cps/helper.py:157 +#: cps/helper.py:146 cps/helper.py:148 cps/helper.py:150 cps/helper.py:158 +#: cps/helper.py:160 cps/helper.py:162 #, python-format msgid "Send %(format)s to Kindle" msgstr "" -#: cps/helper.py:161 cps/helper.py:165 +#: cps/helper.py:166 #, python-format msgid "Convert %(orig)s to %(format)s and send to Kindle" msgstr "" -#: cps/helper.py:200 +#: cps/helper.py:205 #, python-format msgid "E-mail: %(book)s" msgstr "Correo electrónico: %(book)s" -#: cps/helper.py:203 +#: cps/helper.py:208 msgid "The requested file could not be read. Maybe wrong permissions?" msgstr "El fichero solicitado no puede ser leído. ¿Quizás existen problemas con los permisos?" -#: cps/helper.py:311 +#: cps/helper.py:316 #, python-format msgid "Rename title from: '%(src)s' to '%(dest)s' failed with error: %(error)s" msgstr "El renombrado del título de: '%(src)s' a '%(dest)s' falló con errores: %(error)s" -#: cps/helper.py:321 +#: cps/helper.py:326 #, python-format msgid "Rename author from: '%(src)s' to '%(dest)s' failed with error: %(error)s" msgstr "El renombrado del autor de: '%(src)s' a '%(dest)s' falló con errores: %(error)s" -#: cps/helper.py:335 +#: cps/helper.py:340 #, python-format msgid "Rename file in path '%(src)s' to '%(dest)s' failed with error: %(error)s" msgstr "" -#: cps/helper.py:361 cps/helper.py:371 cps/helper.py:379 +#: cps/helper.py:366 cps/helper.py:376 cps/helper.py:384 #, python-format msgid "File %(file)s not found on Google Drive" msgstr "Fichero %(file)s no encontrado en Google Drive" -#: cps/helper.py:400 +#: cps/helper.py:405 #, python-format msgid "Book path %(path)s not found on Google Drive" msgstr "La ruta %(path)s del libro no fue encontrada en Google Drive" -#: cps/helper.py:508 +#: cps/helper.py:556 msgid "Error excecuting UnRar" msgstr "Error ejecutando UnRar" -#: cps/helper.py:510 +#: cps/helper.py:558 msgid "Unrar binary file not found" msgstr "Fichero binario Unrar no encontrado" -#: cps/helper.py:541 +#: cps/helper.py:589 msgid "Waiting" msgstr "Esperando" -#: cps/helper.py:543 +#: cps/helper.py:591 msgid "Failed" msgstr "Fallido" -#: cps/helper.py:545 +#: cps/helper.py:593 msgid "Started" msgstr "Comenzado" -#: cps/helper.py:547 +#: cps/helper.py:595 msgid "Finished" msgstr "Finalizado" -#: cps/helper.py:549 +#: cps/helper.py:597 msgid "Unknown Status" msgstr "" -#: cps/helper.py:554 +#: cps/helper.py:602 msgid "E-mail: " msgstr "" -#: cps/helper.py:556 cps/helper.py:560 +#: cps/helper.py:604 cps/helper.py:608 msgid "Convert: " msgstr "" -#: cps/helper.py:558 +#: cps/helper.py:606 msgid "Upload: " msgstr "" -#: cps/helper.py:562 +#: cps/helper.py:610 msgid "Unknown Task: " msgstr "" @@ -169,19 +170,19 @@ msgstr "Dato inesperado mientras se leía la información de actualización" msgid "No update available. You already have the latest version installed" msgstr "Actualización no disponible. Ya tienes la versión más reciente instalada" -#: cps/updater.py:270 cps/updater.py:501 cps/updater.py:503 cps/web.py:1187 +#: cps/updater.py:270 cps/updater.py:501 cps/updater.py:503 cps/web.py:1206 msgid "HTTP Error" msgstr "Error HTTP" -#: cps/updater.py:272 cps/updater.py:505 cps/web.py:1188 +#: cps/updater.py:272 cps/updater.py:505 cps/web.py:1207 msgid "Connection error" msgstr "Error de conexión" -#: cps/updater.py:274 cps/updater.py:507 cps/web.py:1189 +#: cps/updater.py:274 cps/updater.py:507 cps/web.py:1208 msgid "Timeout while establishing connection" msgstr "Tiempo agotado mientras se trataba de establecer la conexión" -#: cps/updater.py:276 cps/updater.py:509 cps/web.py:1190 +#: cps/updater.py:276 cps/updater.py:509 cps/web.py:1209 msgid "General error" msgstr "Error general" @@ -202,555 +203,545 @@ msgstr "" msgid "A new update is available. Click on the button below to update to version: %(version)s" msgstr "" -#: cps/updater.py:491 cps/web.py:2771 +#: cps/updater.py:491 cps/web.py:2801 msgid "Unknown" msgstr "Desconocido" -#: cps/web.py:1180 +#: cps/web.py:1199 msgid "Requesting update package" msgstr "Solicitando paquete de actualización" -#: cps/web.py:1181 +#: cps/web.py:1200 msgid "Downloading update package" msgstr "Descargando paquete de actualización" -#: cps/web.py:1182 +#: cps/web.py:1201 msgid "Unzipping update package" msgstr "Descomprimiendo paquete de actualización" -#: cps/web.py:1183 +#: cps/web.py:1202 msgid "Replacing files" msgstr "" -#: cps/web.py:1184 +#: cps/web.py:1203 msgid "Database connections are closed" msgstr "Los conexiones de base datos están cerradas" -#: cps/web.py:1185 +#: cps/web.py:1204 msgid "Stopping server" msgstr "" -#: cps/web.py:1186 +#: cps/web.py:1205 msgid "Update finished, please press okay and reload page" msgstr "Actualización finalizada. Por favor, pulse OK y recargue la página" -#: cps/web.py:1187 cps/web.py:1188 cps/web.py:1189 cps/web.py:1190 +#: cps/web.py:1206 cps/web.py:1207 cps/web.py:1208 cps/web.py:1209 msgid "Update failed:" msgstr "" -#: cps/web.py:1213 +#: cps/web.py:1235 msgid "Recently Added Books" msgstr "Libros recientemente añadidos" -#: cps/web.py:1223 +#: cps/web.py:1245 msgid "Newest Books" msgstr "Libros más nuevos" -#: cps/web.py:1235 +#: cps/web.py:1257 msgid "Oldest Books" msgstr "Libros más antiguos" -#: cps/web.py:1247 +#: cps/web.py:1269 msgid "Books (A-Z)" msgstr "Libros (A-Z)" -#: cps/web.py:1258 +#: cps/web.py:1280 msgid "Books (Z-A)" msgstr "Libros (Z-A)" -#: cps/web.py:1287 +#: cps/web.py:1309 msgid "Hot Books (most downloaded)" msgstr "Libros populares (los mas descargados)" -#: cps/web.py:1300 +#: cps/web.py:1322 msgid "Best rated books" msgstr "Libros mejor valorados" -#: cps/templates/index.xml:39 cps/web.py:1313 +#: cps/templates/index.xml:39 cps/web.py:1335 msgid "Random Books" msgstr "Libros al azar" -#: cps/web.py:1340 cps/web.py:1596 cps/web.py:2140 +#: cps/web.py:1362 cps/web.py:1618 cps/web.py:2167 msgid "Error opening eBook. File does not exist or file is not accessible:" msgstr "Error en la apertura del eBook. El archivo no existe o no es accesible:" -#: cps/web.py:1369 +#: cps/web.py:1391 msgid "Publisher list" msgstr "" -#: cps/web.py:1384 +#: cps/web.py:1406 #, python-format msgid "Publisher: %(name)s" msgstr "" -#: cps/templates/index.xml:83 cps/web.py:1416 +#: cps/templates/index.xml:83 cps/web.py:1438 msgid "Series list" msgstr "Lista de series" -#: cps/web.py:1430 +#: cps/web.py:1452 #, python-format msgid "Series: %(serie)s" msgstr "Series : %(serie)s" -#: cps/web.py:1456 +#: cps/web.py:1478 msgid "Available languages" msgstr "Idiomas disponibles" -#: cps/web.py:1476 +#: cps/web.py:1498 #, python-format msgid "Language: %(name)s" msgstr "Idioma: %(name)s" -#: cps/templates/index.xml:76 cps/web.py:1487 +#: cps/templates/index.xml:76 cps/web.py:1509 msgid "Category list" msgstr "Lista de categorías" -#: cps/web.py:1501 +#: cps/web.py:1523 #, python-format msgid "Category: %(name)s" msgstr "Categoría : %(name)s" -#: cps/templates/layout.html:73 cps/web.py:1632 +#: cps/templates/layout.html:73 cps/web.py:1654 msgid "Tasks" msgstr "Tareas" -#: cps/web.py:1666 +#: cps/web.py:1688 msgid "Statistics" msgstr "Estadísticas" -#: cps/web.py:1734 +#: cps/web.py:1756 msgid "Google Drive setup not completed, try to deactivate and activate Google Drive again" msgstr "" -#: cps/web.py:1779 +#: cps/web.py:1801 msgid "Callback domain is not verified, please follow steps to verify domain in google developer console" msgstr "El dominio de devolución de llamada no se ha verificado, siga los pasos para verificar el dominio en la consola de desarrollador de Google" -#: cps/web.py:1855 +#: cps/web.py:1877 msgid "Server restarted, please reload page" msgstr "Servidor reiniciado. Por favor, recargue la página" -#: cps/web.py:1858 +#: cps/web.py:1880 msgid "Performing shutdown of server, please close window" msgstr "Servidor en proceso de apagado. Por favor, cierre la ventana." -#: cps/web.py:1938 +#: cps/web.py:1959 msgid "Published after " msgstr "Publicado antes de" -#: cps/web.py:1945 +#: cps/web.py:1966 msgid "Published before " msgstr "Publicado después de" -#: cps/web.py:1959 +#: cps/web.py:1980 #, python-format msgid "Rating <= %(rating)s" msgstr "Clasificación <= %(rating)s" -#: cps/web.py:1961 +#: cps/web.py:1982 #, python-format msgid "Rating >= %(rating)s" msgstr "Clasificación >= %(rating)s" -#: cps/web.py:2022 cps/web.py:2031 +#: cps/web.py:2042 cps/web.py:2051 msgid "search" msgstr "búsqueda" #: cps/templates/index.xml:47 cps/templates/index.xml:51 -#: cps/templates/layout.html:148 cps/web.py:2099 +#: cps/templates/layout.html:148 cps/web.py:2122 msgid "Read Books" msgstr "Libros leídos" #: cps/templates/index.xml:55 cps/templates/index.xml:59 -#: cps/templates/layout.html:150 cps/web.py:2102 +#: cps/templates/layout.html:150 cps/web.py:2125 msgid "Unread Books" msgstr "Libros no leídos" -#: cps/web.py:2150 cps/web.py:2152 cps/web.py:2154 cps/web.py:2166 +#: cps/web.py:2177 cps/web.py:2179 cps/web.py:2181 cps/web.py:2193 msgid "Read a Book" msgstr "Leer un libro" -#: cps/web.py:2225 cps/web.py:3146 +#: cps/web.py:2205 +msgid "Error opening eBook. Fileformat is not supported." +msgstr "" + +#: cps/web.py:2255 cps/web.py:3176 msgid "Please fill out all fields!" msgstr "¡Por favor completar todos los campos!" -#: cps/web.py:2226 cps/web.py:2248 cps/web.py:2252 cps/web.py:2257 -#: cps/web.py:2259 +#: cps/web.py:2256 cps/web.py:2278 cps/web.py:2282 cps/web.py:2287 +#: cps/web.py:2289 msgid "register" msgstr "registrarse" -#: cps/web.py:2247 cps/web.py:3365 +#: cps/web.py:2277 cps/web.py:3395 msgid "An unknown error occurred. Please try again later." msgstr "Ha ocurrido un error desconocido. Por favor vuelva a intentarlo más tarde." -#: cps/web.py:2250 +#: cps/web.py:2280 msgid "Your e-mail is not allowed to register" msgstr "Su correo electrónico no está permitido para registrarse" -#: cps/web.py:2253 +#: cps/web.py:2283 msgid "Confirmation e-mail was send to your e-mail account." msgstr "Se ha enviado un correo electrónico de verificación a su cuenta de correo electrónico." -#: cps/web.py:2256 +#: cps/web.py:2286 msgid "This username or e-mail address is already in use." msgstr "Este nombre de usuario o correo electrónico ya están en uso." -#: cps/web.py:2273 cps/web.py:2369 +#: cps/web.py:2303 cps/web.py:2399 #, python-format msgid "you are now logged in as: '%(nickname)s'" msgstr "Sesión iniciada como : '%(nickname)s'" -#: cps/web.py:2278 +#: cps/web.py:2308 msgid "Wrong Username or Password" msgstr "Usuario o contraseña inválido" -#: cps/web.py:2284 cps/web.py:2305 +#: cps/web.py:2314 cps/web.py:2335 msgid "login" msgstr "Iniciar sesión" -#: cps/web.py:2317 cps/web.py:2348 +#: cps/web.py:2347 cps/web.py:2378 msgid "Token not found" msgstr "Token no encontrado" -#: cps/web.py:2325 cps/web.py:2356 +#: cps/web.py:2355 cps/web.py:2386 msgid "Token has expired" msgstr "El token ha expirado" -#: cps/web.py:2333 +#: cps/web.py:2363 msgid "Success! Please return to your device" msgstr "¡Correcto! Por favor regrese a su dispositivo" -#: cps/web.py:2383 +#: cps/web.py:2413 msgid "Please configure the SMTP mail settings first..." msgstr "Configurar primero los parámetros SMTP por favor..." -#: cps/web.py:2388 +#: cps/web.py:2418 #, python-format msgid "Book successfully queued for sending to %(kindlemail)s" msgstr "Libro puesto en la cola de envío a %(kindlemail)s" -#: cps/web.py:2392 +#: cps/web.py:2422 #, python-format msgid "There was an error sending this book: %(res)s" msgstr "Ha sucedido un error en el envío del libro: %(res)s" -#: cps/web.py:2394 cps/web.py:3199 +#: cps/web.py:2424 cps/web.py:3229 msgid "Please configure your kindle e-mail address first..." msgstr "Por favor configure primero la dirección de correo de su kindle..." -#: cps/web.py:2405 cps/web.py:2457 +#: cps/web.py:2435 cps/web.py:2487 msgid "Invalid shelf specified" msgstr "Estante especificado inválido" -#: cps/web.py:2412 +#: cps/web.py:2442 #, python-format msgid "Sorry you are not allowed to add a book to the the shelf: %(shelfname)s" msgstr "" -#: cps/web.py:2420 +#: cps/web.py:2450 msgid "You are not allowed to edit public shelves" msgstr "" -#: cps/web.py:2429 +#: cps/web.py:2459 #, python-format msgid "Book is already part of the shelf: %(shelfname)s" msgstr "" -#: cps/web.py:2443 +#: cps/web.py:2473 #, python-format msgid "Book has been added to shelf: %(sname)s" msgstr "El libro fue agregado a el estante: %(sname)s" -#: cps/web.py:2462 +#: cps/web.py:2492 #, python-format msgid "You are not allowed to add a book to the the shelf: %(name)s" msgstr "No tiene permiso para añadir un libro a el estante: %(name)s" -#: cps/web.py:2467 +#: cps/web.py:2497 msgid "User is not allowed to edit public shelves" msgstr "El usuario no tiene permiso para editar estantes públicos" -#: cps/web.py:2485 +#: cps/web.py:2515 #, python-format msgid "Books are already part of the shelf: %(name)s" msgstr "Los libros ya forman parte del estante: %(name)s" -#: cps/web.py:2499 +#: cps/web.py:2529 #, python-format msgid "Books have been added to shelf: %(sname)s" msgstr "Los libros han sido añadidos al estante: %(sname)s" -#: cps/web.py:2501 +#: cps/web.py:2531 #, python-format msgid "Could not add books to shelf: %(sname)s" msgstr "No se pudieron agregar libros al estante: %(sname)s" -#: cps/web.py:2538 +#: cps/web.py:2568 #, python-format msgid "Book has been removed from shelf: %(sname)s" msgstr "El libro fue eliminado del estante: %(sname)s" -#: cps/web.py:2544 +#: cps/web.py:2574 #, python-format msgid "Sorry you are not allowed to remove a book from this shelf: %(sname)s" msgstr "Lo siento, no tiene permiso para eliminar un libro del estante: %(sname)s" -#: cps/web.py:2565 cps/web.py:2589 +#: cps/web.py:2595 cps/web.py:2619 #, python-format msgid "A shelf with the name '%(title)s' already exists." msgstr "Un estante con el nombre '%(title)s' ya existe." -#: cps/web.py:2570 +#: cps/web.py:2600 #, python-format msgid "Shelf %(title)s created" msgstr "Estante %(title)s creado" -#: cps/web.py:2572 cps/web.py:2600 +#: cps/web.py:2602 cps/web.py:2630 msgid "There was an error" msgstr "Ha sucedido un error" -#: cps/web.py:2573 cps/web.py:2575 +#: cps/web.py:2603 cps/web.py:2605 msgid "create a shelf" msgstr "crear un estante" -#: cps/web.py:2598 +#: cps/web.py:2628 #, python-format msgid "Shelf %(title)s changed" msgstr "Estante %(title)s cambiado" -#: cps/web.py:2601 cps/web.py:2603 +#: cps/web.py:2631 cps/web.py:2633 msgid "Edit a shelf" msgstr "Editar un estante" -#: cps/web.py:2624 +#: cps/web.py:2654 #, python-format msgid "successfully deleted shelf %(name)s" msgstr "Estante %(name)s fue borrado correctamente" -#: cps/web.py:2651 +#: cps/web.py:2681 #, python-format msgid "Shelf: '%(name)s'" msgstr "Estante: '%(name)s'" -#: cps/web.py:2654 +#: cps/web.py:2684 msgid "Error opening shelf. Shelf does not exist or is not accessible" msgstr "Error al abrir un estante. El estante no existe o no es accesible" -#: cps/web.py:2685 +#: cps/web.py:2715 #, python-format msgid "Change order of Shelf: '%(name)s'" msgstr "Cambiar orden del estante: '%(name)s'" -#: cps/web.py:2714 cps/web.py:3152 +#: cps/web.py:2744 cps/web.py:3182 msgid "E-mail is not from valid domain" msgstr "El correo electrónico no tiene un nombre de dominio válido" -#: cps/web.py:2716 cps/web.py:2758 cps/web.py:2761 +#: cps/web.py:2746 cps/web.py:2788 cps/web.py:2791 #, python-format msgid "%(name)s's profile" msgstr "Perfil de %(name)s" -#: cps/web.py:2756 +#: cps/web.py:2786 msgid "Found an existing account for this e-mail address." msgstr "Encontrada una cuenta existente para esa dirección de correo electrónico." -#: cps/web.py:2759 +#: cps/web.py:2789 msgid "Profile updated" msgstr "Perfil actualizado" -#: cps/web.py:2790 +#: cps/web.py:2820 msgid "Admin page" msgstr "Página de administración" -#: cps/web.py:2875 cps/web.py:3055 +#: cps/web.py:2905 cps/web.py:3085 msgid "Calibre-Web configuration updated" msgstr "Configuración de Calibre-Web actualizada" -#: cps/templates/admin.html:100 cps/web.py:2889 +#: cps/templates/admin.html:100 cps/web.py:2919 msgid "UI Configuration" msgstr "Configuración de la interfaz del usuario" -#: cps/web.py:2907 +#: cps/web.py:2937 msgid "Import of optional Google Drive requirements missing" msgstr "Falta la importación de requisitos opcionales de Google Drive" -#: cps/web.py:2910 +#: cps/web.py:2940 msgid "client_secrets.json is missing or not readable" msgstr "client_secrets.json está desaparecido o no se puede leer" -#: cps/web.py:2915 cps/web.py:2944 +#: cps/web.py:2945 cps/web.py:2974 msgid "client_secrets.json is not configured for web application" msgstr "client_secrets.json no está configurado para la aplicación web" -#: cps/templates/admin.html:99 cps/web.py:2947 cps/web.py:2973 cps/web.py:2985 -#: cps/web.py:3030 cps/web.py:3045 cps/web.py:3064 cps/web.py:3072 -#: cps/web.py:3088 +#: cps/templates/admin.html:99 cps/web.py:2977 cps/web.py:3003 cps/web.py:3015 +#: cps/web.py:3060 cps/web.py:3075 cps/web.py:3094 cps/web.py:3102 +#: cps/web.py:3118 msgid "Basic Configuration" msgstr "Configuración básica" -#: cps/web.py:2970 +#: cps/web.py:3000 msgid "Keyfile location is not valid, please enter correct path" msgstr "La ubicación del fichero clave (Keyfile) no es válida, por favor introduzca la ruta correcta" -#: cps/web.py:2982 +#: cps/web.py:3012 msgid "Certfile location is not valid, please enter correct path" msgstr "La ubicación del fichero de certificado (Certfile) no es válida, por favor introduzca la ruta correcta" -#: cps/web.py:3027 +#: cps/web.py:3057 msgid "Logfile location is not valid, please enter correct path" msgstr "La ubicación del fichero de registro (Logfile) no es válida, por favor introduzca la ruta correcta" -#: cps/web.py:3068 +#: cps/web.py:3098 msgid "DB location is not valid, please enter correct path" msgstr "Localización de la BD inválida, por favor introduzca la ruta correcta" -#: cps/templates/admin.html:33 cps/web.py:3148 cps/web.py:3154 cps/web.py:3170 +#: cps/templates/admin.html:33 cps/web.py:3178 cps/web.py:3184 cps/web.py:3200 msgid "Add new user" msgstr "Agregar un nuevo usuario" -#: cps/web.py:3160 +#: cps/web.py:3190 #, python-format msgid "User '%(user)s' created" msgstr "Usuario '%(user)s' creado" -#: cps/web.py:3164 +#: cps/web.py:3194 msgid "Found an existing account for this e-mail address or nickname." msgstr "Encontrada una cuenta existente para este correo electrónico o nombre de usuario." -#: cps/web.py:3194 +#: cps/web.py:3224 #, python-format msgid "Test e-mail successfully send to %(kindlemail)s" msgstr "Correo electrónico de prueba enviado con éxito a %(kindlemail)s" -#: cps/web.py:3197 +#: cps/web.py:3227 #, python-format msgid "There was an error sending the Test e-mail: %(res)s" msgstr "Ocurrió un error enviando el correo electrónico de prueba: %(res)s" -#: cps/web.py:3201 +#: cps/web.py:3231 msgid "E-mail server settings updated" msgstr "Actualizados los ajustes del servidor de correo electrónico" -#: cps/web.py:3202 +#: cps/web.py:3232 msgid "Edit e-mail server settings" msgstr "Editar los ajustes del servidor de correo electrónico" -#: cps/web.py:3227 +#: cps/web.py:3257 #, python-format msgid "User '%(nick)s' deleted" msgstr "Usuario '%(nick)s' borrado" -#: cps/web.py:3340 +#: cps/web.py:3370 #, python-format msgid "User '%(nick)s' updated" msgstr "Usuario '%(nick)s' actualizado" -#: cps/web.py:3343 +#: cps/web.py:3373 msgid "An unknown error occured." msgstr "Ocurrió un error inesperado." -#: cps/web.py:3345 +#: cps/web.py:3375 #, python-format msgid "Edit User %(nick)s" msgstr "Editar Usuario %(nick)s" -#: cps/web.py:3362 +#: cps/web.py:3392 #, python-format msgid "Password for user %(user)s reset" msgstr "Contraseña para el usuario %(user)s reinicializada" -#: cps/web.py:3376 cps/web.py:3582 +#: cps/web.py:3406 cps/web.py:3598 msgid "Error opening eBook. File does not exist or file is not accessible" msgstr "Error abriendo un eBook. El archivo no existe o no es accesible" -#: cps/web.py:3404 +#: cps/web.py:3434 msgid "edit metadata" msgstr "editar metadatos" -#: cps/web.py:3497 cps/web.py:3743 +#: cps/web.py:3527 cps/web.py:3760 #, python-format msgid "File extension '%(ext)s' is not allowed to be uploaded to this server" msgstr "No se permite subir archivos con la extensión '%(ext)s' a este servidor" -#: cps/web.py:3501 cps/web.py:3746 +#: cps/web.py:3531 cps/web.py:3763 msgid "File to be uploaded must have an extension" msgstr "El archivo a subir debe tener una extensión" -#: cps/web.py:3513 cps/web.py:3765 +#: cps/web.py:3543 cps/web.py:3782 #, python-format msgid "Failed to create path %(path)s (Permission denied)." msgstr "Fallo al crear la ruta %(path)s (permiso denegado)" -#: cps/web.py:3518 +#: cps/web.py:3548 #, python-format msgid "Failed to store file %(file)s." msgstr "Falla al guardar el archivo %(file)s." -#: cps/web.py:3535 +#: cps/web.py:3565 #, python-format msgid "File format %(ext)s added to %(book)s" msgstr "Fichero con formato %(ext)s añadido a %(book)s" -#: cps/web.py:3553 -#, python-format -msgid "Failed to create path for cover %(path)s (Permission denied)." -msgstr "Fallo al crear la ruta para la cubierta %(path)s (Permiso denegado)." - -#: cps/web.py:3561 -#, python-format -msgid "Failed to store cover-file %(cover)s." -msgstr "Fallo al guardar el archivo de cubierta %(cover)s." - -#: cps/web.py:3564 -msgid "Cover-file is not a valid image file" -msgstr "El archivo de imagen de la portada no es válido" +#: cps/web.py:3579 cps/web.py:3652 +msgid "Cover is not a supported imageformat (jpg/png/webp), can't save" +msgstr "" -#: cps/web.py:3594 cps/web.py:3603 +#: cps/web.py:3611 cps/web.py:3620 msgid "unknown" msgstr "desconocido" -#: cps/web.py:3635 -msgid "Cover is not a jpg file, can't save" -msgstr "La cubierta no es un archivo jpg, no se puede guardar" - -#: cps/web.py:3683 +#: cps/web.py:3700 #, python-format msgid "%(langname)s is not a valid language" msgstr "%(langname)s no es un idioma válido" -#: cps/web.py:3714 +#: cps/web.py:3731 msgid "Metadata successfully updated" msgstr "" -#: cps/web.py:3723 +#: cps/web.py:3740 msgid "Error editing book, please check logfile for details" msgstr "Error al editar el libro, por favor compruebe el fichero de registro (logfile) para tener más detalles" -#: cps/web.py:3769 +#: cps/web.py:3786 #, python-format msgid "Failed to store file %(file)s (Permission denied)." msgstr "Fallo al guardar el archivo %(file)s (permiso denegado)" -#: cps/web.py:3774 +#: cps/web.py:3791 #, python-format msgid "Failed to delete file %(file)s (Permission denied)." msgstr "Fallo al borrar el archivo %(file)s (permiso denegado)" -#: cps/web.py:3857 +#: cps/web.py:3873 #, python-format msgid "File %(title)s" msgstr "" -#: cps/web.py:3886 +#: cps/web.py:3902 msgid "Source or destination format for conversion missing" msgstr "Falta la fuente o el formato de destino para la conversión" -#: cps/web.py:3896 +#: cps/web.py:3912 #, python-format msgid "Book successfully queued for converting to %(book_format)s" msgstr "Libro puesto a la cola con éxito para convertirlo a %(book_format)s" -#: cps/web.py:3900 +#: cps/web.py:3916 #, python-format msgid "There was an error converting this book: %(res)s" msgstr "Ocurrió un error al convertir este libro: %(res)s" @@ -1288,7 +1279,7 @@ msgstr "Número de libros aleatorios a mostrar" msgid "No. of authors to show before hiding (0=disable hiding)" msgstr "" -#: cps/templates/config_view_edit.html:35 cps/templates/readcbr.html:108 +#: cps/templates/config_view_edit.html:35 cps/templates/readcbr.html:118 msgid "Theme" msgstr "Tema" @@ -1602,7 +1593,7 @@ msgid "Advanced Search" msgstr "Búsqueda avanzada" #: cps/templates/layout.html:76 cps/templates/read.html:71 -#: cps/templates/readcbr.html:79 cps/templates/readcbr.html:103 +#: cps/templates/readcbr.html:89 cps/templates/readcbr.html:113 msgid "Settings" msgstr "Ajustes" @@ -1721,98 +1712,106 @@ msgstr "Cátalogo de ebook de Calibre-Web" msgid "Reflow text when sidebars are open." msgstr "Redimensionar el texto cuando las barras laterales están abiertas." -#: cps/templates/readcbr.html:84 +#: cps/templates/readcbr.html:94 msgid "Keyboard Shortcuts" msgstr "Atajos de teclado" -#: cps/templates/readcbr.html:87 +#: cps/templates/readcbr.html:97 msgid "Previous Page" msgstr "Página previa" -#: cps/templates/readcbr.html:88 +#: cps/templates/readcbr.html:98 msgid "Next Page" msgstr "Página siguiente" -#: cps/templates/readcbr.html:89 +#: cps/templates/readcbr.html:99 msgid "Scale to Best" msgstr "Escalar a mejor" -#: cps/templates/readcbr.html:90 +#: cps/templates/readcbr.html:100 msgid "Scale to Width" msgstr "Escalar a la ancho" -#: cps/templates/readcbr.html:91 +#: cps/templates/readcbr.html:101 msgid "Scale to Height" msgstr "Escalar a lo alto" -#: cps/templates/readcbr.html:92 +#: cps/templates/readcbr.html:102 msgid "Scale to Native" msgstr "Escalado nativo" -#: cps/templates/readcbr.html:93 +#: cps/templates/readcbr.html:103 msgid "Rotate Right" msgstr "Rotar hacia la derecha" -#: cps/templates/readcbr.html:94 +#: cps/templates/readcbr.html:104 msgid "Rotate Left" msgstr "Rotar hacia la izquierda" -#: cps/templates/readcbr.html:95 +#: cps/templates/readcbr.html:105 msgid "Flip Image" msgstr "Voltear imagen" -#: cps/templates/readcbr.html:111 +#: cps/templates/readcbr.html:121 msgid "Light" msgstr "Claro" -#: cps/templates/readcbr.html:112 +#: cps/templates/readcbr.html:122 msgid "Dark" msgstr "Oscuro" -#: cps/templates/readcbr.html:117 +#: cps/templates/readcbr.html:127 msgid "Scale" msgstr "Escalar" -#: cps/templates/readcbr.html:120 +#: cps/templates/readcbr.html:130 msgid "Best" msgstr "Mejor" -#: cps/templates/readcbr.html:121 +#: cps/templates/readcbr.html:131 msgid "Width" msgstr "Ancho" -#: cps/templates/readcbr.html:122 +#: cps/templates/readcbr.html:132 msgid "Height" msgstr "Alto" -#: cps/templates/readcbr.html:123 +#: cps/templates/readcbr.html:133 msgid "Native" msgstr "Nativo" -#: cps/templates/readcbr.html:128 +#: cps/templates/readcbr.html:138 msgid "Rotate" msgstr "Rotar" -#: cps/templates/readcbr.html:139 +#: cps/templates/readcbr.html:149 msgid "Flip" msgstr "Voltear" -#: cps/templates/readcbr.html:142 +#: cps/templates/readcbr.html:152 msgid "Horizontal" msgstr "Horizontal" -#: cps/templates/readcbr.html:143 +#: cps/templates/readcbr.html:153 msgid "Vertical" msgstr "Vertical" +#: cps/templates/readcbr.html:158 +msgid "Direction" +msgstr "" + +#: cps/templates/readcbr.html:161 +msgid "Left to Right" +msgstr "" + +#: cps/templates/readcbr.html:162 +msgid "Right to Left" +msgstr "" + #: cps/templates/readpdf.html:29 msgid "PDF.js viewer" msgstr "Visor PDF.js" -#: cps/templates/readpdf.html:418 -msgid "Preparing document for printing..." -msgstr "" - #: cps/templates/readtxt.html:6 msgid "Basic txt Reader" msgstr "Lector básico de txt" @@ -2086,3 +2085,18 @@ msgstr "Descargas recientes" #~ msgid "A new update is available. Click on the button below to update to version: %(version)s" #~ msgstr "" +#~ msgid "Failed to create path for cover %(path)s (Permission denied)." +#~ msgstr "Fallo al crear la ruta para la cubierta %(path)s (Permiso denegado)." + +#~ msgid "Failed to store cover-file %(cover)s." +#~ msgstr "Fallo al guardar el archivo de cubierta %(cover)s." + +#~ msgid "Cover-file is not a valid image file" +#~ msgstr "El archivo de imagen de la portada no es válido" + +#~ msgid "Cover is not a jpg file, can't save" +#~ msgstr "La cubierta no es un archivo jpg, no se puede guardar" + +#~ msgid "Preparing document for printing..." +#~ msgstr "" + diff --git a/cps/translations/fr/LC_MESSAGES/messages.mo b/cps/translations/fr/LC_MESSAGES/messages.mo index c6bc7f4792c6f8aaaafeac5ac2380dec8bb7b242..d60d9eec25693ba6c019b4859490ddcf3a3a0fcb 100644 GIT binary patch delta 14901 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 delta 14912 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\n" "Language: fr\n" @@ -31,8 +31,9 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.6.0\n" -#: cps/book_formats.py:152 cps/book_formats.py:153 cps/book_formats.py:157 -#: cps/book_formats.py:161 cps/converter.py:29 cps/converter.py:45 +#: cps/book_formats.py:199 cps/book_formats.py:200 cps/book_formats.py:204 +#: cps/book_formats.py:208 cps/book_formats.py:212 cps/converter.py:29 +#: cps/converter.py:45 msgid "not installed" msgstr "non installé" @@ -44,133 +45,133 @@ msgstr "Permission d’exécution manquante" msgid "not configured" msgstr "non configuré" -#: cps/helper.py:72 +#: cps/helper.py:79 #, python-format msgid "%(format)s format not found for book id: %(book)d" msgstr "le format %(format)s est introuvable pour le livre : %(book)d" -#: cps/helper.py:84 +#: cps/helper.py:91 #, python-format msgid "%(format)s not found on Google Drive: %(fn)s" msgstr "le %(format)s est introuvable sur Google Drive : %(fn)s" -#: cps/helper.py:91 cps/helper.py:199 cps/templates/detail.html:45 +#: cps/helper.py:98 cps/helper.py:204 cps/templates/detail.html:45 #: cps/templates/detail.html:49 msgid "Send to Kindle" msgstr "Envoyer vers Kindle" -#: cps/helper.py:92 cps/helper.py:110 cps/helper.py:201 +#: cps/helper.py:99 cps/helper.py:117 cps/helper.py:206 msgid "This e-mail has been sent via Calibre-Web." msgstr "Ce courriel a été envoyé depuis Calibre-Web." -#: cps/helper.py:103 +#: cps/helper.py:110 #, python-format msgid "%(format)s not found: %(fn)s" msgstr "%(format)s introuvable : %(fn)s" -#: cps/helper.py:108 +#: cps/helper.py:115 msgid "Calibre-Web test e-mail" msgstr "Courriel de test de Calibre-Web" -#: cps/helper.py:109 +#: cps/helper.py:116 msgid "Test e-mail" msgstr "Courriel de test" -#: cps/helper.py:125 +#: cps/helper.py:132 msgid "Get Started with Calibre-Web" msgstr "Bien démarrer avec Calibre-Web" -#: cps/helper.py:126 +#: cps/helper.py:133 #, python-format msgid "Registration e-mail for user: %(name)s" msgstr "Courriel d’inscription pour l’utilisateur : %(name)s" -#: cps/helper.py:139 cps/helper.py:141 cps/helper.py:143 cps/helper.py:145 -#: cps/helper.py:151 cps/helper.py:153 cps/helper.py:155 cps/helper.py:157 +#: cps/helper.py:146 cps/helper.py:148 cps/helper.py:150 cps/helper.py:158 +#: cps/helper.py:160 cps/helper.py:162 #, python-format msgid "Send %(format)s to Kindle" msgstr "Envoyer %(format)s vers le Kindle" -#: cps/helper.py:161 cps/helper.py:165 +#: cps/helper.py:166 #, python-format msgid "Convert %(orig)s to %(format)s and send to Kindle" msgstr "Convertir de %(orig)s vers %(format)s et envoyer au Kindle" -#: cps/helper.py:200 +#: cps/helper.py:205 #, python-format msgid "E-mail: %(book)s" msgstr "Courriel : %(book)s" -#: cps/helper.py:203 +#: cps/helper.py:208 msgid "The requested file could not be read. Maybe wrong permissions?" msgstr "Le fichier demandé n’a pu être lu. Problème de permission d’accès ?" -#: cps/helper.py:311 +#: cps/helper.py:316 #, python-format msgid "Rename title from: '%(src)s' to '%(dest)s' failed with error: %(error)s" msgstr "Renommer le titre de : '%(src)s' à '%(dest)s' a échoué avec l’erreur : %(error)s" -#: cps/helper.py:321 +#: cps/helper.py:326 #, python-format msgid "Rename author from: '%(src)s' to '%(dest)s' failed with error: %(error)s" msgstr "Renommer l’auteur de : '%(src)s' à '%(dest)s' a échoué avec l’erreur : %(error)s" -#: cps/helper.py:335 +#: cps/helper.py:340 #, python-format msgid "Rename file in path '%(src)s' to '%(dest)s' failed with error: %(error)s" msgstr "La modification du nom de fichier du chemin : '%(src)s' vers '%(dest)s' a échoué avec l’erreur : %(error)s" -#: cps/helper.py:361 cps/helper.py:371 cps/helper.py:379 +#: cps/helper.py:366 cps/helper.py:376 cps/helper.py:384 #, python-format msgid "File %(file)s not found on Google Drive" msgstr "" -#: cps/helper.py:400 +#: cps/helper.py:405 #, python-format msgid "Book path %(path)s not found on Google Drive" msgstr "" -#: cps/helper.py:508 +#: cps/helper.py:556 msgid "Error excecuting UnRar" msgstr "" -#: cps/helper.py:510 +#: cps/helper.py:558 msgid "Unrar binary file not found" msgstr "" -#: cps/helper.py:541 +#: cps/helper.py:589 msgid "Waiting" msgstr "" -#: cps/helper.py:543 +#: cps/helper.py:591 msgid "Failed" msgstr "" -#: cps/helper.py:545 +#: cps/helper.py:593 msgid "Started" msgstr "" -#: cps/helper.py:547 +#: cps/helper.py:595 msgid "Finished" msgstr "Terminé" -#: cps/helper.py:549 +#: cps/helper.py:597 msgid "Unknown Status" msgstr "Statut inconnu" -#: cps/helper.py:554 +#: cps/helper.py:602 msgid "E-mail: " msgstr "Courriel : " -#: cps/helper.py:556 cps/helper.py:560 +#: cps/helper.py:604 cps/helper.py:608 msgid "Convert: " msgstr "Convertir vers : " -#: cps/helper.py:558 +#: cps/helper.py:606 msgid "Upload: " msgstr "Déposer : " -#: cps/helper.py:562 +#: cps/helper.py:610 msgid "Unknown Task: " msgstr "Tâche inconnue : " @@ -182,19 +183,19 @@ msgstr "Données inattendues lors de la lecture des informations de mise à jour msgid "No update available. You already have the latest version installed" msgstr "Aucune mise à jour disponible. Vous avez déjà la dernière version installée" -#: cps/updater.py:270 cps/updater.py:501 cps/updater.py:503 cps/web.py:1187 +#: cps/updater.py:270 cps/updater.py:501 cps/updater.py:503 cps/web.py:1206 msgid "HTTP Error" msgstr "Erreur HTTP" -#: cps/updater.py:272 cps/updater.py:505 cps/web.py:1188 +#: cps/updater.py:272 cps/updater.py:505 cps/web.py:1207 msgid "Connection error" msgstr "Erreur de connexion" -#: cps/updater.py:274 cps/updater.py:507 cps/web.py:1189 +#: cps/updater.py:274 cps/updater.py:507 cps/web.py:1208 msgid "Timeout while establishing connection" msgstr "Délai d'attente dépassé lors de l'établissement de connexion" -#: cps/updater.py:276 cps/updater.py:509 cps/web.py:1190 +#: cps/updater.py:276 cps/updater.py:509 cps/web.py:1209 msgid "General error" msgstr "Erreur générale" @@ -215,555 +216,545 @@ msgstr "Aucune information concernant cette version n’est disponible" msgid "A new update is available. Click on the button below to update to version: %(version)s" msgstr "Une nouvelle mise à jour est disponible. Cliquez sur le bouton ci-dessous pour charger la version %(version)s" -#: cps/updater.py:491 cps/web.py:2771 +#: cps/updater.py:491 cps/web.py:2801 msgid "Unknown" msgstr "Inconnu" -#: cps/web.py:1180 +#: cps/web.py:1199 msgid "Requesting update package" msgstr "Demander une mise à jour" -#: cps/web.py:1181 +#: cps/web.py:1200 msgid "Downloading update package" msgstr "Téléchargement la mise à jour" -#: cps/web.py:1182 +#: cps/web.py:1201 msgid "Unzipping update package" msgstr "Décompression de la mise à jour" -#: cps/web.py:1183 +#: cps/web.py:1202 msgid "Replacing files" msgstr "Remplacement des fichiers" -#: cps/web.py:1184 +#: cps/web.py:1203 msgid "Database connections are closed" msgstr "Connexion à la base de donnée fermée" -#: cps/web.py:1185 +#: cps/web.py:1204 msgid "Stopping server" msgstr "Arrêt du serveur" -#: cps/web.py:1186 +#: cps/web.py:1205 msgid "Update finished, please press okay and reload page" msgstr "Mise à jour terminée, merci d’appuyer sur okay et de rafraîchir la page" -#: cps/web.py:1187 cps/web.py:1188 cps/web.py:1189 cps/web.py:1190 +#: cps/web.py:1206 cps/web.py:1207 cps/web.py:1208 cps/web.py:1209 msgid "Update failed:" msgstr "La mise à jour à échouée : " -#: cps/web.py:1213 +#: cps/web.py:1235 msgid "Recently Added Books" msgstr "Ajouts récents" -#: cps/web.py:1223 +#: cps/web.py:1245 msgid "Newest Books" msgstr "Livres récents" -#: cps/web.py:1235 +#: cps/web.py:1257 msgid "Oldest Books" msgstr "Anciens livres" -#: cps/web.py:1247 +#: cps/web.py:1269 msgid "Books (A-Z)" msgstr "Livres (A-Z)" -#: cps/web.py:1258 +#: cps/web.py:1280 msgid "Books (Z-A)" msgstr "Livres (Z-A)" -#: cps/web.py:1287 +#: cps/web.py:1309 msgid "Hot Books (most downloaded)" msgstr "Livres populaires (les plus téléchargés)" -#: cps/web.py:1300 +#: cps/web.py:1322 msgid "Best rated books" msgstr "Livres les mieux notés" -#: cps/templates/index.xml:39 cps/web.py:1313 +#: cps/templates/index.xml:39 cps/web.py:1335 msgid "Random Books" msgstr "Livres au hasard" -#: cps/web.py:1340 cps/web.py:1596 cps/web.py:2140 +#: cps/web.py:1362 cps/web.py:1618 cps/web.py:2167 msgid "Error opening eBook. File does not exist or file is not accessible:" msgstr "Erreur d'ouverture du livre numérique. Le fichier n'existe pas ou n'est pas accessible :" -#: cps/web.py:1369 +#: cps/web.py:1391 msgid "Publisher list" msgstr "Liste des éditeurs" -#: cps/web.py:1384 +#: cps/web.py:1406 #, python-format msgid "Publisher: %(name)s" msgstr "Editeur : '%(name)s'" -#: cps/templates/index.xml:83 cps/web.py:1416 +#: cps/templates/index.xml:83 cps/web.py:1438 msgid "Series list" msgstr "Liste des séries" -#: cps/web.py:1430 +#: cps/web.py:1452 #, python-format msgid "Series: %(serie)s" msgstr "Séries : %(serie)s" -#: cps/web.py:1456 +#: cps/web.py:1478 msgid "Available languages" msgstr "Langues disponibles" -#: cps/web.py:1476 +#: cps/web.py:1498 #, python-format msgid "Language: %(name)s" msgstr "Langue : %(name)s" -#: cps/templates/index.xml:76 cps/web.py:1487 +#: cps/templates/index.xml:76 cps/web.py:1509 msgid "Category list" msgstr "Liste des catégories" -#: cps/web.py:1501 +#: cps/web.py:1523 #, python-format msgid "Category: %(name)s" msgstr "Catégorie : %(name)s" -#: cps/templates/layout.html:73 cps/web.py:1632 +#: cps/templates/layout.html:73 cps/web.py:1654 msgid "Tasks" msgstr "Tâches" -#: cps/web.py:1666 +#: cps/web.py:1688 msgid "Statistics" msgstr "Statistiques" -#: cps/web.py:1734 +#: cps/web.py:1756 msgid "Google Drive setup not completed, try to deactivate and activate Google Drive again" msgstr "" -#: cps/web.py:1779 +#: cps/web.py:1801 msgid "Callback domain is not verified, please follow steps to verify domain in google developer console" msgstr "Le domaine de retour d’appel (Callback domain) est non vérifié, Veuillez suivre les étapes nécessaires pour vérifier le domaine dans la console de développement de Google" -#: cps/web.py:1855 +#: cps/web.py:1877 msgid "Server restarted, please reload page" msgstr "Serveur redémarré, merci de rafraîchir la page" -#: cps/web.py:1858 +#: cps/web.py:1880 msgid "Performing shutdown of server, please close window" msgstr "Arrêt du serveur en cours, merci de fermer la fenêtre" -#: cps/web.py:1938 +#: cps/web.py:1959 msgid "Published after " msgstr "Publié après le " -#: cps/web.py:1945 +#: cps/web.py:1966 msgid "Published before " msgstr "Publié avant le " -#: cps/web.py:1959 +#: cps/web.py:1980 #, python-format msgid "Rating <= %(rating)s" msgstr "Évaluation <= %(rating)s" -#: cps/web.py:1961 +#: cps/web.py:1982 #, python-format msgid "Rating >= %(rating)s" msgstr "Évaluation >= %(rating)s" -#: cps/web.py:2022 cps/web.py:2031 +#: cps/web.py:2042 cps/web.py:2051 msgid "search" msgstr "recherche" #: cps/templates/index.xml:47 cps/templates/index.xml:51 -#: cps/templates/layout.html:148 cps/web.py:2099 +#: cps/templates/layout.html:148 cps/web.py:2122 msgid "Read Books" msgstr "Livres lus" #: cps/templates/index.xml:55 cps/templates/index.xml:59 -#: cps/templates/layout.html:150 cps/web.py:2102 +#: cps/templates/layout.html:150 cps/web.py:2125 msgid "Unread Books" msgstr "Livres non-lus" -#: cps/web.py:2150 cps/web.py:2152 cps/web.py:2154 cps/web.py:2166 +#: cps/web.py:2177 cps/web.py:2179 cps/web.py:2181 cps/web.py:2193 msgid "Read a Book" msgstr "Lire un livre" -#: cps/web.py:2225 cps/web.py:3146 +#: cps/web.py:2205 +msgid "Error opening eBook. Fileformat is not supported." +msgstr "" + +#: cps/web.py:2255 cps/web.py:3176 msgid "Please fill out all fields!" msgstr "SVP, complétez tous les champs !" -#: cps/web.py:2226 cps/web.py:2248 cps/web.py:2252 cps/web.py:2257 -#: cps/web.py:2259 +#: cps/web.py:2256 cps/web.py:2278 cps/web.py:2282 cps/web.py:2287 +#: cps/web.py:2289 msgid "register" msgstr "s’enregistrer" -#: cps/web.py:2247 cps/web.py:3365 +#: cps/web.py:2277 cps/web.py:3395 msgid "An unknown error occurred. Please try again later." msgstr "Une erreur inconnue est survenue. Veuillez réessayer plus tard." -#: cps/web.py:2250 +#: cps/web.py:2280 msgid "Your e-mail is not allowed to register" msgstr "Votre adresse de courriel n’est pas autorisé pour une inscription" -#: cps/web.py:2253 +#: cps/web.py:2283 msgid "Confirmation e-mail was send to your e-mail account." msgstr "Le courriel de confirmation a été envoyé à votre adresse." -#: cps/web.py:2256 +#: cps/web.py:2286 msgid "This username or e-mail address is already in use." msgstr "Ce nom d’utilisateur ou cette adresse de courriel sont déjà utilisés." -#: cps/web.py:2273 cps/web.py:2369 +#: cps/web.py:2303 cps/web.py:2399 #, python-format msgid "you are now logged in as: '%(nickname)s'" msgstr "Vous êtes maintenant connecté sous : '%(nickname)s'" -#: cps/web.py:2278 +#: cps/web.py:2308 msgid "Wrong Username or Password" msgstr "Mauvais nom d'utilisateur ou mot de passe" -#: cps/web.py:2284 cps/web.py:2305 +#: cps/web.py:2314 cps/web.py:2335 msgid "login" msgstr "connexion" -#: cps/web.py:2317 cps/web.py:2348 +#: cps/web.py:2347 cps/web.py:2378 msgid "Token not found" msgstr "Jeton non trouvé" -#: cps/web.py:2325 cps/web.py:2356 +#: cps/web.py:2355 cps/web.py:2386 msgid "Token has expired" msgstr "Jeton expiré" -#: cps/web.py:2333 +#: cps/web.py:2363 msgid "Success! Please return to your device" msgstr "Réussite! Merci de vous tourner vers votre appareil" -#: cps/web.py:2383 +#: cps/web.py:2413 msgid "Please configure the SMTP mail settings first..." msgstr "Veuillez configurer les paramètres SMTP au préalable…" -#: cps/web.py:2388 +#: cps/web.py:2418 #, python-format msgid "Book successfully queued for sending to %(kindlemail)s" msgstr "Le livre a été mis en file de traitement avec succès pour un envois vers %(kindlemail)s" -#: cps/web.py:2392 +#: cps/web.py:2422 #, python-format msgid "There was an error sending this book: %(res)s" msgstr "Il y a eu une erreur en envoyant ce livre : %(res)s" -#: cps/web.py:2394 cps/web.py:3199 +#: cps/web.py:2424 cps/web.py:3229 msgid "Please configure your kindle e-mail address first..." msgstr "Veuillez configurer votre adresse de courriel Kindle en premier lieu…" -#: cps/web.py:2405 cps/web.py:2457 +#: cps/web.py:2435 cps/web.py:2487 msgid "Invalid shelf specified" msgstr "L’étagère indiquée est invalide" -#: cps/web.py:2412 +#: cps/web.py:2442 #, python-format msgid "Sorry you are not allowed to add a book to the the shelf: %(shelfname)s" msgstr "Désolé, vous n’êtes pas autorisé à ajouter un livre dans l’étagère %(shelfname)s" -#: cps/web.py:2420 +#: cps/web.py:2450 msgid "You are not allowed to edit public shelves" msgstr "Désolé, vous n’êtes pas autorisé à éditer les étagères publiques" -#: cps/web.py:2429 +#: cps/web.py:2459 #, python-format msgid "Book is already part of the shelf: %(shelfname)s" msgstr "Ce livre est déjà sur l’étagère : %(shelfname)s" -#: cps/web.py:2443 +#: cps/web.py:2473 #, python-format msgid "Book has been added to shelf: %(sname)s" msgstr "Le livre a bien été ajouté à l'étagère : %(sname)s" -#: cps/web.py:2462 +#: cps/web.py:2492 #, python-format msgid "You are not allowed to add a book to the the shelf: %(name)s" msgstr "Vous n’êtes pas autorisé à ajouter un livre dans l’étagère %(name)s" -#: cps/web.py:2467 +#: cps/web.py:2497 msgid "User is not allowed to edit public shelves" msgstr "L’utilisateur n’est pas autorisé à éditer les étagères publiques" -#: cps/web.py:2485 +#: cps/web.py:2515 #, python-format msgid "Books are already part of the shelf: %(name)s" msgstr "Ces livres sont déjà sur l’étagère : %(name)s" -#: cps/web.py:2499 +#: cps/web.py:2529 #, python-format msgid "Books have been added to shelf: %(sname)s" msgstr "Les livres ont été ajoutés à l’étagère : %(sname)s" -#: cps/web.py:2501 +#: cps/web.py:2531 #, python-format msgid "Could not add books to shelf: %(sname)s" msgstr "Impossible d’ajouter les livres à l’étagère : %(sname)s" -#: cps/web.py:2538 +#: cps/web.py:2568 #, python-format msgid "Book has been removed from shelf: %(sname)s" msgstr "Le livre a été supprimé de l'étagère %(sname)s" -#: cps/web.py:2544 +#: cps/web.py:2574 #, python-format msgid "Sorry you are not allowed to remove a book from this shelf: %(sname)s" msgstr "Désolé, vous n’êtes pas autorisé à enlever un livre de cette étagère : %(sname)s" -#: cps/web.py:2565 cps/web.py:2589 +#: cps/web.py:2595 cps/web.py:2619 #, python-format msgid "A shelf with the name '%(title)s' already exists." msgstr "Une étagère de ce nom '%(title)s' existe déjà." -#: cps/web.py:2570 +#: cps/web.py:2600 #, python-format msgid "Shelf %(title)s created" msgstr "Étagère %(title)s créée" -#: cps/web.py:2572 cps/web.py:2600 +#: cps/web.py:2602 cps/web.py:2630 msgid "There was an error" msgstr "Il y a eu une erreur" -#: cps/web.py:2573 cps/web.py:2575 +#: cps/web.py:2603 cps/web.py:2605 msgid "create a shelf" msgstr "Créer une étagère" -#: cps/web.py:2598 +#: cps/web.py:2628 #, python-format msgid "Shelf %(title)s changed" msgstr "L’étagère %(title)s a été modifiée" -#: cps/web.py:2601 cps/web.py:2603 +#: cps/web.py:2631 cps/web.py:2633 msgid "Edit a shelf" msgstr "Modifier une étagère" -#: cps/web.py:2624 +#: cps/web.py:2654 #, python-format msgid "successfully deleted shelf %(name)s" msgstr "l’étagère %(name)s a été supprimé avec succès" -#: cps/web.py:2651 +#: cps/web.py:2681 #, python-format msgid "Shelf: '%(name)s'" msgstr "Étagère : '%(name)s'" -#: cps/web.py:2654 +#: cps/web.py:2684 msgid "Error opening shelf. Shelf does not exist or is not accessible" msgstr "Erreur à l’ouverture de l’étagère. Elle n’existe plus ou n’est plus accessible." -#: cps/web.py:2685 +#: cps/web.py:2715 #, python-format msgid "Change order of Shelf: '%(name)s'" msgstr "Modifier l’arrangement de l’étagère : ‘%(name)s’" -#: cps/web.py:2714 cps/web.py:3152 +#: cps/web.py:2744 cps/web.py:3182 msgid "E-mail is not from valid domain" msgstr "Cette adresse de courriel n’appartient pas à un domaine valide" -#: cps/web.py:2716 cps/web.py:2758 cps/web.py:2761 +#: cps/web.py:2746 cps/web.py:2788 cps/web.py:2791 #, python-format msgid "%(name)s's profile" msgstr "Profil de %(name)s" -#: cps/web.py:2756 +#: cps/web.py:2786 msgid "Found an existing account for this e-mail address." msgstr "Un compte existant a été trouvé pour cette adresse de courriel" -#: cps/web.py:2759 +#: cps/web.py:2789 msgid "Profile updated" msgstr "Profil mis à jour" -#: cps/web.py:2790 +#: cps/web.py:2820 msgid "Admin page" msgstr "Page administrateur" -#: cps/web.py:2875 cps/web.py:3055 +#: cps/web.py:2905 cps/web.py:3085 msgid "Calibre-Web configuration updated" msgstr "Configuration de Calibre-Web mise à jour" -#: cps/templates/admin.html:100 cps/web.py:2889 +#: cps/templates/admin.html:100 cps/web.py:2919 msgid "UI Configuration" msgstr "Configuration de l’interface utilisateur" -#: cps/web.py:2907 +#: cps/web.py:2937 msgid "Import of optional Google Drive requirements missing" msgstr "L’import des pré-requis optionnels pour Google Drive est manquant" -#: cps/web.py:2910 +#: cps/web.py:2940 msgid "client_secrets.json is missing or not readable" msgstr "client_secrets.json est manquant ou ne peut être lu" -#: cps/web.py:2915 cps/web.py:2944 +#: cps/web.py:2945 cps/web.py:2974 msgid "client_secrets.json is not configured for web application" msgstr "client_secrets.json n’est pas configuré pour une application web" -#: cps/templates/admin.html:99 cps/web.py:2947 cps/web.py:2973 cps/web.py:2985 -#: cps/web.py:3030 cps/web.py:3045 cps/web.py:3064 cps/web.py:3072 -#: cps/web.py:3088 +#: cps/templates/admin.html:99 cps/web.py:2977 cps/web.py:3003 cps/web.py:3015 +#: cps/web.py:3060 cps/web.py:3075 cps/web.py:3094 cps/web.py:3102 +#: cps/web.py:3118 msgid "Basic Configuration" msgstr "Configuration principale" -#: cps/web.py:2970 +#: cps/web.py:3000 msgid "Keyfile location is not valid, please enter correct path" msgstr "L’emplacement du fichier de la clé de chiffrement (keyfile) n’est pas valide, veuillez saisir un chemin d’accès correct" -#: cps/web.py:2982 +#: cps/web.py:3012 msgid "Certfile location is not valid, please enter correct path" msgstr "L’emplacement du fichier de certificat (cert) n’est pas valide, veuillez saisir un chemin d’accès correct" -#: cps/web.py:3027 +#: cps/web.py:3057 msgid "Logfile location is not valid, please enter correct path" msgstr "L’emplacement du fichier de Log n’est pas valide, veuillez saisir un chemin d’accès correct" -#: cps/web.py:3068 +#: cps/web.py:3098 msgid "DB location is not valid, please enter correct path" msgstr "L’emplacement du fichier de base de donnée (DB) n’est pas valide, veuillez saisir un chemin d’accès correct" -#: cps/templates/admin.html:33 cps/web.py:3148 cps/web.py:3154 cps/web.py:3170 +#: cps/templates/admin.html:33 cps/web.py:3178 cps/web.py:3184 cps/web.py:3200 msgid "Add new user" msgstr "Ajouter un nouvel utilisateur" -#: cps/web.py:3160 +#: cps/web.py:3190 #, python-format msgid "User '%(user)s' created" msgstr "Utilisateur '%(user)s' créé" -#: cps/web.py:3164 +#: cps/web.py:3194 msgid "Found an existing account for this e-mail address or nickname." msgstr "Un compte existant a été trouvé pour cette adresse de courriel ou pour ce surnom." -#: cps/web.py:3194 +#: cps/web.py:3224 #, python-format msgid "Test e-mail successfully send to %(kindlemail)s" msgstr "Courriel de test envoyé avec succès sur %(kindlemail)s" -#: cps/web.py:3197 +#: cps/web.py:3227 #, python-format msgid "There was an error sending the Test e-mail: %(res)s" msgstr "Il y a eu une erreur pendant l’envoi du courriel de test : %(res)s" -#: cps/web.py:3201 +#: cps/web.py:3231 msgid "E-mail server settings updated" msgstr "Les paramètres du serveur de courriels ont été mis à jour" -#: cps/web.py:3202 +#: cps/web.py:3232 msgid "Edit e-mail server settings" msgstr "Modifier les paramètres du serveur de courriels" -#: cps/web.py:3227 +#: cps/web.py:3257 #, python-format msgid "User '%(nick)s' deleted" msgstr "Utilisateur '%(nick)s' supprimé" -#: cps/web.py:3340 +#: cps/web.py:3370 #, python-format msgid "User '%(nick)s' updated" msgstr "Utilisateur '%(nick)s' mis à jour" -#: cps/web.py:3343 +#: cps/web.py:3373 msgid "An unknown error occured." msgstr "Oups ! Une erreur inconnue a eu lieu." -#: cps/web.py:3345 +#: cps/web.py:3375 #, python-format msgid "Edit User %(nick)s" msgstr "Éditer l'utilisateur %(nick)s" -#: cps/web.py:3362 +#: cps/web.py:3392 #, python-format msgid "Password for user %(user)s reset" msgstr "Le mot de passe de l’utilisateur %(user)s a été réinitialisé" -#: cps/web.py:3376 cps/web.py:3582 +#: cps/web.py:3406 cps/web.py:3598 msgid "Error opening eBook. File does not exist or file is not accessible" msgstr "Erreur à l’ouverture du livre. Le fichier n’existe pas ou n’est pas accessible" -#: cps/web.py:3404 +#: cps/web.py:3434 msgid "edit metadata" msgstr "modifier les métadonnées" -#: cps/web.py:3497 cps/web.py:3743 +#: cps/web.py:3527 cps/web.py:3760 #, python-format msgid "File extension '%(ext)s' is not allowed to be uploaded to this server" msgstr "L’extension de fichier '%(ext)s' n’est pas autorisée pour être déposée sur ce serveur" -#: cps/web.py:3501 cps/web.py:3746 +#: cps/web.py:3531 cps/web.py:3763 msgid "File to be uploaded must have an extension" msgstr "Pour être déposé le fichier doit avoir une extension" -#: cps/web.py:3513 cps/web.py:3765 +#: cps/web.py:3543 cps/web.py:3782 #, python-format msgid "Failed to create path %(path)s (Permission denied)." msgstr "Impossible de créer le chemin %(path)s (permission refusée)" -#: cps/web.py:3518 +#: cps/web.py:3548 #, python-format msgid "Failed to store file %(file)s." msgstr "Echec de la sauvegarde du fichier %(file)s." -#: cps/web.py:3535 +#: cps/web.py:3565 #, python-format msgid "File format %(ext)s added to %(book)s" msgstr "Le format de fichier %(ext)s a été ajouté à %(book)s" -#: cps/web.py:3553 -#, python-format -msgid "Failed to create path for cover %(path)s (Permission denied)." -msgstr "Impossible de créer le chemin d’accès pour la couverture %(path)s (Autorisation refusée)" - -#: cps/web.py:3561 -#, python-format -msgid "Failed to store cover-file %(cover)s." -msgstr "Echec de la sauvegarde du fichier de couverture %(cover)s." - -#: cps/web.py:3564 -msgid "Cover-file is not a valid image file" -msgstr "Le fichier de couverture n’est pas un fichier d’image valide" +#: cps/web.py:3579 cps/web.py:3652 +msgid "Cover is not a supported imageformat (jpg/png/webp), can't save" +msgstr "" -#: cps/web.py:3594 cps/web.py:3603 +#: cps/web.py:3611 cps/web.py:3620 msgid "unknown" msgstr "inconnu" -#: cps/web.py:3635 -msgid "Cover is not a jpg file, can't save" -msgstr "Le fichier de couverture n’est pas au format jpg, impossible de sauvegarder" - -#: cps/web.py:3683 +#: cps/web.py:3700 #, python-format msgid "%(langname)s is not a valid language" msgstr "%(langname)s n'est pas une langue valide" -#: cps/web.py:3714 +#: cps/web.py:3731 msgid "Metadata successfully updated" msgstr "Les métadonnées ont bien été mise à jour" -#: cps/web.py:3723 +#: cps/web.py:3740 msgid "Error editing book, please check logfile for details" msgstr "Erreur d’édition du livre, veuillez consulter le journal (log) pour plus de détails" -#: cps/web.py:3769 +#: cps/web.py:3786 #, python-format msgid "Failed to store file %(file)s (Permission denied)." msgstr "Impossible d'enregistrer le fichier %(file)s (permission refusée)" -#: cps/web.py:3774 +#: cps/web.py:3791 #, python-format msgid "Failed to delete file %(file)s (Permission denied)." msgstr "Impossible de supprimer le fichier %(file)s (permission refusée)" -#: cps/web.py:3857 +#: cps/web.py:3873 #, python-format msgid "File %(title)s" msgstr "" -#: cps/web.py:3886 +#: cps/web.py:3902 msgid "Source or destination format for conversion missing" msgstr "Le format de conversion de la source ou de la destination est manquant" -#: cps/web.py:3896 +#: cps/web.py:3912 #, python-format msgid "Book successfully queued for converting to %(book_format)s" msgstr "Le livre a été mis avec succès en file de traitement pour conversion vers %(book_format)s" -#: cps/web.py:3900 +#: cps/web.py:3916 #, python-format msgid "There was an error converting this book: %(res)s" msgstr "Une erreur est survenue au cours de la conversion du livre : %(res)s" @@ -1301,7 +1292,7 @@ msgstr "Nombre de livres choisis au hasard à afficher" msgid "No. of authors to show before hiding (0=disable hiding)" msgstr "" -#: cps/templates/config_view_edit.html:35 cps/templates/readcbr.html:108 +#: cps/templates/config_view_edit.html:35 cps/templates/readcbr.html:118 msgid "Theme" msgstr "Thème" @@ -1615,7 +1606,7 @@ msgid "Advanced Search" msgstr "Recherche avancée" #: cps/templates/layout.html:76 cps/templates/read.html:71 -#: cps/templates/readcbr.html:79 cps/templates/readcbr.html:103 +#: cps/templates/readcbr.html:89 cps/templates/readcbr.html:113 msgid "Settings" msgstr "Paramètres" @@ -1734,98 +1725,106 @@ msgstr "Catalogue de livres électroniques Calibre-Web" msgid "Reflow text when sidebars are open." msgstr "Mettre à jour la mise en page du texte quand les bandeaux latéraux sont ouverts" -#: cps/templates/readcbr.html:84 +#: cps/templates/readcbr.html:94 msgid "Keyboard Shortcuts" msgstr "Raccourcis clavier" -#: cps/templates/readcbr.html:87 +#: cps/templates/readcbr.html:97 msgid "Previous Page" msgstr "Page précédente" -#: cps/templates/readcbr.html:88 +#: cps/templates/readcbr.html:98 msgid "Next Page" msgstr "Page suivante" -#: cps/templates/readcbr.html:89 +#: cps/templates/readcbr.html:99 msgid "Scale to Best" msgstr "Mise à l’échelle optimale" -#: cps/templates/readcbr.html:90 +#: cps/templates/readcbr.html:100 msgid "Scale to Width" msgstr "Mise à l’échelle sur la largeur" -#: cps/templates/readcbr.html:91 +#: cps/templates/readcbr.html:101 msgid "Scale to Height" msgstr "Mise à l’échelle sur la hauteur" -#: cps/templates/readcbr.html:92 +#: cps/templates/readcbr.html:102 msgid "Scale to Native" msgstr "Mise à l’échelle d’origine" -#: cps/templates/readcbr.html:93 +#: cps/templates/readcbr.html:103 msgid "Rotate Right" msgstr "Rotation droite" -#: cps/templates/readcbr.html:94 +#: cps/templates/readcbr.html:104 msgid "Rotate Left" msgstr "Rotation gauche" -#: cps/templates/readcbr.html:95 +#: cps/templates/readcbr.html:105 msgid "Flip Image" msgstr "Inverser l’image" -#: cps/templates/readcbr.html:111 +#: cps/templates/readcbr.html:121 msgid "Light" msgstr "Clair" -#: cps/templates/readcbr.html:112 +#: cps/templates/readcbr.html:122 msgid "Dark" msgstr "Sombre" -#: cps/templates/readcbr.html:117 +#: cps/templates/readcbr.html:127 msgid "Scale" msgstr "Echelle" -#: cps/templates/readcbr.html:120 +#: cps/templates/readcbr.html:130 msgid "Best" msgstr "Optimal" -#: cps/templates/readcbr.html:121 +#: cps/templates/readcbr.html:131 msgid "Width" msgstr "Largeur" -#: cps/templates/readcbr.html:122 +#: cps/templates/readcbr.html:132 msgid "Height" msgstr "Hauteur" -#: cps/templates/readcbr.html:123 +#: cps/templates/readcbr.html:133 msgid "Native" msgstr "Origine" -#: cps/templates/readcbr.html:128 +#: cps/templates/readcbr.html:138 msgid "Rotate" msgstr "Rotation" -#: cps/templates/readcbr.html:139 +#: cps/templates/readcbr.html:149 msgid "Flip" msgstr "Inverser" -#: cps/templates/readcbr.html:142 +#: cps/templates/readcbr.html:152 msgid "Horizontal" msgstr "Horizontal" -#: cps/templates/readcbr.html:143 +#: cps/templates/readcbr.html:153 msgid "Vertical" msgstr "Vertical" +#: cps/templates/readcbr.html:158 +msgid "Direction" +msgstr "" + +#: cps/templates/readcbr.html:161 +msgid "Left to Right" +msgstr "" + +#: cps/templates/readcbr.html:162 +msgid "Right to Left" +msgstr "" + #: cps/templates/readpdf.html:29 msgid "PDF.js viewer" msgstr "Visionneuse PDF.js" -#: cps/templates/readpdf.html:418 -msgid "Preparing document for printing..." -msgstr "Préparation des documents pour l’impression" - #: cps/templates/readtxt.html:6 msgid "Basic txt Reader" msgstr "Lecteur de texte simple" @@ -2054,3 +2053,18 @@ msgstr "Téléchargements récents" #~ msgid "Update done" #~ msgstr "Mise à jour effectuée" +#~ msgid "Failed to create path for cover %(path)s (Permission denied)." +#~ msgstr "Impossible de créer le chemin d’accès pour la couverture %(path)s (Autorisation refusée)" + +#~ msgid "Failed to store cover-file %(cover)s." +#~ msgstr "Echec de la sauvegarde du fichier de couverture %(cover)s." + +#~ msgid "Cover-file is not a valid image file" +#~ msgstr "Le fichier de couverture n’est pas un fichier d’image valide" + +#~ msgid "Cover is not a jpg file, can't save" +#~ msgstr "Le fichier de couverture n’est pas au format jpg, impossible de sauvegarder" + +#~ msgid "Preparing document for printing..." +#~ msgstr "Préparation des documents pour l’impression" + diff --git a/cps/translations/hu/LC_MESSAGES/messages.mo b/cps/translations/hu/LC_MESSAGES/messages.mo index df6d8acabc09ac51326debbc05482c388798dda5..cadd767930b466a7e247d34c436a3579061834e7 100644 GIT binary patch delta 14753 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 diff --git a/cps/translations/hu/LC_MESSAGES/messages.po b/cps/translations/hu/LC_MESSAGES/messages.po index 8d4fe51e..4af771a8 100644 --- a/cps/translations/hu/LC_MESSAGES/messages.po +++ b/cps/translations/hu/LC_MESSAGES/messages.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2019-04-13 16:43+0200\n" +"POT-Creation-Date: 2019-05-08 20:23+0200\n" "PO-Revision-Date: 2019-04-06 23:36+0200\n" "Last-Translator: \n" "Language: hu\n" @@ -18,8 +18,9 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.6.0\n" -#: cps/book_formats.py:152 cps/book_formats.py:153 cps/book_formats.py:157 -#: cps/book_formats.py:161 cps/converter.py:29 cps/converter.py:45 +#: cps/book_formats.py:199 cps/book_formats.py:200 cps/book_formats.py:204 +#: cps/book_formats.py:208 cps/book_formats.py:212 cps/converter.py:29 +#: cps/converter.py:45 msgid "not installed" msgstr "nincs telepítve" @@ -31,133 +32,133 @@ msgstr "Nincs jogosultság a futtatáshoz" msgid "not configured" msgstr "nincs konfigurálva" -#: cps/helper.py:72 +#: cps/helper.py:79 #, python-format msgid "%(format)s format not found for book id: %(book)d" msgstr "A(z) %(format)s formátum nem található a következő könyvhöz: %(book)d" -#: cps/helper.py:84 +#: cps/helper.py:91 #, python-format msgid "%(format)s not found on Google Drive: %(fn)s" msgstr "%(format)s nem található a Google Drive-on: %(fn)s" -#: cps/helper.py:91 cps/helper.py:199 cps/templates/detail.html:45 +#: cps/helper.py:98 cps/helper.py:204 cps/templates/detail.html:45 #: cps/templates/detail.html:49 msgid "Send to Kindle" msgstr "Küldés Kindle-re" -#: cps/helper.py:92 cps/helper.py:110 cps/helper.py:201 +#: cps/helper.py:99 cps/helper.py:117 cps/helper.py:206 msgid "This e-mail has been sent via Calibre-Web." msgstr "Ez az e-mail a Calibre-Web-en keresztül lett küldve." -#: cps/helper.py:103 +#: cps/helper.py:110 #, python-format msgid "%(format)s not found: %(fn)s" msgstr "%(format)s nem található: %(fn)s" -#: cps/helper.py:108 +#: cps/helper.py:115 msgid "Calibre-Web test e-mail" msgstr "Calibre-Web teszt e-mail" -#: cps/helper.py:109 +#: cps/helper.py:116 msgid "Test e-mail" msgstr "Teszt e-mail" -#: cps/helper.py:125 +#: cps/helper.py:132 msgid "Get Started with Calibre-Web" msgstr "Kezdő lépések a Calibre-Web-bel" -#: cps/helper.py:126 +#: cps/helper.py:133 #, python-format msgid "Registration e-mail for user: %(name)s" msgstr "Regisztrációs e-mail a következő felhasználóhoz: %(name)s" -#: cps/helper.py:139 cps/helper.py:141 cps/helper.py:143 cps/helper.py:145 -#: cps/helper.py:151 cps/helper.py:153 cps/helper.py:155 cps/helper.py:157 +#: cps/helper.py:146 cps/helper.py:148 cps/helper.py:150 cps/helper.py:158 +#: cps/helper.py:160 cps/helper.py:162 #, python-format msgid "Send %(format)s to Kindle" msgstr "%(format)s küldése Kindle-re" -#: cps/helper.py:161 cps/helper.py:165 +#: cps/helper.py:166 #, python-format msgid "Convert %(orig)s to %(format)s and send to Kindle" msgstr "%(orig)s konvertálása %(format)s-ra és küldés Kindle-re" -#: cps/helper.py:200 +#: cps/helper.py:205 #, python-format msgid "E-mail: %(book)s" msgstr "E-mail: %(book)s" -#: cps/helper.py:203 +#: cps/helper.py:208 msgid "The requested file could not be read. Maybe wrong permissions?" msgstr "A kért fájl nem olvasható. Esetleg jogosultsági probléma lenne?" -#: cps/helper.py:311 +#: cps/helper.py:316 #, python-format msgid "Rename title from: '%(src)s' to '%(dest)s' failed with error: %(error)s" msgstr "A cím átnevezése \"%(src)s\"-ról \"%(dest)s\"-ra nem sikerült a következő hiba miatt: %(error)s" -#: cps/helper.py:321 +#: cps/helper.py:326 #, python-format msgid "Rename author from: '%(src)s' to '%(dest)s' failed with error: %(error)s" msgstr "A szerző átnevezése \"%(src)s\"-ról \"%(dest)s\"-ra nem sikerült a következő hiba miatt: %(error)s" -#: cps/helper.py:335 +#: cps/helper.py:340 #, python-format msgid "Rename file in path '%(src)s' to '%(dest)s' failed with error: %(error)s" msgstr "\"%(src)s\" fájl átnevezése \"%(dest)s\"-re nem sikerült a következő hiba miatt: %(error)s" -#: cps/helper.py:361 cps/helper.py:371 cps/helper.py:379 +#: cps/helper.py:366 cps/helper.py:376 cps/helper.py:384 #, python-format msgid "File %(file)s not found on Google Drive" msgstr "A \"%(file)s\" fájl nem található a Google Drive-on" -#: cps/helper.py:400 +#: cps/helper.py:405 #, python-format msgid "Book path %(path)s not found on Google Drive" msgstr "A könyv elérési útja (\"%(path)s\") nem található a Google Drive-on" -#: cps/helper.py:508 +#: cps/helper.py:556 msgid "Error excecuting UnRar" msgstr "Hiba az UnRar futtatásakor" -#: cps/helper.py:510 +#: cps/helper.py:558 msgid "Unrar binary file not found" msgstr "Az Unrar futtatható állománya nem található" -#: cps/helper.py:541 +#: cps/helper.py:589 msgid "Waiting" msgstr "Várakozás" -#: cps/helper.py:543 +#: cps/helper.py:591 msgid "Failed" msgstr "Nem sikerült" -#: cps/helper.py:545 +#: cps/helper.py:593 msgid "Started" msgstr "Elindítva" -#: cps/helper.py:547 +#: cps/helper.py:595 msgid "Finished" msgstr "Végrehajtva" -#: cps/helper.py:549 +#: cps/helper.py:597 msgid "Unknown Status" msgstr "Ismeretlen állapot" -#: cps/helper.py:554 +#: cps/helper.py:602 msgid "E-mail: " msgstr "E-mail cím: " -#: cps/helper.py:556 cps/helper.py:560 +#: cps/helper.py:604 cps/helper.py:608 msgid "Convert: " msgstr "Konvertálás:" -#: cps/helper.py:558 +#: cps/helper.py:606 msgid "Upload: " msgstr "Feltöltés:" -#: cps/helper.py:562 +#: cps/helper.py:610 msgid "Unknown Task: " msgstr "Ismeretlen feladat:" @@ -169,19 +170,19 @@ msgstr "Ismeretlen adat a frissítési információk olvasásakor" msgid "No update available. You already have the latest version installed" msgstr "Nem érhető el újabb frissítés. Már a legújabb verzió van telepítve." -#: cps/updater.py:270 cps/updater.py:501 cps/updater.py:503 cps/web.py:1187 +#: cps/updater.py:270 cps/updater.py:501 cps/updater.py:503 cps/web.py:1206 msgid "HTTP Error" msgstr "HTTP hiba" -#: cps/updater.py:272 cps/updater.py:505 cps/web.py:1188 +#: cps/updater.py:272 cps/updater.py:505 cps/web.py:1207 msgid "Connection error" msgstr "Kapcsolódási hiba" -#: cps/updater.py:274 cps/updater.py:507 cps/web.py:1189 +#: cps/updater.py:274 cps/updater.py:507 cps/web.py:1208 msgid "Timeout while establishing connection" msgstr "Időtúllépés a kapcsolódás során" -#: cps/updater.py:276 cps/updater.py:509 cps/web.py:1190 +#: cps/updater.py:276 cps/updater.py:509 cps/web.py:1209 msgid "General error" msgstr "Általános hiba" @@ -202,555 +203,545 @@ msgstr "Nincs információ a kiadásról." msgid "A new update is available. Click on the button below to update to version: %(version)s" msgstr "Új frissítés érhető el. Kattints az alábbi gombra a frissítéshez a következő verzióra: %(version)s" -#: cps/updater.py:491 cps/web.py:2777 +#: cps/updater.py:491 cps/web.py:2801 msgid "Unknown" msgstr "Ismeretlen" -#: cps/web.py:1180 +#: cps/web.py:1199 msgid "Requesting update package" msgstr "Frissítési csomag kérése" -#: cps/web.py:1181 +#: cps/web.py:1200 msgid "Downloading update package" msgstr "Frissítési csomag letöltése" -#: cps/web.py:1182 +#: cps/web.py:1201 msgid "Unzipping update package" msgstr "Frissítési csomag kitömörítése" -#: cps/web.py:1183 +#: cps/web.py:1202 msgid "Replacing files" msgstr "Fájlok cserélése" -#: cps/web.py:1184 +#: cps/web.py:1203 msgid "Database connections are closed" msgstr "Adatbázis kapcsolatok lezárva" -#: cps/web.py:1185 +#: cps/web.py:1204 msgid "Stopping server" msgstr "Szerver leállítása" -#: cps/web.py:1186 +#: cps/web.py:1205 msgid "Update finished, please press okay and reload page" msgstr "A frissítés települt, kattints az OK-ra és újra tölt az oldal" -#: cps/web.py:1187 cps/web.py:1188 cps/web.py:1189 cps/web.py:1190 +#: cps/web.py:1206 cps/web.py:1207 cps/web.py:1208 cps/web.py:1209 msgid "Update failed:" msgstr "A frissítés nem sikerült:" -#: cps/web.py:1216 +#: cps/web.py:1235 msgid "Recently Added Books" msgstr "Legutóbb hozzáadott könyvek" -#: cps/web.py:1226 +#: cps/web.py:1245 msgid "Newest Books" msgstr "Legújabb könyvek" -#: cps/web.py:1238 +#: cps/web.py:1257 msgid "Oldest Books" msgstr "Legrégebbi könyvek" -#: cps/web.py:1250 +#: cps/web.py:1269 msgid "Books (A-Z)" msgstr "Könyvek (A-Zs)" -#: cps/web.py:1261 +#: cps/web.py:1280 msgid "Books (Z-A)" msgstr "Könyvek (Zs-A)" -#: cps/web.py:1290 +#: cps/web.py:1309 msgid "Hot Books (most downloaded)" msgstr "Kelendő könyvek (legtöbbet letöltöttek)" -#: cps/web.py:1303 +#: cps/web.py:1322 msgid "Best rated books" msgstr "Legjobbra értékelt könyvek" -#: cps/templates/index.xml:39 cps/web.py:1316 +#: cps/templates/index.xml:39 cps/web.py:1335 msgid "Random Books" msgstr "Könyvek találomra" -#: cps/web.py:1343 cps/web.py:1599 cps/web.py:2146 +#: cps/web.py:1362 cps/web.py:1618 cps/web.py:2167 msgid "Error opening eBook. File does not exist or file is not accessible:" msgstr "Hiba történt az e-könyv megnyitásakor. A fájl nem létezik vagy nem érhető el:" -#: cps/web.py:1372 +#: cps/web.py:1391 msgid "Publisher list" msgstr "Kiadók listája" -#: cps/web.py:1387 +#: cps/web.py:1406 #, python-format msgid "Publisher: %(name)s" msgstr "Kiadó: %(name)s" -#: cps/templates/index.xml:83 cps/web.py:1419 +#: cps/templates/index.xml:83 cps/web.py:1438 msgid "Series list" msgstr "Sorozatok listája" -#: cps/web.py:1433 +#: cps/web.py:1452 #, python-format msgid "Series: %(serie)s" msgstr "Sorozat: %(serie)s" -#: cps/web.py:1459 +#: cps/web.py:1478 msgid "Available languages" msgstr "Elérhető nyelvek" -#: cps/web.py:1479 +#: cps/web.py:1498 #, python-format msgid "Language: %(name)s" msgstr "Nyelv: %(name)s" -#: cps/templates/index.xml:76 cps/web.py:1490 +#: cps/templates/index.xml:76 cps/web.py:1509 msgid "Category list" msgstr "Címkék listája" -#: cps/web.py:1504 +#: cps/web.py:1523 #, python-format msgid "Category: %(name)s" msgstr "Címke: %(name)s" -#: cps/templates/layout.html:73 cps/web.py:1635 +#: cps/templates/layout.html:73 cps/web.py:1654 msgid "Tasks" msgstr "Feladatok" -#: cps/web.py:1669 +#: cps/web.py:1688 msgid "Statistics" msgstr "Statisztika" -#: cps/web.py:1737 +#: cps/web.py:1756 msgid "Google Drive setup not completed, try to deactivate and activate Google Drive again" msgstr "A Google Drive beállítása nem fejeződött be, próbáld kikapcsolni és újra aktíválni a Google Drive-ot." -#: cps/web.py:1782 +#: cps/web.py:1801 msgid "Callback domain is not verified, please follow steps to verify domain in google developer console" msgstr "A visszahívási tartomány nem ellenőrzött, kövesd az alábbi lépéseket a tartomány ellenőrzéséhez a Google Developer Console-ban:" -#: cps/web.py:1858 +#: cps/web.py:1877 msgid "Server restarted, please reload page" msgstr "A kiszolgáló újraindult, tölts be újra az oldalt!" -#: cps/web.py:1861 +#: cps/web.py:1880 msgid "Performing shutdown of server, please close window" msgstr "A kiszolgáló leállítása folyamatban, zárd be ezt az ablakot" -#: cps/web.py:1941 +#: cps/web.py:1959 msgid "Published after " msgstr "Kiadva ezután: " -#: cps/web.py:1948 +#: cps/web.py:1966 msgid "Published before " msgstr "Kiadva ezelőtt: " -#: cps/web.py:1962 +#: cps/web.py:1980 #, python-format msgid "Rating <= %(rating)s" msgstr "Értékelés <= %(rating)s" -#: cps/web.py:1964 +#: cps/web.py:1982 #, python-format msgid "Rating >= %(rating)s" msgstr "Értékelés <= %(rating)s" -#: cps/web.py:2025 cps/web.py:2034 +#: cps/web.py:2042 cps/web.py:2051 msgid "search" msgstr "keresés" #: cps/templates/index.xml:47 cps/templates/index.xml:51 -#: cps/templates/layout.html:148 cps/web.py:2105 +#: cps/templates/layout.html:148 cps/web.py:2122 msgid "Read Books" msgstr "Olvasott könyvek" #: cps/templates/index.xml:55 cps/templates/index.xml:59 -#: cps/templates/layout.html:150 cps/web.py:2108 +#: cps/templates/layout.html:150 cps/web.py:2125 msgid "Unread Books" msgstr "Olvasatlan könyvek" -#: cps/web.py:2156 cps/web.py:2158 cps/web.py:2160 cps/web.py:2172 +#: cps/web.py:2177 cps/web.py:2179 cps/web.py:2181 cps/web.py:2193 msgid "Read a Book" msgstr "Egy olvasott könyv" -#: cps/web.py:2231 cps/web.py:3152 +#: cps/web.py:2205 +msgid "Error opening eBook. Fileformat is not supported." +msgstr "" + +#: cps/web.py:2255 cps/web.py:3176 msgid "Please fill out all fields!" msgstr "Az összes mezőt ki kell tölteni!" -#: cps/web.py:2232 cps/web.py:2254 cps/web.py:2258 cps/web.py:2263 -#: cps/web.py:2265 +#: cps/web.py:2256 cps/web.py:2278 cps/web.py:2282 cps/web.py:2287 +#: cps/web.py:2289 msgid "register" msgstr "regisztrálás" -#: cps/web.py:2253 cps/web.py:3371 +#: cps/web.py:2277 cps/web.py:3395 msgid "An unknown error occurred. Please try again later." msgstr "Ismeretlen hiba történt. Próbáld újra később!" -#: cps/web.py:2256 +#: cps/web.py:2280 msgid "Your e-mail is not allowed to register" msgstr "Nem engedélyezett a megadott e-mail cím bejegyzése" -#: cps/web.py:2259 +#: cps/web.py:2283 msgid "Confirmation e-mail was send to your e-mail account." msgstr "Jóváhagyó levél elküldve az email címedre." -#: cps/web.py:2262 +#: cps/web.py:2286 msgid "This username or e-mail address is already in use." msgstr "Ez a felhasználónév vagy e-mail cím már használatban van." -#: cps/web.py:2279 cps/web.py:2375 +#: cps/web.py:2303 cps/web.py:2399 #, python-format msgid "you are now logged in as: '%(nickname)s'" msgstr "Be vagy jelentkezve mint: %(nickname)s" -#: cps/web.py:2284 +#: cps/web.py:2308 msgid "Wrong Username or Password" msgstr "Rossz felhasználó név vagy jelszó!" -#: cps/web.py:2290 cps/web.py:2311 +#: cps/web.py:2314 cps/web.py:2335 msgid "login" msgstr "belépés" -#: cps/web.py:2323 cps/web.py:2354 +#: cps/web.py:2347 cps/web.py:2378 msgid "Token not found" msgstr "A token nem található." -#: cps/web.py:2331 cps/web.py:2362 +#: cps/web.py:2355 cps/web.py:2386 msgid "Token has expired" msgstr "A token érvényessége lejárt." -#: cps/web.py:2339 +#: cps/web.py:2363 msgid "Success! Please return to your device" msgstr "Sikerült! Újra használható az eszköz." -#: cps/web.py:2389 +#: cps/web.py:2413 msgid "Please configure the SMTP mail settings first..." msgstr "Először be kell állítani az SMTP levelező beállításokat..." -#: cps/web.py:2394 +#: cps/web.py:2418 #, python-format msgid "Book successfully queued for sending to %(kindlemail)s" msgstr "A könyv sikeresen küldésre lett jelölve a következő címre: %(kindlemail)s" -#: cps/web.py:2398 +#: cps/web.py:2422 #, python-format msgid "There was an error sending this book: %(res)s" msgstr "Hiba történt a könyv küldésekor: %(res)s" -#: cps/web.py:2400 cps/web.py:3205 +#: cps/web.py:2424 cps/web.py:3229 msgid "Please configure your kindle e-mail address first..." msgstr "Először be kell állítani a kindle e-mail címet..." -#: cps/web.py:2411 cps/web.py:2463 +#: cps/web.py:2435 cps/web.py:2487 msgid "Invalid shelf specified" msgstr "A megadott polc érvénytelen!" -#: cps/web.py:2418 +#: cps/web.py:2442 #, python-format msgid "Sorry you are not allowed to add a book to the the shelf: %(shelfname)s" msgstr "Elnézést, nem vagy jogosult hozzáadni a könyvet a következő polcra: %(shelfname)s" -#: cps/web.py:2426 +#: cps/web.py:2450 msgid "You are not allowed to edit public shelves" msgstr "Nem vagy jogosult szerkeszteni nyilvános polcokat" -#: cps/web.py:2435 +#: cps/web.py:2459 #, python-format msgid "Book is already part of the shelf: %(shelfname)s" msgstr "A könyv már a következő polcon van: %(shelfname)s" -#: cps/web.py:2449 +#: cps/web.py:2473 #, python-format msgid "Book has been added to shelf: %(sname)s" msgstr "A könyv hozzá lett adva a következő polchoz: %(sname)s" -#: cps/web.py:2468 +#: cps/web.py:2492 #, python-format msgid "You are not allowed to add a book to the the shelf: %(name)s" msgstr "Nincs jogosultságod könyvet tenni a következő polcra: %(name)s." -#: cps/web.py:2473 +#: cps/web.py:2497 msgid "User is not allowed to edit public shelves" msgstr "A felhasználó nem szerkeszthet nyilvános polcokat" -#: cps/web.py:2491 +#: cps/web.py:2515 #, python-format msgid "Books are already part of the shelf: %(name)s" msgstr "A könyvek már a következő polcon vannak: %(name)s" -#: cps/web.py:2505 +#: cps/web.py:2529 #, python-format msgid "Books have been added to shelf: %(sname)s" msgstr "A könyvek hozzá lettek adva a következő polchoz: %(sname)s" -#: cps/web.py:2507 +#: cps/web.py:2531 #, python-format msgid "Could not add books to shelf: %(sname)s" msgstr "Nem sikerült hozzáadni a könyveket a polchoz: %(sname)s" -#: cps/web.py:2544 +#: cps/web.py:2568 #, python-format msgid "Book has been removed from shelf: %(sname)s" msgstr "A könyv el lett távolítva a polcról: %(sname)s" -#: cps/web.py:2550 +#: cps/web.py:2574 #, python-format msgid "Sorry you are not allowed to remove a book from this shelf: %(sname)s" msgstr "Sajnálom, nincs jogosultságot eltávolítani könyvet erről a polcról: %(sname)s" -#: cps/web.py:2571 cps/web.py:2595 +#: cps/web.py:2595 cps/web.py:2619 #, python-format msgid "A shelf with the name '%(title)s' already exists." msgstr "Már létezik \"%(title)s\" nevű polc!" -#: cps/web.py:2576 +#: cps/web.py:2600 #, python-format msgid "Shelf %(title)s created" msgstr "A következő polc létre lett hozva: %(title)s" -#: cps/web.py:2578 cps/web.py:2606 +#: cps/web.py:2602 cps/web.py:2630 msgid "There was an error" msgstr "Hiba történt" -#: cps/web.py:2579 cps/web.py:2581 +#: cps/web.py:2603 cps/web.py:2605 msgid "create a shelf" msgstr "Polc készítése" -#: cps/web.py:2604 +#: cps/web.py:2628 #, python-format msgid "Shelf %(title)s changed" msgstr "A következő polc megváltoztatva: %(title)s" -#: cps/web.py:2607 cps/web.py:2609 +#: cps/web.py:2631 cps/web.py:2633 msgid "Edit a shelf" msgstr "Polc szerkesztése" -#: cps/web.py:2630 +#: cps/web.py:2654 #, python-format msgid "successfully deleted shelf %(name)s" msgstr "A következő polc sikeresen törölve: %(name)s" -#: cps/web.py:2657 +#: cps/web.py:2681 #, python-format msgid "Shelf: '%(name)s'" msgstr "Polc: '%(name)s'" -#: cps/web.py:2660 +#: cps/web.py:2684 msgid "Error opening shelf. Shelf does not exist or is not accessible" msgstr "Hiba a polc megnyitásakor. A polc nem létezik vagy nem elérhető." -#: cps/web.py:2691 +#: cps/web.py:2715 #, python-format msgid "Change order of Shelf: '%(name)s'" msgstr "A következő polc átrendezése: %(name)s" -#: cps/web.py:2720 cps/web.py:3158 +#: cps/web.py:2744 cps/web.py:3182 msgid "E-mail is not from valid domain" msgstr "Az e-mail tartománya nem érvényes." -#: cps/web.py:2722 cps/web.py:2764 cps/web.py:2767 +#: cps/web.py:2746 cps/web.py:2788 cps/web.py:2791 #, python-format msgid "%(name)s's profile" msgstr "%(name)s profilja" -#: cps/web.py:2762 +#: cps/web.py:2786 msgid "Found an existing account for this e-mail address." msgstr "Már létezik felhasználó ehhez az e-mail címhez." -#: cps/web.py:2765 +#: cps/web.py:2789 msgid "Profile updated" msgstr "A profil frissítve." -#: cps/web.py:2796 +#: cps/web.py:2820 msgid "Admin page" msgstr "Rendszergazda oldala" -#: cps/web.py:2881 cps/web.py:3061 +#: cps/web.py:2905 cps/web.py:3085 msgid "Calibre-Web configuration updated" msgstr "A Calibre-Web konfigurációja frissítve." -#: cps/templates/admin.html:100 cps/web.py:2895 +#: cps/templates/admin.html:100 cps/web.py:2919 msgid "UI Configuration" msgstr "Felhasználói felület beállításai" -#: cps/web.py:2913 +#: cps/web.py:2937 msgid "Import of optional Google Drive requirements missing" msgstr "Hiányzanak a Google Drive használatához szükséges komponensek" -#: cps/web.py:2916 +#: cps/web.py:2940 msgid "client_secrets.json is missing or not readable" msgstr "A client_secrets.json hiányzik vagy nem olvasható." -#: cps/web.py:2921 cps/web.py:2950 +#: cps/web.py:2945 cps/web.py:2974 msgid "client_secrets.json is not configured for web application" msgstr "A client_secrets.json nincs beállítva a web alkalmazáshoz." -#: cps/templates/admin.html:99 cps/web.py:2953 cps/web.py:2979 cps/web.py:2991 -#: cps/web.py:3036 cps/web.py:3051 cps/web.py:3070 cps/web.py:3078 -#: cps/web.py:3094 +#: cps/templates/admin.html:99 cps/web.py:2977 cps/web.py:3003 cps/web.py:3015 +#: cps/web.py:3060 cps/web.py:3075 cps/web.py:3094 cps/web.py:3102 +#: cps/web.py:3118 msgid "Basic Configuration" msgstr "Alapvető beállítások" -#: cps/web.py:2976 +#: cps/web.py:3000 msgid "Keyfile location is not valid, please enter correct path" msgstr "A kulcsfájl helye nem érvényes, adj meg érvényes elérési utat" -#: cps/web.py:2988 +#: cps/web.py:3012 msgid "Certfile location is not valid, please enter correct path" msgstr "A tanusítványfájl helye nem érvényes, adj meg érvényes elérési utat" -#: cps/web.py:3033 +#: cps/web.py:3057 msgid "Logfile location is not valid, please enter correct path" msgstr "A naplófájl helye nem érvényes, adj meg érvényes elérési utat" -#: cps/web.py:3074 +#: cps/web.py:3098 msgid "DB location is not valid, please enter correct path" msgstr "Az adatbázis helye nem érvényes, adj meg érvényes elérési utat" -#: cps/templates/admin.html:33 cps/web.py:3154 cps/web.py:3160 cps/web.py:3176 +#: cps/templates/admin.html:33 cps/web.py:3178 cps/web.py:3184 cps/web.py:3200 msgid "Add new user" msgstr "Új felhasználó hozzáadása" -#: cps/web.py:3166 +#: cps/web.py:3190 #, python-format msgid "User '%(user)s' created" msgstr "A következő felhasználó létrehozva: %(user)s" -#: cps/web.py:3170 +#: cps/web.py:3194 msgid "Found an existing account for this e-mail address or nickname." msgstr "Már létezik felhasználó ehhez az e-mail címhez vagy felhasználói névhez." -#: cps/web.py:3200 +#: cps/web.py:3224 #, python-format msgid "Test e-mail successfully send to %(kindlemail)s" msgstr "A teszt levél sikeresen elküldve ide: %(kindlemail)s" -#: cps/web.py:3203 +#: cps/web.py:3227 #, python-format msgid "There was an error sending the Test e-mail: %(res)s" msgstr "Hiba történt a teszt levél küldése során: %(res)s" -#: cps/web.py:3207 +#: cps/web.py:3231 msgid "E-mail server settings updated" msgstr "Az e-mail kiszolgáló beállításai frissítve." -#: cps/web.py:3208 +#: cps/web.py:3232 msgid "Edit e-mail server settings" msgstr "Az e-mail kiszolgáló beállításainak módosítása" -#: cps/web.py:3233 +#: cps/web.py:3257 #, python-format msgid "User '%(nick)s' deleted" msgstr "A felhasználó törölve: %(nick)s" -#: cps/web.py:3346 +#: cps/web.py:3370 #, python-format msgid "User '%(nick)s' updated" msgstr "A felhasználó frissítve: %(nick)s" -#: cps/web.py:3349 +#: cps/web.py:3373 msgid "An unknown error occured." msgstr "Ismeretlen hiba történt." -#: cps/web.py:3351 +#: cps/web.py:3375 #, python-format msgid "Edit User %(nick)s" msgstr " A felhasználó szerkesztése: %(nick)s" -#: cps/web.py:3368 +#: cps/web.py:3392 #, python-format msgid "Password for user %(user)s reset" msgstr "A(z) %(user)s felhasználó jelszavának alaphelyzetbe állítása" -#: cps/web.py:3382 cps/web.py:3588 +#: cps/web.py:3406 cps/web.py:3598 msgid "Error opening eBook. File does not exist or file is not accessible" msgstr "Hiba az ekönyv megnyitásakor. A fájl nem létezik vagy nem elérhető." -#: cps/web.py:3410 +#: cps/web.py:3434 msgid "edit metadata" msgstr "Metaadatok szerkesztése" -#: cps/web.py:3503 cps/web.py:3749 +#: cps/web.py:3527 cps/web.py:3760 #, python-format msgid "File extension '%(ext)s' is not allowed to be uploaded to this server" msgstr "A(z) \"%(ext)s\" kiterjesztésű fájlok feltöltése nincs engedélyezve ezen a szerveren." -#: cps/web.py:3507 cps/web.py:3752 +#: cps/web.py:3531 cps/web.py:3763 msgid "File to be uploaded must have an extension" msgstr "A feltöltendő fájlnak kiterjesztéssel kell rendelkeznie!" -#: cps/web.py:3519 cps/web.py:3771 +#: cps/web.py:3543 cps/web.py:3782 #, python-format msgid "Failed to create path %(path)s (Permission denied)." msgstr "Nem sikerült létrehozni az elérési utat (engedély megtagadva): %(path)s." -#: cps/web.py:3524 +#: cps/web.py:3548 #, python-format msgid "Failed to store file %(file)s." msgstr "Nem sikerült elmenteni a %(file)s fájlt." -#: cps/web.py:3541 +#: cps/web.py:3565 #, python-format msgid "File format %(ext)s added to %(book)s" msgstr "A(z) %(ext)s fájlformátum hozzáadva a könyvhez: %(book)s." -#: cps/web.py:3559 -#, python-format -msgid "Failed to create path for cover %(path)s (Permission denied)." -msgstr "Nem sikerült létrehozni az elérési utat a borítóhoz (engedély megtagadva): %(path)s." - -#: cps/web.py:3567 -#, python-format -msgid "Failed to store cover-file %(cover)s." -msgstr "Nem sikerült elmenteni a borító-fájlt: %(cover)s." - -#: cps/web.py:3570 -msgid "Cover-file is not a valid image file" -msgstr "A borító-fájl nem érvényes képfájl!" +#: cps/web.py:3579 cps/web.py:3652 +msgid "Cover is not a supported imageformat (jpg/png/webp), can't save" +msgstr "" -#: cps/web.py:3600 cps/web.py:3609 +#: cps/web.py:3611 cps/web.py:3620 msgid "unknown" msgstr "ismeretlen" -#: cps/web.py:3641 -msgid "Cover is not a jpg file, can't save" -msgstr "A borító nem jpg fájl, nem lehet elmenteni." - -#: cps/web.py:3689 +#: cps/web.py:3700 #, python-format msgid "%(langname)s is not a valid language" msgstr "A(z) %(langname)s nem érvényes nyelv" -#: cps/web.py:3720 +#: cps/web.py:3731 msgid "Metadata successfully updated" msgstr "A metaadatok sikeresen frissültek" -#: cps/web.py:3729 +#: cps/web.py:3740 msgid "Error editing book, please check logfile for details" msgstr "Hiba a könyv szerkesztése során, további részletek a naplófájlban." -#: cps/web.py:3775 +#: cps/web.py:3786 #, python-format msgid "Failed to store file %(file)s (Permission denied)." msgstr "Nem sikerült elmenteni a %(file)s fájlt." -#: cps/web.py:3780 +#: cps/web.py:3791 #, python-format msgid "Failed to delete file %(file)s (Permission denied)." msgstr "Nem sikerült törölni a %(file)s fájlt." -#: cps/web.py:3862 +#: cps/web.py:3873 #, python-format msgid "File %(title)s" msgstr "Fájl: %(title)s" -#: cps/web.py:3891 +#: cps/web.py:3902 msgid "Source or destination format for conversion missing" msgstr "Az átalakításhoz hiányzik a forrás- vagy a célformátum!" -#: cps/web.py:3901 +#: cps/web.py:3912 #, python-format msgid "Book successfully queued for converting to %(book_format)s" msgstr "A könyv sikeresen átalakításra lett jelölve a következő formátumra: %(book_format)s" -#: cps/web.py:3905 +#: cps/web.py:3916 #, python-format msgid "There was an error converting this book: %(res)s" msgstr "Hiba történt a könyv átalakításakor: %(res)s" @@ -1288,7 +1279,7 @@ msgstr "Találomra mutatott könyvek száma" msgid "No. of authors to show before hiding (0=disable hiding)" msgstr "Mutatott szerzők száma (0=elrejtés kikapcsolása)" -#: cps/templates/config_view_edit.html:35 cps/templates/readcbr.html:108 +#: cps/templates/config_view_edit.html:35 cps/templates/readcbr.html:118 msgid "Theme" msgstr "Téma" @@ -1602,7 +1593,7 @@ msgid "Advanced Search" msgstr "Részletes keresés" #: cps/templates/layout.html:76 cps/templates/read.html:71 -#: cps/templates/readcbr.html:79 cps/templates/readcbr.html:103 +#: cps/templates/readcbr.html:89 cps/templates/readcbr.html:113 msgid "Settings" msgstr "Beállítások" @@ -1721,90 +1712,102 @@ msgstr "Calibre-Web e-könyv katalógus" msgid "Reflow text when sidebars are open." msgstr "Szöveg újratördelése amikor az oldalsávok nyitva vannak" -#: cps/templates/readcbr.html:84 +#: cps/templates/readcbr.html:94 msgid "Keyboard Shortcuts" msgstr "Gyorsbillentyűk" -#: cps/templates/readcbr.html:87 +#: cps/templates/readcbr.html:97 msgid "Previous Page" msgstr "Előző oldal" -#: cps/templates/readcbr.html:88 +#: cps/templates/readcbr.html:98 msgid "Next Page" msgstr "Következő oldal" -#: cps/templates/readcbr.html:89 +#: cps/templates/readcbr.html:99 msgid "Scale to Best" msgstr "Méretezés a legjobbra" -#: cps/templates/readcbr.html:90 +#: cps/templates/readcbr.html:100 msgid "Scale to Width" msgstr "Méretezés a szélességre" -#: cps/templates/readcbr.html:91 +#: cps/templates/readcbr.html:101 msgid "Scale to Height" msgstr "Méretezés a magasságra" -#: cps/templates/readcbr.html:92 +#: cps/templates/readcbr.html:102 msgid "Scale to Native" msgstr "Méretezés a natívra" -#: cps/templates/readcbr.html:93 +#: cps/templates/readcbr.html:103 msgid "Rotate Right" msgstr "Forgatás balra" -#: cps/templates/readcbr.html:94 +#: cps/templates/readcbr.html:104 msgid "Rotate Left" msgstr "Forgatás jobbra" -#: cps/templates/readcbr.html:95 +#: cps/templates/readcbr.html:105 msgid "Flip Image" msgstr "Kép tükrözése" -#: cps/templates/readcbr.html:111 +#: cps/templates/readcbr.html:121 msgid "Light" msgstr "Világos" -#: cps/templates/readcbr.html:112 +#: cps/templates/readcbr.html:122 msgid "Dark" msgstr "Sötét" -#: cps/templates/readcbr.html:117 +#: cps/templates/readcbr.html:127 msgid "Scale" msgstr "Méretezés" -#: cps/templates/readcbr.html:120 +#: cps/templates/readcbr.html:130 msgid "Best" msgstr "Legjobb" -#: cps/templates/readcbr.html:121 +#: cps/templates/readcbr.html:131 msgid "Width" msgstr "Szélesség" -#: cps/templates/readcbr.html:122 +#: cps/templates/readcbr.html:132 msgid "Height" msgstr "Magasság" -#: cps/templates/readcbr.html:123 +#: cps/templates/readcbr.html:133 msgid "Native" msgstr "Natív" -#: cps/templates/readcbr.html:128 +#: cps/templates/readcbr.html:138 msgid "Rotate" msgstr "Forgatás" -#: cps/templates/readcbr.html:139 +#: cps/templates/readcbr.html:149 msgid "Flip" msgstr "Tökrözés" -#: cps/templates/readcbr.html:142 +#: cps/templates/readcbr.html:152 msgid "Horizontal" msgstr "Vízszintes" -#: cps/templates/readcbr.html:143 +#: cps/templates/readcbr.html:153 msgid "Vertical" msgstr "Függőleges" +#: cps/templates/readcbr.html:158 +msgid "Direction" +msgstr "" + +#: cps/templates/readcbr.html:161 +msgid "Left to Right" +msgstr "" + +#: cps/templates/readcbr.html:162 +msgid "Right to Left" +msgstr "" + #: cps/templates/readpdf.html:29 msgid "PDF.js viewer" msgstr "PDF.js olvasó" @@ -3063,3 +3066,15 @@ msgstr "Utolsó letöltések" #~ msgid "Preparing document for printing..." #~ msgstr "Dokumentum előkészítése nyomtatásra..." +#~ msgid "Failed to create path for cover %(path)s (Permission denied)." +#~ msgstr "Nem sikerült létrehozni az elérési utat a borítóhoz (engedély megtagadva): %(path)s." + +#~ msgid "Failed to store cover-file %(cover)s." +#~ msgstr "Nem sikerült elmenteni a borító-fájlt: %(cover)s." + +#~ msgid "Cover-file is not a valid image file" +#~ msgstr "A borító-fájl nem érvényes képfájl!" + +#~ msgid "Cover is not a jpg file, can't save" +#~ msgstr "A borító nem jpg fájl, nem lehet elmenteni." + diff --git a/cps/translations/it/LC_MESSAGES/messages.mo b/cps/translations/it/LC_MESSAGES/messages.mo index 82b0aea089b9c8548ef9372c6fc5176f590c1242..1b1fd81ed84ff649342b1fc804f76e6c1f0ab895 100644 GIT binary patch literal 47969 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\n" "Language: it\n" @@ -17,756 +17,747 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.6.0\n" -#: cps/book_formats.py:152 cps/book_formats.py:153 cps/book_formats.py:157 -#: cps/book_formats.py:161 cps/converter.py:29 cps/converter.py:45 +#: cps/book_formats.py:199 cps/book_formats.py:200 cps/book_formats.py:204 +#: cps/book_formats.py:208 cps/book_formats.py:212 cps/converter.py:29 +#: cps/converter.py:45 msgid "not installed" msgstr "non installato" #: cps/converter.py:40 cps/converter.py:56 msgid "Excecution permissions missing" -msgstr "Mancano autorizzazioni di esecuzione" +msgstr "Mancano le autorizzazioni di esecuzione" #: cps/converter.py:66 msgid "not configured" -msgstr "" +msgstr "non configurato" -#: cps/helper.py:72 +#: cps/helper.py:79 #, python-format msgid "%(format)s format not found for book id: %(book)d" -msgstr "" +msgstr "%(format)s formato non trovato per il libro: %(book)d" -#: cps/helper.py:84 +#: cps/helper.py:91 #, python-format msgid "%(format)s not found on Google Drive: %(fn)s" -msgstr "" +msgstr "%(format)s non trovato su Google Drive: %(fn)s" -#: cps/helper.py:91 cps/helper.py:199 cps/templates/detail.html:45 +#: cps/helper.py:98 cps/helper.py:204 cps/templates/detail.html:45 #: cps/templates/detail.html:49 msgid "Send to Kindle" msgstr "Invia a Kindle" -#: cps/helper.py:92 cps/helper.py:110 cps/helper.py:201 +#: cps/helper.py:99 cps/helper.py:117 cps/helper.py:206 msgid "This e-mail has been sent via Calibre-Web." -msgstr "" +msgstr "Questo e-mail è stato spedito tramite Calibre-Web." -#: cps/helper.py:103 +#: cps/helper.py:110 #, python-format msgid "%(format)s not found: %(fn)s" -msgstr "" +msgstr "%(format)s non trovato: %(fn)s" -#: cps/helper.py:108 +#: cps/helper.py:115 msgid "Calibre-Web test e-mail" -msgstr "" +msgstr "E-mail di test da Calibre-Web" -#: cps/helper.py:109 +#: cps/helper.py:116 msgid "Test e-mail" -msgstr "" +msgstr "E-mail di test" -#: cps/helper.py:125 +#: cps/helper.py:132 msgid "Get Started with Calibre-Web" -msgstr "" +msgstr "Inizia con Calibre-Web" -#: cps/helper.py:126 +#: cps/helper.py:133 #, python-format msgid "Registration e-mail for user: %(name)s" -msgstr "" +msgstr "E-mail di registrazione per l'utente: %(name)s" -#: cps/helper.py:139 cps/helper.py:141 cps/helper.py:143 cps/helper.py:145 -#: cps/helper.py:151 cps/helper.py:153 cps/helper.py:155 cps/helper.py:157 +#: cps/helper.py:146 cps/helper.py:148 cps/helper.py:150 cps/helper.py:158 +#: cps/helper.py:160 cps/helper.py:162 #, python-format msgid "Send %(format)s to Kindle" -msgstr "" +msgstr "Invia %(format)s a Kindle" -#: cps/helper.py:161 cps/helper.py:165 +#: cps/helper.py:166 #, python-format msgid "Convert %(orig)s to %(format)s and send to Kindle" -msgstr "" +msgstr "Converti %(orig)s in %(format)s e spedisci a Kindle" -#: cps/helper.py:200 +#: cps/helper.py:205 #, python-format msgid "E-mail: %(book)s" -msgstr "" +msgstr "E-mail: %(book)s" -#: cps/helper.py:203 +#: cps/helper.py:208 msgid "The requested file could not be read. Maybe wrong permissions?" -msgstr "" +msgstr "Il file richiesto non può essere letto. I permessi sono corretti?" -#: cps/helper.py:311 +#: cps/helper.py:316 #, python-format msgid "Rename title from: '%(src)s' to '%(dest)s' failed with error: %(error)s" -msgstr "" +msgstr "La modifica del titolo da: '%(src)s' a '%(dest)s' è terminata con l'errore: %(error)s" -#: cps/helper.py:321 +#: cps/helper.py:326 #, python-format msgid "Rename author from: '%(src)s' to '%(dest)s' failed with error: %(error)s" -msgstr "" +msgstr "La modifica dell'autore da: '%(src)s' a '%(dest)s' è terminata con l'errore: %(error)s" -#: cps/helper.py:335 +#: cps/helper.py:340 #, python-format msgid "Rename file in path '%(src)s' to '%(dest)s' failed with error: %(error)s" -msgstr "" +msgstr "La modifica del file nella cartella da '%(src)s' a '%(dest)s' è terminata con l'errore: %(error)s" -#: cps/helper.py:361 cps/helper.py:371 cps/helper.py:379 +#: cps/helper.py:366 cps/helper.py:376 cps/helper.py:384 #, python-format msgid "File %(file)s not found on Google Drive" -msgstr "" +msgstr "File %(file)s non trovato su Google Drive" -#: cps/helper.py:400 +#: cps/helper.py:405 #, python-format msgid "Book path %(path)s not found on Google Drive" -msgstr "" +msgstr "Non ho trovato la cartella %(path)s su Google Drive" -#: cps/helper.py:508 +#: cps/helper.py:556 msgid "Error excecuting UnRar" -msgstr "" +msgstr "Errore nell'esecuzione di UnRar" -#: cps/helper.py:510 +#: cps/helper.py:558 msgid "Unrar binary file not found" -msgstr "" +msgstr "Non ho trovato il file binario di UnRar" -#: cps/helper.py:541 +#: cps/helper.py:589 msgid "Waiting" -msgstr "" +msgstr "Attendere" -#: cps/helper.py:543 +#: cps/helper.py:591 msgid "Failed" -msgstr "" +msgstr "Fallito" -#: cps/helper.py:545 +#: cps/helper.py:593 msgid "Started" -msgstr "" +msgstr "Avviato" -#: cps/helper.py:547 +#: cps/helper.py:595 msgid "Finished" -msgstr "" +msgstr "Terminato" -#: cps/helper.py:549 +#: cps/helper.py:597 msgid "Unknown Status" -msgstr "" +msgstr "Stato sconosciuto" -#: cps/helper.py:554 +#: cps/helper.py:602 msgid "E-mail: " -msgstr "" +msgstr "E-mail: " -#: cps/helper.py:556 cps/helper.py:560 +#: cps/helper.py:604 cps/helper.py:608 msgid "Convert: " -msgstr "" +msgstr "Conversione: " -#: cps/helper.py:558 +#: cps/helper.py:606 msgid "Upload: " -msgstr "" +msgstr "Upload: " -#: cps/helper.py:562 +#: cps/helper.py:610 msgid "Unknown Task: " -msgstr "" +msgstr "Processo sconosciuto: " #: cps/updater.py:251 cps/updater.py:410 cps/updater.py:423 msgid "Unexpected data while reading update information" -msgstr "" +msgstr "Dati inattesi durante il processo di aggiornamento" #: cps/updater.py:258 cps/updater.py:416 msgid "No update available. You already have the latest version installed" -msgstr "" +msgstr "Nessun aggiornamento disponibile. Hai già installato l'ultima versione disponibile" -#: cps/updater.py:270 cps/updater.py:501 cps/updater.py:503 cps/web.py:1187 +#: cps/updater.py:270 cps/updater.py:501 cps/updater.py:503 cps/web.py:1206 msgid "HTTP Error" -msgstr "" +msgstr "HTTP Error" -#: cps/updater.py:272 cps/updater.py:505 cps/web.py:1188 +#: cps/updater.py:272 cps/updater.py:505 cps/web.py:1207 msgid "Connection error" -msgstr "" +msgstr "Errore di connessione" -#: cps/updater.py:274 cps/updater.py:507 cps/web.py:1189 +#: cps/updater.py:274 cps/updater.py:507 cps/web.py:1208 msgid "Timeout while establishing connection" -msgstr "" +msgstr "Tempo scaduto nello stabilire la connessione" -#: cps/updater.py:276 cps/updater.py:509 cps/web.py:1190 +#: cps/updater.py:276 cps/updater.py:509 cps/web.py:1209 msgid "General error" -msgstr "" +msgstr "Errore generale" #: cps/updater.py:283 cps/updater.py:341 cps/updater.py:468 msgid "A new update is available. Click on the button below to update to the latest version." -msgstr "" +msgstr "Nuovo aggiornamento disponibile. Clicca sul pulsante sottostante per aggiornare all'ultima versione." #: cps/updater.py:335 msgid "Could not fetch update information" -msgstr "" +msgstr "Impossibile recuperare informazioni di aggiornamento" #: cps/updater.py:403 msgid "No release information available" -msgstr "" +msgstr "Non sono disponibili informazioni sulla versione" #: cps/updater.py:449 cps/updater.py:458 #, python-format msgid "A new update is available. Click on the button below to update to version: %(version)s" -msgstr "" +msgstr "Nuovo aggiornamento disponibile. Clicca sul pulsante sottostante per aggiornare alla versione: %(version)s" -#: cps/updater.py:491 cps/web.py:2771 +#: cps/updater.py:491 cps/web.py:2801 msgid "Unknown" -msgstr "" +msgstr "Sconosciuto" -#: cps/web.py:1180 +#: cps/web.py:1199 msgid "Requesting update package" msgstr "Richiesta del pacchetto di aggiornamento" -#: cps/web.py:1181 +#: cps/web.py:1200 msgid "Downloading update package" -msgstr "Scaricare il pacchetto di aggiornamento" +msgstr "Scarico il pacchetto di aggiornamento" -#: cps/web.py:1182 +#: cps/web.py:1201 msgid "Unzipping update package" -msgstr "Decomprimere pacchetto di aggiornamento" +msgstr "Decomprimo il pacchetto di aggiornamento" -#: cps/web.py:1183 +#: cps/web.py:1202 msgid "Replacing files" -msgstr "" +msgstr "Sostituzione files" -#: cps/web.py:1184 +#: cps/web.py:1203 msgid "Database connections are closed" -msgstr "Le connessioni di database sono chiuse" +msgstr "Le connessioni al database sono chiuse" -#: cps/web.py:1185 +#: cps/web.py:1204 msgid "Stopping server" -msgstr "" +msgstr "Arresta il server" -#: cps/web.py:1186 +#: cps/web.py:1205 msgid "Update finished, please press okay and reload page" -msgstr "Aggiornamento completato, prego premere bene e ricaricare pagina" +msgstr "Aggiornamento completato, prego premere ok e ricaricare la pagina" -#: cps/web.py:1187 cps/web.py:1188 cps/web.py:1189 cps/web.py:1190 +#: cps/web.py:1206 cps/web.py:1207 cps/web.py:1208 cps/web.py:1209 msgid "Update failed:" -msgstr "" +msgstr "Aggiornamento fallito:" -#: cps/web.py:1213 +#: cps/web.py:1235 msgid "Recently Added Books" msgstr "Libri aggiunti di recente" -#: cps/web.py:1223 +#: cps/web.py:1245 msgid "Newest Books" -msgstr "I più nuovi libri" +msgstr "I libri più nuovi" -#: cps/web.py:1235 +#: cps/web.py:1257 msgid "Oldest Books" -msgstr "Libri più vecchi" +msgstr "I libri più vecchi" -#: cps/web.py:1247 +#: cps/web.py:1269 msgid "Books (A-Z)" -msgstr "Ebook (A-Z)" +msgstr "Libri (A-Z)" -#: cps/web.py:1258 +#: cps/web.py:1280 msgid "Books (Z-A)" -msgstr "Ebook (Z-A)" +msgstr "Libri (Z-A)" -#: cps/web.py:1287 +#: cps/web.py:1309 msgid "Hot Books (most downloaded)" -msgstr "Hot Books (più scaricati)" +msgstr "I libri più richiesti" -#: cps/web.py:1300 +#: cps/web.py:1322 msgid "Best rated books" -msgstr "I migliori libri valutati" +msgstr "I libri con le migliori valutazioni" -#: cps/templates/index.xml:39 cps/web.py:1313 +#: cps/templates/index.xml:39 cps/web.py:1335 msgid "Random Books" -msgstr "Libri casuali" +msgstr "Libri a caso" -#: cps/web.py:1340 cps/web.py:1596 cps/web.py:2140 +#: cps/web.py:1362 cps/web.py:1618 cps/web.py:2167 msgid "Error opening eBook. File does not exist or file is not accessible:" -msgstr "Errore durante l'apertura di eBook. Il file non esiste o il file non è accessibile:" +msgstr "Errore durante l'apertura del libro. Il file non esiste o il file non è accessibile:" -#: cps/web.py:1369 +#: cps/web.py:1391 msgid "Publisher list" -msgstr "" +msgstr "Lista degli editori" -#: cps/web.py:1384 +#: cps/web.py:1406 #, python-format msgid "Publisher: %(name)s" -msgstr "" +msgstr "Editore: %(name)s" -#: cps/templates/index.xml:83 cps/web.py:1416 +#: cps/templates/index.xml:83 cps/web.py:1438 msgid "Series list" msgstr "Lista delle serie" -#: cps/web.py:1430 +#: cps/web.py:1452 #, python-format msgid "Series: %(serie)s" -msgstr "Serie :" +msgstr "Serie: %(serie)s" -#: cps/web.py:1456 +#: cps/web.py:1478 msgid "Available languages" msgstr "Lingue disponibili" -#: cps/web.py:1476 +#: cps/web.py:1498 #, python-format msgid "Language: %(name)s" -msgstr "Lingue: %(name)s" +msgstr "Lingua: %(name)s" -#: cps/templates/index.xml:76 cps/web.py:1487 +#: cps/templates/index.xml:76 cps/web.py:1509 msgid "Category list" msgstr "Elenco categorie" -#: cps/web.py:1501 +#: cps/web.py:1523 #, python-format msgid "Category: %(name)s" -msgstr "Categoria : %(name)s" +msgstr "Categoria: %(name)s" -#: cps/templates/layout.html:73 cps/web.py:1632 +#: cps/templates/layout.html:73 cps/web.py:1654 msgid "Tasks" -msgstr "" +msgstr "Compito" -#: cps/web.py:1666 +#: cps/web.py:1688 msgid "Statistics" msgstr "Statistica" -#: cps/web.py:1734 +#: cps/web.py:1756 msgid "Google Drive setup not completed, try to deactivate and activate Google Drive again" -msgstr "" +msgstr "La configurazione di Google Drive non è stata completata correttamente. Prova a disattivare e riattivare nuovamente Google Drive" -#: cps/web.py:1779 +#: cps/web.py:1801 msgid "Callback domain is not verified, please follow steps to verify domain in google developer console" -msgstr "" +msgstr "Callback domain non è stato verificato. Per favore intraprendi il necessario per verificare il dominio nella developer console di Google" -#: cps/web.py:1855 +#: cps/web.py:1877 msgid "Server restarted, please reload page" -msgstr "Server riavviato, ricarica pagina" +msgstr "Server riavviato, per favore ricarica la pagina" -#: cps/web.py:1858 +#: cps/web.py:1880 msgid "Performing shutdown of server, please close window" -msgstr "Eseguire l'arresto del server, chiudi la finestra." +msgstr "Eseguo l'arresto del server, per favore chiudi la finestra." -#: cps/web.py:1938 +#: cps/web.py:1959 msgid "Published after " -msgstr "" +msgstr "Pubblicato dopo " -#: cps/web.py:1945 +#: cps/web.py:1966 msgid "Published before " -msgstr "" +msgstr "Pubblicato prima " -#: cps/web.py:1959 +#: cps/web.py:1980 #, python-format msgid "Rating <= %(rating)s" -msgstr "" +msgstr "Valutazione <= %(rating)s" -#: cps/web.py:1961 +#: cps/web.py:1982 #, python-format msgid "Rating >= %(rating)s" -msgstr "" +msgstr "Valutazione >= %(rating)s" -#: cps/web.py:2022 cps/web.py:2031 +#: cps/web.py:2042 cps/web.py:2051 msgid "search" msgstr "ricerca" #: cps/templates/index.xml:47 cps/templates/index.xml:51 -#: cps/templates/layout.html:148 cps/web.py:2099 +#: cps/templates/layout.html:148 cps/web.py:2122 msgid "Read Books" -msgstr "Leggere libri" +msgstr "Libri da leggere" #: cps/templates/index.xml:55 cps/templates/index.xml:59 -#: cps/templates/layout.html:150 cps/web.py:2102 +#: cps/templates/layout.html:150 cps/web.py:2125 msgid "Unread Books" msgstr "Libri non letti" -#: cps/web.py:2150 cps/web.py:2152 cps/web.py:2154 cps/web.py:2166 +#: cps/web.py:2177 cps/web.py:2179 cps/web.py:2181 cps/web.py:2193 msgid "Read a Book" msgstr "Leggere un libro" -#: cps/web.py:2225 cps/web.py:3146 +#: cps/web.py:2205 +msgid "Error opening eBook. Fileformat is not supported." +msgstr "" + +#: cps/web.py:2255 cps/web.py:3176 msgid "Please fill out all fields!" -msgstr "Compila tutti i campi" +msgstr "Per favore compila tutti i campi!" -#: cps/web.py:2226 cps/web.py:2248 cps/web.py:2252 cps/web.py:2257 -#: cps/web.py:2259 +#: cps/web.py:2256 cps/web.py:2278 cps/web.py:2282 cps/web.py:2287 +#: cps/web.py:2289 msgid "register" -msgstr "Registrare" +msgstr "registra" -#: cps/web.py:2247 cps/web.py:3365 +#: cps/web.py:2277 cps/web.py:3395 msgid "An unknown error occurred. Please try again later." -msgstr "" +msgstr "Si è verificato un errore sconosciuto: per favore riprova." -#: cps/web.py:2250 +#: cps/web.py:2280 msgid "Your e-mail is not allowed to register" -msgstr "" +msgstr "Il tuo e-mail non può essere utilizzato per la registrazione" -#: cps/web.py:2253 +#: cps/web.py:2283 msgid "Confirmation e-mail was send to your e-mail account." -msgstr "" +msgstr "Un e-mail di conferma è stato inviato al tuo indirizzo." -#: cps/web.py:2256 +#: cps/web.py:2286 msgid "This username or e-mail address is already in use." -msgstr "" +msgstr "Questo nome di utente o questo e-mail sono già utilizzati." -#: cps/web.py:2273 cps/web.py:2369 +#: cps/web.py:2303 cps/web.py:2399 #, python-format msgid "you are now logged in as: '%(nickname)s'" -msgstr "ora sei connesso come : '%(nickname)s'" +msgstr "ora sei connesso come: '%(nickname)s'" -#: cps/web.py:2278 +#: cps/web.py:2308 msgid "Wrong Username or Password" -msgstr "Nome utente o password errata" +msgstr "Nome utente o password errati" -#: cps/web.py:2284 cps/web.py:2305 +#: cps/web.py:2314 cps/web.py:2335 msgid "login" -msgstr "Accesso" +msgstr "accedi" -#: cps/web.py:2317 cps/web.py:2348 +#: cps/web.py:2347 cps/web.py:2378 msgid "Token not found" msgstr "Token non trovato" -#: cps/web.py:2325 cps/web.py:2356 +#: cps/web.py:2355 cps/web.py:2386 msgid "Token has expired" msgstr "Il token è scaduto" -#: cps/web.py:2333 +#: cps/web.py:2363 msgid "Success! Please return to your device" msgstr "Successo! Torna al tuo dispositivo" -#: cps/web.py:2383 +#: cps/web.py:2413 msgid "Please configure the SMTP mail settings first..." -msgstr "Configurare prima le impostazioni della posta SMTP..." +msgstr "Configurare dapprima le impostazioni del server SMTP..." -#: cps/web.py:2388 +#: cps/web.py:2418 #, python-format msgid "Book successfully queued for sending to %(kindlemail)s" -msgstr "" +msgstr "Libro accodato con successo per essere spedito a %(kindlemail)s" -#: cps/web.py:2392 +#: cps/web.py:2422 #, python-format msgid "There was an error sending this book: %(res)s" msgstr "Si è verificato un errore durante l'invio di questo libro: %(res)s" -#: cps/web.py:2394 cps/web.py:3199 +#: cps/web.py:2424 cps/web.py:3229 msgid "Please configure your kindle e-mail address first..." -msgstr "" +msgstr "Per favore configura dapprima il tuo indirizzo e-mail di Kindle..." -#: cps/web.py:2405 cps/web.py:2457 +#: cps/web.py:2435 cps/web.py:2487 msgid "Invalid shelf specified" -msgstr "" +msgstr "Lo scaffale specificato non è valevole" -#: cps/web.py:2412 +#: cps/web.py:2442 #, python-format msgid "Sorry you are not allowed to add a book to the the shelf: %(shelfname)s" -msgstr "" +msgstr "Mi spiace, ma non sei autorizzato ad aggiungere libri allo scaffale: %(shelfname)s" -#: cps/web.py:2420 +#: cps/web.py:2450 msgid "You are not allowed to edit public shelves" -msgstr "" +msgstr "Non sei autorizzato a modificare gli scaffali pubblici" -#: cps/web.py:2429 +#: cps/web.py:2459 #, python-format msgid "Book is already part of the shelf: %(shelfname)s" -msgstr "" +msgstr "Il libro è gia presente nello scaffale: %(shelfname)s" -#: cps/web.py:2443 +#: cps/web.py:2473 #, python-format msgid "Book has been added to shelf: %(sname)s" -msgstr "Il libro è stato aggiunto alla mensola: %(sname)s" +msgstr "Il libro è stato aggiunto allo scaffale: %(sname)s" -#: cps/web.py:2462 +#: cps/web.py:2492 #, python-format msgid "You are not allowed to add a book to the the shelf: %(name)s" -msgstr "" +msgstr "Non sei autorizzato ad aggiungere libri allo scaffale: %(name)s" -#: cps/web.py:2467 +#: cps/web.py:2497 msgid "User is not allowed to edit public shelves" -msgstr "" +msgstr "L'utente non è autorizzato a modificare gli scaffali pubblici" -#: cps/web.py:2485 +#: cps/web.py:2515 #, python-format msgid "Books are already part of the shelf: %(name)s" -msgstr "" +msgstr "I libri sono già presenti nello scaffale: %(name)s" -#: cps/web.py:2499 +#: cps/web.py:2529 #, python-format msgid "Books have been added to shelf: %(sname)s" -msgstr "" +msgstr "I libri sono stati aggiunti allo scaffale: %(sname)s" -#: cps/web.py:2501 +#: cps/web.py:2531 #, python-format msgid "Could not add books to shelf: %(sname)s" -msgstr "" +msgstr "Non posso aggiungere libri allo scaffale: %(sname)s" -#: cps/web.py:2538 +#: cps/web.py:2568 #, python-format msgid "Book has been removed from shelf: %(sname)s" -msgstr "Il libro è stato rimosso dalla mensola: %(sname)s" +msgstr "Il libro è stato rimosso dallo scaffale: %(sname)s" -#: cps/web.py:2544 +#: cps/web.py:2574 #, python-format msgid "Sorry you are not allowed to remove a book from this shelf: %(sname)s" -msgstr "" +msgstr "Spiacente, ma non sei autorizzato a togliere libri dallo scaffale: %(sname)s" -#: cps/web.py:2565 cps/web.py:2589 +#: cps/web.py:2595 cps/web.py:2619 #, python-format msgid "A shelf with the name '%(title)s' already exists." msgstr "Uno scaffale con il nome '%(title)s' esiste già." -#: cps/web.py:2570 +#: cps/web.py:2600 #, python-format msgid "Shelf %(title)s created" -msgstr "Mensola %(title)s creato" +msgstr "Lo scaffale %(title)s è stato creato" -#: cps/web.py:2572 cps/web.py:2600 +#: cps/web.py:2602 cps/web.py:2630 msgid "There was an error" -msgstr "c'era un errore" +msgstr "C'era un errore" -#: cps/web.py:2573 cps/web.py:2575 +#: cps/web.py:2603 cps/web.py:2605 msgid "create a shelf" -msgstr "creare uno scaffale" +msgstr "crea uno scaffale" -#: cps/web.py:2598 +#: cps/web.py:2628 #, python-format msgid "Shelf %(title)s changed" -msgstr "Mensola %(title)s cambiato" +msgstr "Lo scaffale %(title)s è stato modificato" -#: cps/web.py:2601 cps/web.py:2603 +#: cps/web.py:2631 cps/web.py:2633 msgid "Edit a shelf" -msgstr "Modifica un ripiano" +msgstr "Modifica uno scaffale" -#: cps/web.py:2624 +#: cps/web.py:2654 #, python-format msgid "successfully deleted shelf %(name)s" -msgstr "cancellato con successo il ripiano %(name)s" +msgstr "lo scaffale %(name)s è stato eliminato con successo" -#: cps/web.py:2651 +#: cps/web.py:2681 #, python-format msgid "Shelf: '%(name)s'" -msgstr "Mensola: '%(name)s'" +msgstr "Scaffale: '%(name)s'" -#: cps/web.py:2654 +#: cps/web.py:2684 msgid "Error opening shelf. Shelf does not exist or is not accessible" -msgstr "Errore durante l'apertura dello scaffale. La mensola non esiste o non è accessibile" +msgstr "Errore durante l'apertura dello scaffale. Lo scaffale non esiste o non è accessibile" -#: cps/web.py:2685 +#: cps/web.py:2715 #, python-format msgid "Change order of Shelf: '%(name)s'" -msgstr "Modificare l'ordine della mensola: '%(name)s'" +msgstr "Modificare l'ordine dello scaffale: '%(name)s'" -#: cps/web.py:2714 cps/web.py:3152 +#: cps/web.py:2744 cps/web.py:3182 msgid "E-mail is not from valid domain" -msgstr "" +msgstr "L'e-mail non proviene da un dominio valido" -#: cps/web.py:2716 cps/web.py:2758 cps/web.py:2761 +#: cps/web.py:2746 cps/web.py:2788 cps/web.py:2791 #, python-format msgid "%(name)s's profile" msgstr "Profilo di %(name)s" -#: cps/web.py:2756 +#: cps/web.py:2786 msgid "Found an existing account for this e-mail address." -msgstr "" +msgstr "Ho trovato un account creato in precedenza con questo e-mail." -#: cps/web.py:2759 +#: cps/web.py:2789 msgid "Profile updated" msgstr "Profilo aggiornato" -#: cps/web.py:2790 +#: cps/web.py:2820 msgid "Admin page" msgstr "Pagina di amministrazione" -#: cps/web.py:2875 cps/web.py:3055 +#: cps/web.py:2905 cps/web.py:3085 msgid "Calibre-Web configuration updated" -msgstr "Aggiornamento della configurazione del calibro-web" +msgstr "Aggiornamento della configurazione di Calibre-Web" -#: cps/templates/admin.html:100 cps/web.py:2889 +#: cps/templates/admin.html:100 cps/web.py:2919 msgid "UI Configuration" -msgstr "" +msgstr "Configurazione dell'interfaccia utente" -#: cps/web.py:2907 +#: cps/web.py:2937 msgid "Import of optional Google Drive requirements missing" -msgstr "" +msgstr "Importa parametri mancanti per Google Drive" -#: cps/web.py:2910 +#: cps/web.py:2940 msgid "client_secrets.json is missing or not readable" -msgstr "" +msgstr "client_secrets.json manca o è inaccessibile" -#: cps/web.py:2915 cps/web.py:2944 +#: cps/web.py:2945 cps/web.py:2974 msgid "client_secrets.json is not configured for web application" -msgstr "" +msgstr "client_secrets.json non è configurato per questa applicazione web" -#: cps/templates/admin.html:99 cps/web.py:2947 cps/web.py:2973 cps/web.py:2985 -#: cps/web.py:3030 cps/web.py:3045 cps/web.py:3064 cps/web.py:3072 -#: cps/web.py:3088 +#: cps/templates/admin.html:99 cps/web.py:2977 cps/web.py:3003 cps/web.py:3015 +#: cps/web.py:3060 cps/web.py:3075 cps/web.py:3094 cps/web.py:3102 +#: cps/web.py:3118 msgid "Basic Configuration" msgstr "Configurazione di base" -#: cps/web.py:2970 +#: cps/web.py:3000 msgid "Keyfile location is not valid, please enter correct path" -msgstr "" +msgstr "La posizione del Keyfile non è corretta: per favore indica il percorso corretto" -#: cps/web.py:2982 +#: cps/web.py:3012 msgid "Certfile location is not valid, please enter correct path" -msgstr "" +msgstr "La posizione del Cerfile non è corretta: per favore indica il percorso corretto" -#: cps/web.py:3027 +#: cps/web.py:3057 msgid "Logfile location is not valid, please enter correct path" -msgstr "" +msgstr "La posizione del Logfile non è corretta: per favore indica il percorso corretto" -#: cps/web.py:3068 +#: cps/web.py:3098 msgid "DB location is not valid, please enter correct path" -msgstr "Posizione DB non valida. Inserisci il percorso corretto." +msgstr "La posizione DB non è corretta: per favore indica il percorso corretto" -#: cps/templates/admin.html:33 cps/web.py:3148 cps/web.py:3154 cps/web.py:3170 +#: cps/templates/admin.html:33 cps/web.py:3178 cps/web.py:3184 cps/web.py:3200 msgid "Add new user" msgstr "Aggiungi un nuovo utente" -#: cps/web.py:3160 +#: cps/web.py:3190 #, python-format msgid "User '%(user)s' created" -msgstr "utente '%(user)s' creato" +msgstr "Utente '%(user)s' creato" -#: cps/web.py:3164 +#: cps/web.py:3194 msgid "Found an existing account for this e-mail address or nickname." -msgstr "" +msgstr "Trovato un account esistente con questo e-mail o nome di utente" -#: cps/web.py:3194 +#: cps/web.py:3224 #, python-format msgid "Test e-mail successfully send to %(kindlemail)s" -msgstr "" +msgstr "E-mail di test inviato con successo a %(kindlemail)s" -#: cps/web.py:3197 +#: cps/web.py:3227 #, python-format msgid "There was an error sending the Test e-mail: %(res)s" -msgstr "" +msgstr "Si è verificato un errore nell'invio dell'e-mail di test: %(res)s" -#: cps/web.py:3201 +#: cps/web.py:3231 msgid "E-mail server settings updated" -msgstr "" +msgstr "Configurazione del server e-mail aggiornata" -#: cps/web.py:3202 +#: cps/web.py:3232 msgid "Edit e-mail server settings" -msgstr "" +msgstr "Modifica la configurazione del server e-mail" -#: cps/web.py:3227 +#: cps/web.py:3257 #, python-format msgid "User '%(nick)s' deleted" -msgstr "utente '%(nick)s' cancellati" +msgstr "Utente '%(nick)s' eliminato" -#: cps/web.py:3340 +#: cps/web.py:3370 #, python-format msgid "User '%(nick)s' updated" -msgstr "utente '%(nick)s' aggiornato" +msgstr "Utente '%(nick)s' aggiornato" -#: cps/web.py:3343 +#: cps/web.py:3373 msgid "An unknown error occured." -msgstr "Errore imprevisto." +msgstr "Si è verificato un errore imprevisto." -#: cps/web.py:3345 +#: cps/web.py:3375 #, python-format msgid "Edit User %(nick)s" msgstr "Modifica utente %(nick)s" -#: cps/web.py:3362 +#: cps/web.py:3392 #, python-format msgid "Password for user %(user)s reset" -msgstr "" +msgstr "La password dell'utente %(user)s è stata resettata" -#: cps/web.py:3376 cps/web.py:3582 +#: cps/web.py:3406 cps/web.py:3598 msgid "Error opening eBook. File does not exist or file is not accessible" -msgstr "Errore durante l'apertura di eBook. Il file non esiste o il file non è accessibile" +msgstr "Errore durante l'apertura del libro. Il file non esiste o il file non è accessibile" -#: cps/web.py:3404 +#: cps/web.py:3434 msgid "edit metadata" -msgstr "modificare la metainformazione" +msgstr "modifica i metadati" -#: cps/web.py:3497 cps/web.py:3743 +#: cps/web.py:3527 cps/web.py:3760 #, python-format msgid "File extension '%(ext)s' is not allowed to be uploaded to this server" -msgstr "Non è consentito caricare i file con l'estensione '%(ext)s' a questo server" +msgstr "Non è consentito caricare file con l'estensione '%(ext)s' su questo server" -#: cps/web.py:3501 cps/web.py:3746 +#: cps/web.py:3531 cps/web.py:3763 msgid "File to be uploaded must have an extension" msgstr "Il file da caricare deve avere un'estensione" -#: cps/web.py:3513 cps/web.py:3765 +#: cps/web.py:3543 cps/web.py:3782 #, python-format msgid "Failed to create path %(path)s (Permission denied)." -msgstr "Impossibile creare il percorso %(path)s (autorizzazione negata)" +msgstr "Impossibile creare il percorso %(path)s (autorizzazione negata)." -#: cps/web.py:3518 +#: cps/web.py:3548 #, python-format msgid "Failed to store file %(file)s." -msgstr "" +msgstr "Il salvataggio del file %(file)s è fallito." -#: cps/web.py:3535 +#: cps/web.py:3565 #, python-format msgid "File format %(ext)s added to %(book)s" -msgstr "" - -#: cps/web.py:3553 -#, python-format -msgid "Failed to create path for cover %(path)s (Permission denied)." -msgstr "" - -#: cps/web.py:3561 -#, python-format -msgid "Failed to store cover-file %(cover)s." -msgstr "" +msgstr "Ho aggiunto l'estensione %(ext)s al libro %(book)s" -#: cps/web.py:3564 -msgid "Cover-file is not a valid image file" +#: cps/web.py:3579 cps/web.py:3652 +msgid "Cover is not a supported imageformat (jpg/png/webp), can't save" msgstr "" -#: cps/web.py:3594 cps/web.py:3603 +#: cps/web.py:3611 cps/web.py:3620 msgid "unknown" -msgstr "Sconosciuto" - -#: cps/web.py:3635 -msgid "Cover is not a jpg file, can't save" -msgstr "" +msgstr "sconosciuto" -#: cps/web.py:3683 +#: cps/web.py:3700 #, python-format msgid "%(langname)s is not a valid language" -msgstr "" +msgstr "%(langname)s non è una lingua valida" -#: cps/web.py:3714 +#: cps/web.py:3731 msgid "Metadata successfully updated" -msgstr "" +msgstr "I metadati sono stati aggiornati con successo" -#: cps/web.py:3723 +#: cps/web.py:3740 msgid "Error editing book, please check logfile for details" -msgstr "" +msgstr "Errore nella modifica del libro. Per favore verifica i dettagli nel file di registro (logfile)" -#: cps/web.py:3769 +#: cps/web.py:3786 #, python-format msgid "Failed to store file %(file)s (Permission denied)." -msgstr "Impossibile archiviare il file %(file)s (autorizzazione negata)" +msgstr "Impossibile salvare il file %(file)s (autorizzazione negata)" -#: cps/web.py:3774 +#: cps/web.py:3791 #, python-format msgid "Failed to delete file %(file)s (Permission denied)." msgstr "Impossibile eliminare il file %(file)s (autorizzazione negata)" -#: cps/web.py:3857 +#: cps/web.py:3873 #, python-format msgid "File %(title)s" -msgstr "" +msgstr "File %(title)s" -#: cps/web.py:3886 +#: cps/web.py:3902 msgid "Source or destination format for conversion missing" -msgstr "" +msgstr "Il formato sorgente o quello di destinazione per la conversione mancano" -#: cps/web.py:3896 +#: cps/web.py:3912 #, python-format msgid "Book successfully queued for converting to %(book_format)s" -msgstr "" +msgstr "Libro accodato con successo per essere convertito in %(book_format)s" -#: cps/web.py:3900 +#: cps/web.py:3916 #, python-format msgid "There was an error converting this book: %(res)s" -msgstr "" +msgstr "Si è verificato un errore durante la conversione del libro: %(res)s" #: cps/worker.py:305 #, python-format msgid "Ebook-converter failed: %(error)s" -msgstr "" +msgstr "Errore nel convertitore: %(error)s" #: cps/worker.py:316 #, python-format msgid "Kindlegen failed with Error %(error)s. Message: %(message)s" -msgstr "errore" +msgstr "Kindlegen ha restituito l'errore %(error)s. Messaggio: %(message)s" #: cps/templates/admin.html:6 msgid "User list" -msgstr "elenco utenti" +msgstr "Elenco utenti" #: cps/templates/admin.html:9 msgid "Nickname" @@ -774,7 +765,7 @@ msgstr "Nickname" #: cps/templates/admin.html:10 msgid "E-mail" -msgstr "" +msgstr "E-mail" #: cps/templates/admin.html:11 msgid "Kindle" @@ -803,15 +794,15 @@ msgstr "Modifica" #: cps/templates/admin.html:39 msgid "SMTP e-mail server settings" -msgstr "" +msgstr "Configurazione server SMTP" #: cps/templates/admin.html:42 cps/templates/email_edit.html:11 msgid "SMTP hostname" -msgstr "SMTP hostname" +msgstr "indirizzo server SMTP" #: cps/templates/admin.html:43 msgid "SMTP port" -msgstr "SMTP port" +msgstr "porta SMTP" #: cps/templates/admin.html:44 msgid "SSL" @@ -819,15 +810,15 @@ msgstr "SSL" #: cps/templates/admin.html:45 cps/templates/email_edit.html:27 msgid "SMTP login" -msgstr "SMTP login" +msgstr "login SMTP" #: cps/templates/admin.html:46 msgid "From mail" -msgstr "From mail" +msgstr "E-mail da" #: cps/templates/admin.html:56 msgid "Change SMTP settings" -msgstr "Modificare SMTP impostazioni" +msgstr "Modificare le impostazioni SMTP" #: cps/templates/admin.html:62 msgid "Configuration" @@ -835,15 +826,15 @@ msgstr "Configurazione" #: cps/templates/admin.html:65 msgid "Calibre DB dir" -msgstr "Calibre DB dir" +msgstr "Cartella DB Calibre" #: cps/templates/admin.html:69 msgid "Log level" -msgstr "" +msgstr "Livello di log" #: cps/templates/admin.html:73 msgid "Port" -msgstr "Port" +msgstr "Porta" #: cps/templates/admin.html:79 cps/templates/config_view_edit.html:23 msgid "Books per page" @@ -855,7 +846,7 @@ msgstr "Uploading" #: cps/templates/admin.html:87 msgid "Anonymous browsing" -msgstr "" +msgstr "Navigazione anonima" #: cps/templates/admin.html:91 msgid "Public registration" @@ -867,7 +858,7 @@ msgstr "Login remoto" #: cps/templates/admin.html:106 msgid "Administration" -msgstr "Administration" +msgstr "Amministrazione" #: cps/templates/admin.html:107 msgid "Reconnect to Calibre DB" @@ -875,27 +866,27 @@ msgstr "Ricollegare al DB Calibre" #: cps/templates/admin.html:108 msgid "Restart Calibre-Web" -msgstr "Ricomincia Calibre-Web" +msgstr "Riavvia Calibre-Web" #: cps/templates/admin.html:109 msgid "Stop Calibre-Web" -msgstr "Stop Calibre-Web" +msgstr "Arresta Calibre-Web" #: cps/templates/admin.html:115 msgid "Update" -msgstr "" +msgstr "Aggiornamento" #: cps/templates/admin.html:119 msgid "Version" -msgstr "" +msgstr "Versione" #: cps/templates/admin.html:120 msgid "Details" -msgstr "" +msgstr "Dettagli" #: cps/templates/admin.html:126 msgid "Current version" -msgstr "" +msgstr "Versione attuale" #: cps/templates/admin.html:132 msgid "Check for update" @@ -907,7 +898,7 @@ msgstr "Esegui aggiornamento" #: cps/templates/admin.html:145 msgid "Do you really want to restart Calibre-Web?" -msgstr "Vuoi veramente riavviare Caliber-web?" +msgstr "Vuoi veramente riavviare Calibre-Web?" #: cps/templates/admin.html:150 cps/templates/admin.html:164 #: cps/templates/admin.html:184 cps/templates/shelf.html:73 @@ -926,7 +917,7 @@ msgstr "Indietro" #: cps/templates/admin.html:163 msgid "Do you really want to stop Calibre-Web?" -msgstr "Vuoi veramente fermare Caliber-web?" +msgstr "Vuoi veramente arrestare Calibre-Web?" #: cps/templates/admin.html:175 msgid "Updating, please do not reload page" @@ -938,14 +929,14 @@ msgstr "via" #: cps/templates/author.html:23 msgid "In Library" -msgstr "In libreria" +msgstr "Nella libreria" #: cps/templates/author.html:50 cps/templates/author.html:97 #: cps/templates/discover.html:28 cps/templates/index.html:31 #: cps/templates/index.html:86 cps/templates/search.html:55 #: cps/templates/shelf.html:37 msgid "reduce" -msgstr "" +msgstr "riduci" #: cps/templates/author.html:81 msgid "More by" @@ -957,7 +948,7 @@ msgstr "Elimina libro" #: cps/templates/book_edit.html:19 msgid "Delete formats:" -msgstr "" +msgstr "Elimina i formati:" #: cps/templates/book_edit.html:22 cps/templates/book_edit.html:199 #: cps/templates/email_edit.html:73 cps/templates/email_edit.html:74 @@ -966,23 +957,23 @@ msgstr "Elimina" #: cps/templates/book_edit.html:30 msgid "Convert book format:" -msgstr "" +msgstr "Converti il libro nel formato:" #: cps/templates/book_edit.html:34 msgid "Convert from:" -msgstr "" +msgstr "Converti da:" #: cps/templates/book_edit.html:36 cps/templates/book_edit.html:43 msgid "select an option" -msgstr "" +msgstr "seleziona un'opzione" #: cps/templates/book_edit.html:41 msgid "Convert to:" -msgstr "" +msgstr "Converti in:" #: cps/templates/book_edit.html:50 msgid "Convert book" -msgstr "" +msgstr "Converti libro" #: cps/templates/book_edit.html:59 cps/templates/search_form.html:6 msgid "Book Title" @@ -1009,7 +1000,7 @@ msgstr "Serie" #: cps/templates/book_edit.html:79 msgid "Series id" -msgstr "Series id" +msgstr "Serie id" #: cps/templates/book_edit.html:83 msgid "Rating" @@ -1017,11 +1008,11 @@ msgstr "Valutazione" #: cps/templates/book_edit.html:87 msgid "Cover URL (jpg, cover is downloaded and stored in database, field is afterwards empty again)" -msgstr "" +msgstr "URL della copertina (jpg, la copertina viene caricata e salvata nel database, dopodiché il campo viene nuovamente svuotato)" #: cps/templates/book_edit.html:91 msgid "Upload Cover from local drive" -msgstr "" +msgstr "Carica la copertina dal disco locale" #: cps/templates/book_edit.html:96 cps/templates/detail.html:148 msgid "Publishing date" @@ -1047,7 +1038,7 @@ msgstr "No" #: cps/templates/book_edit.html:164 msgid "Upload format" -msgstr "" +msgstr "Carica formato" #: cps/templates/book_edit.html:173 msgid "view book after edit" @@ -1062,7 +1053,7 @@ msgstr "Ottieni metadati" #: cps/templates/search_form.html:150 cps/templates/shelf_edit.html:17 #: cps/templates/user_edit.html:147 msgid "Submit" -msgstr "Sottoscrivi" +msgstr "Invia" #: cps/templates/book_edit.html:191 msgid "Are you really sure?" @@ -1070,7 +1061,7 @@ msgstr "Sei davvero sicuro?" #: cps/templates/book_edit.html:194 msgid "Book will be deleted from Calibre database" -msgstr "l libro verrà cancellato dal database Calibre'" +msgstr "Il libro verrà cancellato dal database di Calibre" #: cps/templates/book_edit.html:195 msgid "and from hard disk" @@ -1086,11 +1077,11 @@ msgstr "Cerca parola chiave" #: cps/templates/book_edit.html:218 cps/templates/layout.html:47 msgid "Go!" -msgstr "Partire" +msgstr "Vai!" #: cps/templates/book_edit.html:222 msgid "Click the cover to load metadata to the form" -msgstr "Fai clic sul coperchio per caricare i metadati nel modulo" +msgstr "Fai clic sulla copertina per caricare i metadati presenti nel modulo" #: cps/templates/book_edit.html:234 cps/templates/book_edit.html:274 msgid "Loading..." @@ -1099,11 +1090,11 @@ msgstr "Caricamento in corso..." #: cps/templates/book_edit.html:239 cps/templates/layout.html:226 #: cps/templates/layout.html:258 msgid "Close" -msgstr "Chiuso" +msgstr "Chiudi" #: cps/templates/book_edit.html:266 cps/templates/book_edit.html:280 msgid "Source" -msgstr "fonte" +msgstr "Fonte" #: cps/templates/book_edit.html:275 msgid "Search error!" @@ -1111,15 +1102,15 @@ msgstr "Errore di ricerca!" #: cps/templates/book_edit.html:276 msgid "No Result(s) found! Please try aonther keyword." -msgstr "" +msgstr "Nessun risultato! Prova con un altro criterio di ricerca." #: cps/templates/config_edit.html:12 msgid "Library Configuration" -msgstr "" +msgstr "Configurazione della libreria" #: cps/templates/config_edit.html:19 msgid "Location of Calibre database" -msgstr "Posizione del database Calibre" +msgstr "Posizione del database di Calibre" #: cps/templates/config_edit.html:24 msgid "Use Google Drive?" @@ -1127,23 +1118,23 @@ msgstr "Usa Google Drive?" #: cps/templates/config_edit.html:30 msgid "Google Drive config problem" -msgstr "" +msgstr "Problema nella configurazione di Google Drive" #: cps/templates/config_edit.html:36 msgid "Authenticate Google Drive" -msgstr "" +msgstr "Autenticazione Google Drive" #: cps/templates/config_edit.html:40 msgid "Please hit submit to continue with setup" -msgstr "" +msgstr "Per favore premi invio per proseguire con la configurazione" #: cps/templates/config_edit.html:43 msgid "Please finish Google Drive setup after login" -msgstr "" +msgstr "Per favore termina la configurazione di Google Drive dopo il login" #: cps/templates/config_edit.html:48 msgid "Google Drive Calibre folder" -msgstr "" +msgstr "Cartella di Calibre in Google Drive" #: cps/templates/config_edit.html:56 msgid "Metadata Watch Channel ID" @@ -1151,11 +1142,11 @@ msgstr "ID canale Watch Metadata" #: cps/templates/config_edit.html:59 msgid "Revoke" -msgstr "" +msgstr "Revoca" #: cps/templates/config_edit.html:78 msgid "Server Configuration" -msgstr "" +msgstr "Configurazione del server" #: cps/templates/config_edit.html:85 msgid "Server Port" @@ -1163,47 +1154,47 @@ msgstr "Porta del server" #: cps/templates/config_edit.html:89 msgid "SSL certfile location (leave it empty for non-SSL Servers)" -msgstr "" +msgstr "Percorso del certfile SSL (lascia vuoto per una configurazione del server senza SSL)" #: cps/templates/config_edit.html:93 msgid "SSL Keyfile location (leave it empty for non-SSL Servers)" -msgstr "" +msgstr "Percorso del Keyfile SSL (lascia vuoto per una configurazione del server senza SSL)" #: cps/templates/config_edit.html:97 msgid "Update channel" -msgstr "" +msgstr "Canale d'aggiornamento" #: cps/templates/config_edit.html:99 msgid "Stable" -msgstr "" +msgstr "Stabile" #: cps/templates/config_edit.html:100 msgid "Stable (Automatic)" -msgstr "" +msgstr "Stabile (Automatico)" #: cps/templates/config_edit.html:101 msgid "Nightly" -msgstr "" +msgstr "Nightly" #: cps/templates/config_edit.html:102 msgid "Nightly (Automatic)" -msgstr "" +msgstr "Nightly (automatico)" #: cps/templates/config_edit.html:113 msgid "Logfile Configuration" -msgstr "" +msgstr "Configurazione del Logfile" #: cps/templates/config_edit.html:120 msgid "Log Level" -msgstr "Livello del registro" +msgstr "Livello di Log" #: cps/templates/config_edit.html:129 msgid "Location and name of logfile (calibre-web.log for no entry)" -msgstr "" +msgstr "Percorso e nome del logfile (senza indicazioni sarà calibre-web.log)" #: cps/templates/config_edit.html:140 msgid "Feature Configuration" -msgstr "" +msgstr "Configurazione della caratteristica" #: cps/templates/config_edit.html:148 msgid "Enable uploading" @@ -1239,31 +1230,31 @@ msgstr "Goodreads API Secret" #: cps/templates/config_edit.html:187 msgid "External binaries" -msgstr "" +msgstr "Files binari esterni" #: cps/templates/config_edit.html:195 msgid "No converter" -msgstr "" +msgstr "Nessun convertitore" #: cps/templates/config_edit.html:197 msgid "Use Kindlegen" -msgstr "" +msgstr "Utilizza Kindlegen" #: cps/templates/config_edit.html:199 msgid "Use calibre's ebook converter" -msgstr "" +msgstr "Utilizza il convertitore di Calibre" #: cps/templates/config_edit.html:203 msgid "E-Book converter settings" -msgstr "" +msgstr "Configurazione del convertitore di e-book" #: cps/templates/config_edit.html:207 msgid "Path to convertertool" -msgstr "" +msgstr "Percorso del convertitore" #: cps/templates/config_edit.html:213 msgid "Location of Unrar binary" -msgstr "" +msgstr "Percorso di UnRar" #: cps/templates/config_edit.html:229 cps/templates/layout.html:84 #: cps/templates/login.html:4 @@ -1272,7 +1263,7 @@ msgstr "Accesso" #: cps/templates/config_view_edit.html:12 msgid "View Configuration" -msgstr "" +msgstr "Visualizza configurazione" #: cps/templates/config_view_edit.html:19 cps/templates/layout.html:135 #: cps/templates/layout.html:136 cps/templates/shelf_edit.html:7 @@ -1285,19 +1276,19 @@ msgstr "Numero di libri casuali da mostrare" #: cps/templates/config_view_edit.html:31 msgid "No. of authors to show before hiding (0=disable hiding)" -msgstr "" +msgstr "Numero di autori da mostrare prima di nascondere (0=disabilita mascheramento)" -#: cps/templates/config_view_edit.html:35 cps/templates/readcbr.html:108 +#: cps/templates/config_view_edit.html:35 cps/templates/readcbr.html:118 msgid "Theme" -msgstr "" +msgstr "Tema" #: cps/templates/config_view_edit.html:37 msgid "Standard Theme" -msgstr "" +msgstr "Tema standard" #: cps/templates/config_view_edit.html:38 msgid "caliBlur! Dark Theme" -msgstr "" +msgstr "caliBlur! Dark Theme" #: cps/templates/config_view_edit.html:42 msgid "Regular expression for ignoring columns" @@ -1305,15 +1296,15 @@ msgstr "Espressione regolare per ignorare le colonne" #: cps/templates/config_view_edit.html:46 msgid "Link read/unread status to Calibre column" -msgstr "" +msgstr "Collega lo stato letto/non letto nella colonna di Calibre" #: cps/templates/config_view_edit.html:55 msgid "Regular expression for title sorting" -msgstr "Espressione regolare per la selezione del titolo" +msgstr "Espressione regolare per 'ordine di visualizzazione del titolo" #: cps/templates/config_view_edit.html:59 msgid "Tags for Mature Content" -msgstr "Tags per Contenuti maturi" +msgstr "Tags per libri per adulti" #: cps/templates/config_view_edit.html:73 msgid "Default settings for new users" @@ -1333,7 +1324,7 @@ msgstr "Consenti caricamenti" #: cps/templates/config_view_edit.html:93 cps/templates/user_edit.html:121 msgid "Allow Edit" -msgstr "Consenti Modifica" +msgstr "Consenti modifica" #: cps/templates/config_view_edit.html:97 cps/templates/user_edit.html:125 msgid "Allow Delete books" @@ -1345,11 +1336,11 @@ msgstr "Consenti la modifica della password" #: cps/templates/config_view_edit.html:105 cps/templates/user_edit.html:134 msgid "Allow Editing Public Shelfs" -msgstr "Consenti la modifica dei ripiani pubblici" +msgstr "Consenti la modifica degli scaffali pubblici" #: cps/templates/config_view_edit.html:115 msgid "Default visibilities for new users" -msgstr "" +msgstr "Visibilità di base per i nuovi utenti" #: cps/templates/config_view_edit.html:123 cps/templates/user_edit.html:50 msgid "Show random books" @@ -1357,11 +1348,11 @@ msgstr "Mostra libro a caso" #: cps/templates/config_view_edit.html:127 cps/templates/user_edit.html:54 msgid "Show recent books" -msgstr "" +msgstr "Mostra i libri recenti" #: cps/templates/config_view_edit.html:131 cps/templates/user_edit.html:58 msgid "Show sorted books" -msgstr "" +msgstr "Mostra i libri ordinati" #: cps/templates/config_view_edit.html:135 cps/templates/user_edit.html:62 msgid "Show hot books" @@ -1369,27 +1360,27 @@ msgstr "Mostra libri popolari" #: cps/templates/config_view_edit.html:139 cps/templates/user_edit.html:66 msgid "Show best rated books" -msgstr "Mostra sezione più votati" +msgstr "Mostra la sezione dei libri più votati" #: cps/templates/config_view_edit.html:143 cps/templates/user_edit.html:70 msgid "Show language selection" -msgstr "Mostra sezione lingua" +msgstr "Mostra la selezione della lingua" #: cps/templates/config_view_edit.html:147 cps/templates/user_edit.html:74 msgid "Show series selection" -msgstr "Mostra sezione serie" +msgstr "Mostra la selezione delle serie" #: cps/templates/config_view_edit.html:151 cps/templates/user_edit.html:78 msgid "Show category selection" -msgstr "Mostra sezione categorie" +msgstr "Mostra la selezione delle categorie" #: cps/templates/config_view_edit.html:155 cps/templates/user_edit.html:82 msgid "Show author selection" -msgstr "Mostra sezione autore" +msgstr "Mostra la selezione dell'autore" #: cps/templates/config_view_edit.html:159 cps/templates/user_edit.html:86 msgid "Show publisher selection" -msgstr "" +msgstr "Mostra la selezione dell'editore" #: cps/templates/config_view_edit.html:163 cps/templates/user_edit.html:91 msgid "Show read and unread" @@ -1397,7 +1388,7 @@ msgstr "Mostra letto e non letto" #: cps/templates/config_view_edit.html:167 cps/templates/user_edit.html:96 msgid "Show random books in detail view" -msgstr "Un libro a caso" +msgstr "Mostra libri a caso nella vista dettagliata" #: cps/templates/config_view_edit.html:171 cps/templates/user_edit.html:109 msgid "Show mature content" @@ -1417,15 +1408,15 @@ msgstr "di" #: cps/templates/detail.html:106 msgid "language" -msgstr "Lingua" +msgstr "lingua" #: cps/templates/detail.html:185 msgid "Mark As Unread" -msgstr "" +msgstr "Marca come non letto" #: cps/templates/detail.html:185 msgid "Mark As Read" -msgstr "" +msgstr "Marca come letto" #: cps/templates/detail.html:186 msgid "Read" @@ -1433,11 +1424,11 @@ msgstr "Leggere" #: cps/templates/detail.html:196 msgid "Description:" -msgstr "Descrizione :" +msgstr "Descrizione:" #: cps/templates/detail.html:209 cps/templates/search.html:14 msgid "Add to shelf" -msgstr "Aggiungi al ripiano" +msgstr "Aggiungi allo scaffale" #: cps/templates/detail.html:271 msgid "Edit metadata" @@ -1445,11 +1436,11 @@ msgstr "Modifica metadati" #: cps/templates/email_edit.html:15 msgid "SMTP port (usually 25 for plain SMTP and 465 for SSL and 587 for STARTTLS)" -msgstr "Puerto SMTP (por lo general 25 para SMTP plano, 465 para SSL y 587 para STARTTLS)" +msgstr "Porta SMTP (normalmente 25 senza condifica, 465 per codifica SSL e 587 per STARTTLS)" #: cps/templates/email_edit.html:19 msgid "Encryption" -msgstr "crittografia" +msgstr "Crittografia" #: cps/templates/email_edit.html:21 msgid "None" @@ -1465,7 +1456,7 @@ msgstr "SSL/TLS" #: cps/templates/email_edit.html:31 msgid "SMTP password" -msgstr "SMTP password" +msgstr "password SMTP" #: cps/templates/email_edit.html:35 msgid "From e-mail" @@ -1477,27 +1468,27 @@ msgstr "Salva le impostazioni" #: cps/templates/email_edit.html:39 msgid "Save settings and send Test E-Mail" -msgstr "Salvare le impostazioni e inviare Test e-mail" +msgstr "Salvare le impostazioni e inviare e-mail di test" #: cps/templates/email_edit.html:43 msgid "Allowed domains for registering" -msgstr "" +msgstr "Dominii autorizzti alla registrazione" #: cps/templates/email_edit.html:47 msgid "Enter domainname" -msgstr "" +msgstr "Digita il nome di dominio" #: cps/templates/email_edit.html:55 msgid "Add Domain" -msgstr "" +msgstr "Aggiungi il dominio" #: cps/templates/email_edit.html:58 msgid "Add" -msgstr "" +msgstr "Aggiungi" #: cps/templates/email_edit.html:72 msgid "Do you really want to delete this domain rule?" -msgstr "" +msgstr "Vuoi veramente eliminare questa regola di dominio?" #: cps/templates/feed.xml:21 cps/templates/layout.html:210 msgid "Next" @@ -1510,11 +1501,11 @@ msgstr "Cerca" #: cps/templates/http_error.html:23 msgid "Back to home" -msgstr "" +msgstr "Ritorno alla pagina principale" #: cps/templates/index.html:5 msgid "Discover (Random Books)" -msgstr "Scoprire (Libri casuali)" +msgstr "Scoprire (libri casuali)" #: cps/templates/index.xml:6 msgid "Start" @@ -1534,7 +1525,7 @@ msgstr "Libri più votati" #: cps/templates/index.xml:29 msgid "Popular publications from this catalog based on Rating." -msgstr "Pubblicazioni popolari di questo catalogo basate su Rating." +msgstr "Pubblicazioni popolari di questo catalogo basate sui voti." #: cps/templates/index.xml:32 msgid "New Books" @@ -1554,15 +1545,15 @@ msgstr "Autori" #: cps/templates/index.xml:66 msgid "Books ordered by Author" -msgstr "Libri ordinati dall'autore" +msgstr "Libri ordinati per autore" #: cps/templates/index.xml:69 cps/templates/layout.html:165 msgid "Publishers" -msgstr "" +msgstr "Editori" #: cps/templates/index.xml:73 msgid "Books ordered by publisher" -msgstr "" +msgstr "Libri ordinati per editori" #: cps/templates/index.xml:80 msgid "Books ordered by category" @@ -1574,11 +1565,11 @@ msgstr "Libri ordinati per serie" #: cps/templates/index.xml:90 cps/templates/layout.html:171 msgid "Public Shelves" -msgstr "Ripiani pubblici" +msgstr "Scaffali pubblici" #: cps/templates/index.xml:94 msgid "Books organized in public shelfs, visible to everyone" -msgstr "" +msgstr "Libri organizzati in scaffali pubblici, visibili a tutti" #: cps/templates/index.xml:98 cps/templates/layout.html:175 msgid "Your Shelves" @@ -1586,28 +1577,28 @@ msgstr "I tuoi scaffali" #: cps/templates/index.xml:102 msgid "User's own shelfs, only visible to the current user himself" -msgstr "" +msgstr "Scaffali dell'utente, visibili unicamente all'utente stesso" #: cps/templates/layout.html:28 msgid "Home" -msgstr "" +msgstr "Home" #: cps/templates/layout.html:34 msgid "Toggle navigation" -msgstr "Toggle navigation" +msgstr "Alterna navigazione" #: cps/templates/layout.html:55 msgid "Advanced Search" msgstr "Ricerca avanzata" #: cps/templates/layout.html:76 cps/templates/read.html:71 -#: cps/templates/readcbr.html:79 cps/templates/readcbr.html:103 +#: cps/templates/readcbr.html:89 cps/templates/readcbr.html:113 msgid "Settings" -msgstr "" +msgstr "Configurazione" #: cps/templates/layout.html:78 msgid "Account" -msgstr "" +msgstr "Account" #: cps/templates/layout.html:80 msgid "Logout" @@ -1619,11 +1610,11 @@ msgstr "Registrare" #: cps/templates/layout.html:111 cps/templates/layout.html:257 msgid "Uploading..." -msgstr "" +msgstr "Caricare" #: cps/templates/layout.html:112 msgid "please don't refresh the page" -msgstr "" +msgstr "per favore non aggiornare la pagina" #: cps/templates/layout.html:122 msgid "Browse" @@ -1648,7 +1639,7 @@ msgstr "i più nuovi" #: cps/templates/layout.html:134 msgid "Oldest" -msgstr "il più vecchio" +msgstr "i più vecchi" #: cps/templates/layout.html:135 msgid "Ascending" @@ -1668,15 +1659,15 @@ msgstr "Categoria" #: cps/templates/layout.html:168 cps/templates/search_form.html:74 msgid "Languages" -msgstr "lingua" +msgstr "Lingua" #: cps/templates/layout.html:180 msgid "Create a Shelf" -msgstr "Crea una mensola" +msgstr "Crea uno scaffale" #: cps/templates/layout.html:181 cps/templates/stats.html:3 msgid "About" -msgstr "Di" +msgstr "Informazioni su" #: cps/templates/layout.html:195 msgid "Previous" @@ -1688,16 +1679,16 @@ msgstr "Dettagli ebook" #: cps/templates/layout.html:256 msgid "Upload done, processing, please wait..." -msgstr "" +msgstr "Caricamento riuscito, sto elaborando, per favore aspetta..." #: cps/templates/layout.html:259 msgid "Error" -msgstr "" +msgstr "Errore" #: cps/templates/login.html:8 cps/templates/login.html:9 #: cps/templates/register.html:7 cps/templates/user_edit.html:8 msgid "Username" -msgstr "Username" +msgstr "Nome utente" #: cps/templates/login.html:12 cps/templates/login.html:13 #: cps/templates/user_edit.html:21 @@ -1710,111 +1701,119 @@ msgstr "Ricordami" #: cps/templates/login.html:22 msgid "Log in with magic link" -msgstr "Accedi con il collegamento magico" +msgstr "Accedi con magic link" #: cps/templates/osd.xml:5 msgid "Calibre-Web ebook catalog" -msgstr "" +msgstr "Catalogo Calibre-Web" #: cps/templates/read.html:74 msgid "Reflow text when sidebars are open." -msgstr "Ridimensionare il testo quando le barre laterali sono aperte" +msgstr "Adatta il testo quando le barre laterali sono aperte." -#: cps/templates/readcbr.html:84 +#: cps/templates/readcbr.html:94 msgid "Keyboard Shortcuts" -msgstr "" +msgstr "Scorciatoie della tastiera" -#: cps/templates/readcbr.html:87 +#: cps/templates/readcbr.html:97 msgid "Previous Page" -msgstr "" +msgstr "Pagina precedente" -#: cps/templates/readcbr.html:88 +#: cps/templates/readcbr.html:98 msgid "Next Page" -msgstr "" +msgstr "Pagina successiva" -#: cps/templates/readcbr.html:89 +#: cps/templates/readcbr.html:99 msgid "Scale to Best" -msgstr "" +msgstr "Adatta al migliore" -#: cps/templates/readcbr.html:90 +#: cps/templates/readcbr.html:100 msgid "Scale to Width" -msgstr "" +msgstr "Adatta alla larghezza" -#: cps/templates/readcbr.html:91 +#: cps/templates/readcbr.html:101 msgid "Scale to Height" -msgstr "" +msgstr "Adatta all'altezza" -#: cps/templates/readcbr.html:92 +#: cps/templates/readcbr.html:102 msgid "Scale to Native" -msgstr "" +msgstr "Dimensione originale" -#: cps/templates/readcbr.html:93 +#: cps/templates/readcbr.html:103 msgid "Rotate Right" -msgstr "" +msgstr "Ruota a destra" -#: cps/templates/readcbr.html:94 +#: cps/templates/readcbr.html:104 msgid "Rotate Left" -msgstr "" +msgstr "Ruota a sinistra" -#: cps/templates/readcbr.html:95 +#: cps/templates/readcbr.html:105 msgid "Flip Image" -msgstr "" +msgstr "Capovolgi immagine" -#: cps/templates/readcbr.html:111 +#: cps/templates/readcbr.html:121 msgid "Light" -msgstr "" +msgstr "Chiaro" -#: cps/templates/readcbr.html:112 +#: cps/templates/readcbr.html:122 msgid "Dark" -msgstr "" +msgstr "Scuro" -#: cps/templates/readcbr.html:117 +#: cps/templates/readcbr.html:127 msgid "Scale" -msgstr "" +msgstr "Adatta" -#: cps/templates/readcbr.html:120 +#: cps/templates/readcbr.html:130 msgid "Best" -msgstr "" +msgstr "Migliore" -#: cps/templates/readcbr.html:121 +#: cps/templates/readcbr.html:131 msgid "Width" -msgstr "" +msgstr "Larghezza" -#: cps/templates/readcbr.html:122 +#: cps/templates/readcbr.html:132 msgid "Height" -msgstr "" +msgstr "Alteszza" -#: cps/templates/readcbr.html:123 +#: cps/templates/readcbr.html:133 msgid "Native" -msgstr "" +msgstr "Originale" -#: cps/templates/readcbr.html:128 +#: cps/templates/readcbr.html:138 msgid "Rotate" -msgstr "" +msgstr "Ruota" -#: cps/templates/readcbr.html:139 +#: cps/templates/readcbr.html:149 msgid "Flip" -msgstr "" +msgstr "Capovolgi" -#: cps/templates/readcbr.html:142 +#: cps/templates/readcbr.html:152 msgid "Horizontal" -msgstr "" +msgstr "Orizzontale" -#: cps/templates/readcbr.html:143 +#: cps/templates/readcbr.html:153 msgid "Vertical" +msgstr "Verticale" + +#: cps/templates/readcbr.html:158 +msgid "Direction" msgstr "" -#: cps/templates/readpdf.html:29 -msgid "PDF.js viewer" -msgstr "Visiera PDF.js" +#: cps/templates/readcbr.html:161 +msgid "Left to Right" +msgstr "" -#: cps/templates/readpdf.html:418 -msgid "Preparing document for printing..." +#: cps/templates/readcbr.html:162 +msgid "Right to Left" msgstr "" +#: cps/templates/readpdf.html:29 +msgid "PDF.js viewer" +msgstr "Visore PDF.js" + #: cps/templates/readtxt.html:6 msgid "Basic txt Reader" -msgstr "Lettore base txt" +msgstr "Lettore di base txt" #: cps/templates/register.html:4 msgid "Register a new account" @@ -1826,7 +1825,7 @@ msgstr "Scegli un nome utente" #: cps/templates/register.html:11 cps/templates/user_edit.html:13 msgid "E-mail address" -msgstr "" +msgstr "Indirizzo e-mail" #: cps/templates/register.html:12 msgid "Your email address" @@ -1834,7 +1833,7 @@ msgstr "Il tuo indirizzo e-mail" #: cps/templates/remote_login.html:6 msgid "Using your another device, visit" -msgstr "Utilizzando l'altro dispositivo, visita" +msgstr "Utilizzando il tuo altro dispositivo, visita" #: cps/templates/remote_login.html:6 msgid "and log in" @@ -1842,7 +1841,7 @@ msgstr "e accedi" #: cps/templates/remote_login.html:9 msgid "Once you do so, you will automatically get logged in on this device." -msgstr "Una volta fatto questo, ti verrà automaticamente connesso in questo dispositivo" +msgstr "Una volta fatto questo, verrai automaticamente connesso con questo dispositivo." #: cps/templates/search.html:5 msgid "No Results for:" @@ -1858,11 +1857,11 @@ msgstr "Risultati per:" #: cps/templates/search_form.html:19 msgid "Publishing date from" -msgstr "" +msgstr "Data di pubblicazione da" #: cps/templates/search_form.html:26 msgid "Publishing date to" -msgstr "" +msgstr "Data di pubblicazione fino a" #: cps/templates/search_form.html:43 msgid "Exclude Tags" @@ -1870,7 +1869,7 @@ msgstr "Elimina i tag" #: cps/templates/search_form.html:63 msgid "Exclude Series" -msgstr "Escludi la serie" +msgstr "Escludi serie" #: cps/templates/search_form.html:84 msgid "Exclude Languages" @@ -1878,19 +1877,19 @@ msgstr "Elimina lingue" #: cps/templates/search_form.html:97 msgid "Rating bigger than" -msgstr "" +msgstr "Valutazione superiore a" #: cps/templates/search_form.html:101 msgid "Rating less than" -msgstr "" +msgstr "Valutazione inferiore a" #: cps/templates/shelf.html:7 msgid "Delete this Shelf" -msgstr "Cancellare questa libreria" +msgstr "Cancellare questo scaffale" #: cps/templates/shelf.html:8 msgid "Edit Shelf" -msgstr "" +msgstr "Modifica scaffale" #: cps/templates/shelf.html:9 cps/templates/shelf_order.html:11 msgid "Change order" @@ -1902,19 +1901,19 @@ msgstr "Vuoi davvero eliminare lo scaffale?" #: cps/templates/shelf.html:71 msgid "Shelf will be lost for everybody and forever!" -msgstr "La mensola sarà perduta per tutti e per sempre!" +msgstr "Lo scaffale sarà perso per tutti e per sempre!" #: cps/templates/shelf_edit.html:13 msgid "should the shelf be public?" -msgstr "Pubblico?" +msgstr "questo scaffale deve essere pubblico?" #: cps/templates/shelf_order.html:5 msgid "Drag 'n drop to rearrange order" -msgstr "Riordina drag and drop" +msgstr "Riordina tramite drag and drop" #: cps/templates/stats.html:7 msgid "Calibre library statistics" -msgstr "Statistiche libreria" +msgstr "Statistiche libreria Calibre" #: cps/templates/stats.html:12 msgid "Books in this Library" @@ -1934,7 +1933,7 @@ msgstr "Serie in questa libreria" #: cps/templates/stats.html:28 msgid "Linked libraries" -msgstr "Link libreria" +msgstr "Librerie collegate" #: cps/templates/stats.html:32 msgid "Program library" @@ -1946,47 +1945,47 @@ msgstr "Versione installata" #: cps/templates/tasks.html:7 msgid "Tasks list" -msgstr "" +msgstr "Lista delle operazioni" #: cps/templates/tasks.html:12 msgid "User" -msgstr "" +msgstr "Utente" #: cps/templates/tasks.html:14 msgid "Task" -msgstr "" +msgstr "Operazioni" #: cps/templates/tasks.html:15 msgid "Status" -msgstr "" +msgstr "Stato" #: cps/templates/tasks.html:16 msgid "Progress" -msgstr "" +msgstr "Avanzamento" #: cps/templates/tasks.html:17 msgid "Runtime" -msgstr "" +msgstr "Tempo d'esecuzione" #: cps/templates/tasks.html:18 msgid "Starttime" -msgstr "" +msgstr "Ora d'inizio" #: cps/templates/tasks.html:24 msgid "Delete finished tasks" -msgstr "" +msgstr "Elimina le operazioni terminate" #: cps/templates/tasks.html:25 msgid "Hide all tasks" -msgstr "" +msgstr "Nascondi tutte le operazioni" #: cps/templates/user_edit.html:18 msgid "Reset user Password" -msgstr "" +msgstr "Resetta la password dell'utente" #: cps/templates/user_edit.html:27 msgid "Kindle E-Mail" -msgstr "Email Kindle" +msgstr "E-mail di Kindle" #: cps/templates/user_edit.html:39 msgid "Show books with language" @@ -2653,7 +2652,7 @@ msgstr "Download Recenti" #~ msgstr "Mongo" #~ msgid "Lozi" -#~ msgstr "lozi" +#~ msgstr "Lozi" #~ msgid "Luxembourgish" #~ msgstr "Lussemburghese" @@ -3258,3 +3257,18 @@ msgstr "Download Recenti" #~ msgid "Zaza" #~ msgstr "Zaza" +#~ msgid "Failed to create path for cover %(path)s (Permission denied)." +#~ msgstr "Impossibile creare il percorso per la copertina %(path)s (autorizzazione negata)." + +#~ msgid "Failed to store cover-file %(cover)s." +#~ msgstr "Impossibile salvare la copertina %(cover)s." + +#~ msgid "Cover-file is not a valid image file" +#~ msgstr "Il file della copertina non è un file d'immagine valido" + +#~ msgid "Cover is not a jpg file, can't save" +#~ msgstr "La copertina non è un file in formato jpg: non posso salvare" + +#~ msgid "Preparing document for printing..." +#~ msgstr "Preparo documento per la stampa..." + diff --git a/cps/translations/ja/LC_MESSAGES/messages.mo b/cps/translations/ja/LC_MESSAGES/messages.mo index df5fa48610c0ec82306a5b8b640650efaebc692c..bd3e2a1fec40e8f0cf919e97fc36c93244070c9c 100644 GIT binary patch literal 52473 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 S$!FRaLQE3q`*L$h3H^UX+Ml!l delta 12145 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 diff --git a/cps/translations/ja/LC_MESSAGES/messages.po b/cps/translations/ja/LC_MESSAGES/messages.po index f3e0451f..12009b97 100644 --- a/cps/translations/ja/LC_MESSAGES/messages.po +++ b/cps/translations/ja/LC_MESSAGES/messages.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Calibre-Web\n" "Report-Msgid-Bugs-To: https://github.com/janeczku/Calibre-Web\n" -"POT-Creation-Date: 2019-03-10 08:03+0100\n" +"POT-Creation-Date: 2019-05-08 20:23+0200\n" "PO-Revision-Date: 2018-02-07 02:20-0500\n" "Last-Translator: white \n" "Language: ja\n" @@ -18,8 +18,9 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.6.0\n" -#: cps/book_formats.py:152 cps/book_formats.py:153 cps/book_formats.py:157 -#: cps/book_formats.py:161 cps/converter.py:29 cps/converter.py:45 +#: cps/book_formats.py:199 cps/book_formats.py:200 cps/book_formats.py:204 +#: cps/book_formats.py:208 cps/book_formats.py:212 cps/converter.py:29 +#: cps/converter.py:45 msgid "not installed" msgstr "インストールされていません" @@ -31,133 +32,133 @@ msgstr "実行許可がありません" msgid "not configured" msgstr "未設定です" -#: cps/helper.py:72 +#: cps/helper.py:79 #, python-format msgid "%(format)s format not found for book id: %(book)d" msgstr "ID: %(book)d の本に %(format)s フォーマットはありません" -#: cps/helper.py:84 +#: cps/helper.py:91 #, python-format msgid "%(format)s not found on Google Drive: %(fn)s" msgstr "Googleドライブ: %(fn)s に %(format)s はありません" -#: cps/helper.py:91 cps/helper.py:199 cps/templates/detail.html:45 +#: cps/helper.py:98 cps/helper.py:204 cps/templates/detail.html:45 #: cps/templates/detail.html:49 msgid "Send to Kindle" msgstr "Kindleに送信" -#: cps/helper.py:92 cps/helper.py:110 cps/helper.py:201 +#: cps/helper.py:99 cps/helper.py:117 cps/helper.py:206 msgid "This e-mail has been sent via Calibre-Web." msgstr "このメールはCalibre-Web経由で送信されました。" -#: cps/helper.py:103 +#: cps/helper.py:110 #, python-format msgid "%(format)s not found: %(fn)s" msgstr "%(format)s がありません: %(fn)s" -#: cps/helper.py:108 +#: cps/helper.py:115 msgid "Calibre-Web test e-mail" msgstr "Calibre-Web テストメール" -#: cps/helper.py:109 +#: cps/helper.py:116 msgid "Test e-mail" msgstr "テストメール" -#: cps/helper.py:125 +#: cps/helper.py:132 msgid "Get Started with Calibre-Web" msgstr "Calibre-Webを始める" -#: cps/helper.py:126 +#: cps/helper.py:133 #, python-format msgid "Registration e-mail for user: %(name)s" msgstr "ユーザ: %(name)s 用の登録メール" -#: cps/helper.py:139 cps/helper.py:141 cps/helper.py:143 cps/helper.py:145 -#: cps/helper.py:151 cps/helper.py:153 cps/helper.py:155 cps/helper.py:157 +#: cps/helper.py:146 cps/helper.py:148 cps/helper.py:150 cps/helper.py:158 +#: cps/helper.py:160 cps/helper.py:162 #, python-format msgid "Send %(format)s to Kindle" msgstr "Kindleに %(format)s を送信" -#: cps/helper.py:161 cps/helper.py:165 +#: cps/helper.py:166 #, python-format msgid "Convert %(orig)s to %(format)s and send to Kindle" msgstr "%(orig)s を %(format)s に変換してからKindleに送信" -#: cps/helper.py:200 +#: cps/helper.py:205 #, python-format msgid "E-mail: %(book)s" msgstr "メール: %(book)s" -#: cps/helper.py:203 +#: cps/helper.py:208 msgid "The requested file could not be read. Maybe wrong permissions?" msgstr "要求されたファイルを読み込めませんでした。権限設定が正しいか確認してください。" -#: cps/helper.py:311 +#: cps/helper.py:316 #, python-format msgid "Rename title from: '%(src)s' to '%(dest)s' failed with error: %(error)s" msgstr "エラー: %(error)s により、タイトルを %(src)s から %(dest)s に変更できませんでした。" -#: cps/helper.py:321 +#: cps/helper.py:326 #, python-format msgid "Rename author from: '%(src)s' to '%(dest)s' failed with error: %(error)s" msgstr "エラー: %(error)s により、著者名を %(src)s から %(dest)s に変更できませんでした。" -#: cps/helper.py:335 +#: cps/helper.py:340 #, python-format msgid "Rename file in path '%(src)s' to '%(dest)s' failed with error: %(error)s" msgstr "エラー: %(error)s により、ファイルパスを %(src)s から %(dest)s に変更できませんでした。" -#: cps/helper.py:361 cps/helper.py:371 cps/helper.py:379 +#: cps/helper.py:366 cps/helper.py:376 cps/helper.py:384 #, python-format msgid "File %(file)s not found on Google Drive" msgstr "ファイル %(file)s はGoogleドライブ上にありません" -#: cps/helper.py:400 +#: cps/helper.py:405 #, python-format msgid "Book path %(path)s not found on Google Drive" msgstr "本のパス %(path)s はGoogleドライブ上にありません" -#: cps/helper.py:508 +#: cps/helper.py:556 msgid "Error excecuting UnRar" msgstr "rarファイルを展開中にエラーが発生しました" -#: cps/helper.py:510 +#: cps/helper.py:558 msgid "Unrar binary file not found" msgstr "Unrarバイナリが見つかりません" -#: cps/helper.py:541 +#: cps/helper.py:589 msgid "Waiting" msgstr "待機中" -#: cps/helper.py:543 +#: cps/helper.py:591 msgid "Failed" msgstr "失敗" -#: cps/helper.py:545 +#: cps/helper.py:593 msgid "Started" msgstr "開始" -#: cps/helper.py:547 +#: cps/helper.py:595 msgid "Finished" msgstr "終了" -#: cps/helper.py:549 +#: cps/helper.py:597 msgid "Unknown Status" msgstr "不明" -#: cps/helper.py:554 +#: cps/helper.py:602 msgid "E-mail: " msgstr "メール: " -#: cps/helper.py:556 cps/helper.py:560 +#: cps/helper.py:604 cps/helper.py:608 msgid "Convert: " msgstr "変換: " -#: cps/helper.py:558 +#: cps/helper.py:606 msgid "Upload: " msgstr "アップロード: " -#: cps/helper.py:562 +#: cps/helper.py:610 msgid "Unknown Task: " msgstr "不明なタスク: " @@ -169,19 +170,19 @@ msgstr "アップデート情報を読み込み中に予期しないデータが msgid "No update available. You already have the latest version installed" msgstr "アップデートはありません。すでに最新バージョンがインストールされています" -#: cps/updater.py:270 cps/updater.py:501 cps/updater.py:503 cps/web.py:1187 +#: cps/updater.py:270 cps/updater.py:501 cps/updater.py:503 cps/web.py:1206 msgid "HTTP Error" msgstr "HTTPエラー" -#: cps/updater.py:272 cps/updater.py:505 cps/web.py:1188 +#: cps/updater.py:272 cps/updater.py:505 cps/web.py:1207 msgid "Connection error" msgstr "接続エラー" -#: cps/updater.py:274 cps/updater.py:507 cps/web.py:1189 +#: cps/updater.py:274 cps/updater.py:507 cps/web.py:1208 msgid "Timeout while establishing connection" msgstr "接続を確立中にタイムアウトしました" -#: cps/updater.py:276 cps/updater.py:509 cps/web.py:1190 +#: cps/updater.py:276 cps/updater.py:509 cps/web.py:1209 msgid "General error" msgstr "エラー発生" @@ -202,555 +203,545 @@ msgstr "リリース情報がありません" msgid "A new update is available. Click on the button below to update to version: %(version)s" msgstr "アップデートが利用可能です。下のボタンをクリックしてバージョン: %(version)s にアップデートしてください。" -#: cps/updater.py:491 cps/web.py:2771 +#: cps/updater.py:491 cps/web.py:2801 msgid "Unknown" msgstr "不明" -#: cps/web.py:1180 +#: cps/web.py:1199 msgid "Requesting update package" msgstr "更新データを要求中" -#: cps/web.py:1181 +#: cps/web.py:1200 msgid "Downloading update package" msgstr "更新データをダウンロード中" -#: cps/web.py:1182 +#: cps/web.py:1201 msgid "Unzipping update package" msgstr "更新データを展開中" -#: cps/web.py:1183 +#: cps/web.py:1202 msgid "Replacing files" msgstr "ファイルを置換中" -#: cps/web.py:1184 +#: cps/web.py:1203 msgid "Database connections are closed" msgstr "データベースの接続を切断完了" -#: cps/web.py:1185 +#: cps/web.py:1204 msgid "Stopping server" msgstr "サーバ停止中" -#: cps/web.py:1186 +#: cps/web.py:1205 msgid "Update finished, please press okay and reload page" msgstr "アップデート完了、OKを押してページをリロードしてください" -#: cps/web.py:1187 cps/web.py:1188 cps/web.py:1189 cps/web.py:1190 +#: cps/web.py:1206 cps/web.py:1207 cps/web.py:1208 cps/web.py:1209 msgid "Update failed:" msgstr "アップデート失敗:" -#: cps/web.py:1213 +#: cps/web.py:1235 msgid "Recently Added Books" msgstr "最近追加された本" -#: cps/web.py:1223 +#: cps/web.py:1245 msgid "Newest Books" msgstr "新着順" -#: cps/web.py:1235 +#: cps/web.py:1257 msgid "Oldest Books" msgstr "投稿順" -#: cps/web.py:1247 +#: cps/web.py:1269 msgid "Books (A-Z)" msgstr "昇順" -#: cps/web.py:1258 +#: cps/web.py:1280 msgid "Books (Z-A)" msgstr "降順" -#: cps/web.py:1287 +#: cps/web.py:1309 msgid "Hot Books (most downloaded)" msgstr "話題(ダウンロード数順)" -#: cps/web.py:1300 +#: cps/web.py:1322 msgid "Best rated books" msgstr "高評価" -#: cps/templates/index.xml:39 cps/web.py:1313 +#: cps/templates/index.xml:39 cps/web.py:1335 msgid "Random Books" msgstr "ランダム" -#: cps/web.py:1340 cps/web.py:1596 cps/web.py:2140 +#: cps/web.py:1362 cps/web.py:1618 cps/web.py:2167 msgid "Error opening eBook. File does not exist or file is not accessible:" msgstr "電子書籍を開けません。ファイルが存在しないかアクセスできません:" -#: cps/web.py:1369 +#: cps/web.py:1391 msgid "Publisher list" msgstr "出版社一覧" -#: cps/web.py:1384 +#: cps/web.py:1406 #, python-format msgid "Publisher: %(name)s" msgstr "出版社: %(name)s" -#: cps/templates/index.xml:83 cps/web.py:1416 +#: cps/templates/index.xml:83 cps/web.py:1438 msgid "Series list" msgstr "シリーズ一覧" -#: cps/web.py:1430 +#: cps/web.py:1452 #, python-format msgid "Series: %(serie)s" msgstr "シリーズ: %(serie)s" -#: cps/web.py:1456 +#: cps/web.py:1478 msgid "Available languages" msgstr "言語" -#: cps/web.py:1476 +#: cps/web.py:1498 #, python-format msgid "Language: %(name)s" msgstr "言語: %(name)s" -#: cps/templates/index.xml:76 cps/web.py:1487 +#: cps/templates/index.xml:76 cps/web.py:1509 msgid "Category list" msgstr "カテゴリ一覧" -#: cps/web.py:1501 +#: cps/web.py:1523 #, python-format msgid "Category: %(name)s" msgstr "カテゴリ: %(name)s" -#: cps/templates/layout.html:73 cps/web.py:1632 +#: cps/templates/layout.html:73 cps/web.py:1654 msgid "Tasks" msgstr "タスク" -#: cps/web.py:1666 +#: cps/web.py:1688 msgid "Statistics" msgstr "統計" -#: cps/web.py:1734 +#: cps/web.py:1756 msgid "Google Drive setup not completed, try to deactivate and activate Google Drive again" msgstr "Googleドライブの設定が完了していません。Googleドライブを無効化してから再度有効にしてみてください" -#: cps/web.py:1779 +#: cps/web.py:1801 msgid "Callback domain is not verified, please follow steps to verify domain in google developer console" msgstr "コールバックドメインが認証されていません。Google Developer Consoleでドメインを認証してください" -#: cps/web.py:1855 +#: cps/web.py:1877 msgid "Server restarted, please reload page" msgstr "サーバを再起動しました。ページを再読み込みしてください" -#: cps/web.py:1858 +#: cps/web.py:1880 msgid "Performing shutdown of server, please close window" msgstr "サーバをシャットダウンしています。ページを閉じてください" -#: cps/web.py:1938 +#: cps/web.py:1959 msgid "Published after " msgstr "これ以降に出版 " -#: cps/web.py:1945 +#: cps/web.py:1966 msgid "Published before " msgstr "これ以前に出版 " -#: cps/web.py:1959 +#: cps/web.py:1980 #, python-format msgid "Rating <= %(rating)s" msgstr "評価 <= %(rating)s" -#: cps/web.py:1961 +#: cps/web.py:1982 #, python-format msgid "Rating >= %(rating)s" msgstr "評価 >= %(rating)s" -#: cps/web.py:2022 cps/web.py:2031 +#: cps/web.py:2042 cps/web.py:2051 msgid "search" msgstr "検索" #: cps/templates/index.xml:47 cps/templates/index.xml:51 -#: cps/templates/layout.html:148 cps/web.py:2099 +#: cps/templates/layout.html:148 cps/web.py:2122 msgid "Read Books" msgstr "読んだ本" #: cps/templates/index.xml:55 cps/templates/index.xml:59 -#: cps/templates/layout.html:150 cps/web.py:2102 +#: cps/templates/layout.html:150 cps/web.py:2125 msgid "Unread Books" msgstr "未読の本" -#: cps/web.py:2150 cps/web.py:2152 cps/web.py:2154 cps/web.py:2166 +#: cps/web.py:2177 cps/web.py:2179 cps/web.py:2181 cps/web.py:2193 msgid "Read a Book" msgstr "本を読む" -#: cps/web.py:2225 cps/web.py:3146 +#: cps/web.py:2205 +msgid "Error opening eBook. Fileformat is not supported." +msgstr "" + +#: cps/web.py:2255 cps/web.py:3176 msgid "Please fill out all fields!" msgstr "全ての項目を入力してください" -#: cps/web.py:2226 cps/web.py:2248 cps/web.py:2252 cps/web.py:2257 -#: cps/web.py:2259 +#: cps/web.py:2256 cps/web.py:2278 cps/web.py:2282 cps/web.py:2287 +#: cps/web.py:2289 msgid "register" msgstr "登録" -#: cps/web.py:2247 cps/web.py:3365 +#: cps/web.py:2277 cps/web.py:3395 msgid "An unknown error occurred. Please try again later." msgstr "不明なエラーが発生しました。あとで再試行してください。" -#: cps/web.py:2250 +#: cps/web.py:2280 msgid "Your e-mail is not allowed to register" msgstr "このメールアドレスは登録が許可されていません" -#: cps/web.py:2253 +#: cps/web.py:2283 msgid "Confirmation e-mail was send to your e-mail account." msgstr "確認メールがこのメールアドレスに送信されました。" -#: cps/web.py:2256 +#: cps/web.py:2286 msgid "This username or e-mail address is already in use." msgstr "このユーザ名またはメールアドレスはすでに使われています。" -#: cps/web.py:2273 cps/web.py:2369 +#: cps/web.py:2303 cps/web.py:2399 #, python-format msgid "you are now logged in as: '%(nickname)s'" msgstr "%(nickname)s としてログイン中" -#: cps/web.py:2278 +#: cps/web.py:2308 msgid "Wrong Username or Password" msgstr "ユーザ名またはパスワードが違います" -#: cps/web.py:2284 cps/web.py:2305 +#: cps/web.py:2314 cps/web.py:2335 msgid "login" msgstr "ログイン" -#: cps/web.py:2317 cps/web.py:2348 +#: cps/web.py:2347 cps/web.py:2378 msgid "Token not found" msgstr "トークンが見つかりません" -#: cps/web.py:2325 cps/web.py:2356 +#: cps/web.py:2355 cps/web.py:2386 msgid "Token has expired" msgstr "トークンが無効です" -#: cps/web.py:2333 +#: cps/web.py:2363 msgid "Success! Please return to your device" msgstr "成功です!端末に戻ってください" -#: cps/web.py:2383 +#: cps/web.py:2413 msgid "Please configure the SMTP mail settings first..." msgstr "初めにSMTPメールの設定をしてください" -#: cps/web.py:2388 +#: cps/web.py:2418 #, python-format msgid "Book successfully queued for sending to %(kindlemail)s" msgstr "本の %(kindlemail)s への送信がキューに追加されました" -#: cps/web.py:2392 +#: cps/web.py:2422 #, python-format msgid "There was an error sending this book: %(res)s" msgstr "%(res)s を送信中にエラーが発生しました" -#: cps/web.py:2394 cps/web.py:3199 +#: cps/web.py:2424 cps/web.py:3229 msgid "Please configure your kindle e-mail address first..." msgstr "初めにKindleのメールアドレスを設定してください" -#: cps/web.py:2405 cps/web.py:2457 +#: cps/web.py:2435 cps/web.py:2487 msgid "Invalid shelf specified" msgstr "指定された本棚は無効です" -#: cps/web.py:2412 +#: cps/web.py:2442 #, python-format msgid "Sorry you are not allowed to add a book to the the shelf: %(shelfname)s" msgstr "申し訳ありませんが、あなたは %(shelfname)s に本を追加することが許可されていません" -#: cps/web.py:2420 +#: cps/web.py:2450 msgid "You are not allowed to edit public shelves" msgstr "みんなの本棚を編集することが許可されていません" -#: cps/web.py:2429 +#: cps/web.py:2459 #, python-format msgid "Book is already part of the shelf: %(shelfname)s" msgstr "この本は %(shelfname)s にすでに追加されています" -#: cps/web.py:2443 +#: cps/web.py:2473 #, python-format msgid "Book has been added to shelf: %(sname)s" msgstr "本を %(sname)s に追加しました" -#: cps/web.py:2462 +#: cps/web.py:2492 #, python-format msgid "You are not allowed to add a book to the the shelf: %(name)s" msgstr "%(name)s に本を追加することが許可されていません" -#: cps/web.py:2467 +#: cps/web.py:2497 msgid "User is not allowed to edit public shelves" msgstr "みんなの本棚を編集することが許可されていません" -#: cps/web.py:2485 +#: cps/web.py:2515 #, python-format msgid "Books are already part of the shelf: %(name)s" msgstr "これらの本は %(name)s にすでに追加されています" -#: cps/web.py:2499 +#: cps/web.py:2529 #, python-format msgid "Books have been added to shelf: %(sname)s" msgstr "本が %(sname)s に追加されました" -#: cps/web.py:2501 +#: cps/web.py:2531 #, python-format msgid "Could not add books to shelf: %(sname)s" msgstr "%(sname)s に本を追加できません" -#: cps/web.py:2538 +#: cps/web.py:2568 #, python-format msgid "Book has been removed from shelf: %(sname)s" msgstr "本が %(sname)s から削除されました" -#: cps/web.py:2544 +#: cps/web.py:2574 #, python-format msgid "Sorry you are not allowed to remove a book from this shelf: %(sname)s" msgstr "申し訳ありませんが、%(sname)s から本を削除することが許可されていません" -#: cps/web.py:2565 cps/web.py:2589 +#: cps/web.py:2595 cps/web.py:2619 #, python-format msgid "A shelf with the name '%(title)s' already exists." msgstr "'%(title)s'は既に存在します" -#: cps/web.py:2570 +#: cps/web.py:2600 #, python-format msgid "Shelf %(title)s created" msgstr "%(title)s を作成しました" -#: cps/web.py:2572 cps/web.py:2600 +#: cps/web.py:2602 cps/web.py:2630 msgid "There was an error" msgstr "エラーが発生しました" -#: cps/web.py:2573 cps/web.py:2575 +#: cps/web.py:2603 cps/web.py:2605 msgid "create a shelf" msgstr "本棚を作成する" -#: cps/web.py:2598 +#: cps/web.py:2628 #, python-format msgid "Shelf %(title)s changed" msgstr "%(title)s を変更しました" -#: cps/web.py:2601 cps/web.py:2603 +#: cps/web.py:2631 cps/web.py:2633 msgid "Edit a shelf" msgstr "本棚を編集する" -#: cps/web.py:2624 +#: cps/web.py:2654 #, python-format msgid "successfully deleted shelf %(name)s" msgstr "%(name)s を削除しました" -#: cps/web.py:2651 +#: cps/web.py:2681 #, python-format msgid "Shelf: '%(name)s'" msgstr "本棚: '%(name)s'" -#: cps/web.py:2654 +#: cps/web.py:2684 msgid "Error opening shelf. Shelf does not exist or is not accessible" msgstr "本棚を開けません。この本棚は存在しないかアクセスできません" -#: cps/web.py:2685 +#: cps/web.py:2715 #, python-format msgid "Change order of Shelf: '%(name)s'" msgstr "'%(name)s' 内の本の順番を変更する" -#: cps/web.py:2714 cps/web.py:3152 +#: cps/web.py:2744 cps/web.py:3182 msgid "E-mail is not from valid domain" msgstr "このメールは有効なドメインからのものではありません" -#: cps/web.py:2716 cps/web.py:2758 cps/web.py:2761 +#: cps/web.py:2746 cps/web.py:2788 cps/web.py:2791 #, python-format msgid "%(name)s's profile" msgstr "%(name)s のプロフィール" -#: cps/web.py:2756 +#: cps/web.py:2786 msgid "Found an existing account for this e-mail address." msgstr "このメールアドレスで登録されたアカウントがあります" -#: cps/web.py:2759 +#: cps/web.py:2789 msgid "Profile updated" msgstr "プロフィールを更新しました" -#: cps/web.py:2790 +#: cps/web.py:2820 msgid "Admin page" msgstr "管理者ページ" -#: cps/web.py:2875 cps/web.py:3055 +#: cps/web.py:2905 cps/web.py:3085 msgid "Calibre-Web configuration updated" msgstr "Calibre-Web の設定を更新しました" -#: cps/templates/admin.html:100 cps/web.py:2889 +#: cps/templates/admin.html:100 cps/web.py:2919 msgid "UI Configuration" msgstr "UI設定" -#: cps/web.py:2907 +#: cps/web.py:2937 msgid "Import of optional Google Drive requirements missing" msgstr "Googleドライブ用のOptional Requirementsがインストールされていません" -#: cps/web.py:2910 +#: cps/web.py:2940 msgid "client_secrets.json is missing or not readable" msgstr "client_secrets.json が存在しないか読み込めません" -#: cps/web.py:2915 cps/web.py:2944 +#: cps/web.py:2945 cps/web.py:2974 msgid "client_secrets.json is not configured for web application" msgstr "client_secrets.json がWebアプリ用に設定されていません" -#: cps/templates/admin.html:99 cps/web.py:2947 cps/web.py:2973 cps/web.py:2985 -#: cps/web.py:3030 cps/web.py:3045 cps/web.py:3064 cps/web.py:3072 -#: cps/web.py:3088 +#: cps/templates/admin.html:99 cps/web.py:2977 cps/web.py:3003 cps/web.py:3015 +#: cps/web.py:3060 cps/web.py:3075 cps/web.py:3094 cps/web.py:3102 +#: cps/web.py:3118 msgid "Basic Configuration" msgstr "基本設定" -#: cps/web.py:2970 +#: cps/web.py:3000 msgid "Keyfile location is not valid, please enter correct path" msgstr "キーファイルが無効です。正しいパスを入力してください" -#: cps/web.py:2982 +#: cps/web.py:3012 msgid "Certfile location is not valid, please enter correct path" msgstr "証明書が無効です。正しいパスを入力してください" -#: cps/web.py:3027 +#: cps/web.py:3057 msgid "Logfile location is not valid, please enter correct path" msgstr "ログファイルが無効です。正しいパスを入力してください" -#: cps/web.py:3068 +#: cps/web.py:3098 msgid "DB location is not valid, please enter correct path" msgstr "データベースが無効です。正しいパスを入力してください" -#: cps/templates/admin.html:33 cps/web.py:3148 cps/web.py:3154 cps/web.py:3170 +#: cps/templates/admin.html:33 cps/web.py:3178 cps/web.py:3184 cps/web.py:3200 msgid "Add new user" msgstr "新規ユーザ追加" -#: cps/web.py:3160 +#: cps/web.py:3190 #, python-format msgid "User '%(user)s' created" msgstr "ユーザ '%(user)s' を作成しました" -#: cps/web.py:3164 +#: cps/web.py:3194 msgid "Found an existing account for this e-mail address or nickname." msgstr "このメールアドレスかニックネームで登録されたアカウントが見つかりました" -#: cps/web.py:3194 +#: cps/web.py:3224 #, python-format msgid "Test e-mail successfully send to %(kindlemail)s" msgstr "テストメールが %(kindlemail)s に送信されました" -#: cps/web.py:3197 +#: cps/web.py:3227 #, python-format msgid "There was an error sending the Test e-mail: %(res)s" msgstr "テストメールを %(res)s に送信中にエラーが発生しました" -#: cps/web.py:3201 +#: cps/web.py:3231 msgid "E-mail server settings updated" msgstr "メールサーバの設定を更新しました" -#: cps/web.py:3202 +#: cps/web.py:3232 msgid "Edit e-mail server settings" msgstr "メールサーバの設定を編集" -#: cps/web.py:3227 +#: cps/web.py:3257 #, python-format msgid "User '%(nick)s' deleted" msgstr "ユーザ '%(nick)s' を削除しました" -#: cps/web.py:3340 +#: cps/web.py:3370 #, python-format msgid "User '%(nick)s' updated" msgstr "ユーザ '%(nick)s' を更新しました" -#: cps/web.py:3343 +#: cps/web.py:3373 msgid "An unknown error occured." msgstr "不明なエラーが発生しました。" -#: cps/web.py:3345 +#: cps/web.py:3375 #, python-format msgid "Edit User %(nick)s" msgstr "%(nick)s を編集" -#: cps/web.py:3362 +#: cps/web.py:3392 #, python-format msgid "Password for user %(user)s reset" msgstr "%(user)s 用のパスワードをリセット" -#: cps/web.py:3376 cps/web.py:3582 +#: cps/web.py:3406 cps/web.py:3598 msgid "Error opening eBook. File does not exist or file is not accessible" msgstr "電子書籍を開けません。ファイルが存在しないかアクセスできません" -#: cps/web.py:3404 +#: cps/web.py:3434 msgid "edit metadata" msgstr "メタデータを編集" -#: cps/web.py:3497 cps/web.py:3743 +#: cps/web.py:3527 cps/web.py:3760 #, python-format msgid "File extension '%(ext)s' is not allowed to be uploaded to this server" msgstr "ファイル拡張子 '%(ext)s' をこのサーバにアップロードすることは許可されていません" -#: cps/web.py:3501 cps/web.py:3746 +#: cps/web.py:3531 cps/web.py:3763 msgid "File to be uploaded must have an extension" msgstr "アップロードするファイルには拡張子が必要です" -#: cps/web.py:3513 cps/web.py:3765 +#: cps/web.py:3543 cps/web.py:3782 #, python-format msgid "Failed to create path %(path)s (Permission denied)." msgstr "%(path)s の作成に失敗しました (Permission denied)。" -#: cps/web.py:3518 +#: cps/web.py:3548 #, python-format msgid "Failed to store file %(file)s." msgstr "%(file)s を保存できません。" -#: cps/web.py:3535 +#: cps/web.py:3565 #, python-format msgid "File format %(ext)s added to %(book)s" msgstr "ファイル形式 %(ext)s が %(book)s に追加されました" -#: cps/web.py:3553 -#, python-format -msgid "Failed to create path for cover %(path)s (Permission denied)." -msgstr "カバー画像 %(path)s の作成に失敗しました (Permission denied)。" - -#: cps/web.py:3561 -#, python-format -msgid "Failed to store cover-file %(cover)s." -msgstr "カバー画像 %(cover)s の保存に失敗しました。" - -#: cps/web.py:3564 -msgid "Cover-file is not a valid image file" -msgstr "カバー画像が無効な画像ファイルです" +#: cps/web.py:3579 cps/web.py:3652 +msgid "Cover is not a supported imageformat (jpg/png/webp), can't save" +msgstr "" -#: cps/web.py:3594 cps/web.py:3603 +#: cps/web.py:3611 cps/web.py:3620 msgid "unknown" msgstr "不明" -#: cps/web.py:3635 -msgid "Cover is not a jpg file, can't save" -msgstr "カバー画像がjpgファイルでないため、保存できません" - -#: cps/web.py:3683 +#: cps/web.py:3700 #, python-format msgid "%(langname)s is not a valid language" msgstr "%(langname)s は有効な言語ではありません" -#: cps/web.py:3714 +#: cps/web.py:3731 msgid "Metadata successfully updated" msgstr "メタデータを更新しました" -#: cps/web.py:3723 +#: cps/web.py:3740 msgid "Error editing book, please check logfile for details" msgstr "本の編集でエラーが発生しました。詳細はログファイルを確認してください" -#: cps/web.py:3769 +#: cps/web.py:3786 #, python-format msgid "Failed to store file %(file)s (Permission denied)." msgstr "ファイル %(file)s の保存に失敗しました (Permission denied)。" -#: cps/web.py:3774 +#: cps/web.py:3791 #, python-format msgid "Failed to delete file %(file)s (Permission denied)." msgstr "ファイル %(file)s の削除に失敗しました (Permission denied)。" -#: cps/web.py:3857 +#: cps/web.py:3873 #, python-format msgid "File %(title)s" msgstr "ファイル %(title)s" -#: cps/web.py:3886 +#: cps/web.py:3902 msgid "Source or destination format for conversion missing" msgstr "変換元の形式または変換後の形式が指定されていません" -#: cps/web.py:3896 +#: cps/web.py:3912 #, python-format msgid "Book successfully queued for converting to %(book_format)s" msgstr "本の %(book_format)s への変換がキューに追加されました" -#: cps/web.py:3900 +#: cps/web.py:3916 #, python-format msgid "There was an error converting this book: %(res)s" msgstr "この本の変換中にエラーが発生しました: %(res)s" @@ -1288,7 +1279,7 @@ msgstr "ランダムで表示する本の冊数" msgid "No. of authors to show before hiding (0=disable hiding)" msgstr "非表示にする前に表示する著者の人数 (0の場合は常に表示)" -#: cps/templates/config_view_edit.html:35 cps/templates/readcbr.html:108 +#: cps/templates/config_view_edit.html:35 cps/templates/readcbr.html:118 msgid "Theme" msgstr "テーマ" @@ -1602,7 +1593,7 @@ msgid "Advanced Search" msgstr "詳細検索" #: cps/templates/layout.html:76 cps/templates/read.html:71 -#: cps/templates/readcbr.html:79 cps/templates/readcbr.html:103 +#: cps/templates/readcbr.html:89 cps/templates/readcbr.html:113 msgid "Settings" msgstr "設定" @@ -1721,99 +1712,99 @@ msgstr "Calibre-Web 電子書籍カタログ" msgid "Reflow text when sidebars are open." msgstr "" -#: cps/templates/readcbr.html:84 +#: cps/templates/readcbr.html:94 msgid "Keyboard Shortcuts" msgstr "キーボードショートカット" -#: cps/templates/readcbr.html:87 +#: cps/templates/readcbr.html:97 msgid "Previous Page" msgstr "前のページ" -#: cps/templates/readcbr.html:88 +#: cps/templates/readcbr.html:98 msgid "Next Page" msgstr "次のページ" -#: cps/templates/readcbr.html:89 +#: cps/templates/readcbr.html:99 msgid "Scale to Best" msgstr "最適なサイズにする" -#: cps/templates/readcbr.html:90 +#: cps/templates/readcbr.html:100 msgid "Scale to Width" msgstr "横に合わせる" -#: cps/templates/readcbr.html:91 +#: cps/templates/readcbr.html:101 msgid "Scale to Height" msgstr "縦に合わせる" -#: cps/templates/readcbr.html:92 +#: cps/templates/readcbr.html:102 msgid "Scale to Native" msgstr "オリジナルのサイズにする" -#: cps/templates/readcbr.html:93 +#: cps/templates/readcbr.html:103 msgid "Rotate Right" msgstr "右に回転する" -#: cps/templates/readcbr.html:94 +#: cps/templates/readcbr.html:104 msgid "Rotate Left" msgstr "左に回転する" -#: cps/templates/readcbr.html:95 +#: cps/templates/readcbr.html:105 msgid "Flip Image" msgstr "画像を反転する" -#: cps/templates/readcbr.html:111 +#: cps/templates/readcbr.html:121 msgid "Light" msgstr "ライト" -#: cps/templates/readcbr.html:112 +#: cps/templates/readcbr.html:122 msgid "Dark" msgstr "ダーク" -#: cps/templates/readcbr.html:117 +#: cps/templates/readcbr.html:127 msgid "Scale" msgstr "サイズ" -#: cps/templates/readcbr.html:120 +#: cps/templates/readcbr.html:130 msgid "Best" msgstr "最適" -#: cps/templates/readcbr.html:121 +#: cps/templates/readcbr.html:131 msgid "Width" msgstr "横に合わせる" -#: cps/templates/readcbr.html:122 +#: cps/templates/readcbr.html:132 msgid "Height" msgstr "縦に合わせる" -#: cps/templates/readcbr.html:123 +#: cps/templates/readcbr.html:133 msgid "Native" msgstr "オリジナル" -#: cps/templates/readcbr.html:128 +#: cps/templates/readcbr.html:138 msgid "Rotate" msgstr "回転" -#: cps/templates/readcbr.html:139 +#: cps/templates/readcbr.html:149 msgid "Flip" msgstr "反転" -#: cps/templates/readcbr.html:142 +#: cps/templates/readcbr.html:152 msgid "Horizontal" msgstr "水平方向" -#: cps/templates/readcbr.html:143 +#: cps/templates/readcbr.html:153 msgid "Vertical" msgstr "垂直方向" -#: cps/templates/readcbr.html:148 +#: cps/templates/readcbr.html:158 msgid "Direction" msgstr "読む方向" -#: cps/templates/readcbr.html:151 +#: cps/templates/readcbr.html:161 msgid "Left to Right" msgstr "左から右" -#: cps/templates/readcbr.html:152 +#: cps/templates/readcbr.html:162 msgid "Right to Left" msgstr "右から左" @@ -1821,10 +1812,6 @@ msgstr "右から左" msgid "PDF.js viewer" msgstr "PDF.js ビューア" -#: cps/templates/readpdf.html:418 -msgid "Preparing document for printing..." -msgstr "印刷用にドキュメントを準備しています..." - #: cps/templates/readtxt.html:6 msgid "Basic txt Reader" msgstr "テキストリーダ" @@ -2016,3 +2003,19 @@ msgstr "このユーザを削除" #: cps/templates/user_edit.html:156 msgid "Recent Downloads" msgstr "最近のダウンロード" + +#~ msgid "Failed to create path for cover %(path)s (Permission denied)." +#~ msgstr "カバー画像 %(path)s の作成に失敗しました (Permission denied)。" + +#~ msgid "Failed to store cover-file %(cover)s." +#~ msgstr "カバー画像 %(cover)s の保存に失敗しました。" + +#~ msgid "Cover-file is not a valid image file" +#~ msgstr "カバー画像が無効な画像ファイルです" + +#~ msgid "Cover is not a jpg file, can't save" +#~ msgstr "カバー画像がjpgファイルでないため、保存できません" + +#~ msgid "Preparing document for printing..." +#~ msgstr "印刷用にドキュメントを準備しています..." + diff --git a/cps/translations/km/LC_MESSAGES/messages.mo b/cps/translations/km/LC_MESSAGES/messages.mo index 80bb00d4e95f4002cef5de3cfa94ced575ef6276..ad42e464c648bc98659d621257119c26dfeede7d 100644 GIT binary patch delta 15182 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 CzW1vD delta 14851 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 %(rating)s" msgstr "ការវាយតម្លៃ >= %(rating)s" -#: cps/web.py:2022 cps/web.py:2031 +#: cps/web.py:2042 cps/web.py:2051 msgid "search" msgstr "ស្វែងរក" #: cps/templates/index.xml:47 cps/templates/index.xml:51 -#: cps/templates/layout.html:148 cps/web.py:2099 +#: cps/templates/layout.html:148 cps/web.py:2122 msgid "Read Books" msgstr "សៀវភៅដែលបានអានរួច" #: cps/templates/index.xml:55 cps/templates/index.xml:59 -#: cps/templates/layout.html:150 cps/web.py:2102 +#: cps/templates/layout.html:150 cps/web.py:2125 msgid "Unread Books" msgstr "សៀវភៅដែលមិនទាន់បានអាន" -#: cps/web.py:2150 cps/web.py:2152 cps/web.py:2154 cps/web.py:2166 +#: cps/web.py:2177 cps/web.py:2179 cps/web.py:2181 cps/web.py:2193 msgid "Read a Book" msgstr "អានសៀវភៅ" -#: cps/web.py:2225 cps/web.py:3146 +#: cps/web.py:2205 +msgid "Error opening eBook. Fileformat is not supported." +msgstr "" + +#: cps/web.py:2255 cps/web.py:3176 msgid "Please fill out all fields!" msgstr "សូមបំពេញចន្លោះទាំងអស់!" -#: cps/web.py:2226 cps/web.py:2248 cps/web.py:2252 cps/web.py:2257 -#: cps/web.py:2259 +#: cps/web.py:2256 cps/web.py:2278 cps/web.py:2282 cps/web.py:2287 +#: cps/web.py:2289 msgid "register" msgstr "ចុះឈ្មោះ" -#: cps/web.py:2247 cps/web.py:3365 +#: cps/web.py:2277 cps/web.py:3395 msgid "An unknown error occurred. Please try again later." msgstr "" -#: cps/web.py:2250 +#: cps/web.py:2280 msgid "Your e-mail is not allowed to register" msgstr "" -#: cps/web.py:2253 +#: cps/web.py:2283 msgid "Confirmation e-mail was send to your e-mail account." msgstr "" -#: cps/web.py:2256 +#: cps/web.py:2286 msgid "This username or e-mail address is already in use." msgstr "" -#: cps/web.py:2273 cps/web.py:2369 +#: cps/web.py:2303 cps/web.py:2399 #, python-format msgid "you are now logged in as: '%(nickname)s'" msgstr "ឥឡូវអ្នកបានចូលដោយមានឈ្មោះថា៖ ‘%(nickname)s’" -#: cps/web.py:2278 +#: cps/web.py:2308 msgid "Wrong Username or Password" msgstr "ខុសឈ្មោះអ្នកប្រើប្រាស់ ឬលេខសម្ងាត់" -#: cps/web.py:2284 cps/web.py:2305 +#: cps/web.py:2314 cps/web.py:2335 msgid "login" msgstr "ចូលប្រើ" -#: cps/web.py:2317 cps/web.py:2348 +#: cps/web.py:2347 cps/web.py:2378 msgid "Token not found" msgstr "រកមិនឃើញវត្ថុតាង" -#: cps/web.py:2325 cps/web.py:2356 +#: cps/web.py:2355 cps/web.py:2386 msgid "Token has expired" msgstr "វត្ថុតាងហួសពេលកំណត់" -#: cps/web.py:2333 +#: cps/web.py:2363 msgid "Success! Please return to your device" msgstr "ជោគជ័យ! សូមវិលមកឧបករណ៍អ្នកវិញ" -#: cps/web.py:2383 +#: cps/web.py:2413 msgid "Please configure the SMTP mail settings first..." msgstr "សូមកំណត់អ៊ីមែល SMTP ជាមុនសិន" -#: cps/web.py:2388 +#: cps/web.py:2418 #, python-format msgid "Book successfully queued for sending to %(kindlemail)s" msgstr "សៀវភៅបានចូលជួរសម្រាប់ផ្ញើទៅ %(kindlemail)s ដោយជោគជ័យ" -#: cps/web.py:2392 +#: cps/web.py:2422 #, python-format msgid "There was an error sending this book: %(res)s" msgstr "មានបញ្ហានៅពេលផ្ញើសៀវភៅនេះ៖ %(res)s" -#: cps/web.py:2394 cps/web.py:3199 +#: cps/web.py:2424 cps/web.py:3229 msgid "Please configure your kindle e-mail address first..." msgstr "" -#: cps/web.py:2405 cps/web.py:2457 +#: cps/web.py:2435 cps/web.py:2487 msgid "Invalid shelf specified" msgstr "" -#: cps/web.py:2412 +#: cps/web.py:2442 #, python-format msgid "Sorry you are not allowed to add a book to the the shelf: %(shelfname)s" msgstr "" -#: cps/web.py:2420 +#: cps/web.py:2450 msgid "You are not allowed to edit public shelves" msgstr "" -#: cps/web.py:2429 +#: cps/web.py:2459 #, python-format msgid "Book is already part of the shelf: %(shelfname)s" msgstr "" -#: cps/web.py:2443 +#: cps/web.py:2473 #, python-format msgid "Book has been added to shelf: %(sname)s" msgstr "សៀវភៅត្រូវបានបន្ថែមទៅធ្នើ៖ %(sname)s" -#: cps/web.py:2462 +#: cps/web.py:2492 #, python-format msgid "You are not allowed to add a book to the the shelf: %(name)s" msgstr "" -#: cps/web.py:2467 +#: cps/web.py:2497 msgid "User is not allowed to edit public shelves" msgstr "" -#: cps/web.py:2485 +#: cps/web.py:2515 #, python-format msgid "Books are already part of the shelf: %(name)s" msgstr "" -#: cps/web.py:2499 +#: cps/web.py:2529 #, python-format msgid "Books have been added to shelf: %(sname)s" msgstr "" -#: cps/web.py:2501 +#: cps/web.py:2531 #, python-format msgid "Could not add books to shelf: %(sname)s" msgstr "" -#: cps/web.py:2538 +#: cps/web.py:2568 #, python-format msgid "Book has been removed from shelf: %(sname)s" msgstr "សៀវភៅត្រូវបានដកចេញពីធ្នើ៖ %(sname)s" -#: cps/web.py:2544 +#: cps/web.py:2574 #, python-format msgid "Sorry you are not allowed to remove a book from this shelf: %(sname)s" msgstr "សូមអភ័យទោស អ្នកមិនមានសិទ្ធិដកសៀវភៅចេញពីធ្នើនេះទេ៖ %(sname)s" -#: cps/web.py:2565 cps/web.py:2589 +#: cps/web.py:2595 cps/web.py:2619 #, python-format msgid "A shelf with the name '%(title)s' already exists." msgstr "មានធ្នើដែលមានឈ្មោះ ‘%(title)s’ រួចហើយ។" -#: cps/web.py:2570 +#: cps/web.py:2600 #, python-format msgid "Shelf %(title)s created" msgstr "ធ្នើឈ្មោះ %(title)s ត្រូវបានបង្កើត" -#: cps/web.py:2572 cps/web.py:2600 +#: cps/web.py:2602 cps/web.py:2630 msgid "There was an error" msgstr "មានបញ្ហា" -#: cps/web.py:2573 cps/web.py:2575 +#: cps/web.py:2603 cps/web.py:2605 msgid "create a shelf" msgstr "បង្កើតធ្នើ" -#: cps/web.py:2598 +#: cps/web.py:2628 #, python-format msgid "Shelf %(title)s changed" msgstr "ធ្នើឈ្មោះ %(title)s ត្រូវបានប្តូរ" -#: cps/web.py:2601 cps/web.py:2603 +#: cps/web.py:2631 cps/web.py:2633 msgid "Edit a shelf" msgstr "កែប្រែធ្នើ" -#: cps/web.py:2624 +#: cps/web.py:2654 #, python-format msgid "successfully deleted shelf %(name)s" msgstr "បានបង្កើតធ្នើឈ្មោះ %(name)s ដោយជោគជ័យ" -#: cps/web.py:2651 +#: cps/web.py:2681 #, python-format msgid "Shelf: '%(name)s'" msgstr "ធ្នើ៖ ‘%(name)s’" -#: cps/web.py:2654 +#: cps/web.py:2684 msgid "Error opening shelf. Shelf does not exist or is not accessible" msgstr "មានបញ្ហាពេលបើកធ្នើ។ ពុំមានធ្នើ ឬមិនអាចបើកបាន" -#: cps/web.py:2685 +#: cps/web.py:2715 #, python-format msgid "Change order of Shelf: '%(name)s'" msgstr "ប្តូរលំដាប់ធ្នើ៖ ‘%(name)s’" -#: cps/web.py:2714 cps/web.py:3152 +#: cps/web.py:2744 cps/web.py:3182 msgid "E-mail is not from valid domain" msgstr "" -#: cps/web.py:2716 cps/web.py:2758 cps/web.py:2761 +#: cps/web.py:2746 cps/web.py:2788 cps/web.py:2791 #, python-format msgid "%(name)s's profile" msgstr "ព័ត៌មានសង្ខេបរបស់ %(name)s" -#: cps/web.py:2756 +#: cps/web.py:2786 msgid "Found an existing account for this e-mail address." msgstr "" -#: cps/web.py:2759 +#: cps/web.py:2789 msgid "Profile updated" msgstr "ព័ត៌មានសង្ខេបបានកែប្រែ" -#: cps/web.py:2790 +#: cps/web.py:2820 msgid "Admin page" msgstr "ទំព័ររដ្ឋបាល" -#: cps/web.py:2875 cps/web.py:3055 +#: cps/web.py:2905 cps/web.py:3085 msgid "Calibre-Web configuration updated" msgstr "" -#: cps/templates/admin.html:100 cps/web.py:2889 +#: cps/templates/admin.html:100 cps/web.py:2919 msgid "UI Configuration" msgstr "ការកំណត់ផ្ទាំងប្រើប្រាស់" -#: cps/web.py:2907 +#: cps/web.py:2937 msgid "Import of optional Google Drive requirements missing" msgstr "ខ្វះការនាំចូលតម្រូវការបន្ថែមរបស់ Google Drive" -#: cps/web.py:2910 +#: cps/web.py:2940 msgid "client_secrets.json is missing or not readable" msgstr "មិនមានឯកសារ client_secrets.json ឬមិនអាចបើកបាន" -#: cps/web.py:2915 cps/web.py:2944 +#: cps/web.py:2945 cps/web.py:2974 msgid "client_secrets.json is not configured for web application" msgstr "ឯកសារ client_secrets.json មិនទាន់បានកំណត់សម្រាប់កម្មវិធីវែប" -#: cps/templates/admin.html:99 cps/web.py:2947 cps/web.py:2973 cps/web.py:2985 -#: cps/web.py:3030 cps/web.py:3045 cps/web.py:3064 cps/web.py:3072 -#: cps/web.py:3088 +#: cps/templates/admin.html:99 cps/web.py:2977 cps/web.py:3003 cps/web.py:3015 +#: cps/web.py:3060 cps/web.py:3075 cps/web.py:3094 cps/web.py:3102 +#: cps/web.py:3118 msgid "Basic Configuration" msgstr "ការកំណត់សាមញ្ញ" -#: cps/web.py:2970 +#: cps/web.py:3000 msgid "Keyfile location is not valid, please enter correct path" msgstr "ទីតាំងរបស់ keyfile មិនត្រឹមត្រូវ សូមបញ្ចូលទីតាំងត្រឹមត្រូវ" -#: cps/web.py:2982 +#: cps/web.py:3012 msgid "Certfile location is not valid, please enter correct path" msgstr "ទីតាំងរបស់ certfile មិនត្រឹមត្រូវ សូមបញ្ចូលទីតាំងត្រឹមត្រូវ" -#: cps/web.py:3027 +#: cps/web.py:3057 msgid "Logfile location is not valid, please enter correct path" msgstr "ទីតាំងរបស់ logfile មិនត្រឹមត្រូវ សូមបញ្ចូលទីតាំងត្រឹមត្រូវ" -#: cps/web.py:3068 +#: cps/web.py:3098 msgid "DB location is not valid, please enter correct path" msgstr "ទីតាំងរបស់ database មិនត្រឹមត្រូវ សូមបញ្ចូលទីតាំងត្រឹមត្រូវ" -#: cps/templates/admin.html:33 cps/web.py:3148 cps/web.py:3154 cps/web.py:3170 +#: cps/templates/admin.html:33 cps/web.py:3178 cps/web.py:3184 cps/web.py:3200 msgid "Add new user" msgstr "បន្ថែមអ្នកប្រើប្រាស់ថ្មី" -#: cps/web.py:3160 +#: cps/web.py:3190 #, python-format msgid "User '%(user)s' created" msgstr "បានបង្កើតអ្នកប្រើប្រាស់ ‘%(user)s’" -#: cps/web.py:3164 +#: cps/web.py:3194 msgid "Found an existing account for this e-mail address or nickname." msgstr "" -#: cps/web.py:3194 +#: cps/web.py:3224 #, python-format msgid "Test e-mail successfully send to %(kindlemail)s" msgstr "" -#: cps/web.py:3197 +#: cps/web.py:3227 #, python-format msgid "There was an error sending the Test e-mail: %(res)s" msgstr "" -#: cps/web.py:3201 +#: cps/web.py:3231 msgid "E-mail server settings updated" msgstr "" -#: cps/web.py:3202 +#: cps/web.py:3232 msgid "Edit e-mail server settings" msgstr "" -#: cps/web.py:3227 +#: cps/web.py:3257 #, python-format msgid "User '%(nick)s' deleted" msgstr "អ្នកប្រើប្រាស់ ‘%(nick)s’ ត្រូវបានលុប" -#: cps/web.py:3340 +#: cps/web.py:3370 #, python-format msgid "User '%(nick)s' updated" msgstr "អ្នកប្រើប្រាស់ ‘%(nick)s’ ត្រូវបានកែប្រែ" -#: cps/web.py:3343 +#: cps/web.py:3373 msgid "An unknown error occured." msgstr "បញ្ហាដែលមិនដឹងបានកើតឡើង។" -#: cps/web.py:3345 +#: cps/web.py:3375 #, python-format msgid "Edit User %(nick)s" msgstr "កែប្រែអ្នកប្រើប្រាស់ %(nick)s" -#: cps/web.py:3362 +#: cps/web.py:3392 #, python-format msgid "Password for user %(user)s reset" msgstr "" -#: cps/web.py:3376 cps/web.py:3582 +#: cps/web.py:3406 cps/web.py:3598 msgid "Error opening eBook. File does not exist or file is not accessible" msgstr "មានបញ្ហាពេលបើកឯកសារ eBook ។ ពុំមានឯកសារ ឬឯកសារនេះមិនអាចបើកបាន" -#: cps/web.py:3404 +#: cps/web.py:3434 msgid "edit metadata" msgstr "កែប្រែទិន្នន័យមេតា" -#: cps/web.py:3497 cps/web.py:3743 +#: cps/web.py:3527 cps/web.py:3760 #, python-format msgid "File extension '%(ext)s' is not allowed to be uploaded to this server" msgstr "ឯកសារប្រភេទ '%(ext)s' មិនត្រូវបានអនុញ្ញាតឲអាប់ឡូដទៅម៉ាស៊ីន server នេះទេ" -#: cps/web.py:3501 cps/web.py:3746 +#: cps/web.py:3531 cps/web.py:3763 msgid "File to be uploaded must have an extension" msgstr "ឯកសារដែលត្រូវអាប់ឡូដត្រូវមានកន្ទុយឯកសារ" -#: cps/web.py:3513 cps/web.py:3765 +#: cps/web.py:3543 cps/web.py:3782 #, python-format msgid "Failed to create path %(path)s (Permission denied)." msgstr "មិនអាចបង្កើតទីតាំង %(path)s (ពុំមានសិទ្ធិ)។" -#: cps/web.py:3518 +#: cps/web.py:3548 #, python-format msgid "Failed to store file %(file)s." msgstr "មិនអាចរក្សាទុកឯកសារ %(file)s ។" -#: cps/web.py:3535 +#: cps/web.py:3565 #, python-format msgid "File format %(ext)s added to %(book)s" msgstr "ឯកសារទម្រង់ %(ext)s ត្រូវបានបន្ថែមទៅ %(book)s" -#: cps/web.py:3553 -#, python-format -msgid "Failed to create path for cover %(path)s (Permission denied)." +#: cps/web.py:3579 cps/web.py:3652 +msgid "Cover is not a supported imageformat (jpg/png/webp), can't save" msgstr "" -#: cps/web.py:3561 -#, python-format -msgid "Failed to store cover-file %(cover)s." -msgstr "" - -#: cps/web.py:3564 -msgid "Cover-file is not a valid image file" -msgstr "" - -#: cps/web.py:3594 cps/web.py:3603 +#: cps/web.py:3611 cps/web.py:3620 msgid "unknown" msgstr "មិនដឹង" -#: cps/web.py:3635 -msgid "Cover is not a jpg file, can't save" -msgstr "គម្របមិនមែនជាឯកសារ JPG មិនអាចរក្សាទុក" - -#: cps/web.py:3683 +#: cps/web.py:3700 #, python-format msgid "%(langname)s is not a valid language" msgstr "" -#: cps/web.py:3714 +#: cps/web.py:3731 msgid "Metadata successfully updated" msgstr "" -#: cps/web.py:3723 +#: cps/web.py:3740 msgid "Error editing book, please check logfile for details" msgstr "មានបញ្ហាពេលកែប្រែសៀវភៅ សូមពិនិត្យមើល logfile សម្រាប់ព័ត៌មានបន្ថែម" -#: cps/web.py:3769 +#: cps/web.py:3786 #, python-format msgid "Failed to store file %(file)s (Permission denied)." msgstr "មិនអាចរក្សាទុកឯកសារ %(file)s (មិនមានសិទ្ធិ)។" -#: cps/web.py:3774 +#: cps/web.py:3791 #, python-format msgid "Failed to delete file %(file)s (Permission denied)." msgstr "មិនអាចលុបឯកសារ %(file)s (មិនមានសិទ្ធិ)។" -#: cps/web.py:3857 +#: cps/web.py:3873 #, python-format msgid "File %(title)s" msgstr "" -#: cps/web.py:3886 +#: cps/web.py:3902 msgid "Source or destination format for conversion missing" msgstr "" -#: cps/web.py:3896 +#: cps/web.py:3912 #, python-format msgid "Book successfully queued for converting to %(book_format)s" msgstr "" -#: cps/web.py:3900 +#: cps/web.py:3916 #, python-format msgid "There was an error converting this book: %(res)s" msgstr "" @@ -1289,7 +1280,7 @@ msgstr "ចំនួនសៀវភៅចៃដន្យដើម្បីបង msgid "No. of authors to show before hiding (0=disable hiding)" msgstr "" -#: cps/templates/config_view_edit.html:35 cps/templates/readcbr.html:108 +#: cps/templates/config_view_edit.html:35 cps/templates/readcbr.html:118 msgid "Theme" msgstr "ការតុបតែង" @@ -1603,7 +1594,7 @@ msgid "Advanced Search" msgstr "ស្វែងរកកម្រិតខ្ពស់" #: cps/templates/layout.html:76 cps/templates/read.html:71 -#: cps/templates/readcbr.html:79 cps/templates/readcbr.html:103 +#: cps/templates/readcbr.html:89 cps/templates/readcbr.html:113 msgid "Settings" msgstr "ការកំណត់" @@ -1722,98 +1713,106 @@ msgstr "" msgid "Reflow text when sidebars are open." msgstr "សេរេអត្ថបទនៅពេលបើកផ្ទាំងចំហៀង។" -#: cps/templates/readcbr.html:84 +#: cps/templates/readcbr.html:94 msgid "Keyboard Shortcuts" msgstr "" -#: cps/templates/readcbr.html:87 +#: cps/templates/readcbr.html:97 msgid "Previous Page" msgstr "" -#: cps/templates/readcbr.html:88 +#: cps/templates/readcbr.html:98 msgid "Next Page" msgstr "" -#: cps/templates/readcbr.html:89 +#: cps/templates/readcbr.html:99 msgid "Scale to Best" msgstr "" -#: cps/templates/readcbr.html:90 +#: cps/templates/readcbr.html:100 msgid "Scale to Width" msgstr "" -#: cps/templates/readcbr.html:91 +#: cps/templates/readcbr.html:101 msgid "Scale to Height" msgstr "" -#: cps/templates/readcbr.html:92 +#: cps/templates/readcbr.html:102 msgid "Scale to Native" msgstr "" -#: cps/templates/readcbr.html:93 +#: cps/templates/readcbr.html:103 msgid "Rotate Right" msgstr "" -#: cps/templates/readcbr.html:94 +#: cps/templates/readcbr.html:104 msgid "Rotate Left" msgstr "" -#: cps/templates/readcbr.html:95 +#: cps/templates/readcbr.html:105 msgid "Flip Image" msgstr "" -#: cps/templates/readcbr.html:111 +#: cps/templates/readcbr.html:121 msgid "Light" msgstr "" -#: cps/templates/readcbr.html:112 +#: cps/templates/readcbr.html:122 msgid "Dark" msgstr "" -#: cps/templates/readcbr.html:117 +#: cps/templates/readcbr.html:127 msgid "Scale" msgstr "" -#: cps/templates/readcbr.html:120 +#: cps/templates/readcbr.html:130 msgid "Best" msgstr "" -#: cps/templates/readcbr.html:121 +#: cps/templates/readcbr.html:131 msgid "Width" msgstr "" -#: cps/templates/readcbr.html:122 +#: cps/templates/readcbr.html:132 msgid "Height" msgstr "" -#: cps/templates/readcbr.html:123 +#: cps/templates/readcbr.html:133 msgid "Native" msgstr "" -#: cps/templates/readcbr.html:128 +#: cps/templates/readcbr.html:138 msgid "Rotate" msgstr "" -#: cps/templates/readcbr.html:139 +#: cps/templates/readcbr.html:149 msgid "Flip" msgstr "" -#: cps/templates/readcbr.html:142 +#: cps/templates/readcbr.html:152 msgid "Horizontal" msgstr "" -#: cps/templates/readcbr.html:143 +#: cps/templates/readcbr.html:153 msgid "Vertical" msgstr "" +#: cps/templates/readcbr.html:158 +msgid "Direction" +msgstr "" + +#: cps/templates/readcbr.html:161 +msgid "Left to Right" +msgstr "" + +#: cps/templates/readcbr.html:162 +msgid "Right to Left" +msgstr "" + #: cps/templates/readpdf.html:29 msgid "PDF.js viewer" msgstr "កម្មវិធីមើល PDF.js" -#: cps/templates/readpdf.html:418 -msgid "Preparing document for printing..." -msgstr "" - #: cps/templates/readtxt.html:6 msgid "Basic txt Reader" msgstr "កម្មវិធីមើល txt សាមញ្ញ" @@ -2087,3 +2086,18 @@ msgstr "ការទាញយកថ្មីៗ" #~ msgid "A new update is available. Click on the button below to update to version: %(version)s" #~ msgstr "" +#~ msgid "Failed to create path for cover %(path)s (Permission denied)." +#~ msgstr "" + +#~ msgid "Failed to store cover-file %(cover)s." +#~ msgstr "" + +#~ msgid "Cover-file is not a valid image file" +#~ msgstr "" + +#~ msgid "Cover is not a jpg file, can't save" +#~ msgstr "គម្របមិនមែនជាឯកសារ JPG មិនអាចរក្សាទុក" + +#~ msgid "Preparing document for printing..." +#~ msgstr "" + diff --git a/cps/translations/nl/LC_MESSAGES/messages.mo b/cps/translations/nl/LC_MESSAGES/messages.mo index 364caf4a0d0f9be9421e9442eaf51bbb14222b3f..6aa3e5f080fbac12572ad79e71031983e17628d4 100644 GIT binary patch delta 15122 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 delta 14800 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 I?DH@DA5kzSB>(^b diff --git a/cps/translations/nl/LC_MESSAGES/messages.po b/cps/translations/nl/LC_MESSAGES/messages.po index b62e0cd8..25f0d60c 100644 --- a/cps/translations/nl/LC_MESSAGES/messages.po +++ b/cps/translations/nl/LC_MESSAGES/messages.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Calibre-Web dutch translation by Ed Driesen (GPL V3)\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2019-03-10 08:03+0100\n" +"POT-Creation-Date: 2019-05-08 20:23+0200\n" "PO-Revision-Date: 2018-12-09 15:07+0100\n" "Last-Translator: \n" "Language: nl\n" @@ -19,8 +19,9 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.6.0\n" -#: cps/book_formats.py:152 cps/book_formats.py:153 cps/book_formats.py:157 -#: cps/book_formats.py:161 cps/converter.py:29 cps/converter.py:45 +#: cps/book_formats.py:199 cps/book_formats.py:200 cps/book_formats.py:204 +#: cps/book_formats.py:208 cps/book_formats.py:212 cps/converter.py:29 +#: cps/converter.py:45 msgid "not installed" msgstr "niet geïnstalleerd" @@ -32,133 +33,133 @@ msgstr "Rechten om uit te voeren ontbreken" msgid "not configured" msgstr "Niet geconfigureerd" -#: cps/helper.py:72 +#: cps/helper.py:79 #, python-format msgid "%(format)s format not found for book id: %(book)d" msgstr "%(format)s formaat niet gevonden voor boek met id: %(book)d" -#: cps/helper.py:84 +#: cps/helper.py:91 #, python-format msgid "%(format)s not found on Google Drive: %(fn)s" msgstr "%(format)s niet gevonden op Google Drive: %(fn)s" -#: cps/helper.py:91 cps/helper.py:199 cps/templates/detail.html:45 +#: cps/helper.py:98 cps/helper.py:204 cps/templates/detail.html:45 #: cps/templates/detail.html:49 msgid "Send to Kindle" msgstr "Stuur naar Kindle" -#: cps/helper.py:92 cps/helper.py:110 cps/helper.py:201 +#: cps/helper.py:99 cps/helper.py:117 cps/helper.py:206 msgid "This e-mail has been sent via Calibre-Web." msgstr "Deze email werd verzonden via Calibre-Web." -#: cps/helper.py:103 +#: cps/helper.py:110 #, python-format msgid "%(format)s not found: %(fn)s" msgstr "%(format)s niet gevonden %(fn)s" -#: cps/helper.py:108 +#: cps/helper.py:115 msgid "Calibre-Web test e-mail" msgstr "Calibre-Web test email" -#: cps/helper.py:109 +#: cps/helper.py:116 msgid "Test e-mail" msgstr "Test email" -#: cps/helper.py:125 +#: cps/helper.py:132 msgid "Get Started with Calibre-Web" msgstr "Aan de slag met Calibre-Web" -#: cps/helper.py:126 +#: cps/helper.py:133 #, python-format msgid "Registration e-mail for user: %(name)s" msgstr "Registratie email voor gebruiker: %(name)s" -#: cps/helper.py:139 cps/helper.py:141 cps/helper.py:143 cps/helper.py:145 -#: cps/helper.py:151 cps/helper.py:153 cps/helper.py:155 cps/helper.py:157 +#: cps/helper.py:146 cps/helper.py:148 cps/helper.py:150 cps/helper.py:158 +#: cps/helper.py:160 cps/helper.py:162 #, python-format msgid "Send %(format)s to Kindle" msgstr "" -#: cps/helper.py:161 cps/helper.py:165 +#: cps/helper.py:166 #, python-format msgid "Convert %(orig)s to %(format)s and send to Kindle" msgstr "" -#: cps/helper.py:200 +#: cps/helper.py:205 #, python-format msgid "E-mail: %(book)s" msgstr "Email: %(book)s" -#: cps/helper.py:203 +#: cps/helper.py:208 msgid "The requested file could not be read. Maybe wrong permissions?" msgstr "Het gevraagde bestand kon niet worden gelezen. Misschien niet de juiste permissies?" -#: cps/helper.py:311 +#: cps/helper.py:316 #, python-format msgid "Rename title from: '%(src)s' to '%(dest)s' failed with error: %(error)s" msgstr "Hernoemen van titel: '%(src)s' naar '%(dest)s' faade met fout: %(error)s" -#: cps/helper.py:321 +#: cps/helper.py:326 #, python-format msgid "Rename author from: '%(src)s' to '%(dest)s' failed with error: %(error)s" msgstr "Hernoemen van de auteur: '%(src)s' naar '%(dest)s' faalde met fout: %(error)s" -#: cps/helper.py:335 +#: cps/helper.py:340 #, python-format msgid "Rename file in path '%(src)s' to '%(dest)s' failed with error: %(error)s" msgstr "" -#: cps/helper.py:361 cps/helper.py:371 cps/helper.py:379 +#: cps/helper.py:366 cps/helper.py:376 cps/helper.py:384 #, python-format msgid "File %(file)s not found on Google Drive" msgstr "Bestand %(file)s niet gevonden op Google Drive" -#: cps/helper.py:400 +#: cps/helper.py:405 #, python-format msgid "Book path %(path)s not found on Google Drive" msgstr "Boek pad %(path)s niet gevonden op Google Drive" -#: cps/helper.py:508 +#: cps/helper.py:556 msgid "Error excecuting UnRar" msgstr "Fout bij het uitvoeren van UnRar" -#: cps/helper.py:510 +#: cps/helper.py:558 msgid "Unrar binary file not found" msgstr "Unrar uitvoeringsbestand niet gevonden" -#: cps/helper.py:541 +#: cps/helper.py:589 msgid "Waiting" msgstr "Wachten" -#: cps/helper.py:543 +#: cps/helper.py:591 msgid "Failed" msgstr "Mislukt" -#: cps/helper.py:545 +#: cps/helper.py:593 msgid "Started" msgstr "Gestart" -#: cps/helper.py:547 +#: cps/helper.py:595 msgid "Finished" msgstr "Voltooid" -#: cps/helper.py:549 +#: cps/helper.py:597 msgid "Unknown Status" msgstr "Onbekende status" -#: cps/helper.py:554 +#: cps/helper.py:602 msgid "E-mail: " msgstr "Email:" -#: cps/helper.py:556 cps/helper.py:560 +#: cps/helper.py:604 cps/helper.py:608 msgid "Convert: " msgstr "Converteer:" -#: cps/helper.py:558 +#: cps/helper.py:606 msgid "Upload: " msgstr "Upload:" -#: cps/helper.py:562 +#: cps/helper.py:610 msgid "Unknown Task: " msgstr "Onbekende taak:" @@ -170,19 +171,19 @@ msgstr "Onverwachte data tijdens het lezen van de update informatie" msgid "No update available. You already have the latest version installed" msgstr "Geen update beschikbaar. Je hebt reeds de laatste versie geïnstalleerd" -#: cps/updater.py:270 cps/updater.py:501 cps/updater.py:503 cps/web.py:1187 +#: cps/updater.py:270 cps/updater.py:501 cps/updater.py:503 cps/web.py:1206 msgid "HTTP Error" msgstr "HTTP fout" -#: cps/updater.py:272 cps/updater.py:505 cps/web.py:1188 +#: cps/updater.py:272 cps/updater.py:505 cps/web.py:1207 msgid "Connection error" msgstr "Verbindingsfout" -#: cps/updater.py:274 cps/updater.py:507 cps/web.py:1189 +#: cps/updater.py:274 cps/updater.py:507 cps/web.py:1208 msgid "Timeout while establishing connection" msgstr "Time-out bij het maken van de verbinding" -#: cps/updater.py:276 cps/updater.py:509 cps/web.py:1190 +#: cps/updater.py:276 cps/updater.py:509 cps/web.py:1209 msgid "General error" msgstr "Algemene fout" @@ -203,555 +204,545 @@ msgstr "" msgid "A new update is available. Click on the button below to update to version: %(version)s" msgstr "" -#: cps/updater.py:491 cps/web.py:2771 +#: cps/updater.py:491 cps/web.py:2801 msgid "Unknown" msgstr "Onbekend" -#: cps/web.py:1180 +#: cps/web.py:1199 msgid "Requesting update package" msgstr "Update pakket wordt aangevraagd" -#: cps/web.py:1181 +#: cps/web.py:1200 msgid "Downloading update package" msgstr "Update pakket wordt gedownload" -#: cps/web.py:1182 +#: cps/web.py:1201 msgid "Unzipping update package" msgstr "Update pakket wordt uitgepakt" -#: cps/web.py:1183 +#: cps/web.py:1202 msgid "Replacing files" msgstr "Bestanden aan het vervangen" -#: cps/web.py:1184 +#: cps/web.py:1203 msgid "Database connections are closed" msgstr "Database verbindingen zijn gesloten" -#: cps/web.py:1185 +#: cps/web.py:1204 msgid "Stopping server" msgstr "Server aan het stoppen" -#: cps/web.py:1186 +#: cps/web.py:1205 msgid "Update finished, please press okay and reload page" msgstr "Update voltooid, klik op ok en herlaad de pagina" -#: cps/web.py:1187 cps/web.py:1188 cps/web.py:1189 cps/web.py:1190 +#: cps/web.py:1206 cps/web.py:1207 cps/web.py:1208 cps/web.py:1209 msgid "Update failed:" msgstr "Update gefaald:" -#: cps/web.py:1213 +#: cps/web.py:1235 msgid "Recently Added Books" msgstr "Recent toegevoegde boeken" -#: cps/web.py:1223 +#: cps/web.py:1245 msgid "Newest Books" msgstr "Nieuwste boeken" -#: cps/web.py:1235 +#: cps/web.py:1257 msgid "Oldest Books" msgstr "Oudste boeken" -#: cps/web.py:1247 +#: cps/web.py:1269 msgid "Books (A-Z)" msgstr "Boeken (A-Z)" -#: cps/web.py:1258 +#: cps/web.py:1280 msgid "Books (Z-A)" msgstr "Boeken (A-Z)" -#: cps/web.py:1287 +#: cps/web.py:1309 msgid "Hot Books (most downloaded)" msgstr "Populaire boeken (meeste downloads)" -#: cps/web.py:1300 +#: cps/web.py:1322 msgid "Best rated books" msgstr "Best beoordeelde boeken" -#: cps/templates/index.xml:39 cps/web.py:1313 +#: cps/templates/index.xml:39 cps/web.py:1335 msgid "Random Books" msgstr "Willekeurige boeken" -#: cps/web.py:1340 cps/web.py:1596 cps/web.py:2140 +#: cps/web.py:1362 cps/web.py:1618 cps/web.py:2167 msgid "Error opening eBook. File does not exist or file is not accessible:" msgstr "Fout bij openen van het boek. Bestand bestaat niet of is niet toegankelijk:" -#: cps/web.py:1369 +#: cps/web.py:1391 msgid "Publisher list" msgstr "Uitgeverslijst" -#: cps/web.py:1384 +#: cps/web.py:1406 #, python-format msgid "Publisher: %(name)s" msgstr "Uitgever: %(name)s" -#: cps/templates/index.xml:83 cps/web.py:1416 +#: cps/templates/index.xml:83 cps/web.py:1438 msgid "Series list" msgstr "Serie lijst" -#: cps/web.py:1430 +#: cps/web.py:1452 #, python-format msgid "Series: %(serie)s" msgstr "Serie: %(serie)s" -#: cps/web.py:1456 +#: cps/web.py:1478 msgid "Available languages" msgstr "Beschikbare talen" -#: cps/web.py:1476 +#: cps/web.py:1498 #, python-format msgid "Language: %(name)s" msgstr "Taal: %(name)s" -#: cps/templates/index.xml:76 cps/web.py:1487 +#: cps/templates/index.xml:76 cps/web.py:1509 msgid "Category list" msgstr "Categorie lijst" -#: cps/web.py:1501 +#: cps/web.py:1523 #, python-format msgid "Category: %(name)s" msgstr "Categorie: %(name)s" -#: cps/templates/layout.html:73 cps/web.py:1632 +#: cps/templates/layout.html:73 cps/web.py:1654 msgid "Tasks" msgstr "Taken" -#: cps/web.py:1666 +#: cps/web.py:1688 msgid "Statistics" msgstr "Statistieken" -#: cps/web.py:1734 +#: cps/web.py:1756 msgid "Google Drive setup not completed, try to deactivate and activate Google Drive again" msgstr "" -#: cps/web.py:1779 +#: cps/web.py:1801 msgid "Callback domain is not verified, please follow steps to verify domain in google developer console" msgstr "Het callback domein is niet geverifieerd, volg de stappen in de google ontwikkelaars console om het domein te verifiëren" -#: cps/web.py:1855 +#: cps/web.py:1877 msgid "Server restarted, please reload page" msgstr "Server herstart, gelieve de pagina herladen" -#: cps/web.py:1858 +#: cps/web.py:1880 msgid "Performing shutdown of server, please close window" msgstr "Bezig met het stoppen van de server, gelieve venster te sluiten" -#: cps/web.py:1938 +#: cps/web.py:1959 msgid "Published after " msgstr "Gepubliceerd na " -#: cps/web.py:1945 +#: cps/web.py:1966 msgid "Published before " msgstr "Gepubliceerd voor " -#: cps/web.py:1959 +#: cps/web.py:1980 #, python-format msgid "Rating <= %(rating)s" msgstr "Waardering <= %(rating)s" -#: cps/web.py:1961 +#: cps/web.py:1982 #, python-format msgid "Rating >= %(rating)s" msgstr "Waardering >= %(rating)s" -#: cps/web.py:2022 cps/web.py:2031 +#: cps/web.py:2042 cps/web.py:2051 msgid "search" msgstr "zoek" #: cps/templates/index.xml:47 cps/templates/index.xml:51 -#: cps/templates/layout.html:148 cps/web.py:2099 +#: cps/templates/layout.html:148 cps/web.py:2122 msgid "Read Books" msgstr "Gelezen Boeken" #: cps/templates/index.xml:55 cps/templates/index.xml:59 -#: cps/templates/layout.html:150 cps/web.py:2102 +#: cps/templates/layout.html:150 cps/web.py:2125 msgid "Unread Books" msgstr "Ongelezen Boeken" -#: cps/web.py:2150 cps/web.py:2152 cps/web.py:2154 cps/web.py:2166 +#: cps/web.py:2177 cps/web.py:2179 cps/web.py:2181 cps/web.py:2193 msgid "Read a Book" msgstr "Lees een boek" -#: cps/web.py:2225 cps/web.py:3146 +#: cps/web.py:2205 +msgid "Error opening eBook. Fileformat is not supported." +msgstr "" + +#: cps/web.py:2255 cps/web.py:3176 msgid "Please fill out all fields!" msgstr "Gelieve alle velden in te vullen!" -#: cps/web.py:2226 cps/web.py:2248 cps/web.py:2252 cps/web.py:2257 -#: cps/web.py:2259 +#: cps/web.py:2256 cps/web.py:2278 cps/web.py:2282 cps/web.py:2287 +#: cps/web.py:2289 msgid "register" msgstr "registreer" -#: cps/web.py:2247 cps/web.py:3365 +#: cps/web.py:2277 cps/web.py:3395 msgid "An unknown error occurred. Please try again later." msgstr "Er was een onbekende fout. Gelieve later nog eens te proberen." -#: cps/web.py:2250 +#: cps/web.py:2280 msgid "Your e-mail is not allowed to register" msgstr "Het is niet toegestaan om te registreren met jou email" -#: cps/web.py:2253 +#: cps/web.py:2283 msgid "Confirmation e-mail was send to your e-mail account." msgstr "Bevestigings email werd verzonden naar jou email account." -#: cps/web.py:2256 +#: cps/web.py:2286 msgid "This username or e-mail address is already in use." msgstr "Deze gebruikersnaam of email adres is reeds in gebruik." -#: cps/web.py:2273 cps/web.py:2369 +#: cps/web.py:2303 cps/web.py:2399 #, python-format msgid "you are now logged in as: '%(nickname)s'" msgstr "je bent nu ingelogd als: '%(nickname)s'" -#: cps/web.py:2278 +#: cps/web.py:2308 msgid "Wrong Username or Password" msgstr "Verkeerde gebruikersnaam of Wachtwoord" -#: cps/web.py:2284 cps/web.py:2305 +#: cps/web.py:2314 cps/web.py:2335 msgid "login" msgstr "login" -#: cps/web.py:2317 cps/web.py:2348 +#: cps/web.py:2347 cps/web.py:2378 msgid "Token not found" msgstr "Token niet gevonden" -#: cps/web.py:2325 cps/web.py:2356 +#: cps/web.py:2355 cps/web.py:2386 msgid "Token has expired" msgstr "Token is verlopen" -#: cps/web.py:2333 +#: cps/web.py:2363 msgid "Success! Please return to your device" msgstr "Gelukt! Ga terug naar je apparaat" -#: cps/web.py:2383 +#: cps/web.py:2413 msgid "Please configure the SMTP mail settings first..." msgstr "Gelieve de SMTP mail instellingen eerst te configureren..." -#: cps/web.py:2388 +#: cps/web.py:2418 #, python-format msgid "Book successfully queued for sending to %(kindlemail)s" msgstr "Boek met succes in de wachtrij geplaatst om te verzenden naar %(kindlemail)s" -#: cps/web.py:2392 +#: cps/web.py:2422 #, python-format msgid "There was an error sending this book: %(res)s" msgstr "Er trad een fout op bij het versturen van dit boek: %(res)s" -#: cps/web.py:2394 cps/web.py:3199 +#: cps/web.py:2424 cps/web.py:3229 msgid "Please configure your kindle e-mail address first..." msgstr "Gelieve eerst je kindle mailadres te configureren..." -#: cps/web.py:2405 cps/web.py:2457 +#: cps/web.py:2435 cps/web.py:2487 msgid "Invalid shelf specified" msgstr "Ongeldige boekenplank gespecificeerd" -#: cps/web.py:2412 +#: cps/web.py:2442 #, python-format msgid "Sorry you are not allowed to add a book to the the shelf: %(shelfname)s" msgstr "Sorry, jij mag geen boeken toe voegen aan boekenplank: %(shelfname)s" -#: cps/web.py:2420 +#: cps/web.py:2450 msgid "You are not allowed to edit public shelves" msgstr "Jij mag geen publieke boekenplanken bewerken" -#: cps/web.py:2429 +#: cps/web.py:2459 #, python-format msgid "Book is already part of the shelf: %(shelfname)s" msgstr "Dit boek maakt al deel uit van boekenplank: %(shelfname)s" -#: cps/web.py:2443 +#: cps/web.py:2473 #, python-format msgid "Book has been added to shelf: %(sname)s" msgstr "Boek werd toegevoegd aan boekenplank: %(sname)s" -#: cps/web.py:2462 +#: cps/web.py:2492 #, python-format msgid "You are not allowed to add a book to the the shelf: %(name)s" msgstr "Jij mag geen boeken plaatsen op boekenplank: %(name)s" -#: cps/web.py:2467 +#: cps/web.py:2497 msgid "User is not allowed to edit public shelves" msgstr "Gebruiker is niet toegestaan om publieke boekenplanken te bewerken" -#: cps/web.py:2485 +#: cps/web.py:2515 #, python-format msgid "Books are already part of the shelf: %(name)s" msgstr "Deze boeken maken reeds deel uit van boekenplank: %(name)s" -#: cps/web.py:2499 +#: cps/web.py:2529 #, python-format msgid "Books have been added to shelf: %(sname)s" msgstr "De boeken werden toegevoegd aan boekenplank: %(sname)s" -#: cps/web.py:2501 +#: cps/web.py:2531 #, python-format msgid "Could not add books to shelf: %(sname)s" msgstr "Kon geen boeken toevoegen aan boekenplank: %(sname)s" -#: cps/web.py:2538 +#: cps/web.py:2568 #, python-format msgid "Book has been removed from shelf: %(sname)s" msgstr "Boek werd verwijderd van boekenplank: %(sname)s" -#: cps/web.py:2544 +#: cps/web.py:2574 #, python-format msgid "Sorry you are not allowed to remove a book from this shelf: %(sname)s" msgstr "Sorry, jij mag geen boeken verwijderen van deze boekenplank: %(sname)s" -#: cps/web.py:2565 cps/web.py:2589 +#: cps/web.py:2595 cps/web.py:2619 #, python-format msgid "A shelf with the name '%(title)s' already exists." msgstr "Een boekenplank met de naam '%(title)s' bestaat reeds." -#: cps/web.py:2570 +#: cps/web.py:2600 #, python-format msgid "Shelf %(title)s created" msgstr "Boekenplank %(title)s aangemaakt" -#: cps/web.py:2572 cps/web.py:2600 +#: cps/web.py:2602 cps/web.py:2630 msgid "There was an error" msgstr "Er deed zich een fout voor" -#: cps/web.py:2573 cps/web.py:2575 +#: cps/web.py:2603 cps/web.py:2605 msgid "create a shelf" msgstr "maak een boekenplank" -#: cps/web.py:2598 +#: cps/web.py:2628 #, python-format msgid "Shelf %(title)s changed" msgstr "Boekenplank %(title)s gewijzigd" -#: cps/web.py:2601 cps/web.py:2603 +#: cps/web.py:2631 cps/web.py:2633 msgid "Edit a shelf" msgstr "Bewerk een boekenplank" -#: cps/web.py:2624 +#: cps/web.py:2654 #, python-format msgid "successfully deleted shelf %(name)s" msgstr "boekenplank %(name)s succesvol gewist" -#: cps/web.py:2651 +#: cps/web.py:2681 #, python-format msgid "Shelf: '%(name)s'" msgstr "Boekenplank: '%(name)s'" -#: cps/web.py:2654 +#: cps/web.py:2684 msgid "Error opening shelf. Shelf does not exist or is not accessible" msgstr "Fout bij openen boekenplank. Boekenplank bestaat niet of is niet toegankelijk" -#: cps/web.py:2685 +#: cps/web.py:2715 #, python-format msgid "Change order of Shelf: '%(name)s'" msgstr "Verander volgorde van Boekenplank: '%(name)s'" -#: cps/web.py:2714 cps/web.py:3152 +#: cps/web.py:2744 cps/web.py:3182 msgid "E-mail is not from valid domain" msgstr "Email is niet van een geldig domein" -#: cps/web.py:2716 cps/web.py:2758 cps/web.py:2761 +#: cps/web.py:2746 cps/web.py:2788 cps/web.py:2791 #, python-format msgid "%(name)s's profile" msgstr "%(name)s's profiel" -#: cps/web.py:2756 +#: cps/web.py:2786 msgid "Found an existing account for this e-mail address." msgstr "Een bestaand account met dit email adres werd gevonden." -#: cps/web.py:2759 +#: cps/web.py:2789 msgid "Profile updated" msgstr "Profiel aangepast" -#: cps/web.py:2790 +#: cps/web.py:2820 msgid "Admin page" msgstr "Administratie pagina" -#: cps/web.py:2875 cps/web.py:3055 +#: cps/web.py:2905 cps/web.py:3085 msgid "Calibre-Web configuration updated" msgstr "Calibre-Web configuratie aangepast" -#: cps/templates/admin.html:100 cps/web.py:2889 +#: cps/templates/admin.html:100 cps/web.py:2919 msgid "UI Configuration" msgstr "Gebruikersinterface configuratie" -#: cps/web.py:2907 +#: cps/web.py:2937 msgid "Import of optional Google Drive requirements missing" msgstr "De import van optionele Google Drive vereisten ontbreken" -#: cps/web.py:2910 +#: cps/web.py:2940 msgid "client_secrets.json is missing or not readable" msgstr "client_secrets.json ontbreekt of is niet leesbaar" -#: cps/web.py:2915 cps/web.py:2944 +#: cps/web.py:2945 cps/web.py:2974 msgid "client_secrets.json is not configured for web application" msgstr "client_secrets.json is niet geconfigureerd voor web applicaties" -#: cps/templates/admin.html:99 cps/web.py:2947 cps/web.py:2973 cps/web.py:2985 -#: cps/web.py:3030 cps/web.py:3045 cps/web.py:3064 cps/web.py:3072 -#: cps/web.py:3088 +#: cps/templates/admin.html:99 cps/web.py:2977 cps/web.py:3003 cps/web.py:3015 +#: cps/web.py:3060 cps/web.py:3075 cps/web.py:3094 cps/web.py:3102 +#: cps/web.py:3118 msgid "Basic Configuration" msgstr "Basis configuratie" -#: cps/web.py:2970 +#: cps/web.py:3000 msgid "Keyfile location is not valid, please enter correct path" msgstr "Sleutelbestand (\"keyfile\") locatie ongeldig, gelieve het correcte pad in te geven" -#: cps/web.py:2982 +#: cps/web.py:3012 msgid "Certfile location is not valid, please enter correct path" msgstr "Certificatiebestand (\"certfile\") locatie ongeldig, gelieve het correcte pad in te geven" -#: cps/web.py:3027 +#: cps/web.py:3057 msgid "Logfile location is not valid, please enter correct path" msgstr "Log bestand (\"logfile\") locatie ongeldig, gelieve het correcte pad in te geven" -#: cps/web.py:3068 +#: cps/web.py:3098 msgid "DB location is not valid, please enter correct path" msgstr "DB locatie is niet geldig, gelieve het correcte pad in te geven" -#: cps/templates/admin.html:33 cps/web.py:3148 cps/web.py:3154 cps/web.py:3170 +#: cps/templates/admin.html:33 cps/web.py:3178 cps/web.py:3184 cps/web.py:3200 msgid "Add new user" msgstr "Voeg nieuwe gebruiker toe" -#: cps/web.py:3160 +#: cps/web.py:3190 #, python-format msgid "User '%(user)s' created" msgstr "Gebruiker '%(user)s' aangemaakt" -#: cps/web.py:3164 +#: cps/web.py:3194 msgid "Found an existing account for this e-mail address or nickname." msgstr "Een bestaande account gevonden met dit email adres of gebruikersnaam." -#: cps/web.py:3194 +#: cps/web.py:3224 #, python-format msgid "Test e-mail successfully send to %(kindlemail)s" msgstr "Test email met succes verzonden naar %(kindlemail)s" -#: cps/web.py:3197 +#: cps/web.py:3227 #, python-format msgid "There was an error sending the Test e-mail: %(res)s" msgstr "Er was een fout bij het verzenden van test email: %(res)s" -#: cps/web.py:3201 +#: cps/web.py:3231 msgid "E-mail server settings updated" msgstr "Email server instellingen aangepast" -#: cps/web.py:3202 +#: cps/web.py:3232 msgid "Edit e-mail server settings" msgstr "Bewerk email server instellingen" -#: cps/web.py:3227 +#: cps/web.py:3257 #, python-format msgid "User '%(nick)s' deleted" msgstr "Gebruiker '%(nick)s' verwijderd" -#: cps/web.py:3340 +#: cps/web.py:3370 #, python-format msgid "User '%(nick)s' updated" msgstr "Gebruiker '%(nick)s' aangepast" -#: cps/web.py:3343 +#: cps/web.py:3373 msgid "An unknown error occured." msgstr "Een onbekende fout deed zich voor." -#: cps/web.py:3345 +#: cps/web.py:3375 #, python-format msgid "Edit User %(nick)s" msgstr "Bewerk gebruiker '%(nick)s" -#: cps/web.py:3362 +#: cps/web.py:3392 #, python-format msgid "Password for user %(user)s reset" msgstr "Wachtwoord voor gebruiker %(user)s gereset" -#: cps/web.py:3376 cps/web.py:3582 +#: cps/web.py:3406 cps/web.py:3598 msgid "Error opening eBook. File does not exist or file is not accessible" msgstr "Fout bij openen eBook. Het bestand bestaat niet of is niet toegankelijk" -#: cps/web.py:3404 +#: cps/web.py:3434 msgid "edit metadata" msgstr "bewerk metadata" -#: cps/web.py:3497 cps/web.py:3743 +#: cps/web.py:3527 cps/web.py:3760 #, python-format msgid "File extension '%(ext)s' is not allowed to be uploaded to this server" msgstr "Het uploaden van bestandsextensie '%(ext)s' is niet toegestaan op deze server" -#: cps/web.py:3501 cps/web.py:3746 +#: cps/web.py:3531 cps/web.py:3763 msgid "File to be uploaded must have an extension" msgstr "Up te loaden bestanden dienen een extensie te hebben" -#: cps/web.py:3513 cps/web.py:3765 +#: cps/web.py:3543 cps/web.py:3782 #, python-format msgid "Failed to create path %(path)s (Permission denied)." msgstr "Het pad %(path)s aanmaken mislukt (Geen toestemming)." -#: cps/web.py:3518 +#: cps/web.py:3548 #, python-format msgid "Failed to store file %(file)s." msgstr "Bestand opslaan niet gelukt voor %(file)s." -#: cps/web.py:3535 +#: cps/web.py:3565 #, python-format msgid "File format %(ext)s added to %(book)s" msgstr "Bestandsformaat %(ext)s toegevoegd aan %(book)s" -#: cps/web.py:3553 -#, python-format -msgid "Failed to create path for cover %(path)s (Permission denied)." -msgstr "Het pad %(path)s aanmaken voor boekomslag is mislukt (Geen toestemming)." - -#: cps/web.py:3561 -#, python-format -msgid "Failed to store cover-file %(cover)s." -msgstr "Boekomslag %(cover)s opslaan mislukt." - -#: cps/web.py:3564 -msgid "Cover-file is not a valid image file" -msgstr "Boekomslag bestand is geen geldig beeldbestand" +#: cps/web.py:3579 cps/web.py:3652 +msgid "Cover is not a supported imageformat (jpg/png/webp), can't save" +msgstr "" -#: cps/web.py:3594 cps/web.py:3603 +#: cps/web.py:3611 cps/web.py:3620 msgid "unknown" msgstr "onbekend" -#: cps/web.py:3635 -msgid "Cover is not a jpg file, can't save" -msgstr "Boekomslag is geen jpg bestand, opslaan niet mogelijk" - -#: cps/web.py:3683 +#: cps/web.py:3700 #, python-format msgid "%(langname)s is not a valid language" msgstr "%(langname)s is geen geldige taal" -#: cps/web.py:3714 +#: cps/web.py:3731 msgid "Metadata successfully updated" msgstr "Metadata succesvol geüpdatet" -#: cps/web.py:3723 +#: cps/web.py:3740 msgid "Error editing book, please check logfile for details" msgstr "Fout bij het bewerken van het boek, gelieve logfile controleren" -#: cps/web.py:3769 +#: cps/web.py:3786 #, python-format msgid "Failed to store file %(file)s (Permission denied)." msgstr "Bestand %(file)s opslaan mislukt (Geen toestemming)." -#: cps/web.py:3774 +#: cps/web.py:3791 #, python-format msgid "Failed to delete file %(file)s (Permission denied)." msgstr "Bestand %(file)s wissen mislukt (Geen toestemming)." -#: cps/web.py:3857 +#: cps/web.py:3873 #, python-format msgid "File %(title)s" msgstr "" -#: cps/web.py:3886 +#: cps/web.py:3902 msgid "Source or destination format for conversion missing" msgstr "Bron of doel formaat voor conversie ontbreekt" -#: cps/web.py:3896 +#: cps/web.py:3912 #, python-format msgid "Book successfully queued for converting to %(book_format)s" msgstr "Boek succesvol in de wachtrij geplaatst voor conversie naar %(book_format)s" -#: cps/web.py:3900 +#: cps/web.py:3916 #, python-format msgid "There was an error converting this book: %(res)s" msgstr "Er trad een fout op bij het converteren van dit boek: %(res)s" @@ -1289,7 +1280,7 @@ msgstr "Aantal boeken te tonen" msgid "No. of authors to show before hiding (0=disable hiding)" msgstr "" -#: cps/templates/config_view_edit.html:35 cps/templates/readcbr.html:108 +#: cps/templates/config_view_edit.html:35 cps/templates/readcbr.html:118 msgid "Theme" msgstr "Thema" @@ -1603,7 +1594,7 @@ msgid "Advanced Search" msgstr "Geavanceerd zoeken" #: cps/templates/layout.html:76 cps/templates/read.html:71 -#: cps/templates/readcbr.html:79 cps/templates/readcbr.html:103 +#: cps/templates/readcbr.html:89 cps/templates/readcbr.html:113 msgid "Settings" msgstr "Instellingen" @@ -1722,98 +1713,106 @@ msgstr "Calibre-Web ebook cataloog" msgid "Reflow text when sidebars are open." msgstr "Herschuif tekst waneer het zijpaneel open staat." -#: cps/templates/readcbr.html:84 +#: cps/templates/readcbr.html:94 msgid "Keyboard Shortcuts" msgstr "Sneltoetsen" -#: cps/templates/readcbr.html:87 +#: cps/templates/readcbr.html:97 msgid "Previous Page" msgstr "Vorige Pagina" -#: cps/templates/readcbr.html:88 +#: cps/templates/readcbr.html:98 msgid "Next Page" msgstr "Volgende Pagina" -#: cps/templates/readcbr.html:89 +#: cps/templates/readcbr.html:99 msgid "Scale to Best" msgstr "Optimaal schalen" -#: cps/templates/readcbr.html:90 +#: cps/templates/readcbr.html:100 msgid "Scale to Width" msgstr "Schalen naar breedte" -#: cps/templates/readcbr.html:91 +#: cps/templates/readcbr.html:101 msgid "Scale to Height" msgstr "Schalen naar hoogte" -#: cps/templates/readcbr.html:92 +#: cps/templates/readcbr.html:102 msgid "Scale to Native" msgstr "Schalen op ware grootte" -#: cps/templates/readcbr.html:93 +#: cps/templates/readcbr.html:103 msgid "Rotate Right" msgstr "Draai rechtsom" -#: cps/templates/readcbr.html:94 +#: cps/templates/readcbr.html:104 msgid "Rotate Left" msgstr "Draai linksom" -#: cps/templates/readcbr.html:95 +#: cps/templates/readcbr.html:105 msgid "Flip Image" msgstr "Keer beeld om" -#: cps/templates/readcbr.html:111 +#: cps/templates/readcbr.html:121 msgid "Light" msgstr "Licht" -#: cps/templates/readcbr.html:112 +#: cps/templates/readcbr.html:122 msgid "Dark" msgstr "Donker" -#: cps/templates/readcbr.html:117 +#: cps/templates/readcbr.html:127 msgid "Scale" msgstr "Schaal" -#: cps/templates/readcbr.html:120 +#: cps/templates/readcbr.html:130 msgid "Best" msgstr "Beste" -#: cps/templates/readcbr.html:121 +#: cps/templates/readcbr.html:131 msgid "Width" msgstr "Breedte" -#: cps/templates/readcbr.html:122 +#: cps/templates/readcbr.html:132 msgid "Height" msgstr "Hoogte" -#: cps/templates/readcbr.html:123 +#: cps/templates/readcbr.html:133 msgid "Native" msgstr "Ware grootte" -#: cps/templates/readcbr.html:128 +#: cps/templates/readcbr.html:138 msgid "Rotate" msgstr "Draai" -#: cps/templates/readcbr.html:139 +#: cps/templates/readcbr.html:149 msgid "Flip" msgstr "Keer" -#: cps/templates/readcbr.html:142 +#: cps/templates/readcbr.html:152 msgid "Horizontal" msgstr "Horizontaal" -#: cps/templates/readcbr.html:143 +#: cps/templates/readcbr.html:153 msgid "Vertical" msgstr "Verticaal" +#: cps/templates/readcbr.html:158 +msgid "Direction" +msgstr "" + +#: cps/templates/readcbr.html:161 +msgid "Left to Right" +msgstr "" + +#: cps/templates/readcbr.html:162 +msgid "Right to Left" +msgstr "" + #: cps/templates/readpdf.html:29 msgid "PDF.js viewer" msgstr "PDF.js viewer" -#: cps/templates/readpdf.html:418 -msgid "Preparing document for printing..." -msgstr "" - #: cps/templates/readtxt.html:6 msgid "Basic txt Reader" msgstr "Basis txt Lezer" @@ -3260,3 +3259,18 @@ msgstr "Recente Downloads" #~ msgid "Zaza" #~ msgstr "Zaza" +#~ msgid "Failed to create path for cover %(path)s (Permission denied)." +#~ msgstr "Het pad %(path)s aanmaken voor boekomslag is mislukt (Geen toestemming)." + +#~ msgid "Failed to store cover-file %(cover)s." +#~ msgstr "Boekomslag %(cover)s opslaan mislukt." + +#~ msgid "Cover-file is not a valid image file" +#~ msgstr "Boekomslag bestand is geen geldig beeldbestand" + +#~ msgid "Cover is not a jpg file, can't save" +#~ msgstr "Boekomslag is geen jpg bestand, opslaan niet mogelijk" + +#~ msgid "Preparing document for printing..." +#~ msgstr "" + diff --git a/cps/translations/pl/LC_MESSAGES/messages.mo b/cps/translations/pl/LC_MESSAGES/messages.mo index b45788fd44dff30314b3422ff181c04f5f87565c..a8c3b7291a6ae54b62691421241636f990f434f5 100644 GIT binary patch delta 15075 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 cOAqpYvV7{nj4JES;&gU~T)hEXb9?dHl|0cE2 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 qn8;W@x?p1Oiqrr1j$c|iu~4GL+BzvY{j\n" "Language: pl\n" @@ -19,8 +19,9 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.6.0\n" -#: cps/book_formats.py:152 cps/book_formats.py:153 cps/book_formats.py:157 -#: cps/book_formats.py:161 cps/converter.py:29 cps/converter.py:45 +#: cps/book_formats.py:199 cps/book_formats.py:200 cps/book_formats.py:204 +#: cps/book_formats.py:208 cps/book_formats.py:212 cps/converter.py:29 +#: cps/converter.py:45 msgid "not installed" msgstr "nie zainstalowane" @@ -32,133 +33,133 @@ msgstr "" msgid "not configured" msgstr "" -#: cps/helper.py:72 +#: cps/helper.py:79 #, python-format msgid "%(format)s format not found for book id: %(book)d" msgstr "" -#: cps/helper.py:84 +#: cps/helper.py:91 #, python-format msgid "%(format)s not found on Google Drive: %(fn)s" msgstr "" -#: cps/helper.py:91 cps/helper.py:199 cps/templates/detail.html:45 +#: cps/helper.py:98 cps/helper.py:204 cps/templates/detail.html:45 #: cps/templates/detail.html:49 msgid "Send to Kindle" msgstr "Wyślij do Kindle" -#: cps/helper.py:92 cps/helper.py:110 cps/helper.py:201 +#: cps/helper.py:99 cps/helper.py:117 cps/helper.py:206 msgid "This e-mail has been sent via Calibre-Web." msgstr "" -#: cps/helper.py:103 +#: cps/helper.py:110 #, python-format msgid "%(format)s not found: %(fn)s" msgstr "" -#: cps/helper.py:108 +#: cps/helper.py:115 msgid "Calibre-Web test e-mail" msgstr "" -#: cps/helper.py:109 +#: cps/helper.py:116 msgid "Test e-mail" msgstr "" -#: cps/helper.py:125 +#: cps/helper.py:132 msgid "Get Started with Calibre-Web" msgstr "" -#: cps/helper.py:126 +#: cps/helper.py:133 #, python-format msgid "Registration e-mail for user: %(name)s" msgstr "" -#: cps/helper.py:139 cps/helper.py:141 cps/helper.py:143 cps/helper.py:145 -#: cps/helper.py:151 cps/helper.py:153 cps/helper.py:155 cps/helper.py:157 +#: cps/helper.py:146 cps/helper.py:148 cps/helper.py:150 cps/helper.py:158 +#: cps/helper.py:160 cps/helper.py:162 #, python-format msgid "Send %(format)s to Kindle" msgstr "" -#: cps/helper.py:161 cps/helper.py:165 +#: cps/helper.py:166 #, python-format msgid "Convert %(orig)s to %(format)s and send to Kindle" msgstr "" -#: cps/helper.py:200 +#: cps/helper.py:205 #, python-format msgid "E-mail: %(book)s" msgstr "" -#: cps/helper.py:203 +#: cps/helper.py:208 msgid "The requested file could not be read. Maybe wrong permissions?" msgstr "" -#: cps/helper.py:311 +#: cps/helper.py:316 #, python-format msgid "Rename title from: '%(src)s' to '%(dest)s' failed with error: %(error)s" msgstr "" -#: cps/helper.py:321 +#: cps/helper.py:326 #, python-format msgid "Rename author from: '%(src)s' to '%(dest)s' failed with error: %(error)s" msgstr "" -#: cps/helper.py:335 +#: cps/helper.py:340 #, python-format msgid "Rename file in path '%(src)s' to '%(dest)s' failed with error: %(error)s" msgstr "" -#: cps/helper.py:361 cps/helper.py:371 cps/helper.py:379 +#: cps/helper.py:366 cps/helper.py:376 cps/helper.py:384 #, python-format msgid "File %(file)s not found on Google Drive" msgstr "" -#: cps/helper.py:400 +#: cps/helper.py:405 #, python-format msgid "Book path %(path)s not found on Google Drive" msgstr "" -#: cps/helper.py:508 +#: cps/helper.py:556 msgid "Error excecuting UnRar" msgstr "" -#: cps/helper.py:510 +#: cps/helper.py:558 msgid "Unrar binary file not found" msgstr "" -#: cps/helper.py:541 +#: cps/helper.py:589 msgid "Waiting" msgstr "" -#: cps/helper.py:543 +#: cps/helper.py:591 msgid "Failed" msgstr "" -#: cps/helper.py:545 +#: cps/helper.py:593 msgid "Started" msgstr "" -#: cps/helper.py:547 +#: cps/helper.py:595 msgid "Finished" msgstr "" -#: cps/helper.py:549 +#: cps/helper.py:597 msgid "Unknown Status" msgstr "" -#: cps/helper.py:554 +#: cps/helper.py:602 msgid "E-mail: " msgstr "" -#: cps/helper.py:556 cps/helper.py:560 +#: cps/helper.py:604 cps/helper.py:608 msgid "Convert: " msgstr "" -#: cps/helper.py:558 +#: cps/helper.py:606 msgid "Upload: " msgstr "" -#: cps/helper.py:562 +#: cps/helper.py:610 msgid "Unknown Task: " msgstr "" @@ -170,19 +171,19 @@ msgstr "" msgid "No update available. You already have the latest version installed" msgstr "" -#: cps/updater.py:270 cps/updater.py:501 cps/updater.py:503 cps/web.py:1187 +#: cps/updater.py:270 cps/updater.py:501 cps/updater.py:503 cps/web.py:1206 msgid "HTTP Error" msgstr "" -#: cps/updater.py:272 cps/updater.py:505 cps/web.py:1188 +#: cps/updater.py:272 cps/updater.py:505 cps/web.py:1207 msgid "Connection error" msgstr "" -#: cps/updater.py:274 cps/updater.py:507 cps/web.py:1189 +#: cps/updater.py:274 cps/updater.py:507 cps/web.py:1208 msgid "Timeout while establishing connection" msgstr "" -#: cps/updater.py:276 cps/updater.py:509 cps/web.py:1190 +#: cps/updater.py:276 cps/updater.py:509 cps/web.py:1209 msgid "General error" msgstr "" @@ -203,555 +204,545 @@ msgstr "" msgid "A new update is available. Click on the button below to update to version: %(version)s" msgstr "" -#: cps/updater.py:491 cps/web.py:2771 +#: cps/updater.py:491 cps/web.py:2801 msgid "Unknown" msgstr "" -#: cps/web.py:1180 +#: cps/web.py:1199 msgid "Requesting update package" msgstr "Żądanie o pakiet aktualizacji" -#: cps/web.py:1181 +#: cps/web.py:1200 msgid "Downloading update package" msgstr "Pobieranie pakietu aktualizacji" -#: cps/web.py:1182 +#: cps/web.py:1201 msgid "Unzipping update package" msgstr "Rozpakowywanie pakietu aktualizacji" -#: cps/web.py:1183 +#: cps/web.py:1202 msgid "Replacing files" msgstr "" -#: cps/web.py:1184 +#: cps/web.py:1203 msgid "Database connections are closed" msgstr "Połączenia z bazą danych zostały zakończone" -#: cps/web.py:1185 +#: cps/web.py:1204 msgid "Stopping server" msgstr "" -#: cps/web.py:1186 +#: cps/web.py:1205 msgid "Update finished, please press okay and reload page" msgstr "Aktualizacja zakończona, proszę nacisnąć OK i odświeżyć stronę" -#: cps/web.py:1187 cps/web.py:1188 cps/web.py:1189 cps/web.py:1190 +#: cps/web.py:1206 cps/web.py:1207 cps/web.py:1208 cps/web.py:1209 msgid "Update failed:" msgstr "" -#: cps/web.py:1213 +#: cps/web.py:1235 msgid "Recently Added Books" msgstr "" -#: cps/web.py:1223 +#: cps/web.py:1245 msgid "Newest Books" msgstr "" -#: cps/web.py:1235 +#: cps/web.py:1257 msgid "Oldest Books" msgstr "" -#: cps/web.py:1247 +#: cps/web.py:1269 msgid "Books (A-Z)" msgstr "" -#: cps/web.py:1258 +#: cps/web.py:1280 msgid "Books (Z-A)" msgstr "" -#: cps/web.py:1287 +#: cps/web.py:1309 msgid "Hot Books (most downloaded)" msgstr "Najpopularniejsze książki (najczęściej pobierane)" -#: cps/web.py:1300 +#: cps/web.py:1322 msgid "Best rated books" msgstr "Najlepiej oceniane książki" -#: cps/templates/index.xml:39 cps/web.py:1313 +#: cps/templates/index.xml:39 cps/web.py:1335 msgid "Random Books" msgstr "Losowe książki" -#: cps/web.py:1340 cps/web.py:1596 cps/web.py:2140 +#: cps/web.py:1362 cps/web.py:1618 cps/web.py:2167 msgid "Error opening eBook. File does not exist or file is not accessible:" msgstr "Błąd otwierania e-booka. Plik nie istnieje lub plik nie jest dostępny:" -#: cps/web.py:1369 +#: cps/web.py:1391 msgid "Publisher list" msgstr "" -#: cps/web.py:1384 +#: cps/web.py:1406 #, python-format msgid "Publisher: %(name)s" msgstr "" -#: cps/templates/index.xml:83 cps/web.py:1416 +#: cps/templates/index.xml:83 cps/web.py:1438 msgid "Series list" msgstr "Lista serii" -#: cps/web.py:1430 +#: cps/web.py:1452 #, python-format msgid "Series: %(serie)s" msgstr "Seria: %(serie)s" -#: cps/web.py:1456 +#: cps/web.py:1478 msgid "Available languages" msgstr "Dostępne języki" -#: cps/web.py:1476 +#: cps/web.py:1498 #, python-format msgid "Language: %(name)s" msgstr "Język: %(name)s" -#: cps/templates/index.xml:76 cps/web.py:1487 +#: cps/templates/index.xml:76 cps/web.py:1509 msgid "Category list" msgstr "Lista kategorii" -#: cps/web.py:1501 +#: cps/web.py:1523 #, python-format msgid "Category: %(name)s" msgstr "Kategoria: %(name)s" -#: cps/templates/layout.html:73 cps/web.py:1632 +#: cps/templates/layout.html:73 cps/web.py:1654 msgid "Tasks" msgstr "" -#: cps/web.py:1666 +#: cps/web.py:1688 msgid "Statistics" msgstr "Statystyki" -#: cps/web.py:1734 +#: cps/web.py:1756 msgid "Google Drive setup not completed, try to deactivate and activate Google Drive again" msgstr "" -#: cps/web.py:1779 +#: cps/web.py:1801 msgid "Callback domain is not verified, please follow steps to verify domain in google developer console" msgstr "" -#: cps/web.py:1855 +#: cps/web.py:1877 msgid "Server restarted, please reload page" msgstr "Serwer uruchomiony ponownie, proszę odświeżyć stronę" -#: cps/web.py:1858 +#: cps/web.py:1880 msgid "Performing shutdown of server, please close window" msgstr "Wykonano wyłączenie serwera, proszę zamknąć okno" -#: cps/web.py:1938 +#: cps/web.py:1959 msgid "Published after " msgstr "" -#: cps/web.py:1945 +#: cps/web.py:1966 msgid "Published before " msgstr "" -#: cps/web.py:1959 +#: cps/web.py:1980 #, python-format msgid "Rating <= %(rating)s" msgstr "" -#: cps/web.py:1961 +#: cps/web.py:1982 #, python-format msgid "Rating >= %(rating)s" msgstr "" -#: cps/web.py:2022 cps/web.py:2031 +#: cps/web.py:2042 cps/web.py:2051 msgid "search" msgstr "szukaj" #: cps/templates/index.xml:47 cps/templates/index.xml:51 -#: cps/templates/layout.html:148 cps/web.py:2099 +#: cps/templates/layout.html:148 cps/web.py:2122 msgid "Read Books" msgstr "Przeczytane książki" #: cps/templates/index.xml:55 cps/templates/index.xml:59 -#: cps/templates/layout.html:150 cps/web.py:2102 +#: cps/templates/layout.html:150 cps/web.py:2125 msgid "Unread Books" msgstr "Nieprzeczytane książki" -#: cps/web.py:2150 cps/web.py:2152 cps/web.py:2154 cps/web.py:2166 +#: cps/web.py:2177 cps/web.py:2179 cps/web.py:2181 cps/web.py:2193 msgid "Read a Book" msgstr "Czytaj książkę" -#: cps/web.py:2225 cps/web.py:3146 +#: cps/web.py:2205 +msgid "Error opening eBook. Fileformat is not supported." +msgstr "" + +#: cps/web.py:2255 cps/web.py:3176 msgid "Please fill out all fields!" msgstr "Proszę wypełnić wszystkie pola!" -#: cps/web.py:2226 cps/web.py:2248 cps/web.py:2252 cps/web.py:2257 -#: cps/web.py:2259 +#: cps/web.py:2256 cps/web.py:2278 cps/web.py:2282 cps/web.py:2287 +#: cps/web.py:2289 msgid "register" msgstr "rejestracja" -#: cps/web.py:2247 cps/web.py:3365 +#: cps/web.py:2277 cps/web.py:3395 msgid "An unknown error occurred. Please try again later." msgstr "" -#: cps/web.py:2250 +#: cps/web.py:2280 msgid "Your e-mail is not allowed to register" msgstr "" -#: cps/web.py:2253 +#: cps/web.py:2283 msgid "Confirmation e-mail was send to your e-mail account." msgstr "" -#: cps/web.py:2256 +#: cps/web.py:2286 msgid "This username or e-mail address is already in use." msgstr "" -#: cps/web.py:2273 cps/web.py:2369 +#: cps/web.py:2303 cps/web.py:2399 #, python-format msgid "you are now logged in as: '%(nickname)s'" msgstr "Zalogowałeś się jako: '%(nickname)s'" -#: cps/web.py:2278 +#: cps/web.py:2308 msgid "Wrong Username or Password" msgstr "Błędna nazwa użytkownika lub hasło" -#: cps/web.py:2284 cps/web.py:2305 +#: cps/web.py:2314 cps/web.py:2335 msgid "login" msgstr "logowanie" -#: cps/web.py:2317 cps/web.py:2348 +#: cps/web.py:2347 cps/web.py:2378 msgid "Token not found" msgstr "" -#: cps/web.py:2325 cps/web.py:2356 +#: cps/web.py:2355 cps/web.py:2386 msgid "Token has expired" msgstr "" -#: cps/web.py:2333 +#: cps/web.py:2363 msgid "Success! Please return to your device" msgstr "" -#: cps/web.py:2383 +#: cps/web.py:2413 msgid "Please configure the SMTP mail settings first..." msgstr "Proszę najpierw skonfigurować ustawienia SMTP poczty e-mail..." -#: cps/web.py:2388 +#: cps/web.py:2418 #, python-format msgid "Book successfully queued for sending to %(kindlemail)s" msgstr "" -#: cps/web.py:2392 +#: cps/web.py:2422 #, python-format msgid "There was an error sending this book: %(res)s" msgstr "Wystąpił błąd podczas wysyłania tej książki: %(res)s" -#: cps/web.py:2394 cps/web.py:3199 +#: cps/web.py:2424 cps/web.py:3229 msgid "Please configure your kindle e-mail address first..." msgstr "" -#: cps/web.py:2405 cps/web.py:2457 +#: cps/web.py:2435 cps/web.py:2487 msgid "Invalid shelf specified" msgstr "" -#: cps/web.py:2412 +#: cps/web.py:2442 #, python-format msgid "Sorry you are not allowed to add a book to the the shelf: %(shelfname)s" msgstr "" -#: cps/web.py:2420 +#: cps/web.py:2450 msgid "You are not allowed to edit public shelves" msgstr "" -#: cps/web.py:2429 +#: cps/web.py:2459 #, python-format msgid "Book is already part of the shelf: %(shelfname)s" msgstr "" -#: cps/web.py:2443 +#: cps/web.py:2473 #, python-format msgid "Book has been added to shelf: %(sname)s" msgstr "Książka została dodana do półki: %(sname)s" -#: cps/web.py:2462 +#: cps/web.py:2492 #, python-format msgid "You are not allowed to add a book to the the shelf: %(name)s" msgstr "" -#: cps/web.py:2467 +#: cps/web.py:2497 msgid "User is not allowed to edit public shelves" msgstr "" -#: cps/web.py:2485 +#: cps/web.py:2515 #, python-format msgid "Books are already part of the shelf: %(name)s" msgstr "" -#: cps/web.py:2499 +#: cps/web.py:2529 #, python-format msgid "Books have been added to shelf: %(sname)s" msgstr "" -#: cps/web.py:2501 +#: cps/web.py:2531 #, python-format msgid "Could not add books to shelf: %(sname)s" msgstr "" -#: cps/web.py:2538 +#: cps/web.py:2568 #, python-format msgid "Book has been removed from shelf: %(sname)s" msgstr "Książka została usunięta z półki: %(sname)s" -#: cps/web.py:2544 +#: cps/web.py:2574 #, python-format msgid "Sorry you are not allowed to remove a book from this shelf: %(sname)s" msgstr "" -#: cps/web.py:2565 cps/web.py:2589 +#: cps/web.py:2595 cps/web.py:2619 #, python-format msgid "A shelf with the name '%(title)s' already exists." msgstr "Półka o nazwie '%(title)s' już istnieje." -#: cps/web.py:2570 +#: cps/web.py:2600 #, python-format msgid "Shelf %(title)s created" msgstr "Półka %(title)s została utworzona" -#: cps/web.py:2572 cps/web.py:2600 +#: cps/web.py:2602 cps/web.py:2630 msgid "There was an error" msgstr "Wystąpił błąd" -#: cps/web.py:2573 cps/web.py:2575 +#: cps/web.py:2603 cps/web.py:2605 msgid "create a shelf" msgstr "utwórz półkę" -#: cps/web.py:2598 +#: cps/web.py:2628 #, python-format msgid "Shelf %(title)s changed" msgstr "Półka %(title)s została zmieniona" -#: cps/web.py:2601 cps/web.py:2603 +#: cps/web.py:2631 cps/web.py:2633 msgid "Edit a shelf" msgstr "Edytuj półkę" -#: cps/web.py:2624 +#: cps/web.py:2654 #, python-format msgid "successfully deleted shelf %(name)s" msgstr "pomyślnie usunięto półkę %(name)s" -#: cps/web.py:2651 +#: cps/web.py:2681 #, python-format msgid "Shelf: '%(name)s'" msgstr "Półka: '%(name)s'" -#: cps/web.py:2654 +#: cps/web.py:2684 msgid "Error opening shelf. Shelf does not exist or is not accessible" msgstr "" -#: cps/web.py:2685 +#: cps/web.py:2715 #, python-format msgid "Change order of Shelf: '%(name)s'" msgstr "Zmieniono kolejność półki: '%(name)s'" -#: cps/web.py:2714 cps/web.py:3152 +#: cps/web.py:2744 cps/web.py:3182 msgid "E-mail is not from valid domain" msgstr "" -#: cps/web.py:2716 cps/web.py:2758 cps/web.py:2761 +#: cps/web.py:2746 cps/web.py:2788 cps/web.py:2791 #, python-format msgid "%(name)s's profile" msgstr "Profil użytkownika %(name)s" -#: cps/web.py:2756 +#: cps/web.py:2786 msgid "Found an existing account for this e-mail address." msgstr "" -#: cps/web.py:2759 +#: cps/web.py:2789 msgid "Profile updated" msgstr "Zaktualizowano profil" -#: cps/web.py:2790 +#: cps/web.py:2820 msgid "Admin page" msgstr "Portal administracyjny" -#: cps/web.py:2875 cps/web.py:3055 +#: cps/web.py:2905 cps/web.py:3085 msgid "Calibre-Web configuration updated" msgstr "Konfiguracja Calibre-Web została zaktualizowana" -#: cps/templates/admin.html:100 cps/web.py:2889 +#: cps/templates/admin.html:100 cps/web.py:2919 msgid "UI Configuration" msgstr "" -#: cps/web.py:2907 +#: cps/web.py:2937 msgid "Import of optional Google Drive requirements missing" msgstr "" -#: cps/web.py:2910 +#: cps/web.py:2940 msgid "client_secrets.json is missing or not readable" msgstr "" -#: cps/web.py:2915 cps/web.py:2944 +#: cps/web.py:2945 cps/web.py:2974 msgid "client_secrets.json is not configured for web application" msgstr "" -#: cps/templates/admin.html:99 cps/web.py:2947 cps/web.py:2973 cps/web.py:2985 -#: cps/web.py:3030 cps/web.py:3045 cps/web.py:3064 cps/web.py:3072 -#: cps/web.py:3088 +#: cps/templates/admin.html:99 cps/web.py:2977 cps/web.py:3003 cps/web.py:3015 +#: cps/web.py:3060 cps/web.py:3075 cps/web.py:3094 cps/web.py:3102 +#: cps/web.py:3118 msgid "Basic Configuration" msgstr "Podstawowa konfiguracja" -#: cps/web.py:2970 +#: cps/web.py:3000 msgid "Keyfile location is not valid, please enter correct path" msgstr "" -#: cps/web.py:2982 +#: cps/web.py:3012 msgid "Certfile location is not valid, please enter correct path" msgstr "" -#: cps/web.py:3027 +#: cps/web.py:3057 msgid "Logfile location is not valid, please enter correct path" msgstr "" -#: cps/web.py:3068 +#: cps/web.py:3098 msgid "DB location is not valid, please enter correct path" msgstr "Lokalizacja bazy danych jest nieprawidłowa, wpisz poprawną ścieżkę" -#: cps/templates/admin.html:33 cps/web.py:3148 cps/web.py:3154 cps/web.py:3170 +#: cps/templates/admin.html:33 cps/web.py:3178 cps/web.py:3184 cps/web.py:3200 msgid "Add new user" msgstr "Dodaj nowego użytkownika" -#: cps/web.py:3160 +#: cps/web.py:3190 #, python-format msgid "User '%(user)s' created" msgstr "Użytkownik '%(user)s' został utworzony" -#: cps/web.py:3164 +#: cps/web.py:3194 msgid "Found an existing account for this e-mail address or nickname." msgstr "" -#: cps/web.py:3194 +#: cps/web.py:3224 #, python-format msgid "Test e-mail successfully send to %(kindlemail)s" msgstr "" -#: cps/web.py:3197 +#: cps/web.py:3227 #, python-format msgid "There was an error sending the Test e-mail: %(res)s" msgstr "" -#: cps/web.py:3201 +#: cps/web.py:3231 msgid "E-mail server settings updated" msgstr "" -#: cps/web.py:3202 +#: cps/web.py:3232 msgid "Edit e-mail server settings" msgstr "" -#: cps/web.py:3227 +#: cps/web.py:3257 #, python-format msgid "User '%(nick)s' deleted" msgstr "Użytkownik '%(nick)s' został usunięty" -#: cps/web.py:3340 +#: cps/web.py:3370 #, python-format msgid "User '%(nick)s' updated" msgstr "Użytkownik '%(nick)s' został zaktualizowany" -#: cps/web.py:3343 +#: cps/web.py:3373 msgid "An unknown error occured." msgstr "Wystąpił nieznany błąd." -#: cps/web.py:3345 +#: cps/web.py:3375 #, python-format msgid "Edit User %(nick)s" msgstr "Edytuj użytkownika %(nick)s" -#: cps/web.py:3362 +#: cps/web.py:3392 #, python-format msgid "Password for user %(user)s reset" msgstr "" -#: cps/web.py:3376 cps/web.py:3582 +#: cps/web.py:3406 cps/web.py:3598 msgid "Error opening eBook. File does not exist or file is not accessible" msgstr "" -#: cps/web.py:3404 +#: cps/web.py:3434 msgid "edit metadata" msgstr "edytuj metadane" -#: cps/web.py:3497 cps/web.py:3743 +#: cps/web.py:3527 cps/web.py:3760 #, python-format msgid "File extension '%(ext)s' is not allowed to be uploaded to this server" msgstr "Rozszerzenie pliku '%(ext)s' nie jest dozwolone do przesłania na ten serwer" -#: cps/web.py:3501 cps/web.py:3746 +#: cps/web.py:3531 cps/web.py:3763 msgid "File to be uploaded must have an extension" msgstr "Plik do przesłania musi mieć rozszerzenie" -#: cps/web.py:3513 cps/web.py:3765 +#: cps/web.py:3543 cps/web.py:3782 #, python-format msgid "Failed to create path %(path)s (Permission denied)." msgstr "Nie udało się utworzyć łącza %(path)s (Odmowa dostępu)." -#: cps/web.py:3518 +#: cps/web.py:3548 #, python-format msgid "Failed to store file %(file)s." msgstr "" -#: cps/web.py:3535 +#: cps/web.py:3565 #, python-format msgid "File format %(ext)s added to %(book)s" msgstr "" -#: cps/web.py:3553 -#, python-format -msgid "Failed to create path for cover %(path)s (Permission denied)." -msgstr "" - -#: cps/web.py:3561 -#, python-format -msgid "Failed to store cover-file %(cover)s." +#: cps/web.py:3579 cps/web.py:3652 +msgid "Cover is not a supported imageformat (jpg/png/webp), can't save" msgstr "" -#: cps/web.py:3564 -msgid "Cover-file is not a valid image file" -msgstr "" - -#: cps/web.py:3594 cps/web.py:3603 +#: cps/web.py:3611 cps/web.py:3620 msgid "unknown" msgstr "" -#: cps/web.py:3635 -msgid "Cover is not a jpg file, can't save" -msgstr "" - -#: cps/web.py:3683 +#: cps/web.py:3700 #, python-format msgid "%(langname)s is not a valid language" msgstr "" -#: cps/web.py:3714 +#: cps/web.py:3731 msgid "Metadata successfully updated" msgstr "" -#: cps/web.py:3723 +#: cps/web.py:3740 msgid "Error editing book, please check logfile for details" msgstr "" -#: cps/web.py:3769 +#: cps/web.py:3786 #, python-format msgid "Failed to store file %(file)s (Permission denied)." msgstr "Nie można przechowywać pliku %(file)s (Odmowa dostępu)." -#: cps/web.py:3774 +#: cps/web.py:3791 #, python-format msgid "Failed to delete file %(file)s (Permission denied)." msgstr "Nie udało się usunąć pliku %(file)s (Odmowa dostępu)." -#: cps/web.py:3857 +#: cps/web.py:3873 #, python-format msgid "File %(title)s" msgstr "" -#: cps/web.py:3886 +#: cps/web.py:3902 msgid "Source or destination format for conversion missing" msgstr "" -#: cps/web.py:3896 +#: cps/web.py:3912 #, python-format msgid "Book successfully queued for converting to %(book_format)s" msgstr "" -#: cps/web.py:3900 +#: cps/web.py:3916 #, python-format msgid "There was an error converting this book: %(res)s" msgstr "" @@ -1290,7 +1281,7 @@ msgstr "Liczba losowych książek do pokazania" msgid "No. of authors to show before hiding (0=disable hiding)" msgstr "" -#: cps/templates/config_view_edit.html:35 cps/templates/readcbr.html:108 +#: cps/templates/config_view_edit.html:35 cps/templates/readcbr.html:118 msgid "Theme" msgstr "" @@ -1604,7 +1595,7 @@ msgid "Advanced Search" msgstr "Zaawansowane wyszukiwanie" #: cps/templates/layout.html:76 cps/templates/read.html:71 -#: cps/templates/readcbr.html:79 cps/templates/readcbr.html:103 +#: cps/templates/readcbr.html:89 cps/templates/readcbr.html:113 msgid "Settings" msgstr "" @@ -1724,98 +1715,106 @@ msgstr "" msgid "Reflow text when sidebars are open." msgstr "Tekst pływający, gdy paski boczne są otwarte." -#: cps/templates/readcbr.html:84 +#: cps/templates/readcbr.html:94 msgid "Keyboard Shortcuts" msgstr "" -#: cps/templates/readcbr.html:87 +#: cps/templates/readcbr.html:97 msgid "Previous Page" msgstr "" -#: cps/templates/readcbr.html:88 +#: cps/templates/readcbr.html:98 msgid "Next Page" msgstr "" -#: cps/templates/readcbr.html:89 +#: cps/templates/readcbr.html:99 msgid "Scale to Best" msgstr "" -#: cps/templates/readcbr.html:90 +#: cps/templates/readcbr.html:100 msgid "Scale to Width" msgstr "" -#: cps/templates/readcbr.html:91 +#: cps/templates/readcbr.html:101 msgid "Scale to Height" msgstr "" -#: cps/templates/readcbr.html:92 +#: cps/templates/readcbr.html:102 msgid "Scale to Native" msgstr "" -#: cps/templates/readcbr.html:93 +#: cps/templates/readcbr.html:103 msgid "Rotate Right" msgstr "" -#: cps/templates/readcbr.html:94 +#: cps/templates/readcbr.html:104 msgid "Rotate Left" msgstr "" -#: cps/templates/readcbr.html:95 +#: cps/templates/readcbr.html:105 msgid "Flip Image" msgstr "" -#: cps/templates/readcbr.html:111 +#: cps/templates/readcbr.html:121 msgid "Light" msgstr "" -#: cps/templates/readcbr.html:112 +#: cps/templates/readcbr.html:122 msgid "Dark" msgstr "" -#: cps/templates/readcbr.html:117 +#: cps/templates/readcbr.html:127 msgid "Scale" msgstr "" -#: cps/templates/readcbr.html:120 +#: cps/templates/readcbr.html:130 msgid "Best" msgstr "" -#: cps/templates/readcbr.html:121 +#: cps/templates/readcbr.html:131 msgid "Width" msgstr "" -#: cps/templates/readcbr.html:122 +#: cps/templates/readcbr.html:132 msgid "Height" msgstr "" -#: cps/templates/readcbr.html:123 +#: cps/templates/readcbr.html:133 msgid "Native" msgstr "" -#: cps/templates/readcbr.html:128 +#: cps/templates/readcbr.html:138 msgid "Rotate" msgstr "" -#: cps/templates/readcbr.html:139 +#: cps/templates/readcbr.html:149 msgid "Flip" msgstr "" -#: cps/templates/readcbr.html:142 +#: cps/templates/readcbr.html:152 msgid "Horizontal" msgstr "" -#: cps/templates/readcbr.html:143 +#: cps/templates/readcbr.html:153 msgid "Vertical" msgstr "" +#: cps/templates/readcbr.html:158 +msgid "Direction" +msgstr "" + +#: cps/templates/readcbr.html:161 +msgid "Left to Right" +msgstr "" + +#: cps/templates/readcbr.html:162 +msgid "Right to Left" +msgstr "" + #: cps/templates/readpdf.html:29 msgid "PDF.js viewer" msgstr "PDF.js viewer" -#: cps/templates/readpdf.html:418 -msgid "Preparing document for printing..." -msgstr "" - #: cps/templates/readtxt.html:6 msgid "Basic txt Reader" msgstr "Podstawowy czytnik txt" @@ -3262,3 +3261,18 @@ msgstr "Ostatnio pobierane" #~ msgid "Zaza" #~ msgstr "zazaki" +#~ msgid "Failed to create path for cover %(path)s (Permission denied)." +#~ msgstr "" + +#~ msgid "Failed to store cover-file %(cover)s." +#~ msgstr "" + +#~ msgid "Cover-file is not a valid image file" +#~ msgstr "" + +#~ msgid "Cover is not a jpg file, can't save" +#~ msgstr "" + +#~ msgid "Preparing document for printing..." +#~ msgstr "" + diff --git a/cps/translations/ru/LC_MESSAGES/messages.mo b/cps/translations/ru/LC_MESSAGES/messages.mo index 3860f5cb1da645d0de93ee0cbe8ce26aeec50c90..132e6fca711602175527578272cf1c105b4a1d5f 100644 GIT binary patch delta 15145 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\n" "Language: ru\n" @@ -18,8 +18,9 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.6.0\n" -#: cps/book_formats.py:152 cps/book_formats.py:153 cps/book_formats.py:157 -#: cps/book_formats.py:161 cps/converter.py:29 cps/converter.py:45 +#: cps/book_formats.py:199 cps/book_formats.py:200 cps/book_formats.py:204 +#: cps/book_formats.py:208 cps/book_formats.py:212 cps/converter.py:29 +#: cps/converter.py:45 msgid "not installed" msgstr "не установлено" @@ -31,133 +32,133 @@ msgstr "Отсутствуют разрешения на выполнение" msgid "not configured" msgstr "не настроен" -#: cps/helper.py:72 +#: cps/helper.py:79 #, python-format msgid "%(format)s format not found for book id: %(book)d" msgstr "%(format)s форма не найден для книги с id: %(book)d" -#: cps/helper.py:84 +#: cps/helper.py:91 #, python-format msgid "%(format)s not found on Google Drive: %(fn)s" msgstr "%(format)s не найден на Google Drive: %(fn)s" -#: cps/helper.py:91 cps/helper.py:199 cps/templates/detail.html:45 +#: cps/helper.py:98 cps/helper.py:204 cps/templates/detail.html:45 #: cps/templates/detail.html:49 msgid "Send to Kindle" msgstr "Отправить на Kindle" -#: cps/helper.py:92 cps/helper.py:110 cps/helper.py:201 +#: cps/helper.py:99 cps/helper.py:117 cps/helper.py:206 msgid "This e-mail has been sent via Calibre-Web." msgstr "Это электронное письмо было отправлено через Caliber-Web." -#: cps/helper.py:103 +#: cps/helper.py:110 #, python-format msgid "%(format)s not found: %(fn)s" msgstr "%(format)s не найден: %(fn)s" -#: cps/helper.py:108 +#: cps/helper.py:115 msgid "Calibre-Web test e-mail" msgstr "Тестовый e-mail для Calibre-Web" -#: cps/helper.py:109 +#: cps/helper.py:116 msgid "Test e-mail" msgstr "Тестовый e-mail" -#: cps/helper.py:125 +#: cps/helper.py:132 msgid "Get Started with Calibre-Web" msgstr "Начать работать с Calibre-Web" -#: cps/helper.py:126 +#: cps/helper.py:133 #, python-format msgid "Registration e-mail for user: %(name)s" msgstr "Регистрационный e-mail для пользователя: %(name)s" -#: cps/helper.py:139 cps/helper.py:141 cps/helper.py:143 cps/helper.py:145 -#: cps/helper.py:151 cps/helper.py:153 cps/helper.py:155 cps/helper.py:157 +#: cps/helper.py:146 cps/helper.py:148 cps/helper.py:150 cps/helper.py:158 +#: cps/helper.py:160 cps/helper.py:162 #, python-format msgid "Send %(format)s to Kindle" msgstr "" -#: cps/helper.py:161 cps/helper.py:165 +#: cps/helper.py:166 #, python-format msgid "Convert %(orig)s to %(format)s and send to Kindle" msgstr "" -#: cps/helper.py:200 +#: cps/helper.py:205 #, python-format msgid "E-mail: %(book)s" msgstr "Эл. почта: %(book)s" -#: cps/helper.py:203 +#: cps/helper.py:208 msgid "The requested file could not be read. Maybe wrong permissions?" msgstr "Запрашиваемый файл не может быть прочитан. Возможно не верные разрешения?" -#: cps/helper.py:311 +#: cps/helper.py:316 #, python-format msgid "Rename title from: '%(src)s' to '%(dest)s' failed with error: %(error)s" msgstr "Переименовывание заголовка с: '%(src)s' на '%(dest)s' не удалось из-за ошибки: %(error)s" -#: cps/helper.py:321 +#: cps/helper.py:326 #, python-format msgid "Rename author from: '%(src)s' to '%(dest)s' failed with error: %(error)s" msgstr "Переименовывание автора с: '%(src)s' на '%(dest)s' не удалось из-за ошибки: %(error)s" -#: cps/helper.py:335 +#: cps/helper.py:340 #, python-format msgid "Rename file in path '%(src)s' to '%(dest)s' failed with error: %(error)s" msgstr "" -#: cps/helper.py:361 cps/helper.py:371 cps/helper.py:379 +#: cps/helper.py:366 cps/helper.py:376 cps/helper.py:384 #, python-format msgid "File %(file)s not found on Google Drive" msgstr "Файл %(file)s не найден на Google Drive" -#: cps/helper.py:400 +#: cps/helper.py:405 #, python-format msgid "Book path %(path)s not found on Google Drive" msgstr "Путь книги %(path)s не найден на Google Drive" -#: cps/helper.py:508 +#: cps/helper.py:556 msgid "Error excecuting UnRar" msgstr "Ошибка извлечения UnRar" -#: cps/helper.py:510 +#: cps/helper.py:558 msgid "Unrar binary file not found" msgstr "Unrar двочиный файл не найден" -#: cps/helper.py:541 +#: cps/helper.py:589 msgid "Waiting" msgstr "Ожидание" -#: cps/helper.py:543 +#: cps/helper.py:591 msgid "Failed" msgstr "Неудачно" -#: cps/helper.py:545 +#: cps/helper.py:593 msgid "Started" msgstr "Начало" -#: cps/helper.py:547 +#: cps/helper.py:595 msgid "Finished" msgstr "Закончено" -#: cps/helper.py:549 +#: cps/helper.py:597 msgid "Unknown Status" msgstr "Неизвестный статус" -#: cps/helper.py:554 +#: cps/helper.py:602 msgid "E-mail: " msgstr "E-mail: " -#: cps/helper.py:556 cps/helper.py:560 +#: cps/helper.py:604 cps/helper.py:608 msgid "Convert: " msgstr "Конвертировать:" -#: cps/helper.py:558 +#: cps/helper.py:606 msgid "Upload: " msgstr "Загрузить:" -#: cps/helper.py:562 +#: cps/helper.py:610 msgid "Unknown Task: " msgstr "Неизвестная задача:" @@ -169,19 +170,19 @@ msgstr "Некорректные данные при чтении информа msgid "No update available. You already have the latest version installed" msgstr "Обновление недоступно. Вы используете самую последнюю версию" -#: cps/updater.py:270 cps/updater.py:501 cps/updater.py:503 cps/web.py:1187 +#: cps/updater.py:270 cps/updater.py:501 cps/updater.py:503 cps/web.py:1206 msgid "HTTP Error" msgstr "Ошибка HTTP" -#: cps/updater.py:272 cps/updater.py:505 cps/web.py:1188 +#: cps/updater.py:272 cps/updater.py:505 cps/web.py:1207 msgid "Connection error" msgstr "Ошибка соединения" -#: cps/updater.py:274 cps/updater.py:507 cps/web.py:1189 +#: cps/updater.py:274 cps/updater.py:507 cps/web.py:1208 msgid "Timeout while establishing connection" msgstr "Таймаут при установлении соединения" -#: cps/updater.py:276 cps/updater.py:509 cps/web.py:1190 +#: cps/updater.py:276 cps/updater.py:509 cps/web.py:1209 msgid "General error" msgstr "Общая ошибка" @@ -202,555 +203,545 @@ msgstr "" msgid "A new update is available. Click on the button below to update to version: %(version)s" msgstr "" -#: cps/updater.py:491 cps/web.py:2771 +#: cps/updater.py:491 cps/web.py:2801 msgid "Unknown" msgstr "Неизвестно" -#: cps/web.py:1180 +#: cps/web.py:1199 msgid "Requesting update package" msgstr "Проверка обновлений" -#: cps/web.py:1181 +#: cps/web.py:1200 msgid "Downloading update package" msgstr "Загрузка обновлений" -#: cps/web.py:1182 +#: cps/web.py:1201 msgid "Unzipping update package" msgstr "Распаковка обновлений" -#: cps/web.py:1183 +#: cps/web.py:1202 msgid "Replacing files" msgstr "Замена файлов" -#: cps/web.py:1184 +#: cps/web.py:1203 msgid "Database connections are closed" msgstr "Соеднинения с базой данных закрыты" -#: cps/web.py:1185 +#: cps/web.py:1204 msgid "Stopping server" msgstr "Остановка сервера" -#: cps/web.py:1186 +#: cps/web.py:1205 msgid "Update finished, please press okay and reload page" msgstr "Обновления установлены, нажмите okay и перезагрузите страницу" -#: cps/web.py:1187 cps/web.py:1188 cps/web.py:1189 cps/web.py:1190 +#: cps/web.py:1206 cps/web.py:1207 cps/web.py:1208 cps/web.py:1209 msgid "Update failed:" msgstr "Ошибка обновления:" -#: cps/web.py:1213 +#: cps/web.py:1235 msgid "Recently Added Books" msgstr "Недавно Добавленные Книги" -#: cps/web.py:1223 +#: cps/web.py:1245 msgid "Newest Books" msgstr "Новые Книги" -#: cps/web.py:1235 +#: cps/web.py:1257 msgid "Oldest Books" msgstr "Старые Книги" -#: cps/web.py:1247 +#: cps/web.py:1269 msgid "Books (A-Z)" msgstr "Книги (А-Я)" -#: cps/web.py:1258 +#: cps/web.py:1280 msgid "Books (Z-A)" msgstr "Книги (Я-А)" -#: cps/web.py:1287 +#: cps/web.py:1309 msgid "Hot Books (most downloaded)" msgstr "Популярные книги (часто загружаемые)" -#: cps/web.py:1300 +#: cps/web.py:1322 msgid "Best rated books" msgstr "Книги с наивысшим рейтингом" -#: cps/templates/index.xml:39 cps/web.py:1313 +#: cps/templates/index.xml:39 cps/web.py:1335 msgid "Random Books" msgstr "Случайный выбор" -#: cps/web.py:1340 cps/web.py:1596 cps/web.py:2140 +#: cps/web.py:1362 cps/web.py:1618 cps/web.py:2167 msgid "Error opening eBook. File does not exist or file is not accessible:" msgstr "Невозможно открыть книгу. Файл не существует или недоступен." -#: cps/web.py:1369 +#: cps/web.py:1391 msgid "Publisher list" msgstr "Список издателей" -#: cps/web.py:1384 +#: cps/web.py:1406 #, python-format msgid "Publisher: %(name)s" msgstr "Издатель: %(name)s" -#: cps/templates/index.xml:83 cps/web.py:1416 +#: cps/templates/index.xml:83 cps/web.py:1438 msgid "Series list" msgstr "Серии" -#: cps/web.py:1430 +#: cps/web.py:1452 #, python-format msgid "Series: %(serie)s" msgstr "Серии: %(serie)s" -#: cps/web.py:1456 +#: cps/web.py:1478 msgid "Available languages" msgstr "Доступные языки" -#: cps/web.py:1476 +#: cps/web.py:1498 #, python-format msgid "Language: %(name)s" msgstr "Язык: %(name)s" -#: cps/templates/index.xml:76 cps/web.py:1487 +#: cps/templates/index.xml:76 cps/web.py:1509 msgid "Category list" msgstr "Категории" -#: cps/web.py:1501 +#: cps/web.py:1523 #, python-format msgid "Category: %(name)s" msgstr "Категория: %(name)s" -#: cps/templates/layout.html:73 cps/web.py:1632 +#: cps/templates/layout.html:73 cps/web.py:1654 msgid "Tasks" msgstr "Задания" -#: cps/web.py:1666 +#: cps/web.py:1688 msgid "Statistics" msgstr "Статистика" -#: cps/web.py:1734 +#: cps/web.py:1756 msgid "Google Drive setup not completed, try to deactivate and activate Google Drive again" msgstr "" -#: cps/web.py:1779 +#: cps/web.py:1801 msgid "Callback domain is not verified, please follow steps to verify domain in google developer console" msgstr "Не удалось проверить домен обратного вызова, пожалуйста, выполните шаги для проверки домена в консоли разработчика Google." -#: cps/web.py:1855 +#: cps/web.py:1877 msgid "Server restarted, please reload page" msgstr "Сервер перезагружен, пожалуйста, перезагрузите страницу" -#: cps/web.py:1858 +#: cps/web.py:1880 msgid "Performing shutdown of server, please close window" msgstr "Производится остановка сервера, пожалуйста, закройте окно" -#: cps/web.py:1938 +#: cps/web.py:1959 msgid "Published after " msgstr "Опубликовано до " -#: cps/web.py:1945 +#: cps/web.py:1966 msgid "Published before " msgstr "Опубликовано после " -#: cps/web.py:1959 +#: cps/web.py:1980 #, python-format msgid "Rating <= %(rating)s" msgstr "Рейтинг <= %(rating)s" -#: cps/web.py:1961 +#: cps/web.py:1982 #, python-format msgid "Rating >= %(rating)s" msgstr "Рейтинг >= %(rating)s" -#: cps/web.py:2022 cps/web.py:2031 +#: cps/web.py:2042 cps/web.py:2051 msgid "search" msgstr "поиск" #: cps/templates/index.xml:47 cps/templates/index.xml:51 -#: cps/templates/layout.html:148 cps/web.py:2099 +#: cps/templates/layout.html:148 cps/web.py:2122 msgid "Read Books" msgstr "Прочитанные Книги" #: cps/templates/index.xml:55 cps/templates/index.xml:59 -#: cps/templates/layout.html:150 cps/web.py:2102 +#: cps/templates/layout.html:150 cps/web.py:2125 msgid "Unread Books" msgstr "Непрочитанные Книги" -#: cps/web.py:2150 cps/web.py:2152 cps/web.py:2154 cps/web.py:2166 +#: cps/web.py:2177 cps/web.py:2179 cps/web.py:2181 cps/web.py:2193 msgid "Read a Book" msgstr "Читать Книгу" -#: cps/web.py:2225 cps/web.py:3146 +#: cps/web.py:2205 +msgid "Error opening eBook. Fileformat is not supported." +msgstr "" + +#: cps/web.py:2255 cps/web.py:3176 msgid "Please fill out all fields!" msgstr "Пожалуйста, заполните все поля!" -#: cps/web.py:2226 cps/web.py:2248 cps/web.py:2252 cps/web.py:2257 -#: cps/web.py:2259 +#: cps/web.py:2256 cps/web.py:2278 cps/web.py:2282 cps/web.py:2287 +#: cps/web.py:2289 msgid "register" msgstr "регистрация" -#: cps/web.py:2247 cps/web.py:3365 +#: cps/web.py:2277 cps/web.py:3395 msgid "An unknown error occurred. Please try again later." msgstr "Неизвестная ошибка. Попробуйте позже." -#: cps/web.py:2250 +#: cps/web.py:2280 msgid "Your e-mail is not allowed to register" msgstr "Ваш e-mail не подходит для регистрации" -#: cps/web.py:2253 +#: cps/web.py:2283 msgid "Confirmation e-mail was send to your e-mail account." msgstr "Письмо с подтверждением отправлено вам на e-mail" -#: cps/web.py:2256 +#: cps/web.py:2286 msgid "This username or e-mail address is already in use." msgstr "Этот никнейм или e-mail уже используются" -#: cps/web.py:2273 cps/web.py:2369 +#: cps/web.py:2303 cps/web.py:2399 #, python-format msgid "you are now logged in as: '%(nickname)s'" msgstr "Вы вошли как пользователь '%(nickname)s'" -#: cps/web.py:2278 +#: cps/web.py:2308 msgid "Wrong Username or Password" msgstr "Ошибка в имени пользователя или пароле" -#: cps/web.py:2284 cps/web.py:2305 +#: cps/web.py:2314 cps/web.py:2335 msgid "login" msgstr "войти" -#: cps/web.py:2317 cps/web.py:2348 +#: cps/web.py:2347 cps/web.py:2378 msgid "Token not found" msgstr "Ключ не найден" -#: cps/web.py:2325 cps/web.py:2356 +#: cps/web.py:2355 cps/web.py:2386 msgid "Token has expired" msgstr "Ключ просрочен" -#: cps/web.py:2333 +#: cps/web.py:2363 msgid "Success! Please return to your device" msgstr "Успешно! Пожалуйста, проверьте свое устройство" -#: cps/web.py:2383 +#: cps/web.py:2413 msgid "Please configure the SMTP mail settings first..." msgstr "Пожалуйста, сначала сконфигурируйте параметры SMTP" -#: cps/web.py:2388 +#: cps/web.py:2418 #, python-format msgid "Book successfully queued for sending to %(kindlemail)s" msgstr "Книга успешно поставлена в очередь для отправки на %(kindlemail)s" -#: cps/web.py:2392 +#: cps/web.py:2422 #, python-format msgid "There was an error sending this book: %(res)s" msgstr "Ошибка при отправке книги: %(res)s" -#: cps/web.py:2394 cps/web.py:3199 +#: cps/web.py:2424 cps/web.py:3229 msgid "Please configure your kindle e-mail address first..." msgstr "Пожалуйста, сначала настройте e-mail на вашем kindle..." -#: cps/web.py:2405 cps/web.py:2457 +#: cps/web.py:2435 cps/web.py:2487 msgid "Invalid shelf specified" msgstr "Указана неверная полка" -#: cps/web.py:2412 +#: cps/web.py:2442 #, python-format msgid "Sorry you are not allowed to add a book to the the shelf: %(shelfname)s" msgstr "Извините, но вам не разрешено добавлять книгу на полку: %(shelfname)s" -#: cps/web.py:2420 +#: cps/web.py:2450 msgid "You are not allowed to edit public shelves" msgstr "Вы не можете редактировать общедоступные полки" -#: cps/web.py:2429 +#: cps/web.py:2459 #, python-format msgid "Book is already part of the shelf: %(shelfname)s" msgstr "Книги уже размещены на полке: %(shelfname)s" -#: cps/web.py:2443 +#: cps/web.py:2473 #, python-format msgid "Book has been added to shelf: %(sname)s" msgstr "Книга добавлена на книжную полку: %(sname)s" -#: cps/web.py:2462 +#: cps/web.py:2492 #, python-format msgid "You are not allowed to add a book to the the shelf: %(name)s" msgstr "Вам не разрешено добавлять книгу на полку: %(name)s" -#: cps/web.py:2467 +#: cps/web.py:2497 msgid "User is not allowed to edit public shelves" msgstr "Пользователь не может редактировать общедоступные полки" -#: cps/web.py:2485 +#: cps/web.py:2515 #, python-format msgid "Books are already part of the shelf: %(name)s" msgstr "Книги уже размещены на полке: %(name)s" -#: cps/web.py:2499 +#: cps/web.py:2529 #, python-format msgid "Books have been added to shelf: %(sname)s" msgstr "Книги добавлены в полку: %(sname)s" -#: cps/web.py:2501 +#: cps/web.py:2531 #, python-format msgid "Could not add books to shelf: %(sname)s" msgstr "Не удалось добавить книги на полку: %(sname)s" -#: cps/web.py:2538 +#: cps/web.py:2568 #, python-format msgid "Book has been removed from shelf: %(sname)s" msgstr "Книга удалена с полки: %(sname)s" -#: cps/web.py:2544 +#: cps/web.py:2574 #, python-format msgid "Sorry you are not allowed to remove a book from this shelf: %(sname)s" msgstr "Извините, вы не можете удалить книгу с полки: %(sname)s" -#: cps/web.py:2565 cps/web.py:2589 +#: cps/web.py:2595 cps/web.py:2619 #, python-format msgid "A shelf with the name '%(title)s' already exists." msgstr "Полка с названием '%(title)s' уже существует." -#: cps/web.py:2570 +#: cps/web.py:2600 #, python-format msgid "Shelf %(title)s created" msgstr "Создана полка %(title)s" -#: cps/web.py:2572 cps/web.py:2600 +#: cps/web.py:2602 cps/web.py:2630 msgid "There was an error" msgstr "Произошла ошибка" -#: cps/web.py:2573 cps/web.py:2575 +#: cps/web.py:2603 cps/web.py:2605 msgid "create a shelf" msgstr "создать полку" -#: cps/web.py:2598 +#: cps/web.py:2628 #, python-format msgid "Shelf %(title)s changed" msgstr "Колка %(title)s изменена" -#: cps/web.py:2601 cps/web.py:2603 +#: cps/web.py:2631 cps/web.py:2633 msgid "Edit a shelf" msgstr "Изменить полку" -#: cps/web.py:2624 +#: cps/web.py:2654 #, python-format msgid "successfully deleted shelf %(name)s" msgstr "удачно удалена полка %(name)s" -#: cps/web.py:2651 +#: cps/web.py:2681 #, python-format msgid "Shelf: '%(name)s'" msgstr "Полка: '%(name)s'" -#: cps/web.py:2654 +#: cps/web.py:2684 msgid "Error opening shelf. Shelf does not exist or is not accessible" msgstr "Ошибка открытия Полки. Полка не существует или недоступна" -#: cps/web.py:2685 +#: cps/web.py:2715 #, python-format msgid "Change order of Shelf: '%(name)s'" msgstr "Изменить расположение полки '%(name)s'" -#: cps/web.py:2714 cps/web.py:3152 +#: cps/web.py:2744 cps/web.py:3182 msgid "E-mail is not from valid domain" msgstr "E-mail не из существующей доменной зоны" -#: cps/web.py:2716 cps/web.py:2758 cps/web.py:2761 +#: cps/web.py:2746 cps/web.py:2788 cps/web.py:2791 #, python-format msgid "%(name)s's profile" msgstr "Профиль %(name)s" -#: cps/web.py:2756 +#: cps/web.py:2786 msgid "Found an existing account for this e-mail address." msgstr "Этот адрес электронной почты уже зарегистрирован." -#: cps/web.py:2759 +#: cps/web.py:2789 msgid "Profile updated" msgstr "Профиль обновлён" -#: cps/web.py:2790 +#: cps/web.py:2820 msgid "Admin page" msgstr "Администрирование" -#: cps/web.py:2875 cps/web.py:3055 +#: cps/web.py:2905 cps/web.py:3085 msgid "Calibre-Web configuration updated" msgstr "Конфигурация Calibre-Web обновлена" -#: cps/templates/admin.html:100 cps/web.py:2889 +#: cps/templates/admin.html:100 cps/web.py:2919 msgid "UI Configuration" msgstr "Настройка интерфейса" -#: cps/web.py:2907 +#: cps/web.py:2937 msgid "Import of optional Google Drive requirements missing" msgstr "Импорт дополнительных требований к Google Диску отсутствует" -#: cps/web.py:2910 +#: cps/web.py:2940 msgid "client_secrets.json is missing or not readable" msgstr "client_secrets.json отсутствует или его невозможно прочесть" -#: cps/web.py:2915 cps/web.py:2944 +#: cps/web.py:2945 cps/web.py:2974 msgid "client_secrets.json is not configured for web application" msgstr "client_secrets.json не настроен для веб-приложения" -#: cps/templates/admin.html:99 cps/web.py:2947 cps/web.py:2973 cps/web.py:2985 -#: cps/web.py:3030 cps/web.py:3045 cps/web.py:3064 cps/web.py:3072 -#: cps/web.py:3088 +#: cps/templates/admin.html:99 cps/web.py:2977 cps/web.py:3003 cps/web.py:3015 +#: cps/web.py:3060 cps/web.py:3075 cps/web.py:3094 cps/web.py:3102 +#: cps/web.py:3118 msgid "Basic Configuration" msgstr "Настройки сервера" -#: cps/web.py:2970 +#: cps/web.py:3000 msgid "Keyfile location is not valid, please enter correct path" msgstr "Неверное расположение файла-ключа, введите правильный путь" -#: cps/web.py:2982 +#: cps/web.py:3012 msgid "Certfile location is not valid, please enter correct path" msgstr "Неверное расположение сертификата, введите правильный путь" -#: cps/web.py:3027 +#: cps/web.py:3057 msgid "Logfile location is not valid, please enter correct path" msgstr "Неверное расположение лог-файла, введите правильный путь" -#: cps/web.py:3068 +#: cps/web.py:3098 msgid "DB location is not valid, please enter correct path" msgstr "Неверное расположение базы данных, введите правильный путь" -#: cps/templates/admin.html:33 cps/web.py:3148 cps/web.py:3154 cps/web.py:3170 +#: cps/templates/admin.html:33 cps/web.py:3178 cps/web.py:3184 cps/web.py:3200 msgid "Add new user" msgstr "Добавить пользователя" -#: cps/web.py:3160 +#: cps/web.py:3190 #, python-format msgid "User '%(user)s' created" msgstr "Пользователь '%(user)s' добавлен" -#: cps/web.py:3164 +#: cps/web.py:3194 msgid "Found an existing account for this e-mail address or nickname." msgstr "Для этого адреса электронной почты или логина уже есть аккаунт." -#: cps/web.py:3194 +#: cps/web.py:3224 #, python-format msgid "Test e-mail successfully send to %(kindlemail)s" msgstr "Тестовое письмо успешно отправлено на %(kindlemail)s" -#: cps/web.py:3197 +#: cps/web.py:3227 #, python-format msgid "There was an error sending the Test e-mail: %(res)s" msgstr "Произошла ошибка при отправке тестового письма на: %(res)s" -#: cps/web.py:3201 +#: cps/web.py:3231 msgid "E-mail server settings updated" msgstr "Настройки E-mail сервера обновлены" -#: cps/web.py:3202 +#: cps/web.py:3232 msgid "Edit e-mail server settings" msgstr "Изменить настройки e-mail сервера" -#: cps/web.py:3227 +#: cps/web.py:3257 #, python-format msgid "User '%(nick)s' deleted" msgstr "Пользователь '%(nick)s' удалён" -#: cps/web.py:3340 +#: cps/web.py:3370 #, python-format msgid "User '%(nick)s' updated" msgstr "Пользователь '%(nick)s' обновлён" -#: cps/web.py:3343 +#: cps/web.py:3373 msgid "An unknown error occured." msgstr "Произошла неизвестная ошибка." -#: cps/web.py:3345 +#: cps/web.py:3375 #, python-format msgid "Edit User %(nick)s" msgstr "Изменить пользователя %(nick)s" -#: cps/web.py:3362 +#: cps/web.py:3392 #, python-format msgid "Password for user %(user)s reset" msgstr "Пароль для пользователя %(user)s сброшен" -#: cps/web.py:3376 cps/web.py:3582 +#: cps/web.py:3406 cps/web.py:3598 msgid "Error opening eBook. File does not exist or file is not accessible" msgstr "Ошибка при открытии eBook. Файл не существует или файл недоступен" -#: cps/web.py:3404 +#: cps/web.py:3434 msgid "edit metadata" msgstr "изменить метаданные" -#: cps/web.py:3497 cps/web.py:3743 +#: cps/web.py:3527 cps/web.py:3760 #, python-format msgid "File extension '%(ext)s' is not allowed to be uploaded to this server" msgstr "Запрещена загрузка файлов с расширением '%(ext)s'" -#: cps/web.py:3501 cps/web.py:3746 +#: cps/web.py:3531 cps/web.py:3763 msgid "File to be uploaded must have an extension" msgstr "Загружаемый файл должен иметь расширение" -#: cps/web.py:3513 cps/web.py:3765 +#: cps/web.py:3543 cps/web.py:3782 #, python-format msgid "Failed to create path %(path)s (Permission denied)." msgstr "Ошибка при создании пути %(path)s (Доступ запрещён)." -#: cps/web.py:3518 +#: cps/web.py:3548 #, python-format msgid "Failed to store file %(file)s." msgstr "Не удалось сохранить файл %(file)s." -#: cps/web.py:3535 +#: cps/web.py:3565 #, python-format msgid "File format %(ext)s added to %(book)s" msgstr "Формат файла %(ext)s добавлен в %(book)s" -#: cps/web.py:3553 -#, python-format -msgid "Failed to create path for cover %(path)s (Permission denied)." -msgstr "Не удалось создать путь для обложки %(path)s (Доступ запрещён)." - -#: cps/web.py:3561 -#, python-format -msgid "Failed to store cover-file %(cover)s." -msgstr "Не удалось сохранить файл обложки %(cover)s." - -#: cps/web.py:3564 -msgid "Cover-file is not a valid image file" -msgstr "Файл обложки не соответствует изображению" +#: cps/web.py:3579 cps/web.py:3652 +msgid "Cover is not a supported imageformat (jpg/png/webp), can't save" +msgstr "" -#: cps/web.py:3594 cps/web.py:3603 +#: cps/web.py:3611 cps/web.py:3620 msgid "unknown" msgstr "неизвестно" -#: cps/web.py:3635 -msgid "Cover is not a jpg file, can't save" -msgstr "Обложка не jpg файл, невозможно сохранить" - -#: cps/web.py:3683 +#: cps/web.py:3700 #, python-format msgid "%(langname)s is not a valid language" msgstr "%(langname)s не допустимый язык" -#: cps/web.py:3714 +#: cps/web.py:3731 msgid "Metadata successfully updated" msgstr "Метаданные обновлены" -#: cps/web.py:3723 +#: cps/web.py:3740 msgid "Error editing book, please check logfile for details" msgstr "Ошибка редактирования книги. Пожалуйста, проверьте лог-файл для дополнительной информации" -#: cps/web.py:3769 +#: cps/web.py:3786 #, python-format msgid "Failed to store file %(file)s (Permission denied)." msgstr "Ошибка записи файла %(file)s (Доступ запрещён)." -#: cps/web.py:3774 +#: cps/web.py:3791 #, python-format msgid "Failed to delete file %(file)s (Permission denied)." msgstr "Ошибка удаления файла %(file)s (Доступ запрещён)." -#: cps/web.py:3857 +#: cps/web.py:3873 #, python-format msgid "File %(title)s" msgstr "" -#: cps/web.py:3886 +#: cps/web.py:3902 msgid "Source or destination format for conversion missing" msgstr "Исходный или целевой формат для конвертирования отсутствует" -#: cps/web.py:3896 +#: cps/web.py:3912 #, python-format msgid "Book successfully queued for converting to %(book_format)s" msgstr "Книга успешно поставлена в очередь для конвертирования в %(book_format)s" -#: cps/web.py:3900 +#: cps/web.py:3916 #, python-format msgid "There was an error converting this book: %(res)s" msgstr "Произошла ошибка при конвертирования этой книги: %(res)s" @@ -1288,7 +1279,7 @@ msgstr "Количество отображаемых случайных кни msgid "No. of authors to show before hiding (0=disable hiding)" msgstr "" -#: cps/templates/config_view_edit.html:35 cps/templates/readcbr.html:108 +#: cps/templates/config_view_edit.html:35 cps/templates/readcbr.html:118 msgid "Theme" msgstr "Тема" @@ -1602,7 +1593,7 @@ msgid "Advanced Search" msgstr "Расширенный поиск" #: cps/templates/layout.html:76 cps/templates/read.html:71 -#: cps/templates/readcbr.html:79 cps/templates/readcbr.html:103 +#: cps/templates/readcbr.html:89 cps/templates/readcbr.html:113 msgid "Settings" msgstr "Настройки" @@ -1721,98 +1712,106 @@ msgstr "Каталог электронных книг Caliber-Web" msgid "Reflow text when sidebars are open." msgstr "Обновить размещение текста при открытии боковой панели" -#: cps/templates/readcbr.html:84 +#: cps/templates/readcbr.html:94 msgid "Keyboard Shortcuts" msgstr "Горячие клавиши" -#: cps/templates/readcbr.html:87 +#: cps/templates/readcbr.html:97 msgid "Previous Page" msgstr "Предыдущая страница" -#: cps/templates/readcbr.html:88 +#: cps/templates/readcbr.html:98 msgid "Next Page" msgstr "Следующая страница" -#: cps/templates/readcbr.html:89 +#: cps/templates/readcbr.html:99 msgid "Scale to Best" msgstr "Масштабировать до лучшего" -#: cps/templates/readcbr.html:90 +#: cps/templates/readcbr.html:100 msgid "Scale to Width" msgstr "Масштабироваать по ширине" -#: cps/templates/readcbr.html:91 +#: cps/templates/readcbr.html:101 msgid "Scale to Height" msgstr "Масштабировать по высоте" -#: cps/templates/readcbr.html:92 +#: cps/templates/readcbr.html:102 msgid "Scale to Native" msgstr "Масштабировать до оригинала" -#: cps/templates/readcbr.html:93 +#: cps/templates/readcbr.html:103 msgid "Rotate Right" msgstr "Повернуть Вправо" -#: cps/templates/readcbr.html:94 +#: cps/templates/readcbr.html:104 msgid "Rotate Left" msgstr "Повернуть Влево" -#: cps/templates/readcbr.html:95 +#: cps/templates/readcbr.html:105 msgid "Flip Image" msgstr "Перевернуть изображение" -#: cps/templates/readcbr.html:111 +#: cps/templates/readcbr.html:121 msgid "Light" msgstr "Светлая" -#: cps/templates/readcbr.html:112 +#: cps/templates/readcbr.html:122 msgid "Dark" msgstr "Темная" -#: cps/templates/readcbr.html:117 +#: cps/templates/readcbr.html:127 msgid "Scale" msgstr "Масштаб" -#: cps/templates/readcbr.html:120 +#: cps/templates/readcbr.html:130 msgid "Best" msgstr "Лучшее" -#: cps/templates/readcbr.html:121 +#: cps/templates/readcbr.html:131 msgid "Width" msgstr "Ширина" -#: cps/templates/readcbr.html:122 +#: cps/templates/readcbr.html:132 msgid "Height" msgstr "Длина" -#: cps/templates/readcbr.html:123 +#: cps/templates/readcbr.html:133 msgid "Native" msgstr "Оригинальный" -#: cps/templates/readcbr.html:128 +#: cps/templates/readcbr.html:138 msgid "Rotate" msgstr "Повернуть" -#: cps/templates/readcbr.html:139 +#: cps/templates/readcbr.html:149 msgid "Flip" msgstr "Перевернуть" -#: cps/templates/readcbr.html:142 +#: cps/templates/readcbr.html:152 msgid "Horizontal" msgstr "Горизонтально" -#: cps/templates/readcbr.html:143 +#: cps/templates/readcbr.html:153 msgid "Vertical" msgstr "Вертикально" +#: cps/templates/readcbr.html:158 +msgid "Direction" +msgstr "" + +#: cps/templates/readcbr.html:161 +msgid "Left to Right" +msgstr "" + +#: cps/templates/readcbr.html:162 +msgid "Right to Left" +msgstr "" + #: cps/templates/readpdf.html:29 msgid "PDF.js viewer" msgstr "Просмотровщик PDF.js" -#: cps/templates/readpdf.html:418 -msgid "Preparing document for printing..." -msgstr "" - #: cps/templates/readtxt.html:6 msgid "Basic txt Reader" msgstr "Средство для чтения txt-файлов" @@ -3259,3 +3258,18 @@ msgstr "Недавние скачивания" #~ msgid "Zaza" #~ msgstr "Зазаки" +#~ msgid "Failed to create path for cover %(path)s (Permission denied)." +#~ msgstr "Не удалось создать путь для обложки %(path)s (Доступ запрещён)." + +#~ msgid "Failed to store cover-file %(cover)s." +#~ msgstr "Не удалось сохранить файл обложки %(cover)s." + +#~ msgid "Cover-file is not a valid image file" +#~ msgstr "Файл обложки не соответствует изображению" + +#~ msgid "Cover is not a jpg file, can't save" +#~ msgstr "Обложка не jpg файл, невозможно сохранить" + +#~ msgid "Preparing document for printing..." +#~ msgstr "" + diff --git a/cps/translations/sv/LC_MESSAGES/messages.mo b/cps/translations/sv/LC_MESSAGES/messages.mo index 814cafa2f73310c86544e19bf84b00b2da264622..46fc2fbb3c27597e579360585b7a81b3c360aca7 100644 GIT binary patch delta 15127 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 delta 14759 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 diff --git a/cps/translations/sv/LC_MESSAGES/messages.po b/cps/translations/sv/LC_MESSAGES/messages.po index 39fb8c45..79654a77 100644 --- a/cps/translations/sv/LC_MESSAGES/messages.po +++ b/cps/translations/sv/LC_MESSAGES/messages.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Calibre-Web\n" "Report-Msgid-Bugs-To: https://github.com/janeczku/Calibre-Web\n" -"POT-Creation-Date: 2019-03-10 08:03+0100\n" +"POT-Creation-Date: 2019-05-08 20:23+0200\n" "PO-Revision-Date: 2018-11-23 02:57+0100\n" "Last-Translator: Jonatan Nyberg \n" "Language: sv\n" @@ -18,8 +18,9 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.6.0\n" -#: cps/book_formats.py:152 cps/book_formats.py:153 cps/book_formats.py:157 -#: cps/book_formats.py:161 cps/converter.py:29 cps/converter.py:45 +#: cps/book_formats.py:199 cps/book_formats.py:200 cps/book_formats.py:204 +#: cps/book_formats.py:208 cps/book_formats.py:212 cps/converter.py:29 +#: cps/converter.py:45 msgid "not installed" msgstr "inte installerad" @@ -31,133 +32,133 @@ msgstr "Utförande behörighet saknas" msgid "not configured" msgstr "inte konfigurerad" -#: cps/helper.py:72 +#: cps/helper.py:79 #, python-format msgid "%(format)s format not found for book id: %(book)d" msgstr "%(format)s formatet hittades inte för bok-id: %(book)d" -#: cps/helper.py:84 +#: cps/helper.py:91 #, python-format msgid "%(format)s not found on Google Drive: %(fn)s" msgstr "%(format)s hittades inte på Google Drive: %(fn)s" -#: cps/helper.py:91 cps/helper.py:199 cps/templates/detail.html:45 +#: cps/helper.py:98 cps/helper.py:204 cps/templates/detail.html:45 #: cps/templates/detail.html:49 msgid "Send to Kindle" msgstr "Skicka till Kindle" -#: cps/helper.py:92 cps/helper.py:110 cps/helper.py:201 +#: cps/helper.py:99 cps/helper.py:117 cps/helper.py:206 msgid "This e-mail has been sent via Calibre-Web." msgstr "Detta e-postmeddelande har skickats via Calibre-Web." -#: cps/helper.py:103 +#: cps/helper.py:110 #, python-format msgid "%(format)s not found: %(fn)s" msgstr "%(format)s hittades inte: %(fn)s" -#: cps/helper.py:108 +#: cps/helper.py:115 msgid "Calibre-Web test e-mail" msgstr "Calibre-Web test e-post" -#: cps/helper.py:109 +#: cps/helper.py:116 msgid "Test e-mail" msgstr "Test e-post" -#: cps/helper.py:125 +#: cps/helper.py:132 msgid "Get Started with Calibre-Web" msgstr "Kom igång med Calibre-Web" -#: cps/helper.py:126 +#: cps/helper.py:133 #, python-format msgid "Registration e-mail for user: %(name)s" msgstr "Registrera e-post för användare: %(name)s" -#: cps/helper.py:139 cps/helper.py:141 cps/helper.py:143 cps/helper.py:145 -#: cps/helper.py:151 cps/helper.py:153 cps/helper.py:155 cps/helper.py:157 +#: cps/helper.py:146 cps/helper.py:148 cps/helper.py:150 cps/helper.py:158 +#: cps/helper.py:160 cps/helper.py:162 #, python-format msgid "Send %(format)s to Kindle" msgstr "" -#: cps/helper.py:161 cps/helper.py:165 +#: cps/helper.py:166 #, python-format msgid "Convert %(orig)s to %(format)s and send to Kindle" msgstr "" -#: cps/helper.py:200 +#: cps/helper.py:205 #, python-format msgid "E-mail: %(book)s" msgstr "E-post: %(book)s" -#: cps/helper.py:203 +#: cps/helper.py:208 msgid "The requested file could not be read. Maybe wrong permissions?" msgstr "Den begärda filen kunde inte läsas. Kanske fel behörigheter?" -#: cps/helper.py:311 +#: cps/helper.py:316 #, python-format msgid "Rename title from: '%(src)s' to '%(dest)s' failed with error: %(error)s" msgstr "Byt namn på titel från: \"%(src)s\" till \"%(dest)s\" misslyckades med fel: %(error)s" -#: cps/helper.py:321 +#: cps/helper.py:326 #, python-format msgid "Rename author from: '%(src)s' to '%(dest)s' failed with error: %(error)s" msgstr "Byt namn på författare från: \"%(src)s\" till \"%(dest)s\" misslyckades med fel: %(error)s" -#: cps/helper.py:335 +#: cps/helper.py:340 #, python-format msgid "Rename file in path '%(src)s' to '%(dest)s' failed with error: %(error)s" msgstr "" -#: cps/helper.py:361 cps/helper.py:371 cps/helper.py:379 +#: cps/helper.py:366 cps/helper.py:376 cps/helper.py:384 #, python-format msgid "File %(file)s not found on Google Drive" msgstr "Filen %(file)s hittades inte på Google Drive" -#: cps/helper.py:400 +#: cps/helper.py:405 #, python-format msgid "Book path %(path)s not found on Google Drive" msgstr "Boksökvägen %(path)s hittades inte på Google Drive" -#: cps/helper.py:508 +#: cps/helper.py:556 msgid "Error excecuting UnRar" msgstr "Fel vid körning av UnRar" -#: cps/helper.py:510 +#: cps/helper.py:558 msgid "Unrar binary file not found" msgstr "Unrar binärfil hittades inte" -#: cps/helper.py:541 +#: cps/helper.py:589 msgid "Waiting" msgstr "Väntar" -#: cps/helper.py:543 +#: cps/helper.py:591 msgid "Failed" msgstr "Misslyckades" -#: cps/helper.py:545 +#: cps/helper.py:593 msgid "Started" msgstr "Startad" -#: cps/helper.py:547 +#: cps/helper.py:595 msgid "Finished" msgstr "Klar" -#: cps/helper.py:549 +#: cps/helper.py:597 msgid "Unknown Status" msgstr "Okänd status" -#: cps/helper.py:554 +#: cps/helper.py:602 msgid "E-mail: " msgstr "E-post: " -#: cps/helper.py:556 cps/helper.py:560 +#: cps/helper.py:604 cps/helper.py:608 msgid "Convert: " msgstr "Konvertera: " -#: cps/helper.py:558 +#: cps/helper.py:606 msgid "Upload: " msgstr "Överför: " -#: cps/helper.py:562 +#: cps/helper.py:610 msgid "Unknown Task: " msgstr "Okänd uppgift: " @@ -169,19 +170,19 @@ msgstr "Oväntade data vid läsning av uppdateringsinformation" msgid "No update available. You already have the latest version installed" msgstr "Ingen uppdatering tillgänglig. Du har redan den senaste versionen installerad" -#: cps/updater.py:270 cps/updater.py:501 cps/updater.py:503 cps/web.py:1187 +#: cps/updater.py:270 cps/updater.py:501 cps/updater.py:503 cps/web.py:1206 msgid "HTTP Error" msgstr "HTTP-fel" -#: cps/updater.py:272 cps/updater.py:505 cps/web.py:1188 +#: cps/updater.py:272 cps/updater.py:505 cps/web.py:1207 msgid "Connection error" msgstr "Anslutningsfel" -#: cps/updater.py:274 cps/updater.py:507 cps/web.py:1189 +#: cps/updater.py:274 cps/updater.py:507 cps/web.py:1208 msgid "Timeout while establishing connection" msgstr "Tiden ute när du etablerade anslutning" -#: cps/updater.py:276 cps/updater.py:509 cps/web.py:1190 +#: cps/updater.py:276 cps/updater.py:509 cps/web.py:1209 msgid "General error" msgstr "Allmänt fel" @@ -202,555 +203,545 @@ msgstr "" msgid "A new update is available. Click on the button below to update to version: %(version)s" msgstr "" -#: cps/updater.py:491 cps/web.py:2771 +#: cps/updater.py:491 cps/web.py:2801 msgid "Unknown" msgstr "Okänd" -#: cps/web.py:1180 +#: cps/web.py:1199 msgid "Requesting update package" msgstr "Begär uppdateringspaketet" -#: cps/web.py:1181 +#: cps/web.py:1200 msgid "Downloading update package" msgstr "Hämtar uppdateringspaketet" -#: cps/web.py:1182 +#: cps/web.py:1201 msgid "Unzipping update package" msgstr "Packar upp uppdateringspaketet" -#: cps/web.py:1183 +#: cps/web.py:1202 msgid "Replacing files" msgstr "Ersätta filer" -#: cps/web.py:1184 +#: cps/web.py:1203 msgid "Database connections are closed" msgstr "Databasanslutningarna är stängda" -#: cps/web.py:1185 +#: cps/web.py:1204 msgid "Stopping server" msgstr "Stoppar server" -#: cps/web.py:1186 +#: cps/web.py:1205 msgid "Update finished, please press okay and reload page" msgstr "Uppdatering klar, tryck på okej och uppdatera sidan" -#: cps/web.py:1187 cps/web.py:1188 cps/web.py:1189 cps/web.py:1190 +#: cps/web.py:1206 cps/web.py:1207 cps/web.py:1208 cps/web.py:1209 msgid "Update failed:" msgstr "Uppdateringen misslyckades:" -#: cps/web.py:1213 +#: cps/web.py:1235 msgid "Recently Added Books" msgstr "Nyligen tillagda böcker" -#: cps/web.py:1223 +#: cps/web.py:1245 msgid "Newest Books" msgstr "Nyaste böcker" -#: cps/web.py:1235 +#: cps/web.py:1257 msgid "Oldest Books" msgstr "Äldsta böcker" -#: cps/web.py:1247 +#: cps/web.py:1269 msgid "Books (A-Z)" msgstr "Böcker (A-Ö)" -#: cps/web.py:1258 +#: cps/web.py:1280 msgid "Books (Z-A)" msgstr "Böcker (Ö-A)" -#: cps/web.py:1287 +#: cps/web.py:1309 msgid "Hot Books (most downloaded)" msgstr "Heta böcker (mest hämtade)" -#: cps/web.py:1300 +#: cps/web.py:1322 msgid "Best rated books" msgstr "Bäst rankade böcker" -#: cps/templates/index.xml:39 cps/web.py:1313 +#: cps/templates/index.xml:39 cps/web.py:1335 msgid "Random Books" msgstr "Slumpmässiga böcker" -#: cps/web.py:1340 cps/web.py:1596 cps/web.py:2140 +#: cps/web.py:1362 cps/web.py:1618 cps/web.py:2167 msgid "Error opening eBook. File does not exist or file is not accessible:" msgstr "Fel vid öppnande av e-bok. Filen finns inte eller filen är inte tillgänglig:" -#: cps/web.py:1369 +#: cps/web.py:1391 msgid "Publisher list" msgstr "Lista över förlag" -#: cps/web.py:1384 +#: cps/web.py:1406 #, python-format msgid "Publisher: %(name)s" msgstr "Förlag: %(name)s" -#: cps/templates/index.xml:83 cps/web.py:1416 +#: cps/templates/index.xml:83 cps/web.py:1438 msgid "Series list" msgstr "Serielista" -#: cps/web.py:1430 +#: cps/web.py:1452 #, python-format msgid "Series: %(serie)s" msgstr "Serier: %(serie)s" -#: cps/web.py:1456 +#: cps/web.py:1478 msgid "Available languages" msgstr "Tillgängliga språk" -#: cps/web.py:1476 +#: cps/web.py:1498 #, python-format msgid "Language: %(name)s" msgstr "Språk: %(name)s" -#: cps/templates/index.xml:76 cps/web.py:1487 +#: cps/templates/index.xml:76 cps/web.py:1509 msgid "Category list" msgstr "Kategorilista" -#: cps/web.py:1501 +#: cps/web.py:1523 #, python-format msgid "Category: %(name)s" msgstr "Kategori: %(name)s" -#: cps/templates/layout.html:73 cps/web.py:1632 +#: cps/templates/layout.html:73 cps/web.py:1654 msgid "Tasks" msgstr "Uppgifter" -#: cps/web.py:1666 +#: cps/web.py:1688 msgid "Statistics" msgstr "Statistik" -#: cps/web.py:1734 +#: cps/web.py:1756 msgid "Google Drive setup not completed, try to deactivate and activate Google Drive again" msgstr "" -#: cps/web.py:1779 +#: cps/web.py:1801 msgid "Callback domain is not verified, please follow steps to verify domain in google developer console" msgstr "Återuppringningsdomänen är inte verifierad, följ stegen för att verifiera domänen i Google utvecklarkonsol" -#: cps/web.py:1855 +#: cps/web.py:1877 msgid "Server restarted, please reload page" msgstr "Server startas om, vänligen uppdatera sidan" -#: cps/web.py:1858 +#: cps/web.py:1880 msgid "Performing shutdown of server, please close window" msgstr "Stänger servern, vänligen stäng fönstret" -#: cps/web.py:1938 +#: cps/web.py:1959 msgid "Published after " msgstr "Publicerad efter " -#: cps/web.py:1945 +#: cps/web.py:1966 msgid "Published before " msgstr "Publicerad före " -#: cps/web.py:1959 +#: cps/web.py:1980 #, python-format msgid "Rating <= %(rating)s" msgstr "Betyg <= %(rating)s" -#: cps/web.py:1961 +#: cps/web.py:1982 #, python-format msgid "Rating >= %(rating)s" msgstr "Betyg >= %(rating)s" -#: cps/web.py:2022 cps/web.py:2031 +#: cps/web.py:2042 cps/web.py:2051 msgid "search" msgstr "sök" #: cps/templates/index.xml:47 cps/templates/index.xml:51 -#: cps/templates/layout.html:148 cps/web.py:2099 +#: cps/templates/layout.html:148 cps/web.py:2122 msgid "Read Books" msgstr "Lästa böcker" #: cps/templates/index.xml:55 cps/templates/index.xml:59 -#: cps/templates/layout.html:150 cps/web.py:2102 +#: cps/templates/layout.html:150 cps/web.py:2125 msgid "Unread Books" msgstr "Olästa böcker" -#: cps/web.py:2150 cps/web.py:2152 cps/web.py:2154 cps/web.py:2166 +#: cps/web.py:2177 cps/web.py:2179 cps/web.py:2181 cps/web.py:2193 msgid "Read a Book" msgstr "Läs en bok" -#: cps/web.py:2225 cps/web.py:3146 +#: cps/web.py:2205 +msgid "Error opening eBook. Fileformat is not supported." +msgstr "" + +#: cps/web.py:2255 cps/web.py:3176 msgid "Please fill out all fields!" msgstr "Fyll i alla fält!" -#: cps/web.py:2226 cps/web.py:2248 cps/web.py:2252 cps/web.py:2257 -#: cps/web.py:2259 +#: cps/web.py:2256 cps/web.py:2278 cps/web.py:2282 cps/web.py:2287 +#: cps/web.py:2289 msgid "register" msgstr "registrera" -#: cps/web.py:2247 cps/web.py:3365 +#: cps/web.py:2277 cps/web.py:3395 msgid "An unknown error occurred. Please try again later." msgstr "Ett okänt fel uppstod. Försök igen senare." -#: cps/web.py:2250 +#: cps/web.py:2280 msgid "Your e-mail is not allowed to register" msgstr "Din e-post är inte tillåten att registrera" -#: cps/web.py:2253 +#: cps/web.py:2283 msgid "Confirmation e-mail was send to your e-mail account." msgstr "Bekräftelsemail skickades till ditt e-postkonto." -#: cps/web.py:2256 +#: cps/web.py:2286 msgid "This username or e-mail address is already in use." msgstr "Det här användarnamnet eller e-postadressen är redan i bruk." -#: cps/web.py:2273 cps/web.py:2369 +#: cps/web.py:2303 cps/web.py:2399 #, python-format msgid "you are now logged in as: '%(nickname)s'" msgstr "du är nu inloggad som: \"%(nickname)s\"" -#: cps/web.py:2278 +#: cps/web.py:2308 msgid "Wrong Username or Password" msgstr "Fel användarnamn eller lösenord" -#: cps/web.py:2284 cps/web.py:2305 +#: cps/web.py:2314 cps/web.py:2335 msgid "login" msgstr "logga in" -#: cps/web.py:2317 cps/web.py:2348 +#: cps/web.py:2347 cps/web.py:2378 msgid "Token not found" msgstr "Token hittades inte" -#: cps/web.py:2325 cps/web.py:2356 +#: cps/web.py:2355 cps/web.py:2386 msgid "Token has expired" msgstr "Token har löpt ut" -#: cps/web.py:2333 +#: cps/web.py:2363 msgid "Success! Please return to your device" msgstr "Lyckades! Vänligen återvänd till din enhet" -#: cps/web.py:2383 +#: cps/web.py:2413 msgid "Please configure the SMTP mail settings first..." msgstr "Konfigurera SMTP-postinställningarna först..." -#: cps/web.py:2388 +#: cps/web.py:2418 #, python-format msgid "Book successfully queued for sending to %(kindlemail)s" msgstr "Boken är i kö för att skicka till %(kindlemail)s" -#: cps/web.py:2392 +#: cps/web.py:2422 #, python-format msgid "There was an error sending this book: %(res)s" msgstr "Det gick inte att skicka den här boken: %(res)s" -#: cps/web.py:2394 cps/web.py:3199 +#: cps/web.py:2424 cps/web.py:3229 msgid "Please configure your kindle e-mail address first..." msgstr "Konfigurera din kindle-e-postadress först..." -#: cps/web.py:2405 cps/web.py:2457 +#: cps/web.py:2435 cps/web.py:2487 msgid "Invalid shelf specified" msgstr "Ogiltig hylla specificerad" -#: cps/web.py:2412 +#: cps/web.py:2442 #, python-format msgid "Sorry you are not allowed to add a book to the the shelf: %(shelfname)s" msgstr "" -#: cps/web.py:2420 +#: cps/web.py:2450 msgid "You are not allowed to edit public shelves" msgstr "Du får inte redigera offentliga hyllor" -#: cps/web.py:2429 +#: cps/web.py:2459 #, python-format msgid "Book is already part of the shelf: %(shelfname)s" msgstr "Boken är redan en del av hyllan: %(shelfname)s" -#: cps/web.py:2443 +#: cps/web.py:2473 #, python-format msgid "Book has been added to shelf: %(sname)s" msgstr "Boken har lagts till i hyllan: %(sname)s" -#: cps/web.py:2462 +#: cps/web.py:2492 #, python-format msgid "You are not allowed to add a book to the the shelf: %(name)s" msgstr "Du får inte lägga till en bok i hyllan: %(name)s" -#: cps/web.py:2467 +#: cps/web.py:2497 msgid "User is not allowed to edit public shelves" msgstr "Användaren får inte redigera publika hyllor" -#: cps/web.py:2485 +#: cps/web.py:2515 #, python-format msgid "Books are already part of the shelf: %(name)s" msgstr "Böcker är redan en del av hyllan: %(name)s" -#: cps/web.py:2499 +#: cps/web.py:2529 #, python-format msgid "Books have been added to shelf: %(sname)s" msgstr "Böcker har lagts till hyllan: %(sname)s" -#: cps/web.py:2501 +#: cps/web.py:2531 #, python-format msgid "Could not add books to shelf: %(sname)s" msgstr "Kunde inte lägga till böcker till hyllan: %(sname)s" -#: cps/web.py:2538 +#: cps/web.py:2568 #, python-format msgid "Book has been removed from shelf: %(sname)s" msgstr "Boken har tagits bort från hyllan: %(sname)s" -#: cps/web.py:2544 +#: cps/web.py:2574 #, python-format msgid "Sorry you are not allowed to remove a book from this shelf: %(sname)s" msgstr "Tyvärr har du inte rätt att ta bort en bok från den här hyllan: %(sname)s" -#: cps/web.py:2565 cps/web.py:2589 +#: cps/web.py:2595 cps/web.py:2619 #, python-format msgid "A shelf with the name '%(title)s' already exists." msgstr "En hylla med namnet '%(title)s' finns redan." -#: cps/web.py:2570 +#: cps/web.py:2600 #, python-format msgid "Shelf %(title)s created" msgstr "Hyllan %(title)s skapad" -#: cps/web.py:2572 cps/web.py:2600 +#: cps/web.py:2602 cps/web.py:2630 msgid "There was an error" msgstr "Det fanns ett fel" -#: cps/web.py:2573 cps/web.py:2575 +#: cps/web.py:2603 cps/web.py:2605 msgid "create a shelf" msgstr "skapa en hylla" -#: cps/web.py:2598 +#: cps/web.py:2628 #, python-format msgid "Shelf %(title)s changed" msgstr "Hyllan %(title)s ändrad" -#: cps/web.py:2601 cps/web.py:2603 +#: cps/web.py:2631 cps/web.py:2633 msgid "Edit a shelf" msgstr "Redigera en hylla" -#: cps/web.py:2624 +#: cps/web.py:2654 #, python-format msgid "successfully deleted shelf %(name)s" msgstr "tog bort hyllan %(name)s" -#: cps/web.py:2651 +#: cps/web.py:2681 #, python-format msgid "Shelf: '%(name)s'" msgstr "Hylla: '%(name)s'" -#: cps/web.py:2654 +#: cps/web.py:2684 msgid "Error opening shelf. Shelf does not exist or is not accessible" msgstr "Fel vid öppning av hyllan. Hylla finns inte eller är inte tillgänglig" -#: cps/web.py:2685 +#: cps/web.py:2715 #, python-format msgid "Change order of Shelf: '%(name)s'" msgstr "Ändra ordning på hyllan: '%(name)s'" -#: cps/web.py:2714 cps/web.py:3152 +#: cps/web.py:2744 cps/web.py:3182 msgid "E-mail is not from valid domain" msgstr "E-posten är inte från giltig domän" -#: cps/web.py:2716 cps/web.py:2758 cps/web.py:2761 +#: cps/web.py:2746 cps/web.py:2788 cps/web.py:2791 #, python-format msgid "%(name)s's profile" msgstr "%(name)ss profil" -#: cps/web.py:2756 +#: cps/web.py:2786 msgid "Found an existing account for this e-mail address." msgstr "Hittade ett befintligt konto för den här e-postadressen." -#: cps/web.py:2759 +#: cps/web.py:2789 msgid "Profile updated" msgstr "Profilen uppdaterad" -#: cps/web.py:2790 +#: cps/web.py:2820 msgid "Admin page" msgstr "Administrationssida" -#: cps/web.py:2875 cps/web.py:3055 +#: cps/web.py:2905 cps/web.py:3085 msgid "Calibre-Web configuration updated" msgstr "Calibre-Web konfiguration uppdaterad" -#: cps/templates/admin.html:100 cps/web.py:2889 +#: cps/templates/admin.html:100 cps/web.py:2919 msgid "UI Configuration" msgstr "Användargränssnitt konfiguration" -#: cps/web.py:2907 +#: cps/web.py:2937 msgid "Import of optional Google Drive requirements missing" msgstr "Import av valfri Google Drive krav saknas" -#: cps/web.py:2910 +#: cps/web.py:2940 msgid "client_secrets.json is missing or not readable" msgstr "client_secrets.json saknas eller inte kan läsas" -#: cps/web.py:2915 cps/web.py:2944 +#: cps/web.py:2945 cps/web.py:2974 msgid "client_secrets.json is not configured for web application" msgstr "client_secrets.json är inte konfigurerad för webbapplikation" -#: cps/templates/admin.html:99 cps/web.py:2947 cps/web.py:2973 cps/web.py:2985 -#: cps/web.py:3030 cps/web.py:3045 cps/web.py:3064 cps/web.py:3072 -#: cps/web.py:3088 +#: cps/templates/admin.html:99 cps/web.py:2977 cps/web.py:3003 cps/web.py:3015 +#: cps/web.py:3060 cps/web.py:3075 cps/web.py:3094 cps/web.py:3102 +#: cps/web.py:3118 msgid "Basic Configuration" msgstr "Grundläggande konfiguration" -#: cps/web.py:2970 +#: cps/web.py:3000 msgid "Keyfile location is not valid, please enter correct path" msgstr "Platsen för Keyfile är inte giltig, ange rätt sökväg" -#: cps/web.py:2982 +#: cps/web.py:3012 msgid "Certfile location is not valid, please enter correct path" msgstr "Platsen för Certfile är inte giltig, ange rätt sökväg" -#: cps/web.py:3027 +#: cps/web.py:3057 msgid "Logfile location is not valid, please enter correct path" msgstr "Platsen för Logfile platsen är inte giltig, ange rätt sökväg" -#: cps/web.py:3068 +#: cps/web.py:3098 msgid "DB location is not valid, please enter correct path" msgstr "Platsen för DB är inte giltig, ange rätt sökväg" -#: cps/templates/admin.html:33 cps/web.py:3148 cps/web.py:3154 cps/web.py:3170 +#: cps/templates/admin.html:33 cps/web.py:3178 cps/web.py:3184 cps/web.py:3200 msgid "Add new user" msgstr "Lägg till ny användare" -#: cps/web.py:3160 +#: cps/web.py:3190 #, python-format msgid "User '%(user)s' created" msgstr "Användaren '%(user)s' skapad" -#: cps/web.py:3164 +#: cps/web.py:3194 msgid "Found an existing account for this e-mail address or nickname." msgstr "Hittade ett befintligt konto för den här e-postadressen eller smeknamnet." -#: cps/web.py:3194 +#: cps/web.py:3224 #, python-format msgid "Test e-mail successfully send to %(kindlemail)s" msgstr "Test-e-post skicka till %(kindlemail)s" -#: cps/web.py:3197 +#: cps/web.py:3227 #, python-format msgid "There was an error sending the Test e-mail: %(res)s" msgstr "Det gick inte att skicka Testmeddelandet: %(res)s" -#: cps/web.py:3201 +#: cps/web.py:3231 msgid "E-mail server settings updated" msgstr "E-postserverinställningar uppdaterade" -#: cps/web.py:3202 +#: cps/web.py:3232 msgid "Edit e-mail server settings" msgstr "Redigera inställningar för e-postserver" -#: cps/web.py:3227 +#: cps/web.py:3257 #, python-format msgid "User '%(nick)s' deleted" msgstr "Användaren '%(nick)s' borttagen" -#: cps/web.py:3340 +#: cps/web.py:3370 #, python-format msgid "User '%(nick)s' updated" msgstr "Användaren '%(nick)s' uppdaterad" -#: cps/web.py:3343 +#: cps/web.py:3373 msgid "An unknown error occured." msgstr "Ett okänt fel uppstod." -#: cps/web.py:3345 +#: cps/web.py:3375 #, python-format msgid "Edit User %(nick)s" msgstr "Redigera användaren %(nick)s" -#: cps/web.py:3362 +#: cps/web.py:3392 #, python-format msgid "Password for user %(user)s reset" msgstr "Lösenord för användaren %(user)s återställd" -#: cps/web.py:3376 cps/web.py:3582 +#: cps/web.py:3406 cps/web.py:3598 msgid "Error opening eBook. File does not exist or file is not accessible" msgstr "Det gick inte att öppna e-boken. Filen finns inte eller filen är inte tillgänglig" -#: cps/web.py:3404 +#: cps/web.py:3434 msgid "edit metadata" msgstr "redigera metadata" -#: cps/web.py:3497 cps/web.py:3743 +#: cps/web.py:3527 cps/web.py:3760 #, python-format msgid "File extension '%(ext)s' is not allowed to be uploaded to this server" msgstr "Filändelsen '%(ext)s' får inte laddas upp till den här servern" -#: cps/web.py:3501 cps/web.py:3746 +#: cps/web.py:3531 cps/web.py:3763 msgid "File to be uploaded must have an extension" msgstr "Filen som ska laddas upp måste ha en ändelse" -#: cps/web.py:3513 cps/web.py:3765 +#: cps/web.py:3543 cps/web.py:3782 #, python-format msgid "Failed to create path %(path)s (Permission denied)." msgstr "Det gick inte att skapa sökväg %(path)s (behörighet nekad)." -#: cps/web.py:3518 +#: cps/web.py:3548 #, python-format msgid "Failed to store file %(file)s." msgstr "Det gick inte att lagra filen %(file)s." -#: cps/web.py:3535 +#: cps/web.py:3565 #, python-format msgid "File format %(ext)s added to %(book)s" msgstr "Filformatet %(ext)s lades till %(book)s" -#: cps/web.py:3553 -#, python-format -msgid "Failed to create path for cover %(path)s (Permission denied)." -msgstr "Det gick inte att skapa sökväg för omslag %(path)s (behörighet nekad)." - -#: cps/web.py:3561 -#, python-format -msgid "Failed to store cover-file %(cover)s." -msgstr "Det gick inte att lagra omslagsfilen %(cover)s." - -#: cps/web.py:3564 -msgid "Cover-file is not a valid image file" -msgstr "Omslagsfilen är inte en giltig bildfil" +#: cps/web.py:3579 cps/web.py:3652 +msgid "Cover is not a supported imageformat (jpg/png/webp), can't save" +msgstr "" -#: cps/web.py:3594 cps/web.py:3603 +#: cps/web.py:3611 cps/web.py:3620 msgid "unknown" msgstr "okänd" -#: cps/web.py:3635 -msgid "Cover is not a jpg file, can't save" -msgstr "Omslag är inte en jpg-fil, kan inte spara" - -#: cps/web.py:3683 +#: cps/web.py:3700 #, python-format msgid "%(langname)s is not a valid language" msgstr "%(langname)s är inte ett giltigt språk" -#: cps/web.py:3714 +#: cps/web.py:3731 msgid "Metadata successfully updated" msgstr "Metadata uppdaterades" -#: cps/web.py:3723 +#: cps/web.py:3740 msgid "Error editing book, please check logfile for details" msgstr "Det gick inte att redigera boken, kontrollera loggfilen för mer information" -#: cps/web.py:3769 +#: cps/web.py:3786 #, python-format msgid "Failed to store file %(file)s (Permission denied)." msgstr "Det gick inte att lagra filen %(file)s (behörighet nekad)." -#: cps/web.py:3774 +#: cps/web.py:3791 #, python-format msgid "Failed to delete file %(file)s (Permission denied)." msgstr "Det gick inte att ta bort filen %(file)s (behörighet nekad)." -#: cps/web.py:3857 +#: cps/web.py:3873 #, python-format msgid "File %(title)s" msgstr "" -#: cps/web.py:3886 +#: cps/web.py:3902 msgid "Source or destination format for conversion missing" msgstr "Källa eller målformat för konvertering saknas" -#: cps/web.py:3896 +#: cps/web.py:3912 #, python-format msgid "Book successfully queued for converting to %(book_format)s" msgstr "Boken är i kö för konvertering till %(book_format)s" -#: cps/web.py:3900 +#: cps/web.py:3916 #, python-format msgid "There was an error converting this book: %(res)s" msgstr "Det gick inte att konvertera den här boken: %(res)s" @@ -1288,7 +1279,7 @@ msgstr "Antal slumpmässiga böcker att visa" msgid "No. of authors to show before hiding (0=disable hiding)" msgstr "" -#: cps/templates/config_view_edit.html:35 cps/templates/readcbr.html:108 +#: cps/templates/config_view_edit.html:35 cps/templates/readcbr.html:118 msgid "Theme" msgstr "Tema" @@ -1602,7 +1593,7 @@ msgid "Advanced Search" msgstr "Avancerad sökning" #: cps/templates/layout.html:76 cps/templates/read.html:71 -#: cps/templates/readcbr.html:79 cps/templates/readcbr.html:103 +#: cps/templates/readcbr.html:89 cps/templates/readcbr.html:113 msgid "Settings" msgstr "Inställningar" @@ -1721,98 +1712,106 @@ msgstr "Calibre-Web e-bokkatalog" msgid "Reflow text when sidebars are open." msgstr "Fyll i texten igen när sidofält är öppna." -#: cps/templates/readcbr.html:84 +#: cps/templates/readcbr.html:94 msgid "Keyboard Shortcuts" msgstr "Kortkommandon" -#: cps/templates/readcbr.html:87 +#: cps/templates/readcbr.html:97 msgid "Previous Page" msgstr "Föregående sida" -#: cps/templates/readcbr.html:88 +#: cps/templates/readcbr.html:98 msgid "Next Page" msgstr "Nästa sida" -#: cps/templates/readcbr.html:89 +#: cps/templates/readcbr.html:99 msgid "Scale to Best" msgstr "Skala till bäst" -#: cps/templates/readcbr.html:90 +#: cps/templates/readcbr.html:100 msgid "Scale to Width" msgstr "Skala till bredd" -#: cps/templates/readcbr.html:91 +#: cps/templates/readcbr.html:101 msgid "Scale to Height" msgstr "Skala till höjd" -#: cps/templates/readcbr.html:92 +#: cps/templates/readcbr.html:102 msgid "Scale to Native" msgstr "Skala till ursprunglig" -#: cps/templates/readcbr.html:93 +#: cps/templates/readcbr.html:103 msgid "Rotate Right" msgstr "Rotera åt höger" -#: cps/templates/readcbr.html:94 +#: cps/templates/readcbr.html:104 msgid "Rotate Left" msgstr "Rotera åt vänster" -#: cps/templates/readcbr.html:95 +#: cps/templates/readcbr.html:105 msgid "Flip Image" msgstr "Vänd bilden" -#: cps/templates/readcbr.html:111 +#: cps/templates/readcbr.html:121 msgid "Light" msgstr "Ljust" -#: cps/templates/readcbr.html:112 +#: cps/templates/readcbr.html:122 msgid "Dark" msgstr "Mörkt" -#: cps/templates/readcbr.html:117 +#: cps/templates/readcbr.html:127 msgid "Scale" msgstr "Skala" -#: cps/templates/readcbr.html:120 +#: cps/templates/readcbr.html:130 msgid "Best" msgstr "Bäst" -#: cps/templates/readcbr.html:121 +#: cps/templates/readcbr.html:131 msgid "Width" msgstr "Bredd" -#: cps/templates/readcbr.html:122 +#: cps/templates/readcbr.html:132 msgid "Height" msgstr "Höjd" -#: cps/templates/readcbr.html:123 +#: cps/templates/readcbr.html:133 msgid "Native" msgstr "Ursprunglig" -#: cps/templates/readcbr.html:128 +#: cps/templates/readcbr.html:138 msgid "Rotate" msgstr "Rotera" -#: cps/templates/readcbr.html:139 +#: cps/templates/readcbr.html:149 msgid "Flip" msgstr "Vänd" -#: cps/templates/readcbr.html:142 +#: cps/templates/readcbr.html:152 msgid "Horizontal" msgstr "Horisontell" -#: cps/templates/readcbr.html:143 +#: cps/templates/readcbr.html:153 msgid "Vertical" msgstr "Vertikal" +#: cps/templates/readcbr.html:158 +msgid "Direction" +msgstr "" + +#: cps/templates/readcbr.html:161 +msgid "Left to Right" +msgstr "" + +#: cps/templates/readcbr.html:162 +msgid "Right to Left" +msgstr "" + #: cps/templates/readpdf.html:29 msgid "PDF.js viewer" msgstr "PDF.js visare" -#: cps/templates/readpdf.html:418 -msgid "Preparing document for printing..." -msgstr "" - #: cps/templates/readtxt.html:6 msgid "Basic txt Reader" msgstr "Grundläggande txt-läsare" @@ -2059,3 +2058,18 @@ msgstr "Senaste hämtningar" #~ msgid "A new update is available. Click on the button below to update to version: %(version)s" #~ msgstr "" +#~ msgid "Failed to create path for cover %(path)s (Permission denied)." +#~ msgstr "Det gick inte att skapa sökväg för omslag %(path)s (behörighet nekad)." + +#~ msgid "Failed to store cover-file %(cover)s." +#~ msgstr "Det gick inte att lagra omslagsfilen %(cover)s." + +#~ msgid "Cover-file is not a valid image file" +#~ msgstr "Omslagsfilen är inte en giltig bildfil" + +#~ msgid "Cover is not a jpg file, can't save" +#~ msgstr "Omslag är inte en jpg-fil, kan inte spara" + +#~ msgid "Preparing document for printing..." +#~ msgstr "" + diff --git a/cps/translations/uk/LC_MESSAGES/messages.mo b/cps/translations/uk/LC_MESSAGES/messages.mo index 189747b48d2eb5f3a9623beba7b3cddb0abd8c08..f67823f39a6c14558d1b30ab41fb58b6a0719a9b 100644 GIT binary patch delta 15291 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 Y|9>-1e%d_1KR2DbkzVxpQo7fF0l3Ni8UO$Q delta 14807 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 wtt${3h}-y0aNy}J^KzE&G\n" "Language: uk\n" @@ -15,10 +15,11 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.5.1\n" +"Generated-By: Babel 2.6.0\n" -#: cps/book_formats.py:152 cps/book_formats.py:153 cps/book_formats.py:157 -#: cps/book_formats.py:161 cps/converter.py:29 cps/converter.py:45 +#: cps/book_formats.py:199 cps/book_formats.py:200 cps/book_formats.py:204 +#: cps/book_formats.py:208 cps/book_formats.py:212 cps/converter.py:29 +#: cps/converter.py:45 msgid "not installed" msgstr "не встановлено" @@ -30,133 +31,133 @@ msgstr "Відсутній дозвіл на виконання" msgid "not configured" msgstr "" -#: cps/helper.py:72 +#: cps/helper.py:79 #, python-format msgid "%(format)s format not found for book id: %(book)d" msgstr "" -#: cps/helper.py:84 +#: cps/helper.py:91 #, python-format msgid "%(format)s not found on Google Drive: %(fn)s" msgstr "" -#: cps/helper.py:91 cps/helper.py:199 cps/templates/detail.html:45 +#: cps/helper.py:98 cps/helper.py:204 cps/templates/detail.html:45 #: cps/templates/detail.html:49 msgid "Send to Kindle" msgstr "Відправити на Kindle" -#: cps/helper.py:92 cps/helper.py:110 cps/helper.py:201 +#: cps/helper.py:99 cps/helper.py:117 cps/helper.py:206 msgid "This e-mail has been sent via Calibre-Web." msgstr "" -#: cps/helper.py:103 +#: cps/helper.py:110 #, python-format msgid "%(format)s not found: %(fn)s" msgstr "" -#: cps/helper.py:108 +#: cps/helper.py:115 msgid "Calibre-Web test e-mail" msgstr "" -#: cps/helper.py:109 +#: cps/helper.py:116 msgid "Test e-mail" msgstr "" -#: cps/helper.py:125 +#: cps/helper.py:132 msgid "Get Started with Calibre-Web" msgstr "" -#: cps/helper.py:126 +#: cps/helper.py:133 #, python-format msgid "Registration e-mail for user: %(name)s" msgstr "" -#: cps/helper.py:139 cps/helper.py:141 cps/helper.py:143 cps/helper.py:145 -#: cps/helper.py:151 cps/helper.py:153 cps/helper.py:155 cps/helper.py:157 +#: cps/helper.py:146 cps/helper.py:148 cps/helper.py:150 cps/helper.py:158 +#: cps/helper.py:160 cps/helper.py:162 #, python-format msgid "Send %(format)s to Kindle" msgstr "" -#: cps/helper.py:161 cps/helper.py:165 +#: cps/helper.py:166 #, python-format msgid "Convert %(orig)s to %(format)s and send to Kindle" msgstr "" -#: cps/helper.py:200 +#: cps/helper.py:205 #, python-format msgid "E-mail: %(book)s" msgstr "" -#: cps/helper.py:203 +#: cps/helper.py:208 msgid "The requested file could not be read. Maybe wrong permissions?" msgstr "" -#: cps/helper.py:311 +#: cps/helper.py:316 #, python-format msgid "Rename title from: '%(src)s' to '%(dest)s' failed with error: %(error)s" msgstr "" -#: cps/helper.py:321 +#: cps/helper.py:326 #, python-format msgid "Rename author from: '%(src)s' to '%(dest)s' failed with error: %(error)s" msgstr "" -#: cps/helper.py:335 +#: cps/helper.py:340 #, python-format msgid "Rename file in path '%(src)s' to '%(dest)s' failed with error: %(error)s" msgstr "" -#: cps/helper.py:361 cps/helper.py:371 cps/helper.py:379 +#: cps/helper.py:366 cps/helper.py:376 cps/helper.py:384 #, python-format msgid "File %(file)s not found on Google Drive" msgstr "" -#: cps/helper.py:400 +#: cps/helper.py:405 #, python-format msgid "Book path %(path)s not found on Google Drive" msgstr "" -#: cps/helper.py:508 +#: cps/helper.py:556 msgid "Error excecuting UnRar" msgstr "" -#: cps/helper.py:510 +#: cps/helper.py:558 msgid "Unrar binary file not found" msgstr "" -#: cps/helper.py:541 +#: cps/helper.py:589 msgid "Waiting" msgstr "" -#: cps/helper.py:543 +#: cps/helper.py:591 msgid "Failed" msgstr "" -#: cps/helper.py:545 +#: cps/helper.py:593 msgid "Started" msgstr "" -#: cps/helper.py:547 +#: cps/helper.py:595 msgid "Finished" msgstr "" -#: cps/helper.py:549 +#: cps/helper.py:597 msgid "Unknown Status" msgstr "" -#: cps/helper.py:554 +#: cps/helper.py:602 msgid "E-mail: " msgstr "" -#: cps/helper.py:556 cps/helper.py:560 +#: cps/helper.py:604 cps/helper.py:608 msgid "Convert: " msgstr "" -#: cps/helper.py:558 +#: cps/helper.py:606 msgid "Upload: " msgstr "" -#: cps/helper.py:562 +#: cps/helper.py:610 msgid "Unknown Task: " msgstr "" @@ -168,19 +169,19 @@ msgstr "" msgid "No update available. You already have the latest version installed" msgstr "" -#: cps/updater.py:270 cps/updater.py:501 cps/updater.py:503 cps/web.py:1188 +#: cps/updater.py:270 cps/updater.py:501 cps/updater.py:503 cps/web.py:1206 msgid "HTTP Error" msgstr "" -#: cps/updater.py:272 cps/updater.py:505 cps/web.py:1189 +#: cps/updater.py:272 cps/updater.py:505 cps/web.py:1207 msgid "Connection error" msgstr "" -#: cps/updater.py:274 cps/updater.py:507 cps/web.py:1190 +#: cps/updater.py:274 cps/updater.py:507 cps/web.py:1208 msgid "Timeout while establishing connection" msgstr "" -#: cps/updater.py:276 cps/updater.py:509 cps/web.py:1191 +#: cps/updater.py:276 cps/updater.py:509 cps/web.py:1209 msgid "General error" msgstr "" @@ -201,555 +202,545 @@ msgstr "" msgid "A new update is available. Click on the button below to update to version: %(version)s" msgstr "" -#: cps/updater.py:491 cps/web.py:2775 +#: cps/updater.py:491 cps/web.py:2801 msgid "Unknown" msgstr "Невідомий" -#: cps/web.py:1181 +#: cps/web.py:1199 msgid "Requesting update package" msgstr "Перевірка оновлень" -#: cps/web.py:1182 +#: cps/web.py:1200 msgid "Downloading update package" msgstr "Завантаження оновлень" -#: cps/web.py:1183 +#: cps/web.py:1201 msgid "Unzipping update package" msgstr "Розпакування оновлення" -#: cps/web.py:1184 +#: cps/web.py:1202 msgid "Replacing files" msgstr "" -#: cps/web.py:1185 +#: cps/web.py:1203 msgid "Database connections are closed" msgstr "З'єднання з базою даних закрите" -#: cps/web.py:1186 +#: cps/web.py:1204 msgid "Stopping server" msgstr "" -#: cps/web.py:1187 +#: cps/web.py:1205 msgid "Update finished, please press okay and reload page" msgstr "Оновлення встановлені, натисніть ok і перезавантажте сторінку" -#: cps/web.py:1188 cps/web.py:1189 cps/web.py:1190 cps/web.py:1191 +#: cps/web.py:1206 cps/web.py:1207 cps/web.py:1208 cps/web.py:1209 msgid "Update failed:" msgstr "" -#: cps/web.py:1214 +#: cps/web.py:1235 msgid "Recently Added Books" msgstr "Нещодавно додані книги" -#: cps/web.py:1224 +#: cps/web.py:1245 msgid "Newest Books" msgstr "Найновіші книги" -#: cps/web.py:1236 +#: cps/web.py:1257 msgid "Oldest Books" msgstr "Найстаріші книги" -#: cps/web.py:1248 +#: cps/web.py:1269 msgid "Books (A-Z)" msgstr "Книги (А-Я)" -#: cps/web.py:1259 +#: cps/web.py:1280 msgid "Books (Z-A)" msgstr "Книги (Я-А)" -#: cps/web.py:1288 +#: cps/web.py:1309 msgid "Hot Books (most downloaded)" msgstr "Популярні книги (найбільш завантажувані)" -#: cps/web.py:1301 +#: cps/web.py:1322 msgid "Best rated books" msgstr "Книги з найкращим рейтингом" -#: cps/templates/index.xml:39 cps/web.py:1314 +#: cps/templates/index.xml:39 cps/web.py:1335 msgid "Random Books" msgstr "Випадковий список книг" -#: cps/web.py:1341 cps/web.py:1597 cps/web.py:2144 +#: cps/web.py:1362 cps/web.py:1618 cps/web.py:2167 msgid "Error opening eBook. File does not exist or file is not accessible:" msgstr "Неможливо відкрити книгу. Файл не існує або немає доступу." -#: cps/web.py:1370 +#: cps/web.py:1391 msgid "Publisher list" msgstr "" -#: cps/web.py:1385 +#: cps/web.py:1406 #, python-format msgid "Publisher: %(name)s" msgstr "" -#: cps/templates/index.xml:83 cps/web.py:1417 +#: cps/templates/index.xml:83 cps/web.py:1438 msgid "Series list" msgstr "Список серій" -#: cps/web.py:1431 +#: cps/web.py:1452 #, python-format msgid "Series: %(serie)s" msgstr "Серії: %(serie)s" -#: cps/web.py:1457 +#: cps/web.py:1478 msgid "Available languages" msgstr "Доступні мови" -#: cps/web.py:1477 +#: cps/web.py:1498 #, python-format msgid "Language: %(name)s" msgstr "Мова: %(name)s" -#: cps/templates/index.xml:76 cps/web.py:1488 +#: cps/templates/index.xml:76 cps/web.py:1509 msgid "Category list" msgstr "Список категорій" -#: cps/web.py:1502 +#: cps/web.py:1523 #, python-format msgid "Category: %(name)s" msgstr "Категорія: %(name)s" -#: cps/templates/layout.html:73 cps/web.py:1633 +#: cps/templates/layout.html:73 cps/web.py:1654 msgid "Tasks" msgstr "" -#: cps/web.py:1667 +#: cps/web.py:1688 msgid "Statistics" msgstr "Статистика" -#: cps/web.py:1735 +#: cps/web.py:1756 msgid "Google Drive setup not completed, try to deactivate and activate Google Drive again" msgstr "" -#: cps/web.py:1780 +#: cps/web.py:1801 msgid "Callback domain is not verified, please follow steps to verify domain in google developer console" msgstr "Домен зворотнього зв'язку не підтверджено. Виконайте дії для підтвердження домену, будь-ласка" -#: cps/web.py:1856 +#: cps/web.py:1877 msgid "Server restarted, please reload page" msgstr "Сервер перезавантажено, будь-ласка, перезавантажте сторінку" -#: cps/web.py:1859 +#: cps/web.py:1880 msgid "Performing shutdown of server, please close window" msgstr "Виконується зупинка серверу, будь-ласка, закрийте вікно" -#: cps/web.py:1939 +#: cps/web.py:1959 msgid "Published after " msgstr "" -#: cps/web.py:1946 +#: cps/web.py:1966 msgid "Published before " msgstr "Опубліковано до" -#: cps/web.py:1960 +#: cps/web.py:1980 #, python-format msgid "Rating <= %(rating)s" msgstr "" -#: cps/web.py:1962 +#: cps/web.py:1982 #, python-format msgid "Rating >= %(rating)s" msgstr "" -#: cps/web.py:2023 cps/web.py:2032 +#: cps/web.py:2042 cps/web.py:2051 msgid "search" msgstr "пошук" #: cps/templates/index.xml:47 cps/templates/index.xml:51 -#: cps/templates/layout.html:148 cps/web.py:2103 +#: cps/templates/layout.html:148 cps/web.py:2122 msgid "Read Books" msgstr "Прочитані книги" #: cps/templates/index.xml:55 cps/templates/index.xml:59 -#: cps/templates/layout.html:150 cps/web.py:2106 +#: cps/templates/layout.html:150 cps/web.py:2125 msgid "Unread Books" msgstr "Непрочитані книги" -#: cps/web.py:2154 cps/web.py:2156 cps/web.py:2158 cps/web.py:2170 +#: cps/web.py:2177 cps/web.py:2179 cps/web.py:2181 cps/web.py:2193 msgid "Read a Book" msgstr "Читати книгу" -#: cps/web.py:2229 cps/web.py:3150 +#: cps/web.py:2205 +msgid "Error opening eBook. Fileformat is not supported." +msgstr "" + +#: cps/web.py:2255 cps/web.py:3176 msgid "Please fill out all fields!" msgstr "Будь-ласка, заповніть всі поля!" -#: cps/web.py:2230 cps/web.py:2252 cps/web.py:2256 cps/web.py:2261 -#: cps/web.py:2263 +#: cps/web.py:2256 cps/web.py:2278 cps/web.py:2282 cps/web.py:2287 +#: cps/web.py:2289 msgid "register" msgstr "зареєструватись" -#: cps/web.py:2251 cps/web.py:3369 +#: cps/web.py:2277 cps/web.py:3395 msgid "An unknown error occurred. Please try again later." msgstr "" -#: cps/web.py:2254 +#: cps/web.py:2280 msgid "Your e-mail is not allowed to register" msgstr "" -#: cps/web.py:2257 +#: cps/web.py:2283 msgid "Confirmation e-mail was send to your e-mail account." msgstr "" -#: cps/web.py:2260 +#: cps/web.py:2286 msgid "This username or e-mail address is already in use." msgstr "" -#: cps/web.py:2277 cps/web.py:2373 +#: cps/web.py:2303 cps/web.py:2399 #, python-format msgid "you are now logged in as: '%(nickname)s'" msgstr "Ви увійшли як користувач: '%(nickname)s'" -#: cps/web.py:2282 +#: cps/web.py:2308 msgid "Wrong Username or Password" msgstr "Помилка в імені користувача або паролі" -#: cps/web.py:2288 cps/web.py:2309 +#: cps/web.py:2314 cps/web.py:2335 msgid "login" msgstr "увійти" -#: cps/web.py:2321 cps/web.py:2352 +#: cps/web.py:2347 cps/web.py:2378 msgid "Token not found" msgstr "Токен не знайдено" -#: cps/web.py:2329 cps/web.py:2360 +#: cps/web.py:2355 cps/web.py:2386 msgid "Token has expired" msgstr "Час дії токено вичерпано" -#: cps/web.py:2337 +#: cps/web.py:2363 msgid "Success! Please return to your device" msgstr "Вдалося! Будь-ласка, поверніться до вашого пристрою" -#: cps/web.py:2387 +#: cps/web.py:2413 msgid "Please configure the SMTP mail settings first..." msgstr "Будь-ласка, спочатку сконфігуруйте параметри SMTP" -#: cps/web.py:2392 +#: cps/web.py:2418 #, python-format msgid "Book successfully queued for sending to %(kindlemail)s" msgstr "" -#: cps/web.py:2396 +#: cps/web.py:2422 #, python-format msgid "There was an error sending this book: %(res)s" msgstr "Помилка при відправці книги: %(res)s" -#: cps/web.py:2398 cps/web.py:3203 +#: cps/web.py:2424 cps/web.py:3229 msgid "Please configure your kindle e-mail address first..." msgstr "" -#: cps/web.py:2409 cps/web.py:2461 +#: cps/web.py:2435 cps/web.py:2487 msgid "Invalid shelf specified" msgstr "" -#: cps/web.py:2416 +#: cps/web.py:2442 #, python-format msgid "Sorry you are not allowed to add a book to the the shelf: %(shelfname)s" msgstr "" -#: cps/web.py:2424 +#: cps/web.py:2450 msgid "You are not allowed to edit public shelves" msgstr "" -#: cps/web.py:2433 +#: cps/web.py:2459 #, python-format msgid "Book is already part of the shelf: %(shelfname)s" msgstr "" -#: cps/web.py:2447 +#: cps/web.py:2473 #, python-format msgid "Book has been added to shelf: %(sname)s" msgstr "Книга додана на книжкову полицю: %(sname)s" -#: cps/web.py:2466 +#: cps/web.py:2492 #, python-format msgid "You are not allowed to add a book to the the shelf: %(name)s" msgstr "" -#: cps/web.py:2471 +#: cps/web.py:2497 msgid "User is not allowed to edit public shelves" msgstr "" -#: cps/web.py:2489 +#: cps/web.py:2515 #, python-format msgid "Books are already part of the shelf: %(name)s" msgstr "" -#: cps/web.py:2503 +#: cps/web.py:2529 #, python-format msgid "Books have been added to shelf: %(sname)s" msgstr "" -#: cps/web.py:2505 +#: cps/web.py:2531 #, python-format msgid "Could not add books to shelf: %(sname)s" msgstr "" -#: cps/web.py:2542 +#: cps/web.py:2568 #, python-format msgid "Book has been removed from shelf: %(sname)s" msgstr "Книга видалена з книжкової полиці: %(sname)s" -#: cps/web.py:2548 +#: cps/web.py:2574 #, python-format msgid "Sorry you are not allowed to remove a book from this shelf: %(sname)s" msgstr "Вибачте, але у вас немає дозволу для видалення книги з цієї полиці" -#: cps/web.py:2569 cps/web.py:2593 +#: cps/web.py:2595 cps/web.py:2619 #, python-format msgid "A shelf with the name '%(title)s' already exists." msgstr "Книжкова полиця з назвою '%(title)s' уже существует." -#: cps/web.py:2574 +#: cps/web.py:2600 #, python-format msgid "Shelf %(title)s created" msgstr "Створена книжкова полиця %(title)s" -#: cps/web.py:2576 cps/web.py:2604 +#: cps/web.py:2602 cps/web.py:2630 msgid "There was an error" msgstr "Сталась помилка" -#: cps/web.py:2577 cps/web.py:2579 +#: cps/web.py:2603 cps/web.py:2605 msgid "create a shelf" msgstr "створити книжкову полицю" -#: cps/web.py:2602 +#: cps/web.py:2628 #, python-format msgid "Shelf %(title)s changed" msgstr "Книжкова полиця %(title)s змінена" -#: cps/web.py:2605 cps/web.py:2607 +#: cps/web.py:2631 cps/web.py:2633 msgid "Edit a shelf" msgstr "Змінити книжкову полицю" -#: cps/web.py:2628 +#: cps/web.py:2654 #, python-format msgid "successfully deleted shelf %(name)s" msgstr "Книжкова полиця %(name)s видалена" -#: cps/web.py:2655 +#: cps/web.py:2681 #, python-format msgid "Shelf: '%(name)s'" msgstr "Книжкова полиця: '%(name)s'" -#: cps/web.py:2658 +#: cps/web.py:2684 msgid "Error opening shelf. Shelf does not exist or is not accessible" msgstr "Помилка при відкриванні полиці. Полиця не існує або до неї відсутній доступ" -#: cps/web.py:2689 +#: cps/web.py:2715 #, python-format msgid "Change order of Shelf: '%(name)s'" msgstr "Змінити розташування книжкової полиці '%(name)s'" -#: cps/web.py:2718 cps/web.py:3156 +#: cps/web.py:2744 cps/web.py:3182 msgid "E-mail is not from valid domain" msgstr "" -#: cps/web.py:2720 cps/web.py:2762 cps/web.py:2765 +#: cps/web.py:2746 cps/web.py:2788 cps/web.py:2791 #, python-format msgid "%(name)s's profile" msgstr "Профіль %(name)s" -#: cps/web.py:2760 +#: cps/web.py:2786 msgid "Found an existing account for this e-mail address." msgstr "" -#: cps/web.py:2763 +#: cps/web.py:2789 msgid "Profile updated" msgstr "Профіль оновлено" -#: cps/web.py:2794 +#: cps/web.py:2820 msgid "Admin page" msgstr "Сторінка адміністратора" -#: cps/web.py:2879 cps/web.py:3059 +#: cps/web.py:2905 cps/web.py:3085 msgid "Calibre-Web configuration updated" msgstr "" -#: cps/templates/admin.html:100 cps/web.py:2893 +#: cps/templates/admin.html:100 cps/web.py:2919 msgid "UI Configuration" msgstr "Конфігурація інтерфейсу" -#: cps/web.py:2911 +#: cps/web.py:2937 msgid "Import of optional Google Drive requirements missing" msgstr "Імпорт додаткових вимог Google Drive відсутній" -#: cps/web.py:2914 +#: cps/web.py:2940 msgid "client_secrets.json is missing or not readable" msgstr "Неможливо зчитати client_secrets.json або він відсутній" -#: cps/web.py:2919 cps/web.py:2948 +#: cps/web.py:2945 cps/web.py:2974 msgid "client_secrets.json is not configured for web application" msgstr "Неможливо зконфігурувати client_secrets.json для веб-додатку" -#: cps/templates/admin.html:99 cps/web.py:2951 cps/web.py:2977 cps/web.py:2989 -#: cps/web.py:3034 cps/web.py:3049 cps/web.py:3068 cps/web.py:3076 -#: cps/web.py:3092 +#: cps/templates/admin.html:99 cps/web.py:2977 cps/web.py:3003 cps/web.py:3015 +#: cps/web.py:3060 cps/web.py:3075 cps/web.py:3094 cps/web.py:3102 +#: cps/web.py:3118 msgid "Basic Configuration" msgstr "Настройки сервера" -#: cps/web.py:2974 +#: cps/web.py:3000 msgid "Keyfile location is not valid, please enter correct path" msgstr "Невідомий шлях до Keyfile. Будь-ласка введіть коректний" -#: cps/web.py:2986 +#: cps/web.py:3012 msgid "Certfile location is not valid, please enter correct path" msgstr "Невідомий шлях до Certfile. Будь-ласка введіть коректний" -#: cps/web.py:3031 +#: cps/web.py:3057 msgid "Logfile location is not valid, please enter correct path" msgstr "Невідомий шлях до Logfile. Будь-ласка введіть коректний" -#: cps/web.py:3072 +#: cps/web.py:3098 msgid "DB location is not valid, please enter correct path" msgstr "Невідомий шлях до БД. Будь-ласка введіть коректний" -#: cps/templates/admin.html:33 cps/web.py:3152 cps/web.py:3158 cps/web.py:3174 +#: cps/templates/admin.html:33 cps/web.py:3178 cps/web.py:3184 cps/web.py:3200 msgid "Add new user" msgstr "Додати користувача" -#: cps/web.py:3164 +#: cps/web.py:3190 #, python-format msgid "User '%(user)s' created" msgstr "Користувач '%(user)s' додан" -#: cps/web.py:3168 +#: cps/web.py:3194 msgid "Found an existing account for this e-mail address or nickname." msgstr "" -#: cps/web.py:3198 +#: cps/web.py:3224 #, python-format msgid "Test e-mail successfully send to %(kindlemail)s" msgstr "" -#: cps/web.py:3201 +#: cps/web.py:3227 #, python-format msgid "There was an error sending the Test e-mail: %(res)s" msgstr "" -#: cps/web.py:3205 +#: cps/web.py:3231 msgid "E-mail server settings updated" msgstr "" -#: cps/web.py:3206 +#: cps/web.py:3232 msgid "Edit e-mail server settings" msgstr "" -#: cps/web.py:3231 +#: cps/web.py:3257 #, python-format msgid "User '%(nick)s' deleted" msgstr "Користувача '%(nick)s' видалено" -#: cps/web.py:3344 +#: cps/web.py:3370 #, python-format msgid "User '%(nick)s' updated" msgstr "Користувача '%(nick)s' оновлено" -#: cps/web.py:3347 +#: cps/web.py:3373 msgid "An unknown error occured." msgstr "Сталась невідома помилка" -#: cps/web.py:3349 +#: cps/web.py:3375 #, python-format msgid "Edit User %(nick)s" msgstr "Змінити користувача %(nick)s" -#: cps/web.py:3366 +#: cps/web.py:3392 #, python-format msgid "Password for user %(user)s reset" msgstr "" -#: cps/web.py:3380 cps/web.py:3586 +#: cps/web.py:3406 cps/web.py:3598 msgid "Error opening eBook. File does not exist or file is not accessible" msgstr "Сталась помилка при відкриванні eBook. Файл не існує або відсутній доступ до нього" -#: cps/web.py:3408 +#: cps/web.py:3434 msgid "edit metadata" msgstr "змінити метадані" -#: cps/web.py:3501 cps/web.py:3747 +#: cps/web.py:3527 cps/web.py:3760 #, python-format msgid "File extension '%(ext)s' is not allowed to be uploaded to this server" msgstr "" -#: cps/web.py:3505 cps/web.py:3750 +#: cps/web.py:3531 cps/web.py:3763 msgid "File to be uploaded must have an extension" msgstr "Завантажувальний файл повинен мати розширення" -#: cps/web.py:3517 cps/web.py:3769 +#: cps/web.py:3543 cps/web.py:3782 #, python-format msgid "Failed to create path %(path)s (Permission denied)." msgstr "" -#: cps/web.py:3522 +#: cps/web.py:3548 #, python-format msgid "Failed to store file %(file)s." msgstr "" -#: cps/web.py:3539 -#, python-format -msgid "File format %(ext)s added to %(book)s" -msgstr "" - -#: cps/web.py:3557 -#, python-format -msgid "Failed to create path for cover %(path)s (Permission denied)." -msgstr "" - #: cps/web.py:3565 #, python-format -msgid "Failed to store cover-file %(cover)s." +msgid "File format %(ext)s added to %(book)s" msgstr "" -#: cps/web.py:3568 -msgid "Cover-file is not a valid image file" +#: cps/web.py:3579 cps/web.py:3652 +msgid "Cover is not a supported imageformat (jpg/png/webp), can't save" msgstr "" -#: cps/web.py:3598 cps/web.py:3607 +#: cps/web.py:3611 cps/web.py:3620 msgid "unknown" msgstr "невідомий" -#: cps/web.py:3639 -msgid "Cover is not a jpg file, can't save" -msgstr "Обкладинка не є .jpg файлом. Неможливо зберегти" - -#: cps/web.py:3687 +#: cps/web.py:3700 #, python-format msgid "%(langname)s is not a valid language" msgstr "" -#: cps/web.py:3718 +#: cps/web.py:3731 msgid "Metadata successfully updated" msgstr "" -#: cps/web.py:3727 +#: cps/web.py:3740 msgid "Error editing book, please check logfile for details" msgstr "Сталась помилка при редагуванні книги. Будь-ласка, перевірте лог-файл для деталей" -#: cps/web.py:3773 +#: cps/web.py:3786 #, python-format msgid "Failed to store file %(file)s (Permission denied)." msgstr "" -#: cps/web.py:3778 +#: cps/web.py:3791 #, python-format msgid "Failed to delete file %(file)s (Permission denied)." msgstr "" -#: cps/web.py:3861 +#: cps/web.py:3873 #, python-format msgid "File %(title)s" msgstr "" -#: cps/web.py:3890 +#: cps/web.py:3902 msgid "Source or destination format for conversion missing" msgstr "" -#: cps/web.py:3900 +#: cps/web.py:3912 #, python-format msgid "Book successfully queued for converting to %(book_format)s" msgstr "" -#: cps/web.py:3904 +#: cps/web.py:3916 #, python-format msgid "There was an error converting this book: %(res)s" msgstr "" @@ -1287,7 +1278,7 @@ msgstr "Кількість показаних випадкових книг" msgid "No. of authors to show before hiding (0=disable hiding)" msgstr "" -#: cps/templates/config_view_edit.html:35 cps/templates/readcbr.html:108 +#: cps/templates/config_view_edit.html:35 cps/templates/readcbr.html:118 msgid "Theme" msgstr "Тема" @@ -1601,7 +1592,7 @@ msgid "Advanced Search" msgstr "Розширений пошук" #: cps/templates/layout.html:76 cps/templates/read.html:71 -#: cps/templates/readcbr.html:79 cps/templates/readcbr.html:103 +#: cps/templates/readcbr.html:89 cps/templates/readcbr.html:113 msgid "Settings" msgstr "Налаштування" @@ -1720,98 +1711,106 @@ msgstr "" msgid "Reflow text when sidebars are open." msgstr "Переформатувати текст, коли відкриті бічні панелі." -#: cps/templates/readcbr.html:84 +#: cps/templates/readcbr.html:94 msgid "Keyboard Shortcuts" msgstr "" -#: cps/templates/readcbr.html:87 +#: cps/templates/readcbr.html:97 msgid "Previous Page" msgstr "" -#: cps/templates/readcbr.html:88 +#: cps/templates/readcbr.html:98 msgid "Next Page" msgstr "" -#: cps/templates/readcbr.html:89 +#: cps/templates/readcbr.html:99 msgid "Scale to Best" msgstr "" -#: cps/templates/readcbr.html:90 +#: cps/templates/readcbr.html:100 msgid "Scale to Width" msgstr "" -#: cps/templates/readcbr.html:91 +#: cps/templates/readcbr.html:101 msgid "Scale to Height" msgstr "" -#: cps/templates/readcbr.html:92 +#: cps/templates/readcbr.html:102 msgid "Scale to Native" msgstr "" -#: cps/templates/readcbr.html:93 +#: cps/templates/readcbr.html:103 msgid "Rotate Right" msgstr "" -#: cps/templates/readcbr.html:94 +#: cps/templates/readcbr.html:104 msgid "Rotate Left" msgstr "" -#: cps/templates/readcbr.html:95 +#: cps/templates/readcbr.html:105 msgid "Flip Image" msgstr "" -#: cps/templates/readcbr.html:111 +#: cps/templates/readcbr.html:121 msgid "Light" msgstr "" -#: cps/templates/readcbr.html:112 +#: cps/templates/readcbr.html:122 msgid "Dark" msgstr "" -#: cps/templates/readcbr.html:117 +#: cps/templates/readcbr.html:127 msgid "Scale" msgstr "" -#: cps/templates/readcbr.html:120 +#: cps/templates/readcbr.html:130 msgid "Best" msgstr "" -#: cps/templates/readcbr.html:121 +#: cps/templates/readcbr.html:131 msgid "Width" msgstr "" -#: cps/templates/readcbr.html:122 +#: cps/templates/readcbr.html:132 msgid "Height" msgstr "" -#: cps/templates/readcbr.html:123 +#: cps/templates/readcbr.html:133 msgid "Native" msgstr "" -#: cps/templates/readcbr.html:128 +#: cps/templates/readcbr.html:138 msgid "Rotate" msgstr "" -#: cps/templates/readcbr.html:139 +#: cps/templates/readcbr.html:149 msgid "Flip" msgstr "" -#: cps/templates/readcbr.html:142 +#: cps/templates/readcbr.html:152 msgid "Horizontal" msgstr "" -#: cps/templates/readcbr.html:143 +#: cps/templates/readcbr.html:153 msgid "Vertical" msgstr "" +#: cps/templates/readcbr.html:158 +msgid "Direction" +msgstr "" + +#: cps/templates/readcbr.html:161 +msgid "Left to Right" +msgstr "" + +#: cps/templates/readcbr.html:162 +msgid "Right to Left" +msgstr "" + #: cps/templates/readpdf.html:29 msgid "PDF.js viewer" msgstr "Переглядач PDF.js" -#: cps/templates/readpdf.html:418 -msgid "Preparing document for printing..." -msgstr "" - #: cps/templates/readtxt.html:6 msgid "Basic txt Reader" msgstr "Засіб для читання txt-файлів" @@ -2154,3 +2153,18 @@ msgstr "Нещодавно переглянуті" #~ msgid "caliBlur! Dark Theme (Beta)" #~ msgstr "caliBlur! Темна тема (тестова версія)" +#~ msgid "Failed to create path for cover %(path)s (Permission denied)." +#~ msgstr "" + +#~ msgid "Failed to store cover-file %(cover)s." +#~ msgstr "" + +#~ msgid "Cover-file is not a valid image file" +#~ msgstr "" + +#~ msgid "Cover is not a jpg file, can't save" +#~ msgstr "Обкладинка не є .jpg файлом. Неможливо зберегти" + +#~ msgid "Preparing document for printing..." +#~ msgstr "" + diff --git a/cps/translations/zh_Hans_CN/LC_MESSAGES/messages.mo b/cps/translations/zh_Hans_CN/LC_MESSAGES/messages.mo index 229896f34a642cd951ef1df85fff73c62f871074..8928437353c0dc98debbe88e60af2e2446d17f97 100644 GIT binary patch delta 15118 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 AJpcdz delta 14763 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 Xzmq>CkiP2M@Icz8mqRiNZe96b8Hz^T diff --git a/cps/translations/zh_Hans_CN/LC_MESSAGES/messages.po b/cps/translations/zh_Hans_CN/LC_MESSAGES/messages.po index 1af2fc34..f343c970 100644 --- a/cps/translations/zh_Hans_CN/LC_MESSAGES/messages.po +++ b/cps/translations/zh_Hans_CN/LC_MESSAGES/messages.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Calibre-Web\n" "Report-Msgid-Bugs-To: https://github.com/janeczku/Calibre-Web\n" -"POT-Creation-Date: 2019-03-10 08:03+0100\n" +"POT-Creation-Date: 2019-05-08 20:23+0200\n" "PO-Revision-Date: 2017-01-06 17:00+0000\n" "Last-Translator: dalin \n" "Language: zh_Hans_CN\n" @@ -18,8 +18,9 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.6.0\n" -#: cps/book_formats.py:152 cps/book_formats.py:153 cps/book_formats.py:157 -#: cps/book_formats.py:161 cps/converter.py:29 cps/converter.py:45 +#: cps/book_formats.py:199 cps/book_formats.py:200 cps/book_formats.py:204 +#: cps/book_formats.py:208 cps/book_formats.py:212 cps/converter.py:29 +#: cps/converter.py:45 msgid "not installed" msgstr "未安装" @@ -31,133 +32,133 @@ msgstr "可执行权限缺失" msgid "not configured" msgstr "未配置" -#: cps/helper.py:72 +#: cps/helper.py:79 #, python-format msgid "%(format)s format not found for book id: %(book)d" msgstr "找不到id为 %(book)d 的书的 %(format)s 格式" -#: cps/helper.py:84 +#: cps/helper.py:91 #, python-format msgid "%(format)s not found on Google Drive: %(fn)s" msgstr "Google Drive %(fn)s 上找不到 %(format)s" -#: cps/helper.py:91 cps/helper.py:199 cps/templates/detail.html:45 +#: cps/helper.py:98 cps/helper.py:204 cps/templates/detail.html:45 #: cps/templates/detail.html:49 msgid "Send to Kindle" msgstr "发送到Kindle" -#: cps/helper.py:92 cps/helper.py:110 cps/helper.py:201 +#: cps/helper.py:99 cps/helper.py:117 cps/helper.py:206 msgid "This e-mail has been sent via Calibre-Web." msgstr "此邮件已经通过Calibre-Web发送" -#: cps/helper.py:103 +#: cps/helper.py:110 #, python-format msgid "%(format)s not found: %(fn)s" msgstr "找不到 %(format)s: %(fn)s" -#: cps/helper.py:108 +#: cps/helper.py:115 msgid "Calibre-Web test e-mail" msgstr "Calibre-Web测试邮件" -#: cps/helper.py:109 +#: cps/helper.py:116 msgid "Test e-mail" msgstr "测试邮件" -#: cps/helper.py:125 +#: cps/helper.py:132 msgid "Get Started with Calibre-Web" msgstr "开启Calibre-Web之旅" -#: cps/helper.py:126 +#: cps/helper.py:133 #, python-format msgid "Registration e-mail for user: %(name)s" msgstr "用户 %(name)s 的注册邮箱" -#: cps/helper.py:139 cps/helper.py:141 cps/helper.py:143 cps/helper.py:145 -#: cps/helper.py:151 cps/helper.py:153 cps/helper.py:155 cps/helper.py:157 +#: cps/helper.py:146 cps/helper.py:148 cps/helper.py:150 cps/helper.py:158 +#: cps/helper.py:160 cps/helper.py:162 #, python-format msgid "Send %(format)s to Kindle" msgstr "" -#: cps/helper.py:161 cps/helper.py:165 +#: cps/helper.py:166 #, python-format msgid "Convert %(orig)s to %(format)s and send to Kindle" msgstr "" -#: cps/helper.py:200 +#: cps/helper.py:205 #, python-format msgid "E-mail: %(book)s" msgstr "" -#: cps/helper.py:203 +#: cps/helper.py:208 msgid "The requested file could not be read. Maybe wrong permissions?" msgstr "无法读取请求的文件。 可能有错误的权限设置?" -#: cps/helper.py:311 +#: cps/helper.py:316 #, python-format msgid "Rename title from: '%(src)s' to '%(dest)s' failed with error: %(error)s" msgstr "将标题从'%(src)s'改为'%(dest)s'时失败,出错信息: %(error)s" -#: cps/helper.py:321 +#: cps/helper.py:326 #, python-format msgid "Rename author from: '%(src)s' to '%(dest)s' failed with error: %(error)s" msgstr "将作者从'%(src)s'改为'%(dest)s'时失败,出错信息: %(error)s" -#: cps/helper.py:335 +#: cps/helper.py:340 #, python-format msgid "Rename file in path '%(src)s' to '%(dest)s' failed with error: %(error)s" msgstr "" -#: cps/helper.py:361 cps/helper.py:371 cps/helper.py:379 +#: cps/helper.py:366 cps/helper.py:376 cps/helper.py:384 #, python-format msgid "File %(file)s not found on Google Drive" msgstr "Google Drive上找不到文件 %(file)s" -#: cps/helper.py:400 +#: cps/helper.py:405 #, python-format msgid "Book path %(path)s not found on Google Drive" msgstr "Google Drive上找不到书籍路径 %(path)s" -#: cps/helper.py:508 +#: cps/helper.py:556 msgid "Error excecuting UnRar" msgstr "执行UnRar时出错" -#: cps/helper.py:510 +#: cps/helper.py:558 msgid "Unrar binary file not found" msgstr "找不到Unrar二进制文件" -#: cps/helper.py:541 +#: cps/helper.py:589 msgid "Waiting" msgstr "等待中" -#: cps/helper.py:543 +#: cps/helper.py:591 msgid "Failed" msgstr "失败" -#: cps/helper.py:545 +#: cps/helper.py:593 msgid "Started" msgstr "已开始" -#: cps/helper.py:547 +#: cps/helper.py:595 msgid "Finished" msgstr "已完成" -#: cps/helper.py:549 +#: cps/helper.py:597 msgid "Unknown Status" msgstr "未知状态" -#: cps/helper.py:554 +#: cps/helper.py:602 msgid "E-mail: " msgstr "" -#: cps/helper.py:556 cps/helper.py:560 +#: cps/helper.py:604 cps/helper.py:608 msgid "Convert: " msgstr "转换:" -#: cps/helper.py:558 +#: cps/helper.py:606 msgid "Upload: " msgstr "上传:" -#: cps/helper.py:562 +#: cps/helper.py:610 msgid "Unknown Task: " msgstr "未知任务:" @@ -169,19 +170,19 @@ msgstr "读取更新信息时出现异常数据" msgid "No update available. You already have the latest version installed" msgstr "没有可用更新。您已经安装了最新版本" -#: cps/updater.py:270 cps/updater.py:501 cps/updater.py:503 cps/web.py:1187 +#: cps/updater.py:270 cps/updater.py:501 cps/updater.py:503 cps/web.py:1206 msgid "HTTP Error" msgstr "HTTP错误" -#: cps/updater.py:272 cps/updater.py:505 cps/web.py:1188 +#: cps/updater.py:272 cps/updater.py:505 cps/web.py:1207 msgid "Connection error" msgstr "连接错误" -#: cps/updater.py:274 cps/updater.py:507 cps/web.py:1189 +#: cps/updater.py:274 cps/updater.py:507 cps/web.py:1208 msgid "Timeout while establishing connection" msgstr "建立连接超时" -#: cps/updater.py:276 cps/updater.py:509 cps/web.py:1190 +#: cps/updater.py:276 cps/updater.py:509 cps/web.py:1209 msgid "General error" msgstr "一般错误" @@ -202,555 +203,545 @@ msgstr "" msgid "A new update is available. Click on the button below to update to version: %(version)s" msgstr "" -#: cps/updater.py:491 cps/web.py:2771 +#: cps/updater.py:491 cps/web.py:2801 msgid "Unknown" msgstr "未知" -#: cps/web.py:1180 +#: cps/web.py:1199 msgid "Requesting update package" msgstr "正在请求更新包" -#: cps/web.py:1181 +#: cps/web.py:1200 msgid "Downloading update package" msgstr "正在下载更新包" -#: cps/web.py:1182 +#: cps/web.py:1201 msgid "Unzipping update package" msgstr "正在解压更新包" -#: cps/web.py:1183 +#: cps/web.py:1202 msgid "Replacing files" msgstr "正在替换文件" -#: cps/web.py:1184 +#: cps/web.py:1203 msgid "Database connections are closed" msgstr "数据库连接已关闭" -#: cps/web.py:1185 +#: cps/web.py:1204 msgid "Stopping server" msgstr "正在停止服务器" -#: cps/web.py:1186 +#: cps/web.py:1205 msgid "Update finished, please press okay and reload page" msgstr "更新完成,请按确定并刷新页面" -#: cps/web.py:1187 cps/web.py:1188 cps/web.py:1189 cps/web.py:1190 +#: cps/web.py:1206 cps/web.py:1207 cps/web.py:1208 cps/web.py:1209 msgid "Update failed:" msgstr "更新失败:" -#: cps/web.py:1213 +#: cps/web.py:1235 msgid "Recently Added Books" msgstr "最近添加的书籍" -#: cps/web.py:1223 +#: cps/web.py:1245 msgid "Newest Books" msgstr "最新书籍" -#: cps/web.py:1235 +#: cps/web.py:1257 msgid "Oldest Books" msgstr "最旧书籍" -#: cps/web.py:1247 +#: cps/web.py:1269 msgid "Books (A-Z)" msgstr "书籍 (A-Z)" -#: cps/web.py:1258 +#: cps/web.py:1280 msgid "Books (Z-A)" msgstr "书籍 (Z-A)" -#: cps/web.py:1287 +#: cps/web.py:1309 msgid "Hot Books (most downloaded)" msgstr "热门书籍(最多下载)" -#: cps/web.py:1300 +#: cps/web.py:1322 msgid "Best rated books" msgstr "最高评分书籍" -#: cps/templates/index.xml:39 cps/web.py:1313 +#: cps/templates/index.xml:39 cps/web.py:1335 msgid "Random Books" msgstr "随机书籍" -#: cps/web.py:1340 cps/web.py:1596 cps/web.py:2140 +#: cps/web.py:1362 cps/web.py:1618 cps/web.py:2167 msgid "Error opening eBook. File does not exist or file is not accessible:" msgstr "无法打开电子书。 文件不存在或者文件不可访问:" -#: cps/web.py:1369 +#: cps/web.py:1391 msgid "Publisher list" msgstr "出版社列表" -#: cps/web.py:1384 +#: cps/web.py:1406 #, python-format msgid "Publisher: %(name)s" msgstr "出版社: %(name)s" -#: cps/templates/index.xml:83 cps/web.py:1416 +#: cps/templates/index.xml:83 cps/web.py:1438 msgid "Series list" msgstr "丛书列表" -#: cps/web.py:1430 +#: cps/web.py:1452 #, python-format msgid "Series: %(serie)s" msgstr "丛书: %(serie)s" -#: cps/web.py:1456 +#: cps/web.py:1478 msgid "Available languages" msgstr "可用语言" -#: cps/web.py:1476 +#: cps/web.py:1498 #, python-format msgid "Language: %(name)s" msgstr "语言: %(name)s" -#: cps/templates/index.xml:76 cps/web.py:1487 +#: cps/templates/index.xml:76 cps/web.py:1509 msgid "Category list" msgstr "分类列表" -#: cps/web.py:1501 +#: cps/web.py:1523 #, python-format msgid "Category: %(name)s" msgstr "分类: %(name)s" -#: cps/templates/layout.html:73 cps/web.py:1632 +#: cps/templates/layout.html:73 cps/web.py:1654 msgid "Tasks" msgstr "任务" -#: cps/web.py:1666 +#: cps/web.py:1688 msgid "Statistics" msgstr "统计" -#: cps/web.py:1734 +#: cps/web.py:1756 msgid "Google Drive setup not completed, try to deactivate and activate Google Drive again" msgstr "" -#: cps/web.py:1779 +#: cps/web.py:1801 msgid "Callback domain is not verified, please follow steps to verify domain in google developer console" msgstr "回调域名尚未被校验,请在google开发者控制台按步骤校验域名" -#: cps/web.py:1855 +#: cps/web.py:1877 msgid "Server restarted, please reload page" msgstr "服务器已重启,请刷新页面" -#: cps/web.py:1858 +#: cps/web.py:1880 msgid "Performing shutdown of server, please close window" msgstr "正在关闭服务器,请关闭窗口" -#: cps/web.py:1938 +#: cps/web.py:1959 msgid "Published after " msgstr "出版时晚于 " -#: cps/web.py:1945 +#: cps/web.py:1966 msgid "Published before " msgstr "出版时早于 " -#: cps/web.py:1959 +#: cps/web.py:1980 #, python-format msgid "Rating <= %(rating)s" msgstr "评分 <= %(rating)s" -#: cps/web.py:1961 +#: cps/web.py:1982 #, python-format msgid "Rating >= %(rating)s" msgstr "评分 >= %(rating)s" -#: cps/web.py:2022 cps/web.py:2031 +#: cps/web.py:2042 cps/web.py:2051 msgid "search" msgstr "搜索" #: cps/templates/index.xml:47 cps/templates/index.xml:51 -#: cps/templates/layout.html:148 cps/web.py:2099 +#: cps/templates/layout.html:148 cps/web.py:2122 msgid "Read Books" msgstr "已读书籍" #: cps/templates/index.xml:55 cps/templates/index.xml:59 -#: cps/templates/layout.html:150 cps/web.py:2102 +#: cps/templates/layout.html:150 cps/web.py:2125 msgid "Unread Books" msgstr "未读书籍" -#: cps/web.py:2150 cps/web.py:2152 cps/web.py:2154 cps/web.py:2166 +#: cps/web.py:2177 cps/web.py:2179 cps/web.py:2181 cps/web.py:2193 msgid "Read a Book" msgstr "阅读一本书" -#: cps/web.py:2225 cps/web.py:3146 +#: cps/web.py:2205 +msgid "Error opening eBook. Fileformat is not supported." +msgstr "" + +#: cps/web.py:2255 cps/web.py:3176 msgid "Please fill out all fields!" msgstr "请填写所有字段" -#: cps/web.py:2226 cps/web.py:2248 cps/web.py:2252 cps/web.py:2257 -#: cps/web.py:2259 +#: cps/web.py:2256 cps/web.py:2278 cps/web.py:2282 cps/web.py:2287 +#: cps/web.py:2289 msgid "register" msgstr "注册" -#: cps/web.py:2247 cps/web.py:3365 +#: cps/web.py:2277 cps/web.py:3395 msgid "An unknown error occurred. Please try again later." msgstr "发生一个未知错误,请稍后再试。" -#: cps/web.py:2250 +#: cps/web.py:2280 msgid "Your e-mail is not allowed to register" msgstr "您的邮箱不能用来注册" -#: cps/web.py:2253 +#: cps/web.py:2283 msgid "Confirmation e-mail was send to your e-mail account." msgstr "确认邮件已经发送到您的邮箱。" -#: cps/web.py:2256 +#: cps/web.py:2286 msgid "This username or e-mail address is already in use." msgstr "这个用户名或者邮箱已经被使用。" -#: cps/web.py:2273 cps/web.py:2369 +#: cps/web.py:2303 cps/web.py:2399 #, python-format msgid "you are now logged in as: '%(nickname)s'" msgstr "您现在已以'%(nickname)s'身份登录" -#: cps/web.py:2278 +#: cps/web.py:2308 msgid "Wrong Username or Password" msgstr "用户名或密码错误" -#: cps/web.py:2284 cps/web.py:2305 +#: cps/web.py:2314 cps/web.py:2335 msgid "login" msgstr "登录" -#: cps/web.py:2317 cps/web.py:2348 +#: cps/web.py:2347 cps/web.py:2378 msgid "Token not found" msgstr "找不到Token" -#: cps/web.py:2325 cps/web.py:2356 +#: cps/web.py:2355 cps/web.py:2386 msgid "Token has expired" msgstr "Token已过期" -#: cps/web.py:2333 +#: cps/web.py:2363 msgid "Success! Please return to your device" msgstr "成功!请返回您的设备" -#: cps/web.py:2383 +#: cps/web.py:2413 msgid "Please configure the SMTP mail settings first..." msgstr "请先配置SMTP邮箱..." -#: cps/web.py:2388 +#: cps/web.py:2418 #, python-format msgid "Book successfully queued for sending to %(kindlemail)s" msgstr "书籍已经被成功加入 %(kindlemail)s 的发送队列" -#: cps/web.py:2392 +#: cps/web.py:2422 #, python-format msgid "There was an error sending this book: %(res)s" msgstr "发送这本书的时候出现错误: %(res)s" -#: cps/web.py:2394 cps/web.py:3199 +#: cps/web.py:2424 cps/web.py:3229 msgid "Please configure your kindle e-mail address first..." msgstr "请先配置您的kindle邮箱..." -#: cps/web.py:2405 cps/web.py:2457 +#: cps/web.py:2435 cps/web.py:2487 msgid "Invalid shelf specified" msgstr "指定的书架无效" -#: cps/web.py:2412 +#: cps/web.py:2442 #, python-format msgid "Sorry you are not allowed to add a book to the the shelf: %(shelfname)s" msgstr "对不起,您没有添加书籍到书架 %(shelfname)s 的权限" -#: cps/web.py:2420 +#: cps/web.py:2450 msgid "You are not allowed to edit public shelves" msgstr "您没有编辑书架的权限" -#: cps/web.py:2429 +#: cps/web.py:2459 #, python-format msgid "Book is already part of the shelf: %(shelfname)s" msgstr "此书已经是书架 %(shelfname)s 的一部分" -#: cps/web.py:2443 +#: cps/web.py:2473 #, python-format msgid "Book has been added to shelf: %(sname)s" msgstr "此书已被添加到书架: %(sname)s" -#: cps/web.py:2462 +#: cps/web.py:2492 #, python-format msgid "You are not allowed to add a book to the the shelf: %(name)s" msgstr "您没有添加书籍到书架 %(name)s 的权限" -#: cps/web.py:2467 +#: cps/web.py:2497 msgid "User is not allowed to edit public shelves" msgstr "用户没有编辑公开书架的权限" -#: cps/web.py:2485 +#: cps/web.py:2515 #, python-format msgid "Books are already part of the shelf: %(name)s" msgstr "书籍已经在书架 %(name)s 中了" -#: cps/web.py:2499 +#: cps/web.py:2529 #, python-format msgid "Books have been added to shelf: %(sname)s" msgstr "书籍已经被添加到书架 %(sname)s 中'" -#: cps/web.py:2501 +#: cps/web.py:2531 #, python-format msgid "Could not add books to shelf: %(sname)s" msgstr "无法添加书籍到书架: %(sname)s" -#: cps/web.py:2538 +#: cps/web.py:2568 #, python-format msgid "Book has been removed from shelf: %(sname)s" msgstr "此书已从书架 %(sname)s 中删除" -#: cps/web.py:2544 +#: cps/web.py:2574 #, python-format msgid "Sorry you are not allowed to remove a book from this shelf: %(sname)s" msgstr "对不起,您没有从书架 %(sname)s 中删除书籍的权限" -#: cps/web.py:2565 cps/web.py:2589 +#: cps/web.py:2595 cps/web.py:2619 #, python-format msgid "A shelf with the name '%(title)s' already exists." msgstr "已存在书架 '%(title)s'。" -#: cps/web.py:2570 +#: cps/web.py:2600 #, python-format msgid "Shelf %(title)s created" msgstr "书架 %(title)s 已被创建" -#: cps/web.py:2572 cps/web.py:2600 +#: cps/web.py:2602 cps/web.py:2630 msgid "There was an error" msgstr "发生错误" -#: cps/web.py:2573 cps/web.py:2575 +#: cps/web.py:2603 cps/web.py:2605 msgid "create a shelf" msgstr "创建书架" -#: cps/web.py:2598 +#: cps/web.py:2628 #, python-format msgid "Shelf %(title)s changed" msgstr "书架 %(title)s 已被修改" -#: cps/web.py:2601 cps/web.py:2603 +#: cps/web.py:2631 cps/web.py:2633 msgid "Edit a shelf" msgstr "编辑书架" -#: cps/web.py:2624 +#: cps/web.py:2654 #, python-format msgid "successfully deleted shelf %(name)s" msgstr "成功删除书架 %(name)s" -#: cps/web.py:2651 +#: cps/web.py:2681 #, python-format msgid "Shelf: '%(name)s'" msgstr "书架: '%(name)s'" -#: cps/web.py:2654 +#: cps/web.py:2684 msgid "Error opening shelf. Shelf does not exist or is not accessible" msgstr "打开书架出错。书架不存在或不可访问" -#: cps/web.py:2685 +#: cps/web.py:2715 #, python-format msgid "Change order of Shelf: '%(name)s'" msgstr "修改书架 '%(name)s' 顺序" -#: cps/web.py:2714 cps/web.py:3152 +#: cps/web.py:2744 cps/web.py:3182 msgid "E-mail is not from valid domain" msgstr "邮箱不在有效域中'" -#: cps/web.py:2716 cps/web.py:2758 cps/web.py:2761 +#: cps/web.py:2746 cps/web.py:2788 cps/web.py:2791 #, python-format msgid "%(name)s's profile" msgstr "%(name)s 的资料" -#: cps/web.py:2756 +#: cps/web.py:2786 msgid "Found an existing account for this e-mail address." msgstr "找到一个已有账号使用这个邮箱。" -#: cps/web.py:2759 +#: cps/web.py:2789 msgid "Profile updated" msgstr "资料已更新" -#: cps/web.py:2790 +#: cps/web.py:2820 msgid "Admin page" msgstr "管理页" -#: cps/web.py:2875 cps/web.py:3055 +#: cps/web.py:2905 cps/web.py:3085 msgid "Calibre-Web configuration updated" msgstr "Calibre-Web配置已更新" -#: cps/templates/admin.html:100 cps/web.py:2889 +#: cps/templates/admin.html:100 cps/web.py:2919 msgid "UI Configuration" msgstr "UI配置" -#: cps/web.py:2907 +#: cps/web.py:2937 msgid "Import of optional Google Drive requirements missing" msgstr "可选的Google Drive依赖导入缺失" -#: cps/web.py:2910 +#: cps/web.py:2940 msgid "client_secrets.json is missing or not readable" msgstr "client_secrets.json文件缺失或不可读" -#: cps/web.py:2915 cps/web.py:2944 +#: cps/web.py:2945 cps/web.py:2974 msgid "client_secrets.json is not configured for web application" msgstr "没有为web应用配置client_secrets.json" -#: cps/templates/admin.html:99 cps/web.py:2947 cps/web.py:2973 cps/web.py:2985 -#: cps/web.py:3030 cps/web.py:3045 cps/web.py:3064 cps/web.py:3072 -#: cps/web.py:3088 +#: cps/templates/admin.html:99 cps/web.py:2977 cps/web.py:3003 cps/web.py:3015 +#: cps/web.py:3060 cps/web.py:3075 cps/web.py:3094 cps/web.py:3102 +#: cps/web.py:3118 msgid "Basic Configuration" msgstr "基本配置" -#: cps/web.py:2970 +#: cps/web.py:3000 msgid "Keyfile location is not valid, please enter correct path" msgstr "key文件位置无效,请输入正确路径" -#: cps/web.py:2982 +#: cps/web.py:3012 msgid "Certfile location is not valid, please enter correct path" msgstr "证书文件位置无效,请输入正确路径" -#: cps/web.py:3027 +#: cps/web.py:3057 msgid "Logfile location is not valid, please enter correct path" msgstr "日志文件位置无效,请输入正确路径" -#: cps/web.py:3068 +#: cps/web.py:3098 msgid "DB location is not valid, please enter correct path" msgstr "DB位置无效,请输入正确路径" -#: cps/templates/admin.html:33 cps/web.py:3148 cps/web.py:3154 cps/web.py:3170 +#: cps/templates/admin.html:33 cps/web.py:3178 cps/web.py:3184 cps/web.py:3200 msgid "Add new user" msgstr "添加新用户" -#: cps/web.py:3160 +#: cps/web.py:3190 #, python-format msgid "User '%(user)s' created" msgstr "用户 '%(user)s' 已被创建" -#: cps/web.py:3164 +#: cps/web.py:3194 msgid "Found an existing account for this e-mail address or nickname." msgstr "此邮箱或昵称的账号已经存在。" -#: cps/web.py:3194 +#: cps/web.py:3224 #, python-format msgid "Test e-mail successfully send to %(kindlemail)s" msgstr "测试邮件已经被成功发到 %(kindlemail)s" -#: cps/web.py:3197 +#: cps/web.py:3227 #, python-format msgid "There was an error sending the Test e-mail: %(res)s" msgstr "发送测试邮件出错了: %(res)s" -#: cps/web.py:3201 +#: cps/web.py:3231 msgid "E-mail server settings updated" msgstr "已更新邮件服务器设置" -#: cps/web.py:3202 +#: cps/web.py:3232 msgid "Edit e-mail server settings" msgstr "编辑邮箱服务器设置" -#: cps/web.py:3227 +#: cps/web.py:3257 #, python-format msgid "User '%(nick)s' deleted" msgstr "用户 '%(nick)s' 已被删除" -#: cps/web.py:3340 +#: cps/web.py:3370 #, python-format msgid "User '%(nick)s' updated" msgstr "用户 '%(nick)s' 已被更新" -#: cps/web.py:3343 +#: cps/web.py:3373 msgid "An unknown error occured." msgstr "发生未知错误。" -#: cps/web.py:3345 +#: cps/web.py:3375 #, python-format msgid "Edit User %(nick)s" msgstr "编辑用户 %(nick)s" -#: cps/web.py:3362 +#: cps/web.py:3392 #, python-format msgid "Password for user %(user)s reset" msgstr "用户 %(user)s 的密码已重置" -#: cps/web.py:3376 cps/web.py:3582 +#: cps/web.py:3406 cps/web.py:3598 msgid "Error opening eBook. File does not exist or file is not accessible" msgstr "打开电子书出错。文件不存在或不可访问" -#: cps/web.py:3404 +#: cps/web.py:3434 msgid "edit metadata" msgstr "编辑元数据" -#: cps/web.py:3497 cps/web.py:3743 +#: cps/web.py:3527 cps/web.py:3760 #, python-format msgid "File extension '%(ext)s' is not allowed to be uploaded to this server" msgstr "不能上传后缀为 '%(ext)s' 的文件到此服务器" -#: cps/web.py:3501 cps/web.py:3746 +#: cps/web.py:3531 cps/web.py:3763 msgid "File to be uploaded must have an extension" msgstr "要上传的文件必须有一个后缀" -#: cps/web.py:3513 cps/web.py:3765 +#: cps/web.py:3543 cps/web.py:3782 #, python-format msgid "Failed to create path %(path)s (Permission denied)." msgstr "创建路径 %(path)s 失败(权限拒绝)。" -#: cps/web.py:3518 +#: cps/web.py:3548 #, python-format msgid "Failed to store file %(file)s." msgstr "保存文件 %(file)s 失败。" -#: cps/web.py:3535 +#: cps/web.py:3565 #, python-format msgid "File format %(ext)s added to %(book)s" msgstr "已添加 %(ext)s 格式到 %(book)s" -#: cps/web.py:3553 -#, python-format -msgid "Failed to create path for cover %(path)s (Permission denied)." -msgstr "为封面 %(path)s 创建路径失败(权限拒绝)。" - -#: cps/web.py:3561 -#, python-format -msgid "Failed to store cover-file %(cover)s." -msgstr "保存封面文件 %(cover)s 失败。" - -#: cps/web.py:3564 -msgid "Cover-file is not a valid image file" -msgstr "封面文件不是一个有效的图片文件" +#: cps/web.py:3579 cps/web.py:3652 +msgid "Cover is not a supported imageformat (jpg/png/webp), can't save" +msgstr "" -#: cps/web.py:3594 cps/web.py:3603 +#: cps/web.py:3611 cps/web.py:3620 msgid "unknown" msgstr "未知" -#: cps/web.py:3635 -msgid "Cover is not a jpg file, can't save" -msgstr "封面不是一个jpg文件,无法保存" - -#: cps/web.py:3683 +#: cps/web.py:3700 #, python-format msgid "%(langname)s is not a valid language" msgstr "%(langname)s 不是一种有效语言" -#: cps/web.py:3714 +#: cps/web.py:3731 msgid "Metadata successfully updated" msgstr "已成功更新元数据" -#: cps/web.py:3723 +#: cps/web.py:3740 msgid "Error editing book, please check logfile for details" msgstr "编辑书籍出错,详情请检查日志文件" -#: cps/web.py:3769 +#: cps/web.py:3786 #, python-format msgid "Failed to store file %(file)s (Permission denied)." msgstr "存储文件 %(file)s 失败(权限拒绝)。" -#: cps/web.py:3774 +#: cps/web.py:3791 #, python-format msgid "Failed to delete file %(file)s (Permission denied)." msgstr "删除文件 %(file)s 失败(权限拒绝)。" -#: cps/web.py:3857 +#: cps/web.py:3873 #, python-format msgid "File %(title)s" msgstr "" -#: cps/web.py:3886 +#: cps/web.py:3902 msgid "Source or destination format for conversion missing" msgstr "转换的源或目的格式缺失" -#: cps/web.py:3896 +#: cps/web.py:3912 #, python-format msgid "Book successfully queued for converting to %(book_format)s" msgstr "书籍已经被成功加入 %(book_format)s 的转换队列" -#: cps/web.py:3900 +#: cps/web.py:3916 #, python-format msgid "There was an error converting this book: %(res)s" msgstr "转换此书时出现错误: %(res)s" @@ -1288,7 +1279,7 @@ msgstr "随机书籍显示数量" msgid "No. of authors to show before hiding (0=disable hiding)" msgstr "" -#: cps/templates/config_view_edit.html:35 cps/templates/readcbr.html:108 +#: cps/templates/config_view_edit.html:35 cps/templates/readcbr.html:118 msgid "Theme" msgstr "主题" @@ -1602,7 +1593,7 @@ msgid "Advanced Search" msgstr "高级搜索" #: cps/templates/layout.html:76 cps/templates/read.html:71 -#: cps/templates/readcbr.html:79 cps/templates/readcbr.html:103 +#: cps/templates/readcbr.html:89 cps/templates/readcbr.html:113 msgid "Settings" msgstr "设置" @@ -1721,98 +1712,106 @@ msgstr "Caliebre-Web电子书目录" msgid "Reflow text when sidebars are open." msgstr "" -#: cps/templates/readcbr.html:84 +#: cps/templates/readcbr.html:94 msgid "Keyboard Shortcuts" msgstr "快捷键" -#: cps/templates/readcbr.html:87 +#: cps/templates/readcbr.html:97 msgid "Previous Page" msgstr "上一页" -#: cps/templates/readcbr.html:88 +#: cps/templates/readcbr.html:98 msgid "Next Page" msgstr "下一页" -#: cps/templates/readcbr.html:89 +#: cps/templates/readcbr.html:99 msgid "Scale to Best" msgstr "缩放到最佳" -#: cps/templates/readcbr.html:90 +#: cps/templates/readcbr.html:100 msgid "Scale to Width" msgstr "按宽度缩放" -#: cps/templates/readcbr.html:91 +#: cps/templates/readcbr.html:101 msgid "Scale to Height" msgstr "按高度缩放" -#: cps/templates/readcbr.html:92 +#: cps/templates/readcbr.html:102 msgid "Scale to Native" msgstr "" -#: cps/templates/readcbr.html:93 +#: cps/templates/readcbr.html:103 msgid "Rotate Right" msgstr "向右旋转" -#: cps/templates/readcbr.html:94 +#: cps/templates/readcbr.html:104 msgid "Rotate Left" msgstr "向左旋转" -#: cps/templates/readcbr.html:95 +#: cps/templates/readcbr.html:105 msgid "Flip Image" msgstr "翻转图片" -#: cps/templates/readcbr.html:111 +#: cps/templates/readcbr.html:121 msgid "Light" msgstr "浅色" -#: cps/templates/readcbr.html:112 +#: cps/templates/readcbr.html:122 msgid "Dark" msgstr "深色" -#: cps/templates/readcbr.html:117 +#: cps/templates/readcbr.html:127 msgid "Scale" msgstr "缩放" -#: cps/templates/readcbr.html:120 +#: cps/templates/readcbr.html:130 msgid "Best" msgstr "最佳" -#: cps/templates/readcbr.html:121 +#: cps/templates/readcbr.html:131 msgid "Width" msgstr "宽度" -#: cps/templates/readcbr.html:122 +#: cps/templates/readcbr.html:132 msgid "Height" msgstr "高度" -#: cps/templates/readcbr.html:123 +#: cps/templates/readcbr.html:133 msgid "Native" msgstr "" -#: cps/templates/readcbr.html:128 +#: cps/templates/readcbr.html:138 msgid "Rotate" msgstr "旋转" -#: cps/templates/readcbr.html:139 +#: cps/templates/readcbr.html:149 msgid "Flip" msgstr "翻转" -#: cps/templates/readcbr.html:142 +#: cps/templates/readcbr.html:152 msgid "Horizontal" msgstr "水平" -#: cps/templates/readcbr.html:143 +#: cps/templates/readcbr.html:153 msgid "Vertical" msgstr "垂直" +#: cps/templates/readcbr.html:158 +msgid "Direction" +msgstr "" + +#: cps/templates/readcbr.html:161 +msgid "Left to Right" +msgstr "" + +#: cps/templates/readcbr.html:162 +msgid "Right to Left" +msgstr "" + #: cps/templates/readpdf.html:29 msgid "PDF.js viewer" msgstr "PDF.js 查看器" -#: cps/templates/readpdf.html:418 -msgid "Preparing document for printing..." -msgstr "" - #: cps/templates/readtxt.html:6 msgid "Basic txt Reader" msgstr "简单txt阅读器" @@ -3259,3 +3258,18 @@ msgstr "最近下载" #~ msgid "Zaza" #~ msgstr "扎扎其语" +#~ msgid "Failed to create path for cover %(path)s (Permission denied)." +#~ msgstr "为封面 %(path)s 创建路径失败(权限拒绝)。" + +#~ msgid "Failed to store cover-file %(cover)s." +#~ msgstr "保存封面文件 %(cover)s 失败。" + +#~ msgid "Cover-file is not a valid image file" +#~ msgstr "封面文件不是一个有效的图片文件" + +#~ msgid "Cover is not a jpg file, can't save" +#~ msgstr "封面不是一个jpg文件,无法保存" + +#~ msgid "Preparing document for printing..." +#~ msgstr "" + diff --git a/messages.pot b/messages.pot index e0b283ad..f7e067a1 100644 --- a/messages.pot +++ b/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2019-03-10 08:03+0100\n" +"POT-Creation-Date: 2019-05-08 20:23+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,8 +17,9 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.6.0\n" -#: cps/book_formats.py:152 cps/book_formats.py:153 cps/book_formats.py:157 -#: cps/book_formats.py:161 cps/converter.py:29 cps/converter.py:45 +#: cps/book_formats.py:199 cps/book_formats.py:200 cps/book_formats.py:204 +#: cps/book_formats.py:208 cps/book_formats.py:212 cps/converter.py:29 +#: cps/converter.py:45 msgid "not installed" msgstr "" @@ -30,133 +31,133 @@ msgstr "" msgid "not configured" msgstr "" -#: cps/helper.py:72 +#: cps/helper.py:79 #, python-format msgid "%(format)s format not found for book id: %(book)d" msgstr "" -#: cps/helper.py:84 +#: cps/helper.py:91 #, python-format msgid "%(format)s not found on Google Drive: %(fn)s" msgstr "" -#: cps/helper.py:91 cps/helper.py:199 cps/templates/detail.html:45 +#: cps/helper.py:98 cps/helper.py:204 cps/templates/detail.html:45 #: cps/templates/detail.html:49 msgid "Send to Kindle" msgstr "" -#: cps/helper.py:92 cps/helper.py:110 cps/helper.py:201 +#: cps/helper.py:99 cps/helper.py:117 cps/helper.py:206 msgid "This e-mail has been sent via Calibre-Web." msgstr "" -#: cps/helper.py:103 +#: cps/helper.py:110 #, python-format msgid "%(format)s not found: %(fn)s" msgstr "" -#: cps/helper.py:108 +#: cps/helper.py:115 msgid "Calibre-Web test e-mail" msgstr "" -#: cps/helper.py:109 +#: cps/helper.py:116 msgid "Test e-mail" msgstr "" -#: cps/helper.py:125 +#: cps/helper.py:132 msgid "Get Started with Calibre-Web" msgstr "" -#: cps/helper.py:126 +#: cps/helper.py:133 #, python-format msgid "Registration e-mail for user: %(name)s" msgstr "" -#: cps/helper.py:139 cps/helper.py:141 cps/helper.py:143 cps/helper.py:145 -#: cps/helper.py:151 cps/helper.py:153 cps/helper.py:155 cps/helper.py:157 +#: cps/helper.py:146 cps/helper.py:148 cps/helper.py:150 cps/helper.py:158 +#: cps/helper.py:160 cps/helper.py:162 #, python-format msgid "Send %(format)s to Kindle" msgstr "" -#: cps/helper.py:161 cps/helper.py:165 +#: cps/helper.py:166 #, python-format msgid "Convert %(orig)s to %(format)s and send to Kindle" msgstr "" -#: cps/helper.py:200 +#: cps/helper.py:205 #, python-format msgid "E-mail: %(book)s" msgstr "" -#: cps/helper.py:203 +#: cps/helper.py:208 msgid "The requested file could not be read. Maybe wrong permissions?" msgstr "" -#: cps/helper.py:311 +#: cps/helper.py:316 #, python-format msgid "Rename title from: '%(src)s' to '%(dest)s' failed with error: %(error)s" msgstr "" -#: cps/helper.py:321 +#: cps/helper.py:326 #, python-format msgid "Rename author from: '%(src)s' to '%(dest)s' failed with error: %(error)s" msgstr "" -#: cps/helper.py:335 +#: cps/helper.py:340 #, python-format msgid "Rename file in path '%(src)s' to '%(dest)s' failed with error: %(error)s" msgstr "" -#: cps/helper.py:361 cps/helper.py:371 cps/helper.py:379 +#: cps/helper.py:366 cps/helper.py:376 cps/helper.py:384 #, python-format msgid "File %(file)s not found on Google Drive" msgstr "" -#: cps/helper.py:400 +#: cps/helper.py:405 #, python-format msgid "Book path %(path)s not found on Google Drive" msgstr "" -#: cps/helper.py:508 +#: cps/helper.py:556 msgid "Error excecuting UnRar" msgstr "" -#: cps/helper.py:510 +#: cps/helper.py:558 msgid "Unrar binary file not found" msgstr "" -#: cps/helper.py:541 +#: cps/helper.py:589 msgid "Waiting" msgstr "" -#: cps/helper.py:543 +#: cps/helper.py:591 msgid "Failed" msgstr "" -#: cps/helper.py:545 +#: cps/helper.py:593 msgid "Started" msgstr "" -#: cps/helper.py:547 +#: cps/helper.py:595 msgid "Finished" msgstr "" -#: cps/helper.py:549 +#: cps/helper.py:597 msgid "Unknown Status" msgstr "" -#: cps/helper.py:554 +#: cps/helper.py:602 msgid "E-mail: " msgstr "" -#: cps/helper.py:556 cps/helper.py:560 +#: cps/helper.py:604 cps/helper.py:608 msgid "Convert: " msgstr "" -#: cps/helper.py:558 +#: cps/helper.py:606 msgid "Upload: " msgstr "" -#: cps/helper.py:562 +#: cps/helper.py:610 msgid "Unknown Task: " msgstr "" @@ -168,19 +169,19 @@ msgstr "" msgid "No update available. You already have the latest version installed" msgstr "" -#: cps/updater.py:270 cps/updater.py:501 cps/updater.py:503 cps/web.py:1187 +#: cps/updater.py:270 cps/updater.py:501 cps/updater.py:503 cps/web.py:1206 msgid "HTTP Error" msgstr "" -#: cps/updater.py:272 cps/updater.py:505 cps/web.py:1188 +#: cps/updater.py:272 cps/updater.py:505 cps/web.py:1207 msgid "Connection error" msgstr "" -#: cps/updater.py:274 cps/updater.py:507 cps/web.py:1189 +#: cps/updater.py:274 cps/updater.py:507 cps/web.py:1208 msgid "Timeout while establishing connection" msgstr "" -#: cps/updater.py:276 cps/updater.py:509 cps/web.py:1190 +#: cps/updater.py:276 cps/updater.py:509 cps/web.py:1209 msgid "General error" msgstr "" @@ -201,555 +202,545 @@ msgstr "" msgid "A new update is available. Click on the button below to update to version: %(version)s" msgstr "" -#: cps/updater.py:491 cps/web.py:2771 +#: cps/updater.py:491 cps/web.py:2801 msgid "Unknown" msgstr "" -#: cps/web.py:1180 +#: cps/web.py:1199 msgid "Requesting update package" msgstr "" -#: cps/web.py:1181 +#: cps/web.py:1200 msgid "Downloading update package" msgstr "" -#: cps/web.py:1182 +#: cps/web.py:1201 msgid "Unzipping update package" msgstr "" -#: cps/web.py:1183 +#: cps/web.py:1202 msgid "Replacing files" msgstr "" -#: cps/web.py:1184 +#: cps/web.py:1203 msgid "Database connections are closed" msgstr "" -#: cps/web.py:1185 +#: cps/web.py:1204 msgid "Stopping server" msgstr "" -#: cps/web.py:1186 +#: cps/web.py:1205 msgid "Update finished, please press okay and reload page" msgstr "" -#: cps/web.py:1187 cps/web.py:1188 cps/web.py:1189 cps/web.py:1190 +#: cps/web.py:1206 cps/web.py:1207 cps/web.py:1208 cps/web.py:1209 msgid "Update failed:" msgstr "" -#: cps/web.py:1213 +#: cps/web.py:1235 msgid "Recently Added Books" msgstr "" -#: cps/web.py:1223 +#: cps/web.py:1245 msgid "Newest Books" msgstr "" -#: cps/web.py:1235 +#: cps/web.py:1257 msgid "Oldest Books" msgstr "" -#: cps/web.py:1247 +#: cps/web.py:1269 msgid "Books (A-Z)" msgstr "" -#: cps/web.py:1258 +#: cps/web.py:1280 msgid "Books (Z-A)" msgstr "" -#: cps/web.py:1287 +#: cps/web.py:1309 msgid "Hot Books (most downloaded)" msgstr "" -#: cps/web.py:1300 +#: cps/web.py:1322 msgid "Best rated books" msgstr "" -#: cps/templates/index.xml:39 cps/web.py:1313 +#: cps/templates/index.xml:39 cps/web.py:1335 msgid "Random Books" msgstr "" -#: cps/web.py:1340 cps/web.py:1596 cps/web.py:2140 +#: cps/web.py:1362 cps/web.py:1618 cps/web.py:2167 msgid "Error opening eBook. File does not exist or file is not accessible:" msgstr "" -#: cps/web.py:1369 +#: cps/web.py:1391 msgid "Publisher list" msgstr "" -#: cps/web.py:1384 +#: cps/web.py:1406 #, python-format msgid "Publisher: %(name)s" msgstr "" -#: cps/templates/index.xml:83 cps/web.py:1416 +#: cps/templates/index.xml:83 cps/web.py:1438 msgid "Series list" msgstr "" -#: cps/web.py:1430 +#: cps/web.py:1452 #, python-format msgid "Series: %(serie)s" msgstr "" -#: cps/web.py:1456 +#: cps/web.py:1478 msgid "Available languages" msgstr "" -#: cps/web.py:1476 +#: cps/web.py:1498 #, python-format msgid "Language: %(name)s" msgstr "" -#: cps/templates/index.xml:76 cps/web.py:1487 +#: cps/templates/index.xml:76 cps/web.py:1509 msgid "Category list" msgstr "" -#: cps/web.py:1501 +#: cps/web.py:1523 #, python-format msgid "Category: %(name)s" msgstr "" -#: cps/templates/layout.html:73 cps/web.py:1632 +#: cps/templates/layout.html:73 cps/web.py:1654 msgid "Tasks" msgstr "" -#: cps/web.py:1666 +#: cps/web.py:1688 msgid "Statistics" msgstr "" -#: cps/web.py:1734 +#: cps/web.py:1756 msgid "Google Drive setup not completed, try to deactivate and activate Google Drive again" msgstr "" -#: cps/web.py:1779 +#: cps/web.py:1801 msgid "Callback domain is not verified, please follow steps to verify domain in google developer console" msgstr "" -#: cps/web.py:1855 +#: cps/web.py:1877 msgid "Server restarted, please reload page" msgstr "" -#: cps/web.py:1858 +#: cps/web.py:1880 msgid "Performing shutdown of server, please close window" msgstr "" -#: cps/web.py:1938 +#: cps/web.py:1959 msgid "Published after " msgstr "" -#: cps/web.py:1945 +#: cps/web.py:1966 msgid "Published before " msgstr "" -#: cps/web.py:1959 +#: cps/web.py:1980 #, python-format msgid "Rating <= %(rating)s" msgstr "" -#: cps/web.py:1961 +#: cps/web.py:1982 #, python-format msgid "Rating >= %(rating)s" msgstr "" -#: cps/web.py:2022 cps/web.py:2031 +#: cps/web.py:2042 cps/web.py:2051 msgid "search" msgstr "" #: cps/templates/index.xml:47 cps/templates/index.xml:51 -#: cps/templates/layout.html:148 cps/web.py:2099 +#: cps/templates/layout.html:148 cps/web.py:2122 msgid "Read Books" msgstr "" #: cps/templates/index.xml:55 cps/templates/index.xml:59 -#: cps/templates/layout.html:150 cps/web.py:2102 +#: cps/templates/layout.html:150 cps/web.py:2125 msgid "Unread Books" msgstr "" -#: cps/web.py:2150 cps/web.py:2152 cps/web.py:2154 cps/web.py:2166 +#: cps/web.py:2177 cps/web.py:2179 cps/web.py:2181 cps/web.py:2193 msgid "Read a Book" msgstr "" -#: cps/web.py:2225 cps/web.py:3146 +#: cps/web.py:2205 +msgid "Error opening eBook. Fileformat is not supported." +msgstr "" + +#: cps/web.py:2255 cps/web.py:3176 msgid "Please fill out all fields!" msgstr "" -#: cps/web.py:2226 cps/web.py:2248 cps/web.py:2252 cps/web.py:2257 -#: cps/web.py:2259 +#: cps/web.py:2256 cps/web.py:2278 cps/web.py:2282 cps/web.py:2287 +#: cps/web.py:2289 msgid "register" msgstr "" -#: cps/web.py:2247 cps/web.py:3365 +#: cps/web.py:2277 cps/web.py:3395 msgid "An unknown error occurred. Please try again later." msgstr "" -#: cps/web.py:2250 +#: cps/web.py:2280 msgid "Your e-mail is not allowed to register" msgstr "" -#: cps/web.py:2253 +#: cps/web.py:2283 msgid "Confirmation e-mail was send to your e-mail account." msgstr "" -#: cps/web.py:2256 +#: cps/web.py:2286 msgid "This username or e-mail address is already in use." msgstr "" -#: cps/web.py:2273 cps/web.py:2369 +#: cps/web.py:2303 cps/web.py:2399 #, python-format msgid "you are now logged in as: '%(nickname)s'" msgstr "" -#: cps/web.py:2278 +#: cps/web.py:2308 msgid "Wrong Username or Password" msgstr "" -#: cps/web.py:2284 cps/web.py:2305 +#: cps/web.py:2314 cps/web.py:2335 msgid "login" msgstr "" -#: cps/web.py:2317 cps/web.py:2348 +#: cps/web.py:2347 cps/web.py:2378 msgid "Token not found" msgstr "" -#: cps/web.py:2325 cps/web.py:2356 +#: cps/web.py:2355 cps/web.py:2386 msgid "Token has expired" msgstr "" -#: cps/web.py:2333 +#: cps/web.py:2363 msgid "Success! Please return to your device" msgstr "" -#: cps/web.py:2383 +#: cps/web.py:2413 msgid "Please configure the SMTP mail settings first..." msgstr "" -#: cps/web.py:2388 +#: cps/web.py:2418 #, python-format msgid "Book successfully queued for sending to %(kindlemail)s" msgstr "" -#: cps/web.py:2392 +#: cps/web.py:2422 #, python-format msgid "There was an error sending this book: %(res)s" msgstr "" -#: cps/web.py:2394 cps/web.py:3199 +#: cps/web.py:2424 cps/web.py:3229 msgid "Please configure your kindle e-mail address first..." msgstr "" -#: cps/web.py:2405 cps/web.py:2457 +#: cps/web.py:2435 cps/web.py:2487 msgid "Invalid shelf specified" msgstr "" -#: cps/web.py:2412 +#: cps/web.py:2442 #, python-format msgid "Sorry you are not allowed to add a book to the the shelf: %(shelfname)s" msgstr "" -#: cps/web.py:2420 +#: cps/web.py:2450 msgid "You are not allowed to edit public shelves" msgstr "" -#: cps/web.py:2429 +#: cps/web.py:2459 #, python-format msgid "Book is already part of the shelf: %(shelfname)s" msgstr "" -#: cps/web.py:2443 +#: cps/web.py:2473 #, python-format msgid "Book has been added to shelf: %(sname)s" msgstr "" -#: cps/web.py:2462 +#: cps/web.py:2492 #, python-format msgid "You are not allowed to add a book to the the shelf: %(name)s" msgstr "" -#: cps/web.py:2467 +#: cps/web.py:2497 msgid "User is not allowed to edit public shelves" msgstr "" -#: cps/web.py:2485 +#: cps/web.py:2515 #, python-format msgid "Books are already part of the shelf: %(name)s" msgstr "" -#: cps/web.py:2499 +#: cps/web.py:2529 #, python-format msgid "Books have been added to shelf: %(sname)s" msgstr "" -#: cps/web.py:2501 +#: cps/web.py:2531 #, python-format msgid "Could not add books to shelf: %(sname)s" msgstr "" -#: cps/web.py:2538 +#: cps/web.py:2568 #, python-format msgid "Book has been removed from shelf: %(sname)s" msgstr "" -#: cps/web.py:2544 +#: cps/web.py:2574 #, python-format msgid "Sorry you are not allowed to remove a book from this shelf: %(sname)s" msgstr "" -#: cps/web.py:2565 cps/web.py:2589 +#: cps/web.py:2595 cps/web.py:2619 #, python-format msgid "A shelf with the name '%(title)s' already exists." msgstr "" -#: cps/web.py:2570 +#: cps/web.py:2600 #, python-format msgid "Shelf %(title)s created" msgstr "" -#: cps/web.py:2572 cps/web.py:2600 +#: cps/web.py:2602 cps/web.py:2630 msgid "There was an error" msgstr "" -#: cps/web.py:2573 cps/web.py:2575 +#: cps/web.py:2603 cps/web.py:2605 msgid "create a shelf" msgstr "" -#: cps/web.py:2598 +#: cps/web.py:2628 #, python-format msgid "Shelf %(title)s changed" msgstr "" -#: cps/web.py:2601 cps/web.py:2603 +#: cps/web.py:2631 cps/web.py:2633 msgid "Edit a shelf" msgstr "" -#: cps/web.py:2624 +#: cps/web.py:2654 #, python-format msgid "successfully deleted shelf %(name)s" msgstr "" -#: cps/web.py:2651 +#: cps/web.py:2681 #, python-format msgid "Shelf: '%(name)s'" msgstr "" -#: cps/web.py:2654 +#: cps/web.py:2684 msgid "Error opening shelf. Shelf does not exist or is not accessible" msgstr "" -#: cps/web.py:2685 +#: cps/web.py:2715 #, python-format msgid "Change order of Shelf: '%(name)s'" msgstr "" -#: cps/web.py:2714 cps/web.py:3152 +#: cps/web.py:2744 cps/web.py:3182 msgid "E-mail is not from valid domain" msgstr "" -#: cps/web.py:2716 cps/web.py:2758 cps/web.py:2761 +#: cps/web.py:2746 cps/web.py:2788 cps/web.py:2791 #, python-format msgid "%(name)s's profile" msgstr "" -#: cps/web.py:2756 +#: cps/web.py:2786 msgid "Found an existing account for this e-mail address." msgstr "" -#: cps/web.py:2759 +#: cps/web.py:2789 msgid "Profile updated" msgstr "" -#: cps/web.py:2790 +#: cps/web.py:2820 msgid "Admin page" msgstr "" -#: cps/web.py:2875 cps/web.py:3055 +#: cps/web.py:2905 cps/web.py:3085 msgid "Calibre-Web configuration updated" msgstr "" -#: cps/templates/admin.html:100 cps/web.py:2889 +#: cps/templates/admin.html:100 cps/web.py:2919 msgid "UI Configuration" msgstr "" -#: cps/web.py:2907 +#: cps/web.py:2937 msgid "Import of optional Google Drive requirements missing" msgstr "" -#: cps/web.py:2910 +#: cps/web.py:2940 msgid "client_secrets.json is missing or not readable" msgstr "" -#: cps/web.py:2915 cps/web.py:2944 +#: cps/web.py:2945 cps/web.py:2974 msgid "client_secrets.json is not configured for web application" msgstr "" -#: cps/templates/admin.html:99 cps/web.py:2947 cps/web.py:2973 cps/web.py:2985 -#: cps/web.py:3030 cps/web.py:3045 cps/web.py:3064 cps/web.py:3072 -#: cps/web.py:3088 +#: cps/templates/admin.html:99 cps/web.py:2977 cps/web.py:3003 cps/web.py:3015 +#: cps/web.py:3060 cps/web.py:3075 cps/web.py:3094 cps/web.py:3102 +#: cps/web.py:3118 msgid "Basic Configuration" msgstr "" -#: cps/web.py:2970 +#: cps/web.py:3000 msgid "Keyfile location is not valid, please enter correct path" msgstr "" -#: cps/web.py:2982 +#: cps/web.py:3012 msgid "Certfile location is not valid, please enter correct path" msgstr "" -#: cps/web.py:3027 +#: cps/web.py:3057 msgid "Logfile location is not valid, please enter correct path" msgstr "" -#: cps/web.py:3068 +#: cps/web.py:3098 msgid "DB location is not valid, please enter correct path" msgstr "" -#: cps/templates/admin.html:33 cps/web.py:3148 cps/web.py:3154 cps/web.py:3170 +#: cps/templates/admin.html:33 cps/web.py:3178 cps/web.py:3184 cps/web.py:3200 msgid "Add new user" msgstr "" -#: cps/web.py:3160 +#: cps/web.py:3190 #, python-format msgid "User '%(user)s' created" msgstr "" -#: cps/web.py:3164 +#: cps/web.py:3194 msgid "Found an existing account for this e-mail address or nickname." msgstr "" -#: cps/web.py:3194 +#: cps/web.py:3224 #, python-format msgid "Test e-mail successfully send to %(kindlemail)s" msgstr "" -#: cps/web.py:3197 +#: cps/web.py:3227 #, python-format msgid "There was an error sending the Test e-mail: %(res)s" msgstr "" -#: cps/web.py:3201 +#: cps/web.py:3231 msgid "E-mail server settings updated" msgstr "" -#: cps/web.py:3202 +#: cps/web.py:3232 msgid "Edit e-mail server settings" msgstr "" -#: cps/web.py:3227 +#: cps/web.py:3257 #, python-format msgid "User '%(nick)s' deleted" msgstr "" -#: cps/web.py:3340 +#: cps/web.py:3370 #, python-format msgid "User '%(nick)s' updated" msgstr "" -#: cps/web.py:3343 +#: cps/web.py:3373 msgid "An unknown error occured." msgstr "" -#: cps/web.py:3345 +#: cps/web.py:3375 #, python-format msgid "Edit User %(nick)s" msgstr "" -#: cps/web.py:3362 +#: cps/web.py:3392 #, python-format msgid "Password for user %(user)s reset" msgstr "" -#: cps/web.py:3376 cps/web.py:3582 +#: cps/web.py:3406 cps/web.py:3598 msgid "Error opening eBook. File does not exist or file is not accessible" msgstr "" -#: cps/web.py:3404 +#: cps/web.py:3434 msgid "edit metadata" msgstr "" -#: cps/web.py:3497 cps/web.py:3743 +#: cps/web.py:3527 cps/web.py:3760 #, python-format msgid "File extension '%(ext)s' is not allowed to be uploaded to this server" msgstr "" -#: cps/web.py:3501 cps/web.py:3746 +#: cps/web.py:3531 cps/web.py:3763 msgid "File to be uploaded must have an extension" msgstr "" -#: cps/web.py:3513 cps/web.py:3765 +#: cps/web.py:3543 cps/web.py:3782 #, python-format msgid "Failed to create path %(path)s (Permission denied)." msgstr "" -#: cps/web.py:3518 +#: cps/web.py:3548 #, python-format msgid "Failed to store file %(file)s." msgstr "" -#: cps/web.py:3535 +#: cps/web.py:3565 #, python-format msgid "File format %(ext)s added to %(book)s" msgstr "" -#: cps/web.py:3553 -#, python-format -msgid "Failed to create path for cover %(path)s (Permission denied)." -msgstr "" - -#: cps/web.py:3561 -#, python-format -msgid "Failed to store cover-file %(cover)s." -msgstr "" - -#: cps/web.py:3564 -msgid "Cover-file is not a valid image file" +#: cps/web.py:3579 cps/web.py:3652 +msgid "Cover is not a supported imageformat (jpg/png/webp), can't save" msgstr "" -#: cps/web.py:3594 cps/web.py:3603 +#: cps/web.py:3611 cps/web.py:3620 msgid "unknown" msgstr "" -#: cps/web.py:3635 -msgid "Cover is not a jpg file, can't save" -msgstr "" - -#: cps/web.py:3683 +#: cps/web.py:3700 #, python-format msgid "%(langname)s is not a valid language" msgstr "" -#: cps/web.py:3714 +#: cps/web.py:3731 msgid "Metadata successfully updated" msgstr "" -#: cps/web.py:3723 +#: cps/web.py:3740 msgid "Error editing book, please check logfile for details" msgstr "" -#: cps/web.py:3769 +#: cps/web.py:3786 #, python-format msgid "Failed to store file %(file)s (Permission denied)." msgstr "" -#: cps/web.py:3774 +#: cps/web.py:3791 #, python-format msgid "Failed to delete file %(file)s (Permission denied)." msgstr "" -#: cps/web.py:3857 +#: cps/web.py:3873 #, python-format msgid "File %(title)s" msgstr "" -#: cps/web.py:3886 +#: cps/web.py:3902 msgid "Source or destination format for conversion missing" msgstr "" -#: cps/web.py:3896 +#: cps/web.py:3912 #, python-format msgid "Book successfully queued for converting to %(book_format)s" msgstr "" -#: cps/web.py:3900 +#: cps/web.py:3916 #, python-format msgid "There was an error converting this book: %(res)s" msgstr "" @@ -1287,7 +1278,7 @@ msgstr "" msgid "No. of authors to show before hiding (0=disable hiding)" msgstr "" -#: cps/templates/config_view_edit.html:35 cps/templates/readcbr.html:108 +#: cps/templates/config_view_edit.html:35 cps/templates/readcbr.html:118 msgid "Theme" msgstr "" @@ -1601,7 +1592,7 @@ msgid "Advanced Search" msgstr "" #: cps/templates/layout.html:76 cps/templates/read.html:71 -#: cps/templates/readcbr.html:79 cps/templates/readcbr.html:103 +#: cps/templates/readcbr.html:89 cps/templates/readcbr.html:113 msgid "Settings" msgstr "" @@ -1720,96 +1711,104 @@ msgstr "" msgid "Reflow text when sidebars are open." msgstr "" -#: cps/templates/readcbr.html:84 +#: cps/templates/readcbr.html:94 msgid "Keyboard Shortcuts" msgstr "" -#: cps/templates/readcbr.html:87 +#: cps/templates/readcbr.html:97 msgid "Previous Page" msgstr "" -#: cps/templates/readcbr.html:88 +#: cps/templates/readcbr.html:98 msgid "Next Page" msgstr "" -#: cps/templates/readcbr.html:89 +#: cps/templates/readcbr.html:99 msgid "Scale to Best" msgstr "" -#: cps/templates/readcbr.html:90 +#: cps/templates/readcbr.html:100 msgid "Scale to Width" msgstr "" -#: cps/templates/readcbr.html:91 +#: cps/templates/readcbr.html:101 msgid "Scale to Height" msgstr "" -#: cps/templates/readcbr.html:92 +#: cps/templates/readcbr.html:102 msgid "Scale to Native" msgstr "" -#: cps/templates/readcbr.html:93 +#: cps/templates/readcbr.html:103 msgid "Rotate Right" msgstr "" -#: cps/templates/readcbr.html:94 +#: cps/templates/readcbr.html:104 msgid "Rotate Left" msgstr "" -#: cps/templates/readcbr.html:95 +#: cps/templates/readcbr.html:105 msgid "Flip Image" msgstr "" -#: cps/templates/readcbr.html:111 +#: cps/templates/readcbr.html:121 msgid "Light" msgstr "" -#: cps/templates/readcbr.html:112 +#: cps/templates/readcbr.html:122 msgid "Dark" msgstr "" -#: cps/templates/readcbr.html:117 +#: cps/templates/readcbr.html:127 msgid "Scale" msgstr "" -#: cps/templates/readcbr.html:120 +#: cps/templates/readcbr.html:130 msgid "Best" msgstr "" -#: cps/templates/readcbr.html:121 +#: cps/templates/readcbr.html:131 msgid "Width" msgstr "" -#: cps/templates/readcbr.html:122 +#: cps/templates/readcbr.html:132 msgid "Height" msgstr "" -#: cps/templates/readcbr.html:123 +#: cps/templates/readcbr.html:133 msgid "Native" msgstr "" -#: cps/templates/readcbr.html:128 +#: cps/templates/readcbr.html:138 msgid "Rotate" msgstr "" -#: cps/templates/readcbr.html:139 +#: cps/templates/readcbr.html:149 msgid "Flip" msgstr "" -#: cps/templates/readcbr.html:142 +#: cps/templates/readcbr.html:152 msgid "Horizontal" msgstr "" -#: cps/templates/readcbr.html:143 +#: cps/templates/readcbr.html:153 msgid "Vertical" msgstr "" -#: cps/templates/readpdf.html:29 -msgid "PDF.js viewer" +#: cps/templates/readcbr.html:158 +msgid "Direction" +msgstr "" + +#: cps/templates/readcbr.html:161 +msgid "Left to Right" msgstr "" -#: cps/templates/readpdf.html:418 -msgid "Preparing document for printing..." +#: cps/templates/readcbr.html:162 +msgid "Right to Left" +msgstr "" + +#: cps/templates/readpdf.html:29 +msgid "PDF.js viewer" msgstr "" #: cps/templates/readtxt.html:6 From c0d136ccd82a8108272d05a274b01607d123fc88 Mon Sep 17 00:00:00 2001 From: subdiox Date: Fri, 10 May 2019 08:16:03 +0900 Subject: [PATCH 14/21] Fix slow loading --- cps/static/js/archive.js | 357 ------- cps/static/js/archive/archive.js | 362 +++++++ cps/static/js/archive/rarvm.js | 1009 ++++++++++++++++++ cps/static/js/archive/unrar.js | 1459 ++++++++++++++++++++++++++ cps/static/js/{ => archive}/untar.js | 41 +- cps/static/js/archive/unzip.js | 665 ++++++++++++ cps/static/js/io/bitstream.js | 288 +++++ cps/static/js/io/bytebuffer.js | 117 +++ cps/static/js/{ => io}/bytestream.js | 0 cps/static/js/unrar.js | 902 ---------------- cps/static/js/unzip.js | 626 ----------- cps/templates/readcbr.html | 2 +- 12 files changed, 3920 insertions(+), 1908 deletions(-) delete mode 100644 cps/static/js/archive.js create mode 100644 cps/static/js/archive/archive.js create mode 100644 cps/static/js/archive/rarvm.js create mode 100644 cps/static/js/archive/unrar.js rename cps/static/js/{ => archive}/untar.js (93%) create mode 100644 cps/static/js/archive/unzip.js create mode 100644 cps/static/js/io/bitstream.js create mode 100644 cps/static/js/io/bytebuffer.js rename cps/static/js/{ => io}/bytestream.js (100%) delete mode 100644 cps/static/js/unrar.js delete mode 100644 cps/static/js/unzip.js diff --git a/cps/static/js/archive.js b/cps/static/js/archive.js deleted file mode 100644 index 3a0ceef3..00000000 --- a/cps/static/js/archive.js +++ /dev/null @@ -1,357 +0,0 @@ -/** - * archive.js - * - * Provides base functionality for unarchiving. - * - * Licensed under the MIT License - * - * Copyright(c) 2011 Google Inc. - */ - -/* global bitjs */ - -var bitjs = bitjs || {}; -bitjs.archive = bitjs.archive || {}; - -(function() { - - // =========================================================================== - // Stolen from Closure because it's the best way to do Java-like inheritance. - bitjs.base = function(me, optMethodName, varArgs) { - var caller = arguments.callee.caller; - if (caller.superClass_) { - // This is a constructor. Call the superclass constructor. - return caller.superClass_.constructor.apply( - me, Array.prototype.slice.call(arguments, 1)); - } - - var args = Array.prototype.slice.call(arguments, 2); - var foundCaller = false; - for (var ctor = me.constructor; - ctor; ctor = ctor.superClass_ && ctor.superClass_.constructor) { - if (ctor.prototype[optMethodName] === caller) { - foundCaller = true; - } else if (foundCaller) { - return ctor.prototype[optMethodName].apply(me, args); - } - } - - // If we did not find the caller in the prototype chain, - // then one of two things happened: - // 1) The caller is an instance method. - // 2) This method was not called by the right caller. - if (me[optMethodName] === caller) { - return me.constructor.prototype[optMethodName].apply(me, args); - } else { - throw Error( - "goog.base called from a method of one name " + - "to a method of a different name"); - } - }; - bitjs.inherits = function(childCtor, parentCtor) { - /** @constructor */ - function TempCtor() {} - TempCtor.prototype = parentCtor.prototype; - childCtor.superClass_ = parentCtor.prototype; - childCtor.prototype = new TempCtor(); - childCtor.prototype.constructor = childCtor; - }; - // =========================================================================== - - /** - * An unarchive event. - * - * @param {string} type The event type. - * @constructor - */ - bitjs.archive.UnarchiveEvent = function(type) { - /** - * The event type. - * - * @type {string} - */ - this.type = type; - }; - - /** - * The UnarchiveEvent types. - */ - bitjs.archive.UnarchiveEvent.Type = { - START: "start", - PROGRESS: "progress", - EXTRACT: "extract", - FINISH: "finish", - INFO: "info", - ERROR: "error" - }; - - /** - * Useful for passing info up to the client (for debugging). - * - * @param {string} msg The info message. - */ - bitjs.archive.UnarchiveInfoEvent = function(msg) { - bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.INFO); - - /** - * The information message. - * - * @type {string} - */ - this.msg = msg; - }; - bitjs.inherits(bitjs.archive.UnarchiveInfoEvent, bitjs.archive.UnarchiveEvent); - - /** - * An unrecoverable error has occured. - * - * @param {string} msg The error message. - */ - bitjs.archive.UnarchiveErrorEvent = function(msg) { - bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.ERROR); - - /** - * The information message. - * - * @type {string} - */ - this.msg = msg; - }; - bitjs.inherits(bitjs.archive.UnarchiveErrorEvent, bitjs.archive.UnarchiveEvent); - - /** - * Start event. - * - * @param {string} msg The info message. - */ - bitjs.archive.UnarchiveStartEvent = function() { - bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.START); - }; - bitjs.inherits(bitjs.archive.UnarchiveStartEvent, bitjs.archive.UnarchiveEvent); - - /** - * Finish event. - * - * @param {string} msg The info message. - */ - bitjs.archive.UnarchiveFinishEvent = function() { - bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.FINISH); - }; - bitjs.inherits(bitjs.archive.UnarchiveFinishEvent, bitjs.archive.UnarchiveEvent); - - /** - * Progress event. - */ - bitjs.archive.UnarchiveProgressEvent = function( - currentFilename, - currentFileNumber, - currentBytesUnarchivedInFile, - currentBytesUnarchived, - totalUncompressedBytesInArchive, - totalFilesInArchive) { - bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.PROGRESS); - - this.currentFilename = currentFilename; - this.currentFileNumber = currentFileNumber; - this.currentBytesUnarchivedInFile = currentBytesUnarchivedInFile; - this.totalFilesInArchive = totalFilesInArchive; - this.currentBytesUnarchived = currentBytesUnarchived; - this.totalUncompressedBytesInArchive = totalUncompressedBytesInArchive; - }; - bitjs.inherits(bitjs.archive.UnarchiveProgressEvent, bitjs.archive.UnarchiveEvent); - - /** - * All extracted files returned by an Unarchiver will implement - * the following interface: - * - * interface UnarchivedFile { - * string filename - * TypedArray fileData - * } - * - */ - - /** - * Extract event. - */ - bitjs.archive.UnarchiveExtractEvent = function(unarchivedFile) { - bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.EXTRACT); - - /** - * @type {UnarchivedFile} - */ - this.unarchivedFile = unarchivedFile; - }; - bitjs.inherits(bitjs.archive.UnarchiveExtractEvent, bitjs.archive.UnarchiveEvent); - - - /** - * Base class for all Unarchivers. - * - * @param {ArrayBuffer} arrayBuffer The Array Buffer. - * @param {string} optPathToBitJS Optional string for where the BitJS files are located. - * @constructor - */ - bitjs.archive.Unarchiver = function(arrayBuffer, optPathToBitJS) { - /** - * The ArrayBuffer object. - * @type {ArrayBuffer} - * @protected - */ - this.ab = arrayBuffer; - - /** - * The path to the BitJS files. - * @type {string} - * @private - */ - this.pathToBitJS_ = optPathToBitJS || ""; - - /** - * A map from event type to an array of listeners. - * @type {Map.} - */ - this.listeners_ = {}; - for (var type in bitjs.archive.UnarchiveEvent.Type) { - this.listeners_[bitjs.archive.UnarchiveEvent.Type[type]] = []; - } - }; - - /** - * Private web worker initialized during start(). - * @type {Worker} - * @private - */ - bitjs.archive.Unarchiver.prototype.worker_ = null; - - /** - * This method must be overridden by the subclass to return the script filename. - * @return {string} The script filename. - * @protected. - */ - bitjs.archive.Unarchiver.prototype.getScriptFileName = function() { - throw "Subclasses of AbstractUnarchiver must overload getScriptFileName()"; - }; - - /** - * Adds an event listener for UnarchiveEvents. - * - * @param {string} Event type. - * @param {function} An event handler function. - */ - bitjs.archive.Unarchiver.prototype.addEventListener = function(type, listener) { - if (type in this.listeners_) { - if (this.listeners_[type].indexOf(listener) === -1) { - this.listeners_[type].push(listener); - } - } - }; - - /** - * Removes an event listener. - * - * @param {string} Event type. - * @param {EventListener|function} An event listener or handler function. - */ - bitjs.archive.Unarchiver.prototype.removeEventListener = function(type, listener) { - if (type in this.listeners_) { - var index = this.listeners_[type].indexOf(listener); - if (index !== -1) { - this.listeners_[type].splice(index, 1); - } - } - }; - - /** - * Receive an event and pass it to the listener functions. - * - * @param {bitjs.archive.UnarchiveEvent} e - * @private - */ - bitjs.archive.Unarchiver.prototype.handleWorkerEvent_ = function(e) { - if ((e instanceof bitjs.archive.UnarchiveEvent || e.type) && - this.listeners_[e.type] instanceof Array) { - this.listeners_[e.type].forEach(function (listener) { - listener(e); - }); - if (e.type === bitjs.archive.UnarchiveEvent.Type.FINISH) { - this.worker_.terminate(); - } - } - }; - - /** - * Starts the unarchive in a separate Web Worker thread and returns immediately. - */ - bitjs.archive.Unarchiver.prototype.start = function() { - var me = this; - var scriptFileName = this.pathToBitJS_ + this.getScriptFileName(); - if (scriptFileName) { - this.worker_ = new Worker(scriptFileName); - - this.worker_.onerror = function(e) { - throw e; - }; - - this.worker_.onmessage = function(e) { - if (typeof e.data !== "string") { - // Assume that it is an UnarchiveEvent. Some browsers preserve the 'type' - // so that instanceof UnarchiveEvent returns true, but others do not. - me.handleWorkerEvent_(e.data); - } - }; - - this.worker_.postMessage({file: this.ab}); - } - }; - - /** - * Terminates the Web Worker for this Unarchiver and returns immediately. - */ - bitjs.archive.Unarchiver.prototype.stop = function() { - if (this.worker_) { - this.worker_.terminate(); - } - }; - - - /** - * Unzipper - * @extends {bitjs.archive.Unarchiver} - * @constructor - */ - bitjs.archive.Unzipper = function(arrayBuffer, optPathToBitJS) { - bitjs.base(this, arrayBuffer, optPathToBitJS); - }; - bitjs.inherits(bitjs.archive.Unzipper, bitjs.archive.Unarchiver); - bitjs.archive.Unzipper.prototype.getScriptFileName = function() { - return "unzip.js"; - }; - - /** - * Unrarrer - * @extends {bitjs.archive.Unarchiver} - * @constructor - */ - bitjs.archive.Unrarrer = function(arrayBuffer, optPathToBitJS) { - bitjs.base(this, arrayBuffer, optPathToBitJS); - }; - bitjs.inherits(bitjs.archive.Unrarrer, bitjs.archive.Unarchiver); - bitjs.archive.Unrarrer.prototype.getScriptFileName = function() { - return "unrar.js"; - }; - - /** - * Untarrer - * @extends {bitjs.archive.Unarchiver} - * @constructor - */ - bitjs.archive.Untarrer = function(arrayBuffer, optPathToBitJS) { - bitjs.base(this, arrayBuffer, optPathToBitJS); - }; - bitjs.inherits(bitjs.archive.Untarrer, bitjs.archive.Unarchiver); - bitjs.archive.Untarrer.prototype.getScriptFileName = function() { - return "untar.js"; - }; - -})(); diff --git a/cps/static/js/archive/archive.js b/cps/static/js/archive/archive.js new file mode 100644 index 00000000..22657b59 --- /dev/null +++ b/cps/static/js/archive/archive.js @@ -0,0 +1,362 @@ +/** + * archive.js + * + * Provides base functionality for unarchiving. + * + * Licensed under the MIT License + * + * Copyright(c) 2011 Google Inc. + */ + +var bitjs = bitjs || {}; +bitjs.archive = bitjs.archive || {}; + +/** + * An unarchive event. + */ +bitjs.archive.UnarchiveEvent = class { + /** + * @param {string} type The event type. + */ + constructor(type) { + /** + * The event type. + * @type {string} + */ + this.type = type; + } +} + +/** + * The UnarchiveEvent types. + */ +bitjs.archive.UnarchiveEvent.Type = { + START: 'start', + PROGRESS: 'progress', + EXTRACT: 'extract', + FINISH: 'finish', + INFO: 'info', + ERROR: 'error' +}; + +/** + * Useful for passing info up to the client (for debugging). + */ +bitjs.archive.UnarchiveInfoEvent = class extends bitjs.archive.UnarchiveEvent { + /** + * @param {string} msg The info message. + */ + constructor(msg) { + super(bitjs.archive.UnarchiveEvent.Type.INFO); + + /** + * The information message. + * @type {string} + */ + this.msg = msg; + } +} + +/** + * An unrecoverable error has occured. + */ +bitjs.archive.UnarchiveErrorEvent = class extends bitjs.archive.UnarchiveEvent { + /** + * @param {string} msg The error message. + */ + constructor(msg) { + super(bitjs.archive.UnarchiveEvent.Type.ERROR); + + /** + * The information message. + * @type {string} + */ + this.msg = msg; + } +} + +/** + * Start event. + */ +bitjs.archive.UnarchiveStartEvent = class extends bitjs.archive.UnarchiveEvent { + constructor() { + super(bitjs.archive.UnarchiveEvent.Type.START); + } +} + +/** + * Finish event. + */ +bitjs.archive.UnarchiveFinishEvent = class extends bitjs.archive.UnarchiveEvent { + constructor() { + super(bitjs.archive.UnarchiveEvent.Type.FINISH); + } +} + +/** + * Progress event. + */ +bitjs.archive.UnarchiveProgressEvent = class extends bitjs.archive.UnarchiveEvent { + /** + * @param {string} currentFilename + * @param {number} currentFileNumber + * @param {number} currentBytesUnarchivedInFile + * @param {number} currentBytesUnarchived + * @param {number} totalUncompressedBytesInArchive + * @param {number} totalFilesInArchive + * @param {number} totalCompressedBytesRead + */ + constructor(currentFilename, currentFileNumber, currentBytesUnarchivedInFile, + currentBytesUnarchived, totalUncompressedBytesInArchive, totalFilesInArchive, + totalCompressedBytesRead) { + super(bitjs.archive.UnarchiveEvent.Type.PROGRESS); + + this.currentFilename = currentFilename; + this.currentFileNumber = currentFileNumber; + this.currentBytesUnarchivedInFile = currentBytesUnarchivedInFile; + this.totalFilesInArchive = totalFilesInArchive; + this.currentBytesUnarchived = currentBytesUnarchived; + this.totalUncompressedBytesInArchive = totalUncompressedBytesInArchive; + this.totalCompressedBytesRead = totalCompressedBytesRead; + } +} + +/** + * Extract event. + */ +bitjs.archive.UnarchiveExtractEvent = class extends bitjs.archive.UnarchiveEvent { + /** + * @param {UnarchivedFile} unarchivedFile + */ + constructor(unarchivedFile) { + super(bitjs.archive.UnarchiveEvent.Type.EXTRACT); + + /** + * @type {UnarchivedFile} + */ + this.unarchivedFile = unarchivedFile; + } +} + +/** + * All extracted files returned by an Unarchiver will implement + * the following interface: + * + * interface UnarchivedFile { + * string filename + * TypedArray fileData + * } + * + */ + +/** + * Base class for all Unarchivers. + */ +bitjs.archive.Unarchiver = class { + /** + * @param {ArrayBuffer} arrayBuffer The Array Buffer. + * @param {string} opt_pathToBitJS Optional string for where the BitJS files are located. + */ + constructor(arrayBuffer, opt_pathToBitJS) { + /** + * The ArrayBuffer object. + * @type {ArrayBuffer} + * @protected + */ + this.ab = arrayBuffer; + + /** + * The path to the BitJS files. + * @type {string} + * @private + */ + this.pathToBitJS_ = opt_pathToBitJS || '/'; + + /** + * A map from event type to an array of listeners. + * @type {Map.} + */ + this.listeners_ = {}; + for (let type in bitjs.archive.UnarchiveEvent.Type) { + this.listeners_[bitjs.archive.UnarchiveEvent.Type[type]] = []; + } + + /** + * Private web worker initialized during start(). + * @type {Worker} + * @private + */ + this.worker_ = null; + } + + /** + * This method must be overridden by the subclass to return the script filename. + * @return {string} The script filename. + * @protected. + */ + getScriptFileName() { + throw 'Subclasses of AbstractUnarchiver must overload getScriptFileName()'; + } + + /** + * Adds an event listener for UnarchiveEvents. + * + * @param {string} Event type. + * @param {function} An event handler function. + */ + addEventListener(type, listener) { + if (type in this.listeners_) { + if (this.listeners_[type].indexOf(listener) == -1) { + this.listeners_[type].push(listener); + } + } + } + + /** + * Removes an event listener. + * + * @param {string} Event type. + * @param {EventListener|function} An event listener or handler function. + */ + removeEventListener(type, listener) { + if (type in this.listeners_) { + const index = this.listeners_[type].indexOf(listener); + if (index != -1) { + this.listeners_[type].splice(index, 1); + } + } + } + + /** + * Receive an event and pass it to the listener functions. + * + * @param {bitjs.archive.UnarchiveEvent} e + * @private + */ + handleWorkerEvent_(e) { + if ((e instanceof bitjs.archive.UnarchiveEvent || e.type) && + this.listeners_[e.type] instanceof Array) { + this.listeners_[e.type].forEach(function (listener) { listener(e) }); + if (e.type == bitjs.archive.UnarchiveEvent.Type.FINISH) { + this.worker_.terminate(); + } + } else { + console.log(e); + } + } + + /** + * Starts the unarchive in a separate Web Worker thread and returns immediately. + */ + start() { + const me = this; + const scriptFileName = this.pathToBitJS_ + this.getScriptFileName(); + if (scriptFileName) { + this.worker_ = new Worker(scriptFileName); + + this.worker_.onerror = function(e) { + console.log('Worker error: message = ' + e.message); + throw e; + }; + + this.worker_.onmessage = function(e) { + if (typeof e.data == 'string') { + // Just log any strings the workers pump our way. + console.log(e.data); + } else { + // Assume that it is an UnarchiveEvent. Some browsers preserve the 'type' + // so that instanceof UnarchiveEvent returns true, but others do not. + me.handleWorkerEvent_(e.data); + } + }; + + const ab = this.ab; + this.worker_.postMessage({ + file: ab, + logToConsole: false, + }); + this.ab = null; + } + } + + /** + * Adds more bytes to the unarchiver's Worker thread. + */ + update(ab) { + if (this.worker_) { + this.worker_.postMessage({bytes: ab}); + } + } + + /** + * Terminates the Web Worker for this Unarchiver and returns immediately. + */ + stop() { + if (this.worker_) { + this.worker_.terminate(); + } + } +} + + +/** + * Unzipper + */ +bitjs.archive.Unzipper = class extends bitjs.archive.Unarchiver { + constructor(arrayBuffer, opt_pathToBitJS) { + super(arrayBuffer, opt_pathToBitJS); + } + + getScriptFileName() { return 'archive/unzip.js'; } +} + + +/** + * Unrarrer + */ +bitjs.archive.Unrarrer = class extends bitjs.archive.Unarchiver { + constructor(arrayBuffer, opt_pathToBitJS) { + super(arrayBuffer, opt_pathToBitJS); + } + + getScriptFileName() { return 'archive/unrar.js'; } +} + +/** + * Untarrer + * @extends {bitjs.archive.Unarchiver} + * @constructor + */ +bitjs.archive.Untarrer = class extends bitjs.archive.Unarchiver { + constructor(arrayBuffer, opt_pathToBitJS) { + super(arrayBuffer, opt_pathToBitJS); + } + + getScriptFileName() { return 'archive/untar.js'; }; +} + +/** + * Factory method that creates an unarchiver based on the byte signature found + * in the arrayBuffer. + * @param {ArrayBuffer} ab + * @param {string=} opt_pathToBitJS Path to the unarchiver script files. + * @return {bitjs.archive.Unarchiver} + */ +bitjs.archive.GetUnarchiver = function(ab, opt_pathToBitJS) { + if (ab.byteLength < 10) { + return null; + } + + let unarchiver = null; + const pathToBitJS = opt_pathToBitJS || ''; + const h = new Uint8Array(ab, 0, 10); + + if (h[0] == 0x52 && h[1] == 0x61 && h[2] == 0x72 && h[3] == 0x21) { // Rar! + unarchiver = new bitjs.archive.Unrarrer(ab, pathToBitJS); + } else if (h[0] == 0x50 && h[1] == 0x4B) { // PK (Zip) + unarchiver = new bitjs.archive.Unzipper(ab, pathToBitJS); + } else { // Try with tar + unarchiver = new bitjs.archive.Untarrer(ab, pathToBitJS); + } + return unarchiver; +}; diff --git a/cps/static/js/archive/rarvm.js b/cps/static/js/archive/rarvm.js new file mode 100644 index 00000000..98a1cb8c --- /dev/null +++ b/cps/static/js/archive/rarvm.js @@ -0,0 +1,1009 @@ +/** + * rarvm.js + * + * Licensed under the MIT License + * + * Copyright(c) 2017 Google Inc. + */ + +/** + * CRC Implementation. + */ +const CRCTab = new Array(256).fill(0); + +// Helper functions between signed and unsigned integers. + +/** + * -1 becomes 0xffffffff + */ +function fromSigned32ToUnsigned32(val) { + return (val < 0) ? (val += 0x100000000) : val; +} + +/** + * 0xffffffff becomes -1 + */ +function fromUnsigned32ToSigned32(val) { + return (val >= 0x80000000) ? (val -= 0x100000000) : val; +} + +/** + * -1 becomes 0xff + */ +function fromSigned8ToUnsigned8(val) { + return (val < 0) ? (val += 0x100) : val; +} + +/** + * 0xff becomes -1 + */ +function fromUnsigned8ToSigned8(val) { + return (val >= 0x80) ? (val -= 0x100) : val; +} + +function InitCRC() { + for (let i = 0; i < 256; ++i) { + let c = i; + for (let j = 0; j < 8; ++j) { + // Read http://stackoverflow.com/questions/6798111/bitwise-operations-on-32-bit-unsigned-ints + // for the bitwise operator issue (JS interprets operands as 32-bit signed + // integers and we need to deal with unsigned ones here). + c = ((c & 1) ? ((c >>> 1) ^ 0xEDB88320) : (c >>> 1)) >>> 0; + } + CRCTab[i] = c; + } +} + +/** + * @param {number} startCRC + * @param {Uint8Array} arr + * @return {number} + */ +function CRC(startCRC, arr) { + if (CRCTab[1] == 0) { + InitCRC(); + } + +/* +#if defined(LITTLE_ENDIAN) && defined(PRESENT_INT32) && defined(ALLOW_NOT_ALIGNED_INT) + while (Size>0 && ((long)Data & 7)) + { + StartCRC=CRCTab[(byte)(StartCRC^Data[0])]^(StartCRC>>8); + Size--; + Data++; + } + while (Size>=8) + { + StartCRC^=*(uint32 *)Data; + StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); + StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); + StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); + StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); + StartCRC^=*(uint32 *)(Data+4); + StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); + StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); + StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); + StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); + Data+=8; + Size-=8; + } +#endif +*/ + + for (let i = 0; i < arr.length; ++i) { + const byte = ((startCRC ^ arr[i]) >>> 0) & 0xff; + startCRC = (CRCTab[byte] ^ (startCRC >>> 8)) >>> 0; + } + + return startCRC; +} + +// ============================================================================================== // + + +/** + * RarVM Implementation. + */ +const VM_MEMSIZE = 0x40000; +const VM_MEMMASK = (VM_MEMSIZE - 1); +const VM_GLOBALMEMADDR = 0x3C000; +const VM_GLOBALMEMSIZE = 0x2000; +const VM_FIXEDGLOBALSIZE = 64; +const MAXWINSIZE = 0x400000; +const MAXWINMASK = (MAXWINSIZE - 1); + +/** + */ +const VM_Commands = { + VM_MOV: 0, + VM_CMP: 1, + VM_ADD: 2, + VM_SUB: 3, + VM_JZ: 4, + VM_JNZ: 5, + VM_INC: 6, + VM_DEC: 7, + VM_JMP: 8, + VM_XOR: 9, + VM_AND: 10, + VM_OR: 11, + VM_TEST: 12, + VM_JS: 13, + VM_JNS: 14, + VM_JB: 15, + VM_JBE: 16, + VM_JA: 17, + VM_JAE: 18, + VM_PUSH: 19, + VM_POP: 20, + VM_CALL: 21, + VM_RET: 22, + VM_NOT: 23, + VM_SHL: 24, + VM_SHR: 25, + VM_SAR: 26, + VM_NEG: 27, + VM_PUSHA: 28, + VM_POPA: 29, + VM_PUSHF: 30, + VM_POPF: 31, + VM_MOVZX: 32, + VM_MOVSX: 33, + VM_XCHG: 34, + VM_MUL: 35, + VM_DIV: 36, + VM_ADC: 37, + VM_SBB: 38, + VM_PRINT: 39, + +/* +#ifdef VM_OPTIMIZE + VM_MOVB, VM_MOVD, VM_CMPB, VM_CMPD, + + VM_ADDB, VM_ADDD, VM_SUBB, VM_SUBD, VM_INCB, VM_INCD, VM_DECB, VM_DECD, + VM_NEGB, VM_NEGD, +#endif +*/ + + // TODO: This enum value would be much larger if VM_OPTIMIZE. + VM_STANDARD: 40, +}; + +/** + */ +const VM_StandardFilters = { + VMSF_NONE: 0, + VMSF_E8: 1, + VMSF_E8E9: 2, + VMSF_ITANIUM: 3, + VMSF_RGB: 4, + VMSF_AUDIO: 5, + VMSF_DELTA: 6, + VMSF_UPCASE: 7, +}; + +/** + */ +const VM_Flags = { + VM_FC: 1, + VM_FZ: 2, + VM_FS: 0x80000000, +}; + +/** + */ +const VM_OpType = { + VM_OPREG: 0, + VM_OPINT: 1, + VM_OPREGMEM: 2, + VM_OPNONE: 3, +}; + +/** + * Finds the key that maps to a given value in an object. This function is useful in debugging + * variables that use the above enums. + * @param {Object} obj + * @param {number} val + * @return {string} The key/enum value as a string. + */ +function findKeyForValue(obj, val) { + for (let key in obj) { + if (obj[key] === val) { + return key; + } + } + return null; +} + +function getDebugString(obj, val) { + let s = 'Unknown.'; + if (obj === VM_Commands) { + s = 'VM_Commands.'; + } else if (obj === VM_StandardFilters) { + s = 'VM_StandardFilters.'; + } else if (obj === VM_Flags) { + s = 'VM_OpType.'; + } else if (obj === VM_OpType) { + s = 'VM_OpType.'; + } + + return s + findKeyForValue(obj, val); +} + +/** + */ +class VM_PreparedOperand { + constructor() { + /** @type {VM_OpType} */ + this.Type; + + /** @type {number} */ + this.Data = 0; + + /** @type {number} */ + this.Base = 0; + + // TODO: In C++ this is a uint* + /** @type {Array} */ + this.Addr = null; + }; + + /** @return {string} */ + toString() { + if (this.Type === null) { + return 'Error: Type was null in VM_PreparedOperand'; + } + return '{ ' + + 'Type: ' + getDebugString(VM_OpType, this.Type) + + ', Data: ' + this.Data + + ', Base: ' + this.Base + + ' }'; + } +} + +/** + */ +class VM_PreparedCommand { + constructor() { + /** @type {VM_Commands} */ + this.OpCode; + + /** @type {boolean} */ + this.ByteMode = false; + + /** @type {VM_PreparedOperand} */ + this.Op1 = new VM_PreparedOperand(); + + /** @type {VM_PreparedOperand} */ + this.Op2 = new VM_PreparedOperand(); + } + + /** @return {string} */ + toString(indent) { + if (this.OpCode === null) { + return 'Error: OpCode was null in VM_PreparedCommand'; + } + indent = indent || ''; + return indent + '{\n' + + indent + ' OpCode: ' + getDebugString(VM_Commands, this.OpCode) + ',\n' + + indent + ' ByteMode: ' + this.ByteMode + ',\n' + + indent + ' Op1: ' + this.Op1.toString() + ',\n' + + indent + ' Op2: ' + this.Op2.toString() + ',\n' + + indent + '}'; + } +} + +/** + */ +class VM_PreparedProgram { + constructor() { + /** @type {Array} */ + this.Cmd = []; + + /** @type {Array} */ + this.AltCmd = null; + + /** @type {Uint8Array} */ + this.GlobalData = new Uint8Array(); + + /** @type {Uint8Array} */ + this.StaticData = new Uint8Array(); // static data contained in DB operators + + /** @type {Uint32Array} */ + this.InitR = new Uint32Array(7); + + /** + * A pointer to bytes that have been filtered by a program. + * @type {Uint8Array} + */ + this.FilteredData = null; + } + + /** @return {string} */ + toString() { + let s = '{\n Cmd: [\n'; + for (let i = 0; i < this.Cmd.length; ++i) { + s += this.Cmd[i].toString(' ') + ',\n'; + } + s += '],\n'; + // TODO: Dump GlobalData, StaticData, InitR? + s += ' }\n'; + return s; + } +} + +/** + */ +class UnpackFilter { + constructor() { + /** @type {number} */ + this.BlockStart = 0; + + /** @type {number} */ + this.BlockLength = 0; + + /** @type {number} */ + this.ExecCount = 0; + + /** @type {boolean} */ + this.NextWindow = false; + + // position of parent filter in Filters array used as prototype for filter + // in PrgStack array. Not defined for filters in Filters array. + /** @type {number} */ + this.ParentFilter = null; + + /** @type {VM_PreparedProgram} */ + this.Prg = new VM_PreparedProgram(); + } +} + +const VMCF_OP0 = 0; +const VMCF_OP1 = 1; +const VMCF_OP2 = 2; +const VMCF_OPMASK = 3; +const VMCF_BYTEMODE = 4; +const VMCF_JUMP = 8; +const VMCF_PROC = 16; +const VMCF_USEFLAGS = 32; +const VMCF_CHFLAGS = 64; + +const VM_CmdFlags = [ + /* VM_MOV */ VMCF_OP2 | VMCF_BYTEMODE , + /* VM_CMP */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , + /* VM_ADD */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , + /* VM_SUB */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , + /* VM_JZ */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS , + /* VM_JNZ */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS , + /* VM_INC */ VMCF_OP1 | VMCF_BYTEMODE | VMCF_CHFLAGS , + /* VM_DEC */ VMCF_OP1 | VMCF_BYTEMODE | VMCF_CHFLAGS , + /* VM_JMP */ VMCF_OP1 | VMCF_JUMP , + /* VM_XOR */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , + /* VM_AND */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , + /* VM_OR */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , + /* VM_TEST */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , + /* VM_JS */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS , + /* VM_JNS */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS , + /* VM_JB */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS , + /* VM_JBE */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS , + /* VM_JA */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS , + /* VM_JAE */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS , + /* VM_PUSH */ VMCF_OP1 , + /* VM_POP */ VMCF_OP1 , + /* VM_CALL */ VMCF_OP1 | VMCF_PROC , + /* VM_RET */ VMCF_OP0 | VMCF_PROC , + /* VM_NOT */ VMCF_OP1 | VMCF_BYTEMODE , + /* VM_SHL */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , + /* VM_SHR */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , + /* VM_SAR */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , + /* VM_NEG */ VMCF_OP1 | VMCF_BYTEMODE | VMCF_CHFLAGS , + /* VM_PUSHA */ VMCF_OP0 , + /* VM_POPA */ VMCF_OP0 , + /* VM_PUSHF */ VMCF_OP0 | VMCF_USEFLAGS , + /* VM_POPF */ VMCF_OP0 | VMCF_CHFLAGS , + /* VM_MOVZX */ VMCF_OP2 , + /* VM_MOVSX */ VMCF_OP2 , + /* VM_XCHG */ VMCF_OP2 | VMCF_BYTEMODE , + /* VM_MUL */ VMCF_OP2 | VMCF_BYTEMODE , + /* VM_DIV */ VMCF_OP2 | VMCF_BYTEMODE , + /* VM_ADC */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_USEFLAGS | VMCF_CHFLAGS , + /* VM_SBB */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_USEFLAGS | VMCF_CHFLAGS , + /* VM_PRINT */ VMCF_OP0 , +]; + + +/** + */ +class StandardFilterSignature { + /** + * @param {number} length + * @param {number} crc + * @param {VM_StandardFilters} type + */ + constructor(length, crc, type) { + /** @type {number} */ + this.Length = length; + + /** @type {number} */ + this.CRC = crc; + + /** @type {VM_StandardFilters} */ + this.Type = type; + } +} + +/** + * @type {Array} + */ +const StdList = [ + new StandardFilterSignature(53, 0xad576887, VM_StandardFilters.VMSF_E8), + new StandardFilterSignature(57, 0x3cd7e57e, VM_StandardFilters.VMSF_E8E9), + new StandardFilterSignature(120, 0x3769893f, VM_StandardFilters.VMSF_ITANIUM), + new StandardFilterSignature(29, 0x0e06077d, VM_StandardFilters.VMSF_DELTA), + new StandardFilterSignature(149, 0x1c2c5dc8, VM_StandardFilters.VMSF_RGB), + new StandardFilterSignature(216, 0xbc85e701, VM_StandardFilters.VMSF_AUDIO), + new StandardFilterSignature(40, 0x46b9c560, VM_StandardFilters.VMSF_UPCASE), +]; + +/** + * @constructor + */ +class RarVM { + constructor() { + /** @private {Uint8Array} */ + this.mem_ = null; + + /** @private {Uint32Array} */ + this.R_ = new Uint32Array(8); + + /** @private {number} */ + this.flags_ = 0; + } + + /** + * Initializes the memory of the VM. + */ + init() { + if (!this.mem_) { + this.mem_ = new Uint8Array(VM_MEMSIZE); + } + } + + /** + * @param {Uint8Array} code + * @return {VM_StandardFilters} + */ + isStandardFilter(code) { + const codeCRC = (CRC(0xffffffff, code, code.length) ^ 0xffffffff) >>> 0; + for (let i = 0; i < StdList.length; ++i) { + if (StdList[i].CRC == codeCRC && StdList[i].Length == code.length) + return StdList[i].Type; + } + + return VM_StandardFilters.VMSF_NONE; + } + + /** + * @param {VM_PreparedOperand} op + * @param {boolean} byteMode + * @param {bitjs.io.BitStream} bstream A rtl bit stream. + */ + decodeArg(op, byteMode, bstream) { + const data = bstream.peekBits(16); + if (data & 0x8000) { + op.Type = VM_OpType.VM_OPREG; // Operand is register (R[0]..R[7]) + bstream.readBits(1); // 1 flag bit and... + op.Data = bstream.readBits(3); // ... 3 register number bits + op.Addr = [this.R_[op.Data]] // TODO &R[Op.Data] // Register address + } else { + if ((data & 0xc000) == 0) { + op.Type = VM_OpType.VM_OPINT; // Operand is integer + bstream.readBits(2); // 2 flag bits + if (byteMode) { + op.Data = bstream.readBits(8); // Byte integer. + } else { + op.Data = RarVM.readData(bstream); // 32 bit integer. + } + } else { + // Operand is data addressed by register data, base address or both. + op.Type = VM_OpType.VM_OPREGMEM; + if ((data & 0x2000) == 0) { + bstream.readBits(3); // 3 flag bits + // Base address is zero, just use the address from register. + op.Data = bstream.readBits(3); // (Data>>10)&7 + op.Addr = [this.R_[op.Data]]; // TODO &R[op.Data] + op.Base = 0; + } else { + bstream.readBits(4); // 4 flag bits + if ((data & 0x1000) == 0) { + // Use both register and base address. + op.Data = bstream.readBits(3); + op.Addr = [this.R_[op.Data]]; // TODO &R[op.Data] + } else { + // Use base address only. Access memory by fixed address. + op.Data = 0; + } + op.Base = RarVM.readData(bstream); // Read base address. + } + } + } + } + + /** + * @param {VM_PreparedProgram} prg + */ + execute(prg) { + this.R_.set(prg.InitR); + + const globalSize = Math.min(prg.GlobalData.length, VM_GLOBALMEMSIZE); + if (globalSize) { + this.mem_.set(prg.GlobalData.subarray(0, globalSize), VM_GLOBALMEMADDR); + } + + const staticSize = Math.min(prg.StaticData.length, VM_GLOBALMEMSIZE - globalSize); + if (staticSize) { + this.mem_.set(prg.StaticData.subarray(0, staticSize), VM_GLOBALMEMADDR + globalSize); + } + + this.R_[7] = VM_MEMSIZE; + this.flags_ = 0; + + const preparedCodes = prg.AltCmd ? prg.AltCmd : prg.Cmd; + if (prg.Cmd.length > 0 && !this.executeCode(preparedCodes)) { + // Invalid VM program. Let's replace it with 'return' command. + preparedCode.OpCode = VM_Commands.VM_RET; + } + + const dataView = new DataView(this.mem_.buffer, VM_GLOBALMEMADDR); + let newBlockPos = dataView.getUint32(0x20, true /* little endian */) & VM_MEMMASK; + const newBlockSize = dataView.getUint32(0x1c, true /* little endian */) & VM_MEMMASK; + if (newBlockPos + newBlockSize >= VM_MEMSIZE) { + newBlockPos = newBlockSize = 0; + } + prg.FilteredData = this.mem_.subarray(newBlockPos, newBlockPos + newBlockSize); + + prg.GlobalData = new Uint8Array(0); + + const dataSize = Math.min(dataView.getUint32(0x30), (VM_GLOBALMEMSIZE - VM_FIXEDGLOBALSIZE)); + if (dataSize != 0) { + const len = dataSize + VM_FIXEDGLOBALSIZE; + prg.GlobalData = new Uint8Array(len); + prg.GlobalData.set(mem.subarray(VM_GLOBALMEMADDR, VM_GLOBALMEMADDR + len)); + } + } + + /** + * @param {Array} preparedCodes + * @return {boolean} + */ + executeCode(preparedCodes) { + let codeIndex = 0; + let cmd = preparedCodes[codeIndex]; + // TODO: Why is this an infinite loop instead of just returning + // when a VM_RET is hit? + while (1) { + switch (cmd.OpCode) { + case VM_Commands.VM_RET: + if (this.R_[7] >= VM_MEMSIZE) { + return true; + } + //SET_IP(GET_VALUE(false,(uint *)&Mem[R[7] & VM_MEMMASK])); + this.R_[7] += 4; + continue; + + case VM_Commands.VM_STANDARD: + this.executeStandardFilter(cmd.Op1.Data); + break; + + default: + console.error('RarVM OpCode not supported: ' + getDebugString(VM_Commands, cmd.OpCode)); + break; + } // switch (cmd.OpCode) + codeIndex++; + cmd = preparedCodes[codeIndex]; + } + } + + /** + * @param {number} filterType + */ + executeStandardFilter(filterType) { + switch (filterType) { + case VM_StandardFilters.VMSF_RGB: { + const dataSize = this.R_[4]; + const width = this.R_[0] - 3; + const posR = this.R_[1]; + const Channels = 3; + let srcOffset = 0; + let destOffset = dataSize; + + // byte *SrcData=Mem,*DestData=SrcData+DataSize; + // SET_VALUE(false,&Mem[VM_GLOBALMEMADDR+0x20],DataSize); + const dataView = new DataView(this.mem_.buffer, VM_GLOBALMEMADDR /* offset */); + dataView.setUint32(0x20 /* byte offset */, + dataSize /* value */, + true /* little endian */); + + if (dataSize >= (VM_GLOBALMEMADDR / 2) || posR < 0) { + break; + } + + for (let curChannel = 0; curChannel < Channels; ++curChannel) { + let prevByte=0; + + for (let i = curChannel; i < dataSize; i += Channels) { + let predicted; + const upperPos = i - width; + if (upperPos >= 3) { + const upperByte = this.mem_[destOffset + upperPos]; + const upperLeftByte = this.mem_[destOffset + upperPos - 3]; + predicted = prevByte + upperByte - upperLeftByte; + + const pa = Math.abs(predicted - prevByte); + const pb = Math.abs(predicted - upperByte); + const pc = Math.abs(predicted - upperLeftByte); + if (pa <= pb && pa <= pc) { + predicted = prevByte; + } else if (pb <= pc) { + predicted = upperByte; + } else { + predicted = upperLeftByte; + } + } else { + predicted = prevByte; + } + //DestData[I]=PrevByte=(byte)(Predicted-*(SrcData++)); + prevByte = (predicted - this.mem_[srcOffset++]) & 0xff; + this.mem_[destOffset + i] = prevByte; + } + } + for (let i = posR, border = dataSize - 2; i < border; i += 3) { + const g = this.mem_[destOffset + i + 1]; + this.mem_[destOffset + i] += g; + this.mem_[destOffset + i + 2] += g; + } + + break; + } + + // The C++ version of this standard filter uses an odd mixture of + // signed and unsigned integers, bytes and various casts. Careful! + case VM_StandardFilters.VMSF_AUDIO: { + const dataSize = this.R_[4]; + const channels = this.R_[0]; + let srcOffset = 0; + let destOffset = dataSize; + + //SET_VALUE(false,&Mem[VM_GLOBALMEMADDR+0x20],DataSize); + const dataView = new DataView(this.mem_.buffer, VM_GLOBALMEMADDR); + dataView.setUint32(0x20 /* byte offset */, + dataSize /* value */, + true /* little endian */); + + if (dataSize >= VM_GLOBALMEMADDR / 2) { + break; + } + + for (let curChannel = 0; curChannel < channels; ++curChannel) { + let prevByte = 0; // uint + let prevDelta = 0; // uint + let dif = [0, 0, 0, 0, 0, 0, 0]; + let d1 = 0, d2 = 0, d3; // ints + let k1 = 0, k2 = 0, k3 = 0; // ints + + for (var i = curChannel, byteCount = 0; + i < dataSize; + i += channels, ++byteCount) { + d3 = d2; + d2 = fromUnsigned32ToSigned32(prevDelta - d1); + d1 = fromUnsigned32ToSigned32(prevDelta); + + let predicted = fromSigned32ToUnsigned32(8*prevByte + k1*d1 + k2*d2 + k3*d3); // uint + predicted = (predicted >>> 3) & 0xff; + + let curByte = this.mem_[srcOffset++]; // uint + + // Predicted-=CurByte; + predicted = fromSigned32ToUnsigned32(predicted - curByte); + this.mem_[destOffset + i] = (predicted & 0xff); + + // PrevDelta=(signed char)(Predicted-PrevByte); + // where Predicted, PrevByte, PrevDelta are all unsigned int (32) + // casting this subtraction to a (signed char) is kind of invalid + // but it does the following: + // - do the subtraction + // - get the bottom 8 bits of the result + // - if it was >= 0x80, then the value is negative (subtract 0x100) + // - if the value is now negative, add 0x100000000 to make unsigned + // + // Example: + // predicted = 101 + // prevByte = 4294967158 + // (predicted - prevByte) = -4294967057 + // take lower 8 bits: 1110 1111 = 239 + // since > 127, subtract 256 = -17 + // since < 0, add 0x100000000 = 4294967279 + prevDelta = fromSigned32ToUnsigned32( + fromUnsigned8ToSigned8((predicted - prevByte) & 0xff)); + prevByte = predicted; + + // int D=((signed char)CurByte)<<3; + let curByteAsSignedChar = fromUnsigned8ToSigned8(curByte); // signed char + let d = (curByteAsSignedChar << 3); + + dif[0] += Math.abs(d); + dif[1] += Math.abs(d-d1); + dif[2] += Math.abs(d+d1); + dif[3] += Math.abs(d-d2); + dif[4] += Math.abs(d+d2); + dif[5] += Math.abs(d-d3); + dif[6] += Math.abs(d+d3); + + if ((byteCount & 0x1f) == 0) { + let minDif = dif[0], numMinDif = 0; + dif[0] = 0; + for (let j = 1; j < 7; ++j) { + if (dif[j] < minDif) { + minDif = dif[j]; + numMinDif = j; + } + dif[j] = 0; + } + switch (numMinDif) { + case 1: if (k1>=-16) k1--; break; + case 2: if (k1 < 16) k1++; break; + case 3: if (k2>=-16) k2--; break; + case 4: if (k2 < 16) k2++; break; + case 5: if (k3>=-16) k3--; break; + case 6: if (k3 < 16) k3++; break; + } + } + } + } + + break; + } + + case VM_StandardFilters.VMSF_DELTA: { + const dataSize = this.R_[4]; + const channels = this.R_[0]; + let srcPos = 0; + const border = dataSize * 2; + + //SET_VALUE(false,&Mem[VM_GLOBALMEMADDR+0x20],DataSize); + const dataView = new DataView(this.mem_.buffer, VM_GLOBALMEMADDR); + dataView.setUint32(0x20 /* byte offset */, + dataSize /* value */, + true /* little endian */); + + if (dataSize >= VM_GLOBALMEMADDR / 2) { + break; + } + + // Bytes from same channels are grouped to continual data blocks, + // so we need to place them back to their interleaving positions. + for (let curChannel = 0; curChannel < channels; ++curChannel) { + let prevByte = 0; + for (let destPos = dataSize + curChannel; destPos < border; destPos += channels) { + prevByte = (prevByte - this.mem_[srcPos++]) & 0xff; + this.mem_[destPos] = prevByte; + } + } + + break; + } + + default: + console.error('RarVM Standard Filter not supported: ' + getDebugString(VM_StandardFilters, filterType)); + break; + } + } + + /** + * @param {Uint8Array} code + * @param {VM_PreparedProgram} prg + */ + prepare(code, prg) { + let codeSize = code.length; + + //InitBitInput(); + //memcpy(InBuf,Code,Min(CodeSize,BitInput::MAX_SIZE)); + const bstream = new bitjs.io.BitStream(code.buffer, true /* rtl */); + + // Calculate the single byte XOR checksum to check validity of VM code. + let xorSum = 0; + for (let i = 1; i < codeSize; ++i) { + xorSum ^= code[i]; + } + + bstream.readBits(8); + + prg.Cmd = []; // TODO: Is this right? I don't see it being done in rarvm.cpp. + + // VM code is valid if equal. + if (xorSum == code[0]) { + const filterType = this.isStandardFilter(code); + if (filterType != VM_StandardFilters.VMSF_NONE) { + // VM code is found among standard filters. + const curCmd = new VM_PreparedCommand(); + prg.Cmd.push(curCmd); + + curCmd.OpCode = VM_Commands.VM_STANDARD; + curCmd.Op1.Data = filterType; + // TODO: Addr=&CurCmd->Op1.Data + curCmd.Op1.Addr = [curCmd.Op1.Data]; + curCmd.Op2.Addr = [null]; // &CurCmd->Op2.Data; + curCmd.Op1.Type = VM_OpType.VM_OPNONE; + curCmd.Op2.Type = VM_OpType.VM_OPNONE; + codeSize = 0; + } + + const dataFlag = bstream.readBits(1); + + // Read static data contained in DB operators. This data cannot be + // changed, it is a part of VM code, not a filter parameter. + + if (dataFlag & 0x8000) { + const dataSize = RarVM.readData(bstream) + 1; + // TODO: This accesses the byte pointer of the bstream directly. Is that ok? + for (let i = 0; i < bstream.bytePtr < codeSize && i < dataSize; ++i) { + // Append a byte to the program's static data. + const newStaticData = new Uint8Array(prg.StaticData.length + 1); + newStaticData.set(prg.StaticData); + newStaticData[newStaticData.length - 1] = bstream.readBits(8); + prg.StaticData = newStaticData; + } + } + + while (bstream.bytePtr < codeSize) { + const curCmd = new VM_PreparedCommand(); + prg.Cmd.push(curCmd); // Prg->Cmd.Add(1) + const flag = bstream.peekBits(1); + if (!flag) { // (Data&0x8000)==0 + curCmd.OpCode = bstream.readBits(4); + } else { + curCmd.OpCode = (bstream.readBits(6) - 24); + } + + if (VM_CmdFlags[curCmd.OpCode] & VMCF_BYTEMODE) { + curCmd.ByteMode = (bstream.readBits(1) != 0); + } else { + curCmd.ByteMode = 0; + } + curCmd.Op1.Type = VM_OpType.VM_OPNONE; + curCmd.Op2.Type = VM_OpType.VM_OPNONE; + const opNum = (VM_CmdFlags[curCmd.OpCode] & VMCF_OPMASK); + curCmd.Op1.Addr = null; + curCmd.Op2.Addr = null; + if (opNum > 0) { + this.decodeArg(curCmd.Op1, curCmd.ByteMode, bstream); // reading the first operand + if (opNum == 2) { + this.decodeArg(curCmd.Op2, curCmd.ByteMode, bstream); // reading the second operand + } else { + if (curCmd.Op1.Type == VM_OpType.VM_OPINT && (VM_CmdFlags[curCmd.OpCode] & (VMCF_JUMP|VMCF_PROC))) { + // Calculating jump distance. + let distance = curCmd.Op1.Data; + if (distance >= 256) { + distance -= 256; + } else { + if (distance >= 136) { + distance -= 264; + } else { + if (distance >= 16) { + distance -= 8; + } else { + if (distance >= 8) { + distance -= 16; + } + } + } + distance += prg.Cmd.length; + } + curCmd.Op1.Data = distance; + } + } + } // if (OpNum>0) + } // while ((uint)InAddrOp1.Data + curCmd.Op1.Addr = [curCmd.Op1.Data]; + curCmd.Op2.Addr = [curCmd.Op2.Data]; + curCmd.Op1.Type = VM_OpType.VM_OPNONE; + curCmd.Op2.Type = VM_OpType.VM_OPNONE; + + // If operand 'Addr' field has not been set by DecodeArg calls above, + // let's set it to point to operand 'Data' field. It is necessary for + // VM_OPINT type operands (usual integers) or maybe if something was + // not set properly for other operands. 'Addr' field is required + // for quicker addressing of operand data. + for (let i = 0; i < prg.Cmd.length; ++i) { + const cmd = prg.Cmd[i]; + if (cmd.Op1.Addr == null) { + cmd.Op1.Addr = [cmd.Op1.Data]; + } + if (cmd.Op2.Addr == null) { + cmd.Op2.Addr = [cmd.Op2.Data]; + } + } + + /* + #ifdef VM_OPTIMIZE + if (CodeSize!=0) + Optimize(Prg); + #endif + */ + } + + /** + * @param {Uint8Array} arr The byte array to set a value in. + * @param {number} value The unsigned 32-bit value to set. + * @param {number} offset Offset into arr to start setting the value, defaults to 0. + */ + setLowEndianValue(arr, value, offset) { + const i = offset || 0; + arr[i] = value & 0xff; + arr[i + 1] = (value >>> 8) & 0xff; + arr[i + 2] = (value >>> 16) & 0xff; + arr[i + 3] = (value >>> 24) & 0xff; + } + + /** + * Sets a number of bytes of the VM memory at the given position from a + * source buffer of bytes. + * @param {number} pos The position in the VM memory to start writing to. + * @param {Uint8Array} buffer The source buffer of bytes. + * @param {number} dataSize The number of bytes to set. + */ + setMemory(pos, buffer, dataSize) { + if (pos < VM_MEMSIZE) { + const numBytes = Math.min(dataSize, VM_MEMSIZE - pos); + for (let i = 0; i < numBytes; ++i) { + this.mem_[pos + i] = buffer[i]; + } + } + } + + /** + * Static function that reads in the next set of bits for the VM + * (might return 4, 8, 16 or 32 bits). + * @param {bitjs.io.BitStream} bstream A RTL bit stream. + * @return {number} The value of the bits read. + */ + static readData(bstream) { + // Read in the first 2 bits. + const flags = bstream.readBits(2); + switch (flags) { // Data&0xc000 + // Return the next 4 bits. + case 0: + return bstream.readBits(4); // (Data>>10)&0xf + + case 1: // 0x4000 + // 0x3c00 => 0011 1100 0000 0000 + if (bstream.peekBits(4) == 0) { // (Data&0x3c00)==0 + // Skip the 4 zero bits. + bstream.readBits(4); + // Read in the next 8 and pad with 1s to 32 bits. + return (0xffffff00 | bstream.readBits(8)) >>> 0; // ((Data>>2)&0xff) + } + + // Else, read in the next 8. + return bstream.readBits(8); + + // Read in the next 16. + case 2: // 0x8000 + const val = bstream.getBits(); + bstream.readBits(16); + return val; //bstream.readBits(16); + + // case 3 + default: + return (bstream.readBits(16) << 16) | bstream.readBits(16); + } + } +} + +// ============================================================================================== // diff --git a/cps/static/js/archive/unrar.js b/cps/static/js/archive/unrar.js new file mode 100644 index 00000000..b0c97881 --- /dev/null +++ b/cps/static/js/archive/unrar.js @@ -0,0 +1,1459 @@ +/** + * unrar.js + * + * Licensed under the MIT License + * + * Copyright(c) 2011 Google Inc. + * Copyright(c) 2011 antimatter15 + */ + +// TODO: Rewrite the RarLocalHeader parsing to use a ByteStream instead +// of a BitStream so that it throws properly when not enough bytes are +// present. + +// This file expects to be invoked as a Worker (see onmessage below). +importScripts('../io/bitstream.js'); +importScripts('../io/bytestream.js'); +importScripts('../io/bytebuffer.js'); +importScripts('archive.js'); +importScripts('rarvm.js'); + +const UnarchiveState = { + NOT_STARTED: 0, + UNARCHIVING: 1, + WAITING: 2, + FINISHED: 3, +}; + +// State - consider putting these into a class. +let unarchiveState = UnarchiveState.NOT_STARTED; +let bytestream = null; +let allLocalFiles = null; +let logToConsole = false; + +// Progress variables. +let currentFilename = ''; +let currentFileNumber = 0; +let currentBytesUnarchivedInFile = 0; +let currentBytesUnarchived = 0; +let totalUncompressedBytesInArchive = 0; +let totalFilesInArchive = 0; + +// Helper functions. +const info = function(str) { + postMessage(new bitjs.archive.UnarchiveInfoEvent(str)); +}; +const err = function(str) { + postMessage(new bitjs.archive.UnarchiveErrorEvent(str)); +}; +const postProgress = function() { + postMessage(new bitjs.archive.UnarchiveProgressEvent( + currentFilename, + currentFileNumber, + currentBytesUnarchivedInFile, + currentBytesUnarchived, + totalUncompressedBytesInArchive, + totalFilesInArchive, + parseInt(bytestream.getNumBytesRead(), 10), + )); +}; + +// shows a byte value as its hex representation +const nibble = '0123456789ABCDEF'; +const byteValueToHexString = function(num) { + return nibble[num>>4] + nibble[num&0xF]; +}; +const twoByteValueToHexString = function(num) { + return nibble[(num>>12)&0xF] + nibble[(num>>8)&0xF] + nibble[(num>>4)&0xF] + nibble[num&0xF]; +}; + + +// Volume Types +const MARK_HEAD = 0x72; +const MAIN_HEAD = 0x73; +const FILE_HEAD = 0x74; +const COMM_HEAD = 0x75; +const AV_HEAD = 0x76; +const SUB_HEAD = 0x77; +const PROTECT_HEAD = 0x78; +const SIGN_HEAD = 0x79; +const NEWSUB_HEAD = 0x7a; +const ENDARC_HEAD = 0x7b; + +// ============================================================================================== // + +/** + */ +class RarVolumeHeader { + /** + * @param {bitjs.io.ByteStream} bstream + */ + constructor(bstream) { + let headBytesRead = 0; + + // byte 1,2 + this.crc = bstream.readNumber(2); + + // byte 3 + this.headType = bstream.readNumber(1); + + // Get flags + // bytes 4,5 + this.flags = {}; + this.flags.value = bstream.readNumber(2); + const flagsValue = this.flags.value; + + switch (this.headType) { + case MAIN_HEAD: + this.flags.MHD_VOLUME = !!(flagsValue & 0x01); + this.flags.MHD_COMMENT = !!(flagsValue & 0x02); + this.flags.MHD_LOCK = !!(flagsValue & 0x04); + this.flags.MHD_SOLID = !!(flagsValue & 0x08); + this.flags.MHD_PACK_COMMENT = !!(flagsValue & 0x10); + this.flags.MHD_NEWNUMBERING = this.flags.MHD_PACK_COMMENT; + this.flags.MHD_AV = !!(flagsValue & 0x20); + this.flags.MHD_PROTECT = !!(flagsValue & 0x40); + this.flags.MHD_PASSWORD = !!(flagsValue & 0x80); + this.flags.MHD_FIRSTVOLUME = !!(flagsValue & 0x100); + this.flags.MHD_ENCRYPTVER = !!(flagsValue & 0x200); + //bstream.readBits(6); // unused + break; + case FILE_HEAD: + this.flags.LHD_SPLIT_BEFORE = !!(flagsValue & 0x01); + this.flags.LHD_SPLIT_AFTER = !!(flagsValue & 0x02); + this.flags.LHD_PASSWORD = !!(flagsValue & 0x04); + this.flags.LHD_COMMENT = !!(flagsValue & 0x08); + this.flags.LHD_SOLID = !!(flagsValue & 0x10); + // 3 bits unused + this.flags.LHD_LARGE = !!(flagsValue & 0x100); + this.flags.LHD_UNICODE = !!(flagsValue & 0x200); + this.flags.LHD_SALT = !!(flagsValue & 0x400); + this.flags.LHD_VERSION = !!(flagsValue & 0x800); + this.flags.LHD_EXTTIME = !!(flagsValue & 0x1000); + this.flags.LHD_EXTFLAGS = !!(flagsValue & 0x2000); + // 2 bits unused + //info(' LHD_SPLIT_BEFORE = ' + this.flags.LHD_SPLIT_BEFORE); + break; + default: + break; + } + + // byte 6,7 + this.headSize = bstream.readNumber(2); + headBytesRead += 7; + + switch (this.headType) { + case MAIN_HEAD: + this.highPosAv = bstream.readNumber(2); + this.posAv = bstream.readNumber(4); + headBytesRead += 6; + if (this.flags.MHD_ENCRYPTVER) { + this.encryptVer = bstream.readNumber(1); + headBytesRead += 1; + } + //info('Found MAIN_HEAD with highPosAv=' + this.highPosAv + ', posAv=' + this.posAv); + break; + case FILE_HEAD: + this.packSize = bstream.readNumber(4); + this.unpackedSize = bstream.readNumber(4); + this.hostOS = bstream.readNumber(1); + this.fileCRC = bstream.readNumber(4); + this.fileTime = bstream.readNumber(4); + this.unpVer = bstream.readNumber(1); + this.method = bstream.readNumber(1); + this.nameSize = bstream.readNumber(2); + this.fileAttr = bstream.readNumber(4); + headBytesRead += 25; + + if (this.flags.LHD_LARGE) { + //info('Warning: Reading in LHD_LARGE 64-bit size values'); + this.HighPackSize = bstream.readNumber(4); + this.HighUnpSize = bstream.readNumber(4); + headBytesRead += 8; + } else { + this.HighPackSize = 0; + this.HighUnpSize = 0; + if (this.unpackedSize == 0xffffffff) { + this.HighUnpSize = 0x7fffffff + this.unpackedSize = 0xffffffff; + } + } + this.fullPackSize = 0; + this.fullUnpackSize = 0; + this.fullPackSize |= this.HighPackSize; + this.fullPackSize <<= 32; + this.fullPackSize |= this.packSize; + + // read in filename + + // TODO: Use readString? + this.filename = bstream.readBytes(this.nameSize); + headBytesRead += this.nameSize; + let _s = ''; + for (let _i = 0; _i < this.filename.length; _i++) { + _s += String.fromCharCode(this.filename[_i]); + } + + this.filename = _s; + + if (this.flags.LHD_SALT) { + //info('Warning: Reading in 64-bit salt value'); + this.salt = bstream.readBytes(8); // 8 bytes + headBytesRead += 8; + } + + if (this.flags.LHD_EXTTIME) { + // 16-bit flags + const extTimeFlags = bstream.readNumber(2); + headBytesRead += 2; + + // this is adapted straight out of arcread.cpp, Archive::ReadHeader() + for (let I = 0; I < 4; ++I) { + const rmode = extTimeFlags >> ((3 - I) * 4); + if ((rmode & 8) == 0) { + continue; + } + if (I != 0) { + bstream.readBytes(2); + headBytesRead += 2; + } + const count = (rmode & 3); + for (let J = 0; J < count; ++J) { + bstream.readNumber(1); + headBytesRead += 1; + } + } + } + + if (this.flags.LHD_COMMENT) { + //info('Found a LHD_COMMENT'); + } + + if (headBytesRead < this.headSize) { + bstream.readBytes(this.headSize - headBytesRead); + } + + break; + case ENDARC_HEAD: + break; + default: + if (logToConsole) { + info('Found a header of type 0x' + byteValueToHexString(this.headType)); + } + // skip the rest of the header bytes (for now) + bstream.readBytes(this.headSize - 7); + break; + } + } + + dump() { + info(' crc=' + this.crc); + info(' headType=' + this.headType); + info(' flags=' + twoByteValueToHexString(this.flags.value)); + info(' headSize=' + this.headSize); + if (this.headType == FILE_HEAD) { + info('Found FILE_HEAD with packSize=' + this.packSize + ', unpackedSize= ' + + this.unpackedSize + ', hostOS=' + this.hostOS + ', unpVer=' + this.unpVer + ', method=' + + this.method + ', filename=' + this.filename); + } + } +} + +const BLOCK_LZ = 0; +const BLOCK_PPM = 1; + +const rLDecode = [0,1,2,3,4,5,6,7,8,10,12,14,16,20,24,28,32,40,48,56,64,80,96,112,128,160,192,224]; +const rLBits = [0,0,0,0,0,0,0,0,1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5]; +const rDBitLengthCounts = [4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,14,0,12]; +const rSDDecode = [0,4,8,16,32,64,128,192]; +const rSDBits = [2,2,3, 4, 5, 6, 6, 6]; + +const rDDecode = [0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, + 48, 64, 96, 128, 192, 256, 384, 512, 768, 1024, 1536, 2048, 3072, + 4096, 6144, 8192, 12288, 16384, 24576, 32768, 49152, 65536, 98304, + 131072, 196608, 262144, 327680, 393216, 458752, 524288, 589824, + 655360, 720896, 786432, 851968, 917504, 983040]; + +const rDBits = [0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, + 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, + 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16]; + +const rLOW_DIST_REP_COUNT = 16; + +const rNC = 299; +const rDC = 60; +const rLDC = 17; +const rRC = 28; +const rBC = 20; +const rHUFF_TABLE_SIZE = (rNC+rDC+rRC+rLDC); + +const UnpOldTable = new Array(rHUFF_TABLE_SIZE); + +const BD = { //bitdecode + DecodeLen: new Array(16), + DecodePos: new Array(16), + DecodeNum: new Array(rBC) +}; +const LD = { //litdecode + DecodeLen: new Array(16), + DecodePos: new Array(16), + DecodeNum: new Array(rNC) +}; +const DD = { //distdecode + DecodeLen: new Array(16), + DecodePos: new Array(16), + DecodeNum: new Array(rDC) +}; +const LDD = { //low dist decode + DecodeLen: new Array(16), + DecodePos: new Array(16), + DecodeNum: new Array(rLDC) +}; +const RD = { //rep decode + DecodeLen: new Array(16), + DecodePos: new Array(16), + DecodeNum: new Array(rRC) +}; + +/** + * @type {Array} + */ +const rOldBuffers = []; + +/** + * The current buffer we are unpacking to. + * @type {bitjs.io.ByteBuffer} + */ +let rBuffer; + +/** + * The buffer of the final bytes after filtering (only used in Unpack29). + * @type {bitjs.io.ByteBuffer} + */ +let wBuffer; + + +/** + * In unpack.cpp, UnpPtr keeps track of what bytes have been unpacked + * into the Window buffer and WrPtr keeps track of what bytes have been + * actually written to disk after the unpacking and optional filtering + * has been done. + * + * In our case, rBuffer is the buffer for the unpacked bytes and wBuffer is + * the final output bytes. + */ + + +/** + * Read in Huffman tables for RAR + * @param {bitjs.io.BitStream} bstream + */ +function RarReadTables(bstream) { + const BitLength = new Array(rBC); + const Table = new Array(rHUFF_TABLE_SIZE); + + // before we start anything we need to get byte-aligned + bstream.readBits( (8 - bstream.bitPtr) & 0x7 ); + + if (bstream.readBits(1)) { + info('Error! PPM not implemented yet'); + return; + } + + if (!bstream.readBits(1)) { //discard old table + for (let i = UnpOldTable.length; i--;) { + UnpOldTable[i] = 0; + } + } + + // read in bit lengths + for (let I = 0; I < rBC; ++I) { + const Length = bstream.readBits(4); + if (Length == 15) { + let ZeroCount = bstream.readBits(4); + if (ZeroCount == 0) { + BitLength[I] = 15; + } else { + ZeroCount += 2; + while (ZeroCount-- > 0 && I < rBC) { + BitLength[I++] = 0; + } + --I; + } + } else { + BitLength[I] = Length; + } + } + + // now all 20 bit lengths are obtained, we construct the Huffman Table: + + RarMakeDecodeTables(BitLength, 0, BD, rBC); + + const TableSize = rHUFF_TABLE_SIZE; + for (let i = 0; i < TableSize;) { + const num = RarDecodeNumber(bstream, BD); + if (num < 16) { + Table[i] = (num + UnpOldTable[i]) & 0xf; + i++; + } else if (num < 18) { + let N = (num == 16) ? (bstream.readBits(3) + 3) : (bstream.readBits(7) + 11); + + while (N-- > 0 && i < TableSize) { + Table[i] = Table[i - 1]; + i++; + } + } else { + let N = (num == 18) ? (bstream.readBits(3) + 3) : (bstream.readBits(7) + 11); + + while (N-- > 0 && i < TableSize) { + Table[i++] = 0; + } + } + } + + RarMakeDecodeTables(Table, 0, LD, rNC); + RarMakeDecodeTables(Table, rNC, DD, rDC); + RarMakeDecodeTables(Table, rNC + rDC, LDD, rLDC); + RarMakeDecodeTables(Table, rNC + rDC + rLDC, RD, rRC); + + for (let i = UnpOldTable.length; i--;) { + UnpOldTable[i] = Table[i]; + } + return true; +} + + +function RarDecodeNumber(bstream, dec) { + const DecodeLen = dec.DecodeLen; + const DecodePos = dec.DecodePos; + const DecodeNum = dec.DecodeNum; + const bitField = bstream.getBits() & 0xfffe; + //some sort of rolled out binary search + const bits = ((bitField < DecodeLen[8])? + ((bitField < DecodeLen[4])? + ((bitField < DecodeLen[2])? + ((bitField < DecodeLen[1])?1:2) + :((bitField < DecodeLen[3])?3:4)) + :(bitField < DecodeLen[6])? + ((bitField < DecodeLen[5])?5:6) + :((bitField < DecodeLen[7])?7:8)) + :((bitField < DecodeLen[12])? + ((bitField < DecodeLen[10])? + ((bitField < DecodeLen[9])?9:10) + :((bitField < DecodeLen[11])?11:12)) + :(bitField < DecodeLen[14])? + ((bitField < DecodeLen[13])?13:14) + :15)); + bstream.readBits(bits); + const N = DecodePos[bits] + ((bitField - DecodeLen[bits -1]) >>> (16 - bits)); + + return DecodeNum[N]; +} + + +function RarMakeDecodeTables(BitLength, offset, dec, size) { + const DecodeLen = dec.DecodeLen; + const DecodePos = dec.DecodePos; + const DecodeNum = dec.DecodeNum; + const LenCount = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]; + const TmpPos = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]; + let N = 0; + let M = 0; + + for (let i = DecodeNum.length; i--;) { + DecodeNum[i] = 0; + } + for (let i = 0; i < size; i++) { + LenCount[BitLength[i + offset] & 0xF]++; + } + LenCount[0] = 0; + TmpPos[0] = 0; + DecodePos[0] = 0; + DecodeLen[0] = 0; + + for (let I = 1; I < 16; ++I) { + N = 2 * (N+LenCount[I]); + M = (N << (15-I)); + if (M > 0xFFFF) { + M = 0xFFFF; + } + DecodeLen[I] = M; + DecodePos[I] = DecodePos[I-1] + LenCount[I-1]; + TmpPos[I] = DecodePos[I]; + } + for (let I = 0; I < size; ++I) { + if (BitLength[I + offset] != 0) { + DecodeNum[ TmpPos[ BitLength[offset + I] & 0xF ]++] = I; + } + } + +} + +// TODO: implement +/** + * @param {bitjs.io.BitStream} bstream + * @param {boolean} Solid + */ +function Unpack15(bstream, Solid) { + info('ERROR! RAR 1.5 compression not supported'); +} + +/** + * Unpacks the bit stream into rBuffer using the Unpack20 algorithm. + * @param {bitjs.io.BitStream} bstream + * @param {boolean} Solid + */ +function Unpack20(bstream, Solid) { + const destUnpSize = rBuffer.data.length; + let oldDistPtr = 0; + + if (!Solid) { + RarReadTables20(bstream); + } + while (destUnpSize > rBuffer.ptr) { + let num = RarDecodeNumber(bstream, LD); + if (num < 256) { + rBuffer.insertByte(num); + continue; + } + if (num > 269) { + let Length = rLDecode[num -= 270] + 3; + if ((Bits = rLBits[num]) > 0) { + Length += bstream.readBits(Bits); + } + let DistNumber = RarDecodeNumber(bstream, DD); + let Distance = rDDecode[DistNumber] + 1; + if ((Bits = rDBits[DistNumber]) > 0) { + Distance += bstream.readBits(Bits); + } + if (Distance >= 0x2000) { + Length++; + if (Distance >= 0x40000) { + Length++; + } + } + lastLength = Length; + lastDist = rOldDist[oldDistPtr++ & 3] = Distance; + RarCopyString(Length, Distance); + continue; + } + if (num == 269) { + RarReadTables20(bstream); + RarUpdateProgress(); + continue; + } + if (num == 256) { + lastDist = rOldDist[oldDistPtr++ & 3] = lastDist; + RarCopyString(lastLength, lastDist); + continue; + } + if (num < 261) { + const Distance = rOldDist[(oldDistPtr - (num - 256)) & 3]; + const LengthNumber = RarDecodeNumber(bstream, RD); + let Length = rLDecode[LengthNumber] +2; + if ((Bits = rLBits[LengthNumber]) > 0) { + Length += bstream.readBits(Bits); + } + if (Distance >= 0x101) { + Length++; + if (Distance >= 0x2000) { + Length++ + if (Distance >= 0x40000) { + Length++; + } + } + } + lastLength = Length; + lastDist = rOldDist[oldDistPtr++ & 3] = Distance; + RarCopyString(Length, Distance); + continue; + } + if (num < 270) { + let Distance = rSDDecode[num -= 261] + 1; + if ((Bits = rSDBits[num]) > 0) { + Distance += bstream.readBits(Bits); + } + lastLength = 2; + lastDist = rOldDist[oldDistPtr++ & 3] = Distance; + RarCopyString(2, Distance); + continue; + } + + } + RarUpdateProgress(); +} + +function RarUpdateProgress() { + const change = rBuffer.ptr - currentBytesUnarchivedInFile; + currentBytesUnarchivedInFile = rBuffer.ptr; + currentBytesUnarchived += change; + postProgress(); +} + +const rNC20 = 298; +const rDC20 = 48; +const rRC20 = 28; +const rBC20 = 19; +const rMC20 = 257; + +const UnpOldTable20 = new Array(rMC20 * 4); + +// TODO: This function should return a boolean value, see unpack20.cpp. +function RarReadTables20(bstream) { + const BitLength = new Array(rBC20); + const Table = new Array(rMC20 * 4); + let TableSize; + let N; + let I; + const AudioBlock = bstream.readBits(1); + if (!bstream.readBits(1)) { + for (let i = UnpOldTable20.length; i--;) { + UnpOldTable20[i] = 0; + } + } + TableSize = rNC20 + rDC20 + rRC20; + for (I = 0; I < rBC20; I++) { + BitLength[I] = bstream.readBits(4); + } + RarMakeDecodeTables(BitLength, 0, BD, rBC20); + I = 0; + while (I < TableSize) { + const num = RarDecodeNumber(bstream, BD); + if (num < 16) { + Table[I] = num + UnpOldTable20[I] & 0xf; + I++; + } else if (num == 16) { + N = bstream.readBits(2) + 3; + while (N-- > 0 && I < TableSize) { + Table[I] = Table[I - 1]; + I++; + } + } else { + if (num == 17) { + N = bstream.readBits(3) + 3; + } else { + N = bstream.readBits(7) + 11; + } + while (N-- > 0 && I < TableSize) { + Table[I++] = 0; + } + } + } + RarMakeDecodeTables(Table, 0, LD, rNC20); + RarMakeDecodeTables(Table, rNC20, DD, rDC20); + RarMakeDecodeTables(Table, rNC20 + rDC20, RD, rRC20); + for (let i = UnpOldTable20.length; i--;) { + UnpOldTable20[i] = Table[i]; + } +} + +let lowDistRepCount = 0; +let prevLowDist = 0; + +let rOldDist = [0,0,0,0]; +let lastDist; +let lastLength; + +// ============================================================================================== // + +// Unpack code specific to RarVM +const VM = new RarVM(); + +/** + * Filters code, one entry per filter. + * @type {Array} + */ +let Filters = []; + +/** + * Filters stack, several entrances of same filter are possible. + * @type {Array} + */ +let PrgStack = []; + +/** + * Lengths of preceding blocks, one length per filter. Used to reduce + * size required to write block length if lengths are repeating. + * @type {Array} + */ +let OldFilterLengths = []; + +let LastFilter = 0; + +function InitFilters() { + OldFilterLengths = []; + LastFilter = 0; + Filters = []; + PrgStack = []; +} + + +/** + * @param {number} firstByte The first byte (flags). + * @param {Uint8Array} vmCode An array of bytes. + */ +function RarAddVMCode(firstByte, vmCode) { + VM.init(); + const bstream = new bitjs.io.BitStream(vmCode.buffer, true /* rtl */); + + let filtPos; + if (firstByte & 0x80) { + filtPos = RarVM.readData(bstream); + if (filtPos == 0) { + InitFilters(); + } else { + filtPos--; + } + } else { + filtPos = LastFilter; + } + + if (filtPos > Filters.length || filtPos > OldFilterLengths.length) { + return false; + } + + LastFilter = filtPos; + const newFilter = (filtPos == Filters.length); + + // new filter for PrgStack + const stackFilter = new UnpackFilter(); + let filter = null; + // new filter code, never used before since VM reset + if (newFilter) { + // too many different filters, corrupt archive + if (filtPos > 1024) { + return false; + } + + filter = new UnpackFilter(); + Filters.push(filter); + stackFilter.ParentFilter = (Filters.length - 1); + OldFilterLengths.push(0); // OldFilterLengths.Add(1) + filter.ExecCount = 0; + } else { // filter was used in the past + filter = Filters[filtPos]; + stackFilter.ParentFilter = filtPos; + filter.ExecCount++; + } + + let emptyCount = 0; + for (let i = 0; i < PrgStack.length; ++i) { + PrgStack[i - emptyCount] = PrgStack[i]; + + if (PrgStack[i] == null) { + emptyCount++; + } + if (emptyCount > 0) { + PrgStack[i] = null; + } + } + + if (emptyCount == 0) { + PrgStack.push(null); //PrgStack.Add(1); + emptyCount = 1; + } + + const stackPos = PrgStack.length - emptyCount; + PrgStack[stackPos] = stackFilter; + stackFilter.ExecCount = filter.ExecCount; + + let blockStart = RarVM.readData(bstream); + if (firstByte & 0x40) { + blockStart += 258; + } + stackFilter.BlockStart = (blockStart + rBuffer.ptr) & MAXWINMASK; + + if (firstByte & 0x20) { + stackFilter.BlockLength = RarVM.readData(bstream); + } else { + stackFilter.BlockLength = filtPos < OldFilterLengths.length + ? OldFilterLengths[filtPos] + : 0; + } + stackFilter.NextWindow = (wBuffer.ptr != rBuffer.ptr) && + (((wBuffer.ptr - rBuffer.ptr) & MAXWINMASK) <= blockStart); + + OldFilterLengths[filtPos] = stackFilter.BlockLength; + + for (let i = 0; i < 7; ++i) { + stackFilter.Prg.InitR[i] = 0; + } + stackFilter.Prg.InitR[3] = VM_GLOBALMEMADDR; + stackFilter.Prg.InitR[4] = stackFilter.BlockLength; + stackFilter.Prg.InitR[5] = stackFilter.ExecCount; + + // set registers to optional parameters if any + if (firstByte & 0x10) { + const initMask = bstream.readBits(7); + for (let i = 0; i < 7; ++i) { + if (initMask & (1 << i)) { + stackFilter.Prg.InitR[i] = RarVM.readData(bstream); + } + } + } + + if (newFilter) { + const vmCodeSize = RarVM.readData(bstream); + if (vmCodeSize >= 0x10000 || vmCodeSize == 0) { + return false; + } + const vmCode = new Uint8Array(vmCodeSize); + for (let i = 0; i < vmCodeSize; ++i) { + //if (Inp.Overflow(3)) + // return(false); + vmCode[i] = bstream.readBits(8); + } + VM.prepare(vmCode, filter.Prg); + } + stackFilter.Prg.Cmd = filter.Prg.Cmd; + stackFilter.Prg.AltCmd = filter.Prg.Cmd; + + const staticDataSize = filter.Prg.StaticData.length; + if (staticDataSize > 0 && staticDataSize < VM_GLOBALMEMSIZE) { + // read statically defined data contained in DB commands + for (let i = 0; i < staticDataSize; ++i) { + stackFilter.Prg.StaticData[i] = filter.Prg.StaticData[i]; + } + } + + if (stackFilter.Prg.GlobalData.length < VM_FIXEDGLOBALSIZE) { + stackFilter.Prg.GlobalData = new Uint8Array(VM_FIXEDGLOBALSIZE); + } + + const globalData = stackFilter.Prg.GlobalData; + for (let i = 0; i < 7; ++i) { + VM.setLowEndianValue(globalData, stackFilter.Prg.InitR[i], i * 4); + } + + VM.setLowEndianValue(globalData, stackFilter.BlockLength, 0x1c); + VM.setLowEndianValue(globalData, 0, 0x20); + VM.setLowEndianValue(globalData, stackFilter.ExecCount, 0x2c); + for (let i = 0; i < 16; ++i) { + globalData[0x30 + i] = 0; + } + + // put data block passed as parameter if any + if (firstByte & 8) { + //if (Inp.Overflow(3)) + // return(false); + const dataSize = RarVM.readData(bstream); + if (dataSize > (VM_GLOBALMEMSIZE - VM_FIXEDGLOBALSIZE)) { + return false; + } + + const curSize = stackFilter.Prg.GlobalData.length; + if (curSize < dataSize + VM_FIXEDGLOBALSIZE) { + // Resize global data and update the stackFilter and local variable. + const numBytesToAdd = dataSize + VM_FIXEDGLOBALSIZE - curSize; + const newGlobalData = new Uint8Array(globalData.length + numBytesToAdd); + newGlobalData.set(globalData); + + stackFilter.Prg.GlobalData = newGlobalData; + globalData = newGlobalData; + } + //byte *GlobalData=&StackFilter->Prg.GlobalData[VM_FIXEDGLOBALSIZE]; + for (let i = 0; i < dataSize; ++i) { + //if (Inp.Overflow(3)) + // return(false); + globalData[VM_FIXEDGLOBALSIZE + i] = bstream.readBits(8); + } + } + + return true; +} + + +/** + * @param {!bitjs.io.BitStream} bstream + */ +function RarReadVMCode(bstream) { + const firstByte = bstream.readBits(8); + let length = (firstByte & 7) + 1; + if (length == 7) { + length = bstream.readBits(8) + 7; + } else if (length == 8) { + length = bstream.readBits(16); + } + + // Read all bytes of VM code into an array. + const vmCode = new Uint8Array(length); + for (let i = 0; i < length; i++) { + // Do something here with checking readbuf. + vmCode[i] = bstream.readBits(8); + } + return RarAddVMCode(firstByte, vmCode); +} + +/** + * Unpacks the bit stream into rBuffer using the Unpack29 algorithm. + * @param {bitjs.io.BitStream} bstream + * @param {boolean} Solid + */ +function Unpack29(bstream, Solid) { + // lazy initialize rDDecode and rDBits + + const DDecode = new Array(rDC); + const DBits = new Array(rDC); + + let Dist = 0; + let BitLength = 0; + let Slot = 0; + + for (let I = 0; I < rDBitLengthCounts.length; I++,BitLength++) { + for (let J = 0; J < rDBitLengthCounts[I]; J++,Slot++,Dist+=(1<= 271) { + let Length = rLDecode[num -= 271] + 3; + if ((Bits = rLBits[num]) > 0) { + Length += bstream.readBits(Bits); + } + const DistNumber = RarDecodeNumber(bstream, DD); + let Distance = DDecode[DistNumber] + 1; + if ((Bits = DBits[DistNumber]) > 0) { + if (DistNumber > 9) { + if (Bits > 4) { + Distance += ((bstream.getBits() >>> (20 - Bits)) << 4); + bstream.readBits(Bits - 4); + //todo: check this + } + if (lowDistRepCount > 0) { + lowDistRepCount--; + Distance += prevLowDist; + } else { + const LowDist = RarDecodeNumber(bstream, LDD); + if (LowDist == 16) { + lowDistRepCount = rLOW_DIST_REP_COUNT - 1; + Distance += prevLowDist; + } else { + Distance += LowDist; + prevLowDist = LowDist; + } + } + } else { + Distance += bstream.readBits(Bits); + } + } + if (Distance >= 0x2000) { + Length++; + if (Distance >= 0x40000) { + Length++; + } + } + RarInsertOldDist(Distance); + RarInsertLastMatch(Length, Distance); + RarCopyString(Length, Distance); + continue; + } + if (num == 256) { + if (!RarReadEndOfBlock(bstream)) { + break; + } + continue; + } + if (num == 257) { + if (!RarReadVMCode(bstream)) { + break; + } + continue; + } + if (num == 258) { + if (lastLength != 0) { + RarCopyString(lastLength, lastDist); + } + continue; + } + if (num < 263) { + const DistNum = num - 259; + const Distance = rOldDist[DistNum]; + + for (let I = DistNum; I > 0; I--) { + rOldDist[I] = rOldDist[I-1]; + } + rOldDist[0] = Distance; + + const LengthNumber = RarDecodeNumber(bstream, RD); + let Length = rLDecode[LengthNumber] + 2; + if ((Bits = rLBits[LengthNumber]) > 0) { + Length += bstream.readBits(Bits); + } + RarInsertLastMatch(Length, Distance); + RarCopyString(Length, Distance); + continue; + } + if (num < 272) { + let Distance = rSDDecode[num -= 263] + 1; + if ((Bits = rSDBits[num]) > 0) { + Distance += bstream.readBits(Bits); + } + RarInsertOldDist(Distance); + RarInsertLastMatch(2, Distance); + RarCopyString(2, Distance); + continue; + } + } // while (true) + RarUpdateProgress(); + RarWriteBuf(); +} + +/** + * Does stuff to the current byte buffer (rBuffer) based on + * the filters loaded into the RarVM and writes out to wBuffer. + */ +function RarWriteBuf() { + let writeSize = (rBuffer.ptr & MAXWINMASK); + + for (let i = 0; i < PrgStack.length; ++i) { + const flt = PrgStack[i]; + if (flt == null) { + continue; + } + + if (flt.NextWindow) { + flt.NextWindow = false; + continue; + } + + const blockStart = flt.BlockStart; + const blockLength = flt.BlockLength; + + // WrittenBorder = wBuffer.ptr + if (((blockStart - wBuffer.ptr) & MAXWINMASK) < writeSize) { + if (wBuffer.ptr != blockStart) { + // Copy blockStart bytes from rBuffer into wBuffer. + RarWriteArea(wBuffer.ptr, blockStart); + writeSize = (rBuffer.ptr - wBuffer.ptr) & MAXWINMASK; + } + if (blockLength <= writeSize) { + const blockEnd = (blockStart + blockLength) & MAXWINMASK; + if (blockStart < blockEnd || blockEnd == 0) { + VM.setMemory(0, rBuffer.data.subarray(blockStart, blockStart + blockLength), blockLength); + } else { + const firstPartLength = MAXWINSIZE - blockStart; + VM.setMemory(0, rBuffer.data.subarray(blockStart, blockStart + firstPartLength), firstPartLength); + VM.setMemory(firstPartLength, rBuffer.data, blockEnd); + } + + const parentPrg = Filters[flt.ParentFilter].Prg; + const prg = flt.Prg; + + if (parentPrg.GlobalData.length > VM_FIXEDGLOBALSIZE) { + // Copy global data from previous script execution if any. + prg.GlobalData = new Uint8Array(parentPrg.GlobalData); + } + + RarExecuteCode(prg); + + if (prg.GlobalData.length > VM_FIXEDGLOBALSIZE) { + // Save global data for next script execution. + const globalDataLen = prg.GlobalData.length; + if (parentPrg.GlobalData.length < globalDataLen) { + parentPrg.GlobalData = new Uint8Array(globalDataLen); + } + parentPrg.GlobalData.set( + this.mem_.subarray(VM_FIXEDGLOBALSIZE, VM_FIXEDGLOBALSIZE + globalDataLen), + VM_FIXEDGLOBALSIZE); + } else { + parentPrg.GlobalData = new Uint8Array(0); + } + + let filteredData = prg.FilteredData; + + PrgStack[i] = null; + while (i + 1 < PrgStack.length) { + const nextFilter = PrgStack[i + 1]; + if (nextFilter == null || nextFilter.BlockStart != blockStart || + nextFilter.BlockLength != filteredData.length || nextFilter.NextWindow) { + break; + } + + // Apply several filters to same data block. + + VM.setMemory(0, filteredData, filteredData.length); + + const innerParentPrg = Filters[nextFilter.ParentFilter].Prg; + const nextPrg = nextFilter.Prg; + + const globalDataLen = innerParentPrg.GlobalData.length; + if (globalDataLen > VM_FIXEDGLOBALSIZE) { + // Copy global data from previous script execution if any. + nextPrg.GlobalData = new Uint8Array(globalDataLen); + nextPrg.GlobalData.set(innerParentPrg.GlobalData.subarray(VM_FIXEDGLOBALSIZE, VM_FIXEDGLOBALSIZE + globalDataLen), VM_FIXEDGLOBALSIZE); + } + + RarExecuteCode(nextPrg); + + if (nextPrg.GlobalData.length > VM_GLOBALMEMSIZE) { + // Save global data for next script execution. + const globalDataLen = nextPrg.GlobalData.length; + if (innerParentPrg.GlobalData.length < globalDataLen) { + innerParentPrg.GlobalData = new Uint8Array(globalDataLen); + } + innerParentPrg.GlobalData.set( + this.mem_.subarray(VM_FIXEDGLOBALSIZE, VM_FIXEDGLOBALSIZE + globalDataLen), + VM_FIXEDGLOBALSIZE); + } else { + innerParentPrg.GlobalData = new Uint8Array(0); + } + + filteredData = nextPrg.FilteredData; + i++; + PrgStack[i] = null; + } // while (i + 1 < PrgStack.length) + + for (let j = 0; j < filteredData.length; ++j) { + wBuffer.insertByte(filteredData[j]); + } + writeSize = (rBuffer.ptr - wBuffer.ptr) & MAXWINMASK; + } // if (blockLength <= writeSize) + else { + for (let j = i; j < PrgStack.length; ++j) { + const theFlt = PrgStack[j]; + if (theFlt != null && theFlt.NextWindow) { + theFlt.NextWindow = false; + } + } + return; + } + } // if (((blockStart - wBuffer.ptr) & MAXWINMASK) < writeSize) + } // for (let i = 0; i < PrgStack.length; ++i) + + // Write any remaining bytes from rBuffer to wBuffer; + RarWriteArea(wBuffer.ptr, rBuffer.ptr); + + // Now that the filtered buffer has been written, swap it back to rBuffer. + rBuffer = wBuffer; +} + +/** + * Copy bytes from rBuffer to wBuffer. + * @param {number} startPtr The starting point to copy from rBuffer. + * @param {number} endPtr The ending point to copy from rBuffer. + */ +function RarWriteArea(startPtr, endPtr) { + if (endPtr < startPtr) { + console.error('endPtr < startPtr, endPtr=' + endPtr + ', startPtr=' + startPtr); +// RarWriteData(startPtr, -(int)StartPtr & MAXWINMASK); +// RarWriteData(0, endPtr); + return; + } else if (startPtr < endPtr) { + RarWriteData(startPtr, endPtr - startPtr); + } +} + +/** + * Writes bytes into wBuffer from rBuffer. + * @param {number} offset The starting point to copy bytes from rBuffer. + * @param {number} numBytes The number of bytes to copy. + */ +function RarWriteData(offset, numBytes) { + if (wBuffer.ptr >= rBuffer.data.length) { + return; + } + const leftToWrite = rBuffer.data.length - wBuffer.ptr; + if (numBytes > leftToWrite) { + numBytes = leftToWrite; + } + for (let i = 0; i < numBytes; ++i) { + wBuffer.insertByte(rBuffer.data[offset + i]); + } +} + +/** + * @param {VM_PreparedProgram} prg + */ +function RarExecuteCode(prg) +{ + if (prg.GlobalData.length > 0) { + const writtenFileSize = wBuffer.ptr; + prg.InitR[6] = writtenFileSize; + VM.setLowEndianValue(prg.GlobalData, writtenFileSize, 0x24); + VM.setLowEndianValue(prg.GlobalData, (writtenFileSize >>> 32) >> 0, 0x28); + VM.execute(prg); + } +} + +function RarReadEndOfBlock(bstream) { + RarUpdateProgress(); + + let NewTable = false; + let NewFile = false; + if (bstream.readBits(1)) { + NewTable = true; + } else { + NewFile = true; + NewTable = !!bstream.readBits(1); + } + //tablesRead = !NewTable; + return !(NewFile || NewTable && !RarReadTables(bstream)); +} + +function RarInsertLastMatch(length, distance) { + lastDist = distance; + lastLength = length; +} + +function RarInsertOldDist(distance) { + rOldDist.splice(3,1); + rOldDist.splice(0,0,distance); +} + +/** + * Copies len bytes from distance bytes ago in the buffer to the end of the + * current byte buffer. + * @param {number} length How many bytes to copy. + * @param {number} distance How far back in the buffer from the current write + * pointer to start copying from. + */ +function RarCopyString(len, distance) { + let srcPtr = rBuffer.ptr - distance; + // If we need to go back to previous buffers, then seek back. + if (srcPtr < 0) { + let l = rOldBuffers.length; + while (srcPtr < 0) { + srcPtr = rOldBuffers[--l].data.length + srcPtr; + } + // TODO: lets hope that it never needs to read across buffer boundaries + while (len--) { + rBuffer.insertByte(rOldBuffers[l].data[srcPtr++]); + } + } + if (len > distance) { + while (len--) { + rBuffer.insertByte(rBuffer.data[srcPtr++]); + } + } else { + rBuffer.insertBytes(rBuffer.data.subarray(srcPtr, srcPtr + len)); + } +} + +/** + * @param {RarLocalFile} v + */ +function unpack(v) { + // TODO: implement what happens when unpVer is < 15 + const Ver = v.header.unpVer <= 15 ? 15 : v.header.unpVer; + const Solid = v.header.flags.LHD_SOLID; + const bstream = new bitjs.io.BitStream(v.fileData.buffer, true /* rtl */, v.fileData.byteOffset, v.fileData.byteLength ); + + rBuffer = new bitjs.io.ByteBuffer(v.header.unpackedSize); + + if (logToConsole) { + info('Unpacking ' + v.filename + ' RAR v' + Ver); + } + + switch (Ver) { + case 15: // rar 1.5 compression + Unpack15(bstream, Solid); + break; + case 20: // rar 2.x compression + case 26: // files larger than 2GB + Unpack20(bstream, Solid); + break; + case 29: // rar 3.x compression + case 36: // alternative hash + wBuffer = new bitjs.io.ByteBuffer(rBuffer.data.length); + Unpack29(bstream, Solid); + break; + } // switch(method) + + rOldBuffers.push(rBuffer); + // TODO: clear these old buffers when there's over 4MB of history + return rBuffer.data; +} + +/** + */ +class RarLocalFile { + /** + * @param {bitjs.io.ByteStream} bstream + */ + constructor(bstream) { + this.header = new RarVolumeHeader(bstream); + this.filename = this.header.filename; + + if (this.header.headType != FILE_HEAD && this.header.headType != ENDARC_HEAD) { + this.isValid = false; + info('Error! RAR Volume did not include a FILE_HEAD header '); + } + else { + // read in the compressed data + this.fileData = null; + if (this.header.packSize > 0) { + this.fileData = bstream.readBytes(this.header.packSize); + this.isValid = true; + } + } + } + + unrar() { + if (!this.header.flags.LHD_SPLIT_BEFORE) { + // unstore file + if (this.header.method == 0x30) { + if (logToConsole) { + info('Unstore ' + this.filename); + } + this.isValid = true; + + currentBytesUnarchivedInFile += this.fileData.length; + currentBytesUnarchived += this.fileData.length; + + // Create a new buffer and copy it over. + const len = this.header.packSize; + const newBuffer = new bitjs.io.ByteBuffer(len); + newBuffer.insertBytes(this.fileData); + this.fileData = newBuffer.data; + } else { + this.isValid = true; + this.fileData = unpack(this); + } + } + } +} + +// Reads in the volume and main header. +function unrar_start() { + let bstream = bytestream.tee(); + const header = new RarVolumeHeader(bstream); + if (header.crc == 0x6152 && + header.headType == 0x72 && + header.flags.value == 0x1A21 && + header.headSize == 7) { + if (logToConsole) { + info('Found RAR signature'); + } + + const mhead = new RarVolumeHeader(bstream); + if (mhead.headType != MAIN_HEAD) { + info('Error! RAR did not include a MAIN_HEAD header'); + } else { + bytestream = bstream.tee(); + } + } +} + +function unrar() { + let bstream = bytestream.tee(); + + let localFile = null; + do { + localFile = new RarLocalFile(bstream); + if (logToConsole) { + info('RAR localFile isValid=' + localFile.isValid + ', volume packSize=' + localFile.header.packSize); + localFile.header.dump(); + } + + if (localFile && localFile.isValid && localFile.header.packSize > 0) { + bytestream = bstream.tee(); + totalUncompressedBytesInArchive += localFile.header.unpackedSize; + allLocalFiles.push(localFile); + + currentFilename = localFile.header.filename; + currentBytesUnarchivedInFile = 0; + localFile.unrar(); + + if (localFile.isValid) { + postMessage(new bitjs.archive.UnarchiveExtractEvent(localFile)); + postProgress(); + } + } else if (localFile.header.packSize == 0 && localFile.header.unpackedSize == 0) { + // Skip this file. + localFile.isValid = true; + } + } while (localFile.isValid && bstream.getNumBytesLeft() > 0); + + totalFilesInArchive = allLocalFiles.length; + + postProgress(); + + bytestream = bstream.tee(); +}; + +// event.data.file has the first ArrayBuffer. +// event.data.bytes has all subsequent ArrayBuffers. +onmessage = function(event) { + const bytes = event.data.file || event.data.bytes; + logToConsole = !!event.data.logToConsole; + + // This is the very first time we have been called. Initialize the bytestream. + if (!bytestream) { + bytestream = new bitjs.io.ByteStream(bytes); + + currentFilename = ''; + currentFileNumber = 0; + currentBytesUnarchivedInFile = 0; + currentBytesUnarchived = 0; + totalUncompressedBytesInArchive = 0; + totalFilesInArchive = 0; + allLocalFiles = []; + postMessage(new bitjs.archive.UnarchiveStartEvent()); + } else { + bytestream.push(bytes); + } + + if (unarchiveState === UnarchiveState.NOT_STARTED) { + try { + unrar_start(); + unarchiveState = UnarchiveState.UNARCHIVING; + } catch (e) { + if (typeof e === 'string' && e.startsWith('Error! Overflowed')) { + if (logToConsole) { + console.dir(e); + } + // Overrun the buffer. + unarchiveState = UnarchiveState.WAITING; + postProgress(); + } else { + console.error('Found an error while unrarring'); + console.dir(e); + throw e; + } + } + } + + if (unarchiveState === UnarchiveState.UNARCHIVING || + unarchiveState === UnarchiveState.WAITING) { + try { + unrar(); + unarchiveState = UnarchiveState.FINISHED; + postMessage(new bitjs.archive.UnarchiveFinishEvent()); + } catch (e) { + if (typeof e === 'string' && e.startsWith('Error! Overflowed')) { + if (logToConsole) { + console.dir(e); + } + // Overrun the buffer. + unarchiveState = UnarchiveState.WAITING; + } else { + console.error('Found an error while unrarring'); + console.dir(e); + throw e; + } + } + } +}; diff --git a/cps/static/js/untar.js b/cps/static/js/archive/untar.js similarity index 93% rename from cps/static/js/untar.js rename to cps/static/js/archive/untar.js index ae6a206c..99b3d608 100644 --- a/cps/static/js/untar.js +++ b/cps/static/js/archive/untar.js @@ -1,17 +1,17 @@ /** -* untar.js -* -* Licensed under the MIT License -* -* Copyright(c) 2011 Google Inc. -* -* Reference Documentation: -* -* TAR format: http://www.gnu.org/software/automake/manual/tar/Standard.html -*/ + * untar.js + * + * Licensed under the MIT License + * + * Copyright(c) 2011 Google Inc. + * + * Reference Documentation: + * + * TAR format: http://www.gnu.org/software/automake/manual/tar/Standard.html + */ // This file expects to be invoked as a Worker (see onmessage below). -importScripts('bytestream.js'); +importScripts('../io/bytestream.js'); importScripts('archive.js'); const UnarchiveState = { @@ -42,15 +42,6 @@ const info = function(str) { const err = function(str) { postMessage(new bitjs.archive.UnarchiveErrorEvent(str)); }; - -// Removes all characters from the first zero-byte in the string onwards. -var readCleanString = function(bstr, numBytes) { - var str = bstr.readString(numBytes); - var zIndex = str.indexOf(String.fromCharCode(0)); - return zIndex != -1 ? str.substr(0, zIndex) : str; -}; - - const postProgress = function() { postMessage(new bitjs.archive.UnarchiveProgressEvent( currentFilename, @@ -63,6 +54,12 @@ const postProgress = function() { )); }; +// Removes all characters from the first zero-byte in the string onwards. +const readCleanString = function(bstr, numBytes) { + const str = bstr.readString(numBytes); + const zIndex = str.indexOf(String.fromCharCode(0)); + return zIndex != -1 ? str.substr(0, zIndex) : str; +}; class TarLocalFile { // takes a ByteStream and parses out the local file information @@ -82,7 +79,7 @@ class TarLocalFile { this.typeflag = readCleanString(bstream, 1); this.linkname = readCleanString(bstream, 100); this.maybeMagic = readCleanString(bstream, 6); - + if (this.maybeMagic == "ustar") { this.version = readCleanString(bstream, 2); this.uname = readCleanString(bstream, 32); @@ -94,7 +91,7 @@ class TarLocalFile { if (this.prefix.length) { this.name = this.prefix + this.name; } - bstream.readBytes(12); // 512 - 500in + bstream.readBytes(12); // 512 - 500 } else { bstream.readBytes(255); // 512 - 257 } diff --git a/cps/static/js/archive/unzip.js b/cps/static/js/archive/unzip.js new file mode 100644 index 00000000..6c966a0c --- /dev/null +++ b/cps/static/js/archive/unzip.js @@ -0,0 +1,665 @@ +/** + * unzip.js + * + * Licensed under the MIT License + * + * Copyright(c) 2011 Google Inc. + * Copyright(c) 2011 antimatter15 + * + * Reference Documentation: + * + * ZIP format: http://www.pkware.com/documents/casestudies/APPNOTE.TXT + * DEFLATE format: http://tools.ietf.org/html/rfc1951 + */ + +// This file expects to be invoked as a Worker (see onmessage below). +importScripts('../io/bitstream.js'); +importScripts('../io/bytebuffer.js'); +importScripts('../io/bytestream.js'); +importScripts('archive.js'); + +const UnarchiveState = { + NOT_STARTED: 0, + UNARCHIVING: 1, + WAITING: 2, + FINISHED: 3, +}; + +// State - consider putting these into a class. +let unarchiveState = UnarchiveState.NOT_STARTED; +let bytestream = null; +let allLocalFiles = null; +let logToConsole = false; + +// Progress variables. +let currentFilename = ""; +let currentFileNumber = 0; +let currentBytesUnarchivedInFile = 0; +let currentBytesUnarchived = 0; +let totalUncompressedBytesInArchive = 0; +let totalFilesInArchive = 0; + +// Helper functions. +const info = function(str) { + postMessage(new bitjs.archive.UnarchiveInfoEvent(str)); +}; +const err = function(str) { + postMessage(new bitjs.archive.UnarchiveErrorEvent(str)); +}; +const postProgress = function() { + postMessage(new bitjs.archive.UnarchiveProgressEvent( + currentFilename, + currentFileNumber, + currentBytesUnarchivedInFile, + currentBytesUnarchived, + totalUncompressedBytesInArchive, + totalFilesInArchive, + bytestream.getNumBytesRead(), + )); +}; + +const zLocalFileHeaderSignature = 0x04034b50; +const zArchiveExtraDataSignature = 0x08064b50; +const zCentralFileHeaderSignature = 0x02014b50; +const zDigitalSignatureSignature = 0x05054b50; +const zEndOfCentralDirSignature = 0x06064b50; +const zEndOfCentralDirLocatorSignature = 0x07064b50; + +// mask for getting the Nth bit (zero-based) +const BIT = [ 0x01, 0x02, 0x04, 0x08, + 0x10, 0x20, 0x40, 0x80, + 0x100, 0x200, 0x400, 0x800, + 0x1000, 0x2000, 0x4000, 0x8000]; + + +class ZipLocalFile { + // takes a ByteStream and parses out the local file information + constructor(bstream) { + if (typeof bstream != typeof {} || !bstream.readNumber || typeof bstream.readNumber != typeof function(){}) { + return null; + } + + bstream.readNumber(4); // swallow signature + this.version = bstream.readNumber(2); + this.generalPurpose = bstream.readNumber(2); + this.compressionMethod = bstream.readNumber(2); + this.lastModFileTime = bstream.readNumber(2); + this.lastModFileDate = bstream.readNumber(2); + this.crc32 = bstream.readNumber(4); + this.compressedSize = bstream.readNumber(4); + this.uncompressedSize = bstream.readNumber(4); + this.fileNameLength = bstream.readNumber(2); + this.extraFieldLength = bstream.readNumber(2); + + this.filename = null; + if (this.fileNameLength > 0) { + this.filename = bstream.readString(this.fileNameLength); + } + + this.extraField = null; + if (this.extraFieldLength > 0) { + this.extraField = bstream.readString(this.extraFieldLength); + //info(" extra field=" + this.extraField); + } + + // read in the compressed data + this.fileData = null; + if (this.compressedSize > 0) { + this.fileData = new Uint8Array(bstream.readBytes(this.compressedSize)); + } + + // TODO: deal with data descriptor if present (we currently assume no data descriptor!) + // "This descriptor exists only if bit 3 of the general purpose bit flag is set" + // But how do you figure out how big the file data is if you don't know the compressedSize + // from the header?!? + if ((this.generalPurpose & BIT[3]) != 0) { + this.crc32 = bstream.readNumber(4); + this.compressedSize = bstream.readNumber(4); + this.uncompressedSize = bstream.readNumber(4); + } + + // Now that we have all the bytes for this file, we can print out some information. + if (logToConsole) { + info("Zip Local File Header:"); + info(" version=" + this.version); + info(" general purpose=" + this.generalPurpose); + info(" compression method=" + this.compressionMethod); + info(" last mod file time=" + this.lastModFileTime); + info(" last mod file date=" + this.lastModFileDate); + info(" crc32=" + this.crc32); + info(" compressed size=" + this.compressedSize); + info(" uncompressed size=" + this.uncompressedSize); + info(" file name length=" + this.fileNameLength); + info(" extra field length=" + this.extraFieldLength); + info(" filename = '" + this.filename + "'"); + } + } + + // determine what kind of compressed data we have and decompress + unzip() { + // Zip Version 1.0, no compression (store only) + if (this.compressionMethod == 0 ) { + if (logToConsole) { + info("ZIP v"+this.version+", store only: " + this.filename + " (" + this.compressedSize + " bytes)"); + } + currentBytesUnarchivedInFile = this.compressedSize; + currentBytesUnarchived += this.compressedSize; + } + // version == 20, compression method == 8 (DEFLATE) + else if (this.compressionMethod == 8) { + if (logToConsole) { + info("ZIP v2.0, DEFLATE: " + this.filename + " (" + this.compressedSize + " bytes)"); + } + this.fileData = inflate(this.fileData, this.uncompressedSize); + } + else { + err("UNSUPPORTED VERSION/FORMAT: ZIP v" + this.version + ", compression method=" + this.compressionMethod + ": " + this.filename + " (" + this.compressedSize + " bytes)"); + this.fileData = null; + } + } +} + +// returns a table of Huffman codes +// each entry's index is its code and its value is a JavaScript object +// containing {length: 6, symbol: X} +function getHuffmanCodes(bitLengths) { + // ensure bitLengths is an array containing at least one element + if (typeof bitLengths != typeof [] || bitLengths.length < 1) { + err("Error! getHuffmanCodes() called with an invalid array"); + return null; + } + + // Reference: http://tools.ietf.org/html/rfc1951#page-8 + const numLengths = bitLengths.length; + const bl_count = []; + let MAX_BITS = 1; + + // Step 1: count up how many codes of each length we have + for (let i = 0; i < numLengths; ++i) { + const length = bitLengths[i]; + // test to ensure each bit length is a positive, non-zero number + if (typeof length != typeof 1 || length < 0) { + err("bitLengths contained an invalid number in getHuffmanCodes(): " + length + " of type " + (typeof length)); + return null; + } + // increment the appropriate bitlength count + if (bl_count[length] == undefined) bl_count[length] = 0; + // a length of zero means this symbol is not participating in the huffman coding + if (length > 0) bl_count[length]++; + if (length > MAX_BITS) MAX_BITS = length; + } + + // Step 2: Find the numerical value of the smallest code for each code length + const next_code = []; + let code = 0; + for (let bits = 1; bits <= MAX_BITS; ++bits) { + const length = bits-1; + // ensure undefined lengths are zero + if (bl_count[length] == undefined) bl_count[length] = 0; + code = (code + bl_count[bits-1]) << 1; + next_code[bits] = code; + } + + // Step 3: Assign numerical values to all codes + const table = {}; + let tableLength = 0; + for (let n = 0; n < numLengths; ++n) { + const len = bitLengths[n]; + if (len != 0) { + table[next_code[len]] = { length: len, symbol: n }; //, bitstring: binaryValueToString(next_code[len],len) }; + tableLength++; + next_code[len]++; + } + } + table.maxLength = tableLength; + + return table; +} + +/* + The Huffman codes for the two alphabets are fixed, and are not + represented explicitly in the data. The Huffman code lengths + for the literal/length alphabet are: + + Lit Value Bits Codes + --------- ---- ----- + 0 - 143 8 00110000 through + 10111111 + 144 - 255 9 110010000 through + 111111111 + 256 - 279 7 0000000 through + 0010111 + 280 - 287 8 11000000 through + 11000111 +*/ +// fixed Huffman codes go from 7-9 bits, so we need an array whose index can hold up to 9 bits +let fixedHCtoLiteral = null; +let fixedHCtoDistance = null; +function getFixedLiteralTable() { + // create once + if (!fixedHCtoLiteral) { + const bitlengths = new Array(288); + for (let i = 0; i <= 143; ++i) bitlengths[i] = 8; + for (let i = 144; i <= 255; ++i) bitlengths[i] = 9; + for (let i = 256; i <= 279; ++i) bitlengths[i] = 7; + for (let i = 280; i <= 287; ++i) bitlengths[i] = 8; + + // get huffman code table + fixedHCtoLiteral = getHuffmanCodes(bitlengths); + } + return fixedHCtoLiteral; +} + +function getFixedDistanceTable() { + // create once + if (!fixedHCtoDistance) { + const bitlengths = new Array(32); + for (let i = 0; i < 32; ++i) { bitlengths[i] = 5; } + + // get huffman code table + fixedHCtoDistance = getHuffmanCodes(bitlengths); + } + return fixedHCtoDistance; +} + +// extract one bit at a time until we find a matching Huffman Code +// then return that symbol +function decodeSymbol(bstream, hcTable) { + let code = 0; + let len = 0; + let match = false; + + // loop until we match + for (;;) { + // read in next bit + const bit = bstream.readBits(1); + code = (code<<1) | bit; + ++len; + + // check against Huffman Code table and break if found + if (hcTable.hasOwnProperty(code) && hcTable[code].length == len) { + break; + } + if (len > hcTable.maxLength) { + err("Bit stream out of sync, didn't find a Huffman Code, length was " + len + + " and table only max code length of " + hcTable.maxLength); + break; + } + } + return hcTable[code].symbol; +} + + +const CodeLengthCodeOrder = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]; + +/* + Extra Extra Extra +Code Bits Length(s) Code Bits Lengths Code Bits Length(s) +---- ---- ------ ---- ---- ------- ---- ---- ------- + 257 0 3 267 1 15,16 277 4 67-82 + 258 0 4 268 1 17,18 278 4 83-98 + 259 0 5 269 2 19-22 279 4 99-114 + 260 0 6 270 2 23-26 280 4 115-130 + 261 0 7 271 2 27-30 281 5 131-162 + 262 0 8 272 2 31-34 282 5 163-194 + 263 0 9 273 3 35-42 283 5 195-226 + 264 0 10 274 3 43-50 284 5 227-257 + 265 1 11,12 275 3 51-58 285 0 258 + 266 1 13,14 276 3 59-66 +*/ +const LengthLookupTable = [ + [0,3], [0,4], [0,5], [0,6], + [0,7], [0,8], [0,9], [0,10], + [1,11], [1,13], [1,15], [1,17], + [2,19], [2,23], [2,27], [2,31], + [3,35], [3,43], [3,51], [3,59], + [4,67], [4,83], [4,99], [4,115], + [5,131], [5,163], [5,195], [5,227], + [0,258] +]; + +/* + Extra Extra Extra + Code Bits Dist Code Bits Dist Code Bits Distance + ---- ---- ---- ---- ---- ------ ---- ---- -------- + 0 0 1 10 4 33-48 20 9 1025-1536 + 1 0 2 11 4 49-64 21 9 1537-2048 + 2 0 3 12 5 65-96 22 10 2049-3072 + 3 0 4 13 5 97-128 23 10 3073-4096 + 4 1 5,6 14 6 129-192 24 11 4097-6144 + 5 1 7,8 15 6 193-256 25 11 6145-8192 + 6 2 9-12 16 7 257-384 26 12 8193-12288 + 7 2 13-16 17 7 385-512 27 12 12289-16384 + 8 3 17-24 18 8 513-768 28 13 16385-24576 + 9 3 25-32 19 8 769-1024 29 13 24577-32768 +*/ +const DistLookupTable = [ + [0,1], [0,2], [0,3], [0,4], + [1,5], [1,7], + [2,9], [2,13], + [3,17], [3,25], + [4,33], [4,49], + [5,65], [5,97], + [6,129], [6,193], + [7,257], [7,385], + [8,513], [8,769], + [9,1025], [9,1537], + [10,2049], [10,3073], + [11,4097], [11,6145], + [12,8193], [12,12289], + [13,16385], [13,24577] +]; + +function inflateBlockData(bstream, hcLiteralTable, hcDistanceTable, buffer) { + /* + loop (until end of block code recognized) + decode literal/length value from input stream + if value < 256 + copy value (literal byte) to output stream + otherwise + if value = end of block (256) + break from loop + otherwise (value = 257..285) + decode distance from input stream + + move backwards distance bytes in the output + stream, and copy length bytes from this + position to the output stream. + */ + let numSymbols = 0; + let blockSize = 0; + for (;;) { + const symbol = decodeSymbol(bstream, hcLiteralTable); + ++numSymbols; + if (symbol < 256) { + // copy literal byte to output + buffer.insertByte(symbol); + blockSize++; + } else { + // end of block reached + if (symbol == 256) { + break; + } else { + const lengthLookup = LengthLookupTable[symbol - 257]; + let length = lengthLookup[1] + bstream.readBits(lengthLookup[0]); + const distLookup = DistLookupTable[decodeSymbol(bstream, hcDistanceTable)]; + let distance = distLookup[1] + bstream.readBits(distLookup[0]); + + // now apply length and distance appropriately and copy to output + + // TODO: check that backward distance < data.length? + + // http://tools.ietf.org/html/rfc1951#page-11 + // "Note also that the referenced string may overlap the current + // position; for example, if the last 2 bytes decoded have values + // X and Y, a string reference with + // adds X,Y,X,Y,X to the output stream." + // + // loop for each character + let ch = buffer.ptr - distance; + blockSize += length; + if(length > distance) { + const data = buffer.data; + while (length--) { + buffer.insertByte(data[ch++]); + } + } else { + buffer.insertBytes(buffer.data.subarray(ch, ch + length)) + } + } // length-distance pair + } // length-distance pair or end-of-block + } // loop until we reach end of block + return blockSize; +} + +// {Uint8Array} compressedData A Uint8Array of the compressed file data. +// compression method 8 +// deflate: http://tools.ietf.org/html/rfc1951 +function inflate(compressedData, numDecompressedBytes) { + // Bit stream representing the compressed data. + const bstream = new bitjs.io.BitStream(compressedData.buffer, + false /* rtl */, + compressedData.byteOffset, + compressedData.byteLength); + const buffer = new bitjs.io.ByteBuffer(numDecompressedBytes); + let blockSize = 0; + + // block format: http://tools.ietf.org/html/rfc1951#page-9 + let bFinal = 0; + do { + bFinal = bstream.readBits(1); + let bType = bstream.readBits(2); + blockSize = 0; + // no compression + if (bType == 0) { + // skip remaining bits in this byte + while (bstream.bitPtr != 0) bstream.readBits(1); + const len = bstream.readBits(16); + const nlen = bstream.readBits(16); + // TODO: check if nlen is the ones-complement of len? + if (len > 0) buffer.insertBytes(bstream.readBytes(len)); + blockSize = len; + } + // fixed Huffman codes + else if (bType == 1) { + blockSize = inflateBlockData(bstream, getFixedLiteralTable(), getFixedDistanceTable(), buffer); + } + // dynamic Huffman codes + else if (bType == 2) { + const numLiteralLengthCodes = bstream.readBits(5) + 257; + const numDistanceCodes = bstream.readBits(5) + 1; + const numCodeLengthCodes = bstream.readBits(4) + 4; + + // populate the array of code length codes (first de-compaction) + const codeLengthsCodeLengths = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]; + for (let i = 0; i < numCodeLengthCodes; ++i) { + codeLengthsCodeLengths[ CodeLengthCodeOrder[i] ] = bstream.readBits(3); + } + + // get the Huffman Codes for the code lengths + const codeLengthsCodes = getHuffmanCodes(codeLengthsCodeLengths); + + // now follow this mapping + /* + 0 - 15: Represent code lengths of 0 - 15 + 16: Copy the previous code length 3 - 6 times. + The next 2 bits indicate repeat length + (0 = 3, ... , 3 = 6) + Example: Codes 8, 16 (+2 bits 11), + 16 (+2 bits 10) will expand to + 12 code lengths of 8 (1 + 6 + 5) + 17: Repeat a code length of 0 for 3 - 10 times. + (3 bits of length) + 18: Repeat a code length of 0 for 11 - 138 times + (7 bits of length) + */ + // to generate the true code lengths of the Huffman Codes for the literal + // and distance tables together + const literalCodeLengths = []; + let prevCodeLength = 0; + while (literalCodeLengths.length < numLiteralLengthCodes + numDistanceCodes) { + const symbol = decodeSymbol(bstream, codeLengthsCodes); + if (symbol <= 15) { + literalCodeLengths.push(symbol); + prevCodeLength = symbol; + } else if (symbol == 16) { + let repeat = bstream.readBits(2) + 3; + while (repeat--) { + literalCodeLengths.push(prevCodeLength); + } + } else if (symbol == 17) { + let repeat = bstream.readBits(3) + 3; + while (repeat--) { + literalCodeLengths.push(0); + } + } else if (symbol == 18) { + let repeat = bstream.readBits(7) + 11; + while (repeat--) { + literalCodeLengths.push(0); + } + } + } + + // now split the distance code lengths out of the literal code array + const distanceCodeLengths = literalCodeLengths.splice(numLiteralLengthCodes, numDistanceCodes); + + // now generate the true Huffman Code tables using these code lengths + const hcLiteralTable = getHuffmanCodes(literalCodeLengths); + const hcDistanceTable = getHuffmanCodes(distanceCodeLengths); + blockSize = inflateBlockData(bstream, hcLiteralTable, hcDistanceTable, buffer); + } else { // error + err("Error! Encountered deflate block of type 3"); + return null; + } + + // update progress + currentBytesUnarchivedInFile += blockSize; + currentBytesUnarchived += blockSize; + postProgress(); + } while (bFinal != 1); + // we are done reading blocks if the bFinal bit was set for this block + + // return the buffer data bytes + return buffer.data; +} + +function unzip() { + let bstream = bytestream.tee(); + + // loop until we don't see any more local files + while (bstream.peekNumber(4) == zLocalFileHeaderSignature) { + const oneLocalFile = new ZipLocalFile(bstream); + // this should strip out directories/folders + if (oneLocalFile && oneLocalFile.uncompressedSize > 0 && oneLocalFile.fileData) { + // If we make it to this point and haven't thrown an error, we have successfully + // read in the data for a local file, so we can update the actual bytestream. + bytestream = bstream.tee(); + + allLocalFiles.push(oneLocalFile); + totalUncompressedBytesInArchive += oneLocalFile.uncompressedSize; + + // update progress + currentFilename = oneLocalFile.filename; + currentFileNumber = allLocalFiles.length - 1; + currentBytesUnarchivedInFile = 0; + + // Actually do the unzipping. + oneLocalFile.unzip(); + + if (oneLocalFile.fileData != null) { + postMessage(new bitjs.archive.UnarchiveExtractEvent(oneLocalFile)); + postProgress(); + } + } + } + totalFilesInArchive = allLocalFiles.length; + + // archive extra data record + if (bstream.peekNumber(4) == zArchiveExtraDataSignature) { + if (logToConsole) { + info(" Found an Archive Extra Data Signature"); + } + + // skipping this record for now + bstream.readNumber(4); + const archiveExtraFieldLength = bstream.readNumber(4); + bstream.readString(archiveExtraFieldLength); + } + + // central directory structure + // TODO: handle the rest of the structures (Zip64 stuff) + if (bytestream.peekNumber(4) == zCentralFileHeaderSignature) { + if (logToConsole) { + info(" Found a Central File Header"); + } + + // read all file headers + while (bstream.peekNumber(4) == zCentralFileHeaderSignature) { + bstream.readNumber(4); // signature + bstream.readNumber(2); // version made by + bstream.readNumber(2); // version needed to extract + bstream.readNumber(2); // general purpose bit flag + bstream.readNumber(2); // compression method + bstream.readNumber(2); // last mod file time + bstream.readNumber(2); // last mod file date + bstream.readNumber(4); // crc32 + bstream.readNumber(4); // compressed size + bstream.readNumber(4); // uncompressed size + const fileNameLength = bstream.readNumber(2); // file name length + const extraFieldLength = bstream.readNumber(2); // extra field length + const fileCommentLength = bstream.readNumber(2); // file comment length + bstream.readNumber(2); // disk number start + bstream.readNumber(2); // internal file attributes + bstream.readNumber(4); // external file attributes + bstream.readNumber(4); // relative offset of local header + + bstream.readString(fileNameLength); // file name + bstream.readString(extraFieldLength); // extra field + bstream.readString(fileCommentLength); // file comment + } + } + + // digital signature + if (bstream.peekNumber(4) == zDigitalSignatureSignature) { + if (logToConsole) { + info(" Found a Digital Signature"); + } + + bstream.readNumber(4); + const sizeOfSignature = bstream.readNumber(2); + bstream.readString(sizeOfSignature); // digital signature data + } + + postProgress(); + + bytestream = bstream.tee(); +} + +// event.data.file has the first ArrayBuffer. +// event.data.bytes has all subsequent ArrayBuffers. +onmessage = function(event) { + const bytes = event.data.file || event.data.bytes; + logToConsole = !!event.data.logToConsole; + + // This is the very first time we have been called. Initialize the bytestream. + if (!bytestream) { + bytestream = new bitjs.io.ByteStream(bytes); + } else { + bytestream.push(bytes); + } + + if (unarchiveState === UnarchiveState.NOT_STARTED) { + currentFilename = ""; + currentFileNumber = 0; + currentBytesUnarchivedInFile = 0; + currentBytesUnarchived = 0; + totalUncompressedBytesInArchive = 0; + totalFilesInArchive = 0; + currentBytesUnarchived = 0; + allLocalFiles = []; + + postMessage(new bitjs.archive.UnarchiveStartEvent()); + + unarchiveState = UnarchiveState.UNARCHIVING; + + postProgress(); + } + + if (unarchiveState === UnarchiveState.UNARCHIVING || + unarchiveState === UnarchiveState.WAITING) { + try { + unzip(); + unarchiveState = UnarchiveState.FINISHED; + postMessage(new bitjs.archive.UnarchiveFinishEvent()); + } catch (e) { + if (typeof e === 'string' && e.startsWith('Error! Overflowed')) { + // Overrun the buffer. + unarchiveState = UnarchiveState.WAITING; + } else { + console.error('Found an error while unzipping'); + console.dir(e); + throw e; + } + } + } +}; diff --git a/cps/static/js/io/bitstream.js b/cps/static/js/io/bitstream.js new file mode 100644 index 00000000..3ae1c3ed --- /dev/null +++ b/cps/static/js/io/bitstream.js @@ -0,0 +1,288 @@ +/* + * bitstream.js + * + * Provides readers for bitstreams. + * + * Licensed under the MIT License + * + * Copyright(c) 2011 Google Inc. + * Copyright(c) 2011 antimatter15 + */ + +var bitjs = bitjs || {}; +bitjs.io = bitjs.io || {}; + + +/** + * This object allows you to peek and consume bits and bytes out of a stream. + * Note that this stream is optimized, and thus, will *NOT* throw an error if + * the end of the stream is reached. Only use this in scenarios where you + * already have all the bits you need. + */ +bitjs.io.BitStream = class { + /** + * @param {ArrayBuffer} ab An ArrayBuffer object or a Uint8Array. + * @param {boolean} rtl Whether the stream reads bits from the byte starting + * from bit 7 to 0 (true) or bit 0 to 7 (false). + * @param {Number} opt_offset The offset into the ArrayBuffer + * @param {Number} opt_length The length of this BitStream + */ + constructor(ab, rtl, opt_offset, opt_length) { + if (!(ab instanceof ArrayBuffer)) { + throw 'Error! BitArray constructed with an invalid ArrayBuffer object'; + } + + const offset = opt_offset || 0; + const length = opt_length || ab.byteLength; + + /** + * The bytes in the stream. + * @type {Uint8Array} + * @private + */ + this.bytes = new Uint8Array(ab, offset, length); + + /** + * The byte in the stream that we are currently on. + * @type {Number} + * @private + */ + this.bytePtr = 0; + + /** + * The bit in the current byte that we will read next (can have values 0 through 7). + * @type {Number} + * @private + */ + this.bitPtr = 0; // tracks which bit we are on (can have values 0 through 7) + + /** + * An ever-increasing number. + * @type {Number} + * @private + */ + this.bitsRead_ = 0; + + this.peekBits = rtl ? this.peekBits_rtl : this.peekBits_ltr; + } + + /** + * Returns how many bites have been read in the stream since the beginning of time. + */ + getNumBitsRead() { + return this.bitsRead_; + } + + /** + * Returns how many bits are currently in the stream left to be read. + */ + getNumBitsLeft() { + const bitsLeftInByte = 8 - this.bitPtr; + return (this.bytes.byteLength - this.bytePtr - 1) * 8 + bitsLeftInByte; + } + + /** + * byte0 byte1 byte2 byte3 + * 7......0 | 7......0 | 7......0 | 7......0 + * + * The bit pointer starts at bit0 of byte0 and moves left until it reaches + * bit7 of byte0, then jumps to bit0 of byte1, etc. + * @param {number} n The number of bits to peek, must be a positive integer. + * @param {boolean=} movePointers Whether to move the pointer, defaults false. + * @return {number} The peeked bits, as an unsigned number. + */ + peekBits_ltr(n, opt_movePointers) { + const NUM = parseInt(n, 10); + let num = NUM; + if (n !== num || num <= 0) { + return 0; + } + + const BITMASK = bitjs.io.BitStream.BITMASK; + const movePointers = opt_movePointers || false; + let bytes = this.bytes; + let bytePtr = this.bytePtr; + let bitPtr = this.bitPtr; + let result = 0; + let bitsIn = 0; + + // keep going until we have no more bits left to peek at + while (num > 0) { + // We overflowed the stream, so just return what we got. + if (bytePtr >= bytes.length) { + break; + } + + const numBitsLeftInThisByte = (8 - bitPtr); + if (num >= numBitsLeftInThisByte) { + const mask = (BITMASK[numBitsLeftInThisByte] << bitPtr); + result |= (((bytes[bytePtr] & mask) >> bitPtr) << bitsIn); + + bytePtr++; + bitPtr = 0; + bitsIn += numBitsLeftInThisByte; + num -= numBitsLeftInThisByte; + } else { + const mask = (BITMASK[num] << bitPtr); + result |= (((bytes[bytePtr] & mask) >> bitPtr) << bitsIn); + + bitPtr += num; + break; + } + } + + if (movePointers) { + this.bitPtr = bitPtr; + this.bytePtr = bytePtr; + this.bitsRead_ += NUM; + } + + return result; + } + + /** + * byte0 byte1 byte2 byte3 + * 7......0 | 7......0 | 7......0 | 7......0 + * + * The bit pointer starts at bit7 of byte0 and moves right until it reaches + * bit0 of byte0, then goes to bit7 of byte1, etc. + * @param {number} n The number of bits to peek. Must be a positive integer. + * @param {boolean=} movePointers Whether to move the pointer, defaults false. + * @return {number} The peeked bits, as an unsigned number. + */ + peekBits_rtl(n, opt_movePointers) { + const NUM = parseInt(n, 10); + let num = NUM; + if (n !== num || num <= 0) { + return 0; + } + + const BITMASK = bitjs.io.BitStream.BITMASK; + const movePointers = opt_movePointers || false; + let bytes = this.bytes; + let bytePtr = this.bytePtr; + let bitPtr = this.bitPtr; + let result = 0; + + // keep going until we have no more bits left to peek at + while (num > 0) { + // We overflowed the stream, so just return the bits we got. + if (bytePtr >= bytes.length) { + break; + } + + const numBitsLeftInThisByte = (8 - bitPtr); + if (num >= numBitsLeftInThisByte) { + result <<= numBitsLeftInThisByte; + result |= (BITMASK[numBitsLeftInThisByte] & bytes[bytePtr]); + bytePtr++; + bitPtr = 0; + num -= numBitsLeftInThisByte; + } else { + result <<= num; + const numBits = 8 - num - bitPtr; + result |= ((bytes[bytePtr] & (BITMASK[num] << numBits)) >> numBits); + + bitPtr += num; + break; + } + } + + if (movePointers) { + this.bitPtr = bitPtr; + this.bytePtr = bytePtr; + this.bitsRead_ += NUM; + } + + return result; + } + + /** + * Peek at 16 bits from current position in the buffer. + * Bit at (bytePtr,bitPtr) has the highest position in returning data. + * Taken from getbits.hpp in unrar. + * TODO: Move this out of BitStream and into unrar. + */ + getBits() { + return (((((this.bytes[this.bytePtr] & 0xff) << 16) + + ((this.bytes[this.bytePtr+1] & 0xff) << 8) + + ((this.bytes[this.bytePtr+2] & 0xff))) >>> (8-this.bitPtr)) & 0xffff); + } + + /** + * Reads n bits out of the stream, consuming them (moving the bit pointer). + * @param {number} n The number of bits to read. Must be a positive integer. + * @return {number} The read bits, as an unsigned number. + */ + readBits(n) { + return this.peekBits(n, true); + } + + /** + * This returns n bytes as a sub-array, advancing the pointer if movePointers + * is true. Only use this for uncompressed blocks as this throws away remaining + * bits in the current byte. + * @param {number} n The number of bytes to peek. Must be a positive integer. + * @param {boolean=} movePointers Whether to move the pointer, defaults false. + * @return {Uint8Array} The subarray. + */ + peekBytes(n, opt_movePointers) { + const num = parseInt(n, 10); + if (n !== num || num < 0) { + throw 'Error! Called peekBytes() with a non-positive integer: ' + n; + } else if (num === 0) { + return new Uint8Array(); + } + + // Flush bits until we are byte-aligned. + // from http://tools.ietf.org/html/rfc1951#page-11 + // "Any bits of input up to the next byte boundary are ignored." + while (this.bitPtr != 0) { + this.readBits(1); + } + + const numBytesLeft = this.getNumBitsLeft() / 8; + if (num > numBytesLeft) { + throw 'Error! Overflowed the bit stream! n=' + num + ', bytePtr=' + this.bytePtr + + ', bytes.length=' + this.bytes.length + ', bitPtr=' + this.bitPtr; + } + + const movePointers = opt_movePointers || false; + const result = new Uint8Array(num); + let bytes = this.bytes; + let ptr = this.bytePtr; + let bytesLeftToCopy = num; + while (bytesLeftToCopy > 0) { + const bytesLeftInStream = bytes.length - ptr; + const sourceLength = Math.min(bytesLeftToCopy, bytesLeftInStream); + + result.set(bytes.subarray(ptr, ptr + sourceLength), num - bytesLeftToCopy); + + ptr += sourceLength; + // Overflowed the stream, just return what we got. + if (ptr >= bytes.length) { + break; + } + + bytesLeftToCopy -= sourceLength; + } + + if (movePointers) { + this.bytePtr += num; + this.bitsRead_ += (num * 8); + } + + return result; + } + + /** + * @param {number} n The number of bytes to read. + * @return {Uint8Array} The subarray. + */ + readBytes(n) { + return this.peekBytes(n, true); + } +} + +// mask for getting N number of bits (0-8) +bitjs.io.BitStream.BITMASK = [0, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF ]; diff --git a/cps/static/js/io/bytebuffer.js b/cps/static/js/io/bytebuffer.js new file mode 100644 index 00000000..71f7401e --- /dev/null +++ b/cps/static/js/io/bytebuffer.js @@ -0,0 +1,117 @@ +/* + * bytestream.js + * + * Provides a writer for bytes. + * + * Licensed under the MIT License + * + * Copyright(c) 2011 Google Inc. + * Copyright(c) 2011 antimatter15 + */ + +var bitjs = bitjs || {}; +bitjs.io = bitjs.io || {}; + + +/** + * A write-only Byte buffer which uses a Uint8 Typed Array as a backing store. + */ +bitjs.io.ByteBuffer = class { + /** + * @param {number} numBytes The number of bytes to allocate. + */ + constructor(numBytes) { + if (typeof numBytes != typeof 1 || numBytes <= 0) { + throw "Error! ByteBuffer initialized with '" + numBytes + "'"; + } + this.data = new Uint8Array(numBytes); + this.ptr = 0; + } + + + /** + * @param {number} b The byte to insert. + */ + insertByte(b) { + // TODO: throw if byte is invalid? + this.data[this.ptr++] = b; + } + + /** + * @param {Array.|Uint8Array|Int8Array} bytes The bytes to insert. + */ + insertBytes(bytes) { + // TODO: throw if bytes is invalid? + this.data.set(bytes, this.ptr); + this.ptr += bytes.length; + } + + /** + * Writes an unsigned number into the next n bytes. If the number is too large + * to fit into n bytes or is negative, an error is thrown. + * @param {number} num The unsigned number to write. + * @param {number} numBytes The number of bytes to write the number into. + */ + writeNumber(num, numBytes) { + if (numBytes < 1 || !numBytes) { + throw 'Trying to write into too few bytes: ' + numBytes; + } + if (num < 0) { + throw 'Trying to write a negative number (' + num + + ') as an unsigned number to an ArrayBuffer'; + } + if (num > (Math.pow(2, numBytes * 8) - 1)) { + throw 'Trying to write ' + num + ' into only ' + numBytes + ' bytes'; + } + + // Roll 8-bits at a time into an array of bytes. + const bytes = []; + while (numBytes-- > 0) { + const eightBits = num & 255; + bytes.push(eightBits); + num >>= 8; + } + + this.insertBytes(bytes); + } + + /** + * Writes a signed number into the next n bytes. If the number is too large + * to fit into n bytes, an error is thrown. + * @param {number} num The signed number to write. + * @param {number} numBytes The number of bytes to write the number into. + */ + writeSignedNumber(num, numBytes) { + if (numBytes < 1) { + throw 'Trying to write into too few bytes: ' + numBytes; + } + + const HALF = Math.pow(2, (numBytes * 8) - 1); + if (num >= HALF || num < -HALF) { + throw 'Trying to write ' + num + ' into only ' + numBytes + ' bytes'; + } + + // Roll 8-bits at a time into an array of bytes. + const bytes = []; + while (numBytes-- > 0) { + const eightBits = num & 255; + bytes.push(eightBits); + num >>= 8; + } + + this.insertBytes(bytes); + } + + /** + * @param {string} str The ASCII string to write. + */ + writeASCIIString(str) { + for (let i = 0; i < str.length; ++i) { + const curByte = str.charCodeAt(i); + if (curByte < 0 || curByte > 255) { + throw 'Trying to write a non-ASCII string!'; + } + this.insertByte(curByte); + } + }; +} diff --git a/cps/static/js/bytestream.js b/cps/static/js/io/bytestream.js similarity index 100% rename from cps/static/js/bytestream.js rename to cps/static/js/io/bytestream.js diff --git a/cps/static/js/unrar.js b/cps/static/js/unrar.js deleted file mode 100644 index 30263bb2..00000000 --- a/cps/static/js/unrar.js +++ /dev/null @@ -1,902 +0,0 @@ -/** - * unrar.js - * - * Copyright(c) 2011 Google Inc. - * Copyright(c) 2011 antimatter15 - * - * Reference Documentation: - * - * http://kthoom.googlecode.com/hg/docs/unrar.html - */ -/* global bitjs, importScripts */ - -// This file expects to be invoked as a Worker (see onmessage below). -importScripts("io.js"); -importScripts("archive.js"); - -// Progress variables. -var currentFilename = ""; -var currentFileNumber = 0; -var currentBytesUnarchivedInFile = 0; -var currentBytesUnarchived = 0; -var totalUncompressedBytesInArchive = 0; -var totalFilesInArchive = 0; - -// Helper functions. -var info = function(str) { - postMessage(new bitjs.archive.UnarchiveInfoEvent(str)); -}; -var err = function(str) { - postMessage(new bitjs.archive.UnarchiveErrorEvent(str)); -}; -var postProgress = function() { - postMessage(new bitjs.archive.UnarchiveProgressEvent( - currentFilename, - currentFileNumber, - currentBytesUnarchivedInFile, - currentBytesUnarchived, - totalUncompressedBytesInArchive, - totalFilesInArchive)); -}; - -// shows a byte value as its hex representation -var nibble = "0123456789ABCDEF"; -var byteValueToHexString = function(num) { - return nibble[num >> 4] + nibble[num & 0xF]; -}; -var twoByteValueToHexString = function(num) { - return nibble[(num >> 12) & 0xF] + nibble[(num >> 8) & 0xF] + nibble[(num >> 4) & 0xF] + nibble[num & 0xF]; -}; - - -// Volume Types -// MARK_HEAD = 0x72; -var MAIN_HEAD = 0x73, - FILE_HEAD = 0x74, - // COMM_HEAD = 0x75, - // AV_HEAD = 0x76, - // SUB_HEAD = 0x77, - // PROTECT_HEAD = 0x78, - // SIGN_HEAD = 0x79, - // NEWSUB_HEAD = 0x7a, - ENDARC_HEAD = 0x7b; - -// bstream is a bit stream -var RarVolumeHeader = function(bstream) { - - var headPos = bstream.bytePtr; - // byte 1,2 - info("Rar Volume Header @" + bstream.bytePtr); - - this.crc = bstream.readBits(16); - info(" crc=" + this.crc); - - // byte 3 - this.headType = bstream.readBits(8); - info(" headType=" + this.headType); - - // Get flags - // bytes 4,5 - this.flags = {}; - this.flags.value = bstream.peekBits(16); - - info(" flags=" + twoByteValueToHexString(this.flags.value)); - switch (this.headType) { - case MAIN_HEAD: - this.flags.MHD_VOLUME = !!bstream.readBits(1); - this.flags.MHD_COMMENT = !!bstream.readBits(1); - this.flags.MHD_LOCK = !!bstream.readBits(1); - this.flags.MHD_SOLID = !!bstream.readBits(1); - this.flags.MHD_PACK_COMMENT = !!bstream.readBits(1); - this.flags.MHD_NEWNUMBERING = this.flags.MHD_PACK_COMMENT; - this.flags.MHD_AV = !!bstream.readBits(1); - this.flags.MHD_PROTECT = !!bstream.readBits(1); - this.flags.MHD_PASSWORD = !!bstream.readBits(1); - this.flags.MHD_FIRSTVOLUME = !!bstream.readBits(1); - this.flags.MHD_ENCRYPTVER = !!bstream.readBits(1); - bstream.readBits(6); // unused - break; - case FILE_HEAD: - this.flags.LHD_SPLIT_BEFORE = !!bstream.readBits(1); // 0x0001 - this.flags.LHD_SPLIT_AFTER = !!bstream.readBits(1); // 0x0002 - this.flags.LHD_PASSWORD = !!bstream.readBits(1); // 0x0004 - this.flags.LHD_COMMENT = !!bstream.readBits(1); // 0x0008 - this.flags.LHD_SOLID = !!bstream.readBits(1); // 0x0010 - bstream.readBits(3); // unused - this.flags.LHD_LARGE = !!bstream.readBits(1); // 0x0100 - this.flags.LHD_UNICODE = !!bstream.readBits(1); // 0x0200 - this.flags.LHD_SALT = !!bstream.readBits(1); // 0x0400 - this.flags.LHD_VERSION = !!bstream.readBits(1); // 0x0800 - this.flags.LHD_EXTTIME = !!bstream.readBits(1); // 0x1000 - this.flags.LHD_EXTFLAGS = !!bstream.readBits(1); // 0x2000 - bstream.readBits(2); // unused - info(" LHD_SPLIT_BEFORE = " + this.flags.LHD_SPLIT_BEFORE); - break; - default: - bstream.readBits(16); - } - - // byte 6,7 - this.headSize = bstream.readBits(16); - info(" headSize=" + this.headSize); - switch (this.headType) { - case MAIN_HEAD: - this.highPosAv = bstream.readBits(16); - this.posAv = bstream.readBits(32); - if (this.flags.MHD_ENCRYPTVER) { - this.encryptVer = bstream.readBits(8); - } - info("Found MAIN_HEAD with highPosAv=" + this.highPosAv + ", posAv=" + this.posAv); - break; - case FILE_HEAD: - this.packSize = bstream.readBits(32); - this.unpackedSize = bstream.readBits(32); - this.hostOS = bstream.readBits(8); - this.fileCRC = bstream.readBits(32); - this.fileTime = bstream.readBits(32); - this.unpVer = bstream.readBits(8); - this.method = bstream.readBits(8); - this.nameSize = bstream.readBits(16); - this.fileAttr = bstream.readBits(32); - - if (this.flags.LHD_LARGE) { - info("Warning: Reading in LHD_LARGE 64-bit size values"); - this.HighPackSize = bstream.readBits(32); - this.HighUnpSize = bstream.readBits(32); - } else { - this.HighPackSize = 0; - this.HighUnpSize = 0; - if (this.unpackedSize === 0xffffffff) { - this.HighUnpSize = 0x7fffffff; - this.unpackedSize = 0xffffffff; - } - } - this.fullPackSize = 0; - this.fullUnpackSize = 0; - this.fullPackSize |= this.HighPackSize; - this.fullPackSize <<= 32; - this.fullPackSize |= this.packSize; - - // read in filename - - this.filename = bstream.readBytes(this.nameSize); - var _s = ""; - for (var _i = 0; _i < this.filename.length ; _i++) { - _s += String.fromCharCode(this.filename[_i]); - } - - this.filename = _s; - - if (this.flags.LHD_SALT) { - info("Warning: Reading in 64-bit salt value"); - this.salt = bstream.readBits(64); // 8 bytes - } - - if (this.flags.LHD_EXTTIME) { - // 16-bit flags - var extTimeFlags = bstream.readBits(16); - - // this is adapted straight out of arcread.cpp, Archive::ReadHeader() - for (var I = 0; I < 4; ++I) { - var rmode = extTimeFlags >> ((3 - I) * 4); - if ((rmode & 8) === 0) { - continue; - } - if (I !== 0) { - bstream.readBits(16); - } - var count = (rmode & 3); - for (var J = 0; J < count; ++J) { - bstream.readBits(8); - } - } - } - - if (this.flags.LHD_COMMENT) { - info("Found a LHD_COMMENT"); - } - - - while (headPos + this.headSize > bstream.bytePtr) bstream.readBits(1); - - info("Found FILE_HEAD with packSize=" + this.packSize + ", unpackedSize= " + this.unpackedSize + ", hostOS=" + this.hostOS + ", unpVer=" + this.unpVer + ", method=" + this.method + ", filename=" + this.filename); - - break; - default: - info("Found a header of type 0x" + byteValueToHexString(this.headType)); - // skip the rest of the header bytes (for now) - bstream.readBytes( this.headSize - 7 ); - break; - } -}; - -//var BLOCK_LZ = 0; - -var rLDecode = [0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224], - rLBits = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5], - rDBitLengthCounts = [4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 14, 0, 12], - rSDDecode = [0, 4, 8, 16, 32, 64, 128, 192], - rSDBits = [2, 2, 3, 4, 5, 6, 6, 6]; - -var rDDecode = [0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, - 48, 64, 96, 128, 192, 256, 384, 512, 768, 1024, 1536, 2048, 3072, - 4096, 6144, 8192, 12288, 16384, 24576, 32768, 49152, 65536, 98304, - 131072, 196608, 262144, 327680, 393216, 458752, 524288, 589824, - 655360, 720896, 786432, 851968, 917504, 983040]; - -var rDBits = [0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, - 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, - 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16]; - -var rLowDistRepCount = 16; - -var rNC = 299, - rDC = 60, - rLDC = 17, - rRC = 28, - rBC = 20, - rHuffTableSize = (rNC + rDC + rRC + rLDC); - -//var UnpBlockType = BLOCK_LZ; -var UnpOldTable = new Array(rHuffTableSize); - -var BD = { //bitdecode - DecodeLen: new Array(16), - DecodePos: new Array(16), - DecodeNum: new Array(rBC) -}; -var LD = { //litdecode - DecodeLen: new Array(16), - DecodePos: new Array(16), - DecodeNum: new Array(rNC) -}; -var DD = { //distdecode - DecodeLen: new Array(16), - DecodePos: new Array(16), - DecodeNum: new Array(rDC) -}; -var LDD = { //low dist decode - DecodeLen: new Array(16), - DecodePos: new Array(16), - DecodeNum: new Array(rLDC) -}; -var RD = { //rep decode - DecodeLen: new Array(16), - DecodePos: new Array(16), - DecodeNum: new Array(rRC) -}; - -var rBuffer; - -// read in Huffman tables for RAR -function rarReadTables(bstream) { - var BitLength = new Array(rBC), - Table = new Array(rHuffTableSize); - var i; - // before we start anything we need to get byte-aligned - bstream.readBits( (8 - bstream.bitPtr) & 0x7 ); - - if (bstream.readBits(1)) { - info("Error! PPM not implemented yet"); - return; - } - - if (!bstream.readBits(1)) { //discard old table - for (i = UnpOldTable.length; i--;) UnpOldTable[i] = 0; - } - - // read in bit lengths - for (var I = 0; I < rBC; ++I) { - - var Length = bstream.readBits(4); - if (Length === 15) { - var ZeroCount = bstream.readBits(4); - if (ZeroCount === 0) { - BitLength[I] = 15; - } else { - ZeroCount += 2; - while (ZeroCount-- > 0 && I < rBC) { - BitLength[I++] = 0; - } - --I; - } - } else { - BitLength[I] = Length; - } - } - - // now all 20 bit lengths are obtained, we construct the Huffman Table: - - rarMakeDecodeTables(BitLength, 0, BD, rBC); - - var TableSize = rHuffTableSize; - //console.log(DecodeLen, DecodePos, DecodeNum); - for (i = 0; i < TableSize;) { - var N; - var num = rarDecodeNumber(bstream, BD); - if (num < 16) { - Table[i] = (num + UnpOldTable[i]) & 0xf; - i++; - } else if (num < 18) { - N = (num === 16) ? (bstream.readBits(3) + 3) : (bstream.readBits(7) + 11); - - while (N-- > 0 && i < TableSize) { - Table[i] = Table[i - 1]; - i++; - } - } else { - N = (num === 18) ? (bstream.readBits(3) + 3) : (bstream.readBits(7) + 11); - - while (N-- > 0 && i < TableSize) { - Table[i++] = 0; - } - } - } - - rarMakeDecodeTables(Table, 0, LD, rNC); - rarMakeDecodeTables(Table, rNC, DD, rDC); - rarMakeDecodeTables(Table, rNC + rDC, LDD, rLDC); - rarMakeDecodeTables(Table, rNC + rDC + rLDC, RD, rRC); - - for (i = UnpOldTable.length; i--;) { - UnpOldTable[i] = Table[i]; - } - return true; -} - - -function rarDecodeNumber(bstream, dec) { - var DecodeLen = dec.DecodeLen, DecodePos = dec.DecodePos, DecodeNum = dec.DecodeNum; - var bitField = bstream.getBits() & 0xfffe; - //some sort of rolled out binary search - var bits = ((bitField < DecodeLen[8]) ? - ((bitField < DecodeLen[4]) ? - ((bitField < DecodeLen[2]) ? - ((bitField < DecodeLen[1]) ? 1 : 2) - : ((bitField < DecodeLen[3]) ? 3 : 4)) - : (bitField < DecodeLen[6]) ? - ((bitField < DecodeLen[5]) ? 5 : 6) - : ((bitField < DecodeLen[7]) ? 7 : 8)) - : ((bitField < DecodeLen[12]) ? - ((bitField < DecodeLen[10]) ? - ((bitField < DecodeLen[9]) ? 9 : 10) - : ((bitField < DecodeLen[11]) ? 11 : 12)) - : (bitField < DecodeLen[14]) ? - ((bitField < DecodeLen[13]) ? 13 : 14) - : 15)); - bstream.readBits(bits); - var N = DecodePos[bits] + ((bitField - DecodeLen[bits - 1]) >>> (16 - bits)); - - return DecodeNum[N]; -} - - -function rarMakeDecodeTables(BitLength, offset, dec, size) { - var DecodeLen = dec.DecodeLen, DecodePos = dec.DecodePos, DecodeNum = dec.DecodeNum; - var LenCount = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - TmpPos = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - N = 0, M = 0; - var i; - for (i = DecodeNum.length; i--;) DecodeNum[i] = 0; - for (i = 0; i < size; i++) { - LenCount[BitLength[i + offset] & 0xF]++; - } - LenCount[0] = 0; - TmpPos[0] = 0; - DecodePos[0] = 0; - DecodeLen[0] = 0; - - var I; - for (I = 1; I < 16; ++I) { - N = 2 * (N + LenCount[I]); - M = (N << (15 - I)); - if (M > 0xFFFF) { - M = 0xFFFF; - } - DecodeLen[I] = M; - DecodePos[I] = DecodePos[I - 1] + LenCount[I - 1]; - TmpPos[I] = DecodePos[I]; - } - for (I = 0; I < size; ++I) { - if (BitLength[I + offset] !== 0) { - DecodeNum[ TmpPos[ BitLength[offset + I] & 0xF ]++] = I; - } - } -} - -// TODO: implement -function Unpack15() { //bstream, Solid) { - info("ERROR! RAR 1.5 compression not supported"); -} - -var lowDistRepCount = 0, prevLowDist = 0; - -var rOldDist = [0, 0, 0, 0]; - -var lastDist = 0; -var lastLength = 0; - -function Unpack20(bstream) { //, Solid) { - var destUnpSize = rBuffer.data.length; - var oldDistPtr = 0; - var Length; - var Distance; - rarReadTables20(bstream); - while (destUnpSize > rBuffer.ptr) { - var num = rarDecodeNumber(bstream, LD); - var Bits; - if (num < 256) { - rBuffer.insertByte(num); - continue; - } - if (num > 269) { - Length = rLDecode[num -= 270] + 3; - if ((Bits = rLBits[num]) > 0) { - Length += bstream.readBits(Bits); - } - var DistNumber = rarDecodeNumber(bstream, DD); - Distance = rDDecode[DistNumber] + 1; - if ((Bits = rDBits[DistNumber]) > 0) { - Distance += bstream.readBits(Bits); - } - if (Distance >= 0x2000) { - Length++; - if (Distance >= 0x40000) Length++; - } - lastLength = Length; - lastDist = rOldDist[oldDistPtr++ & 3] = Distance; - rarCopyString(Length, Distance); - continue; - } - if (num === 269) { - rarReadTables20(bstream); - - rarUpdateProgress(); - - continue; - } - if (num === 256) { - lastDist = rOldDist[oldDistPtr++ & 3] = lastDist; - rarCopyString(lastLength, lastDist); - continue; - } - if (num < 261) { - Distance = rOldDist[(oldDistPtr - (num - 256)) & 3]; - var LengthNumber = rarDecodeNumber(bstream, RD); - Length = rLDecode[LengthNumber] + 2; - if ((Bits = rLBits[LengthNumber]) > 0) { - Length += bstream.readBits(Bits); - } - if (Distance >= 0x101) { - Length++; - if (Distance >= 0x2000) { - Length++; - if (Distance >= 0x40000) Length++; - } - } - lastLength = Length; - lastDist = rOldDist[oldDistPtr++ & 3] = Distance; - rarCopyString(Length, Distance); - continue; - } - if (num < 270) { - Distance = rSDDecode[num -= 261] + 1; - if ((Bits = rSDBits[num]) > 0) { - Distance += bstream.readBits(Bits); - } - lastLength = 2; - lastDist = rOldDist[oldDistPtr++ & 3] = Distance; - rarCopyString(2, Distance); - continue; - } - } - rarUpdateProgress(); -} - -function rarUpdateProgress() { - var change = rBuffer.ptr - currentBytesUnarchivedInFile; - currentBytesUnarchivedInFile = rBuffer.ptr; - currentBytesUnarchived += change; - postProgress(); -} - - -var rNC20 = 298, - rDC20 = 48, - rRC20 = 28, - rBC20 = 19, - rMC20 = 257; - -var UnpOldTable20 = new Array(rMC20 * 4); - -function rarReadTables20(bstream) { - var BitLength = new Array(rBC20); - var Table = new Array(rMC20 * 4); - var TableSize, N, I; - var i; - bstream.readBits(1); - if (!bstream.readBits(1)) { - for (i = UnpOldTable20.length; i--;) UnpOldTable20[i] = 0; - } - TableSize = rNC20 + rDC20 + rRC20; - for (I = 0; I < rBC20; I++) { - BitLength[I] = bstream.readBits(4); - } - rarMakeDecodeTables(BitLength, 0, BD, rBC20); - I = 0; - while (I < TableSize) { - var num = rarDecodeNumber(bstream, BD); - if (num < 16) { - Table[I] = num + UnpOldTable20[I] & 0xf; - I++; - } else if (num === 16) { - N = bstream.readBits(2) + 3; - while (N-- > 0 && I < TableSize) { - Table[I] = Table[I - 1]; - I++; - } - } else { - if (num === 17) { - N = bstream.readBits(3) + 3; - } else { - N = bstream.readBits(7) + 11; - } - while (N-- > 0 && I < TableSize) { - Table[I++] = 0; - } - } - } - rarMakeDecodeTables(Table, 0, LD, rNC20); - rarMakeDecodeTables(Table, rNC20, DD, rDC20); - rarMakeDecodeTables(Table, rNC20 + rDC20, RD, rRC20); - for (i = UnpOldTable20.length; i--;) UnpOldTable20[i] = Table[i]; -} - - -function Unpack29(bstream) { - // lazy initialize rDDecode and rDBits - - var DDecode = new Array(rDC); - var DBits = new Array(rDC); - var Distance = 0; - var Length = 0; - var Dist = 0, BitLength = 0, Slot = 0; - var I; - for (I = 0; I < rDBitLengthCounts.length; I++, BitLength++) { - for (var J = 0; J < rDBitLengthCounts[I]; J++, Slot++, Dist += (1 << BitLength)) { - DDecode[Slot] = Dist; - DBits[Slot] = BitLength; - } - } - - var Bits; - //tablesRead = false; - - rOldDist = [0, 0, 0, 0]; - - lastDist = 0; - lastLength = 0; - var i; - for (i = UnpOldTable.length; i--;) UnpOldTable[i] = 0; - - // read in Huffman tables - rarReadTables(bstream); - - while (true) { - var num = rarDecodeNumber(bstream, LD); - - if (num < 256) { - rBuffer.insertByte(num); - continue; - } - if (num >= 271) { - Length = rLDecode[num -= 271] + 3; - if ((Bits = rLBits[num]) > 0) { - Length += bstream.readBits(Bits); - } - var DistNumber = rarDecodeNumber(bstream, DD); - Distance = DDecode[DistNumber] + 1; - if ((Bits = DBits[DistNumber]) > 0) { - if (DistNumber > 9) { - if (Bits > 4) { - Distance += ((bstream.getBits() >>> (20 - Bits)) << 4); - bstream.readBits(Bits - 4); - //todo: check this - } - if (lowDistRepCount > 0) { - lowDistRepCount--; - Distance += prevLowDist; - } else { - var LowDist = rarDecodeNumber(bstream, LDD); - if (LowDist === 16) { - lowDistRepCount = rLowDistRepCount - 1; - Distance += prevLowDist; - } else { - Distance += LowDist; - prevLowDist = LowDist; - } - } - } else { - Distance += bstream.readBits(Bits); - } - } - if (Distance >= 0x2000) { - Length++; - if (Distance >= 0x40000) { - Length++; - } - } - rarInsertOldDist(Distance); - rarInsertLastMatch(Length, Distance); - rarCopyString(Length, Distance); - continue; - } - if (num === 256) { - if (!rarReadEndOfBlock(bstream)) break; - continue; - } - if (num === 257) { - //console.log("READVMCODE"); - if (!rarReadVMCode(bstream)) break; - continue; - } - if (num === 258) { - if (lastLength != 0) { - rarCopyString(lastLength, lastDist); - } - continue; - } - if (num < 263) { - var DistNum = num - 259; - Distance = rOldDist[DistNum]; - - for (var I = DistNum; I > 0; I--) { - rOldDist[I] = rOldDist[I - 1]; - } - rOldDist[0] = Distance; - - var LengthNumber = rarDecodeNumber(bstream, RD); - Length = rLDecode[LengthNumber] + 2; - if ((Bits = rLBits[LengthNumber]) > 0) { - Length += bstream.readBits(Bits); - } - rarInsertLastMatch(Length, Distance); - rarCopyString(Length, Distance); - continue; - } - if (num < 272) { - Distance = rSDDecode[num -= 263] + 1; - if ((Bits = rSDBits[num]) > 0) { - Distance += bstream.readBits(Bits); - } - rarInsertOldDist(Distance); - rarInsertLastMatch(2, Distance); - rarCopyString(2, Distance); - continue; - } - } - rarUpdateProgress(); -} - -function rarReadEndOfBlock(bstream) { - - rarUpdateProgress(); - - var NewTable = false, NewFile = false; - if (bstream.readBits(1)) { - NewTable = true; - } else { - NewFile = true; - NewTable = !!bstream.readBits(1); - } - //tablesRead = !NewTable; - return !(NewFile || NewTable && !rarReadTables(bstream)); -} - - -function rarReadVMCode(bstream) { - var FirstByte = bstream.readBits(8); - var Length = (FirstByte & 7) + 1; - if (Length === 7) { - Length = bstream.readBits(8) + 7; - } else if (Length === 8) { - Length = bstream.readBits(16); - } - var vmCode = []; - for (var I = 0; I < Length; I++) { - //do something here with cheking readbuf - vmCode.push(bstream.readBits(8)); - } - return RarAddVMCode(FirstByte, vmCode, Length); -} - -function RarAddVMCode(firstByte, vmCode, length) { - //console.log(vmCode); - if (vmCode.length > 0) { - info("Error! RarVM not supported yet!"); - } - return true; -} - -function rarInsertLastMatch(length, distance) { - lastDist = distance; - lastLength = length; -} - -function rarInsertOldDist(distance) { - rOldDist.splice(3, 1); - rOldDist.splice(0, 0, distance); -} - -//this is the real function, the other one is for debugging -function rarCopyString(length, distance) { - var destPtr = rBuffer.ptr - distance; - if (destPtr < 0) { - var l = rOldBuffers.length; - while (destPtr < 0) { - destPtr = rOldBuffers[--l].data.length + destPtr; - } - //TODO: lets hope that it never needs to read beyond file boundaries - while (length--) rBuffer.insertByte(rOldBuffers[l].data[destPtr++]); - } - if (length > distance) { - while (length--) rBuffer.insertByte(rBuffer.data[destPtr++]); - } else { - rBuffer.insertBytes(rBuffer.data.subarray(destPtr, destPtr + length)); - } -} - -var rOldBuffers = []; -// v must be a valid RarVolume -function unpack(v) { - - // TODO: implement what happens when unpVer is < 15 - var Ver = v.header.unpVer <= 15 ? 15 : v.header.unpVer, - Solid = v.header.LHD_SOLID, - bstream = new bitjs.io.BitStream(v.fileData.buffer, true /* rtl */, v.fileData.byteOffset, v.fileData.byteLength ); - - rBuffer = new bitjs.io.ByteBuffer(v.header.unpackedSize); - - info("Unpacking " + v.filename + " RAR v" + Ver); - - switch(Ver) { - case 15: // rar 1.5 compression - Unpack15(); //(bstream, Solid); - break; - case 20: // rar 2.x compression - case 26: // files larger than 2GB - Unpack20(bstream); //, Solid); - break; - case 29: // rar 3.x compression - case 36: // alternative hash - Unpack29(bstream); - break; - } // switch(method) - - rOldBuffers.push(rBuffer); - //TODO: clear these old buffers when there's over 4MB of history - return rBuffer.data; -} - -// bstream is a bit stream -var RarLocalFile = function(bstream) { - - this.header = new RarVolumeHeader(bstream); - this.filename = this.header.filename; - - if (this.header.headType != FILE_HEAD && this.header.headType != ENDARC_HEAD) { - this.isValid = false; - info("Error! RAR Volume did not include a FILE_HEAD header "); - } else { - // read in the compressed data - this.fileData = null; - if (this.header.packSize > 0) { - this.fileData = bstream.readBytes(this.header.packSize); - this.isValid = true; - } - } -}; - -RarLocalFile.prototype.unrar = function() { - - if (!this.header.flags.LHD_SPLIT_BEFORE) { - // unstore file - if (this.header.method === 0x30) { - info("Unstore " + this.filename); - this.isValid = true; - - currentBytesUnarchivedInFile += this.fileData.length; - currentBytesUnarchived += this.fileData.length; - - // Create a new buffer and copy it over. - var len = this.header.packSize; - var newBuffer = new bitjs.io.ByteBuffer(len); - newBuffer.insertBytes(this.fileData); - this.fileData = newBuffer.data; - } else { - this.isValid = true; - this.fileData = unpack(this); - } - } -}; - -var unrar = function(arrayBuffer) { - currentFilename = ""; - currentFileNumber = 0; - currentBytesUnarchivedInFile = 0; - currentBytesUnarchived = 0; - totalUncompressedBytesInArchive = 0; - totalFilesInArchive = 0; - - postMessage(new bitjs.archive.UnarchiveStartEvent()); - var bstream = new bitjs.io.BitStream(arrayBuffer, false /* rtl */); - - var header = new RarVolumeHeader(bstream); - if (header.crc === 0x6152 && - header.headType === 0x72 && - header.flags.value === 0x1A21 && - header.headSize === 7) { - - info("Found RAR signature"); - - var mhead = new RarVolumeHeader(bstream); - if (mhead.headType != MAIN_HEAD) { - info("Error! RAR did not include a MAIN_HEAD header"); - } else { - var localFiles = []; - var localFile = null; - do { - try { - localFile = new RarLocalFile(bstream); - info("RAR localFile isValid=" + localFile.isValid + ", volume packSize=" + localFile.header.packSize); - if (localFile && localFile.isValid && localFile.header.packSize > 0) { - totalUncompressedBytesInArchive += localFile.header.unpackedSize; - localFiles.push(localFile); - } else if (localFile.header.packSize === 0 && localFile.header.unpackedSize === 0) { - localFile.isValid = true; - } - } catch (err) { - break; - } - //info("bstream" + bstream.bytePtr+"/"+bstream.bytes.length); - } while ( localFile.isValid ); - totalFilesInArchive = localFiles.length; - - // now we have all information but things are unpacked - // TODO: unpack - localFiles = localFiles.sort(function(a, b) { - var aname = a.filename.toLowerCase(); - var bname = b.filename.toLowerCase(); - return aname > bname ? 1 : -1; - }); - - info(localFiles.map(function(a) {return a.filename;}).join(", ")); - for (var i = 0; i < localFiles.length; ++i) { - var localfile = localFiles[i]; - - // update progress - currentFilename = localfile.header.filename; - currentBytesUnarchivedInFile = 0; - - // actually do the unzipping - localfile.unrar(); - - if (localfile.isValid) { - postMessage(new bitjs.archive.UnarchiveExtractEvent(localfile)); - postProgress(); - } - } - - postProgress(); - } - } else { - err("Invalid RAR file"); - } - postMessage(new bitjs.archive.UnarchiveFinishEvent()); -}; - -// event.data.file has the ArrayBuffer. -onmessage = function(event) { - var ab = event.data.file; - unrar(ab, true); -}; diff --git a/cps/static/js/unzip.js b/cps/static/js/unzip.js deleted file mode 100644 index 0a067516..00000000 --- a/cps/static/js/unzip.js +++ /dev/null @@ -1,626 +0,0 @@ -/** - * unzip.js - * - * Copyright(c) 2011 Google Inc. - * Copyright(c) 2011 antimatter15 - * - * Reference Documentation: - * - * ZIP format: http://www.pkware.com/documents/casestudies/APPNOTE.TXT - * DEFLATE format: http://tools.ietf.org/html/rfc1951 - */ -/* global bitjs, importScripts, Uint8Array*/ - -// This file expects to be invoked as a Worker (see onmessage below). -importScripts("io.js"); -importScripts("archive.js"); - -// Progress variables. -var currentFilename = ""; -var currentFileNumber = 0; -var currentBytesUnarchivedInFile = 0; -var currentBytesUnarchived = 0; -var totalUncompressedBytesInArchive = 0; -var totalFilesInArchive = 0; - -// Helper functions. -var info = function(str) { - postMessage(new bitjs.archive.UnarchiveInfoEvent(str)); -}; -var err = function(str) { - postMessage(new bitjs.archive.UnarchiveErrorEvent(str)); -}; -var postProgress = function() { - postMessage(new bitjs.archive.UnarchiveProgressEvent( - currentFilename, - currentFileNumber, - currentBytesUnarchivedInFile, - currentBytesUnarchived, - totalUncompressedBytesInArchive, - totalFilesInArchive)); -}; - -var zLocalFileHeaderSignature = 0x04034b50; -var zArchiveExtraDataSignature = 0x08064b50; -var zCentralFileHeaderSignature = 0x02014b50; -var zDigitalSignatureSignature = 0x05054b50; - -// takes a ByteStream and parses out the local file information -var ZipLocalFile = function(bstream) { - if (typeof bstream != typeof {} || !bstream.readNumber || typeof bstream.readNumber != typeof function() {}) { - return null; - } - - bstream.readNumber(4); // swallow signature - this.version = bstream.readNumber(2); - this.generalPurpose = bstream.readNumber(2); - this.compressionMethod = bstream.readNumber(2); - this.lastModFileTime = bstream.readNumber(2); - this.lastModFileDate = bstream.readNumber(2); - this.crc32 = bstream.readNumber(4); - this.compressedSize = bstream.readNumber(4); - this.uncompressedSize = bstream.readNumber(4); - this.fileNameLength = bstream.readNumber(2); - this.extraFieldLength = bstream.readNumber(2); - - this.filename = null; - if (this.fileNameLength > 0) { - this.filename = bstream.readString(this.fileNameLength); - } - - info("Zip Local File Header:"); - info(" version=" + this.version); - info(" general purpose=" + this.generalPurpose); - info(" compression method=" + this.compressionMethod); - info(" last mod file time=" + this.lastModFileTime); - info(" last mod file date=" + this.lastModFileDate); - info(" crc32=" + this.crc32); - info(" compressed size=" + this.compressedSize); - info(" uncompressed size=" + this.uncompressedSize); - info(" file name length=" + this.fileNameLength); - info(" extra field length=" + this.extraFieldLength); - info(" filename = '" + this.filename + "'"); - - this.extraField = null; - if (this.extraFieldLength > 0) { - this.extraField = bstream.readString(this.extraFieldLength); - info(" extra field=" + this.extraField); - } - - // read in the compressed data - this.fileData = null; - if (this.compressedSize > 0) { - this.fileData = new Uint8Array(bstream.bytes.buffer, bstream.ptr, this.compressedSize); - bstream.ptr += this.compressedSize; - } - - // TODO: deal with data descriptor if present (we currently assume no data descriptor!) - // "This descriptor exists only if bit 3 of the general purpose bit flag is set" - // But how do you figure out how big the file data is if you don't know the compressedSize - // from the header?!? - if ((this.generalPurpose & bitjs.BIT[3]) != 0) { - this.crc32 = bstream.readNumber(4); - this.compressedSize = bstream.readNumber(4); - this.uncompressedSize = bstream.readNumber(4); - } -}; - -// determine what kind of compressed data we have and decompress -ZipLocalFile.prototype.unzip = function() { - - // Zip Version 1.0, no compression (store only) - if (this.compressionMethod == 0 ) { - info("ZIP v" + this.version + ", store only: " + this.filename + " (" + this.compressedSize + " bytes)"); - currentBytesUnarchivedInFile = this.compressedSize; - currentBytesUnarchived += this.compressedSize; - this.fileData = zeroCompression(this.fileData, this.uncompressedSize); - } - // version == 20, compression method == 8 (DEFLATE) - else if (this.compressionMethod == 8) { - info("ZIP v2.0, DEFLATE: " + this.filename + " (" + this.compressedSize + " bytes)"); - this.fileData = inflate(this.fileData, this.uncompressedSize); - } - else { - err("UNSUPPORTED VERSION/FORMAT: ZIP v" + this.version + ", compression method=" + this.compressionMethod + ": " + this.filename + " (" + this.compressedSize + " bytes)"); - this.fileData = null; - } -}; - - -// Takes an ArrayBuffer of a zip file in -// returns null on error -// returns an array of DecompressedFile objects on success -var unzip = function(arrayBuffer) { - postMessage(new bitjs.archive.UnarchiveStartEvent()); - - currentFilename = ""; - currentFileNumber = 0; - currentBytesUnarchivedInFile = 0; - currentBytesUnarchived = 0; - totalUncompressedBytesInArchive = 0; - totalFilesInArchive = 0; - currentBytesUnarchived = 0; - - var bstream = new bitjs.io.ByteStream(arrayBuffer); - // detect local file header signature or return null - if (bstream.peekNumber(4) == zLocalFileHeaderSignature) { - var localFiles = []; - // loop until we don't see any more local files - while (bstream.peekNumber(4) == zLocalFileHeaderSignature) { - var oneLocalFile = new ZipLocalFile(bstream); - // this should strip out directories/folders - if (oneLocalFile && oneLocalFile.uncompressedSize > 0 && oneLocalFile.fileData) { - localFiles.push(oneLocalFile); - totalUncompressedBytesInArchive += oneLocalFile.uncompressedSize; - } - } - totalFilesInArchive = localFiles.length; - - // got all local files, now sort them - localFiles.sort(function(a, b) { - var aname = a.filename.toLowerCase(); - var bname = b.filename.toLowerCase(); - return aname > bname ? 1 : -1; - }); - - // archive extra data record - if (bstream.peekNumber(4) == zArchiveExtraDataSignature) { - info(" Found an Archive Extra Data Signature"); - - // skipping this record for now - bstream.readNumber(4); - var archiveExtraFieldLength = bstream.readNumber(4); - bstream.readString(archiveExtraFieldLength); - } - - // central directory structure - // TODO: handle the rest of the structures (Zip64 stuff) - if (bstream.peekNumber(4) == zCentralFileHeaderSignature) { - info(" Found a Central File Header"); - - // read all file headers - while (bstream.peekNumber(4) == zCentralFileHeaderSignature) { - bstream.readNumber(4); // signature - bstream.readNumber(2); // version made by - bstream.readNumber(2); // version needed to extract - bstream.readNumber(2); // general purpose bit flag - bstream.readNumber(2); // compression method - bstream.readNumber(2); // last mod file time - bstream.readNumber(2); // last mod file date - bstream.readNumber(4); // crc32 - bstream.readNumber(4); // compressed size - bstream.readNumber(4); // uncompressed size - var fileNameLength = bstream.readNumber(2); // file name length - var extraFieldLength = bstream.readNumber(2); // extra field length - var fileCommentLength = bstream.readNumber(2); // file comment length - bstream.readNumber(2); // disk number start - bstream.readNumber(2); // internal file attributes - bstream.readNumber(4); // external file attributes - bstream.readNumber(4); // relative offset of local header - - bstream.readString(fileNameLength); // file name - bstream.readString(extraFieldLength); // extra field - bstream.readString(fileCommentLength); // file comment - } - } - - // digital signature - if (bstream.peekNumber(4) == zDigitalSignatureSignature) { - info(" Found a Digital Signature"); - - bstream.readNumber(4); - var sizeOfSignature = bstream.readNumber(2); - bstream.readString(sizeOfSignature); // digital signature data - } - - // report # files and total length - if (localFiles.length > 0) { - postProgress(); - } - - // now do the unzipping of each file - for (var i = 0; i < localFiles.length; ++i) { - var localfile = localFiles[i]; - - // update progress - currentFilename = localfile.filename; - currentFileNumber = i; - currentBytesUnarchivedInFile = 0; - - // actually do the unzipping - localfile.unzip(); - - if (localfile.fileData != null) { - postMessage(new bitjs.archive.UnarchiveExtractEvent(localfile)); - postProgress(); - } - } - postProgress(); - postMessage(new bitjs.archive.UnarchiveFinishEvent()); - } -}; - -// returns a table of Huffman codes -// each entry's index is its code and its value is a JavaScript object -// containing {length: 6, symbol: X} -function getHuffmanCodes(bitLengths) { - // ensure bitLengths is an array containing at least one element - if (typeof bitLengths != typeof [] || bitLengths.length < 1) { - err("Error! getHuffmanCodes() called with an invalid array"); - return null; - } - - // Reference: http://tools.ietf.org/html/rfc1951#page-8 - var numLengths = bitLengths.length, - blCount = [], - MAX_BITS = 1; - - // Step 1: count up how many codes of each length we have - for (var i = 0; i < numLengths; ++i) { - var length = bitLengths[i]; - // test to ensure each bit length is a positive, non-zero number - if (typeof length != typeof 1 || length < 0) { - err("bitLengths contained an invalid number in getHuffmanCodes(): " + length + " of type " + (typeof length)); - return null; - } - // increment the appropriate bitlength count - if (blCount[length] == undefined) blCount[length] = 0; - // a length of zero means this symbol is not participating in the huffman coding - if (length > 0) blCount[length]++; - - if (length > MAX_BITS) MAX_BITS = length; - } - - // Step 2: Find the numerical value of the smallest code for each code length - var nextCode = [], - code = 0; - for (var bits = 1; bits <= MAX_BITS; ++bits) { - var length = bits - 1; - // ensure undefined lengths are zero - if (blCount[length] == undefined) blCount[length] = 0; - code = (code + blCount[bits - 1]) << 1; - nextCode [bits] = code; - } - - // Step 3: Assign numerical values to all codes - var table = {}, tableLength = 0; - for (var n = 0; n < numLengths; ++n) { - var len = bitLengths[n]; - if (len != 0) { - table[nextCode [len]] = { length: len, symbol: n }; //, bitstring: binaryValueToString(nextCode [len],len) }; - tableLength++; - nextCode [len]++; - } - } - table.maxLength = tableLength; - - return table; -} - -/* - The Huffman codes for the two alphabets are fixed, and are not - represented explicitly in the data. The Huffman code lengths - for the literal/length alphabet are: - - Lit Value Bits Codes - --------- ---- ----- - 0 - 143 8 00110000 through - 10111111 - 144 - 255 9 110010000 through - 111111111 - 256 - 279 7 0000000 through - 0010111 - 280 - 287 8 11000000 through - 11000111 -*/ -// fixed Huffman codes go from 7-9 bits, so we need an array whose index can hold up to 9 bits -var fixedHCtoLiteral = null; -var fixedHCtoDistance = null; -function getFixedLiteralTable() { - // create once - if (!fixedHCtoLiteral) { - var bitlengths = new Array(288); - var i; - for (i = 0; i <= 143; ++i) bitlengths[i] = 8; - for (i = 144; i <= 255; ++i) bitlengths[i] = 9; - for (i = 256; i <= 279; ++i) bitlengths[i] = 7; - for (i = 280; i <= 287; ++i) bitlengths[i] = 8; - - // get huffman code table - fixedHCtoLiteral = getHuffmanCodes(bitlengths); - } - return fixedHCtoLiteral; -} -function getFixedDistanceTable() { - // create once - if (!fixedHCtoDistance) { - var bitlengths = new Array(32); - for (var i = 0; i < 32; ++i) { - bitlengths[i] = 5; - } - - // get huffman code table - fixedHCtoDistance = getHuffmanCodes(bitlengths); - } - return fixedHCtoDistance; -} - -// extract one bit at a time until we find a matching Huffman Code -// then return that symbol -function decodeSymbol(bstream, hcTable) { - var code = 0, len = 0; - - // loop until we match - for (;;) { - // read in next bit - var bit = bstream.readBits(1); - code = (code << 1) | bit; - ++len; - - // check against Huffman Code table and break if found - if (hcTable.hasOwnProperty(code) && hcTable[code].length == len) { - - break; - } - if (len > hcTable.maxLength) { - err("Bit stream out of sync, didn't find a Huffman Code, length was " + len + - " and table only max code length of " + hcTable.maxLength); - break; - } - } - return hcTable[code].symbol; -} - - -var CodeLengthCodeOrder = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]; -/* - Extra Extra Extra -Code Bits Length(s) Code Bits Lengths Code Bits Length(s) ----- ---- ------ ---- ---- ------- ---- ---- ------- - 257 0 3 267 1 15,16 277 4 67-82 - 258 0 4 268 1 17,18 278 4 83-98 - 259 0 5 269 2 19-22 279 4 99-114 - 260 0 6 270 2 23-26 280 4 115-130 - 261 0 7 271 2 27-30 281 5 131-162 - 262 0 8 272 2 31-34 282 5 163-194 - 263 0 9 273 3 35-42 283 5 195-226 - 264 0 10 274 3 43-50 284 5 227-257 - 265 1 11,12 275 3 51-58 285 0 258 - 266 1 13,14 276 3 59-66 - -*/ -var LengthLookupTable = [ - [0, 3], [0, 4], [0, 5], [0, 6], - [0, 7], [0, 8], [0, 9], [0, 10], - [1, 11], [1, 13], [1, 15], [1, 17], - [2, 19], [2, 23], [2, 27], [2, 31], - [3, 35], [3, 43], [3, 51], [3, 59], - [4, 67], [4, 83], [4, 99], [4, 115], - [5, 131], [5, 163], [5, 195], [5, 227], - [0, 258] -]; -/* - Extra Extra Extra - Code Bits Dist Code Bits Dist Code Bits Distance - ---- ---- ---- ---- ---- ------ ---- ---- -------- - 0 0 1 10 4 33-48 20 9 1025-1536 - 1 0 2 11 4 49-64 21 9 1537-2048 - 2 0 3 12 5 65-96 22 10 2049-3072 - 3 0 4 13 5 97-128 23 10 3073-4096 - 4 1 5,6 14 6 129-192 24 11 4097-6144 - 5 1 7,8 15 6 193-256 25 11 6145-8192 - 6 2 9-12 16 7 257-384 26 12 8193-12288 - 7 2 13-16 17 7 385-512 27 12 12289-16384 - 8 3 17-24 18 8 513-768 28 13 16385-24576 - 9 3 25-32 19 8 769-1024 29 13 24577-32768 -*/ -var DistLookupTable = [ - [0, 1], [0, 2], [0, 3], [0, 4], - [1, 5], [1, 7], - [2, 9], [2, 13], - [3, 17], [3, 25], - [4, 33], [4, 49], - [5, 65], [5, 97], - [6, 129], [6, 193], - [7, 257], [7, 385], - [8, 513], [8, 769], - [9, 1025], [9, 1537], - [10, 2049], [10, 3073], - [11, 4097], [11, 6145], - [12, 8193], [12, 12289], - [13, 16385], [13, 24577] -]; - -function inflateBlockData(bstream, hcLiteralTable, hcDistanceTable, buffer) { - /* - loop (until end of block code recognized) - decode literal/length value from input stream - if value < 256 - copy value (literal byte) to output stream - otherwise - if value = end of block (256) - break from loop - otherwise (value = 257..285) - decode distance from input stream - - move backwards distance bytes in the output - stream, and copy length bytes from this - position to the output stream. - */ - var blockSize = 0; - for (;;) { - var symbol = decodeSymbol(bstream, hcLiteralTable); - if (symbol < 256) { - // copy literal byte to output - buffer.insertByte(symbol); - blockSize++; - } - else { - // end of block reached - if (symbol == 256) { - break; - } - else { - var lengthLookup = LengthLookupTable[symbol - 257], - length = lengthLookup[1] + bstream.readBits(lengthLookup[0]), - distLookup = DistLookupTable[decodeSymbol(bstream, hcDistanceTable)], - distance = distLookup[1] + bstream.readBits(distLookup[0]); - - // now apply length and distance appropriately and copy to output - - // TODO: check that backward distance < data.length? - - // http://tools.ietf.org/html/rfc1951#page-11 - // "Note also that the referenced string may overlap the current - // position; for example, if the last 2 bytes decoded have values - // X and Y, a string reference with - // adds X,Y,X,Y,X to the output stream." - // - // loop for each character - var ch = buffer.ptr - distance; - blockSize += length; - if (length > distance) { - var data = buffer.data; - while (length--) { - buffer.insertByte(data[ch++]); - } - } else { - buffer.insertBytes(buffer.data.subarray(ch, ch + length)); - } - - } // length-distance pair - } // length-distance pair or end-of-block - } // loop until we reach end of block - return blockSize; -} - -function zeroCompression(compressedData, numDecompressedBytes) { - var bstream = new bitjs.io.BitStream(compressedData.buffer, - false /* rtl */, - compressedData.byteOffset, - compressedData.byteLength); - var buffer = new bitjs.io.ByteBuffer(numDecompressedBytes); - buffer.insertBytes(bstream.readBytes(numDecompressedBytes)); - return buffer.data; -} - -// {Uint8Array} compressedData A Uint8Array of the compressed file data. -// compression method 8 -// deflate: http://tools.ietf.org/html/rfc1951 -function inflate(compressedData, numDecompressedBytes) { - // Bit stream representing the compressed data. - var bstream = new bitjs.io.BitStream(compressedData.buffer, - false /* rtl */, - compressedData.byteOffset, - compressedData.byteLength); - var buffer = new bitjs.io.ByteBuffer(numDecompressedBytes); - var numBlocks = 0, blockSize = 0; - - // block format: http://tools.ietf.org/html/rfc1951#page-9 - do { - var bFinal = bstream.readBits(1), - bType = bstream.readBits(2); - blockSize = 0; - ++numBlocks; - // no compression - if (bType == 0) { - // skip remaining bits in this byte - while (bstream.bitPtr != 0) bstream.readBits(1); - var len = bstream.readBits(16); - bstream.readBits(16); - // TODO: check if nlen is the ones-complement of len? - - if (len > 0) buffer.insertBytes(bstream.readBytes(len)); - blockSize = len; - } - // fixed Huffman codes - else if(bType == 1) { - blockSize = inflateBlockData(bstream, getFixedLiteralTable(), getFixedDistanceTable(), buffer); - } - // dynamic Huffman codes - else if(bType == 2) { - var numLiteralLengthCodes = bstream.readBits(5) + 257; - var numDistanceCodes = bstream.readBits(5) + 1, - numCodeLengthCodes = bstream.readBits(4) + 4; - - // populate the array of code length codes (first de-compaction) - var codeLengthsCodeLengths = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]; - for (var i = 0; i < numCodeLengthCodes; ++i) { - codeLengthsCodeLengths[ CodeLengthCodeOrder[i] ] = bstream.readBits(3); - } - - // get the Huffman Codes for the code lengths - var codeLengthsCodes = getHuffmanCodes(codeLengthsCodeLengths); - - // now follow this mapping - /* - 0 - 15: Represent code lengths of 0 - 15 - 16: Copy the previous code length 3 - 6 times. - The next 2 bits indicate repeat length - (0 = 3, ... , 3 = 6) - Example: Codes 8, 16 (+2 bits 11), - 16 (+2 bits 10) will expand to - 12 code lengths of 8 (1 + 6 + 5) - 17: Repeat a code length of 0 for 3 - 10 times. - (3 bits of length) - 18: Repeat a code length of 0 for 11 - 138 times - (7 bits of length) - */ - // to generate the true code lengths of the Huffman Codes for the literal - // and distance tables together - var literalCodeLengths = []; - var prevCodeLength = 0; - while (literalCodeLengths.length < numLiteralLengthCodes + numDistanceCodes) { - var symbol = decodeSymbol(bstream, codeLengthsCodes); - if (symbol <= 15) { - literalCodeLengths.push(symbol); - prevCodeLength = symbol; - } - else if (symbol == 16) { - var repeat = bstream.readBits(2) + 3; - while (repeat--) { - literalCodeLengths.push(prevCodeLength); - } - } - else if (symbol == 17) { - var repeat1 = bstream.readBits(3) + 3; - while (repeat1--) { - literalCodeLengths.push(0); - } - } else if (symbol == 18) { - var repeat2 = bstream.readBits(7) + 11; - while (repeat2--) { - literalCodeLengths.push(0); - } - } - } - - // now split the distance code lengths out of the literal code array - var distanceCodeLengths = literalCodeLengths.splice(numLiteralLengthCodes, numDistanceCodes); - - // now generate the true Huffman Code tables using these code lengths - var hcLiteralTable = getHuffmanCodes(literalCodeLengths), - hcDistanceTable = getHuffmanCodes(distanceCodeLengths); - blockSize = inflateBlockData(bstream, hcLiteralTable, hcDistanceTable, buffer); - } else { - // error - err("Error! Encountered deflate block of type 3"); - return null; - } - - // update progress - currentBytesUnarchivedInFile += blockSize; - currentBytesUnarchived += blockSize; - postProgress(); - - } while (bFinal != 1); - // we are done reading blocks if the bFinal bit was set for this block - - // return the buffer data bytes - return buffer.data; -} - -// event.data.file has the ArrayBuffer. -onmessage = function(event) { - unzip(event.data.file, true); -}; diff --git a/cps/templates/readcbr.html b/cps/templates/readcbr.html index 9fdce0f8..b7f8f532 100644 --- a/cps/templates/readcbr.html +++ b/cps/templates/readcbr.html @@ -15,7 +15,7 @@ - +