Skip to content

fix: restore translate_formatted() method #830

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion tagstudio/src/qt/modals/about.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ def __init__(self, config_path):
if ff_version["ffprobe"] is not None:
ffprobe = '<span style="color:green">Found</span> (' + ff_version["ffprobe"] + ")"
self.content_widget = QLabel(
Translations["about.content"].format(
Translations.format(
"about.content",
version=VERSION,
branch=VERSION_BRANCH,
config_path=config_path,
Expand Down
7 changes: 4 additions & 3 deletions tagstudio/src/qt/modals/delete_unlinked.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ def __init__(self, driver: "QtDriver", tracker: MissingRegistry):
self.root_layout.setContentsMargins(6, 6, 6, 6)

self.desc_widget = QLabel(
Translations["entries.unlinked.delete.confirm"].format(
Translations.format(
"entries.unlinked.delete.confirm",
count=self.tracker.missing_file_entries_count,
)
)
Expand Down Expand Up @@ -65,8 +66,8 @@ def __init__(self, driver: "QtDriver", tracker: MissingRegistry):

def refresh_list(self):
self.desc_widget.setText(
Translations["entries.unlinked.delete.confirm"].format(
count=self.tracker.missing_file_entries_count
Translations.format(
"entries.unlinked.delete.confirm", count=self.tracker.missing_file_entries_count
)
)

Expand Down
9 changes: 4 additions & 5 deletions tagstudio/src/qt/modals/drop_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,8 @@ def ask_duplicates_choice(self):
self.desc_widget.setText(
Translations["drop_import.duplicates_choice.singular"]
if len(self.duplicate_files) == 1
else Translations["drop_import.duplicates_choice.plural"].format(
count=len(self.duplicate_files)
else Translations.format(
"drop_import.duplicates_choice.plural", count=len(self.duplicate_files)
)
)

Expand All @@ -154,11 +154,10 @@ def begin_transfer(self, choice: DuplicateChoice | None = None):
return

def displayed_text(x):
return Translations[
return Translations.format(
"drop_import.progress.label.singular"
if x[0] + 1 == 1
else "drop_import.progress.label.plural"
].format(
else "drop_import.progress.label.plural",
count=x[0] + 1,
suffix=f" {x[1]} {self.choice.value}" if self.choice else "",
)
Expand Down
4 changes: 2 additions & 2 deletions tagstudio/src/qt/modals/fix_dupes.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,10 @@ def set_dupe_count(self, count: int):
self.dupe_count.setText(Translations["file.duplicates.matches_uninitialized"])
elif count == 0:
self.mirror_button.setDisabled(True)
self.dupe_count.setText(Translations["file.duplicates.matches"].format(count=count))
self.dupe_count.setText(Translations.format("file.duplicates.matches", count=count))
else:
self.mirror_button.setDisabled(False)
self.dupe_count.setText(Translations["file.duplicates.matches"].format(count=count))
self.dupe_count.setText(Translations.format("file.duplicates.matches", count=count))

@override
def keyPressEvent(self, event: QtGui.QKeyEvent) -> None: # noqa N802
Expand Down
2 changes: 1 addition & 1 deletion tagstudio/src/qt/modals/fix_unlinked.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ def set_missing_count(self, count: int | None = None):
self.search_button.setDisabled(self.missing_count == 0)
self.delete_button.setDisabled(self.missing_count == 0)
self.missing_count_label.setText(
Translations["entries.unlinked.missing_count.some"].format(count=self.missing_count)
Translations.format("entries.unlinked.missing_count.some", count=self.missing_count)
)

@override
Expand Down
8 changes: 4 additions & 4 deletions tagstudio/src/qt/modals/mirror_entities.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def __init__(self, driver: "QtDriver", tracker: DupeRegistry):
self.tracker = tracker

self.desc_widget = QLabel(
Translations["entries.mirror.confirmation"].format(count=self.tracker.groups_count)
Translations.format("entries.mirror.confirmation", count=self.tracker.groups_count)
)
self.desc_widget.setObjectName("descriptionLabel")
self.desc_widget.setWordWrap(True)
Expand Down Expand Up @@ -63,7 +63,7 @@ def __init__(self, driver: "QtDriver", tracker: DupeRegistry):

def refresh_list(self):
self.desc_widget.setText(
Translations["entries.mirror.confirmation"].format(count=self.tracker.groups_count)
Translations.format("entries.mirror.confirmation", count=self.tracker.groups_count)
)

self.model.clear()
Expand All @@ -72,8 +72,8 @@ def refresh_list(self):

def mirror_entries(self):
def displayed_text(x):
return Translations["entries.mirror.label"].format(
idx=x + 1, count=self.tracker.groups_count
return Translations.format(
"entries.mirror.label", idx=x + 1, count=self.tracker.groups_count
)

pw = ProgressWidget(
Expand Down
3 changes: 2 additions & 1 deletion tagstudio/src/qt/modals/relink_unlinked.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ def __init__(self, tracker: MissingRegistry):

def repair_entries(self):
def displayed_text(x):
return Translations["entries.unlinked.relink.attempting"].format(
return Translations.format(
"entries.unlinked.relink.attempting",
idx=x,
missing_count=self.tracker.missing_file_entries_count,
fixed_count=self.tracker.files_fixed_count,
Expand Down
4 changes: 1 addition & 3 deletions tagstudio/src/qt/modals/tag_database.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,7 @@ def delete_tag(self, tag: Tag):
message_box = QMessageBox(
QMessageBox.Question, # type: ignore
Translations["tag.remove"],
Translations["tag.confirm_delete"].format(
tag_name=self.lib.tag_display_name(tag.id),
),
Translations.format("tag.confirm_delete", tag_name=self.lib.tag_display_name(tag.id)),
QMessageBox.Ok | QMessageBox.Cancel, # type: ignore
)

Expand Down
7 changes: 4 additions & 3 deletions tagstudio/src/qt/modals/tag_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,9 @@ def set_driver(self, driver):
"""Set the QtDriver for this search panel. Used for main window operations."""
self.driver = driver

def build_create_button(self, query: str | None, key: str, format_args: dict):
def build_create_button(self, query: str | None):
"""Constructs a "Create & Add Tag" QPushButton."""
create_button = QPushButton(Translations[key].format(**format_args), self)
create_button = QPushButton(self)
create_button.setFlat(True)

create_button.setMinimumSize(22, 22)
Expand Down Expand Up @@ -244,7 +244,8 @@ def update_tags(self, query: str | None = None):

# Add back the "Create & Add" button
if query and query.strip():
cb: QPushButton = self.build_create_button(query, "tag.create_add", {"query": query})
cb: QPushButton = self.build_create_button(query)
cb.setText(Translations.format("tag.create_add", query=query))
with catch_warnings(record=True):
cb.clicked.disconnect()
cb.clicked.connect(lambda: self.create_and_add_tag(query or ""))
Expand Down
19 changes: 19 additions & 0 deletions tagstudio/src/qt/translations.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from collections import defaultdict
from pathlib import Path
from typing import Any

import structlog
import ujson
Expand Down Expand Up @@ -27,6 +29,23 @@ def change_language(self, lang: str):
self._lang = lang
self._strings = self.__get_translation_dict(lang)

def __format(self, text: str, **kwargs) -> str:
Copy link
Collaborator

@Computerdores Computerdores Mar 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest porting the improvements from #829

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would change it to this (plus an import for defaultdict at the top):

    def __format(self, text: str, *args, **kwargs) -> str:
        try:
            return text.format(*args, **kwargs)
        except (KeyError, ValueError):
            logger.error(
                "[Translations] Error while formatting translation.",
                text=text,
                kwargs=kwargs,
                language=self._lang,
            )
            params: defaultdict = defaultdict(lambda: "{missing_key}")
            params.update(kwargs)
            return super().format_map(params)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 499b548

try:
return text.format(**kwargs)
except (KeyError, ValueError):
logger.error(
"[Translations] Error while formatting translation.",
text=text,
kwargs=kwargs,
language=self._lang,
)
params: defaultdict[str, Any] = defaultdict(lambda: "{unknown_key}")
params.update(kwargs)
return text.format_map(params)

def format(self, key: str, **kwargs) -> str:
return self.__format(self[key], **kwargs)

def __getitem__(self, key: str) -> str:
return self._strings.get(key) or self._default_strings.get(key) or f"[{key}]"

Expand Down
52 changes: 28 additions & 24 deletions tagstudio/src/qt/ts_qt.py
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@ def start(self) -> None:
edit_menu.addSeparator()

self.delete_file_action = QAction(
Translations["menu.delete_selected_files_ambiguous"].format(trash_term=trash_term()),
Translations.format("menu.delete_selected_files_ambiguous", trash_term=trash_term()),
menu_bar,
)
self.delete_file_action.triggered.connect(lambda f="": self.delete_files_callback(f))
Expand Down Expand Up @@ -876,8 +876,8 @@ def close_library(self, is_shutdown: bool = False):

end_time = time.time()
self.main_window.statusbar.showMessage(
Translations["status.library_closed"].format(
time_span=format_timespan(end_time - start_time)
Translations.format(
"status.library_closed", time_span=format_timespan(end_time - start_time)
)
)

Expand All @@ -888,7 +888,8 @@ def backup_library(self):
target_path = self.lib.save_library_backup_to_disk()
end_time = time.time()
self.main_window.statusbar.showMessage(
Translations["status.library_backup_success"].format(
Translations.format(
"status.library_backup_success",
path=target_path,
time_span=format_timespan(end_time - start_time),
)
Expand Down Expand Up @@ -986,8 +987,8 @@ def delete_files_callback(self, origin_path: str | Path, origin_id: int | None =
self.preview_panel.thumb.stop_file_use()
if delete_file(self.lib.library_dir / f):
self.main_window.statusbar.showMessage(
Translations["status.deleting_file"].format(
i=i, count=len(pending), path=f
Translations.format(
"status.deleting_file", i=i, count=len(pending), path=f
)
)
self.main_window.statusbar.repaint()
Expand All @@ -1004,17 +1005,17 @@ def delete_files_callback(self, origin_path: str | Path, origin_id: int | None =
self.main_window.statusbar.showMessage(Translations["status.deleted_none"])
elif len(self.selected) <= 1 and deleted_count == 1:
self.main_window.statusbar.showMessage(
Translations["status.deleted_file_plural"].format(count=deleted_count)
Translations.format("status.deleted_file_plural", count=deleted_count)
)
elif len(self.selected) > 1 and deleted_count == 0:
self.main_window.statusbar.showMessage(Translations["status.deleted_none"])
elif len(self.selected) > 1 and deleted_count < len(self.selected):
self.main_window.statusbar.showMessage(
Translations["status.deleted_partial_warning"].format(count=deleted_count)
Translations.format("status.deleted_partial_warning", count=deleted_count)
)
elif len(self.selected) > 1 and deleted_count == len(self.selected):
self.main_window.statusbar.showMessage(
Translations["status.deleted_file_plural"].format(count=deleted_count)
Translations.format("status.deleted_file_plural", count=deleted_count)
)
self.main_window.statusbar.repaint()

Expand All @@ -1031,8 +1032,8 @@ def delete_file_confirmation(self, count: int, filename: Path | None = None) ->
# https://github.com/arsenetar/send2trash/issues/28
# This warning is applied to all platforms until at least macOS and Linux can be verified
# to not exhibit this same behavior.
perm_warning_msg = Translations["trash.dialog.permanent_delete_warning"].format(
trash_term=trash_term()
perm_warning_msg = Translations.format(
"trash.dialog.permanent_delete_warning", trash_term=trash_term()
)
perm_warning: str = (
f"<h4 style='color: {get_ui_color(ColorType.PRIMARY, UiColor.RED)}'>"
Expand All @@ -1049,8 +1050,8 @@ def delete_file_confirmation(self, count: int, filename: Path | None = None) ->
)
msg.setIcon(QMessageBox.Icon.Warning)
if count <= 1:
msg_text = Translations["trash.dialog.move.confirmation.singular"].format(
trash_term=trash_term()
msg_text = Translations.format(
"trash.dialog.move.confirmation.singular", trash_term=trash_term()
)
msg.setText(
f"<h3>{msg_text}</h3>"
Expand All @@ -1059,7 +1060,8 @@ def delete_file_confirmation(self, count: int, filename: Path | None = None) ->
f"{perm_warning}<br>"
)
elif count > 1:
msg_text = Translations["trash.dialog.move.confirmation.plural"].format(
msg_text = Translations.format(
"trash.dialog.move.confirmation.plural",
count=count,
trash_term=trash_term(),
)
Expand Down Expand Up @@ -1094,11 +1096,10 @@ def add_new_files_callback(self):
lambda x: (
pw.update_progress(x + 1),
pw.update_label(
Translations[
Translations.format(
"library.refresh.scanning.plural"
if x + 1 != 1
else "library.refresh.scanning.singular"
].format(
else "library.refresh.scanning.singular",
searched_count=f"{x+1:n}",
found_count=f"{tracker.files_count:n}",
)
Expand Down Expand Up @@ -1130,15 +1131,15 @@ def add_new_files_runnable(self, tracker: RefreshDirTracker):
)
pw.setWindowTitle(Translations["entries.running.dialog.title"])
pw.update_label(
Translations["entries.running.dialog.new_entries"].format(total=f"{files_count:n}")
Translations.format("entries.running.dialog.new_entries", total=f"{files_count:n}")
)
pw.show()

iterator.value.connect(
lambda: (
pw.update_label(
Translations["entries.running.dialog.new_entries"].format(
total=f"{files_count:n}"
Translations.format(
"entries.running.dialog.new_entries", total=f"{files_count:n}"
)
),
)
Expand Down Expand Up @@ -1698,7 +1699,8 @@ def filter_items(self, filter: FilterState | None = None) -> None:

# inform user about completed search
self.main_window.statusbar.showMessage(
Translations["status.results_found"].format(
Translations.format(
"status.results_found",
count=results.total_count,
time_span=format_timespan(end_time - start_time),
)
Expand Down Expand Up @@ -1824,7 +1826,7 @@ def update_language_settings(self, language: str):

def open_library(self, path: Path) -> None:
"""Open a TagStudio library."""
message = Translations["splash.opening_library"].format(library_path=str(path))
message = Translations.format("splash.opening_library", library_path=str(path))
self.main_window.landing_widget.set_status_label(message)
self.main_window.statusbar.showMessage(message, 3)
self.main_window.repaint()
Expand Down Expand Up @@ -1871,8 +1873,10 @@ def init_library(self, path: Path, open_status: LibraryStatus):

self.update_libs_list(path)
self.main_window.setWindowTitle(
Translations["app.title"].format(
base_title=self.base_title, library_dir=self.lib.library_dir
Translations.format(
"app.title",
base_title=self.base_title,
library_dir=self.lib.library_dir,
)
)
self.main_window.setAcceptDrops(True)
Expand Down
2 changes: 1 addition & 1 deletion tagstudio/src/qt/widgets/color_box.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ def delete_color(self, color_group: TagColorGroup):
message_box = QMessageBox(
QMessageBox.Icon.Warning,
Translations["color.delete"],
Translations["color.confirm_delete"].format(color_name=color_group.name),
Translations.format("color.confirm_delete", color_name=color_group.name),
)
cancel_button = message_box.addButton(
Translations["generic.cancel_alt"], QMessageBox.ButtonRole.RejectRole
Expand Down
3 changes: 2 additions & 1 deletion tagstudio/src/qt/widgets/item_thumb.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,8 @@ def __init__(
open_explorer_action.triggered.connect(self.opener.open_explorer)

self.delete_action = QAction(
Translations["trash.context.ambiguous"].format(trash_term=trash_term()), self
Translations.format("trash.context.ambiguous", trash_term=trash_term()),
self,
)

self.thumb_button.addAction(open_file_action)
Expand Down
2 changes: 1 addition & 1 deletion tagstudio/src/qt/widgets/landing.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def __init__(self, driver: "QtDriver", pixel_ratio: float):
else:
open_shortcut_text = "(Ctrl+O)"
self.open_button: QPushButton = QPushButton(
Translations["landing.open_create_library"].format(shortcut=open_shortcut_text)
Translations.format("landing.open_create_library", shortcut=open_shortcut_text)
)
self.open_button.setMinimumWidth(200)
self.open_button.clicked.connect(self.driver.open_library_from_dialog)
Expand Down
6 changes: 3 additions & 3 deletions tagstudio/src/qt/widgets/migration_modal.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def __init__(self, path: Path):
self.is_migration_initialized: bool = False
self.discrepancies: list[str] = []

self.title: str = Translations["json_migration.title"].format(path=self.path)
self.title: str = Translations.format("json_migration.title", path=self.path)
self.warning: str = "<b><a style='color: #e22c3c'>(!)</a></b>"

self.old_entry_count: int = 0
Expand Down Expand Up @@ -405,8 +405,8 @@ def migration_iterator(self):
logger.info('Temporary migration file "temp_path" already exists. Removing...')
self.temp_path.unlink()
self.sql_lib.open_sqlite_library(self.json_lib.library_dir, is_new=True)
yield Translations["json_migration.migrating_files_entries"].format(
entries=len(self.json_lib.entries)
yield Translations.format(
"json_migration.migrating_files_entries", entries=len(self.json_lib.entries)
)
self.sql_lib.migrate_json_to_sqlite(self.json_lib)
yield Translations["json_migration.checking_for_parity"]
Expand Down
2 changes: 1 addition & 1 deletion tagstudio/src/qt/widgets/preview/field_containers.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ def add_to_cluster(tag_id: int, p_ids: list[int] | None = None):
return cats

def remove_field_prompt(self, name: str) -> str:
return Translations["library.field.confirm_remove"].format(name=name)
return Translations.format("library.field.confirm_remove", name=name)

def add_field_to_selected(self, field_list: list):
"""Add list of entry fields to one or more selected items.
Expand Down
Loading