feat: translation in ts_qt.py

This commit is contained in:
Jann Stute
2024-12-23 02:10:30 +01:00
parent 9ab681febb
commit c857eb4f1c
3 changed files with 166 additions and 79 deletions

View File

@@ -1,6 +1,7 @@
{
"app.git": "Git Commit",
"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": "Merge Duplicate Entries",
@@ -69,6 +70,11 @@
"home.search_library": "Search Library",
"home.search_tags": "Search Tags",
"home.search": "Search",
"home.thumbnail_size.extra_large": "Extra Large Thumbnails",
"home.thumbnail_size.large": "Large Thumbnails",
"home.thumbnail_size.medium": "Medium Thumbnails",
"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.mode.exclude": "Exclude",
@@ -82,23 +88,34 @@
"library.missing": "Library Location is Missing",
"library.name": "Library",
"library.refresh.scanning_preparing": "Scanning Directories for New Files...\nPreparing...",
"library.refresh.scanning": "Scanning Directories for New Files...\n%{x + 1} File%{\"s\" if x + 1 != 1 else \"\"} Searched, %{len(self.lib.files_not_in_library)} New Files Found",
"library.refresh.scanning.plural": "Scanning Directories for New Files...\n{searched_count} Files Searched, {found_count} New Files Found",
"library.refresh.scanning.singular": "Scanning Directories for New Files...\n{searched_count} File Searched, {found_count} New Files Found",
"library.refresh.title": "Refreshing Directories",
"library.scan_library.title": "Scanning Library",
"macros.running.dialog.new_entries": "Running Configured Macros on %{x + 1}/%{len(new_ids)} New Entries",
"macros.running.dialog.new_entries": "Running Configured Macros on {count}/{total} New Entries",
"macros.running.dialog.title": "Running Macros on New Entries",
"menu.edit.ignore_list": "Ignore Files and Folders",
"menu.edit": "Edit",
"menu.edit.manage_file_extensions": "Manage File Extensions",
"menu.edit.manage_tags": "Manage Tags",
"menu.edit.new_tag": "New &Tag",
"menu.edit": "&Edit",
"menu.file.close_library": "&Close Library",
"menu.file.new_library": "New Library",
"menu.file.open_create_library": "Open/Create Library",
"menu.file.open_create_library": "&Open/Create Library",
"menu.file.open_library": "Open Library",
"menu.file.save_library": "Save Library",
"menu.file": "File",
"menu.help": "Help",
"menu.macros": "Macros",
"menu.file.save_backup": "&Save Library Backup",
"menu.file.refresh_directories": "&Refresh Directories",
"menu.file": "&File",
"menu.help": "&Help",
"menu.macros.autofill": "Autofill",
"menu.macros.folders_to_tags": "Folders to Tags",
"menu.macros": "&Macros",
"menu.select": "Select",
"menu.tools": "Tools",
"menu.view": "View",
"menu.tools.fix_duplicate_files": "Fix Duplicate &Files",
"menu.tools.fix_unlinked_entries": "Fix &Unlinked Entries",
"menu.tools": "&Tools",
"menu.view": "&View",
"menu.window": "Window",
"preview.no_selection": "No Items Selected",
"select.all": "Select All",
@@ -106,11 +123,14 @@
"settings.open_library_on_start": "Open Library on Start",
"settings.show_filenames_in_grid": "Show Filenames in Grid",
"settings.show_recent_libraries": "Show Recent Libraries",
"splash.opening_library": "Opening Library",
"status.library_backup_success": "Library Backup Saved at:",
"splash.opening_library": "Opening Library \"{library_path}\"...",
"status.library_closing": "Closing Library...",
"status.library_closed": "Library Closed ({time_span})",
"status.library_backup_in_progress": "Saving Library Backup...",
"status.library_backup_success": "Library Backup Saved at: \"{path}\" ({time_span})",
"status.library_save_success": "Library Saved and Closed!",
"status.library_search_query": "Searching Library for",
"status.results_found": "{results.total_count} Results Found",
"status.library_search_query": "Searching Library...",
"status.results_found": "{count} Results Found ({time_span})",
"status.results": "Results",
"tag_manager.title": "Library Tags",
"tag.add_to_search": "Add to Search",
@@ -128,5 +148,9 @@
"view.size.1": "Small",
"view.size.2": "Medium",
"view.size.3": "Large",
"view.size.4": "Extra 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."
}

View File

@@ -46,7 +46,7 @@ class Translator:
for k in self._strings:
self._strings[k].value = translated.get(k, None)
def translate_widget(self, widget: QObject, key: str, **kwargs):
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)):
self.translate_with_setter(widget.setText, key, **kwargs)
@@ -66,9 +66,9 @@ class Translator:
if key in self._strings:
self._strings[key].changed.connect(set_text)
set_text(self.translate(key))
set_text(self.translate_formatted(key))
def translate(self, key: str, **kwargs) -> str:
def translate_formatted(self, key: str, **kwargs) -> str:
return self[key].format(**kwargs)
def __getitem__(self, key: str) -> str:

View File

@@ -91,6 +91,8 @@ from src.qt.widgets.preview_panel import PreviewPanel
from src.qt.widgets.progress import ProgressWidget
from src.qt.widgets.thumb_renderer import ThumbRenderer
from .translations import Translations
# SIGQUIT is not defined on Windows
if sys.platform == "win32":
from signal import SIGINT, SIGTERM, signal
@@ -186,10 +188,10 @@ class QtDriver(DriverMixin, QObject):
def open_library_from_dialog(self):
dir = QFileDialog.getExistingDirectory(
None,
"Open/Create Library",
"/",
QFileDialog.Option.ShowDirsOnly,
parent=None,
caption=Translations["window.title.open_create_library"],
dir="/",
options=QFileDialog.Option.ShowDirsOnly,
)
if dir not in (None, ""):
self.open_library(Path(dir))
@@ -254,15 +256,22 @@ class QtDriver(DriverMixin, QObject):
self.main_window.setMenuBar(menu_bar)
menu_bar.setNativeMenuBar(True)
file_menu = QMenu("&File", menu_bar)
edit_menu = QMenu("&Edit", menu_bar)
view_menu = QMenu("&View", menu_bar)
tools_menu = QMenu("&Tools", menu_bar)
macros_menu = QMenu("&Macros", menu_bar)
help_menu = QMenu("&Help", menu_bar)
file_menu = QMenu(menu_bar)
Translations.translate_qobject(file_menu, "menu.file")
edit_menu = QMenu(menu_bar)
Translations.translate_qobject(edit_menu, "menu.edit")
view_menu = QMenu(menu_bar)
Translations.translate_qobject(view_menu, "menu.view")
tools_menu = QMenu(menu_bar)
Translations.translate_qobject(tools_menu, "menu.tools")
macros_menu = QMenu(menu_bar)
Translations.translate_qobject(macros_menu, "menu.macros")
help_menu = QMenu(menu_bar)
Translations.translate_qobject(help_menu, "menu.help")
# File Menu ============================================================
open_library_action = QAction("&Open/Create Library", menu_bar)
open_library_action = QAction(menu_bar)
Translations.translate_qobject(open_library_action, "menu.file.open_create_library")
open_library_action.triggered.connect(lambda: self.open_library_from_dialog())
open_library_action.setShortcut(
QtCore.QKeyCombination(
@@ -273,7 +282,8 @@ class QtDriver(DriverMixin, QObject):
open_library_action.setToolTip("Ctrl+O")
file_menu.addAction(open_library_action)
save_library_backup_action = QAction("&Save Library Backup", menu_bar)
save_library_backup_action = QAction(menu_bar)
Translations.translate_qobject(save_library_backup_action, "menu.file.save_backup")
save_library_backup_action.triggered.connect(
lambda: self.callback_library_needed_check(self.backup_library)
)
@@ -291,7 +301,8 @@ class QtDriver(DriverMixin, QObject):
file_menu.addSeparator()
add_new_files_action = QAction("&Refresh Directories", menu_bar)
add_new_files_action = QAction(menu_bar)
Translations.translate_qobject(add_new_files_action, "menu.file.refresh_directories")
add_new_files_action.triggered.connect(
lambda: self.callback_library_needed_check(self.add_new_files_callback)
)
@@ -305,12 +316,14 @@ class QtDriver(DriverMixin, QObject):
file_menu.addAction(add_new_files_action)
file_menu.addSeparator()
close_library_action = QAction("&Close Library", menu_bar)
close_library_action = QAction(menu_bar)
Translations.translate_qobject(close_library_action, "menu.file.close_library")
close_library_action.triggered.connect(self.close_library)
file_menu.addAction(close_library_action)
file_menu.addSeparator()
open_on_start_action = QAction("Open Library on Start", self)
open_on_start_action = QAction(self)
Translations.translate_qobject(open_on_start_action, "settings.open_library_on_start")
open_on_start_action.setCheckable(True)
open_on_start_action.setChecked(
bool(self.settings.value(SettingItems.START_LOAD_LAST, defaultValue=True, type=bool))
@@ -321,7 +334,8 @@ class QtDriver(DriverMixin, QObject):
file_menu.addAction(open_on_start_action)
# Edit Menu ============================================================
new_tag_action = QAction("New &Tag", menu_bar)
new_tag_action = QAction(menu_bar)
Translations.translate_qobject(new_tag_action, "menu.edit.new_tag")
new_tag_action.triggered.connect(lambda: self.add_tag_action_callback())
new_tag_action.setShortcut(
QtCore.QKeyCombination(
@@ -334,7 +348,8 @@ class QtDriver(DriverMixin, QObject):
edit_menu.addSeparator()
select_all_action = QAction("Select All", menu_bar)
select_all_action = QAction(menu_bar)
Translations.translate_qobject(select_all_action, "select.all")
select_all_action.triggered.connect(self.select_all_action_callback)
select_all_action.setShortcut(
QtCore.QKeyCombination(
@@ -345,7 +360,8 @@ class QtDriver(DriverMixin, QObject):
select_all_action.setToolTip("Ctrl+A")
edit_menu.addAction(select_all_action)
clear_select_action = QAction("Clear Selection", menu_bar)
clear_select_action = QAction(menu_bar)
Translations.translate_qobject(clear_select_action, "select.clear")
clear_select_action.triggered.connect(self.clear_select_action_callback)
clear_select_action.setShortcut(QtCore.Qt.Key.Key_Escape)
clear_select_action.setToolTip("Esc")
@@ -353,16 +369,21 @@ class QtDriver(DriverMixin, QObject):
edit_menu.addSeparator()
manage_file_extensions_action = QAction("Manage File Extensions", menu_bar)
manage_file_extensions_action = QAction(menu_bar)
Translations.translate_qobject(
manage_file_extensions_action, "menu.edit.manage_file_extensions"
)
manage_file_extensions_action.triggered.connect(self.show_file_extension_modal)
edit_menu.addAction(manage_file_extensions_action)
tag_database_action = QAction("Manage Tags", menu_bar)
tag_database_action = QAction(menu_bar)
Translations.translate_qobject(tag_database_action, "menu.edit.manage_tags")
tag_database_action.triggered.connect(lambda: self.show_tag_database())
edit_menu.addAction(tag_database_action)
# View Menu ============================================================
show_libs_list_action = QAction("Show Recent Libraries", menu_bar)
show_libs_list_action = QAction(menu_bar)
Translations.translate_qobject(show_libs_list_action, "settings.show_recent_libraries")
show_libs_list_action.setCheckable(True)
show_libs_list_action.setChecked(
bool(self.settings.value(SettingItems.WINDOW_SHOW_LIBS, defaultValue=True, type=bool))
@@ -375,7 +396,8 @@ class QtDriver(DriverMixin, QObject):
)
view_menu.addAction(show_libs_list_action)
show_filenames_action = QAction("Show Filenames in Grid", menu_bar)
show_filenames_action = QAction(menu_bar)
Translations.translate_qobject(show_filenames_action, "settings.show_filenames_in_grid")
show_filenames_action.setCheckable(True)
show_filenames_action.setChecked(
bool(self.settings.value(SettingItems.SHOW_FILENAMES, defaultValue=True, type=bool))
@@ -394,7 +416,10 @@ class QtDriver(DriverMixin, QObject):
self.unlinked_modal = FixUnlinkedEntriesModal(self.lib, self)
self.unlinked_modal.show()
fix_unlinked_entries_action = QAction("Fix &Unlinked Entries", menu_bar)
fix_unlinked_entries_action = QAction(menu_bar)
Translations.translate_qobject(
fix_unlinked_entries_action, "menu.tools.fix_unlinked_entries"
)
fix_unlinked_entries_action.triggered.connect(create_fix_unlinked_entries_modal)
tools_menu.addAction(fix_unlinked_entries_action)
@@ -403,7 +428,8 @@ class QtDriver(DriverMixin, QObject):
self.dupe_modal = FixDupeFilesModal(self.lib, self)
self.dupe_modal.show()
fix_dupe_files_action = QAction("Fix Duplicate &Files", menu_bar)
fix_dupe_files_action = QAction(menu_bar)
Translations.translate_qobject(fix_dupe_files_action, "menu.tools.fix_duplicate_files")
fix_dupe_files_action.triggered.connect(create_dupe_files_modal)
tools_menu.addAction(fix_dupe_files_action)
@@ -412,7 +438,8 @@ class QtDriver(DriverMixin, QObject):
# tools_menu.addAction(create_collage_action)
# Macros Menu ==========================================================
self.autofill_action = QAction("Autofill", menu_bar)
self.autofill_action = QAction(menu_bar)
Translations.translate_qobject(self.autofill_action, "menu.macros.autofill")
self.autofill_action.triggered.connect(
lambda: (
self.run_macros(MacroID.AUTOFILL, self.selected),
@@ -426,12 +453,14 @@ class QtDriver(DriverMixin, QObject):
self.folders_modal = FoldersToTagsModal(self.lib, self)
self.folders_modal.show()
folders_to_tags_action = QAction("Folders to Tags", menu_bar)
folders_to_tags_action = QAction(menu_bar)
Translations.translate_qobject(folders_to_tags_action, "menu.macros.folders_to_tags")
folders_to_tags_action.triggered.connect(create_folders_tags_modal)
macros_menu.addAction(folders_to_tags_action)
# Help Menu ============================================================
self.repo_action = QAction("Visit GitHub Repository", menu_bar)
self.repo_action = QAction(menu_bar)
Translations.translate_qobject(self.repo_action, "help.visit_github")
self.repo_action.triggered.connect(
lambda: webbrowser.open("https://github.com/TagStudioDev/TagStudio")
)
@@ -455,12 +484,13 @@ class QtDriver(DriverMixin, QObject):
str(Path(__file__).parents[2] / "resources/qt/fonts/Oxanium-Bold.ttf")
)
# TODO this doesn't update when the language is changed
self.thumb_sizes: list[tuple[str, int]] = [
("Extra Large Thumbnails", 256),
("Large Thumbnails", 192),
("Medium Thumbnails", 128),
("Small Thumbnails", 96),
("Mini Thumbnails", 76),
(Translations["home.thumbnail_size.extra_large"], 256),
(Translations["home.thumbnail_size.large"], 192),
(Translations["home.thumbnail_size.medium"], 128),
(Translations["home.thumbnail_size.small"], 96),
(Translations["home.thumbnail_size.mini"], 76),
]
self.item_thumbs: list[ItemThumb] = []
self.thumb_renderers: list[ThumbRenderer] = []
@@ -472,7 +502,9 @@ class QtDriver(DriverMixin, QObject):
# check status of library path evaluating
if path_result.success and path_result.library_path:
self.splash.showMessage(
f'Opening Library "{path_result.library_path}"...',
Translations.translate_formatted(
"splash.opening_library", library_path=path_result.library_path
),
int(Qt.AlignmentFlag.AlignBottom | Qt.AlignmentFlag.AlignHCenter),
QColor("#9782ff"),
)
@@ -489,8 +521,8 @@ class QtDriver(DriverMixin, QObject):
msg_box = QMessageBox()
msg_box.setIcon(QMessageBox.Icon.Critical)
msg_box.setText(message)
msg_box.setWindowTitle("Error")
msg_box.addButton("Close", QMessageBox.ButtonRole.AcceptRole)
msg_box.setWindowTitle(Translations["window.title.error"])
msg_box.addButton(Translations["window.button.close"], QMessageBox.ButtonRole.AcceptRole)
# Show the message box
msg_box.exec()
@@ -584,7 +616,7 @@ class QtDriver(DriverMixin, QObject):
return
logger.info("Closing Library...")
self.main_window.statusbar.showMessage("Closing Library...")
self.main_window.statusbar.showMessage(Translations["status.library_closing"])
start_time = time.time()
self.settings.setValue(SettingItems.LAST_LIBRARY, str(self.lib.library_dir))
@@ -610,26 +642,32 @@ class QtDriver(DriverMixin, QObject):
end_time = time.time()
self.main_window.statusbar.showMessage(
f"Library Closed ({format_timespan(end_time - start_time)})"
Translations.translate_formatted(
"status.library_closed", time_span=format_timespan(end_time - start_time)
)
)
def backup_library(self):
logger.info("Backing Up Library...")
self.main_window.statusbar.showMessage("Saving Library...")
self.main_window.statusbar.showMessage(Translations["status.library_backup_in_progress"])
start_time = time.time()
target_path = self.lib.save_library_backup_to_disk()
end_time = time.time()
self.main_window.statusbar.showMessage(
f'Library Backup Saved at: "{target_path}" ({format_timespan(end_time - start_time)})'
Translations.translate_formatted(
"status.library_backup_success",
path=target_path,
time_span=format_timespan(end_time - start_time),
)
)
def add_tag_action_callback(self):
self.modal = PanelModal(
BuildTagPanel(self.lib),
"New Tag",
"Add Tag",
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(
@@ -662,21 +700,21 @@ class QtDriver(DriverMixin, QObject):
def show_tag_database(self):
self.modal = PanelModal(
widget=TagDatabasePanel(self.lib),
title="Library Tags",
window_title="Library Tags",
done_callback=self.preview_panel.update_widgets,
has_save=False,
)
Translations.translate_with_setter(self.modal.setTitle, "tag_manager.title")
Translations.translate_with_setter(self.modal.setWindowTitle, "tag_manager.title")
self.modal.show()
def show_file_extension_modal(self):
panel = FileExtensionModal(self.lib)
self.modal = PanelModal(
panel,
"File Extensions",
"File Extensions",
has_save=True,
)
Translations.translate_with_setter(self.modal.setTitle, "ignore_list.title")
Translations.translate_with_setter(self.modal.setWindowTitle, "ignore_list.title")
self.modal.saved.connect(lambda: (panel.save(), self.filter_items()))
self.modal.show()
@@ -686,12 +724,13 @@ class QtDriver(DriverMixin, QObject):
tracker = RefreshDirTracker(self.lib)
pw = ProgressWidget(
window_title="Refreshing Directories",
label_text="Scanning Directories for New Files...\nPreparing...",
cancel_button_text=None,
minimum=0,
maximum=0,
)
Translations.translate_with_setter(pw.setWindowTitle, "library.refresh.title")
Translations.translate_with_setter(pw.update_label, "library.refresh.scanning_preparing")
pw.show()
iterator = FunctionIterator(lambda: tracker.refresh_dir(self.lib.library_dir))
@@ -699,9 +738,13 @@ class QtDriver(DriverMixin, QObject):
lambda x: (
pw.update_progress(x + 1),
pw.update_label(
f"Scanning Directories for New Files...\n{x + 1}"
f' File{"s" if x + 1 != 1 else ""} Searched,'
f" {tracker.files_count} New Files Found"
Translations.translate_formatted(
"library.refresh.scanning.plural"
if x + 1 != 1
else "library.refresh.scanning.singular",
searched_count=x + 1,
found_count=tracker.files_count,
)
),
)
)
@@ -724,18 +767,24 @@ class QtDriver(DriverMixin, QObject):
iterator = FunctionIterator(tracker.save_new_files)
pw = ProgressWidget(
window_title="Running Macros on New Entries",
label_text=f"Running Configured Macros on 1/{files_count} New Entries",
cancel_button_text=None,
minimum=0,
maximum=files_count,
)
Translations.translate_with_setter(pw.setWindowTitle, "macros.running.dialog.title")
Translations.translate_with_setter(
pw.update_label, "macros.running.dialog.new_entries", count=1, total=files_count
)
pw.show()
iterator.value.connect(
lambda x: (
pw.update_progress(x + 1),
pw.update_label(f"Running Configured Macros on {x + 1}/{files_count} New Entries"),
pw.update_label(
Translations.translate_formatted(
"macros.running.dialog.new_entries", count=x + 1, total=files_count
)
),
)
)
r = CustomRunnable(iterator.run)
@@ -1131,7 +1180,7 @@ class QtDriver(DriverMixin, QObject):
self.filter = dataclasses.replace(self.filter, **dataclasses.asdict(filter))
# inform user about running search
self.main_window.statusbar.showMessage("Searching Library...")
self.main_window.statusbar.showMessage(Translations["status.library_search_query"])
self.main_window.statusbar.repaint()
# search the library
@@ -1146,7 +1195,11 @@ class QtDriver(DriverMixin, QObject):
# inform user about completed search
self.main_window.statusbar.showMessage(
f"{results.total_count} Results Found ({format_timespan(end_time - start_time)})"
Translations.translate_formatted(
"status.results_found",
count=results.total_count,
time_span=format_timespan(end_time - start_time),
)
)
# update page content
@@ -1193,9 +1246,13 @@ class QtDriver(DriverMixin, QObject):
def open_library(self, path: Path) -> None:
"""Open a TagStudio library."""
open_message: str = f'Opening Library "{str(path)}"...'
self.main_window.landing_widget.set_status_label(open_message)
self.main_window.statusbar.showMessage(open_message, 3)
translation_params = {"key": "splash.opening_library", "library_path": str(path)}
Translations.translate_with_setter(
self.main_window.landing_widget.set_status_label, **translation_params
)
self.main_window.statusbar.showMessage(
Translations.translate_formatted(**translation_params), 3
)
self.main_window.repaint()
open_status: LibraryStatus = self.lib.open_library(path)
@@ -1213,7 +1270,9 @@ class QtDriver(DriverMixin, QObject):
def init_library(self, path: Path, open_status: LibraryStatus):
if not open_status.success:
self.show_error_message(open_status.message or "Error opening library.")
self.show_error_message(
open_status.message or Translations["window.message.error_opening_library"]
)
return open_status
self.init_workers()
@@ -1225,8 +1284,12 @@ class QtDriver(DriverMixin, QObject):
self.add_new_files_callback()
self.update_libs_list(path)
title_text = f"{self.base_title} - Library '{self.lib.library_dir}'"
self.main_window.setWindowTitle(title_text)
Translations.translate_with_setter(
self.main_window.setWindowTitle,
"app.title",
base_title=self.base_title,
library_dir=self.lib.library_dir,
)
self.main_window.setAcceptDrops(True)
self.selected.clear()