From 92b27801c3418afedc0b26d1139b7da318efa71e Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Thu, 30 Jun 2022 23:03:40 -0500 Subject: [PATCH 01/56] Update translations, remove iphoto_plist.py --- core/pe/iphoto_plist.py | 33 ------------- locale/ja/LC_MESSAGES/core.po | 91 ++++++++++++++++++----------------- locale/ja/LC_MESSAGES/ui.po | 44 ++++++++--------- 3 files changed, 68 insertions(+), 100 deletions(-) delete mode 100644 core/pe/iphoto_plist.py diff --git a/core/pe/iphoto_plist.py b/core/pe/iphoto_plist.py deleted file mode 100644 index 8479d2ff..00000000 --- a/core/pe/iphoto_plist.py +++ /dev/null @@ -1,33 +0,0 @@ -# Created By: Virgil Dupras -# Created On: 2014-03-15 -# Copyright 2015 Hardcoded Software (http://www.hardcoded.net) -# -# This software is licensed under the "GPLv3" License as described in the "LICENSE" file, -# which should be included with this package. The terms are also available at -# http://www.gnu.org/licenses/gpl-3.0.html - -import plistlib - - -class IPhotoPlistParser(plistlib._PlistParser): - """A parser for iPhoto plists. - - iPhoto plists tend to be malformed, so we have to subclass the built-in parser to be a bit more - lenient. - """ - - def __init__(self): - plistlib._PlistParser.__init__(self, use_builtin_types=True, dict_type=dict) - # For debugging purposes, we remember the last bit of data to be analyzed so that we can - # log it in case of an exception - self.lastdata = "" - - def get_data(self): - self.lastdata = plistlib._PlistParser.get_data(self) - return self.lastdata - - def end_integer(self): - try: - self.add_object(int(self.get_data())) - except ValueError: - self.add_object(0) diff --git a/locale/ja/LC_MESSAGES/core.po b/locale/ja/LC_MESSAGES/core.po index 034751da..77878001 100644 --- a/locale/ja/LC_MESSAGES/core.po +++ b/locale/ja/LC_MESSAGES/core.po @@ -1,138 +1,139 @@ # Translators: -# Fuan , 2021 +# Yuji Sasaki, 2022 +# Fuan , 2022 # msgid "" msgstr "" -"Last-Translator: Fuan , 2021\n" +"Last-Translator: Fuan , 2022\n" "Language-Team: Japanese (https://www.transifex.com/voltaicideas/teams/116153/ja/)\n" "Language: ja\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: core\app.py:42 +#: core\app.py:44 msgid "There are no marked duplicates. Nothing has been done." -msgstr "マークされた重複はありません。 何も行われていません。" +msgstr "チェックを入れた重複はありません。 何も行われませんでした。" -#: core\app.py:43 +#: core\app.py:45 msgid "There are no selected duplicates. Nothing has been done." msgstr "選択された重複はありません。 何も行われていません。" -#: core\app.py:44 +#: core\app.py:46 msgid "" "You're about to open many files at once. Depending on what those files are " "opened with, doing so can create quite a mess. Continue?" msgstr "一度に多くのファイルを開こうとしています。 これらのファイルを開く対象によっては、これを行うとかなり混乱する可能性があります。 継続する?" -#: core\app.py:71 +#: core\app.py:73 msgid "Scanning for duplicates" msgstr "重複のスキャン" -#: core\app.py:72 +#: core\app.py:74 msgid "Loading" msgstr "読み込み中" -#: core\app.py:73 +#: core\app.py:75 msgid "Moving" msgstr "移動します" -#: core\app.py:74 +#: core\app.py:76 msgid "Copying" msgstr "コピー中" -#: core\app.py:75 +#: core\app.py:77 msgid "Sending to Trash" msgstr "ごみ箱に送信します" -#: core\app.py:289 +#: core\app.py:291 msgid "" "A previous action is still hanging in there. You can't start a new one yet. " "Wait a few seconds, then try again." msgstr "前のアクションはまだそこにぶら下がっています。 まだ新しいものを始めることはできません。 数秒待ってから、再試行してください。" -#: core\app.py:300 +#: core\app.py:302 msgid "No duplicates found." msgstr "重複は見つかりませんでした。" -#: core\app.py:315 +#: core\app.py:317 msgid "All marked files were copied successfully." -msgstr "マークされたファイルはすべて正常にコピーされました。" +msgstr "チェックを入れたファイルをすべてコピーしました。" -#: core\app.py:317 +#: core\app.py:319 msgid "All marked files were moved successfully." -msgstr "マークされたファイルはすべて正常に移動されました。" +msgstr "チェックを入れたファイルをすべて移動しました。" -#: core\app.py:319 +#: core\app.py:321 msgid "All marked files were deleted successfully." -msgstr "" +msgstr "チェックを入れたファイルをすべて削除しました。" -#: core\app.py:321 +#: core\app.py:323 msgid "All marked files were successfully sent to Trash." -msgstr "マークされたファイルはすべてごみ箱に正常に送信されました。" +msgstr "チェックを入れたファイルをすべてごみ箱に移動しました。" -#: core\app.py:326 +#: core\app.py:328 msgid "Could not load file: {}" msgstr "ファイルを読み込めませんでした:{}" -#: core\app.py:382 +#: core\app.py:384 msgid "'{}' already is in the list." msgstr "「{}」既にリストに含まれています。" -#: core\app.py:384 +#: core\app.py:386 msgid "'{}' does not exist." msgstr "'{}' 存在しません。" -#: core\app.py:392 +#: core\app.py:394 msgid "" "All selected %d matches are going to be ignored in all subsequent scans. " "Continue?" msgstr "選択した%d個の一致は、以降のすべてのスキャンで無視されます。 継続する?" -#: core\app.py:469 +#: core\app.py:471 msgid "Select a directory to copy marked files to" msgstr "マークされたファイルをコピーするディレクトリを選択してください" -#: core\app.py:471 +#: core\app.py:473 msgid "Select a directory to move marked files to" msgstr "マークされたファイルを移動するディレクトリを選択してください" -#: core\app.py:510 +#: core\app.py:512 msgid "Select a destination for your exported CSV" msgstr "エクスポートしたCSVの宛先を選択します。" -#: core\app.py:516 core\app.py:771 core\app.py:781 +#: core\app.py:518 core\app.py:773 core\app.py:783 msgid "Couldn't write to file: {}" msgstr "ファイルに書き込めませんでした:{}" -#: core\app.py:539 +#: core\app.py:541 msgid "You have no custom command set up. Set it up in your preferences." msgstr "カスタムコマンドは設定されていません。 お好みで設定してください。" -#: core\app.py:695 core\app.py:707 +#: core\app.py:697 core\app.py:709 msgid "You are about to remove %d files from results. Continue?" msgstr "結果から%d個のファイルを削除しようとしています。 継続する?" -#: core\app.py:743 +#: core\app.py:745 msgid "{} duplicate groups were changed by the re-prioritization." msgstr "{}重複するグループは、再優先順位付けによって変更されました。" -#: core\app.py:790 +#: core\app.py:792 msgid "The selected directories contain no scannable file." msgstr "選択したディレクトリにはスキャン可能なファイルが含まれていません。" -#: core\app.py:803 +#: core\app.py:808 msgid "Collecting files to scan" msgstr "スキャンするファイルを収集しています" -#: core\app.py:850 +#: core\app.py:858 msgid "%s (%d discarded)" msgstr "%s (%d 廃棄)" -#: core\directories.py:191 +#: core\directories.py:190 msgid "Collected {} files to scan" msgstr "" -#: core\directories.py:207 +#: core\directories.py:206 msgid "Collected {} folders to scan" msgstr "" @@ -200,35 +201,35 @@ msgstr "EXIFタイムスタンプ" msgid "None" msgstr "無し" -#: core\prioritize.py:100 +#: core\prioritize.py:102 msgid "Ends with number" msgstr "番号で終わっている" -#: core\prioritize.py:101 +#: core\prioritize.py:103 msgid "Doesn't end with number" msgstr "数字で終わっていない" -#: core\prioritize.py:102 +#: core\prioritize.py:104 msgid "Longest" msgstr "最長" -#: core\prioritize.py:103 +#: core\prioritize.py:105 msgid "Shortest" msgstr "最短" -#: core\prioritize.py:140 +#: core\prioritize.py:142 msgid "Highest" msgstr "最高" -#: core\prioritize.py:140 +#: core\prioritize.py:142 msgid "Lowest" msgstr "最低" -#: core\prioritize.py:169 +#: core\prioritize.py:171 msgid "Newest" msgstr "最新" -#: core\prioritize.py:169 +#: core\prioritize.py:171 msgid "Oldest" msgstr "最古" diff --git a/locale/ja/LC_MESSAGES/ui.po b/locale/ja/LC_MESSAGES/ui.po index 34a7b50d..4cc46276 100644 --- a/locale/ja/LC_MESSAGES/ui.po +++ b/locale/ja/LC_MESSAGES/ui.po @@ -81,7 +81,7 @@ msgstr "(非対応)" #: qt/deletion_options.py:49 cocoa/en.lproj/Localizable.strings:0 msgid "Directly delete files" -msgstr "ファイルを直接削除する" +msgstr "ファイルを完全に削除" #: qt/deletion_options.py:51 cocoa/en.lproj/Localizable.strings:0 msgid "" @@ -100,7 +100,7 @@ msgstr "キャンセル" #: qt/details_table.py:16 cocoa/en.lproj/Localizable.strings:0 msgid "Attribute" -msgstr "アトリビュート" +msgstr "属性" #: qt/details_table.py:16 cocoa/en.lproj/Localizable.strings:0 msgid "Selected" @@ -163,7 +163,7 @@ msgstr "スキャンの種類:" #: qt/directories_dialog.py:135 msgid "More Options" -msgstr "もっとオプション" +msgstr "詳細設定" #: qt/directories_dialog.py:139 cocoa/en.lproj/Localizable.strings:0 msgid "Select folders to scan and press \"Scan\"." @@ -179,7 +179,7 @@ msgstr "スキャン" #: qt/directories_dialog.py:230 msgid "Unsaved results" -msgstr "保存されていない結果" +msgstr "未保存の結果" #: qt/directories_dialog.py:231 cocoa/en.lproj/Localizable.strings:0 msgid "You have unsaved results, do you really want to quit?" @@ -280,27 +280,27 @@ msgstr "単語の重み付け" #: qt/me/preferences_dialog.py:52 qt/se/preferences_dialog.py:32 #: cocoa/en.lproj/Localizable.strings:0 msgid "Match similar words" -msgstr "類似の単語に一致する" +msgstr "類似の単語を一致" #: qt/me/preferences_dialog.py:54 qt/pe/preferences_dialog.py:21 #: qt/se/preferences_dialog.py:34 cocoa/en.lproj/Localizable.strings:0 msgid "Can mix file kind" -msgstr "ファイルの種類を混在させることができる" +msgstr "ファイルの種類を混在" #: qt/me/preferences_dialog.py:56 qt/pe/preferences_dialog.py:23 #: qt/se/preferences_dialog.py:36 cocoa/en.lproj/Localizable.strings:0 msgid "Use regular expressions when filtering" -msgstr "フィルタリング時に正規表現を使用する" +msgstr "フィルタに正規表現を使用" #: qt/me/preferences_dialog.py:58 qt/pe/preferences_dialog.py:25 #: qt/se/preferences_dialog.py:38 cocoa/en.lproj/Localizable.strings:0 msgid "Remove empty folders on delete or move" -msgstr "削除または移動時に空のフォルダを削除する" +msgstr "削除や移動で空になったフォルダを削除" #: qt/me/preferences_dialog.py:60 qt/pe/preferences_dialog.py:27 #: qt/se/preferences_dialog.py:59 cocoa/en.lproj/Localizable.strings:0 msgid "Ignore duplicates hardlinking to the same file" -msgstr "同じファイルへの重複ハードリンクを無視する" +msgstr "同じファイルへの重複ハードリンクを無視" #: qt/me/preferences_dialog.py:62 qt/pe/preferences_dialog.py:29 #: qt/se/preferences_dialog.py:62 cocoa/en.lproj/Localizable.strings:0 @@ -309,11 +309,11 @@ msgstr "デバッグモード(再起動が必要)" #: qt/pe/preferences_dialog.py:19 cocoa/en.lproj/Localizable.strings:0 msgid "Match pictures of different dimensions" -msgstr "異なる寸法の写真を一致させる" +msgstr "異なるサイズの写真を一致" #: qt/preferences_dialog.py:43 msgid "Filter Hardness:" -msgstr "フィルター硬度:" +msgstr "フィルタの強さ:" #: qt/preferences_dialog.py:69 msgid "More Results" @@ -325,7 +325,7 @@ msgstr "より少ない結果" #: qt/preferences_dialog.py:81 msgid "Font size:" -msgstr "フォントサイズ:" +msgstr "文字サイズ:" #: qt/preferences_dialog.py:85 msgid "Language:" @@ -333,7 +333,7 @@ msgstr "言語:" #: qt/preferences_dialog.py:91 cocoa/en.lproj/Localizable.strings:0 msgid "Copy and Move:" -msgstr "コピーと移動:" +msgstr "コピーと移動:" #: qt/preferences_dialog.py:94 cocoa/en.lproj/Localizable.strings:0 msgid "Right in destination" @@ -349,7 +349,7 @@ msgstr "絶対パスを再作成" #: qt/preferences_dialog.py:99 msgid "Custom Command (arguments: %d for dupe, %r for ref):" -msgstr "カスタムコマンド (引数:重複の場合は%d、参照の場合は%r):" +msgstr "カスタムコマンド (引数: %dは重複・%rは参照):" #: qt/preferences_dialog.py:174 msgid "dupeGuru has to restart for language changes to take effect." @@ -719,7 +719,7 @@ msgstr "ウィンドウ" #: cocoa/en.lproj/Localizable.strings:0 msgid "Zoom" -msgstr "ズーム" +msgstr "拡大" #: qt\app.py:158 msgid "Exclusion Filters" @@ -909,15 +909,15 @@ msgstr "結果" #: qt\preferences_dialog.py:150 msgid "General Interface" -msgstr "一般的なインターフェイス" +msgstr "一般" #: qt\preferences_dialog.py:176 msgid "Result Table" -msgstr "結果表" +msgstr "結果" #: qt\preferences_dialog.py:205 msgid "Details Window" -msgstr "詳細ウィンドウ" +msgstr "詳細画面" #: qt\preferences_dialog.py:285 msgid "General" @@ -985,7 +985,7 @@ msgstr "" #: qt\about_box.py:31 msgid "About {}" -msgstr "{}について。" +msgstr "{}について" #: qt\about_box.py:47 msgid "Version {}" @@ -997,7 +997,7 @@ msgstr "" #: qt\about_box.py:54 msgid "Licensed under GPLv3" -msgstr "GPLv3としてライセンス供与。" +msgstr "GPLv3のもとでライセンスされています" #: qt\about_box.py:68 msgid "No update available." @@ -1013,7 +1013,7 @@ msgstr "エラーレポート" #: qt\error_report_dialog.py:54 msgid "Something went wrong. How about reporting the error?" -msgstr "何かがうまくいかなかった。 エラーを報告するのはどうですか?" +msgstr "不明な理由により失敗しました。問題を報告しませんか?" #: qt\error_report_dialog.py:60 msgid "" @@ -1035,7 +1035,7 @@ msgstr "" #: qt\error_report_dialog.py:80 msgid "Go to Github" -msgstr "Githubに移動する" +msgstr "Githubに移動" #: qt\preferences.py:24 msgid "Czech" From 360dceca7bbab9f7a3cbdecb9d0c9973d2af15b2 Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Thu, 30 Jun 2022 23:27:14 -0500 Subject: [PATCH 02/56] Update to version 4.3.0, update changelog --- core/__init__.py | 2 +- help/changelog | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/core/__init__.py b/core/__init__.py index 56d09396..d8f78c73 100644 --- a/core/__init__.py +++ b/core/__init__.py @@ -1,2 +1,2 @@ -__version__ = "4.2.1" +__version__ = "4.3.0" __appname__ = "dupeGuru" diff --git a/help/changelog b/help/changelog index 5cdf3fde..25bc5e94 100644 --- a/help/changelog +++ b/help/changelog @@ -1,3 +1,16 @@ +=== 4.3.0 (2022-07-01) +* Redirect stdout from custom command to the log files (#1008) +* Update translations +* Fix typo in debian control file (#989) +* Add option to profile scans +* Update fs.py to optimize stat() calls +* Fix Error when delete after scan (#988) +* Update directory scanning to use os.scandir() and DirEntry objects +* Improve performance of Directories.get_state() +* Migrate from hscommon.path to pathlib +* Switch file hashing to xxhash with fallback to md5 +* Add update check feature to about box + === 4.2.1 (2022-03-25) * Default to English on unsupported system language (#976) * Fix image viewer zoom datatype issue (#978) From d369bcddd7e07b327ce91715288b03c6a42ce139 Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Thu, 7 Jul 2022 19:00:09 -0500 Subject: [PATCH 03/56] Updates from investigation of #1015 - Add protection for empty hash digests in comparison of non-zero size files - Bump version to 4.3.1-dev for identification --- core/__init__.py | 2 +- core/engine.py | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/core/__init__.py b/core/__init__.py index d8f78c73..7a15dbab 100644 --- a/core/__init__.py +++ b/core/__init__.py @@ -1,2 +1,2 @@ -__version__ = "4.3.0" +__version__ = "4.3.1-dev" __appname__ = "dupeGuru" diff --git a/core/engine.py b/core/engine.py index f9e9a519..fb0692ca 100644 --- a/core/engine.py +++ b/core/engine.py @@ -303,12 +303,13 @@ def getmatches_by_contents(files, bigsize=0, j=job.nulljob): # skip hashing for zero length files result.append(Match(first, second, 100)) continue - if first.digest_partial == second.digest_partial: + # if digests are the same (and not None) then files match + if first.digest_partial == second.digest_partial and first.digest_partial is not None: if bigsize > 0 and first.size > bigsize: - if first.digest_samples == second.digest_samples: + if first.digest_samples == second.digest_samples and first.digest_samples is not None: result.append(Match(first, second, 100)) else: - if first.digest == second.digest: + if first.digest == second.digest and first.digest_samples is not None: result.append(Match(first, second, 100)) group_count += 1 j.add_progress(desc=PROGRESS_MESSAGE % (len(result), group_count)) From 97f490b8b7e95bac91e68dacdf723597006ed2ec Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Thu, 7 Jul 2022 19:06:35 -0500 Subject: [PATCH 04/56] Fix typo in engine.py --- core/engine.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/engine.py b/core/engine.py index fb0692ca..ca05a61f 100644 --- a/core/engine.py +++ b/core/engine.py @@ -309,7 +309,7 @@ def getmatches_by_contents(files, bigsize=0, j=job.nulljob): if first.digest_samples == second.digest_samples and first.digest_samples is not None: result.append(Match(first, second, 100)) else: - if first.digest == second.digest and first.digest_samples is not None: + if first.digest == second.digest and first.digest is not None: result.append(Match(first, second, 100)) group_count += 1 j.add_progress(desc=PROGRESS_MESSAGE % (len(result), group_count)) From 71af825b3799e00116ce4113d7023df01305d9a0 Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Thu, 7 Jul 2022 21:52:22 -0500 Subject: [PATCH 05/56] Move try/except of cache db to get() and put() - Move the try/except of cache db calls to the calls themselves. - Add some additional information to logging statements on cache db exception to improve troubleshooting. --- core/fs.py | 63 ++++++++++++++++++++++++++---------------------------- 1 file changed, 30 insertions(+), 33 deletions(-) diff --git a/core/fs.py b/core/fs.py index 7d9003bb..cc812238 100644 --- a/core/fs.py +++ b/core/fs.py @@ -144,13 +144,17 @@ def get(self, path: Path, key: str) -> Union[bytes, None]: stat = path.stat() size = stat.st_size mtime_ns = stat.st_mtime_ns + try: + with self.lock: + self.cur.execute( + self.select_query.format(key=key), {"path": str(path), "size": size, "mtime_ns": mtime_ns} + ) + result = self.cur.fetchone() - with self.lock: - self.cur.execute(self.select_query.format(key=key), {"path": str(path), "size": size, "mtime_ns": mtime_ns}) - result = self.cur.fetchone() - - if result: - return result[0] + if result: + return result[0] + except Exception as ex: + logging.warning(f"Couldn't get {key} for {path} w/{size}, {mtime_ns}: {ex}") return None @@ -158,12 +162,14 @@ def put(self, path: Path, key: str, value: Any) -> None: stat = path.stat() size = stat.st_size mtime_ns = stat.st_mtime_ns - - with self.lock: - self.cur.execute( - self.insert_query.format(key=key), - {"path": str(path), "size": size, "mtime_ns": mtime_ns, "value": value}, - ) + try: + with self.lock: + self.cur.execute( + self.insert_query.format(key=key), + {"path": str(path), "size": size, "mtime_ns": mtime_ns, "value": value}, + ) + except Exception as ex: + logging.warning(f"Couldn't put {key} for {path} w/{size}, {mtime_ns}: {ex}") def commit(self) -> None: with self.lock: @@ -265,34 +271,25 @@ def _read_info(self, field): self.size = nonone(stats.st_size, 0) self.mtime = nonone(stats.st_mtime, 0) elif field == "digest_partial": - try: - self.digest_partial = filesdb.get(self.path, "digest_partial") - if self.digest_partial is None: - self.digest_partial = self._calc_digest_partial() - filesdb.put(self.path, "digest_partial", self.digest_partial) - except Exception as e: - logging.warning("Couldn't get digest_partial for %s: %s", self.path, e) + self.digest_partial = filesdb.get(self.path, "digest_partial") + if self.digest_partial is None: + self.digest_partial = self._calc_digest_partial() + filesdb.put(self.path, "digest_partial", self.digest_partial) elif field == "digest": - try: - self.digest = filesdb.get(self.path, "digest") - if self.digest is None: - self.digest = self._calc_digest() - filesdb.put(self.path, "digest", self.digest) - except Exception as e: - logging.warning("Couldn't get digest for %s: %s", self.path, e) + self.digest = filesdb.get(self.path, "digest") + if self.digest is None: + self.digest = self._calc_digest() + filesdb.put(self.path, "digest", self.digest) elif field == "digest_samples": size = self.size # Might as well hash such small files entirely. if size <= MIN_FILE_SIZE: setattr(self, field, self.digest) return - try: - self.digest_samples = filesdb.get(self.path, "digest_samples") - if self.digest_samples is None: - self.digest_samples = self._calc_digest_samples() - filesdb.put(self.path, "digest_samples", self.digest_samples) - except Exception as e: - logging.warning(f"Couldn't get digest_samples for {self.path}: {e}") + self.digest_samples = filesdb.get(self.path, "digest_samples") + if self.digest_samples is None: + self.digest_samples = self._calc_digest_samples() + filesdb.put(self.path, "digest_samples", self.digest_samples) def _read_all_info(self, attrnames=None): """Cache all possible info. From 916c5204cf1767f0db74f320ed6d0f72b3d227b1 Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Thu, 7 Jul 2022 21:57:59 -0500 Subject: [PATCH 06/56] Update translations from transifex --- locale/it/LC_MESSAGES/ui.po | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/locale/it/LC_MESSAGES/ui.po b/locale/it/LC_MESSAGES/ui.po index b5ac832d..58627bbe 100644 --- a/locale/it/LC_MESSAGES/ui.po +++ b/locale/it/LC_MESSAGES/ui.po @@ -2,15 +2,16 @@ # Andrew Senetar , 2022 # Emanuele, 2022 # Fuan , 2022 +# Giovanni, 2022 # msgid "" msgstr "" -"Last-Translator: Fuan , 2022\n" +"Last-Translator: Giovanni, 2022\n" "Language-Team: Italian (https://www.transifex.com/voltaicideas/teams/116153/it/)\n" "Language: it\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" #: qt/app.py:81 msgid "Quit" @@ -979,37 +980,40 @@ msgstr "Ignora file più grandi di" #: qt\app.py:135 qt\app.py:293 msgid "Clear Cache" -msgstr "" +msgstr "Svuota cache" #: qt\app.py:294 msgid "" "Do you really want to clear the cache? This will remove all cached file " "hashes and picture analysis." msgstr "" +"Vuoi davvero svuotare la cache? Ciò rimuoverà tutti gli hash dei file " +"memorizzati nella cache e le analisi delle immagini." #: qt\app.py:299 msgid "Cache cleared." -msgstr "" +msgstr "Cache svuotata" #: qt\preferences_dialog.py:173 msgid "Use dark style" -msgstr "" +msgstr "Usa stile scuro" #: qt\preferences_dialog.py:241 msgid "Profile scan operation" -msgstr "" +msgstr "Profila l'operazione di scansione" #: qt\preferences_dialog.py:242 msgid "Profile the scan operation and save logs for optimization." msgstr "" +"Profila l'operazione di scansione e salva i registri per l'ottimizzazione." #: qt\preferences_dialog.py:246 msgid "Logs located in: {}" -msgstr "" +msgstr "I log si trovano in: {}" #: qt\preferences_dialog.py:291 msgid "Debug" -msgstr "" +msgstr "Debug" #: qt\about_box.py:31 msgid "About {}" @@ -1021,7 +1025,7 @@ msgstr "Versione {}" #: qt\about_box.py:49 qt\about_box.py:75 msgid "Checking for updates..." -msgstr "" +msgstr "Controllo degli aggiornamenti..." #: qt\about_box.py:54 msgid "Licensed under GPLv3" @@ -1029,11 +1033,11 @@ msgstr "Distribuito sotto licenza GPLv3" #: qt\about_box.py:68 msgid "No update available." -msgstr "" +msgstr "Nessun aggiornamento disponibile." #: qt\about_box.py:71 msgid "New version {} available, download here." -msgstr "" +msgstr "È disponibile la nuova versione {}, scaricabile qui." #: qt\error_report_dialog.py:50 msgid "Error Report" From 1f1dfa88dc5f6ffe13f4de514c884ab9c83bd7ce Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Thu, 7 Jul 2022 22:06:06 -0500 Subject: [PATCH 07/56] Update version & changelog for 4.3.1 release --- core/__init__.py | 2 +- help/changelog | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/core/__init__.py b/core/__init__.py index 7a15dbab..1982827c 100644 --- a/core/__init__.py +++ b/core/__init__.py @@ -1,2 +1,2 @@ -__version__ = "4.3.1-dev" +__version__ = "4.3.1" __appname__ = "dupeGuru" diff --git a/help/changelog b/help/changelog index 25bc5e94..25392b69 100644 --- a/help/changelog +++ b/help/changelog @@ -1,3 +1,8 @@ +=== 4.3.1 (2022-07-08) +* Fix issue where cache db exceptions could prevent files being hashed (#1015) +* Add extra guard for non-zero length files without digests to prevent false duplicates +* Update Italian translations + === 4.3.0 (2022-07-01) * Redirect stdout from custom command to the log files (#1008) * Update translations From db174d4e63dc3cd53702d4988e3609a0e30e7829 Mon Sep 17 00:00:00 2001 From: Muath Alsowadi <34031333+muath-ye@users.noreply.github.com> Date: Sun, 7 Aug 2022 09:32:33 +0300 Subject: [PATCH 08/56] Update columns.po --- locale/ar/LC_MESSAGES/columns.po | 52 ++++++++++++++++---------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/locale/ar/LC_MESSAGES/columns.po b/locale/ar/LC_MESSAGES/columns.po index e3d68eeb..afedd1e1 100644 --- a/locale/ar/LC_MESSAGES/columns.po +++ b/locale/ar/LC_MESSAGES/columns.po @@ -10,110 +10,110 @@ msgstr "" #: core\gui\ignore_list_table.py:19 core\gui\ignore_list_table.py:20 #: core\gui\problem_table.py:18 msgid "File Path" -msgstr "" +msgstr "مسار الملف" #: core\gui\problem_table.py:19 msgid "Error Message" -msgstr "" +msgstr "رسالة خطأ" #: core\me\prioritize.py:23 msgid "Duration" -msgstr "" +msgstr "مدة" #: core\me\prioritize.py:30 core\me\result_table.py:23 msgid "Bitrate" -msgstr "" +msgstr "معدل البت" #: core\me\prioritize.py:37 msgid "Samplerate" -msgstr "" +msgstr "معدل العينة" #: core\me\result_table.py:19 core\pe\result_table.py:19 core\prioritize.py:92 #: core\se\result_table.py:19 msgid "Filename" -msgstr "" +msgstr "اسم الملف" #: core\me\result_table.py:20 core\pe\result_table.py:20 core\prioritize.py:75 #: core\se\result_table.py:20 msgid "Folder" -msgstr "" +msgstr "مجلد" #: core\me\result_table.py:21 msgid "Size (MB)" -msgstr "" +msgstr "الحجم (ميغا بايت)" #: core\me\result_table.py:22 msgid "Time" -msgstr "" +msgstr "زمن" #: core\me\result_table.py:24 msgid "Sample Rate" -msgstr "" +msgstr "معدل العينة" #: core\me\result_table.py:25 core\pe\result_table.py:22 core\prioritize.py:65 #: core\se\result_table.py:22 msgid "Kind" -msgstr "" +msgstr "طيب القلب" #: core\me\result_table.py:26 core\pe\result_table.py:25 #: core\prioritize.py:163 core\se\result_table.py:23 msgid "Modification" -msgstr "" +msgstr "تعديل" #: core\me\result_table.py:27 msgid "Title" -msgstr "" +msgstr "عنوان" #: core\me\result_table.py:28 msgid "Artist" -msgstr "" +msgstr "فنان" #: core\me\result_table.py:29 msgid "Album" -msgstr "" +msgstr "البوم" #: core\me\result_table.py:30 msgid "Genre" -msgstr "" +msgstr "النوع" #: core\me\result_table.py:31 msgid "Year" -msgstr "" +msgstr "سنة" #: core\me\result_table.py:32 msgid "Track Number" -msgstr "" +msgstr "رقم الشاحنة" #: core\me\result_table.py:33 msgid "Comment" -msgstr "" +msgstr "تعليق" #: core\me\result_table.py:34 core\pe\result_table.py:26 #: core\se\result_table.py:24 msgid "Match %" -msgstr "" +msgstr "مباراة ٪" #: core\me\result_table.py:35 core\se\result_table.py:25 msgid "Words Used" -msgstr "" +msgstr "الكلمات المستخدمة" #: core\me\result_table.py:36 core\pe\result_table.py:27 #: core\se\result_table.py:26 msgid "Dupe Count" -msgstr "" +msgstr "عدد المخادعين" #: core\pe\prioritize.py:23 core\pe\result_table.py:23 msgid "Dimensions" -msgstr "" +msgstr "أبعاد" #: core\pe\result_table.py:21 core\se\result_table.py:21 msgid "Size (KB)" -msgstr "" +msgstr "الحجم (كيلو بايت)" #: core\pe\result_table.py:24 msgid "EXIF Timestamp" -msgstr "" +msgstr "الطابع الزمني EXIF" #: core\prioritize.py:156 msgid "Size" -msgstr "" +msgstr "بحجم" From 1827827fdf1dc59fe2b4b11a051d47c5419cf18e Mon Sep 17 00:00:00 2001 From: Luca Falavigna Date: Wed, 31 Aug 2022 14:57:16 +0000 Subject: [PATCH 09/56] Add Keywords tag to desktop file --- pkg/dupeguru.desktop | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/dupeguru.desktop b/pkg/dupeguru.desktop index 994cf4b3..91e8bf6e 100644 --- a/pkg/dupeguru.desktop +++ b/pkg/dupeguru.desktop @@ -6,3 +6,4 @@ Icon=dupeguru Terminal=false Type=Application Categories=Utility; +Keywords=file manager;gui; From 1eee3fd7e45d543c58fcdba1d4e0b8ebe4989588 Mon Sep 17 00:00:00 2001 From: Fabio Scognamiglio Date: Sat, 10 Sep 2022 13:29:04 +0200 Subject: [PATCH 10/56] Update core.po fix mispelled translation --- locale/it/LC_MESSAGES/core.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locale/it/LC_MESSAGES/core.po b/locale/it/LC_MESSAGES/core.po index 81a66399..1646dd16 100644 --- a/locale/it/LC_MESSAGES/core.po +++ b/locale/it/LC_MESSAGES/core.po @@ -150,7 +150,7 @@ msgstr "Raccolte {} cartelle da scansionare" #: core\engine.py:27 msgid "%d matches found from %d groups" -msgstr "%d corrispondeze trovate da %d gruppi" +msgstr "%d corrispondenze trovate da %d gruppi" #: core\gui\deletion_options.py:71 msgid "You are sending {} file(s) to the Trash." From f1153c85c0684497101002aa8adbe8ee6a52d100 Mon Sep 17 00:00:00 2001 From: Dobatymo Date: Tue, 27 Sep 2022 17:34:57 +0800 Subject: [PATCH 11/56] serialize/deserialize colors to/from bytes instead of strings it's a tiny bit faster and saves a bit of memory --- core/pe/cache.py | 23 +++++-------------- core/pe/cache.pyi | 4 ++-- core/pe/cache_shelve.py | 8 +++---- core/pe/cache_sqlite.py | 10 ++++---- core/pe/matchblock.py | 2 +- core/pe/modules/cache.c | 49 ++++++++++++---------------------------- core/pe/modules/common.c | 2 +- core/pe/modules/common.h | 2 +- core/tests/cache_test.py | 25 ++++++++++---------- 9 files changed, 47 insertions(+), 78 deletions(-) diff --git a/core/pe/cache.py b/core/pe/cache.py index 31fcb0bb..738037f7 100644 --- a/core/pe/cache.py +++ b/core/pe/cache.py @@ -4,24 +4,13 @@ # which should be included with this package. The terms are also available at # http://www.gnu.org/licenses/gpl-3.0.html -from core.pe._cache import string_to_colors # noqa +from core.pe._cache import bytes_to_colors # noqa -def colors_to_string(colors): - """Transform the 3 sized tuples 'colors' into a hex string. +def colors_to_bytes(colors): + """Transform the 3 sized tuples 'colors' into a bytes string. - [(0,100,255)] --> 0064ff - [(1,2,3),(4,5,6)] --> 010203040506 + [(0,100,255)] --> b'\x00d\xff' + [(1,2,3),(4,5,6)] --> b'\x01\x02\x03\x04\x05\x06' """ - return "".join("{:02x}{:02x}{:02x}".format(r, g, b) for r, g, b in colors) - - -# This function is an important bottleneck of dupeGuru PE. It has been converted to C. -# def string_to_colors(s): -# """Transform the string 's' in a list of 3 sized tuples. -# """ -# result = [] -# for i in xrange(0, len(s), 6): -# number = int(s[i:i+6], 16) -# result.append((number >> 16, (number >> 8) & 0xff, number & 0xff)) -# return result + return b"".join(map(bytes, colors)) diff --git a/core/pe/cache.pyi b/core/pe/cache.pyi index fbf1e8c8..dd59b510 100644 --- a/core/pe/cache.pyi +++ b/core/pe/cache.pyi @@ -2,5 +2,5 @@ from typing import Union, Tuple, List _block = Tuple[int, int, int] -def colors_to_string(colors: List[_block]) -> str: ... # noqa: E302 -def string_to_colors(s: str) -> Union[List[_block], None]: ... +def colors_to_bytes(colors: List[_block]) -> bytes: ... # noqa: E302 +def bytes_to_colors(s: bytes) -> Union[List[_block], None]: ... diff --git a/core/pe/cache_shelve.py b/core/pe/cache_shelve.py index 57f42775..9092ab9f 100644 --- a/core/pe/cache_shelve.py +++ b/core/pe/cache_shelve.py @@ -10,7 +10,7 @@ import tempfile from collections import namedtuple -from core.pe.cache import string_to_colors, colors_to_string +from core.pe.cache import bytes_to_colors, colors_to_bytes def wrap_path(path): @@ -57,7 +57,7 @@ def __getitem__(self, key): skey = self.shelve[wrap_id(key)] else: skey = wrap_path(key) - return string_to_colors(self.shelve[skey].blocks) + return bytes_to_colors(self.shelve[skey].blocks) def __iter__(self): return (unwrap_path(k) for k in self.shelve if k.startswith("path:")) @@ -66,7 +66,7 @@ def __len__(self): return sum(1 for k in self.shelve if k.startswith("path:")) def __setitem__(self, path_str, blocks): - blocks = colors_to_string(blocks) + blocks = colors_to_bytes(blocks) if op.exists(path_str): mtime = int(os.stat(path_str).st_mtime) else: @@ -114,7 +114,7 @@ def get_multiple(self, rowids): skey = self.shelve[wrap_id(rowid)] except KeyError: continue - yield (rowid, string_to_colors(self.shelve[skey].blocks)) + yield (rowid, bytes_to_colors(self.shelve[skey].blocks)) def purge_outdated(self): """Go through the cache and purge outdated records. diff --git a/core/pe/cache_sqlite.py b/core/pe/cache_sqlite.py index ebaa8e66..bf9f7c5b 100644 --- a/core/pe/cache_sqlite.py +++ b/core/pe/cache_sqlite.py @@ -9,7 +9,7 @@ import logging import sqlite3 as sqlite -from core.pe.cache import string_to_colors, colors_to_string +from core.pe.cache import bytes_to_colors, colors_to_bytes class SqliteCache: @@ -40,7 +40,7 @@ def __getitem__(self, key): sql = "select blocks from pictures where path = ?" result = self.con.execute(sql, [key]).fetchone() if result: - result = string_to_colors(result[0]) + result = bytes_to_colors(result[0]) return result else: raise KeyError(key) @@ -56,7 +56,7 @@ def __len__(self): return result[0][0] def __setitem__(self, path_str, blocks): - blocks = colors_to_string(blocks) + blocks = colors_to_bytes(blocks) if op.exists(path_str): mtime = int(os.stat(path_str).st_mtime) else: @@ -77,7 +77,7 @@ def create_tables(): logging.debug("Creating picture cache tables.") self.con.execute("drop table if exists pictures") self.con.execute("drop index if exists idx_path") - self.con.execute("create table pictures(path TEXT, mtime INTEGER, blocks TEXT)") + self.con.execute("create table pictures(path TEXT, mtime INTEGER, blocks BLOB)") self.con.execute("create index idx_path on pictures (path)") self.con = sqlite.connect(self.dbname, isolation_level=None) @@ -120,7 +120,7 @@ def get_id(self, path): def get_multiple(self, rowids): sql = "select rowid, blocks from pictures where rowid in (%s)" % ",".join(map(str, rowids)) cur = self.con.execute(sql) - return ((rowid, string_to_colors(blocks)) for rowid, blocks in cur) + return ((rowid, bytes_to_colors(blocks)) for rowid, blocks in cur) def purge_outdated(self): """Go through the cache and purge outdated records. diff --git a/core/pe/matchblock.py b/core/pe/matchblock.py index 447d8ae7..a98a48cd 100644 --- a/core/pe/matchblock.py +++ b/core/pe/matchblock.py @@ -27,7 +27,7 @@ # to files in other chunks. So chunkifying doesn't save us any actual comparison, but the advantage # is that instead of reading blocks from disk number_of_files**2 times, we read it # number_of_files*number_of_chunks times. -# Determining the right chunk size is tricky, bceause if it's too big, too many blocks will be in +# Determining the right chunk size is tricky, because if it's too big, too many blocks will be in # memory at the same time and we might end up with memory trashing, which is awfully slow. So, # because our *real* bottleneck is CPU, the chunk size must simply be enough so that the CPU isn't # starved by Disk IOs. diff --git a/core/pe/modules/cache.c b/core/pe/modules/cache.c index 1ebb611f..b975a2e8 100644 --- a/core/pe/modules/cache.c +++ b/core/pe/modules/cache.c @@ -9,51 +9,31 @@ #include "common.h" -/* I know that there strtol out there, but it requires a pointer to - * a char, which would in turn require me to buffer my chars around, - * making the whole process slower. - */ -static long -xchar_to_long(char c) -{ - if ((c >= 48) && (c <= 57)) { /* 0-9 */ - return c - 48; - } - else if ((c >= 65) && (c <= 70)) { /* A-F */ - return c - 55; - } - else if ((c >= 97) && (c <= 102)) { /* a-f */ - return c - 87; - } - return 0; -} - static PyObject* -cache_string_to_colors(PyObject *self, PyObject *args) +cache_bytes_to_colors(PyObject *self, PyObject *args) { - char *s; - Py_ssize_t char_count, color_count, i; + char *y; + Py_ssize_t char_count, i, color_count; PyObject *result; - - if (!PyArg_ParseTuple(args, "s#", &s, &char_count)) { + unsigned long r, g, b; + Py_ssize_t ci; + PyObject *color_tuple; + + if (!PyArg_ParseTuple(args, "y#", &y, &char_count)) { return NULL; } - color_count = (char_count / 6); + color_count = char_count / 3; result = PyList_New(color_count); if (result == NULL) { return NULL; } for (i=0; i Date: Wed, 28 Sep 2022 07:05:46 -0500 Subject: [PATCH 12/56] fix: Add W503 to flake8 extend-ignore For some reason flake8 is now throwing W503, which should be disabled by default, adding to extend-ignore fixes it, so doing that for now. --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index e1cfc5bb..8596e66c 100644 --- a/tox.ini +++ b/tox.ini @@ -19,4 +19,4 @@ deps = exclude = .tox,env,build,cocoalib,cocoa,help,./qt/dg_rc.py,cocoa/run_template.py,./pkg max-line-length = 120 select = C,E,F,W,B,B950 -extend-ignore = E203, E501 +extend-ignore = E203, E501, W503 From c92041285641c3dcfb03717a1c6deab112917ef7 Mon Sep 17 00:00:00 2001 From: "Eugene San (eugenesan)" Date: Thu, 24 Nov 2022 13:53:27 -0700 Subject: [PATCH 13/56] Add webp image format support --- core/pe/photo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/pe/photo.py b/core/pe/photo.py index 04225500..128e3c1f 100644 --- a/core/pe/photo.py +++ b/core/pe/photo.py @@ -29,7 +29,7 @@ class Photo(fs.File): __slots__ = fs.File.__slots__ + tuple(INITIAL_INFO.keys()) # These extensions are supported on all platforms - HANDLED_EXTS = {"png", "jpg", "jpeg", "gif", "bmp", "tiff", "tif"} + HANDLED_EXTS = {"png", "jpg", "jpeg", "gif", "bmp", "tiff", "tif", "webp"} def _plat_get_dimensions(self): raise NotImplementedError() From e30a1354511b781fc603457685fac2adc18b4c6c Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Thu, 5 Jan 2023 23:01:16 -0600 Subject: [PATCH 14/56] feat: Add additional scan time options - Add option to include file existence check at end of scan, speeds up end of scan operation time considerably, however if user has removed or moved files since starting a scan there could be later errors when interacting with results. Defaults to existing behavior of including the check, until it can be verified later dialogs and actions handle non-existent items better. - Add option to ignore differences in mtime when checking hash cache. Option is present in advanced tab of preferences. Closes #1022. - Regenerate pot files for translations --- core/app.py | 7 ++++++- core/fs.py | 12 +++++++++--- core/scanner.py | 4 +++- locale/core.pot | 40 ++++++++++++++++++++-------------------- locale/ui.pot | 14 ++++++++++++++ qt/app.py | 2 ++ qt/preferences.py | 6 ++++++ qt/preferences_dialog.py | 27 ++++++++++++++++++++++++++- 8 files changed, 86 insertions(+), 26 deletions(-) diff --git a/core/app.py b/core/app.py index 0d30680b..9a907aed 100644 --- a/core/app.py +++ b/core/app.py @@ -154,6 +154,8 @@ def __init__(self, view, portable=False): "ignore_hardlink_matches": False, "copymove_dest_type": DestType.RELATIVE, "picture_cache_type": self.PICTURE_CACHE_TYPE, + "include_exists_check": True, + "rehash_ignore_mtime": False, } self.selected_dupes = [] self.details_panel = DetailsPanel(self) @@ -555,7 +557,9 @@ def invoke_custom_command(self): # a workaround to make the damn thing work. exepath, args = match.groups() path, exename = op.split(exepath) - p = subprocess.Popen(exename + args, shell=True, cwd=path, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + p = subprocess.Popen( + exename + args, shell=True, cwd=path, stdout=subprocess.PIPE, stderr=subprocess.STDOUT + ) output = p.stdout.read() logging.info("Custom command %s %s: %s", exename, args, output) else: @@ -792,6 +796,7 @@ def start_scanning(self, profile_scan=False): Scans folders selected in :attr:`directories` and put the results in :attr:`results` """ scanner = self.SCANNER_CLASS() + fs.filesdb.ignore_mtime = self.options["rehash_ignore_mtime"] is True if not self.directories.has_any_file(): self.view.show_message(tr("The selected directories contain no scannable file.")) return diff --git a/core/fs.py b/core/fs.py index cc812238..647a5cfd 100644 --- a/core/fs.py +++ b/core/fs.py @@ -100,11 +100,14 @@ class FilesDB: create_table_query = "CREATE TABLE IF NOT EXISTS files (path TEXT PRIMARY KEY, size INTEGER, mtime_ns INTEGER, entry_dt DATETIME, digest BLOB, digest_partial BLOB, digest_samples BLOB)" drop_table_query = "DROP TABLE IF EXISTS files;" select_query = "SELECT {key} FROM files WHERE path=:path AND size=:size and mtime_ns=:mtime_ns" + select_query_ignore_mtime = "SELECT {key} FROM files WHERE path=:path AND size=:size" insert_query = """ INSERT INTO files (path, size, mtime_ns, entry_dt, {key}) VALUES (:path, :size, :mtime_ns, datetime('now'), :value) ON CONFLICT(path) DO UPDATE SET size=:size, mtime_ns=:mtime_ns, entry_dt=datetime('now'), {key}=:value; """ + ignore_mtime = False + def __init__(self): self.conn = None self.cur = None @@ -146,9 +149,12 @@ def get(self, path: Path, key: str) -> Union[bytes, None]: mtime_ns = stat.st_mtime_ns try: with self.lock: - self.cur.execute( - self.select_query.format(key=key), {"path": str(path), "size": size, "mtime_ns": mtime_ns} - ) + if self.ignore_mtime: + self.cur.execute(self.select_query_ignore_mtime.format(key=key), {"path": str(path), "size": size}) + else: + self.cur.execute( + self.select_query.format(key=key), {"path": str(path), "size": size, "mtime_ns": mtime_ns} + ) result = self.cur.fetchone() if result: diff --git a/core/scanner.py b/core/scanner.py index 639b1be9..72550497 100644 --- a/core/scanner.py +++ b/core/scanner.py @@ -171,7 +171,8 @@ def get_dupe_groups(self, files, ignore_list=None, j=job.nulljob): matches = [m for m in matches if m.first.path not in toremove or m.second.path not in toremove] if not self.mix_file_kind: matches = [m for m in matches if get_file_ext(m.first.name) == get_file_ext(m.second.name)] - matches = [m for m in matches if m.first.path.exists() and m.second.path.exists()] + if self.include_exists_check: + matches = [m for m in matches if m.first.path.exists() and m.second.path.exists()] matches = [m for m in matches if not (m.first.is_ref and m.second.is_ref)] if ignore_list: matches = [m for m in matches if not ignore_list.are_ignored(str(m.first.path), str(m.second.path))] @@ -212,3 +213,4 @@ def get_dupe_groups(self, files, ignore_list=None, j=job.nulljob): large_size_threshold = 0 big_file_size_threshold = 0 word_weighting = False + include_exists_check = True diff --git a/locale/core.pot b/locale/core.pot index a643ee59..622ec726 100644 --- a/locale/core.pot +++ b/locale/core.pot @@ -36,83 +36,83 @@ msgstr "" msgid "Sending to Trash" msgstr "" -#: core\app.py:291 +#: core\app.py:293 msgid "A previous action is still hanging in there. You can't start a new one yet. Wait a few seconds, then try again." msgstr "" -#: core\app.py:302 +#: core\app.py:304 msgid "No duplicates found." msgstr "" -#: core\app.py:317 +#: core\app.py:319 msgid "All marked files were copied successfully." msgstr "" -#: core\app.py:319 +#: core\app.py:321 msgid "All marked files were moved successfully." msgstr "" -#: core\app.py:321 +#: core\app.py:323 msgid "All marked files were deleted successfully." msgstr "" -#: core\app.py:323 +#: core\app.py:325 msgid "All marked files were successfully sent to Trash." msgstr "" -#: core\app.py:328 +#: core\app.py:330 msgid "Could not load file: {}" msgstr "" -#: core\app.py:384 +#: core\app.py:386 msgid "'{}' already is in the list." msgstr "" -#: core\app.py:386 +#: core\app.py:388 msgid "'{}' does not exist." msgstr "" -#: core\app.py:394 +#: core\app.py:396 msgid "All selected %d matches are going to be ignored in all subsequent scans. Continue?" msgstr "" -#: core\app.py:471 +#: core\app.py:473 msgid "Select a directory to copy marked files to" msgstr "" -#: core\app.py:473 +#: core\app.py:475 msgid "Select a directory to move marked files to" msgstr "" -#: core\app.py:512 +#: core\app.py:514 msgid "Select a destination for your exported CSV" msgstr "" -#: core\app.py:518 core\app.py:773 core\app.py:783 +#: core\app.py:520 core\app.py:781 core\app.py:791 msgid "Couldn't write to file: {}" msgstr "" -#: core\app.py:541 +#: core\app.py:543 msgid "You have no custom command set up. Set it up in your preferences." msgstr "" -#: core\app.py:697 core\app.py:709 +#: core\app.py:705 core\app.py:717 msgid "You are about to remove %d files from results. Continue?" msgstr "" -#: core\app.py:745 +#: core\app.py:753 msgid "{} duplicate groups were changed by the re-prioritization." msgstr "" -#: core\app.py:792 +#: core\app.py:801 msgid "The selected directories contain no scannable file." msgstr "" -#: core\app.py:808 +#: core\app.py:817 msgid "Collecting files to scan" msgstr "" -#: core\app.py:858 +#: core\app.py:867 msgid "%s (%d discarded)" msgstr "" diff --git a/locale/ui.pot b/locale/ui.pot index 642c8d43..f820a13f 100644 --- a/locale/ui.pot +++ b/locale/ui.pot @@ -1092,3 +1092,17 @@ msgstr "" #: qt\search_edit.py:78 msgid "Search..." msgstr "" + +#: qt\preferences_dialog.py:219 +msgid "" +"These options are for advanced users or for very specific situations, most " +"users should not have to modify these." +msgstr "" + +#: qt\preferences_dialog.py:225 +msgid "Include existence check after scan completion" +msgstr "" + +#: qt\preferences_dialog.py:227 +msgid "Ignore difference in mtime when loading cached digests" +msgstr "" diff --git a/qt/app.py b/qt/app.py index 27ac6ed5..d0533743 100644 --- a/qt/app.py +++ b/qt/app.py @@ -193,6 +193,8 @@ def _update_options(self): self.model.options["scanned_tags"] = scanned_tags self.model.options["match_scaled"] = self.prefs.match_scaled self.model.options["picture_cache_type"] = self.prefs.picture_cache_type + self.model.options["include_exists_check"] = self.prefs.include_exists_check + self.model.options["rehash_ignore_mtime"] = self.prefs.rehash_ignore_mtime if self.details_dialog: self.details_dialog.update_options() diff --git a/qt/preferences.py b/qt/preferences.py index d0b84820..0c4cb651 100644 --- a/qt/preferences.py +++ b/qt/preferences.py @@ -161,6 +161,8 @@ def _load_values(self, settings): self.ignore_hardlink_matches = get("IgnoreHardlinkMatches", self.ignore_hardlink_matches) self.use_regexp = get("UseRegexp", self.use_regexp) self.remove_empty_folders = get("RemoveEmptyFolders", self.remove_empty_folders) + self.rehash_ignore_mtime = get("RehashIgnoreMTime", self.rehash_ignore_mtime) + self.include_exists_check = get("IncludeExistsCheck", self.include_exists_check) self.debug_mode = get("DebugMode", self.debug_mode) self.profile_scan = get("ProfileScan", self.profile_scan) self.destination_type = get("DestinationType", self.destination_type) @@ -231,6 +233,8 @@ def reset(self): self.use_regexp = False self.ignore_hardlink_matches = False self.remove_empty_folders = False + self.rehash_ignore_mtime = False + self.include_exists_check = True self.debug_mode = False self.profile_scan = False self.destination_type = 1 @@ -283,6 +287,8 @@ def _save_values(self, settings): set_("IgnoreHardlinkMatches", self.ignore_hardlink_matches) set_("UseRegexp", self.use_regexp) set_("RemoveEmptyFolders", self.remove_empty_folders) + set_("RehashIgnoreMTime", self.rehash_ignore_mtime) + set_("IncludeExistsCheck", self.include_exists_check) set_("DebugMode", self.debug_mode) set_("ProfileScan", self.profile_scan) set_("DestinationType", self.destination_type) diff --git a/qt/preferences_dialog.py b/qt/preferences_dialog.py index caa6eb00..0182a4ec 100644 --- a/qt/preferences_dialog.py +++ b/qt/preferences_dialog.py @@ -47,8 +47,9 @@ class Sections(Flag): GENERAL = auto() DISPLAY = auto() + ADVANCED = auto() DEBUG = auto() - ALL = GENERAL | DISPLAY | DEBUG + ALL = GENERAL | DISPLAY | ADVANCED | DEBUG class PreferencesDialogBase(QDialog): @@ -213,6 +214,19 @@ def _setupDisplayPage(self): details_groupbox.setLayout(self.details_groupbox_layout) self.displayVLayout.addWidget(details_groupbox) + def _setup_advanced_page(self): + tab_label = QLabel( + tr( + "These options are for advanced users or for very specific situations, most users should not have to modify these." + ), + wordWrap=True, + ) + self.advanced_vlayout.addWidget(tab_label) + self._setupAddCheckbox("include_exists_check_box", tr("Include existence check after scan completion")) + self.advanced_vlayout.addWidget(self.include_exists_check_box) + self._setupAddCheckbox("rehash_ignore_mtime_box", tr("Ignore difference in mtime when loading cached digests")) + self.advanced_vlayout.addWidget(self.rehash_ignore_mtime_box) + def _setupDebugPage(self): self._setupAddCheckbox("debugModeBox", tr("Debug mode (restart required)")) self._setupAddCheckbox("profile_scan_box", tr("Profile scan operation")) @@ -244,16 +258,20 @@ def _setupUi(self): self.tabwidget = QTabWidget() self.page_general = QWidget() self.page_display = QWidget() + self.page_advanced = QWidget() self.page_debug = QWidget() self.widgetsVLayout = QVBoxLayout() self.page_general.setLayout(self.widgetsVLayout) self.displayVLayout = QVBoxLayout() self.displayVLayout.setSpacing(5) # arbitrary value, might conflict with style self.page_display.setLayout(self.displayVLayout) + self.advanced_vlayout = QVBoxLayout() + self.page_advanced.setLayout(self.advanced_vlayout) self.debugVLayout = QVBoxLayout() self.page_debug.setLayout(self.debugVLayout) self._setupPreferenceWidgets() self._setupDisplayPage() + self._setup_advanced_page() self._setupDebugPage() # self.mainVLayout.addLayout(self.widgetsVLayout) self.buttonBox = QDialogButtonBox(self) @@ -265,9 +283,11 @@ def _setupUi(self): self.layout().setSizeConstraint(QLayout.SetFixedSize) self.tabwidget.addTab(self.page_general, tr("General")) self.tabwidget.addTab(self.page_display, tr("Display")) + self.tabwidget.addTab(self.page_advanced, tr("Advanced")) self.tabwidget.addTab(self.page_debug, tr("Debug")) self.displayVLayout.addStretch(0) self.widgetsVLayout.addStretch(0) + self.advanced_vlayout.addStretch(0) self.debugVLayout.addStretch(0) def _load(self, prefs, setchecked, section): @@ -318,6 +338,9 @@ def setchecked(cb, b): except KeyError: selected_lang = self.supportedLanguages["en"] self.languageComboBox.setCurrentText(selected_lang) + if section & Sections.ADVANCED: + setchecked(self.rehash_ignore_mtime_box, prefs.rehash_ignore_mtime) + setchecked(self.include_exists_check_box, prefs.include_exists_check) if section & Sections.DEBUG: setchecked(self.debugModeBox, prefs.debug_mode) setchecked(self.profile_scan_box, prefs.profile_scan) @@ -334,6 +357,8 @@ def ischecked(cb): prefs.use_regexp = ischecked(self.useRegexpBox) prefs.remove_empty_folders = ischecked(self.removeEmptyFoldersBox) prefs.ignore_hardlink_matches = ischecked(self.ignoreHardlinkMatches) + prefs.rehash_ignore_mtime = ischecked(self.rehash_ignore_mtime_box) + prefs.include_exists_check = ischecked(self.include_exists_check_box) prefs.debug_mode = ischecked(self.debugModeBox) prefs.profile_scan = ischecked(self.profile_scan_box) prefs.reference_bold_font = ischecked(self.reference_bold_font) From 091cae0cc69f00fb80f667e1e9a48a9bfe4d94fa Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Fri, 6 Jan 2023 00:06:55 -0600 Subject: [PATCH 15/56] feat: Add confirmation dialog when canceling job - Implement a confirmation dialog for cancellation of jobs, required changing from QProgressDialog to QDialog to keep cleaner. - Update ui translation source file Close #1033, #515 --- locale/ui.pot | 8 ++++++++ qt/progress_window.py | 47 ++++++++++++++++++++++++++++++++----------- 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/locale/ui.pot b/locale/ui.pot index f820a13f..2f7e134d 100644 --- a/locale/ui.pot +++ b/locale/ui.pot @@ -1106,3 +1106,11 @@ msgstr "" #: qt\preferences_dialog.py:227 msgid "Ignore difference in mtime when loading cached digests" msgstr "" + +#: qt\progress_window.py:64 +msgid "Cancel?" +msgstr "" + +#: qt\progress_window.py:65 +msgid "Are you sure you want to cancel? All progress will be lost." +msgstr "" diff --git a/qt/progress_window.py b/qt/progress_window.py index c28a063f..ad67dd03 100644 --- a/qt/progress_window.py +++ b/qt/progress_window.py @@ -5,7 +5,7 @@ # http://www.gnu.org/licenses/gpl-3.0.html from PyQt5.QtCore import Qt, QTimer -from PyQt5.QtWidgets import QProgressDialog +from PyQt5.QtWidgets import QDialog, QMessageBox, QVBoxLayout, QLabel, QProgressBar, QPushButton from hscommon.trans import tr @@ -25,37 +25,60 @@ def __init__(self, parent, model): def refresh(self): # Labels if self._window is not None: self._window.setWindowTitle(self.model.jobdesc_textfield.text) - self._window.setLabelText(self.model.progressdesc_textfield.text) + self._label.setText(self.model.progressdesc_textfield.text) def set_progress(self, last_progress): if self._window is not None: if last_progress < 0: - self._window.setRange(0, 0) + self._progress_bar.setRange(0, 0) else: - self._window.setRange(0, 100) - self._window.setValue(last_progress) + self._progress_bar.setRange(0, 100) + self._progress_bar.setValue(last_progress) def show(self): flags = Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowSystemMenuHint - self._window = QProgressDialog("", tr("Cancel"), 0, 100, self.parent, flags) + self._window = QDialog(self.parent, flags) + self._setup_ui() self._window.setModal(True) - self._window.setAutoReset(False) - self._window.setAutoClose(False) self._timer = QTimer(self._window) self._timer.timeout.connect(self.model.pulse) self._window.show() - self._window.canceled.connect(self.model.cancel) self._timer.start(500) + def _setup_ui(self): + self._window.setWindowTitle(tr("Cancel")) + vertical_layout = QVBoxLayout(self._window) + self._label = QLabel("", self._window) + vertical_layout.addWidget(self._label) + self._progress_bar = QProgressBar(self._window) + self._progress_bar.setRange(0, 100) + vertical_layout.addWidget(self._progress_bar) + self._cancel_button = QPushButton(tr("Cancel"), self._window) + self._cancel_button.clicked.connect(self.cancel) + vertical_layout.addWidget(self._cancel_button) + + def cancel(self): + if self._window is not None: + confirm_dialog = QMessageBox( + QMessageBox.Icon.Question, + tr("Cancel?"), + tr("Are you sure you want to cancel? All progress will be lost."), + QMessageBox.StandardButton.No | QMessageBox.StandardButton.Yes, + self._window, + ) + confirm_dialog.setDefaultButton(QMessageBox.StandardButton.No) + result = confirm_dialog.exec_() + if result != QMessageBox.StandardButton.Yes: + return + self.close() + def close(self): # it seems it is possible for close to be called without a corresponding # show, only perform a close if there is a window to close if self._window is not None: self._timer.stop() del self._timer - # For some weird reason, canceled() signal is sent upon close, whether the user canceled - # or not. If we don't want a false cancellation, we have to disconnect it. - self._window.canceled.disconnect() self._window.close() self._window.setParent(None) self._window = None + self.model.cancel() From 83f5e8042794ed48c7170c6e0362c3c4baaa3131 Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Fri, 6 Jan 2023 00:35:23 -0600 Subject: [PATCH 16/56] feat: Remove shelve picture cache - Remove shelve picture cache as it has had a fair number of historical issues. Original issue for which it was added should be long resolved. Additionally this allows additional consolidation of the various cache code and potentially dbs in the future. - Remove all related preferences and related code for changing cache backend between sqlite and shelve. --- core/app.py | 6 +- core/pe/cache_shelve.py | 141 ------------------------------------ core/pe/matchblock.py | 10 +-- core/tests/cache_test.py | 6 -- qt/app.py | 1 - qt/pe/preferences_dialog.py | 11 +-- qt/preferences.py | 3 - 7 files changed, 4 insertions(+), 174 deletions(-) delete mode 100644 core/pe/cache_shelve.py diff --git a/core/app.py b/core/app.py index 9a907aed..7cde6317 100644 --- a/core/app.py +++ b/core/app.py @@ -126,8 +126,6 @@ class DupeGuru(Broadcaster): NAME = PROMPT_NAME = "dupeGuru" - PICTURE_CACHE_TYPE = "sqlite" # set to 'shelve' for a ShelveCache - def __init__(self, view, portable=False): if view.get_default(DEBUG_MODE_PREFERENCE): logging.getLogger().setLevel(logging.DEBUG) @@ -153,7 +151,6 @@ def __init__(self, view, portable=False): "clean_empty_dirs": False, "ignore_hardlink_matches": False, "copymove_dest_type": DestType.RELATIVE, - "picture_cache_type": self.PICTURE_CACHE_TYPE, "include_exists_check": True, "rehash_ignore_mtime": False, } @@ -185,8 +182,7 @@ def _recreate_result_table(self): self.view.create_results_window() def _get_picture_cache_path(self): - cache_type = self.options["picture_cache_type"] - cache_name = "cached_pictures.shelve" if cache_type == "shelve" else "cached_pictures.db" + cache_name = "cached_pictures.db" return op.join(self.appdata, cache_name) def _get_dupe_sort_key(self, dupe, get_group, key, delta): diff --git a/core/pe/cache_shelve.py b/core/pe/cache_shelve.py deleted file mode 100644 index 57f42775..00000000 --- a/core/pe/cache_shelve.py +++ /dev/null @@ -1,141 +0,0 @@ -# Copyright 2016 Virgil Dupras -# -# This software is licensed under the "GPLv3" License as described in the "LICENSE" file, -# which should be included with this package. The terms are also available at -# http://www.gnu.org/licenses/gpl-3.0.html - -import os -import os.path as op -import shelve -import tempfile -from collections import namedtuple - -from core.pe.cache import string_to_colors, colors_to_string - - -def wrap_path(path): - return f"path:{path}" - - -def unwrap_path(key): - return key[5:] - - -def wrap_id(path): - return f"id:{path}" - - -def unwrap_id(key): - return int(key[3:]) - - -CacheRow = namedtuple("CacheRow", "id path blocks mtime") - - -class ShelveCache: - """A class to cache picture blocks in a shelve backend.""" - - def __init__(self, db=None, readonly=False): - self.istmp = db is None - if self.istmp: - self.dtmp = tempfile.mkdtemp() - self.ftmp = db = op.join(self.dtmp, "tmpdb") - flag = "r" if readonly else "c" - self.shelve = shelve.open(db, flag) - self.maxid = self._compute_maxid() - - def __contains__(self, key): - return wrap_path(key) in self.shelve - - def __delitem__(self, key): - row = self.shelve[wrap_path(key)] - del self.shelve[wrap_path(key)] - del self.shelve[wrap_id(row.id)] - - def __getitem__(self, key): - if isinstance(key, int): - skey = self.shelve[wrap_id(key)] - else: - skey = wrap_path(key) - return string_to_colors(self.shelve[skey].blocks) - - def __iter__(self): - return (unwrap_path(k) for k in self.shelve if k.startswith("path:")) - - def __len__(self): - return sum(1 for k in self.shelve if k.startswith("path:")) - - def __setitem__(self, path_str, blocks): - blocks = colors_to_string(blocks) - if op.exists(path_str): - mtime = int(os.stat(path_str).st_mtime) - else: - mtime = 0 - if path_str in self: - rowid = self.shelve[wrap_path(path_str)].id - else: - rowid = self._get_new_id() - row = CacheRow(rowid, path_str, blocks, mtime) - self.shelve[wrap_path(path_str)] = row - self.shelve[wrap_id(rowid)] = wrap_path(path_str) - - def _compute_maxid(self): - return max((unwrap_id(k) for k in self.shelve if k.startswith("id:")), default=1) - - def _get_new_id(self): - self.maxid += 1 - return self.maxid - - def clear(self): - self.shelve.clear() - - def close(self): - if self.shelve is not None: - self.shelve.close() - if self.istmp: - os.remove(self.ftmp) - os.rmdir(self.dtmp) - self.shelve = None - - def filter(self, func): - to_delete = [key for key in self if not func(key)] - for key in to_delete: - del self[key] - - def get_id(self, path): - if path in self: - return self.shelve[wrap_path(path)].id - else: - raise ValueError(path) - - def get_multiple(self, rowids): - for rowid in rowids: - try: - skey = self.shelve[wrap_id(rowid)] - except KeyError: - continue - yield (rowid, string_to_colors(self.shelve[skey].blocks)) - - def purge_outdated(self): - """Go through the cache and purge outdated records. - - A record is outdated if the picture doesn't exist or if its mtime is greater than the one in - the db. - """ - todelete = [] - for path in self: - row = self.shelve[wrap_path(path)] - if row.mtime and op.exists(path): - picture_mtime = os.stat(path).st_mtime - if int(picture_mtime) <= row.mtime: - # not outdated - continue - todelete.append(path) - for path in todelete: - try: - del self[path] - except KeyError: - # I have no idea why a KeyError sometimes happen, but it does, as we can see in - # #402 and #439. I don't think it hurts to silently ignore the error, so that's - # what we do - pass diff --git a/core/pe/matchblock.py b/core/pe/matchblock.py index 447d8ae7..bc203175 100644 --- a/core/pe/matchblock.py +++ b/core/pe/matchblock.py @@ -16,6 +16,7 @@ from core.engine import Match from core.pe.block import avgdiff, DifferentBlockCountError, NoBlocksError +from core.pe.cache_sqlite import SqliteCache # OPTIMIZATION NOTES: # The bottleneck of the matching phase is CPU, which is why we use multiprocessing. However, another @@ -50,14 +51,7 @@ def get_cache(cache_path, readonly=False): - if cache_path.endswith("shelve"): - from core.pe.cache_shelve import ShelveCache - - return ShelveCache(cache_path, readonly=readonly) - else: - from core.pe.cache_sqlite import SqliteCache - - return SqliteCache(cache_path, readonly=readonly) + return SqliteCache(cache_path, readonly=readonly) def prepare_pictures(pictures, cache_path, with_dimensions, j=job.nulljob): diff --git a/core/tests/cache_test.py b/core/tests/cache_test.py index 7b11c02c..b47cb998 100644 --- a/core/tests/cache_test.py +++ b/core/tests/cache_test.py @@ -12,7 +12,6 @@ try: from core.pe.cache import colors_to_string, string_to_colors from core.pe.cache_sqlite import SqliteCache - from core.pe.cache_shelve import ShelveCache except ImportError: skip("Can't import the cache module, probably hasn't been compiled.") @@ -133,11 +132,6 @@ def test_corrupted_db(self, tmpdir, monkeypatch): eq_(c["foo"], [(1, 2, 3)]) -class TestCaseShelveCache(BaseTestCaseCache): - def get_cache(self, dbname=None): - return ShelveCache(dbname) - - class TestCaseCacheSQLEscape: def get_cache(self): return SqliteCache() diff --git a/qt/app.py b/qt/app.py index d0533743..5e6271c0 100644 --- a/qt/app.py +++ b/qt/app.py @@ -192,7 +192,6 @@ def _update_options(self): scanned_tags.add("year") self.model.options["scanned_tags"] = scanned_tags self.model.options["match_scaled"] = self.prefs.match_scaled - self.model.options["picture_cache_type"] = self.prefs.picture_cache_type self.model.options["include_exists_check"] = self.prefs.include_exists_check self.model.options["rehash_ignore_mtime"] = self.prefs.rehash_ignore_mtime diff --git a/qt/pe/preferences_dialog.py b/qt/pe/preferences_dialog.py index 2ded026d..375cc779 100644 --- a/qt/pe/preferences_dialog.py +++ b/qt/pe/preferences_dialog.py @@ -4,11 +4,9 @@ # which should be included with this package. The terms are also available at # http://www.gnu.org/licenses/gpl-3.0.html -from PyQt5.QtWidgets import QFormLayout -from PyQt5.QtCore import Qt + from hscommon.trans import trget from hscommon.plat import ISLINUX -from qt.radio_box import RadioBox from core.scanner import ScanType from core.app import AppMode @@ -35,11 +33,6 @@ def _setupPreferenceWidgets(self): ) self.widgetsVLayout.addWidget(self.ignoreHardlinkMatches) - self.cacheTypeRadio = RadioBox(self, items=["Sqlite", "Shelve"], spread=False) - cache_form = QFormLayout() - cache_form.setLabelAlignment(Qt.AlignLeft) - cache_form.addRow(tr("Picture cache mode:"), self.cacheTypeRadio) - self.widgetsVLayout.addLayout(cache_form) self._setupBottomPart() def _setupDisplayPage(self): @@ -64,7 +57,6 @@ def _setupDisplayPage(self): def _load(self, prefs, setchecked, section): setchecked(self.matchScaledBox, prefs.match_scaled) - self.cacheTypeRadio.selected_index = 1 if prefs.picture_cache_type == "shelve" else 0 # Update UI state based on selected scan type scan_type = prefs.get_scan_type(AppMode.PICTURE) @@ -75,6 +67,5 @@ def _load(self, prefs, setchecked, section): def _save(self, prefs, ischecked): prefs.match_scaled = ischecked(self.matchScaledBox) - prefs.picture_cache_type = "shelve" if self.cacheTypeRadio.selected_index == 1 else "sqlite" prefs.details_dialog_override_theme_icons = ischecked(self.details_dialog_override_theme_icons) prefs.details_dialog_viewers_show_scrollbars = ischecked(self.details_dialog_viewers_show_scrollbars) diff --git a/qt/preferences.py b/qt/preferences.py index 0c4cb651..17ae3bf9 100644 --- a/qt/preferences.py +++ b/qt/preferences.py @@ -225,7 +225,6 @@ def _load_values(self, settings): self.scan_tag_genre = get("ScanTagGenre", self.scan_tag_genre) self.scan_tag_year = get("ScanTagYear", self.scan_tag_year) self.match_scaled = get("MatchScaled", self.match_scaled) - self.picture_cache_type = get("PictureCacheType", self.picture_cache_type) def reset(self): self.filter_hardness = 95 @@ -278,7 +277,6 @@ def reset(self): self.scan_tag_genre = False self.scan_tag_year = False self.match_scaled = False - self.picture_cache_type = "sqlite" def _save_values(self, settings): set_ = self.set_value @@ -332,7 +330,6 @@ def _save_values(self, settings): set_("ScanTagGenre", self.scan_tag_genre) set_("ScanTagYear", self.scan_tag_year) set_("MatchScaled", self.match_scaled) - set_("PictureCacheType", self.picture_cache_type) # scan_type is special because we save it immediately when we set it. def get_scan_type(self, app_mode): From 2dd2a801cc743105970a1145788adea1d1ffebe0 Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Mon, 9 Jan 2023 21:53:22 -0600 Subject: [PATCH 17/56] feat: Add pre-commit and commitlint --- .pre-commit-config.yaml | 22 ++++++++++++++++++++++ commitlint.config.js | 17 +++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 .pre-commit-config.yaml create mode 100644 commitlint.config.js diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..9633bb1c --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,22 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v2.3.0 + hooks: + - id: check-yaml + - id: check-toml + - id: end-of-file-fixer + - id: trailing-whitespace + - repo: https://github.com/psf/black + rev: 22.10.0 + hooks: + - id: black + - repo: https://github.com/PyCQA/flake8 + rev: 6.0.0 + hooks: + - id: flake8 + - repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook + rev: v9.3.0 + hooks: + - id: commitlint + stages: [commit-msg] + additional_dependencies: ["@commitlint/config-conventional"] diff --git a/commitlint.config.js b/commitlint.config.js new file mode 100644 index 00000000..5c9041ca --- /dev/null +++ b/commitlint.config.js @@ -0,0 +1,17 @@ +const Configuration = { + /* + * Resolve and load @commitlint/config-conventional from node_modules. + * Referenced packages must be installed + */ + extends: ['@commitlint/config-conventional'], + /* + * Any rules defined here will override rules from @commitlint/config-conventional + */ + rules: { + 'header-max-length': [2, 'always', 72], + 'subject-case': [2, 'always', 'sentence-case'], + 'scope-enum': [2, 'always'], + }, +}; + +module.exports = Configuration; From 6db2fa2be665742f5a4a32bdc149e272c563cafb Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Mon, 9 Jan 2023 22:35:12 -0600 Subject: [PATCH 18/56] fix: Correct flake8 config - Add exclude pattern for flake8 when running with pre-commit as it does not fully honor the exclude paths. - Cleanup exclude paths for flake8 in tox.ini - Re-enable line length check and correct three affected files --- .pre-commit-config.yaml | 1 + core/fs.py | 9 ++++++--- qt/exclude_list_dialog.py | 4 ++-- qt/preferences_dialog.py | 6 ++++-- tox.ini | 4 ++-- 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9633bb1c..364a9285 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,6 +14,7 @@ repos: rev: 6.0.0 hooks: - id: flake8 + exclude: ^(.tox|env|build|dist|help|qt/dg_rc.py|pkg).* - repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook rev: v9.3.0 hooks: diff --git a/core/fs.py b/core/fs.py index 647a5cfd..6b3c2864 100644 --- a/core/fs.py +++ b/core/fs.py @@ -97,12 +97,14 @@ class FilesDB: schema_version = 1 schema_version_description = "Changed from md5 to xxhash if available." - create_table_query = "CREATE TABLE IF NOT EXISTS files (path TEXT PRIMARY KEY, size INTEGER, mtime_ns INTEGER, entry_dt DATETIME, digest BLOB, digest_partial BLOB, digest_samples BLOB)" + create_table_query = """CREATE TABLE IF NOT EXISTS files (path TEXT PRIMARY KEY, size INTEGER, mtime_ns INTEGER, + entry_dt DATETIME, digest BLOB, digest_partial BLOB, digest_samples BLOB)""" drop_table_query = "DROP TABLE IF EXISTS files;" select_query = "SELECT {key} FROM files WHERE path=:path AND size=:size and mtime_ns=:mtime_ns" select_query_ignore_mtime = "SELECT {key} FROM files WHERE path=:path AND size=:size" insert_query = """ - INSERT INTO files (path, size, mtime_ns, entry_dt, {key}) VALUES (:path, :size, :mtime_ns, datetime('now'), :value) + INSERT INTO files (path, size, mtime_ns, entry_dt, {key}) + VALUES (:path, :size, :mtime_ns, datetime('now'), :value) ON CONFLICT(path) DO UPDATE SET size=:size, mtime_ns=:mtime_ns, entry_dt=datetime('now'), {key}=:value; """ @@ -153,7 +155,8 @@ def get(self, path: Path, key: str) -> Union[bytes, None]: self.cur.execute(self.select_query_ignore_mtime.format(key=key), {"path": str(path), "size": size}) else: self.cur.execute( - self.select_query.format(key=key), {"path": str(path), "size": size, "mtime_ns": mtime_ns} + self.select_query.format(key=key), + {"path": str(path), "size": size, "mtime_ns": mtime_ns}, ) result = self.cur.fetchone() diff --git a/qt/exclude_list_dialog.py b/qt/exclude_list_dialog.py index 7b5c9c64..43747b5d 100644 --- a/qt/exclude_list_dialog.py +++ b/qt/exclude_list_dialog.py @@ -165,8 +165,8 @@ def display_help_message(self): in the Directories tab if their name happens to match one of the selected regular expressions.
\ For each file collected, two tests are performed to determine whether or not to completely ignore it:
\
  • 1. Regular expressions with no path separator in them will be compared to the file name only.
  • -
  • 2. Regular expressions with at least one path separator in them will be compared to the full path to the file.

  • -Example: if you want to filter out .PNG files from the "My Pictures" directory only:
    \ +
  • 2. Regular expressions with at least one path separator in them will be compared to the full path to the file.
  • \ +
    Example: if you want to filter out .PNG files from the "My Pictures" directory only:
    \ .*My\\sPictures\\\\.*\\.png

    \ You can test the regular expression with the "test string" button after pasting a fake path in the test field:
    \ C:\\\\User\\My Pictures\\test.png

    diff --git a/qt/preferences_dialog.py b/qt/preferences_dialog.py index 0182a4ec..216d3f7e 100644 --- a/qt/preferences_dialog.py +++ b/qt/preferences_dialog.py @@ -146,7 +146,8 @@ def _setupDisplayPage(self): ) self.use_native_dialogs.setToolTip( tr( - "For actions such as file/folder selection use the OS native dialogs.\nSome native dialogs have limited functionality." + "For actions such as file/folder selection use the OS native dialogs.\n\ +Some native dialogs have limited functionality." ) ) layout.addWidget(self.use_native_dialogs) @@ -217,7 +218,8 @@ def _setupDisplayPage(self): def _setup_advanced_page(self): tab_label = QLabel( tr( - "These options are for advanced users or for very specific situations, most users should not have to modify these." + "These options are for advanced users or for very specific situations, \ +most users should not have to modify these." ), wordWrap=True, ) diff --git a/tox.ini b/tox.ini index 8596e66c..a9f8b827 100644 --- a/tox.ini +++ b/tox.ini @@ -16,7 +16,7 @@ deps = -r{toxinidir}/requirements-extra.txt [flake8] -exclude = .tox,env,build,cocoalib,cocoa,help,./qt/dg_rc.py,cocoa/run_template.py,./pkg +exclude = .tox,env*,build,help,qt/dg_rc.py,pkg max-line-length = 120 select = C,E,F,W,B,B950 -extend-ignore = E203, E501, W503 +extend-ignore = E203,W503 From 0cf6c9a1a247e749f9cbfef56ab7ac0e7a2fc014 Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Mon, 9 Jan 2023 22:44:10 -0600 Subject: [PATCH 19/56] ci: Update to include python 3.11 & pre-commit --- .github/workflows/default.yml | 45 +++++++++++------------------------ .sonarcloud.properties | 2 +- 2 files changed, 15 insertions(+), 32 deletions(-) diff --git a/.github/workflows/default.yml b/.github/workflows/default.yml index 413060a7..4395798e 100644 --- a/.github/workflows/default.yml +++ b/.github/workflows/default.yml @@ -9,43 +9,22 @@ on: branches: [master] jobs: - lint: + pre-commit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Set up Python 3.10 - uses: actions/setup-python@v2 + - uses: actions/checkout@v3 + - name: Set up Python 3.11 + uses: actions/setup-python@v4 with: - python-version: "3.10" - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements.txt -r requirements-extra.txt - - name: Lint with flake8 - run: | - flake8 . - format: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Set up Python 3.10 - uses: actions/setup-python@v2 - with: - python-version: "3.10" - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements.txt -r requirements-extra.txt - - name: Check format with black - run: | - black . + python-version: "3.11" + - uses: pre-commit/action@v3.0.0 test: - needs: [lint, format] + needs: [pre-commit] runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] - python-version: [3.7, 3.8, 3.9, "3.10"] + python-version: [3.7, 3.8, 3.9, "3.10", "3.11"] exclude: - os: macos-latest python-version: 3.7 @@ -53,17 +32,21 @@ jobs: python-version: 3.8 - os: macos-latest python-version: 3.9 + - os: macos-latest + python-version: "3.10" - os: windows-latest python-version: 3.7 - os: windows-latest python-version: 3.8 - os: windows-latest python-version: 3.9 + - os: windows-latest + python-version: "3.10" steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install dependencies diff --git a/.sonarcloud.properties b/.sonarcloud.properties index c8eecdf6..7a578887 100644 --- a/.sonarcloud.properties +++ b/.sonarcloud.properties @@ -1 +1 @@ -sonar.python.version=3.7, 3.8, 3.9, 3.10 \ No newline at end of file +sonar.python.version=3.7, 3.8, 3.9, 3.10, 3.11 From 46d1afb566c165fd59c58213cb0461a5bc09262b Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Mon, 9 Jan 2023 22:58:08 -0600 Subject: [PATCH 20/56] chore: Apply whitespace fixes from hooks - Remove trailing whitespace - Correct single newline at end of files (skip for json) - Update to formatting in a few places due to black --- .gitignore | 2 +- .pre-commit-config.yaml | 1 + .tx/config | 1 - LICENSE | 1 - Makefile | 8 +-- README.md | 2 +- Windows.md | 2 +- core/pe/modules/block.c | 2 +- core/pe/modules/block_osx.m | 64 +++++++++---------- core/pe/modules/cache.c | 18 +++--- core/pe/modules/common.c | 10 +-- core/pe/modules/common.h | 6 +- help/changelog | 18 +++--- help/de/faq.rst | 8 +-- help/de/folders.rst | 2 +- help/de/index.rst | 2 +- help/de/preferences.rst | 10 +-- help/de/reprioritize.rst | 2 +- help/de/results.rst | 2 +- help/en/developer/core/engine.rst | 8 +-- help/en/developer/core/gui/index.rst | 2 +- help/en/developer/core/index.rst | 2 +- help/en/developer/hscommon/gui/base.rst | 4 +- help/en/developer/hscommon/gui/column.rst | 10 +-- .../hscommon/gui/progress_window.rst | 7 +- .../hscommon/gui/selectable_list.rst | 10 +-- help/en/developer/hscommon/gui/table.rst | 10 +-- help/en/developer/hscommon/gui/text_field.rst | 4 +- help/en/developer/hscommon/gui/tree.rst | 9 ++- help/en/developer/hscommon/index.rst | 3 +- .../en/developer/hscommon/jobprogress/job.rst | 5 +- .../hscommon/jobprogress/performer.rst | 5 +- help/en/developer/index.rst | 2 +- help/en/faq.rst | 3 +- help/en/index.rst | 2 +- help/en/preferences.rst | 4 +- help/en/reprioritize.rst | 2 +- help/en/results.rst | 2 +- help/fr/faq.rst | 4 +- help/fr/folders.rst | 10 +-- help/fr/index.rst | 2 +- help/fr/preferences.rst | 10 +-- help/fr/reprioritize.rst | 2 +- help/fr/results.rst | 2 +- help/hy/faq.rst | 14 ++-- help/hy/index.rst | 2 +- help/hy/preferences.rst | 10 +-- help/hy/quick_start.rst | 1 - help/hy/reprioritize.rst | 3 +- help/hy/results.rst | 2 +- help/ru/faq.rst | 8 +-- help/ru/index.rst | 2 +- help/ru/preferences.rst | 10 +-- help/ru/quick_start.rst | 2 +- help/ru/reprioritize.rst | 2 +- help/ru/results.rst | 2 +- help/uk/faq.rst | 9 ++- help/uk/index.rst | 4 +- help/uk/preferences.rst | 8 +-- help/uk/quick_start.rst | 2 +- help/uk/reprioritize.rst | 2 +- help/uk/results.rst | 2 +- hscommon/LICENSE | 2 +- hscommon/trans.py | 3 +- locale/columns.pot | 1 - locale/core.pot | 1 - locale/ui.pot | 7 ++ macos.md | 14 ++-- pkg/debian/changelog | 1 - pkg/debian/source/format | 2 +- pyproject.toml | 2 +- qt/pe/modules/block.c | 2 +- qt/pe/photo.py | 5 +- requirements-extra.txt | 2 +- setup.cfg | 6 +- setup.nsi | 34 +++++----- win_version_info.temp | 2 +- 77 files changed, 226 insertions(+), 228 deletions(-) diff --git a/.gitignore b/.gitignore index 9ddb775b..46487bc4 100644 --- a/.gitignore +++ b/.gitignore @@ -108,4 +108,4 @@ cocoa/autogen *.waf* .lock-waf* -/tags \ No newline at end of file +/tags diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 364a9285..0b2318ff 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,6 +5,7 @@ repos: - id: check-yaml - id: check-toml - id: end-of-file-fixer + exclude: ".*.json" - id: trailing-whitespace - repo: https://github.com/psf/black rev: 22.10.0 diff --git a/.tx/config b/.tx/config index 4b94e1c3..db941c24 100644 --- a/.tx/config +++ b/.tx/config @@ -18,4 +18,3 @@ file_filter = locale//LC_MESSAGES/ui.po source_file = locale/ui.pot source_lang = en type = PO - diff --git a/LICENSE b/LICENSE index e963df82..94a04532 100644 --- a/LICENSE +++ b/LICENSE @@ -619,4 +619,3 @@ Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS - diff --git a/Makefile b/Makefile index 71be91cb..c6a1d6b7 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ PYRCC5 ?= pyrcc5 REQ_MINOR_VERSION = 7 PREFIX ?= /usr/local -# Window compatability via Msys2 +# Window compatability via Msys2 # - venv creates Scripts instead of bin # - compile generates .pyd instead of .so # - venv with --sytem-site-packages has issues on windows as well... @@ -12,7 +12,7 @@ PREFIX ?= /usr/local ifeq ($(shell ${PYTHON} -c "import platform; print(platform.system())"), Windows) BIN = Scripts SO = *.pyd - VENV_OPTIONS = + VENV_OPTIONS = else BIN = bin SO = *.so @@ -43,7 +43,7 @@ mofiles = $(patsubst %.po,%.mo,$(pofiles)) vpath %.po $(localedirs) vpath %.mo $(localedirs) -all: | env i18n modules qt/dg_rc.py +all: | env i18n modules qt/dg_rc.py @echo "Build complete! You can run dupeGuru with 'make run'" run: @@ -82,7 +82,7 @@ qt/dg_rc.py: qt/dg.qrc i18n: $(mofiles) %.mo: %.po - msgfmt -o $@ $< + msgfmt -o $@ $< modules: | env $(VENV_PYTHON) build.py --modules diff --git a/README.md b/README.md index 60194318..f574dee0 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ dupeGuru comes with a makefile that can be used to build and run: $ python run.py ### Generating Debian/Ubuntu package -To generate packages the extra requirements in requirements-extra.txt must be installed, the +To generate packages the extra requirements in requirements-extra.txt must be installed, the steps are as follows: $ cd diff --git a/Windows.md b/Windows.md index 13f60ae1..e54737b7 100644 --- a/Windows.md +++ b/Windows.md @@ -29,7 +29,7 @@ To build with a different python version 3.7 vs 3.8 or 32 bit vs 64 bit specify It is possible to build dupeGuru with the makefile on windows using a compatable POSIX environment. The following steps have been tested using [msys2][msys2]. Before running make: 1. Install msys2 or other POSIX environment 2. Install PyQt5 globally via pip -3. Use the respective console for msys2 it is `msys2 msys` +3. Use the respective console for msys2 it is `msys2 msys` Then the following execution of the makefile should work. Pass the correct value for PYTHON to the makefile if not on the path as python3. diff --git a/core/pe/modules/block.c b/core/pe/modules/block.c index 9db9f605..5690b2e1 100644 --- a/core/pe/modules/block.c +++ b/core/pe/modules/block.c @@ -245,4 +245,4 @@ PyObject *PyInit__block(void) { PyModule_AddObject(m, "DifferentBlockCountError", DifferentBlockCountError); return m; -} \ No newline at end of file +} diff --git a/core/pe/modules/block_osx.m b/core/pe/modules/block_osx.m index 9eb46d86..258de21f 100644 --- a/core/pe/modules/block_osx.m +++ b/core/pe/modules/block_osx.m @@ -2,8 +2,8 @@ * Created On: 2010-02-04 * Copyright 2015 Hardcoded Software (http://www.hardcoded.net) * - * This software is licensed under the "GPLv3" License as described in the "LICENSE" file, - * which should be included with this package. The terms are also available at + * This software is licensed under the "GPLv3" License as described in the "LICENSE" file, + * which should be included with this package. The terms are also available at * http://www.gnu.org/licenses/gpl-3.0.html **/ @@ -22,7 +22,7 @@ UInt8 *s; CFIndex size; CFStringRef result; - + if (PyUnicode_Check(pystring)) { encoded = PyUnicode_AsUTF8String(pystring); if (encoded == NULL) { @@ -32,7 +32,7 @@ encoded = pystring; Py_INCREF(encoded); } - + s = (UInt8*)PyBytes_AS_STRING(encoded); size = PyBytes_GET_SIZE(encoded); result = CFStringCreateWithBytes(NULL, s, size, kCFStringEncodingUTF8, FALSE); @@ -50,20 +50,20 @@ long width, height; PyObject *pwidth, *pheight; PyObject *result; - + width = 0; height = 0; if (!PyArg_ParseTuple(args, "O", &path)) { return NULL; } - + image_path = pystring2cfstring(path); if (image_path == NULL) { return PyErr_NoMemory(); } image_url = CFURLCreateWithFileSystemPath(NULL, image_path, kCFURLPOSIXPathStyle, FALSE); CFRelease(image_path); - + source = CGImageSourceCreateWithURL(image_url, NULL); CFRelease(image_url); if (source != NULL) { @@ -75,7 +75,7 @@ } CFRelease(source); } - + pwidth = PyLong_FromLong(width); if (pwidth == NULL) { return NULL; @@ -91,19 +91,19 @@ } static CGContextRef -MyCreateBitmapContext(int width, int height) +MyCreateBitmapContext(int width, int height) { CGContextRef context = NULL; CGColorSpaceRef colorSpace; void *bitmapData; int bitmapByteCount; int bitmapBytesPerRow; - + bitmapBytesPerRow = (width * 4); bitmapByteCount = (bitmapBytesPerRow * height); - + colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); - + // calloc() must be used to allocate bitmapData here because the buffer has to be zeroed. // If it's not zeroes, when images with transparency are drawn in the context, this buffer // will stay with undefined pixels, which means that two pictures with the same pixels will @@ -113,7 +113,7 @@ fprintf(stderr, "Memory not allocated!"); return NULL; } - + context = CGBitmapContextCreate(bitmapData, width, height, 8, bitmapBytesPerRow, colorSpace, (CGBitmapInfo)kCGImageAlphaNoneSkipLast); if (context== NULL) { @@ -128,7 +128,7 @@ static PyObject* getblock(unsigned char *imageData, int imageWidth, int imageHeight, int boxX, int boxY, int boxW, int boxH) { int i,j, totalR, totalG, totalB; - + totalR = totalG = totalB = 0; for(i=boxY; i 8) || (orientation < 0)) { orientation = 0; // simplifies checks later since we can only have values in 0-8 } - + image_path = pystring2cfstring(path); if (image_path == NULL) { return PyErr_NoMemory(); } image_url = CFURLCreateWithFileSystemPath(NULL, image_path, kCFURLPOSIXPathStyle, FALSE); CFRelease(image_path); - + source = CGImageSourceCreateWithURL(image_url, NULL); CFRelease(image_url); if (source == NULL) { @@ -187,8 +187,8 @@ CFRelease(source); return PyErr_NoMemory(); } - - + + width = image_width = CGImageGetWidth(image); height = image_height = CGImageGetHeight(image); if (orientation >= 5) { @@ -196,9 +196,9 @@ width = image_height; height = image_width; } - + CGContextRef context = MyCreateBitmapContext(width, height); - + if (orientation == 2) { // Flip X CGContextTranslateCTM(context, width, 0); @@ -207,7 +207,7 @@ else if (orientation == 3) { // Rot 180 CGContextTranslateCTM(context, width, height); - CGContextRotateCTM(context, RADIANS(180)); + CGContextRotateCTM(context, RADIANS(180)); } else if (orientation == 4) { // Flip Y @@ -242,21 +242,21 @@ CGContextDrawImage(context, myBoundingBox, image); unsigned char *bitmapData = CGBitmapContextGetData(context); CGContextRelease(context); - + CGImageRelease(image); CFRelease(source); if (bitmapData == NULL) { return PyErr_NoMemory(); } - + block_width = max(width/block_count, 1); block_height = max(height/block_count, 1); - + result = PyList_New(block_count * block_count); if (result == NULL) { return NULL; } - + for(i=0; i Alle Markieren**. .. only:: edition_me - + .. topic:: Ich möchte alle Stücke markieren, die mehr als 3 Sekunden von ihrer Referenz verschieden sind. Was kann ich tun? * Aktivieren Sie den :doc:`Nur Duplikate ` Modus. @@ -83,7 +83,7 @@ Häufig gestellte Fragen * Klicken Sie auf **Entferne Ausgewählte von den Ergebnissen**. .. topic:: Ich möchte meine Stücke mit der höchsten Bitrate zur Referenz machen. Was kann ich tun? - + * Aktivieren Sie den :doc:`Nur Duplikate ` Modus. * Aktivieren Sie den **Deltawerte** Modus. * Klicken Sie auf die "Bitrate" Spalte, um nach Bitrate zu sortieren. @@ -92,9 +92,9 @@ Häufig gestellte Fragen * Klicken Sie auf **Mache Ausgewählte zur Referenz**. .. topic:: Ich möchte nicht das [live] und [remix] Versionen meiner Stücke als Duplikate erkannt werden. Was kann ich tun? - + Ist Ihre Vergleichsschwelle niedrig genug, werden möglicherweise die live und remix Versionen in der Ergebnisliste landen. Das kann nicht verhindert werden, aber es gibt die Möglichkeit die Ergebnisse nach dem Scan zu entfernen, mittels dem Filter. Möchten Sie jedes Stück mit irgendetwas in eckigen Klammern [] im Dateinamen entfernen, so: - + * **Windows**: Klicken Sie auf **Aktionen --> Filter anwenden**, geben "[*]" ein und klicken OK. * **Mac OS X**: Geben Sie "[*]" in das "Filter" Feld der Werkzeugleiste ein. * Klicken Sie auf **Markieren --> Alle Markieren**. diff --git a/help/de/folders.rst b/help/de/folders.rst index 22103b24..5a0a7fe5 100644 --- a/help/de/folders.rst +++ b/help/de/folders.rst @@ -16,7 +16,7 @@ Jeder Ordner kann in einem von 3 Zuständen sein: * **Referenz:** Duplikate in diesem Ordner können **nicht** gelöscht werden. Dateien dieses Ordners können sich nur in der **Referenz** Position einer Duplikatgruppe befinden. Ist mehr als eine Datei des Referenzordners in derselben Duplikatgruppe, so wird nur Eine behalten. Die Anderen werden aus der Gruppe entfernt. * **Ausgeschlossen:** Dateien in diesem Verzeichnis sind nicht im Scan eingeschlossen. -Der Standardzustand eines Ordners ist natürlich **Normal**. Sie können den **Referenz** Zustand für Ordner nutzen, in denen auf keinen Fall eine Datei gelöscht werden soll. +Der Standardzustand eines Ordners ist natürlich **Normal**. Sie können den **Referenz** Zustand für Ordner nutzen, in denen auf keinen Fall eine Datei gelöscht werden soll. Wenn sie einen Zustand für ein Verzeichnis setzen, erben alle Unterordner automatisch diesen Zustand, es sei denn Sie ändern den Zustand der Unterordner explizit. diff --git a/help/de/index.rst b/help/de/index.rst index 7c1a5fc4..6c2d0396 100644 --- a/help/de/index.rst +++ b/help/de/index.rst @@ -21,7 +21,7 @@ Inhalte: .. toctree:: :maxdepth: 2 - + quick_start folders preferences diff --git a/help/de/preferences.rst b/help/de/preferences.rst index 301261b4..7115ae7e 100644 --- a/help/de/preferences.rst +++ b/help/de/preferences.rst @@ -4,9 +4,9 @@ Einstellungen .. only:: edition_se **Scan Typ:** Diese Option bestimmt nach welcher Eigenschaft die Dateien in einem Duplikate Scan verglichen werden. Wenn Sie **Dateiname** auswählen, wird dupeGuru jeden Dateinamen Wort für Wort vergleichen und, abhängig von den unteren Einstellungen, feststellen ob genügend Wörter übereinstimmen, um 2 Dateien als Duplikate zu betrachten. Wenn Sie **Inhalt** wählen, werden nur Dateien mit dem exakt gleichen Inhalt zusammenpassen. - + Der **Ordner** Scan Typ ist etwas speziell. Wird er ausgewählt, scannt dupeGuru nach doppelten Ordnern anstelle von Dateien. Um festzustellen ob 2 Ordner identisch sind, werden alle Datein im Ordner gescannt und wenn die Inhalte aller Dateien der Ordner übereinstimmen, werden die Ordner als Duplikate erkannt. - + **Filterempfindlichkeit:** Wenn Sie den **Dateiname** Scan Typ wählen, bestimmt diese Option wie ähnlich 2 Dateinamen für dupeGuru sein müssen, um Duplikate zu sein. Ist die Empfindlichkeit zum Beispiel 80, müssen 80% der Worte der 2 Dateinamen übereinstimmen. Um den Übereinstimmungsanteil herauszufinden, zählt dupeGuru zuerst die Gesamtzahl der Wörter **beider** Dateinamen, dann werden die gleichen Wörter gezählt (jedes Wort zählt als 2) und durch die Gesamtzahl der Wörter dividiert. Ist das Resultat größer oder gleich der Filterempfindlichkeit, haben wir ein Duplikat. Zum Beispiel, "a b c d" und "c d e" haben einen Übereinstimmungsanteil von 57 (4 gleiche Wörter, insgesamt 7 Wörter). .. only:: edition_me @@ -33,7 +33,7 @@ Einstellungen .. only:: edition_pe **Scan Typ:** Diese option bestimmt, welcher Scan Typ bei Ihren Bildern angewendet wird. Der **Inhalte** Scan Typ vergleicht den Inhalt der Bilder auf eine ungenaue Art und Weise (so werden nicht nur exakte Duplikate gefunden, sondern auch Ähnliche). Der **EXIF Zeitstempel** Scan Typ schaut auf die EXIF Metadaten der Bilder (wenn vorhanden) und erkennt Bilder die den Selben haben. Er ist viel schneller als der Inhalte Scan. **Warnung:** Veränderte Bilder behalten oft den selben EXIF Zeitstempel, also achten Sie auf Falschpositive bei der Nutzung dieses Scans. - + **Filterempfindlichkeit:** *Nur Inhalte Scan.* Je höher diese Einstellung, desto strenger ist der Filter (Mit anderen Worten, desto weniger Ergebnisse erhalten Sie). Die meisten Bilder der selben Qualität stimmen zu 100% überein, selbst wenn das Format anders ist (PNG und JPG zum Beispiel). Wie auch immer, wenn ein PNG mit einem JPG niederiger Qualität übereinstimmen soll, muss die Filterempfindlichkeit kleiner als 100 sein. Die Voreinstellung, 95, ist eine gute Wahl. **Bilder unterschiedlicher Abmessung gleich:** Wird diese Box gewählt, dürfen Bilder unterschiedlicher Abmessung in einer Duplikategruppe sein.. @@ -57,7 +57,7 @@ Auf jeden Fall behandelt dupeGuru Namenskonflikte indem es dem Ziel-Dateinamen e **Eigener Befehl:** Diese Einstellung bestimmt den Befehl der durch "Führe eigenen Befehl aus" ausgeführt wird. Sie können jede externe Anwendung durch diese Aktion aufrufen. Dies ist zum Beispiel hilfreich, wenn Sie eine gute diff-Anwendung installiert haben. Das Format des Befehls ist das Selbe wie in einer Befehlszeile, außer das 2 Platzhalter vorhanden sind: **%d** und **%r**. Diese Platzhalter werden durch den Pfad des markierten Duplikates (%d) und dem Pfad der Duplikatereferenz ersetzt (%r). - + Wenn der Pfad Ihrer ausführbaren Datei Leerzeichen enthält, so schließen sie ihn bitte mit "" Zeichen ein. Sie sollten auch Platzhalter mit den Zitatzeichen einschließen, denn es ist möglich, das die Pfade der Duplikate und Referenzen ebenfalls Leerzeichen enthalten. Hier ist ein Beispiel eines eigenen Befehls:: - + "C:\Program Files\SuperDiffProg\SuperDiffProg.exe" "%d" "%r" diff --git a/help/de/reprioritize.rst b/help/de/reprioritize.rst index ff37088b..eab22619 100644 --- a/help/de/reprioritize.rst +++ b/help/de/reprioritize.rst @@ -22,4 +22,4 @@ criterion is used and so on and so on. For example, if your arguments are "Size "Filename (Doesn't end with a number)", the reference file that will be picked in a group will be the biggest file, and if two or more files have the same size, the one that has a filename that doesn't end with a number will be used. When all criteria result in ties, the order in which dupes -previously were in the group will be used. \ No newline at end of file +previously were in the group will be used. diff --git a/help/de/results.rst b/help/de/results.rst index ccd3f948..4d086dee 100644 --- a/help/de/results.rst +++ b/help/de/results.rst @@ -98,4 +98,4 @@ Aktionen Menü * **Ausgewählte umbenennen:** Fragt nach einem neuen Namen und benennt die ausgewählte Datei um. .. todo:: Add Move and iPhoto/iTunes warning -.. todo:: Add "Deletion Options" section. \ No newline at end of file +.. todo:: Add "Deletion Options" section. diff --git a/help/en/developer/core/engine.rst b/help/en/developer/core/engine.rst index 3258c776..658111c3 100644 --- a/help/en/developer/core/engine.rst +++ b/help/en/developer/core/engine.rst @@ -2,12 +2,12 @@ core.engine =========== .. automodule:: core.engine - + .. autoclass:: Match - + .. autoclass:: Group :members: - + .. autofunction:: build_word_dict .. autofunction:: compare .. autofunction:: compare_fields @@ -16,7 +16,7 @@ core.engine .. autofunction:: get_groups .. autofunction:: merge_similar_words .. autofunction:: reduce_common_words - + .. _fields: Fields diff --git a/help/en/developer/core/gui/index.rst b/help/en/developer/core/gui/index.rst index 0298f4b9..74da0305 100644 --- a/help/en/developer/core/gui/index.rst +++ b/help/en/developer/core/gui/index.rst @@ -6,5 +6,5 @@ core.gui .. toctree:: :maxdepth: 2 - + deletion_options diff --git a/help/en/developer/core/index.rst b/help/en/developer/core/index.rst index 8c88e7e1..a861573e 100644 --- a/help/en/developer/core/index.rst +++ b/help/en/developer/core/index.rst @@ -3,7 +3,7 @@ core .. toctree:: :maxdepth: 2 - + app fs engine diff --git a/help/en/developer/hscommon/gui/base.rst b/help/en/developer/hscommon/gui/base.rst index 0a20b963..739cf367 100644 --- a/help/en/developer/hscommon/gui/base.rst +++ b/help/en/developer/hscommon/gui/base.rst @@ -4,9 +4,9 @@ hscommon.gui.base .. automodule:: hscommon.gui.base .. autosummary:: - + GUIObject - + .. autoclass:: GUIObject :members: :private-members: diff --git a/help/en/developer/hscommon/gui/column.rst b/help/en/developer/hscommon/gui/column.rst index 5780a19d..3b202b1f 100644 --- a/help/en/developer/hscommon/gui/column.rst +++ b/help/en/developer/hscommon/gui/column.rst @@ -4,22 +4,22 @@ hscommon.gui.column .. automodule:: hscommon.gui.column .. autosummary:: - + Columns Column ColumnsView PrefAccessInterface - + .. autoclass:: Columns :members: :private-members: - + .. autoclass:: Column :members: :private-members: - + .. autoclass:: ColumnsView :members: - + .. autoclass:: PrefAccessInterface :members: diff --git a/help/en/developer/hscommon/gui/progress_window.rst b/help/en/developer/hscommon/gui/progress_window.rst index 2453b705..f62a16d8 100644 --- a/help/en/developer/hscommon/gui/progress_window.rst +++ b/help/en/developer/hscommon/gui/progress_window.rst @@ -4,15 +4,14 @@ hscommon.gui.progress_window .. automodule:: hscommon.gui.progress_window .. autosummary:: - + ProgressWindow ProgressWindowView - + .. autoclass:: ProgressWindow :members: :private-members: - + .. autoclass:: ProgressWindowView :members: :private-members: - diff --git a/help/en/developer/hscommon/gui/selectable_list.rst b/help/en/developer/hscommon/gui/selectable_list.rst index e9d6c9c1..b0200386 100644 --- a/help/en/developer/hscommon/gui/selectable_list.rst +++ b/help/en/developer/hscommon/gui/selectable_list.rst @@ -4,23 +4,23 @@ hscommon.gui.selectable_list .. automodule:: hscommon.gui.selectable_list .. autosummary:: - + Selectable SelectableList GUISelectableList GUISelectableListView - + .. autoclass:: Selectable :members: :private-members: - + .. autoclass:: SelectableList :members: :private-members: - + .. autoclass:: GUISelectableList :members: :private-members: - + .. autoclass:: GUISelectableListView :members: diff --git a/help/en/developer/hscommon/gui/table.rst b/help/en/developer/hscommon/gui/table.rst index 6f539d04..d1ed1dd8 100644 --- a/help/en/developer/hscommon/gui/table.rst +++ b/help/en/developer/hscommon/gui/table.rst @@ -2,18 +2,18 @@ hscommon.gui.table ================== .. automodule:: hscommon.gui.table - + .. autosummary:: - + Table Row GUITable GUITableView - + .. autoclass:: Table :members: :private-members: - + .. autoclass:: Row :members: :private-members: @@ -21,6 +21,6 @@ hscommon.gui.table .. autoclass:: GUITable :members: :private-members: - + .. autoclass:: GUITableView :members: diff --git a/help/en/developer/hscommon/gui/text_field.rst b/help/en/developer/hscommon/gui/text_field.rst index 7f142bc0..1799af02 100644 --- a/help/en/developer/hscommon/gui/text_field.rst +++ b/help/en/developer/hscommon/gui/text_field.rst @@ -4,10 +4,10 @@ hscommon.gui.text_field .. automodule:: hscommon.gui.text_field .. autosummary:: - + TextField TextFieldView - + .. autoclass:: TextField :members: :private-members: diff --git a/help/en/developer/hscommon/gui/tree.rst b/help/en/developer/hscommon/gui/tree.rst index 1c1e02b2..fb2dd253 100644 --- a/help/en/developer/hscommon/gui/tree.rst +++ b/help/en/developer/hscommon/gui/tree.rst @@ -2,17 +2,16 @@ hscommon.gui.tree ================= .. automodule:: hscommon.gui.tree - + .. autosummary:: - + Tree Node - + .. autoclass:: Tree :members: :private-members: - + .. autoclass:: Node :members: :private-members: - diff --git a/help/en/developer/hscommon/index.rst b/help/en/developer/hscommon/index.rst index 4fc45f97..ee03fc6e 100644 --- a/help/en/developer/hscommon/index.rst +++ b/help/en/developer/hscommon/index.rst @@ -4,7 +4,7 @@ hscommon .. toctree:: :maxdepth: 2 :glob: - + build conflict desktop @@ -13,4 +13,3 @@ hscommon util jobprogress/* gui/* - diff --git a/help/en/developer/hscommon/jobprogress/job.rst b/help/en/developer/hscommon/jobprogress/job.rst index af1553f8..c60a9da4 100644 --- a/help/en/developer/hscommon/jobprogress/job.rst +++ b/help/en/developer/hscommon/jobprogress/job.rst @@ -4,14 +4,13 @@ hscommon.jobprogress.job .. automodule:: hscommon.jobprogress.job .. autosummary:: - + Job NullJob - + .. autoclass:: Job :members: :private-members: .. autoclass:: NullJob :members: - diff --git a/help/en/developer/hscommon/jobprogress/performer.rst b/help/en/developer/hscommon/jobprogress/performer.rst index 8a9ead0a..e714c7a0 100644 --- a/help/en/developer/hscommon/jobprogress/performer.rst +++ b/help/en/developer/hscommon/jobprogress/performer.rst @@ -4,9 +4,8 @@ hscommon.jobprogress.performer .. automodule:: hscommon.jobprogress.performer .. autosummary:: - + ThreadedJobPerformer - + .. autoclass:: ThreadedJobPerformer :members: - diff --git a/help/en/developer/index.rst b/help/en/developer/index.rst index 0c9699c5..0d3a9d19 100644 --- a/help/en/developer/index.rst +++ b/help/en/developer/index.rst @@ -69,6 +69,6 @@ API .. toctree:: :maxdepth: 2 - + core/index hscommon/index diff --git a/help/en/faq.rst b/help/en/faq.rst index 49b5028b..97a75ad0 100644 --- a/help/en/faq.rst +++ b/help/en/faq.rst @@ -31,7 +31,7 @@ How can I report a bug a suggest a feature? ------------------------------------------- dupeGuru is hosted on `Github`_ and it's also where issues are tracked. The best way to report a -bug or suggest a feature is to sign up on Github and `open an issue`_. +bug or suggest a feature is to sign up on Github and `open an issue`_. The mark box of a file I want to delete is disabled. What must I do? -------------------------------------------------------------------- @@ -178,4 +178,3 @@ Preferences are stored elsewhere: .. _Github: https://github.com/arsenetar/dupeguru .. _open an issue: https://github.com/arsenetar/dupeguru/wiki/issue-labels - diff --git a/help/en/index.rst b/help/en/index.rst index 97c345fa..f720f928 100644 --- a/help/en/index.rst +++ b/help/en/index.rst @@ -24,7 +24,7 @@ Contents: .. toctree:: :maxdepth: 2 - + contribute quick_start folders diff --git a/help/en/preferences.rst b/help/en/preferences.rst index 2c0cf43a..eadc4f3c 100644 --- a/help/en/preferences.rst +++ b/help/en/preferences.rst @@ -67,11 +67,11 @@ filename if the filename already exists in the destination. The format of the command is the same as what you would write in the command line, except that there are 2 placeholders: **%d** and **%r**. These placeholders will be replaced by the path of the selected dupe (%d) and the path of the selected dupe's reference file (%r). - + If the path to your executable contains space characters, you should enclose it in "" quotes. You should also enclose placeholders in quotes because it's very possible that paths to dupes and refs will contain spaces. Here's an example custom command:: - + "C:\Program Files\SuperDiffProg\SuperDiffProg.exe" "%d" "%r" .. _inode: http://en.wikipedia.org/wiki/Inode diff --git a/help/en/reprioritize.rst b/help/en/reprioritize.rst index 4febfca4..2eb286e6 100644 --- a/help/en/reprioritize.rst +++ b/help/en/reprioritize.rst @@ -22,4 +22,4 @@ criterion is used and so on and so on. For example, if your arguments are "Size "Filename (Doesn't end with a number)", the reference file that will be picked in a group will be the biggest file, and if two or more files have the same size, the one that has a filename that doesn't end with a number will be used. When all criteria result in ties, the order in which dupes -previously were in the group will be used. \ No newline at end of file +previously were in the group will be used. diff --git a/help/en/results.rst b/help/en/results.rst index 91778168..4b4041ce 100644 --- a/help/en/results.rst +++ b/help/en/results.rst @@ -180,7 +180,7 @@ any of them. the file's path. If the original file is deleted or moved, the link is broken. A hardlink is a link to the file *itself*. That link is as good as a "real" file. Only when *all* hardlinks to a file are deleted is the file itself deleted. - + On OSX and Linux, this feature is supported fully, but under Windows, it's a bit complicated. Windows XP doesn't support it, but Vista and up support it. However, for the feature to work, dupeGuru has to run with administrative privileges. diff --git a/help/fr/faq.rst b/help/fr/faq.rst index f8eae327..25d46468 100644 --- a/help/fr/faq.rst +++ b/help/fr/faq.rst @@ -51,7 +51,7 @@ Tour groupe de doublons contient au moins un fichier dit "référence" et ce fic effacé. Par contre, ce que vous pouvez faire c'est de le remplacer par un autre fichier du groupe. Pour ce faire, sélectionnez un fichier du groupe et cliquez sur l'action **Transformer sélectionnés en références**. - + Notez que si le fichier référence du groupe vient d'un dossier qui a été défini comme dossier référence, ce fichier ne peut pas être déplacé de sa position de référence du groupe. @@ -71,7 +71,7 @@ doublons. Example: Nous avons 3 fichiers, A, B et C. Nous les comparons en utili de filtre. La comparaison détermine que A est un double de B, A est un double C, mais que B n'est **pas** un double de C. dupeGuru a ici un problème. Il ne peut pas créer un groupe avec A, B et C. Il décide donc de jeter C hors du groupe. C'est de là que vient la notice '(X hors-groupe)'. - + Cette notice veut dire que si jamais vous effacez tout les doubles contenus dans vos résultats et que vous faites un nouveau scan, vous pourriez avoir de nouveaux résultats. diff --git a/help/fr/folders.rst b/help/fr/folders.rst index 54acf952..0a7f4b39 100644 --- a/help/fr/folders.rst +++ b/help/fr/folders.rst @@ -3,7 +3,7 @@ Sélection de dossiers La première fenêtre qui apparaît lorsque dupeGuru démarre est la fenêtre de sélection de dossiers à scanner. Elle détermine la liste des dossiers qui seront scannés lorsque vous cliquerez sur **Scan**. -Pour ajouter un dossier, cliquez sur le bouton **+**. Si vous avez ajouté des dossiers dans le passé, un menu vous permettra de rapidement choisir un de ceux ci. Autrement, il vous sera demandé d'indiquer le dossier à ajouter. +Pour ajouter un dossier, cliquez sur le bouton **+**. Si vous avez ajouté des dossiers dans le passé, un menu vous permettra de rapidement choisir un de ceux ci. Autrement, il vous sera demandé d'indiquer le dossier à ajouter. Vous pouvez aussi utiliser le drag & drop pour ajouter des dossiers à la liste. @@ -26,14 +26,14 @@ Le type d'un dossier s'applique à ses sous-dossiers, excepté si un sous-dossie Bibliothèques iPhoto et Aperture ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - + dupeGuru PE supporte iPhoto et Aperture, ce qui veut dire qu'il sait comment lire le contenu de ces bibliothèques et comment communiquer avec ces applications pour correctement supprimer des photos de celles-ci. Pour utiliser cette fonctionnalité, vous devez ajouter iPhoto et/ou Aperture avec les boutons spéciaux "Ajouter librairie iPhoto" et "Ajouter librairie Aperture", qui apparaissent quand on clique sur le petit "+". Les dossiers ajoutés seront alors correctement interprétés par dupeGuru. - + Quand une photo est supprimée d'iPhoto, elle est envoyée dans la corbeille d'iPhoto. Quand une photo est supprimée d'Aperture, il n'est malheureusement pas possible de l'envoyer @@ -45,13 +45,13 @@ Le type d'un dossier s'applique à ses sous-dossiers, excepté si un sous-dossie Bibliothèques iTunes ^^^^^^^^^^^^^^^^^^^^ - + dupeGuru ME supporte iTunes, ce qui veut dire qu'il sait comment lire le contenu de sa bibliothèque et comment communiquer avec iTunes pour correctement supprimer des chansons de sa bibliothèque. Pour utiliser cette fonctionnalité, vous devez ajouter iTunes avec le bouton spécial "Ajouter librairie iTunes", qui apparait quand on clique sur le petit "+". Le dossier ajouté sera alors correctement interprété par dupeGuru. - + Quand une chanson est supprimée d'iTunes, elle est envoyée à la corebeille du système, comme un fichier normal. La différence ici, c'est qu'après la suppression, iTunes est correctement mis au fait de cette suppression et retire sa référence à cette chanson de sa bibliothèque. diff --git a/help/fr/index.rst b/help/fr/index.rst index 66898fb9..3ea93baa 100644 --- a/help/fr/index.rst +++ b/help/fr/index.rst @@ -21,7 +21,7 @@ Contents: .. toctree:: :maxdepth: 2 - + quick_start folders preferences diff --git a/help/fr/preferences.rst b/help/fr/preferences.rst index 25811581..1c8b9450 100644 --- a/help/fr/preferences.rst +++ b/help/fr/preferences.rst @@ -4,9 +4,9 @@ Préférences .. only:: edition_se **Type de scan:** Cette option détermine quels aspects du fichier doit être comparé. Un scan par **Nom de fichier** compare les noms de fichiers mot-à-mot et, dépendant des autres préférences ci-dessous, déterminera si les noms se ressemblent assez pour être considérés comme doublons. Un scan par **Contenu** trouvera les doublons qui ont exactement le même contenu. - + Le scan **Dossiers** est spécial. Si vous le sélectionnez, dupeGuru cherchera des doublons de *dossiers* plutôt que des doublons de fichiers. Pour déterminer si deux dossiers sont des doublons, dupeGuru regarde le contenu de tous les fichiers dans les dossiers, et si **tous** sont les mêmes, les dossiers sont considérés comme des doublons. - + **Seuil du filtre:** Pour les scan de type **Nom de fichier**, cette option détermine le degré de similtude nécessaire afin de considérer deux noms comme doublons. Avec un seuil de 80, 80% des mots doivent être égaux. Pour déterminer ce pourcentage, dupeGuru compte le nombre de mots total des deux noms, puis compte le nombre de mots égaux, puis fait la division des deux. Un résultat égalisant ou dépassant le seuil sera considéré comme un doublon. Exemple: "a b c d" et "c d e" ont un pourcentage de 57 (4 mots égaux, 7 au total). .. only:: edition_me @@ -33,7 +33,7 @@ Préférences .. only:: edition_pe **Type de scan:** Détermine le type de scan qui sera fait sur vos images. Le type **Contenu** compare le contenu des images de façon "fuzzy", rendant possible de trouver non seulement les doublons exactes, mais aussi les similaires. Le type **EXIF Timestamp** compare les métadonnées EXIF des images (si existantes) et détermine si le "timestamp" (moment de prise de la photo) est pareille. C'est beaucoup plus rapide que le scan par Contenu. **Attention:** Les photos modifiées gardent souvent le même timestamp, donc faites attention aux faux doublons si vous utilisez cette méthode. - + **Seuil du filtre:** *Scan par Contenu seulement.* Plus il est élevé, plus les images doivent être similaires pour être considérées comme des doublons. Le défaut de 95% permet quelques petites différence, comme par exemple une différence de qualité ou bien une légère modification des couleurs. **Comparer les images de tailles différentes:** Le nom dit tout. Sans cette option, les images de tailles différentes ne sont pas comparées. @@ -58,6 +58,6 @@ Dans tous les cas, dupeGuru résout les conflits de noms de fichier en ajoutant Le format de la ligne de commande est la même que celle que vous écrireriez manuellement, excepté pour les arguments, **%d** et **%r**. L'endroit où vous placez ces deux arguments sera remplacé par le chemin du fichier sélectionné (%d) et le chemin de son fichier référence dans le groupe (%r). -Si le chemin de votre executable contient un espace, vous devez le placer entre guillemets "". Vous devriez aussi placer vos arguments %d et %r entre guillemets parce qu'il est très possible d'avoir des chemins de fichier contenant des espaces. Voici un exemple de commande personnelle:: - +Si le chemin de votre executable contient un espace, vous devez le placer entre guillemets "". Vous devriez aussi placer vos arguments %d et %r entre guillemets parce qu'il est très possible d'avoir des chemins de fichier contenant des espaces. Voici un exemple de commande personnelle:: + "C:\Program Files\SuperDiffProg\SuperDiffProg.exe" "%d" "%r" diff --git a/help/fr/reprioritize.rst b/help/fr/reprioritize.rst index 4febfca4..2eb286e6 100644 --- a/help/fr/reprioritize.rst +++ b/help/fr/reprioritize.rst @@ -22,4 +22,4 @@ criterion is used and so on and so on. For example, if your arguments are "Size "Filename (Doesn't end with a number)", the reference file that will be picked in a group will be the biggest file, and if two or more files have the same size, the one that has a filename that doesn't end with a number will be used. When all criteria result in ties, the order in which dupes -previously were in the group will be used. \ No newline at end of file +previously were in the group will be used. diff --git a/help/fr/results.rst b/help/fr/results.rst index 45690327..a75304ec 100644 --- a/help/fr/results.rst +++ b/help/fr/results.rst @@ -131,7 +131,7 @@ s'exécute. La plupart du temps, ces options n'ont pas a être activées. liens (`symlink`_ ou `hardlink`_) vers leur fichiers de référence respectifs. Un symlink est un lien symbolique (qui devient caduque si l'original est supprimé) et un hardlink est un lien direct au contenu du fichier (même si l'original est supprimé, le lien reste valide). - + Sur OS X et Linux, cette fonction est supportée pleinement, mais sur Windows, c'est un peu compliqué. Windows XP ne le supporte pas, mais Vista oui. De plus, cette fonction ne peut être utilisée que si dupeGuru roule avec les privilèges administratifs. Ouaip, Windows c'est la joie. diff --git a/help/hy/faq.rst b/help/hy/faq.rst index eeb42dea..c6511082 100644 --- a/help/hy/faq.rst +++ b/help/hy/faq.rst @@ -25,7 +25,7 @@ .. topic:: Ո՞րոն եք dupeGuru-ի լիցենզիայի սահմանափակումները: - Փորձնական եղանակում, Դուք կարող եք միայն կատարել գործողություններ 10 կրկնօրինակների հետ միաժամանակ: Ծրագրի + Փորձնական եղանակում, Դուք կարող եք միայն կատարել գործողություններ 10 կրկնօրինակների հետ միաժամանակ: Ծրագրի `Անվճար տարբերակում `_ mode, այնուհանդերձ չկան էական սահմանափակումներ: .. topic::Ջնջելու համար նշելու դաշտի պատուհանը ակտիվ չէ: Ի՞նչ անել: @@ -72,7 +72,7 @@ * Սեղմեք **Նշել --> Նշել բոլորը**: .. only:: edition_me - + .. topic:: Ես ցանկանում եմ հեռացնել բոլոր երգերը, որոնք 3 վայրկյանից հեռու են իրենց հղման ֆայլից: Ի՞նչ կարող եմ ես անել: * Միացնել :doc:`Միայն Սխալները ` եղանակում: @@ -84,7 +84,7 @@ * Սեղմեք **Ջնջել ընտրվածը արդյունքներից**: .. topic:: Ես ցանկանում եմ դարձնել իմ բարձրագույն բիթրեյթ ունեցող երգերը հղման ֆայլեր: Ի՞նչ կարող եմ ես անել: - + * Միացնել :doc:`Միայն Սխալները ` եղանակում: * Միացնել **Դելտա նշանակությունները** եղանակը: * Սեղմեք "Բիթրեյթը" սյանը՝ դասավորելու համար արդյունքները ըստ բիթրեյթի: @@ -93,12 +93,12 @@ * Սեղմեք **Դարձնել ընտրվածը հղում**: .. topic:: Ես չեմ ցանկանում [live] և [remix] տարբերակները իմ երգերի՝ հաշված որպես կրկնօրինակ: Ինչպե՞ս դա անել: - - Եթե Ձեր համեմատության սահմանը բավականին ցածր է, հնարավոր է Դուք ավարտվեք կենդանի և ռեմիքս տարբերակներով Ձեր երգերի արդյունեքներում: Դուք ոչինչ չեք կարող անել դրա համար, բայց կա ինչ-որ եղանակ՝ դրանք ստուգման արդյունքներից ջնջելու համար: Եթե օրինակի համար, Դուք ցանկանում եք ջնջել ամեն մի երգ, որը գտնվում է գծիկների միջև []:. + + Եթե Ձեր համեմատության սահմանը բավականին ցածր է, հնարավոր է Դուք ավարտվեք կենդանի և ռեմիքս տարբերակներով Ձեր երգերի արդյունեքներում: Դուք ոչինչ չեք կարող անել դրա համար, բայց կա ինչ-որ եղանակ՝ դրանք ստուգման արդյունքներից ջնջելու համար: Եթե օրինակի համար, Դուք ցանկանում եք ջնջել ամեն մի երգ, որը գտնվում է գծիկների միջև []:. * **Windows**. Սեղմեք **Գործողություններ --> Կիրառել ֆիլտրը**, ապա տեսակը "[*]", ապա սեղմեք ԼԱՎ: * **Mac OS X**. Տեսակը "[*]" "Ֆիլտր" դաշտում՝ գործիքաշերտի: * Սեղմեք **Նշել --> Նշել բոլորը**: - * Սեղմեք **Գործողություններ --> Ջնջել ընտրվածը արդյունքներից**. + * Սեղմեք **Գործողություններ --> Ջնջել ընտրվածը արդյունքներից**. .. topic:: Ես փորձում եմ կրկնօրինակները ուղարկել Աղբարկղ, բայց dupeGuru-ն ինձ ասում է, որ չես կարող: Ինչու՞: Ի՞նչ կարող եմ ես անել: @@ -112,4 +112,4 @@ Եթե այս ամենը ձախողվի, `կապնվեք HS աջակցության թիմի հետ `_, մենք կփորձեք օգնել Ձեզ: -.. todo:: This FAQ qestion is outdated, see english version. \ No newline at end of file +.. todo:: This FAQ qestion is outdated, see english version. diff --git a/help/hy/index.rst b/help/hy/index.rst index 2a86b6fb..5ccca2a2 100644 --- a/help/hy/index.rst +++ b/help/hy/index.rst @@ -21,7 +21,7 @@ .. toctree:: :maxdepth: 2 - + quick_start folders preferences diff --git a/help/hy/preferences.rst b/help/hy/preferences.rst index 7faeab09..74fc96bf 100644 --- a/help/hy/preferences.rst +++ b/help/hy/preferences.rst @@ -4,9 +4,9 @@ .. only:: edition_se **Ստուգելու տեսակը.** Այս ընտրանքը որոշում է, թե ֆայլերի որ ասպեկտը կհամեմատվի կրկնօրինակված ստուգման հետ: Եթե Դուք ընտրեք **Ֆայլի անունը**, ապա dupeGuru-ն կհամեմատի յուրաքանչյուրը բառ-առ-բառ և կախված է հետևյալ այլ ընտրանքներից, այն կորոշի արդյոք բավական են համընկնող բառերը դիտելու համար 2 ֆայլերի կրկնօրինակները: Եթե ընտրեք միայն **Բովանդակությունը**, ապա նույնատիպ ֆայլերը նույն բովանդակությամբ կհամընկնեն: - + **Թղթապանակներ.** ստուգելու հատուկ տեսակ է: Երբ ընտրեք սա, dupeGuru-ն կստուգի կրկնօրինակ *թղթապանակները*՝ կրկնօրինակ ֆայլերի փոխարեն: Որոշելու համար արդյոք անկախ երկու թղթապանակները կրկնօրինակ են, կստուգվեն թղթապանակների ամբողջ պարունակությունը և եթե **բոլոր** ֆայլերի բովանդակությունը համընկնի, ապա թղթապանակները կորոշվեն որպես կրկնօրինակներ: - + **Ֆիլտրի խստությունը.** Եթե Դուք ընտրեք **Ֆայլի անունը** ստուգելու տեսակը, այս ընտրանքը կորոշի, թե ինչքանով նման պետք է լինեն ֆայլերի անունները, որ dupeGuru-ն ճանաչի դրանք որպես կրկնօրինակներ: Եթե ֆիլտրը առավել խիստ է, օրինակ՝ 80, ապա դա նշանակում է, որ երկու ֆայլերի անունների բառերի 80%-ը պետք է համընկնի: Որոշելու համար համընկնման տոկոսը, dupeGuru-ն նախ հաշվում է բառերի ընդհանուր քիանակը **երկու** ֆայլերի անուններում, ապա հաշվում է համընկնումների քանակը (ամեն բառ համընկնում է 2-ի հաշվին) և բաժանում ընդհանուր գտնված բառերի համընկնումների միջև: Եթե արդյունքը բարձր է կամ հավասար ֆիլտրի խստությանը, ապա մենք ունեք կրկնօրինակի համընկնում: Օրինակ՝ "a b c d" և "c d e" ունեն համընկնման տոկոս, որը հավասար է 57-ի (4 բառ են համընկնում, 7 ընդհանուր բառից): .. only:: edition_me @@ -33,7 +33,7 @@ .. only:: edition_pe **Ստուգելու եղանակը.** Այս ընտրանքը որոշում է ստուգելու եղանակը, որը կկիրառվի նկարների նկատմամբ: **Պարունակությունը** ստուգելու եղանակը համեմատում է ակտուալ նկարների բովանդակությունը ոչ ճշգրիտ եղանակով (հնարավորություն տալով գտնելու ոչ միայն անմիջապես կրկնօրինակները, այլ նաև նմանատիպ այլ ֆայլերը): **EXIF Timestamp** ստուգելու եղանակը նայում է նկարի EXIF մետատվյալը (եթե այն կա) և համընկնող նկարները, որոնք որ նույնն են: Սա ավելի արագ է, քան բովանդակությամբ ստուգելը: **Զգուշացում.** Փոփոխված նկարները սովորաբար պահում են նույն EXIF timestamp-ը, ուստի նախ նայեք արդյունքները, ապա գործեք: - + **Ֆիլտրի խստությունը.** *Ստուգում է միայն բովանդակությունը:* Այս ընտարնքի բարձրագույն նիշը, բնորոշում է ֆիլտրի "խստությունը" (Այլ կերպ ասաց, արդյունքը ավելի քիչ է լինում): Նույն որակի նկարներից շատերը երբեմն համընկնում են 100%-ով՝ անգամ եթե տեսակը ուրիշ է (PNG և JPG օրինակի համար): Այնուհանդերձ, եթե ցանկանում եք, որ PNG-ն համապատասխանի ցածր որակի JPG-ին, պետք է նշեք ֆիլտրի խստությունը 100-ից ցածր: Ծրագրայինը 95 է: **Տարբեր չափերով նկարների համապատասխանեցում.** Եթե ընտրեք սա, տարբեր չափերի նկարները կթույլատրվեն կրկնօրինակվող նույն խմբում: @@ -57,7 +57,7 @@ **Ընտրված հրամանը.** Այս կարգավորումը որոշում է հրամանը, որը կկանչվի "Կանչել Ընտրված հրամանը" գործողությամբ: Կարող եք կանչել ցանկացած արտաքին ծրագիր՝ այս գործողությամբ: Սա կարող է օգտակար լինել եթե օրինակ փոխարենը ունեք տվյալների փոխանցման լավ ծրագիր: Հրամանի տեսակը նույնն է, ինչ Դուք կգրեք Հրամանի տողում, բացառությամբ որտեղ կան 2 լրացումներ. **%d** և **%r**: Այս լրացումները կվերագրվեն ընտրված զոհի (%d) ճանապարհով և ընտրված զոհի հղման ֆայլով (%r): - + Եթե կատարելի ֆայլի ճանապարհը պարունակում է բացատներ, ապա պետք է փակեք այն "" չակերտներով: Նաև պետք է փակեք լրացումները չակերտներով, որովհետև շատ հնարավոր է, որ զոհի ճանապարհները և հղումները կպարունակեն բացատներ: Ահա ընտրված հրամանի օրինակ՝ :: - + "C:\Program Files\SuperDiffProg\SuperDiffProg.exe" "%d" "%r" diff --git a/help/hy/quick_start.rst b/help/hy/quick_start.rst index 6dc7d2b4..37f61679 100644 --- a/help/hy/quick_start.rst +++ b/help/hy/quick_start.rst @@ -12,4 +12,3 @@ * Եթե համոզված եք, որ կրկնօրինակը արդյունքներում կա, ապա սեղմեք **Խմբագրել-->Նշել բոլորը**, և ապա **Գործողություններ-->Ուղարկել Նշվածը Աղբարկղ**: Սա միայն բազային ստուգում է: Կան բազմաթիվ կարգավորումներ, որոնք հնարավորություն են տալիս նշելու տարբեր արդյունքներ և մի քանի եղանակներ արդյունքների փոփոխման: Մանրամասների համար կարդացեք Օգնության ֆայլը: - diff --git a/help/hy/reprioritize.rst b/help/hy/reprioritize.rst index 2b60a905..e9aec679 100644 --- a/help/hy/reprioritize.rst +++ b/help/hy/reprioritize.rst @@ -1,7 +1,7 @@ Վերաառաջնայնության կրկնօրինակներ ================================ -dupeGuru-ը փորձում է որոշել, թե որ կրկնօրինակները պետք է գնան յուրաքանչյուր խմբի դիրքում, +dupeGuru-ը փորձում է որոշել, թե որ կրկնօրինակները պետք է գնան յուրաքանչյուր խմբի դիրքում, բայց երբեմն սխալ է ստանում: Շատ դեպքերում, խելամիտ դասավորումը "Դելտա նշանակության" և "Միայն սխալները" ընտրանքների ավելացնելով "Դարձնել ընտրվածը հղում" գործողության խորամանկություն է, բայց երբեմն, պահանջվում են ավելի լավ ընտրանքներ: Ահա այստեղ է, որ վերաառաջնայնավորման պատուհանը բացվում է: @@ -23,4 +23,3 @@ dupeGuru-ը փորձում է որոշել, թե որ կրկնօրինակներ մեծագույն ֆայլը և եթե երկու կամ ավելի ֆայլեր ունեն նույն չափը, ապա մեկը ունի ֆայլի անուն, որը չի ավարտվում թվով, կօգտագործվի: Երբ փաստարկի արդյունքը կապված է, կարգը, որի սխալները նախկինում էին, խումբը պետք է օգտագործվի: - diff --git a/help/hy/results.rst b/help/hy/results.rst index fb5ab88c..9406b004 100644 --- a/help/hy/results.rst +++ b/help/hy/results.rst @@ -98,4 +98,4 @@ dupeGuru-ն աջակցում է հետստուգման ֆիլտրում։ Սրա * **Անվանափոխել ընտրվածը.** Ձեզ հարցում կկատարվի նոր անվան համար, ապա ընտրված ֆայլը կանվանափոխվի։ .. todo:: Add Move and iPhoto/iTunes warning -.. todo:: Add "Deletion Options" section. \ No newline at end of file +.. todo:: Add "Deletion Options" section. diff --git a/help/ru/faq.rst b/help/ru/faq.rst index e31d119f..214d674d 100644 --- a/help/ru/faq.rst +++ b/help/ru/faq.rst @@ -72,7 +72,7 @@ * Нажмите на **Отметить -> Отметить все**. .. only:: edition_me - + .. topic:: Я хочу, чтобы удалить все песни, которые более чем на 3 секунды от своей ссылке на файл. Что я могу сделать? * Включить: документ: `обманутые Только <результаты>` режим. @@ -84,7 +84,7 @@ * Нажмите на **Удалить выбранные из результатов**. .. topic:: Я хочу, чтобы мой высокий битрейт файлов песни ссылки. Что я могу сделать? - + * Включить: документ: `обманутые Только <результаты>` режиме * Включить **Значения делта** режим. * Нажмите на кнопку "Битрейт" колонку для сортировки результатов по битрейт. @@ -93,9 +93,9 @@ * Нажмите на **Сделать выбранной ссылки**. .. topic:: Я не хочу [жить] и [ремикс] версии моих песен считаться дубликатами. Как мне это сделать? - + Если ваше сравнение порог достаточно низким, вы, вероятно, в конечном итоге с живой и ремикс версии ваших песен в своих результатах. Там вы ничего не можете сделать, чтобы предотвратить это, но есть кое-что можно сделать, чтобы легко удалить их со своего результаты после сканирования: после сканирования, фильтрации. Если, например, вы хотите удалить все песни с чем-либо в квадратных скобках []: - + * **Windows**: Нажмите на **Действия -> Применить фильтр**, а затем введите "[*]", нажмите кнопку ОК. * **Mac OS X**: Тип "[*]" в "Фильтр" поле на панели инструментов. * Нажмите на Отметить **-> Отметить все**. diff --git a/help/ru/index.rst b/help/ru/index.rst index fdec2c46..8b3be6b2 100644 --- a/help/ru/index.rst +++ b/help/ru/index.rst @@ -18,7 +18,7 @@ .. toctree:: :maxdepth: 2 - + quick_start folders preferences diff --git a/help/ru/preferences.rst b/help/ru/preferences.rst index 7e67b6b5..f8140bfa 100644 --- a/help/ru/preferences.rst +++ b/help/ru/preferences.rst @@ -4,9 +4,9 @@ .. only:: edition_se **Тип сканирования:** Этот параметр определяет, какой аспект файлы будут сравниваться в дубликат сканирования. Если выбрать **Имя файла**, dupeGuru будем сравнивать каждое имена файлов слово за слово, и, в зависимости от других параметров ниже, он будет определять, достаточно ли слов соответствие рассмотреть 2 файлов дубликатов. Если выбрать **Содержимое**, только файлы с точно такой же контент будет матч. - + **Папки** типа сканирования немного особенным. Когда вы выбираете его, dupeGuru проведет поиск дубликатов *папки* вместо того, чтобы дубликатов файлов. Для определения того, две папки, дублируют друг друга, все файлы, содержащиеся в папках будут проверяться, и если содержание **все** файлы в матче папки, папки будут считаться дубликатами. - + **Фильтра Твердость:** Если вы выбрали **Имя файла** типа сканирования, эта опция определяет, как похожи два имени должно быть для dupeGuru рассматривать их дубликатов. Если фильтр твердости, например 80, то это означает, что 80% слов из двух имен файлов должны совпадать. Для определения соответствия процент, dupeGuru первой подсчитывает общее количество слов в **обоих** файла, то подсчитать количество слов соответствия (каждое слово соответствия считаются 2), а затем разделите количество слов соответствия на общее число слов. Если результат больше или равно фильтр твердость, у нас есть дубликаты матча. Например, "ABCD" и "CDE" имеют соответствующий процент 57 (4 слова соответствия, 7 всего слов). .. only:: edition_me @@ -35,7 +35,7 @@ **Тип сканирования:** Этот параметр определяет тип сканирования, которые будут сделаны на ваши картины.**Сканирования** Содержание типа сравнивает фактическое содержание фотографий нечеткие пути (что делает его можно найти не только точными копиями, но и подобные).**EXIF Timestamp** тип сканирования смотрит на метаданные EXIF с фото (если он существует) и соответствует фотографии, которые имеют такой же. Это намного быстрее, чем сканирование содержимого. **Внимание:** Измененные фотографии часто держат же метка EXIF, так что следите за ложных срабатываний, когда вы используете, что тип сканирования. **Фильтра Твердость:** *Содержание тип сканирования только.* Чем больше этот параметр, "тяжелее" является фильтром (Другими словами, тем меньше результатов Вы получите). Большинство фотографий одного и того же матча качества на 100%, даже если формат отличается (PNG и JPG, например.). Однако, если вы хотите, чтобы соответствовать PNG с более низким качеством JPG, вам придется установить фильтром твердость ниже, чем 100.По умолчанию, 95, это сладкое место. - + **Совпадения рисунки разных размеров:** Если вы установите этот флажок, фотографии разных размеров будет разрешен в том же дубликат группы. **Можно смешивать файл вида:** Если вы установите этот флажок, дублировать группам разрешается есть файлы с различными расширениями. Если вы не проверить его, ну, они не являются! @@ -57,7 +57,7 @@ **Специальной команды:** Это предпочтение определяет команду, которая будет вызываться "Вызов специальной команды" действия. Вы можете ссылаться ни на какие внешние приложения через это действие. Это может быть полезно, если, например, у вас есть хорошее приложение сравниваете установлены. Формат команды такой же, как то, что вы должны написать в командной строке, за исключением того, что Есть 2 заполнителей: **%d** and **%r**. Эти заполнители будут заменены на путь выбран обманут (%d) и путь к ссылке на файл выбранного обмануть (%r). - + Если путь к исполняемому содержит пробелы, необходимо заключить его в "" кавычки. Вы также должны приложить заполнителей в кавычки, потому что это очень возможно, что путь к обманутых и ссылки будут содержать пробелы. Вот пример пользовательской команды: - + "C:\Program Files\SuperDiffProg\SuperDiffProg.exe" "%d" "%r" diff --git a/help/ru/quick_start.rst b/help/ru/quick_start.rst index d12d0803..6d4bded0 100644 --- a/help/ru/quick_start.rst +++ b/help/ru/quick_start.rst @@ -11,4 +11,4 @@ * Если файл ложных дубликатов, выделите ее и нажмите **Действия -> Удалить выбранные из результатов**. * Если вы уверены, что нет ложных дубликатов в результатах, нажмите на **Изменить -> Отметить Все**, а затем **Действия -> Отправить Помечено в Корзину**. -Это только основные сканирования. Есть много настройки вы можете сделать, чтобы получить разные результаты и несколько методов изучения и изменения ваших результатов. Чтобы узнать о них, только что прочитал остальную часть этого файла справки. \ No newline at end of file +Это только основные сканирования. Есть много настройки вы можете сделать, чтобы получить разные результаты и несколько методов изучения и изменения ваших результатов. Чтобы узнать о них, только что прочитал остальную часть этого файла справки. diff --git a/help/ru/reprioritize.rst b/help/ru/reprioritize.rst index 0147744a..da5b8de7 100644 --- a/help/ru/reprioritize.rst +++ b/help/ru/reprioritize.rst @@ -22,4 +22,4 @@ subargument в приведенном ниже списке, а затем на "Имя файла (Не оканчивается на номер)", ссылке на файл, который будет выбран в группе будет крупнейших файл, а если два или несколько файлов имеют одинаковый размер, который имеет имя файла с не заканчивается номер будет использоваться. Когда все критерии привести к связи, порядок, в котором обманутые -ранее были в группе будет использоваться. \ No newline at end of file +ранее были в группе будет использоваться. diff --git a/help/ru/results.rst b/help/ru/results.rst index 9fdea957..c7e5db5e 100644 --- a/help/ru/results.rst +++ b/help/ru/results.rst @@ -98,4 +98,4 @@ dupeGuru поддерживает после сканирования, филь * **Переименования выбрано:** Запрашивает новое имя, а затем переименовать выбранный файл. .. todo:: Add Move and iPhoto/iTunes warning -.. todo:: Add "Deletion Options" section. \ No newline at end of file +.. todo:: Add "Deletion Options" section. diff --git a/help/uk/faq.rst b/help/uk/faq.rst index cf685d5f..e63ebd08 100644 --- a/help/uk/faq.rst +++ b/help/uk/faq.rst @@ -72,7 +72,7 @@ * Натисніть на Марка **-> Позначити всі**. .. only:: edition_me - + .. topic:: Я хочу, щоб видалити всі пісні, які більш ніж на 3 секунди від своєї посиланням на файл. Що я можу зробити? * Включити :doc:`ошукані Тільки ` режимі. @@ -84,7 +84,7 @@ * Натисніть на **Видалити вибрані з результатів**. .. topic:: Я хочу, щоб мій високий бітрейт файлів пісні посилання. Що я можу зробити? - + * Включити :doc:`ошукані Тільки ` режимі. * Включити **Значення Delta** режимі. * Натисніть на "Бітрейт" колонку для сортування результатів по бітрейт. @@ -93,9 +93,9 @@ * Натисніть на **Зробити вибраної посилання**. .. topic:: Я не хочу [жити] і [ремікс] версії моїх пісень вважатися дублікатами. Як мені це зробити? - + Якщо ваше порівняння поріг досить низьким, ви, ймовірно, в кінцевому підсумку з живою і ремікс версії ваших пісень у своїх результатах. Там ви нічого не можете зробити, щоб запобігти цьому, але є дещо можна зробити, щоб легко видалити їх зі свого результати після сканування: після сканування, фільтрації. Якщо, наприклад, ви хочете видалити всі пісні з чим-небудь у квадратних дужках []: - + * **Windows**: Натисніть на **Дії -> Застосувати фільтр**, а потім введіть "[*]", натисніть кнопку ОК. * **Mac OS X**: Тип "[*]" в "Фільтр" поле на панелі інструментів. * Натисніть на Марка **-> Позначити всі**. @@ -114,4 +114,3 @@ Якщо все це не так, `контакт УГ підтримки `_, ми зрозуміти це. .. todo:: This FAQ qestion is outdated, see english version. - diff --git a/help/uk/index.rst b/help/uk/index.rst index 26f49dc9..0209b804 100644 --- a/help/uk/index.rst +++ b/help/uk/index.rst @@ -13,7 +13,7 @@ dupeGuru Picture Edition (PE для стислості) являє собою інструмент для пошуку дублікатів фотографій на вашому комп'ютері. Не тільки він може знайти точні відповідності, але він також може знайти дублікати серед фотографій різного роду (PNG, JPG, GIF і т.д..) І якість. -Хоча dupeGuru може бути легко використана без документації, читання цього файлу допоможе вам освоїти його. Якщо ви шукаєте керівництво для вашої першої дублювати сканування, ви можете поглянути на: :doc:`Quick Start ` +Хоча dupeGuru може бути легко використана без документації, читання цього файлу допоможе вам освоїти його. Якщо ви шукаєте керівництво для вашої першої дублювати сканування, ви можете поглянути на: :doc:`Quick Start ` Це гарна ідея, щоб зберегти dupeGuru оновлено. Ви можете завантажити останню версію на своєму http://dupeguru.voltaicideas.net. @@ -21,7 +21,7 @@ Contents: .. toctree:: :maxdepth: 2 - + quick_start folders preferences diff --git a/help/uk/preferences.rst b/help/uk/preferences.rst index 6489f418..6b2391e4 100644 --- a/help/uk/preferences.rst +++ b/help/uk/preferences.rst @@ -4,9 +4,9 @@ .. only:: edition_se **Тип сканування:** Цей параметр визначає, який аспект файли будуть порівнюватися в дублікат сканування. Якщо вибрати **Файл** , dupeGuru будемо порівнювати кожне імена файлів слово за слово, і, залежно від інших параметрів нижче, він буде визначати, чи достатньо слів відповідність розглянути 2 файлів дублікатів. Якщо вибрати **Вміст**, тільки файли з точно такою ж контент буде матч. - + **Папки** типу сканування трохи особливим. Коли ви обираєте його, dupeGuru проведе пошук дублікатів *папки* замість того, щоб дублікатів файлів. Для визначення того, дві папки, дублюють один одного, всі файли, що містяться в папках будуть перевірятися, і якщо вміст **всі** файли в матчі папки, папки будуть вважатися дублікатами. - + **Фільтра Твердість:** Якщо ви вибрали **Папки** Файл типу сканування, ця опція визначає, як схожі два імені повинно бути для dupeGuru розглядати їх дублікатів. Якщо фільтр твердості, наприклад 80, то це означає, що 80% слів з ​​двох імен файлів повинні збігатися. Для визначення відповідності відсоток, dupeGuru перший підраховує загальну кількість слів в **обох** файлу, то підрахувати кількість слів відповідності (кожне слово відповідності вважаються 2), а потім розділіть кількість слів відповідності на загальне число слів. Якщо результат більше або дорівнює фільтр твердість, у нас є дублікати матчу. Наприклад, "ABCD" і "CDE" мають відповідний відсоток 57 (4 слова відповідності, 7 всього слів). .. only:: edition_me @@ -33,7 +33,7 @@ .. only:: edition_pe **Тип сканування:** Цей параметр визначає тип сканування, які будуть зроблені на ваші картини. **Сканування** Зміст типу порівнює фактичний зміст фотографій нечіткі шляху (що робить його можна знайти не тільки точними копіями, але і подібні). **EXIF Timestamp** тип сканування дивиться на метадані EXIF з фото (якщо він існує) і відповідає фотографії, які мають такий же. Це набагато швидше, ніж сканування вмісту. **Увага:** Змінені фотографії часто тримають ж мітка EXIF, так що слідкуйте за помилкових спрацьовувань, коли ви використовуєте, що тип сканування. - + **Фільтра Твердість:** *Вміст тип сканування тільки*. Чим більше цей параметр, "важче" є фільтром (Іншими словами, тим менше результатів Ви отримаєте). Більшість фотографій одного й того ж матчу якості на 100%, навіть якщо формат відрізняється (PNG і JPG, наприклад.). Однак, якщо ви хочете, щоб відповідати PNG з більш низькою якістю JPG, вам доведеться встановити фільтром твердість нижче, ніж 100. За замовчуванням, 95, це солодке місце. **Матч малюнки різних розмірів:** Якщо ви встановите цей прапорець, фотографії різних розмірів буде дозволений в тому ж дублікат групи. @@ -60,5 +60,5 @@ Формат команди такий же, як те, що ви повинні написати в командному рядку, за винятком того, що Є 2 заповнювачів: **%d** and **%r**. Ці наповнювачі будуть замінені на шлях вибраний обдурити (% г) і шлях до заслання на файл вибраного обдурити (%r). Якщо шлях до виконуваного містить прогалини, необхідно укласти його в "" лапки. Ви також повинні докласти заповнювачів в лапки, бо це дуже можливо, що шлях до обдурених і посилання будуть містити пробіли. Ось приклад користувальницької команди:: - + "C:\Program Files\SuperDiffProg\SuperDiffProg.exe" "%d" "%r" diff --git a/help/uk/quick_start.rst b/help/uk/quick_start.rst index 6e39aa58..343ac3d5 100644 --- a/help/uk/quick_start.rst +++ b/help/uk/quick_start.rst @@ -11,4 +11,4 @@ * Якщо файл помилкових дублікатів, виділіть її та натисніть **Дії -> Видалити вибрані з результатів**. * Якщо ви впевнені, що немає помилкових дублікатів в результатах, натисніть на **Редагувати -> Позначити Всі**, а потім **Дії -> Отправить Позначено до кошику**. -Це тільки основні сканування. Є багато налаштування ви можете зробити, щоб отримати різні результати і кілька методів вивчення та зміни ваших результатів. Щоб дізнатися про них, щойно прочитав решту цього файлу довідки. \ No newline at end of file +Це тільки основні сканування. Є багато налаштування ви можете зробити, щоб отримати різні результати і кілька методів вивчення та зміни ваших результатів. Щоб дізнатися про них, щойно прочитав решту цього файлу довідки. diff --git a/help/uk/reprioritize.rst b/help/uk/reprioritize.rst index d763464d..27afb7ec 100644 --- a/help/uk/reprioritize.rst +++ b/help/uk/reprioritize.rst @@ -22,4 +22,4 @@ subargument в наведеному нижче списку, а потім на "Файл (Не закінчується на номер)", заслання на файл, який буде обраний у групі буде найбільших файл, а якщо два або декілька файлів мають однаковий розмір, який має ім'я файлу з не закінчується номер буде використовуватися. Коли всі критерії привести до зв'язку, порядок, в якому ошукані -раніше були в групі буде використовуватися. \ No newline at end of file +раніше були в групі буде використовуватися. diff --git a/help/uk/results.rst b/help/uk/results.rst index f015175a..0fbbc327 100644 --- a/help/uk/results.rst +++ b/help/uk/results.rst @@ -98,4 +98,4 @@ dupeGuru підтримує після сканування, фільтраці * **Перейменування обрано:** Запит нове ім'я, а потім перейменувати вибраний файл. .. todo:: Add Move and iPhoto/iTunes warning -.. todo:: Add "Deletion Options" section. \ No newline at end of file +.. todo:: Add "Deletion Options" section. diff --git a/hscommon/LICENSE b/hscommon/LICENSE index 5a8d3ceb..88f4f87f 100644 --- a/hscommon/LICENSE +++ b/hscommon/LICENSE @@ -7,4 +7,4 @@ Redistribution and use in source and binary forms, with or without modification, * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Hardcoded Software Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/hscommon/trans.py b/hscommon/trans.py index 86628831..694a36ba 100644 --- a/hscommon/trans.py +++ b/hscommon/trans.py @@ -41,7 +41,8 @@ def trget(domain: str) -> Callable[[str], str]: def set_tr( - new_tr: Callable[[str, Union[str, None]], str], new_trget: Union[Callable[[str], Callable[[str], str]], None] = None + new_tr: Callable[[str, Union[str, None]], str], + new_trget: Union[Callable[[str], Callable[[str], str]], None] = None, ) -> None: global _trfunc, _trget _trfunc = new_tr diff --git a/locale/columns.pot b/locale/columns.pot index eddb5b80..9da01da0 100644 --- a/locale/columns.pot +++ b/locale/columns.pot @@ -114,4 +114,3 @@ msgstr "" #: core\prioritize.py:158 msgid "Size" msgstr "" - diff --git a/locale/core.pot b/locale/core.pot index 622ec726..31da3865 100644 --- a/locale/core.pot +++ b/locale/core.pot @@ -243,4 +243,3 @@ msgstr "" #: core\se\scanner.py:18 msgid "Folders" msgstr "" - diff --git a/locale/ui.pot b/locale/ui.pot index 2f7e134d..ae8a7b94 100644 --- a/locale/ui.pot +++ b/locale/ui.pot @@ -1114,3 +1114,10 @@ msgstr "" #: qt\progress_window.py:65 msgid "Are you sure you want to cancel? All progress will be lost." msgstr "" + +#: qt\exclude_list_dialog.py:161 +msgid "" +"These (case sensitive) python regular expressions will filter out files during scans.
    Directores will also have their default state set to Excluded in the Directories tab if their name happens to match one of the selected regular expressions.
    For each file collected, two tests are performed to determine whether or not to completely ignore it:
  • 1. Regular expressions with no path separator in them will be compared to the file name only.
  • \n" +"
  • 2. Regular expressions with at least one path separator in them will be compared to the full path to the file.

  • Example: if you want to filter out .PNG files from the \"My Pictures\" directory only:
    .*My\\sPictures\\\\.*\\.png

    You can test the regular expression with the \"test string\" button after pasting a fake path in the test field:
    C:\\\\User\\My Pictures\\test.png

    \n" +"Matching regular expressions will be highlighted.
    If there is at least one highlight, the path or filename tested will be ignored during scans.

    Directories and files starting with a period '.' are filtered out by default.

    " +msgstr "" diff --git a/macos.md b/macos.md index ae0c8c74..1270666d 100644 --- a/macos.md +++ b/macos.md @@ -11,10 +11,10 @@ These instructions are for the Qt version of the UI on macOS. #### Prerequisite setup 1. Install Xcode if desired -2. Install [Homebrew][homebrew], if not on the path after install (arm based Macs) create `~/.zshrc` -with `export PATH="/opt/homebrew/bin:$PATH"`. Will need to reload terminal or source the file to take +2. Install [Homebrew][homebrew], if not on the path after install (arm based Macs) create `~/.zshrc` +with `export PATH="/opt/homebrew/bin:$PATH"`. Will need to reload terminal or source the file to take effect. -3. Install qt5 with `brew`. If you are using a version of macos without system python 3.7+ then you will +3. Install qt5 with `brew`. If you are using a version of macos without system python 3.7+ then you will also need to install that via brew or with pyenv. $ brew install qt5 @@ -25,9 +25,9 @@ also need to install that via brew or with pyenv. 4. May need to launch a new terminal to have everything working. ### With build.py -OSX comes with a version of python 3 by default in newer versions of OSX. To produce universal +OSX comes with a version of python 3 by default in newer versions of OSX. To produce universal builds either the 3.8 version shipped in macos or 3.9.1 or newer needs to be used. If needing to -build pyqt5 from source then the first line below is needed, else it may be omitted. (Path shown is +build pyqt5 from source then the first line below is needed, else it may be omitted. (Path shown is for an arm mac.) $ export PATH="/opt/homebrew/opt/qt/bin:$PATH" @@ -39,7 +39,7 @@ for an arm mac.) $ python run.py ### Generate OSX Packages -The extra requirements need to be installed to run packaging: `pip install -r requirements-extra.txt`. +The extra requirements need to be installed to run packaging: `pip install -r requirements-extra.txt`. Run the following in the respective virtual environment. $ python package.py @@ -47,7 +47,7 @@ Run the following in the respective virtual environment. This will produce a dupeGuru.app in the dist folder. ### Running tests -The complete test suite can be run with tox just like on linux. NOTE: The extra requirements need to +The complete test suite can be run with tox just like on linux. NOTE: The extra requirements need to be installed to run unit tests: `pip install -r requirements-extra.txt`. [python]: http://www.python.org/ diff --git a/pkg/debian/changelog b/pkg/debian/changelog index d70cffe3..297d4219 100644 --- a/pkg/debian/changelog +++ b/pkg/debian/changelog @@ -348,4 +348,3 @@ dupeguru (2.9.2-1) unstable; urgency=low * Fixed selection glitches, especially while renaming. (#93) -- Virgil Dupras Wed, 10 Feb 2010 00:00:00 +0000 - diff --git a/pkg/debian/source/format b/pkg/debian/source/format index 9f674278..89ae9db8 100644 --- a/pkg/debian/source/format +++ b/pkg/debian/source/format @@ -1 +1 @@ -3.0 (native) \ No newline at end of file +3.0 (native) diff --git a/pyproject.toml b/pyproject.toml index 3f452fd0..a19877f9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,5 +5,5 @@ build-backend = "setuptools.build_meta" line-length = 120 [tool.isort] # make it compatible with black -profile = "black" +profile = "black" skip_gitignore = true diff --git a/qt/pe/modules/block.c b/qt/pe/modules/block.c index a14cd6fc..1c22ce61 100644 --- a/qt/pe/modules/block.c +++ b/qt/pe/modules/block.c @@ -163,4 +163,4 @@ PyObject *PyInit__block_qt(void) { return NULL; } return m; -} \ No newline at end of file +} diff --git a/qt/pe/photo.py b/qt/pe/photo.py index 53bc8a0f..84de9469 100644 --- a/qt/pe/photo.py +++ b/qt/pe/photo.py @@ -31,7 +31,10 @@ def _plat_get_blocks(self, block_count_per_side, orientation): image = image.convertToFormat(QImage.Format_RGB888) if type(orientation) != int: logging.warning( - "Orientation for file '%s' was a %s '%s', not an int.", str(self.path), type(orientation), orientation + "Orientation for file '%s' was a %s '%s', not an int.", + str(self.path), + type(orientation), + orientation, ) try: orientation = int(orientation) diff --git a/requirements-extra.txt b/requirements-extra.txt index 163d000b..1d175bf8 100644 --- a/requirements-extra.txt +++ b/requirements-extra.txt @@ -1,4 +1,4 @@ pytest>=6,<7 flake8 black -pyinstaller>=4.5,<5.0; sys_platform != 'linux' \ No newline at end of file +pyinstaller>=4.5,<5.0; sys_platform != 'linux' diff --git a/setup.cfg b/setup.cfg index 41898a2f..b52bdd37 100644 --- a/setup.cfg +++ b/setup.cfg @@ -2,7 +2,7 @@ name = dupeGuru version = attr: core.__version__ url = https://github.com/arsenetar/dupeguru -project_urls = +project_urls = Bug Reports = https://github.com/arsenetar/dupeguru/issues author = Andrew Senetar author_email = arsenetar@voltaicideas.net @@ -28,7 +28,7 @@ classifiers = [options] packages = find: python_requires = >=3.7 -install_requires = +install_requires = Send2Trash>=1.3.0 mutagen>=1.45.1 distro>=1.5.0 @@ -39,7 +39,7 @@ install_requires = setup_requires = sphinx>=3.0.0 polib>=1.1.0 -tests_require = +tests_require = pytest >=6,<7 include_package_data = true diff --git a/setup.nsi b/setup.nsi index 1480ad94..a6809cf7 100644 --- a/setup.nsi +++ b/setup.nsi @@ -51,7 +51,7 @@ SetCompressor /SOLID lzma !define APPICON "images\dgse_logo.ico" ; nor is the icon !define DISTDIR "dist" !define HELPURL "https://github.com/arsenetar/dupeguru/issues" -!define UPDATEURL "https://dupeguru.voltaicideas.net/" +!define UPDATEURL "https://dupeguru.voltaicideas.net/" !define ABOUTURL "https://dupeguru.voltaicideas.net/" ; Static Defines @@ -85,7 +85,7 @@ var InstallSize !define MULTIUSER_USE_PROGRAMFILES64 !endif !include MultiUser.nsh - + ; Modern UI 2 !include MUI2.nsh !define MUI_ICON "${APPICON}" @@ -111,8 +111,8 @@ Icon "${APPICON}" !insertmacro MUI_PAGE_DIRECTORY ; values for start menu page -!define MUI_STARTMENUPAGE_REGISTRY_ROOT "SHCTX" ; uses shell context -!define MUI_STARTMENUPAGE_REGISTRY_KEY "${BASEREGKEY}" +!define MUI_STARTMENUPAGE_REGISTRY_ROOT "SHCTX" ; uses shell context +!define MUI_STARTMENUPAGE_REGISTRY_KEY "${BASEREGKEY}" !define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder" !insertmacro MUI_PAGE_STARTMENU Application $StartMenuFolder @@ -122,11 +122,11 @@ Icon "${APPICON}" ; uninstaller pages !insertmacro MUI_UNPAGE_CONFIRM !insertmacro MUI_UNPAGE_INSTFILES - + ;============================================================================== ; Languages ;============================================================================== - + !insertmacro MUI_LANGUAGE "English" ;first language is the default language !insertmacro MUI_LANGUAGE "French" !insertmacro MUI_LANGUAGE "German" @@ -147,24 +147,24 @@ Icon "${APPICON}" ;============================================================================== ; Reserve Files ;============================================================================== - + ; If you are using solid compression, files that are required before ; the actual installation should be stored first in the data block, ; because this will make your installer start faster. - + !insertmacro MUI_RESERVEFILE_LANGDLL ReserveFile /nonfatal "${NSISDIR}\Plugins\*.dll" ;reserve if needed - + ;============================================================================== ; Installer Sections ;============================================================================== Section "!Application" AppSec SetOutPath "$INSTDIR" ; set from result of installer pages - + ; Files to install File /r "${SOURCEPATH}\${APPNAME}-win${BITS}\*" - + ; Create Start Menu Items !insertmacro MUI_STARTMENU_WRITE_BEGIN Application CreateDirectory "$SMPROGRAMS\$StartMenuFolder" @@ -193,7 +193,7 @@ Section "!Application" AppSec WriteRegStr HKCR ".dupeguru" "backup_val" "$1" ; backup current value NoBackup: WriteRegStr HKCR ".dupeguru" "" "${APPNAME}.File" ; set our file association - + ReadRegStr $0 HKCR "${APPNAME}.File" "" StrCmp $0 "" 0 Skip WriteRegStr HKCR "${APPNAME}.File" "" "${APPNAME} File" @@ -204,7 +204,7 @@ Skip: WriteRegStr HKCR "${APPNAME}.File\shell\edit" "" "Edit ${APPNAME} File" WriteRegStr HKCR "${APPNAME}.File\shell\edit\command" "" '"$INSTDIR\${APPNAME}-win${BITS}.exe" "%1"' - ; Uninstall Entry + ; Uninstall Entry WriteRegStr SHCTX "${UNINSTALLREG}" "DisplayName" "${APPNAME} ${VERSIONMAJOR}.${VERSIONMINOR}.${VERSIONPATCH}" WriteRegStr SHCTX "${UNINSTALLREG}" "DisplayVersion" "${VERSIONMAJOR}.${VERSIONMINOR}.${VERSIONPATCH}" WriteRegStr SHCTX "${UNINSTALLREG}" "DisplayIcon" "$INSTDIR\${APPNAME}.exe" @@ -253,7 +253,7 @@ Section "Uninstall" Delete "$INSTDIR\*.pyd" Delete "$INSTDIR\*.zip" Delete "$INSTDIR\*.manifest" - + ; Remove Install Folder if empty RMDir "$INSTDIR" @@ -263,7 +263,7 @@ Section "Uninstall" StrCmp $1 "" 0 Restore ; if backup="" then delete the whole key DeleteRegKey HKCR ".dupeGuru" Goto NotOwn - + Restore: WriteRegStr HKCR ".dupeguru" "" $1 DeleteRegValue HKCR ".dupeguru" "backup_val" @@ -293,7 +293,7 @@ Function .onInit !insertmacro MULTIUSER_INIT ; it appears that the languages shown may not always be filtered correctly !define MUI_LANGDLL_ALLLANGUAGES - !insertmacro MUI_LANGDLL_DISPLAY + !insertmacro MUI_LANGDLL_DISPLAY FunctionEnd Function un.onInit @@ -304,4 +304,4 @@ Function un.onInit !endif !insertmacro MULTIUSER_UNINIT !insertmacro MUI_UNGETLANGUAGE -FunctionEnd \ No newline at end of file +FunctionEnd diff --git a/win_version_info.temp b/win_version_info.temp index 7a3ee4ec..28f0fd47 100644 --- a/win_version_info.temp +++ b/win_version_info.temp @@ -37,7 +37,7 @@ VSVersionInfo( StringStruct(u'OriginalFilename', u'dupeguru-win{3}.exe'), StringStruct(u'ProductName', u'dupeGuru'), StringStruct(u'ProductVersion', u'{0}.{1}.{2}.0')]) - ]), + ]), VarFileInfo([VarStruct(u'Translation', [1033, 1200])]) ] ) From 78f4145910bca096be92013f387b7946ea45e1e5 Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Mon, 9 Jan 2023 23:02:19 -0600 Subject: [PATCH 21/56] chore: Remove unused qtlib.pot file --- locale/qtlib.pot | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 locale/qtlib.pot diff --git a/locale/qtlib.pot b/locale/qtlib.pot deleted file mode 100644 index 51dee467..00000000 --- a/locale/qtlib.pot +++ /dev/null @@ -1,6 +0,0 @@ - -msgid "" -msgstr "" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: utf-8\n" - From 81daddd072a29b4e916c132b836cffc5c888f868 Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Wed, 11 Jan 2023 00:58:29 -0600 Subject: [PATCH 22/56] refactor: Improve digest cache db method performance - Remove lock on read operations, only needed for write operations - Change to use context manager for sqlite connection - Remove long lived cursor object and use short lived cursors instead Fixes #1080 --- core/fs.py | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/core/fs.py b/core/fs.py index 6b3c2864..6d37fb83 100644 --- a/core/fs.py +++ b/core/fs.py @@ -112,53 +112,53 @@ class FilesDB: def __init__(self): self.conn = None - self.cur = None self.lock = None def connect(self, path: Union[AnyStr, os.PathLike]) -> None: self.conn = sqlite3.connect(path, check_same_thread=False) - self.cur = self.conn.cursor() self.lock = Lock() self._check_upgrade() def _check_upgrade(self) -> None: - with self.lock: - has_schema = self.cur.execute( + with self.lock, self.conn as conn: + has_schema = conn.execute( "SELECT NAME FROM sqlite_master WHERE type='table' AND name='schema_version'" ).fetchall() version = None if has_schema: - version = self.cur.execute("SELECT version FROM schema_version ORDER BY version DESC").fetchone()[0] + version = conn.execute("SELECT version FROM schema_version ORDER BY version DESC").fetchone()[0] else: - self.cur.execute("CREATE TABLE schema_version (version int PRIMARY KEY, description TEXT)") + conn.execute("CREATE TABLE schema_version (version int PRIMARY KEY, description TEXT)") if version != self.schema_version: - self.cur.execute(self.drop_table_query) - self.cur.execute( + conn.execute(self.drop_table_query) + conn.execute( "INSERT OR REPLACE INTO schema_version VALUES (:version, :description)", {"version": self.schema_version, "description": self.schema_version_description}, ) - self.cur.execute(self.create_table_query) - self.conn.commit() + conn.execute(self.create_table_query) def clear(self) -> None: - with self.lock: - self.cur.execute(self.drop_table_query) - self.cur.execute(self.create_table_query) + with self.lock, self.conn as conn: + conn.execute(self.drop_table_query) + conn.execute(self.create_table_query) def get(self, path: Path, key: str) -> Union[bytes, None]: stat = path.stat() size = stat.st_size mtime_ns = stat.st_mtime_ns try: - with self.lock: + with self.conn as conn: if self.ignore_mtime: - self.cur.execute(self.select_query_ignore_mtime.format(key=key), {"path": str(path), "size": size}) + cursor = conn.execute( + self.select_query_ignore_mtime.format(key=key), {"path": str(path), "size": size} + ) else: - self.cur.execute( + cursor = conn.execute( self.select_query.format(key=key), {"path": str(path), "size": size, "mtime_ns": mtime_ns}, ) - result = self.cur.fetchone() + result = cursor.fetchone() + cursor.close() if result: return result[0] @@ -172,8 +172,8 @@ def put(self, path: Path, key: str, value: Any) -> None: size = stat.st_size mtime_ns = stat.st_mtime_ns try: - with self.lock: - self.cur.execute( + with self.lock, self.conn as conn: + conn.execute( self.insert_query.format(key=key), {"path": str(path), "size": size, "mtime_ns": mtime_ns, "value": value}, ) @@ -186,7 +186,6 @@ def commit(self) -> None: def close(self) -> None: with self.lock: - self.cur.close() self.conn.close() From 057be0294aae40bb39bcfbec51c777489b5ab1a3 Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Wed, 11 Jan 2023 23:07:06 -0600 Subject: [PATCH 23/56] fix: Prevent exception during existence check - Add "safe" existence check to files which catches OSErrors that may occur when trying to stat files - Use "safe" existence check during final existence check --- core/fs.py | 8 ++++++++ core/scanner.py | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/core/fs.py b/core/fs.py index 6d37fb83..11a29a20 100644 --- a/core/fs.py +++ b/core/fs.py @@ -315,6 +315,14 @@ def can_handle(cls, path): """Returns whether this file wrapper class can handle ``path``.""" return not path.is_symlink() and path.is_file() + def exists(self) -> bool: + """Safely check if the underlying file exists, treat error as non-existent""" + try: + return self.path.exists() + except OSError as ex: + logging.warning(f"Checking {self.path} raised: {ex}") + return False + def rename(self, newname): if newname == self.name: return diff --git a/core/scanner.py b/core/scanner.py index 72550497..67124f20 100644 --- a/core/scanner.py +++ b/core/scanner.py @@ -172,7 +172,7 @@ def get_dupe_groups(self, files, ignore_list=None, j=job.nulljob): if not self.mix_file_kind: matches = [m for m in matches if get_file_ext(m.first.name) == get_file_ext(m.second.name)] if self.include_exists_check: - matches = [m for m in matches if m.first.path.exists() and m.second.path.exists()] + matches = [m for m in matches if m.first.exists() and m.second.exists()] matches = [m for m in matches if not (m.first.is_ref and m.second.is_ref)] if ignore_list: matches = [m for m in matches if not ignore_list.are_ignored(str(m.first.path), str(m.second.path))] From c57042fdd2445558aa1de6c4fb82fdcd39c34e7e Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Wed, 11 Jan 2023 23:20:40 -0600 Subject: [PATCH 24/56] fix: Resolve issue with mock object for core test Last change introduced a new method on the fs.File object that the test object did not have. Add similar method to test object. --- core/tests/scanner_test.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/tests/scanner_test.py b/core/tests/scanner_test.py index e9379246..35b18395 100644 --- a/core/tests/scanner_test.py +++ b/core/tests/scanner_test.py @@ -17,6 +17,7 @@ from core.me.scanner import ScannerME +# TODO update this to be able to inherit from fs.File class NamedObject: def __init__(self, name="foobar", size=1, path=None): if path is None: @@ -31,6 +32,9 @@ def __init__(self, name="foobar", size=1, path=None): def __repr__(self): return "".format(self.name, self.path) + def exists(self): + return self.path.exists() + no = NamedObject From d81759f77fc64969477f0844063f1ade6ee372e8 Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Wed, 11 Jan 2023 23:53:02 -0600 Subject: [PATCH 25/56] fix: Specify maximum python version for deb Specify maximum supported python version so attempts to install are met with better errors. --- pkg/debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/debian/control b/pkg/debian/control index 37a41f55..1f84eac4 100644 --- a/pkg/debian/control +++ b/pkg/debian/control @@ -10,7 +10,7 @@ Vcs-Git: https://github.com/arsenetar/dupeguru.git Package: {pkgname} Architecture: {arch} -Depends: ${shlibs:Depends}, python3 (>=3.7), python3-pyqt5, python3-mutagen, python3-semantic-version +Depends: ${shlibs:Depends}, python3 (>=3.7), python3 (<<3.12), python3-pyqt5, python3-mutagen, python3-semantic-version Provides: dupeguru-se, dupeguru-me, dupeguru-pe Replaces: dupeguru-se, dupeguru-me, dupeguru-pe Conflicts: dupeguru-se, dupeguru-me, dupeguru-pe From 8125e3ec9735bbb6dbfa1cf47abe752c8b2249c2 Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Thu, 12 Jan 2023 23:30:35 -0600 Subject: [PATCH 26/56] chore: Add rulers to vscode settings, format --- .vscode/extensions.json | 3 ++- .vscode/settings.json | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 67d11681..4139be42 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -5,6 +5,7 @@ "ms-python.vscode-pylance", "ms-python.python" ], - // List of extensions recommended by VS Code that should not be recommended for users of this workspace. + // List of extensions recommended by VS Code that should not be recommended for + // users of this workspace. "unwantedRecommendations": [] } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 8bcde0b9..7fdb42b7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,9 +4,10 @@ "Dupras", "hscommon" ], + "editor.rulers": [ + 88, + 120 + ], "python.languageServer": "Pylance", "yaml.schemaStore.enable": true, - "yaml.schemas": { - "https://json.schemastore.org/github-workflow.json": ".github/workflows/*.yml" - } } \ No newline at end of file From 549eb7f1539bbce4b054c4e7d545f034eb5e5853 Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Thu, 12 Jan 2023 23:51:05 -0600 Subject: [PATCH 27/56] chore: Add vscode launch.json --- .gitignore | 4 ++-- .vscode/launch.json | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 .vscode/launch.json diff --git a/.gitignore b/.gitignore index 46487bc4..13131b1c 100644 --- a/.gitignore +++ b/.gitignore @@ -87,8 +87,8 @@ cython_debug/ # Visual Studio Code .vscode/* !.vscode/settings.json -#!.vscode/tasks.json -#!.vscode/launch.json +!.vscode/tasks.json +!.vscode/launch.json !.vscode/extensions.json !.vscode/*.code-snippets diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..6d3ca8ce --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,17 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "DupuGuru", + "type": "python", + "request": "launch", + "program": "run.py", + "console": "integratedTerminal", + "subProcess": true, + "justMyCode": false + }, + ] +} \ No newline at end of file From 46521c8af11cdf072a7fb0af8be70c528bd3261e Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Fri, 13 Jan 2023 00:05:47 -0600 Subject: [PATCH 28/56] feat: Add migration for picture cache db - Add migration (just delete db and change to new schema) for picture cache following the same sort of strategy as the file digest cache - Rename mtime column to mtime_ns to match file cache for consistency --- core/pe/cache_sqlite.py | 52 +++++++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/core/pe/cache_sqlite.py b/core/pe/cache_sqlite.py index bf9f7c5b..4cb3c588 100644 --- a/core/pe/cache_sqlite.py +++ b/core/pe/cache_sqlite.py @@ -15,6 +15,14 @@ class SqliteCache: """A class to cache picture blocks in a sqlite backend.""" + schema_version = 1 + schema_version_description = "Changed from string to bytes for blocks." + + create_table_query = "CREATE TABLE IF NOT EXISTS pictures(path TEXT, mtime_ns INTEGER, blocks BLOB)" + create_index_query = "CREATE INDEX IF NOT EXISTS idx_path on pictures (path)" + drop_table_query = "DROP TABLE IF EXISTS pictures" + drop_index_query = "DROP INDEX IF EXISTS idx_path" + def __init__(self, db=":memory:", readonly=False): # readonly is not used in the sqlite version of the cache self.dbname = db @@ -62,9 +70,9 @@ def __setitem__(self, path_str, blocks): else: mtime = 0 if path_str in self: - sql = "update pictures set blocks = ?, mtime = ? where path = ?" + sql = "update pictures set blocks = ?, mtime_ns = ? where path = ?" else: - sql = "insert into pictures(blocks,mtime,path) values(?,?,?)" + sql = "insert into pictures(blocks,mtime_ns,path) values(?,?,?)" try: self.con.execute(sql, [blocks, mtime, path_str]) except sqlite.OperationalError: @@ -73,18 +81,9 @@ def __setitem__(self, path_str, blocks): logging.warning("DatabaseError while setting value for key %r: %s", path_str, str(e)) def _create_con(self, second_try=False): - def create_tables(): - logging.debug("Creating picture cache tables.") - self.con.execute("drop table if exists pictures") - self.con.execute("drop index if exists idx_path") - self.con.execute("create table pictures(path TEXT, mtime INTEGER, blocks BLOB)") - self.con.execute("create index idx_path on pictures (path)") - - self.con = sqlite.connect(self.dbname, isolation_level=None) try: - self.con.execute("select path, mtime, blocks from pictures where 1=2") - except sqlite.OperationalError: # new db - create_tables() + self.con = sqlite.connect(self.dbname, isolation_level=None) + self._check_upgrade() except sqlite.DatabaseError as e: # corrupted db if second_try: raise # Something really strange is happening @@ -93,6 +92,25 @@ def create_tables(): os.remove(self.dbname) self._create_con(second_try=True) + def _check_upgrade(self) -> None: + with self.con as conn: + has_schema = conn.execute( + "SELECT NAME FROM sqlite_master WHERE type='table' AND name='schema_version'" + ).fetchall() + version = None + if has_schema: + version = conn.execute("SELECT version FROM schema_version ORDER BY version DESC").fetchone()[0] + else: + conn.execute("CREATE TABLE schema_version (version int PRIMARY KEY, description TEXT)") + if version != self.schema_version: + conn.execute(self.drop_table_query) + conn.execute( + "INSERT OR REPLACE INTO schema_version VALUES (:version, :description)", + {"version": self.schema_version, "description": self.schema_version_description}, + ) + conn.execute(self.create_table_query) + conn.execute(self.create_index_query) + def clear(self): self.close() if self.dbname != ":memory:": @@ -129,12 +147,12 @@ def purge_outdated(self): the db. """ todelete = [] - sql = "select rowid, path, mtime from pictures" + sql = "select rowid, path, mtime_ns from pictures" cur = self.con.execute(sql) - for rowid, path_str, mtime in cur: - if mtime and op.exists(path_str): + for rowid, path_str, mtime_ns in cur: + if mtime_ns and op.exists(path_str): picture_mtime = os.stat(path_str).st_mtime - if int(picture_mtime) <= mtime: + if int(picture_mtime) <= mtime_ns: # not outdated continue todelete.append(rowid) From 6d8b86b7eb1ae1763948b31dfba7a83dd5a8f1d7 Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Mon, 27 Feb 2023 17:58:15 -0600 Subject: [PATCH 29/56] fix(core): Remove old directory state logic - Remove code forcing the exclusion of `.` directories by default, the new default exclusion filters do this by default - Change default state code to always return a value --- core/directories.py | 5 +++-- core/tests/directories_test.py | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/core/directories.py b/core/directories.py index 3dbf58e0..47418077 100644 --- a/core/directories.py +++ b/core/directories.py @@ -84,10 +84,11 @@ def _default_state_for_path(self, path): for denied_path_re in self._exclude_list.compiled: if denied_path_re.match(str(path.name)): return DirectoryState.EXCLUDED - # return # We still use the old logic to force state on hidden dirs + return DirectoryState.NORMAL # Override this in subclasses to specify the state of some special folders. if path.name.startswith("."): return DirectoryState.EXCLUDED + return DirectoryState.NORMAL def _get_files(self, from_path, fileclasses, j): try: @@ -214,7 +215,7 @@ def get_state(self, path): # direct match? easy result. if path in self.states: return self.states[path] - state = self._default_state_for_path(path) or DirectoryState.NORMAL + state = self._default_state_for_path(path) # Save non-default states in cache, necessary for _get_files() if state != DirectoryState.NORMAL: self.states[path] = state diff --git a/core/tests/directories_test.py b/core/tests/directories_test.py index f9837534..8e0fe7bf 100644 --- a/core/tests/directories_test.py +++ b/core/tests/directories_test.py @@ -326,6 +326,7 @@ class MyDirectories(Directories): def _default_state_for_path(self, path): if "foobar" in path.parts: return DirectoryState.EXCLUDED + return DirectoryState.NORMAL d = MyDirectories() p1 = Path(str(tmpdir)) From aade6593ac8165a6d22bb52de9d43d7088c67e25 Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Thu, 27 Apr 2023 00:49:03 -0500 Subject: [PATCH 30/56] feat: Update translations from transifex --- locale/ar/LC_MESSAGES/columns.po | 11 +- locale/ar/LC_MESSAGES/core.po | 115 ++++++++-------- locale/ar/LC_MESSAGES/ui.po | 216 ++++++++++++++++++++++++++++++- locale/de/LC_MESSAGES/ui.po | 41 ++++-- locale/it/LC_MESSAGES/core.po | 84 ++++++------ locale/ko/LC_MESSAGES/columns.po | 12 +- locale/ko/LC_MESSAGES/core.po | 72 +++++------ locale/ko/LC_MESSAGES/ui.po | 98 ++++++++------ locale/ru/LC_MESSAGES/columns.po | 15 ++- locale/ru/LC_MESSAGES/core.po | 99 +++++++------- locale/ru/LC_MESSAGES/ui.po | 144 ++++++++++++--------- locale/tr/LC_MESSAGES/ui.po | 34 +++-- locale/zh_CN/LC_MESSAGES/ui.po | 48 ++++--- locale/zh_TW/LC_MESSAGES/ui.po | 46 ++++--- 14 files changed, 675 insertions(+), 360 deletions(-) diff --git a/locale/ar/LC_MESSAGES/columns.po b/locale/ar/LC_MESSAGES/columns.po index afedd1e1..39efb9e7 100644 --- a/locale/ar/LC_MESSAGES/columns.po +++ b/locale/ar/LC_MESSAGES/columns.po @@ -1,7 +1,10 @@ +# Translators: +# Andrew Senetar , 2022 # msgid "" msgstr "" -"Language-Team: Arabic (https://www.transifex.com/voltaicideas/teams/116153/ar/)\n" +"Last-Translator: Andrew Senetar , 2022\n" +"Language-Team: Arabic (https://app.transifex.com/voltaicideas/teams/116153/ar/)\n" "Language: ar\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" @@ -28,7 +31,7 @@ msgstr "معدل البت" msgid "Samplerate" msgstr "معدل العينة" -#: core\me\result_table.py:19 core\pe\result_table.py:19 core\prioritize.py:92 +#: core\me\result_table.py:19 core\pe\result_table.py:19 core\prioritize.py:94 #: core\se\result_table.py:19 msgid "Filename" msgstr "اسم الملف" @@ -56,7 +59,7 @@ msgid "Kind" msgstr "طيب القلب" #: core\me\result_table.py:26 core\pe\result_table.py:25 -#: core\prioritize.py:163 core\se\result_table.py:23 +#: core\prioritize.py:165 core\se\result_table.py:23 msgid "Modification" msgstr "تعديل" @@ -114,6 +117,6 @@ msgstr "الحجم (كيلو بايت)" msgid "EXIF Timestamp" msgstr "الطابع الزمني EXIF" -#: core\prioritize.py:156 +#: core\prioritize.py:158 msgid "Size" msgstr "بحجم" diff --git a/locale/ar/LC_MESSAGES/core.po b/locale/ar/LC_MESSAGES/core.po index 9f182536..c155dc1f 100644 --- a/locale/ar/LC_MESSAGES/core.po +++ b/locale/ar/LC_MESSAGES/core.po @@ -1,139 +1,150 @@ +# Translators: +# Andrew Senetar , 2022 # msgid "" msgstr "" -"Language-Team: Arabic (https://www.transifex.com/voltaicideas/teams/116153/ar/)\n" +"Last-Translator: Andrew Senetar , 2022\n" +"Language-Team: Arabic (https://app.transifex.com/voltaicideas/teams/116153/ar/)\n" "Language: ar\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" "Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n" -#: core\app.py:42 +#: core\app.py:44 msgid "There are no marked duplicates. Nothing has been done." msgstr "" -#: core\app.py:43 +#: core\app.py:45 msgid "There are no selected duplicates. Nothing has been done." msgstr "" -#: core\app.py:44 +#: core\app.py:46 msgid "" "You're about to open many files at once. Depending on what those files are " "opened with, doing so can create quite a mess. Continue?" msgstr "" -#: core\app.py:71 +#: core\app.py:73 msgid "Scanning for duplicates" msgstr "" -#: core\app.py:72 +#: core\app.py:74 msgid "Loading" msgstr "" -#: core\app.py:73 +#: core\app.py:75 msgid "Moving" msgstr "" -#: core\app.py:74 +#: core\app.py:76 msgid "Copying" msgstr "" -#: core\app.py:75 +#: core\app.py:77 msgid "Sending to Trash" msgstr "" -#: core\app.py:308 +#: core\app.py:293 msgid "" "A previous action is still hanging in there. You can't start a new one yet. " "Wait a few seconds, then try again." msgstr "" -#: core\app.py:318 +#: core\app.py:304 msgid "No duplicates found." msgstr "" -#: core\app.py:333 +#: core\app.py:319 msgid "All marked files were copied successfully." msgstr "" -#: core\app.py:334 +#: core\app.py:321 msgid "All marked files were moved successfully." msgstr "" -#: core\app.py:335 +#: core\app.py:323 +msgid "All marked files were deleted successfully." +msgstr "" + +#: core\app.py:325 msgid "All marked files were successfully sent to Trash." msgstr "" -#: core\app.py:343 +#: core\app.py:330 msgid "Could not load file: {}" msgstr "" -#: core\app.py:399 +#: core\app.py:386 msgid "'{}' already is in the list." msgstr "" -#: core\app.py:401 +#: core\app.py:388 msgid "'{}' does not exist." msgstr "" -#: core\app.py:410 +#: core\app.py:396 msgid "" "All selected %d matches are going to be ignored in all subsequent scans. " "Continue?" msgstr "" -#: core\app.py:486 +#: core\app.py:473 msgid "Select a directory to copy marked files to" msgstr "" -#: core\app.py:487 +#: core\app.py:475 msgid "Select a directory to move marked files to" msgstr "" -#: core\app.py:527 +#: core\app.py:514 msgid "Select a destination for your exported CSV" msgstr "" -#: core\app.py:534 core\app.py:801 core\app.py:811 +#: core\app.py:520 core\app.py:781 core\app.py:791 msgid "Couldn't write to file: {}" msgstr "" -#: core\app.py:559 +#: core\app.py:543 msgid "You have no custom command set up. Set it up in your preferences." msgstr "" -#: core\app.py:727 core\app.py:740 +#: core\app.py:705 core\app.py:717 msgid "You are about to remove %d files from results. Continue?" msgstr "" -#: core\app.py:774 +#: core\app.py:753 msgid "{} duplicate groups were changed by the re-prioritization." msgstr "" -#: core\app.py:821 +#: core\app.py:801 msgid "The selected directories contain no scannable file." msgstr "" -#: core\app.py:835 +#: core\app.py:817 msgid "Collecting files to scan" msgstr "" -#: core\app.py:891 +#: core\app.py:867 msgid "%s (%d discarded)" msgstr "" -#: core\engine.py:244 core\engine.py:288 -msgid "0 matches found" +#: core\directories.py:190 +msgid "Collected {} files to scan" +msgstr "" + +#: core\directories.py:206 +msgid "Collected {} folders to scan" msgstr "" -#: core\engine.py:262 core\engine.py:296 -msgid "%d matches found" +#: core\engine.py:27 +msgid "%d matches found from %d groups" msgstr "" -#: core\gui\deletion_options.py:73 +#: core\gui\deletion_options.py:71 msgid "You are sending {} file(s) to the Trash." msgstr "" -#: core\gui\exclude_list_table.py:15 +#: core\gui\exclude_list_table.py:14 msgid "Regular Expressions" msgstr "" @@ -143,7 +154,7 @@ msgstr "" #: core\me\scanner.py:20 core\se\scanner.py:16 msgid "Filename" -msgstr "" +msgstr "اسم الملف" #: core\me\scanner.py:21 msgid "Filename - Fields" @@ -165,15 +176,15 @@ msgstr "" msgid "Analyzed %d/%d pictures" msgstr "" -#: core\pe\matchblock.py:181 +#: core\pe\matchblock.py:177 msgid "Performed %d/%d chunk matches" msgstr "" -#: core\pe\matchblock.py:191 +#: core\pe\matchblock.py:185 msgid "Preparing for matching" msgstr "" -#: core\pe\matchblock.py:244 +#: core\pe\matchblock.py:234 msgid "Verified %d/%d matches" msgstr "" @@ -183,61 +194,61 @@ msgstr "" #: core\pe\scanner.py:22 msgid "EXIF Timestamp" -msgstr "" +msgstr "الطابع الزمني EXIF" #: core\prioritize.py:70 msgid "None" msgstr "" -#: core\prioritize.py:100 +#: core\prioritize.py:102 msgid "Ends with number" msgstr "" -#: core\prioritize.py:101 +#: core\prioritize.py:103 msgid "Doesn't end with number" msgstr "" -#: core\prioritize.py:102 +#: core\prioritize.py:104 msgid "Longest" msgstr "" -#: core\prioritize.py:103 +#: core\prioritize.py:105 msgid "Shortest" msgstr "" -#: core\prioritize.py:140 +#: core\prioritize.py:142 msgid "Highest" msgstr "" -#: core\prioritize.py:140 +#: core\prioritize.py:142 msgid "Lowest" msgstr "" -#: core\prioritize.py:169 +#: core\prioritize.py:171 msgid "Newest" msgstr "" -#: core\prioritize.py:169 +#: core\prioritize.py:171 msgid "Oldest" msgstr "" -#: core\results.py:142 +#: core\results.py:134 msgid "%d / %d (%s / %s) duplicates marked." msgstr "" -#: core\results.py:149 +#: core\results.py:141 msgid " filter: %s" msgstr "" -#: core\scanner.py:85 +#: core\scanner.py:90 msgid "Read size of %d/%d files" msgstr "" -#: core\scanner.py:109 +#: core\scanner.py:116 msgid "Read metadata of %d/%d files" msgstr "" -#: core\scanner.py:147 +#: core\scanner.py:154 msgid "Almost done! Fiddling with results..." msgstr "" diff --git a/locale/ar/LC_MESSAGES/ui.po b/locale/ar/LC_MESSAGES/ui.po index 99f5db4b..ad0b055b 100644 --- a/locale/ar/LC_MESSAGES/ui.po +++ b/locale/ar/LC_MESSAGES/ui.po @@ -1,7 +1,10 @@ +# Translators: +# Andrew Senetar , 2022 # msgid "" msgstr "" -"Language-Team: Arabic (https://www.transifex.com/voltaicideas/teams/116153/ar/)\n" +"Last-Translator: Andrew Senetar , 2022\n" +"Language-Team: Arabic (https://app.transifex.com/voltaicideas/teams/116153/ar/)\n" "Language: ar\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" @@ -249,23 +252,23 @@ msgstr "" #: qt/me/preferences_dialog.py:38 cocoa/en.lproj/Localizable.strings:0 msgid "Artist" -msgstr "" +msgstr "فنان" #: qt/me/preferences_dialog.py:40 cocoa/en.lproj/Localizable.strings:0 msgid "Album" -msgstr "" +msgstr "البوم" #: qt/me/preferences_dialog.py:42 cocoa/en.lproj/Localizable.strings:0 msgid "Title" -msgstr "" +msgstr "عنوان" #: qt/me/preferences_dialog.py:44 cocoa/en.lproj/Localizable.strings:0 msgid "Genre" -msgstr "" +msgstr "النوع" #: qt/me/preferences_dialog.py:46 cocoa/en.lproj/Localizable.strings:0 msgid "Year" -msgstr "" +msgstr "سنة" #: qt/me/preferences_dialog.py:50 qt/se/preferences_dialog.py:30 #: cocoa/en.lproj/Localizable.strings:0 @@ -908,3 +911,204 @@ msgstr "" #: qt\preferences_dialog.py:286 msgid "Display" msgstr "" + +#: qt\se\preferences_dialog.py:70 +msgid "Partially hash files bigger than" +msgstr "" + +#: qt\se\preferences_dialog.py:80 +msgid "MB" +msgstr "" + +#: qt\preferences_dialog.py:163 +msgid "Use native OS dialogs" +msgstr "" + +#: qt\preferences_dialog.py:166 +msgid "" +"For actions such as file/folder selection use the OS native dialogs.\n" +"Some native dialogs have limited functionality." +msgstr "" + +#: qt\se\preferences_dialog.py:68 +msgid "Ignore files larger than" +msgstr "" + +#: qt\app.py:135 qt\app.py:293 +msgid "Clear Cache" +msgstr "" + +#: qt\app.py:294 +msgid "" +"Do you really want to clear the cache? This will remove all cached file " +"hashes and picture analysis." +msgstr "" + +#: qt\app.py:299 +msgid "Cache cleared." +msgstr "" + +#: qt\preferences_dialog.py:173 +msgid "Use dark style" +msgstr "" + +#: qt\preferences_dialog.py:241 +msgid "Profile scan operation" +msgstr "" + +#: qt\preferences_dialog.py:242 +msgid "Profile the scan operation and save logs for optimization." +msgstr "" + +#: qt\preferences_dialog.py:246 +msgid "Logs located in: {}" +msgstr "" + +#: qt\preferences_dialog.py:291 +msgid "Debug" +msgstr "" + +#: qt\about_box.py:31 +msgid "About {}" +msgstr "" + +#: qt\about_box.py:47 +msgid "Version {}" +msgstr "" + +#: qt\about_box.py:49 qt\about_box.py:75 +msgid "Checking for updates..." +msgstr "" + +#: qt\about_box.py:54 +msgid "Licensed under GPLv3" +msgstr "" + +#: qt\about_box.py:68 +msgid "No update available." +msgstr "" + +#: qt\about_box.py:71 +msgid "New version {} available, download here." +msgstr "" + +#: qt\error_report_dialog.py:50 +msgid "Error Report" +msgstr "" + +#: qt\error_report_dialog.py:54 +msgid "Something went wrong. How about reporting the error?" +msgstr "" + +#: qt\error_report_dialog.py:60 +msgid "" +"Error reports should be reported as Github issues. You can copy the error traceback above and paste it in a new issue.\n" +"\n" +"Please make sure to run a search for any already existing issues beforehand. Also make sure to test the very latest version available from the repository, since the bug you are experiencing might have already been patched.\n" +"\n" +"What usually really helps is if you add a description of how you got the error. Thanks!\n" +"\n" +"Although the application should continue to run after this error, it may be in an unstable state, so it is recommended that you restart the application." +msgstr "" + +#: qt\error_report_dialog.py:80 +msgid "Go to Github" +msgstr "" + +#: qt\preferences.py:24 +msgid "Czech" +msgstr "" + +#: qt\preferences.py:25 +msgid "German" +msgstr "" + +#: qt\preferences.py:26 +msgid "Greek" +msgstr "" + +#: qt\preferences.py:27 +msgid "English" +msgstr "" + +#: qt\preferences.py:28 +msgid "Spanish" +msgstr "" + +#: qt\preferences.py:29 +msgid "French" +msgstr "" + +#: qt\preferences.py:30 +msgid "Armenian" +msgstr "" + +#: qt\preferences.py:31 +msgid "Italian" +msgstr "" + +#: qt\preferences.py:32 +msgid "Japanese" +msgstr "" + +#: qt\preferences.py:33 +msgid "Korean" +msgstr "" + +#: qt\preferences.py:34 +msgid "Malay" +msgstr "" + +#: qt\preferences.py:35 +msgid "Dutch" +msgstr "" + +#: qt\preferences.py:36 +msgid "Polish" +msgstr "" + +#: qt\preferences.py:37 +msgid "Brazilian" +msgstr "" + +#: qt\preferences.py:38 +msgid "Russian" +msgstr "" + +#: qt\preferences.py:39 +msgid "Turkish" +msgstr "" + +#: qt\preferences.py:40 +msgid "Ukrainian" +msgstr "" + +#: qt\preferences.py:41 +msgid "Vietnamese" +msgstr "" + +#: qt\preferences.py:42 +msgid "Chinese (Simplified)" +msgstr "" + +#: qt\recent.py:54 +msgid "Clear List" +msgstr "" + +#: qt\search_edit.py:78 +msgid "Search..." +msgstr "" + +#: qt\preferences_dialog.py:219 +msgid "" +"These options are for advanced users or for very specific situations, most " +"users should not have to modify these." +msgstr "" + +#: qt\preferences_dialog.py:225 +msgid "Include existence check after scan completion" +msgstr "" + +#: qt\preferences_dialog.py:227 +msgid "Ignore difference in mtime when loading cached digests" +msgstr "" diff --git a/locale/de/LC_MESSAGES/ui.po b/locale/de/LC_MESSAGES/ui.po index a407fc09..34ff0874 100644 --- a/locale/de/LC_MESSAGES/ui.po +++ b/locale/de/LC_MESSAGES/ui.po @@ -2,11 +2,12 @@ # Robert M, 2022 # Andrew Senetar , 2022 # Fuan , 2022 +# Frederik Gschaider , 2022 # msgid "" msgstr "" -"Last-Translator: Fuan , 2022\n" -"Language-Team: German (https://www.transifex.com/voltaicideas/teams/116153/de/)\n" +"Last-Translator: Frederik Gschaider , 2022\n" +"Language-Team: German (https://app.transifex.com/voltaicideas/teams/116153/de/)\n" "Language: de\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" @@ -974,37 +975,41 @@ msgstr "Ignoriere Dateien größer als" #: qt\app.py:135 qt\app.py:293 msgid "Clear Cache" -msgstr "" +msgstr "Zwischenspeicher leeren" #: qt\app.py:294 msgid "" "Do you really want to clear the cache? This will remove all cached file " "hashes and picture analysis." msgstr "" +"Möchten Sie den Zwischenspeicher wirklich löschen? Dadurch werden alle " +"zwischengespeicherten Datei-Prüfsummen und Bildanalysen entfernt." #: qt\app.py:299 msgid "Cache cleared." -msgstr "" +msgstr "Zwischenspeicher geleert." #: qt\preferences_dialog.py:173 msgid "Use dark style" -msgstr "" +msgstr "Dunklen Stil anwenden" #: qt\preferences_dialog.py:241 msgid "Profile scan operation" -msgstr "" +msgstr "Profil-Scanvorgang" #: qt\preferences_dialog.py:242 msgid "Profile the scan operation and save logs for optimization." msgstr "" +"Erstellen Sie ein Profil des Scanvorgangs und speichern Sie die Protokolle " +"zur Optimierung." #: qt\preferences_dialog.py:246 msgid "Logs located in: {}" -msgstr "" +msgstr "Die Protokolle befinden sich in: {}" #: qt\preferences_dialog.py:291 msgid "Debug" -msgstr "" +msgstr "Fehlerbehebung" #: qt\about_box.py:31 msgid "About {}" @@ -1016,7 +1021,7 @@ msgstr "Version {}" #: qt\about_box.py:49 qt\about_box.py:75 msgid "Checking for updates..." -msgstr "" +msgstr "Nach Aktualisierungen suchen..." #: qt\about_box.py:54 msgid "Licensed under GPLv3" @@ -1024,11 +1029,11 @@ msgstr "Lizenziert unter GPLv3" #: qt\about_box.py:68 msgid "No update available." -msgstr "" +msgstr "Keine Aktualisierung verfügbar." #: qt\about_box.py:71 msgid "New version {} available, download here." -msgstr "" +msgstr "Neue Version {} verfügbar, hier herunterladen." #: qt\error_report_dialog.py:50 msgid "Error Report" @@ -1143,3 +1148,17 @@ msgstr "Liste löschen" #: qt\search_edit.py:78 msgid "Search..." msgstr "Suche..." + +#: qt\preferences_dialog.py:219 +msgid "" +"These options are for advanced users or for very specific situations, most " +"users should not have to modify these." +msgstr "" + +#: qt\preferences_dialog.py:225 +msgid "Include existence check after scan completion" +msgstr "" + +#: qt\preferences_dialog.py:227 +msgid "Ignore difference in mtime when loading cached digests" +msgstr "" diff --git a/locale/it/LC_MESSAGES/core.po b/locale/it/LC_MESSAGES/core.po index 1646dd16..0219cd8d 100644 --- a/locale/it/LC_MESSAGES/core.po +++ b/locale/it/LC_MESSAGES/core.po @@ -1,27 +1,27 @@ # Translators: -# Andrew Senetar , 2021 # Fuan , 2021 # Emanuele, 2021 +# Andrew Senetar , 2022 # msgid "" msgstr "" -"Last-Translator: Emanuele, 2021\n" -"Language-Team: Italian (https://www.transifex.com/voltaicideas/teams/116153/it/)\n" +"Last-Translator: Andrew Senetar , 2022\n" +"Language-Team: Italian (https://app.transifex.com/voltaicideas/teams/116153/it/)\n" "Language: it\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" -#: core\app.py:42 +#: core\app.py:44 msgid "There are no marked duplicates. Nothing has been done." msgstr "Non ci sono duplicati marcati. Nessuna operazione è stata completata." -#: core\app.py:43 +#: core\app.py:45 msgid "There are no selected duplicates. Nothing has been done." msgstr "" "Non ci sono duplicati selezionati. Nessuna operazione è stata completata." -#: core\app.py:44 +#: core\app.py:46 msgid "" "You're about to open many files at once. Depending on what those files are " "opened with, doing so can create quite a mess. Continue?" @@ -29,27 +29,27 @@ msgstr "" "Stai per aprire molti file contemporaneamente. A seconda di quale programma " "li aprirà, potrebbe crearsi un bel casino. Vuoi continuare?" -#: core\app.py:71 +#: core\app.py:73 msgid "Scanning for duplicates" msgstr "Scansione per i duplicati" -#: core\app.py:72 +#: core\app.py:74 msgid "Loading" msgstr "Caricamento" -#: core\app.py:73 +#: core\app.py:75 msgid "Moving" msgstr "Spostamento" -#: core\app.py:74 +#: core\app.py:76 msgid "Copying" msgstr "Copia in corso" -#: core\app.py:75 +#: core\app.py:77 msgid "Sending to Trash" msgstr "Spostamento nel cestino" -#: core\app.py:289 +#: core\app.py:293 msgid "" "A previous action is still hanging in there. You can't start a new one yet. " "Wait a few seconds, then try again." @@ -57,39 +57,39 @@ msgstr "" "Un'azione precedente è ancora in corso. Non puoi cominciarne una nuova. " "Aspetta qualche secondo e quindi riprova." -#: core\app.py:300 +#: core\app.py:304 msgid "No duplicates found." msgstr "Non sono stati trovati dei duplicati." -#: core\app.py:315 +#: core\app.py:319 msgid "All marked files were copied successfully." msgstr "Tutti i file marcati sono stati copiati correttamente." -#: core\app.py:317 +#: core\app.py:321 msgid "All marked files were moved successfully." msgstr "Tutti i file marcati sono stati spostati correttamente." -#: core\app.py:319 +#: core\app.py:323 msgid "All marked files were deleted successfully." msgstr "Tutti i file marcati sono stati cancellati correttamente." -#: core\app.py:321 +#: core\app.py:325 msgid "All marked files were successfully sent to Trash." msgstr "Tutti i file marcati sono stati spostati nel cestino." -#: core\app.py:326 +#: core\app.py:330 msgid "Could not load file: {}" msgstr "Impossibile caricare il file: {}" -#: core\app.py:382 +#: core\app.py:386 msgid "'{}' already is in the list." msgstr "'{}' è già nella lista." -#: core\app.py:384 +#: core\app.py:388 msgid "'{}' does not exist." msgstr "'{}' non esiste." -#: core\app.py:392 +#: core\app.py:396 msgid "" "All selected %d matches are going to be ignored in all subsequent scans. " "Continue?" @@ -97,54 +97,54 @@ msgstr "" "Tutti i %d elementi che coincidono verranno ignorati in tutte le scansioni " "successive. Continuare?" -#: core\app.py:469 +#: core\app.py:473 msgid "Select a directory to copy marked files to" msgstr "Seleziona una directory in cui desideri copiare i file contrassegnati" -#: core\app.py:471 +#: core\app.py:475 msgid "Select a directory to move marked files to" msgstr "" "Seleziona una directory in cui desideri spostare i file contrassegnati" -#: core\app.py:510 +#: core\app.py:514 msgid "Select a destination for your exported CSV" msgstr "Seleziona una destinazione per il file CSV" -#: core\app.py:516 core\app.py:771 core\app.py:781 +#: core\app.py:520 core\app.py:781 core\app.py:791 msgid "Couldn't write to file: {}" msgstr "Impossibile modificare il file: {}" -#: core\app.py:539 +#: core\app.py:543 msgid "You have no custom command set up. Set it up in your preferences." msgstr "" "Non hai impostato nessun comando personalizzato. Impostalo nelle tue " "preferenze." -#: core\app.py:695 core\app.py:707 +#: core\app.py:705 core\app.py:717 msgid "You are about to remove %d files from results. Continue?" msgstr "Stai per rimuovere %d file dai risultati. Continuare?" -#: core\app.py:743 +#: core\app.py:753 msgid "{} duplicate groups were changed by the re-prioritization." msgstr "{} gruppi duplicati sono stati cambiati dalla nuova priorirità" -#: core\app.py:790 +#: core\app.py:801 msgid "The selected directories contain no scannable file." msgstr "Le cartelle selezionate non contengono file da scansionare." -#: core\app.py:803 +#: core\app.py:817 msgid "Collecting files to scan" msgstr "Raccolta file da scansionare" -#: core\app.py:850 +#: core\app.py:867 msgid "%s (%d discarded)" msgstr "%s (%d scartati)" -#: core\directories.py:191 +#: core\directories.py:190 msgid "Collected {} files to scan" msgstr "Raccolti {} file da scansionare" -#: core\directories.py:207 +#: core\directories.py:206 msgid "Collected {} folders to scan" msgstr "Raccolte {} cartelle da scansionare" @@ -214,35 +214,35 @@ msgstr "Timestamp EXIF" msgid "None" msgstr "Nessuno" -#: core\prioritize.py:100 +#: core\prioritize.py:102 msgid "Ends with number" msgstr "Termina con un numero" -#: core\prioritize.py:101 +#: core\prioritize.py:103 msgid "Doesn't end with number" msgstr "Non termina con un numero" -#: core\prioritize.py:102 +#: core\prioritize.py:104 msgid "Longest" msgstr "Più lungo" -#: core\prioritize.py:103 +#: core\prioritize.py:105 msgid "Shortest" msgstr "Più corto" -#: core\prioritize.py:140 +#: core\prioritize.py:142 msgid "Highest" msgstr "Il più alto" -#: core\prioritize.py:140 +#: core\prioritize.py:142 msgid "Lowest" msgstr "Il più basso" -#: core\prioritize.py:169 +#: core\prioritize.py:171 msgid "Newest" msgstr "Il più nuovo" -#: core\prioritize.py:169 +#: core\prioritize.py:171 msgid "Oldest" msgstr "Il più vecchio" diff --git a/locale/ko/LC_MESSAGES/columns.po b/locale/ko/LC_MESSAGES/columns.po index f04f8054..83ab66c8 100644 --- a/locale/ko/LC_MESSAGES/columns.po +++ b/locale/ko/LC_MESSAGES/columns.po @@ -5,7 +5,7 @@ msgid "" msgstr "" "Last-Translator: Sangdon Lim, 2022\n" -"Language-Team: Korean (https://www.transifex.com/voltaicideas/teams/116153/ko/)\n" +"Language-Team: Korean (https://app.transifex.com/voltaicideas/teams/116153/ko/)\n" "Language: ko\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" @@ -18,7 +18,7 @@ msgstr "파일 경로" #: core\gui\problem_table.py:19 msgid "Error Message" -msgstr "에러 메시지" +msgstr "오류 메시지" #: core\me\prioritize.py:23 msgid "Duration" @@ -35,7 +35,7 @@ msgstr "샘플레이트" #: core\me\result_table.py:19 core\pe\result_table.py:19 core\prioritize.py:94 #: core\se\result_table.py:19 msgid "Filename" -msgstr "폴더명" +msgstr "파일 이름" #: core\me\result_table.py:20 core\pe\result_table.py:20 core\prioritize.py:75 #: core\se\result_table.py:20 @@ -95,11 +95,11 @@ msgstr "주석" #: core\me\result_table.py:34 core\pe\result_table.py:26 #: core\se\result_table.py:24 msgid "Match %" -msgstr "일치정도" +msgstr "일치율%" #: core\me\result_table.py:35 core\se\result_table.py:25 msgid "Words Used" -msgstr "사용된 단어수" +msgstr "단어 목록" #: core\me\result_table.py:36 core\pe\result_table.py:27 #: core\se\result_table.py:26 @@ -108,7 +108,7 @@ msgstr "중복파일 갯수" #: core\pe\prioritize.py:23 core\pe\result_table.py:23 msgid "Dimensions" -msgstr "치수" +msgstr "가로세로 크기" #: core\pe\result_table.py:21 core\se\result_table.py:21 msgid "Size (KB)" diff --git a/locale/ko/LC_MESSAGES/core.po b/locale/ko/LC_MESSAGES/core.po index e17eea5f..4c594560 100644 --- a/locale/ko/LC_MESSAGES/core.po +++ b/locale/ko/LC_MESSAGES/core.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Last-Translator: Sangdon Lim, 2022\n" -"Language-Team: Korean (https://www.transifex.com/voltaicideas/teams/116153/ko/)\n" +"Language-Team: Korean (https://app.transifex.com/voltaicideas/teams/116153/ko/)\n" "Language: ko\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" @@ -14,11 +14,11 @@ msgstr "" #: core\app.py:44 msgid "There are no marked duplicates. Nothing has been done." -msgstr "표시된 중복 항목이 없습니다. 아무것도하지 않았습니다." +msgstr "아무 파일도 마크되지 않아 작업을 수행하지 않았습니다." #: core\app.py:45 msgid "There are no selected duplicates. Nothing has been done." -msgstr "선택한 중복 항목이 없습니다. 아무것도하지 않았습니다." +msgstr "아무 파일도 선택되지 않아 작업을 수행하지 않았습니다." #: core\app.py:46 msgid "" @@ -28,11 +28,11 @@ msgstr "한 번에 많은 파일을 열려고 합니다. 시스템 설정에 따 #: core\app.py:73 msgid "Scanning for duplicates" -msgstr "중복 검색" +msgstr "중복 파일 검색 중" #: core\app.py:74 msgid "Loading" -msgstr "불러오는중" +msgstr "불러오는 중" #: core\app.py:75 msgid "Moving" @@ -46,87 +46,87 @@ msgstr "복사중" msgid "Sending to Trash" msgstr "휴지통으로 보내기" -#: core\app.py:291 +#: core\app.py:293 msgid "" "A previous action is still hanging in there. You can't start a new one yet. " "Wait a few seconds, then try again." msgstr "이전 작업이 아직 진행 중이어서 새 작업을 시작할 수 없습니다. 몇 초 후에 다시 시도해 보세요." -#: core\app.py:302 +#: core\app.py:304 msgid "No duplicates found." msgstr "중복 파일이 없습니다." -#: core\app.py:317 +#: core\app.py:319 msgid "All marked files were copied successfully." -msgstr "표시된 모든 파일이 성공적으로 복사되었습니다." +msgstr "마크한 모든 파일이 성공적으로 복사되었습니다." -#: core\app.py:319 +#: core\app.py:321 msgid "All marked files were moved successfully." -msgstr "표시된 모든 파일이 성공적으로 이동되었습니다." +msgstr "마크한 모든 파일이 성공적으로 이동되었습니다." -#: core\app.py:321 +#: core\app.py:323 msgid "All marked files were deleted successfully." -msgstr "표시된 모든 파일이 성공적으로 제거되었습니다." +msgstr "마크한 모든 파일이 성공적으로 삭제되었습니다." -#: core\app.py:323 +#: core\app.py:325 msgid "All marked files were successfully sent to Trash." -msgstr "표시된 모든 파일이 성공적으로 휴지통으로 전송되었습니다." +msgstr "마크한 모든 파일을 휴지통으로 보냈습니다." -#: core\app.py:328 +#: core\app.py:330 msgid "Could not load file: {}" -msgstr "파일을로드 할 수 없습니다 : {}" +msgstr "파일을 불러올 수 없습니다: {}" -#: core\app.py:384 +#: core\app.py:386 msgid "'{}' already is in the list." msgstr "'{}' 는 이미 목록에 있습니다." -#: core\app.py:386 +#: core\app.py:388 msgid "'{}' does not exist." msgstr "'{}' 가 존재하지 않습니다." -#: core\app.py:394 +#: core\app.py:396 msgid "" "All selected %d matches are going to be ignored in all subsequent scans. " "Continue?" -msgstr "선택된 %d개의 일치 항목은 모든 후속 검색에서 무시됩니다. 계속하다?" +msgstr "선택한 %d개 항목을 앞으로 검색에서 무시합니다. 진행할까요?" -#: core\app.py:471 +#: core\app.py:473 msgid "Select a directory to copy marked files to" -msgstr "표시된 파일을 복사 할 디렉토리를 선택하십시오" +msgstr "마크한 파일을 복사할 경로를 선택하세요:" -#: core\app.py:473 +#: core\app.py:475 msgid "Select a directory to move marked files to" -msgstr "표시된 파일을 이동할 디렉토리를 선택하십시오" +msgstr "마크한 파일을 이동할 경로를 선택하세요:" -#: core\app.py:512 +#: core\app.py:514 msgid "Select a destination for your exported CSV" -msgstr "내 보낸 CSV의 대상을 선택하십시오" +msgstr "CSV 파일을 저장할 폴더를 선택하세요" -#: core\app.py:518 core\app.py:773 core\app.py:783 +#: core\app.py:520 core\app.py:781 core\app.py:791 msgid "Couldn't write to file: {}" msgstr "파일에 쓸 수 없습니다 : {}" -#: core\app.py:541 +#: core\app.py:543 msgid "You have no custom command set up. Set it up in your preferences." msgstr "사용자 지정 명령을 설정하지 않았습니다. 기본 설정에서 설정하십시오." -#: core\app.py:697 core\app.py:709 +#: core\app.py:705 core\app.py:717 msgid "You are about to remove %d files from results. Continue?" msgstr "결과에서 %d 개의 파일을 제거하려고합니다. 실행할까요?" -#: core\app.py:745 +#: core\app.py:753 msgid "{} duplicate groups were changed by the re-prioritization." msgstr "{} 개의 중복 그룹이 우선 순위 재 지정으로 변경되었습니다." -#: core\app.py:792 +#: core\app.py:801 msgid "The selected directories contain no scannable file." msgstr "선택한 디렉토리에 스캔 가능한 파일이 없습니다." -#: core\app.py:808 +#: core\app.py:817 msgid "Collecting files to scan" msgstr "스캔 할 파일 수집" -#: core\app.py:858 +#: core\app.py:867 msgid "%s (%d discarded)" msgstr "%s (%d 폐기)" @@ -176,7 +176,7 @@ msgstr "내용" #: core\pe\matchblock.py:72 msgid "Analyzed %d/%d pictures" -msgstr "%d/%d 사진 분석" +msgstr "사진 %d/%d 개 분석됨" #: core\pe\matchblock.py:177 msgid "Performed %d/%d chunk matches" @@ -236,7 +236,7 @@ msgstr "가장 오래된" #: core\results.py:134 msgid "%d / %d (%s / %s) duplicates marked." -msgstr "%d / %d (%s / %s) 개의 중복이 표시되었습니다." +msgstr "%d / %d (%s / %s) 개의 중복 파일을 마크했습니다." #: core\results.py:141 msgid " filter: %s" diff --git a/locale/ko/LC_MESSAGES/ui.po b/locale/ko/LC_MESSAGES/ui.po index a9786ecf..6a1eafdc 100644 --- a/locale/ko/LC_MESSAGES/ui.po +++ b/locale/ko/LC_MESSAGES/ui.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Last-Translator: Sangdon Lim, 2022\n" -"Language-Team: Korean (https://www.transifex.com/voltaicideas/teams/116153/ko/)\n" +"Language-Team: Korean (https://app.transifex.com/voltaicideas/teams/116153/ko/)\n" "Language: ko\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" @@ -104,12 +104,12 @@ msgstr "속성" #: qt/details_table.py:16 cocoa/en.lproj/Localizable.strings:0 msgid "Selected" -msgstr "선택됨" +msgstr "선택한 파일" #: qt/details_table.py:16 qt/directories_model.py:24 #: cocoa/en.lproj/Localizable.strings:0 msgid "Reference" -msgstr "참조" +msgstr "기준 파일" #: qt/directories_dialog.py:64 cocoa/en.lproj/Localizable.strings:0 msgid "Load Results..." @@ -159,15 +159,15 @@ msgstr "표준" #: qt/directories_dialog.py:128 cocoa/en.lproj/Localizable.strings:0 msgid "Scan Type:" -msgstr "스캔 유형 :" +msgstr "검색 방식:" #: qt/directories_dialog.py:135 msgid "More Options" -msgstr "더 많은 옵션" +msgstr "설정" #: qt/directories_dialog.py:139 cocoa/en.lproj/Localizable.strings:0 msgid "Select folders to scan and press \"Scan\"." -msgstr "스캔 할 폴더를 선택하고 \"스캔\"을 누르십시오." +msgstr "검색할 폴더들을 목록에 추가하고 오른쪽 아래의 \"스캔\" 버튼을 누르십시오." #: qt/directories_dialog.py:163 cocoa/en.lproj/Localizable.strings:0 msgid "Load Results" @@ -219,7 +219,7 @@ msgstr "상태" #: qt/directories_model.py:24 cocoa/en.lproj/Localizable.strings:0 msgid "Excluded" -msgstr "제외된" +msgstr "제외" #: qt/directories_model.py:24 cocoa/en.lproj/Localizable.strings:0 msgid "Normal" @@ -242,7 +242,7 @@ msgstr "닫기" #: qt/result_window.py:56 qt/result_window.py:192 qt/se/details_dialog.py:18 #: cocoa/en.lproj/Localizable.strings:0 msgid "Details" -msgstr "세부사항" +msgstr "파일 속성" #: qt/me/preferences_dialog.py:30 cocoa/en.lproj/Localizable.strings:0 msgid "Tags to scan:" @@ -275,17 +275,17 @@ msgstr "년" #: qt/me/preferences_dialog.py:50 qt/se/preferences_dialog.py:30 #: cocoa/en.lproj/Localizable.strings:0 msgid "Word weighting" -msgstr "단어 가중치" +msgstr "파일 이름 중 긴 단어에 가중치 적용" #: qt/me/preferences_dialog.py:52 qt/se/preferences_dialog.py:32 #: cocoa/en.lproj/Localizable.strings:0 msgid "Match similar words" -msgstr "유사한 단어와 일치" +msgstr "파일 이름 중 비슷한 단어도 중복으로 허용" #: qt/me/preferences_dialog.py:54 qt/pe/preferences_dialog.py:21 #: qt/se/preferences_dialog.py:34 cocoa/en.lproj/Localizable.strings:0 msgid "Can mix file kind" -msgstr "다른 확장자의 파일도 비교에 포함" +msgstr "확장자가 다른 파일도 중복 여부 확인" #: qt/me/preferences_dialog.py:56 qt/pe/preferences_dialog.py:23 #: qt/se/preferences_dialog.py:36 cocoa/en.lproj/Localizable.strings:0 @@ -295,7 +295,7 @@ msgstr "필터링 할 때 정규식 사용" #: qt/me/preferences_dialog.py:58 qt/pe/preferences_dialog.py:25 #: qt/se/preferences_dialog.py:38 cocoa/en.lproj/Localizable.strings:0 msgid "Remove empty folders on delete or move" -msgstr "삭제 또는 이동시 빈 폴더 제거" +msgstr "삭제 또는 이동 후 폴더가 빈 폴더가 되면 폴더 삭제" #: qt/me/preferences_dialog.py:60 qt/pe/preferences_dialog.py:27 #: qt/se/preferences_dialog.py:59 cocoa/en.lproj/Localizable.strings:0 @@ -309,11 +309,11 @@ msgstr "디버그 모드 (다시 시작 필요)" #: qt/pe/preferences_dialog.py:19 cocoa/en.lproj/Localizable.strings:0 msgid "Match pictures of different dimensions" -msgstr "가로세로 크기가 다른 이미지도 비교" +msgstr "가로세로 크기가 다른 이미지도 중복 여부 확인" #: qt/preferences_dialog.py:43 msgid "Filter Hardness:" -msgstr "필터 경도 :" +msgstr "필터 민감도:" #: qt/preferences_dialog.py:69 msgid "More Results" @@ -337,15 +337,15 @@ msgstr "복사 및 이동 :" #: qt/preferences_dialog.py:94 cocoa/en.lproj/Localizable.strings:0 msgid "Right in destination" -msgstr "목적지에 직접" +msgstr "대상 폴더에" #: qt/preferences_dialog.py:95 cocoa/en.lproj/Localizable.strings:0 msgid "Recreate relative path" -msgstr "상대 경로 재생성" +msgstr "대상 폴더를 시작으로 상대 경로를 재생성" #: qt/preferences_dialog.py:96 cocoa/en.lproj/Localizable.strings:0 msgid "Recreate absolute path" -msgstr "절대 경로 재생성" +msgstr "대상 폴더를 시작으로 절대 경로를 재생성" #: qt/preferences_dialog.py:99 msgid "Custom Command (arguments: %d for dupe, %r for ref):" @@ -388,7 +388,7 @@ msgstr "선택한 항목 표시" #: qt/result_window.py:57 qt/result_window.py:104 qt/result_window.py:167 #: qt/result_window.py:191 cocoa/en.lproj/Localizable.strings:0 msgid "Actions" -msgstr "행위" +msgstr "작업" #: qt/result_window.py:58 cocoa/en.lproj/Localizable.strings:0 msgid "Show Dupes Only" @@ -400,27 +400,27 @@ msgstr "델타 값만 보기" #: qt/result_window.py:60 msgid "Send Marked to Recycle Bin..." -msgstr "선택 항목을 휴지통으로 보내기..." +msgstr "마크한 모든 파일을 휴지통으로 보내기" #: qt/result_window.py:61 cocoa/en.lproj/Localizable.strings:0 msgid "Move Marked to..." -msgstr "선택항목을 이동..." +msgstr "마크한 모든 파일을 이동..." #: qt/result_window.py:62 cocoa/en.lproj/Localizable.strings:0 msgid "Copy Marked to..." -msgstr "선택항목을 복사..." +msgstr "마크한 모든 파일을 복사..." #: qt/result_window.py:63 cocoa/en.lproj/Localizable.strings:0 msgid "Remove Marked from Results" -msgstr "결과에서 표시된 항목을 제거하다." +msgstr "결과 목록에서 마크한 모든 파일을 제외" #: qt/result_window.py:64 cocoa/en.lproj/Localizable.strings:0 msgid "Re-Prioritize Results..." -msgstr "결과 우선 순위 재 지정..." +msgstr "기준 파일 규칙 재설정" #: qt/result_window.py:67 cocoa/en.lproj/Localizable.strings:0 msgid "Remove Selected from Results" -msgstr "결과에서 선택한 항목 제거" +msgstr "결과 목록에서 선택한 파일을 제외" #: qt/result_window.py:71 cocoa/en.lproj/Localizable.strings:0 msgid "Add Selected to Ignore List" @@ -428,35 +428,35 @@ msgstr "무시 목록에 선택 항목 추가" #: qt/result_window.py:75 cocoa/en.lproj/Localizable.strings:0 msgid "Make Selected into Reference" -msgstr "선택한 항목을 참조 항목으로 만들기" +msgstr "선택한 파일을 기준 파일로 설정" #: qt/result_window.py:77 cocoa/en.lproj/Localizable.strings:0 msgid "Open Selected with Default Application" -msgstr "기본 응용 프로그램으로 선택한 항목 열기" +msgstr "선택한 파일을 기본 앱으로 열기" #: qt/result_window.py:80 msgid "Open Containing Folder of Selected" -msgstr "선택한 항목의 포함 폴더 열기" +msgstr "선택한 파일의 폴더 열기" #: qt/result_window.py:82 cocoa/en.lproj/Localizable.strings:0 msgid "Rename Selected" -msgstr "선택한 이름 변경" +msgstr "선택한 파일의 이름 변경" #: qt/result_window.py:83 cocoa/en.lproj/Localizable.strings:0 msgid "Mark All" -msgstr "모두 표시" +msgstr "모든 파일 마크" #: qt/result_window.py:84 cocoa/en.lproj/Localizable.strings:0 msgid "Mark None" -msgstr "없음으로 표시" +msgstr "모든 파일의 마크 해제" #: qt/result_window.py:85 cocoa/en.lproj/Localizable.strings:0 msgid "Invert Marking" -msgstr "마킹 반전" +msgstr "마크 목록 반전" #: qt/result_window.py:86 cocoa/en.lproj/Localizable.strings:0 msgid "Mark Selected" -msgstr "선택한 항목 표시" +msgstr "선택한 파일 마크" #: qt/result_window.py:87 msgid "Export To HTML" @@ -476,7 +476,7 @@ msgstr "사용자 지정 명령 호출" #: qt/result_window.py:102 msgid "Mark" -msgstr "표시" +msgstr "마크" #: qt/result_window.py:106 cocoa/en.lproj/Localizable.strings:0 msgid "Columns" @@ -492,7 +492,7 @@ msgstr "{} 결과" #: qt/result_window.py:193 cocoa/en.lproj/Localizable.strings:0 msgid "Dupes Only" -msgstr "복제 만" +msgstr "기준 파일 숨기기" #: qt/result_window.py:194 msgid "Delta Values" @@ -696,7 +696,7 @@ msgstr "모두 선택" #: cocoa/en.lproj/Localizable.strings:0 msgid "Send Marked to Trash..." -msgstr "표시된 항목을 휴지통으로 보내기 ..." +msgstr "마크한 파일을 휴지통으로 보내기" #: cocoa/en.lproj/Localizable.strings:0 msgid "Services" @@ -851,15 +851,15 @@ msgstr "" #: qt\preferences_dialog.py:172 msgid "Use bold font for references" -msgstr "참조 용으로 굵은 글꼴 사용" +msgstr "기준 파일을 굵게 표시" #: qt\preferences_dialog.py:176 msgid "Reference foreground color:" -msgstr "참조 전경색 :" +msgstr "기준 파일 이름의 색:" #: qt\preferences_dialog.py:179 msgid "Reference background color:" -msgstr "참조 배경색 :" +msgstr "기준 파일 이름의 배경색:" #: qt\preferences_dialog.py:182 qt\preferences_dialog.py:216 msgid "Delta foreground color:" @@ -914,7 +914,7 @@ msgstr "일반 인터페이스" #: qt\preferences_dialog.py:176 msgid "Result Table" -msgstr "결과표" +msgstr "검색 결과" #: qt\preferences_dialog.py:205 msgid "Details Window" @@ -970,15 +970,15 @@ msgstr "다크 모드 사용" #: qt\preferences_dialog.py:241 msgid "Profile scan operation" -msgstr "" +msgstr "검색 과정 프로파일" #: qt\preferences_dialog.py:242 msgid "Profile the scan operation and save logs for optimization." -msgstr "" +msgstr "검색 과정을 프로파일하고 로그를 저장합니다." #: qt\preferences_dialog.py:246 msgid "Logs located in: {}" -msgstr "" +msgstr "로그 저장 경로: {}" #: qt\preferences_dialog.py:291 msgid "Debug" @@ -1121,3 +1121,17 @@ msgstr "목록 지우기" #: qt\search_edit.py:78 msgid "Search..." msgstr "검색.." + +#: qt\preferences_dialog.py:219 +msgid "" +"These options are for advanced users or for very specific situations, most " +"users should not have to modify these." +msgstr "" + +#: qt\preferences_dialog.py:225 +msgid "Include existence check after scan completion" +msgstr "" + +#: qt\preferences_dialog.py:227 +msgid "Ignore difference in mtime when loading cached digests" +msgstr "" diff --git a/locale/ru/LC_MESSAGES/columns.po b/locale/ru/LC_MESSAGES/columns.po index e79d441b..0040ff6b 100644 --- a/locale/ru/LC_MESSAGES/columns.po +++ b/locale/ru/LC_MESSAGES/columns.po @@ -1,10 +1,11 @@ # Translators: # Andrew Senetar , 2021 +# AHOHNMYC , 2023 # msgid "" msgstr "" -"Last-Translator: Andrew Senetar , 2021\n" -"Language-Team: Russian (https://www.transifex.com/voltaicideas/teams/116153/ru/)\n" +"Last-Translator: AHOHNMYC , 2023\n" +"Language-Team: Russian (https://app.transifex.com/voltaicideas/teams/116153/ru/)\n" "Language: ru\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" @@ -29,9 +30,9 @@ msgstr "Битрейт" #: core\me\prioritize.py:37 msgid "Samplerate" -msgstr "Частота оцифровки" +msgstr "Частота дискретизации" -#: core\me\result_table.py:19 core\pe\result_table.py:19 core\prioritize.py:92 +#: core\me\result_table.py:19 core\pe\result_table.py:19 core\prioritize.py:94 #: core\se\result_table.py:19 msgid "Filename" msgstr "Имя файла" @@ -51,7 +52,7 @@ msgstr "Время" #: core\me\result_table.py:24 msgid "Sample Rate" -msgstr "Частота" +msgstr "Частота дискретизации" #: core\me\result_table.py:25 core\pe\result_table.py:22 core\prioritize.py:65 #: core\se\result_table.py:22 @@ -59,7 +60,7 @@ msgid "Kind" msgstr "Тип" #: core\me\result_table.py:26 core\pe\result_table.py:25 -#: core\prioritize.py:163 core\se\result_table.py:23 +#: core\prioritize.py:165 core\se\result_table.py:23 msgid "Modification" msgstr "Время изменения" @@ -117,6 +118,6 @@ msgstr "Размер (КБ)" msgid "EXIF Timestamp" msgstr "Временная отметка EXIF" -#: core\prioritize.py:156 +#: core\prioritize.py:158 msgid "Size" msgstr "Размер" diff --git a/locale/ru/LC_MESSAGES/core.po b/locale/ru/LC_MESSAGES/core.po index c2b1321d..90cc69f8 100644 --- a/locale/ru/LC_MESSAGES/core.po +++ b/locale/ru/LC_MESSAGES/core.po @@ -1,25 +1,26 @@ # Translators: # Andrew Senetar , 2021 # Fuan , 2021 +# AHOHNMYC , 2023 # msgid "" msgstr "" -"Last-Translator: Fuan , 2021\n" -"Language-Team: Russian (https://www.transifex.com/voltaicideas/teams/116153/ru/)\n" +"Last-Translator: AHOHNMYC , 2023\n" +"Language-Team: Russian (https://app.transifex.com/voltaicideas/teams/116153/ru/)\n" "Language: ru\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" "Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" -#: core\app.py:42 +#: core\app.py:44 msgid "There are no marked duplicates. Nothing has been done." msgstr "Дубликаты не отмечены. Нечего выполнять." -#: core\app.py:43 +#: core\app.py:45 msgid "There are no selected duplicates. Nothing has been done." msgstr "Дубликаты не выбраны. Нечего выполнять." -#: core\app.py:44 +#: core\app.py:46 msgid "" "You're about to open many files at once. Depending on what those files are " "opened with, doing so can create quite a mess. Continue?" @@ -28,27 +29,27 @@ msgstr "" "файлы будут открыты, это действие может создать настоящий беспорядок. " "Продолжать?" -#: core\app.py:71 +#: core\app.py:73 msgid "Scanning for duplicates" msgstr "Проверка на наличие дубликатов" -#: core\app.py:72 +#: core\app.py:74 msgid "Loading" msgstr "Загрузка" -#: core\app.py:73 +#: core\app.py:75 msgid "Moving" msgstr "Перемещение" -#: core\app.py:74 +#: core\app.py:76 msgid "Copying" msgstr "Копирование" -#: core\app.py:75 +#: core\app.py:77 msgid "Sending to Trash" msgstr "Перемещение в Корзину" -#: core\app.py:289 +#: core\app.py:293 msgid "" "A previous action is still hanging in there. You can't start a new one yet. " "Wait a few seconds, then try again." @@ -56,39 +57,39 @@ msgstr "" "Предыдущее действие до сих пор выполняется. Вы не можете начать новое. " "Подождите несколько секунд, затем повторите попытку." -#: core\app.py:300 +#: core\app.py:304 msgid "No duplicates found." msgstr "Дубликаты не найдены." -#: core\app.py:315 +#: core\app.py:319 msgid "All marked files were copied successfully." msgstr "Все отмеченные файлы были скопированы успешно." -#: core\app.py:317 +#: core\app.py:321 msgid "All marked files were moved successfully." msgstr "Все отмеченные файлы были перемещены успешно." -#: core\app.py:319 +#: core\app.py:323 msgid "All marked files were deleted successfully." -msgstr "" +msgstr "Все отмеченные файлы были удалены успешно." -#: core\app.py:321 +#: core\app.py:325 msgid "All marked files were successfully sent to Trash." msgstr "Все отмеченные файлы были успешно отправлены в Корзину." -#: core\app.py:326 +#: core\app.py:330 msgid "Could not load file: {}" msgstr "Не удалось загрузить файл: {}" -#: core\app.py:382 +#: core\app.py:386 msgid "'{}' already is in the list." msgstr "'{}' уже присутствует в списке." -#: core\app.py:384 +#: core\app.py:388 msgid "'{}' does not exist." msgstr "'{}' не существует." -#: core\app.py:392 +#: core\app.py:396 msgid "" "All selected %d matches are going to be ignored in all subsequent scans. " "Continue?" @@ -96,57 +97,57 @@ msgstr "" "Все выбранные %d совпадений будут игнорироваться при всех последующих " "проверках. Продолжить?" -#: core\app.py:469 +#: core\app.py:473 msgid "Select a directory to copy marked files to" msgstr "Выберите каталог, в который вы хотите скопировать отмеченные файлы" -#: core\app.py:471 +#: core\app.py:475 msgid "Select a directory to move marked files to" msgstr "Выберите каталог, в который вы хотите переместить отмеченные файлы" -#: core\app.py:510 +#: core\app.py:514 msgid "Select a destination for your exported CSV" msgstr "Выберите назначение для экспортируемого " -#: core\app.py:516 core\app.py:771 core\app.py:781 +#: core\app.py:520 core\app.py:781 core\app.py:791 msgid "Couldn't write to file: {}" msgstr "Не удалось записать в файл: {}" -#: core\app.py:539 +#: core\app.py:543 msgid "You have no custom command set up. Set it up in your preferences." msgstr "Вы не создали пользовательскую команду. Задайте её в настройках." -#: core\app.py:695 core\app.py:707 +#: core\app.py:705 core\app.py:717 msgid "You are about to remove %d files from results. Continue?" msgstr "Вы собираетесь удалить %d файлов из результата поиска. Продолжить?" -#: core\app.py:743 +#: core\app.py:753 msgid "{} duplicate groups were changed by the re-prioritization." msgstr "{} групп дубликатов было изменено при реприоритезации." -#: core\app.py:790 +#: core\app.py:801 msgid "The selected directories contain no scannable file." msgstr "Выбранные каталоги не содержат файлов для сканирования." -#: core\app.py:803 +#: core\app.py:817 msgid "Collecting files to scan" msgstr "Сбор файлов для сканирования" -#: core\app.py:850 +#: core\app.py:867 msgid "%s (%d discarded)" msgstr "%s. (%d отменено)" -#: core\directories.py:191 +#: core\directories.py:190 msgid "Collected {} files to scan" -msgstr "" +msgstr "Собрано {} файлов для сканирования" -#: core\directories.py:207 +#: core\directories.py:206 msgid "Collected {} folders to scan" -msgstr "" +msgstr "Собрано {} каталогов для сканирования" #: core\engine.py:27 msgid "%d matches found from %d groups" -msgstr "" +msgstr "Найдено %d совпадений из %d групп" #: core\gui\deletion_options.py:71 msgid "You are sending {} file(s) to the Trash." @@ -154,7 +155,7 @@ msgstr "Вы перемещаете {} файлов в Корзину." #: core\gui\exclude_list_table.py:14 msgid "Regular Expressions" -msgstr "Обычные выражения" +msgstr "Регулярные выражения" #: core\gui\ignore_list_dialog.py:25 msgid "Do you really want to remove all %d items from the ignore list?" @@ -171,7 +172,7 @@ msgstr "Имя файла - Поля" #: core\me\scanner.py:22 msgid "Filename - Fields (No Order)" -msgstr "Имя файла - поля (без порядка)" +msgstr "Имя файла - Поля (без сортировки)" #: core\me\scanner.py:23 msgid "Tags" @@ -183,11 +184,11 @@ msgstr "Содержание" #: core\pe\matchblock.py:72 msgid "Analyzed %d/%d pictures" -msgstr "Анализируется %d/%d изображений" +msgstr "Проанализировано %d из %d изображений" #: core\pe\matchblock.py:177 msgid "Performed %d/%d chunk matches" -msgstr "Выполнено %d/%d совпадений блоков" +msgstr "Проверено %d/%d совпадений" #: core\pe\matchblock.py:185 msgid "Preparing for matching" @@ -209,37 +210,37 @@ msgstr "Метка времени EXIF" msgid "None" msgstr "Ни один" -#: core\prioritize.py:100 +#: core\prioritize.py:102 msgid "Ends with number" msgstr "Заканчивается номером" -#: core\prioritize.py:101 +#: core\prioritize.py:103 msgid "Doesn't end with number" msgstr "Не заканчивается номером" -#: core\prioritize.py:102 +#: core\prioritize.py:104 msgid "Longest" msgstr "Самый длинный" -#: core\prioritize.py:103 +#: core\prioritize.py:105 msgid "Shortest" msgstr "Самый короткий" -#: core\prioritize.py:140 +#: core\prioritize.py:142 msgid "Highest" msgstr "Наивысший" -#: core\prioritize.py:140 +#: core\prioritize.py:142 msgid "Lowest" msgstr "Самый низкий" -#: core\prioritize.py:169 +#: core\prioritize.py:171 msgid "Newest" msgstr "Новейший" -#: core\prioritize.py:169 +#: core\prioritize.py:171 msgid "Oldest" -msgstr "Старейшие" +msgstr "Старейший" #: core\results.py:134 msgid "%d / %d (%s / %s) duplicates marked." diff --git a/locale/ru/LC_MESSAGES/ui.po b/locale/ru/LC_MESSAGES/ui.po index 30dd4272..cb7a5b68 100644 --- a/locale/ru/LC_MESSAGES/ui.po +++ b/locale/ru/LC_MESSAGES/ui.po @@ -1,11 +1,13 @@ # Translators: # Fuan , 2022 # Andrew Senetar , 2022 +# AHOHNMYC , 2023 +# Captain Quake , 2023 # msgid "" msgstr "" -"Last-Translator: Andrew Senetar , 2022\n" -"Language-Team: Russian (https://www.transifex.com/voltaicideas/teams/116153/ru/)\n" +"Last-Translator: Captain Quake , 2023\n" +"Language-Team: Russian (https://app.transifex.com/voltaicideas/teams/116153/ru/)\n" "Language: ru\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" @@ -18,7 +20,7 @@ msgstr "Выйти" #: qt/app.py:82 qt/preferences_dialog.py:116 #: cocoa/en.lproj/Localizable.strings:0 msgid "Options" -msgstr "Параметры" +msgstr "Настройки" #: qt/app.py:83 qt/ignore_list_dialog.py:32 #: cocoa/en.lproj/Localizable.strings:0 @@ -75,7 +77,7 @@ msgstr "Жёсткая ссылка" #: qt/deletion_options.py:44 msgid "Symlink" -msgstr "Символьная ссылка" +msgstr "Символическая ссылка" #: qt/deletion_options.py:48 msgid " (unsupported)" @@ -90,8 +92,8 @@ msgid "" "Instead of sending files to trash, delete them directly. This option is " "usually used as a workaround when the normal deletion method doesn't work." msgstr "" -"Вместо отправки файлов в Корзину удалить их с диска. Этот параметр обычно " -"используется как обходной путь, когда нормальный метод удаления не работает." +"Удалить файлы с диска вместо отправки в Корзину. Используйте если нормальный" +" метод удаления не работает." #: qt/deletion_options.py:59 cocoa/en.lproj/Localizable.strings:0 msgid "Proceed" @@ -154,11 +156,11 @@ msgstr "Музыка" #: qt/directories_dialog.py:121 cocoa/en.lproj/Localizable.strings:0 msgid "Picture" -msgstr "Рисунок" +msgstr "Изображения" #: qt/directories_dialog.py:121 cocoa/en.lproj/Localizable.strings:0 msgid "Standard" -msgstr "Стандарт" +msgstr "Обычный" #: qt/directories_dialog.py:128 cocoa/en.lproj/Localizable.strings:0 msgid "Scan Type:" @@ -170,7 +172,7 @@ msgstr "Больше вариантов" #: qt/directories_dialog.py:139 cocoa/en.lproj/Localizable.strings:0 msgid "Select folders to scan and press \"Scan\"." -msgstr "Выберите каталоги для поиска и нажмите \"Сканирование\"." +msgstr "Выберите каталоги для поиска и нажмите \"Сканировать\"." #: qt/directories_dialog.py:163 cocoa/en.lproj/Localizable.strings:0 msgid "Load Results" @@ -178,7 +180,7 @@ msgstr "Загрузить результаты" #: qt/directories_dialog.py:166 cocoa/en.lproj/Localizable.strings:0 msgid "Scan" -msgstr "Сканирование" +msgstr "Сканировать" #: qt/directories_dialog.py:230 msgid "Unsaved results" @@ -222,11 +224,11 @@ msgstr "Состояние" #: qt/directories_model.py:24 cocoa/en.lproj/Localizable.strings:0 msgid "Excluded" -msgstr "Исключённые" +msgstr "Исключён" #: qt/directories_model.py:24 cocoa/en.lproj/Localizable.strings:0 msgid "Normal" -msgstr "Нормальный" +msgstr "Обычный" #: qt/ignore_list_dialog.py:45 cocoa/en.lproj/Localizable.strings:0 msgid "Remove Selected" @@ -293,7 +295,7 @@ msgstr "Можно смешивать типы файлов" #: qt/me/preferences_dialog.py:56 qt/pe/preferences_dialog.py:23 #: qt/se/preferences_dialog.py:36 cocoa/en.lproj/Localizable.strings:0 msgid "Use regular expressions when filtering" -msgstr "Использование регулярных выражений при фильтрации" +msgstr "Использовать регулярные выражения для фильтров" #: qt/me/preferences_dialog.py:58 qt/pe/preferences_dialog.py:25 #: qt/se/preferences_dialog.py:38 cocoa/en.lproj/Localizable.strings:0 @@ -312,7 +314,7 @@ msgstr "Режим отладки (требуется перезапуск)" #: qt/pe/preferences_dialog.py:19 cocoa/en.lproj/Localizable.strings:0 msgid "Match pictures of different dimensions" -msgstr "Совпадение рисунков разных размеров" +msgstr "Искать дубликаты изображений разных размеров" #: qt/preferences_dialog.py:43 msgid "Filter Hardness:" @@ -357,8 +359,7 @@ msgstr "" #: qt/preferences_dialog.py:174 msgid "dupeGuru has to restart for language changes to take effect." -msgstr "" -"dupeGuru необходимо перезапустить, чтобы языковые изменения вступили в силу." +msgstr "Новый язык будет загружен при следующем запуске dupeGuru." #: qt/prioritize_dialog.py:75 cocoa/en.lproj/Localizable.strings:0 msgid "Re-Prioritize duplicates" @@ -377,7 +378,7 @@ msgstr "" #: qt/problem_dialog.py:33 cocoa/en.lproj/Localizable.strings:0 msgid "Problems!" -msgstr "Проблемы!" +msgstr "Проблемка!" #: qt/problem_dialog.py:37 cocoa/en.lproj/Localizable.strings:0 msgid "" @@ -471,7 +472,7 @@ msgstr "Экспорт в HTML" #: qt/result_window.py:88 msgid "Export To CSV" -msgstr "Экспорт в " +msgstr "Экспорт в CSV" #: qt/result_window.py:89 cocoa/en.lproj/Localizable.strings:0 msgid "Save Results..." @@ -644,7 +645,7 @@ msgstr "Скрыть остальные" #: cocoa/en.lproj/Localizable.strings:0 msgid "Ignore files smaller than:" -msgstr "Игнорировать файлы меньше чем:" +msgstr "Пропускать файлы меньше чем:" #: cocoa/en.lproj/Localizable.strings:0 msgid "Load from file..." @@ -652,7 +653,7 @@ msgstr "Загрузить из файла…" #: cocoa/en.lproj/Localizable.strings:0 msgid "Minimize" -msgstr "Минимизировать" +msgstr "Свернуть" #: cocoa/en.lproj/Localizable.strings:0 msgid "Mode" @@ -720,7 +721,7 @@ msgstr "Начать поиск дубликатов" #: cocoa/en.lproj/Localizable.strings:0 msgid "The name '%@' already exists." -msgstr " Имя '%@' уже существует." +msgstr "Имя '%@' уже существует." #: cocoa/en.lproj/Localizable.strings:0 msgid "Window" @@ -752,7 +753,7 @@ msgstr "Выберите файл каталогов для загрузки" #: qt\directories_dialog.py:338 msgid "dupeGuru Results (*.dupegurudirs)" -msgstr "каталоги dupeGuru (*.dupegurudirs)" +msgstr "Каталоги dupeGuru (*.dupegurudirs)" #: qt\directories_dialog.py:347 msgid "Select a file to save your directories to" @@ -760,11 +761,11 @@ msgstr "Выберите файл для сохранения каталогов #: qt\directories_dialog.py:348 msgid "dupeGuru Directories (*.dupegurudirs)" -msgstr "каталоги dupeGuru (*.dupegurudirs)" +msgstr "Каталоги dupeGuru (*.dupegurudirs)" #: qt\exclude_list_dialog.py:44 msgid "Add" -msgstr "добавлять" +msgstr "Добавить" #: qt\exclude_list_dialog.py:46 msgid "Restore defaults" @@ -772,7 +773,7 @@ msgstr "Восстановить значения по умолчанию" #: qt\exclude_list_dialog.py:47 msgid "Test string" -msgstr "Тестовая строка" +msgstr "Проверить строку" #: qt\exclude_list_dialog.py:83 msgid "Type a python regular expression here..." @@ -789,10 +790,10 @@ msgid "" "Example: if you want to filter out .PNG files from the \"My Pictures\" directory only:
    .*My\\sPictures\\\\.*\\.png

    You can test the regular expression with the test string feature by pasting a fake path in it:
    C:\\\\User\\My Pictures\\test.png

    \n" "Matching regular expressions will be highlighted.
    If there is at least one highlight, the path tested will be ignored during scans.

    Directories and files starting with a period '.' are filtered out by default.

    " msgstr "" -"Эти (чувствительные к регистру) регулярные выражения Python будут отфильтровывать файлы во время сканирования.
    Директорам также будет установлено состояние по умолчанию «Исключено» на вкладке «Каталоги», если их имя совпадает с одним из выбранных регулярных выражений.
    Для каждого собранного файла выполняется два теста, чтобы определить, следует ли его полностью игнорировать:
  • 1. Регулярные выражения без разделителя пути будут сравниваться только с именем файла.
  • \n" +"Эти (чувствительные к регистру) регулярные выражения Python будут отфильтровывать файлы во время сканирования.
    Если имя каталога подходит под какое-либо из выбранных регулярных выражений, для него на вкладке «Каталоги» будет выставлено состояние по умолчанию «Исключено».
    Для каждого файла выполняется две проверки, чтобы определить, следует ли его полностью игнорировать:
  • 1. Регулярные выражения без разделителя пути будут сравниваться только с именем файла.
  • \n" "
  • 2. Регулярные выражения, содержащие хотя бы один разделитель пути, будут сравниваться с полным путем к файлу.

  • \n" -"Пример: если вы хотите отфильтровать файлы .PNG только из каталога «Мои изображения»:
    .*Мои\\sизображения\\\\.*\\.png

    Вы можете проверить регулярное выражение с помощью кнопки «тестовая строка» после вставки поддельного пути в тестовое поле:
    C:\\\\Пользователь\\Мои изображения\\test.png

    \n" -"Соответствующие регулярные выражения будут выделены.
    Если есть хотя бы одно выделение, проверенный путь или имя файла будет проигнорирован во время сканирования.

    Каталоги и файлы, начинающиеся с точки \".\" по умолчанию отфильтрованы.

    " +"Пример: отфильтровать .PNG из каталога «Мои изображения»:
    .*Мои\\sизображения\\\\.*\\.png

    Вы можете проверить регулярное выражение с помощью кнопки «Проверить строку», указав, например, такой путь:
    C:\\\\Пользователь\\Мои изображения\\test.png

    \n" +"Сработавшие регулярные выражения будут выделены.
    Если есть хотя бы одно выделение, проверенный путь или имя файла будет проигнорирован во время сканирования.

    Каталоги и файлы, начинающиеся с точки \".\" по умолчанию будут отфильтрованы.

    " #: qt\exclude_list_table.py:36 msgid "Compilation error: " @@ -833,9 +834,7 @@ msgstr "Переопределить значки темы на панели и #: qt\pe\preferences_dialog.py:58 msgid "" "Use our own internal icons instead of those provided by the theme engine" -msgstr "" -"Используйте наши собственные внутренние значки вместо тех, которые " -"предоставляются движком темы." +msgstr "Используйте внутренние значки вместо значков, встроенных в тему" #: qt\pe\preferences_dialog.py:66 msgid "Show scrollbars in image viewers" @@ -845,9 +844,7 @@ msgstr "Показывать полосы прокрутки в средства msgid "" "When the image displayed doesn't fit the viewport, show scrollbars to span " "the view around" -msgstr "" -"Когда отображаемое изображение не умещается в области просмотра, покажите " -"полосы прокрутки, чтобы охватить область просмотра" +msgstr "Показывать полосы прокрутки для больших изображений" #: qt\preferences_dialog.py:156 msgid "Use default position for tab bar (requires restart)" @@ -864,19 +861,19 @@ msgstr "" #: qt\preferences_dialog.py:172 msgid "Use bold font for references" -msgstr "Используйте жирный шрифт для ссылок" +msgstr "Использовать жирный шрифт для ссылок" #: qt\preferences_dialog.py:176 msgid "Reference foreground color:" -msgstr "Эталонный цвет переднего плана:" +msgstr "Цвет шрифта для эталонных файлов:" #: qt\preferences_dialog.py:179 msgid "Reference background color:" -msgstr "Цвет фона справки:" +msgstr "Цвет фона для эталонных файлов:" #: qt\preferences_dialog.py:182 qt\preferences_dialog.py:216 msgid "Delta foreground color:" -msgstr "Цвет переднего плана дельты:" +msgstr "Цвет шрифта для дельты:" #: qt\preferences_dialog.py:195 msgid "Show the title bar and can be docked" @@ -887,8 +884,8 @@ msgid "" "While the title bar is hidden, use the modifier key to drag the floating " "window around" msgstr "" -"Пока строка заголовка скрыта, используйте клавишу-модификатор, чтобы " -"перетащить плавающее окно вокруг" +"Если строка заголовка скрыта, перемещать плавающее окно с клавишей-" +"модификатором" #: qt\preferences_dialog.py:199 msgid "The title bar can only be disabled while the window is docked" @@ -915,10 +912,10 @@ msgid "" "Example: if you want to filter out .PNG files from the \"My Pictures\" directory only:
    .*My\\sPictures\\\\.*\\.png

    You can test the regular expression with the \"test string\" button after pasting a fake path in the test field:
    C:\\\\User\\My Pictures\\test.png

    \n" "Matching regular expressions will be highlighted.
    If there is at least one highlight, the path or filename tested will be ignored during scans.

    Directories and files starting with a period '.' are filtered out by default.

    " msgstr "" -"Эти (чувствительные к регистру) регулярные выражения Python будут отфильтровывать файлы во время сканирования.
    Директорам также будет установлено состояние по умолчанию «Исключено» на вкладке «Каталоги», если их имя совпадает с одним из выбранных регулярных выражений.
    Для каждого собранного файла выполняется два теста, чтобы определить, следует ли его полностью игнорировать:
  • 1. Регулярные выражения без разделителя пути будут сравниваться только с именем файла.
  • \n" +"Эти (чувствительные к регистру) регулярные выражения Python будут отфильтровывать файлы во время сканирования.
    Если имя каталога подходит под какое-либо из выбранных регулярных выражений, для него на вкладке «Каталоги» будет выставлено состояние по умолчанию «Исключено».
    Для каждого файла выполняется две проверки, чтобы определить, следует ли его полностью игнорировать:
  • 1. Регулярные выражения без разделителя пути будут сравниваться только с именем файла.
  • \n" "
  • 2. Регулярные выражения, содержащие хотя бы один разделитель пути, будут сравниваться с полным путем к файлу.

  • \n" -"Пример: если вы хотите отфильтровать файлы .PNG только из каталога «Мои изображения»:
    .*Мои\\sизображения\\\\.*\\.png

    Вы можете проверить регулярное выражение с помощью кнопки «тестовая строка» после вставки поддельного пути в тестовое поле:
    C:\\\\Пользователь\\Мои изображения\\test.png

    \n" -"Соответствующие регулярные выражения будут выделены.
    Если есть хотя бы одно выделение, проверенный путь или имя файла будет проигнорирован во время сканирования.

    Каталоги и файлы, начинающиеся с точки \".\" по умолчанию отфильтрованы.

    " +"Пример: отфильтровать .PNG из каталога «Мои изображения»:
    .*Мои\\sизображения\\\\.*\\.png

    Вы можете проверить регулярное выражение с помощью кнопки «Проверить строку», указав, например, такой путь:
    C:\\\\Пользователь\\Мои изображения\\test.png

    \n" +"Сработавшие регулярные выражения будут выделены.
    Если есть хотя бы одно выделение, проверенный путь или имя файла будет проигнорирован во время сканирования.

    Каталоги и файлы, начинающиеся с точки \".\" по умолчанию будут отфильтрованы.

    " #: qt\app.py:256 msgid "Results" @@ -930,79 +927,86 @@ msgstr "Общий интерфейс" #: qt\preferences_dialog.py:176 msgid "Result Table" -msgstr "Таблица результатов" +msgstr "Таблица с результатами" #: qt\preferences_dialog.py:205 msgid "Details Window" -msgstr "Окно деталей" +msgstr "Окно с подробностями" #: qt\preferences_dialog.py:285 msgid "General" -msgstr "Общий" +msgstr "Общие" #: qt\preferences_dialog.py:286 msgid "Display" -msgstr "Отображать" +msgstr "Внешний вид" #: qt\se\preferences_dialog.py:70 msgid "Partially hash files bigger than" -msgstr "" +msgstr "Частично хешировать большие файлы, размером более" #: qt\se\preferences_dialog.py:80 msgid "MB" -msgstr "" +msgstr "МБ" #: qt\preferences_dialog.py:163 msgid "Use native OS dialogs" -msgstr "" +msgstr "Использовать системное окно выбора файлов и папок" #: qt\preferences_dialog.py:166 msgid "" "For actions such as file/folder selection use the OS native dialogs.\n" "Some native dialogs have limited functionality." msgstr "" +"При выборе файлов или папок для добавления в список сканирования будет " +"использоваться диалоговое окно системы, а не встроенное в dupeGuru. " +"Некоторые системные диалоговые окна имеют ограниченную функциональность." #: qt\se\preferences_dialog.py:68 msgid "Ignore files larger than" -msgstr "" +msgstr "Игнорировать файлы больше чем" #: qt\app.py:135 qt\app.py:293 msgid "Clear Cache" -msgstr "" +msgstr "Очистить кэш" #: qt\app.py:294 msgid "" "Do you really want to clear the cache? This will remove all cached file " "hashes and picture analysis." msgstr "" +"Вы действительно хотите очистить кэш? Это удалит все кэшированные хеши " +"файлов и анализ данных изображений." #: qt\app.py:299 msgid "Cache cleared." -msgstr "" +msgstr "Кэш очищен " #: qt\preferences_dialog.py:173 msgid "Use dark style" -msgstr "" +msgstr "Использовать темную тему" #: qt\preferences_dialog.py:241 msgid "Profile scan operation" -msgstr "" +msgstr "Сохранить профиль сканирования" #: qt\preferences_dialog.py:242 msgid "Profile the scan operation and save logs for optimization." msgstr "" +"В папке установленной или портативной программы, есть папка Data в которую " +"сохраняется логи и файл с раширением *.profile для оптимизации." #: qt\preferences_dialog.py:246 msgid "Logs located in: {}" -msgstr "" +msgstr "Сохранять отчеты в: {}" #: qt\preferences_dialog.py:291 msgid "Debug" -msgstr "" +msgstr "Отладка" #: qt\about_box.py:31 msgid "About {}" -msgstr "О {}" +msgstr "О программе {}" #: qt\about_box.py:47 msgid "Version {}" @@ -1010,7 +1014,7 @@ msgstr "Версия {}" #: qt\about_box.py:49 qt\about_box.py:75 msgid "Checking for updates..." -msgstr "" +msgstr "Проверка обновлений..." #: qt\about_box.py:54 msgid "Licensed under GPLv3" @@ -1018,11 +1022,11 @@ msgstr "Под лицензией GPLv3" #: qt\about_box.py:68 msgid "No update available." -msgstr "" +msgstr "У вас самая свежая версия" #: qt\about_box.py:71 msgid "New version {} available, download here." -msgstr "" +msgstr "Обнаружена новая {} версия, загружать тут." #: qt\error_report_dialog.py:50 msgid "Error Report" @@ -1042,7 +1046,7 @@ msgid "" "\n" "Although the application should continue to run after this error, it may be in an unstable state, so it is recommended that you restart the application." msgstr "" -"Отчеты об ошибках следует сообщать как о проблемах Github. Вы можете скопировать трассировку ошибки выше и вставить ее в новый выпуск.\n" +"Отчеты об ошибках следует оформлять в виде issues на Github. Вы можете скопировать трассировку ошибки выше и вставить ее в новый issue.\n" "\n" "Обязательно заранее выполните поиск любых уже существующих проблем. Также не забудьте протестировать самую последнюю версию, доступную в репозитории, поскольку ошибка, с которой вы столкнулись, могла уже быть исправлена.\n" "\n" @@ -1137,3 +1141,19 @@ msgstr "Очистить список" #: qt\search_edit.py:78 msgid "Search..." msgstr "Искать..." + +#: qt\preferences_dialog.py:219 +msgid "" +"These options are for advanced users or for very specific situations, most " +"users should not have to modify these." +msgstr "" +"Опция для опытных пользователей для различных специфичных ситуаций. Если не " +"знаете, что делаете, не трогайте!" + +#: qt\preferences_dialog.py:225 +msgid "Include existence check after scan completion" +msgstr "Включить проверку после завершения сканирования" + +#: qt\preferences_dialog.py:227 +msgid "Ignore difference in mtime when loading cached digests" +msgstr "Игнорировать разницу во времени при загрузке кэшированных дайджестов" diff --git a/locale/tr/LC_MESSAGES/ui.po b/locale/tr/LC_MESSAGES/ui.po index 1502b1e0..82bd7bfc 100644 --- a/locale/tr/LC_MESSAGES/ui.po +++ b/locale/tr/LC_MESSAGES/ui.po @@ -1,11 +1,11 @@ # Translators: -# Emin Tufan Çetin , 2022 # Ahmet Haydar Işık , 2022 +# Emin Tufan Çetin , 2022 # msgid "" msgstr "" -"Last-Translator: Ahmet Haydar Işık , 2022\n" -"Language-Team: Turkish (https://www.transifex.com/voltaicideas/teams/116153/tr/)\n" +"Last-Translator: Emin Tufan Çetin , 2022\n" +"Language-Team: Turkish (https://app.transifex.com/voltaicideas/teams/116153/tr/)\n" "Language: tr\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" @@ -987,19 +987,19 @@ msgstr "Karanlık biçem kullan" #: qt\preferences_dialog.py:241 msgid "Profile scan operation" -msgstr "" +msgstr "Tarama işlemini profille" #: qt\preferences_dialog.py:242 msgid "Profile the scan operation and save logs for optimization." -msgstr "" +msgstr "Tarama işlemini profille ve iyileştirme için günlükleri kaydet." #: qt\preferences_dialog.py:246 msgid "Logs located in: {}" -msgstr "" +msgstr "Günlükler şuradadır: {}" #: qt\preferences_dialog.py:291 msgid "Debug" -msgstr "" +msgstr "Hata ayıklama" #: qt\about_box.py:31 msgid "About {}" @@ -1011,7 +1011,7 @@ msgstr "Sürüm {}" #: qt\about_box.py:49 qt\about_box.py:75 msgid "Checking for updates..." -msgstr "" +msgstr "Güncellemeler denetleniyor..." #: qt\about_box.py:54 msgid "Licensed under GPLv3" @@ -1019,11 +1019,11 @@ msgstr "GPLv3 altında lisanslanmıştır." #: qt\about_box.py:68 msgid "No update available." -msgstr "" +msgstr "Güncelleme yok." #: qt\about_box.py:71 msgid "New version {} available, download here." -msgstr "" +msgstr "Yeni {} sürümü var, buradan indirilebilir." #: qt\error_report_dialog.py:50 msgid "Error Report" @@ -1138,3 +1138,17 @@ msgstr "Listeyi Temizle" #: qt\search_edit.py:78 msgid "Search..." msgstr "Ara..." + +#: qt\preferences_dialog.py:219 +msgid "" +"These options are for advanced users or for very specific situations, most " +"users should not have to modify these." +msgstr "" + +#: qt\preferences_dialog.py:225 +msgid "Include existence check after scan completion" +msgstr "" + +#: qt\preferences_dialog.py:227 +msgid "Ignore difference in mtime when loading cached digests" +msgstr "" diff --git a/locale/zh_CN/LC_MESSAGES/ui.po b/locale/zh_CN/LC_MESSAGES/ui.po index 90d1354a..8d95f2ee 100644 --- a/locale/zh_CN/LC_MESSAGES/ui.po +++ b/locale/zh_CN/LC_MESSAGES/ui.po @@ -2,12 +2,12 @@ # Fuan , 2022 # 太子 VC , 2022 # Andrew Senetar , 2022 -# Chris Ocelot, 2022 +# Chris Ocelot, 2023 # msgid "" msgstr "" -"Last-Translator: Chris Ocelot, 2022\n" -"Language-Team: Chinese (China) (https://www.transifex.com/voltaicideas/teams/116153/zh_CN/)\n" +"Last-Translator: Chris Ocelot, 2023\n" +"Language-Team: Chinese (China) (https://app.transifex.com/voltaicideas/teams/116153/zh_CN/)\n" "Language: zh_CN\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" @@ -927,11 +927,11 @@ msgstr "展示" #: qt\se\preferences_dialog.py:70 msgid "Partially hash files bigger than" -msgstr "只哈希部分如果文件大于" +msgstr "只计算部分hash,如果文件大于" #: qt\se\preferences_dialog.py:80 msgid "MB" -msgstr "" +msgstr "MB" #: qt\preferences_dialog.py:163 msgid "Use native OS dialogs" @@ -945,41 +945,41 @@ msgstr "使用操作系统原生对话窗口选择文件、文件夹。部分系 #: qt\se\preferences_dialog.py:68 msgid "Ignore files larger than" -msgstr "" +msgstr "忽略文件,如果大于" #: qt\app.py:135 qt\app.py:293 msgid "Clear Cache" -msgstr "" +msgstr "清除缓存" #: qt\app.py:294 msgid "" "Do you really want to clear the cache? This will remove all cached file " "hashes and picture analysis." -msgstr "" +msgstr "你确定要清除缓存吗?所有缓存的文件hash和图片分析都会被移除。" #: qt\app.py:299 msgid "Cache cleared." -msgstr "" +msgstr "缓存已清除。" #: qt\preferences_dialog.py:173 msgid "Use dark style" -msgstr "" +msgstr "使用暗色主题" #: qt\preferences_dialog.py:241 msgid "Profile scan operation" -msgstr "" +msgstr "将扫描操作保存为配置" #: qt\preferences_dialog.py:242 msgid "Profile the scan operation and save logs for optimization." -msgstr "" +msgstr "将扫描操作保存为配置,并保存日志用于优化。" #: qt\preferences_dialog.py:246 msgid "Logs located in: {}" -msgstr "" +msgstr "日志位于:{}" #: qt\preferences_dialog.py:291 msgid "Debug" -msgstr "" +msgstr "调试" #: qt\about_box.py:31 msgid "About {}" @@ -991,7 +991,7 @@ msgstr "版本 {}" #: qt\about_box.py:49 qt\about_box.py:75 msgid "Checking for updates..." -msgstr "" +msgstr "检查更新..." #: qt\about_box.py:54 msgid "Licensed under GPLv3" @@ -999,11 +999,11 @@ msgstr "本项目基于GPLv3开源协议发布" #: qt\about_box.py:68 msgid "No update available." -msgstr "" +msgstr "没有新版本。" #: qt\about_box.py:71 msgid "New version {} available, download here." -msgstr "" +msgstr "有新版本{}可用,在这里下载。" #: qt\error_report_dialog.py:50 msgid "Error Report" @@ -1118,3 +1118,17 @@ msgstr "清空列表" #: qt\search_edit.py:78 msgid "Search..." msgstr "搜索..." + +#: qt\preferences_dialog.py:219 +msgid "" +"These options are for advanced users or for very specific situations, most " +"users should not have to modify these." +msgstr "这些选项是供高级用户或者特定情况下使用,大多数用户不该修改它们。" + +#: qt\preferences_dialog.py:225 +msgid "Include existence check after scan completion" +msgstr "在扫描结束后检查文件是否存在" + +#: qt\preferences_dialog.py:227 +msgid "Ignore difference in mtime when loading cached digests" +msgstr "载入缓存摘要时忽略mtime的不同" diff --git a/locale/zh_TW/LC_MESSAGES/ui.po b/locale/zh_TW/LC_MESSAGES/ui.po index 682f517f..8feed51a 100644 --- a/locale/zh_TW/LC_MESSAGES/ui.po +++ b/locale/zh_TW/LC_MESSAGES/ui.po @@ -2,12 +2,12 @@ # Fuan , 2022 # 太子 VC , 2022 # Andrew Senetar , 2022 -# Chris Ocelot, 2022 +# Chris Ocelot, 2023 # msgid "" msgstr "" -"Last-Translator: Chris Ocelot, 2022\n" -"Language-Team: Chinese (Taiwan) (https://www.transifex.com/voltaicideas/teams/116153/zh_TW/)\n" +"Last-Translator: Chris Ocelot, 2023\n" +"Language-Team: Chinese (Taiwan) (https://app.transifex.com/voltaicideas/teams/116153/zh_TW/)\n" "Language: zh_TW\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" @@ -931,7 +931,7 @@ msgstr "只哈希部分如果文件大于" #: qt\se\preferences_dialog.py:80 msgid "MB" -msgstr "" +msgstr "MB" #: qt\preferences_dialog.py:163 msgid "Use native OS dialogs" @@ -945,41 +945,41 @@ msgstr "使用操作系统原生对话窗口选择文件、文件夹。部分系 #: qt\se\preferences_dialog.py:68 msgid "Ignore files larger than" -msgstr "" +msgstr "忽略文件,如果大于" #: qt\app.py:135 qt\app.py:293 msgid "Clear Cache" -msgstr "" +msgstr "清除缓存" #: qt\app.py:294 msgid "" "Do you really want to clear the cache? This will remove all cached file " "hashes and picture analysis." -msgstr "" +msgstr "你确定要清除缓存吗?所有缓存的文件hash和图片分析都会被移除。" #: qt\app.py:299 msgid "Cache cleared." -msgstr "" +msgstr "缓存已清除。" #: qt\preferences_dialog.py:173 msgid "Use dark style" -msgstr "" +msgstr "使用暗色主题" #: qt\preferences_dialog.py:241 msgid "Profile scan operation" -msgstr "" +msgstr "将扫描操作保存为配置" #: qt\preferences_dialog.py:242 msgid "Profile the scan operation and save logs for optimization." -msgstr "" +msgstr "将扫描操作保存为配置,并保存日志用于优化。" #: qt\preferences_dialog.py:246 msgid "Logs located in: {}" -msgstr "" +msgstr "日志位于:{}" #: qt\preferences_dialog.py:291 msgid "Debug" -msgstr "" +msgstr "调试" #: qt\about_box.py:31 msgid "About {}" @@ -991,7 +991,7 @@ msgstr "版本 {}" #: qt\about_box.py:49 qt\about_box.py:75 msgid "Checking for updates..." -msgstr "" +msgstr "检查更新..." #: qt\about_box.py:54 msgid "Licensed under GPLv3" @@ -999,11 +999,11 @@ msgstr "本项目基于GPLv3开源协议发布" #: qt\about_box.py:68 msgid "No update available." -msgstr "" +msgstr "没有新版本。" #: qt\about_box.py:71 msgid "New version {} available, download here." -msgstr "" +msgstr "有新版本{}可用,在这里下载。" #: qt\error_report_dialog.py:50 msgid "Error Report" @@ -1118,3 +1118,17 @@ msgstr "清空列表" #: qt\search_edit.py:78 msgid "Search..." msgstr "搜索..." + +#: qt\preferences_dialog.py:219 +msgid "" +"These options are for advanced users or for very specific situations, most " +"users should not have to modify these." +msgstr "这些选项是供高级用户或者特定情况下使用,大多数用户不该修改它们。" + +#: qt\preferences_dialog.py:225 +msgid "Include existence check after scan completion" +msgstr "在扫描结束后检查文件是否存在" + +#: qt\preferences_dialog.py:227 +msgid "Ignore difference in mtime when loading cached digests" +msgstr "载入缓存摘要时忽略mtime的不同" From 7a4506ece31dbfe15c27381476fda17a3cdb7bd2 Mon Sep 17 00:00:00 2001 From: Daniel Chalmers Date: Thu, 27 Apr 2023 00:54:39 -0500 Subject: [PATCH 31/56] Github -> GitHub (#1115) Co-authored-by: Andrew Senetar --- help/en/contribute.rst | 4 ++-- help/en/faq.rst | 6 +++--- locale/cs/LC_MESSAGES/ui.po | 6 +++--- locale/de/LC_MESSAGES/ui.po | 8 ++++---- locale/el/LC_MESSAGES/ui.po | 8 ++++---- locale/es/LC_MESSAGES/ui.po | 8 ++++---- locale/fr/LC_MESSAGES/ui.po | 8 ++++---- locale/hy/LC_MESSAGES/ui.po | 6 +++--- locale/it/LC_MESSAGES/ui.po | 8 ++++---- locale/ja/LC_MESSAGES/ui.po | 8 ++++---- locale/ko/LC_MESSAGES/ui.po | 8 ++++---- locale/ms/LC_MESSAGES/ui.po | 8 ++++---- locale/nl/LC_MESSAGES/ui.po | 8 ++++---- locale/pl_PL/LC_MESSAGES/ui.po | 8 ++++---- locale/pt_BR/LC_MESSAGES/ui.po | 8 ++++---- locale/ru/LC_MESSAGES/ui.po | 8 ++++---- locale/tr/LC_MESSAGES/ui.po | 8 ++++---- locale/ui.pot | 4 ++-- locale/uk/LC_MESSAGES/ui.po | 8 ++++---- locale/vi/LC_MESSAGES/ui.po | 8 ++++---- locale/zh_CN/LC_MESSAGES/ui.po | 8 ++++---- locale/zh_TW/LC_MESSAGES/ui.po | 8 ++++---- qt/error_report_dialog.py | 8 ++++---- 23 files changed, 85 insertions(+), 85 deletions(-) diff --git a/help/en/contribute.rst b/help/en/contribute.rst index 4d722c77..9292e688 100644 --- a/help/en/contribute.rst +++ b/help/en/contribute.rst @@ -24,7 +24,7 @@ Development process * `Issue Tracker`_ * `Issue labels meaning`_ -dupeGuru's source code is on Github and thus managed in a Git repository. At all times, you should +dupeGuru's source code is on GitHub and thus managed in a Git repository. At all times, you should be able to build from source a fresh checkout of the ``master`` branch using instructions from the ``README.md`` file at the root of this project. If you can't, it's a bug. Please report it. @@ -61,7 +61,7 @@ It's the same thing with feature requests. Description of a feature request, whe already been given to how such a feature would fit in the current design, are precious to developers and help them figure out a clear roadmap for the project. -So, even if you're not a developer, you can always open a Github account and create/comment issues. +So, even if you're not a developer, you can always open a GitHub account and create/comment issues. Your contribution will be much appreciated. **Documentation**. This is a bit trickier because dupeGuru's documentation is written with a rather diff --git a/help/en/faq.rst b/help/en/faq.rst index 97a75ad0..21b08dea 100644 --- a/help/en/faq.rst +++ b/help/en/faq.rst @@ -30,8 +30,8 @@ that makes sure that you will **always** keep at least one member of the duplica How can I report a bug a suggest a feature? ------------------------------------------- -dupeGuru is hosted on `Github`_ and it's also where issues are tracked. The best way to report a -bug or suggest a feature is to sign up on Github and `open an issue`_. +dupeGuru is hosted on `GitHub`_ and it's also where issues are tracked. The best way to report a +bug or suggest a feature is to sign up on GitHub and `open an issue`_. The mark box of a file I want to delete is disabled. What must I do? -------------------------------------------------------------------- @@ -176,5 +176,5 @@ Preferences are stored elsewhere: * Linux: ``~/.config/Hardcoded Software/dupeGuru.conf`` * Mac OS X: In the built-in ``defaults`` system, as ``com.hardcoded-software.dupeguru`` -.. _Github: https://github.com/arsenetar/dupeguru +.. _GitHub: https://github.com/arsenetar/dupeguru .. _open an issue: https://github.com/arsenetar/dupeguru/wiki/issue-labels diff --git a/locale/cs/LC_MESSAGES/ui.po b/locale/cs/LC_MESSAGES/ui.po index da5a9189..79cb68d0 100644 --- a/locale/cs/LC_MESSAGES/ui.po +++ b/locale/cs/LC_MESSAGES/ui.po @@ -1028,7 +1028,7 @@ msgstr "Něco se pokazilo. Co takhle nahlásit chybu?" #: qt\error_report_dialog.py:60 msgid "" -"Error reports should be reported as Github issues. You can copy the error traceback above and paste it in a new issue.\n" +"Error reports should be reported as GitHub issues. You can copy the error traceback above and paste it in a new issue.\n" "\n" "Please make sure to run a search for any already existing issues beforehand. Also make sure to test the very latest version available from the repository, since the bug you are experiencing might have already been patched.\n" "\n" @@ -1045,8 +1045,8 @@ msgstr "" "Přestože by aplikace měla po této chybě pokračovat, může být v nestabilním stavu, proto se doporučuje aplikaci restartovat." #: qt\error_report_dialog.py:80 -msgid "Go to Github" -msgstr "Přejít na Github" +msgid "Go to GitHub" +msgstr "Přejít na GitHub" #: qt\preferences.py:24 msgid "Czech" diff --git a/locale/de/LC_MESSAGES/ui.po b/locale/de/LC_MESSAGES/ui.po index 34ff0874..2fce0a7e 100644 --- a/locale/de/LC_MESSAGES/ui.po +++ b/locale/de/LC_MESSAGES/ui.po @@ -1045,7 +1045,7 @@ msgstr "Etwas ist schief gelaufen. Wie wäre es, den Fehler zu melden?" #: qt\error_report_dialog.py:60 msgid "" -"Error reports should be reported as Github issues. You can copy the error traceback above and paste it in a new issue.\n" +"Error reports should be reported as GitHub issues. You can copy the error traceback above and paste it in a new issue.\n" "\n" "Please make sure to run a search for any already existing issues beforehand. Also make sure to test the very latest version available from the repository, since the bug you are experiencing might have already been patched.\n" "\n" @@ -1053,7 +1053,7 @@ msgid "" "\n" "Although the application should continue to run after this error, it may be in an unstable state, so it is recommended that you restart the application." msgstr "" -"Fehlerberichte sollten als Github-Probleme gemeldet werden. Sie können den obigen Fehler-Traceback kopieren und in eine neue Ausgabe einfügen.\n" +"Fehlerberichte sollten als GitHub-Probleme gemeldet werden. Sie können den obigen Fehler-Traceback kopieren und in eine neue Ausgabe einfügen.\n" "\n" "Bitte stellen Sie sicher, dass Sie vorher nach bereits vorhandenen Problemen suchen. Stellen Sie außerdem sicher, dass Sie die neueste Version testen, die im Repository verfügbar ist, da der aufgetretene Fehler möglicherweise bereits behoben wurde.\n" "\n" @@ -1062,8 +1062,8 @@ msgstr "" "Obwohl die Anwendung nach diesem Fehler weiterhin ausgeführt werden sollte, befindet sie sich möglicherweise in einem instabilen Zustand. Es wird daher empfohlen, die Anwendung neu zu starten." #: qt\error_report_dialog.py:80 -msgid "Go to Github" -msgstr "Geh zu Github" +msgid "Go to GitHub" +msgstr "Geh zu GitHub" #: qt\preferences.py:24 msgid "Czech" diff --git a/locale/el/LC_MESSAGES/ui.po b/locale/el/LC_MESSAGES/ui.po index 1ee0562a..cce0fb65 100644 --- a/locale/el/LC_MESSAGES/ui.po +++ b/locale/el/LC_MESSAGES/ui.po @@ -1045,7 +1045,7 @@ msgstr "Κάτι πήγε στραβά. Μήπως να αναφερθεί το #: qt\error_report_dialog.py:60 msgid "" -"Error reports should be reported as Github issues. You can copy the error traceback above and paste it in a new issue.\n" +"Error reports should be reported as GitHub issues. You can copy the error traceback above and paste it in a new issue.\n" "\n" "Please make sure to run a search for any already existing issues beforehand. Also make sure to test the very latest version available from the repository, since the bug you are experiencing might have already been patched.\n" "\n" @@ -1053,7 +1053,7 @@ msgid "" "\n" "Although the application should continue to run after this error, it may be in an unstable state, so it is recommended that you restart the application." msgstr "" -"Οι αναφορές σφαλμάτων πρέπει να αναφέρονται ως ζητήματα Github. Μπορείτε να αντιγράψετε την ανίχνευση σφαλμάτων παραπάνω και να την επικολλήσετε σε ένα νέο ζήτημα.\n" +"Οι αναφορές σφαλμάτων πρέπει να αναφέρονται ως ζητήματα GitHub. Μπορείτε να αντιγράψετε την ανίχνευση σφαλμάτων παραπάνω και να την επικολλήσετε σε ένα νέο ζήτημα.\n" "\n" "Βεβαιωθείτε ότι έχετε πραγματοποιήσει αναζήτηση για τυχόν υπάρχοντα ζητήματα εκ των προτέρων. Επίσης, φροντίστε να δοκιμάσετε την πιο πρόσφατη διαθέσιμη έκδοση από το αποθετήριο, καθώς το σφάλμα που αντιμετωπίζετε ενδέχεται να έχει ήδη διορθωθεί.\n" "\n" @@ -1062,8 +1062,8 @@ msgstr "" "Παρόλο που η εφαρμογή θα πρέπει να συνεχίσει να εκτελείται μετά από αυτό το σφάλμα, ενδέχεται να βρίσκεται σε ασταθή κατάσταση, επομένως συνιστάται να κάνετε επανεκκίνηση της εφαρμογής." #: qt\error_report_dialog.py:80 -msgid "Go to Github" -msgstr "Επίσκεψη Github" +msgid "Go to GitHub" +msgstr "Επίσκεψη GitHub" #: qt\preferences.py:24 msgid "Czech" diff --git a/locale/es/LC_MESSAGES/ui.po b/locale/es/LC_MESSAGES/ui.po index 58cdea0b..d4de1b12 100644 --- a/locale/es/LC_MESSAGES/ui.po +++ b/locale/es/LC_MESSAGES/ui.po @@ -1045,7 +1045,7 @@ msgstr "Algo salió mal. ¿Qué tal informar el error?" #: qt\error_report_dialog.py:60 msgid "" -"Error reports should be reported as Github issues. You can copy the error traceback above and paste it in a new issue.\n" +"Error reports should be reported as GitHub issues. You can copy the error traceback above and paste it in a new issue.\n" "\n" "Please make sure to run a search for any already existing issues beforehand. Also make sure to test the very latest version available from the repository, since the bug you are experiencing might have already been patched.\n" "\n" @@ -1053,7 +1053,7 @@ msgid "" "\n" "Although the application should continue to run after this error, it may be in an unstable state, so it is recommended that you restart the application." msgstr "" -"Los informes de errores deben notificarse en Problemas de Github. Puede copiar el seguimiento del error anterior y pegarlo en un nuevo número.\n" +"Los informes de errores deben notificarse en Problemas de GitHub. Puede copiar el seguimiento del error anterior y pegarlo en un nuevo número.\n" "\n" "Asegúrese de realizar una búsqueda de los problemas ya existentes de antemano. También asegúrese de probar la última versión disponible en el repositorio, ya que es posible que el error que está experimentando ya se haya corregido.\n" "\n" @@ -1062,8 +1062,8 @@ msgstr "" "Aunque la aplicación debería continuar ejecutándose después de este error, puede estar en un estado inestable, por lo que se recomienda que reinicie la aplicación." #: qt\error_report_dialog.py:80 -msgid "Go to Github" -msgstr "Ir a Github" +msgid "Go to GitHub" +msgstr "Ir a GitHub" #: qt\preferences.py:24 msgid "Czech" diff --git a/locale/fr/LC_MESSAGES/ui.po b/locale/fr/LC_MESSAGES/ui.po index 2d6fb530..f9f2cdc7 100644 --- a/locale/fr/LC_MESSAGES/ui.po +++ b/locale/fr/LC_MESSAGES/ui.po @@ -1033,7 +1033,7 @@ msgstr "Un problème est survenu. Rapporter l'erreur?" #: qt\error_report_dialog.py:60 msgid "" -"Error reports should be reported as Github issues. You can copy the error traceback above and paste it in a new issue.\n" +"Error reports should be reported as GitHub issues. You can copy the error traceback above and paste it in a new issue.\n" "\n" "Please make sure to run a search for any already existing issues beforehand. Also make sure to test the very latest version available from the repository, since the bug you are experiencing might have already been patched.\n" "\n" @@ -1041,7 +1041,7 @@ msgid "" "\n" "Although the application should continue to run after this error, it may be in an unstable state, so it is recommended that you restart the application." msgstr "" -"Les rapports d'erreur doivent être envoyé via les tickets Github. Vous pouvez copier l'historique d'erreur ci-dessus et le coller dans un nouveau ticket.\n" +"Les rapports d'erreur doivent être envoyé via les tickets GitHub. Vous pouvez copier l'historique d'erreur ci-dessus et le coller dans un nouveau ticket.\n" "\n" "Veuillez vous assurer auparavant d'avoir fait une recherche pour un ticket similaire. Assurez-vous aussi d'avoir testé la toute dernière version disponible depuis le dépôt car le bug que vous avez rencontré a peut-être déjà été corrigé. \n" "\n" @@ -1050,8 +1050,8 @@ msgstr "" " Même si cette application continue de fonctionner après cette erreur, elle peut être dans un état instable, et il est donc recommandé de relancer l'application." #: qt\error_report_dialog.py:80 -msgid "Go to Github" -msgstr "Aller sur Github" +msgid "Go to GitHub" +msgstr "Aller sur GitHub" #: qt\preferences.py:24 msgid "Czech" diff --git a/locale/hy/LC_MESSAGES/ui.po b/locale/hy/LC_MESSAGES/ui.po index ad12a0e2..98d06b76 100755 --- a/locale/hy/LC_MESSAGES/ui.po +++ b/locale/hy/LC_MESSAGES/ui.po @@ -1013,7 +1013,7 @@ msgstr "Ինչ որ բան այնպես չգնաց. Հաղորդել սխալը? #: qt\error_report_dialog.py:60 msgid "" -"Error reports should be reported as Github issues. You can copy the error traceback above and paste it in a new issue.\n" +"Error reports should be reported as GitHub issues. You can copy the error traceback above and paste it in a new issue.\n" "\n" "Please make sure to run a search for any already existing issues beforehand. Also make sure to test the very latest version available from the repository, since the bug you are experiencing might have already been patched.\n" "\n" @@ -1021,7 +1021,7 @@ msgid "" "\n" "Although the application should continue to run after this error, it may be in an unstable state, so it is recommended that you restart the application." msgstr "" -"Error հաշվետվությունները պետք է հրապարակվեն որպես Github հարցերի շուրջ: Կարող եք վերևում պատճենել սխալի հետևումը և տեղադրել այն նոր համարում:\n" +"Error հաշվետվությունները պետք է հրապարակվեն որպես GitHub հարցերի շուրջ: Կարող եք վերևում պատճենել սխալի հետևումը և տեղադրել այն նոր համարում:\n" "\n" "Խնդրում ենք համոզվեք, որ նախապես փնտրեք արդեն գոյություն ունեցող ցանկացած խնդիր: Նաեւ համոզվեք, որ ստուգել են հենց վերջին տարբերակը մատչելի շտեմարան, քանի որ Bug դուք ապրում գուցե արդեն patched.\n" "\n" @@ -1030,7 +1030,7 @@ msgstr "" "Չնայած այս սխալից հետո ծրագիրը պետք է շարունակի գործել, այն կարող է լինել անկայուն վիճակում, ուստի խորհուրդ է տրվում վերագործարկել ծրագիրը:" #: qt\error_report_dialog.py:80 -msgid "Go to Github" +msgid "Go to GitHub" msgstr "Գնացեք Գիթուբ" #: qt\preferences.py:24 diff --git a/locale/it/LC_MESSAGES/ui.po b/locale/it/LC_MESSAGES/ui.po index 58627bbe..4c0e8e55 100644 --- a/locale/it/LC_MESSAGES/ui.po +++ b/locale/it/LC_MESSAGES/ui.po @@ -1049,7 +1049,7 @@ msgstr "Qualcosa è andato storto. Che ne dici di segnalare l'errore?" #: qt\error_report_dialog.py:60 msgid "" -"Error reports should be reported as Github issues. You can copy the error traceback above and paste it in a new issue.\n" +"Error reports should be reported as GitHub issues. You can copy the error traceback above and paste it in a new issue.\n" "\n" "Please make sure to run a search for any already existing issues beforehand. Also make sure to test the very latest version available from the repository, since the bug you are experiencing might have already been patched.\n" "\n" @@ -1057,7 +1057,7 @@ msgid "" "\n" "Although the application should continue to run after this error, it may be in an unstable state, so it is recommended that you restart the application." msgstr "" -"I rapporti di errore dovrebbero essere segnalati come problemi di Github. Puoi copiare il traceback degli errori sopra e incollarlo in un nuovo numero.\n" +"I rapporti di errore dovrebbero essere segnalati come problemi di GitHub. Puoi copiare il traceback degli errori sopra e incollarlo in un nuovo numero.\n" "\n" "Assicurati di eseguire prima una ricerca per eventuali problemi già esistenti. Assicurati anche di testare l'ultima versione disponibile dal repository, poiché il bug che stai riscontrando potrebbe essere già stato corretto.\n" "\n" @@ -1066,8 +1066,8 @@ msgstr "" "Sebbene l'applicazione debba continuare a essere eseguita dopo questo errore, potrebbe essere in uno stato instabile, quindi si consiglia di riavviare l'applicazione." #: qt\error_report_dialog.py:80 -msgid "Go to Github" -msgstr "Apri in Github" +msgid "Go to GitHub" +msgstr "Apri in GitHub" #: qt\preferences.py:24 msgid "Czech" diff --git a/locale/ja/LC_MESSAGES/ui.po b/locale/ja/LC_MESSAGES/ui.po index 4cc46276..95537d2b 100644 --- a/locale/ja/LC_MESSAGES/ui.po +++ b/locale/ja/LC_MESSAGES/ui.po @@ -1017,7 +1017,7 @@ msgstr "不明な理由により失敗しました。問題を報告しません #: qt\error_report_dialog.py:60 msgid "" -"Error reports should be reported as Github issues. You can copy the error traceback above and paste it in a new issue.\n" +"Error reports should be reported as GitHub issues. You can copy the error traceback above and paste it in a new issue.\n" "\n" "Please make sure to run a search for any already existing issues beforehand. Also make sure to test the very latest version available from the repository, since the bug you are experiencing might have already been patched.\n" "\n" @@ -1025,7 +1025,7 @@ msgid "" "\n" "Although the application should continue to run after this error, it may be in an unstable state, so it is recommended that you restart the application." msgstr "" -"エラーレポートはGithubの問題として報告する必要があります。 上記のエラートレースバックをコピーして、新しい問題に貼り付けることができます。\n" +"エラーレポートはGitHubの問題として報告する必要があります。 上記のエラートレースバックをコピーして、新しい問題に貼り付けることができます。\n" "\n" "事前に既存の問題を検索してください。 また、発生しているバグにはすでにパッチが適用されている可能性があるため、リポジトリから入手できる最新バージョンをテストしてください。\n" "\n" @@ -1034,8 +1034,8 @@ msgstr "" "このエラーの後もアプリケーションは実行を継続するはずですが、不安定な状態になっている可能性があるため、アプリケーションを再起動することをお勧めします。" #: qt\error_report_dialog.py:80 -msgid "Go to Github" -msgstr "Githubに移動" +msgid "Go to GitHub" +msgstr "GitHubに移動" #: qt\preferences.py:24 msgid "Czech" diff --git a/locale/ko/LC_MESSAGES/ui.po b/locale/ko/LC_MESSAGES/ui.po index 6a1eafdc..be69c23c 100644 --- a/locale/ko/LC_MESSAGES/ui.po +++ b/locale/ko/LC_MESSAGES/ui.po @@ -1018,7 +1018,7 @@ msgstr "문제가 발생했습니다. 오류를보고하는 것은 어떻습니 #: qt\error_report_dialog.py:60 msgid "" -"Error reports should be reported as Github issues. You can copy the error traceback above and paste it in a new issue.\n" +"Error reports should be reported as GitHub issues. You can copy the error traceback above and paste it in a new issue.\n" "\n" "Please make sure to run a search for any already existing issues beforehand. Also make sure to test the very latest version available from the repository, since the bug you are experiencing might have already been patched.\n" "\n" @@ -1026,7 +1026,7 @@ msgid "" "\n" "Although the application should continue to run after this error, it may be in an unstable state, so it is recommended that you restart the application." msgstr "" -"오류 보고서는 Github 문제로보고해야합니다. 위의 오류 추적을 복사하여 새 문제에 붙여 넣을 수 있습니다.\n" +"오류 보고서는 GitHub 문제로보고해야합니다. 위의 오류 추적을 복사하여 새 문제에 붙여 넣을 수 있습니다.\n" "\n" "이미 존재하는 문제에 대해 사전에 검색을 실행하십시오. 또한 경험하고있는 버그가 이미 패치되었을 수 있으므로 저장소에서 사용 가능한 최신 버전을 테스트해야합니다.\n" "\n" @@ -1035,8 +1035,8 @@ msgstr "" "이 오류 후에도 응용 프로그램이 계속 실행되어야하지만 불안정한 상태 일 수 있으므로 응용 프로그램을 다시 시작하는 것이 좋습니다." #: qt\error_report_dialog.py:80 -msgid "Go to Github" -msgstr "Github로 이동" +msgid "Go to GitHub" +msgstr "GitHub로 이동" #: qt\preferences.py:24 msgid "Czech" diff --git a/locale/ms/LC_MESSAGES/ui.po b/locale/ms/LC_MESSAGES/ui.po index ac322486..437a9ece 100644 --- a/locale/ms/LC_MESSAGES/ui.po +++ b/locale/ms/LC_MESSAGES/ui.po @@ -1040,7 +1040,7 @@ msgstr "Terdapat kesulitan yang terjadi. Apa kata laporkan ralat tersebut?" #: qt\error_report_dialog.py:60 msgid "" -"Error reports should be reported as Github issues. You can copy the error traceback above and paste it in a new issue.\n" +"Error reports should be reported as GitHub issues. You can copy the error traceback above and paste it in a new issue.\n" "\n" "Please make sure to run a search for any already existing issues beforehand. Also make sure to test the very latest version available from the repository, since the bug you are experiencing might have already been patched.\n" "\n" @@ -1048,7 +1048,7 @@ msgid "" "\n" "Although the application should continue to run after this error, it may be in an unstable state, so it is recommended that you restart the application." msgstr "" -"Laporan ralat patut dilaporkan sebagai isu Github. Anda boleh salin runut balik ralat di atas dan tampal dalam isu baharu.\n" +"Laporan ralat patut dilaporkan sebagai isu GitHub. Anda boleh salin runut balik ralat di atas dan tampal dalam isu baharu.\n" "\n" "Sila pastikan anda menggelintar dahulu kalau-kalau isu sudah wujud. Juga pastikan untuk cuba versi paling terbaharu yang disediakan dari repositori, kerana pepijat yang anda alami mungkin sudah ditampung.\n" "\n" @@ -1057,8 +1057,8 @@ msgstr "" "Walaupun aplikasi sepatutnya masih boleh digunakan selepas ralat ini, ia mungkin berada dalam keadaan tidak stabil, jadi anda digalakkan untuk memulakan semula aplikasi ini." #: qt\error_report_dialog.py:80 -msgid "Go to Github" -msgstr "Pergi ke Github" +msgid "Go to GitHub" +msgstr "Pergi ke GitHub" #: qt\preferences.py:24 msgid "Czech" diff --git a/locale/nl/LC_MESSAGES/ui.po b/locale/nl/LC_MESSAGES/ui.po index 91194a46..a5d97573 100644 --- a/locale/nl/LC_MESSAGES/ui.po +++ b/locale/nl/LC_MESSAGES/ui.po @@ -1036,7 +1036,7 @@ msgstr "Er is iets fout gegaan. Hoe zit het met het melden van de fout?" #: qt\error_report_dialog.py:60 msgid "" -"Error reports should be reported as Github issues. You can copy the error traceback above and paste it in a new issue.\n" +"Error reports should be reported as GitHub issues. You can copy the error traceback above and paste it in a new issue.\n" "\n" "Please make sure to run a search for any already existing issues beforehand. Also make sure to test the very latest version available from the repository, since the bug you are experiencing might have already been patched.\n" "\n" @@ -1044,7 +1044,7 @@ msgid "" "\n" "Although the application should continue to run after this error, it may be in an unstable state, so it is recommended that you restart the application." msgstr "" -"Foutrapporten moeten worden gerapporteerd als Github-problemen. U kunt de bovenstaande foutopsporing kopiëren en in een nieuwe uitgave plakken.\n" +"Foutrapporten moeten worden gerapporteerd als GitHub-problemen. U kunt de bovenstaande foutopsporing kopiëren en in een nieuwe uitgave plakken.\n" "\n" "Zorg ervoor dat u van tevoren een zoekopdracht uitvoert naar reeds bestaande problemen. Zorg er ook voor dat u de allernieuwste versie uit de repository test, aangezien de bug die u ondervindt mogelijk al gepatcht is.\n" "\n" @@ -1053,8 +1053,8 @@ msgstr "" "Hoewel de toepassing na deze fout zou moeten blijven werken, kan deze in een onstabiele toestand verkeren, dus het wordt aanbevolen de toepassing opnieuw te starten." #: qt\error_report_dialog.py:80 -msgid "Go to Github" -msgstr "Ga naar Github" +msgid "Go to GitHub" +msgstr "Ga naar GitHub" #: qt\preferences.py:24 msgid "Czech" diff --git a/locale/pl_PL/LC_MESSAGES/ui.po b/locale/pl_PL/LC_MESSAGES/ui.po index fa1db9fa..44f80d3b 100644 --- a/locale/pl_PL/LC_MESSAGES/ui.po +++ b/locale/pl_PL/LC_MESSAGES/ui.po @@ -1034,7 +1034,7 @@ msgstr "Coś poszło nie tak. Co powiesz na zgłoszenie błędu?" #: qt\error_report_dialog.py:60 msgid "" -"Error reports should be reported as Github issues. You can copy the error traceback above and paste it in a new issue.\n" +"Error reports should be reported as GitHub issues. You can copy the error traceback above and paste it in a new issue.\n" "\n" "Please make sure to run a search for any already existing issues beforehand. Also make sure to test the very latest version available from the repository, since the bug you are experiencing might have already been patched.\n" "\n" @@ -1042,7 +1042,7 @@ msgid "" "\n" "Although the application should continue to run after this error, it may be in an unstable state, so it is recommended that you restart the application." msgstr "" -"Raporty o błędach powinny być zgłaszane jako problemy z Github. Możesz skopiować powyższy opis błędu i wkleić go w nowym zgłoszeniu.\n" +"Raporty o błędach powinny być zgłaszane jako problemy z GitHub. Możesz skopiować powyższy opis błędu i wkleić go w nowym zgłoszeniu.\n" "\n" "Upewnij się, że wcześniej wyszukałeś już istniejący bilet. Upewnij się również, że przetestowałeś najnowszą wersję dostępną w repozytorium, ponieważ napotkany błąd mógł już zostać załatany.\n" "\n" @@ -1051,8 +1051,8 @@ msgstr "" "Chociaż aplikacja powinna nadal działać po tym błędzie, może być w stanie niestabilnym, dlatego zaleca się ponowne uruchomienie aplikacji." #: qt\error_report_dialog.py:80 -msgid "Go to Github" -msgstr "Przejdź do Github" +msgid "Go to GitHub" +msgstr "Przejdź do GitHub" #: qt\preferences.py:24 msgid "Czech" diff --git a/locale/pt_BR/LC_MESSAGES/ui.po b/locale/pt_BR/LC_MESSAGES/ui.po index 6a718524..534ee1a3 100644 --- a/locale/pt_BR/LC_MESSAGES/ui.po +++ b/locale/pt_BR/LC_MESSAGES/ui.po @@ -1034,7 +1034,7 @@ msgstr "Algo deu errado. Deseja relatar o erro?" #: qt\error_report_dialog.py:60 msgid "" -"Error reports should be reported as Github issues. You can copy the error traceback above and paste it in a new issue.\n" +"Error reports should be reported as GitHub issues. You can copy the error traceback above and paste it in a new issue.\n" "\n" "Please make sure to run a search for any already existing issues beforehand. Also make sure to test the very latest version available from the repository, since the bug you are experiencing might have already been patched.\n" "\n" @@ -1042,7 +1042,7 @@ msgid "" "\n" "Although the application should continue to run after this error, it may be in an unstable state, so it is recommended that you restart the application." msgstr "" -"Os relatórios de erros devem ser relatados como problemas do Github. Você pode copiar o rastreamento do erro acima e colá-lo em uma nova edição.\n" +"Os relatórios de erros devem ser relatados como problemas do GitHub. Você pode copiar o rastreamento do erro acima e colá-lo em uma nova edição.\n" "\n" "Por favor, certifique-se de executar uma pesquisa de qualquer problema já existente com antecedência. Certifique-se também de testar a versão mais recente disponível no repositório, uma vez que o bug que você está enfrentando pode já ter sido corrigido.\n" "\n" @@ -1051,8 +1051,8 @@ msgstr "" "Embora o aplicativo deva continuar a ser executado após esse erro, ele pode estar em um estado instável, portanto, é recomendável reiniciar o aplicativo." #: qt\error_report_dialog.py:80 -msgid "Go to Github" -msgstr "Ir para o Github" +msgid "Go to GitHub" +msgstr "Ir para o GitHub" #: qt\preferences.py:24 msgid "Czech" diff --git a/locale/ru/LC_MESSAGES/ui.po b/locale/ru/LC_MESSAGES/ui.po index cb7a5b68..a909457b 100644 --- a/locale/ru/LC_MESSAGES/ui.po +++ b/locale/ru/LC_MESSAGES/ui.po @@ -1038,7 +1038,7 @@ msgstr "Что-то пошло не так. Хотите отправить от #: qt\error_report_dialog.py:60 msgid "" -"Error reports should be reported as Github issues. You can copy the error traceback above and paste it in a new issue.\n" +"Error reports should be reported as GitHub issues. You can copy the error traceback above and paste it in a new issue.\n" "\n" "Please make sure to run a search for any already existing issues beforehand. Also make sure to test the very latest version available from the repository, since the bug you are experiencing might have already been patched.\n" "\n" @@ -1046,7 +1046,7 @@ msgid "" "\n" "Although the application should continue to run after this error, it may be in an unstable state, so it is recommended that you restart the application." msgstr "" -"Отчеты об ошибках следует оформлять в виде issues на Github. Вы можете скопировать трассировку ошибки выше и вставить ее в новый issue.\n" +"Отчеты об ошибках следует оформлять в виде issues на GitHub. Вы можете скопировать трассировку ошибки выше и вставить ее в новый issue.\n" "\n" "Обязательно заранее выполните поиск любых уже существующих проблем. Также не забудьте протестировать самую последнюю версию, доступную в репозитории, поскольку ошибка, с которой вы столкнулись, могла уже быть исправлена.\n" "\n" @@ -1055,8 +1055,8 @@ msgstr "" "Хотя приложение должно продолжить работу после этой ошибки, оно может находиться в нестабильном состоянии, поэтому рекомендуется перезапустить приложение." #: qt\error_report_dialog.py:80 -msgid "Go to Github" -msgstr "Перейти на Github" +msgid "Go to GitHub" +msgstr "Перейти на GitHub" #: qt\preferences.py:24 msgid "Czech" diff --git a/locale/tr/LC_MESSAGES/ui.po b/locale/tr/LC_MESSAGES/ui.po index 82bd7bfc..d99c871e 100644 --- a/locale/tr/LC_MESSAGES/ui.po +++ b/locale/tr/LC_MESSAGES/ui.po @@ -1035,7 +1035,7 @@ msgstr "Bir şeyler yanlış gitti. Hatayı raporlamak ister misin?" #: qt\error_report_dialog.py:60 msgid "" -"Error reports should be reported as Github issues. You can copy the error traceback above and paste it in a new issue.\n" +"Error reports should be reported as GitHub issues. You can copy the error traceback above and paste it in a new issue.\n" "\n" "Please make sure to run a search for any already existing issues beforehand. Also make sure to test the very latest version available from the repository, since the bug you are experiencing might have already been patched.\n" "\n" @@ -1043,7 +1043,7 @@ msgid "" "\n" "Although the application should continue to run after this error, it may be in an unstable state, so it is recommended that you restart the application." msgstr "" -"Hata raporları Github'da sorun (issue) olarak bildirilmelidir. Yukarıdaki hata kaynağını kopyalayabilir ve yeni sorun bildirimine yapıştırabilirsiniz\n" +"Hata raporları GitHub'da sorun (issue) olarak bildirilmelidir. Yukarıdaki hata kaynağını kopyalayabilir ve yeni sorun bildirimine yapıştırabilirsiniz\n" "\n" "Lütfen yeni sorun bildirimi oluşturmadan önce var olan sorunları aradığınızdan emin olun. Ayrıca depoda bulunan en son sürümü sınadığınızdan emin olun, karşılaştığınız hata hâlihazırda düzeltilmiş olabilir.\n" "\n" @@ -1052,8 +1052,8 @@ msgstr "" "Bu hatadan sonra uygulama çalışmaya sürdürebilse de kararsız durumda olabilir, bu nedenle uygulamayı yeniden başlatmanız önerilir." #: qt\error_report_dialog.py:80 -msgid "Go to Github" -msgstr "Github'a Git" +msgid "Go to GitHub" +msgstr "GitHub'a Git" #: qt\preferences.py:24 msgid "Czech" diff --git a/locale/ui.pot b/locale/ui.pot index ae8a7b94..c41c917e 100644 --- a/locale/ui.pot +++ b/locale/ui.pot @@ -996,7 +996,7 @@ msgstr "" #: qt\error_report_dialog.py:60 msgid "" -"Error reports should be reported as Github issues. You can copy the error traceback above and paste it in a new issue.\n" +"Error reports should be reported as GitHub issues. You can copy the error traceback above and paste it in a new issue.\n" "\n" "Please make sure to run a search for any already existing issues beforehand. Also make sure to test the very latest version available from the repository, since the bug you are experiencing might have already been patched.\n" "\n" @@ -1006,7 +1006,7 @@ msgid "" msgstr "" #: qt\error_report_dialog.py:80 -msgid "Go to Github" +msgid "Go to GitHub" msgstr "" #: qt\preferences.py:24 diff --git a/locale/uk/LC_MESSAGES/ui.po b/locale/uk/LC_MESSAGES/ui.po index 55510d24..c8d66e79 100755 --- a/locale/uk/LC_MESSAGES/ui.po +++ b/locale/uk/LC_MESSAGES/ui.po @@ -1034,7 +1034,7 @@ msgstr "Щось пішло не так. Як щодо повідомлення #: qt\error_report_dialog.py:60 msgid "" -"Error reports should be reported as Github issues. You can copy the error traceback above and paste it in a new issue.\n" +"Error reports should be reported as GitHub issues. You can copy the error traceback above and paste it in a new issue.\n" "\n" "Please make sure to run a search for any already existing issues beforehand. Also make sure to test the very latest version available from the repository, since the bug you are experiencing might have already been patched.\n" "\n" @@ -1042,7 +1042,7 @@ msgid "" "\n" "Although the application should continue to run after this error, it may be in an unstable state, so it is recommended that you restart the application." msgstr "" -"Звіти про помилки слід повідомляти як проблеми Github. Ви можете скопіювати помилку відстеження помилки вище та вставити її в нове видання.\n" +"Звіти про помилки слід повідомляти як проблеми GitHub. Ви можете скопіювати помилку відстеження помилки вище та вставити її в нове видання.\n" "\n" "Будь ласка, не забудьте заздалегідь здійснити пошук уже існуючих проблем. Також не забудьте протестувати найновішу версію, доступну зі сховища, оскільки виправлена помилка, можливо, вже виправлена.\n" "\n" @@ -1051,8 +1051,8 @@ msgstr "" "Незважаючи на те, що програма повинна продовжувати працювати після цієї помилки, вона може бути в нестабільному стані, тому рекомендується перезапустити програму." #: qt\error_report_dialog.py:80 -msgid "Go to Github" -msgstr "Перейдіть до Github" +msgid "Go to GitHub" +msgstr "Перейдіть до GitHub" #: qt\preferences.py:24 msgid "Czech" diff --git a/locale/vi/LC_MESSAGES/ui.po b/locale/vi/LC_MESSAGES/ui.po index 8560740b..6a225ada 100644 --- a/locale/vi/LC_MESSAGES/ui.po +++ b/locale/vi/LC_MESSAGES/ui.po @@ -1035,7 +1035,7 @@ msgstr "Đã xảy ra lỗi. Làm thế nào về việc báo cáo lỗi?" #: qt\error_report_dialog.py:60 msgid "" -"Error reports should be reported as Github issues. You can copy the error traceback above and paste it in a new issue.\n" +"Error reports should be reported as GitHub issues. You can copy the error traceback above and paste it in a new issue.\n" "\n" "Please make sure to run a search for any already existing issues beforehand. Also make sure to test the very latest version available from the repository, since the bug you are experiencing might have already been patched.\n" "\n" @@ -1043,7 +1043,7 @@ msgid "" "\n" "Although the application should continue to run after this error, it may be in an unstable state, so it is recommended that you restart the application." msgstr "" -"Các báo cáo lỗi phải được báo cáo dưới dạng sự cố Github. Bạn có thể sao chép các traceback lỗi trên và dán nó vào một vấn đề mới.\n" +"Các báo cáo lỗi phải được báo cáo dưới dạng sự cố GitHub. Bạn có thể sao chép các traceback lỗi trên và dán nó vào một vấn đề mới.\n" "\n" "Vui lòng đảm bảo chạy tìm kiếm bất kỳ vấn đề nào đã tồn tại trước đó. Ngoài ra, hãy đảm bảo kiểm tra phiên bản mới nhất có sẵn từ kho lưu trữ, vì lỗi bạn đang gặp phải có thể đã được vá.\n" "\n" @@ -1052,8 +1052,8 @@ msgstr "" "Mặc dù ứng dụng sẽ tiếp tục chạy sau lỗi này, nhưng nó có thể ở trạng thái không ổn định, vì vậy bạn nên khởi động lại ứng dụng." #: qt\error_report_dialog.py:80 -msgid "Go to Github" -msgstr "Truy cập Github" +msgid "Go to GitHub" +msgstr "Truy cập GitHub" #: qt\preferences.py:24 msgid "Czech" diff --git a/locale/zh_CN/LC_MESSAGES/ui.po b/locale/zh_CN/LC_MESSAGES/ui.po index 8d95f2ee..d43fc695 100644 --- a/locale/zh_CN/LC_MESSAGES/ui.po +++ b/locale/zh_CN/LC_MESSAGES/ui.po @@ -1015,7 +1015,7 @@ msgstr "发生错误,是否要报告错误?" #: qt\error_report_dialog.py:60 msgid "" -"Error reports should be reported as Github issues. You can copy the error traceback above and paste it in a new issue.\n" +"Error reports should be reported as GitHub issues. You can copy the error traceback above and paste it in a new issue.\n" "\n" "Please make sure to run a search for any already existing issues beforehand. Also make sure to test the very latest version available from the repository, since the bug you are experiencing might have already been patched.\n" "\n" @@ -1023,7 +1023,7 @@ msgid "" "\n" "Although the application should continue to run after this error, it may be in an unstable state, so it is recommended that you restart the application." msgstr "" -"错误报告应该以Github issue的形式进行提交。您可以把错误信息复制粘贴到新的issue中\n" +"错误报告应该以GitHub issue的形式进行提交。您可以把错误信息复制粘贴到新的issue中\n" "\n" "在提交新issue前,请搜索已经存在的issue,以确保没有其他人已经报告了相同的错误。同时请确保使用仓库中的最新版进行测试,因为您所遇到的bug可能已经被最新版修复。\n" "\n" @@ -1032,8 +1032,8 @@ msgstr "" "虽然本程序在此错误后依然会继续运行,但是可能处于不稳定的状态,因此推荐重启本程序。" #: qt\error_report_dialog.py:80 -msgid "Go to Github" -msgstr "前往Github" +msgid "Go to GitHub" +msgstr "前往GitHub" #: qt\preferences.py:24 msgid "Czech" diff --git a/locale/zh_TW/LC_MESSAGES/ui.po b/locale/zh_TW/LC_MESSAGES/ui.po index 8feed51a..ad604662 100644 --- a/locale/zh_TW/LC_MESSAGES/ui.po +++ b/locale/zh_TW/LC_MESSAGES/ui.po @@ -1015,7 +1015,7 @@ msgstr "发生错误,是否要报告错误?" #: qt\error_report_dialog.py:60 msgid "" -"Error reports should be reported as Github issues. You can copy the error traceback above and paste it in a new issue.\n" +"Error reports should be reported as GitHub issues. You can copy the error traceback above and paste it in a new issue.\n" "\n" "Please make sure to run a search for any already existing issues beforehand. Also make sure to test the very latest version available from the repository, since the bug you are experiencing might have already been patched.\n" "\n" @@ -1023,7 +1023,7 @@ msgid "" "\n" "Although the application should continue to run after this error, it may be in an unstable state, so it is recommended that you restart the application." msgstr "" -"错误报告应该以Github issue的形式进行提交。您可以把错误信息复制粘贴到新的issue中\n" +"错误报告应该以GitHub issue的形式进行提交。您可以把错误信息复制粘贴到新的issue中\n" "\n" "在提交新issue前,请搜索已经存在的issue,以确保没有其他人已经报告了相同的错误。同时请确保使用仓库中的最新版进行测试,因为您所遇到的bug可能已经被最新版修复。\n" "\n" @@ -1032,8 +1032,8 @@ msgstr "" "虽然本程序在此错误后依然会继续运行,但是可能处于不稳定的状态,因此推荐重启本程序。" #: qt\error_report_dialog.py:80 -msgid "Go to Github" -msgstr "前往Github" +msgid "Go to GitHub" +msgstr "前往GitHub" #: qt\preferences.py:24 msgid "Czech" diff --git a/qt/error_report_dialog.py b/qt/error_report_dialog.py index 9a8dc2f9..acf8f15e 100644 --- a/qt/error_report_dialog.py +++ b/qt/error_report_dialog.py @@ -43,7 +43,7 @@ def __init__(self, parent, github_url, error, **kwargs): self.errorTextEdit.setPlainText(error_text) self.github_url = github_url - self.sendButton.clicked.connect(self.goToGithub) + self.sendButton.clicked.connect(self.goToGitHub) self.dontSendButton.clicked.connect(self.reject) def _setupUi(self): @@ -58,7 +58,7 @@ def _setupUi(self): self.errorTextEdit.setReadOnly(True) self.verticalLayout.addWidget(self.errorTextEdit) msg = tr( - "Error reports should be reported as Github issues. You can copy the error traceback " + "Error reports should be reported as GitHub issues. You can copy the error traceback " "above and paste it in a new issue.\n\nPlease make sure to run a search for any already " "existing issues beforehand. Also make sure to test the very latest version available from the repository, " "since the bug you are experiencing might have already been patched.\n\n" @@ -77,13 +77,13 @@ def _setupUi(self): self.dontSendButton.setMinimumSize(QSize(110, 0)) self.horizontalLayout.addWidget(self.dontSendButton) self.sendButton = QPushButton(self) - self.sendButton.setText(tr("Go to Github")) + self.sendButton.setText(tr("Go to GitHub")) self.sendButton.setMinimumSize(QSize(110, 0)) self.sendButton.setDefault(True) self.horizontalLayout.addWidget(self.sendButton) self.verticalLayout.addLayout(self.horizontalLayout) - def goToGithub(self): + def goToGitHub(self): open_url(self.github_url) From a257dbf0d5d89b57074762171877d31a89f1ff85 Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Thu, 27 Apr 2023 01:22:06 -0500 Subject: [PATCH 32/56] fix(win): Shorten file description Fix #1119 --- win_version_info.temp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/win_version_info.temp b/win_version_info.temp index 28f0fd47..e50e20fb 100644 --- a/win_version_info.temp +++ b/win_version_info.temp @@ -30,7 +30,7 @@ VSVersionInfo( StringTable( u'040904B0', [StringStruct(u'CompanyName', u'Hardcoded Software'), - StringStruct(u'FileDescription', u'dupeGuru is a tool to find duplicate files on your computer.'), + StringStruct(u'FileDescription', u'dupeGuru'), StringStruct(u'FileVersion', u'{0}.{1}.{2}.0'), StringStruct(u'InternalName', u'dupeGuru'), StringStruct(u'LegalCopyright', u'© Hardcoded Software'), From 10405ad063785cb026b07908e4a5ba4f3f634560 Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Thu, 27 Apr 2023 01:36:32 -0500 Subject: [PATCH 33/56] fix(build): Clean prior qt/dg_rc.py file before build Since calls without pyqrcc5 may result in a broken file, clean prior qt/gg_rc.py file before calling pyqrcc5. This makes troubleshooting pyqrcc5 issues more straightforward. Fix #1103 --- build.py | 1 + 1 file changed, 1 insertion(+) diff --git a/build.py b/build.py index 06905a11..cedb8bb0 100644 --- a/build.py +++ b/build.py @@ -129,6 +129,7 @@ def build_normal(): print("Building localizations") build_localizations() print("Building Qt stuff") + Path("qt", "dg_rc.py").unlink(missing_ok=True) print_and_do("pyrcc5 {} > {}".format(Path("qt", "dg.qrc"), Path("qt", "dg_rc.py"))) fix_qt_resource_file(Path("qt", "dg_rc.py")) build_help() From c5a71f61b8d543af940e3d8955c12877651c8a0f Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Thu, 27 Apr 2023 02:20:02 -0500 Subject: [PATCH 34/56] feat: Upgrade dependencies Upgrade required dependency versions for most dependencies. Add maximum version to most dependencies as well. --- requirements-extra.txt | 4 ++-- requirements.txt | 14 +++++++------- setup.cfg | 8 ++++---- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/requirements-extra.txt b/requirements-extra.txt index 1d175bf8..d22806fd 100644 --- a/requirements-extra.txt +++ b/requirements-extra.txt @@ -1,4 +1,4 @@ -pytest>=6,<7 +pytest>=7,<8 flake8 black -pyinstaller>=4.5,<5.0; sys_platform != 'linux' +pyinstaller>=5.6,<6.0; sys_platform != 'linux' diff --git a/requirements.txt b/requirements.txt index e52757ba..99c52cad 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,9 @@ -distro>=1.5.0 -mutagen>=1.44.0 -polib>=1.1.0 -PyQt5 >=5.14.1,<6.0; sys_platform != 'linux' -pywin32>=228; sys_platform == 'win32' +distro>=1.8.0,<2.0.0 +mutagen>=1.46.0,<2.0.0 +polib>=1.1.0,<2.0.0 +PyQt5 >=5.15.0,<6.0; sys_platform != 'linux' +pywin32>=304; sys_platform == 'win32' semantic-version>=2.0.0,<3.0.0 -Send2Trash>=1.3.0 -sphinx>=3.0.0 +Send2Trash>=1.8.2,<2.0.0 +sphinx>=5.3.0,<7.0.0 xxhash>=3.0.0,<4.0.0 diff --git a/setup.cfg b/setup.cfg index b52bdd37..bc12410b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -29,10 +29,10 @@ classifiers = packages = find: python_requires = >=3.7 install_requires = - Send2Trash>=1.3.0 - mutagen>=1.45.1 - distro>=1.5.0 - PyQt5 >=5.14.1,<6.0; sys_platform != 'linux' + Send2Trash>=1.8.2,<2.0.0 + mutagen>=1.46.0,<2.0.0 + distro>=1.8.0,<2.0.0 + PyQt5 >=5.15.0,<6.0; sys_platform != 'linux' pywin32>=228; sys_platform == 'win32' semantic-version>=2.0.0,<3.0.0 xxhash>=3.0.0,<4.0.0 From fe0e4bef91955dcc0f8aa44f0c272f658379fbf3 Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 1 Jun 2023 11:40:20 +1000 Subject: [PATCH 35/56] RE: Rewrote some of the korean translation to be more understandable I have updated my fork and moved my changes from before. --- locale/ko/LC_MESSAGES/columns.po | 6 +- locale/ko/LC_MESSAGES/core.po | 24 ++++---- locale/ko/LC_MESSAGES/ui.po | 96 ++++++++++++++++---------------- 3 files changed, 63 insertions(+), 63 deletions(-) diff --git a/locale/ko/LC_MESSAGES/columns.po b/locale/ko/LC_MESSAGES/columns.po index 83ab66c8..ef8677cf 100644 --- a/locale/ko/LC_MESSAGES/columns.po +++ b/locale/ko/LC_MESSAGES/columns.po @@ -26,11 +26,11 @@ msgstr "길이" #: core\me\prioritize.py:30 core\me\result_table.py:23 msgid "Bitrate" -msgstr "비트레이트" +msgstr "비트전송률" #: core\me\prioritize.py:37 msgid "Samplerate" -msgstr "샘플레이트" +msgstr "샘플전송률" #: core\me\result_table.py:19 core\pe\result_table.py:19 core\prioritize.py:94 #: core\se\result_table.py:19 @@ -52,7 +52,7 @@ msgstr "시간" #: core\me\result_table.py:24 msgid "Sample Rate" -msgstr "샘플레이트" +msgstr "샘플전송률" #: core\me\result_table.py:25 core\pe\result_table.py:22 core\prioritize.py:65 #: core\se\result_table.py:22 diff --git a/locale/ko/LC_MESSAGES/core.po b/locale/ko/LC_MESSAGES/core.po index 4c594560..fd10b9b1 100644 --- a/locale/ko/LC_MESSAGES/core.po +++ b/locale/ko/LC_MESSAGES/core.po @@ -58,19 +58,19 @@ msgstr "중복 파일이 없습니다." #: core\app.py:319 msgid "All marked files were copied successfully." -msgstr "마크한 모든 파일이 성공적으로 복사되었습니다." +msgstr "마크된 모든 파일이 성공적으로 복사되었습니다." #: core\app.py:321 msgid "All marked files were moved successfully." -msgstr "마크한 모든 파일이 성공적으로 이동되었습니다." +msgstr "마크된 모든 파일이 성공적으로 이동되었습니다." #: core\app.py:323 msgid "All marked files were deleted successfully." -msgstr "마크한 모든 파일이 성공적으로 삭제되었습니다." +msgstr "마크된 모든 파일이 성공적으로 삭제되었습니다." #: core\app.py:325 msgid "All marked files were successfully sent to Trash." -msgstr "마크한 모든 파일을 휴지통으로 보냈습니다." +msgstr "마크된 모든 파일을 휴지통으로 보냈습니다." #: core\app.py:330 msgid "Could not load file: {}" @@ -88,19 +88,19 @@ msgstr "'{}' 가 존재하지 않습니다." msgid "" "All selected %d matches are going to be ignored in all subsequent scans. " "Continue?" -msgstr "선택한 %d개 항목을 앞으로 검색에서 무시합니다. 진행할까요?" +msgstr "선택한 %d개 항목을 검색에서 무시합니다. 진행하시겠습니까?" #: core\app.py:473 msgid "Select a directory to copy marked files to" -msgstr "마크한 파일을 복사할 경로를 선택하세요:" +msgstr "마크하신 파일을 복사할 경로를 선택하세요:" #: core\app.py:475 msgid "Select a directory to move marked files to" -msgstr "마크한 파일을 이동할 경로를 선택하세요:" +msgstr "마크하신 파일을 이동할 경로를 선택하세요:" #: core\app.py:514 msgid "Select a destination for your exported CSV" -msgstr "CSV 파일을 저장할 폴더를 선택하세요" +msgstr "CSV 파일의 저장 경로를 지정해주세요" #: core\app.py:520 core\app.py:781 core\app.py:791 msgid "Couldn't write to file: {}" @@ -112,7 +112,7 @@ msgstr "사용자 지정 명령을 설정하지 않았습니다. 기본 설정 #: core\app.py:705 core\app.py:717 msgid "You are about to remove %d files from results. Continue?" -msgstr "결과에서 %d 개의 파일을 제거하려고합니다. 실행할까요?" +msgstr "결과에서 %d 개의 파일을 제거하려고합니다. 실행하시겠습니까?" #: core\app.py:753 msgid "{} duplicate groups were changed by the re-prioritization." @@ -120,11 +120,11 @@ msgstr "{} 개의 중복 그룹이 우선 순위 재 지정으로 변경되었 #: core\app.py:801 msgid "The selected directories contain no scannable file." -msgstr "선택한 디렉토리에 스캔 가능한 파일이 없습니다." +msgstr "선택한 경로에 스캔 가능한 파일이 없습니다." #: core\app.py:817 msgid "Collecting files to scan" -msgstr "스캔 할 파일 수집" +msgstr "스캔 가능 파일 수집중" #: core\app.py:867 msgid "%s (%d discarded)" @@ -220,7 +220,7 @@ msgstr "최단" #: core\prioritize.py:142 msgid "Highest" -msgstr "제일 높은" +msgstr "최고" #: core\prioritize.py:142 msgid "Lowest" diff --git a/locale/ko/LC_MESSAGES/ui.po b/locale/ko/LC_MESSAGES/ui.po index be69c23c..084b3210 100644 --- a/locale/ko/LC_MESSAGES/ui.po +++ b/locale/ko/LC_MESSAGES/ui.po @@ -19,7 +19,7 @@ msgstr "나가기" #: qt/app.py:82 qt/preferences_dialog.py:116 #: cocoa/en.lproj/Localizable.strings:0 msgid "Options" -msgstr "옵션" +msgstr "설정" #: qt/app.py:83 qt/ignore_list_dialog.py:32 #: cocoa/en.lproj/Localizable.strings:0 @@ -82,7 +82,7 @@ msgstr "(미지원)" #: qt/deletion_options.py:49 cocoa/en.lproj/Localizable.strings:0 msgid "Directly delete files" -msgstr "즉시 삭제" +msgstr "파일 직접삭제" #: qt/deletion_options.py:51 cocoa/en.lproj/Localizable.strings:0 msgid "" @@ -159,7 +159,7 @@ msgstr "표준" #: qt/directories_dialog.py:128 cocoa/en.lproj/Localizable.strings:0 msgid "Scan Type:" -msgstr "검색 방식:" +msgstr "스캔 방식:" #: qt/directories_dialog.py:135 msgid "More Options" @@ -167,7 +167,7 @@ msgstr "설정" #: qt/directories_dialog.py:139 cocoa/en.lproj/Localizable.strings:0 msgid "Select folders to scan and press \"Scan\"." -msgstr "검색할 폴더들을 목록에 추가하고 오른쪽 아래의 \"스캔\" 버튼을 누르십시오." +msgstr "스캔할 폴더들을 목록에 추가하고 오른쪽 아래의 \"스캔\" 버튼을 누르십시오." #: qt/directories_dialog.py:163 cocoa/en.lproj/Localizable.strings:0 msgid "Load Results" @@ -183,7 +183,7 @@ msgstr "저장되지 않은 결과" #: qt/directories_dialog.py:231 cocoa/en.lproj/Localizable.strings:0 msgid "You have unsaved results, do you really want to quit?" -msgstr "저장되지 않은 결과가 있습니다. 종료하시겠습니까?" +msgstr "결과를 저장하지않고 종료하시겠습니까?" #: qt/directories_dialog.py:239 cocoa/en.lproj/Localizable.strings:0 msgid "Select a folder to add to the scanning list" @@ -191,7 +191,7 @@ msgstr "스캔 목록에 추가 할 폴더를 선택하십시오" #: qt/directories_dialog.py:266 cocoa/en.lproj/Localizable.strings:0 msgid "Select a results file to load" -msgstr "로드 할 결과 파일을 선택하십시오" +msgstr "불러올 결과 파일을 선택하십시오" #: qt/directories_dialog.py:267 msgid "All Files (*.*)" @@ -207,7 +207,7 @@ msgstr "새 스캔 시작" #: qt/directories_dialog.py:279 cocoa/en.lproj/Localizable.strings:0 msgid "You have unsaved results, do you really want to continue?" -msgstr "저장되지 않은 결과가 있습니다. 계속 하시겠습니까?" +msgstr "결과를 저장하지않고 진행하시겠습니까?" #: qt/directories_model.py:23 cocoa/en.lproj/Localizable.strings:0 msgid "Name" @@ -246,7 +246,7 @@ msgstr "파일 속성" #: qt/me/preferences_dialog.py:30 cocoa/en.lproj/Localizable.strings:0 msgid "Tags to scan:" -msgstr "스캔 할 태그 :" +msgstr "스캔 태그 :" #: qt/me/preferences_dialog.py:36 cocoa/en.lproj/Localizable.strings:0 msgid "Track" @@ -317,11 +317,11 @@ msgstr "필터 민감도:" #: qt/preferences_dialog.py:69 msgid "More Results" -msgstr "더 많은 결과" +msgstr "결과 더보기" #: qt/preferences_dialog.py:74 msgid "Fewer Results" -msgstr "더 적은 결과" +msgstr "결과 줄이기" #: qt/preferences_dialog.py:81 msgid "Font size:" @@ -353,7 +353,7 @@ msgstr "사용자 지정 명령 (인수: %d 은 중복, %r 은 참조):" #: qt/preferences_dialog.py:174 msgid "dupeGuru has to restart for language changes to take effect." -msgstr "언어 변경 사항을 적용하려면 dupeGuru를 다시 시작해야합니다." +msgstr "언어 변경은 dupeGuru의 재시작이 필요합니다." #: qt/prioritize_dialog.py:75 cocoa/en.lproj/Localizable.strings:0 msgid "Re-Prioritize duplicates" @@ -365,12 +365,12 @@ msgid "" " the best to these criteria to their respective group's reference position. " "Read the help file for more information." msgstr "" -"오른쪽 상자에 기준을 추가하고 확인을 클릭하여 이러한 기준에 가장 적합한 복제를 해당 그룹의 참조 위치로 보냅니다. 자세한 정보는 도움말" -" 파일을 읽으십시오." +"오른쪽 박스에 기준을 추가하고 확인을 눌러 가장 기준에 해당되는 복제를 해당 그룹의 참조 위치로 보냅니다." +" 상세한 정보는 도움말에 있습니다" #: qt/problem_dialog.py:33 cocoa/en.lproj/Localizable.strings:0 msgid "Problems!" -msgstr "문제!" +msgstr "문제 발생!" #: qt/problem_dialog.py:37 cocoa/en.lproj/Localizable.strings:0 msgid "" @@ -378,12 +378,12 @@ msgid "" "these problems are described in the table below. Those files were not " "removed from your results." msgstr "" -"일부 (또는 전체) 파일을 처리하는 데 문제가 있습니다. 이러한 문제의 원인은 아래 표에 설명되어 있습니다. 해당 파일은 결과에서 " +"일부 (또는 전체) 파일을 처리하는 데 문제가 있었습니다. 문제의 원인은 아래 표에 표시 되있습니다. 문제되는 파일들은 결과에서 " "제거되지 않았습니다." #: qt/problem_dialog.py:56 msgid "Reveal Selected" -msgstr "선택한 항목 표시" +msgstr "선택 항목 표시" #: qt/result_window.py:57 qt/result_window.py:104 qt/result_window.py:167 #: qt/result_window.py:191 cocoa/en.lproj/Localizable.strings:0 @@ -396,23 +396,23 @@ msgstr "중복파일만 보기" #: qt/result_window.py:59 cocoa/en.lproj/Localizable.strings:0 msgid "Show Delta Values" -msgstr "델타 값만 보기" +msgstr "델타 값 표시" #: qt/result_window.py:60 msgid "Send Marked to Recycle Bin..." -msgstr "마크한 모든 파일을 휴지통으로 보내기" +msgstr "마크된 모든 파일을 휴지통으로 보내기" #: qt/result_window.py:61 cocoa/en.lproj/Localizable.strings:0 msgid "Move Marked to..." -msgstr "마크한 모든 파일을 이동..." +msgstr "마크된 모든 파일을 이동..." #: qt/result_window.py:62 cocoa/en.lproj/Localizable.strings:0 msgid "Copy Marked to..." -msgstr "마크한 모든 파일을 복사..." +msgstr "마크된 모든 파일을 복사..." #: qt/result_window.py:63 cocoa/en.lproj/Localizable.strings:0 msgid "Remove Marked from Results" -msgstr "결과 목록에서 마크한 모든 파일을 제외" +msgstr "결과 목록에서 마크된 모든 파일을 제외" #: qt/result_window.py:64 cocoa/en.lproj/Localizable.strings:0 msgid "Re-Prioritize Results..." @@ -516,7 +516,7 @@ msgstr "%@ 결과" #: cocoa/en.lproj/Localizable.strings:0 msgid "Action" -msgstr "행위" +msgstr "동작" #: cocoa/en.lproj/Localizable.strings:0 msgid "Add New Folder..." @@ -564,7 +564,7 @@ msgstr "델타" #: cocoa/en.lproj/Localizable.strings:0 msgid "Details of Selected File" -msgstr "선택한 파일의 세부 정보" +msgstr "선택 파일 세부 정보" #: cocoa/en.lproj/Localizable.strings:0 msgid "Details Panel" @@ -572,7 +572,7 @@ msgstr "세부 정보 패널" #: cocoa/en.lproj/Localizable.strings:0 msgid "Directories" -msgstr "디렉토리" +msgstr "경로" #: cocoa/en.lproj/Localizable.strings:0 msgid "dupeGuru" @@ -588,7 +588,7 @@ msgstr "dupeGuru 결과" #: cocoa/en.lproj/Localizable.strings:0 msgid "dupeGuru Website" -msgstr "dupeGuru 웹 사이트" +msgstr "dupeGuru 홈페이지" #: cocoa/en.lproj/Localizable.strings:0 msgid "Edit" @@ -604,7 +604,7 @@ msgstr "XHTML로 결과 내보내기" #: cocoa/en.lproj/Localizable.strings:0 msgid "Fewer results" -msgstr "더 적은 결과" +msgstr "결과 축소" #: cocoa/en.lproj/Localizable.strings:0 msgid "Filter" @@ -640,7 +640,7 @@ msgstr "이보다 작은 파일 무시 :" #: cocoa/en.lproj/Localizable.strings:0 msgid "Load from file..." -msgstr "파일에서로드..." +msgstr "파일에서 불러오기..." #: cocoa/en.lproj/Localizable.strings:0 msgid "Minimize" @@ -652,7 +652,7 @@ msgstr "모드" #: cocoa/en.lproj/Localizable.strings:0 msgid "More results" -msgstr "더 많은 결과" +msgstr "결과 더보기" #: cocoa/en.lproj/Localizable.strings:0 msgid "Ok" @@ -660,7 +660,7 @@ msgstr "확인" #: cocoa/en.lproj/Localizable.strings:0 msgid "Paste" -msgstr "붙여 넣다" +msgstr "붙여넣기" #: cocoa/en.lproj/Localizable.strings:0 msgid "Preferences..." @@ -668,7 +668,7 @@ msgstr "환경 설정..." #: cocoa/en.lproj/Localizable.strings:0 msgid "Quick Look" -msgstr "한눈에" +msgstr "빠른보기" #: cocoa/en.lproj/Localizable.strings:0 msgid "Quit dupeGuru" @@ -696,7 +696,7 @@ msgstr "모두 선택" #: cocoa/en.lproj/Localizable.strings:0 msgid "Send Marked to Trash..." -msgstr "마크한 파일을 휴지통으로 보내기" +msgstr "마크된 파일을 휴지통으로 보내기" #: cocoa/en.lproj/Localizable.strings:0 msgid "Services" @@ -712,7 +712,7 @@ msgstr "중복 스캔 시작" #: cocoa/en.lproj/Localizable.strings:0 msgid "The name '%@' already exists." -msgstr "'%@' 이름이 이미 존재합니다." +msgstr "'%@' 의 이름이 이미 존재합니다." #: cocoa/en.lproj/Localizable.strings:0 msgid "Window" @@ -720,7 +720,7 @@ msgstr "창" #: cocoa/en.lproj/Localizable.strings:0 msgid "Zoom" -msgstr "줌" +msgstr "확대" #: qt\app.py:158 msgid "Exclusion Filters" @@ -732,27 +732,27 @@ msgstr "스캔 결과" #: qt\directories_dialog.py:95 msgid "Load Directories..." -msgstr "디렉토리로드 ..." +msgstr "경로 불러오는중 ..." #: qt\directories_dialog.py:96 msgid "Save Directories..." -msgstr "디렉토리 저장 ..." +msgstr "경로 저장중 ..." #: qt\directories_dialog.py:337 msgid "Select a directories file to load" -msgstr "로드 할 디렉토리 파일을 선택하십시오" +msgstr "불러올 경로를 선택하십시오" #: qt\directories_dialog.py:338 msgid "dupeGuru Results (*.dupegurudirs)" -msgstr "dupeguru 디렉토리 파일 (*.dupegurudirs)" +msgstr "dupeguru 경로 파일 (*.dupegurudirs)" #: qt\directories_dialog.py:347 msgid "Select a file to save your directories to" -msgstr "디렉토리를 저장할 파일을 선택하십시오" +msgstr "경로를 저장할 파일을 선택하십시오" #: qt\directories_dialog.py:348 msgid "dupeGuru Directories (*.dupegurudirs)" -msgstr "dupeguru 디렉토리 파일 (*.dupegurudirs)" +msgstr "dupeguru 경로 파일 (*.dupegurudirs)" #: qt\exclude_list_dialog.py:44 msgid "Add" @@ -768,11 +768,11 @@ msgstr "테스트 문자열" #: qt\exclude_list_dialog.py:83 msgid "Type a python regular expression here..." -msgstr "여기에 파이썬 정규식을 입력하십시오 ..." +msgstr "여기에 파이썬 정규식을 입력해주세요 ..." #: qt\exclude_list_dialog.py:85 msgid "Type a file system path or filename here..." -msgstr "여기에 파일 시스템 경로 또는 파일 이름을 입력하십시오 ..." +msgstr "여기에 파일 시스템 경로 또는 파일 이름을 입력해주세요 ..." #: qt\exclude_list_dialog.py:152 msgid "" @@ -792,11 +792,11 @@ msgstr "컴파일 오류 :" #: qt\pe\image_viewer.py:56 msgid "Increase zoom" -msgstr "줌을 증가" +msgstr "확대 증가" #: qt\pe\image_viewer.py:66 msgid "Decrease zoom" -msgstr "줌을 감소" +msgstr "확대 감소" #: qt\pe\image_viewer.py:71 msgid "Ctrl+/" @@ -812,7 +812,7 @@ msgstr "Ctrl+*" #: qt\pe\image_viewer.py:86 msgid "Best fit" -msgstr "최고로 잘 맞는" +msgstr "최고 일치" #: qt\pe\preferences_dialog.py:49 msgid "Picture cache mode:" @@ -958,7 +958,7 @@ msgstr "캐시 제거" msgid "" "Do you really want to clear the cache? This will remove all cached file " "hashes and picture analysis." -msgstr "캐시를 제거할까요? 캐시에는 파일 해시 및 이미지 분석 결과가 포함되어 있습니다." +msgstr "캐시를 제거하시겠습니까? 캐시에는 파일 해시 및 이미지 분석 결과가 포함되어 있습니다." #: qt\app.py:299 msgid "Cache cleared." @@ -970,11 +970,11 @@ msgstr "다크 모드 사용" #: qt\preferences_dialog.py:241 msgid "Profile scan operation" -msgstr "검색 과정 프로파일" +msgstr "프로파일 스캔 작업" #: qt\preferences_dialog.py:242 msgid "Profile the scan operation and save logs for optimization." -msgstr "검색 과정을 프로파일하고 로그를 저장합니다." +msgstr "최적화를 위해 스캔 프로파일 작업과 로그를 저장합니다." #: qt\preferences_dialog.py:246 msgid "Logs located in: {}" @@ -1014,7 +1014,7 @@ msgstr "오류보고" #: qt\error_report_dialog.py:54 msgid "Something went wrong. How about reporting the error?" -msgstr "문제가 발생했습니다. 오류를보고하는 것은 어떻습니까?" +msgstr "예상치 못한 문제가 발생했습니다. 오류 보고를 추천드립니다." #: qt\error_report_dialog.py:60 msgid "" From 99ec4e0f27cf189618cbdb838af60db5080be117 Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Thu, 8 Jun 2023 01:14:52 -0500 Subject: [PATCH 36/56] fix: Minor cleanups and fixes - Update NullJob to subclass Job - Remove unnecessary size pre-read in _getMatches() as file sizes are already loaded during file scan via stat call - Skip ref check if contents scan as the scan already prevents this from happening, some of the other scans do things differently and need to be reviewed before removing this post step completely - Add guard on partial hashing to just hash the whole file if smaller than the offset and size and use the value for both the partial digest and digest --- core/fs.py | 20 +++++++++++--------- core/scanner.py | 6 +++--- hscommon/jobprogress/job.py | 7 ++----- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/core/fs.py b/core/fs.py index 11a29a20..e927fd0b 100644 --- a/core/fs.py +++ b/core/fs.py @@ -54,6 +54,9 @@ # Minimum size below which partial hashing is not used MIN_FILE_SIZE = 3 * CHUNK_SIZE # 3MiB, because we take 3 samples +# Partial hashing offset and size +PARTIAL_OFFSET_SIZE = (0x4000, 0x4000) + class FSError(Exception): cls_message = "An error has occured on '{name}' in '{parent}'" @@ -243,14 +246,9 @@ def _calc_digest(self): def _calc_digest_partial(self): # type: () -> bytes - - # This offset is where we should start reading the file to get a partial hash - # For audio file, it should be where audio data starts - offset, size = (0x4000, 0x4000) - with self.path.open("rb") as fp: - fp.seek(offset) - partial_data = fp.read(size) + fp.seek(PARTIAL_OFFSET_SIZE[0]) + partial_data = fp.read(PARTIAL_OFFSET_SIZE[1]) return hasher(partial_data).digest() def _calc_digest_samples(self) -> bytes: @@ -281,7 +279,11 @@ def _read_info(self, field): elif field == "digest_partial": self.digest_partial = filesdb.get(self.path, "digest_partial") if self.digest_partial is None: - self.digest_partial = self._calc_digest_partial() + # If file is smaller than partial requirements just use the full digest + if self.size < PARTIAL_OFFSET_SIZE[0] + PARTIAL_OFFSET_SIZE[1]: + self.digest_partial = self.digest + else: + self.digest_partial = self._calc_digest_partial() filesdb.put(self.path, "digest_partial", self.digest_partial) elif field == "digest": self.digest = filesdb.get(self.path, "digest") @@ -292,7 +294,7 @@ def _read_info(self, field): size = self.size # Might as well hash such small files entirely. if size <= MIN_FILE_SIZE: - setattr(self, field, self.digest) + self.digest_samples = self.digest return self.digest_samples = filesdb.get(self.path, "digest_samples") if self.digest_samples is None: diff --git a/core/scanner.py b/core/scanner.py index 67124f20..cd2c0f02 100644 --- a/core/scanner.py +++ b/core/scanner.py @@ -87,8 +87,6 @@ def _getmatches(self, files, j): } ): j = j.start_subjob([2, 8]) - for f in j.iter_with_progress(files, tr("Read size of %d/%d files")): - f.size # pre-read, makes a smoother progress if read here (especially for bundles) if self.size_threshold: files = [f for f in files if f.size >= self.size_threshold] if self.large_size_threshold: @@ -173,7 +171,9 @@ def get_dupe_groups(self, files, ignore_list=None, j=job.nulljob): matches = [m for m in matches if get_file_ext(m.first.name) == get_file_ext(m.second.name)] if self.include_exists_check: matches = [m for m in matches if m.first.exists() and m.second.exists()] - matches = [m for m in matches if not (m.first.is_ref and m.second.is_ref)] + # Contents already handles ref checks, other scan types might not catch during scan + if self.scan_type != ScanType.CONTENTS: + matches = [m for m in matches if not (m.first.is_ref and m.second.is_ref)] if ignore_list: matches = [m for m in matches if not ignore_list.are_ignored(str(m.first.path), str(m.second.path))] logging.info("Grouping matches") diff --git a/hscommon/jobprogress/job.py b/hscommon/jobprogress/job.py index 5f52d2d9..eb9cca08 100644 --- a/hscommon/jobprogress/job.py +++ b/hscommon/jobprogress/job.py @@ -7,7 +7,7 @@ # http://www.gnu.org/licenses/gpl-3.0.html -from typing import Any, Callable, Generator, Iterator, List, Union +from typing import Any, Callable, Generator, List, Union class JobCancelled(Exception): @@ -148,7 +148,7 @@ def set_progress(self, progress: int, desc: str = "") -> None: self._do_update(desc) -class NullJob: +class NullJob(Job): def __init__(self, *args, **kwargs) -> None: # Null job does nothing pass @@ -161,9 +161,6 @@ def check_if_cancelled(self) -> None: # Null job does nothing pass - def iter_with_progress(self, sequence, *args, **kwargs) -> Iterator: - return iter(sequence) - def start_job(self, *args, **kwargs) -> None: # Null job does nothing pass From 18f32fda1947646a78b62a8c6fb5480195fffdc3 Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Thu, 8 Jun 2023 19:14:57 -0500 Subject: [PATCH 37/56] chore(translations): Synchronize translations --- locale/cs/LC_MESSAGES/ui.po | 31 +++++++++++++++++++++++++- locale/de/LC_MESSAGES/ui.po | 15 +++++++++++++ locale/el/LC_MESSAGES/ui.po | 31 +++++++++++++++++++++++++- locale/es/LC_MESSAGES/ui.po | 33 ++++++++++++++++++++++++++-- locale/fr/LC_MESSAGES/ui.po | 33 ++++++++++++++++++++++++++-- locale/hy/LC_MESSAGES/ui.po | 31 +++++++++++++++++++++++++- locale/it/LC_MESSAGES/ui.po | 31 +++++++++++++++++++++++++- locale/ja/LC_MESSAGES/ui.po | 31 +++++++++++++++++++++++++- locale/ko/LC_MESSAGES/ui.po | 15 +++++++++++++ locale/ms/LC_MESSAGES/ui.po | 40 +++++++++++++++++++++++++++++++--- locale/nl/LC_MESSAGES/ui.po | 31 +++++++++++++++++++++++++- locale/pl_PL/LC_MESSAGES/ui.po | 31 +++++++++++++++++++++++++- locale/pt_BR/LC_MESSAGES/ui.po | 33 ++++++++++++++++++++++++++-- locale/ru/LC_MESSAGES/ui.po | 23 +++++++++++++++---- locale/tr/LC_MESSAGES/ui.po | 15 +++++++++++++ locale/uk/LC_MESSAGES/ui.po | 31 +++++++++++++++++++++++++- locale/vi/LC_MESSAGES/ui.po | 31 +++++++++++++++++++++++++- locale/zh_CN/LC_MESSAGES/ui.po | 15 +++++++++++++ locale/zh_TW/LC_MESSAGES/ui.po | 15 +++++++++++++ 19 files changed, 494 insertions(+), 22 deletions(-) diff --git a/locale/cs/LC_MESSAGES/ui.po b/locale/cs/LC_MESSAGES/ui.po index 79cb68d0..e5f89862 100644 --- a/locale/cs/LC_MESSAGES/ui.po +++ b/locale/cs/LC_MESSAGES/ui.po @@ -5,7 +5,7 @@ msgid "" msgstr "" "Last-Translator: Fuan , 2022\n" -"Language-Team: Czech (https://www.transifex.com/voltaicideas/teams/116153/cs/)\n" +"Language-Team: Czech (https://app.transifex.com/voltaicideas/teams/116153/cs/)\n" "Language: cs\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" @@ -1131,3 +1131,32 @@ msgstr "Vymazání seznamu" #: qt\search_edit.py:78 msgid "Search..." msgstr "Hledat..." + +#: qt\preferences_dialog.py:219 +msgid "" +"These options are for advanced users or for very specific situations, most " +"users should not have to modify these." +msgstr "" + +#: qt\preferences_dialog.py:225 +msgid "Include existence check after scan completion" +msgstr "" + +#: qt\preferences_dialog.py:227 +msgid "Ignore difference in mtime when loading cached digests" +msgstr "" + +#: qt\progress_window.py:64 +msgid "Cancel?" +msgstr "" + +#: qt\progress_window.py:65 +msgid "Are you sure you want to cancel? All progress will be lost." +msgstr "" + +#: qt\exclude_list_dialog.py:161 +msgid "" +"These (case sensitive) python regular expressions will filter out files during scans.
    Directores will also have their default state set to Excluded in the Directories tab if their name happens to match one of the selected regular expressions.
    For each file collected, two tests are performed to determine whether or not to completely ignore it:
  • 1. Regular expressions with no path separator in them will be compared to the file name only.
  • \n" +"
  • 2. Regular expressions with at least one path separator in them will be compared to the full path to the file.

  • Example: if you want to filter out .PNG files from the \"My Pictures\" directory only:
    .*My\\sPictures\\\\.*\\.png

    You can test the regular expression with the \"test string\" button after pasting a fake path in the test field:
    C:\\\\User\\My Pictures\\test.png

    \n" +"Matching regular expressions will be highlighted.
    If there is at least one highlight, the path or filename tested will be ignored during scans.

    Directories and files starting with a period '.' are filtered out by default.

    " +msgstr "" diff --git a/locale/de/LC_MESSAGES/ui.po b/locale/de/LC_MESSAGES/ui.po index 2fce0a7e..3ea091c0 100644 --- a/locale/de/LC_MESSAGES/ui.po +++ b/locale/de/LC_MESSAGES/ui.po @@ -1162,3 +1162,18 @@ msgstr "" #: qt\preferences_dialog.py:227 msgid "Ignore difference in mtime when loading cached digests" msgstr "" + +#: qt\progress_window.py:64 +msgid "Cancel?" +msgstr "" + +#: qt\progress_window.py:65 +msgid "Are you sure you want to cancel? All progress will be lost." +msgstr "" + +#: qt\exclude_list_dialog.py:161 +msgid "" +"These (case sensitive) python regular expressions will filter out files during scans.
    Directores will also have their default state set to Excluded in the Directories tab if their name happens to match one of the selected regular expressions.
    For each file collected, two tests are performed to determine whether or not to completely ignore it:
  • 1. Regular expressions with no path separator in them will be compared to the file name only.
  • \n" +"
  • 2. Regular expressions with at least one path separator in them will be compared to the full path to the file.

  • Example: if you want to filter out .PNG files from the \"My Pictures\" directory only:
    .*My\\sPictures\\\\.*\\.png

    You can test the regular expression with the \"test string\" button after pasting a fake path in the test field:
    C:\\\\User\\My Pictures\\test.png

    \n" +"Matching regular expressions will be highlighted.
    If there is at least one highlight, the path or filename tested will be ignored during scans.

    Directories and files starting with a period '.' are filtered out by default.

    " +msgstr "" diff --git a/locale/el/LC_MESSAGES/ui.po b/locale/el/LC_MESSAGES/ui.po index cce0fb65..50aedf90 100644 --- a/locale/el/LC_MESSAGES/ui.po +++ b/locale/el/LC_MESSAGES/ui.po @@ -5,7 +5,7 @@ msgid "" msgstr "" "Last-Translator: Andrew Senetar , 2022\n" -"Language-Team: Greek (https://www.transifex.com/voltaicideas/teams/116153/el/)\n" +"Language-Team: Greek (https://app.transifex.com/voltaicideas/teams/116153/el/)\n" "Language: el\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" @@ -1148,3 +1148,32 @@ msgstr "Εκκαθάριση λίστας" #: qt\search_edit.py:78 msgid "Search..." msgstr "Αναζήτηση..." + +#: qt\preferences_dialog.py:219 +msgid "" +"These options are for advanced users or for very specific situations, most " +"users should not have to modify these." +msgstr "" + +#: qt\preferences_dialog.py:225 +msgid "Include existence check after scan completion" +msgstr "" + +#: qt\preferences_dialog.py:227 +msgid "Ignore difference in mtime when loading cached digests" +msgstr "" + +#: qt\progress_window.py:64 +msgid "Cancel?" +msgstr "" + +#: qt\progress_window.py:65 +msgid "Are you sure you want to cancel? All progress will be lost." +msgstr "" + +#: qt\exclude_list_dialog.py:161 +msgid "" +"These (case sensitive) python regular expressions will filter out files during scans.
    Directores will also have their default state set to Excluded in the Directories tab if their name happens to match one of the selected regular expressions.
    For each file collected, two tests are performed to determine whether or not to completely ignore it:
  • 1. Regular expressions with no path separator in them will be compared to the file name only.
  • \n" +"
  • 2. Regular expressions with at least one path separator in them will be compared to the full path to the file.

  • Example: if you want to filter out .PNG files from the \"My Pictures\" directory only:
    .*My\\sPictures\\\\.*\\.png

    You can test the regular expression with the \"test string\" button after pasting a fake path in the test field:
    C:\\\\User\\My Pictures\\test.png

    \n" +"Matching regular expressions will be highlighted.
    If there is at least one highlight, the path or filename tested will be ignored during scans.

    Directories and files starting with a period '.' are filtered out by default.

    " +msgstr "" diff --git a/locale/es/LC_MESSAGES/ui.po b/locale/es/LC_MESSAGES/ui.po index d4de1b12..c0bcee6f 100644 --- a/locale/es/LC_MESSAGES/ui.po +++ b/locale/es/LC_MESSAGES/ui.po @@ -6,11 +6,11 @@ msgid "" msgstr "" "Last-Translator: IlluminatiWave, 2022\n" -"Language-Team: Spanish (https://www.transifex.com/voltaicideas/teams/116153/es/)\n" +"Language-Team: Spanish (https://app.transifex.com/voltaicideas/teams/116153/es/)\n" "Language: es\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" #: qt/app.py:81 msgid "Quit" @@ -1148,3 +1148,32 @@ msgstr "Limpiar lista" #: qt\search_edit.py:78 msgid "Search..." msgstr "Búsqueda..." + +#: qt\preferences_dialog.py:219 +msgid "" +"These options are for advanced users or for very specific situations, most " +"users should not have to modify these." +msgstr "" + +#: qt\preferences_dialog.py:225 +msgid "Include existence check after scan completion" +msgstr "" + +#: qt\preferences_dialog.py:227 +msgid "Ignore difference in mtime when loading cached digests" +msgstr "" + +#: qt\progress_window.py:64 +msgid "Cancel?" +msgstr "" + +#: qt\progress_window.py:65 +msgid "Are you sure you want to cancel? All progress will be lost." +msgstr "" + +#: qt\exclude_list_dialog.py:161 +msgid "" +"These (case sensitive) python regular expressions will filter out files during scans.
    Directores will also have their default state set to Excluded in the Directories tab if their name happens to match one of the selected regular expressions.
    For each file collected, two tests are performed to determine whether or not to completely ignore it:
  • 1. Regular expressions with no path separator in them will be compared to the file name only.
  • \n" +"
  • 2. Regular expressions with at least one path separator in them will be compared to the full path to the file.

  • Example: if you want to filter out .PNG files from the \"My Pictures\" directory only:
    .*My\\sPictures\\\\.*\\.png

    You can test the regular expression with the \"test string\" button after pasting a fake path in the test field:
    C:\\\\User\\My Pictures\\test.png

    \n" +"Matching regular expressions will be highlighted.
    If there is at least one highlight, the path or filename tested will be ignored during scans.

    Directories and files starting with a period '.' are filtered out by default.

    " +msgstr "" diff --git a/locale/fr/LC_MESSAGES/ui.po b/locale/fr/LC_MESSAGES/ui.po index f9f2cdc7..89770d06 100644 --- a/locale/fr/LC_MESSAGES/ui.po +++ b/locale/fr/LC_MESSAGES/ui.po @@ -5,11 +5,11 @@ msgid "" msgstr "" "Last-Translator: Fuan , 2022\n" -"Language-Team: French (https://www.transifex.com/voltaicideas/teams/116153/fr/)\n" +"Language-Team: French (https://app.transifex.com/voltaicideas/teams/116153/fr/)\n" "Language: fr\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" -"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" #: qt/app.py:81 msgid "Quit" @@ -1136,3 +1136,32 @@ msgstr "Vider la liste" #: qt\search_edit.py:78 msgid "Search..." msgstr "Recherche..." + +#: qt\preferences_dialog.py:219 +msgid "" +"These options are for advanced users or for very specific situations, most " +"users should not have to modify these." +msgstr "" + +#: qt\preferences_dialog.py:225 +msgid "Include existence check after scan completion" +msgstr "" + +#: qt\preferences_dialog.py:227 +msgid "Ignore difference in mtime when loading cached digests" +msgstr "" + +#: qt\progress_window.py:64 +msgid "Cancel?" +msgstr "" + +#: qt\progress_window.py:65 +msgid "Are you sure you want to cancel? All progress will be lost." +msgstr "" + +#: qt\exclude_list_dialog.py:161 +msgid "" +"These (case sensitive) python regular expressions will filter out files during scans.
    Directores will also have their default state set to Excluded in the Directories tab if their name happens to match one of the selected regular expressions.
    For each file collected, two tests are performed to determine whether or not to completely ignore it:
  • 1. Regular expressions with no path separator in them will be compared to the file name only.
  • \n" +"
  • 2. Regular expressions with at least one path separator in them will be compared to the full path to the file.

  • Example: if you want to filter out .PNG files from the \"My Pictures\" directory only:
    .*My\\sPictures\\\\.*\\.png

    You can test the regular expression with the \"test string\" button after pasting a fake path in the test field:
    C:\\\\User\\My Pictures\\test.png

    \n" +"Matching regular expressions will be highlighted.
    If there is at least one highlight, the path or filename tested will be ignored during scans.

    Directories and files starting with a period '.' are filtered out by default.

    " +msgstr "" diff --git a/locale/hy/LC_MESSAGES/ui.po b/locale/hy/LC_MESSAGES/ui.po index 98d06b76..6f2942ad 100755 --- a/locale/hy/LC_MESSAGES/ui.po +++ b/locale/hy/LC_MESSAGES/ui.po @@ -5,7 +5,7 @@ msgid "" msgstr "" "Last-Translator: Fuan , 2022\n" -"Language-Team: Armenian (https://www.transifex.com/voltaicideas/teams/116153/hy/)\n" +"Language-Team: Armenian (https://app.transifex.com/voltaicideas/teams/116153/hy/)\n" "Language: hy\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" @@ -1116,3 +1116,32 @@ msgstr "Մաքրել ցանկը" #: qt\search_edit.py:78 msgid "Search..." msgstr "Որոնել..." + +#: qt\preferences_dialog.py:219 +msgid "" +"These options are for advanced users or for very specific situations, most " +"users should not have to modify these." +msgstr "" + +#: qt\preferences_dialog.py:225 +msgid "Include existence check after scan completion" +msgstr "" + +#: qt\preferences_dialog.py:227 +msgid "Ignore difference in mtime when loading cached digests" +msgstr "" + +#: qt\progress_window.py:64 +msgid "Cancel?" +msgstr "" + +#: qt\progress_window.py:65 +msgid "Are you sure you want to cancel? All progress will be lost." +msgstr "" + +#: qt\exclude_list_dialog.py:161 +msgid "" +"These (case sensitive) python regular expressions will filter out files during scans.
    Directores will also have their default state set to Excluded in the Directories tab if their name happens to match one of the selected regular expressions.
    For each file collected, two tests are performed to determine whether or not to completely ignore it:
  • 1. Regular expressions with no path separator in them will be compared to the file name only.
  • \n" +"
  • 2. Regular expressions with at least one path separator in them will be compared to the full path to the file.

  • Example: if you want to filter out .PNG files from the \"My Pictures\" directory only:
    .*My\\sPictures\\\\.*\\.png

    You can test the regular expression with the \"test string\" button after pasting a fake path in the test field:
    C:\\\\User\\My Pictures\\test.png

    \n" +"Matching regular expressions will be highlighted.
    If there is at least one highlight, the path or filename tested will be ignored during scans.

    Directories and files starting with a period '.' are filtered out by default.

    " +msgstr "" diff --git a/locale/it/LC_MESSAGES/ui.po b/locale/it/LC_MESSAGES/ui.po index 4c0e8e55..60320420 100644 --- a/locale/it/LC_MESSAGES/ui.po +++ b/locale/it/LC_MESSAGES/ui.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Last-Translator: Giovanni, 2022\n" -"Language-Team: Italian (https://www.transifex.com/voltaicideas/teams/116153/it/)\n" +"Language-Team: Italian (https://app.transifex.com/voltaicideas/teams/116153/it/)\n" "Language: it\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" @@ -1152,3 +1152,32 @@ msgstr "Cancellare l'elenco" #: qt\search_edit.py:78 msgid "Search..." msgstr "Ricerca..." + +#: qt\preferences_dialog.py:219 +msgid "" +"These options are for advanced users or for very specific situations, most " +"users should not have to modify these." +msgstr "" + +#: qt\preferences_dialog.py:225 +msgid "Include existence check after scan completion" +msgstr "" + +#: qt\preferences_dialog.py:227 +msgid "Ignore difference in mtime when loading cached digests" +msgstr "" + +#: qt\progress_window.py:64 +msgid "Cancel?" +msgstr "" + +#: qt\progress_window.py:65 +msgid "Are you sure you want to cancel? All progress will be lost." +msgstr "" + +#: qt\exclude_list_dialog.py:161 +msgid "" +"These (case sensitive) python regular expressions will filter out files during scans.
    Directores will also have their default state set to Excluded in the Directories tab if their name happens to match one of the selected regular expressions.
    For each file collected, two tests are performed to determine whether or not to completely ignore it:
  • 1. Regular expressions with no path separator in them will be compared to the file name only.
  • \n" +"
  • 2. Regular expressions with at least one path separator in them will be compared to the full path to the file.

  • Example: if you want to filter out .PNG files from the \"My Pictures\" directory only:
    .*My\\sPictures\\\\.*\\.png

    You can test the regular expression with the \"test string\" button after pasting a fake path in the test field:
    C:\\\\User\\My Pictures\\test.png

    \n" +"Matching regular expressions will be highlighted.
    If there is at least one highlight, the path or filename tested will be ignored during scans.

    Directories and files starting with a period '.' are filtered out by default.

    " +msgstr "" diff --git a/locale/ja/LC_MESSAGES/ui.po b/locale/ja/LC_MESSAGES/ui.po index 95537d2b..11f47ca8 100644 --- a/locale/ja/LC_MESSAGES/ui.po +++ b/locale/ja/LC_MESSAGES/ui.po @@ -5,7 +5,7 @@ msgid "" msgstr "" "Last-Translator: Fuan , 2022\n" -"Language-Team: Japanese (https://www.transifex.com/voltaicideas/teams/116153/ja/)\n" +"Language-Team: Japanese (https://app.transifex.com/voltaicideas/teams/116153/ja/)\n" "Language: ja\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" @@ -1120,3 +1120,32 @@ msgstr "リストをクリア" #: qt\search_edit.py:78 msgid "Search..." msgstr "探索..." + +#: qt\preferences_dialog.py:219 +msgid "" +"These options are for advanced users or for very specific situations, most " +"users should not have to modify these." +msgstr "" + +#: qt\preferences_dialog.py:225 +msgid "Include existence check after scan completion" +msgstr "" + +#: qt\preferences_dialog.py:227 +msgid "Ignore difference in mtime when loading cached digests" +msgstr "" + +#: qt\progress_window.py:64 +msgid "Cancel?" +msgstr "" + +#: qt\progress_window.py:65 +msgid "Are you sure you want to cancel? All progress will be lost." +msgstr "" + +#: qt\exclude_list_dialog.py:161 +msgid "" +"These (case sensitive) python regular expressions will filter out files during scans.
    Directores will also have their default state set to Excluded in the Directories tab if their name happens to match one of the selected regular expressions.
    For each file collected, two tests are performed to determine whether or not to completely ignore it:
  • 1. Regular expressions with no path separator in them will be compared to the file name only.
  • \n" +"
  • 2. Regular expressions with at least one path separator in them will be compared to the full path to the file.

  • Example: if you want to filter out .PNG files from the \"My Pictures\" directory only:
    .*My\\sPictures\\\\.*\\.png

    You can test the regular expression with the \"test string\" button after pasting a fake path in the test field:
    C:\\\\User\\My Pictures\\test.png

    \n" +"Matching regular expressions will be highlighted.
    If there is at least one highlight, the path or filename tested will be ignored during scans.

    Directories and files starting with a period '.' are filtered out by default.

    " +msgstr "" diff --git a/locale/ko/LC_MESSAGES/ui.po b/locale/ko/LC_MESSAGES/ui.po index be69c23c..b7990dc3 100644 --- a/locale/ko/LC_MESSAGES/ui.po +++ b/locale/ko/LC_MESSAGES/ui.po @@ -1135,3 +1135,18 @@ msgstr "" #: qt\preferences_dialog.py:227 msgid "Ignore difference in mtime when loading cached digests" msgstr "" + +#: qt\progress_window.py:64 +msgid "Cancel?" +msgstr "" + +#: qt\progress_window.py:65 +msgid "Are you sure you want to cancel? All progress will be lost." +msgstr "" + +#: qt\exclude_list_dialog.py:161 +msgid "" +"These (case sensitive) python regular expressions will filter out files during scans.
    Directores will also have their default state set to Excluded in the Directories tab if their name happens to match one of the selected regular expressions.
    For each file collected, two tests are performed to determine whether or not to completely ignore it:
  • 1. Regular expressions with no path separator in them will be compared to the file name only.
  • \n" +"
  • 2. Regular expressions with at least one path separator in them will be compared to the full path to the file.

  • Example: if you want to filter out .PNG files from the \"My Pictures\" directory only:
    .*My\\sPictures\\\\.*\\.png

    You can test the regular expression with the \"test string\" button after pasting a fake path in the test field:
    C:\\\\User\\My Pictures\\test.png

    \n" +"Matching regular expressions will be highlighted.
    If there is at least one highlight, the path or filename tested will be ignored during scans.

    Directories and files starting with a period '.' are filtered out by default.

    " +msgstr "" diff --git a/locale/ms/LC_MESSAGES/ui.po b/locale/ms/LC_MESSAGES/ui.po index 437a9ece..01302ac6 100644 --- a/locale/ms/LC_MESSAGES/ui.po +++ b/locale/ms/LC_MESSAGES/ui.po @@ -1,11 +1,11 @@ # Translators: # Andrew Senetar , 2022 -# Yaya - Nurul Azeera Hidayah @ Muhammad Nur Hidayat Yasuyoshi (MNH48) , 2022 +# Yaya - Nurul Azeera Hidayah @ Muhammad Nur Hidayat Yasuyoshi (MNH48) , 2023 # msgid "" msgstr "" -"Last-Translator: Yaya - Nurul Azeera Hidayah @ Muhammad Nur Hidayat Yasuyoshi (MNH48) , 2022\n" -"Language-Team: Malay (https://www.transifex.com/voltaicideas/teams/116153/ms/)\n" +"Last-Translator: Yaya - Nurul Azeera Hidayah @ Muhammad Nur Hidayat Yasuyoshi (MNH48) , 2023\n" +"Language-Team: Malay (https://app.transifex.com/voltaicideas/teams/116153/ms/)\n" "Language: ms\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" @@ -1143,3 +1143,37 @@ msgstr "Kosongkan Senarai" #: qt\search_edit.py:78 msgid "Search..." msgstr "Cari..." + +#: qt\preferences_dialog.py:219 +msgid "" +"These options are for advanced users or for very specific situations, most " +"users should not have to modify these." +msgstr "" +"Pilihan ini adalah untuk pengguna lanjutan atau untuk keadaan khas tertentu," +" kebanyakan pengguna tidak perlu mengubahsuai pilihan ini." + +#: qt\preferences_dialog.py:225 +msgid "Include existence check after scan completion" +msgstr "Sertakan pemeriksaan kewujudan selepas selesai imbasan" + +#: qt\preferences_dialog.py:227 +msgid "Ignore difference in mtime when loading cached digests" +msgstr "Abaikan perbezaan dalam masa ubahsuai ketika memuatkan cerna tercache" + +#: qt\progress_window.py:64 +msgid "Cancel?" +msgstr "Batal?" + +#: qt\progress_window.py:65 +msgid "Are you sure you want to cancel? All progress will be lost." +msgstr "Adakah anda pasti anda ingin batalkan? Semua kemajuan akan hilang." + +#: qt\exclude_list_dialog.py:161 +msgid "" +"These (case sensitive) python regular expressions will filter out files during scans.
    Directores will also have their default state set to Excluded in the Directories tab if their name happens to match one of the selected regular expressions.
    For each file collected, two tests are performed to determine whether or not to completely ignore it:
  • 1. Regular expressions with no path separator in them will be compared to the file name only.
  • \n" +"
  • 2. Regular expressions with at least one path separator in them will be compared to the full path to the file.

  • Example: if you want to filter out .PNG files from the \"My Pictures\" directory only:
    .*My\\sPictures\\\\.*\\.png

    You can test the regular expression with the \"test string\" button after pasting a fake path in the test field:
    C:\\\\User\\My Pictures\\test.png

    \n" +"Matching regular expressions will be highlighted.
    If there is at least one highlight, the path or filename tested will be ignored during scans.

    Directories and files starting with a period '.' are filtered out by default.

    " +msgstr "" +"Ungkapan nalar python (sensitif huruf) ini akan menapis keluar fail ketika imbasan.
    Direktori juga akan ada keadaan lalai sendiri ditetapkan kepada Dikecualikan dalam tab Direktori jika nama tersebut sepadan dengan salah satu daripada ungkapan nalar yang dipilih.
    Untuk setiap fail yang terhimpun, dua percubaan akan dilaksanakan untuk menentukan sama ada fail tersebut perlu diabaikan sepenuhnya:
  • 1. Ungkapan nalar tanpa pemisah laluan di dalamnya akan dibandingkan dengan nama fail sahaja.
  • \n" +"
  • 2. Ungkapan nalar dengan sekurang-kurangnya satu pemisah laluan di dalamnya akan dibandingkan dengan laluan penuh ke fail.

  • Contoh: jika anda ingin tapis keluar fail .PNG dari direktori \"My Pictures\" sahaja:
    .*My\\sPictures\\\\.*\\.png

    Anda boleh cuba ungkapan nalar dengan butang \"cuba rentetan\" selepas menampal laluan palsu dalam medan percubaan:
    C:\\\\User\\My Pictures\\test.png

    \n" +"Ungkapan nalar yang terpadan akan ditonjolkan.
    Sekiranya ada sekurang-kurangnya satu tonjolan, laluan atau nama fail yang dicuba akan diabaikan ketika imbasan.

    Direktori dan fail yang bermula dengan tanda titik '.' ditapis keluar secara lalainya.

    " diff --git a/locale/nl/LC_MESSAGES/ui.po b/locale/nl/LC_MESSAGES/ui.po index a5d97573..48464c50 100644 --- a/locale/nl/LC_MESSAGES/ui.po +++ b/locale/nl/LC_MESSAGES/ui.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Last-Translator: Fuan , 2022\n" -"Language-Team: Dutch (https://www.transifex.com/voltaicideas/teams/116153/nl/)\n" +"Language-Team: Dutch (https://app.transifex.com/voltaicideas/teams/116153/nl/)\n" "Language: nl\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" @@ -1139,3 +1139,32 @@ msgstr "Lijst leegmaken" #: qt\search_edit.py:78 msgid "Search..." msgstr "Zoeken..." + +#: qt\preferences_dialog.py:219 +msgid "" +"These options are for advanced users or for very specific situations, most " +"users should not have to modify these." +msgstr "" + +#: qt\preferences_dialog.py:225 +msgid "Include existence check after scan completion" +msgstr "" + +#: qt\preferences_dialog.py:227 +msgid "Ignore difference in mtime when loading cached digests" +msgstr "" + +#: qt\progress_window.py:64 +msgid "Cancel?" +msgstr "" + +#: qt\progress_window.py:65 +msgid "Are you sure you want to cancel? All progress will be lost." +msgstr "" + +#: qt\exclude_list_dialog.py:161 +msgid "" +"These (case sensitive) python regular expressions will filter out files during scans.
    Directores will also have their default state set to Excluded in the Directories tab if their name happens to match one of the selected regular expressions.
    For each file collected, two tests are performed to determine whether or not to completely ignore it:
  • 1. Regular expressions with no path separator in them will be compared to the file name only.
  • \n" +"
  • 2. Regular expressions with at least one path separator in them will be compared to the full path to the file.

  • Example: if you want to filter out .PNG files from the \"My Pictures\" directory only:
    .*My\\sPictures\\\\.*\\.png

    You can test the regular expression with the \"test string\" button after pasting a fake path in the test field:
    C:\\\\User\\My Pictures\\test.png

    \n" +"Matching regular expressions will be highlighted.
    If there is at least one highlight, the path or filename tested will be ignored during scans.

    Directories and files starting with a period '.' are filtered out by default.

    " +msgstr "" diff --git a/locale/pl_PL/LC_MESSAGES/ui.po b/locale/pl_PL/LC_MESSAGES/ui.po index 44f80d3b..c4de495b 100644 --- a/locale/pl_PL/LC_MESSAGES/ui.po +++ b/locale/pl_PL/LC_MESSAGES/ui.po @@ -5,7 +5,7 @@ msgid "" msgstr "" "Last-Translator: Fuan , 2022\n" -"Language-Team: Polish (Poland) (https://www.transifex.com/voltaicideas/teams/116153/pl_PL/)\n" +"Language-Team: Polish (Poland) (https://app.transifex.com/voltaicideas/teams/116153/pl_PL/)\n" "Language: pl_PL\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" @@ -1137,3 +1137,32 @@ msgstr "Wyczyść listę" #: qt\search_edit.py:78 msgid "Search..." msgstr "Szukaj..." + +#: qt\preferences_dialog.py:219 +msgid "" +"These options are for advanced users or for very specific situations, most " +"users should not have to modify these." +msgstr "" + +#: qt\preferences_dialog.py:225 +msgid "Include existence check after scan completion" +msgstr "" + +#: qt\preferences_dialog.py:227 +msgid "Ignore difference in mtime when loading cached digests" +msgstr "" + +#: qt\progress_window.py:64 +msgid "Cancel?" +msgstr "" + +#: qt\progress_window.py:65 +msgid "Are you sure you want to cancel? All progress will be lost." +msgstr "" + +#: qt\exclude_list_dialog.py:161 +msgid "" +"These (case sensitive) python regular expressions will filter out files during scans.
    Directores will also have their default state set to Excluded in the Directories tab if their name happens to match one of the selected regular expressions.
    For each file collected, two tests are performed to determine whether or not to completely ignore it:
  • 1. Regular expressions with no path separator in them will be compared to the file name only.
  • \n" +"
  • 2. Regular expressions with at least one path separator in them will be compared to the full path to the file.

  • Example: if you want to filter out .PNG files from the \"My Pictures\" directory only:
    .*My\\sPictures\\\\.*\\.png

    You can test the regular expression with the \"test string\" button after pasting a fake path in the test field:
    C:\\\\User\\My Pictures\\test.png

    \n" +"Matching regular expressions will be highlighted.
    If there is at least one highlight, the path or filename tested will be ignored during scans.

    Directories and files starting with a period '.' are filtered out by default.

    " +msgstr "" diff --git a/locale/pt_BR/LC_MESSAGES/ui.po b/locale/pt_BR/LC_MESSAGES/ui.po index 534ee1a3..92e80b73 100644 --- a/locale/pt_BR/LC_MESSAGES/ui.po +++ b/locale/pt_BR/LC_MESSAGES/ui.po @@ -5,11 +5,11 @@ msgid "" msgstr "" "Last-Translator: Andrew Senetar , 2022\n" -"Language-Team: Portuguese (Brazil) (https://www.transifex.com/voltaicideas/teams/116153/pt_BR/)\n" +"Language-Team: Portuguese (Brazil) (https://app.transifex.com/voltaicideas/teams/116153/pt_BR/)\n" "Language: pt_BR\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" -"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" #: qt/app.py:81 msgid "Quit" @@ -1137,3 +1137,32 @@ msgstr "Limpar Lista" #: qt\search_edit.py:78 msgid "Search..." msgstr "Buscar…" + +#: qt\preferences_dialog.py:219 +msgid "" +"These options are for advanced users or for very specific situations, most " +"users should not have to modify these." +msgstr "" + +#: qt\preferences_dialog.py:225 +msgid "Include existence check after scan completion" +msgstr "" + +#: qt\preferences_dialog.py:227 +msgid "Ignore difference in mtime when loading cached digests" +msgstr "" + +#: qt\progress_window.py:64 +msgid "Cancel?" +msgstr "" + +#: qt\progress_window.py:65 +msgid "Are you sure you want to cancel? All progress will be lost." +msgstr "" + +#: qt\exclude_list_dialog.py:161 +msgid "" +"These (case sensitive) python regular expressions will filter out files during scans.
    Directores will also have their default state set to Excluded in the Directories tab if their name happens to match one of the selected regular expressions.
    For each file collected, two tests are performed to determine whether or not to completely ignore it:
  • 1. Regular expressions with no path separator in them will be compared to the file name only.
  • \n" +"
  • 2. Regular expressions with at least one path separator in them will be compared to the full path to the file.

  • Example: if you want to filter out .PNG files from the \"My Pictures\" directory only:
    .*My\\sPictures\\\\.*\\.png

    You can test the regular expression with the \"test string\" button after pasting a fake path in the test field:
    C:\\\\User\\My Pictures\\test.png

    \n" +"Matching regular expressions will be highlighted.
    If there is at least one highlight, the path or filename tested will be ignored during scans.

    Directories and files starting with a period '.' are filtered out by default.

    " +msgstr "" diff --git a/locale/ru/LC_MESSAGES/ui.po b/locale/ru/LC_MESSAGES/ui.po index a909457b..f2025a19 100644 --- a/locale/ru/LC_MESSAGES/ui.po +++ b/locale/ru/LC_MESSAGES/ui.po @@ -1046,13 +1046,13 @@ msgid "" "\n" "Although the application should continue to run after this error, it may be in an unstable state, so it is recommended that you restart the application." msgstr "" -"Отчеты об ошибках следует оформлять в виде issues на GitHub. Вы можете скопировать трассировку ошибки выше и вставить ее в новый issue.\n" +"Отчеты об ошибках следует сообщать в виде проблем (issues) на GitHub. Вы можете скопировать приведенный выше отчет об ошибке и сообщить о нем на GitHub.\n" "\n" -"Обязательно заранее выполните поиск любых уже существующих проблем. Также не забудьте протестировать самую последнюю версию, доступную в репозитории, поскольку ошибка, с которой вы столкнулись, могла уже быть исправлена.\n" +"Пожалуйста, предварительно сделайте поиск уже существующих проблем в репозитории программы на GitHub. Также убедитесь, что вы используете самую последнюю версию, доступную из репозитория, поскольку ошибка, с которой вы столкнулись, возможно, уже исправлена.\n" "\n" -"Что обычно действительно помогает, так это то, что вы добавляете описание того, как вы получили ошибку. Благодаря!\n" +"Чтобы быстро и качественно решить проблему, очень поможет пошаговое описание действий пришедших к ошибке. Спасибо!\n" "\n" -"Хотя приложение должно продолжить работу после этой ошибки, оно может находиться в нестабильном состоянии, поэтому рекомендуется перезапустить приложение." +"Приложение должно продолжать работу после ошибки, хотя и не стабильно. Рекомендуется перезапустить программу." #: qt\error_report_dialog.py:80 msgid "Go to GitHub" @@ -1157,3 +1157,18 @@ msgstr "Включить проверку после завершения ска #: qt\preferences_dialog.py:227 msgid "Ignore difference in mtime when loading cached digests" msgstr "Игнорировать разницу во времени при загрузке кэшированных дайджестов" + +#: qt\progress_window.py:64 +msgid "Cancel?" +msgstr "Отменить?" + +#: qt\progress_window.py:65 +msgid "Are you sure you want to cancel? All progress will be lost." +msgstr "Вы уверены, что хотите отменить? Весь прогресс будет потерян." + +#: qt\exclude_list_dialog.py:161 +msgid "" +"These (case sensitive) python regular expressions will filter out files during scans.
    Directores will also have their default state set to Excluded in the Directories tab if their name happens to match one of the selected regular expressions.
    For each file collected, two tests are performed to determine whether or not to completely ignore it:
  • 1. Regular expressions with no path separator in them will be compared to the file name only.
  • \n" +"
  • 2. Regular expressions with at least one path separator in them will be compared to the full path to the file.

  • Example: if you want to filter out .PNG files from the \"My Pictures\" directory only:
    .*My\\sPictures\\\\.*\\.png

    You can test the regular expression with the \"test string\" button after pasting a fake path in the test field:
    C:\\\\User\\My Pictures\\test.png

    \n" +"Matching regular expressions will be highlighted.
    If there is at least one highlight, the path or filename tested will be ignored during scans.

    Directories and files starting with a period '.' are filtered out by default.

    " +msgstr "" diff --git a/locale/tr/LC_MESSAGES/ui.po b/locale/tr/LC_MESSAGES/ui.po index d99c871e..3a8590c4 100644 --- a/locale/tr/LC_MESSAGES/ui.po +++ b/locale/tr/LC_MESSAGES/ui.po @@ -1152,3 +1152,18 @@ msgstr "" #: qt\preferences_dialog.py:227 msgid "Ignore difference in mtime when loading cached digests" msgstr "" + +#: qt\progress_window.py:64 +msgid "Cancel?" +msgstr "" + +#: qt\progress_window.py:65 +msgid "Are you sure you want to cancel? All progress will be lost." +msgstr "" + +#: qt\exclude_list_dialog.py:161 +msgid "" +"These (case sensitive) python regular expressions will filter out files during scans.
    Directores will also have their default state set to Excluded in the Directories tab if their name happens to match one of the selected regular expressions.
    For each file collected, two tests are performed to determine whether or not to completely ignore it:
  • 1. Regular expressions with no path separator in them will be compared to the file name only.
  • \n" +"
  • 2. Regular expressions with at least one path separator in them will be compared to the full path to the file.

  • Example: if you want to filter out .PNG files from the \"My Pictures\" directory only:
    .*My\\sPictures\\\\.*\\.png

    You can test the regular expression with the \"test string\" button after pasting a fake path in the test field:
    C:\\\\User\\My Pictures\\test.png

    \n" +"Matching regular expressions will be highlighted.
    If there is at least one highlight, the path or filename tested will be ignored during scans.

    Directories and files starting with a period '.' are filtered out by default.

    " +msgstr "" diff --git a/locale/uk/LC_MESSAGES/ui.po b/locale/uk/LC_MESSAGES/ui.po index c8d66e79..ebca9b07 100755 --- a/locale/uk/LC_MESSAGES/ui.po +++ b/locale/uk/LC_MESSAGES/ui.po @@ -5,7 +5,7 @@ msgid "" msgstr "" "Last-Translator: Fuan , 2022\n" -"Language-Team: Ukrainian (https://www.transifex.com/voltaicideas/teams/116153/uk/)\n" +"Language-Team: Ukrainian (https://app.transifex.com/voltaicideas/teams/116153/uk/)\n" "Language: uk\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" @@ -1137,3 +1137,32 @@ msgstr "Очистити список" #: qt\search_edit.py:78 msgid "Search..." msgstr "Шукати..." + +#: qt\preferences_dialog.py:219 +msgid "" +"These options are for advanced users or for very specific situations, most " +"users should not have to modify these." +msgstr "" + +#: qt\preferences_dialog.py:225 +msgid "Include existence check after scan completion" +msgstr "" + +#: qt\preferences_dialog.py:227 +msgid "Ignore difference in mtime when loading cached digests" +msgstr "" + +#: qt\progress_window.py:64 +msgid "Cancel?" +msgstr "" + +#: qt\progress_window.py:65 +msgid "Are you sure you want to cancel? All progress will be lost." +msgstr "" + +#: qt\exclude_list_dialog.py:161 +msgid "" +"These (case sensitive) python regular expressions will filter out files during scans.
    Directores will also have their default state set to Excluded in the Directories tab if their name happens to match one of the selected regular expressions.
    For each file collected, two tests are performed to determine whether or not to completely ignore it:
  • 1. Regular expressions with no path separator in them will be compared to the file name only.
  • \n" +"
  • 2. Regular expressions with at least one path separator in them will be compared to the full path to the file.

  • Example: if you want to filter out .PNG files from the \"My Pictures\" directory only:
    .*My\\sPictures\\\\.*\\.png

    You can test the regular expression with the \"test string\" button after pasting a fake path in the test field:
    C:\\\\User\\My Pictures\\test.png

    \n" +"Matching regular expressions will be highlighted.
    If there is at least one highlight, the path or filename tested will be ignored during scans.

    Directories and files starting with a period '.' are filtered out by default.

    " +msgstr "" diff --git a/locale/vi/LC_MESSAGES/ui.po b/locale/vi/LC_MESSAGES/ui.po index 6a225ada..8b4f49e2 100644 --- a/locale/vi/LC_MESSAGES/ui.po +++ b/locale/vi/LC_MESSAGES/ui.po @@ -5,7 +5,7 @@ msgid "" msgstr "" "Last-Translator: Fuan , 2022\n" -"Language-Team: Vietnamese (https://www.transifex.com/voltaicideas/teams/116153/vi/)\n" +"Language-Team: Vietnamese (https://app.transifex.com/voltaicideas/teams/116153/vi/)\n" "Language: vi\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" @@ -1138,3 +1138,32 @@ msgstr "Xóa danh sách" #: qt\search_edit.py:78 msgid "Search..." msgstr "Tìm kiếm..." + +#: qt\preferences_dialog.py:219 +msgid "" +"These options are for advanced users or for very specific situations, most " +"users should not have to modify these." +msgstr "" + +#: qt\preferences_dialog.py:225 +msgid "Include existence check after scan completion" +msgstr "" + +#: qt\preferences_dialog.py:227 +msgid "Ignore difference in mtime when loading cached digests" +msgstr "" + +#: qt\progress_window.py:64 +msgid "Cancel?" +msgstr "" + +#: qt\progress_window.py:65 +msgid "Are you sure you want to cancel? All progress will be lost." +msgstr "" + +#: qt\exclude_list_dialog.py:161 +msgid "" +"These (case sensitive) python regular expressions will filter out files during scans.
    Directores will also have their default state set to Excluded in the Directories tab if their name happens to match one of the selected regular expressions.
    For each file collected, two tests are performed to determine whether or not to completely ignore it:
  • 1. Regular expressions with no path separator in them will be compared to the file name only.
  • \n" +"
  • 2. Regular expressions with at least one path separator in them will be compared to the full path to the file.

  • Example: if you want to filter out .PNG files from the \"My Pictures\" directory only:
    .*My\\sPictures\\\\.*\\.png

    You can test the regular expression with the \"test string\" button after pasting a fake path in the test field:
    C:\\\\User\\My Pictures\\test.png

    \n" +"Matching regular expressions will be highlighted.
    If there is at least one highlight, the path or filename tested will be ignored during scans.

    Directories and files starting with a period '.' are filtered out by default.

    " +msgstr "" diff --git a/locale/zh_CN/LC_MESSAGES/ui.po b/locale/zh_CN/LC_MESSAGES/ui.po index d43fc695..8457cdc7 100644 --- a/locale/zh_CN/LC_MESSAGES/ui.po +++ b/locale/zh_CN/LC_MESSAGES/ui.po @@ -1132,3 +1132,18 @@ msgstr "在扫描结束后检查文件是否存在" #: qt\preferences_dialog.py:227 msgid "Ignore difference in mtime when loading cached digests" msgstr "载入缓存摘要时忽略mtime的不同" + +#: qt\progress_window.py:64 +msgid "Cancel?" +msgstr "" + +#: qt\progress_window.py:65 +msgid "Are you sure you want to cancel? All progress will be lost." +msgstr "" + +#: qt\exclude_list_dialog.py:161 +msgid "" +"These (case sensitive) python regular expressions will filter out files during scans.
    Directores will also have their default state set to Excluded in the Directories tab if their name happens to match one of the selected regular expressions.
    For each file collected, two tests are performed to determine whether or not to completely ignore it:
  • 1. Regular expressions with no path separator in them will be compared to the file name only.
  • \n" +"
  • 2. Regular expressions with at least one path separator in them will be compared to the full path to the file.

  • Example: if you want to filter out .PNG files from the \"My Pictures\" directory only:
    .*My\\sPictures\\\\.*\\.png

    You can test the regular expression with the \"test string\" button after pasting a fake path in the test field:
    C:\\\\User\\My Pictures\\test.png

    \n" +"Matching regular expressions will be highlighted.
    If there is at least one highlight, the path or filename tested will be ignored during scans.

    Directories and files starting with a period '.' are filtered out by default.

    " +msgstr "" diff --git a/locale/zh_TW/LC_MESSAGES/ui.po b/locale/zh_TW/LC_MESSAGES/ui.po index ad604662..044112fd 100644 --- a/locale/zh_TW/LC_MESSAGES/ui.po +++ b/locale/zh_TW/LC_MESSAGES/ui.po @@ -1132,3 +1132,18 @@ msgstr "在扫描结束后检查文件是否存在" #: qt\preferences_dialog.py:227 msgid "Ignore difference in mtime when loading cached digests" msgstr "载入缓存摘要时忽略mtime的不同" + +#: qt\progress_window.py:64 +msgid "Cancel?" +msgstr "" + +#: qt\progress_window.py:65 +msgid "Are you sure you want to cancel? All progress will be lost." +msgstr "" + +#: qt\exclude_list_dialog.py:161 +msgid "" +"These (case sensitive) python regular expressions will filter out files during scans.
    Directores will also have their default state set to Excluded in the Directories tab if their name happens to match one of the selected regular expressions.
    For each file collected, two tests are performed to determine whether or not to completely ignore it:
  • 1. Regular expressions with no path separator in them will be compared to the file name only.
  • \n" +"
  • 2. Regular expressions with at least one path separator in them will be compared to the full path to the file.

  • Example: if you want to filter out .PNG files from the \"My Pictures\" directory only:
    .*My\\sPictures\\\\.*\\.png

    You can test the regular expression with the \"test string\" button after pasting a fake path in the test field:
    C:\\\\User\\My Pictures\\test.png

    \n" +"Matching regular expressions will be highlighted.
    If there is at least one highlight, the path or filename tested will be ignored during scans.

    Directories and files starting with a period '.' are filtered out by default.

    " +msgstr "" From 8160fe4fcc0dd4a8d30ebfc0581526de66aefd56 Mon Sep 17 00:00:00 2001 From: Cebtenzzre Date: Sun, 16 Jul 2023 18:31:45 -0400 Subject: [PATCH 38/56] Update requirements.txt sphinx 7.0.0 was released on April 29th. --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 99c52cad..9618b5b1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,5 +5,5 @@ PyQt5 >=5.15.0,<6.0; sys_platform != 'linux' pywin32>=304; sys_platform == 'win32' semantic-version>=2.0.0,<3.0.0 Send2Trash>=1.8.2,<2.0.0 -sphinx>=5.3.0,<7.0.0 +sphinx>=5.3.0,<8.0.0 xxhash>=3.0.0,<4.0.0 From f56bef67e1bd67bb23d67f14170c24c7fdc8368f Mon Sep 17 00:00:00 2001 From: Cebtenzzre Date: Sun, 16 Jul 2023 18:44:53 -0400 Subject: [PATCH 39/56] Update python versions in tox.ini Python 3.6 support was already dropped in commit b9dfeac2 ("Drop Python 3.6 Support"). Don't attempt to run tests with it if it is installed. Python 3.11 is currently supported, so add it to the list. --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index a9f8b827..a6e641ad 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py36,py37,py38,py39,py310 +envlist = py37,py38,py39,py310,py311 skipsdist = True skip_missing_interpreters = True From 332b814c00bad026dda5a2c8c0bce624ccda8c5b Mon Sep 17 00:00:00 2001 From: Cebtenzzre Date: Sun, 16 Jul 2023 19:04:11 -0400 Subject: [PATCH 40/56] Do not package send2trash on Arch Linux send2trash is provided by the python-send2trash package in the 'extra' repository. --- package.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.py b/package.py index 66d2b076..90140c58 100644 --- a/package.py +++ b/package.py @@ -122,7 +122,7 @@ def package_arch(): # need to include them). print("Packaging for Arch") srcpath = op.join("build", "dupeguru-arch") - packages = ["hscommon", "core", "qt", "send2trash"] + packages = ["hscommon", "core", "qt"] copy_files_to_package(srcpath, packages, with_so=True) shutil.copy(op.join("images", "dgse_logo_128.png"), srcpath) debopts = json.load(open(op.join("pkg", "arch", "dupeguru.json"))) From 007404f46a0c317685197b4c5616f27994f3d3ff Mon Sep 17 00:00:00 2001 From: Luca Falavigna Date: Mon, 12 Feb 2024 18:02:13 +0100 Subject: [PATCH 41/56] Use isolation_level=None mode for GNU Hurd --- core/fs.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/fs.py b/core/fs.py index e927fd0b..13e6ab52 100644 --- a/core/fs.py +++ b/core/fs.py @@ -16,6 +16,7 @@ from math import floor import logging import sqlite3 +from sys import platform from threading import Lock from typing import Any, AnyStr, Union, Callable @@ -118,7 +119,10 @@ def __init__(self): self.lock = None def connect(self, path: Union[AnyStr, os.PathLike]) -> None: - self.conn = sqlite3.connect(path, check_same_thread=False) + if platform.startswith("gnu0"): + self.conn = sqlite3.connect(path, check_same_thread=False, isolation_level=None) + else: + self.conn = sqlite3.connect(path, check_same_thread=False) self.lock = Lock() self._check_upgrade() From 85a455752586ddec223bcce4f282656ea1f60e4f Mon Sep 17 00:00:00 2001 From: Bruno Cabral Date: Mon, 19 Feb 2024 07:19:33 -0800 Subject: [PATCH 42/56] match all orientations (#1127) * match all orientations * use rotation as option --------- Co-authored-by: Andrew Senetar Co-authored-by: Luke --- core/engine.py | 6 ++--- core/fs.py | 4 +++- core/pe/cache_sqlite.py | 28 +++++++++++------------ core/pe/matchblock.py | 42 ++++++++++++++++++++++------------ core/pe/photo.py | 7 ++++-- core/pe/scanner.py | 2 ++ help/en/preferences.rst | 4 ++++ locale/en/LC_MESSAGES/ui.po | 4 ++++ locale/es/LC_MESSAGES/ui.po | 4 ++++ locale/pt_BR/LC_MESSAGES/ui.po | 4 ++++ qt/app.py | 1 + qt/pe/preferences_dialog.py | 4 ++++ qt/preferences.py | 3 +++ 13 files changed, 78 insertions(+), 35 deletions(-) diff --git a/core/engine.py b/core/engine.py index ca05a61f..934152fb 100644 --- a/core/engine.py +++ b/core/engine.py @@ -304,12 +304,12 @@ def getmatches_by_contents(files, bigsize=0, j=job.nulljob): result.append(Match(first, second, 100)) continue # if digests are the same (and not None) then files match - if first.digest_partial == second.digest_partial and first.digest_partial is not None: + if first.digest_partial is not None and first.digest_partial == second.digest_partial: if bigsize > 0 and first.size > bigsize: - if first.digest_samples == second.digest_samples and first.digest_samples is not None: + if first.digest_samples is not None and first.digest_samples == second.digest_samples: result.append(Match(first, second, 100)) else: - if first.digest == second.digest and first.digest is not None: + if first.digest is not None and first.digest == second.digest: result.append(Match(first, second, 100)) group_count += 1 j.add_progress(desc=PROGRESS_MESSAGE % (len(result), group_count)) diff --git a/core/fs.py b/core/fs.py index 13e6ab52..a62d29ce 100644 --- a/core/fs.py +++ b/core/fs.py @@ -206,7 +206,7 @@ class File: # Slots for File make us save quite a bit of memory. In a memory test I've made with a lot of # files, I saved 35% memory usage with "unread" files (no _read_info() call) and gains become # even greater when we take into account read attributes (70%!). Yeah, it's worth it. - __slots__ = ("path", "is_ref", "words") + tuple(INITIAL_INFO.keys()) + __slots__ = ("path", "unicode_path", "is_ref", "words") + tuple(INITIAL_INFO.keys()) def __init__(self, path): for attrname in self.INITIAL_INFO: @@ -217,6 +217,8 @@ def __init__(self, path): self.mtime = nonone(path.stat().st_mtime, 0) else: self.path = path + if self.path: + self.unicode_path = str(self.path) def __repr__(self): return f"<{self.__class__.__name__} {str(self.path)}>" diff --git a/core/pe/cache_sqlite.py b/core/pe/cache_sqlite.py index 4cb3c588..5aaf2940 100644 --- a/core/pe/cache_sqlite.py +++ b/core/pe/cache_sqlite.py @@ -15,10 +15,10 @@ class SqliteCache: """A class to cache picture blocks in a sqlite backend.""" - schema_version = 1 - schema_version_description = "Changed from string to bytes for blocks." + schema_version = 2 + schema_version_description = "Added blocks for all 8 orientations." - create_table_query = "CREATE TABLE IF NOT EXISTS pictures(path TEXT, mtime_ns INTEGER, blocks BLOB)" + create_table_query = "CREATE TABLE IF NOT EXISTS pictures(path TEXT, mtime_ns INTEGER, blocks BLOB, blocks2 BLOB, blocks3 BLOB, blocks4 BLOB, blocks5 BLOB, blocks6 BLOB, blocks7 BLOB, blocks8 BLOB)" create_index_query = "CREATE INDEX IF NOT EXISTS idx_path on pictures (path)" drop_table_query = "DROP TABLE IF EXISTS pictures" drop_index_query = "DROP INDEX IF EXISTS idx_path" @@ -43,12 +43,12 @@ def __delitem__(self, key): # Optimized def __getitem__(self, key): if isinstance(key, int): - sql = "select blocks from pictures where rowid = ?" + sql = "select blocks, blocks2, blocks3, blocks4, blocks5, blocks6, blocks7, blocks8 from pictures where rowid = ?" else: - sql = "select blocks from pictures where path = ?" - result = self.con.execute(sql, [key]).fetchone() - if result: - result = bytes_to_colors(result[0]) + sql = "select blocks, blocks2, blocks3, blocks4, blocks5, blocks6, blocks7, blocks8 from pictures where path = ?" + blocks = self.con.execute(sql, [key]).fetchone() + if blocks: + result = [bytes_to_colors(block) for block in blocks] return result else: raise KeyError(key) @@ -64,17 +64,17 @@ def __len__(self): return result[0][0] def __setitem__(self, path_str, blocks): - blocks = colors_to_bytes(blocks) + blocks = [colors_to_bytes(block) for block in blocks] if op.exists(path_str): mtime = int(os.stat(path_str).st_mtime) else: mtime = 0 if path_str in self: - sql = "update pictures set blocks = ?, mtime_ns = ? where path = ?" + sql = "update pictures set blocks = ?, blocks2 = ?, blocks3 = ?, blocks4 = ?, blocks5 = ?, blocks6 = ?, blocks7 = ?, blocks8 = ?, mtime_ns = ? where path = ?" else: - sql = "insert into pictures(blocks,mtime_ns,path) values(?,?,?)" + sql = "insert into pictures(blocks,blocks2,blocks3,blocks4,blocks5,blocks6,blocks7,blocks8,mtime_ns,path) values(?,?,?,?,?,?,?,?,?,?)" try: - self.con.execute(sql, [blocks, mtime, path_str]) + self.con.execute(sql, blocks + [mtime, path_str]) except sqlite.OperationalError: logging.warning("Picture cache could not set value for key %r", path_str) except sqlite.DatabaseError as e: @@ -136,9 +136,9 @@ def get_id(self, path): raise ValueError(path) def get_multiple(self, rowids): - sql = "select rowid, blocks from pictures where rowid in (%s)" % ",".join(map(str, rowids)) + sql = "select rowid, blocks, blocks2, blocks3, blocks4, blocks5, blocks6, blocks7, blocks8 from pictures where rowid in (%s)" % ",".join(map(str, rowids)) cur = self.con.execute(sql) - return ((rowid, bytes_to_colors(blocks)) for rowid, blocks in cur) + return ((rowid, [bytes_to_colors(blocks), bytes_to_colors(blocks2), bytes_to_colors(blocks3), bytes_to_colors(blocks4), bytes_to_colors(blocks5), bytes_to_colors(blocks6), bytes_to_colors(blocks7), bytes_to_colors(blocks8)]) for rowid, blocks, blocks2, blocks3, blocks4, blocks5, blocks6, blocks7, blocks8 in cur) def purge_outdated(self): """Go through the cache and purge outdated records. diff --git a/core/pe/matchblock.py b/core/pe/matchblock.py index 9af739bd..f312b2ec 100644 --- a/core/pe/matchblock.py +++ b/core/pe/matchblock.py @@ -72,13 +72,12 @@ def prepare_pictures(pictures, cache_path, with_dimensions, j=job.nulljob): # entry in iPhoto library. logging.warning("We have a picture with a null path here") continue - picture.unicode_path = str(picture.path) logging.debug("Analyzing picture at %s", picture.unicode_path) if with_dimensions: picture.dimensions # pre-read dimensions try: if picture.unicode_path not in cache: - blocks = picture.get_blocks(BLOCK_COUNT_PER_SIDE) + blocks = [picture.get_blocks(BLOCK_COUNT_PER_SIDE, orientation) for orientation in range(1, 9)] cache[picture.unicode_path] = blocks prepared.append(picture) except (OSError, ValueError) as e: @@ -119,13 +118,13 @@ def get_match(first, second, percentage): return Match(first, second, percentage) -def async_compare(ref_ids, other_ids, dbname, threshold, picinfo): +def async_compare(ref_ids, other_ids, dbname, threshold, picinfo, match_rotated=False): # The list of ids in ref_ids have to be compared to the list of ids in other_ids. other_ids # can be None. In this case, ref_ids has to be compared with itself # picinfo is a dictionary {pic_id: (dimensions, is_ref)} cache = get_cache(dbname, readonly=True) limit = 100 - threshold - ref_pairs = list(cache.get_multiple(ref_ids)) + ref_pairs = list(cache.get_multiple(ref_ids)) # (rowid, [b, b2, ..., b8]) if other_ids is not None: other_pairs = list(cache.get_multiple(other_ids)) comparisons_to_do = [(r, o) for r in ref_pairs for o in other_pairs] @@ -138,22 +137,35 @@ def async_compare(ref_ids, other_ids, dbname, threshold, picinfo): if ref_is_ref and other_is_ref: continue if ref_dimensions != other_dimensions: - continue - try: - diff = avgdiff(ref_blocks, other_blocks, limit, MIN_ITERATIONS) - percentage = 100 - diff - except (DifferentBlockCountError, NoBlocksError): - percentage = 0 - if percentage >= threshold: - results.append((ref_id, other_id, percentage)) + if match_rotated: + rotated_ref_dimensions = (ref_dimensions[1], ref_dimensions[0]) + if rotated_ref_dimensions != other_dimensions: + continue + else: + continue + + orientation_range = 1 + if match_rotated: + orientation_range = 8 + + for orientation_ref in range(orientation_range): + try: + diff = avgdiff(ref_blocks[orientation_ref], other_blocks[0], limit, MIN_ITERATIONS) + percentage = 100 - diff + except (DifferentBlockCountError, NoBlocksError): + percentage = 0 + if percentage >= threshold: + results.append((ref_id, other_id, percentage)) + break + cache.close() return results -def getmatches(pictures, cache_path, threshold, match_scaled=False, j=job.nulljob): +def getmatches(pictures, cache_path, threshold, match_scaled=False, match_rotated=False, j=job.nulljob): def get_picinfo(p): if match_scaled: - return (None, p.is_ref) + return ((None, None), p.is_ref) else: return (p.dimensions, p.is_ref) @@ -205,7 +217,7 @@ def collect_results(collect_all=False): picinfo.update({p.cache_id: get_picinfo(p) for p in other_chunk}) else: other_ids = None - args = (ref_ids, other_ids, cache_path, threshold, picinfo) + args = (ref_ids, other_ids, cache_path, threshold, picinfo, match_rotated) async_results.append(pool.apply_async(async_compare, args)) collect_results() collect_results(collect_all=True) diff --git a/core/pe/photo.py b/core/pe/photo.py index 128e3c1f..5bc8356f 100644 --- a/core/pe/photo.py +++ b/core/pe/photo.py @@ -100,5 +100,8 @@ def _read_info(self, field): elif field == "exif_timestamp": self.exif_timestamp = self._get_exif_timestamp() - def get_blocks(self, block_count_per_side): - return self._plat_get_blocks(block_count_per_side, self._get_orientation()) + def get_blocks(self, block_count_per_side, orientation: int = None): + if orientation is None: + return self._plat_get_blocks(block_count_per_side, self._get_orientation()) + else: + return self._plat_get_blocks(block_count_per_side, orientation) diff --git a/core/pe/scanner.py b/core/pe/scanner.py index e58c4a90..8a1d53d2 100644 --- a/core/pe/scanner.py +++ b/core/pe/scanner.py @@ -14,6 +14,7 @@ class ScannerPE(Scanner): cache_path = None match_scaled = False + match_rotated = False @staticmethod def get_scan_options(): @@ -29,6 +30,7 @@ def _getmatches(self, files, j): cache_path=self.cache_path, threshold=self.min_match_percentage, match_scaled=self.match_scaled, + match_rotated=self.match_rotated, j=j, ) elif self.scan_type == ScanType.EXIFTIMESTAMP: diff --git a/help/en/preferences.rst b/help/en/preferences.rst index eadc4f3c..0a1423d5 100644 --- a/help/en/preferences.rst +++ b/help/en/preferences.rst @@ -14,6 +14,10 @@ Preferences If you check this box, pictures of different dimensions will be allowed in the same duplicate group. +**Match pictures of different rotations:** + If you check this box, pictures of different rotations will be allowed in the same + duplicate group. + .. _filter-hardness: **Filter Hardness:** diff --git a/locale/en/LC_MESSAGES/ui.po b/locale/en/LC_MESSAGES/ui.po index 7ef3b1d9..f77da262 100644 --- a/locale/en/LC_MESSAGES/ui.po +++ b/locale/en/LC_MESSAGES/ui.po @@ -307,6 +307,10 @@ msgstr "Debug mode (restart required)" msgid "Match pictures of different dimensions" msgstr "Match pictures of different dimensions" +#: qt/pe/preferences_dialog.py:19 cocoa/en.lproj/Localizable.strings:0 +msgid "Match pictures of different rotations" +msgstr "Match pictures of different rotations" + #: qt/preferences_dialog.py:43 msgid "Filter Hardness:" msgstr "Filter Hardness:" diff --git a/locale/es/LC_MESSAGES/ui.po b/locale/es/LC_MESSAGES/ui.po index c0bcee6f..7089a348 100644 --- a/locale/es/LC_MESSAGES/ui.po +++ b/locale/es/LC_MESSAGES/ui.po @@ -316,6 +316,10 @@ msgstr "Mode de depuración (se requiere reinicio)" msgid "Match pictures of different dimensions" msgstr "Coincidencia de imágenes de distintas dimensiones" +#: qt/pe/preferences_dialog.py:19 cocoa/en.lproj/Localizable.strings:0 +msgid "Match pictures of different rotations" +msgstr "Coincidencia de imágenes de distintas rotaciones" + #: qt/preferences_dialog.py:43 msgid "Filter Hardness:" msgstr "Dureza del Filtro:" diff --git a/locale/pt_BR/LC_MESSAGES/ui.po b/locale/pt_BR/LC_MESSAGES/ui.po index 92e80b73..56eec41e 100644 --- a/locale/pt_BR/LC_MESSAGES/ui.po +++ b/locale/pt_BR/LC_MESSAGES/ui.po @@ -314,6 +314,10 @@ msgstr "Modo de Depuração (requer reinício)" msgid "Match pictures of different dimensions" msgstr "Coincidir fotos de dimensões diferentes" +#: qt/pe/preferences_dialog.py:19 cocoa/en.lproj/Localizable.strings:0 +msgid "Match pictures of different rotations" +msgstr "Coincidir fotos de rotações diferentes" + #: qt/preferences_dialog.py:43 msgid "Filter Hardness:" msgstr "Pressão do Filtro:" diff --git a/qt/app.py b/qt/app.py index 5e6271c0..30312cd8 100644 --- a/qt/app.py +++ b/qt/app.py @@ -192,6 +192,7 @@ def _update_options(self): scanned_tags.add("year") self.model.options["scanned_tags"] = scanned_tags self.model.options["match_scaled"] = self.prefs.match_scaled + self.model.options["match_rotated"] = self.prefs.match_rotated self.model.options["include_exists_check"] = self.prefs.include_exists_check self.model.options["rehash_ignore_mtime"] = self.prefs.rehash_ignore_mtime diff --git a/qt/pe/preferences_dialog.py b/qt/pe/preferences_dialog.py index 375cc779..735d4eda 100644 --- a/qt/pe/preferences_dialog.py +++ b/qt/pe/preferences_dialog.py @@ -21,6 +21,8 @@ def _setupPreferenceWidgets(self): self.widgetsVLayout.addLayout(self.filterHardnessHLayout) self._setupAddCheckbox("matchScaledBox", tr("Match pictures of different dimensions")) self.widgetsVLayout.addWidget(self.matchScaledBox) + self._setupAddCheckbox("matchRotatedBox", tr("Match pictures of different rotations")) + self.widgetsVLayout.addWidget(self.matchRotatedBox) self._setupAddCheckbox("mixFileKindBox", tr("Can mix file kind")) self.widgetsVLayout.addWidget(self.mixFileKindBox) self._setupAddCheckbox("useRegexpBox", tr("Use regular expressions when filtering")) @@ -57,6 +59,7 @@ def _setupDisplayPage(self): def _load(self, prefs, setchecked, section): setchecked(self.matchScaledBox, prefs.match_scaled) + setchecked(self.matchRotatedBox, prefs.match_rotated) # Update UI state based on selected scan type scan_type = prefs.get_scan_type(AppMode.PICTURE) @@ -67,5 +70,6 @@ def _load(self, prefs, setchecked, section): def _save(self, prefs, ischecked): prefs.match_scaled = ischecked(self.matchScaledBox) + prefs.match_rotated = ischecked(self.matchRotatedBox) prefs.details_dialog_override_theme_icons = ischecked(self.details_dialog_override_theme_icons) prefs.details_dialog_viewers_show_scrollbars = ischecked(self.details_dialog_viewers_show_scrollbars) diff --git a/qt/preferences.py b/qt/preferences.py index 17ae3bf9..1f88cc31 100644 --- a/qt/preferences.py +++ b/qt/preferences.py @@ -225,6 +225,7 @@ def _load_values(self, settings): self.scan_tag_genre = get("ScanTagGenre", self.scan_tag_genre) self.scan_tag_year = get("ScanTagYear", self.scan_tag_year) self.match_scaled = get("MatchScaled", self.match_scaled) + self.match_rotated = get("MatchRotated", self.match_rotated) def reset(self): self.filter_hardness = 95 @@ -277,6 +278,7 @@ def reset(self): self.scan_tag_genre = False self.scan_tag_year = False self.match_scaled = False + self.match_rotated = False def _save_values(self, settings): set_ = self.set_value @@ -330,6 +332,7 @@ def _save_values(self, settings): set_("ScanTagGenre", self.scan_tag_genre) set_("ScanTagYear", self.scan_tag_year) set_("MatchScaled", self.match_scaled) + set_("MatchRotated", self.match_rotated) # scan_type is special because we save it immediately when we set it. def get_scan_type(self, app_mode): From 9f22835f73fe728fd726553d5ddb16104e1854c9 Mon Sep 17 00:00:00 2001 From: Luca Falavigna Date: Mon, 19 Feb 2024 16:38:24 +0100 Subject: [PATCH 43/56] Use errno.EISDIR and errno.EACCESS instead of hardcoding values (#1196) --- core/results.py | 5 +++-- hscommon/conflict.py | 7 +++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/results.py b/core/results.py index 3606e38a..1ba728a9 100644 --- a/core/results.py +++ b/core/results.py @@ -10,6 +10,7 @@ import re import os import os.path as op +from errno import EISDIR, EACCES from xml.etree import ElementTree as ET from hscommon.jobprogress.job import nulljob @@ -376,8 +377,8 @@ def do_write(outfile): do_write(outfile) except OSError as e: # If our OSError is because dest is already a directory, we want to handle that. 21 is - # the code we get on OS X and Linux, 13 is what we get on Windows. - if e.errno in {21, 13}: + # the code we get on OS X and Linux (EISDIR), 13 is what we get on Windows (EACCES). + if e.errno in (EISDIR, EACCES): p = str(outfile) dirname, basename = op.split(p) otherfiles = os.listdir(dirname) diff --git a/hscommon/conflict.py b/hscommon/conflict.py index af133ac7..0675efd0 100644 --- a/hscommon/conflict.py +++ b/hscommon/conflict.py @@ -14,6 +14,7 @@ import os import shutil +from errno import EISDIR, EACCES from pathlib import Path from typing import Callable, List @@ -75,10 +76,8 @@ def smart_copy(source_path: Path, dest_path: Path) -> None: try: _smart_move_or_copy(shutil.copy, source_path, dest_path) except OSError as e: - if e.errno in { - 21, - 13, - }: # it's a directory, code is 21 on OS X / Linux and 13 on Windows + # It's a directory, code is 21 on OS X / Linux (EISDIR) and 13 on Windows (EACCES) + if e.errno in (EISDIR, EACCES): _smart_move_or_copy(shutil.copytree, source_path, dest_path) else: raise From 13dd00c79865d8a0c23ddade9acd5793e167cdd9 Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Mon, 19 Feb 2024 09:39:12 -0800 Subject: [PATCH 44/56] fix: Replace use of `imp` with `importlib` Original PR and information found at #1187 --- hscommon/pygettext.py | 50 +++++++------------------------------------ 1 file changed, 8 insertions(+), 42 deletions(-) diff --git a/hscommon/pygettext.py b/hscommon/pygettext.py index 026219bf..85aaf6d9 100644 --- a/hscommon/pygettext.py +++ b/hscommon/pygettext.py @@ -15,7 +15,8 @@ # import os -import imp +import importlib.machinery +import importlib.util import sys import glob import token @@ -110,7 +111,7 @@ def _visit_pyfiles(list, dirname, names): # get extension for python source files if "_py_ext" not in globals(): global _py_ext - _py_ext = [triple[0] for triple in imp.get_suffixes() if triple[2] == imp.PY_SOURCE][0] + _py_ext = importlib.machinery.SOURCE_SUFFIXES[0] # don't recurse into CVS directories if "CVS" in names: @@ -120,45 +121,6 @@ def _visit_pyfiles(list, dirname, names): list.extend([os.path.join(dirname, file) for file in names if os.path.splitext(file)[1] == _py_ext]) -def _get_modpkg_path(dotted_name, pathlist=None): - """Get the filesystem path for a module or a package. - - Return the file system path to a file for a module, and to a directory for - a package. Return None if the name is not found, or is a builtin or - extension module. - """ - # split off top-most name - parts = dotted_name.split(".", 1) - - if len(parts) > 1: - # we have a dotted path, import top-level package - try: - file, pathname, description = imp.find_module(parts[0], pathlist) - if file: - file.close() - except ImportError: - return None - - # check if it's indeed a package - if description[2] == imp.PKG_DIRECTORY: - # recursively handle the remaining name parts - pathname = _get_modpkg_path(parts[1], [pathname]) - else: - pathname = None - else: - # plain name - try: - file, pathname, description = imp.find_module(dotted_name, pathlist) - if file: - file.close() - if description[2] not in [imp.PY_SOURCE, imp.PKG_DIRECTORY]: - pathname = None - except ImportError: - pathname = None - - return pathname - - def getFilesForName(name): """Get a list of module files for a filename, a module or package name, or a directory. @@ -173,7 +135,11 @@ def getFilesForName(name): return file_list # try to find module or package - name = _get_modpkg_path(name) + try: + spec = importlib.util.find_spec(name) + name = spec.origin + except ImportError: + name = None if not name: return [] From 53d5ac06bf3bd66e94fba849cdfd16f008155c6b Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Mon, 19 Feb 2024 10:32:13 -0800 Subject: [PATCH 45/56] fix: Linting Errors & VS Code config - Add additional settings to VS Code for formatter changes in plugins - Fix black formatting - Fix flake8 errors due to long lines - Fix flake8 errors due to type comparisons --- .vscode/extensions.json | 3 ++- .vscode/settings.json | 5 ++++- core/directories.py | 4 ++-- core/pe/cache_sqlite.py | 41 +++++++++++++++++++++++++++++++------- core/tests/engine_test.py | 5 ++++- core/tests/scanner_test.py | 12 +++++------ qt/pe/image_viewer.py | 32 ++++++++++++++++++----------- qt/pe/photo.py | 2 +- 8 files changed, 73 insertions(+), 31 deletions(-) diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 4139be42..05c1549c 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -3,7 +3,8 @@ "recommendations": [ "redhat.vscode-yaml", "ms-python.vscode-pylance", - "ms-python.python" + "ms-python.python", + "ms-python.black-formatter", ], // List of extensions recommended by VS Code that should not be recommended for // users of this workspace. diff --git a/.vscode/settings.json b/.vscode/settings.json index 7fdb42b7..7fd9c956 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,4 @@ { - "python.formatting.provider": "black", "cSpell.words": [ "Dupras", "hscommon" @@ -10,4 +9,8 @@ ], "python.languageServer": "Pylance", "yaml.schemaStore.enable": true, + "[python]": { + "editor.formatOnSave": true, + "editor.defaultFormatter": "ms-python.black-formatter" + } } \ No newline at end of file diff --git a/core/directories.py b/core/directories.py index 47418077..a30e263a 100644 --- a/core/directories.py +++ b/core/directories.py @@ -187,7 +187,7 @@ def get_files(self, fileclasses=None, j=job.nulljob): for path in self._dirs: for file in self._get_files(path, fileclasses=fileclasses, j=j): file_count += 1 - if type(j) != job.NullJob: + if not isinstance(j, job.NullJob): j.set_progress(-1, tr("Collected {} files to scan").format(file_count)) yield file @@ -203,7 +203,7 @@ def get_folders(self, folderclass=None, j=job.nulljob): from_folder = folderclass(path) for folder in self._get_folders(from_folder, j): folder_count += 1 - if type(j) != job.NullJob: + if not isinstance(j, job.NullJob): j.set_progress(-1, tr("Collected {} folders to scan").format(folder_count)) yield folder diff --git a/core/pe/cache_sqlite.py b/core/pe/cache_sqlite.py index 5aaf2940..898223ff 100644 --- a/core/pe/cache_sqlite.py +++ b/core/pe/cache_sqlite.py @@ -18,7 +18,9 @@ class SqliteCache: schema_version = 2 schema_version_description = "Added blocks for all 8 orientations." - create_table_query = "CREATE TABLE IF NOT EXISTS pictures(path TEXT, mtime_ns INTEGER, blocks BLOB, blocks2 BLOB, blocks3 BLOB, blocks4 BLOB, blocks5 BLOB, blocks6 BLOB, blocks7 BLOB, blocks8 BLOB)" + create_table_query = ("CREATE TABLE IF NOT EXISTS " + "pictures(path TEXT, mtime_ns INTEGER, blocks BLOB, blocks2 BLOB, blocks3 BLOB, " + "blocks4 BLOB, blocks5 BLOB, blocks6 BLOB, blocks7 BLOB, blocks8 BLOB)") create_index_query = "CREATE INDEX IF NOT EXISTS idx_path on pictures (path)" drop_table_query = "DROP TABLE IF EXISTS pictures" drop_index_query = "DROP INDEX IF EXISTS idx_path" @@ -43,9 +45,13 @@ def __delitem__(self, key): # Optimized def __getitem__(self, key): if isinstance(key, int): - sql = "select blocks, blocks2, blocks3, blocks4, blocks5, blocks6, blocks7, blocks8 from pictures where rowid = ?" + sql = ("select blocks, blocks2, blocks3, blocks4, blocks5, blocks6, blocks7, blocks8 " + "from pictures " + "where rowid = ?") else: - sql = "select blocks, blocks2, blocks3, blocks4, blocks5, blocks6, blocks7, blocks8 from pictures where path = ?" + sql = ("select blocks, blocks2, blocks3, blocks4, blocks5, blocks6, blocks7, blocks8 " + "from pictures " + "where path = ?") blocks = self.con.execute(sql, [key]).fetchone() if blocks: result = [bytes_to_colors(block) for block in blocks] @@ -70,9 +76,12 @@ def __setitem__(self, path_str, blocks): else: mtime = 0 if path_str in self: - sql = "update pictures set blocks = ?, blocks2 = ?, blocks3 = ?, blocks4 = ?, blocks5 = ?, blocks6 = ?, blocks7 = ?, blocks8 = ?, mtime_ns = ? where path = ?" + sql = ("update pictures set blocks = ?, blocks2 = ?, blocks3 = ?, blocks4 = ?, blocks5 = ?, blocks6 = ?, " + "blocks7 = ?, blocks8 = ?, mtime_ns = ?" + "where path = ?") else: - sql = "insert into pictures(blocks,blocks2,blocks3,blocks4,blocks5,blocks6,blocks7,blocks8,mtime_ns,path) values(?,?,?,?,?,?,?,?,?,?)" + sql = ("insert into pictures(blocks,blocks2,blocks3,blocks4,blocks5,blocks6,blocks7,blocks8,mtime_ns,path) " + "values(?,?,?,?,?,?,?,?,?,?)") try: self.con.execute(sql, blocks + [mtime, path_str]) except sqlite.OperationalError: @@ -136,9 +145,27 @@ def get_id(self, path): raise ValueError(path) def get_multiple(self, rowids): - sql = "select rowid, blocks, blocks2, blocks3, blocks4, blocks5, blocks6, blocks7, blocks8 from pictures where rowid in (%s)" % ",".join(map(str, rowids)) + sql = ( + "select rowid, blocks, blocks2, blocks3, blocks4, blocks5, blocks6, blocks7, blocks8 " + f"from pictures where rowid in {",".join(map(str, rowids))}" + ) cur = self.con.execute(sql) - return ((rowid, [bytes_to_colors(blocks), bytes_to_colors(blocks2), bytes_to_colors(blocks3), bytes_to_colors(blocks4), bytes_to_colors(blocks5), bytes_to_colors(blocks6), bytes_to_colors(blocks7), bytes_to_colors(blocks8)]) for rowid, blocks, blocks2, blocks3, blocks4, blocks5, blocks6, blocks7, blocks8 in cur) + return ( + ( + rowid, + [ + bytes_to_colors(blocks), + bytes_to_colors(blocks2), + bytes_to_colors(blocks3), + bytes_to_colors(blocks4), + bytes_to_colors(blocks5), + bytes_to_colors(blocks6), + bytes_to_colors(blocks7), + bytes_to_colors(blocks8), + ], + ) + for rowid, blocks, blocks2, blocks3, blocks4, blocks5, blocks6, blocks7, blocks8 in cur + ) def purge_outdated(self): """Go through the cache and purge outdated records. diff --git a/core/tests/engine_test.py b/core/tests/engine_test.py index 03522d10..2e5170b9 100644 --- a/core/tests/engine_test.py +++ b/core/tests/engine_test.py @@ -71,7 +71,10 @@ def test_spaces(self): def test_unicode(self): eq_(["e", "c", "0", "a", "o", "u", "e", "u"], getwords("é ç 0 à ö û è ¤ ù")) - eq_(["02", "君のこころは輝いてるかい?", "国木田花丸", "solo", "ver"], getwords("02 君のこころは輝いてるかい? 国木田花丸 Solo Ver")) + eq_( + ["02", "君のこころは輝いてるかい?", "国木田花丸", "solo", "ver"], + getwords("02 君のこころは輝いてるかい? 国木田花丸 Solo Ver"), + ) def test_splitter_chars(self): eq_( diff --git a/core/tests/scanner_test.py b/core/tests/scanner_test.py index 35b18395..6dc315fe 100644 --- a/core/tests/scanner_test.py +++ b/core/tests/scanner_test.py @@ -242,12 +242,12 @@ def test_content_scan_doesnt_put_digest_in_words_at_the_end(fake_fileexists): s = Scanner() s.scan_type = ScanType.CONTENTS f = [no("foo"), no("bar")] - f[0].digest = f[0].digest_partial = f[ - 0 - ].digest_samples = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" - f[1].digest = f[1].digest_partial = f[ - 1 - ].digest_samples = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + f[0].digest = f[0].digest_partial = f[0].digest_samples = ( + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + ) + f[1].digest = f[1].digest_partial = f[1].digest_samples = ( + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + ) r = s.get_dupe_groups(f) # FIXME looks like we are missing something here? r[0] diff --git a/qt/pe/image_viewer.py b/qt/pe/image_viewer.py index 61c815eb..06d849af 100644 --- a/qt/pe/image_viewer.py +++ b/qt/pe/image_viewer.py @@ -58,36 +58,44 @@ def setupActions(self, controller): ( "actionZoomIn", QKeySequence.ZoomIn, - QIcon.fromTheme("zoom-in") - if ISLINUX and not self.parent.app.prefs.details_dialog_override_theme_icons - else QIcon(QPixmap(":/" + "zoom_in")), + ( + QIcon.fromTheme("zoom-in") + if ISLINUX and not self.parent.app.prefs.details_dialog_override_theme_icons + else QIcon(QPixmap(":/" + "zoom_in")) + ), tr("Increase zoom"), controller.zoomIn, ), ( "actionZoomOut", QKeySequence.ZoomOut, - QIcon.fromTheme("zoom-out") - if ISLINUX and not self.parent.app.prefs.details_dialog_override_theme_icons - else QIcon(QPixmap(":/" + "zoom_out")), + ( + QIcon.fromTheme("zoom-out") + if ISLINUX and not self.parent.app.prefs.details_dialog_override_theme_icons + else QIcon(QPixmap(":/" + "zoom_out")) + ), tr("Decrease zoom"), controller.zoomOut, ), ( "actionNormalSize", tr("Ctrl+/"), - QIcon.fromTheme("zoom-original") - if ISLINUX and not self.parent.app.prefs.details_dialog_override_theme_icons - else QIcon(QPixmap(":/" + "zoom_original")), + ( + QIcon.fromTheme("zoom-original") + if ISLINUX and not self.parent.app.prefs.details_dialog_override_theme_icons + else QIcon(QPixmap(":/" + "zoom_original")) + ), tr("Normal size"), controller.zoomNormalSize, ), ( "actionBestFit", tr("Ctrl+*"), - QIcon.fromTheme("zoom-best-fit") - if ISLINUX and not self.parent.app.prefs.details_dialog_override_theme_icons - else QIcon(QPixmap(":/" + "zoom_best_fit")), + ( + QIcon.fromTheme("zoom-best-fit") + if ISLINUX and not self.parent.app.prefs.details_dialog_override_theme_icons + else QIcon(QPixmap(":/" + "zoom_best_fit")) + ), tr("Best fit"), controller.zoomBestFit, ), diff --git a/qt/pe/photo.py b/qt/pe/photo.py index 84de9469..e0d0b96e 100644 --- a/qt/pe/photo.py +++ b/qt/pe/photo.py @@ -29,7 +29,7 @@ def _plat_get_dimensions(self): def _plat_get_blocks(self, block_count_per_side, orientation): image = QImage(str(self.path)) image = image.convertToFormat(QImage.Format_RGB888) - if type(orientation) != int: + if not isinstance(orientation, int): logging.warning( "Orientation for file '%s' was a %s '%s', not an int.", str(self.path), From e3a612a7040329e1133780669203975d949ce211 Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Mon, 19 Feb 2024 10:41:28 -0800 Subject: [PATCH 46/56] fix: Correct change that broke black formmating --- core/pe/cache_sqlite.py | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/core/pe/cache_sqlite.py b/core/pe/cache_sqlite.py index 898223ff..43ffac06 100644 --- a/core/pe/cache_sqlite.py +++ b/core/pe/cache_sqlite.py @@ -18,9 +18,11 @@ class SqliteCache: schema_version = 2 schema_version_description = "Added blocks for all 8 orientations." - create_table_query = ("CREATE TABLE IF NOT EXISTS " - "pictures(path TEXT, mtime_ns INTEGER, blocks BLOB, blocks2 BLOB, blocks3 BLOB, " - "blocks4 BLOB, blocks5 BLOB, blocks6 BLOB, blocks7 BLOB, blocks8 BLOB)") + create_table_query = ( + "CREATE TABLE IF NOT EXISTS " + "pictures(path TEXT, mtime_ns INTEGER, blocks BLOB, blocks2 BLOB, blocks3 BLOB, " + "blocks4 BLOB, blocks5 BLOB, blocks6 BLOB, blocks7 BLOB, blocks8 BLOB)" + ) create_index_query = "CREATE INDEX IF NOT EXISTS idx_path on pictures (path)" drop_table_query = "DROP TABLE IF EXISTS pictures" drop_index_query = "DROP INDEX IF EXISTS idx_path" @@ -45,13 +47,17 @@ def __delitem__(self, key): # Optimized def __getitem__(self, key): if isinstance(key, int): - sql = ("select blocks, blocks2, blocks3, blocks4, blocks5, blocks6, blocks7, blocks8 " - "from pictures " - "where rowid = ?") + sql = ( + "select blocks, blocks2, blocks3, blocks4, blocks5, blocks6, blocks7, blocks8 " + "from pictures " + "where rowid = ?" + ) else: - sql = ("select blocks, blocks2, blocks3, blocks4, blocks5, blocks6, blocks7, blocks8 " - "from pictures " - "where path = ?") + sql = ( + "select blocks, blocks2, blocks3, blocks4, blocks5, blocks6, blocks7, blocks8 " + "from pictures " + "where path = ?" + ) blocks = self.con.execute(sql, [key]).fetchone() if blocks: result = [bytes_to_colors(block) for block in blocks] @@ -76,12 +82,16 @@ def __setitem__(self, path_str, blocks): else: mtime = 0 if path_str in self: - sql = ("update pictures set blocks = ?, blocks2 = ?, blocks3 = ?, blocks4 = ?, blocks5 = ?, blocks6 = ?, " - "blocks7 = ?, blocks8 = ?, mtime_ns = ?" - "where path = ?") + sql = ( + "update pictures set blocks = ?, blocks2 = ?, blocks3 = ?, blocks4 = ?, blocks5 = ?, blocks6 = ?, " + "blocks7 = ?, blocks8 = ?, mtime_ns = ?" + "where path = ?" + ) else: - sql = ("insert into pictures(blocks,blocks2,blocks3,blocks4,blocks5,blocks6,blocks7,blocks8,mtime_ns,path) " - "values(?,?,?,?,?,?,?,?,?,?)") + sql = ( + "insert into pictures(blocks,blocks2,blocks3,blocks4,blocks5,blocks6,blocks7,blocks8,mtime_ns,path) " + "values(?,?,?,?,?,?,?,?,?,?)" + ) try: self.con.execute(sql, blocks + [mtime, path_str]) except sqlite.OperationalError: @@ -145,9 +155,10 @@ def get_id(self, path): raise ValueError(path) def get_multiple(self, rowids): + ids = ",".join(map(str, rowids)) sql = ( "select rowid, blocks, blocks2, blocks3, blocks4, blocks5, blocks6, blocks7, blocks8 " - f"from pictures where rowid in {",".join(map(str, rowids))}" + f"from pictures where rowid in {ids}" ) cur = self.con.execute(sql) return ( From 95e04c4d82917a4020c29c8ae61814ca664f8571 Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Mon, 19 Feb 2024 11:35:39 -0800 Subject: [PATCH 47/56] ci: Update .pre-commit-config.yaml Update .pre-commit-config.yaml to use the latest versions of black, flake8, etc. --- .pre-commit-config.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0b2318ff..4874306d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v2.3.0 + rev: v4.5.0 hooks: - id: check-yaml - id: check-toml @@ -8,16 +8,16 @@ repos: exclude: ".*.json" - id: trailing-whitespace - repo: https://github.com/psf/black - rev: 22.10.0 + rev: 24.2.0 hooks: - id: black - repo: https://github.com/PyCQA/flake8 - rev: 6.0.0 + rev: 7.0.0 hooks: - id: flake8 exclude: ^(.tox|env|build|dist|help|qt/dg_rc.py|pkg).* - repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook - rev: v9.3.0 + rev: v9.11.0 hooks: - id: commitlint stages: [commit-msg] From 6e87f53f91b1f9b9e6e4396ce70c8e1d694cf656 Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Mon, 19 Feb 2024 13:26:07 -0800 Subject: [PATCH 48/56] chore: Regenerate translation source files --- locale/core.pot | 68 +++++++++++++++++++++++-------------------------- locale/ui.pot | 4 +++ 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/locale/core.pot b/locale/core.pot index 31da3865..4e428a7f 100644 --- a/locale/core.pot +++ b/locale/core.pot @@ -36,91 +36,91 @@ msgstr "" msgid "Sending to Trash" msgstr "" -#: core\app.py:293 +#: core\app.py:289 msgid "A previous action is still hanging in there. You can't start a new one yet. Wait a few seconds, then try again." msgstr "" -#: core\app.py:304 +#: core\app.py:300 msgid "No duplicates found." msgstr "" -#: core\app.py:319 +#: core\app.py:315 msgid "All marked files were copied successfully." msgstr "" -#: core\app.py:321 +#: core\app.py:317 msgid "All marked files were moved successfully." msgstr "" -#: core\app.py:323 +#: core\app.py:319 msgid "All marked files were deleted successfully." msgstr "" -#: core\app.py:325 +#: core\app.py:321 msgid "All marked files were successfully sent to Trash." msgstr "" -#: core\app.py:330 +#: core\app.py:326 msgid "Could not load file: {}" msgstr "" -#: core\app.py:386 +#: core\app.py:382 msgid "'{}' already is in the list." msgstr "" -#: core\app.py:388 +#: core\app.py:384 msgid "'{}' does not exist." msgstr "" -#: core\app.py:396 +#: core\app.py:392 msgid "All selected %d matches are going to be ignored in all subsequent scans. Continue?" msgstr "" -#: core\app.py:473 +#: core\app.py:469 msgid "Select a directory to copy marked files to" msgstr "" -#: core\app.py:475 +#: core\app.py:471 msgid "Select a directory to move marked files to" msgstr "" -#: core\app.py:514 +#: core\app.py:510 msgid "Select a destination for your exported CSV" msgstr "" -#: core\app.py:520 core\app.py:781 core\app.py:791 +#: core\app.py:516 core\app.py:777 core\app.py:787 msgid "Couldn't write to file: {}" msgstr "" -#: core\app.py:543 +#: core\app.py:539 msgid "You have no custom command set up. Set it up in your preferences." msgstr "" -#: core\app.py:705 core\app.py:717 +#: core\app.py:701 core\app.py:713 msgid "You are about to remove %d files from results. Continue?" msgstr "" -#: core\app.py:753 +#: core\app.py:749 msgid "{} duplicate groups were changed by the re-prioritization." msgstr "" -#: core\app.py:801 +#: core\app.py:797 msgid "The selected directories contain no scannable file." msgstr "" -#: core\app.py:817 +#: core\app.py:813 msgid "Collecting files to scan" msgstr "" -#: core\app.py:867 +#: core\app.py:863 msgid "%s (%d discarded)" msgstr "" -#: core\directories.py:190 +#: core\directories.py:191 msgid "Collected {} files to scan" msgstr "" -#: core\directories.py:206 +#: core\directories.py:207 msgid "Collected {} folders to scan" msgstr "" @@ -156,23 +156,23 @@ msgstr "" msgid "Tags" msgstr "" -#: core\me\scanner.py:24 core\pe\scanner.py:21 core\se\scanner.py:17 +#: core\me\scanner.py:24 core\pe\scanner.py:22 core\se\scanner.py:17 msgid "Contents" msgstr "" -#: core\pe\matchblock.py:72 +#: core\pe\matchblock.py:66 msgid "Analyzed %d/%d pictures" msgstr "" -#: core\pe\matchblock.py:177 +#: core\pe\matchblock.py:183 msgid "Performed %d/%d chunk matches" msgstr "" -#: core\pe\matchblock.py:185 +#: core\pe\matchblock.py:191 msgid "Preparing for matching" msgstr "" -#: core\pe\matchblock.py:234 +#: core\pe\matchblock.py:240 msgid "Verified %d/%d matches" msgstr "" @@ -180,7 +180,7 @@ msgstr "" msgid "Read EXIF of %d/%d pictures" msgstr "" -#: core\pe\scanner.py:22 +#: core\pe\scanner.py:23 msgid "EXIF Timestamp" msgstr "" @@ -220,23 +220,19 @@ msgstr "" msgid "Oldest" msgstr "" -#: core\results.py:134 +#: core\results.py:135 msgid "%d / %d (%s / %s) duplicates marked." msgstr "" -#: core\results.py:141 +#: core\results.py:142 msgid " filter: %s" msgstr "" -#: core\scanner.py:90 -msgid "Read size of %d/%d files" -msgstr "" - -#: core\scanner.py:116 +#: core\scanner.py:114 msgid "Read metadata of %d/%d files" msgstr "" -#: core\scanner.py:154 +#: core\scanner.py:152 msgid "Almost done! Fiddling with results..." msgstr "" diff --git a/locale/ui.pot b/locale/ui.pot index c41c917e..76f57ce1 100644 --- a/locale/ui.pot +++ b/locale/ui.pot @@ -1121,3 +1121,7 @@ msgid "" "
  • 2. Regular expressions with at least one path separator in them will be compared to the full path to the file.

  • Example: if you want to filter out .PNG files from the \"My Pictures\" directory only:
    .*My\\sPictures\\\\.*\\.png

    You can test the regular expression with the \"test string\" button after pasting a fake path in the test field:
    C:\\\\User\\My Pictures\\test.png

    \n" "Matching regular expressions will be highlighted.
    If there is at least one highlight, the path or filename tested will be ignored during scans.

    Directories and files starting with a period '.' are filtered out by default.

    " msgstr "" + +#: qt\pe\preferences_dialog.py:24 +msgid "Match pictures of different rotations" +msgstr "" From 253dfd897cbc921d6329bcdcc922217e453f534c Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Mon, 19 Feb 2024 13:35:35 -0800 Subject: [PATCH 49/56] chore: Pull existing language translations from Transifex --- locale/cs/LC_MESSAGES/core.po | 70 ++++++++++---------- locale/cs/LC_MESSAGES/ui.po | 8 ++- locale/de/LC_MESSAGES/core.po | 70 ++++++++++---------- locale/de/LC_MESSAGES/ui.po | 8 ++- locale/el/LC_MESSAGES/core.po | 70 ++++++++++---------- locale/el/LC_MESSAGES/ui.po | 8 ++- locale/es/LC_MESSAGES/core.po | 72 ++++++++++----------- locale/fr/LC_MESSAGES/core.po | 72 ++++++++++----------- locale/fr/LC_MESSAGES/ui.po | 8 ++- locale/hy/LC_MESSAGES/core.po | 70 ++++++++++---------- locale/hy/LC_MESSAGES/ui.po | 8 ++- locale/it/LC_MESSAGES/core.po | 68 +++++++++----------- locale/it/LC_MESSAGES/ui.po | 10 ++- locale/ja/LC_MESSAGES/core.po | 70 ++++++++++---------- locale/ja/LC_MESSAGES/ui.po | 8 ++- locale/ko/LC_MESSAGES/columns.po | 4 +- locale/ko/LC_MESSAGES/core.po | 72 ++++++++++----------- locale/ko/LC_MESSAGES/ui.po | 16 +++-- locale/ms/LC_MESSAGES/core.po | 70 ++++++++++---------- locale/nl/LC_MESSAGES/core.po | 70 ++++++++++---------- locale/nl/LC_MESSAGES/ui.po | 8 ++- locale/pl_PL/LC_MESSAGES/core.po | 70 ++++++++++---------- locale/pl_PL/LC_MESSAGES/ui.po | 8 ++- locale/pt_BR/LC_MESSAGES/core.po | 72 ++++++++++----------- locale/ru/LC_MESSAGES/core.po | 73 ++++++++++----------- locale/ru/LC_MESSAGES/ui.po | 9 ++- locale/tr/LC_MESSAGES/core.po | 70 ++++++++++---------- locale/tr/LC_MESSAGES/ui.po | 7 +- locale/uk/LC_MESSAGES/core.po | 70 ++++++++++---------- locale/uk/LC_MESSAGES/ui.po | 8 ++- locale/vi/LC_MESSAGES/core.po | 70 ++++++++++---------- locale/vi/LC_MESSAGES/ui.po | 8 ++- locale/zh_CN/LC_MESSAGES/core.po | 107 +++++++++++++++---------------- locale/zh_CN/LC_MESSAGES/ui.po | 101 ++++++++++++++++------------- locale/zh_TW/LC_MESSAGES/core.po | 70 ++++++++++---------- locale/zh_TW/LC_MESSAGES/ui.po | 8 ++- 36 files changed, 805 insertions(+), 806 deletions(-) diff --git a/locale/cs/LC_MESSAGES/core.po b/locale/cs/LC_MESSAGES/core.po index 3c5210fb..762483c5 100644 --- a/locale/cs/LC_MESSAGES/core.po +++ b/locale/cs/LC_MESSAGES/core.po @@ -5,21 +5,21 @@ msgid "" msgstr "" "Last-Translator: Fuan , 2021\n" -"Language-Team: Czech (https://www.transifex.com/voltaicideas/teams/116153/cs/)\n" +"Language-Team: Czech (https://app.transifex.com/voltaicideas/teams/116153/cs/)\n" "Language: cs\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" "Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n" -#: core\app.py:42 +#: core\app.py:44 msgid "There are no marked duplicates. Nothing has been done." msgstr "Neexistují žádné označené duplikáty. Nic se nestalo." -#: core\app.py:43 +#: core\app.py:45 msgid "There are no selected duplicates. Nothing has been done." msgstr "Nejsou k dispozici žádné vybrané duplikáty. Nic se nestalo." -#: core\app.py:44 +#: core\app.py:46 msgid "" "You're about to open many files at once. Depending on what those files are " "opened with, doing so can create quite a mess. Continue?" @@ -27,23 +27,23 @@ msgstr "" "Chystáte se otevřít více souborů najednou. V závislosti na tom, s čím jsou " "tyto soubory otevřeny, to může způsobit docela nepořádek. Pokračovat?" -#: core\app.py:71 +#: core\app.py:73 msgid "Scanning for duplicates" msgstr "Vyhledávám duplicity" -#: core\app.py:72 +#: core\app.py:74 msgid "Loading" msgstr "Nahrávám" -#: core\app.py:73 +#: core\app.py:75 msgid "Moving" msgstr "Přesouvám" -#: core\app.py:74 +#: core\app.py:76 msgid "Copying" msgstr "Kopíruji" -#: core\app.py:75 +#: core\app.py:77 msgid "Sending to Trash" msgstr "Vyhazuji do koše" @@ -107,7 +107,7 @@ msgstr "Vyberte adresář, kam chcete přesunout označené soubory" msgid "Select a destination for your exported CSV" msgstr "Vyberte cíl pro exportovaný soubor CSV" -#: core\app.py:516 core\app.py:771 core\app.py:781 +#: core\app.py:516 core\app.py:777 core\app.py:787 msgid "Couldn't write to file: {}" msgstr "Nelze zapisovat do souboru: {}" @@ -116,23 +116,23 @@ msgid "You have no custom command set up. Set it up in your preferences." msgstr "" "Nedefinoval jste žádný uživatelský příkaz. Nadefinujete ho v předvolbách." -#: core\app.py:695 core\app.py:707 +#: core\app.py:701 core\app.py:713 msgid "You are about to remove %d files from results. Continue?" msgstr "Chystáte se z výsledků odstranit %d souborů. Pokračovat?" -#: core\app.py:743 +#: core\app.py:749 msgid "{} duplicate groups were changed by the re-prioritization." msgstr "{} duplicitní skupiny byly změněny změně priorit." -#: core\app.py:790 +#: core\app.py:797 msgid "The selected directories contain no scannable file." msgstr "Vybrané adresáře neobsahují žádné soubory vhodné k prohledávání." -#: core\app.py:803 +#: core\app.py:813 msgid "Collecting files to scan" msgstr "Shromažďuji prohlížené soubory" -#: core\app.py:850 +#: core\app.py:863 msgid "%s (%d discarded)" msgstr "%s (%d vyřazeno)" @@ -176,23 +176,23 @@ msgstr "Název souboru - pole (bez objednávky)" msgid "Tags" msgstr "Tagy" -#: core\me\scanner.py:24 core\pe\scanner.py:21 core\se\scanner.py:17 +#: core\me\scanner.py:24 core\pe\scanner.py:22 core\se\scanner.py:17 msgid "Contents" msgstr "Obsah" -#: core\pe\matchblock.py:72 +#: core\pe\matchblock.py:66 msgid "Analyzed %d/%d pictures" msgstr "Analyzováno %d/%d snímků" -#: core\pe\matchblock.py:177 +#: core\pe\matchblock.py:183 msgid "Performed %d/%d chunk matches" msgstr "Provedeno %d/%d porovnání bloků" -#: core\pe\matchblock.py:185 +#: core\pe\matchblock.py:191 msgid "Preparing for matching" msgstr "Připravuji porovnávání" -#: core\pe\matchblock.py:234 +#: core\pe\matchblock.py:240 msgid "Verified %d/%d matches" msgstr "Ověřeno %d/%d shod" @@ -200,7 +200,7 @@ msgstr "Ověřeno %d/%d shod" msgid "Read EXIF of %d/%d pictures" msgstr "Přečetl EXIF %d/%d obrázků" -#: core\pe\scanner.py:22 +#: core\pe\scanner.py:23 msgid "EXIF Timestamp" msgstr "Časové razítko EXIF" @@ -208,55 +208,51 @@ msgstr "Časové razítko EXIF" msgid "None" msgstr "Zádný" -#: core\prioritize.py:100 +#: core\prioritize.py:102 msgid "Ends with number" msgstr "Končí číslem" -#: core\prioritize.py:101 +#: core\prioritize.py:103 msgid "Doesn't end with number" msgstr "Nekončí číslem" -#: core\prioritize.py:102 +#: core\prioritize.py:104 msgid "Longest" msgstr "Nejdelší" -#: core\prioritize.py:103 +#: core\prioritize.py:105 msgid "Shortest" msgstr "Nejkratší" -#: core\prioritize.py:140 +#: core\prioritize.py:142 msgid "Highest" msgstr "Nejvyšší" -#: core\prioritize.py:140 +#: core\prioritize.py:142 msgid "Lowest" msgstr "Nejnižší" -#: core\prioritize.py:169 +#: core\prioritize.py:171 msgid "Newest" msgstr "Nejnovější" -#: core\prioritize.py:169 +#: core\prioritize.py:171 msgid "Oldest" msgstr "Nejstarší" -#: core\results.py:134 +#: core\results.py:135 msgid "%d / %d (%s / %s) duplicates marked." msgstr "%d / %d (%s / %s) duplicit označeno." -#: core\results.py:141 +#: core\results.py:142 msgid " filter: %s" msgstr " filtr: %s" -#: core\scanner.py:90 -msgid "Read size of %d/%d files" -msgstr "Read size of %d/%d files" - -#: core\scanner.py:116 +#: core\scanner.py:114 msgid "Read metadata of %d/%d files" msgstr "Načtena metadata %d/%d souborů" -#: core\scanner.py:154 +#: core\scanner.py:152 msgid "Almost done! Fiddling with results..." msgstr "Skoro hotovo! Fidlování s výsledky..." diff --git a/locale/cs/LC_MESSAGES/ui.po b/locale/cs/LC_MESSAGES/ui.po index e5f89862..ec7c8ab9 100644 --- a/locale/cs/LC_MESSAGES/ui.po +++ b/locale/cs/LC_MESSAGES/ui.po @@ -1,10 +1,10 @@ # Translators: -# Andrew Senetar , 2022 # Fuan , 2022 +# Andrew Senetar , 2023 # msgid "" msgstr "" -"Last-Translator: Fuan , 2022\n" +"Last-Translator: Andrew Senetar , 2023\n" "Language-Team: Czech (https://app.transifex.com/voltaicideas/teams/116153/cs/)\n" "Language: cs\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -1160,3 +1160,7 @@ msgid "" "
  • 2. Regular expressions with at least one path separator in them will be compared to the full path to the file.

  • Example: if you want to filter out .PNG files from the \"My Pictures\" directory only:
    .*My\\sPictures\\\\.*\\.png

    You can test the regular expression with the \"test string\" button after pasting a fake path in the test field:
    C:\\\\User\\My Pictures\\test.png

    \n" "Matching regular expressions will be highlighted.
    If there is at least one highlight, the path or filename tested will be ignored during scans.

    Directories and files starting with a period '.' are filtered out by default.

    " msgstr "" + +#: qt\pe\preferences_dialog.py:24 +msgid "Match pictures of different rotations" +msgstr "" diff --git a/locale/de/LC_MESSAGES/core.po b/locale/de/LC_MESSAGES/core.po index 60feabb8..df1c10d9 100644 --- a/locale/de/LC_MESSAGES/core.po +++ b/locale/de/LC_MESSAGES/core.po @@ -6,21 +6,21 @@ msgid "" msgstr "" "Last-Translator: Robert M, 2021\n" -"Language-Team: German (https://www.transifex.com/voltaicideas/teams/116153/de/)\n" +"Language-Team: German (https://app.transifex.com/voltaicideas/teams/116153/de/)\n" "Language: de\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: core\app.py:42 +#: core\app.py:44 msgid "There are no marked duplicates. Nothing has been done." msgstr "Keine markierten Duplikate, daher wurde nichts getan." -#: core\app.py:43 +#: core\app.py:45 msgid "There are no selected duplicates. Nothing has been done." msgstr "Keine ausgewählten Duplikate, daher wurde nichts getan." -#: core\app.py:44 +#: core\app.py:46 msgid "" "You're about to open many files at once. Depending on what those files are " "opened with, doing so can create quite a mess. Continue?" @@ -28,23 +28,23 @@ msgstr "" "Sie sind dabei, sehr viele Dateien gleichzeitig zu öffnen. Das kann zu " "ziemlichem Durcheinander führen! Trotzdem fortfahren?" -#: core\app.py:71 +#: core\app.py:73 msgid "Scanning for duplicates" msgstr "Suche nach Duplikaten" -#: core\app.py:72 +#: core\app.py:74 msgid "Loading" msgstr "Lade" -#: core\app.py:73 +#: core\app.py:75 msgid "Moving" msgstr "Verschiebe" -#: core\app.py:74 +#: core\app.py:76 msgid "Copying" msgstr "Kopiere" -#: core\app.py:75 +#: core\app.py:77 msgid "Sending to Trash" msgstr "Verschiebe in den Papierkorb" @@ -113,7 +113,7 @@ msgstr "" msgid "Select a destination for your exported CSV" msgstr "Zielverzeichnis für den CSV Export angeben" -#: core\app.py:516 core\app.py:771 core\app.py:781 +#: core\app.py:516 core\app.py:777 core\app.py:787 msgid "Couldn't write to file: {}" msgstr "Konnte Datei {} nicht schreiben." @@ -123,23 +123,23 @@ msgstr "" "Sie haben noch keinen Befehl erstellt. Bitte dies in den Einstellungen vornehmen.\n" "Bsp.: \"C:\\Program Files\\Diff\\Diff.exe\" \"%d\" \"%r\"" -#: core\app.py:695 core\app.py:707 +#: core\app.py:701 core\app.py:713 msgid "You are about to remove %d files from results. Continue?" msgstr "%d Dateien werden aus der Ergebnisliste entfernt. Fortfahren?" -#: core\app.py:743 +#: core\app.py:749 msgid "{} duplicate groups were changed by the re-prioritization." msgstr "{} Duplikat-Gruppen wurden durch die Neu-Priorisierung geändert." -#: core\app.py:790 +#: core\app.py:797 msgid "The selected directories contain no scannable file." msgstr "Ausgewählte Ordner enthalten keine scannbaren Dateien." -#: core\app.py:803 +#: core\app.py:813 msgid "Collecting files to scan" msgstr "Sammle zu scannende Dateien..." -#: core\app.py:850 +#: core\app.py:863 msgid "%s (%d discarded)" msgstr "%s (%d verworfen)" @@ -183,23 +183,23 @@ msgstr "Dateiname - Bereiche (ohne Reihenfolge)" msgid "Tags" msgstr "Tags" -#: core\me\scanner.py:24 core\pe\scanner.py:21 core\se\scanner.py:17 +#: core\me\scanner.py:24 core\pe\scanner.py:22 core\se\scanner.py:17 msgid "Contents" msgstr "Inhalt" -#: core\pe\matchblock.py:72 +#: core\pe\matchblock.py:66 msgid "Analyzed %d/%d pictures" msgstr "Analysiere Bild %d/%d" -#: core\pe\matchblock.py:177 +#: core\pe\matchblock.py:183 msgid "Performed %d/%d chunk matches" msgstr "%d/%d Chunk-Matches ausgeführt" -#: core\pe\matchblock.py:185 +#: core\pe\matchblock.py:191 msgid "Preparing for matching" msgstr "Bereite Matching vor" -#: core\pe\matchblock.py:234 +#: core\pe\matchblock.py:240 msgid "Verified %d/%d matches" msgstr "%d/%d verifizierte Übereinstimmungen" @@ -207,7 +207,7 @@ msgstr "%d/%d verifizierte Übereinstimmungen" msgid "Read EXIF of %d/%d pictures" msgstr "Lese EXIF von Bild %d/%d" -#: core\pe\scanner.py:22 +#: core\pe\scanner.py:23 msgid "EXIF Timestamp" msgstr "EXIF Zeitstempel" @@ -215,55 +215,51 @@ msgstr "EXIF Zeitstempel" msgid "None" msgstr "Nichts" -#: core\prioritize.py:100 +#: core\prioritize.py:102 msgid "Ends with number" msgstr "Endet mit Zahl" -#: core\prioritize.py:101 +#: core\prioritize.py:103 msgid "Doesn't end with number" msgstr "Endet nicht mit Zahl" -#: core\prioritize.py:102 +#: core\prioritize.py:104 msgid "Longest" msgstr "Längste" -#: core\prioritize.py:103 +#: core\prioritize.py:105 msgid "Shortest" msgstr "Kürzeste" -#: core\prioritize.py:140 +#: core\prioritize.py:142 msgid "Highest" msgstr "Höchste" -#: core\prioritize.py:140 +#: core\prioritize.py:142 msgid "Lowest" msgstr "Niedrigste" -#: core\prioritize.py:169 +#: core\prioritize.py:171 msgid "Newest" msgstr "Neuste" -#: core\prioritize.py:169 +#: core\prioritize.py:171 msgid "Oldest" msgstr "Älterste" -#: core\results.py:134 +#: core\results.py:135 msgid "%d / %d (%s / %s) duplicates marked." msgstr "%d / %d (%s / %s) Duplikate markiert." -#: core\results.py:141 +#: core\results.py:142 msgid " filter: %s" msgstr " Filter: %s" -#: core\scanner.py:90 -msgid "Read size of %d/%d files" -msgstr "Lese Größe von %d/%d Dateien" - -#: core\scanner.py:116 +#: core\scanner.py:114 msgid "Read metadata of %d/%d files" msgstr "Lese Metadaten von %d/%d Dateien" -#: core\scanner.py:154 +#: core\scanner.py:152 msgid "Almost done! Fiddling with results..." msgstr "Fast fertig! Arrangiere Ergebnisse..." diff --git a/locale/de/LC_MESSAGES/ui.po b/locale/de/LC_MESSAGES/ui.po index 3ea091c0..c5c85d92 100644 --- a/locale/de/LC_MESSAGES/ui.po +++ b/locale/de/LC_MESSAGES/ui.po @@ -1,12 +1,12 @@ # Translators: # Robert M, 2022 -# Andrew Senetar , 2022 # Fuan , 2022 # Frederik Gschaider , 2022 +# Andrew Senetar , 2023 # msgid "" msgstr "" -"Last-Translator: Frederik Gschaider , 2022\n" +"Last-Translator: Andrew Senetar , 2023\n" "Language-Team: German (https://app.transifex.com/voltaicideas/teams/116153/de/)\n" "Language: de\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -1177,3 +1177,7 @@ msgid "" "
  • 2. Regular expressions with at least one path separator in them will be compared to the full path to the file.

  • Example: if you want to filter out .PNG files from the \"My Pictures\" directory only:
    .*My\\sPictures\\\\.*\\.png

    You can test the regular expression with the \"test string\" button after pasting a fake path in the test field:
    C:\\\\User\\My Pictures\\test.png

    \n" "Matching regular expressions will be highlighted.
    If there is at least one highlight, the path or filename tested will be ignored during scans.

    Directories and files starting with a period '.' are filtered out by default.

    " msgstr "" + +#: qt\pe\preferences_dialog.py:24 +msgid "Match pictures of different rotations" +msgstr "" diff --git a/locale/el/LC_MESSAGES/core.po b/locale/el/LC_MESSAGES/core.po index 7cd4db51..2e4f0ad9 100644 --- a/locale/el/LC_MESSAGES/core.po +++ b/locale/el/LC_MESSAGES/core.po @@ -5,21 +5,21 @@ msgid "" msgstr "" "Last-Translator: Fuan , 2021\n" -"Language-Team: Greek (https://www.transifex.com/voltaicideas/teams/116153/el/)\n" +"Language-Team: Greek (https://app.transifex.com/voltaicideas/teams/116153/el/)\n" "Language: el\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: core\app.py:42 +#: core\app.py:44 msgid "There are no marked duplicates. Nothing has been done." msgstr "Δεν υπάρχουν μαρκαρισμένα διπλότυπα. Δεν έγινε τίποτα." -#: core\app.py:43 +#: core\app.py:45 msgid "There are no selected duplicates. Nothing has been done." msgstr "Δεν υπάρχουν επιλεγμένα διπλότυπα. Δεν έγινε τίποτα." -#: core\app.py:44 +#: core\app.py:46 msgid "" "You're about to open many files at once. Depending on what those files are " "opened with, doing so can create quite a mess. Continue?" @@ -28,23 +28,23 @@ msgstr "" "ανοίγουν αυτάτα αρχεία, κάτι τέτοιο μπορεί να προκαλέσει ένα μικρό χάος. " "Συνέχεια;" -#: core\app.py:71 +#: core\app.py:73 msgid "Scanning for duplicates" msgstr "Σάρωση για διπλότυπα" -#: core\app.py:72 +#: core\app.py:74 msgid "Loading" msgstr "Φόρτωση" -#: core\app.py:73 +#: core\app.py:75 msgid "Moving" msgstr "Μετακίνηση" -#: core\app.py:74 +#: core\app.py:76 msgid "Copying" msgstr "Αντιγραφή" -#: core\app.py:75 +#: core\app.py:77 msgid "Sending to Trash" msgstr "Αποστολή στα σκουπίδια" @@ -107,7 +107,7 @@ msgstr "Επιλέξτε έναν κατάλογο για να μετακινή msgid "Select a destination for your exported CSV" msgstr "Επιλέξτε έναν προορισμό για το εξαγόμενο CSV σας" -#: core\app.py:516 core\app.py:771 core\app.py:781 +#: core\app.py:516 core\app.py:777 core\app.py:787 msgid "Couldn't write to file: {}" msgstr "Δεν ήταν δυνατή η εγγραφή στο αρχείο: {}" @@ -115,23 +115,23 @@ msgstr "Δεν ήταν δυνατή η εγγραφή στο αρχείο: {}" msgid "You have no custom command set up. Set it up in your preferences." msgstr "Δεν έχετε ορίσει ειδική εντολή. Ρυθμίστε τη στις προτιμήσεις σας. " -#: core\app.py:695 core\app.py:707 +#: core\app.py:701 core\app.py:713 msgid "You are about to remove %d files from results. Continue?" msgstr "Πρόκειται να αφαιρέσετε %d αρχεία από τα αποτελέσματα. Συνέχεια;" -#: core\app.py:743 +#: core\app.py:749 msgid "{} duplicate groups were changed by the re-prioritization." msgstr "{} ομάδες διπλοτύπων άλλαξαν από το επαναπροσδιορισμό." -#: core\app.py:790 +#: core\app.py:797 msgid "The selected directories contain no scannable file." msgstr "Οι επιλεγμένοι φάκελοι δεν περιέχουν σαρώσιμα αρχεία." -#: core\app.py:803 +#: core\app.py:813 msgid "Collecting files to scan" msgstr "Συλλογή αρχείων για σάρωση" -#: core\app.py:850 +#: core\app.py:863 msgid "%s (%d discarded)" msgstr "%s (%d απορρίφθηκαν)" @@ -175,23 +175,23 @@ msgstr "Όνομα αρχείου - Πεδία (Χωρίς παραγγελία) msgid "Tags" msgstr "ετικέτα" -#: core\me\scanner.py:24 core\pe\scanner.py:21 core\se\scanner.py:17 +#: core\me\scanner.py:24 core\pe\scanner.py:22 core\se\scanner.py:17 msgid "Contents" msgstr "Περιεχόμενα" -#: core\pe\matchblock.py:72 +#: core\pe\matchblock.py:66 msgid "Analyzed %d/%d pictures" msgstr "Ανάλυση %d/%d εικόνων" -#: core\pe\matchblock.py:177 +#: core\pe\matchblock.py:183 msgid "Performed %d/%d chunk matches" msgstr "Εκτέλεση %d/%d μερικής ταυτοποίησης" -#: core\pe\matchblock.py:185 +#: core\pe\matchblock.py:191 msgid "Preparing for matching" msgstr "Προετοιμασία για σύγκριση" -#: core\pe\matchblock.py:234 +#: core\pe\matchblock.py:240 msgid "Verified %d/%d matches" msgstr "Πιστοποίηση %d/%d ταυτόσημων" @@ -199,7 +199,7 @@ msgstr "Πιστοποίηση %d/%d ταυτόσημων" msgid "Read EXIF of %d/%d pictures" msgstr "Ανάγνωση EXIF %d/%d εικόνες" -#: core\pe\scanner.py:22 +#: core\pe\scanner.py:23 msgid "EXIF Timestamp" msgstr "Χρονική σήμανση EXIF" @@ -207,55 +207,51 @@ msgstr "Χρονική σήμανση EXIF" msgid "None" msgstr "Καμμία" -#: core\prioritize.py:100 +#: core\prioritize.py:102 msgid "Ends with number" msgstr "Λήγει με αριθμό" -#: core\prioritize.py:101 +#: core\prioritize.py:103 msgid "Doesn't end with number" msgstr "Δεν λήγει με αριθμό" -#: core\prioritize.py:102 +#: core\prioritize.py:104 msgid "Longest" msgstr "Μεγαλύτερο" -#: core\prioritize.py:103 +#: core\prioritize.py:105 msgid "Shortest" msgstr "Μικρότερο" -#: core\prioritize.py:140 +#: core\prioritize.py:142 msgid "Highest" msgstr "Υψηλότερη" -#: core\prioritize.py:140 +#: core\prioritize.py:142 msgid "Lowest" msgstr "Χαμηλότερη" -#: core\prioritize.py:169 +#: core\prioritize.py:171 msgid "Newest" msgstr "Νεώτερο" -#: core\prioritize.py:169 +#: core\prioritize.py:171 msgid "Oldest" msgstr "Παλαιότερο" -#: core\results.py:134 +#: core\results.py:135 msgid "%d / %d (%s / %s) duplicates marked." msgstr "%d / %d (%s / %s) επιλεγμένα διπλότυπα." -#: core\results.py:141 +#: core\results.py:142 msgid " filter: %s" msgstr " φίλτρο: %s" -#: core\scanner.py:90 -msgid "Read size of %d/%d files" -msgstr "Ανάγνωση μεγέθους %d/%d αρχείων" - -#: core\scanner.py:116 +#: core\scanner.py:114 msgid "Read metadata of %d/%d files" msgstr "Ανάγνωση μεταδεδομένων των %d/%d αρχείων" -#: core\scanner.py:154 +#: core\scanner.py:152 msgid "Almost done! Fiddling with results..." msgstr "Σχεδόν τελείωσα! Παιχνίδι με αποτελέσματα ..." diff --git a/locale/el/LC_MESSAGES/ui.po b/locale/el/LC_MESSAGES/ui.po index 50aedf90..2133ceaf 100644 --- a/locale/el/LC_MESSAGES/ui.po +++ b/locale/el/LC_MESSAGES/ui.po @@ -1,10 +1,10 @@ # Translators: # Fuan , 2022 -# Andrew Senetar , 2022 +# Andrew Senetar , 2023 # msgid "" msgstr "" -"Last-Translator: Andrew Senetar , 2022\n" +"Last-Translator: Andrew Senetar , 2023\n" "Language-Team: Greek (https://app.transifex.com/voltaicideas/teams/116153/el/)\n" "Language: el\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -1177,3 +1177,7 @@ msgid "" "
  • 2. Regular expressions with at least one path separator in them will be compared to the full path to the file.

  • Example: if you want to filter out .PNG files from the \"My Pictures\" directory only:
    .*My\\sPictures\\\\.*\\.png

    You can test the regular expression with the \"test string\" button after pasting a fake path in the test field:
    C:\\\\User\\My Pictures\\test.png

    \n" "Matching regular expressions will be highlighted.
    If there is at least one highlight, the path or filename tested will be ignored during scans.

    Directories and files starting with a period '.' are filtered out by default.

    " msgstr "" + +#: qt\pe\preferences_dialog.py:24 +msgid "Match pictures of different rotations" +msgstr "" diff --git a/locale/es/LC_MESSAGES/core.po b/locale/es/LC_MESSAGES/core.po index 85cd46c6..9b54bfbc 100644 --- a/locale/es/LC_MESSAGES/core.po +++ b/locale/es/LC_MESSAGES/core.po @@ -6,11 +6,11 @@ msgid "" msgstr "" "Last-Translator: IlluminatiWave, 2022\n" -"Language-Team: Spanish (https://www.transifex.com/voltaicideas/teams/116153/es/)\n" +"Language-Team: Spanish (https://app.transifex.com/voltaicideas/teams/116153/es/)\n" "Language: es\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" #: core\app.py:44 msgid "There are no marked duplicates. Nothing has been done." @@ -48,7 +48,7 @@ msgstr "Copiando" msgid "Sending to Trash" msgstr "Enviando a la Papelera" -#: core\app.py:291 +#: core\app.py:289 msgid "" "A previous action is still hanging in there. You can't start a new one yet. " "Wait a few seconds, then try again." @@ -56,40 +56,40 @@ msgstr "" "Una acción previa sigue ejecutándose. No puede abrir una nueva todavía. " "Espere unos segundos y vuelva a intentarlo." -#: core\app.py:302 +#: core\app.py:300 msgid "No duplicates found." msgstr "No se han encontrado duplicados." -#: core\app.py:317 +#: core\app.py:315 msgid "All marked files were copied successfully." msgstr "" "Todos los ficheros seleccionados han sido copiados satisfactoriamente." -#: core\app.py:319 +#: core\app.py:317 msgid "All marked files were moved successfully." msgstr "Todos los ficheros seleccionados se han movidos satisfactoriamente." -#: core\app.py:321 +#: core\app.py:319 msgid "All marked files were deleted successfully." msgstr "Todos los ficheros seleccionados se han eliminado satisfactoriamente." -#: core\app.py:323 +#: core\app.py:321 msgid "All marked files were successfully sent to Trash." msgstr "Todo los ficheros marcados se han enviado a la papelera exitosamente." -#: core\app.py:328 +#: core\app.py:326 msgid "Could not load file: {}" msgstr "No se pudo cargar el archivo: {}" -#: core\app.py:384 +#: core\app.py:382 msgid "'{}' already is in the list." msgstr "'{}' ya está en la lista." -#: core\app.py:386 +#: core\app.py:384 msgid "'{}' does not exist." msgstr "'{}' no existe." -#: core\app.py:394 +#: core\app.py:392 msgid "" "All selected %d matches are going to be ignored in all subsequent scans. " "Continue?" @@ -97,51 +97,51 @@ msgstr "" "Todas las %d coincidencias seleccionadas van a ser ignoradas en las " "subsiguientes exploraciones. ¿Continuar?" -#: core\app.py:471 +#: core\app.py:469 msgid "Select a directory to copy marked files to" msgstr "Seleccione un directorio donde desee copiar los archivos marcados" -#: core\app.py:473 +#: core\app.py:471 msgid "Select a directory to move marked files to" msgstr "Seleccione un directorio al que desee mover los archivos marcados" -#: core\app.py:512 +#: core\app.py:510 msgid "Select a destination for your exported CSV" msgstr "Seleccionar un destino para el CSV seleccionado" -#: core\app.py:518 core\app.py:773 core\app.py:783 +#: core\app.py:516 core\app.py:777 core\app.py:787 msgid "Couldn't write to file: {}" msgstr "No se pudo escribir en el archivo: {}" -#: core\app.py:541 +#: core\app.py:539 msgid "You have no custom command set up. Set it up in your preferences." msgstr "No hay comandos configurados. Establézcalos en sus preferencias." -#: core\app.py:697 core\app.py:709 +#: core\app.py:701 core\app.py:713 msgid "You are about to remove %d files from results. Continue?" msgstr "Está a punto de eliminar %d ficheros de resultados. ¿Continuar?" -#: core\app.py:745 +#: core\app.py:749 msgid "{} duplicate groups were changed by the re-prioritization." msgstr "{} grupos de duplicados han sido cambiados por la re-priorización." -#: core\app.py:792 +#: core\app.py:797 msgid "The selected directories contain no scannable file." msgstr "Las carpetas seleccionadas no contienen ficheros para explorar." -#: core\app.py:808 +#: core\app.py:813 msgid "Collecting files to scan" msgstr "Recopilando ficheros a explorar" -#: core\app.py:858 +#: core\app.py:863 msgid "%s (%d discarded)" msgstr "%s (%d descartados)" -#: core\directories.py:190 +#: core\directories.py:191 msgid "Collected {} files to scan" msgstr "{} ficheros recopilados para explorar" -#: core\directories.py:206 +#: core\directories.py:207 msgid "Collected {} folders to scan" msgstr "{} carpetas recopiladas para explorar" @@ -178,23 +178,23 @@ msgstr "Nombre de archivo - Campos (sin orden)" msgid "Tags" msgstr "Etiquetas" -#: core\me\scanner.py:24 core\pe\scanner.py:21 core\se\scanner.py:17 +#: core\me\scanner.py:24 core\pe\scanner.py:22 core\se\scanner.py:17 msgid "Contents" msgstr "Contenido" -#: core\pe\matchblock.py:72 +#: core\pe\matchblock.py:66 msgid "Analyzed %d/%d pictures" msgstr "Analizadas %d/%d imágenes" -#: core\pe\matchblock.py:177 +#: core\pe\matchblock.py:183 msgid "Performed %d/%d chunk matches" msgstr "Realizado %d/%d trozos coincidentes" -#: core\pe\matchblock.py:185 +#: core\pe\matchblock.py:191 msgid "Preparing for matching" msgstr "Preparando para coincidencias" -#: core\pe\matchblock.py:234 +#: core\pe\matchblock.py:240 msgid "Verified %d/%d matches" msgstr "Verificadas %d/%d coincidencias" @@ -202,7 +202,7 @@ msgstr "Verificadas %d/%d coincidencias" msgid "Read EXIF of %d/%d pictures" msgstr "Leído EXIF de %d/%d imágenes" -#: core\pe\scanner.py:22 +#: core\pe\scanner.py:23 msgid "EXIF Timestamp" msgstr "Marca horaria EXIF" @@ -242,23 +242,19 @@ msgstr "El más nuevo" msgid "Oldest" msgstr "El más antiguo" -#: core\results.py:134 +#: core\results.py:135 msgid "%d / %d (%s / %s) duplicates marked." msgstr "%d / %d (%s / %s) duplicados marcados." -#: core\results.py:141 +#: core\results.py:142 msgid " filter: %s" msgstr "filtro: %s" -#: core\scanner.py:90 -msgid "Read size of %d/%d files" -msgstr "Tamaño de lectura de %d/%d ficheros" - -#: core\scanner.py:116 +#: core\scanner.py:114 msgid "Read metadata of %d/%d files" msgstr "Leyendo metadatos de %d/%d ficheros" -#: core\scanner.py:154 +#: core\scanner.py:152 msgid "Almost done! Fiddling with results..." msgstr "¡Casi termino! Jugando con los resultados..." diff --git a/locale/fr/LC_MESSAGES/core.po b/locale/fr/LC_MESSAGES/core.po index cfe1bbda..1ff37ae0 100644 --- a/locale/fr/LC_MESSAGES/core.po +++ b/locale/fr/LC_MESSAGES/core.po @@ -5,21 +5,21 @@ msgid "" msgstr "" "Last-Translator: Fuan , 2021\n" -"Language-Team: French (https://www.transifex.com/voltaicideas/teams/116153/fr/)\n" +"Language-Team: French (https://app.transifex.com/voltaicideas/teams/116153/fr/)\n" "Language: fr\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" -"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" -#: core\app.py:42 +#: core\app.py:44 msgid "There are no marked duplicates. Nothing has been done." msgstr "Aucun doublon marqué. Rien à faire." -#: core\app.py:43 +#: core\app.py:45 msgid "There are no selected duplicates. Nothing has been done." msgstr "Aucun doublon sélectionné. Rien à faire." -#: core\app.py:44 +#: core\app.py:46 msgid "" "You're about to open many files at once. Depending on what those files are " "opened with, doing so can create quite a mess. Continue?" @@ -27,23 +27,23 @@ msgstr "" "Beaucoup de fichiers seront ouverts en même temps. Cela peut gravement " "encombrer votre système. Continuer?" -#: core\app.py:71 +#: core\app.py:73 msgid "Scanning for duplicates" msgstr "Scan de doublons en cours" -#: core\app.py:72 +#: core\app.py:74 msgid "Loading" msgstr "Chargement en cours" -#: core\app.py:73 +#: core\app.py:75 msgid "Moving" msgstr "Déplacement en cours" -#: core\app.py:74 +#: core\app.py:76 msgid "Copying" msgstr "Copie en cours" -#: core\app.py:75 +#: core\app.py:77 msgid "Sending to Trash" msgstr "Envoi de fichiers à la corbeille" @@ -106,7 +106,7 @@ msgstr "Sélectionnez un dossier vers lequel déplacer les fichiers marqués." msgid "Select a destination for your exported CSV" msgstr "Choisissez une destination pour votre exportation CSV" -#: core\app.py:516 core\app.py:771 core\app.py:781 +#: core\app.py:516 core\app.py:777 core\app.py:787 msgid "Couldn't write to file: {}" msgstr "Impossible d'écrire le fichier: {}" @@ -115,23 +115,23 @@ msgid "You have no custom command set up. Set it up in your preferences." msgstr "" "Vous n'avez pas de commande personnalisée. Ajoutez-la dans vos préférences." -#: core\app.py:695 core\app.py:707 +#: core\app.py:701 core\app.py:713 msgid "You are about to remove %d files from results. Continue?" msgstr "%d fichiers seront retirés des résultats. Continuer?" -#: core\app.py:743 +#: core\app.py:749 msgid "{} duplicate groups were changed by the re-prioritization." msgstr "{} groupes de doublons ont été modifiés par la re-prioritisation." -#: core\app.py:790 +#: core\app.py:797 msgid "The selected directories contain no scannable file." msgstr "Les dossiers sélectionnés ne contiennent pas de fichiers valides." -#: core\app.py:803 +#: core\app.py:813 msgid "Collecting files to scan" msgstr "Collecte des fichiers à scanner" -#: core\app.py:850 +#: core\app.py:863 msgid "%s (%d discarded)" msgstr "%s (%d hors-groupe)" @@ -177,23 +177,23 @@ msgstr "Nom de fichier - Champs (sans ordre)" msgid "Tags" msgstr "Tags" -#: core\me\scanner.py:24 core\pe\scanner.py:21 core\se\scanner.py:17 +#: core\me\scanner.py:24 core\pe\scanner.py:22 core\se\scanner.py:17 msgid "Contents" msgstr "Contenu" -#: core\pe\matchblock.py:72 +#: core\pe\matchblock.py:66 msgid "Analyzed %d/%d pictures" msgstr "Analyzé %d/%d images" -#: core\pe\matchblock.py:177 +#: core\pe\matchblock.py:183 msgid "Performed %d/%d chunk matches" msgstr "%d/%d blocs d'images comparés" -#: core\pe\matchblock.py:185 +#: core\pe\matchblock.py:191 msgid "Preparing for matching" msgstr "Préparation pour la comparaison" -#: core\pe\matchblock.py:234 +#: core\pe\matchblock.py:240 msgid "Verified %d/%d matches" msgstr "Vérifié %d/%d paires" @@ -201,7 +201,7 @@ msgstr "Vérifié %d/%d paires" msgid "Read EXIF of %d/%d pictures" msgstr "Lu l'EXIF de %d/%d images" -#: core\pe\scanner.py:22 +#: core\pe\scanner.py:23 msgid "EXIF Timestamp" msgstr "Date EXIF" @@ -209,55 +209,51 @@ msgstr "Date EXIF" msgid "None" msgstr "Aucune" -#: core\prioritize.py:100 +#: core\prioritize.py:102 msgid "Ends with number" msgstr "Chiffres à la fin" -#: core\prioritize.py:101 +#: core\prioritize.py:103 msgid "Doesn't end with number" msgstr "Pas de chiffres à la finr" -#: core\prioritize.py:102 +#: core\prioritize.py:104 msgid "Longest" msgstr "Le plus long" -#: core\prioritize.py:103 +#: core\prioritize.py:105 msgid "Shortest" msgstr "Le plus court" -#: core\prioritize.py:140 +#: core\prioritize.py:142 msgid "Highest" msgstr "Plus grand" -#: core\prioritize.py:140 +#: core\prioritize.py:142 msgid "Lowest" msgstr "Moins grand" -#: core\prioritize.py:169 +#: core\prioritize.py:171 msgid "Newest" msgstr "Plus récent" -#: core\prioritize.py:169 +#: core\prioritize.py:171 msgid "Oldest" msgstr "Moins récent" -#: core\results.py:134 +#: core\results.py:135 msgid "%d / %d (%s / %s) duplicates marked." msgstr "%d / %d (%s / %s) doublons marqués." -#: core\results.py:141 +#: core\results.py:142 msgid " filter: %s" msgstr " filtre: %s" -#: core\scanner.py:90 -msgid "Read size of %d/%d files" -msgstr "Lu la taille de %d/%d fichiers" - -#: core\scanner.py:116 +#: core\scanner.py:114 msgid "Read metadata of %d/%d files" msgstr "Lu les métadonnées de %d/%d fichiers" -#: core\scanner.py:154 +#: core\scanner.py:152 msgid "Almost done! Fiddling with results..." msgstr "Bientôt terminé! Bidouille des résultats..." diff --git a/locale/fr/LC_MESSAGES/ui.po b/locale/fr/LC_MESSAGES/ui.po index 89770d06..3b8c231c 100644 --- a/locale/fr/LC_MESSAGES/ui.po +++ b/locale/fr/LC_MESSAGES/ui.po @@ -1,10 +1,10 @@ # Translators: -# Andrew Senetar , 2022 # Fuan , 2022 +# Andrew Senetar , 2023 # msgid "" msgstr "" -"Last-Translator: Fuan , 2022\n" +"Last-Translator: Andrew Senetar , 2023\n" "Language-Team: French (https://app.transifex.com/voltaicideas/teams/116153/fr/)\n" "Language: fr\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -1165,3 +1165,7 @@ msgid "" "
  • 2. Regular expressions with at least one path separator in them will be compared to the full path to the file.

  • Example: if you want to filter out .PNG files from the \"My Pictures\" directory only:
    .*My\\sPictures\\\\.*\\.png

    You can test the regular expression with the \"test string\" button after pasting a fake path in the test field:
    C:\\\\User\\My Pictures\\test.png

    \n" "Matching regular expressions will be highlighted.
    If there is at least one highlight, the path or filename tested will be ignored during scans.

    Directories and files starting with a period '.' are filtered out by default.

    " msgstr "" + +#: qt\pe\preferences_dialog.py:24 +msgid "Match pictures of different rotations" +msgstr "" diff --git a/locale/hy/LC_MESSAGES/core.po b/locale/hy/LC_MESSAGES/core.po index 60f054c0..757578fd 100755 --- a/locale/hy/LC_MESSAGES/core.po +++ b/locale/hy/LC_MESSAGES/core.po @@ -5,21 +5,21 @@ msgid "" msgstr "" "Last-Translator: Fuan , 2021\n" -"Language-Team: Armenian (https://www.transifex.com/voltaicideas/teams/116153/hy/)\n" +"Language-Team: Armenian (https://app.transifex.com/voltaicideas/teams/116153/hy/)\n" "Language: hy\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: core\app.py:42 +#: core\app.py:44 msgid "There are no marked duplicates. Nothing has been done." msgstr "Նշված կրկնօրինակներ չկան: Ոչինչ չի արվել." -#: core\app.py:43 +#: core\app.py:45 msgid "There are no selected duplicates. Nothing has been done." msgstr "Ընտրված կրկնօրինակներ չկան: Ոչինչ չի արվել." -#: core\app.py:44 +#: core\app.py:46 msgid "" "You're about to open many files at once. Depending on what those files are " "opened with, doing so can create quite a mess. Continue?" @@ -28,23 +28,23 @@ msgstr "" "են բացվում այդ ֆայլերը, դա անելը կարող է բավականին խառնաշփոթ ստեղծել: " "Շարունակել?" -#: core\app.py:71 +#: core\app.py:73 msgid "Scanning for duplicates" msgstr "Ստուգվում են կրկնօրինակները" -#: core\app.py:72 +#: core\app.py:74 msgid "Loading" msgstr "Բացվում է" -#: core\app.py:73 +#: core\app.py:75 msgid "Moving" msgstr "Տեղափոխվում է" -#: core\app.py:74 +#: core\app.py:76 msgid "Copying" msgstr "Պատճենվում է" -#: core\app.py:75 +#: core\app.py:77 msgid "Sending to Trash" msgstr "Ուղարկվում է Աղբարկղ" @@ -108,7 +108,7 @@ msgstr "" msgid "Select a destination for your exported CSV" msgstr "Ընտրեք նպատակակետ ձեր արտահանված CSV- ի համար" -#: core\app.py:516 core\app.py:771 core\app.py:781 +#: core\app.py:516 core\app.py:777 core\app.py:787 msgid "Couldn't write to file: {}" msgstr "Չէր կարող գրել է ֆայլը: {}" @@ -116,23 +116,23 @@ msgstr "Չէր կարող գրել է ֆայլը: {}" msgid "You have no custom command set up. Set it up in your preferences." msgstr "Դուք չեք կատարել Հրամանի ընտրություն: Կատարեք այն կարգավորումներում:" -#: core\app.py:695 core\app.py:707 +#: core\app.py:701 core\app.py:713 msgid "You are about to remove %d files from results. Continue?" msgstr "Դուք պատրաստվում եք ջնջելու %d ֆայլեր: Շարունակե՞լ:" -#: core\app.py:743 +#: core\app.py:749 msgid "{} duplicate groups were changed by the re-prioritization." msgstr "{} կրկնօրինակ խմբերը փոխվել են առաջնահերթության կարգով:" -#: core\app.py:790 +#: core\app.py:797 msgid "The selected directories contain no scannable file." msgstr "Ընտրված թղթապանակները պարունակում են չստուգվող ֆայլ:" -#: core\app.py:803 +#: core\app.py:813 msgid "Collecting files to scan" msgstr "Հավաքվում են ֆայլեր՝ ստուգելու համար" -#: core\app.py:850 +#: core\app.py:863 msgid "%s (%d discarded)" msgstr "%s (%d անպիտան)" @@ -176,23 +176,23 @@ msgstr "Ֆայլի անուն - դաշտեր (պատվեր չկա)" msgid "Tags" msgstr "Tags" -#: core\me\scanner.py:24 core\pe\scanner.py:21 core\se\scanner.py:17 +#: core\me\scanner.py:24 core\pe\scanner.py:22 core\se\scanner.py:17 msgid "Contents" msgstr "Բովանդակություն" -#: core\pe\matchblock.py:72 +#: core\pe\matchblock.py:66 msgid "Analyzed %d/%d pictures" msgstr "Ստուգվում է %d/%d նկարները" -#: core\pe\matchblock.py:177 +#: core\pe\matchblock.py:183 msgid "Performed %d/%d chunk matches" msgstr "Կատարվում է %d/%d տվյալի համընկնում" -#: core\pe\matchblock.py:185 +#: core\pe\matchblock.py:191 msgid "Preparing for matching" msgstr "Նախապատրաստեցվում է համընկնումը" -#: core\pe\matchblock.py:234 +#: core\pe\matchblock.py:240 msgid "Verified %d/%d matches" msgstr "Ստուգում է %d/%d համընկնումները" @@ -200,7 +200,7 @@ msgstr "Ստուգում է %d/%d համընկնումները" msgid "Read EXIF of %d/%d pictures" msgstr "Կարդալ EXIF-ը d/%d նկարներից" -#: core\pe\scanner.py:22 +#: core\pe\scanner.py:23 msgid "EXIF Timestamp" msgstr "EXIF Timestamp" @@ -208,55 +208,51 @@ msgstr "EXIF Timestamp" msgid "None" msgstr "Ոչինչ" -#: core\prioritize.py:100 +#: core\prioritize.py:102 msgid "Ends with number" msgstr "Ավարտվում է թվով" -#: core\prioritize.py:101 +#: core\prioritize.py:103 msgid "Doesn't end with number" msgstr "Չի ավարտվում է թվով" -#: core\prioritize.py:102 +#: core\prioritize.py:104 msgid "Longest" msgstr "Ամենաերկար" -#: core\prioritize.py:103 +#: core\prioritize.py:105 msgid "Shortest" msgstr "Ամենակարճը" -#: core\prioritize.py:140 +#: core\prioritize.py:142 msgid "Highest" msgstr "Ամենաբարձրը" -#: core\prioritize.py:140 +#: core\prioritize.py:142 msgid "Lowest" msgstr "Ամենացածրը" -#: core\prioritize.py:169 +#: core\prioritize.py:171 msgid "Newest" msgstr "Նորագույնը" -#: core\prioritize.py:169 +#: core\prioritize.py:171 msgid "Oldest" msgstr "Ամենահինը" -#: core\results.py:134 +#: core\results.py:135 msgid "%d / %d (%s / %s) duplicates marked." msgstr "%d / %d (%s / %s) նշված կրկնօրինակներ:" -#: core\results.py:141 +#: core\results.py:142 msgid " filter: %s" msgstr "ֆիլտր. %s" -#: core\scanner.py:90 -msgid "Read size of %d/%d files" -msgstr "Կարդալ %d/%d ֆայլերի չափը" - -#: core\scanner.py:116 +#: core\scanner.py:114 msgid "Read metadata of %d/%d files" msgstr "Կարդալ %d/%d ֆայլերի մետատվյալները" -#: core\scanner.py:154 +#: core\scanner.py:152 msgid "Almost done! Fiddling with results..." msgstr "Գրեթե արված է! Արդյունքների կազմակերպում..." diff --git a/locale/hy/LC_MESSAGES/ui.po b/locale/hy/LC_MESSAGES/ui.po index 6f2942ad..d3e250e7 100755 --- a/locale/hy/LC_MESSAGES/ui.po +++ b/locale/hy/LC_MESSAGES/ui.po @@ -1,10 +1,10 @@ # Translators: -# Andrew Senetar , 2022 # Fuan , 2022 +# Andrew Senetar , 2023 # msgid "" msgstr "" -"Last-Translator: Fuan , 2022\n" +"Last-Translator: Andrew Senetar , 2023\n" "Language-Team: Armenian (https://app.transifex.com/voltaicideas/teams/116153/hy/)\n" "Language: hy\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -1145,3 +1145,7 @@ msgid "" "
  • 2. Regular expressions with at least one path separator in them will be compared to the full path to the file.

  • Example: if you want to filter out .PNG files from the \"My Pictures\" directory only:
    .*My\\sPictures\\\\.*\\.png

    You can test the regular expression with the \"test string\" button after pasting a fake path in the test field:
    C:\\\\User\\My Pictures\\test.png

    \n" "Matching regular expressions will be highlighted.
    If there is at least one highlight, the path or filename tested will be ignored during scans.

    Directories and files starting with a period '.' are filtered out by default.

    " msgstr "" + +#: qt\pe\preferences_dialog.py:24 +msgid "Match pictures of different rotations" +msgstr "" diff --git a/locale/it/LC_MESSAGES/core.po b/locale/it/LC_MESSAGES/core.po index 0219cd8d..8c593904 100644 --- a/locale/it/LC_MESSAGES/core.po +++ b/locale/it/LC_MESSAGES/core.po @@ -49,7 +49,7 @@ msgstr "Copia in corso" msgid "Sending to Trash" msgstr "Spostamento nel cestino" -#: core\app.py:293 +#: core\app.py:289 msgid "" "A previous action is still hanging in there. You can't start a new one yet. " "Wait a few seconds, then try again." @@ -57,39 +57,39 @@ msgstr "" "Un'azione precedente è ancora in corso. Non puoi cominciarne una nuova. " "Aspetta qualche secondo e quindi riprova." -#: core\app.py:304 +#: core\app.py:300 msgid "No duplicates found." msgstr "Non sono stati trovati dei duplicati." -#: core\app.py:319 +#: core\app.py:315 msgid "All marked files were copied successfully." msgstr "Tutti i file marcati sono stati copiati correttamente." -#: core\app.py:321 +#: core\app.py:317 msgid "All marked files were moved successfully." msgstr "Tutti i file marcati sono stati spostati correttamente." -#: core\app.py:323 +#: core\app.py:319 msgid "All marked files were deleted successfully." msgstr "Tutti i file marcati sono stati cancellati correttamente." -#: core\app.py:325 +#: core\app.py:321 msgid "All marked files were successfully sent to Trash." msgstr "Tutti i file marcati sono stati spostati nel cestino." -#: core\app.py:330 +#: core\app.py:326 msgid "Could not load file: {}" msgstr "Impossibile caricare il file: {}" -#: core\app.py:386 +#: core\app.py:382 msgid "'{}' already is in the list." msgstr "'{}' è già nella lista." -#: core\app.py:388 +#: core\app.py:384 msgid "'{}' does not exist." msgstr "'{}' non esiste." -#: core\app.py:396 +#: core\app.py:392 msgid "" "All selected %d matches are going to be ignored in all subsequent scans. " "Continue?" @@ -97,54 +97,54 @@ msgstr "" "Tutti i %d elementi che coincidono verranno ignorati in tutte le scansioni " "successive. Continuare?" -#: core\app.py:473 +#: core\app.py:469 msgid "Select a directory to copy marked files to" msgstr "Seleziona una directory in cui desideri copiare i file contrassegnati" -#: core\app.py:475 +#: core\app.py:471 msgid "Select a directory to move marked files to" msgstr "" "Seleziona una directory in cui desideri spostare i file contrassegnati" -#: core\app.py:514 +#: core\app.py:510 msgid "Select a destination for your exported CSV" msgstr "Seleziona una destinazione per il file CSV" -#: core\app.py:520 core\app.py:781 core\app.py:791 +#: core\app.py:516 core\app.py:777 core\app.py:787 msgid "Couldn't write to file: {}" msgstr "Impossibile modificare il file: {}" -#: core\app.py:543 +#: core\app.py:539 msgid "You have no custom command set up. Set it up in your preferences." msgstr "" "Non hai impostato nessun comando personalizzato. Impostalo nelle tue " "preferenze." -#: core\app.py:705 core\app.py:717 +#: core\app.py:701 core\app.py:713 msgid "You are about to remove %d files from results. Continue?" msgstr "Stai per rimuovere %d file dai risultati. Continuare?" -#: core\app.py:753 +#: core\app.py:749 msgid "{} duplicate groups were changed by the re-prioritization." msgstr "{} gruppi duplicati sono stati cambiati dalla nuova priorirità" -#: core\app.py:801 +#: core\app.py:797 msgid "The selected directories contain no scannable file." msgstr "Le cartelle selezionate non contengono file da scansionare." -#: core\app.py:817 +#: core\app.py:813 msgid "Collecting files to scan" msgstr "Raccolta file da scansionare" -#: core\app.py:867 +#: core\app.py:863 msgid "%s (%d discarded)" msgstr "%s (%d scartati)" -#: core\directories.py:190 +#: core\directories.py:191 msgid "Collected {} files to scan" msgstr "Raccolti {} file da scansionare" -#: core\directories.py:206 +#: core\directories.py:207 msgid "Collected {} folders to scan" msgstr "Raccolte {} cartelle da scansionare" @@ -182,23 +182,23 @@ msgstr "Nome file - Campi (Nessun Ordine)" msgid "Tags" msgstr "Tag" -#: core\me\scanner.py:24 core\pe\scanner.py:21 core\se\scanner.py:17 +#: core\me\scanner.py:24 core\pe\scanner.py:22 core\se\scanner.py:17 msgid "Contents" msgstr "Contenuti" -#: core\pe\matchblock.py:72 +#: core\pe\matchblock.py:66 msgid "Analyzed %d/%d pictures" msgstr "Analizzate %d/%d immagini" -#: core\pe\matchblock.py:177 +#: core\pe\matchblock.py:183 msgid "Performed %d/%d chunk matches" msgstr "Effettuate %d/%d comparazioni sui sottogruppi di immagini" -#: core\pe\matchblock.py:185 +#: core\pe\matchblock.py:191 msgid "Preparing for matching" msgstr "Preparazione per la comparazione" -#: core\pe\matchblock.py:234 +#: core\pe\matchblock.py:240 msgid "Verified %d/%d matches" msgstr "Verificate %d/%d somiglianze" @@ -206,7 +206,7 @@ msgstr "Verificate %d/%d somiglianze" msgid "Read EXIF of %d/%d pictures" msgstr "Leggi dati EXIF da %d/%d immagini" -#: core\pe\scanner.py:22 +#: core\pe\scanner.py:23 msgid "EXIF Timestamp" msgstr "Timestamp EXIF" @@ -246,23 +246,19 @@ msgstr "Il più nuovo" msgid "Oldest" msgstr "Il più vecchio" -#: core\results.py:134 +#: core\results.py:135 msgid "%d / %d (%s / %s) duplicates marked." msgstr "%d / %d (%s / %s) duplicati marcati." -#: core\results.py:141 +#: core\results.py:142 msgid " filter: %s" msgstr " filtro: %s" -#: core\scanner.py:90 -msgid "Read size of %d/%d files" -msgstr "Lettura dimensione di %d/%d file" - -#: core\scanner.py:116 +#: core\scanner.py:114 msgid "Read metadata of %d/%d files" msgstr "Lettura metadata di %d/%d files" -#: core\scanner.py:154 +#: core\scanner.py:152 msgid "Almost done! Fiddling with results..." msgstr "Quasi finito! Sto organizzando i risultati..." diff --git a/locale/it/LC_MESSAGES/ui.po b/locale/it/LC_MESSAGES/ui.po index 60320420..e8504a8f 100644 --- a/locale/it/LC_MESSAGES/ui.po +++ b/locale/it/LC_MESSAGES/ui.po @@ -1,12 +1,12 @@ # Translators: -# Andrew Senetar , 2022 # Emanuele, 2022 # Fuan , 2022 -# Giovanni, 2022 +# Giovanni Donisi, 2022 +# Andrew Senetar , 2023 # msgid "" msgstr "" -"Last-Translator: Giovanni, 2022\n" +"Last-Translator: Andrew Senetar , 2023\n" "Language-Team: Italian (https://app.transifex.com/voltaicideas/teams/116153/it/)\n" "Language: it\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -1181,3 +1181,7 @@ msgid "" "
  • 2. Regular expressions with at least one path separator in them will be compared to the full path to the file.

  • Example: if you want to filter out .PNG files from the \"My Pictures\" directory only:
    .*My\\sPictures\\\\.*\\.png

    You can test the regular expression with the \"test string\" button after pasting a fake path in the test field:
    C:\\\\User\\My Pictures\\test.png

    \n" "Matching regular expressions will be highlighted.
    If there is at least one highlight, the path or filename tested will be ignored during scans.

    Directories and files starting with a period '.' are filtered out by default.

    " msgstr "" + +#: qt\pe\preferences_dialog.py:24 +msgid "Match pictures of different rotations" +msgstr "" diff --git a/locale/ja/LC_MESSAGES/core.po b/locale/ja/LC_MESSAGES/core.po index 77878001..553647cd 100644 --- a/locale/ja/LC_MESSAGES/core.po +++ b/locale/ja/LC_MESSAGES/core.po @@ -5,7 +5,7 @@ msgid "" msgstr "" "Last-Translator: Fuan , 2022\n" -"Language-Team: Japanese (https://www.transifex.com/voltaicideas/teams/116153/ja/)\n" +"Language-Team: Japanese (https://app.transifex.com/voltaicideas/teams/116153/ja/)\n" "Language: ja\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" @@ -45,95 +45,95 @@ msgstr "コピー中" msgid "Sending to Trash" msgstr "ごみ箱に送信します" -#: core\app.py:291 +#: core\app.py:289 msgid "" "A previous action is still hanging in there. You can't start a new one yet. " "Wait a few seconds, then try again." msgstr "前のアクションはまだそこにぶら下がっています。 まだ新しいものを始めることはできません。 数秒待ってから、再試行してください。" -#: core\app.py:302 +#: core\app.py:300 msgid "No duplicates found." msgstr "重複は見つかりませんでした。" -#: core\app.py:317 +#: core\app.py:315 msgid "All marked files were copied successfully." msgstr "チェックを入れたファイルをすべてコピーしました。" -#: core\app.py:319 +#: core\app.py:317 msgid "All marked files were moved successfully." msgstr "チェックを入れたファイルをすべて移動しました。" -#: core\app.py:321 +#: core\app.py:319 msgid "All marked files were deleted successfully." msgstr "チェックを入れたファイルをすべて削除しました。" -#: core\app.py:323 +#: core\app.py:321 msgid "All marked files were successfully sent to Trash." msgstr "チェックを入れたファイルをすべてごみ箱に移動しました。" -#: core\app.py:328 +#: core\app.py:326 msgid "Could not load file: {}" msgstr "ファイルを読み込めませんでした:{}" -#: core\app.py:384 +#: core\app.py:382 msgid "'{}' already is in the list." msgstr "「{}」既にリストに含まれています。" -#: core\app.py:386 +#: core\app.py:384 msgid "'{}' does not exist." msgstr "'{}' 存在しません。" -#: core\app.py:394 +#: core\app.py:392 msgid "" "All selected %d matches are going to be ignored in all subsequent scans. " "Continue?" msgstr "選択した%d個の一致は、以降のすべてのスキャンで無視されます。 継続する?" -#: core\app.py:471 +#: core\app.py:469 msgid "Select a directory to copy marked files to" msgstr "マークされたファイルをコピーするディレクトリを選択してください" -#: core\app.py:473 +#: core\app.py:471 msgid "Select a directory to move marked files to" msgstr "マークされたファイルを移動するディレクトリを選択してください" -#: core\app.py:512 +#: core\app.py:510 msgid "Select a destination for your exported CSV" msgstr "エクスポートしたCSVの宛先を選択します。" -#: core\app.py:518 core\app.py:773 core\app.py:783 +#: core\app.py:516 core\app.py:777 core\app.py:787 msgid "Couldn't write to file: {}" msgstr "ファイルに書き込めませんでした:{}" -#: core\app.py:541 +#: core\app.py:539 msgid "You have no custom command set up. Set it up in your preferences." msgstr "カスタムコマンドは設定されていません。 お好みで設定してください。" -#: core\app.py:697 core\app.py:709 +#: core\app.py:701 core\app.py:713 msgid "You are about to remove %d files from results. Continue?" msgstr "結果から%d個のファイルを削除しようとしています。 継続する?" -#: core\app.py:745 +#: core\app.py:749 msgid "{} duplicate groups were changed by the re-prioritization." msgstr "{}重複するグループは、再優先順位付けによって変更されました。" -#: core\app.py:792 +#: core\app.py:797 msgid "The selected directories contain no scannable file." msgstr "選択したディレクトリにはスキャン可能なファイルが含まれていません。" -#: core\app.py:808 +#: core\app.py:813 msgid "Collecting files to scan" msgstr "スキャンするファイルを収集しています" -#: core\app.py:858 +#: core\app.py:863 msgid "%s (%d discarded)" msgstr "%s (%d 廃棄)" -#: core\directories.py:190 +#: core\directories.py:191 msgid "Collected {} files to scan" msgstr "" -#: core\directories.py:206 +#: core\directories.py:207 msgid "Collected {} folders to scan" msgstr "" @@ -169,23 +169,23 @@ msgstr "ファイル名 - フィールド(順序なし)" msgid "Tags" msgstr "タグ" -#: core\me\scanner.py:24 core\pe\scanner.py:21 core\se\scanner.py:17 +#: core\me\scanner.py:24 core\pe\scanner.py:22 core\se\scanner.py:17 msgid "Contents" msgstr "内容" -#: core\pe\matchblock.py:72 +#: core\pe\matchblock.py:66 msgid "Analyzed %d/%d pictures" msgstr "%d/%d 枚の写真を分析しました" -#: core\pe\matchblock.py:177 +#: core\pe\matchblock.py:183 msgid "Performed %d/%d chunk matches" msgstr "チャンクマッチを%d/%d回実行しました" -#: core\pe\matchblock.py:185 +#: core\pe\matchblock.py:191 msgid "Preparing for matching" msgstr "マッチングの準備" -#: core\pe\matchblock.py:234 +#: core\pe\matchblock.py:240 msgid "Verified %d/%d matches" msgstr "%d/%d件の一致を確認" @@ -193,7 +193,7 @@ msgstr "%d/%d件の一致を確認" msgid "Read EXIF of %d/%d pictures" msgstr "%d/%d枚の写真のEXIFを読みました" -#: core\pe\scanner.py:22 +#: core\pe\scanner.py:23 msgid "EXIF Timestamp" msgstr "EXIFタイムスタンプ" @@ -233,23 +233,19 @@ msgstr "最新" msgid "Oldest" msgstr "最古" -#: core\results.py:134 +#: core\results.py:135 msgid "%d / %d (%s / %s) duplicates marked." msgstr "%d / %d (%s / %s)マークされた重複。" -#: core\results.py:141 +#: core\results.py:142 msgid " filter: %s" msgstr "フィルタ: %s" -#: core\scanner.py:90 -msgid "Read size of %d/%d files" -msgstr "%d/%dファイルのサイズを読み取った" - -#: core\scanner.py:116 +#: core\scanner.py:114 msgid "Read metadata of %d/%d files" msgstr "%d/%dファイルのメタデータを読み取った" -#: core\scanner.py:154 +#: core\scanner.py:152 msgid "Almost done! Fiddling with results..." msgstr "ほぼ完了しました! 結果をいじっています..." diff --git a/locale/ja/LC_MESSAGES/ui.po b/locale/ja/LC_MESSAGES/ui.po index 11f47ca8..eb156960 100644 --- a/locale/ja/LC_MESSAGES/ui.po +++ b/locale/ja/LC_MESSAGES/ui.po @@ -1,10 +1,10 @@ # Translators: -# Andrew Senetar , 2022 # Fuan , 2022 +# Andrew Senetar , 2023 # msgid "" msgstr "" -"Last-Translator: Fuan , 2022\n" +"Last-Translator: Andrew Senetar , 2023\n" "Language-Team: Japanese (https://app.transifex.com/voltaicideas/teams/116153/ja/)\n" "Language: ja\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -1149,3 +1149,7 @@ msgid "" "
  • 2. Regular expressions with at least one path separator in them will be compared to the full path to the file.

  • Example: if you want to filter out .PNG files from the \"My Pictures\" directory only:
    .*My\\sPictures\\\\.*\\.png

    You can test the regular expression with the \"test string\" button after pasting a fake path in the test field:
    C:\\\\User\\My Pictures\\test.png

    \n" "Matching regular expressions will be highlighted.
    If there is at least one highlight, the path or filename tested will be ignored during scans.

    Directories and files starting with a period '.' are filtered out by default.

    " msgstr "" + +#: qt\pe\preferences_dialog.py:24 +msgid "Match pictures of different rotations" +msgstr "" diff --git a/locale/ko/LC_MESSAGES/columns.po b/locale/ko/LC_MESSAGES/columns.po index ef8677cf..a6b71a33 100644 --- a/locale/ko/LC_MESSAGES/columns.po +++ b/locale/ko/LC_MESSAGES/columns.po @@ -1,10 +1,10 @@ # Translators: -# Andrew Senetar , 2021 # Sangdon Lim, 2022 +# Andrew Senetar , 2023 # msgid "" msgstr "" -"Last-Translator: Sangdon Lim, 2022\n" +"Last-Translator: Andrew Senetar , 2023\n" "Language-Team: Korean (https://app.transifex.com/voltaicideas/teams/116153/ko/)\n" "Language: ko\n" "Content-Type: text/plain; charset=UTF-8\n" diff --git a/locale/ko/LC_MESSAGES/core.po b/locale/ko/LC_MESSAGES/core.po index fd10b9b1..ab86d457 100644 --- a/locale/ko/LC_MESSAGES/core.po +++ b/locale/ko/LC_MESSAGES/core.po @@ -1,11 +1,11 @@ # Translators: -# Andrew Senetar , 2021 # Fuan , 2021 # Sangdon Lim, 2022 +# Andrew Senetar , 2023 # msgid "" msgstr "" -"Last-Translator: Sangdon Lim, 2022\n" +"Last-Translator: Andrew Senetar , 2023\n" "Language-Team: Korean (https://app.transifex.com/voltaicideas/teams/116153/ko/)\n" "Language: ko\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -46,95 +46,95 @@ msgstr "복사중" msgid "Sending to Trash" msgstr "휴지통으로 보내기" -#: core\app.py:293 +#: core\app.py:289 msgid "" "A previous action is still hanging in there. You can't start a new one yet. " "Wait a few seconds, then try again." msgstr "이전 작업이 아직 진행 중이어서 새 작업을 시작할 수 없습니다. 몇 초 후에 다시 시도해 보세요." -#: core\app.py:304 +#: core\app.py:300 msgid "No duplicates found." msgstr "중복 파일이 없습니다." -#: core\app.py:319 +#: core\app.py:315 msgid "All marked files were copied successfully." msgstr "마크된 모든 파일이 성공적으로 복사되었습니다." -#: core\app.py:321 +#: core\app.py:317 msgid "All marked files were moved successfully." msgstr "마크된 모든 파일이 성공적으로 이동되었습니다." -#: core\app.py:323 +#: core\app.py:319 msgid "All marked files were deleted successfully." msgstr "마크된 모든 파일이 성공적으로 삭제되었습니다." -#: core\app.py:325 +#: core\app.py:321 msgid "All marked files were successfully sent to Trash." msgstr "마크된 모든 파일을 휴지통으로 보냈습니다." -#: core\app.py:330 +#: core\app.py:326 msgid "Could not load file: {}" msgstr "파일을 불러올 수 없습니다: {}" -#: core\app.py:386 +#: core\app.py:382 msgid "'{}' already is in the list." msgstr "'{}' 는 이미 목록에 있습니다." -#: core\app.py:388 +#: core\app.py:384 msgid "'{}' does not exist." msgstr "'{}' 가 존재하지 않습니다." -#: core\app.py:396 +#: core\app.py:392 msgid "" "All selected %d matches are going to be ignored in all subsequent scans. " "Continue?" msgstr "선택한 %d개 항목을 검색에서 무시합니다. 진행하시겠습니까?" -#: core\app.py:473 +#: core\app.py:469 msgid "Select a directory to copy marked files to" msgstr "마크하신 파일을 복사할 경로를 선택하세요:" -#: core\app.py:475 +#: core\app.py:471 msgid "Select a directory to move marked files to" msgstr "마크하신 파일을 이동할 경로를 선택하세요:" -#: core\app.py:514 +#: core\app.py:510 msgid "Select a destination for your exported CSV" msgstr "CSV 파일의 저장 경로를 지정해주세요" -#: core\app.py:520 core\app.py:781 core\app.py:791 +#: core\app.py:516 core\app.py:777 core\app.py:787 msgid "Couldn't write to file: {}" msgstr "파일에 쓸 수 없습니다 : {}" -#: core\app.py:543 +#: core\app.py:539 msgid "You have no custom command set up. Set it up in your preferences." msgstr "사용자 지정 명령을 설정하지 않았습니다. 기본 설정에서 설정하십시오." -#: core\app.py:705 core\app.py:717 +#: core\app.py:701 core\app.py:713 msgid "You are about to remove %d files from results. Continue?" msgstr "결과에서 %d 개의 파일을 제거하려고합니다. 실행하시겠습니까?" -#: core\app.py:753 +#: core\app.py:749 msgid "{} duplicate groups were changed by the re-prioritization." msgstr "{} 개의 중복 그룹이 우선 순위 재 지정으로 변경되었습니다." -#: core\app.py:801 +#: core\app.py:797 msgid "The selected directories contain no scannable file." msgstr "선택한 경로에 스캔 가능한 파일이 없습니다." -#: core\app.py:817 +#: core\app.py:813 msgid "Collecting files to scan" msgstr "스캔 가능 파일 수집중" -#: core\app.py:867 +#: core\app.py:863 msgid "%s (%d discarded)" msgstr "%s (%d 폐기)" -#: core\directories.py:190 +#: core\directories.py:191 msgid "Collected {} files to scan" msgstr "파일 목록 생성 중: {}개 파일" -#: core\directories.py:206 +#: core\directories.py:207 msgid "Collected {} folders to scan" msgstr "폴더 목록 생성 중: {}개 폴더" @@ -170,23 +170,23 @@ msgstr "파일 이름 - 필드 (순서 없음)" msgid "Tags" msgstr "태그" -#: core\me\scanner.py:24 core\pe\scanner.py:21 core\se\scanner.py:17 +#: core\me\scanner.py:24 core\pe\scanner.py:22 core\se\scanner.py:17 msgid "Contents" msgstr "내용" -#: core\pe\matchblock.py:72 +#: core\pe\matchblock.py:66 msgid "Analyzed %d/%d pictures" msgstr "사진 %d/%d 개 분석됨" -#: core\pe\matchblock.py:177 +#: core\pe\matchblock.py:183 msgid "Performed %d/%d chunk matches" msgstr "%d/%d 청크 매치 수행" -#: core\pe\matchblock.py:185 +#: core\pe\matchblock.py:191 msgid "Preparing for matching" msgstr "매칭 준비" -#: core\pe\matchblock.py:234 +#: core\pe\matchblock.py:240 msgid "Verified %d/%d matches" msgstr "%d/%d 일치 확인" @@ -194,7 +194,7 @@ msgstr "%d/%d 일치 확인" msgid "Read EXIF of %d/%d pictures" msgstr "사진 EXIF 읽는 중: %d/%d" -#: core\pe\scanner.py:22 +#: core\pe\scanner.py:23 msgid "EXIF Timestamp" msgstr "EXIF 타임 스탬프" @@ -234,23 +234,19 @@ msgstr "최신" msgid "Oldest" msgstr "가장 오래된" -#: core\results.py:134 +#: core\results.py:135 msgid "%d / %d (%s / %s) duplicates marked." msgstr "%d / %d (%s / %s) 개의 중복 파일을 마크했습니다." -#: core\results.py:141 +#: core\results.py:142 msgid " filter: %s" msgstr "필터: %s" -#: core\scanner.py:90 -msgid "Read size of %d/%d files" -msgstr "파일 크기 읽는 중: %d/%d" - -#: core\scanner.py:116 +#: core\scanner.py:114 msgid "Read metadata of %d/%d files" msgstr "파일 메타데이터 읽는 중: %d/%d" -#: core\scanner.py:154 +#: core\scanner.py:152 msgid "Almost done! Fiddling with results..." msgstr "거의 완료되었습니다! 결과를 취합하고 있습니다." diff --git a/locale/ko/LC_MESSAGES/ui.po b/locale/ko/LC_MESSAGES/ui.po index c1f06471..62c4b35f 100644 --- a/locale/ko/LC_MESSAGES/ui.po +++ b/locale/ko/LC_MESSAGES/ui.po @@ -1,11 +1,11 @@ # Translators: -# Andrew Senetar , 2022 # Fuan , 2022 # Sangdon Lim, 2022 +# Andrew Senetar , 2023 # msgid "" msgstr "" -"Last-Translator: Sangdon Lim, 2022\n" +"Last-Translator: Andrew Senetar , 2023\n" "Language-Team: Korean (https://app.transifex.com/voltaicideas/teams/116153/ko/)\n" "Language: ko\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -365,8 +365,8 @@ msgid "" " the best to these criteria to their respective group's reference position. " "Read the help file for more information." msgstr "" -"오른쪽 박스에 기준을 추가하고 확인을 눌러 가장 기준에 해당되는 복제를 해당 그룹의 참조 위치로 보냅니다." -" 상세한 정보는 도움말에 있습니다" +"오른쪽 박스에 기준을 추가하고 확인을 눌러 가장 기준에 해당되는 복제를 해당 그룹의 참조 위치로 보냅니다. 상세한 정보는 도움말에 " +"있습니다" #: qt/problem_dialog.py:33 cocoa/en.lproj/Localizable.strings:0 msgid "Problems!" @@ -378,8 +378,8 @@ msgid "" "these problems are described in the table below. Those files were not " "removed from your results." msgstr "" -"일부 (또는 전체) 파일을 처리하는 데 문제가 있었습니다. 문제의 원인은 아래 표에 표시 되있습니다. 문제되는 파일들은 결과에서 " -"제거되지 않았습니다." +"일부 (또는 전체) 파일을 처리하는 데 문제가 있었습니다. 문제의 원인은 아래 표에 표시 되있습니다. 문제되는 파일들은 결과에서 제거되지" +" 않았습니다." #: qt/problem_dialog.py:56 msgid "Reveal Selected" @@ -1150,3 +1150,7 @@ msgid "" "
  • 2. Regular expressions with at least one path separator in them will be compared to the full path to the file.

  • Example: if you want to filter out .PNG files from the \"My Pictures\" directory only:
    .*My\\sPictures\\\\.*\\.png

    You can test the regular expression with the \"test string\" button after pasting a fake path in the test field:
    C:\\\\User\\My Pictures\\test.png

    \n" "Matching regular expressions will be highlighted.
    If there is at least one highlight, the path or filename tested will be ignored during scans.

    Directories and files starting with a period '.' are filtered out by default.

    " msgstr "" + +#: qt\pe\preferences_dialog.py:24 +msgid "Match pictures of different rotations" +msgstr "" diff --git a/locale/ms/LC_MESSAGES/core.po b/locale/ms/LC_MESSAGES/core.po index d8774751..68c5fa8d 100644 --- a/locale/ms/LC_MESSAGES/core.po +++ b/locale/ms/LC_MESSAGES/core.po @@ -4,21 +4,21 @@ msgid "" msgstr "" "Last-Translator: Yaya - Nurul Azeera Hidayah @ Muhammad Nur Hidayat Yasuyoshi (MNH48) , 2021\n" -"Language-Team: Malay (https://www.transifex.com/voltaicideas/teams/116153/ms/)\n" +"Language-Team: Malay (https://app.transifex.com/voltaicideas/teams/116153/ms/)\n" "Language: ms\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: core\app.py:42 +#: core\app.py:44 msgid "There are no marked duplicates. Nothing has been done." msgstr "Tiada duplikasi yang ditandai. Tiada apa yang dilakukan." -#: core\app.py:43 +#: core\app.py:45 msgid "There are no selected duplicates. Nothing has been done." msgstr "Tiada duplikasi yang dipilih. Tiada apa yang dilakukan." -#: core\app.py:44 +#: core\app.py:46 msgid "" "You're about to open many files at once. Depending on what those files are " "opened with, doing so can create quite a mess. Continue?" @@ -27,23 +27,23 @@ msgstr "" "digunakan untuk membuka fail tersebut, ia mungkin menyebabkan sepah. Ingin " "teruskan?" -#: core\app.py:71 +#: core\app.py:73 msgid "Scanning for duplicates" msgstr "Mengimbas untuk duplikasi" -#: core\app.py:72 +#: core\app.py:74 msgid "Loading" msgstr "Memuatkan" -#: core\app.py:73 +#: core\app.py:75 msgid "Moving" msgstr "Memindahkan" -#: core\app.py:74 +#: core\app.py:76 msgid "Copying" msgstr "Menyalinkan" -#: core\app.py:75 +#: core\app.py:77 msgid "Sending to Trash" msgstr "Menghantarkan ke Tong Sampah" @@ -107,7 +107,7 @@ msgstr "Pilih direktori dituju untuk pindah fail yang ditandai" msgid "Select a destination for your exported CSV" msgstr "Pilih tempat tujuan untuk eksport CSV anda" -#: core\app.py:516 core\app.py:771 core\app.py:781 +#: core\app.py:516 core\app.py:777 core\app.py:787 msgid "Couldn't write to file: {}" msgstr "Tidak mampu menulis ke fail: {}" @@ -117,23 +117,23 @@ msgstr "" "Anda tidak ada perintah tersuai ditetapkan. Tetapkannya melalui menu " "keutamaan anda." -#: core\app.py:695 core\app.py:707 +#: core\app.py:701 core\app.py:713 msgid "You are about to remove %d files from results. Continue?" msgstr "Anda bakal mengalih keluar %d fail dari keputusan. Ingin teruskan?" -#: core\app.py:743 +#: core\app.py:749 msgid "{} duplicate groups were changed by the re-prioritization." msgstr "{} kumpulan duplikasi telah diubah oleh pengutamaan semula." -#: core\app.py:790 +#: core\app.py:797 msgid "The selected directories contain no scannable file." msgstr "Direktori yang dipilih tidak mempunyai fail yang boleh diimbas." -#: core\app.py:803 +#: core\app.py:813 msgid "Collecting files to scan" msgstr "Mengumpulkan fail untuk diimbas" -#: core\app.py:850 +#: core\app.py:863 msgid "%s (%d discarded)" msgstr "%s (%d dibuang)" @@ -178,23 +178,23 @@ msgstr "Nama Fail - Medan (Tiada Tertib)" msgid "Tags" msgstr "Tag" -#: core\me\scanner.py:24 core\pe\scanner.py:21 core\se\scanner.py:17 +#: core\me\scanner.py:24 core\pe\scanner.py:22 core\se\scanner.py:17 msgid "Contents" msgstr "Kandungan" -#: core\pe\matchblock.py:72 +#: core\pe\matchblock.py:66 msgid "Analyzed %d/%d pictures" msgstr "%d / %d gambar dianalisis" -#: core\pe\matchblock.py:177 +#: core\pe\matchblock.py:183 msgid "Performed %d/%d chunk matches" msgstr "%d / %d padanan ketulan dilaksanakan" -#: core\pe\matchblock.py:185 +#: core\pe\matchblock.py:191 msgid "Preparing for matching" msgstr "Membuat persediaan untuk pemadanan" -#: core\pe\matchblock.py:234 +#: core\pe\matchblock.py:240 msgid "Verified %d/%d matches" msgstr "%d / %d padanan disahkan" @@ -202,7 +202,7 @@ msgstr "%d / %d padanan disahkan" msgid "Read EXIF of %d/%d pictures" msgstr "EXIF bagi %d / %d gambar dibaca" -#: core\pe\scanner.py:22 +#: core\pe\scanner.py:23 msgid "EXIF Timestamp" msgstr "Cap masa EXIF" @@ -210,55 +210,51 @@ msgstr "Cap masa EXIF" msgid "None" msgstr "Tiada" -#: core\prioritize.py:100 +#: core\prioritize.py:102 msgid "Ends with number" msgstr "Tamat dengan nombor" -#: core\prioritize.py:101 +#: core\prioritize.py:103 msgid "Doesn't end with number" msgstr "Tidak tamat dengan nombor" -#: core\prioritize.py:102 +#: core\prioritize.py:104 msgid "Longest" msgstr "Terpanjang" -#: core\prioritize.py:103 +#: core\prioritize.py:105 msgid "Shortest" msgstr "Terpendek" -#: core\prioritize.py:140 +#: core\prioritize.py:142 msgid "Highest" msgstr "Tertinggi" -#: core\prioritize.py:140 +#: core\prioritize.py:142 msgid "Lowest" msgstr "Terendah" -#: core\prioritize.py:169 +#: core\prioritize.py:171 msgid "Newest" msgstr "Terbaru" -#: core\prioritize.py:169 +#: core\prioritize.py:171 msgid "Oldest" msgstr "Terlama" -#: core\results.py:134 +#: core\results.py:135 msgid "%d / %d (%s / %s) duplicates marked." msgstr "%d / %d (%s / %s) duplikasi ditandai." -#: core\results.py:141 +#: core\results.py:142 msgid " filter: %s" msgstr "penapis: %s" -#: core\scanner.py:90 -msgid "Read size of %d/%d files" -msgstr "Saiz bagi %d / %d gambar dibaca" - -#: core\scanner.py:116 +#: core\scanner.py:114 msgid "Read metadata of %d/%d files" msgstr "Metadata bagi %d / %d gambar dibaca" -#: core\scanner.py:154 +#: core\scanner.py:152 msgid "Almost done! Fiddling with results..." msgstr "Hampir selesai! Menyusun keputusan..." diff --git a/locale/nl/LC_MESSAGES/core.po b/locale/nl/LC_MESSAGES/core.po index 108c461a..a8e726c0 100644 --- a/locale/nl/LC_MESSAGES/core.po +++ b/locale/nl/LC_MESSAGES/core.po @@ -6,21 +6,21 @@ msgid "" msgstr "" "Last-Translator: Bas , 2021\n" -"Language-Team: Dutch (https://www.transifex.com/voltaicideas/teams/116153/nl/)\n" +"Language-Team: Dutch (https://app.transifex.com/voltaicideas/teams/116153/nl/)\n" "Language: nl\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: core\app.py:42 +#: core\app.py:44 msgid "There are no marked duplicates. Nothing has been done." msgstr "Er zijn geen gemarkeerde dubbelingen. Er is niks gedaam" -#: core\app.py:43 +#: core\app.py:45 msgid "There are no selected duplicates. Nothing has been done." msgstr "Er zijn geen dubelingen geselecteerd. Er is niks gedaan" -#: core\app.py:44 +#: core\app.py:46 msgid "" "You're about to open many files at once. Depending on what those files are " "opened with, doing so can create quite a mess. Continue?" @@ -29,23 +29,23 @@ msgstr "" "Afhankelijk met welke applicaties die bestanden worden geopened kan het best" " een rommeltje worden. Doorgaan?" -#: core\app.py:71 +#: core\app.py:73 msgid "Scanning for duplicates" msgstr "Dubbelingen aan het opsporen" -#: core\app.py:72 +#: core\app.py:74 msgid "Loading" msgstr "Laden" -#: core\app.py:73 +#: core\app.py:75 msgid "Moving" msgstr "Verplaatsen" -#: core\app.py:74 +#: core\app.py:76 msgid "Copying" msgstr "Kopiëren" -#: core\app.py:75 +#: core\app.py:77 msgid "Sending to Trash" msgstr "Naar de prullebak verplaatsen" @@ -111,7 +111,7 @@ msgstr "" msgid "Select a destination for your exported CSV" msgstr "Selecteer een locatie voor de CSV export" -#: core\app.py:516 core\app.py:771 core\app.py:781 +#: core\app.py:516 core\app.py:777 core\app.py:787 msgid "Couldn't write to file: {}" msgstr "Kan niet schrijven naar bestand: {}" @@ -121,28 +121,28 @@ msgstr "" "Er is nog geen \"aangepaste opdracht\" ingericht. Je kan dit doen bij de " "voorkeuren." -#: core\app.py:695 core\app.py:707 +#: core\app.py:701 core\app.py:713 msgid "You are about to remove %d files from results. Continue?" msgstr "" "Je staat op het punt om %d bestanden te verwijderen uit de resultaten. " "Doorgaan?" -#: core\app.py:743 +#: core\app.py:749 msgid "{} duplicate groups were changed by the re-prioritization." msgstr "" "{} dubbelingen groepen waren veranderd door de prioriteits verschuiving." -#: core\app.py:790 +#: core\app.py:797 msgid "The selected directories contain no scannable file." msgstr "" "De geselecteerde folders bevatten geen bestanden die onderzocht kunnen " "worden." -#: core\app.py:803 +#: core\app.py:813 msgid "Collecting files to scan" msgstr "Bestanden aan het verzamelen om te onderzoeken" -#: core\app.py:850 +#: core\app.py:863 msgid "%s (%d discarded)" msgstr "%s (%d weggelaten)" @@ -187,23 +187,23 @@ msgstr "Bestandsnaam - Velden (geen volgorde)" msgid "Tags" msgstr "Tags" -#: core\me\scanner.py:24 core\pe\scanner.py:21 core\se\scanner.py:17 +#: core\me\scanner.py:24 core\pe\scanner.py:22 core\se\scanner.py:17 msgid "Contents" msgstr "Inhoud" -#: core\pe\matchblock.py:72 +#: core\pe\matchblock.py:66 msgid "Analyzed %d/%d pictures" msgstr "%d van de %d afbeeldingen aan het analyseren" -#: core\pe\matchblock.py:177 +#: core\pe\matchblock.py:183 msgid "Performed %d/%d chunk matches" msgstr "%d van de %d bulk overeenkomsten uitgevoerd" -#: core\pe\matchblock.py:185 +#: core\pe\matchblock.py:191 msgid "Preparing for matching" msgstr "Voorbereiden voor dubbelingen bepaling" -#: core\pe\matchblock.py:234 +#: core\pe\matchblock.py:240 msgid "Verified %d/%d matches" msgstr "%d van de %d overeenkomsten nagekeken" @@ -211,7 +211,7 @@ msgstr "%d van de %d overeenkomsten nagekeken" msgid "Read EXIF of %d/%d pictures" msgstr "EXIF informatie van %d van de %d afbeeldingen gelezen" -#: core\pe\scanner.py:22 +#: core\pe\scanner.py:23 msgid "EXIF Timestamp" msgstr "EXIF-tijdstempel" @@ -219,55 +219,51 @@ msgstr "EXIF-tijdstempel" msgid "None" msgstr "Geen" -#: core\prioritize.py:100 +#: core\prioritize.py:102 msgid "Ends with number" msgstr "Eindigt met nummer" -#: core\prioritize.py:101 +#: core\prioritize.py:103 msgid "Doesn't end with number" msgstr "Eindigt niet met een nummer" -#: core\prioritize.py:102 +#: core\prioritize.py:104 msgid "Longest" msgstr "langste" -#: core\prioritize.py:103 +#: core\prioritize.py:105 msgid "Shortest" msgstr "kortste" -#: core\prioritize.py:140 +#: core\prioritize.py:142 msgid "Highest" msgstr "hoogste" -#: core\prioritize.py:140 +#: core\prioritize.py:142 msgid "Lowest" msgstr "laagste" -#: core\prioritize.py:169 +#: core\prioritize.py:171 msgid "Newest" msgstr "nieuwste" -#: core\prioritize.py:169 +#: core\prioritize.py:171 msgid "Oldest" msgstr "oudste" -#: core\results.py:134 +#: core\results.py:135 msgid "%d / %d (%s / %s) duplicates marked." msgstr "%d / %d (%s /%s) dubbelingen gemarkeerd" -#: core\results.py:141 +#: core\results.py:142 msgid " filter: %s" msgstr "filter: %s" -#: core\scanner.py:90 -msgid "Read size of %d/%d files" -msgstr "Bestandsgrootte van %d/%d bestanden aan het lezen." - -#: core\scanner.py:116 +#: core\scanner.py:114 msgid "Read metadata of %d/%d files" msgstr "Metadata van %d/%d bestanden gelezen" -#: core\scanner.py:154 +#: core\scanner.py:152 msgid "Almost done! Fiddling with results..." msgstr "Bijna klaar! Gehannes met resultaten..." diff --git a/locale/nl/LC_MESSAGES/ui.po b/locale/nl/LC_MESSAGES/ui.po index 48464c50..95f82d29 100644 --- a/locale/nl/LC_MESSAGES/ui.po +++ b/locale/nl/LC_MESSAGES/ui.po @@ -1,11 +1,11 @@ # Translators: # Bas , 2022 -# Andrew Senetar , 2022 # Fuan , 2022 +# Andrew Senetar , 2023 # msgid "" msgstr "" -"Last-Translator: Fuan , 2022\n" +"Last-Translator: Andrew Senetar , 2023\n" "Language-Team: Dutch (https://app.transifex.com/voltaicideas/teams/116153/nl/)\n" "Language: nl\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -1168,3 +1168,7 @@ msgid "" "
  • 2. Regular expressions with at least one path separator in them will be compared to the full path to the file.

  • Example: if you want to filter out .PNG files from the \"My Pictures\" directory only:
    .*My\\sPictures\\\\.*\\.png

    You can test the regular expression with the \"test string\" button after pasting a fake path in the test field:
    C:\\\\User\\My Pictures\\test.png

    \n" "Matching regular expressions will be highlighted.
    If there is at least one highlight, the path or filename tested will be ignored during scans.

    Directories and files starting with a period '.' are filtered out by default.

    " msgstr "" + +#: qt\pe\preferences_dialog.py:24 +msgid "Match pictures of different rotations" +msgstr "" diff --git a/locale/pl_PL/LC_MESSAGES/core.po b/locale/pl_PL/LC_MESSAGES/core.po index f904aac0..e1b36765 100644 --- a/locale/pl_PL/LC_MESSAGES/core.po +++ b/locale/pl_PL/LC_MESSAGES/core.po @@ -5,21 +5,21 @@ msgid "" msgstr "" "Last-Translator: Fuan , 2021\n" -"Language-Team: Polish (Poland) (https://www.transifex.com/voltaicideas/teams/116153/pl_PL/)\n" +"Language-Team: Polish (Poland) (https://app.transifex.com/voltaicideas/teams/116153/pl_PL/)\n" "Language: pl_PL\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" "Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n" -#: core\app.py:42 +#: core\app.py:44 msgid "There are no marked duplicates. Nothing has been done." msgstr "Brak wykrytych duplikatów. Nic nie zrobiono." -#: core\app.py:43 +#: core\app.py:45 msgid "There are no selected duplicates. Nothing has been done." msgstr "Brak wybranych duplikatów. Nic nie zrobiono." -#: core\app.py:44 +#: core\app.py:46 msgid "" "You're about to open many files at once. Depending on what those files are " "opened with, doing so can create quite a mess. Continue?" @@ -28,23 +28,23 @@ msgstr "" "pomocą czego te pliki są otwierane, może to spowodować spory bałagan. " "Kontyntynuj?" -#: core\app.py:71 +#: core\app.py:73 msgid "Scanning for duplicates" msgstr "Wyszukiwanie duplikatów" -#: core\app.py:72 +#: core\app.py:74 msgid "Loading" msgstr "Ładuję" -#: core\app.py:73 +#: core\app.py:75 msgid "Moving" msgstr "Przenoszę" -#: core\app.py:74 +#: core\app.py:76 msgid "Copying" msgstr "Kopiowanie" -#: core\app.py:75 +#: core\app.py:77 msgid "Sending to Trash" msgstr "Wysyłam do kosza" @@ -108,7 +108,7 @@ msgstr "Wybierz katalog, do którego chcesz przenieść zaznaczone pliki" msgid "Select a destination for your exported CSV" msgstr "Wybierz miejsce docelowe dla eksportowanego pliku CSV" -#: core\app.py:516 core\app.py:771 core\app.py:781 +#: core\app.py:516 core\app.py:777 core\app.py:787 msgid "Couldn't write to file: {}" msgstr "Nie udało się zapisać do pliku: {}" @@ -118,23 +118,23 @@ msgstr "" "Nie masz skonfigurowanego polecenia niestandardowego. Ustaw to w swoich " "preferencjach." -#: core\app.py:695 core\app.py:707 +#: core\app.py:701 core\app.py:713 msgid "You are about to remove %d files from results. Continue?" msgstr "Zamierzasz usunąć %d plików z wyników. Kontyntynuj?" -#: core\app.py:743 +#: core\app.py:749 msgid "{} duplicate groups were changed by the re-prioritization." msgstr "{} zduplikowanych grup zmieniono przez ponowne ustalenie priorytetów." -#: core\app.py:790 +#: core\app.py:797 msgid "The selected directories contain no scannable file." msgstr "Wybrane katalogi nie zawierają plik skanowalną." -#: core\app.py:803 +#: core\app.py:813 msgid "Collecting files to scan" msgstr "Zbieranie plików do skanowania" -#: core\app.py:850 +#: core\app.py:863 msgid "%s (%d discarded)" msgstr "%s(%d odrzucone)" @@ -178,23 +178,23 @@ msgstr "Nazwa pliku - pola (bez kolejności)" msgid "Tags" msgstr "Tagi" -#: core\me\scanner.py:24 core\pe\scanner.py:21 core\se\scanner.py:17 +#: core\me\scanner.py:24 core\pe\scanner.py:22 core\se\scanner.py:17 msgid "Contents" msgstr "Treść" -#: core\pe\matchblock.py:72 +#: core\pe\matchblock.py:66 msgid "Analyzed %d/%d pictures" msgstr "Analizowane %d/%d zdjęć" -#: core\pe\matchblock.py:177 +#: core\pe\matchblock.py:183 msgid "Performed %d/%d chunk matches" msgstr "Wykonano %d/%d dopasowań fragmentów" -#: core\pe\matchblock.py:185 +#: core\pe\matchblock.py:191 msgid "Preparing for matching" msgstr "Przygotowanie do dopasowania" -#: core\pe\matchblock.py:234 +#: core\pe\matchblock.py:240 msgid "Verified %d/%d matches" msgstr "Zweryfikowane %d/%d meczów" @@ -202,7 +202,7 @@ msgstr "Zweryfikowane %d/%d meczów" msgid "Read EXIF of %d/%d pictures" msgstr "Przeczytaj EXIF z %d/%d zdjęć" -#: core\pe\scanner.py:22 +#: core\pe\scanner.py:23 msgid "EXIF Timestamp" msgstr "Sygnatura czasowa EXIF" @@ -210,55 +210,51 @@ msgstr "Sygnatura czasowa EXIF" msgid "None" msgstr "Nie" -#: core\prioritize.py:100 +#: core\prioritize.py:102 msgid "Ends with number" msgstr "Kończy się numerem" -#: core\prioritize.py:101 +#: core\prioritize.py:103 msgid "Doesn't end with number" msgstr "Nie kończy się liczbą" -#: core\prioritize.py:102 +#: core\prioritize.py:104 msgid "Longest" msgstr "Najdłużej" -#: core\prioritize.py:103 +#: core\prioritize.py:105 msgid "Shortest" msgstr "Najkrótsza" -#: core\prioritize.py:140 +#: core\prioritize.py:142 msgid "Highest" msgstr "Najwyższa" -#: core\prioritize.py:140 +#: core\prioritize.py:142 msgid "Lowest" msgstr "Najniższa" -#: core\prioritize.py:169 +#: core\prioritize.py:171 msgid "Newest" msgstr "Najnowsza" -#: core\prioritize.py:169 +#: core\prioritize.py:171 msgid "Oldest" msgstr "Najstarszy" -#: core\results.py:134 +#: core\results.py:135 msgid "%d / %d (%s / %s) duplicates marked." msgstr "%d / %d (%s / %s) duplikaty oznakowane." -#: core\results.py:141 +#: core\results.py:142 msgid " filter: %s" msgstr " filtr: %s" -#: core\scanner.py:90 -msgid "Read size of %d/%d files" -msgstr "Odczytaj rozmiar %d/%d plików" - -#: core\scanner.py:116 +#: core\scanner.py:114 msgid "Read metadata of %d/%d files" msgstr "Przeczytaj metadane %d/%d plików" -#: core\scanner.py:154 +#: core\scanner.py:152 msgid "Almost done! Fiddling with results..." msgstr "Prawie skończone! Porządkowanie wyników..." diff --git a/locale/pl_PL/LC_MESSAGES/ui.po b/locale/pl_PL/LC_MESSAGES/ui.po index c4de495b..f994aafb 100644 --- a/locale/pl_PL/LC_MESSAGES/ui.po +++ b/locale/pl_PL/LC_MESSAGES/ui.po @@ -1,10 +1,10 @@ # Translators: -# Andrew Senetar , 2022 # Fuan , 2022 +# Andrew Senetar , 2023 # msgid "" msgstr "" -"Last-Translator: Fuan , 2022\n" +"Last-Translator: Andrew Senetar , 2023\n" "Language-Team: Polish (Poland) (https://app.transifex.com/voltaicideas/teams/116153/pl_PL/)\n" "Language: pl_PL\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -1166,3 +1166,7 @@ msgid "" "
  • 2. Regular expressions with at least one path separator in them will be compared to the full path to the file.

  • Example: if you want to filter out .PNG files from the \"My Pictures\" directory only:
    .*My\\sPictures\\\\.*\\.png

    You can test the regular expression with the \"test string\" button after pasting a fake path in the test field:
    C:\\\\User\\My Pictures\\test.png

    \n" "Matching regular expressions will be highlighted.
    If there is at least one highlight, the path or filename tested will be ignored during scans.

    Directories and files starting with a period '.' are filtered out by default.

    " msgstr "" + +#: qt\pe\preferences_dialog.py:24 +msgid "Match pictures of different rotations" +msgstr "" diff --git a/locale/pt_BR/LC_MESSAGES/core.po b/locale/pt_BR/LC_MESSAGES/core.po index c8fbc4f2..b2e5f489 100644 --- a/locale/pt_BR/LC_MESSAGES/core.po +++ b/locale/pt_BR/LC_MESSAGES/core.po @@ -5,21 +5,21 @@ msgid "" msgstr "" "Last-Translator: Fuan , 2021\n" -"Language-Team: Portuguese (Brazil) (https://www.transifex.com/voltaicideas/teams/116153/pt_BR/)\n" +"Language-Team: Portuguese (Brazil) (https://app.transifex.com/voltaicideas/teams/116153/pt_BR/)\n" "Language: pt_BR\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" -"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" -#: core\app.py:42 +#: core\app.py:44 msgid "There are no marked duplicates. Nothing has been done." msgstr "Não há duplicatas marcadas. Nada foi feito." -#: core\app.py:43 +#: core\app.py:45 msgid "There are no selected duplicates. Nothing has been done." msgstr "Não há duplicatas selecionadas. Nada foi feito." -#: core\app.py:44 +#: core\app.py:46 msgid "" "You're about to open many files at once. Depending on what those files are " "opened with, doing so can create quite a mess. Continue?" @@ -27,23 +27,23 @@ msgstr "" "Você está prestes a abrir muitos arquivos de uma vez. Problemas podem surgir" " dependendo de qual app seja usado para abri-los. Deseja continuar?" -#: core\app.py:71 +#: core\app.py:73 msgid "Scanning for duplicates" msgstr "Buscando por duplicatas" -#: core\app.py:72 +#: core\app.py:74 msgid "Loading" msgstr "Carregando" -#: core\app.py:73 +#: core\app.py:75 msgid "Moving" msgstr "Movendo" -#: core\app.py:74 +#: core\app.py:76 msgid "Copying" msgstr "Copiando" -#: core\app.py:75 +#: core\app.py:77 msgid "Sending to Trash" msgstr "Movendo para o Lixo" @@ -105,7 +105,7 @@ msgstr "Selecione um diretório para onde deseja mover os arquivos marcados" msgid "Select a destination for your exported CSV" msgstr "Selecione uma pasta para o CSV exportado" -#: core\app.py:516 core\app.py:771 core\app.py:781 +#: core\app.py:516 core\app.py:777 core\app.py:787 msgid "Couldn't write to file: {}" msgstr "Não foi possível gravar no arquivo: {}" @@ -114,23 +114,23 @@ msgid "You have no custom command set up. Set it up in your preferences." msgstr "" "Você não possui nenhum comando personalizado. Crie um nas preferências." -#: core\app.py:695 core\app.py:707 +#: core\app.py:701 core\app.py:713 msgid "You are about to remove %d files from results. Continue?" msgstr "Remover %d arquivo(s) dos resultados?" -#: core\app.py:743 +#: core\app.py:749 msgid "{} duplicate groups were changed by the re-prioritization." msgstr "{} grupos de duplicatas alterados ao repriorizar." -#: core\app.py:790 +#: core\app.py:797 msgid "The selected directories contain no scannable file." msgstr "As pastas selecionadas não contém arquivos escaneáveis." -#: core\app.py:803 +#: core\app.py:813 msgid "Collecting files to scan" msgstr "Juntando arquivos para escanear" -#: core\app.py:850 +#: core\app.py:863 msgid "%s (%d discarded)" msgstr "%s (%d rejeitado(s))" @@ -174,23 +174,23 @@ msgstr "Nome do arquivo - campos (sem pedido)" msgid "Tags" msgstr "Tags" -#: core\me\scanner.py:24 core\pe\scanner.py:21 core\se\scanner.py:17 +#: core\me\scanner.py:24 core\pe\scanner.py:22 core\se\scanner.py:17 msgid "Contents" msgstr "Conteúdo" -#: core\pe\matchblock.py:72 +#: core\pe\matchblock.py:66 msgid "Analyzed %d/%d pictures" msgstr "%d/%d fotos analizadas" -#: core\pe\matchblock.py:177 +#: core\pe\matchblock.py:183 msgid "Performed %d/%d chunk matches" msgstr "%d/%d resultados em blocos executados" -#: core\pe\matchblock.py:185 +#: core\pe\matchblock.py:191 msgid "Preparing for matching" msgstr "Preparando para comparação" -#: core\pe\matchblock.py:234 +#: core\pe\matchblock.py:240 msgid "Verified %d/%d matches" msgstr "%d/%d resultados verificados" @@ -198,7 +198,7 @@ msgstr "%d/%d resultados verificados" msgid "Read EXIF of %d/%d pictures" msgstr "EXIF lido em %d/%d fotos" -#: core\pe\scanner.py:22 +#: core\pe\scanner.py:23 msgid "EXIF Timestamp" msgstr "Timestamp EXIF" @@ -206,55 +206,51 @@ msgstr "Timestamp EXIF" msgid "None" msgstr "Nenhum" -#: core\prioritize.py:100 +#: core\prioritize.py:102 msgid "Ends with number" msgstr "Termina com número" -#: core\prioritize.py:101 +#: core\prioritize.py:103 msgid "Doesn't end with number" msgstr "Não termina com número" -#: core\prioritize.py:102 +#: core\prioritize.py:104 msgid "Longest" msgstr "Mais longo" -#: core\prioritize.py:103 +#: core\prioritize.py:105 msgid "Shortest" msgstr "Mais curto" -#: core\prioritize.py:140 +#: core\prioritize.py:142 msgid "Highest" msgstr "Maior" -#: core\prioritize.py:140 +#: core\prioritize.py:142 msgid "Lowest" msgstr "Menor" -#: core\prioritize.py:169 +#: core\prioritize.py:171 msgid "Newest" msgstr "Mais recente" -#: core\prioritize.py:169 +#: core\prioritize.py:171 msgid "Oldest" msgstr "Mais antigo" -#: core\results.py:134 +#: core\results.py:135 msgid "%d / %d (%s / %s) duplicates marked." msgstr "%d / %d (%s / %s) duplicatas marcadas." -#: core\results.py:141 +#: core\results.py:142 msgid " filter: %s" msgstr " filtro: %s" -#: core\scanner.py:90 -msgid "Read size of %d/%d files" -msgstr "Tamanho lido em %d/%d arquivos" - -#: core\scanner.py:116 +#: core\scanner.py:114 msgid "Read metadata of %d/%d files" msgstr "Metadados lidos em %d/%d arquivos" -#: core\scanner.py:154 +#: core\scanner.py:152 msgid "Almost done! Fiddling with results..." msgstr "Quase pronto! Mexendo nos resultados ..." diff --git a/locale/ru/LC_MESSAGES/core.po b/locale/ru/LC_MESSAGES/core.po index 90cc69f8..ea3b9415 100644 --- a/locale/ru/LC_MESSAGES/core.po +++ b/locale/ru/LC_MESSAGES/core.po @@ -2,10 +2,11 @@ # Andrew Senetar , 2021 # Fuan , 2021 # AHOHNMYC , 2023 +# Eugene Morozov , 2023 # msgid "" msgstr "" -"Last-Translator: AHOHNMYC , 2023\n" +"Last-Translator: Eugene Morozov , 2023\n" "Language-Team: Russian (https://app.transifex.com/voltaicideas/teams/116153/ru/)\n" "Language: ru\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -49,7 +50,7 @@ msgstr "Копирование" msgid "Sending to Trash" msgstr "Перемещение в Корзину" -#: core\app.py:293 +#: core\app.py:289 msgid "" "A previous action is still hanging in there. You can't start a new one yet. " "Wait a few seconds, then try again." @@ -57,39 +58,39 @@ msgstr "" "Предыдущее действие до сих пор выполняется. Вы не можете начать новое. " "Подождите несколько секунд, затем повторите попытку." -#: core\app.py:304 +#: core\app.py:300 msgid "No duplicates found." msgstr "Дубликаты не найдены." -#: core\app.py:319 +#: core\app.py:315 msgid "All marked files were copied successfully." msgstr "Все отмеченные файлы были скопированы успешно." -#: core\app.py:321 +#: core\app.py:317 msgid "All marked files were moved successfully." msgstr "Все отмеченные файлы были перемещены успешно." -#: core\app.py:323 +#: core\app.py:319 msgid "All marked files were deleted successfully." msgstr "Все отмеченные файлы были удалены успешно." -#: core\app.py:325 +#: core\app.py:321 msgid "All marked files were successfully sent to Trash." msgstr "Все отмеченные файлы были успешно отправлены в Корзину." -#: core\app.py:330 +#: core\app.py:326 msgid "Could not load file: {}" msgstr "Не удалось загрузить файл: {}" -#: core\app.py:386 +#: core\app.py:382 msgid "'{}' already is in the list." msgstr "'{}' уже присутствует в списке." -#: core\app.py:388 +#: core\app.py:384 msgid "'{}' does not exist." msgstr "'{}' не существует." -#: core\app.py:396 +#: core\app.py:392 msgid "" "All selected %d matches are going to be ignored in all subsequent scans. " "Continue?" @@ -97,51 +98,51 @@ msgstr "" "Все выбранные %d совпадений будут игнорироваться при всех последующих " "проверках. Продолжить?" -#: core\app.py:473 +#: core\app.py:469 msgid "Select a directory to copy marked files to" msgstr "Выберите каталог, в который вы хотите скопировать отмеченные файлы" -#: core\app.py:475 +#: core\app.py:471 msgid "Select a directory to move marked files to" msgstr "Выберите каталог, в который вы хотите переместить отмеченные файлы" -#: core\app.py:514 +#: core\app.py:510 msgid "Select a destination for your exported CSV" msgstr "Выберите назначение для экспортируемого " -#: core\app.py:520 core\app.py:781 core\app.py:791 +#: core\app.py:516 core\app.py:777 core\app.py:787 msgid "Couldn't write to file: {}" msgstr "Не удалось записать в файл: {}" -#: core\app.py:543 +#: core\app.py:539 msgid "You have no custom command set up. Set it up in your preferences." msgstr "Вы не создали пользовательскую команду. Задайте её в настройках." -#: core\app.py:705 core\app.py:717 +#: core\app.py:701 core\app.py:713 msgid "You are about to remove %d files from results. Continue?" msgstr "Вы собираетесь удалить %d файлов из результата поиска. Продолжить?" -#: core\app.py:753 +#: core\app.py:749 msgid "{} duplicate groups were changed by the re-prioritization." msgstr "{} групп дубликатов было изменено при реприоритезации." -#: core\app.py:801 +#: core\app.py:797 msgid "The selected directories contain no scannable file." msgstr "Выбранные каталоги не содержат файлов для сканирования." -#: core\app.py:817 +#: core\app.py:813 msgid "Collecting files to scan" msgstr "Сбор файлов для сканирования" -#: core\app.py:867 +#: core\app.py:863 msgid "%s (%d discarded)" msgstr "%s. (%d отменено)" -#: core\directories.py:190 +#: core\directories.py:191 msgid "Collected {} files to scan" msgstr "Собрано {} файлов для сканирования" -#: core\directories.py:206 +#: core\directories.py:207 msgid "Collected {} folders to scan" msgstr "Собрано {} каталогов для сканирования" @@ -178,23 +179,23 @@ msgstr "Имя файла - Поля (без сортировки)" msgid "Tags" msgstr "Теги" -#: core\me\scanner.py:24 core\pe\scanner.py:21 core\se\scanner.py:17 +#: core\me\scanner.py:24 core\pe\scanner.py:22 core\se\scanner.py:17 msgid "Contents" msgstr "Содержание" -#: core\pe\matchblock.py:72 +#: core\pe\matchblock.py:66 msgid "Analyzed %d/%d pictures" msgstr "Проанализировано %d из %d изображений" -#: core\pe\matchblock.py:177 +#: core\pe\matchblock.py:183 msgid "Performed %d/%d chunk matches" msgstr "Проверено %d/%d совпадений" -#: core\pe\matchblock.py:185 +#: core\pe\matchblock.py:191 msgid "Preparing for matching" msgstr "Подготовка для сравнения" -#: core\pe\matchblock.py:234 +#: core\pe\matchblock.py:240 msgid "Verified %d/%d matches" msgstr "Проверено %d/%d совпадений" @@ -202,7 +203,7 @@ msgstr "Проверено %d/%d совпадений" msgid "Read EXIF of %d/%d pictures" msgstr "Прочитана EXIF-информация %d/%d фотографий" -#: core\pe\scanner.py:22 +#: core\pe\scanner.py:23 msgid "EXIF Timestamp" msgstr "Метка времени EXIF" @@ -242,25 +243,21 @@ msgstr "Новейший" msgid "Oldest" msgstr "Старейший" -#: core\results.py:134 +#: core\results.py:135 msgid "%d / %d (%s / %s) duplicates marked." msgstr "%d / %d (%s / %s) дубликатов отмечено." -#: core\results.py:141 +#: core\results.py:142 msgid " filter: %s" msgstr "фильтр: %s" -#: core\scanner.py:90 -msgid "Read size of %d/%d files" -msgstr "Подсчитан размер %d/%d файлов" - -#: core\scanner.py:116 +#: core\scanner.py:114 msgid "Read metadata of %d/%d files" msgstr "Прочитаны метаданные %d/%d файлов" -#: core\scanner.py:154 +#: core\scanner.py:152 msgid "Almost done! Fiddling with results..." -msgstr "Почти готово! Возиться с результатами..." +msgstr "Почти готово! Вожусь с результатами..." #: core\se\scanner.py:18 msgid "Folders" diff --git a/locale/ru/LC_MESSAGES/ui.po b/locale/ru/LC_MESSAGES/ui.po index f2025a19..c61b25ea 100644 --- a/locale/ru/LC_MESSAGES/ui.po +++ b/locale/ru/LC_MESSAGES/ui.po @@ -3,10 +3,11 @@ # Andrew Senetar , 2022 # AHOHNMYC , 2023 # Captain Quake , 2023 +# Eugene Morozov , 2023 # msgid "" msgstr "" -"Last-Translator: Captain Quake , 2023\n" +"Last-Translator: Eugene Morozov , 2023\n" "Language-Team: Russian (https://app.transifex.com/voltaicideas/teams/116153/ru/)\n" "Language: ru\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -97,7 +98,7 @@ msgstr "" #: qt/deletion_options.py:59 cocoa/en.lproj/Localizable.strings:0 msgid "Proceed" -msgstr "Выполняется" +msgstr "Приступить" #: qt/deletion_options.py:60 cocoa/en.lproj/Localizable.strings:0 msgid "Cancel" @@ -1172,3 +1173,7 @@ msgid "" "
  • 2. Regular expressions with at least one path separator in them will be compared to the full path to the file.

  • Example: if you want to filter out .PNG files from the \"My Pictures\" directory only:
    .*My\\sPictures\\\\.*\\.png

    You can test the regular expression with the \"test string\" button after pasting a fake path in the test field:
    C:\\\\User\\My Pictures\\test.png

    \n" "Matching regular expressions will be highlighted.
    If there is at least one highlight, the path or filename tested will be ignored during scans.

    Directories and files starting with a period '.' are filtered out by default.

    " msgstr "" + +#: qt\pe\preferences_dialog.py:24 +msgid "Match pictures of different rotations" +msgstr "" diff --git a/locale/tr/LC_MESSAGES/core.po b/locale/tr/LC_MESSAGES/core.po index 232098cb..81b33cc5 100644 --- a/locale/tr/LC_MESSAGES/core.po +++ b/locale/tr/LC_MESSAGES/core.po @@ -5,21 +5,21 @@ msgid "" msgstr "" "Last-Translator: Emin Tufan Çetin , 2021\n" -"Language-Team: Turkish (https://www.transifex.com/voltaicideas/teams/116153/tr/)\n" +"Language-Team: Turkish (https://app.transifex.com/voltaicideas/teams/116153/tr/)\n" "Language: tr\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -#: core\app.py:42 +#: core\app.py:44 msgid "There are no marked duplicates. Nothing has been done." msgstr "İmlenen kopya yok. İşlem yapılmadı." -#: core\app.py:43 +#: core\app.py:45 msgid "There are no selected duplicates. Nothing has been done." msgstr "Seçilen kopya yok. İşlem yapılmadı." -#: core\app.py:44 +#: core\app.py:46 msgid "" "You're about to open many files at once. Depending on what those files are " "opened with, doing so can create quite a mess. Continue?" @@ -27,23 +27,23 @@ msgstr "" "Birden çok dosyayı aynı anda açmaya çalışıyorsunuz. Dosyaların açıldığı " "programlara bağlı olarak, bu sorun yaratabilir. Sürdürülsün mü?" -#: core\app.py:71 +#: core\app.py:73 msgid "Scanning for duplicates" msgstr "Kopyalar aranıyor" -#: core\app.py:72 +#: core\app.py:74 msgid "Loading" msgstr "Yükleniyor" -#: core\app.py:73 +#: core\app.py:75 msgid "Moving" msgstr "Taşınıyor" -#: core\app.py:74 +#: core\app.py:76 msgid "Copying" msgstr "Kopyalanıyor" -#: core\app.py:75 +#: core\app.py:77 msgid "Sending to Trash" msgstr "Çöpe Gönderiliyor" @@ -106,7 +106,7 @@ msgstr "İmlenen dosyaları taşımak için dizin seç" msgid "Select a destination for your exported CSV" msgstr "Dışa aktarılan CSV'niz için hedef seçin" -#: core\app.py:516 core\app.py:771 core\app.py:781 +#: core\app.py:516 core\app.py:777 core\app.py:787 msgid "Couldn't write to file: {}" msgstr "Dosyaya yazılamadı: {}" @@ -114,23 +114,23 @@ msgstr "Dosyaya yazılamadı: {}" msgid "You have no custom command set up. Set it up in your preferences." msgstr "Ayarlı özel komutunuz yok. Tercihlerinizden ayarlayınız." -#: core\app.py:695 core\app.py:707 +#: core\app.py:701 core\app.py:713 msgid "You are about to remove %d files from results. Continue?" msgstr "%d ögeyi sonuçlardan kaldırıyorsunuz. Sürdür?" -#: core\app.py:743 +#: core\app.py:749 msgid "{} duplicate groups were changed by the re-prioritization." msgstr "{} kopya küme, yeniden önceliklendirme tarafından değiştirildi." -#: core\app.py:790 +#: core\app.py:797 msgid "The selected directories contain no scannable file." msgstr "Seçili dizinler taranabilir dosya içermiyor." -#: core\app.py:803 +#: core\app.py:813 msgid "Collecting files to scan" msgstr "Taranacak dosyalar toplanıyor" -#: core\app.py:850 +#: core\app.py:863 msgid "%s (%d discarded)" msgstr "%s (%d göz ardı edilen)" @@ -175,23 +175,23 @@ msgstr "Dosya Adı - Alanlar (Düzen Yok)" msgid "Tags" msgstr "Etiketler" -#: core\me\scanner.py:24 core\pe\scanner.py:21 core\se\scanner.py:17 +#: core\me\scanner.py:24 core\pe\scanner.py:22 core\se\scanner.py:17 msgid "Contents" msgstr "İçindekiler" -#: core\pe\matchblock.py:72 +#: core\pe\matchblock.py:66 msgid "Analyzed %d/%d pictures" msgstr "%d/%d fotoğraf incelendi" -#: core\pe\matchblock.py:177 +#: core\pe\matchblock.py:183 msgid "Performed %d/%d chunk matches" msgstr "%d/%d yığın eşleşmesi gerçekleştirildi" -#: core\pe\matchblock.py:185 +#: core\pe\matchblock.py:191 msgid "Preparing for matching" msgstr "Eşlemeye hazırlanıyor" -#: core\pe\matchblock.py:234 +#: core\pe\matchblock.py:240 msgid "Verified %d/%d matches" msgstr "%d/%d eşleşme doğrulandı" @@ -199,7 +199,7 @@ msgstr "%d/%d eşleşme doğrulandı" msgid "Read EXIF of %d/%d pictures" msgstr "%d/%d fotorğafın EXIF'i okundu" -#: core\pe\scanner.py:22 +#: core\pe\scanner.py:23 msgid "EXIF Timestamp" msgstr "EXIF Zaman damgası" @@ -207,55 +207,51 @@ msgstr "EXIF Zaman damgası" msgid "None" msgstr "Hiçbiri" -#: core\prioritize.py:100 +#: core\prioritize.py:102 msgid "Ends with number" msgstr "Sayıyla bitenler" -#: core\prioritize.py:101 +#: core\prioritize.py:103 msgid "Doesn't end with number" msgstr "Sayıyla bitmeyenler" -#: core\prioritize.py:102 +#: core\prioritize.py:104 msgid "Longest" msgstr "En uzun" -#: core\prioritize.py:103 +#: core\prioritize.py:105 msgid "Shortest" msgstr "En kısa" -#: core\prioritize.py:140 +#: core\prioritize.py:142 msgid "Highest" msgstr "En yüksek" -#: core\prioritize.py:140 +#: core\prioritize.py:142 msgid "Lowest" msgstr "En düşük" -#: core\prioritize.py:169 +#: core\prioritize.py:171 msgid "Newest" msgstr "En yeni" -#: core\prioritize.py:169 +#: core\prioritize.py:171 msgid "Oldest" msgstr "En eski" -#: core\results.py:134 +#: core\results.py:135 msgid "%d / %d (%s / %s) duplicates marked." msgstr "%d / %d (%s / %s) kopya imlendi." -#: core\results.py:141 +#: core\results.py:142 msgid " filter: %s" msgstr "süz: %s" -#: core\scanner.py:90 -msgid "Read size of %d/%d files" -msgstr "%d/%d dosyanın boyutu okundu" - -#: core\scanner.py:116 +#: core\scanner.py:114 msgid "Read metadata of %d/%d files" msgstr "%d/%d dosyanın üst verisi okundu" -#: core\scanner.py:154 +#: core\scanner.py:152 msgid "Almost done! Fiddling with results..." msgstr "Neredeyse bitti! Sonuçlarla uğraşılıyor..." diff --git a/locale/tr/LC_MESSAGES/ui.po b/locale/tr/LC_MESSAGES/ui.po index 3a8590c4..d8ecd23f 100644 --- a/locale/tr/LC_MESSAGES/ui.po +++ b/locale/tr/LC_MESSAGES/ui.po @@ -1,10 +1,11 @@ # Translators: # Ahmet Haydar Işık , 2022 # Emin Tufan Çetin , 2022 +# Andrew Senetar , 2023 # msgid "" msgstr "" -"Last-Translator: Emin Tufan Çetin , 2022\n" +"Last-Translator: Andrew Senetar , 2023\n" "Language-Team: Turkish (https://app.transifex.com/voltaicideas/teams/116153/tr/)\n" "Language: tr\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -1167,3 +1168,7 @@ msgid "" "
  • 2. Regular expressions with at least one path separator in them will be compared to the full path to the file.

  • Example: if you want to filter out .PNG files from the \"My Pictures\" directory only:
    .*My\\sPictures\\\\.*\\.png

    You can test the regular expression with the \"test string\" button after pasting a fake path in the test field:
    C:\\\\User\\My Pictures\\test.png

    \n" "Matching regular expressions will be highlighted.
    If there is at least one highlight, the path or filename tested will be ignored during scans.

    Directories and files starting with a period '.' are filtered out by default.

    " msgstr "" + +#: qt\pe\preferences_dialog.py:24 +msgid "Match pictures of different rotations" +msgstr "" diff --git a/locale/uk/LC_MESSAGES/core.po b/locale/uk/LC_MESSAGES/core.po index 77d5fd8e..2a3c481e 100755 --- a/locale/uk/LC_MESSAGES/core.po +++ b/locale/uk/LC_MESSAGES/core.po @@ -5,21 +5,21 @@ msgid "" msgstr "" "Last-Translator: Fuan , 2021\n" -"Language-Team: Ukrainian (https://www.transifex.com/voltaicideas/teams/116153/uk/)\n" +"Language-Team: Ukrainian (https://app.transifex.com/voltaicideas/teams/116153/uk/)\n" "Language: uk\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" "Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n % 10 == 1 && n % 100 != 11 ? 0 : n % 1 == 0 && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || (n % 100 >=11 && n % 100 <=14 )) ? 2: 3);\n" -#: core\app.py:42 +#: core\app.py:44 msgid "There are no marked duplicates. Nothing has been done." msgstr "Немає позначених дублікатів - нічого робити." -#: core\app.py:43 +#: core\app.py:45 msgid "There are no selected duplicates. Nothing has been done." msgstr "Немає обраних дублікатів - нічого робити." -#: core\app.py:44 +#: core\app.py:46 msgid "" "You're about to open many files at once. Depending on what those files are " "opened with, doing so can create quite a mess. Continue?" @@ -27,23 +27,23 @@ msgstr "" "Ви збираєтеся відкрити багато файлів одночасно.\n" "Залежно від того, з чим відкриваються ці файли, це може створити неабияку халепу. Продовжити?" -#: core\app.py:71 +#: core\app.py:73 msgid "Scanning for duplicates" msgstr "Пошук дублікатів" -#: core\app.py:72 +#: core\app.py:74 msgid "Loading" msgstr "Завантаження" -#: core\app.py:73 +#: core\app.py:75 msgid "Moving" msgstr "Переміщення" -#: core\app.py:74 +#: core\app.py:76 msgid "Copying" msgstr "Копіювання" -#: core\app.py:75 +#: core\app.py:77 msgid "Sending to Trash" msgstr "Відправка до кошику" @@ -107,7 +107,7 @@ msgstr "Виберіть каталог, куди ви хочете перемі msgid "Select a destination for your exported CSV" msgstr "Виберіть каталог, куди потрібно скопіювати позначені файли" -#: core\app.py:516 core\app.py:771 core\app.py:781 +#: core\app.py:516 core\app.py:777 core\app.py:787 msgid "Couldn't write to file: {}" msgstr "Не вдалося записати у файл: {}" @@ -115,25 +115,25 @@ msgstr "Не вдалося записати у файл: {}" msgid "You have no custom command set up. Set it up in your preferences." msgstr "Власна команда не встановлена. Встановіть її у налаштуваннях." -#: core\app.py:695 core\app.py:707 +#: core\app.py:701 core\app.py:713 msgid "You are about to remove %d files from results. Continue?" msgstr "Ви збираєтеся видалити %d файлів з результату пошуку. Продовжити?" -#: core\app.py:743 +#: core\app.py:749 msgid "{} duplicate groups were changed by the re-prioritization." msgstr "" "{} повторюваних груп було змінено шляхом повторного встановлення " "пріоритетів." -#: core\app.py:790 +#: core\app.py:797 msgid "The selected directories contain no scannable file." msgstr "Обрані папки не містять файлів придатних для пошуку." -#: core\app.py:803 +#: core\app.py:813 msgid "Collecting files to scan" msgstr "Збір файлів для пошуку" -#: core\app.py:850 +#: core\app.py:863 msgid "%s (%d discarded)" msgstr "%s (%d відкинуто)" @@ -177,23 +177,23 @@ msgstr "Назва файлу - поля (без замовлення)" msgid "Tags" msgstr "Теги" -#: core\me\scanner.py:24 core\pe\scanner.py:21 core\se\scanner.py:17 +#: core\me\scanner.py:24 core\pe\scanner.py:22 core\se\scanner.py:17 msgid "Contents" msgstr "Зміст" -#: core\pe\matchblock.py:72 +#: core\pe\matchblock.py:66 msgid "Analyzed %d/%d pictures" msgstr "Проаналізовано %d/%d фотографій" -#: core\pe\matchblock.py:177 +#: core\pe\matchblock.py:183 msgid "Performed %d/%d chunk matches" msgstr "Виконано %d/%d порівнянь шматків" -#: core\pe\matchblock.py:185 +#: core\pe\matchblock.py:191 msgid "Preparing for matching" msgstr "Підготовка до порівняння" -#: core\pe\matchblock.py:234 +#: core\pe\matchblock.py:240 msgid "Verified %d/%d matches" msgstr "Перевірено %d/%d результатів" @@ -201,7 +201,7 @@ msgstr "Перевірено %d/%d результатів" msgid "Read EXIF of %d/%d pictures" msgstr "Прочитано EXIF з %d/%d фотографій" -#: core\pe\scanner.py:22 +#: core\pe\scanner.py:23 msgid "EXIF Timestamp" msgstr "Відмітка часу EXIF" @@ -209,55 +209,51 @@ msgstr "Відмітка часу EXIF" msgid "None" msgstr "Жоден" -#: core\prioritize.py:100 +#: core\prioritize.py:102 msgid "Ends with number" msgstr "Закінчується номером" -#: core\prioritize.py:101 +#: core\prioritize.py:103 msgid "Doesn't end with number" msgstr "Не закінчується номером" -#: core\prioritize.py:102 +#: core\prioritize.py:104 msgid "Longest" msgstr "Найдовший" -#: core\prioritize.py:103 +#: core\prioritize.py:105 msgid "Shortest" msgstr "Найкоротший" -#: core\prioritize.py:140 +#: core\prioritize.py:142 msgid "Highest" msgstr "Найвища" -#: core\prioritize.py:140 +#: core\prioritize.py:142 msgid "Lowest" msgstr "Найнижча" -#: core\prioritize.py:169 +#: core\prioritize.py:171 msgid "Newest" msgstr "Найновіші" -#: core\prioritize.py:169 +#: core\prioritize.py:171 msgid "Oldest" msgstr "Найдавніший" -#: core\results.py:134 +#: core\results.py:135 msgid "%d / %d (%s / %s) duplicates marked." msgstr "%d / %d (%s / %s) дублікатів позначено." -#: core\results.py:141 +#: core\results.py:142 msgid " filter: %s" msgstr "фільтр: %s" -#: core\scanner.py:90 -msgid "Read size of %d/%d files" -msgstr "Прочитано розмір %d/%d файлів" - -#: core\scanner.py:116 +#: core\scanner.py:114 msgid "Read metadata of %d/%d files" msgstr "Прочитано метаданих з %d/%d файлів" -#: core\scanner.py:154 +#: core\scanner.py:152 msgid "Almost done! Fiddling with results..." msgstr "Майже зроблено! Возився з результатами..." diff --git a/locale/uk/LC_MESSAGES/ui.po b/locale/uk/LC_MESSAGES/ui.po index ebca9b07..fc0f4e5c 100755 --- a/locale/uk/LC_MESSAGES/ui.po +++ b/locale/uk/LC_MESSAGES/ui.po @@ -1,10 +1,10 @@ # Translators: -# Andrew Senetar , 2022 # Fuan , 2022 +# Andrew Senetar , 2023 # msgid "" msgstr "" -"Last-Translator: Fuan , 2022\n" +"Last-Translator: Andrew Senetar , 2023\n" "Language-Team: Ukrainian (https://app.transifex.com/voltaicideas/teams/116153/uk/)\n" "Language: uk\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -1166,3 +1166,7 @@ msgid "" "
  • 2. Regular expressions with at least one path separator in them will be compared to the full path to the file.

  • Example: if you want to filter out .PNG files from the \"My Pictures\" directory only:
    .*My\\sPictures\\\\.*\\.png

    You can test the regular expression with the \"test string\" button after pasting a fake path in the test field:
    C:\\\\User\\My Pictures\\test.png

    \n" "Matching regular expressions will be highlighted.
    If there is at least one highlight, the path or filename tested will be ignored during scans.

    Directories and files starting with a period '.' are filtered out by default.

    " msgstr "" + +#: qt\pe\preferences_dialog.py:24 +msgid "Match pictures of different rotations" +msgstr "" diff --git a/locale/vi/LC_MESSAGES/core.po b/locale/vi/LC_MESSAGES/core.po index dea08f03..2fd24c4f 100644 --- a/locale/vi/LC_MESSAGES/core.po +++ b/locale/vi/LC_MESSAGES/core.po @@ -5,23 +5,23 @@ msgid "" msgstr "" "Last-Translator: Fuan , 2021\n" -"Language-Team: Vietnamese (https://www.transifex.com/voltaicideas/teams/116153/vi/)\n" +"Language-Team: Vietnamese (https://app.transifex.com/voltaicideas/teams/116153/vi/)\n" "Language: vi\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: core\app.py:42 +#: core\app.py:44 msgid "There are no marked duplicates. Nothing has been done." msgstr "" "Không có phần đánh dấu nào trùng nhau. Vẫn chưa thực hiện thao tác nào." -#: core\app.py:43 +#: core\app.py:45 msgid "There are no selected duplicates. Nothing has been done." msgstr "" "Không có phần đánh dấu nào trùng nhau. Vẫn chưa thực hiện thao tác nào." -#: core\app.py:44 +#: core\app.py:46 msgid "" "You're about to open many files at once. Depending on what those files are " "opened with, doing so can create quite a mess. Continue?" @@ -29,23 +29,23 @@ msgstr "" "Bạn chuẩn bị mở nhiều tập tin cùng lúc. Dựa trên chương trình các tập tin " "được mở, thao tác này có thể gây ra trạng thái lộn xộn. Vẫn muốn tiếp tục?" -#: core\app.py:71 +#: core\app.py:73 msgid "Scanning for duplicates" msgstr "Quét các phần trùng nhau" -#: core\app.py:72 +#: core\app.py:74 msgid "Loading" msgstr "Đang tải" -#: core\app.py:73 +#: core\app.py:75 msgid "Moving" msgstr "Đang di chuyển" -#: core\app.py:74 +#: core\app.py:76 msgid "Copying" msgstr "Đang sao chép" -#: core\app.py:75 +#: core\app.py:77 msgid "Sending to Trash" msgstr "Đang gửi vào thùng rác" @@ -112,7 +112,7 @@ msgstr "" msgid "Select a destination for your exported CSV" msgstr "Chọn một điểm xuất dữ liệu dạng CSV" -#: core\app.py:516 core\app.py:771 core\app.py:781 +#: core\app.py:516 core\app.py:777 core\app.py:787 msgid "Couldn't write to file: {}" msgstr "Không thể ghi vào tệp: {}" @@ -122,23 +122,23 @@ msgstr "" "Bạn vẫn chưa chỉnh sửa phần thiết lập dòng lệnh. Hãy sử dụng tính năng này " "trong phần tùy biến của bạn." -#: core\app.py:695 core\app.py:707 +#: core\app.py:701 core\app.py:713 msgid "You are about to remove %d files from results. Continue?" msgstr "Bạn chuẩn bị loại bỏ %d tập tin từ phần kết quả. Tiếp tục?" -#: core\app.py:743 +#: core\app.py:749 msgid "{} duplicate groups were changed by the re-prioritization." msgstr "{} các nhóm trùng nhau đã được thay đổi bởi thứ tự-tái ưu tiên." -#: core\app.py:790 +#: core\app.py:797 msgid "The selected directories contain no scannable file." msgstr "Các thứ mục được chọn chứa các tập tin không thể quét được." -#: core\app.py:803 +#: core\app.py:813 msgid "Collecting files to scan" msgstr "Đang thu thập các tập tin để quét" -#: core\app.py:850 +#: core\app.py:863 msgid "%s (%d discarded)" msgstr "%s (%d bị bỏ qua)" @@ -182,23 +182,23 @@ msgstr "Tên tệp - Trường (Không có thứ tự)" msgid "Tags" msgstr "Tags" -#: core\me\scanner.py:24 core\pe\scanner.py:21 core\se\scanner.py:17 +#: core\me\scanner.py:24 core\pe\scanner.py:22 core\se\scanner.py:17 msgid "Contents" msgstr "Nội dung" -#: core\pe\matchblock.py:72 +#: core\pe\matchblock.py:66 msgid "Analyzed %d/%d pictures" msgstr "Đã phân tích %d/%d hình ảnh" -#: core\pe\matchblock.py:177 +#: core\pe\matchblock.py:183 msgid "Performed %d/%d chunk matches" msgstr "Đã thể thiện %d/%d các phần khớp nhau" -#: core\pe\matchblock.py:185 +#: core\pe\matchblock.py:191 msgid "Preparing for matching" msgstr "Đang chuẩn bị phần khớp nhau" -#: core\pe\matchblock.py:234 +#: core\pe\matchblock.py:240 msgid "Verified %d/%d matches" msgstr "Đã xác nhận %d/%d phần khớp nhau" @@ -206,7 +206,7 @@ msgstr "Đã xác nhận %d/%d phần khớp nhau" msgid "Read EXIF of %d/%d pictures" msgstr "Đọc thông tin EXIF của %d/%d hình ảnh" -#: core\pe\scanner.py:22 +#: core\pe\scanner.py:23 msgid "EXIF Timestamp" msgstr "Dấu thời gian EXIF" @@ -214,55 +214,51 @@ msgstr "Dấu thời gian EXIF" msgid "None" msgstr "Không " -#: core\prioritize.py:100 +#: core\prioritize.py:102 msgid "Ends with number" msgstr "Tận cùng là số" -#: core\prioritize.py:101 +#: core\prioritize.py:103 msgid "Doesn't end with number" msgstr "Tận cùng không chứa số" -#: core\prioritize.py:102 +#: core\prioritize.py:104 msgid "Longest" msgstr "Dài nhất" -#: core\prioritize.py:103 +#: core\prioritize.py:105 msgid "Shortest" msgstr "Ngắn nhất" -#: core\prioritize.py:140 +#: core\prioritize.py:142 msgid "Highest" msgstr "Cao nhất" -#: core\prioritize.py:140 +#: core\prioritize.py:142 msgid "Lowest" msgstr "Thấp nhất" -#: core\prioritize.py:169 +#: core\prioritize.py:171 msgid "Newest" msgstr "Mới nhất" -#: core\prioritize.py:169 +#: core\prioritize.py:171 msgid "Oldest" msgstr "Cũ nhất" -#: core\results.py:134 +#: core\results.py:135 msgid "%d / %d (%s / %s) duplicates marked." msgstr "%d / %d (%s / %s) phần trùng nhau đã được đánh dấu." -#: core\results.py:141 +#: core\results.py:142 msgid " filter: %s" msgstr " bộ lọc: %s" -#: core\scanner.py:90 -msgid "Read size of %d/%d files" -msgstr "Đọc kích thước của các tập tin %d/%d" - -#: core\scanner.py:116 +#: core\scanner.py:114 msgid "Read metadata of %d/%d files" msgstr "Đọc thông tin chi tiết của %d/%d tập tin" -#: core\scanner.py:154 +#: core\scanner.py:152 msgid "Almost done! Fiddling with results..." msgstr "Sắp xong! Loay hoay với kết quả..." diff --git a/locale/vi/LC_MESSAGES/ui.po b/locale/vi/LC_MESSAGES/ui.po index 8b4f49e2..bb7b5a59 100644 --- a/locale/vi/LC_MESSAGES/ui.po +++ b/locale/vi/LC_MESSAGES/ui.po @@ -1,10 +1,10 @@ # Translators: -# Andrew Senetar , 2022 # Fuan , 2022 +# Andrew Senetar , 2023 # msgid "" msgstr "" -"Last-Translator: Fuan , 2022\n" +"Last-Translator: Andrew Senetar , 2023\n" "Language-Team: Vietnamese (https://app.transifex.com/voltaicideas/teams/116153/vi/)\n" "Language: vi\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -1167,3 +1167,7 @@ msgid "" "
  • 2. Regular expressions with at least one path separator in them will be compared to the full path to the file.

  • Example: if you want to filter out .PNG files from the \"My Pictures\" directory only:
    .*My\\sPictures\\\\.*\\.png

    You can test the regular expression with the \"test string\" button after pasting a fake path in the test field:
    C:\\\\User\\My Pictures\\test.png

    \n" "Matching regular expressions will be highlighted.
    If there is at least one highlight, the path or filename tested will be ignored during scans.

    Directories and files starting with a period '.' are filtered out by default.

    " msgstr "" + +#: qt\pe\preferences_dialog.py:24 +msgid "Match pictures of different rotations" +msgstr "" diff --git a/locale/zh_CN/LC_MESSAGES/core.po b/locale/zh_CN/LC_MESSAGES/core.po index fbe35846..3285b89d 100644 --- a/locale/zh_CN/LC_MESSAGES/core.po +++ b/locale/zh_CN/LC_MESSAGES/core.po @@ -3,47 +3,48 @@ # Chris Ocelot, 2021 # Fuan , 2021 # YaNing Lu, 2021 +# Mèng yáo, 2023 # msgid "" msgstr "" -"Last-Translator: YaNing Lu, 2021\n" -"Language-Team: Chinese (China) (https://www.transifex.com/voltaicideas/teams/116153/zh_CN/)\n" +"Last-Translator: Mèng yáo, 2023\n" +"Language-Team: Chinese (China) (https://app.transifex.com/voltaicideas/teams/116153/zh_CN/)\n" "Language: zh_CN\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: core\app.py:42 +#: core\app.py:44 msgid "There are no marked duplicates. Nothing has been done." msgstr "没有已标记的重复项。无需任何操作。" -#: core\app.py:43 +#: core\app.py:45 msgid "There are no selected duplicates. Nothing has been done." msgstr "没有已选定的重复项。无需任何操作。" -#: core\app.py:44 +#: core\app.py:46 msgid "" "You're about to open many files at once. Depending on what those files are " "opened with, doing so can create quite a mess. Continue?" msgstr "您即将一次性打开多个文件。取决于这些文件的默认打开方式,此项操作可能导致非常混乱的状况。是否继续?" -#: core\app.py:71 +#: core\app.py:73 msgid "Scanning for duplicates" msgstr "正在扫描重复内容" -#: core\app.py:72 +#: core\app.py:74 msgid "Loading" msgstr "载入中" -#: core\app.py:73 +#: core\app.py:75 msgid "Moving" msgstr "移动中" -#: core\app.py:74 +#: core\app.py:76 msgid "Copying" msgstr "复制中" -#: core\app.py:75 +#: core\app.py:77 msgid "Sending to Trash" msgstr "正在移至回收站" @@ -75,15 +76,15 @@ msgstr "所有已标记的文件已成功移至回收站。" #: core\app.py:326 msgid "Could not load file: {}" -msgstr "无法加载文件: {}" +msgstr "无法加载文件:{}" #: core\app.py:382 msgid "'{}' already is in the list." -msgstr "'{}' 已在列表中。" +msgstr "“{}”已在列表中。" #: core\app.py:384 msgid "'{}' does not exist." -msgstr "'{}' 不存在。" +msgstr "“{}”不存在。" #: core\app.py:392 msgid "" @@ -103,45 +104,45 @@ msgstr "请选择要将标记文件移动到的目录" msgid "Select a destination for your exported CSV" msgstr "选择您导出 CSV 的目标文件夹" -#: core\app.py:516 core\app.py:771 core\app.py:781 +#: core\app.py:516 core\app.py:777 core\app.py:787 msgid "Couldn't write to file: {}" -msgstr "不能写入文件: {}" +msgstr "不能写入文件:{}" #: core\app.py:539 msgid "You have no custom command set up. Set it up in your preferences." msgstr "您没有设定自定义命令。请在设置中进行设定。" -#: core\app.py:695 core\app.py:707 +#: core\app.py:701 core\app.py:713 msgid "You are about to remove %d files from results. Continue?" msgstr "您将从结果中移除 %d 个文件。是否继续?" -#: core\app.py:743 +#: core\app.py:749 msgid "{} duplicate groups were changed by the re-prioritization." -msgstr "{}个重复的组已被重新排列。" +msgstr "{} 个重复的组已被重新排列。" -#: core\app.py:790 +#: core\app.py:797 msgid "The selected directories contain no scannable file." msgstr "所选目录中不包含可供扫描的文件。" -#: core\app.py:803 +#: core\app.py:813 msgid "Collecting files to scan" msgstr "收集文件以供扫描" -#: core\app.py:850 +#: core\app.py:863 msgid "%s (%d discarded)" msgstr "%s (%d 项已丢弃)" #: core\directories.py:191 msgid "Collected {} files to scan" -msgstr "收集要扫描的{}文件" +msgstr "收集要扫描的 {} 个文件" #: core\directories.py:207 msgid "Collected {} folders to scan" -msgstr "收集要扫描的{}文件夹" +msgstr "收集要扫描的 {} 个文件夹" #: core\engine.py:27 msgid "%d matches found from %d groups" -msgstr "从1%d组中找到1%d个匹配" +msgstr "找到 %d 个匹配,来自于 %d 个组中" #: core\gui\deletion_options.py:71 msgid "You are sending {} file(s) to the Trash." @@ -153,7 +154,7 @@ msgstr "正则表达式" #: core\gui\ignore_list_dialog.py:25 msgid "Do you really want to remove all %d items from the ignore list?" -msgstr "确定要从忽略列表中移除所有 %d 项吗?" +msgstr "确定要从忽略列表中移除所有 %d 项吗?" #: core\me\scanner.py:20 core\se\scanner.py:16 msgid "Filename" @@ -161,41 +162,41 @@ msgstr "文件名" #: core\me\scanner.py:21 msgid "Filename - Fields" -msgstr "分组比较文件名(如作者-歌曲名)" +msgstr "分组比较文件名(如:作者-歌曲名)" #: core\me\scanner.py:22 msgid "Filename - Fields (No Order)" -msgstr "分组比较文件名(不固定顺序(如作者-歌曲名或者歌曲名-作者))" +msgstr "分组比较文件名(不固定顺序,如:作者-歌曲名 或者 歌曲名-作者)" #: core\me\scanner.py:23 msgid "Tags" msgstr "标签" -#: core\me\scanner.py:24 core\pe\scanner.py:21 core\se\scanner.py:17 +#: core\me\scanner.py:24 core\pe\scanner.py:22 core\se\scanner.py:17 msgid "Contents" msgstr "内容" -#: core\pe\matchblock.py:72 +#: core\pe\matchblock.py:66 msgid "Analyzed %d/%d pictures" -msgstr "已分析 %d/%d 图像" +msgstr "已分析 %d 张图像(共 %d 张)" -#: core\pe\matchblock.py:177 +#: core\pe\matchblock.py:183 msgid "Performed %d/%d chunk matches" -msgstr "已执行 %d/%d 个区块匹配" +msgstr "已执行 %d 个区块匹配(共 %d 个)" -#: core\pe\matchblock.py:185 +#: core\pe\matchblock.py:191 msgid "Preparing for matching" msgstr "准备进行匹配" -#: core\pe\matchblock.py:234 +#: core\pe\matchblock.py:240 msgid "Verified %d/%d matches" -msgstr "已验证 %d/%d 匹配项" +msgstr "已验证 %d 个匹配项(共 %d 个)" #: core\pe\matchexif.py:19 msgid "Read EXIF of %d/%d pictures" -msgstr "已读取 %d/%d 张图片的 EXIF" +msgstr "已读取 %d 张图片的 EXIF(共 %d 张)" -#: core\pe\scanner.py:22 +#: core\pe\scanner.py:23 msgid "EXIF Timestamp" msgstr "EXIF 时间戳" @@ -203,55 +204,51 @@ msgstr "EXIF 时间戳" msgid "None" msgstr "无" -#: core\prioritize.py:100 +#: core\prioritize.py:102 msgid "Ends with number" msgstr "以数字结尾" -#: core\prioritize.py:101 +#: core\prioritize.py:103 msgid "Doesn't end with number" msgstr "不以数字结尾" -#: core\prioritize.py:102 +#: core\prioritize.py:104 msgid "Longest" msgstr "最长" -#: core\prioritize.py:103 +#: core\prioritize.py:105 msgid "Shortest" msgstr "最短" -#: core\prioritize.py:140 +#: core\prioritize.py:142 msgid "Highest" msgstr "最高" -#: core\prioritize.py:140 +#: core\prioritize.py:142 msgid "Lowest" msgstr "最低" -#: core\prioritize.py:169 +#: core\prioritize.py:171 msgid "Newest" msgstr "最新" -#: core\prioritize.py:169 +#: core\prioritize.py:171 msgid "Oldest" msgstr "最旧" -#: core\results.py:134 +#: core\results.py:135 msgid "%d / %d (%s / %s) duplicates marked." msgstr "已标记 %d / %d (%s / %s) 个重复项。" -#: core\results.py:141 +#: core\results.py:142 msgid " filter: %s" -msgstr " 过滤: %s" - -#: core\scanner.py:90 -msgid "Read size of %d/%d files" -msgstr "已读取 %d/%d 文件大小" +msgstr " 过滤:%s" -#: core\scanner.py:116 +#: core\scanner.py:114 msgid "Read metadata of %d/%d files" -msgstr "已读取 %d/%d 文件元数据" +msgstr "已读取 %d 个文件元数据(共 %d 个)" -#: core\scanner.py:154 +#: core\scanner.py:152 msgid "Almost done! Fiddling with results..." msgstr "即将完成!整理结果中..." diff --git a/locale/zh_CN/LC_MESSAGES/ui.po b/locale/zh_CN/LC_MESSAGES/ui.po index 8457cdc7..3868aa01 100644 --- a/locale/zh_CN/LC_MESSAGES/ui.po +++ b/locale/zh_CN/LC_MESSAGES/ui.po @@ -1,12 +1,13 @@ # Translators: # Fuan , 2022 # 太子 VC , 2022 -# Andrew Senetar , 2022 # Chris Ocelot, 2023 +# Andrew Senetar , 2023 +# Mèng yáo, 2023 # msgid "" msgstr "" -"Last-Translator: Chris Ocelot, 2023\n" +"Last-Translator: Mèng yáo, 2023\n" "Language-Team: Chinese (China) (https://app.transifex.com/voltaicideas/teams/116153/zh_CN/)\n" "Language: zh_CN\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -45,7 +46,7 @@ msgstr "打开调试记录" #: qt/app.py:180 cocoa/en.lproj/Localizable.strings:0 msgid "Do you really want to remove all your cached picture analysis?" -msgstr "确定要移除所有缓存图片?" +msgstr "确定要移除所有缓存的图片分析吗?" #: qt/app.py:184 msgid "Picture cache cleared." @@ -89,7 +90,7 @@ msgstr "直接删除文件" msgid "" "Instead of sending files to trash, delete them directly. This option is " "usually used as a workaround when the normal deletion method doesn't work." -msgstr "直接将文件删除,而不是将其移至回收站。此选项通常作为普通删除方法不能正常使用时替代方案。" +msgstr "直接将文件删除,而不是将其移至回收站。此选项通常作为常规删除方法不起作用时的替代方案。" #: qt/deletion_options.py:59 cocoa/en.lproj/Localizable.strings:0 msgid "Proceed" @@ -160,7 +161,7 @@ msgstr "标准" #: qt/directories_dialog.py:128 cocoa/en.lproj/Localizable.strings:0 msgid "Scan Type:" -msgstr "扫描类型:" +msgstr "扫描类型:" #: qt/directories_dialog.py:135 msgid "More Options" @@ -168,7 +169,7 @@ msgstr "更多选项" #: qt/directories_dialog.py:139 cocoa/en.lproj/Localizable.strings:0 msgid "Select folders to scan and press \"Scan\"." -msgstr "请选择要扫描的文件夹,然后点击 \"扫描\"。" +msgstr "请选择要扫描的文件夹,然后点击“扫描”。" #: qt/directories_dialog.py:163 cocoa/en.lproj/Localizable.strings:0 msgid "Load Results" @@ -184,7 +185,7 @@ msgstr "未保存的结果" #: qt/directories_dialog.py:231 cocoa/en.lproj/Localizable.strings:0 msgid "You have unsaved results, do you really want to quit?" -msgstr "您还没有保存扫描结果,确定要退出吗?" +msgstr "您还没有保存扫描结果,确定要退出吗?" #: qt/directories_dialog.py:239 cocoa/en.lproj/Localizable.strings:0 msgid "Select a folder to add to the scanning list" @@ -247,7 +248,7 @@ msgstr "详细信息" #: qt/me/preferences_dialog.py:30 cocoa/en.lproj/Localizable.strings:0 msgid "Tags to scan:" -msgstr "扫描标签:" +msgstr "扫描标签:" #: qt/me/preferences_dialog.py:36 cocoa/en.lproj/Localizable.strings:0 msgid "Track" @@ -306,7 +307,7 @@ msgstr "忽略硬链接到相同文件的重复文件" #: qt/me/preferences_dialog.py:62 qt/pe/preferences_dialog.py:29 #: qt/se/preferences_dialog.py:62 cocoa/en.lproj/Localizable.strings:0 msgid "Debug mode (restart required)" -msgstr "调试模式 (需要重新启动)" +msgstr "调试模式(需要重新启动)" #: qt/pe/preferences_dialog.py:19 cocoa/en.lproj/Localizable.strings:0 msgid "Match pictures of different dimensions" @@ -314,7 +315,7 @@ msgstr "匹配不同尺寸的图像" #: qt/preferences_dialog.py:43 msgid "Filter Hardness:" -msgstr "过滤强度:" +msgstr "过滤强度:" #: qt/preferences_dialog.py:69 msgid "More Results" @@ -326,15 +327,15 @@ msgstr "较少结果" #: qt/preferences_dialog.py:81 msgid "Font size:" -msgstr "字体大小:" +msgstr "字体大小:" #: qt/preferences_dialog.py:85 msgid "Language:" -msgstr "语言:" +msgstr "语言:" #: qt/preferences_dialog.py:91 cocoa/en.lproj/Localizable.strings:0 msgid "Copy and Move:" -msgstr "复制并移动:" +msgstr "复制并移动:" #: qt/preferences_dialog.py:94 cocoa/en.lproj/Localizable.strings:0 msgid "Right in destination" @@ -350,11 +351,11 @@ msgstr "重建绝对路径" #: qt/preferences_dialog.py:99 msgid "Custom Command (arguments: %d for dupe, %r for ref):" -msgstr "自定义命令 (参数: %d 指重复文件, %r 指源文件):" +msgstr "自定义命令(参数:%d 指重复文件,%r 指源文件):" #: qt/preferences_dialog.py:174 msgid "dupeGuru has to restart for language changes to take effect." -msgstr "dupeGuru需要重新启动以使语言修改生效。" +msgstr "dupeGuru 需要重新启动以使语言修改生效。" #: qt/prioritize_dialog.py:75 cocoa/en.lproj/Localizable.strings:0 msgid "Re-Prioritize duplicates" @@ -369,7 +370,7 @@ msgstr "在右侧的框内添加规则然后点击确定,以将最符合规则 #: qt/problem_dialog.py:33 cocoa/en.lproj/Localizable.strings:0 msgid "Problems!" -msgstr "有问题!" +msgstr "有问题!" #: qt/problem_dialog.py:37 cocoa/en.lproj/Localizable.strings:0 msgid "" @@ -501,7 +502,7 @@ msgstr "选择一个文件来保存您的结果" #: qt/se/preferences_dialog.py:41 msgid "Ignore files smaller than" -msgstr "忽略文件当其小于" +msgstr "忽略文件,当其小于" #: qt/se/preferences_dialog.py:52 cocoa/en.lproj/Localizable.strings:0 msgid "KB" @@ -549,7 +550,7 @@ msgstr "复制" #: cocoa/en.lproj/Localizable.strings:0 msgid "Custom command (arguments: %d for dupe, %r for ref):" -msgstr "自定义命令 (参数: %d 指重复文件, %r 指源文件):" +msgstr "自定义命令(参数:%d 指重复文件,%r 指源文件):" #: cocoa/en.lproj/Localizable.strings:0 msgid "Cut" @@ -609,7 +610,7 @@ msgstr "过滤器" #: cocoa/en.lproj/Localizable.strings:0 msgid "Filter hardness:" -msgstr "过滤强度:" +msgstr "过滤强度:" #: cocoa/en.lproj/Localizable.strings:0 msgid "Filter Results..." @@ -621,7 +622,7 @@ msgstr "文件夹选择窗口" #: cocoa/en.lproj/Localizable.strings:0 msgid "Font Size:" -msgstr "字体大小:" +msgstr "字体大小:" #: cocoa/en.lproj/Localizable.strings:0 msgid "Hide dupeGuru" @@ -633,7 +634,7 @@ msgstr "隐藏其他" #: cocoa/en.lproj/Localizable.strings:0 msgid "Ignore files smaller than:" -msgstr "忽略文件当其小于:" +msgstr "忽略文件,当其小于:" #: cocoa/en.lproj/Localizable.strings:0 msgid "Load from file..." @@ -709,7 +710,7 @@ msgstr "开始重复内容扫描" #: cocoa/en.lproj/Localizable.strings:0 msgid "The name '%@' already exists." -msgstr "名称 '%@' 已存在。" +msgstr "名称“%@”已存在。" #: cocoa/en.lproj/Localizable.strings:0 msgid "Window" @@ -741,7 +742,7 @@ msgstr "选择一个目录文件以载入" #: qt\directories_dialog.py:338 msgid "dupeGuru Results (*.dupegurudirs)" -msgstr "dupeGuru 结果 (*.dupegurudirs)" +msgstr "dupeGuru 结果 (*.dupegurudirs)" #: qt\directories_dialog.py:347 msgid "Select a file to save your directories to" @@ -749,7 +750,7 @@ msgstr "选择一个文件来保存您的目录" #: qt\directories_dialog.py:348 msgid "dupeGuru Directories (*.dupegurudirs)" -msgstr "dupeGuru 目录 (*.dupegurudirs)" +msgstr "dupeGuru 目录 (*.dupegurudirs)" #: qt\exclude_list_dialog.py:44 msgid "Add" @@ -765,7 +766,7 @@ msgstr "测试字符串" #: qt\exclude_list_dialog.py:83 msgid "Type a python regular expression here..." -msgstr "在此输入一个python正则表达式..." +msgstr "在此输入一个 python 正则表达式..." #: qt\exclude_list_dialog.py:85 msgid "Type a file system path or filename here..." @@ -778,7 +779,7 @@ msgid "" "Example: if you want to filter out .PNG files from the \"My Pictures\" directory only:
    .*My\\sPictures\\\\.*\\.png

    You can test the regular expression with the test string feature by pasting a fake path in it:
    C:\\\\User\\My Pictures\\test.png

    \n" "Matching regular expressions will be highlighted.
    If there is at least one highlight, the path tested will be ignored during scans.

    Directories and files starting with a period '.' are filtered out by default.

    " msgstr "" -"这些(大小写敏感)的python正则表达式会在扫描过程中筛选文件。
    如果目录的名称和某一个正则表达式匹配的话,它们的默认状态将为被设为排除状态。
    每一个被采集的文件都会被进行两种不同的测试来决定它是否会被排除掉:
  • 1. 没有路径分隔符的正则表达式只会和文件名作比较。
  • \n" +"这些(大小写敏感)的 python 正则表达式会在扫描过程中筛选文件。
    如果目录的名称和某一个正则表达式匹配的话,它们的默认状态将为被设为排除状态。
    每一个被采集的文件都会被进行两种不同的测试来决定它是否会被排除掉:
  • 1. 没有路径分隔符的正则表达式只会和文件名作比较。
  • \n" "
  • 2. 有路径分隔符的正则表达式,会和文件的完整路径作比较。

  • \n" "如:假如您想要仅从“我的图片”目录排除掉 .PNG 文件的话:
    .*My\\sPictures\\\\.*\\.png

    您可以使用测试字符串功能来测试正则表达式,只需要在其中粘贴一个假的路径:
    C:\\\\User\\My Pictures\\test.png

    \n" "匹配的正则表达式会被高亮。
    假如至少有一个高亮的话,在扫描中这个路径将会被忽略。

    以“.”开头的目录和文件默认就会被忽略。

    " @@ -836,15 +837,15 @@ msgstr "当图片尺寸大于显示窗口时,显示滚动条来移动图片。 #: qt\preferences_dialog.py:156 msgid "Use default position for tab bar (requires restart)" -msgstr "在默认位置显示Tab Bar(需要重启)" +msgstr "在默认位置显示标签栏(需要重启)" #: qt\preferences_dialog.py:158 msgid "" "Place the tab bar below the main menu instead of next to it\n" "On MacOS, the tab bar will fill up the window's width instead." msgstr "" -"把Tab Bar放在主菜单下面而不是旁边\n" -"在MacOS上,Tab Bar会填充满整个窗口的宽度。" +"把标签栏放在主菜单下面而不是旁边\n" +"在 MacOS 上,标签栏会填充满整个窗口的宽度。" #: qt\preferences_dialog.py:172 msgid "Use bold font for references" @@ -887,7 +888,7 @@ msgstr "把标题栏从顶部横向改为左侧竖直" #: qt\tabbed_window.py:44 msgid "Show tab bar" -msgstr "显示 Tab Bar" +msgstr "显示标签栏" #: qt\exclude_list_dialog.py:152 msgid "" @@ -896,10 +897,10 @@ msgid "" "Example: if you want to filter out .PNG files from the \"My Pictures\" directory only:
    .*My\\sPictures\\\\.*\\.png

    You can test the regular expression with the \"test string\" button after pasting a fake path in the test field:
    C:\\\\User\\My Pictures\\test.png

    \n" "Matching regular expressions will be highlighted.
    If there is at least one highlight, the path or filename tested will be ignored during scans.

    Directories and files starting with a period '.' are filtered out by default.

    " msgstr "" -"这些(大小写敏感)的python正则表达式会在扫描过程中对结果进行过滤。
    除非目录名称和正则表达式匹配,它们的默认状态会被设成从目录标签排除。
    每一个文件都会经过两种测试,以确定它是否会被完全忽略:
  • 1. 没有路径分隔符的正则表达式,仅用于和文件名进行比较。
  • \n" -"
  • 2. 至少有一个路径分隔符的正则表达式,会被用于和文件的完整路径进行比较。

  • \n" -"例如:如果你想要仅在“图片”目录中排除所有.PNG文件:
    .*My\\sPictures\\\\.*\\.png

    你可以使用“测试字符串”按钮来测试你的正则表达式,只需要将虚拟的路径输入测试框即可:
    C:\\\\User\\My Pictures\\test.png

    \n" -"匹配的正则表达式会被高亮。
    假如有至少一个高亮,测试文件的文件名或者路径就会在扫描中被忽略。

    以“.”开头的目录或文件默认会被忽略。

    " +"这些 python 正则表达式(区分大小写)将在扫描期间过滤掉文件。
    如果目录的名称恰好与某个选定的正则表达式匹配,则目录的默认状态也将在“目录”选项卡中设置为“已排除”。
    对于收集的每个文件,将执行以下两个测试以确定是否完全忽略它:
  • 1、没有路径分隔符的正则表达式将仅与文件名进行比较。
  • \n" +"
  • 2、包含至少一个路径分隔符的正则表达式将与文件的完整路径进行比较。

  • \n" +"示例:如果只想从“我的图片”目录中过滤掉 .PNG 文件:
    .*My\\sPictures\\\\.*\\.png

    那么在测试框中粘贴如下虚拟路径后,可以使用“测试字符串”按钮测试正则表达式:
    C:\\\\User\\My Pictures\\test.png

    \n" +"匹配的正则表达式将突出显示。
    如果至少有一个突出显示,则在扫描过程中将忽略测试的路径或文件名。

    默认情况下,以英文句点“.”开头的目录和文件将被过滤掉。

    " #: qt\app.py:256 msgid "Results" @@ -907,7 +908,7 @@ msgstr "结果" #: qt\preferences_dialog.py:150 msgid "General Interface" -msgstr "通用介面" +msgstr "通用界面" #: qt\preferences_dialog.py:176 msgid "Result Table" @@ -919,15 +920,15 @@ msgstr "详细信息窗口" #: qt\preferences_dialog.py:285 msgid "General" -msgstr "一般" +msgstr "常规" #: qt\preferences_dialog.py:286 msgid "Display" -msgstr "展示" +msgstr "显示" #: qt\se\preferences_dialog.py:70 msgid "Partially hash files bigger than" -msgstr "只计算部分hash,如果文件大于" +msgstr "只计算部分哈希值,如果文件大于" #: qt\se\preferences_dialog.py:80 msgid "MB" @@ -955,7 +956,10 @@ msgstr "清除缓存" msgid "" "Do you really want to clear the cache? This will remove all cached file " "hashes and picture analysis." -msgstr "你确定要清除缓存吗?所有缓存的文件hash和图片分析都会被移除。" +msgstr "" +"确定要清除缓存吗?\n" +"\n" +"这将删除所有缓存的文件哈希和图片分析。" #: qt\app.py:299 msgid "Cache cleared." @@ -975,7 +979,7 @@ msgstr "将扫描操作保存为配置,并保存日志用于优化。" #: qt\preferences_dialog.py:246 msgid "Logs located in: {}" -msgstr "日志位于:{}" +msgstr "日志位于:{}" #: qt\preferences_dialog.py:291 msgid "Debug" @@ -991,11 +995,11 @@ msgstr "版本 {}" #: qt\about_box.py:49 qt\about_box.py:75 msgid "Checking for updates..." -msgstr "检查更新..." +msgstr "正在检查更新..." #: qt\about_box.py:54 msgid "Licensed under GPLv3" -msgstr "本项目基于GPLv3开源协议发布" +msgstr "本项目基于 GPLv3 开源协议发布" #: qt\about_box.py:68 msgid "No update available." @@ -1003,7 +1007,7 @@ msgstr "没有新版本。" #: qt\about_box.py:71 msgid "New version {} available, download here." -msgstr "有新版本{}可用,在这里下载。" +msgstr "新版本 {} 可用,点这里下载。" #: qt\error_report_dialog.py:50 msgid "Error Report" @@ -1135,11 +1139,11 @@ msgstr "载入缓存摘要时忽略mtime的不同" #: qt\progress_window.py:64 msgid "Cancel?" -msgstr "" +msgstr "取消?" #: qt\progress_window.py:65 msgid "Are you sure you want to cancel? All progress will be lost." -msgstr "" +msgstr "确定要取消吗?这将会导致所有进展都付诸东流。" #: qt\exclude_list_dialog.py:161 msgid "" @@ -1147,3 +1151,10 @@ msgid "" "
  • 2. Regular expressions with at least one path separator in them will be compared to the full path to the file.

  • Example: if you want to filter out .PNG files from the \"My Pictures\" directory only:
    .*My\\sPictures\\\\.*\\.png

    You can test the regular expression with the \"test string\" button after pasting a fake path in the test field:
    C:\\\\User\\My Pictures\\test.png

    \n" "Matching regular expressions will be highlighted.
    If there is at least one highlight, the path or filename tested will be ignored during scans.

    Directories and files starting with a period '.' are filtered out by default.

    " msgstr "" +"这些 python 正则表达式(区分大小写)将在扫描期间过滤掉文件。
    如果目录的名称恰好与某个选定的正则表达式匹配,则目录的默认状态也将在“目录”选项卡中设置为“已排除”。
    对于收集的每个文件,将执行以下两个测试以确定是否完全忽略它:
  • 1、没有路径分隔符的正则表达式将仅与文件名进行比较。
  • \n" +"
  • 2、包含至少一个路径分隔符的正则表达式将与文件的完整路径进行比较。

  • 示例:如果只想从“我的图片”目录中过滤掉 .PNG 文件:
    .*My\\sPictures\\\\.*\\.png

    那么在测试框中粘贴如下虚拟路径后,可以使用“测试字符串”按钮测试正则表达式:
    C:\\\\User\\My Pictures\\test.png

    \n" +"匹配的正则表达式将突出显示。
    如果至少有一个突出显示,则在扫描过程中将忽略测试的路径或文件名。

    默认情况下,以英文句点“.”开头的目录和文件将被过滤掉。

    " + +#: qt\pe\preferences_dialog.py:24 +msgid "Match pictures of different rotations" +msgstr "" diff --git a/locale/zh_TW/LC_MESSAGES/core.po b/locale/zh_TW/LC_MESSAGES/core.po index fb8603ba..62a65e9a 100644 --- a/locale/zh_TW/LC_MESSAGES/core.po +++ b/locale/zh_TW/LC_MESSAGES/core.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Last-Translator: Chris Ocelot, 2022\n" -"Language-Team: Chinese (Taiwan) (https://www.transifex.com/voltaicideas/teams/116153/zh_TW/)\n" +"Language-Team: Chinese (Taiwan) (https://app.transifex.com/voltaicideas/teams/116153/zh_TW/)\n" "Language: zh_TW\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" @@ -47,95 +47,95 @@ msgstr "复制中" msgid "Sending to Trash" msgstr "正在移至回收站" -#: core\app.py:291 +#: core\app.py:289 msgid "" "A previous action is still hanging in there. You can't start a new one yet. " "Wait a few seconds, then try again." msgstr "前一项操作还在执行,无法启动新操作。请等待几秒钟后再重试一次。" -#: core\app.py:302 +#: core\app.py:300 msgid "No duplicates found." msgstr "没有找到重复文件。" -#: core\app.py:317 +#: core\app.py:315 msgid "All marked files were copied successfully." msgstr "所有已标记的文件已复制成功。" -#: core\app.py:319 +#: core\app.py:317 msgid "All marked files were moved successfully." msgstr "所有已标记的文件已移动成功。" -#: core\app.py:321 +#: core\app.py:319 msgid "All marked files were deleted successfully." msgstr "已复制所有标记文件" -#: core\app.py:323 +#: core\app.py:321 msgid "All marked files were successfully sent to Trash." msgstr "所有已标记的文件已成功移至回收站。" -#: core\app.py:328 +#: core\app.py:326 msgid "Could not load file: {}" msgstr "无法加载文件: {}" -#: core\app.py:384 +#: core\app.py:382 msgid "'{}' already is in the list." msgstr "'{}' 已在列表中。" -#: core\app.py:386 +#: core\app.py:384 msgid "'{}' does not exist." msgstr "'{}' 不存在。" -#: core\app.py:394 +#: core\app.py:392 msgid "" "All selected %d matches are going to be ignored in all subsequent scans. " "Continue?" msgstr "目前已选的 %d 个匹配项将在后续的扫描中被忽略。是否继续?" -#: core\app.py:471 +#: core\app.py:469 msgid "Select a directory to copy marked files to" msgstr "请选择要将标记文件复制到的目录" -#: core\app.py:473 +#: core\app.py:471 msgid "Select a directory to move marked files to" msgstr "请选择要将标记文件移动到的目录" -#: core\app.py:512 +#: core\app.py:510 msgid "Select a destination for your exported CSV" msgstr "选择您导出 CSV 的目标文件夹" -#: core\app.py:518 core\app.py:773 core\app.py:783 +#: core\app.py:516 core\app.py:777 core\app.py:787 msgid "Couldn't write to file: {}" msgstr "不能写入文件: {}" -#: core\app.py:541 +#: core\app.py:539 msgid "You have no custom command set up. Set it up in your preferences." msgstr "您没有设定自定义命令。请在设置中进行设定。" -#: core\app.py:697 core\app.py:709 +#: core\app.py:701 core\app.py:713 msgid "You are about to remove %d files from results. Continue?" msgstr "您将从结果中移除 %d 个文件。是否继续?" -#: core\app.py:745 +#: core\app.py:749 msgid "{} duplicate groups were changed by the re-prioritization." msgstr "{}个重复的组已被重新排列。" -#: core\app.py:792 +#: core\app.py:797 msgid "The selected directories contain no scannable file." msgstr "所选目录中不包含可供扫描的文件。" -#: core\app.py:808 +#: core\app.py:813 msgid "Collecting files to scan" msgstr "收集文件以供扫描" -#: core\app.py:858 +#: core\app.py:863 msgid "%s (%d discarded)" msgstr "%s (%d 项已丢弃)" -#: core\directories.py:190 +#: core\directories.py:191 msgid "Collected {} files to scan" msgstr "收集要扫描的{}文件" -#: core\directories.py:206 +#: core\directories.py:207 msgid "Collected {} folders to scan" msgstr "收集要扫描的{}文件夹" @@ -171,23 +171,23 @@ msgstr "分组比较文件名(不固定顺序(如作者-歌曲名或者歌 msgid "Tags" msgstr "标签" -#: core\me\scanner.py:24 core\pe\scanner.py:21 core\se\scanner.py:17 +#: core\me\scanner.py:24 core\pe\scanner.py:22 core\se\scanner.py:17 msgid "Contents" msgstr "内容" -#: core\pe\matchblock.py:72 +#: core\pe\matchblock.py:66 msgid "Analyzed %d/%d pictures" msgstr "已分析 %d/%d 图像" -#: core\pe\matchblock.py:177 +#: core\pe\matchblock.py:183 msgid "Performed %d/%d chunk matches" msgstr "已执行 %d/%d 个区块匹配" -#: core\pe\matchblock.py:185 +#: core\pe\matchblock.py:191 msgid "Preparing for matching" msgstr "准备进行匹配" -#: core\pe\matchblock.py:234 +#: core\pe\matchblock.py:240 msgid "Verified %d/%d matches" msgstr "已验证 %d/%d 匹配项" @@ -195,7 +195,7 @@ msgstr "已验证 %d/%d 匹配项" msgid "Read EXIF of %d/%d pictures" msgstr "已读取 %d/%d 张图片的 EXIF" -#: core\pe\scanner.py:22 +#: core\pe\scanner.py:23 msgid "EXIF Timestamp" msgstr "EXIF 时间戳" @@ -235,23 +235,19 @@ msgstr "最新" msgid "Oldest" msgstr "最旧" -#: core\results.py:134 +#: core\results.py:135 msgid "%d / %d (%s / %s) duplicates marked." msgstr "已标记 %d / %d (%s / %s) 个重复项。" -#: core\results.py:141 +#: core\results.py:142 msgid " filter: %s" msgstr " 过滤: %s" -#: core\scanner.py:90 -msgid "Read size of %d/%d files" -msgstr "已读取 %d/%d 文件大小" - -#: core\scanner.py:116 +#: core\scanner.py:114 msgid "Read metadata of %d/%d files" msgstr "已读取 %d/%d 文件元数据" -#: core\scanner.py:154 +#: core\scanner.py:152 msgid "Almost done! Fiddling with results..." msgstr "即将完成!整理结果中..." diff --git a/locale/zh_TW/LC_MESSAGES/ui.po b/locale/zh_TW/LC_MESSAGES/ui.po index 044112fd..472ca6be 100644 --- a/locale/zh_TW/LC_MESSAGES/ui.po +++ b/locale/zh_TW/LC_MESSAGES/ui.po @@ -1,12 +1,12 @@ # Translators: # Fuan , 2022 # 太子 VC , 2022 -# Andrew Senetar , 2022 # Chris Ocelot, 2023 +# Andrew Senetar , 2023 # msgid "" msgstr "" -"Last-Translator: Chris Ocelot, 2023\n" +"Last-Translator: Andrew Senetar , 2023\n" "Language-Team: Chinese (Taiwan) (https://app.transifex.com/voltaicideas/teams/116153/zh_TW/)\n" "Language: zh_TW\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -1147,3 +1147,7 @@ msgid "" "
  • 2. Regular expressions with at least one path separator in them will be compared to the full path to the file.

  • Example: if you want to filter out .PNG files from the \"My Pictures\" directory only:
    .*My\\sPictures\\\\.*\\.png

    You can test the regular expression with the \"test string\" button after pasting a fake path in the test field:
    C:\\\\User\\My Pictures\\test.png

    \n" "Matching regular expressions will be highlighted.
    If there is at least one highlight, the path or filename tested will be ignored during scans.

    Directories and files starting with a period '.' are filtered out by default.

    " msgstr "" + +#: qt\pe\preferences_dialog.py:24 +msgid "Match pictures of different rotations" +msgstr "" From 3d866cec9a11c5035a2f4b65df8945774c6daa5b Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Mon, 19 Feb 2024 14:17:40 -0800 Subject: [PATCH 50/56] ci: Add action to push translation source to transifex --- .github/workflows/tx-push.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/tx-push.yml diff --git a/.github/workflows/tx-push.yml b/.github/workflows/tx-push.yml new file mode 100644 index 00000000..0231d4d4 --- /dev/null +++ b/.github/workflows/tx-push.yml @@ -0,0 +1,26 @@ +# Push translation source to Transifex +name: Transifex Sync + +on: + push: + branches: + - master + paths: + - locale/*.pot + +env: + TX_VERSION: "1.6.10" + +jobs: + push-source: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Get Transifex Client + run: | + curl -o- https://raw.githubusercontent.com/transifex/cli/master/install.sh | bash -s -- $TX_VERSION + - name: Update & Push Translation Sources + env: + TX_TOKEN: ${{ secrets.TX_TOKEN }} + run: | + ./tx push -s --use-git-timestamps From a95a9db08bad886931371a9c2c1a6e31613c74f4 Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Mon, 19 Feb 2024 14:22:55 -0800 Subject: [PATCH 51/56] ci: Fix version for tx-push worklflow --- .github/workflows/tx-push.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tx-push.yml b/.github/workflows/tx-push.yml index 0231d4d4..50c34ce3 100644 --- a/.github/workflows/tx-push.yml +++ b/.github/workflows/tx-push.yml @@ -9,7 +9,7 @@ on: - locale/*.pot env: - TX_VERSION: "1.6.10" + TX_VERSION: "v1.6.10" jobs: push-source: From 08154815d0729583e4a603d70b801e09be9f3003 Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Mon, 19 Feb 2024 14:31:04 -0800 Subject: [PATCH 52/56] ci: Upgrade to latest actions & python versions --- .github/workflows/default.yml | 42 +++++++++++++---------------------- 1 file changed, 15 insertions(+), 27 deletions(-) diff --git a/.github/workflows/default.yml b/.github/workflows/default.yml index 4395798e..a465dbfc 100644 --- a/.github/workflows/default.yml +++ b/.github/workflows/default.yml @@ -4,7 +4,6 @@ name: Default CI/CD on: push: - branches: [master] pull_request: branches: [master] @@ -12,46 +11,35 @@ jobs: pre-commit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - name: Set up Python 3.11 - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - name: Set up Python 3.12 + uses: actions/setup-python@v5 with: - python-version: "3.11" - - uses: pre-commit/action@v3.0.0 + python-version: "3.12" + - uses: pre-commit/action@v3.0.1 test: needs: [pre-commit] runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - python-version: [3.7, 3.8, 3.9, "3.10", "3.11"] - exclude: - - os: macos-latest - python-version: 3.7 - - os: macos-latest - python-version: 3.8 - - os: macos-latest - python-version: 3.9 - - os: macos-latest - python-version: "3.10" + os: [ubuntu-latest] + python-version: [3.7, 3.8, 3.9, "3.10", "3.11", "3.12"] + include: - os: windows-latest - python-version: 3.7 - - os: windows-latest - python-version: 3.8 - - os: windows-latest - python-version: 3.9 - - os: windows-latest - python-version: "3.10" + python-version: "3.12" + - os: macos-latest + python-version: "3.12" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip + pip install setuptools pip install -r requirements.txt -r requirements-extra.txt - name: Build python modules run: | @@ -61,7 +49,7 @@ jobs: pytest core hscommon - name: Upload Artifacts if: matrix.os == 'ubuntu-latest' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: modules ${{ matrix.python-version }} path: ${{ github.workspace }}/**/*.so From a81069be61677958b273fad891f8ef097f715209 Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Sat, 11 May 2024 00:11:27 -0700 Subject: [PATCH 53/56] fix: Photo matching fixes - Correct bad query introduced in rotation matching - Promote get_orientation from "private" on photo class - Fix prepare_pictures to only generate the needed blocks, add check for missing blocks when rotation matchin is true - Fix cache test inputs to match schema --- core/pe/cache_sqlite.py | 2 +- core/pe/matchblock.py | 14 ++++++++++---- core/pe/photo.py | 6 +++--- core/tests/cache_test.py | 28 ++++++++++++++-------------- 4 files changed, 28 insertions(+), 22 deletions(-) diff --git a/core/pe/cache_sqlite.py b/core/pe/cache_sqlite.py index 43ffac06..f7020e46 100644 --- a/core/pe/cache_sqlite.py +++ b/core/pe/cache_sqlite.py @@ -158,7 +158,7 @@ def get_multiple(self, rowids): ids = ",".join(map(str, rowids)) sql = ( "select rowid, blocks, blocks2, blocks3, blocks4, blocks5, blocks6, blocks7, blocks8 " - f"from pictures where rowid in {ids}" + f"from pictures where rowid in ({ids})" ) cur = self.con.execute(sql) return ( diff --git a/core/pe/matchblock.py b/core/pe/matchblock.py index f312b2ec..0aa0d253 100644 --- a/core/pe/matchblock.py +++ b/core/pe/matchblock.py @@ -54,7 +54,7 @@ def get_cache(cache_path, readonly=False): return SqliteCache(cache_path, readonly=readonly) -def prepare_pictures(pictures, cache_path, with_dimensions, j=job.nulljob): +def prepare_pictures(pictures, cache_path, with_dimensions, match_rotated, j=job.nulljob): # The MemoryError handlers in there use logging without first caring about whether or not # there is enough memory left to carry on the operation because it is assumed that the # MemoryError happens when trying to read an image file, which is freed from memory by the @@ -76,8 +76,14 @@ def prepare_pictures(pictures, cache_path, with_dimensions, j=job.nulljob): if with_dimensions: picture.dimensions # pre-read dimensions try: - if picture.unicode_path not in cache: - blocks = [picture.get_blocks(BLOCK_COUNT_PER_SIDE, orientation) for orientation in range(1, 9)] + if picture.unicode_path not in cache or ( + match_rotated and any(block == [] for block in cache[picture.unicode_path]) + ): + if match_rotated: + blocks = [picture.get_blocks(BLOCK_COUNT_PER_SIDE, orientation) for orientation in range(1, 9)] + else: + blocks = [[]] * 8 + blocks[max(picture.get_orientation() - 1, 0)] = picture.get_blocks(BLOCK_COUNT_PER_SIDE) cache[picture.unicode_path] = blocks prepared.append(picture) except (OSError, ValueError) as e: @@ -187,7 +193,7 @@ def collect_results(collect_all=False): j.set_progress(comparison_count, progress_msg) j = j.start_subjob([3, 7]) - pictures = prepare_pictures(pictures, cache_path, with_dimensions=not match_scaled, j=j) + pictures = prepare_pictures(pictures, cache_path, not match_scaled, match_rotated, j=j) j = j.start_subjob([9, 1], tr("Preparing for matching")) cache = get_cache(cache_path) id2picture = {} diff --git a/core/pe/photo.py b/core/pe/photo.py index 5bc8356f..06c6c245 100644 --- a/core/pe/photo.py +++ b/core/pe/photo.py @@ -37,7 +37,7 @@ def _plat_get_dimensions(self): def _plat_get_blocks(self, block_count_per_side, orientation): raise NotImplementedError() - def _get_orientation(self): + def get_orientation(self): if not hasattr(self, "_cached_orientation"): try: with self.path.open("rb") as fp: @@ -95,13 +95,13 @@ def _read_info(self, field): fs.File._read_info(self, field) if field == "dimensions": self.dimensions = self._plat_get_dimensions() - if self._get_orientation() in {5, 6, 7, 8}: + if self.get_orientation() in {5, 6, 7, 8}: self.dimensions = (self.dimensions[1], self.dimensions[0]) elif field == "exif_timestamp": self.exif_timestamp = self._get_exif_timestamp() def get_blocks(self, block_count_per_side, orientation: int = None): if orientation is None: - return self._plat_get_blocks(block_count_per_side, self._get_orientation()) + return self._plat_get_blocks(block_count_per_side, self.get_orientation()) else: return self._plat_get_blocks(block_count_per_side, orientation) diff --git a/core/tests/cache_test.py b/core/tests/cache_test.py index 8dbead64..6e9e0e88 100644 --- a/core/tests/cache_test.py +++ b/core/tests/cache_test.py @@ -59,13 +59,13 @@ def test_empty(self): def test_set_then_retrieve_blocks(self): c = self.get_cache() - b = [(0, 0, 0), (1, 2, 3)] + b = [[(0, 0, 0), (1, 2, 3)]] * 8 c["foo"] = b eq_(b, c["foo"]) def test_delitem(self): c = self.get_cache() - c["foo"] = "" + c["foo"] = [[]] * 8 del c["foo"] assert "foo" not in c with raises(KeyError): @@ -74,16 +74,16 @@ def test_delitem(self): def test_persistance(self, tmpdir): DBNAME = tmpdir.join("hstest.db") c = self.get_cache(str(DBNAME)) - c["foo"] = [(1, 2, 3)] + c["foo"] = [[(1, 2, 3)]] * 8 del c c = self.get_cache(str(DBNAME)) - eq_([(1, 2, 3)], c["foo"]) + eq_([[(1, 2, 3)]] * 8, c["foo"]) def test_filter(self): c = self.get_cache() - c["foo"] = "" - c["bar"] = "" - c["baz"] = "" + c["foo"] = [[]] * 8 + c["bar"] = [[]] * 8 + c["baz"] = [[]] * 8 c.filter(lambda p: p != "bar") # only 'bar' is removed eq_(2, len(c)) assert "foo" in c @@ -92,9 +92,9 @@ def test_filter(self): def test_clear(self): c = self.get_cache() - c["foo"] = "" - c["bar"] = "" - c["baz"] = "" + c["foo"] = [[]] * 8 + c["bar"] = [[]] * 8 + c["baz"] = [[]] * 8 c.clear() eq_(0, len(c)) assert "foo" not in c @@ -104,7 +104,7 @@ def test_clear(self): def test_by_id(self): # it's possible to use the cache by referring to the files by their row_id c = self.get_cache() - b = [(0, 0, 0), (1, 2, 3)] + b = [[(0, 0, 0), (1, 2, 3)]] * 8 c["foo"] = b foo_id = c.get_id("foo") eq_(c[foo_id], b) @@ -127,10 +127,10 @@ def test_corrupted_db(self, tmpdir, monkeypatch): fp.write("invalid sqlite content") fp.close() c = self.get_cache(dbname) # should not raise a DatabaseError - c["foo"] = [(1, 2, 3)] + c["foo"] = [[(1, 2, 3)]] * 8 del c c = self.get_cache(dbname) - eq_(c["foo"], [(1, 2, 3)]) + eq_(c["foo"], [[(1, 2, 3)]] * 8) class TestCaseCacheSQLEscape: @@ -152,7 +152,7 @@ def test_setitem(self): def test_delitem(self): c = self.get_cache() - c["foo'bar"] = [] + c["foo'bar"] = [[]] * 8 try: del c["foo'bar"] except KeyError: From e3bcf9d686c6db573a0c86eeab998a918c6ea9f7 Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Sat, 11 May 2024 00:12:19 -0700 Subject: [PATCH 54/56] chore: Update VS Code configuration --- .vscode/launch.json | 2 +- .vscode/settings.json | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 6d3ca8ce..1c21c0fa 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -6,7 +6,7 @@ "configurations": [ { "name": "DupuGuru", - "type": "python", + "type": "debugpy", "request": "launch", "program": "run.py", "console": "integratedTerminal", diff --git a/.vscode/settings.json b/.vscode/settings.json index 7fd9c956..e29b5786 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -12,5 +12,6 @@ "[python]": { "editor.formatOnSave": true, "editor.defaultFormatter": "ms-python.black-formatter" - } + }, + "python.testing.pytestEnabled": true } \ No newline at end of file From 3a97ba941a0e6b69f20ddba227c5909e8f44666b Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Sat, 11 May 2024 01:21:58 -0700 Subject: [PATCH 55/56] ci: Merge artifacts - Merge the resulting artifacts - Use only the .so files from build --- .github/workflows/default.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/default.yml b/.github/workflows/default.yml index a465dbfc..453edc2f 100644 --- a/.github/workflows/default.yml +++ b/.github/workflows/default.yml @@ -52,4 +52,14 @@ jobs: uses: actions/upload-artifact@v4 with: name: modules ${{ matrix.python-version }} - path: ${{ github.workspace }}/**/*.so + path: build/**/*.so + merge-artifacts: + needs: [test] + runs-on: ubuntu-latest + steps: + - name: Merge Artifacts + uses: actions/upload-artifact/merge@v4 + with: + name: modules + pattern: modules* + delete-merged: true From 8f197ea7e1ba8864d616f1493f5d9b94546b74d2 Mon Sep 17 00:00:00 2001 From: Alexander Gee Date: Fri, 23 Aug 2024 18:31:46 -0500 Subject: [PATCH 56/56] feat: Create longest and shortest path criteria (#1242) * Create longest and shortest path criteria --- core/prioritize.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/core/prioritize.py b/core/prioritize.py index fc5de01d..065969a6 100644 --- a/core/prioritize.py +++ b/core/prioritize.py @@ -96,6 +96,8 @@ class FilenameCategory(CriterionCategory): DOESNT_END_WITH_NUMBER = 1 LONGEST = 2 SHORTEST = 3 + LONGEST_PATH = 4 + SHORTEST_PATH = 5 def format_criterion_value(self, value): return { @@ -103,6 +105,8 @@ def format_criterion_value(self, value): self.DOESNT_END_WITH_NUMBER: tr("Doesn't end with number"), self.LONGEST: tr("Longest"), self.SHORTEST: tr("Shortest"), + self.LONGEST_PATH: tr("Longest Path"), + self.SHORTEST_PATH: tr("Shortest Path"), }[value] def extract_value(self, dupe): @@ -116,6 +120,10 @@ def sort_key(self, dupe, crit_value): return 0 if ends_with_digit else 1 else: return 1 if ends_with_digit else 0 + elif crit_value == self.LONGEST_PATH: + return len(str(dupe.folder_path)) * -1 + elif crit_value == self.SHORTEST_PATH: + return len(str(dupe.folder_path)) else: value = len(value) if crit_value == self.LONGEST: @@ -130,6 +138,8 @@ def criteria_list(self): self.DOESNT_END_WITH_NUMBER, self.LONGEST, self.SHORTEST, + self.LONGEST_PATH, + self.SHORTEST_PATH, ] ]