v1.9.7.0 - Ajout support multilangues (beta) , correction de bugs de logique, amélioration des erreurs
This commit is contained in:
349
language.py
Normal file
349
language.py
Normal file
@@ -0,0 +1,349 @@
|
||||
import os
|
||||
import json
|
||||
import pygame #type: ignore
|
||||
import logging
|
||||
import config
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Langue par défaut et variables globales
|
||||
DEFAULT_LANGUAGE = "fr"
|
||||
current_language = DEFAULT_LANGUAGE
|
||||
translations = {}
|
||||
show_language_selector_on_startup = False
|
||||
|
||||
def load_language(lang_code=None):
|
||||
"""Charge les traductions pour la langue spécifiée ou la langue par défaut."""
|
||||
global current_language, translations
|
||||
|
||||
if lang_code is None:
|
||||
lang_code = DEFAULT_LANGUAGE
|
||||
|
||||
lang_file = os.path.join(config.APP_FOLDER, "languages", f"{lang_code}.json")
|
||||
|
||||
try:
|
||||
if not os.path.exists(lang_file):
|
||||
if lang_code != DEFAULT_LANGUAGE:
|
||||
logger.warning(f"Fichier de langue {lang_code} non trouvé, utilisation de la langue par défaut")
|
||||
return load_language(DEFAULT_LANGUAGE)
|
||||
else:
|
||||
logger.error(f"Fichier de langue par défaut {lang_file} non trouvé")
|
||||
return False
|
||||
|
||||
with open(lang_file, 'r', encoding='utf-8') as f:
|
||||
translations = json.load(f)
|
||||
|
||||
current_language = lang_code
|
||||
logger.debug(f"Langue {lang_code} chargée avec succès ({len(translations)} traductions)")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur lors du chargement de la langue {lang_code}: {str(e)}")
|
||||
if lang_code != DEFAULT_LANGUAGE:
|
||||
logger.warning(f"Tentative de chargement de la langue par défaut")
|
||||
return load_language(DEFAULT_LANGUAGE)
|
||||
return False
|
||||
|
||||
def get_text(key, default=None):
|
||||
"""Récupère la traduction correspondant à la clé."""
|
||||
if not translations:
|
||||
load_language()
|
||||
|
||||
if key in translations:
|
||||
return translations[key]
|
||||
|
||||
# Si la clé n'existe pas, retourner la valeur par défaut ou la clé elle-même
|
||||
if default is not None:
|
||||
return default
|
||||
|
||||
logger.warning(f"Clé de traduction '{key}' non trouvée dans la langue {current_language}")
|
||||
return key
|
||||
|
||||
def get_available_languages():
|
||||
"""Récupère la liste des langues disponibles."""
|
||||
languages_dir = os.path.join(config.APP_FOLDER, "languages")
|
||||
|
||||
if not os.path.exists(languages_dir):
|
||||
logger.warning(f"Dossier des langues {languages_dir} non trouvé")
|
||||
return []
|
||||
|
||||
languages = []
|
||||
for file in os.listdir(languages_dir):
|
||||
if file.endswith(".json"):
|
||||
lang_code = os.path.splitext(file)[0]
|
||||
languages.append(lang_code)
|
||||
|
||||
return languages
|
||||
|
||||
def set_language(lang_code):
|
||||
"""Change la langue courante et sauvegarde la préférence."""
|
||||
if load_language(lang_code):
|
||||
config.current_language = lang_code
|
||||
save_language_preference(lang_code)
|
||||
return True
|
||||
return False
|
||||
|
||||
def save_language_preference(lang_code):
|
||||
"""Sauvegarde la préférence de langue dans un fichier."""
|
||||
try:
|
||||
# S'assurer que le dossier existe
|
||||
os.makedirs(os.path.dirname(config.LANGUAGE_CONFIG_PATH), exist_ok=True)
|
||||
|
||||
# Sauvegarder la préférence
|
||||
with open(config.LANGUAGE_CONFIG_PATH, 'w', encoding='utf-8') as f:
|
||||
json.dump({"language": lang_code}, f)
|
||||
|
||||
logger.debug(f"Préférence de langue sauvegardée: {lang_code}")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur lors de la sauvegarde de la préférence de langue: {str(e)}")
|
||||
return False
|
||||
|
||||
def load_language_preference():
|
||||
"""Charge la préférence de langue depuis le fichier."""
|
||||
global show_language_selector_on_startup
|
||||
|
||||
try:
|
||||
if not os.path.exists(config.LANGUAGE_CONFIG_PATH):
|
||||
logger.info("Aucune préférence de langue trouvée, utilisation du français par défaut")
|
||||
# Créer le fichier avec le français par défaut
|
||||
save_language_preference(DEFAULT_LANGUAGE)
|
||||
return DEFAULT_LANGUAGE
|
||||
|
||||
with open(config.LANGUAGE_CONFIG_PATH, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
lang_code = data.get("language", DEFAULT_LANGUAGE)
|
||||
|
||||
logger.debug(f"Préférence de langue chargée: {lang_code}")
|
||||
return lang_code
|
||||
except json.JSONDecodeError:
|
||||
logger.warning("Fichier de préférence de langue corrompu, utilisation du français par défaut")
|
||||
# Recréer le fichier avec le français par défaut
|
||||
save_language_preference(DEFAULT_LANGUAGE)
|
||||
return DEFAULT_LANGUAGE
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur lors du chargement de la préférence de langue: {str(e)}")
|
||||
# Recréer le fichier avec le français par défaut
|
||||
save_language_preference(DEFAULT_LANGUAGE)
|
||||
return DEFAULT_LANGUAGE
|
||||
|
||||
def get_language_name(lang_code):
|
||||
"""Retourne le nom de la langue à partir du code."""
|
||||
language_names = {
|
||||
"fr": "Français",
|
||||
"en": "English",
|
||||
"es": "Español",
|
||||
"de": "Deutsch",
|
||||
"it": "Italiano",
|
||||
"pt": "Português",
|
||||
"ja": "日本語",
|
||||
"zh": "中文",
|
||||
"ru": "Русский"
|
||||
}
|
||||
return language_names.get(lang_code, lang_code)
|
||||
|
||||
def draw_language_selector(screen, selected_language_index):
|
||||
"""Affiche le sélecteur de langue."""
|
||||
from display import THEME_COLORS, OVERLAY
|
||||
|
||||
# Obtenir les langues disponibles
|
||||
available_languages = get_available_languages()
|
||||
|
||||
if not available_languages:
|
||||
logger.error("Aucune langue disponible")
|
||||
return
|
||||
|
||||
# Afficher l'overlay
|
||||
screen.blit(OVERLAY, (0, 0))
|
||||
|
||||
# Titre
|
||||
title_text = _("language_select_title")
|
||||
title_surface = config.font.render(title_text, True, THEME_COLORS["text"])
|
||||
title_rect = title_surface.get_rect(center=(config.screen_width // 2, config.screen_height // 4))
|
||||
|
||||
# Fond du titre
|
||||
title_bg_rect = title_rect.inflate(40, 20)
|
||||
pygame.draw.rect(screen, THEME_COLORS["button_idle"], title_bg_rect, border_radius=10)
|
||||
pygame.draw.rect(screen, THEME_COLORS["border"], title_bg_rect, 2, border_radius=10)
|
||||
screen.blit(title_surface, title_rect)
|
||||
|
||||
# Options de langue
|
||||
button_height = 60
|
||||
button_width = 300
|
||||
button_spacing = 20
|
||||
|
||||
total_height = len(available_languages) * (button_height + button_spacing) - button_spacing
|
||||
start_y = (config.screen_height - total_height) // 2
|
||||
|
||||
for i, lang_code in enumerate(available_languages):
|
||||
# Obtenir le nom de la langue
|
||||
lang_name = get_language_name(lang_code)
|
||||
|
||||
# Position du bouton
|
||||
button_x = (config.screen_width - button_width) // 2
|
||||
button_y = start_y + i * (button_height + button_spacing)
|
||||
|
||||
# Dessiner le bouton
|
||||
button_color = THEME_COLORS["button_hover"] if i == selected_language_index else THEME_COLORS["button_idle"]
|
||||
pygame.draw.rect(screen, button_color, (button_x, button_y, button_width, button_height), border_radius=10)
|
||||
pygame.draw.rect(screen, THEME_COLORS["border"], (button_x, button_y, button_width, button_height), 2, border_radius=10)
|
||||
|
||||
# Texte du bouton
|
||||
text_surface = config.font.render(lang_name, True, THEME_COLORS["text"])
|
||||
text_rect = text_surface.get_rect(center=(button_x + button_width // 2, button_y + button_height // 2))
|
||||
screen.blit(text_surface, text_rect)
|
||||
|
||||
# Instructions
|
||||
instruction_text = _("language_select_instruction")
|
||||
instruction_surface = config.small_font.render(instruction_text, True, THEME_COLORS["text"])
|
||||
instruction_rect = instruction_surface.get_rect(center=(config.screen_width // 2, config.screen_height - 50))
|
||||
screen.blit(instruction_surface, instruction_rect)
|
||||
|
||||
def handle_language_menu_events(event, screen):
|
||||
"""Gère les événements du menu de sélection de langue avec support clavier et manette."""
|
||||
available_languages = get_available_languages()
|
||||
|
||||
if not available_languages:
|
||||
logger.error("Aucune langue disponible")
|
||||
config.menu_state = "platform" # Toujours revenir à platform en cas d'erreur
|
||||
config.needs_redraw = True
|
||||
return
|
||||
|
||||
# Navigation avec les touches du clavier
|
||||
if event.type == pygame.KEYDOWN:
|
||||
# Navigation vers le haut
|
||||
if event.key == pygame.K_UP:
|
||||
config.selected_language_index = (config.selected_language_index - 1) % len(available_languages)
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Navigation vers le haut dans le sélecteur de langue: {config.selected_language_index}")
|
||||
|
||||
# Navigation vers le bas
|
||||
elif event.key == pygame.K_DOWN:
|
||||
config.selected_language_index = (config.selected_language_index + 1) % len(available_languages)
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Navigation vers le bas dans le sélecteur de langue: {config.selected_language_index}")
|
||||
|
||||
# Sélection de la langue
|
||||
elif event.key == pygame.K_RETURN:
|
||||
lang_code = available_languages[config.selected_language_index]
|
||||
if set_language(lang_code):
|
||||
logger.info(f"Langue changée pour {lang_code}")
|
||||
config.current_language = lang_code
|
||||
|
||||
# Déterminer l'état suivant en fonction du contexte
|
||||
if config.previous_menu_state is None:
|
||||
# Premier démarrage - passer à l'état loading pour charger les plateformes
|
||||
config.menu_state = "loading"
|
||||
logger.debug("Premier démarrage: passage à l'état loading après sélection de la langue")
|
||||
elif config.previous_menu_state == "pause_menu":
|
||||
# Si on vient du menu pause, retourner au menu pause avec un message
|
||||
config.menu_state = "restart_popup"
|
||||
config.popup_message = _("language_changed").format(lang_code)
|
||||
config.popup_timer = 2000 # 2 secondes
|
||||
config.previous_menu_state = "platform" # Pour revenir à l'écran principal après le popup
|
||||
logger.debug("Message de confirmation de changement de langue affiché, retour au menu pause")
|
||||
else:
|
||||
# Autre cas, retourner à l'état précédent avec un message
|
||||
config.menu_state = "platform" # Toujours revenir à platform pour éviter les problèmes
|
||||
logger.debug(f"Retour à l'écran principal après sélection de la langue")
|
||||
else:
|
||||
# Retour au menu pause en cas d'erreur
|
||||
config.menu_state = "platform" # Toujours revenir à platform en cas d'erreur
|
||||
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Sélection de la langue: {lang_code}")
|
||||
|
||||
# Annulation (seulement si on n'est pas au démarrage)
|
||||
elif event.key == pygame.K_ESCAPE and config.previous_menu_state is not None:
|
||||
config.menu_state = "pause_menu"
|
||||
config.needs_redraw = True
|
||||
logger.debug("Annulation de la sélection de langue, retour au menu pause")
|
||||
|
||||
# Support de la manette
|
||||
elif event.type == pygame.JOYBUTTONDOWN:
|
||||
# Sélection avec le bouton A (généralement 0)
|
||||
if event.button == 0: # Bouton A
|
||||
lang_code = available_languages[config.selected_language_index]
|
||||
if set_language(lang_code):
|
||||
logger.info(f"Langue changée pour {lang_code} (manette)")
|
||||
config.current_language = lang_code
|
||||
|
||||
# Déterminer l'état suivant en fonction du contexte
|
||||
if config.previous_menu_state is None:
|
||||
# Premier démarrage - passer à l'état loading pour charger les plateformes
|
||||
config.menu_state = "loading"
|
||||
logger.debug("Premier démarrage: passage à l'état loading après sélection de la langue (manette)")
|
||||
else:
|
||||
config.menu_state = "platform"
|
||||
else:
|
||||
config.menu_state = "platform"
|
||||
config.needs_redraw = True
|
||||
|
||||
# Annulation avec le bouton B (généralement 1)
|
||||
elif event.button == 1 and config.previous_menu_state is not None: # Bouton B
|
||||
config.menu_state = "pause_menu"
|
||||
config.needs_redraw = True
|
||||
logger.debug("Annulation de la sélection de langue (manette), retour au menu pause")
|
||||
|
||||
# Navigation avec le D-pad
|
||||
elif event.type == pygame.JOYHATMOTION:
|
||||
if event.value == (0, 1): # Haut
|
||||
config.selected_language_index = (config.selected_language_index - 1) % len(available_languages)
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Navigation vers le haut dans le sélecteur de langue (D-pad): {config.selected_language_index}")
|
||||
elif event.value == (0, -1): # Bas
|
||||
config.selected_language_index = (config.selected_language_index + 1) % len(available_languages)
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Navigation vers le bas dans le sélecteur de langue (D-pad): {config.selected_language_index}")
|
||||
|
||||
# Navigation avec les joysticks analogiques
|
||||
elif event.type == pygame.JOYAXISMOTION:
|
||||
# Joystick gauche vertical (généralement axe 1)
|
||||
if event.axis == 1 and abs(event.value) > 0.5:
|
||||
if event.value < -0.5: # Haut
|
||||
config.selected_language_index = (config.selected_language_index - 1) % len(available_languages)
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Navigation vers le haut dans le sélecteur de langue (joystick): {config.selected_language_index}")
|
||||
elif event.value > 0.5: # Bas
|
||||
config.selected_language_index = (config.selected_language_index + 1) % len(available_languages)
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Navigation vers le bas dans le sélecteur de langue (joystick): {config.selected_language_index}")
|
||||
|
||||
|
||||
def update_valid_states():
|
||||
"""Ajoute l'état language_select à la liste des états valides."""
|
||||
from controls import VALID_STATES
|
||||
if "language_select" not in VALID_STATES:
|
||||
VALID_STATES.append("language_select")
|
||||
logger.debug("État language_select ajouté aux états valides")
|
||||
|
||||
def initialize_language():
|
||||
"""Initialise la langue au démarrage de l'application."""
|
||||
global show_language_selector_on_startup
|
||||
|
||||
# Vérifier si le fichier de préférence de langue existe
|
||||
language_file_exists = os.path.exists(config.LANGUAGE_CONFIG_PATH)
|
||||
|
||||
# Si le fichier n'existe pas, créer un fichier avec le français par défaut
|
||||
if not language_file_exists:
|
||||
logger.info("Aucun fichier de préférence de langue trouvé, création avec le français par défaut")
|
||||
save_language_preference(DEFAULT_LANGUAGE)
|
||||
show_language_selector_on_startup = False # Ne pas afficher le sélecteur au démarrage
|
||||
else:
|
||||
# Le fichier existe, charger normalement
|
||||
show_language_selector_on_startup = False # Ne jamais afficher le sélecteur au démarrage
|
||||
|
||||
# Charger la préférence de langue
|
||||
lang_code = load_language_preference()
|
||||
|
||||
# Charger la langue par défaut ou préférée
|
||||
if load_language(lang_code):
|
||||
logger.info(f"Langue chargée au démarrage: {lang_code}")
|
||||
else:
|
||||
logger.warning(f"Impossible de charger la langue {lang_code}, utilisation de la langue par défaut")
|
||||
load_language(DEFAULT_LANGUAGE)
|
||||
|
||||
return True
|
||||
|
||||
# Alias pour faciliter l'utilisation
|
||||
_ = get_text
|
||||
Reference in New Issue
Block a user