diff --git a/languages/de.json b/languages/de.json
index 17fd0ee..a707cb1 100644
--- a/languages/de.json
+++ b/languages/de.json
@@ -10,6 +10,7 @@
"russian": "Russisch",
"chinese": "Chinesisch",
"german": "Deutsch",
+ "french": "Französisch",
"help_text": "Wählen Sie Ihre bevorzugte Sprache für die Benutzeroberfläche.",
"restart_notice": "Die Sprachänderung wird nach dem Neustart der Anwendung wirksam."
},
diff --git a/languages/en.json b/languages/en.json
index a852430..e1b598f 100644
--- a/languages/en.json
+++ b/languages/en.json
@@ -10,6 +10,7 @@
"russian": "Русский (Russian)",
"chinese": "中文 (简体) (Chinese Simplified)",
"german": "Deutsch (German)",
+ "french": "Français (French)",
"help_text": "Select your preferred language for the interface.",
"restart_notice": "Language change will take effect after restarting the application."
},
diff --git a/languages/es.json b/languages/es.json
index 4339bad..c3519f0 100644
--- a/languages/es.json
+++ b/languages/es.json
@@ -9,7 +9,8 @@
"portuguese": "Português (Portuguese)",
"russian": "Русский (Russian)",
"chinese": "中文 (简体) (Chinese Simplified)",
- "german": "Deutsch (German)"
+ "german": "Deutsch (German)",
+ "french": "Français (French)"
},
"app": {
"title": "YTSage",
diff --git a/languages/fr.json b/languages/fr.json
new file mode 100644
index 0000000..439e03a
--- /dev/null
+++ b/languages/fr.json
@@ -0,0 +1,331 @@
+{
+ "language": {
+ "display_name": "Français (French)",
+ "select_language": "Sélectionner la langue :",
+ "current_language": "Langue actuelle : {language}",
+ "restart_required": "Les modifications de langue prendront effet après le redémarrage de l'application.",
+ "english": "Anglais",
+ "spanish": "Espagnol",
+ "portuguese": "Portugais",
+ "russian": "Russe",
+ "chinese": "Chinois",
+ "german": "Allemand",
+ "french": "Français",
+ "help_text": "Sélectionnez votre langue préférée pour l'interface.",
+ "restart_notice": "Le changement de langue prendra effet après le redémarrage de l'application."
+ },
+ "app": {
+ "title": "YTSage",
+ "version": "v{version}",
+ "ready": "Prêt"
+ },
+ "formats": {
+ "show_formats": "Afficher les formats :",
+ "video_format": "Format vidéo :",
+ "audio_format": "Format audio :",
+ "no_formats": "Aucun format disponible",
+ "loading": "Chargement des formats...",
+ "select": "Sélectionner",
+ "quality": "Qualité",
+ "extension": "Extension",
+ "resolution": "Résolution",
+ "file_size": "Taille du fichier",
+ "codec": "Codec",
+ "audio": "Audio",
+ "notes": "Notes",
+ "will_merge_audio": "L'audio sera fusionné",
+ "has_audio": "✓ Contient de l'audio",
+ "audio_only": "Audio uniquement",
+ "large_size": "Taille importante",
+ "medium_size": "Taille moyenne",
+ "standard_size": "Taille standard",
+ "compatible": "Compatible",
+ "efficient": "Efficace",
+ "high_quality": "Haute qualité",
+ "best_4k": "Meilleure (4K)",
+ "best_2k": "Meilleure (2K)",
+ "high_1080p": "Haute (1080p)",
+ "high_720p": "Haute (720p)",
+ "medium_480p": "Moyenne (480p)",
+ "low_quality": "Qualité faible",
+ "best_audio": "Meilleur audio",
+ "high_audio": "Audio élevé",
+ "medium_audio": "Audio moyen",
+ "low_audio": "Audio faible",
+ "audio_only_resolution": "Audio uniquement"
+ },
+ "buttons": {
+ "download": "Télécharger",
+ "pause": "Pause",
+ "resume": "Reprendre",
+ "cancel": "Annuler",
+ "browse": "Parcourir",
+ "clear": "Effacer",
+ "ok": "OK",
+ "apply": "Appliquer",
+ "close": "Fermer",
+ "run_command": "Exécuter la commande",
+ "custom_command_help": "Aide",
+ "about": "À propos",
+ "analyze": "Analyser",
+ "paste_url": "Coller l'URL",
+ "select_videos": "Sélectionner les vidéos...",
+ "video": "Vidéo",
+ "audio_only": "Audio uniquement",
+ "custom_options": "Options personnalisées",
+ "trim_video": "Découper la vidéo",
+ "download_settings": "Paramètres de téléchargement",
+ "update": "Mettre à jour yt-dlp",
+ "select_defaults": "Sélectionner par défaut",
+ "select_all": "Tout sélectionner",
+ "deselect_all": "Tout désélectionner"
+ },
+ "dialogs": {
+ "custom_options": "Options personnalisées",
+ "settings": "Paramètres",
+ "select_folder": "Sélectionner le dossier de téléchargement",
+ "sponsorblock_categories": "Catégories SponsorBlock",
+ "sponsorblock_description": "Choisissez les types de segments vidéo à supprimer automatiquement pendant le téléchargement.\nSponsorBlock utilise des données soumises par la communauté pour identifier ces segments.",
+ "select_subtitles": "Sélectionner les sous-titres",
+ "filter_languages_placeholder": "Filtrer les langues (ex: en, fr)...",
+ "no_subtitles_available": "Aucun sous-titre disponible",
+ "matching": "correspondant"
+ },
+ "tabs": {
+ "cookies": "Se connecter avec des cookies",
+ "custom_command": "Commande personnalisée",
+ "proxy": "Proxy",
+ "language": "Langue"
+ },
+ "cookies": {
+ "help_text": "Choisissez comment fournir les cookies pour l'authentification.\nCeci permet de télécharger des vidéos privées et des fichiers audio de haute qualité.",
+ "cookie_source": "Source des cookies",
+ "use_cookie_file": "Utiliser un fichier de cookies",
+ "extract_from_browser": "Extraire du navigateur",
+ "cookie_file": "Fichier de cookies",
+ "cookie_file_placeholder": "Chemin vers le fichier cookies.txt...",
+ "browser_selection": "Sélection du navigateur",
+ "browser_help": "Sélectionnez le navigateur depuis lequel extraire les cookies :",
+ "browser_label": "Navigateur :",
+ "profile_label": "Profil :",
+ "profile_placeholder": "Par défaut",
+ "browser_extract_message": "Les cookies du navigateur seront extraits lors de l'application",
+ "file_selected_message": "Fichier de cookies sélectionné - Cliquez sur OK pour appliquer",
+ "select_file_title": "Sélectionner le fichier de cookies",
+ "file_filter": "Fichiers de cookies (*.txt *.lwp)"
+ },
+ "custom_command": {
+ "help_text": "Entrez votre commande yt-dlp personnalisée ci-dessous. L'URL actuelle sera automatiquement ajoutée.
Pour la liste complète des options et exemples d'utilisation cliquez ici pour voir la documentation officielle yt-dlp.
Note : Le chemin de téléchargement et le modèle de nom de fichier sont gérés automatiquement.",
+ "input_label": "Arguments yt-dlp :",
+ "input_placeholder": "Entrez les arguments yt-dlp ici...\n\nEx : --extract-audio --audio-format mp3",
+ "output_label": "Sortie de la commande :",
+ "output_placeholder": "La sortie de la commande apparaîtra ici...",
+ "command_placeholder": "Entrer une commande yt-dlp personnalisée...",
+ "command_help": "Espaces réservés disponibles :\n{url} - URL de la vidéo\n{output} - Dossier de sortie\n\nExemple : --write-info-json --write-thumbnail",
+ "full_command": "🔧 Commande complète : {command}",
+ "command_success": "✅ Commande personnalisée exécutée avec succès !",
+ "command_failed": "❌ Commande échouée avec le code de sortie {code}",
+ "command_error": "❌ Erreur lors de l'exécution de la commande personnalisée : {error}"
+ },
+ "proxy": {
+ "help_text": "Configurer les paramètres proxy pour les téléchargements. Laisser vide pour une connexion directe.",
+ "main_proxy": "Proxy principal",
+ "main_proxy_help": "Serveur proxy principal pour tous les téléchargements. Prend en charge les protocoles HTTP/HTTPS et SOCKS5.",
+ "proxy_url_label": "URL du proxy :",
+ "proxy_url_placeholder": "http://proxy:port ou socks5://proxy:port",
+ "proxy_examples": "Exemples :\n• HTTP : http://proxy.example.com:8080\n• SOCKS5 : socks5://proxy.example.com:1080",
+ "geo_proxy": "Proxy de contournement géographique",
+ "geo_proxy_help": "Proxy secondaire spécialement pour contourner les restrictions géographiques.",
+ "geo_proxy_url_label": "URL du proxy de contournement géographique :",
+ "geo_proxy_url_placeholder": "http://geo-proxy:port",
+ "clear_main_proxy": "Effacer le proxy principal",
+ "clear_geo_proxy": "Effacer le proxy géographique",
+ "proxy_url": "URL du proxy",
+ "proxy_placeholder": "http://proxy:port ou socks5://proxy:port",
+ "geo_bypass": "Proxy de contournement géographique (pour les restrictions géographiques)",
+ "geo_bypass_placeholder": "http://proxy:port pour contourner le géo-blocage",
+ "invalid_main_url": "Format d'URL de proxy principal invalide",
+ "invalid_geo_url": "Format d'URL de proxy géographique invalide",
+ "main_configured": "Proxy principal configuré",
+ "geo_configured": "Proxy géographique configuré"
+ },
+ "download": {
+ "preparing": "Préparation du téléchargement...",
+ "please_enter_url": "Veuillez d'abord entrer une URL.",
+ "please_set_path": "Veuillez d'abord définir un chemin de téléchargement.",
+ "please_enter_url_and_path": "Veuillez entrer une URL et définir un chemin de téléchargement.",
+ "please_select_format": "Veuillez d'abord sélectionner un format."
+ },
+ "update": {
+ "title": "Mettre à jour yt-dlp",
+ "checking": "Vérification des mises à jour...",
+ "update_available": "Mise à jour disponible !\nVersion actuelle : {current}\nDernière version : {latest}",
+ "up_to_date": "yt-dlp est à jour (Version {version})",
+ "could_not_determine": "Impossible de déterminer les versions.",
+ "error_comparing": "Erreur lors de la comparaison des versions : {error}",
+ "update_available_failed": "Mise à jour disponible ! (Comparaison échouée)\nActuelle : {current}\nDernière : {latest}",
+ "updating": "Mise à jour...",
+ "initializing": "🚀 Initialisation du processus de mise à jour...",
+ "checking_current": "🔍 Vérification de l'installation actuelle...",
+ "found_at": "📍 yt-dlp trouvé à : {path}",
+ "error_getting_path": "❌ Erreur lors de la récupération du chemin yt-dlp : {error}",
+ "updating_binary": "📦 Mise à jour du binaire yt-dlp géré par l'application...",
+ "updating_pip": "🐍 Mise à jour du yt-dlp système via pip...",
+ "update_failed": "❌ Échec de la mise à jour yt-dlp. Veuillez réessayer ou vérifier votre connexion Internet.",
+ "binary_updated": "✅ Binaire mis à jour avec succès !",
+ "update_failed_stderr": "❌ Échec de la mise à jour yt-dlp : {error}",
+ "update_timeout": "❌ Délai d'attente de la mise à jour yt-dlp dépassé.",
+ "unexpected_error": "❌ Erreur inattendue lors de la mise à jour : {error}",
+ "checking_pip": "🔍 Vérification de l'installation pip actuelle...",
+ "current_version": "📋 Version actuelle : {version}",
+ "not_found_pip": "⚠️ yt-dlp non trouvé via pip, tentative d'installation...",
+ "checking_latest": "🌐 Vérification de la dernière version...",
+ "failed_check_updates": "❌ Échec de la vérification des mises à jour",
+ "latest_version": "🆕 Dernière version : {version}",
+ "updating_from_to": "⬆️ Mise à jour de {current} vers {latest}...",
+ "running_pip_install": "📦 Exécution de pip install --upgrade...",
+ "pip_completed": "✅ Mise à jour pip terminée avec succès !",
+ "pip_failed": "❌ Échec de la mise à jour pip : {error}"
+ },
+ "about": {
+ "title": "À propos de YTSage",
+ "version": "Version {version}",
+ "description": "Téléchargeur YouTube moderne avec une interface PySide6 propre.",
+ "author": "Par : {author}",
+ "github": "GitHub : {repo}",
+ "system_info": "Informations système",
+ "loading": "🔄 Chargement des informations système...",
+ "refresh": "🔄",
+ "refreshing": "🔄 Actualisation...",
+ "refresh_failed": "Échec de l'actualisation",
+ "refresh_failed_message": "Impossible d'actualiser les informations de version.",
+ "detected": "✓ Détecté",
+ "missing": "✗ Manquant",
+ "not_available": "Non disponible"
+ },
+ "time_range": {
+ "title": "Découper la vidéo",
+ "time_range_group": "Plage horaire",
+ "start_time": "Heure de début (HH:MM:SS)",
+ "end_time": "Heure de fin (HH:MM:SS)",
+ "start_placeholder": "00:00:00",
+ "end_placeholder": "00:00:00",
+ "start_time_placeholder": "Heure de début (HH:MM:SS)",
+ "end_time_placeholder": "Heure de fin (HH:MM:SS)",
+ "force_keyframes": "Forcer les images clés aux points de coupe",
+ "help_text": "Définissez les heures de début et de fin pour découper la vidéo.\nLaissez vide pour télécharger la vidéo complète.",
+ "invalid_format": "Format d'heure invalide. Utilisez le format HH:MM:SS.",
+ "start_after_end": "L'heure de début ne peut pas être après l'heure de fin."
+ },
+ "settings": {
+ "title": "Paramètres de téléchargement",
+ "download_path": "Chemin de téléchargement",
+ "browse": "Parcourir...",
+ "speed_limit": "Limite de vitesse",
+ "speed_limit_placeholder": "Aucune",
+ "auto_update_ytdlp": "Mise à jour automatique de yt-dlp",
+ "enable_auto_updates": "Activer les mises à jour automatiques de yt-dlp",
+ "update_frequency": "Fréquence de mise à jour :",
+ "check_startup": "Vérifier à chaque démarrage (minimum 1 heure entre les vérifications)",
+ "check_daily": "Vérifier quotidiennement",
+ "check_weekly": "Vérifier hebdomadairement",
+ "check_updates_now": "Vérifier les mises à jour maintenant",
+ "update_check_title": "Vérification de mise à jour",
+ "could_not_determine_version": "Impossible de déterminer la version actuelle de yt-dlp.",
+ "update_available_dialog": "Mise à jour disponible !\n\nActuelle : {current}\nDernière : {latest}\n\nUtilisez le bouton 'Mettre à jour yt-dlp' dans la fenêtre principale pour mettre à jour.",
+ "up_to_date_dialog": "yt-dlp est à jour !\n\nVersion actuelle : {version}",
+ "error_checking_updates": "Erreur lors de la vérification des mises à jour : {error}",
+ "settings_saved_title": "Paramètres sauvegardés",
+ "settings_saved_message": "Les paramètres de mise à jour automatique ont été sauvegardés avec succès !",
+ "error_title": "Erreur",
+ "failed_save_settings": "Échec de la sauvegarde des paramètres de mise à jour automatique.",
+ "error_saving_settings": "Erreur lors de la sauvegarde des paramètres de mise à jour automatique : {error}",
+ "auto_update_title": "Paramètres de mise à jour automatique",
+ "auto_update_header": "🔄 Paramètres de mise à jour automatique",
+ "auto_update_description": "Configurez les mises à jour automatiques pour yt-dlp afin de vous assurer d'avoir toujours les dernières fonctionnalités et corrections de bogues.",
+ "current_status": "État actuel",
+ "current_version_label": "Version actuelle de yt-dlp : Vérification...",
+ "last_check_label": "Dernière vérification de mise à jour : Jamais",
+ "next_check_label": "Prochaine vérification : Basée sur les paramètres",
+ "manual_check_button": "🔍 Vérifier les mises à jour maintenant",
+ "update_frequency_group": "Fréquence de mise à jour",
+ "save_settings": "Sauvegarder les paramètres",
+ "settings_saved_successfully": "✅ Paramètres sauvegardés avec succès !",
+ "error_saving": "❌ Erreur lors de la sauvegarde des paramètres : {error}"
+ },
+ "main_ui": {
+ "url_placeholder": "Entrer l'URL de la vidéo ou de la playlist YouTube",
+ "merge_subtitles": "Fusionner les sous-titres",
+ "save_thumbnail": "Sauvegarder la miniature",
+ "save_description": "Sauvegarder la description",
+ "embed_chapters": "Intégrer les chapitres",
+ "subtitles_selected": "{count} sélectionné(s)",
+ "all_selected": "Tous sélectionnés",
+ "select_videos_all": "Sélectionner les vidéos... (Tous sélectionnés)",
+ "please_enter_url": "Veuillez d'abord entrer une URL",
+ "cookie_file_selected_title": "Fichier de cookies sélectionné",
+ "cookie_file_selected_message": "Fichier de cookies sélectionné : {path}",
+ "browser_cookies_selected_title": "Cookies de navigateur sélectionnés",
+ "browser_cookies_selected_message": "Les cookies du navigateur seront extraits depuis : {browser}",
+ "error_no_format_info": "Erreur : Aucune information de format disponible.",
+ "error_extract_info": "Erreur : Impossible d'extraire les informations de base de la vidéo. Veuillez vérifier votre lien.",
+ "analyzing_preparing": "Analyse (0%)... Préparation de la requête",
+ "analyzing_extracting_basic": "Analyse (15%)... Extraction des informations de base",
+ "analyzing_extracting_detailed": "Analyse (30%)... Extraction des informations détaillées",
+ "analyzing_processing_video": "Analyse (45%)... Traitement des données vidéo",
+ "analyzing_processing_formats": "Analyse (60%)... Traitement des formats",
+ "analyzing_loading_thumbnail": "Analyse (75%)... Chargement de la miniature",
+ "analyzing_processing_subtitles": "Analyse (85%)... Traitement des sous-titres",
+ "analyzing_updating_table": "Analyse (95%)... Mise à jour du tableau des formats",
+ "analysis_complete": "Analyse terminée !",
+ "analyzing_extracting_ytdlp": "Analyse (30%)... Extraction d'informations avec l'exécutable yt-dlp",
+ "analyzing_processing_data": "Analyse (60%)... Traitement des données",
+ "analyzing_processing_formats_ytdlp": "Analyse (75%)... Traitement des formats",
+ "analyzing_loading_thumbnail_ytdlp": "Analyse (85%)... Chargement de la miniature",
+ "analyzing_processing_subtitles_ytdlp": "Analyse (90%)... Traitement des sous-titres",
+ "select_subtitles": "Sélectionner les sous-titres...",
+ "sponsorblock_categories": "Catégories SponsorBlock..."
+ },
+ "sponsorblock": {
+ "sponsor": "Sponsor",
+ "sponsor_desc": "Publicité payante, recommandations payantes et publicité directe",
+ "selfpromo": "Auto-promotion non payante",
+ "selfpromo_desc": "Publicité non payante pour le contenu propre du créateur",
+ "interaction": "Rappel d'interaction",
+ "interaction_desc": "Les spectateurs sont invités à aimer, s'abonner ou suivre sur les réseaux sociaux",
+ "intro": "Intro",
+ "intro_desc": "Introduction de la vidéo qui peut être ignorée",
+ "outro": "Outro/Cartes de fin",
+ "outro_desc": "Crédits ou fin de la vidéo",
+ "preview": "Aperçu/Récapitulatif",
+ "preview_desc": "Bref récapitulatif des vidéos précédentes ou aperçu du contenu à venir",
+ "music_offtopic": "Section hors-musique",
+ "music_offtopic_desc": "Uniquement pour les vidéos musicales. Marque les sections non-musicales",
+ "filler": "Digression de remplissage",
+ "filler_desc": "Scènes de diversion ajoutées uniquement comme remplissage ou pour l'humour"
+ },
+ "video_info": {
+ "channel": "Chaîne",
+ "views": "Vues",
+ "likes": "J'aime",
+ "upload_date": "Date de mise en ligne",
+ "duration": "Durée",
+ "unknown_channel": "Chaîne inconnue",
+ "unknown_date": "Date inconnue",
+ "unknown_title": "Titre inconnu"
+ },
+ "command": {
+ "running": "En cours...",
+ "run_command": "Exécuter la commande"
+ },
+ "selection": {
+ "none_selected": "0 sélectionné",
+ "one_selected": "1 catégorie sélectionnée",
+ "count_selected": "{count} sélectionné(s)"
+ },
+ "status": {
+ "ready": "Prêt",
+ "file_exists": "⚠️ Le fichier existe déjà",
+ "cancelling": "Annulation du téléchargement..."
+ }
+}
\ No newline at end of file
diff --git a/languages/pt.json b/languages/pt.json
index 29f9727..6bc935e 100644
--- a/languages/pt.json
+++ b/languages/pt.json
@@ -10,6 +10,7 @@
"russian": "Русский (Russian)",
"chinese": "中文 (简体) (Chinese Simplified)",
"german": "Alemão",
+ "french": "Francês",
"help_text": "Selecione seu idioma preferido para a interface.",
"restart_notice": "A alteração de idioma terá efeito após reiniciar a aplicação."
},
diff --git a/languages/ru.json b/languages/ru.json
index e48501d..b4db0ca 100644
--- a/languages/ru.json
+++ b/languages/ru.json
@@ -10,6 +10,7 @@
"russian": "Русский",
"chinese": "中文 (简体) (Chinese Simplified)",
"german": "Немецкий",
+ "french": "Французский",
"help_text": "Выберите предпочитаемый язык для интерфейса.",
"restart_notice": "Изменение языка вступит в силу после перезапуска приложения."
},
diff --git a/languages/zh.json b/languages/zh.json
index 96ea952..b89adc3 100644
--- a/languages/zh.json
+++ b/languages/zh.json
@@ -10,6 +10,7 @@
"russian": "俄语",
"chinese": "中文",
"german": "德语",
+ "french": "法语",
"help_text": "选择您喜欢的界面语言。",
"restart_notice": "语言更改将在重启应用程序后生效。"
},
diff --git a/src/gui/ytsage_gui_format_table.py b/src/gui/ytsage_gui_format_table.py
index e3180c0..f04f86b 100644
--- a/src/gui/ytsage_gui_format_table.py
+++ b/src/gui/ytsage_gui_format_table.py
@@ -264,14 +264,15 @@ class FormatTableMixin:
# Column 1: Quality (Always shown)
quality_text = self.get_quality_label(f)
quality_item = QTableWidgetItem(quality_text)
- # Set color based on quality (check English, Spanish, Portuguese, Russian, and Chinese terms)
- if any(term in quality_text for term in ["Best", "Óptima", "Mejor", "Melhor", "Лучшее", "最佳", "Beste"]):
+ # Set color based on quality (check English, Spanish, Portuguese, Russian, Chinese, German, and French terms)
+ quality_lower = quality_text.lower() # Make comparison case-insensitive
+ if any(term.lower() in quality_lower for term in ["Best", "Óptima", "Mejor", "Melhor", "Лучшее", "最佳", "Beste", "Meilleure"]):
quality_item.setForeground(QColor("#00ff00")) # Green for best quality
- elif any(term in quality_text for term in ["High", "Alta", "Alto", "Áudio Alto", "Audio Alto", "Высокое", "高清", "高质量", "Hoch"]):
+ elif any(term.lower() in quality_lower for term in ["High", "Alta", "Alto", "Áudio Alto", "Audio Alto", "Высокое", "高清", "高质量", "Hoch", "Haute", "Élevé", "Audio élevé"]):
quality_item.setForeground(QColor("#00cc00")) # Light green for high quality
- elif any(term in quality_text for term in ["Medium", "Media", "Medio", "Média", "Áudio Médio", "Audio Medio", "Среднее", "中等", "Mittel"]):
+ elif any(term.lower() in quality_lower for term in ["Medium", "Media", "Medio", "Média", "Áudio Médio", "Audio Medio", "Среднее", "中等", "Mittel", "Moyenne", "Audio moyen"]):
quality_item.setForeground(QColor("#ffaa00")) # Orange for medium quality
- elif any(term in quality_text for term in ["Low", "Baja", "Bajo", "Baixa", "Áudio Baixo", "Audio Bajo", "Низкое", "低质量", "Niedrig", "Niedriges Audio"]):
+ elif any(term.lower() in quality_lower for term in ["Low", "Baja", "Bajo", "Baixa", "Áudio Baixo", "Audio Bajo", "Низкое", "低质量", "Niedrig", "Niedriges Audio", "Faible", "Audio faible", "Qualité faible"]):
quality_item.setForeground(QColor("#ff5555")) # Red for low quality
self.format_table.setItem(row, 1, quality_item)
diff --git a/src/utils/ytsage_localization.py b/src/utils/ytsage_localization.py
index a31c4c8..5826694 100644
--- a/src/utils/ytsage_localization.py
+++ b/src/utils/ytsage_localization.py
@@ -85,7 +85,8 @@ class LocalizationManager:
"portuguese": "Português (Portuguese)",
"russian": "Русский (Russian)",
"chinese": "中文 (简体) (Chinese Simplified)",
- "german": "Deutsch (German)"
+ "german": "Deutsch (German)",
+ "french": "Français (French)"
},
"download": {
"preparing": "🚀 Preparing your download...",