Add localization support and language selection

Introduces English and Spanish translation files, integrates localization throughout dialogs, and adds a language selection tab to the custom options dialog. UI strings are now loaded via the localization manager, enabling dynamic language switching and improved internationalization.
This commit is contained in:
Your Name
2025-09-30 20:53:59 +03:00
parent 0376c1c4cd
commit c5916d57c0
12 changed files with 1293 additions and 311 deletions
@@ -19,6 +19,8 @@ from PySide6.QtWidgets import (
QWidget,
)
from src.utils.ytsage_localization import _
from src.core.ytsage_ffmpeg import get_ffmpeg_path
from src.core.ytsage_utils import _version_cache, check_ffmpeg, get_ffmpeg_version, get_ytdlp_version, refresh_version_cache
from src.core.ytsage_yt_dlp import check_ytdlp_installed, get_yt_dlp_path
@@ -60,7 +62,7 @@ class AboutDialog(QDialog):
def __init__(self, parent=None) -> None:
super().__init__(parent)
self._parent = parent # Store parent to access version etc.
self.setWindowTitle("About YTSage")
self.setWindowTitle(_("about.title"))
self.setMinimumSize(460, 420) # Slightly increased to accommodate paths
self.resize(460, 440) # Slightly increased initial size
self.setMaximumSize(500, 480) # Reasonable maximum size
@@ -157,13 +159,13 @@ class AboutDialog(QDialog):
layout.addWidget(title_label)
version_label = QLabel(
f"<span style='color: #cccccc; font-size: 13px; font-weight: normal;'>Version {getattr(self._parent, 'version', '4.9.0b')}</span>"
f"<span style='color: #cccccc; font-size: 13px; font-weight: normal;'>{_('about.version', version=getattr(self._parent, 'version', '4.9.0b'))}</span>"
)
version_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
layout.addWidget(version_label)
# Description - more compact
description_label = QLabel("Modern YouTube downloader with a clean PySide6 interface.")
description_label = QLabel(_("about.description"))
description_label.setWordWrap(True)
description_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
description_label.setStyleSheet("color: #ffffff; font-size: 11px; margin: 6px 0;")
@@ -174,13 +176,13 @@ class AboutDialog(QDialog):
info_layout.setSpacing(15)
author_label = QLabel(
"By: <a href='https://github.com/oop7/' style='color: #c90000; text-decoration: none; font-size: 10px;'>oop7</a>"
f"{_('about.author', author='<a href=\'https://github.com/oop7/\' style=\'color: #c90000; text-decoration: none; font-size: 10px;\'>oop7</a>')}"
)
author_label.setOpenExternalLinks(True)
info_layout.addWidget(author_label)
repo_label = QLabel(
"GitHub: <a href='https://github.com/oop7/YTSage/' style='color: #c90000; text-decoration: none; font-size: 10px;'>YTSage</a>"
f"{_('about.github', repo='<a href=\'https://github.com/oop7/YTSage/\' style=\'color: #c90000; text-decoration: none; font-size: 10px;\'>YTSage</a>')}"
)
repo_label.setOpenExternalLinks(True)
info_layout.addWidget(repo_label)
@@ -219,7 +221,7 @@ class AboutDialog(QDialog):
header_layout.setContentsMargins(0, 0, 0, 5)
# System Information title
title_label = QLabel("System Information")
title_label = QLabel(_("about.system_info"))
title_label.setStyleSheet(
"""
QLabel {
@@ -237,7 +239,7 @@ class AboutDialog(QDialog):
header_layout.addStretch()
# Create refresh button
self.refresh_btn = QPushButton("🔄")
self.refresh_btn = QPushButton(_("about.refresh"))
self.refresh_btn.setFixedSize(16, 16)
self.refresh_btn.setStyleSheet(
"""
@@ -283,7 +285,7 @@ class AboutDialog(QDialog):
def _show_loading_message(self) -> None:
"""Show a compact loading message while system information is being gathered."""
loading_label = QLabel("🔄 Loading system information...")
loading_label = QLabel(_("about.loading"))
loading_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
loading_label.setStyleSheet(
"""
@@ -381,7 +383,7 @@ class AboutDialog(QDialog):
# yt-dlp Status - compact version with path
ytdlp_found = check_ytdlp_installed()
ytdlp_status_text = (
"<span style='color: #4CAF50;'>✓ Detected</span>" if ytdlp_found else "<span style='color: #F44336;'>✗ Missing</span>"
f"<span style='color: #4CAF50;'>{_('about.detected')}</span>" if ytdlp_found else f"<span style='color: #F44336;'>{_('about.missing')}</span>"
)
ytdlp_version = get_ytdlp_version()
@@ -409,11 +411,11 @@ class AboutDialog(QDialog):
# FFmpeg Status - compact version with path
ffmpeg_found = check_ffmpeg()
ffmpeg_status_text = (
"<span style='color: #4CAF50;'>✓ Detected</span>"
f"<span style='color: #4CAF50;'>{_('about.detected')}</span>"
if ffmpeg_found
else "<span style='color: #F44336;'>✗ Missing</span>"
else f"<span style='color: #F44336;'>{_('about.missing')}</span>"
)
ffmpeg_version = get_ffmpeg_version() if ffmpeg_found else "Not Available"
ffmpeg_version = get_ffmpeg_version() if ffmpeg_found else _('about.not_available')
# Get FFmpeg path
ffmpeg_path_text = None
@@ -440,7 +442,7 @@ class AboutDialog(QDialog):
def refresh_version_info(self) -> None:
"""Refresh version information manually."""
self.refresh_btn.setText("🔄 Refreshing...")
self.refresh_btn.setText(_('about.refreshing'))
self.refresh_btn.setEnabled(False)
# Perform refresh in a separate thread to avoid blocking UI
@@ -457,7 +459,7 @@ class AboutDialog(QDialog):
def on_refresh_finished(self, success) -> None:
"""Handle refresh completion."""
self.refresh_btn.setText("🔄 Refresh")
self.refresh_btn.setText(_('about.refresh'))
self.refresh_btn.setEnabled(True)
if success:
@@ -466,8 +468,8 @@ class AboutDialog(QDialog):
# Show error message with proper styling
msg_box = QMessageBox(self)
msg_box.setIcon(QMessageBox.Icon.Warning)
msg_box.setWindowTitle("Refresh Failed")
msg_box.setText("Failed to refresh version information.")
msg_box.setWindowTitle(_('about.refresh_failed'))
msg_box.setText(_('about.refresh_failed_message'))
msg_box.setWindowIcon(self.windowIcon())
msg_box.setStyleSheet(
"""