From 2c127adf597485eca3dc3d1169fcac9ba425bf95 Mon Sep 17 00:00:00 2001 From: Virgil Dupras Date: Fri, 21 Jan 2011 13:57:54 +0100 Subject: [PATCH] [#32] Internationalized the qt layer and localized it to French. In the process of doing so, I also added a new preferences_dialog base class to reduce code duplication in the three pref dialogs (I didn't want to copy/paste the language combobox addition three times). --- .hgignore | 2 + core_me/__init__.py | 3 +- core_pe/__init__.py | 3 +- core_se/__init__.py | 1 + qt/base/app.py | 53 ++-- qt/base/details_table.py | 4 +- qt/base/dg.qrc | 1 + qt/base/directories_dialog.py | 36 +-- qt/base/directories_model.py | 5 +- qt/base/preferences.py | 4 + qt/base/preferences_dialog.py | 164 +++++++++++ qt/base/problem_dialog.py | 10 +- qt/base/problem_table.py | 5 +- qt/base/result_window.py | 103 +++---- qt/lang/en.ts | 99 +++++++ qt/lang/fr.qm | Bin 2821 -> 0 bytes qt/lang/fr.ts | 524 ++++++++++++++++++++++++++++++++++ qt/me/app.py | 5 +- qt/me/details_dialog.py | 3 +- qt/me/preferences_dialog.py | 197 +++---------- qt/pe/app.py | 5 +- qt/pe/details_dialog.py | 3 +- qt/pe/preferences_dialog.py | 150 ++-------- qt/pe/result_window.py | 9 +- qt/se/app.py | 5 +- qt/se/details_dialog.py | 3 +- qt/se/preferences_dialog.py | 163 ++--------- run_template_qt.py | 7 +- 28 files changed, 1023 insertions(+), 544 deletions(-) create mode 100644 qt/base/preferences_dialog.py create mode 100644 qt/lang/en.ts delete mode 100644 qt/lang/fr.qm diff --git a/.hgignore b/.hgignore index f411dc1f..ce6e2296 100644 --- a/.hgignore +++ b/.hgignore @@ -17,3 +17,5 @@ cocoa/*/Info.plist cocoa/*/build cocoa/*/dg_cocoa.plugin qt/base/*_rc.py +qt/lang/fr.qm +qt/lang/en.qm \ No newline at end of file diff --git a/core_me/__init__.py b/core_me/__init__.py index 2dc05ef9..03096484 100644 --- a/core_me/__init__.py +++ b/core_me/__init__.py @@ -1 +1,2 @@ -__version__ = '5.10.4' \ No newline at end of file +__version__ = '5.10.4' +__appname__ = 'dupeGuru Music Edition' \ No newline at end of file diff --git a/core_pe/__init__.py b/core_pe/__init__.py index 1f0566c7..b98c616f 100644 --- a/core_pe/__init__.py +++ b/core_pe/__init__.py @@ -1 +1,2 @@ -__version__ = '1.11.3' \ No newline at end of file +__version__ = '1.11.3' +__appname__ = 'dupeGuru Picture Edition' \ No newline at end of file diff --git a/core_se/__init__.py b/core_se/__init__.py index 2de3060d..77fecb3a 100644 --- a/core_se/__init__.py +++ b/core_se/__init__.py @@ -1 +1,2 @@ __version__ = '2.12.3' +__appname__ = 'dupeGuru' diff --git a/qt/base/app.py b/qt/base/app.py index 4ca60aae..a9d8dd0f 100644 --- a/qt/base/app.py +++ b/qt/base/app.py @@ -15,9 +15,10 @@ from PyQt4.QtGui import QDesktopServices, QFileDialog, QDialog, QMessageBox, QAp from jobprogress import job from jobprogress.qt import Progress +from hscommon.trans import tr, trmsg from core.app import DupeGuru as DupeGuruBase, JOB_SCAN, JOB_LOAD, JOB_MOVE, JOB_COPY, JOB_DELETE - + from qtlib.about_box import AboutBox from qtlib.recent import Recent from qtlib.reg import Registration @@ -29,11 +30,11 @@ from .problem_dialog import ProblemDialog from .util import createActions JOBID2TITLE = { - JOB_SCAN: "Scanning for duplicates", - JOB_LOAD: "Loading", - JOB_MOVE: "Moving", - JOB_COPY: "Copying", - JOB_DELETE: "Sending files to the recycle bin", + JOB_SCAN: tr("Scanning for duplicates"), + JOB_LOAD: tr("Loading"), + JOB_MOVE: tr("Moving"), + JOB_COPY: tr("Copying"), + JOB_DELETE: tr("Sending files to the recycle bin"), } class DupeGuru(DupeGuruBase, QObject): @@ -86,13 +87,13 @@ class DupeGuru(DupeGuruBase, QObject): # Setup actions that are common to both the directory dialog and the results window. # (name, shortcut, icon, desc, func) ACTIONS = [ - ('actionQuit', 'Ctrl+Q', '', "Quit", self.quitTriggered), - ('actionPreferences', 'Ctrl+P', '', "Preferences", self.preferencesTriggered), - ('actionShowHelp', 'F1', '', "dupeGuru Help", self.showHelpTriggered), - ('actionAbout', '', '', "About dupeGuru", self.showAboutBoxTriggered), - ('actionRegister', '', '', "Register dupeGuru", self.registerTriggered), - ('actionCheckForUpdate', '', '', "Check for Update", self.checkForUpdateTriggered), - ('actionOpenDebugLog', '', '', "Open Debug Log", self.openDebugLogTriggered), + ('actionQuit', 'Ctrl+Q', '', tr("Quit"), self.quitTriggered), + ('actionPreferences', 'Ctrl+P', '', tr("Preferences"), self.preferencesTriggered), + ('actionShowHelp', 'F1', '', tr("dupeGuru Help"), self.showHelpTriggered), + ('actionAbout', '', '', tr("About dupeGuru"), self.showAboutBoxTriggered), + ('actionRegister', '', '', tr("Register dupeGuru"), self.registerTriggered), + ('actionCheckForUpdate', '', '', tr("Check for Update"), self.checkForUpdateTriggered), + ('actionOpenDebugLog', '', '', tr("Open Debug Log"), self.openDebugLogTriggered), ] createActions(ACTIONS, self) @@ -139,21 +140,21 @@ class DupeGuru(DupeGuruBase, QObject): args = tuple([j] + list(args)) self._progress.run(jobid, title, func, args=args) except job.JobInProgressError: - msg = "A previous action is still hanging in there. You can't start a new one yet. Wait a few seconds, then try again." + msg = trmsg("TaskHangingMsg") QMessageBox.information(self.resultWindow, 'Action in progress', msg) def add_selected_to_ignore_list(self): dupes = self.without_ref(self.selected_dupes) if not dupes: return - title = "Add to Ignore List" - msg = "All selected {0} matches are going to be ignored in all subsequent scans. Continue?".format(len(dupes)) + title = tr("Add to Ignore List") + msg = trmsg("IgnoreConfirmMsg").format(len(dupes)) if self.confirm(title, msg): DupeGuruBase.add_selected_to_ignore_list(self) def copy_or_move_marked(self, copy): - opname = 'copy' if copy else 'move' - title = "Select a directory to {0} marked files to".format(opname) + opname = tr("copy") if copy else tr("move") + title = trmsg("SelectCopyOrMoveDestinationMsg").format(opname) flags = QFileDialog.ShowDirsOnly destination = str(QFileDialog.getExistingDirectory(self.resultWindow, title, '', flags)) if not destination: @@ -165,8 +166,8 @@ class DupeGuru(DupeGuruBase, QObject): dupes = self.without_ref(self.selected_dupes) if not dupes: return - title = "Remove duplicates" - msg = "You are about to remove {0} files from results. Continue?".format(len(dupes)) + title = tr("Remove duplicates") + msg = trmsg("FileRemovalConfirmMsg").format(len(dupes)) if self.confirm(title, msg): DupeGuruBase.remove_selected(self) @@ -185,8 +186,8 @@ class DupeGuru(DupeGuruBase, QObject): if cmd: self.invoke_command(cmd) else: - msg = "You have no custom command set up. Please, set it up in your preferences." - QMessageBox.warning(self.resultWindow, 'Custom Command', msg) + msg = trmsg("NoCustomCommandMsg") + QMessageBox.warning(self.resultWindow, tr("Custom Command"), msg) def show_details(self): self.details_dialog.show() @@ -212,12 +213,12 @@ class DupeGuru(DupeGuruBase, QObject): if self.results.problems: self.problemDialog.show() else: - msg = "All files were processed successfully." - QMessageBox.information(self.resultWindow, 'Operation Complete', msg) + msg = trmsg("OperationSuccessMsg") + QMessageBox.information(self.resultWindow, tr("Operation Complete"), msg) elif jobid == JOB_SCAN: if not self.results.groups: - title = "Scan complete" - msg = "No duplicates found." + title = tr("Scan complete") + msg = trmsg("NoDuplicateFoundMsg") QMessageBox.information(self.resultWindow, title, msg) else: self.showResultsWindow() diff --git a/qt/base/details_table.py b/qt/base/details_table.py index aa5b6c0e..95e38732 100644 --- a/qt/base/details_table.py +++ b/qt/base/details_table.py @@ -9,7 +9,9 @@ from PyQt4.QtCore import Qt, SIGNAL, QAbstractTableModel from PyQt4.QtGui import QHeaderView, QTableView -HEADER = ['Attribute', 'Selected', 'Reference'] +from hscommon.trans import tr + +HEADER = [tr("Attribute"), tr("Selected"), tr("Reference")] class DetailsModel(QAbstractTableModel): def __init__(self, model): diff --git a/qt/base/dg.qrc b/qt/base/dg.qrc index db460c83..836685d2 100644 --- a/qt/base/dg.qrc +++ b/qt/base/dg.qrc @@ -1,5 +1,6 @@ + ../lang/en.qm ../lang/fr.qm ../lang/qt_fr.qm ../../images/dgpe_logo_32.png diff --git a/qt/base/directories_dialog.py b/qt/base/directories_dialog.py index 4655cbf1..d1f09f15 100644 --- a/qt/base/directories_dialog.py +++ b/qt/base/directories_dialog.py @@ -11,6 +11,7 @@ from PyQt4.QtGui import (QWidget, QFileDialog, QHeaderView, QVBoxLayout, QHBoxLa QAbstractItemView, QSpacerItem, QSizePolicy, QPushButton, QApplication, QMessageBox, QMainWindow, QMenuBar, QMenu, QIcon, QPixmap) +from hscommon.trans import tr, trmsg from qtlib.recent import Recent from core.app import NoScannableFileError @@ -49,9 +50,9 @@ class DirectoriesDialog(QMainWindow): def _setupActions(self): # (name, shortcut, icon, desc, func) ACTIONS = [ - ('actionLoadResults', 'Ctrl+L', '', "Load Results...", self.loadResultsTriggered), - ('actionShowResultsWindow', '', '', "Results Window", self.app.showResultsWindow), - ('actionAddFolder', '', '', "Add Folder...", self.addFolderTriggered), + ('actionLoadResults', 'Ctrl+L', '', tr("Load Results..."), self.loadResultsTriggered), + ('actionShowResultsWindow', '', '', tr("Results Window"), self.app.showResultsWindow), + ('actionAddFolder', '', '', tr("Add Folder..."), self.addFolderTriggered), ] createActions(ACTIONS, self) @@ -59,13 +60,13 @@ class DirectoriesDialog(QMainWindow): self.menubar = QMenuBar(self) self.menubar.setGeometry(QRect(0, 0, 42, 22)) self.menuFile = QMenu(self.menubar) - self.menuFile.setTitle("File") + self.menuFile.setTitle(tr("File")) self.menuView = QMenu(self.menubar) - self.menuView.setTitle("View") + self.menuView.setTitle(tr("View")) self.menuHelp = QMenu(self.menubar) - self.menuHelp.setTitle("Help") + self.menuHelp.setTitle(tr("Help")) self.menuLoadRecent = QMenu(self.menuFile) - self.menuLoadRecent.setTitle("Load Recent Results") + self.menuLoadRecent.setTitle(tr("Load Recent Results")) self.setMenuBar(self.menubar) self.menuFile.addAction(self.actionLoadResults) @@ -126,10 +127,10 @@ class DirectoriesDialog(QMainWindow): spacerItem1 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.horizontalLayout.addItem(spacerItem1) self.loadResultsButton = QPushButton(self.centralwidget) - self.loadResultsButton.setText("Load Results") + self.loadResultsButton.setText(tr("Load Results")) self.horizontalLayout.addWidget(self.loadResultsButton) self.scanButton = QPushButton(self.centralwidget) - self.scanButton.setText("Scan") + self.scanButton.setText(tr("Scan")) self.scanButton.setDefault(True) self.horizontalLayout.addWidget(self.scanButton) self.verticalLayout.addLayout(self.horizontalLayout) @@ -167,8 +168,8 @@ class DirectoriesDialog(QMainWindow): def closeEvent(self, event): event.accept() if self.app.results.is_modified: - title = "Unsaved results" - msg = "You have unsaved results, do you really want to quit?" + title = tr("Unsaved results") + msg = trmsg("ReallyWantToQuitMsg") if not self.app.confirm(title, msg): event.ignore() if event.isAccepted(): @@ -176,7 +177,7 @@ class DirectoriesDialog(QMainWindow): #--- Events def addFolderTriggered(self): - title = "Select a directory to add to the scanning list" + title = trmsg("SelectFolderToAddMsg") flags = QFileDialog.ShowDirsOnly dirpath = str(QFileDialog.getExistingDirectory(self, title, self.lastAddedFolder, flags)) if not dirpath: @@ -189,8 +190,8 @@ class DirectoriesDialog(QMainWindow): self.app.prefs.directoriesWindowRect = self.geometry() def loadResultsTriggered(self): - title = "Select a results file to load" - files = "dupeGuru Results (*.dupeguru)" + title = trmsg("SelectResultToLoadMsg") + files = tr("dupeGuru Results (*.dupeguru)") destination = QFileDialog.getOpenFileName(self, title, '', files) if destination: self.app.load_from(destination) @@ -207,15 +208,16 @@ class DirectoriesDialog(QMainWindow): self.app.remove_directory(row) def scanButtonClicked(self): - title = "Start a new scan" + title = tr("Start a new scan") + # XXX must be triggered on unsaved results if len(self.app.results.groups) > 0: - msg = "Are you sure you want to start a new duplicate scan?" + msg = trmsg("ReallyWantToContinueMsg") if not self.app.confirm(title, msg): return try: self.app.start_scanning() except NoScannableFileError: - msg = "The selected directories contain no scannable file." + msg = trmsg("NoScannableFileMsg") QMessageBox.warning(self, title, msg) def selectionChanged(self, selected, deselected): diff --git a/qt/base/directories_model.py b/qt/base/directories_model.py index d1b7c4dd..d9bab5d7 100644 --- a/qt/base/directories_model.py +++ b/qt/base/directories_model.py @@ -12,12 +12,13 @@ from PyQt4.QtCore import QModelIndex, Qt, QRect, QEvent, QPoint, QUrl from PyQt4.QtGui import (QComboBox, QStyledItemDelegate, QMouseEvent, QApplication, QBrush, QStyle, QStyleOptionComboBox, QStyleOptionViewItemV4) +from hscommon.trans import tr from qtlib.tree_model import RefNode, TreeModel from core.gui.directory_tree import DirectoryTree -HEADERS = ['Name', 'State'] -STATES = ['Normal', 'Reference', 'Excluded'] +HEADERS = [tr("Name"), tr("State")] +STATES = [tr("Normal"), tr("Reference"), tr("Excluded")] class DirectoriesDelegate(QStyledItemDelegate): def createEditor(self, parent, option, index): diff --git a/qt/base/preferences.py b/qt/base/preferences.py index 2fecf619..1b63df01 100644 --- a/qt/base/preferences.py +++ b/qt/base/preferences.py @@ -29,6 +29,8 @@ class Preferences(PreferencesBase): self.remove_empty_folders = get('RemoveEmptyFolders', self.remove_empty_folders) self.destination_type = get('DestinationType', self.destination_type) self.custom_command = get('CustomCommand', self.custom_command) + self.language = get('Language', self.language) + widths = get('ColumnsWidth', self.columns_width) # only set nonzero values for index, width in enumerate(widths[:len(self.columns_width)]): @@ -59,6 +61,7 @@ class Preferences(PreferencesBase): self.remove_empty_folders = False self.destination_type = 1 self.custom_command = '' + self.language = '' self.resultWindowIsMaximized = False self.resultWindowRect = None @@ -90,6 +93,7 @@ class Preferences(PreferencesBase): set_('CustomCommand', self.custom_command) set_('ColumnsWidth', self.columns_width) set_('ColumnsVisible', self.columns_visible) + set_('Language', self.language) set_('ResultWindowIsMaximized', self.resultWindowIsMaximized) self.set_rect('ResultWindowRect', self.resultWindowRect) diff --git a/qt/base/preferences_dialog.py b/qt/base/preferences_dialog.py new file mode 100644 index 00000000..c00baec1 --- /dev/null +++ b/qt/base/preferences_dialog.py @@ -0,0 +1,164 @@ +# Created By: Virgil Dupras +# Created On: 2011-01-21 +# Copyright 2011 Hardcoded Software (http://www.hardcoded.net) +# +# This software is licensed under the "BSD" License as described in the "LICENSE" file, +# which should be included with this package. The terms are also available at +# http://www.hardcoded.net/licenses/bsd_license + +import sys +from PyQt4.QtCore import SIGNAL, Qt, QSize +from PyQt4.QtGui import (QDialog, QDialogButtonBox, QVBoxLayout, QHBoxLayout, QLabel, QComboBox, + QSlider, QSizePolicy, QSpacerItem, QCheckBox, QLineEdit, QMessageBox) + +from hscommon.trans import tr, trmsg + +class PreferencesDialogBase(QDialog): + def __init__(self, parent, app): + flags = Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowSystemMenuHint + QDialog.__init__(self, parent, flags) + self.app = app + self._setupUi() + + self.connect(self.filterHardnessSlider, SIGNAL("valueChanged(int)"), self.filterHardnessLabel.setNum) + self.connect(self.buttonBox, SIGNAL('clicked(QAbstractButton*)'), self.buttonClicked) + self.buttonBox.accepted.connect(self.accept) + self.buttonBox.rejected.connect(self.reject) + + def _setupFilterHardnessBox(self): + self.filterHardnessHLayout = QHBoxLayout() + self.filterHardnessLabel = QLabel(self) + self.filterHardnessLabel.setText(tr("Filter Hardness:")) + self.filterHardnessLabel.setMinimumSize(QSize(0, 0)) + self.filterHardnessHLayout.addWidget(self.filterHardnessLabel) + self.filterHardnessVLayout = QVBoxLayout() + self.filterHardnessVLayout.setSpacing(0) + self.filterHardnessHLayoutSub1 = QHBoxLayout() + self.filterHardnessHLayoutSub1.setSpacing(12) + self.filterHardnessSlider = QSlider(self) + sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.filterHardnessSlider.sizePolicy().hasHeightForWidth()) + self.filterHardnessSlider.setSizePolicy(sizePolicy) + self.filterHardnessSlider.setMinimum(1) + self.filterHardnessSlider.setMaximum(100) + self.filterHardnessSlider.setTracking(True) + self.filterHardnessSlider.setOrientation(Qt.Horizontal) + self.filterHardnessHLayoutSub1.addWidget(self.filterHardnessSlider) + self.filterHardnessLabel = QLabel(self) + self.filterHardnessLabel.setText("100") + self.filterHardnessLabel.setMinimumSize(QSize(21, 0)) + self.filterHardnessHLayoutSub1.addWidget(self.filterHardnessLabel) + self.filterHardnessVLayout.addLayout(self.filterHardnessHLayoutSub1) + self.filterHardnessHLayoutSub2 = QHBoxLayout() + self.filterHardnessHLayoutSub2.setContentsMargins(-1, 0, -1, -1) + self.moreResultsLabel = QLabel(self) + self.moreResultsLabel.setText(tr("More Results")) + self.filterHardnessHLayoutSub2.addWidget(self.moreResultsLabel) + spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + self.filterHardnessHLayoutSub2.addItem(spacerItem) + self.fewerResultsLabel = QLabel(self) + self.fewerResultsLabel.setText(tr("Fewer Results")) + self.filterHardnessHLayoutSub2.addWidget(self.fewerResultsLabel) + self.filterHardnessVLayout.addLayout(self.filterHardnessHLayoutSub2) + self.filterHardnessHLayout.addLayout(self.filterHardnessVLayout) + + def _setupBottomPart(self): + # The bottom part of the pref panel is always the same in all editions. + self.languageLabel = QLabel(tr("Language:"), self) + self.widgetsVLayout.addWidget(self.languageLabel) + self.languageComboBox = QComboBox(self) + self.languageComboBox.addItem(tr("English")) + self.languageComboBox.addItem(tr("French")) + self.widgetsVLayout.addWidget(self.languageComboBox) + self.copyMoveLabel = QLabel(self) + self.copyMoveLabel.setText(tr("Copy and Move:")) + self.widgetsVLayout.addWidget(self.copyMoveLabel) + self.copyMoveDestinationComboBox = QComboBox(self) + self.copyMoveDestinationComboBox.addItem(tr("Right in destination")) + self.copyMoveDestinationComboBox.addItem(tr("Recreate relative path")) + self.copyMoveDestinationComboBox.addItem(tr("Recreate absolute path")) + self.widgetsVLayout.addWidget(self.copyMoveDestinationComboBox) + self.customCommandLabel = QLabel(self) + self.customCommandLabel.setText(tr("Custom Command (arguments: %d for dupe, %r for ref):")) + self.widgetsVLayout.addWidget(self.customCommandLabel) + self.customCommandEdit = QLineEdit(self) + self.widgetsVLayout.addWidget(self.customCommandEdit) + + def _setupAddCheckbox(self, name, label, parent=None): + if parent is None: + parent = self + cb = QCheckBox(parent) + cb.setText(label) + setattr(self, name, cb) + + def _setupPreferenceWidgets(self): + # Edition-specific + pass + + def _setupUi(self): + self.setWindowTitle(tr("Preferences")) + self.resize(304, 263) + self.setSizeGripEnabled(False) + self.setModal(True) + self.mainVLayout = QVBoxLayout(self) + self.widgetsVLayout = QVBoxLayout() + self._setupPreferenceWidgets() + self.mainVLayout.addLayout(self.widgetsVLayout) + self.buttonBox = QDialogButtonBox(self) + self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Ok|QDialogButtonBox.RestoreDefaults) + self.mainVLayout.addWidget(self.buttonBox) + if sys.platform not in {'darwin', 'linux2'}: + self.verticalLayout.removeWidget(self.ignoreHardlinkMatches) + self.ignoreHardlinkMatches.setHidden(True) + + def _load(self, prefs, setchecked): + # Edition-specific + pass + + def _save(self, prefs, ischecked): + # Edition-specific + pass + + def load(self, prefs=None): + if prefs is None: + prefs = self.app.prefs + self.filterHardnessSlider.setValue(prefs.filter_hardness) + self.filterHardnessLabel.setNum(prefs.filter_hardness) + setchecked = lambda cb, b: cb.setCheckState(Qt.Checked if b else Qt.Unchecked) + setchecked(self.mixFileKindBox, prefs.mix_file_kind) + setchecked(self.useRegexpBox, prefs.use_regexp) + setchecked(self.removeEmptyFoldersBox, prefs.remove_empty_folders) + setchecked(self.ignoreHardlinkMatches, prefs.ignore_hardlink_matches) + self.copyMoveDestinationComboBox.setCurrentIndex(prefs.destination_type) + self.customCommandEdit.setText(prefs.custom_command) + langindex = {'fr': 1}.get(self.app.prefs.language, 0) + self.languageComboBox.setCurrentIndex(langindex) + self._load(prefs, setchecked) + + def save(self): + prefs = self.app.prefs + prefs.filter_hardness = self.filterHardnessSlider.value() + ischecked = lambda cb: cb.checkState() == Qt.Checked + prefs.mix_file_kind = ischecked(self.mixFileKindBox) + prefs.use_regexp = ischecked(self.useRegexpBox) + prefs.remove_empty_folders = ischecked(self.removeEmptyFoldersBox) + prefs.ignore_hardlink_matches = ischecked(self.ignoreHardlinkMatches) + prefs.destination_type = self.copyMoveDestinationComboBox.currentIndex() + prefs.custom_command = str(self.customCommandEdit.text()) + langs = ['en', 'fr'] + lang = langs[self.languageComboBox.currentIndex()] + oldlang = self.app.prefs.language + if oldlang not in langs: + oldlang = 'en' + if lang != oldlang: + QMessageBox.information(self, "", trmsg("NeedsToRestartToApplyLangMsg")) + self.app.prefs.language = lang + self._save(prefs, ischecked) + + #--- Events + def buttonClicked(self, button): + role = self.buttonBox.buttonRole(button) + if role == QDialogButtonBox.ResetRole: + self.resetToDefaults() \ No newline at end of file diff --git a/qt/base/problem_dialog.py b/qt/base/problem_dialog.py index 68507f08..d94cbcae 100644 --- a/qt/base/problem_dialog.py +++ b/qt/base/problem_dialog.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Created By: Virgil Dupras # Created On: 2010-04-12 # Copyright 2010 Hardcoded Software (http://www.hardcoded.net) @@ -11,6 +10,7 @@ from PyQt4.QtCore import Qt from PyQt4.QtGui import (QDialog, QVBoxLayout, QHBoxLayout, QPushButton, QSpacerItem, QSizePolicy, QLabel, QTableView, QAbstractItemView, QApplication) +from hscommon.trans import tr, trmsg from core.gui.problem_dialog import ProblemDialog as ProblemDialogModel from .problem_table import ProblemTable @@ -29,11 +29,11 @@ class ProblemDialog(QDialog): self.closeButton.clicked.connect(self.accept) def _setupUi(self): - self.setWindowTitle("Problems!") + self.setWindowTitle(tr("Problems!")) self.resize(413, 323) self.verticalLayout = QVBoxLayout(self) self.label = QLabel(self) - self.label.setText("There were problems processing some (or all) of the files. The cause of these problems are described in the table below. Those files were not removed from your results.") + self.label.setText(trmsg("ProblemsDuringProcessingMsg")) self.label.setWordWrap(True) self.verticalLayout.addWidget(self.label) self.tableView = QTableView(self) @@ -47,12 +47,12 @@ class ProblemDialog(QDialog): self.verticalLayout.addWidget(self.tableView) self.horizontalLayout = QHBoxLayout() self.revealButton = QPushButton(self) - self.revealButton.setText("Reveal Selected") + self.revealButton.setText(tr("Reveal Selected")) self.horizontalLayout.addWidget(self.revealButton) spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.horizontalLayout.addItem(spacerItem) self.closeButton = QPushButton(self) - self.closeButton.setText("Close") + self.closeButton.setText(tr("Close")) self.closeButton.setDefault(True) self.horizontalLayout.addWidget(self.closeButton) self.verticalLayout.addLayout(self.horizontalLayout) diff --git a/qt/base/problem_table.py b/qt/base/problem_table.py index 0f4e335b..4f266bcd 100644 --- a/qt/base/problem_table.py +++ b/qt/base/problem_table.py @@ -7,14 +7,15 @@ # which should be included with this package. The terms are also available at # http://www.hardcoded.net/licenses/bsd_license +from hscommon.trans import tr from qtlib.column import Column from qtlib.table import Table from core.gui.problem_table import ProblemTable as ProblemTableModel class ProblemTable(Table): COLUMNS = [ - Column('path', 'File Path', 150), - Column('msg', 'Error Message', 150), + Column('path', tr("File Path"), 150), + Column('msg', tr("Error Message"), 150), ] def __init__(self, problem_dialog, view): diff --git a/qt/base/result_window.py b/qt/base/result_window.py index 05b13ef5..44076928 100644 --- a/qt/base/result_window.py +++ b/qt/base/result_window.py @@ -9,10 +9,11 @@ import sys from PyQt4.QtCore import Qt, SIGNAL, QUrl, QRect -from PyQt4.QtGui import (QMainWindow, QMenu, QPixmap, QIcon, QLabel, QHeaderView, QMessageBox, - QInputDialog, QLineEdit, QDesktopServices, QFileDialog, QMenuBar, QWidget, QVBoxLayout, - QAbstractItemView, QStatusBar) +from PyQt4.QtGui import (QMainWindow, QMenu, QLabel, QHeaderView, QMessageBox, QInputDialog, + QLineEdit, QDesktopServices, QFileDialog, QMenuBar, QWidget, QVBoxLayout, QAbstractItemView, + QStatusBar) +from hscommon.trans import tr, trmsg from hscommon.util import nonone from .results_model import ResultsModel, ResultsView @@ -38,31 +39,31 @@ class ResultWindow(QMainWindow): def _setupActions(self): # (name, shortcut, icon, desc, func) ACTIONS = [ - ('actionDetails', 'Ctrl+I', '', "Details", self.detailsTriggered), - ('actionActions', '', '', "Actions", self.actionsTriggered), - ('actionPowerMarker', 'Ctrl+1', '', "Show Dupes Only", self.powerMarkerTriggered), - ('actionDelta', 'Ctrl+2', '', "Show Delta Values", self.deltaTriggered), - ('actionDeleteMarked', 'Ctrl+D', '', "Send Marked to Recycle Bin", self.deleteTriggered), - ('actionHardlinkMarked', 'Ctrl+Shift+D', '', "Delete Marked and Replace with Hardlinks", self.hardlinkTriggered), - ('actionMoveMarked', 'Ctrl+M', '', "Move Marked to...", self.moveTriggered), - ('actionCopyMarked', 'Ctrl+Shift+M', '', "Copy Marked to...", self.copyTriggered), - ('actionRemoveMarked', 'Ctrl+R', '', "Remove Marked from Results", self.removeMarkedTriggered), - ('actionRemoveSelected', 'Ctrl+Del', '', "Remove Selected from Results", self.removeSelectedTriggered), - ('actionIgnoreSelected', 'Ctrl+Shift+Del', '', "Add Selected to Ignore List", self.addToIgnoreListTriggered), - ('actionMakeSelectedReference', 'Ctrl+Space', '', "Make Selected Reference", self.makeReferenceTriggered), - ('actionOpenSelected', 'Ctrl+O', '', "Open Selected with Default Application", self.openTriggered), - ('actionRevealSelected', 'Ctrl+Shift+O', '', "Open Containing Folder of Selected", self.revealTriggered), - ('actionRenameSelected', 'F2', '', "Rename Selected", self.renameTriggered), - ('actionMarkAll', 'Ctrl+A', '', "Mark All", self.markAllTriggered), - ('actionMarkNone', 'Ctrl+Shift+A', '', "Mark None", self.markNoneTriggered), - ('actionInvertMarking', 'Ctrl+Alt+A', '', "Invert Marking", self.markInvertTriggered), - ('actionMarkSelected', '', '', "Mark Selected", self.markSelectedTriggered), - ('actionClearIgnoreList', '', '', "Clear Ignore List", self.clearIgnoreListTriggered), - ('actionApplyFilter', 'Ctrl+F', '', "Apply Filter", self.applyFilterTriggered), - ('actionCancelFilter', 'Ctrl+Shift+F', '', "Cancel Filter", self.cancelFilterTriggered), - ('actionExport', '', '', "Export To HTML", self.exportTriggered), - ('actionSaveResults', 'Ctrl+S', '', "Save Results...", self.saveResultsTriggered), - ('actionInvokeCustomCommand', 'Ctrl+Alt+I', '', "Invoke Custom Command", self.app.invokeCustomCommand), + ('actionDetails', 'Ctrl+I', '', tr("Details"), self.detailsTriggered), + ('actionActions', '', '', tr("Actions"), self.actionsTriggered), + ('actionPowerMarker', 'Ctrl+1', '', tr("Show Dupes Only"), self.powerMarkerTriggered), + ('actionDelta', 'Ctrl+2', '', tr("Show Delta Values"), self.deltaTriggered), + ('actionDeleteMarked', 'Ctrl+D', '', tr("Send Marked to Recycle Bin"), self.deleteTriggered), + ('actionHardlinkMarked', 'Ctrl+Shift+D', '', tr("Delete Marked and Replace with Hardlinks"), self.hardlinkTriggered), + ('actionMoveMarked', 'Ctrl+M', '', tr("Move Marked to..."), self.moveTriggered), + ('actionCopyMarked', 'Ctrl+Shift+M', '', tr("Copy Marked to..."), self.copyTriggered), + ('actionRemoveMarked', 'Ctrl+R', '', tr("Remove Marked from Results"), self.removeMarkedTriggered), + ('actionRemoveSelected', 'Ctrl+Del', '', tr("Remove Selected from Results"), self.removeSelectedTriggered), + ('actionIgnoreSelected', 'Ctrl+Shift+Del', '', tr("Add Selected to Ignore List"), self.addToIgnoreListTriggered), + ('actionMakeSelectedReference', 'Ctrl+Space', '', tr("Make Selected Reference"), self.makeReferenceTriggered), + ('actionOpenSelected', 'Ctrl+O', '', tr("Open Selected with Default Application"), self.openTriggered), + ('actionRevealSelected', 'Ctrl+Shift+O', '', tr("Open Containing Folder of Selected"), self.revealTriggered), + ('actionRenameSelected', 'F2', '', tr("Rename Selected"), self.renameTriggered), + ('actionMarkAll', 'Ctrl+A', '', tr("Mark All"), self.markAllTriggered), + ('actionMarkNone', 'Ctrl+Shift+A', '', tr("Mark None"), self.markNoneTriggered), + ('actionInvertMarking', 'Ctrl+Alt+A', '', tr("Invert Marking"), self.markInvertTriggered), + ('actionMarkSelected', '', '', tr("Mark Selected"), self.markSelectedTriggered), + ('actionClearIgnoreList', '', '', tr("Clear Ignore List"), self.clearIgnoreListTriggered), + ('actionApplyFilter', 'Ctrl+F', '', tr("Apply Filter"), self.applyFilterTriggered), + ('actionCancelFilter', 'Ctrl+Shift+F', '', tr("Cancel Filter"), self.cancelFilterTriggered), + ('actionExport', '', '', tr("Export To HTML"), self.exportTriggered), + ('actionSaveResults', 'Ctrl+S', '', tr("Save Results..."), self.saveResultsTriggered), + ('actionInvokeCustomCommand', 'Ctrl+Alt+I', '', tr("Invoke Custom Command"), self.app.invokeCustomCommand), ] createActions(ACTIONS, self) self.actionDelta.setCheckable(True) @@ -72,17 +73,17 @@ class ResultWindow(QMainWindow): self.menubar = QMenuBar(self) self.menubar.setGeometry(QRect(0, 0, 630, 22)) self.menuFile = QMenu(self.menubar) - self.menuFile.setTitle("File") + self.menuFile.setTitle(tr("File")) self.menuMark = QMenu(self.menubar) - self.menuMark.setTitle("Mark") + self.menuMark.setTitle(tr("Mark")) self.menuActions = QMenu(self.menubar) - self.menuActions.setTitle("Actions") + self.menuActions.setTitle(tr("Actions")) self.menuColumns = QMenu(self.menubar) - self.menuColumns.setTitle("Columns") + self.menuColumns.setTitle(tr("Columns")) self.menuView = QMenu(self.menubar) - self.menuView.setTitle("View") + self.menuView.setTitle(tr("View")) self.menuHelp = QMenu(self.menubar) - self.menuHelp.setTitle("Help") + self.menuHelp.setTitle(tr("Help")) self.setMenuBar(self.menubar) self.menuActions.addAction(self.actionDeleteMarked) @@ -138,11 +139,11 @@ class ResultWindow(QMainWindow): action.column_index = index self._column_actions.append(action) menu.addSeparator() - action = menu.addAction("Reset to Defaults") + action = menu.addAction(tr("Reset to Defaults")) action.column_index = -1 # Action menu - actionMenu = QMenu('Actions', self.menubar) + actionMenu = QMenu(tr("Actions"), self.menubar) actionMenu.addAction(self.actionDeleteMarked) actionMenu.addAction(self.actionHardlinkMarked) actionMenu.addAction(self.actionMoveMarked) @@ -160,7 +161,7 @@ class ResultWindow(QMainWindow): self.actionActions.setMenu(actionMenu) def _setupUi(self): - self.setWindowTitle("dupeGuru Results") + self.setWindowTitle(tr("dupeGuru Results")) self.resize(630, 514) self.centralwidget = QWidget(self) self.verticalLayout_2 = QVBoxLayout(self.centralwidget) @@ -222,8 +223,8 @@ class ResultWindow(QMainWindow): self.app.add_selected_to_ignore_list() def applyFilterTriggered(self): - title = "Apply Filter" - msg = "Type the filter you want to apply on your results. See help for details." + title = tr("Apply Filter") + msg = trmsg("TypeFilterMsg") text = nonone(self._last_filter, '[*]') answer, ok = QInputDialog.getText(self, title, msg, QLineEdit.Normal, text) if not ok: @@ -236,15 +237,15 @@ class ResultWindow(QMainWindow): self.app.apply_filter('') def clearIgnoreListTriggered(self): - title = "Clear Ignore List" + title = tr("Clear Ignore List") count = len(self.app.scanner.ignore_list) if not count: - QMessageBox.information(self, title, "Nothing to clear.") + QMessageBox.information(self, title, trmsg("NothingToClearMsg")) return - msg = "Do you really want to remove all {0} items from the ignore list?".format(count) + msg = trmsg("ClearIgnoreListConfirmMsg").format(count) if self.app.confirm(title, msg, QMessageBox.No): self.app.scanner.ignore_list.Clear() - QMessageBox.information(self, title, "Ignore list cleared.") + QMessageBox.information(self, title, trmsg("IgnoreListClearedMsg")) def copyTriggered(self): self.app.copy_or_move_marked(True) @@ -253,8 +254,8 @@ class ResultWindow(QMainWindow): count = self.app.results.mark_count if not count: return - title = "Delete duplicates" - msg = "You are about to send {0} files to the recycle bin. Continue?".format(count) + title = tr("Delete duplicates") + msg = trmsg("SendToTrashConfirmMsg").format(count) if self.app.confirm(title, msg): self.app.delete_marked() @@ -278,8 +279,8 @@ class ResultWindow(QMainWindow): count = self.app.results.mark_count if not count: return - title = "Delete and hardlink duplicates" - msg = "You are about to send {0} files to the trash and hardlink them afterwards. Continue?".format(count) + title = tr("Delete and hardlink duplicates") + msg = trmsg("HardlinkConfirmMsg").format(count) if self.app.confirm(title, msg): self.app.delete_marked(replace_with_hardlinks=True) @@ -314,8 +315,8 @@ class ResultWindow(QMainWindow): count = self.app.results.mark_count if not count: return - title = "Remove duplicates" - msg = "You are about to remove {0} files from results. Continue?".format(count) + title = tr("Remove duplicates") + msg = trmsg("FileRemovalConfirmMsg").format(count) if self.app.confirm(title, msg): self.app.remove_marked() @@ -329,8 +330,8 @@ class ResultWindow(QMainWindow): self.app.reveal_selected() def saveResultsTriggered(self): - title = "Select a file to save your results to" - files = "dupeGuru Results (*.dupeguru)" + title = trmsg("SelectResultToSaveMsg") + files = tr("dupeGuru Results (*.dupeguru)") destination = QFileDialog.getSaveFileName(self, title, '', files) if destination: self.app.save_as(destination) diff --git a/qt/lang/en.ts b/qt/lang/en.ts new file mode 100644 index 00000000..6b929db7 --- /dev/null +++ b/qt/lang/en.ts @@ -0,0 +1,99 @@ + + + + +message + + TaskHangingMsg + A previous action is still hanging in there. You can't start a new one yet. Wait a few seconds, then try again. + + + IgnoreConfirmMsg + All selected {} matches are going to be ignored in all subsequent scans. Continue? + + + SelectCopyOrMoveDestinationMsg + Select a directory to {} marked files to + + + FileRemovalConfirmMsg + You are about to remove {} files from results. Continue? + + + NoCustomCommandMsg + You have no custom command set up. Please, set it up in your preferences. + + + OperationSuccessMsg + All files were processed successfully. + + + NoDuplicateFoundMsg + No duplicates found. + + + ReallyWantToQuitMsg + You have unsaved results, do you really want to quit? + + + SelectFolderToAddMsg + Select a folder to add to the scanning list + + + SelectResultToLoadMsg + Select a results file to load + + + ReallyWantToContinueMsg + You have unsaved results, do you really want to continue? + + + NoScannableFileMsg + The selected folders contain no scannable file. + + + ProblemsDuringProcessingMsg + There were problems processing some (or all) of the files. The cause of these problems are described in the table below. Those files were not removed from your results. + + + TypeFilterMsg + Type the filter you want to apply on your results. See help for details. + + + NothingToClearMsg + Nothing to clear. + + + ClearIgnoreListConfirmMsg + Do you really want to remove all {} items from the ignore list? + + + IgnoreListClearedMsg + Ignore list cleared. + + + SendToTrashConfirmMsg + You are about to send {} files to the recycle bin. Continue? + + + HardlinkConfirmMsg + You are about to send {} files to the trash and hardlink them afterwards. Continue? + + + SelectResultToSaveMsg + Select a file to save your results to + + + NeedsToRestartToApplyLangMsg + dupeGuru has to restart for language changes to take effect. + + + ClearPictureCacheConfirmMsg + Do you really want to remove all your cached picture analysis? + + + PictureCacheClearedMsg + Picture cache cleared. + + + \ No newline at end of file diff --git a/qt/lang/fr.qm b/qt/lang/fr.qm deleted file mode 100644 index d4dcd62669b6bb2dd67bf56a0cf81f839f9ecd48..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2821 zcmb7GU1%It6h6r|n{XE z4QyVE;;Vn)gCK%f6(0m26r}i|LMSTrT}2QP=|jQln-sruXLqu*YnDpb%+8&;-}%mW zzH{y_ejZHRxc&B<-%U+^dF9>fKb<9_9U46O0MW=HYQ8aw`-Q8rEMOI(PFt(Z1Z!&Raj=`dR+k%d^-Y=YL!N3;VCBw~8&3pJ)oR0ZRJnIFGsHS{jW$e|3*_6Rtmco%1 zg$R(!(dY1-2nXFl0NIhsL15Q~^{ap@>qZ!4!H%}ra~7OU6(9x-k5Q!s7y)iJ<$DM; zi+~JN>;mNy-_=GydSe7oq=`&8a8PgNpXb&=$`OY#EMojsg9osMXk9l z1L!#qj*;?h!^o}*o~9+_Si_$rAF@dx2aJO&d%}kX3iqsnN59|`V@-fd$g8Om6HB^Q zlfG49h1~*qt@a>Fm=$Ewg^L`d2Y?WOzN-`hq+r8KI%)zbyY~dGf`)P>QJ|_Igm723 z)h_MMQte_MUe?U5Vss<&BBQiXR&5KbbQi_PGsNuq^$fNWBWe z#?`6rlDD9}rwA)r1U(`Yoq%=cDRWOg7`y2F-rojAS_W7I8wk`x!cI)9AHDoVz173+ zv?~z}O21B|U2841G$D{GVVMNhD5ONu)BJOl*Wl3kIw zL7?~9(jHb;T_amO>5-e}u=Xs?psjdn@XTP#OL!}3+?b|PS|?VG-e6K{wW_V2^Okiu zW8w6MU-gv5ta6lXReCy-#+hp>>w+tE2|}}tlkV!I%zc;b>#X|%B$&)kE)yqK!97nX z-dDqYOg1Gu53;_D<8D`aq9RlngEnZYN{CtR)l_V=JTK4+q+rx!7SzGoA}Y^v?tSNf zJn!Y)H8oN`lO-uIe5DnEu)@>j)?9MaA{%@$4bEwDSU#MX%Kr&C;R42L8`bC&tV|^U z^MS!(*z)c9m#q1;>1JQuDa{j-Ct;HJ7S@t8Gtqyeg~Ih^vWnK7IXjP_VXnO zh&96B#9?G~-D~QG@zi=F#Q0oM8%CIBR+y7}a?LtLp+Z~8hdtr4NvklHMlXw6EZav; zHq4W>=DMjpRXN@6BYdy1HNJ+SAGSMp_w>$2xX=?Ju{0CzBO)Z-EAGQ-T<7aPB9`cB zCyz#~b|HK7 core + Collecting files to scan Collecte des fichiers à scanner @@ -107,5 +108,528 @@ Sending dupes to the Trash Envoi de doublons à la corbeille en cours + + + + Sending files to the recycle bin + Envoi de fichiers à la corbeille + + + Quit + Quitter + + + Preferences + Préférences + + + dupeGuru Help + Aide dupeGuru + + + About dupeGuru + À propos de dupeGuru + + + Register dupeGuru + Enregistrer dupeGuru + + + Check for Update + Vérifier les mises à jour + + + Open Debug Log + Ouvrir logs de déboguage + + + Add to Ignore List + Ignorer ces doublons à l'avenir + + + copy + copier + + + move + déplacer + + + Remove duplicates + Retirer des doublons + + + Custom Command + Commande personnalisée + + + Operation Complete + Opération complétée + + + Scan complete + Scan complété + + + Attribute + Attribut + + + Selected + Sélectionné + + + Reference + Référence + + + Load Results... + Charger résultats... + + + Results Window + Fenêtre de résultats + + + Add Folder... + Ajouter dossier... + + + File + Fichier + + + View + Voir + + + Help + Aide + + + Load Recent Results + Charger résultats récents + + + Load Results + Charger + + + Scan + Scan + + + Unsaved results + Résultats non sauvegardés + + + dupeGuru Results (*.dupeguru) + Résultats dupeGuru (*.dupeguru) + + + Start a new scan + Commencer un nouveau scan + + + Name + Nom + + + State + Type + + + Normal + Normal + + + Excluded + Exclus + + + Problems! + Problèmes! + + + Reveal Selected + Révéler Fichier + + + Close + Fermer + + + File Path + Chemin du fichier + + + Error Message + Message d'erreur + + + Details + Détails + + + Actions + Actions + + + Show Dupes Only + Ne pas montrer les références + + + Show Delta Values + Montrer les valeurs en tant que delta + + + Send Marked to Recycle Bin + Envoyer marqués à la corbeille + + + Delete Marked and Replace with Hardlinks + Remplacer marqués par des hardlinks + + + Move Marked to... + Déplacer marqués vers... + + + Copy Marked to... + Copier marqués vers... + + + Remove Marked from Results + Retirer marqués des résultats + + + Remove Selected from Results + Retirer sélectionnés des résultats + + + Add Selected to Ignore List + Ajouter sélectionnés à la liste de fichiers ignorés + + + Make Selected Reference + Transformer sélectionnés en références + + + Open Selected with Default Application + Ouvrir sélectionné avec l'application par défaut + + + Open Containing Folder of Selected + Ouvrir le dossier contenant le fichier sélectionné + + + Rename Selected + Renommer sélectionné + + + Mark All + Tout marquer + + + Mark None + Tout démarquer + + + Invert Marking + Inverser le marquage + + + Mark Selected + Marquer sélectionnés + + + Clear Ignore List + Vider la liste de fichiers ignorés + + + Apply Filter + Appliquer filtre + + + Cancel Filter + Annuler filtre + + + Export To HTML + Exporter vers HTML + + + Save Results... + Sauvegarder résultats... + + + Invoke Custom Command + Invoquer commande personnalisée + + + Mark + Marquer + + + Columns + Colonnes + + + Reset to Defaults + Réinitialiser + + + dupeGuru Results + dupeGuru (Résultats) + + + Delete duplicates + Effacement de doublons + + + Delete and hardlink duplicates + Hardlinking de doublons + + + Scan Type: + Type de scan: + + + Filename + Nom de fichier + + + Contents + Contenu + + + Filter Hardness: + Seuil du filtre: + + + More Results + + de doublons + + + Fewer Results + - de doublons + + + Word weighting + Proportionalité des mots + + + Match similar words + Comparer les mots similaires + + + Can mix file kind + Comparer les fichiers de différents types + + + Use regular expressions when filtering + Utiliser les expressions régulières pour les filtres + + + Remove empty folders on delete or move + Effacer les dossiers vides après un déplacement + + + Ignore files smaller than + Ignorer les fichiers plus petits que: + + + KB + KB + + + Ignore duplicates hardlinking to the same file + Ignorer doublons avec hardlink vers le même fichier + + + Copy and Move: + Déplacements de fichiers: + + + Right in destination + Directement à la destination + + + Recreate relative path + Re-créer chemins relatifs + + + Recreate absolute path + Re-créer chemins absolus + + + Custom Command (arguments: %d for dupe, %r for ref): + Commande perso. (arguments: %d pour doublon, %r pour réf): + + + Filename - Fields + Nom de fichier (Champs) + + + Filename - Fields (No Order) + Nom de fichier (Champs sans ordre) + + + Tags + Tags + + + Audio Contents + Contenu Audio + + + Tags to scan: + Tags à scanner: + + + Track + Track + + + Artist + Artiste + + + Album + Album + + + Title + Titre + + + Genre + Genre + + + Year + Année + + + Match scaled pictures together + Comparer les images de taille différente + + + Clear Picture Cache + Vider la cache d'images + + + Clear List + Vider la liste + + + Language: + Langue: + + + English + Anglais + + + French + Français + + + + + +message + + TaskHangingMsg + Une action précédente est encore en cours. Attendez quelques secondes avant d'en repartir une nouvelle. + + + IgnoreConfirmMsg + %d fichiers seront ignorés des prochains scans. Continuer? + + + SelectCopyOrMoveDestinationMsg + Sélectionnez un dossier vers lequel {} les fichiers marqués. + + + FileRemovalConfirmMsg + {} fichiers seront retirés des résultats. Continuer? + + + NoCustomCommandMsg + Vous n'avez pas de commande personnalisée. Ajoutez-la dans vos préférences. + + + OperationSuccessMsg + Tous les fichiers ont été traités avec succès. + + + NoDuplicateFoundMsg + Aucun doublon trouvé. + + + ReallyWantToQuitMsg + Vos résultats ne sont pas sauvegardés. Voulez-vous vraiment quitter? + + + SelectFolderToAddMsg + Sélectionnez un dossier à ajouter à la liste + + + SelectResultToLoadMsg + Sélectionnez un fichier résultats à charger + + + ReallyWantToContinueMsg + Vos résultats ne sont pas sauvegardés. Voulez-vous vraiment continuer? + + + NoScannableFileMsg + Les dossiers sélectionnés ne continnent pas de fichiers valides. + + + ProblemsDuringProcessingMsg + Des problèmes ont été rencontrés lors du traitement de certains fichiers. La nature de ces problèmes est décrite dans la liste ci-dessous. Ces fichiers n'ont pas été retirés des résultats. + + + TypeFilterMsg + Entrer le filtre que vous voulez appliquer sur vos résultats. + + + NothingToClearMsg + Il n'y a rien à vider. + + + ClearIgnoreListConfirmMsg + Voulez-vous vider la liste de fichiers ignorés des {} items qu'elle contient? + + + IgnoreListClearedMsg + La liste de doublons ignorés a été vidée. + + + SendToTrashConfirmMsg + {} fichiers seront envoyés à la corbeille. Continuer? + + + HardlinkConfirmMsg + {} fichiers seront envoyés à la corbeille (puis 'hardlinkés'). Continuer? + + + SelectResultToSaveMsg + Sélectionnez un fichier résultats dans lequel sauvegarder + + + NeedsToRestartToApplyLangMsg + dupeGuru doit redémarrer pour appliquer le changement de langue. + + + ClearPictureCacheConfirmMsg + Voulez-vous vraiment vider la cache de vos analyses précédentes? + + + PictureCacheClearedMsg + La cache des analyses précédentes a été vidée. + \ No newline at end of file diff --git a/qt/me/app.py b/qt/me/app.py index 676fc249..03e2c07b 100644 --- a/qt/me/app.py +++ b/qt/me/app.py @@ -6,7 +6,7 @@ # which should be included with this package. The terms are also available at # http://www.hardcoded.net/licenses/bsd_license -from core_me import data, scanner, fs, __version__ +from core_me import data, scanner, fs, __appname__ from ..base.app import DupeGuru as DupeGuruBase from .details_dialog import DetailsDialog @@ -16,8 +16,7 @@ from .preferences_dialog import PreferencesDialog class DupeGuru(DupeGuruBase): EDITION = 'me' LOGO_NAME = 'logo_me' - NAME = 'dupeGuru Music Edition' - VERSION = __version__ + NAME = __appname__ DELTA_COLUMNS = frozenset([2, 3, 4, 5, 7]) def __init__(self): diff --git a/qt/me/details_dialog.py b/qt/me/details_dialog.py index 3a33cd2f..70e694a1 100644 --- a/qt/me/details_dialog.py +++ b/qt/me/details_dialog.py @@ -9,12 +9,13 @@ from PyQt4.QtCore import QSize from PyQt4.QtGui import QVBoxLayout, QAbstractItemView +from hscommon.trans import tr from ..base.details_dialog import DetailsDialog as DetailsDialogBase from ..base.details_table import DetailsTable class DetailsDialog(DetailsDialogBase): def _setupUi(self): - self.setWindowTitle("Details") + self.setWindowTitle(tr("Details")) self.resize(502, 295) self.setMinimumSize(QSize(250, 250)) self.verticalLayout = QVBoxLayout(self) diff --git a/qt/me/preferences_dialog.py b/qt/me/preferences_dialog.py index f80e6a8f..77edca4b 100644 --- a/qt/me/preferences_dialog.py +++ b/qt/me/preferences_dialog.py @@ -7,12 +7,14 @@ # http://www.hardcoded.net/licenses/bsd_license import sys -from PyQt4.QtCore import SIGNAL, Qt, QSize -from PyQt4.QtGui import (QDialog, QDialogButtonBox, QVBoxLayout, QHBoxLayout, QLabel, QComboBox, - QSlider, QSizePolicy, QSpacerItem, QWidget, QCheckBox, QLineEdit, QDialogButtonBox, QApplication) +from PyQt4.QtCore import SIGNAL, QSize +from PyQt4.QtGui import (QVBoxLayout, QHBoxLayout, QLabel, QComboBox, QSizePolicy, QSpacerItem, + QWidget, QApplication) +from hscommon.trans import tr from core.scanner import ScanType +from ..base.preferences_dialog import PreferencesDialogBase from . import preferences SCAN_TYPE_ORDER = [ @@ -24,169 +26,73 @@ SCAN_TYPE_ORDER = [ ScanType.ContentsAudio, ] -class PreferencesDialog(QDialog): +class PreferencesDialog(PreferencesDialogBase): def __init__(self, parent, app): - flags = Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowSystemMenuHint - QDialog.__init__(self, parent, flags) - self.app = app - self._setupUi() + PreferencesDialogBase.__init__(self, parent, app) - self.connect(self.buttonBox, SIGNAL('clicked(QAbstractButton*)'), self.buttonClicked) self.connect(self.scanTypeComboBox, SIGNAL('currentIndexChanged(int)'), self.scanTypeChanged) - self.connect(self.filterHardnessSlider, SIGNAL("valueChanged(int)"), self.filterHardnessLabel.setNum) - self.buttonBox.accepted.connect(self.accept) - self.buttonBox.rejected.connect(self.reject) - def _setupUi(self): - self.setWindowTitle("Preferences") - self.resize(325, 360) - self.setSizeGripEnabled(False) - self.setModal(True) - self.verticalLayout_2 = QVBoxLayout(self) - self.verticalLayout = QVBoxLayout() + def _setupPreferenceWidgets(self): self.horizontalLayout = QHBoxLayout() self.label_2 = QLabel(self) - self.label_2.setText("Scan Type:") + self.label_2.setText(tr("Scan Type:")) self.label_2.setMinimumSize(QSize(100, 0)) self.label_2.setMaximumSize(QSize(100, 16777215)) self.horizontalLayout.addWidget(self.label_2) self.scanTypeComboBox = QComboBox(self) - self.scanTypeComboBox.addItem("Filename") - self.scanTypeComboBox.addItem("Filename - Fields") - self.scanTypeComboBox.addItem("Filename - Fields (No Order)") - self.scanTypeComboBox.addItem("Tags") - self.scanTypeComboBox.addItem("Contents") - self.scanTypeComboBox.addItem("Audio Contents") + self.scanTypeComboBox.addItem(tr("Filename")) + self.scanTypeComboBox.addItem(tr("Filename - Fields")) + self.scanTypeComboBox.addItem(tr("Filename - Fields (No Order)")) + self.scanTypeComboBox.addItem(tr("Tags")) + self.scanTypeComboBox.addItem(tr("Contents")) + self.scanTypeComboBox.addItem(tr("Audio Contents")) self.horizontalLayout.addWidget(self.scanTypeComboBox) - self.verticalLayout.addLayout(self.horizontalLayout) - self.horizontalLayout_3 = QHBoxLayout() - self.label = QLabel(self) - self.label.setText("Filter Hardness:") - self.label.setMinimumSize(QSize(100, 0)) - self.label.setMaximumSize(QSize(100, 16777215)) - self.horizontalLayout_3.addWidget(self.label) - self.verticalLayout_3 = QVBoxLayout() - self.verticalLayout_3.setSpacing(0) - self.horizontalLayout_6 = QHBoxLayout() - self.horizontalLayout_6.setSpacing(12) - self.filterHardnessSlider = QSlider(self) - sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.filterHardnessSlider.sizePolicy().hasHeightForWidth()) - self.filterHardnessSlider.setSizePolicy(sizePolicy) - self.filterHardnessSlider.setMinimum(1) - self.filterHardnessSlider.setMaximum(100) - self.filterHardnessSlider.setTracking(True) - self.filterHardnessSlider.setOrientation(Qt.Horizontal) - self.horizontalLayout_6.addWidget(self.filterHardnessSlider) - self.filterHardnessLabel = QLabel(self) - self.filterHardnessLabel.setText("100") - self.filterHardnessLabel.setMinimumSize(QSize(21, 0)) - self.horizontalLayout_6.addWidget(self.filterHardnessLabel) - self.verticalLayout_3.addLayout(self.horizontalLayout_6) - self.horizontalLayout_5 = QHBoxLayout() - self.horizontalLayout_5.setContentsMargins(-1, 0, -1, -1) - self.label_4 = QLabel(self) - self.label_4.setText("More Results") - self.horizontalLayout_5.addWidget(self.label_4) - spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) - self.horizontalLayout_5.addItem(spacerItem) - self.label_3 = QLabel(self) - self.label_3.setText("Fewer Results") - self.horizontalLayout_5.addWidget(self.label_3) - self.verticalLayout_3.addLayout(self.horizontalLayout_5) - self.horizontalLayout_3.addLayout(self.verticalLayout_3) - self.verticalLayout.addLayout(self.horizontalLayout_3) + self.widgetsVLayout.addLayout(self.horizontalLayout) + self._setupFilterHardnessBox() + self.widgetsVLayout.addLayout(self.filterHardnessHLayout) self.widget = QWidget(self) self.widget.setMinimumSize(QSize(0, 40)) self.verticalLayout_4 = QVBoxLayout(self.widget) self.verticalLayout_4.setSpacing(0) self.verticalLayout_4.setMargin(0) self.label_6 = QLabel(self.widget) - self.label_6.setText("Tags to scan:") + self.label_6.setText(tr("Tags to scan:")) self.verticalLayout_4.addWidget(self.label_6) self.horizontalLayout_2 = QHBoxLayout() self.horizontalLayout_2.setSpacing(0) spacerItem1 = QSpacerItem(15, 20, QSizePolicy.Fixed, QSizePolicy.Minimum) self.horizontalLayout_2.addItem(spacerItem1) - self.tagTrackBox = QCheckBox(self.widget) - self.tagTrackBox.setText("Track") + self._setupAddCheckbox('tagTrackBox', tr("Track"), self.widget) self.horizontalLayout_2.addWidget(self.tagTrackBox) - self.tagArtistBox = QCheckBox(self.widget) - self.tagArtistBox.setText("Artist") + self._setupAddCheckbox('tagArtistBox', tr("Artist"), self.widget) self.horizontalLayout_2.addWidget(self.tagArtistBox) - self.tagAlbumBox = QCheckBox(self.widget) - self.tagAlbumBox.setText("Album") + self._setupAddCheckbox('tagAlbumBox', tr("Album"), self.widget) self.horizontalLayout_2.addWidget(self.tagAlbumBox) - self.tagTitleBox = QCheckBox(self.widget) - self.tagTitleBox.setText("Title") + self._setupAddCheckbox('tagTitleBox', tr("Title"), self.widget) self.horizontalLayout_2.addWidget(self.tagTitleBox) - self.tagGenreBox = QCheckBox(self.widget) - self.tagGenreBox.setText("Genre") + self._setupAddCheckbox('tagGenreBox', tr("Genre"), self.widget) self.horizontalLayout_2.addWidget(self.tagGenreBox) - self.tagYearBox = QCheckBox(self.widget) - self.tagYearBox.setText("Year") + self._setupAddCheckbox('tagYearBox', tr("Year"), self.widget) self.horizontalLayout_2.addWidget(self.tagYearBox) self.verticalLayout_4.addLayout(self.horizontalLayout_2) - self.verticalLayout.addWidget(self.widget) - self.wordWeightingBox = QCheckBox(self) - self.wordWeightingBox.setText("Word weighting") - self.verticalLayout.addWidget(self.wordWeightingBox) - self.matchSimilarBox = QCheckBox(self) - self.matchSimilarBox.setText("Match similar words") - self.verticalLayout.addWidget(self.matchSimilarBox) - self.mixFileKindBox = QCheckBox(self) - self.mixFileKindBox.setText("Can mix file kind") - self.verticalLayout.addWidget(self.mixFileKindBox) - self.useRegexpBox = QCheckBox(self) - self.useRegexpBox.setText("Use regular expressions when filtering") - self.verticalLayout.addWidget(self.useRegexpBox) - self.removeEmptyFoldersBox = QCheckBox(self) - self.removeEmptyFoldersBox.setText("Remove empty folders on delete or move") - self.verticalLayout.addWidget(self.removeEmptyFoldersBox) - self.ignoreHardlinkMatches = QCheckBox(self) - self.ignoreHardlinkMatches.setText("Ignore duplicates hardlinking to the same file") - self.verticalLayout.addWidget(self.ignoreHardlinkMatches) - self.horizontalLayout_4 = QHBoxLayout() - self.label_5 = QLabel(self) - self.label_5.setText("Copy and Move:") - self.label_5.setMinimumSize(QSize(100, 0)) - self.label_5.setMaximumSize(QSize(100, 16777215)) - self.horizontalLayout_4.addWidget(self.label_5) - self.copyMoveDestinationComboBox = QComboBox(self) - sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.copyMoveDestinationComboBox.sizePolicy().hasHeightForWidth()) - self.copyMoveDestinationComboBox.setSizePolicy(sizePolicy) - self.copyMoveDestinationComboBox.addItem("Right in destination") - self.copyMoveDestinationComboBox.addItem("Recreate relative path") - self.copyMoveDestinationComboBox.addItem("Recreate absolute path") - self.horizontalLayout_4.addWidget(self.copyMoveDestinationComboBox) - self.verticalLayout.addLayout(self.horizontalLayout_4) - self.label_7 = QLabel(self) - self.label_7.setText("Custom Command (arguments: %d for dupe, %r for ref):") - self.verticalLayout.addWidget(self.label_7) - self.customCommandEdit = QLineEdit(self) - self.verticalLayout.addWidget(self.customCommandEdit) - self.verticalLayout_2.addLayout(self.verticalLayout) - self.buttonBox = QDialogButtonBox(self) - self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Ok|QDialogButtonBox.RestoreDefaults) - self.verticalLayout_2.addWidget(self.buttonBox) - - if sys.platform not in {'darwin', 'linux2'}: - self.verticalLayout.removeWidget(self.ignoreHardlinkMatches) - self.ignoreHardlinkMatches.setHidden(True) + self.widgetsVLayout.addWidget(self.widget) + self._setupAddCheckbox('wordWeightingBox', tr("Word weighting")) + self.widgetsVLayout.addWidget(self.wordWeightingBox) + self._setupAddCheckbox('matchSimilarBox', tr("Match similar words")) + self.widgetsVLayout.addWidget(self.matchSimilarBox) + self._setupAddCheckbox('mixFileKindBox', tr("Can mix file kind")) + self.widgetsVLayout.addWidget(self.mixFileKindBox) + self._setupAddCheckbox('useRegexpBox', tr("Use regular expressions when filtering")) + self.widgetsVLayout.addWidget(self.useRegexpBox) + self._setupAddCheckbox('removeEmptyFoldersBox', tr("Remove empty folders on delete or move")) + self.widgetsVLayout.addWidget(self.removeEmptyFoldersBox) + self._setupAddCheckbox('ignoreHardlinkMatches', tr("Ignore duplicates hardlinking to the same file")) + self.widgetsVLayout.addWidget(self.ignoreHardlinkMatches) + self._setupBottomPart() - def load(self, prefs=None): - if prefs is None: - prefs = self.app.prefs - self.filterHardnessSlider.setValue(prefs.filter_hardness) - self.filterHardnessLabel.setNum(prefs.filter_hardness) + def _load(self, prefs, setchecked): scan_type_index = SCAN_TYPE_ORDER.index(prefs.scan_type) self.scanTypeComboBox.setCurrentIndex(scan_type_index) - setchecked = lambda cb, b: cb.setCheckState(Qt.Checked if b else Qt.Unchecked) setchecked(self.tagTrackBox, prefs.scan_tag_track) setchecked(self.tagArtistBox, prefs.scan_tag_artist) setchecked(self.tagAlbumBox, prefs.scan_tag_album) @@ -195,18 +101,9 @@ class PreferencesDialog(QDialog): setchecked(self.tagYearBox, prefs.scan_tag_year) setchecked(self.matchSimilarBox, prefs.match_similar) setchecked(self.wordWeightingBox, prefs.word_weighting) - setchecked(self.mixFileKindBox, prefs.mix_file_kind) - setchecked(self.ignoreHardlinkMatches, prefs.ignore_hardlink_matches) - setchecked(self.useRegexpBox, prefs.use_regexp) - setchecked(self.removeEmptyFoldersBox, prefs.remove_empty_folders) - self.copyMoveDestinationComboBox.setCurrentIndex(prefs.destination_type) - self.customCommandEdit.setText(prefs.custom_command) - def save(self): - prefs = self.app.prefs - prefs.filter_hardness = self.filterHardnessSlider.value() + def _save(self, prefs, ischecked): prefs.scan_type = SCAN_TYPE_ORDER[self.scanTypeComboBox.currentIndex()] - ischecked = lambda cb: cb.checkState() == Qt.Checked prefs.scan_tag_track = ischecked(self.tagTrackBox) prefs.scan_tag_artist = ischecked(self.tagArtistBox) prefs.scan_tag_album = ischecked(self.tagAlbumBox) @@ -215,22 +112,11 @@ class PreferencesDialog(QDialog): prefs.scan_tag_year = ischecked(self.tagYearBox) prefs.match_similar = ischecked(self.matchSimilarBox) prefs.word_weighting = ischecked(self.wordWeightingBox) - prefs.mix_file_kind = ischecked(self.mixFileKindBox) - prefs.ignore_hardlink_matches = ischecked(self.ignoreHardlinkMatches) - prefs.use_regexp = ischecked(self.useRegexpBox) - prefs.remove_empty_folders = ischecked(self.removeEmptyFoldersBox) - prefs.destination_type = self.copyMoveDestinationComboBox.currentIndex() - prefs.custom_command = str(self.customCommandEdit.text()) def resetToDefaults(self): self.load(preferences.Preferences()) #--- Events - def buttonClicked(self, button): - role = self.buttonBox.buttonRole(button) - if role == QDialogButtonBox.ResetRole: - self.resetToDefaults() - def scanTypeChanged(self, index): scan_type = SCAN_TYPE_ORDER[self.scanTypeComboBox.currentIndex()] word_based = scan_type in (ScanType.Filename, ScanType.Fields, ScanType.FieldsNoOrder, @@ -248,7 +134,6 @@ class PreferencesDialog(QDialog): if __name__ == '__main__': - import sys from ..testapp import TestApp app = QApplication([]) dgapp = TestApp() diff --git a/qt/pe/app.py b/qt/pe/app.py index 7bb648d1..24a8fd9e 100644 --- a/qt/pe/app.py +++ b/qt/pe/app.py @@ -14,7 +14,7 @@ from PyQt4.QtGui import QImage, QImageReader from hscommon.util import get_file_ext from core import fs -from core_pe import data as data_pe, __version__ +from core_pe import data as data_pe, __appname__ from core_pe.scanner import ScannerPE from ..base.app import DupeGuru as DupeGuruBase @@ -58,8 +58,7 @@ class File(fs.File): class DupeGuru(DupeGuruBase): EDITION = 'pe' LOGO_NAME = 'logo_pe' - NAME = 'dupeGuru Picture Edition' - VERSION = __version__ + NAME = __appname__ DELTA_COLUMNS = frozenset([2, 5]) def __init__(self): diff --git a/qt/pe/details_dialog.py b/qt/pe/details_dialog.py index cefa19e4..c7dee1d3 100644 --- a/qt/pe/details_dialog.py +++ b/qt/pe/details_dialog.py @@ -9,6 +9,7 @@ from PyQt4.QtCore import Qt, QSize from PyQt4.QtGui import QVBoxLayout, QAbstractItemView, QHBoxLayout, QLabel, QSizePolicy, QPixmap +from hscommon.trans import tr from ..base.details_dialog import DetailsDialog as DetailsDialogBase from ..base.details_table import DetailsTable @@ -19,7 +20,7 @@ class DetailsDialog(DetailsDialogBase): self.referencePixmap = None def _setupUi(self): - self.setWindowTitle("Details") + self.setWindowTitle(tr("Details")) self.resize(502, 295) self.setMinimumSize(QSize(250, 250)) self.verticalLayout = QVBoxLayout(self) diff --git a/qt/pe/preferences_dialog.py b/qt/pe/preferences_dialog.py index 149cb0f2..113fb109 100644 --- a/qt/pe/preferences_dialog.py +++ b/qt/pe/preferences_dialog.py @@ -7,148 +7,40 @@ # http://www.hardcoded.net/licenses/bsd_license import sys -from PyQt4.QtCore import SIGNAL, Qt, QSize -from PyQt4.QtGui import (QDialog, QDialogButtonBox, QVBoxLayout, QHBoxLayout, QLabel, QComboBox, - QSlider, QSizePolicy, QSpacerItem, QWidget, QCheckBox, QLineEdit, QDialogButtonBox, QApplication) +from PyQt4.QtGui import QLabel, QApplication +from hscommon.trans import tr + +from ..base.preferences_dialog import PreferencesDialogBase from . import preferences -class PreferencesDialog(QDialog): - def __init__(self, parent, app): - flags = Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowSystemMenuHint - QDialog.__init__(self, parent, flags) - self.app = app - self._setupUi() - - self.connect(self.buttonBox, SIGNAL('clicked(QAbstractButton*)'), self.buttonClicked) - self.connect(self.filterHardnessSlider, SIGNAL("valueChanged(int)"), self.filterHardnessLabel.setNum) - self.buttonBox.accepted.connect(self.accept) - self.buttonBox.rejected.connect(self.reject) +class PreferencesDialog(PreferencesDialogBase): + def _setupPreferenceWidgets(self): + self._setupFilterHardnessBox() + self.widgetsVLayout.addLayout(self.filterHardnessHLayout) + self._setupAddCheckbox('matchScaledBox', tr("Match scaled pictures together")) + self.widgetsVLayout.addWidget(self.matchScaledBox) + self._setupAddCheckbox('mixFileKindBox', tr("Can mix file kind")) + self.widgetsVLayout.addWidget(self.mixFileKindBox) + self._setupAddCheckbox('useRegexpBox', tr("Use regular expressions when filtering")) + self.widgetsVLayout.addWidget(self.useRegexpBox) + self._setupAddCheckbox('removeEmptyFoldersBox', tr("Remove empty folders on delete or move")) + self.widgetsVLayout.addWidget(self.removeEmptyFoldersBox) + self._setupAddCheckbox('ignoreHardlinkMatches', tr("Ignore duplicates hardlinking to the same file")) + self.widgetsVLayout.addWidget(self.ignoreHardlinkMatches) + self._setupBottomPart() - def _setupUi(self): - self.setWindowTitle("Preferences") - self.resize(304, 263) - self.setSizeGripEnabled(False) - self.setModal(True) - self.verticalLayout_2 = QVBoxLayout(self) - self.verticalLayout = QVBoxLayout() - self.horizontalLayout_3 = QHBoxLayout() - self.label = QLabel(self) - self.label.setText("Filter Hardness:") - self.label.setMinimumSize(QSize(0, 0)) - self.horizontalLayout_3.addWidget(self.label) - self.verticalLayout_3 = QVBoxLayout() - self.verticalLayout_3.setSpacing(0) - self.horizontalLayout_6 = QHBoxLayout() - self.horizontalLayout_6.setSpacing(12) - self.filterHardnessSlider = QSlider(self) - sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.filterHardnessSlider.sizePolicy().hasHeightForWidth()) - self.filterHardnessSlider.setSizePolicy(sizePolicy) - self.filterHardnessSlider.setMinimum(1) - self.filterHardnessSlider.setMaximum(100) - self.filterHardnessSlider.setTracking(True) - self.filterHardnessSlider.setOrientation(Qt.Horizontal) - self.horizontalLayout_6.addWidget(self.filterHardnessSlider) - self.filterHardnessLabel = QLabel(self) - self.filterHardnessLabel.setText("100") - self.filterHardnessLabel.setMinimumSize(QSize(21, 0)) - self.horizontalLayout_6.addWidget(self.filterHardnessLabel) - self.verticalLayout_3.addLayout(self.horizontalLayout_6) - self.horizontalLayout_5 = QHBoxLayout() - self.horizontalLayout_5.setContentsMargins(-1, 0, -1, -1) - self.label_4 = QLabel(self) - self.label_4.setText("More Results") - self.horizontalLayout_5.addWidget(self.label_4) - spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) - self.horizontalLayout_5.addItem(spacerItem) - self.label_3 = QLabel(self) - self.label_3.setText("Fewer Results") - self.horizontalLayout_5.addWidget(self.label_3) - self.verticalLayout_3.addLayout(self.horizontalLayout_5) - self.horizontalLayout_3.addLayout(self.verticalLayout_3) - self.verticalLayout.addLayout(self.horizontalLayout_3) - self.matchScaledBox = QCheckBox(self) - self.matchScaledBox.setText("Match scaled pictures together") - self.verticalLayout.addWidget(self.matchScaledBox) - self.mixFileKindBox = QCheckBox(self) - self.mixFileKindBox.setText("Can mix file kind") - self.verticalLayout.addWidget(self.mixFileKindBox) - self.useRegexpBox = QCheckBox(self) - self.useRegexpBox.setText("Use regular expressions when filtering") - self.verticalLayout.addWidget(self.useRegexpBox) - self.removeEmptyFoldersBox = QCheckBox(self) - self.removeEmptyFoldersBox.setText("Remove empty folders on delete or move") - self.verticalLayout.addWidget(self.removeEmptyFoldersBox) - self.ignoreHardlinkMatches = QCheckBox(self) - self.ignoreHardlinkMatches.setText("Ignore duplicates hardlinking to the same file") - self.verticalLayout.addWidget(self.ignoreHardlinkMatches) - self.horizontalLayout_4 = QHBoxLayout() - self.label_5 = QLabel(self) - self.label_5.setText("Copy and Move:") - self.label_5.setMinimumSize(QSize(0, 0)) - self.horizontalLayout_4.addWidget(self.label_5) - self.copyMoveDestinationComboBox = QComboBox(self) - sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.copyMoveDestinationComboBox.sizePolicy().hasHeightForWidth()) - self.copyMoveDestinationComboBox.addItem("Right in destination") - self.copyMoveDestinationComboBox.addItem("Recreate relative path") - self.copyMoveDestinationComboBox.addItem("Recreate absolute path") - self.horizontalLayout_4.addWidget(self.copyMoveDestinationComboBox) - self.verticalLayout.addLayout(self.horizontalLayout_4) - self.label_2 = QLabel(self) - self.label_2.setText("Custom Command (arguments: %d for dupe %r for ref):") - self.verticalLayout.addWidget(self.label_2) - self.customCommandEdit = QLineEdit(self) - self.verticalLayout.addWidget(self.customCommandEdit) - self.verticalLayout_2.addLayout(self.verticalLayout) - self.buttonBox = QDialogButtonBox(self) - self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Ok|QDialogButtonBox.RestoreDefaults) - self.verticalLayout_2.addWidget(self.buttonBox) - if sys.platform not in {'darwin', 'linux2'}: - self.verticalLayout.removeWidget(self.ignoreHardlinkMatches) - self.ignoreHardlinkMatches.setHidden(True) - - def load(self, prefs=None): - if prefs is None: - prefs = self.app.prefs - self.filterHardnessSlider.setValue(prefs.filter_hardness) - self.filterHardnessLabel.setNum(prefs.filter_hardness) - setchecked = lambda cb, b: cb.setCheckState(Qt.Checked if b else Qt.Unchecked) + def _load(self, prefs, setchecked): setchecked(self.matchScaledBox, prefs.match_scaled) - setchecked(self.mixFileKindBox, prefs.mix_file_kind) - setchecked(self.useRegexpBox, prefs.use_regexp) - setchecked(self.removeEmptyFoldersBox, prefs.remove_empty_folders) - self.copyMoveDestinationComboBox.setCurrentIndex(prefs.destination_type) - self.customCommandEdit.setText(prefs.custom_command) - def save(self): - prefs = self.app.prefs - prefs.filter_hardness = self.filterHardnessSlider.value() - ischecked = lambda cb: cb.checkState() == Qt.Checked + def _save(self, prefs, ischecked): prefs.match_scaled = ischecked(self.matchScaledBox) - prefs.mix_file_kind = ischecked(self.mixFileKindBox) - prefs.use_regexp = ischecked(self.useRegexpBox) - prefs.remove_empty_folders = ischecked(self.removeEmptyFoldersBox) - prefs.destination_type = self.copyMoveDestinationComboBox.currentIndex() - prefs.custom_command = str(self.customCommandEdit.text()) def resetToDefaults(self): self.load(preferences.Preferences()) - #--- Events - def buttonClicked(self, button): - role = self.buttonBox.buttonRole(button) - if role == QDialogButtonBox.ResetRole: - self.resetToDefaults() - if __name__ == '__main__': - import sys from ..testapp import TestApp app = QApplication([]) dgapp = TestApp() diff --git a/qt/pe/result_window.py b/qt/pe/result_window.py index b03b23b9..3be41667 100644 --- a/qt/pe/result_window.py +++ b/qt/pe/result_window.py @@ -9,19 +9,20 @@ from PyQt4.QtCore import SIGNAL from PyQt4.QtGui import QMessageBox, QAction +from hscommon.trans import tr, trmsg from ..base.result_window import ResultWindow as ResultWindowBase class ResultWindow(ResultWindowBase): def _setupUi(self): ResultWindowBase._setupUi(self) - self.actionClearPictureCache = QAction("Clear Picture Cache", self) + self.actionClearPictureCache = QAction(tr("Clear Picture Cache"), self) self.menuFile.insertAction(self.actionClearIgnoreList, self.actionClearPictureCache) self.connect(self.actionClearPictureCache, SIGNAL("triggered()"), self.clearPictureCacheTriggered) def clearPictureCacheTriggered(self): - title = "Clear Picture Cache" - msg = "Do you really want to remove all your cached picture analysis?" + title = tr("Clear Picture Cache") + msg = trmsg("ClearPictureCacheConfirmMsg") if self.app.confirm(title, msg, QMessageBox.No): self.app.scanner.clear_picture_cache() - QMessageBox.information(self, title, "Picture cache cleared.") + QMessageBox.information(self, title, trmsg("PictureCacheClearedMsg")) \ No newline at end of file diff --git a/qt/se/app.py b/qt/se/app.py index 642bbf6b..6addd556 100644 --- a/qt/se/app.py +++ b/qt/se/app.py @@ -6,7 +6,7 @@ # which should be included with this package. The terms are also available at # http://www.hardcoded.net/licenses/bsd_license -from core_se import data, __version__ +from core_se import data, __appname__ from core.directories import Directories as DirectoriesBase, STATE_EXCLUDED from ..base.app import DupeGuru as DupeGuruBase @@ -26,8 +26,7 @@ class Directories(DirectoriesBase): class DupeGuru(DupeGuruBase): EDITION = 'se' LOGO_NAME = 'logo_se' - NAME = 'dupeGuru' - VERSION = __version__ + NAME = __appname__ DELTA_COLUMNS = frozenset([2, 4]) def __init__(self): diff --git a/qt/se/details_dialog.py b/qt/se/details_dialog.py index dfe7c366..8717b813 100644 --- a/qt/se/details_dialog.py +++ b/qt/se/details_dialog.py @@ -9,12 +9,13 @@ from PyQt4.QtCore import QSize from PyQt4.QtGui import QVBoxLayout, QAbstractItemView +from hscommon.trans import tr from ..base.details_dialog import DetailsDialog as DetailsDialogBase from ..base.details_table import DetailsTable class DetailsDialog(DetailsDialogBase): def _setupUi(self): - self.setWindowTitle("Details") + self.setWindowTitle(tr("Details")) self.resize(502, 186) self.setMinimumSize(QSize(200, 0)) self.verticalLayout = QVBoxLayout(self) diff --git a/qt/se/preferences_dialog.py b/qt/se/preferences_dialog.py index 68342609..1dc1caac 100644 --- a/qt/se/preferences_dialog.py +++ b/qt/se/preferences_dialog.py @@ -7,14 +7,16 @@ # http://www.hardcoded.net/licenses/bsd_license import sys -from PyQt4.QtCore import SIGNAL, Qt, QSize -from PyQt4.QtGui import (QDialog, QDialogButtonBox, QVBoxLayout, QHBoxLayout, QLabel, QComboBox, - QSlider, QSizePolicy, QSpacerItem, QWidget, QCheckBox, QLineEdit, QDialogButtonBox, QApplication) +from PyQt4.QtCore import SIGNAL, QSize +from PyQt4.QtGui import (QVBoxLayout, QHBoxLayout, QLabel, QComboBox, QSizePolicy, QSpacerItem, + QWidget, QLineEdit, QApplication) +from hscommon.trans import tr from hscommon.util import tryint from core.scanner import ScanType +from ..base.preferences_dialog import PreferencesDialogBase from . import preferences SCAN_TYPE_ORDER = [ @@ -22,97 +24,41 @@ SCAN_TYPE_ORDER = [ ScanType.Contents, ] -class PreferencesDialog(QDialog): +class PreferencesDialog(PreferencesDialogBase): def __init__(self, parent, app): - flags = Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowSystemMenuHint - QDialog.__init__(self, parent, flags) - self.app = app - self._setupUi() + PreferencesDialogBase.__init__(self, parent, app) - self.connect(self.buttonBox, SIGNAL('clicked(QAbstractButton*)'), self.buttonClicked) self.connect(self.scanTypeComboBox, SIGNAL('currentIndexChanged(int)'), self.scanTypeChanged) - self.connect(self.filterHardnessSlider, SIGNAL("valueChanged(int)"), self.filterHardnessLabel.setNum) - self.buttonBox.accepted.connect(self.accept) - self.buttonBox.rejected.connect(self.reject) - def _setupUi(self): - self.setWindowTitle("Preferences") - self.resize(308, 361) - self.setSizeGripEnabled(False) - self.setModal(True) - self.verticalLayout_2 = QVBoxLayout(self) - self.verticalLayout = QVBoxLayout() + def _setupPreferenceWidgets(self): self.horizontalLayout = QHBoxLayout() self.label_2 = QLabel(self) - self.label_2.setText("Scan Type:") + self.label_2.setText(tr("Scan Type:")) self.label_2.setMinimumSize(QSize(100, 0)) self.label_2.setMaximumSize(QSize(100, 16777215)) self.horizontalLayout.addWidget(self.label_2) self.scanTypeComboBox = QComboBox(self) - self.scanTypeComboBox.addItem("Filename") - self.scanTypeComboBox.addItem("Contents") + self.scanTypeComboBox.addItem(tr("Filename")) + self.scanTypeComboBox.addItem(tr("Contents")) self.horizontalLayout.addWidget(self.scanTypeComboBox) - self.verticalLayout.addLayout(self.horizontalLayout) - self.horizontalLayout_3 = QHBoxLayout() - self.label = QLabel(self) - self.label.setText("Filter Hardness:") - self.label.setMinimumSize(QSize(100, 0)) - self.label.setMaximumSize(QSize(100, 16777215)) - self.horizontalLayout_3.addWidget(self.label) - self.verticalLayout_3 = QVBoxLayout() - self.verticalLayout_3.setSpacing(0) - self.horizontalLayout_6 = QHBoxLayout() - self.horizontalLayout_6.setSpacing(12) - self.filterHardnessSlider = QSlider(self) - sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.filterHardnessSlider.sizePolicy().hasHeightForWidth()) - self.filterHardnessSlider.setSizePolicy(sizePolicy) - self.filterHardnessSlider.setMinimum(1) - self.filterHardnessSlider.setMaximum(100) - self.filterHardnessSlider.setTracking(True) - self.filterHardnessSlider.setOrientation(Qt.Horizontal) - self.horizontalLayout_6.addWidget(self.filterHardnessSlider) - self.filterHardnessLabel = QLabel(self) - self.filterHardnessLabel.setText("100") - self.filterHardnessLabel.setMinimumSize(QSize(21, 0)) - self.horizontalLayout_6.addWidget(self.filterHardnessLabel) - self.verticalLayout_3.addLayout(self.horizontalLayout_6) - self.horizontalLayout_5 = QHBoxLayout() - self.horizontalLayout_5.setContentsMargins(-1, 0, -1, -1) - self.label_4 = QLabel(self) - self.label_4.setText("More Results") - self.horizontalLayout_5.addWidget(self.label_4) - spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) - self.horizontalLayout_5.addItem(spacerItem) - self.label_3 = QLabel(self) - self.label_3.setText("Fewer Results") - self.horizontalLayout_5.addWidget(self.label_3) - self.verticalLayout_3.addLayout(self.horizontalLayout_5) - self.horizontalLayout_3.addLayout(self.verticalLayout_3) - self.verticalLayout.addLayout(self.horizontalLayout_3) + self.widgetsVLayout.addLayout(self.horizontalLayout) + self._setupFilterHardnessBox() + self.widgetsVLayout.addLayout(self.filterHardnessHLayout) self.widget = QWidget(self) self.widget.setMinimumSize(QSize(0, 136)) self.verticalLayout_4 = QVBoxLayout(self.widget) - self.wordWeightingBox = QCheckBox(self.widget) - self.wordWeightingBox.setText("Word weighting") + self._setupAddCheckbox('wordWeightingBox', tr("Word weighting"), self.widget) self.verticalLayout_4.addWidget(self.wordWeightingBox) - self.matchSimilarBox = QCheckBox(self.widget) - self.matchSimilarBox.setText("Match similar words") + self._setupAddCheckbox('matchSimilarBox', tr("Match similar words"), self.widget) self.verticalLayout_4.addWidget(self.matchSimilarBox) - self.mixFileKindBox = QCheckBox(self.widget) - self.mixFileKindBox.setText("Can mix file kind") + self._setupAddCheckbox('mixFileKindBox', tr("Can mix file kind"), self.widget) self.verticalLayout_4.addWidget(self.mixFileKindBox) - self.useRegexpBox = QCheckBox(self.widget) - self.useRegexpBox.setText("Use regular expressions when filtering") + self._setupAddCheckbox('useRegexpBox', tr("Use regular expressions when filtering"), self.widget) self.verticalLayout_4.addWidget(self.useRegexpBox) - self.removeEmptyFoldersBox = QCheckBox(self.widget) - self.removeEmptyFoldersBox.setText("Remove empty folders on delete or move") + self._setupAddCheckbox('removeEmptyFoldersBox', tr("Remove empty folders on delete or move"), self.widget) self.verticalLayout_4.addWidget(self.removeEmptyFoldersBox) self.horizontalLayout_2 = QHBoxLayout() - self.ignoreSmallFilesBox = QCheckBox(self.widget) - self.ignoreSmallFilesBox.setText("Ignore files smaller than") + self._setupAddCheckbox('ignoreSmallFilesBox', tr("Ignore files smaller than"), self.widget) self.horizontalLayout_2.addWidget(self.ignoreSmallFilesBox) self.sizeThresholdEdit = QLineEdit(self.widget) sizePolicy = QSizePolicy(QSizePolicy.Maximum, QSizePolicy.Fixed) @@ -123,94 +69,43 @@ class PreferencesDialog(QDialog): self.sizeThresholdEdit.setMaximumSize(QSize(50, 16777215)) self.horizontalLayout_2.addWidget(self.sizeThresholdEdit) self.label_6 = QLabel(self.widget) - self.label_6.setText("KB") + self.label_6.setText(tr("KB")) self.horizontalLayout_2.addWidget(self.label_6) spacerItem1 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.horizontalLayout_2.addItem(spacerItem1) self.verticalLayout_4.addLayout(self.horizontalLayout_2) - self.ignoreHardlinkMatches = QCheckBox(self.widget) - self.ignoreHardlinkMatches.setText("Ignore duplicates hardlinking to the same file") + self._setupAddCheckbox('ignoreHardlinkMatches', tr("Ignore duplicates hardlinking to the same file"), self.widget) self.verticalLayout_4.addWidget(self.ignoreHardlinkMatches) - self.verticalLayout.addWidget(self.widget) - self.horizontalLayout_4 = QHBoxLayout() - self.label_5 = QLabel(self) - self.label_5.setText("Copy and Move:") - self.label_5.setMinimumSize(QSize(100, 0)) - self.label_5.setMaximumSize(QSize(100, 16777215)) - self.horizontalLayout_4.addWidget(self.label_5) - self.copyMoveDestinationComboBox = QComboBox(self) - sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.copyMoveDestinationComboBox.sizePolicy().hasHeightForWidth()) - self.copyMoveDestinationComboBox.setSizePolicy(sizePolicy) - self.copyMoveDestinationComboBox.addItem("Right in destination") - self.copyMoveDestinationComboBox.addItem("Recreate relative path") - self.copyMoveDestinationComboBox.addItem("Recreate absolute path") - self.horizontalLayout_4.addWidget(self.copyMoveDestinationComboBox) - self.verticalLayout.addLayout(self.horizontalLayout_4) - self.label_7 = QLabel(self) - self.label_7.setText("Custom Command (arguments: %d for dupe, %r for ref):") - self.verticalLayout.addWidget(self.label_7) - self.customCommandEdit = QLineEdit(self) - self.verticalLayout.addWidget(self.customCommandEdit) - self.verticalLayout_2.addLayout(self.verticalLayout) - self.buttonBox = QDialogButtonBox(self) - self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Ok|QDialogButtonBox.RestoreDefaults) - self.verticalLayout_2.addWidget(self.buttonBox) + self.widgetsVLayout.addWidget(self.widget) + self._setupBottomPart() + + def _setupUi(self): + PreferencesDialogBase._setupUi(self) - if sys.platform not in {'darwin', 'linux2'}: - self.verticalLayout_4.removeWidget(self.ignoreHardlinkMatches) - self.ignoreHardlinkMatches.setHidden(True) if sys.platform == 'linux2': # Under linux, whether it's a Qt layout bug or something else, the size threshold text edit # doesn't have enough space, so we make the pref pane higher to compensate. self.resize(self.width(), 400) - def load(self, prefs=None): - if prefs is None: - prefs = self.app.prefs - self.filterHardnessSlider.setValue(prefs.filter_hardness) - self.filterHardnessLabel.setNum(prefs.filter_hardness) + def _load(self, prefs, setchecked): scan_type_index = SCAN_TYPE_ORDER.index(prefs.scan_type) self.scanTypeComboBox.setCurrentIndex(scan_type_index) - setchecked = lambda cb, b: cb.setCheckState(Qt.Checked if b else Qt.Unchecked) setchecked(self.matchSimilarBox, prefs.match_similar) setchecked(self.wordWeightingBox, prefs.word_weighting) - setchecked(self.mixFileKindBox, prefs.mix_file_kind) - setchecked(self.ignoreHardlinkMatches, prefs.ignore_hardlink_matches) - setchecked(self.useRegexpBox, prefs.use_regexp) - setchecked(self.removeEmptyFoldersBox, prefs.remove_empty_folders) setchecked(self.ignoreSmallFilesBox, prefs.ignore_small_files) self.sizeThresholdEdit.setText(str(prefs.small_file_threshold)) - self.copyMoveDestinationComboBox.setCurrentIndex(prefs.destination_type) - self.customCommandEdit.setText(prefs.custom_command) - def save(self): - prefs = self.app.prefs - prefs.filter_hardness = self.filterHardnessSlider.value() + def _save(self, prefs, ischecked): prefs.scan_type = SCAN_TYPE_ORDER[self.scanTypeComboBox.currentIndex()] - ischecked = lambda cb: cb.checkState() == Qt.Checked prefs.match_similar = ischecked(self.matchSimilarBox) prefs.word_weighting = ischecked(self.wordWeightingBox) - prefs.mix_file_kind = ischecked(self.mixFileKindBox) - prefs.ignore_hardlink_matches = ischecked(self.ignoreHardlinkMatches) - prefs.use_regexp = ischecked(self.useRegexpBox) - prefs.remove_empty_folders = ischecked(self.removeEmptyFoldersBox) prefs.ignore_small_files = ischecked(self.ignoreSmallFilesBox) prefs.small_file_threshold = tryint(self.sizeThresholdEdit.text()) - prefs.destination_type = self.copyMoveDestinationComboBox.currentIndex() - prefs.custom_command = str(self.customCommandEdit.text()) def resetToDefaults(self): self.load(preferences.Preferences()) #--- Events - def buttonClicked(self, button): - role = self.buttonBox.buttonRole(button) - if role == QDialogButtonBox.ResetRole: - self.resetToDefaults() - def scanTypeChanged(self, index): scan_type = SCAN_TYPE_ORDER[self.scanTypeComboBox.currentIndex()] word_based = scan_type == ScanType.Filename diff --git a/run_template_qt.py b/run_template_qt.py index dacc643a..e1b1a678 100644 --- a/run_template_qt.py +++ b/run_template_qt.py @@ -15,12 +15,16 @@ from PyQt4.QtGui import QApplication, QIcon, QPixmap from hscommon.trans import install_qt_trans from qtlib.error_report_dialog import install_excepthook from qt.base import dg_rc +from core_{{edition}} import __version__, __appname__ if sys.platform == 'win32': import qt.base.cxfreeze_fix if __name__ == "__main__": app = QApplication(sys.argv) + QCoreApplication.setOrganizationName('Hardcoded Software') + QCoreApplication.setApplicationName(__appname__) + QCoreApplication.setApplicationVersion(__version__) settings = QSettings() lang = settings.value('Language').toString() install_qt_trans(lang) @@ -28,9 +32,6 @@ if __name__ == "__main__": # has been installed from qt.{{edition}}.app import DupeGuru app.setWindowIcon(QIcon(QPixmap(":/{0}".format(DupeGuru.LOGO_NAME)))) - QCoreApplication.setOrganizationName('Hardcoded Software') - QCoreApplication.setApplicationName(DupeGuru.NAME) - QCoreApplication.setApplicationVersion(DupeGuru.VERSION) dgapp = DupeGuru() install_excepthook() sys.exit(app.exec_())