feat: translate modals

This commit is contained in:
Jann Stute
2024-12-23 19:21:50 +01:00
parent a38847cb7a
commit 03b2e842fd
16 changed files with 216 additions and 147 deletions

View File

@@ -3,27 +3,31 @@
"app.pre_release": "Pre-Release",
"app.title": "{base_title} - Library '{library_dir}'",
"edit.tag_manager": "Manage Tags",
"entries.duplicate.merge.label": "Merging Duplicate Entries",
"entries.duplicate.merge.label": "Merging Duplicate Entries...",
"entries.duplicate.merge": "Merge Duplicate Entries",
"entries.duplicate.refresh": "Refresh Duplicate Entries",
"entries.duplicates.description": "Duplicate entries are defined as multiple entries which point to the same file on disk. Merging these will combine the tags and metadata from all duplicates into a single consolidated entry. These are not to be confused with \"duplicate files\", which are duplicates of your files themselves outside of TagStudio.",
"entries.mirror.confirmation": "Are you sure you want to mirror the following %{len(self.lib.dupe_files)} Entries?",
"entries.mirror.label": "Mirroring 1/%{count} Entries...",
"entries.mirror.confirmation": "Are you sure you want to mirror the following {count} Entries?",
"entries.mirror.label": "Mirroring {idx}/{total} Entries...",
"entries.mirror.title": "Mirroring Entries",
"entries.mirror": "Mirror",
"entries.mirror.window_title": "Mirror Entries",
"entries.mirror": "&Mirror",
"entries.tags": "Tags",
"entries.unlinked.delete.confirm": "Are you sure you want to delete the following %{len(self.lib.missing_files)} entries?",
"entries.unlinked.delete.deleting_count": "Deleting %{x[0]+1}/{len(self.lib.missing_files)} Unlinked Entries",
"entries.unlinked.delete.confirm": "Are you sure you want to delete the following {count} entries?",
"entries.unlinked.delete.deleting_count": "Deleting {idx}/{count} Unlinked Entries",
"entries.unlinked.delete.deleting": "Deleting Entries",
"entries.unlinked.delete": "Delete Unlinked Entries",
"entries.unlinked.delete_alt": "De&lete Unlinked Entries",
"entries.unlinked.description": "Each library entry is linked to a file in one of your directories. If a file linked to an entry is moved or deleted outside of TagStudio, it is then considered unlinked. Unlinked entries may be automatically relinked via searching your directories or deleted if desired.",
"entries.unlinked.refresh_all": "Refresh All",
"entries.unlinked.relink.attempting": "Attempting to Relink %{x[0]+1}/%{len(self.lib.missing_files)} Entries, %{self.fixed} Successfully Relinked",
"entries.unlinked.relink.manual": "Manual Relink",
"entries.unlinked.refresh_all": "&Refresh All",
"entries.unlinked.relink.attempting": "Attempting to Relink {idx}/{missing_count} Entries, {fixed_count} Successfully Relinked",
"entries.unlinked.relink.manual": "&Manual Relink",
"entries.unlinked.relink.title": "Relinking Entries",
"entries.unlinked.scanning": "Scanning Library for Unlinked Entries...",
"entries.unlinked.search_and_relink": "Search && Relink",
"entries.unlinked.search_and_relink": "&Search && Relink",
"entries.unlinked.title": "Fix Unlinked Entries",
"entries.unlinked.missing_count.none": "Unlinked Entries: N/A",
"entries.unlinked.missing_count.some": "Unlinked Entries: {count}",
"field.copy": "Copy Field",
"field.edit": "Edit Field",
"field.paste": "Paste Field",
@@ -31,15 +35,16 @@
"file.date_created": "Date Created",
"file.date_modified": "Date Modified",
"file.dimensions": "Dimensions",
"file.duplicates.description": "TagStudio supports importing DupeGuru results to manage duplicate files.",
"file.duplicates.dupeguru.advice": "After mirroring, you're free to use DupeGuru to delete the unwanted files. Afterwards, use TagStudio's \"Fix Unlinked Entries\" feature in the Tools menu in order to delete the unlinked Entries.",
"file.duplicates.dupeguru.file_extension": "DupeGuru Files (*.dupeguru)",
"file.duplicates.dupeguru.load_file": "Load DupeGuru File",
"file.duplicates.dupeguru.load_file": "&Load DupeGuru File",
"file.duplicates.dupeguru.no_file": "No DupeGuru File Selected",
"file.duplicates.dupeguru.open_file": "Open DupeGuru Results File",
"file.duplicates.fix": "Fix Duplicate Files",
"file.duplicates.matches_uninitialized": "Duplicate File Matches: N/A",
"file.duplicates.matches": "Duplicate File Matches: %{count}",
"file.duplicates.mirror_entries": "Mirror Entries",
"file.duplicates.matches": "Duplicate File Matches: {count}",
"file.duplicates.mirror_entries": "&Mirror Entries",
"file.duplicates.mirror.description": "Mirror the Entry data across each duplicate match set, combining all data while not removing or duplicating fields. This operation will not delete any files or data.",
"file.duration": "Length",
"file.not_found": "File Not Found",
@@ -55,12 +60,23 @@
"folders_to_tags.title": "Create Tags From Folders",
"generic.add": "Add",
"generic.apply": "Apply",
"generic.apply_alt": "&Apply",
"generic.cancel": "Cancel",
"generic.cancel_alt": "&Cancel",
"generic.close": "Close",
"generic.copy": "Copy",
"generic.cut": "Cut",
"generic.delete": "Delete",
"generic.delete_alt": "&Delete",
"generic.done": "Done",
"generic.done_alt": "&Done",
"generic.edit": "Edit",
"generic.rename": "Rename",
"generic.rename_alt": "&Rename",
"generic.overwrite": "Overwrite",
"generic.overwrite_alt": "&Overwrite",
"generic.skip": "Skip",
"generic.skip_alt": "&Skip",
"generic.navigation.back": "Back",
"generic.navigation.next": "Next",
"generic.paste": "Paste",
@@ -76,10 +92,10 @@
"home.thumbnail_size.small": "Small Thumbnails",
"home.thumbnail_size.mini": "Mini Thumbnails",
"home.thumbnail_size": "Thumbnail Size",
"ignore_list.add_extension": "Add Extension",
"ignore_list.add_extension": "&Add Extension",
"ignore_list.mode.exclude": "Exclude",
"ignore_list.mode.include": "Include",
"ignore_list.mode.label": "List Mode",
"ignore_list.mode.label": "List Mode:",
"ignore_list.title": "File Extensions",
"library.field.add": "Add Field",
"library.field.confirm_remove": "Are you sure you want to remove this \"%{self.lib.get_field_attr(field, \"name\")}\" field?",
@@ -134,7 +150,10 @@
"status.results": "Results",
"tag_manager.title": "Library Tags",
"tag.add_to_search": "Add to Search",
"tag.edit": "Edit Tag",
"tag.add": "Add Tag",
"tag.create": "Create Tag",
"tag.remove": "Remove Tag",
"tag.aliases": "Aliases",
"tag.color": "Color",
"tag.name": "Name",
@@ -144,13 +163,23 @@
"tag.parent_tags": "Parent Tags",
"tag.search_for_tag": "Search for Tag",
"tag.shorthand": "Shorthand",
"tag.remove_alias": "Remove selected alias",
"tag.tag_name_required": "Tag Name (Required)",
"view.size.0": "Mini",
"view.size.1": "Small",
"view.size.2": "Medium",
"view.size.3": "Large",
"view.size.4": "Extra Large",
"window.button.close": "Close",
"window.title.error": "Error",
"window.title.open_create_library": "Open/Create Library",
"window.message.error_opening_library": "Error opening library."
"window.message.error_opening_library": "Error opening library.",
"tag_database.confirmation": "Are you sure you want to delete the tag \"{tag_name}\"?",
"drop_import.title": "Conflicting File(s)",
"drop_import.decription": "The following files have filenames already exist in the library",
"drop_import.progress.label.initial": "Importing New Files...",
"drop_import.progress.label.singular": "Importing New Files...\n1 File imported.{suffix}",
"drop_import.progress.label.plural": "Importing New Files...\n{count} Files Imported.{suffix}",
"drop_import.progress.window_title": "Import Files",
"drop_import.duplicates_choice.singular": "The following file has a filename that already exists in the library.",
"drop_import.duplicates_choice.plural": "The following {count} files have filenames that already exist in the library.",
}

View File

@@ -15,6 +15,8 @@ from PySide6.QtWidgets import (
)
from src.core.library import Library
from ..translations import Translations
class AddFieldModal(QWidget):
done = Signal(list)
@@ -26,7 +28,7 @@ class AddFieldModal(QWidget):
super().__init__()
self.is_connected = False
self.lib = library
self.setWindowTitle("Add Field") # TODO translate
Translations.translate_with_setter(self.setWindowTitle, "library.field.add")
self.setWindowModality(Qt.WindowModality.ApplicationModal)
self.setMinimumSize(400, 300)
self.root_layout = QVBoxLayout(self)
@@ -40,7 +42,7 @@ class AddFieldModal(QWidget):
# 'text-align:center;'
"font-weight:bold;" "font-size:14px;" "padding-top: 6px" ""
)
self.title_widget.setText("Add Field") # TODO translate
Translations.translate_qobject(self.title_widget, "library.field.add")
self.title_widget.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.list_widget = QListWidget()
@@ -54,13 +56,13 @@ class AddFieldModal(QWidget):
# self.cancel_button.setText('Cancel')
self.cancel_button = QPushButton()
self.cancel_button.setText("Cancel") # TODO translate
Translations.translate_qobject(self.cancel_button, "generic.cancel")
self.cancel_button.clicked.connect(self.hide)
# self.cancel_button.clicked.connect(widget.reset)
self.button_layout.addWidget(self.cancel_button)
self.save_button = QPushButton()
self.save_button.setText("Add") # TODO translate
Translations.translate_qobject(self.save_button, "generic.add")
# self.save_button.setAutoDefault(True)
self.save_button.setDefault(True)
self.save_button.clicked.connect(self.hide)

View File

@@ -29,6 +29,8 @@ from src.qt.modals.tag_search import TagSearchPanel
from src.qt.widgets.panel import PanelModal, PanelWidget
from src.qt.widgets.tag import TagAliasWidget, TagWidget
from ..translations import Translations
logger = structlog.get_logger(__name__)
@@ -54,12 +56,14 @@ class BuildTagPanel(PanelWidget):
self.name_layout.setSpacing(0)
self.name_layout.setAlignment(Qt.AlignmentFlag.AlignLeft)
self.name_title = QLabel()
self.name_title.setText("Name")
Translations.translate_qobject(self.name_title, "tag.name")
self.name_layout.addWidget(self.name_title)
self.name_field = QLineEdit()
self.name_field.setFixedHeight(24)
self.name_field.textChanged.connect(self.on_name_changed)
self.name_field.setPlaceholderText("Tag Name (Required)") # TODO translate
Translations.translate_with_setter(
self.name_field.setPlaceholderText, "tag.tag_name_required"
)
self.name_layout.addWidget(self.name_field)
# Shorthand ------------------------------------------------------------
@@ -70,7 +74,7 @@ class BuildTagPanel(PanelWidget):
self.shorthand_layout.setSpacing(0)
self.shorthand_layout.setAlignment(Qt.AlignmentFlag.AlignLeft)
self.shorthand_title = QLabel()
self.shorthand_title.setText("Shorthand") # TODO translate
Translations.translate_qobject(self.shorthand_title, "tag.shorthand")
self.shorthand_layout.addWidget(self.shorthand_title)
self.shorthand_field = QLineEdit()
self.shorthand_layout.addWidget(self.shorthand_field)
@@ -83,7 +87,7 @@ class BuildTagPanel(PanelWidget):
self.aliases_layout.setSpacing(0)
self.aliases_layout.setAlignment(Qt.AlignmentFlag.AlignLeft)
self.aliases_title = QLabel()
self.aliases_title.setText("Aliases") # TODO translate
Translations.translate_qobject(self.aliases_title, "tag.aliases")
self.aliases_layout.addWidget(self.aliases_title)
self.aliases_flow_widget = QWidget()
@@ -134,7 +138,7 @@ class BuildTagPanel(PanelWidget):
self.subtags_layout.setAlignment(Qt.AlignmentFlag.AlignLeft)
self.subtags_title = QLabel()
self.subtags_title.setText("Parent Tags") # TODO translate
Translations.translate_qobject(self.subtags_title, "tag.parent_tags")
self.subtags_layout.addWidget(self.subtags_title)
self.subtag_flow_widget = QWidget()
@@ -180,7 +184,9 @@ class BuildTagPanel(PanelWidget):
tsp = TagSearchPanel(self.lib, exclude_ids)
tsp.tag_chosen.connect(lambda x: self.add_subtag_callback(x))
self.add_tag_modal = PanelModal(tsp, "Add Parent Tags", "Add Parent Tags") # TODO translate
self.add_tag_modal = PanelModal(tsp)
Translations.translate_with_setter(self.add_tag_modal.setTitle, "tag.parent_tags.add")
Translations.translate_with_setter(self.add_tag_modal.setWindowTitle, "tag.parent_tags.add")
self.subtags_add_button.clicked.connect(self.add_tag_modal.show)
# self.subtags_layout.addWidget(self.subtags_add_button)
@@ -196,7 +202,7 @@ class BuildTagPanel(PanelWidget):
self.color_layout.setSpacing(0)
self.color_layout.setAlignment(Qt.AlignmentFlag.AlignLeft)
self.color_title = QLabel()
self.color_title.setText("Color") # TODO translate
Translations.translate_qobject(self.color_title, "tag.color")
self.color_layout.addWidget(self.color_title)
self.color_field = QComboBox()
self.color_field.setEditable(False)
@@ -218,7 +224,8 @@ class BuildTagPanel(PanelWidget):
)
)
self.color_layout.addWidget(self.color_field)
remove_selected_alias_action = QAction("remove selected alias", self) # TODO translate
remove_selected_alias_action = QAction(self)
Translations.translate_qobject(remove_selected_alias_action, "tag.remove_alias")
remove_selected_alias_action.triggered.connect(self.remove_selected_alias)
remove_selected_alias_action.setShortcut(
QtCore.QKeyCombination(
@@ -243,7 +250,7 @@ class BuildTagPanel(PanelWidget):
self.alias_names: set[str] = set()
self.new_alias_names: dict = dict()
self.set_tag(tag or Tag(name="New Tag")) # TODO translate
self.set_tag(tag or Tag(name=Translations["tag.new"]))
if tag is None:
self.name_field.selectAll()

View File

@@ -17,6 +17,8 @@ from PySide6.QtWidgets import (
from src.core.utils.missing_files import MissingRegistry
from src.qt.widgets.progress import ProgressWidget
from ..translations import Translations
# Only import for type checking/autocompletion, will not be imported at runtime.
if typing.TYPE_CHECKING:
from src.qt.ts_qt import QtDriver
@@ -29,7 +31,7 @@ class DeleteUnlinkedEntriesModal(QWidget):
super().__init__()
self.driver = driver
self.tracker = tracker
self.setWindowTitle("Delete Unlinked Entries") # TODO translate
Translations.translate_with_setter(self.setWindowTitle, "entries.unlinked.delete")
self.setWindowModality(Qt.WindowModality.ApplicationModal)
self.setMinimumSize(500, 400)
self.root_layout = QVBoxLayout(self)
@@ -38,9 +40,11 @@ class DeleteUnlinkedEntriesModal(QWidget):
self.desc_widget = QLabel()
self.desc_widget.setObjectName("descriptionLabel")
self.desc_widget.setWordWrap(True)
self.desc_widget.setText(f"""
Are you sure you want to delete the following {self.tracker.missing_files_count} entries?
""") # TODO translate
Translations.translate_qobject(
self.desc_widget,
"entries.unlinked.delete.confirm",
count=self.tracker.missing_files_count,
)
self.desc_widget.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.list_view = QListView()
@@ -53,13 +57,13 @@ class DeleteUnlinkedEntriesModal(QWidget):
self.button_layout.addStretch(1)
self.cancel_button = QPushButton()
self.cancel_button.setText("&Cancel") # TODO translate
Translations.translate_qobject(self.cancel_button, "generic.cancel_alt")
self.cancel_button.setDefault(True)
self.cancel_button.clicked.connect(self.hide)
self.button_layout.addWidget(self.cancel_button)
self.delete_button = QPushButton()
self.delete_button.setText("&Delete") # TODO translate
Translations.translate_qobject(self.delete_button, "generic.delete_alt")
self.delete_button.clicked.connect(self.hide)
self.delete_button.clicked.connect(lambda: self.delete_entries())
self.button_layout.addWidget(self.delete_button)
@@ -69,9 +73,11 @@ class DeleteUnlinkedEntriesModal(QWidget):
self.root_layout.addWidget(self.button_container)
def refresh_list(self):
self.desc_widget.setText(f"""
Are you sure you want to delete the following {self.tracker.missing_files_count} entries?
""") # TODO translate
self.desc_widget.setText(
Translations.translate_formatted(
"entries.unlinked.delete.confirm", count=self.tracker.missing_files_count
)
)
self.model.clear()
for i in self.tracker.missing_files:
@@ -81,14 +87,17 @@ class DeleteUnlinkedEntriesModal(QWidget):
def delete_entries(self):
def displayed_text(x):
return f"Deleting {x}/{self.tracker.missing_files_count} Unlinked Entries" # TODO translate
return Translations.translate_formatted(
"entries.unlinked.delete.deleting_count",
idx=x,
count=self.tracker.missing_files_count,
)
pw = ProgressWidget(
window_title="Deleting Entries", # TODO translate
label_text="",
cancel_button_text=None,
minimum=0,
maximum=self.tracker.missing_files_count,
)
Translations.translate_with_setter(pw.setWindowTitle, "entries.unlinked.delete.deleting")
pw.from_iterable_function(self.tracker.execute_deletion, displayed_text, self.done.emit)

View File

@@ -19,6 +19,8 @@ from PySide6.QtWidgets import (
)
from src.qt.widgets.progress import ProgressWidget
from ..translations import Translations
if TYPE_CHECKING:
from src.qt.ts_qt import QtDriver
@@ -41,7 +43,7 @@ class DropImportModal(QWidget):
self.driver: QtDriver = driver
# Widget ======================
self.setWindowTitle("Conflicting File(s)") # TODO translate
Translations.translate_with_setter(self.setWindowTitle, "drop_import.title")
self.setWindowModality(Qt.WindowModality.ApplicationModal)
self.setMinimumSize(500, 400)
self.root_layout = QVBoxLayout(self)
@@ -50,9 +52,7 @@ class DropImportModal(QWidget):
self.desc_widget = QLabel()
self.desc_widget.setObjectName("descriptionLabel")
self.desc_widget.setWordWrap(True)
self.desc_widget.setText(
"The following files have filenames already exist in the library"
) # TODO translate
self.desc_widget.setText(Translations["drop_import.decription"])
self.desc_widget.setAlignment(Qt.AlignmentFlag.AlignCenter)
# Duplicate File List ========
@@ -67,25 +67,25 @@ class DropImportModal(QWidget):
self.button_layout.addStretch(1)
self.skip_button = QPushButton()
self.skip_button.setText("&Skip") # TODO translate
Translations.translate_qobject(self.skip_button, "generic.skip_alt")
self.skip_button.setDefault(True)
self.skip_button.clicked.connect(lambda: self.begin_transfer(DuplicateChoice.SKIP))
self.button_layout.addWidget(self.skip_button)
self.overwrite_button = QPushButton()
self.overwrite_button.setText("&Overwrite") # TODO translate
Translations.translate_qobject(self.overwrite_button, "generic.overwrite_alt")
self.overwrite_button.clicked.connect(
lambda: self.begin_transfer(DuplicateChoice.OVERWRITE)
)
self.button_layout.addWidget(self.overwrite_button)
self.rename_button = QPushButton()
self.rename_button.setText("&Rename") # TODO translate
Translations.translate_qobject(self.rename_button, "generic.rename_alt")
self.rename_button.clicked.connect(lambda: self.begin_transfer(DuplicateChoice.RENAME))
self.button_layout.addWidget(self.rename_button)
self.cancel_button = QPushButton()
self.cancel_button.setText("&Cancel") # TODO translate
Translations.translate_qobject(self.cancel_button, "generic.cancel_alt")
self.cancel_button.clicked.connect(lambda: self.begin_transfer(DuplicateChoice.CANCEL))
self.button_layout.addWidget(self.cancel_button)
@@ -139,8 +139,12 @@ class DropImportModal(QWidget):
def ask_duplicates_choice(self):
"""Display the message widgeth with a list of the duplicated files."""
self.desc_widget.setText(
f"The following {len(self.duplicate_files)} file(s) have filenames already exist in the library." # noqa: E501
) # TODO translate
Translations["drop_import.duplicates_choice.singular"]
if len(self.duplicate_files) == 1
else Translations.translate_formatted(
"drop_import.duplicates_choice.plural", count=len(self.duplicate_files)
)
)
self.model.clear()
for dupe in self.duplicate_files:
@@ -160,19 +164,21 @@ class DropImportModal(QWidget):
return
def displayed_text(x):
text = f"Importing New Files...\n{x[0] + 1} File{'s' if x[0] + 1 != 1 else ''} Imported." # TODO translate
if self.choice:
text += f" {x[1]} {self.choice.value}"
return text
return Translations.translate_formatted(
"drop_import.progress.label.singular"
if x[0] + 1 == 1
else "drop_import.progress.label.plural",
count=x[0] + 1,
suffix=f" {x[1]} {self.choice.value}" if self.choice else "",
)
pw = ProgressWidget(
window_title="Import Files", # TODO translate
label_text="Importing New Files...", # TODO translate
cancel_button_text=None,
minimum=0,
maximum=len(self.files),
)
Translations.translate_with_setter(pw.setWindowTitle, "drop_import.progress.window_title")
Translations.translate_with_setter(pw.update_label, "drop_import.progress.label.initial")
pw.from_iterable_function(
self.copy_files,

View File

@@ -20,6 +20,8 @@ from src.core.enums import LibraryPrefs
from src.core.library import Library
from src.qt.widgets.panel import PanelWidget
from ..translations import Translations
class FileExtensionItemDelegate(QStyledItemDelegate):
def setModelData(self, editor, model, index): # noqa: N802
@@ -35,7 +37,7 @@ class FileExtensionModal(PanelWidget):
super().__init__()
# Initialize Modal =====================================================
self.lib = library
self.setWindowTitle("File Extensions") # TODO translate
Translations.translate_with_setter(self.setWindowTitle, "ignore_list.title")
self.setWindowModality(Qt.WindowModality.ApplicationModal)
self.setMinimumSize(240, 400)
self.root_layout = QVBoxLayout(self)
@@ -50,7 +52,7 @@ class FileExtensionModal(PanelWidget):
# Create "Add Button" Widget -------------------------------------------
self.add_button = QPushButton()
self.add_button.setText("&Add Extension") # TODO translate
Translations.translate_qobject(self.add_button, "ignore_list.add_extension")
self.add_button.clicked.connect(self.add_item)
self.add_button.setDefault(True)
self.add_button.setMinimumWidth(100)
@@ -61,11 +63,17 @@ class FileExtensionModal(PanelWidget):
self.mode_layout.setContentsMargins(0, 0, 0, 0)
self.mode_layout.setSpacing(12)
self.mode_label = QLabel()
self.mode_label.setText("List Mode:") # TODO translate
Translations.translate_qobject(self.mode_label, "ignore_list.mode.label")
self.mode_combobox = QComboBox()
self.mode_combobox.setEditable(False)
self.mode_combobox.addItem("Include") # TODO translate
self.mode_combobox.addItem("Exclude") # TODO translate
self.mode_combobox.addItem("")
self.mode_combobox.addItem("")
Translations.translate_with_setter(
lambda text: self.mode_combobox.setItemText(0, text), "ignore_list.mode.include"
)
Translations.translate_with_setter(
lambda text: self.mode_combobox.setItemText(0, text), "ignore_list.mode.exclude"
)
is_exclude_list = int(bool(self.lib.prefs(LibraryPrefs.IS_EXCLUDE_LIST)))

View File

@@ -18,6 +18,8 @@ from src.core.library import Library
from src.core.utils.dupe_files import DupeRegistry
from src.qt.modals.mirror_entities import MirrorEntriesModal
from ..translations import Translations
# Only import for type checking/autocompletion, will not be imported at runtime.
if typing.TYPE_CHECKING:
from src.qt.ts_qt import QtDriver
@@ -30,7 +32,7 @@ class FixDupeFilesModal(QWidget):
self.driver = driver
self.count = -1
self.filename = ""
self.setWindowTitle("Fix Duplicate Files") # TODO translate
Translations.translate_with_setter(self.setWindowTitle, "file.duplicates.fix")
self.setWindowModality(Qt.WindowModality.ApplicationModal)
self.setMinimumSize(400, 300)
self.root_layout = QVBoxLayout(self)
@@ -42,9 +44,7 @@ class FixDupeFilesModal(QWidget):
self.desc_widget.setObjectName("descriptionLabel")
self.desc_widget.setWordWrap(True)
self.desc_widget.setStyleSheet("text-align:left;")
self.desc_widget.setText(
"TagStudio supports importing DupeGuru results to manage duplicate files."
) # TODO translate
Translations.translate_qobject(self.desc_widget, "file.duplicates.description")
self.desc_widget.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.dupe_count = QLabel()
@@ -54,34 +54,25 @@ class FixDupeFilesModal(QWidget):
self.file_label = QLabel()
self.file_label.setObjectName("fileLabel")
self.file_label.setText("No DupeGuru File Selected") # TODO translate
Translations.translate_qobject(self.file_label, "file.duplicates.dupeguru.no_file")
self.open_button = QPushButton()
self.open_button.setText("&Load DupeGuru File") # TODO translate
Translations.translate_qobject(self.open_button, "file.duplicates.dupeguru.load_file")
self.open_button.clicked.connect(self.select_file)
self.mirror_modal = MirrorEntriesModal(self.driver, self.tracker)
self.mirror_modal.done.connect(self.refresh_dupes)
self.mirror_button = QPushButton()
self.mirror_button.setText("&Mirror Entries") # TODO translate
Translations.translate_qobject(self.mirror_button, "file.duplicates.mirror_entries")
self.mirror_button.clicked.connect(self.mirror_modal.show)
self.mirror_desc = QLabel()
self.mirror_desc.setWordWrap(True)
self.mirror_desc.setText(
"Mirror the Entry data across each duplicate match set, combining all data while not "
"removing or duplicating fields. This operation will not delete any files or data."
) # TODO translate
Translations.translate_qobject(self.mirror_desc, "file.duplicates.mirror.description")
self.advice_label = QLabel()
self.advice_label.setWordWrap(True)
# fmt: off
self.advice_label.setText(
"After mirroring, you're free to use DupeGuru to delete the unwanted files. "
"Afterwards, use TagStudio's \"Fix Unlinked Entries\" feature in the "
"Tools menu in order to delete the unlinked Entries."
)# TODO translate
# fmt: on
Translations.translate_qobject(self.advice_label, "file.duplicates.dupeguru.advice")
self.button_container = QWidget()
self.button_layout = QHBoxLayout(self.button_container)
@@ -89,7 +80,7 @@ class FixDupeFilesModal(QWidget):
self.button_layout.addStretch(1)
self.done_button = QPushButton()
self.done_button.setText("&Done") # TODO translate
Translations.translate_qobject(self.done_button, "generic.done_alt")
self.done_button.setDefault(True)
self.done_button.clicked.connect(self.hide)
self.button_layout.addWidget(self.done_button)
@@ -109,10 +100,10 @@ class FixDupeFilesModal(QWidget):
def select_file(self):
qfd = QFileDialog(
self, "Open DupeGuru Results File", str(self.lib.library_dir)
) # TODO translate
self, Translations["file.duplicates.dupeguru.open_file"], str(self.lib.library_dir)
)
qfd.setFileMode(QFileDialog.FileMode.ExistingFile)
qfd.setNameFilter("DupeGuru Files (*.dupeguru)") # TODO translate
qfd.setNameFilter(Translations["file.duplicates.dupeguru.file_extension"])
if qfd.exec_():
filename = qfd.selectedFiles()
if filename:
@@ -122,7 +113,7 @@ class FixDupeFilesModal(QWidget):
if filename:
self.file_label.setText(filename)
else:
self.file_label.setText("No DupeGuru File Selected") # TODO translate
self.file_label.setText(Translations["file.duplicates.dupeguru.no_file"])
self.filename = filename
self.refresh_dupes()
self.mirror_modal.refresh_list()
@@ -134,10 +125,14 @@ class FixDupeFilesModal(QWidget):
def set_dupe_count(self, count: int):
if count < 0:
self.mirror_button.setDisabled(True)
self.dupe_count.setText("Duplicate File Matches: N/A") # TODO translate
self.dupe_count.setText(Translations["file.duplicates.matches_uninitialized"])
elif count == 0:
self.mirror_button.setDisabled(True)
self.dupe_count.setText(f"Duplicate File Matches: {count}") # TODO translate
self.dupe_count.setText(
Translations.translate_formatted("file.duplicates.matches", count=count)
)
else:
self.mirror_button.setDisabled(False)
self.dupe_count.setText(f"Duplicate File Matches: {count}") # TODO translate
self.dupe_count.setText(
Translations.translate_formatted("file.duplicates.matches", count=count)
)

View File

@@ -14,6 +14,8 @@ from src.qt.modals.merge_dupe_entries import MergeDuplicateEntries
from src.qt.modals.relink_unlinked import RelinkUnlinkedEntries
from src.qt.widgets.progress import ProgressWidget
from ..translations import Translations
# Only import for type checking/autocompletion, will not be imported at runtime.
if typing.TYPE_CHECKING:
from src.qt.ts_qt import QtDriver
@@ -29,7 +31,7 @@ class FixUnlinkedEntriesModal(QWidget):
self.missing_count = -1
self.dupe_count = -1
self.setWindowTitle("Fix Unlinked Entries") # TODO translate
Translations.translate_with_setter(self.setWindowTitle, "entries.unlinked.title")
self.setWindowModality(Qt.WindowModality.ApplicationModal)
self.setMinimumSize(400, 300)
self.root_layout = QVBoxLayout(self)
@@ -39,13 +41,7 @@ class FixUnlinkedEntriesModal(QWidget):
self.unlinked_desc_widget.setObjectName("unlinkedDescriptionLabel")
self.unlinked_desc_widget.setWordWrap(True)
self.unlinked_desc_widget.setStyleSheet("text-align:left;")
self.unlinked_desc_widget.setText(
"Each library entry is linked to a file in one of your directories. "
"If a file linked to an entry is moved or deleted outside of TagStudio, "
"it is then considered unlinked.\n\n"
"Unlinked entries may be automatically relinked via searching your directories, "
"manually relinked by the user, or deleted if desired."
) # TODO translate
Translations.translate_qobject(self.unlinked_desc_widget, "entries.unlinked.description")
self.missing_count_label = QLabel()
self.missing_count_label.setObjectName("missingCountLabel")
@@ -58,14 +54,14 @@ class FixUnlinkedEntriesModal(QWidget):
self.dupe_count_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.refresh_unlinked_button = QPushButton()
self.refresh_unlinked_button.setText("&Refresh All") # TODO translate
Translations.translate_qobject(self.refresh_unlinked_button, "entries.unlinked.refresh_all")
self.refresh_unlinked_button.clicked.connect(self.refresh_missing_files)
self.merge_class = MergeDuplicateEntries(self.lib, self.driver)
self.relink_class = RelinkUnlinkedEntries(self.tracker)
self.search_button = QPushButton()
self.search_button.setText("&Search && Relink") # TODO translate
Translations.translate_qobject(self.search_button, "entries.unlinked.search_and_relink")
self.relink_class.done.connect(
# refresh the grid
lambda: (
@@ -76,7 +72,7 @@ class FixUnlinkedEntriesModal(QWidget):
self.search_button.clicked.connect(self.relink_class.repair_entries)
self.manual_button = QPushButton()
self.manual_button.setText("&Manual Relink") # TODO translate
Translations.translate_qobject(self.manual_button, "entries.unlinked.relink.manual")
self.manual_button.setHidden(True)
self.delete_button = QPushButton()
@@ -88,7 +84,7 @@ class FixUnlinkedEntriesModal(QWidget):
self.driver.filter_items(),
)
)
self.delete_button.setText("De&lete Unlinked Entries") # TODO translate
Translations.translate_qobject(self.delete_button, "entries.unlinked.delete_alt")
self.delete_button.clicked.connect(self.delete_modal.show)
self.button_container = QWidget()
@@ -97,7 +93,7 @@ class FixUnlinkedEntriesModal(QWidget):
self.button_layout.addStretch(1)
self.done_button = QPushButton()
self.done_button.setText("&Done") # TODO translate
Translations.translate_qobject(self.done_button, "generic.done_alt")
self.done_button.setDefault(True)
self.done_button.clicked.connect(self.hide)
self.button_layout.addWidget(self.done_button)
@@ -116,12 +112,12 @@ class FixUnlinkedEntriesModal(QWidget):
def refresh_missing_files(self):
pw = ProgressWidget(
window_title="Scanning Library", # TODO translate
label_text="Scanning Library for Unlinked Entries...", # TODO translate
cancel_button_text=None,
minimum=0,
maximum=self.lib.entries_count,
)
Translations.translate_with_setter(pw.setWindowTitle, "library.scan_library.title")
Translations.translate_with_setter(pw.update_label, "entries.unlinked.scanning")
pw.from_iterable_function(
self.tracker.refresh_missing_files,
@@ -139,11 +135,13 @@ class FixUnlinkedEntriesModal(QWidget):
if self.missing_count < 0:
self.search_button.setDisabled(True)
self.delete_button.setDisabled(True)
self.missing_count_label.setText("Unlinked Entries: N/A") # TODO translate
self.missing_count_label.setText(Translations["entries.unlinked.missing_count.none"])
else:
# disable buttons if there are no files to fix
self.search_button.setDisabled(self.missing_count == 0)
self.delete_button.setDisabled(self.missing_count == 0)
self.missing_count_label.setText(
f"Unlinked Entries: {self.missing_count}"
) # TODO translate
Translations.translate_formatted(
"entries.unlinked.missing_count.some", count=self.missing_count
)
)

View File

@@ -24,6 +24,8 @@ from src.core.library.alchemy.fields import _FieldID
from src.core.palette import ColorType, get_tag_color
from src.qt.flowlayout import FlowLayout
from ..translations import Translations
if typing.TYPE_CHECKING:
from src.qt.ts_qt import QtDriver
@@ -164,7 +166,7 @@ class FoldersToTagsModal(QWidget):
self.count = -1
self.filename = ""
self.setWindowTitle("Create Tags From Folders") # TODO translate
Translations.translate_with_setter(self.setWindowTitle, "folders_to_tags.title")
self.setWindowModality(Qt.WindowModality.ApplicationModal)
self.setMinimumSize(640, 640)
self.root_layout = QVBoxLayout(self)
@@ -174,7 +176,7 @@ class FoldersToTagsModal(QWidget):
self.title_widget.setObjectName("title")
self.title_widget.setWordWrap(True)
self.title_widget.setStyleSheet("font-weight:bold;" "font-size:14px;" "padding-top: 6px")
self.title_widget.setText("Create Tags From Folders") # TODO translate
Translations.translate_qobject(self.title_widget, "folders_to_tags.title")
self.title_widget.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.desc_widget = QLabel()
@@ -190,10 +192,10 @@ class FoldersToTagsModal(QWidget):
self.open_close_button_layout = QHBoxLayout(self.open_close_button_w)
self.open_all_button = QPushButton()
self.open_all_button.setText("Open All") # TODO translate
Translations.translate_qobject(self.open_all_button, "folders_to_tags.open_all")
self.open_all_button.clicked.connect(lambda: self.set_all_branches(False))
self.close_all_button = QPushButton()
self.close_all_button.setText("Close All") # TODO translate
Translations.translate_qobject(self.close_all_button, "folders_to_tags.close_all")
self.close_all_button.clicked.connect(lambda: self.set_all_branches(True))
self.open_close_button_layout.addWidget(self.open_all_button)
@@ -212,7 +214,7 @@ class FoldersToTagsModal(QWidget):
self.scroll_area.setWidget(self.scroll_contents)
self.apply_button = QPushButton()
self.apply_button.setText("&Apply") # TODO translate
Translations.translate_qobject(self.apply_button, "generic.apply_alt")
self.apply_button.setMinimumWidth(100)
self.apply_button.clicked.connect(self.on_apply)

View File

@@ -9,6 +9,8 @@ from src.core.library import Library
from src.core.utils.dupe_files import DupeRegistry
from src.qt.widgets.progress import ProgressWidget
from ..translations import Translations
# Only import for type checking/autocompletion, will not be imported at runtime.
if typing.TYPE_CHECKING:
from src.qt.ts_qt import QtDriver
@@ -25,11 +27,11 @@ class MergeDuplicateEntries(QObject):
def merge_entries(self):
pw = ProgressWidget(
window_title="Merging Duplicate Entries", # TODO translate
label_text="Merging Duplicate Entries...", # TODO translate
cancel_button_text=None,
minimum=0,
maximum=self.tracker.groups_count,
)
Translations.translate_with_setter(pw.setWindowTitle, "entries.duplicate.merge.label")
Translations.translate_with_setter(pw.update_label, "entries.duplicate.merge.label")
pw.from_iterable_function(self.tracker.merge_dupe_entries, None, self.done.emit)

View File

@@ -19,6 +19,8 @@ from PySide6.QtWidgets import (
from src.core.utils.dupe_files import DupeRegistry
from src.qt.widgets.progress import ProgressWidget
from ..translations import Translations
# Only import for type checking/autocompletion, will not be imported at runtime.
if typing.TYPE_CHECKING:
from src.qt.ts_qt import QtDriver
@@ -30,7 +32,7 @@ class MirrorEntriesModal(QWidget):
def __init__(self, driver: "QtDriver", tracker: DupeRegistry):
super().__init__()
self.driver = driver
self.setWindowTitle("Mirror Entries") # TODO translate
Translations.translate_with_setter(self.setWindowTitle, "entries.mirror.window_title")
self.setWindowModality(Qt.WindowModality.ApplicationModal)
self.setMinimumSize(500, 400)
self.root_layout = QVBoxLayout(self)
@@ -40,10 +42,9 @@ class MirrorEntriesModal(QWidget):
self.desc_widget = QLabel()
self.desc_widget.setObjectName("descriptionLabel")
self.desc_widget.setWordWrap(True)
self.desc_widget.setText(f"""
Are you sure you want to mirror the following {self.tracker.groups_count} Entries?
""") # TODO translate
Translations.translate_qobject(
self.desc_widget, "entries.mirror.confirmation", count=self.tracker.groups_count
)
self.desc_widget.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.list_view = QListView()
@@ -56,13 +57,13 @@ class MirrorEntriesModal(QWidget):
self.button_layout.addStretch(1)
self.cancel_button = QPushButton()
self.cancel_button.setText("&Cancel") # TODO translate
Translations.translate_qobject(self.cancel_button, "generic.cancel_alt")
self.cancel_button.setDefault(True)
self.cancel_button.clicked.connect(self.hide)
self.button_layout.addWidget(self.cancel_button)
self.mirror_button = QPushButton()
self.mirror_button.setText("&Mirror") # TODO translate
Translations.translate_qobject(self.mirror_button, "entries.mirror")
self.mirror_button.clicked.connect(self.hide)
self.mirror_button.clicked.connect(self.mirror_entries)
self.button_layout.addWidget(self.mirror_button)
@@ -72,9 +73,11 @@ class MirrorEntriesModal(QWidget):
self.root_layout.addWidget(self.button_container)
def refresh_list(self):
self.desc_widget.setText(f"""
Are you sure you want to mirror the following {self.tracker.groups_count} Entries?
""")
self.desc_widget.setText(
Translations.translate_formatted(
"entries.mirror.confirmation", count=self.tracker.groups_count
)
)
self.model.clear()
for i in self.tracker.groups:
@@ -82,15 +85,16 @@ class MirrorEntriesModal(QWidget):
def mirror_entries(self):
def displayed_text(x):
return f"Mirroring {x + 1}/{self.tracker.groups_count} Entries..." # TODO translate
return Translations.translate_formatted(
"entries.mirror.label", idx=x + 1, count=self.tracker.groups_count
)
pw = ProgressWidget(
window_title="Mirroring Entries", # TODO translate
label_text="",
cancel_button_text=None,
minimum=0,
maximum=self.tracker.groups_count,
)
Translations.translate_with_setter(pw.setWindowTitle, "entries.mirror.title")
pw.from_iterable_function(
self.mirror_entries_runnable,

View File

@@ -7,6 +7,8 @@ from PySide6.QtCore import QObject, Signal
from src.core.utils.missing_files import MissingRegistry
from src.qt.widgets.progress import ProgressWidget
from ..translations import Translations
class RelinkUnlinkedEntries(QObject):
done = Signal()
@@ -17,16 +19,19 @@ class RelinkUnlinkedEntries(QObject):
def repair_entries(self):
def displayed_text(x):
text = f"Attempting to Relink {x}/{self.tracker.missing_files_count} Entries. \n" # TODO translate
text += f"{self.tracker.files_fixed_count} Successfully Relinked." # TODO translate
return text
return Translations.translate_formatted(
"entries.unlinked.relink.attempting",
idx=x,
missing_count=self.tracker.missing_files_count,
fixed_count=self.tracker.files_fixed_count,
)
pw = ProgressWidget(
window_title="Relinking Entries", # TODO translate
label_text="",
cancel_button_text=None,
minimum=0,
maximum=self.tracker.missing_files_count,
)
Translations.translate_with_setter(pw.setWindowTitle, "entries.unlinked.relink.title")
pw.from_iterable_function(self.tracker.fix_missing_files, displayed_text, self.done.emit)

View File

@@ -21,6 +21,8 @@ from src.qt.modals.build_tag import BuildTagPanel
from src.qt.widgets.panel import PanelModal, PanelWidget
from src.qt.widgets.tag import TagWidget
from ..translations import Translations
logger = structlog.get_logger(__name__)
# TODO: This class shares the majority of its code with tag_search.py.
@@ -44,7 +46,7 @@ class TagDatabasePanel(PanelWidget):
self.search_field = QLineEdit()
self.search_field.setObjectName("searchField")
self.search_field.setMinimumSize(QSize(0, 32))
self.search_field.setPlaceholderText("Search Tags") # TODO translate
Translations.translate_with_setter(self.search_field.setPlaceholderText, "home.search_tags")
self.search_field.textEdited.connect(lambda: self.update_tags(self.search_field.text()))
self.search_field.returnPressed.connect(
lambda checked=False: self.on_return(self.search_field.text())
@@ -63,7 +65,7 @@ class TagDatabasePanel(PanelWidget):
self.scroll_area.setWidget(self.scroll_contents)
self.create_tag_button = QPushButton()
self.create_tag_button.setText("Create Tag") # TODO translate
Translations.translate_qobject(self.create_tag_button, "tag.create")
self.create_tag_button.clicked.connect(self.build_tag)
self.root_layout.addWidget(self.search_field)
@@ -72,14 +74,14 @@ class TagDatabasePanel(PanelWidget):
self.update_tags()
def build_tag(self):
panel = BuildTagPanel(self.lib)
self.modal = PanelModal(
BuildTagPanel(self.lib),
"New Tag", # TODO translate
"Add Tag", # TODO translate
panel,
has_save=True,
)
Translations.translate_with_setter(self.modal.setTitle, "tag.new")
Translations.translate_with_setter(self.modal.setWindowTitle, "tag.add")
panel: BuildTagPanel = self.modal.widget
self.modal.saved.connect(
lambda: (
self.lib.add_tag(
@@ -134,10 +136,8 @@ class TagDatabasePanel(PanelWidget):
return
message_box = QMessageBox()
message_box.setWindowTitle("Remove Tag") # TODO translate
message_box.setText(
f'Are you sure you want to delete the tag "{tag.name}"?'
) # TODO translate
Translations.translate_with_setter(message_box.setWindowTitle, "tag.remove")
Translations.translate_qobject(message_box, "tag_database.confirmation", tag_name=tag.name)
message_box.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel) # type: ignore
message_box.setIcon(QMessageBox.Question) # type: ignore
@@ -158,10 +158,10 @@ class TagDatabasePanel(PanelWidget):
self.edit_modal = PanelModal(
build_tag_panel,
tag.name,
"Edit Tag", # TODO translate
done_callback=(self.update_tags(self.search_field.text())),
has_save=True,
)
Translations.translate_with_setter(self.edit_modal.setWindowTitle, "tag.edit")
# TODO Check Warning: Expected type 'BuildTagPanel', got 'PanelWidget' instead
self.edit_modal.saved.connect(lambda: self.edit_tag_callback(build_tag_panel))
self.edit_modal.show()

View File

@@ -22,6 +22,8 @@ from src.core.palette import ColorType, get_tag_color
from src.qt.widgets.panel import PanelWidget
from src.qt.widgets.tag import TagWidget
from ..translations import Translations
logger = structlog.get_logger(__name__)
@@ -41,7 +43,7 @@ class TagSearchPanel(PanelWidget):
self.search_field = QLineEdit()
self.search_field.setObjectName("searchField")
self.search_field.setMinimumSize(QSize(0, 32))
self.search_field.setPlaceholderText("Search Tags") # TODO translate
Translations.translate_with_setter(self.search_field.setPlaceholderText, "home.search_tags")
self.search_field.textEdited.connect(lambda: self.update_tags(self.search_field.text()))
self.search_field.returnPressed.connect(
lambda checked=False: self.on_return(self.search_field.text())

View File

@@ -4,7 +4,7 @@ from typing import Callable
import ujson
from PySide6.QtCore import QObject, Signal
from PySide6.QtGui import QAction
from PySide6.QtWidgets import QLabel, QMenu, QPushButton
from PySide6.QtWidgets import QLabel, QMenu, QMessageBox, QPushButton
DEFAULT_TRANSLATION = "de"
@@ -49,7 +49,7 @@ class Translator:
def translate_qobject(self, widget: QObject, key: str, **kwargs):
"""Translates the text of the QObject using :func:`translate_with_setter`."""
if isinstance(widget, (QLabel, QAction, QPushButton)):
if isinstance(widget, (QLabel, QAction, QPushButton, QMessageBox)):
self.translate_with_setter(widget.setText, key, **kwargs)
elif isinstance(widget, (QMenu)):
self.translate_with_setter(widget.setTitle, key, **kwargs)

View File

@@ -522,7 +522,7 @@ class QtDriver(DriverMixin, QObject):
msg_box.setIcon(QMessageBox.Icon.Critical)
msg_box.setText(message)
msg_box.setWindowTitle(Translations["window.title.error"])
msg_box.addButton(Translations["window.button.close"], QMessageBox.ButtonRole.AcceptRole)
msg_box.addButton(Translations["generic.close"], QMessageBox.ButtonRole.AcceptRole)
# Show the message box
msg_box.exec()