Skip to content

Commit 58180ad

Browse files
committed
Add full translation support for UI and log messages
Refactored the application to use translation keys for all UI labels, combo box items, tooltips, and log messages. Updated MainWindow and BrowserThread to use the selected language's translations throughout the UI and logging. Extended translation files (en, fil, ja, ko, zh) with new keys for combo box items, system info, log messages, and about/license sections. Improved language/theme combo box handling and ensured settings load/apply with proper translation context.
1 parent 4c48ac9 commit 58180ad

File tree

7 files changed

+663
-104
lines changed

7 files changed

+663
-104
lines changed

src/advancedtabmanager/app.py

Lines changed: 195 additions & 82 deletions
Large diffs are not rendered by default.

src/advancedtabmanager/core/browser_thread.py

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class BrowserThread(QThread):
1919
log_message = pyqtSignal(str, str)
2020
error_occurred = pyqtSignal(str)
2121

22-
def __init__(self, url, iterations, interval, browser_options, instance_id, browser_type='chrome'):
22+
def __init__(self, url, iterations, interval, browser_options, instance_id, browser_type='chrome', translations=None):
2323
super().__init__()
2424
self.url = url
2525
self.iterations = iterations
@@ -33,6 +33,7 @@ def __init__(self, url, iterations, interval, browser_options, instance_id, brow
3333
self.driver_process = None
3434
self.browser_processes = [] # Will contain only specific processes we create
3535
self.instance_id = instance_id
36+
self.translations = translations or {}
3637
self.progress = 0
3738
self.cycle = 0
3839
self.stop_requested = False # Flag to prevent error emissions during stop
@@ -54,7 +55,7 @@ def _try_reinitialize_driver(self):
5455
return False
5556

5657
try:
57-
self.log_message.emit(f"Attempting to reinitialize WebDriver for instance {self.instance_id}", "WARNING")
58+
self.log_message.emit(self.translations.get("log_reinitialize_attempt", "Attempting to reinitialize WebDriver for instance {instance_id}").format(instance_id=self.instance_id), "WARNING")
5859

5960
# Clean up existing driver if any
6061
if self.driver:
@@ -97,11 +98,11 @@ def _try_reinitialize_driver(self):
9798
self.driver = driver
9899
self.driver.get(self.url) # Reopen initial tab
99100

100-
self.log_message.emit(f"Successfully reinitialized WebDriver for instance {self.instance_id}", "INFO")
101+
self.log_message.emit(self.translations.get("log_reinitialize_success", "Successfully reinitialized WebDriver for instance {instance_id}").format(instance_id=self.instance_id), "INFO")
101102
return True
102103

103104
except Exception as e:
104-
self.log_message.emit(f"Failed to reinitialize WebDriver for instance {self.instance_id}: {str(e)}", "ERROR")
105+
self.log_message.emit(self.translations.get("log_reinitialize_failed", "Failed to reinitialize WebDriver for instance {instance_id}: {error}").format(instance_id=self.instance_id, error=str(e)), "ERROR")
105106
return False
106107

107108
def wait_for_service(self, host='127.0.0.1', port=0, timeout=10):
@@ -142,7 +143,7 @@ def run(self):
142143
if startup_delay > 0:
143144
time.sleep(startup_delay)
144145

145-
self.log_message.emit(f"Starting browser thread for instance {self.instance_id} on port {self.port}", "INFO")
146+
self.log_message.emit(self.translations.get("log_browser_thread_start", "Starting browser thread for instance {instance_id} on port {port}").format(instance_id=self.instance_id, port=self.port), "INFO")
146147

147148
# Create service in background thread to avoid blocking UI
148149
if self.browser_type == 'chrome':
@@ -155,7 +156,7 @@ def run(self):
155156
if self.is_port_in_use(self.port):
156157
self.port = self.find_available_port()
157158
self.service.port = self.port
158-
self.log_message.emit(f"Port {self.port} was in use, switching to new port {self.port}", "WARNING")
159+
self.log_message.emit(self.translations.get("log_port_in_use", "Port {old_port} was in use, switching to new port {new_port}").format(old_port=self.port, new_port=self.port), "WARNING")
159160

160161
self.service.start()
161162

@@ -178,20 +179,20 @@ def run(self):
178179
error_msg = str(e).lower()
179180
# Provide more specific error messages for common issues
180181
if "chrome instance exited" in error_msg:
181-
self.log_message.emit(f"Chrome browser instance exited immediately. This usually indicates:", "ERROR")
182-
self.log_message.emit(f" - Chrome binary not found at: {getattr(self.browser_options, 'binary_location', 'Not set')}", "ERROR")
183-
self.log_message.emit(f" - Chrome version incompatible with ChromeDriver", "ERROR")
184-
self.log_message.emit(f" - Conflicting Chrome command line arguments", "ERROR")
185-
self.log_message.emit(f" - Chrome already running with conflicting profile", "ERROR")
182+
self.log_message.emit(self.translations.get("log_browser_exited_immediately", "Chrome browser instance exited immediately. This usually indicates:"), "ERROR")
183+
self.log_message.emit(self.translations.get("log_chrome_binary_not_found_error", " - Chrome binary not found at: {binary}").format(binary=getattr(self.browser_options, 'binary_location', 'Not set')), "ERROR")
184+
self.log_message.emit(self.translations.get("log_chrome_version_incompatible", " - Chrome version incompatible with ChromeDriver"), "ERROR")
185+
self.log_message.emit(self.translations.get("log_conflicting_arguments", " - Conflicting Chrome command line arguments"), "ERROR")
186+
self.log_message.emit(self.translations.get("log_conflicting_profile", " - Chrome already running with conflicting profile"), "ERROR")
186187
elif "session not created" in error_msg:
187-
self.log_message.emit(f"WebDriver session creation failed. Check browser version compatibility.", "ERROR")
188+
self.log_message.emit(self.translations.get("log_session_creation_failed", "WebDriver session creation failed. Check browser version compatibility."), "ERROR")
188189
elif "executable needs to be in path" in error_msg:
189-
self.log_message.emit(f"Browser executable not found in PATH or specified location.", "ERROR")
190+
self.log_message.emit(self.translations.get("log_executable_not_found", "Browser executable not found in PATH or specified location."), "ERROR")
190191
else:
191-
self.log_message.emit(f"WebDriver initialization error: {str(e)}", "ERROR")
192+
self.log_message.emit(self.translations.get("log_webdriver_init_error", "WebDriver initialization error: {error}").format(error=str(e)), "ERROR")
192193

193194
self.error_occurred.emit(f"Failed to initialize WebDriver for instance {self.instance_id}: {str(e)}")
194-
self.log_message.emit(f"Failed to initialize WebDriver for instance {self.instance_id}: {str(e)}", "ERROR")
195+
self.log_message.emit(self.translations.get("log_failed_init_webdriver", "Failed to initialize WebDriver for instance {instance_id}: {error}").format(instance_id=self.instance_id, error=str(e)), "ERROR")
195196
return
196197

197198
self.driver_process = psutil.Process(self.service.process.pid)
@@ -223,13 +224,13 @@ def run(self):
223224
else:
224225
self.browser_processes = []
225226
except Exception as e:
226-
self.log_message.emit(f"Could not track browser process for instance {self.instance_id}: {str(e)}", "WARNING")
227+
self.log_message.emit(self.translations.get("log_cannot_track_process", "Could not track browser process for instance {instance_id}: {error}").format(instance_id=self.instance_id, error=str(e)), "WARNING")
227228
self.browser_processes = []
228229

229230
try:
230231
self.driver.get(self.url)
231232
first_tab_handle = self.driver.current_window_handle
232-
self.log_message.emit(f"Opened initial tab with URL: {self.url} for instance {self.instance_id}", "INFO")
233+
self.log_message.emit(self.translations.get("log_opened_initial_tab", "Opened initial tab with URL: {url} for instance {instance_id}").format(url=self.url, instance_id=self.instance_id), "INFO")
233234

234235
iteration = 0
235236
consecutive_errors = 0 # Track consecutive errors for performance optimization
@@ -240,7 +241,7 @@ def run(self):
240241

241242
if not self.is_driver_valid():
242243
self.error_occurred.emit(f"WebDriver is invalid or None for instance {self.instance_id}, cannot proceed with tab operations")
243-
self.log_message.emit(f"WebDriver is invalid or None for instance {self.instance_id}, cannot proceed with tab operations", "ERROR")
244+
self.log_message.emit(self.translations.get("log_webdriver_invalid", "WebDriver is invalid or None for instance {instance_id}, cannot proceed with tab operations").format(instance_id=self.instance_id), "ERROR")
244245
break
245246

246247
try:
@@ -256,7 +257,7 @@ def run(self):
256257
if not self.is_driver_valid():
257258
if not self.stop_requested:
258259
self.error_occurred.emit(f"WebDriver became invalid before script execution for instance {self.instance_id}")
259-
self.log_message.emit(f"WebDriver became invalid before script execution for instance {self.instance_id}", "ERROR")
260+
self.log_message.emit(self.translations.get("log_webdriver_invalid_before_script", "WebDriver became invalid before script execution for instance {instance_id}").format(instance_id=self.instance_id), "ERROR")
260261
# Try to reinitialize driver
261262
if self._try_reinitialize_driver():
262263
continue # Skip this iteration and try again
@@ -269,7 +270,7 @@ def run(self):
269270
if not self.is_driver_valid():
270271
if not self.stop_requested:
271272
self.error_occurred.emit(f"WebDriver became invalid after script execution for instance {self.instance_id}")
272-
self.log_message.emit(f"WebDriver became invalid after script execution for instance {self.instance_id}", "ERROR")
273+
self.log_message.emit(self.translations.get("log_webdriver_invalid_after_script", "WebDriver became invalid after script execution for instance {instance_id}").format(instance_id=self.instance_id), "ERROR")
273274
# Try to reinitialize driver
274275
if self._try_reinitialize_driver():
275276
continue # Skip this iteration and try again
@@ -278,7 +279,7 @@ def run(self):
278279

279280
handles = self.driver.window_handles
280281
if not handles:
281-
self.log_message.emit(f"No window handles found for instance {self.instance_id}, skipping tab operation", "WARNING")
282+
self.log_message.emit(self.translations.get("log_no_window_handles", "No window handles found for instance {instance_id}, skipping tab operation").format(instance_id=self.instance_id), "WARNING")
282283
continue
283284

284285
new_tab_handle = handles[-1]
@@ -295,7 +296,7 @@ def run(self):
295296
suppress_keywords = ['browsing context has been discarded', 'session does not exist', 'invalid session', 'marionette', 'no such window']
296297
should_suppress = any(keyword in error_msg for keyword in suppress_keywords)
297298
if not should_suppress and not self.stop_requested:
298-
self.log_message.emit(f"Timeout waiting for page to load in instance {self.instance_id}: {str(e)}", "WARNING")
299+
self.log_message.emit(self.translations.get("log_timeout_waiting_page_load", "Timeout waiting for page to load in instance {instance_id}: {error}").format(instance_id=self.instance_id, error=str(e)), "WARNING")
299300

300301
if new_tab_handle != first_tab_handle:
301302
# Double-check driver is still valid before closing tab

src/advancedtabmanager/translations/en.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,4 +204,93 @@
204204
"proxy_test_button": "Test Proxy",
205205
"reset_settings_title": "Reset Settings",
206206
"reset_settings_message": "Are you sure you want to reset all settings to their default values?\n\nThis action cannot be undone.",
207+
# Combo box items
208+
"browser_chrome": "Chrome",
209+
"browser_firefox": "Firefox",
210+
"theme_dark_navy": "Dark Navy",
211+
"theme_light_blue": "Light Blue",
212+
"theme_dark_green": "Dark Green",
213+
"theme_light_green": "Light Green",
214+
"theme_soft_pink": "Soft Pink",
215+
"theme_soft_lavender": "Soft Lavender",
216+
"language_english": "English",
217+
"language_japanese": "Japanese",
218+
"language_korean": "Korean",
219+
"language_chinese": "Chinese",
220+
"language_filipino": "Filipino",
221+
# System information labels
222+
"system_info_os": "OS:",
223+
"system_info_python_version": "Python Version:",
224+
"system_info_cpu": "CPU:",
225+
"system_info_cpu_cores": "CPU Cores:",
226+
"system_info_memory": "Memory:",
227+
"system_info_disk_space": "Disk Space:",
228+
# Status bar
229+
"status_bar_ready": "Ready",
230+
# Log messages
231+
"log_please_enter_valid_url": "Please enter a valid URL!",
232+
"log_chrome_binary_found": "Found Chrome binary at: {path}",
233+
"log_chrome_binary_not_found": "Chrome binary not found in standard locations. Chrome paths checked: {chrome_paths}",
234+
"log_chrome_in_path": "Found Chrome in PATH: {chrome_in_path}",
235+
"log_chrome_not_in_path": "Chrome binary not found. WebDriver may fail to start.",
236+
"log_browser_info": "Browser: {browser_type}, Binary: {binary}",
237+
"log_browser_arguments": "Browser arguments: {arguments}",
238+
"log_browser_thread_finished": "Browser thread for instance {instance_id} finished",
239+
"log_stopping_operations": "Stopping all browser operations...",
240+
"log_operations_stopped": "All browser operations stopped",
241+
"log_process_cleanup_error": "Process cleanup error: {error}",
242+
"log_process_cleanup_completed": "Process cleanup completed",
243+
"log_fields_reset": "Fields reset to default",
244+
"log_logs_exported": "Logs exported to {file_path}",
245+
"log_failed_export_logs": "Failed to export logs: {error}",
246+
"log_logs_cleared": "Logs cleared",
247+
"log_theme_changed": "Theme changed to {theme_name}",
248+
"log_font_size_changed": "Font size changed to {size}pt",
249+
"log_language_changed": "Language changed to {language}",
250+
"log_invalid_theme_index": "Invalid theme index, defaulting to Dark Navy",
251+
"log_invalid_language_index": "Invalid language index: {index}, ignoring",
252+
"log_settings_saved": "Settings saved successfully",
253+
"log_settings_loaded": "Settings loaded successfully",
254+
"log_settings_exported": "Settings exported to {file_path}",
255+
"log_settings_imported": "Settings imported from {file_path}",
256+
"log_failed_export_settings": "Failed to export settings: {error}",
257+
"log_failed_import_settings": "Failed to import settings: {error}",
258+
"log_settings_reset": "All settings reset to default values",
259+
"log_failed_reset_settings": "Failed to reset settings: {error}",
260+
"log_settings_auto_saved": "Settings auto-saved on exit",
261+
"log_failed_auto_save_settings": "Failed to auto-save settings: {error}",
262+
"log_invalid_language_index_settings": "Invalid language index in settings, defaulting to English",
263+
"log_window_size_lock": "Window size {status}",
264+
# Browser thread log messages
265+
"log_reinitialize_attempt": "Attempting to reinitialize WebDriver for instance {instance_id}",
266+
"log_reinitialize_success": "Successfully reinitialized WebDriver for instance {instance_id}",
267+
"log_reinitialize_failed": "Failed to reinitialize WebDriver for instance {instance_id}: {error}",
268+
"log_browser_thread_start": "Starting browser thread for instance {instance_id} on port {port}",
269+
"log_port_in_use": "Port {old_port} was in use, switching to new port {new_port}",
270+
"log_browser_exited_immediately": "Chrome browser instance exited immediately. This usually indicates:",
271+
"log_chrome_binary_not_found_error": " - Chrome binary not found at: {binary}",
272+
"log_chrome_version_incompatible": " - Chrome version incompatible with ChromeDriver",
273+
"log_conflicting_arguments": " - Conflicting Chrome command line arguments",
274+
"log_conflicting_profile": " - Chrome already running with conflicting profile",
275+
"log_session_creation_failed": "WebDriver session creation failed. Check browser version compatibility.",
276+
"log_executable_not_found": "Browser executable not found in PATH or specified location.",
277+
"log_webdriver_init_error": "WebDriver initialization error: {error}",
278+
"log_failed_init_webdriver": "Failed to initialize WebDriver for instance {instance_id}: {error}",
279+
"log_cannot_track_process": "Could not track browser process for instance {instance_id}: {error}",
280+
"log_opened_initial_tab": "Opened initial tab with URL: {url} for instance {instance_id}",
281+
"log_webdriver_invalid": "WebDriver is invalid or None for instance {instance_id}, cannot proceed with tab operations",
282+
"log_webdriver_invalid_before_script": "WebDriver became invalid before script execution for instance {instance_id}",
283+
"log_webdriver_invalid_after_script": "WebDriver became invalid after script execution for instance {instance_id}",
284+
"log_no_window_handles": "No window handles found for instance {instance_id}, skipping tab operation",
285+
"log_timeout_waiting_page_load": "Timeout waiting for page to load in instance {instance_id}: {error}",
286+
"system_info_cpu_usage": "CPU Usage:",
287+
"system_info_memory_usage": "Memory Usage:",
288+
"about_support": "Support:",
289+
"about_issues_page": "Issues Page",
290+
"about_license_text": "This software is licensed under the {license}. See {license} for details.",
291+
"about_description": "Advanced Tab Manager is a Python-based desktop application built with PyQt6 and Selenium. It provides a user-friendly interface to automate browser tab management, allowing users to open and close Chrome tabs programmatically with extensive customization options. This tool is ideal for testing, simulation, or repetitive browser automation tasks.",
292+
"update_check_no_internet": "No internet connection. Update check failed.",
293+
"update_check_failed": "Failed to check for updates: {error}",
294+
"update_check_failed_title": "Update Check Failed",
295+
"update_check_failed_text": "Unable to check for updates.",
207296
}

0 commit comments

Comments
 (0)