Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
9222c75
feat: implement Translator class
Computerdores Dec 22, 2024
96f9163
feat: add translate_with_setter and implement formatting of translations
Computerdores Dec 22, 2024
de57225
feat: extend PanelModal to allow for translation
Computerdores Dec 23, 2024
9ab681f
feat: extend ProgressWidget to allow for translation
Computerdores Dec 23, 2024
c857eb4
feat: translation in ts_qt.py
Computerdores Dec 23, 2024
bf5d18a
debug: set default lang to DE and show "Not Translated" when replacin…
Computerdores Dec 23, 2024
a38847c
add translation todos
Computerdores Dec 23, 2024
03b2e84
feat: translate modals
Computerdores Dec 23, 2024
c76b6ed
Merge branch 'main' into feat/translations
Computerdores Dec 23, 2024
e2c1440
feat: translate more stuff
Computerdores Dec 23, 2024
2f79458
fix: UI test wasn't comparing to translated strings
Computerdores Dec 23, 2024
05ee989
feat: translations for most of the remaining stuff
Computerdores Dec 23, 2024
399e4eb
fix: replace debug changes with simpler one
Computerdores Dec 23, 2024
2889cf7
uncomment debug change
Computerdores Dec 23, 2024
a555286
fix: missing parameter in call
Computerdores Dec 23, 2024
ba43c42
fix: various review feedback
Computerdores Dec 24, 2024
68fcce1
fix: don't translate json migration discrepancies list
Computerdores Dec 24, 2024
a688cad
fix: typo
Computerdores Dec 24, 2024
f37833d
fix: various PR feedback
Computerdores Dec 24, 2024
cf48d6a
fix: correctly read non-ascii characters
Computerdores Dec 25, 2024
0f44af9
fix: add missing new line at eof
Computerdores Dec 29, 2024
d594e84
fix: comment out line of debug code
Computerdores Dec 29, 2024
465bc41
fix: address latest review comment
Computerdores Dec 29, 2024
cc4b6df
fix: KeyError that occurred when formatting translations
Computerdores Dec 29, 2024
73010cc
fix: regression of d594e84
Computerdores Dec 30, 2024
cdc5bf1
fix: add newline to en.json
CyanVoxel Dec 31, 2024
39562e9
fix: organize en.json, fix typo
CyanVoxel Dec 31, 2024
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
154 changes: 124 additions & 30 deletions tagstudio/resources/translations/en.json

Large diffs are not rendered by default.

34 changes: 8 additions & 26 deletions tagstudio/src/qt/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
from src.qt.pagination import Pagination
from src.qt.widgets.landing import LandingWidget

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
Expand Down Expand Up @@ -77,6 +79,8 @@ def setupUi(self, MainWindow):
# Thumbnail Size placeholder
self.thumb_size_combobox = QComboBox(self.centralwidget)
self.thumb_size_combobox.setObjectName(u"thumbSizeComboBox")
Translations.translate_with_setter(self.thumb_size_combobox.setPlaceholderText, "home.thumbnail_size")
self.thumb_size_combobox.setCurrentText("")
sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
Expand Down Expand Up @@ -128,7 +132,7 @@ def setupUi(self, MainWindow):
self.horizontalLayout_2 = QHBoxLayout()
self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
self.horizontalLayout_2.setSizeConstraint(QLayout.SetMinimumSize)
self.backButton = QPushButton(self.centralwidget)
self.backButton = QPushButton("<", self.centralwidget)
self.backButton.setObjectName(u"backButton")
self.backButton.setMinimumSize(QSize(0, 32))
self.backButton.setMaximumSize(QSize(32, 16777215))
Expand All @@ -139,7 +143,7 @@ def setupUi(self, MainWindow):

self.horizontalLayout_2.addWidget(self.backButton)

self.forwardButton = QPushButton(self.centralwidget)
self.forwardButton = QPushButton(">", self.centralwidget)
self.forwardButton.setObjectName(u"forwardButton")
self.forwardButton.setMinimumSize(QSize(0, 32))
self.forwardButton.setMaximumSize(QSize(32, 16777215))
Expand All @@ -152,6 +156,7 @@ def setupUi(self, MainWindow):
self.horizontalLayout_2.addWidget(self.forwardButton)

self.searchField = QLineEdit(self.centralwidget)
Translations.translate_with_setter(self.searchField.setPlaceholderText, "home.search_entries")
self.searchField.setObjectName(u"searchField")
self.searchField.setMinimumSize(QSize(0, 32))
font2 = QFont()
Expand All @@ -167,6 +172,7 @@ def setupUi(self, MainWindow):
self.horizontalLayout_2.addWidget(self.searchField)

self.searchButton = QPushButton(self.centralwidget)
Translations.translate_qobject(self.searchButton, "home.search")
self.searchButton.setObjectName(u"searchButton")
self.searchButton.setMinimumSize(QSize(0, 32))
self.searchButton.setFont(font2)
Expand All @@ -186,33 +192,9 @@ def setupUi(self, MainWindow):
self.statusbar.setSizePolicy(sizePolicy1)
MainWindow.setStatusBar(self.statusbar)

self.retranslateUi(MainWindow)

QMetaObject.connectSlotsByName(MainWindow)
# setupUi

def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(QCoreApplication.translate(
"MainWindow", u"MainWindow", None))
# Navigation buttons
self.backButton.setText(
QCoreApplication.translate("MainWindow", u"<", None))
self.forwardButton.setText(
QCoreApplication.translate("MainWindow", u">", None))

# Search field
self.searchField.setPlaceholderText(
QCoreApplication.translate("MainWindow", u"Search Entries", None))
self.searchButton.setText(
QCoreApplication.translate("MainWindow", u"Search", None))

self.thumb_size_combobox.setCurrentText("")

# Thumbnail size selector
self.thumb_size_combobox.setPlaceholderText(
QCoreApplication.translate("MainWindow", u"Thumbnail Size", None))
# retranslateUi

def moveEvent(self, event) -> None:
# time.sleep(0.02) # sleep for 20ms
pass
Expand Down
10 changes: 6 additions & 4 deletions tagstudio/src/qt/modals/add_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
)
from src.core.library import Library

from ..translations import Translations


class AddFieldModal(QWidget):
done = Signal(list)
Expand All @@ -26,7 +28,7 @@ def __init__(self, library: Library):
super().__init__()
self.is_connected = False
self.lib = library
self.setWindowTitle("Add Field")
Translations.translate_with_setter(self.setWindowTitle, "library.field.add")
self.setWindowModality(Qt.WindowModality.ApplicationModal)
self.setMinimumSize(400, 300)
self.root_layout = QVBoxLayout(self)
Expand All @@ -40,7 +42,7 @@ def __init__(self, library: Library):
# 'text-align:center;'
"font-weight:bold;" "font-size:14px;" "padding-top: 6px" ""
)
self.title_widget.setText("Add Field")
Translations.translate_qobject(self.title_widget, "library.field.add")
self.title_widget.setAlignment(Qt.AlignmentFlag.AlignCenter)

self.list_widget = QListWidget()
Expand All @@ -54,13 +56,13 @@ def __init__(self, library: Library):
# self.cancel_button.setText('Cancel')

self.cancel_button = QPushButton()
self.cancel_button.setText("Cancel")
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")
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)
Expand Down
22 changes: 14 additions & 8 deletions tagstudio/src/qt/modals/build_tag.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
from src.qt.widgets.panel import PanelModal, PanelWidget
from src.qt.widgets.tag import TagWidget

from ..translations import Translations

logger = structlog.get_logger(__name__)


Expand Down Expand Up @@ -69,12 +71,14 @@ def __init__(self, library: Library, tag: Tag | None = None):
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)")
Translations.translate_with_setter(
self.name_field.setPlaceholderText, "tag.tag_name_required"
)
self.name_layout.addWidget(self.name_field)

# Shorthand ------------------------------------------------------------
Expand All @@ -85,7 +89,7 @@ def __init__(self, library: Library, tag: Tag | None = None):
self.shorthand_layout.setSpacing(0)
self.shorthand_layout.setAlignment(Qt.AlignmentFlag.AlignLeft)
self.shorthand_title = QLabel()
self.shorthand_title.setText("Shorthand")
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)
Expand All @@ -98,7 +102,7 @@ def __init__(self, library: Library, tag: Tag | None = None):
self.aliases_layout.setSpacing(0)
self.aliases_layout.setAlignment(Qt.AlignmentFlag.AlignLeft)
self.aliases_title = QLabel()
self.aliases_title.setText("Aliases")
Translations.translate_qobject(self.aliases_title, "tag.aliases")
self.aliases_layout.addWidget(self.aliases_title)

self.aliases_table = QTableWidget(0, 2)
Expand All @@ -122,7 +126,7 @@ def __init__(self, library: Library, tag: Tag | None = None):
self.subtags_layout.setAlignment(Qt.AlignmentFlag.AlignLeft)

self.subtags_title = QLabel()
self.subtags_title.setText("Parent Tags")
Translations.translate_qobject(self.subtags_title, "tag.parent_tags")
self.subtags_layout.addWidget(self.subtags_title)

self.scroll_contents = QWidget()
Expand Down Expand Up @@ -151,7 +155,9 @@ def __init__(self, library: Library, tag: Tag | None = None):

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")
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)

# Shorthand ------------------------------------------------------------
Expand All @@ -162,7 +168,7 @@ def __init__(self, library: Library, tag: Tag | None = None):
self.color_layout.setSpacing(0)
self.color_layout.setAlignment(Qt.AlignmentFlag.AlignLeft)
self.color_title = QLabel()
self.color_title.setText("Color")
Translations.translate_qobject(self.color_title, "tag.color")
self.color_layout.addWidget(self.color_title)
self.color_field = QComboBox()
self.color_field.setEditable(False)
Expand Down Expand Up @@ -200,7 +206,7 @@ def __init__(self, library: Library, tag: Tag | None = None):
self.new_alias_names: dict = {}
self.new_item_id = sys.maxsize

self.set_tag(tag or Tag(name="New Tag"))
self.set_tag(tag or Tag(name=Translations["tag.new"]))
if tag is None:
self.name_field.selectAll()

Expand Down
33 changes: 21 additions & 12 deletions tagstudio/src/qt/modals/delete_unlinked.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
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
Expand All @@ -29,7 +31,7 @@ def __init__(self, driver: "QtDriver", tracker: MissingRegistry):
super().__init__()
self.driver = driver
self.tracker = tracker
self.setWindowTitle("Delete Unlinked Entries")
Translations.translate_with_setter(self.setWindowTitle, "entries.unlinked.delete")
self.setWindowModality(Qt.WindowModality.ApplicationModal)
self.setMinimumSize(500, 400)
self.root_layout = QVBoxLayout(self)
Expand All @@ -38,9 +40,11 @@ def __init__(self, driver: "QtDriver", tracker: MissingRegistry):
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?
""")
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()
Expand All @@ -53,13 +57,13 @@ def __init__(self, driver: "QtDriver", tracker: MissingRegistry):
self.button_layout.addStretch(1)

self.cancel_button = QPushButton()
self.cancel_button.setText("&Cancel")
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")
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)
Expand All @@ -69,9 +73,11 @@ def __init__(self, driver: "QtDriver", tracker: MissingRegistry):
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?
""")
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:
Expand All @@ -81,14 +87,17 @@ def refresh_list(self):

def delete_entries(self):
def displayed_text(x):
return f"Deleting {x}/{self.tracker.missing_files_count} Unlinked Entries"
return Translations.translate_formatted(
"entries.unlinked.delete.deleting_count",
idx=x,
count=self.tracker.missing_files_count,
)

pw = ProgressWidget(
window_title="Deleting Entries",
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)
36 changes: 21 additions & 15 deletions tagstudio/src/qt/modals/drop_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
)
from src.qt.widgets.progress import ProgressWidget

from ..translations import Translations

if TYPE_CHECKING:
from src.qt.ts_qt import QtDriver

Expand All @@ -41,7 +43,7 @@ def __init__(self, driver: "QtDriver"):
self.driver: QtDriver = driver

# Widget ======================
self.setWindowTitle("Conflicting File(s)")
Translations.translate_with_setter(self.setWindowTitle, "drop_import.title")
self.setWindowModality(Qt.WindowModality.ApplicationModal)
self.setMinimumSize(500, 400)
self.root_layout = QVBoxLayout(self)
Expand All @@ -50,7 +52,7 @@ def __init__(self, driver: "QtDriver"):
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")
self.desc_widget.setText(Translations["drop_import.decription"])
self.desc_widget.setAlignment(Qt.AlignmentFlag.AlignCenter)

# Duplicate File List ========
Expand All @@ -65,25 +67,25 @@ def __init__(self, driver: "QtDriver"):
self.button_layout.addStretch(1)

self.skip_button = QPushButton()
self.skip_button.setText("&Skip")
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")
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")
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")
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)

Expand Down Expand Up @@ -137,7 +139,11 @@ def collect_files_to_import(self, urls: list[QUrl]):
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
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()
Expand All @@ -158,21 +164,21 @@ def begin_transfer(self, choice: DuplicateChoice | None = None):
return

def displayed_text(x):
text = (
f"Importing New Files...\n{x[0] + 1} File{'s' if x[0] + 1 != 1 else ''} Imported."
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 "",
)
if self.choice:
text += f" {x[1]} {self.choice.value}"

return text

pw = ProgressWidget(
window_title="Import Files",
label_text="Importing New Files...",
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,
Expand Down
Loading
Loading