Skip to content

Commit 25e74cd

Browse files
committed
Add language selection and app icon support
1 parent 8e8d964 commit 25e74cd

1 file changed

Lines changed: 105 additions & 17 deletions

File tree

bookmark_checker/ui/main_window.py

Lines changed: 105 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55
from typing import Any
66

77
from PyQt6.QtCore import Qt
8-
from PyQt6.QtGui import QColor, QDragEnterEvent, QDropEvent, QPalette
8+
from PyQt6.QtGui import QColor, QDragEnterEvent, QDropEvent, QIcon, QPalette
99
from PyQt6.QtWidgets import (
1010
QApplication,
11+
QComboBox,
1112
QFileDialog,
1213
QHBoxLayout,
1314
QHeaderView,
@@ -26,6 +27,7 @@
2627
from bookmark_checker.core.exporters import export_dedupe_report_csv, export_netscape_html
2728
from bookmark_checker.core.merge import merge_collections
2829
from bookmark_checker.core.parsers import parse_many
30+
from bookmark_checker.i18n.translations import TRANSLATIONS, get_translation
2931

3032

3133
class MainWindow(QMainWindow):
@@ -34,18 +36,24 @@ class MainWindow(QMainWindow):
3436
def __init__(self) -> None:
3537
"""Initialize the main window."""
3638
super().__init__()
37-
self.setWindowTitle("BrowserBookmarkChecker")
3839
self.setMinimumSize(1000, 640)
3940

4041
# Data
4142
from bookmark_checker.core.models import BookmarkCollection
4243

4344
self.current_collection: BookmarkCollection | None = None
4445
self.current_report: list[dict[str, Any]] = []
46+
self.current_language = "en"
47+
48+
# Set window icon
49+
icon_path = Path(__file__).parent.parent.parent / "assets" / "icon.svg"
50+
if icon_path.exists():
51+
self.setWindowIcon(QIcon(str(icon_path)))
4552

4653
# Setup UI
4754
self._setup_ui()
4855
self._setup_style()
56+
self._update_ui_texts()
4957

5058
# Enable drag and drop
5159
self.setAcceptDrops(True)
@@ -60,25 +68,49 @@ def _setup_ui(self) -> None:
6068
top_bar = QWidget()
6169
top_layout = QHBoxLayout(top_bar)
6270

63-
self.btn_import = QPushButton("Import Files")
71+
# Language selector
72+
lang_label = QLabel("Language:")
73+
top_layout.addWidget(lang_label)
74+
self.language_combo = QComboBox()
75+
self.language_combo.addItems(
76+
[
77+
"English",
78+
"Русский",
79+
"Português",
80+
"Español",
81+
"Eesti",
82+
"Français",
83+
"Deutsch",
84+
"日本語",
85+
"中文",
86+
"한국어",
87+
"Bahasa Indonesia",
88+
]
89+
)
90+
self.language_combo.setCurrentIndex(0)
91+
self.language_combo.currentIndexChanged.connect(self._on_language_changed)
92+
top_layout.addWidget(self.language_combo)
93+
top_layout.addSpacing(10)
94+
95+
self.btn_import = QPushButton()
6496
self.btn_import.clicked.connect(self._import_files)
6597
top_layout.addWidget(self.btn_import)
6698

67-
self.btn_merge = QPushButton("Find & Merge")
99+
self.btn_merge = QPushButton()
68100
self.btn_merge.clicked.connect(self._find_and_merge)
69101
self.btn_merge.setEnabled(False)
70102
top_layout.addWidget(self.btn_merge)
71103

72-
self.btn_export = QPushButton("Export Merged")
104+
self.btn_export = QPushButton()
73105
self.btn_export.clicked.connect(self._export_merged)
74106
self.btn_export.setEnabled(False)
75107
top_layout.addWidget(self.btn_export)
76108

77109
top_layout.addStretch()
78110

79111
# Similarity slider
80-
similarity_label = QLabel("Similarity:")
81-
top_layout.addWidget(similarity_label)
112+
self.similarity_label = QLabel()
113+
top_layout.addWidget(self.similarity_label)
82114

83115
self.similarity_slider = QSlider(Qt.Orientation.Horizontal)
84116
self.similarity_slider.setMinimum(0)
@@ -102,9 +134,6 @@ def _setup_ui(self) -> None:
102134
# Table
103135
self.table = QTableWidget()
104136
self.table.setColumnCount(5)
105-
self.table.setHorizontalHeaderLabels(
106-
["Title", "URL (canonical)", "Folder", "Source", "Count"]
107-
)
108137
header = self.table.horizontalHeader()
109138
if header:
110139
header.setSectionResizeMode(0, QHeaderView.ResizeMode.Stretch)
@@ -124,11 +153,51 @@ def _setup_ui(self) -> None:
124153
self.progress_bar.setVisible(False)
125154
bottom_layout.addWidget(self.progress_bar)
126155

127-
self.status_label = QLabel("Ready")
156+
self.status_label = QLabel()
128157
bottom_layout.addWidget(self.status_label)
129158

130159
layout.addWidget(bottom_widget)
131160

161+
def _update_ui_texts(self) -> None:
162+
"""Update all UI texts based on current language."""
163+
lang_codes = ["en", "ru", "pt", "es", "et", "fr", "de", "ja", "zh", "ko", "id"]
164+
lang = lang_codes[self.language_combo.currentIndex()]
165+
166+
self.setWindowTitle(get_translation(lang, "app_title", "BrowserBookmarkChecker"))
167+
self.btn_import.setText(get_translation(lang, "import_files", "Import Files"))
168+
self.btn_merge.setText(get_translation(lang, "find_merge", "Find & Merge"))
169+
self.btn_export.setText(get_translation(lang, "export_merged", "Export Merged"))
170+
self.similarity_label.setText(get_translation(lang, "similarity", "Similarity") + ":")
171+
172+
# Update table headers
173+
self.table.setHorizontalHeaderLabels(
174+
[
175+
get_translation(lang, "title", "Title"),
176+
get_translation(lang, "url_canonical", "URL (canonical)"),
177+
get_translation(lang, "folder", "Folder"),
178+
get_translation(lang, "source", "Source"),
179+
get_translation(lang, "count", "Count"),
180+
]
181+
)
182+
183+
# Update status if it's the default "Ready" message
184+
if (
185+
self.status_label.text()
186+
in [
187+
get_translation(old_lang, "ready", "Ready")
188+
for old_lang in lang_codes
189+
if old_lang != lang
190+
]
191+
or self.status_label.text() == "Ready"
192+
):
193+
self.status_label.setText(get_translation(lang, "ready", "Ready"))
194+
195+
self.current_language = lang
196+
197+
def _on_language_changed(self, index: int) -> None:
198+
"""Handle language change."""
199+
self._update_ui_texts()
200+
132201
def _setup_style(self) -> None:
133202
"""Set up dark Fusion style."""
134203
self.setStyleSheet(
@@ -215,7 +284,9 @@ def _import_files(self) -> None:
215284

216285
def _process_files(self, files: list[str]) -> None:
217286
"""Process imported files."""
218-
self.status_label.setText("Parsing files...")
287+
self.status_label.setText(
288+
get_translation(self.current_language, "parsing_files", "Parsing files...")
289+
)
219290
self.progress_bar.setVisible(True)
220291
self.progress_bar.setRange(0, 0) # Indeterminate
221292

@@ -225,7 +296,7 @@ def _process_files(self, files: list[str]) -> None:
225296
if len(collection.bookmarks) == 0:
226297
QMessageBox.warning(
227298
self,
228-
"No Bookmarks Found",
299+
get_translation(self.current_language, "error", "Error"),
229300
f"No bookmarks were found in the selected files.\n\nFiles: {', '.join(files)}",
230301
)
231302
self.status_label.setText("No bookmarks found in files")
@@ -241,7 +312,9 @@ def _process_files(self, files: list[str]) -> None:
241312
import traceback
242313

243314
error_msg = f"Failed to parse files:\n{str(e)}\n\n{traceback.format_exc()}"
244-
QMessageBox.critical(self, "Error", error_msg)
315+
QMessageBox.critical(
316+
self, get_translation(self.current_language, "error", "Error"), error_msg
317+
)
245318
self.status_label.setText("Error loading files")
246319
self.btn_merge.setEnabled(False)
247320
finally:
@@ -270,7 +343,11 @@ def _find_and_merge(self) -> None:
270343
)
271344
self.btn_export.setEnabled(True)
272345
except Exception as e:
273-
QMessageBox.critical(self, "Error", f"Failed to merge:\n{e}")
346+
QMessageBox.critical(
347+
self,
348+
get_translation(self.current_language, "error", "Error"),
349+
f"Failed to merge:\n{e}",
350+
)
274351
self.status_label.setText("Error during merge")
275352
finally:
276353
self.progress_bar.setVisible(False)
@@ -345,10 +422,16 @@ def _export_merged(self) -> None:
345422

346423
self.status_label.setText(f"Exported to {output_path} and {csv_path}")
347424
QMessageBox.information(
348-
self, "Success", f"Exported successfully!\n\n{output_path}\n{csv_path}"
425+
self,
426+
get_translation(self.current_language, "success", "Success"),
427+
f"{get_translation(self.current_language, 'exported_successfully', 'Exported successfully!')}\n\n{output_path}\n{csv_path}",
349428
)
350429
except Exception as e:
351-
QMessageBox.critical(self, "Error", f"Failed to export:\n{e}")
430+
QMessageBox.critical(
431+
self,
432+
get_translation(self.current_language, "error", "Error"),
433+
f"Failed to export:\n{e}",
434+
)
352435
self.status_label.setText("Error during export")
353436
finally:
354437
self.progress_bar.setVisible(False)
@@ -359,6 +442,11 @@ def launch_gui() -> None:
359442
app = QApplication(sys.argv)
360443
app.setStyle("Fusion")
361444

445+
# Set application icon
446+
icon_path = Path(__file__).parent.parent.parent / "assets" / "icon.svg"
447+
if icon_path.exists():
448+
app.setWindowIcon(QIcon(str(icon_path)))
449+
362450
# Set dark palette
363451
palette = QPalette()
364452
palette.setColor(QPalette.ColorRole.Window, QColor(43, 43, 43))

0 commit comments

Comments
 (0)