diff --git a/picodulce.py b/picodulce.py index c974098..af49300 100644 --- a/picodulce.py +++ b/picodulce.py @@ -10,35 +10,48 @@ import requests import json import os import time + +from updateManager import UpdateChecker from authser import MinecraftAuthenticator +from healtcheck import HealthCheck + from PyQt5.QtWidgets import QApplication, QComboBox, QWidget, QInputDialog, QVBoxLayout, QListWidget, QPushButton, QMessageBox, QDialog, QHBoxLayout, QLabel, QLineEdit, QCheckBox, QTabWidget, QFrame, QSpacerItem, QSizePolicy, QMainWindow, QGridLayout, QTextEdit, QListWidget, QListWidgetItem, QMenu from PyQt5.QtGui import QFont, QIcon, QColor, QPalette, QMovie, QPixmap, QDesktopServices, QBrush -from PyQt5.QtCore import Qt, QObject, pyqtSignal, QThread, QUrl, QMetaObject, Q_ARG, QByteArray, QSize +from PyQt5.QtCore import Qt, QObject, pyqtSignal, QThread, QUrl, QMetaObject, Q_ARG, QByteArray, QSize, QTranslator, QLocale from datetime import datetime logging.basicConfig(level=logging.ERROR, format='%(asctime)s - %(levelname)s - %(message)s') class PicomcVersionSelector(QWidget): + def __init__(self): + super().__init__() # Initialize the parent class properly self.current_state = "menu" self.open_dialogs = [] - self.check_config_file() - self.themes_integrity() - themes_folder = "themes" + # Load translations before initializing UI + self.translators = [] + self.load_locale("./locales") + + self.update_checker = UpdateChecker(self) + + # Set up and use the health_check module + health_checker = HealthCheck() + health_checker.themes_integrity() + health_checker.check_config_file() + self.config = health_checker.config + + themes_folder = "themes" theme_file = self.config.get("Theme", "Dark.json") - # Ensure the theme file exists in the themes directory theme_file_path = os.path.join(themes_folder, theme_file) try: - # Load and apply the theme from the file self.load_theme_from_file(theme_file_path, app) print(f"Theme '{theme_file}' loaded successfully.") except Exception as e: print(f"Error: Could not load theme '{theme_file}'. Falling back to default theme. {e}") - super().__init__() self.init_ui() if self.config.get("CheckUpdate", False): @@ -46,7 +59,7 @@ class PicomcVersionSelector(QWidget): if self.config.get("IsRCPenabled", False): discord_rcp_thread = Thread(target=self.start_discord_rcp) - discord_rcp_thread.daemon = True # Make the thread a daemon so it terminates when the main program exits + discord_rcp_thread.daemon = True discord_rcp_thread.start() if self.config.get("IsFirstLaunch", False): @@ -55,6 +68,46 @@ class PicomcVersionSelector(QWidget): self.authenticator = MinecraftAuthenticator(self) self.authenticator.auth_finished.connect(self._on_auth_finished) + def load_locale(self, locale_dir_path): + self.config_path = "config.json" + if not os.path.exists(locale_dir_path): + print(f"Warning: Locale directory {locale_dir_path} does not exist") + return + + # Try to load and read config.json + try: + with open(self.config_path, 'r', encoding='utf-8') as config_file: + config = json.load(config_file) + locale_code = config.get("Locale", "") + except FileNotFoundError: + print(f"Warning: Config file {self.config_path} not found") + return + except json.JSONDecodeError: + print(f"Error: Invalid JSON in {self.config_path}") + return + + if not locale_code: + print("Warning: No locale specified in config.json") + return + + # Look for matching locale file + locale_pattern = f"*_{locale_code}.qm" # Pattern like *_es.qm for Spanish + found_matching_locale = False + + for filename in os.listdir(locale_dir_path): + if filename.endswith(".qm") and f"_{locale_code}" in filename: + locale_file_path = os.path.join(locale_dir_path, filename) + translator = QTranslator() + if translator.load(locale_file_path): + QApplication.instance().installTranslator(translator) + self.translators.append(translator) + print(f"Loaded locale: {locale_file_path}") + found_matching_locale = True + else: + print(f"Failed to load locale: {locale_file_path}") + + if not found_matching_locale: + print(f"Warning: No matching locale file found for language code: {locale_code}") def load_theme_from_file(self, file_path, app): self.theme = {} @@ -115,73 +168,6 @@ class PicomcVersionSelector(QWidget): else: print("Theme dosn't seem to have a stylesheet") - def themes_integrity(self): - # Define folder and file paths - themes_folder = "themes" - dark_theme_file = os.path.join(themes_folder, "Dark.json") - native_theme_file = os.path.join(themes_folder, "Native.json") - - # Define the default content for Dark.json - dark_theme_content = { - "manifest": { - "name": "Dark", - "description": "The default picodulce launcher theme", - "author": "Nixietab", - "license": "MIT" - }, - "palette": { - "Window": "#353535", - "WindowText": "#ffffff", - "Base": "#191919", - "AlternateBase": "#353535", - "ToolTipBase": "#ffffff", - "ToolTipText": "#ffffff", - "Text": "#ffffff", - "Button": "#353535", - "ButtonText": "#ffffff", - "BrightText": "#ff0000", - "Link": "#2a82da", - "Highlight": "#4bb679", - "HighlightedText": "#ffffff" - }, - "background_image_base64": "" - } - - # Define the default content for Native.json - native_theme_content = { - "manifest": { - "name": "Native", - "description": "The native looks of your OS", - "author": "Your Qt Style", - "license": "Any" - }, - "palette": {} - } - - # Step 1: Ensure the themes folder exists - if not os.path.exists(themes_folder): - print(f"Creating folder: {themes_folder}") - os.makedirs(themes_folder) - - # Step 2: Ensure Dark.json exists - if not os.path.isfile(dark_theme_file): - print(f"Creating file: {dark_theme_file}") - with open(dark_theme_file, "w", encoding="utf-8") as file: - json.dump(dark_theme_content, file, indent=2) - print("Dark.json has been created successfully.") - - # Step 3: Ensure Native.json exists - if not os.path.isfile(native_theme_file): - print(f"Creating file: {native_theme_file}") - with open(native_theme_file, "w", encoding="utf-8") as file: - json.dump(native_theme_content, file, indent=2) - print("Native.json has been created successfully.") - - # Check if both files exist and print OK message - if os.path.isfile(dark_theme_file) and os.path.isfile(native_theme_file): - print("Theme Integrity OK") - - def FirstLaunch(self): try: self.config_path = "config.json" @@ -277,7 +263,7 @@ class PicomcVersionSelector(QWidget): title_label.setFont(QFont("Arial", 24, QFont.Bold)) # Create installed versions section - installed_versions_label = QLabel('Installed Versions:') + installed_versions_label = QLabel(self.tr('Installed Versions:')) installed_versions_label.setFont(QFont("Arial", 14)) self.installed_version_combo = QComboBox() self.installed_version_combo.setMinimumWidth(200) @@ -287,32 +273,32 @@ class PicomcVersionSelector(QWidget): buttons_layout = QVBoxLayout() # Create play button for installed versions - self.play_button = QPushButton('Play') + self.play_button = QPushButton(self.tr('Play')) self.play_button.clicked.connect(self.play_instance) highlight_color = self.palette().color(QPalette.Highlight) self.play_button.setStyleSheet(f"background-color: {highlight_color.name()}; color: white;") buttons_layout.addWidget(self.play_button) # Version Manager Button - self.open_menu_button = QPushButton('Version Manager') + self.open_menu_button = QPushButton(self.tr('Version Manager')) self.open_menu_button.clicked.connect(self.open_mod_loader_and_version_menu) buttons_layout.addWidget(self.open_menu_button) # Create button to manage accounts - self.manage_accounts_button = QPushButton('Manage Accounts') + self.manage_accounts_button = QPushButton(self.tr('Manage Accounts')) self.manage_accounts_button.clicked.connect(self.manage_accounts) buttons_layout.addWidget(self.manage_accounts_button) # Create a button for the marroc mod loader - self.open_marroc_button = QPushButton('Marroc Mod Manager') + self.open_marroc_button = QPushButton(self.tr('Marroc Mod Manager')) self.open_marroc_button.clicked.connect(self.open_marroc_script) buttons_layout.addWidget(self.open_marroc_button) # Create grid layout for Settings and About buttons grid_layout = QGridLayout() - self.settings_button = QPushButton('Settings') + self.settings_button = QPushButton(self.tr('Settings')) self.settings_button.clicked.connect(self.open_settings_dialog) - self.about_button = QPushButton('About') + self.about_button = QPushButton(self.tr('About')) self.about_button.clicked.connect(self.show_about_dialog) grid_layout.addWidget(self.settings_button, 0, 0) @@ -350,53 +336,9 @@ class PicomcVersionSelector(QWidget): else: super().keyPressEvent(event) - def check_config_file(self): - config_path = "config.json" - default_config = { - "IsRCPenabled": False, - "CheckUpdate": False, - "IsBleeding": False, - "LastPlayed": "", - "IsFirstLaunch": True, - "Instance": "default", - "Theme": "Dark.json", - "ThemeBackground": True, - "ThemeRepository": "https://raw.githubusercontent.com/nixietab/picodulce-themes/main/repo.json" - } - - # Step 1: Check if the file exists; if not, create it with default values - if not os.path.exists(config_path): - with open(config_path, "w") as config_file: - json.dump(default_config, config_file, indent=4) - self.config = default_config - return - - # Step 2: Try loading the config file, handle invalid JSON - try: - with open(config_path, "r") as config_file: - self.config = json.load(config_file) - except (json.JSONDecodeError, ValueError): - # File is corrupted, overwrite it with default configuration - with open(config_path, "w") as config_file: - json.dump(default_config, config_file, indent=4) - self.config = default_config - return - - # Step 3: Check for missing keys and add defaults if necessary - updated = False - for key, value in default_config.items(): - if key not in self.config: # Field is missing - self.config[key] = value - updated = True - - # Step 4: Save the repaired config back to the file - if updated: - with open(config_path, "w") as config_file: - json.dump(self.config, config_file, indent=4) - def open_settings_dialog(self): dialog = QDialog(self) - dialog.setWindowTitle('Settings') + dialog.setWindowTitle(self.tr('Settings')) # Make the window resizable dialog.setMinimumSize(400, 300) @@ -408,17 +350,17 @@ class PicomcVersionSelector(QWidget): settings_tab = QWidget() settings_layout = QVBoxLayout() - title_label = QLabel('Settings') + title_label = QLabel(self.tr('Settings')) title_label.setFont(QFont("Arial", 14)) # Create checkboxes for settings tab - discord_rcp_checkbox = QCheckBox('Discord Rich Presence') + discord_rcp_checkbox = QCheckBox(self.tr('Discord Rich Presence')) discord_rcp_checkbox.setChecked(self.config.get("IsRCPenabled", False)) - check_updates_checkbox = QCheckBox('Check Updates on Start') + check_updates_checkbox = QCheckBox(self.tr('Check Updates on Start')) check_updates_checkbox.setChecked(self.config.get("CheckUpdate", False)) - bleeding_edge_checkbox = QCheckBox('Bleeding Edge') + bleeding_edge_checkbox = QCheckBox(self.tr('Bleeding Edge')) bleeding_edge_checkbox.setChecked(self.config.get("IsBleeding", False)) bleeding_edge_checkbox.stateChanged.connect(lambda: self.show_bleeding_edge_popup(bleeding_edge_checkbox)) @@ -428,13 +370,13 @@ class PicomcVersionSelector(QWidget): settings_layout.addWidget(bleeding_edge_checkbox) # Add buttons in the settings tab - update_button = QPushButton('Check for updates') + update_button = QPushButton(self.tr('Check for updates')) update_button.clicked.connect(self.check_for_update) - open_game_directory_button = QPushButton('Open game directory') + open_game_directory_button = QPushButton(self.tr('Open game directory')) open_game_directory_button.clicked.connect(self.open_game_directory) - stats_button = QPushButton('Stats for Nerds') + stats_button = QPushButton(self.tr('Stats for Nerds')) stats_button.clicked.connect(self.show_system_info) settings_layout.addWidget(update_button) @@ -448,15 +390,15 @@ class PicomcVersionSelector(QWidget): customization_layout = QVBoxLayout() # Create theme background checkbox for customization tab - theme_background_checkbox = QCheckBox('Theme Background') + theme_background_checkbox = QCheckBox(self.tr('Theme Background')) theme_background_checkbox.setChecked(self.config.get("ThemeBackground", False)) # Label to show currently selected theme theme_filename = self.config.get('Theme', 'Dark.json') - current_theme_label = QLabel(f"Current Theme: {theme_filename}") + current_theme_label = QLabel(self.tr(f"Current Theme: {theme_filename}")) # QListWidget to display available themes - json_files_label = QLabel('Installed Themes:') + json_files_label = QLabel(self.tr('Installed Themes:')) self.json_files_list_widget = QListWidget() # Track selected theme @@ -480,7 +422,7 @@ class PicomcVersionSelector(QWidget): customization_layout.addWidget(self.json_files_list_widget) # Button to download themes - download_themes_button = QPushButton("Download More Themes") + download_themes_button = QPushButton(self.tr("Download More Themes")) download_themes_button.clicked.connect(self.download_themes_window) customization_layout.addWidget(download_themes_button) @@ -488,11 +430,11 @@ class PicomcVersionSelector(QWidget): customization_tab.setLayout(customization_layout) # Add the tabs to the TabWidget - tab_widget.addTab(settings_tab, "Settings") - tab_widget.addTab(customization_tab, "Customization") + tab_widget.addTab(settings_tab, self.tr("Settings")) + tab_widget.addTab(customization_tab, self.tr("Customization")) # Save button - save_button = QPushButton('Save') + save_button = QPushButton(self.tr('Save')) save_button.clicked.connect( lambda: self.save_settings( discord_rcp_checkbox.isChecked(), @@ -571,13 +513,13 @@ class PicomcVersionSelector(QWidget): selected_item = json_files_list_widget.currentItem() if selected_item: self.selected_theme = selected_item.data(Qt.UserRole) - current_theme_label.setText(f"Current Theme: {self.selected_theme}") + current_theme_label.setText(self.tr(f"Current Theme: {self.selected_theme}")) ## REPOSITORY BLOCK BEGGINS def download_themes_window(self): dialog = QDialog(self) - dialog.setWindowTitle("Themes Repository") + dialog.setWindowTitle(self.tr("Themes Repository")) dialog.setGeometry(100, 100, 800, 600) main_layout = QHBoxLayout(dialog) @@ -599,7 +541,7 @@ class PicomcVersionSelector(QWidget): self.image_label.setStyleSheet("padding: 10px;") right_layout.addWidget(self.image_label) - download_button = QPushButton("Download Theme", dialog) + download_button = QPushButton(self.tr("Download Theme"), dialog) download_button.clicked.connect(self.theme_download) right_layout.addWidget(download_button) @@ -612,7 +554,6 @@ class PicomcVersionSelector(QWidget): dialog.finished.connect(lambda: self.update_themes_list()) - self.load_themes() dialog.exec_() @@ -626,18 +567,18 @@ class PicomcVersionSelector(QWidget): config = json.load(config_file) url = config.get("ThemeRepository") if not url: - raise ValueError("ThemeRepository is not defined in config.json") + raise ValueError(self.tr("ThemeRepository is not defined in config.json")) response = requests.get(url) response.raise_for_status() return response.json() except (FileNotFoundError, json.JSONDecodeError) as config_error: - self.show_error_popup("Error reading configuration", f"An error occurred while reading config.json: {config_error}") + self.show_error_popup(self.tr("Error reading configuration"), self.tr(f"An error occurred while reading config.json: {config_error}")) return {} except requests.exceptions.RequestException as fetch_error: - self.show_error_popup("Error fetching themes", f"An error occurred while fetching themes: {fetch_error}") + self.show_error_popup(self.tr("Error fetching themes"), self.tr(f"An error occurred while fetching themes: {fetch_error}")) return {} except ValueError as value_error: - self.show_error_popup("Configuration Error", str(value_error)) + self.show_error_popup(self.tr("Configuration Error"), self.tr(str(value_error))) return {} def download_theme_json(self, theme_url, theme_name): @@ -649,15 +590,15 @@ class PicomcVersionSelector(QWidget): theme_filename = os.path.join('themes', f'{theme_name}.json') with open(theme_filename, 'wb') as f: f.write(response.content) - print(f"Downloaded {theme_name} theme to {theme_filename}") + print(self.tr(f"Downloaded {theme_name} theme to {theme_filename}")) except requests.exceptions.RequestException as e: - self.show_error_popup("Error downloading theme", f"An error occurred while downloading {theme_name}: {e}") + self.show_error_popup(self.tr("Error downloading theme"), self.tr(f"An error occurred while downloading {theme_name}: {e}")) def show_error_popup(self, title, message): msg = QMessageBox() msg.setIcon(QMessageBox.Critical) - msg.setWindowTitle(title) - msg.setText(message) + msg.setWindowTitle(self.tr(title)) + msg.setText(self.tr(message)) msg.exec_() def is_theme_installed(self, theme_name): @@ -671,7 +612,7 @@ class PicomcVersionSelector(QWidget): for theme in themes: theme_display_name = f"{theme['name']} by {theme['author']}" if self.is_theme_installed(theme['name']): - theme_display_name += " [I]" + theme_display_name += self.tr(" [I]") installed_themes.append(theme_display_name) else: uninstalled_themes.append(theme_display_name) @@ -691,11 +632,11 @@ class PicomcVersionSelector(QWidget): theme = self.find_theme_by_name(theme_name) if theme: self.details_label.setText( - f"Name: {theme['name']}
" - f"Description: {theme['description']}
" - f"Author: {theme['author']}
" - f"License: {theme['license']}
" - f"Link: {theme['link']}
" + self.tr(f"Name: {theme['name']}
" + f"Description: {theme['description']}
" + f"Author: {theme['author']}
" + f"License: {theme['license']}
" + f"Link: {theme['link']}
") ) self.details_label.setTextFormat(Qt.RichText) self.details_label.setOpenExternalLinks(True) @@ -715,7 +656,7 @@ class PicomcVersionSelector(QWidget): response.raise_for_status() return response.content except requests.exceptions.RequestException as e: - self.show_error_popup("Error fetching image", f"An error occurred while fetching the image: {e}") + self.show_error_popup(self.tr("Error fetching image"), self.tr(f"An error occurred while fetching the image: {e}")) return None def find_theme_by_name(self, theme_name): @@ -735,7 +676,7 @@ class PicomcVersionSelector(QWidget): theme_url = theme["link"] self.download_theme_json(theme_url, theme_name) self.load_themes() - + ## REPOSITORY BLOCK ENDS @@ -763,20 +704,6 @@ class PicomcVersionSelector(QWidget): ) self.__init__() - def get_palette(self, palette_type): - """Retrieve the corresponding palette based on the palette type.""" - palettes = { - "Dark": self.create_dark_palette, - "Obsidian": self.create_obsidian_palette, - "Redstone": self.create_redstone_palette, - "Alpha": self.create_alpha_palette, - "Strawberry": self.create_strawberry_palette, - "Native": self.create_native_palette, - "Christmas": self.create_christmas_palette, - } - # Default to dark palette if the type is not specified or invalid - return palettes.get(palette_type, self.create_dark_palette)() - def get_system_info(self): # Get system information java_version = subprocess.getoutput("java -version 2>&1 | head -n 1") @@ -988,11 +915,11 @@ class PicomcVersionSelector(QWidget): # Main account management dialog dialog = QDialog(self) self.open_dialogs.append(dialog) - dialog.setWindowTitle('Manage Accounts') + dialog.setWindowTitle(self.tr('Manage Accounts')) dialog.setFixedSize(400, 250) # Title - title_label = QLabel('Manage Accounts') + title_label = QLabel(self.tr('Manage Accounts')) title_label.setFont(QFont("Arial", 14)) title_label.setAlignment(Qt.AlignCenter) # Center the text # Dropdown for selecting accounts @@ -1000,17 +927,17 @@ class PicomcVersionSelector(QWidget): self.populate_accounts(account_combo) # Buttons - create_account_button = QPushButton('Create Account') + create_account_button = QPushButton(self.tr('Create Account')) create_account_button.clicked.connect(self.open_create_account_dialog) - authenticate_button = QPushButton('Authenticate Account') + authenticate_button = QPushButton(self.tr('Authenticate Account')) authenticate_button.clicked.connect(lambda: self.authenticate_account(dialog, account_combo.currentText())) - remove_account_button = QPushButton('Remove Account') + remove_account_button = QPushButton(self.tr('Remove Account')) remove_account_button.clicked.connect(lambda: self.remove_account(dialog, account_combo.currentText())) # New button to set the account idk - set_default_button = QPushButton('Select') + set_default_button = QPushButton(self.tr('Select')) set_default_button.setFixedWidth(100) # Set button width to a quarter set_default_button.clicked.connect(lambda: self.set_default_account(account_combo.currentText(), dialog)) @@ -1038,15 +965,15 @@ class PicomcVersionSelector(QWidget): # Dialog for creating a new account dialog = QDialog(self) self.open_dialogs.append(dialog) - dialog.setWindowTitle('Create Account') + dialog.setWindowTitle(self.tr('Create Account')) dialog.setFixedSize(300, 150) username_input = QLineEdit() - username_input.setPlaceholderText('Enter Username') + username_input.setPlaceholderText(self.tr('Enter Username')) - microsoft_checkbox = QCheckBox('Microsoft Account') + microsoft_checkbox = QCheckBox(self.tr('Microsoft Account')) - create_button = QPushButton('Create') + create_button = QPushButton(self.tr('Create')) create_button.clicked.connect(lambda: self.create_account(dialog, username_input.text(), microsoft_checkbox.isChecked())) layout = QVBoxLayout() @@ -1063,11 +990,12 @@ class PicomcVersionSelector(QWidget): username = username.strip() if not username: - QMessageBox.warning(dialog, "Warning", "Username cannot be blank.") + QMessageBox.warning(dialog, self.tr("Warning"), self.tr("Username cannot be blank.")) return if not self.is_valid_username(username): - QMessageBox.warning(dialog, "Warning", "Invalid username. Usernames must be 3-16 characters long and can only contain letters, numbers, and underscores.") + QMessageBox.warning(dialog, self.tr("Warning"), + self.tr("Invalid username. Usernames must be 3-16 characters long and can only contain letters, numbers, and underscores.")) return try: @@ -1076,13 +1004,14 @@ class PicomcVersionSelector(QWidget): command.append('--ms') subprocess.run(command, check=True) - QMessageBox.information(dialog, "Success", f"Account '{username}' created successfully!") + QMessageBox.information(dialog, self.tr("Success"), + self.tr("Account '{username}' created successfully!").format(username=username)) self.populate_accounts_for_all_dialogs() dialog.accept() except subprocess.CalledProcessError as e: - error_message = f"Error creating account: {e.stderr.decode()}" + error_message = self.tr("Error creating account: {error}").format(error=e.stderr.decode()) logging.error(error_message) - QMessageBox.critical(dialog, "Error", error_message) + QMessageBox.critical(dialog, self.tr("Error"), error_message) def is_valid_username(self, username): # Validate the username according to Minecraft's rules @@ -1094,7 +1023,8 @@ class PicomcVersionSelector(QWidget): # Clean up the account name account_name = account_name.strip().lstrip(" * ") if not account_name: - QMessageBox.warning(dialog, "Warning", "Please select an account to authenticate.") + QMessageBox.warning(dialog, self.tr("Warning"), + self.tr("Please select an account to authenticate.")) return try: @@ -1107,15 +1037,18 @@ class PicomcVersionSelector(QWidget): self.authenticator.authenticate(account_name) except Exception as e: - error_message = f"Error authenticating account '{account_name}': {str(e)}" + error_message = self.tr("Error authenticating account '{account}': {error}").format( + account=account_name, error=str(e)) logging.error(error_message) - QMessageBox.critical(dialog, "Error", error_message) + QMessageBox.critical(dialog, self.tr("Error"), error_message) def _on_auth_finished(self, success): if success: - QMessageBox.information(self, "Success", "Account authenticated successfully!") + QMessageBox.information(self, self.tr("Success"), + self.tr("Account authenticated successfully!")) else: - QMessageBox.critical(self, "Error", "Failed to authenticate account") + QMessageBox.critical(self, self.tr("Error"), + self.tr("Failed to authenticate account")) # Cleanup if self.authenticator: @@ -1126,20 +1059,23 @@ class PicomcVersionSelector(QWidget): # Remove a selected account username = username.strip().lstrip(" * ") if not username: - QMessageBox.warning(dialog, "Warning", "Please select an account to remove.") + QMessageBox.warning(dialog, self.tr("Warning"), + self.tr("Please select an account to remove.")) return - confirm_message = f"Are you sure you want to remove the account '{username}'?\nThis action cannot be undone." - confirm_dialog = QMessageBox.question(dialog, "Confirm Removal", confirm_message, QMessageBox.Yes | QMessageBox.No, QMessageBox.No) + confirm_message = self.tr("Are you sure you want to remove the account '{username}'?\nThis action cannot be undone.").format(username=username) + confirm_dialog = QMessageBox.question(dialog, self.tr("Confirm Removal"), + confirm_message, QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if confirm_dialog == QMessageBox.Yes: try: subprocess.run(['picomc', 'account', 'remove', username], check=True) - QMessageBox.information(dialog, "Success", f"Account '{username}' removed successfully!") + QMessageBox.information(dialog, self.tr("Success"), + self.tr("Account '{username}' removed successfully!").format(username=username)) self.populate_accounts_for_all_dialogs() except subprocess.CalledProcessError as e: - error_message = f"Error removing account: {e.stderr.decode()}" + error_message = self.tr("Error removing account: {error}").format(error=e.stderr.decode()) logging.error(error_message) - QMessageBox.critical(dialog, "Error", error_message) + QMessageBox.critical(dialog, self.tr("Error"), error_message) def populate_accounts(self, account_combo): @@ -1224,148 +1160,23 @@ class PicomcVersionSelector(QWidget): if is_bleeding and version_bleeding: version_number = version_bleeding - about_message = ( - f"PicoDulce Launcher (v{version_number})\n\n" + # Create the about message without translation for the version placeholder + about_message = self.tr( + "PicoDulce Launcher (v{0})\n\n" "A simple Minecraft launcher built using Qt, based on the picomc project.\n\n" "Credits:\n" "Nixietab: Code and UI design\n" "Wabaano: Graphic design\n" "Olinad: Christmas!!!!" - ) - QMessageBox.about(self, "About", about_message) + ).format(version_number) - def check_for_update_start(self): - try: - with open("version.json") as f: - local_version_info = json.load(f) - local_version = local_version_info.get("version") - local_version_bleeding = local_version_info.get("versionBleeding") - logging.info(f"Local version: {local_version}") - logging.info(f"Local bleeding version: {local_version_bleeding}") - - with open("config.json") as config_file: - config = json.load(config_file) - is_bleeding = config.get("IsBleeding", False) - - if local_version: - remote_version_info = self.fetch_remote_version() - remote_version = remote_version_info.get("version") - remote_version_bleeding = remote_version_info.get("versionBleeding") - logging.info(f"Remote version: {remote_version}") - logging.info(f"Remote bleeding version: {remote_version_bleeding}") - - if is_bleeding: - remote_version_to_check = remote_version_bleeding - local_version_to_check = local_version_bleeding - else: - remote_version_to_check = remote_version - local_version_to_check = local_version - - if remote_version_to_check and (remote_version_to_check != local_version_to_check): - if is_bleeding: - update_message = f"Do you want to update to the bleeding edge version ({remote_version_bleeding})?" - else: - update_message = f"A new version ({remote_version}) is available!\nDo you want to download it now?" - update_dialog = QMessageBox.question(self, "Update Available", update_message, QMessageBox.Yes | QMessageBox.No, QMessageBox.No) - if update_dialog == QMessageBox.Yes: - # Download and apply the update - self.download_update(remote_version_info) - else: - print(f"You already have the latest version!") - else: - logging.error("Failed to read local version information.") - QMessageBox.critical(self, "Error", "Failed to check for updates.") - except Exception as e: - logging.error("Error checking for updates: %s", str(e)) - QMessageBox.critical(self, "Error", "Failed to check for updates.") + QMessageBox.about(self, self.tr("About"), about_message) def check_for_update(self): - try: - with open("version.json") as f: - local_version_info = json.load(f) - local_version = local_version_info.get("version") - local_version_bleeding = local_version_info.get("versionBleeding") - logging.info(f"Local version: {local_version}") - logging.info(f"Local bleeding version: {local_version_bleeding}") + self.update_checker.check_for_update() - with open("config.json") as config_file: - config = json.load(config_file) - is_bleeding = config.get("IsBleeding", False) - - if local_version: - remote_version_info = self.fetch_remote_version() - remote_version = remote_version_info.get("version") - remote_version_bleeding = remote_version_info.get("versionBleeding") - logging.info(f"Remote version: {remote_version}") - logging.info(f"Remote bleeding version: {remote_version_bleeding}") - - if is_bleeding: - remote_version_to_check = remote_version_bleeding - local_version_to_check = local_version_bleeding - else: - remote_version_to_check = remote_version - local_version_to_check = local_version - - if remote_version_to_check and (remote_version_to_check != local_version_to_check): - if is_bleeding: - update_message = f"Do you want to update to the bleeding edge version ({remote_version_bleeding})?" - else: - update_message = f"A new version ({remote_version}) is available!\nDo you want to download it now?" - update_dialog = QMessageBox.question(self, "Update Available", update_message, QMessageBox.Yes | QMessageBox.No, QMessageBox.No) - if update_dialog == QMessageBox.Yes: - # Download and apply the update - self.download_update(remote_version_info) - else: - QMessageBox.information(self, "Up to Date", "You already have the latest version!") - else: - logging.error("Failed to read local version information.") - QMessageBox.critical(self, "Error", "Failed to check for updates.") - except Exception as e: - logging.error("Error checking for updates: %s", str(e)) - QMessageBox.critical(self, "Error", "Failed to check for updates.") - - def fetch_remote_version(self): - try: - update_url = "https://raw.githubusercontent.com/nixietab/picodulce/main/version.json" - response = requests.get(update_url) - if response.status_code == 200: - remote_version_info = response.json() - return remote_version_info - else: - logging.error("Failed to fetch update information.") - return None - except Exception as e: - logging.error("Error fetching remote version: %s", str(e)) - return None - - def download_update(self, version_info): - try: - update_folder = "update" - if not os.path.exists(update_folder): - os.makedirs(update_folder) - for link in version_info.get("links", []): - filename = os.path.basename(link) - response = requests.get(link, stream=True) - if response.status_code == 200: - with open(os.path.join(update_folder, filename), 'wb') as f: - for chunk in response.iter_content(chunk_size=1024): - f.write(chunk) - else: - QMessageBox.critical(self, "Error", f"Failed to download update file: {filename}") - - # Move downloaded files one directory up - for file in os.listdir(update_folder): - src = os.path.join(update_folder, file) - dst = os.path.join(os.path.dirname(update_folder), file) - shutil.move(src, dst) - - # Remove the update folder - shutil.rmtree(update_folder) - - QMessageBox.information(self, "Update", "Updates downloaded successfully.") - except Exception as e: - logging.error("Error downloading updates: %s", str(e)) - QMessageBox.critical(self, "Error", "Failed to download updates.") + def check_for_update_start(self): + self.update_checker.check_for_update_start() def start_discord_rcp(self): from pypresence import Presence @@ -1698,7 +1509,7 @@ class ModLoaderAndVersionMenu(QDialog): layout = QVBoxLayout(install_mod_tab) # Create title label - title_label = QLabel('Mod Loader Installer') + title_label = QLabel(self.tr('Mod Loader Installer')) title_label.setFont(QFont("Arial", 14)) layout.addWidget(title_label) @@ -1723,7 +1534,7 @@ class ModLoaderAndVersionMenu(QDialog): self.fabric_checkbox.clicked.connect(update_versions) # Create install button - install_button = QPushButton('Install') + install_button = QPushButton(self.tr('Install')) install_button.clicked.connect(lambda: self.install_mod_loader( self.version_combo_mod.currentText(), self.forge_checkbox.isChecked(), @@ -1735,15 +1546,15 @@ class ModLoaderAndVersionMenu(QDialog): layout = QVBoxLayout(download_version_tab) # Create title label - title_label = QLabel('Download Version') + title_label = QLabel(self.tr('Download Version')) title_label.setFont(QFont("Arial", 14)) layout.addWidget(title_label) # Create checkboxes for different version types - self.release_checkbox = QCheckBox('Releases') - self.snapshot_checkbox = QCheckBox('Snapshots') - self.alpha_checkbox = QCheckBox('Alpha') - self.beta_checkbox = QCheckBox('Beta') + self.release_checkbox = QCheckBox(self.tr('Releases')) + self.snapshot_checkbox = QCheckBox(self.tr('Snapshots')) + self.alpha_checkbox = QCheckBox(self.tr('Alpha')) + self.beta_checkbox = QCheckBox(self.tr('Beta')) layout.addWidget(self.release_checkbox) layout.addWidget(self.snapshot_checkbox) layout.addWidget(self.alpha_checkbox) @@ -1803,10 +1614,10 @@ class ModLoaderAndVersionMenu(QDialog): def show_popup(self): self.popup = QDialog(self) - self.popup.setWindowTitle("Installing Version") + self.popup.setWindowTitle(self.tr("Installing Version")) layout = QVBoxLayout(self.popup) - label = QLabel("The version is being installed...") + label = QLabel(self.tr("The version is being installed...")) layout.addWidget(label) movie = QMovie("drums.gif") @@ -1860,7 +1671,7 @@ class ModLoaderAndVersionMenu(QDialog): def install_mod_loader(self, version, install_forge, install_fabric): if not install_forge and not install_fabric: - QMessageBox.warning(self, "Select Mod Loader", "Please select at least one mod loader.") + QMessageBox.warning(self, self.tr("Select Mod Loader"), self.tr("Please select at least one mod loader.")) return mod_loader = None