remade all the translation in a easier, faster and more readable way

This commit is contained in:
Faraphel 2022-09-01 18:10:38 +02:00
parent 97477bba79
commit 423a02ce4c
39 changed files with 394 additions and 435 deletions

View file

@ -7,8 +7,8 @@
},
"installation_completed": {
"text": {
"fr": "Merci d'avoir téléchargé {{ mod_config.name }} !\nSi vous avez un problème en démarrant le jeu avec Dolphin, essayer dans Configurer > Avancé > Modifier la taille de la mémoire émulée et mettre MEM2 à 128MB\n\nAmusez-vous !",
"en": "Thanks for downloading {{ mod_config.name }} !\nIf You have an issue starting the game with Dolphin, try in Config > Advanced > Enable Emulated Memory Size Override and set MEM2 to 128MB\n\nHave fun !"
"fr": "Merci d'avoir téléchargé {{ mod_config.name }} !\nSi vous avez un problème en démarrant le jeu avec Dolphin, essayer de mettre MEM2 dans Configurer > Avancé > Modifier la taille de la mémoire émulée à 128MB\n\nAmusez-vous !",
"en": "Thanks for downloading {{ mod_config.name }} !\nIf You have an issue when starting the game with Dolphin, try to set MEM2 in Config > Advanced > Enable Emulated Memory Size Override to 128MB\n\nHave fun !"
}
}
}

View file

@ -1,124 +1,112 @@
{
"name": "English",
"translation": {
"INSTALLER_TITLE": "MKWF-Install",
"LANGUAGE_SELECTION": "Language",
"TRACK_FILTER": "Track Filters",
"ADVANCED_CONFIGURATION": "Advanced",
"HELP": "Help",
"INVALID_SOURCE_PATH": "Invalid path for source game",
"DISCORD": "Discord",
"GITHUB WIKI": "Github Wiki",
"READTHEDOCS": "ReadTheDocs",
"ORIGINAL_GAME_FILE": "Original Game File",
"SELECT_SOURCE_GAME": "Select the source game",
"WII GAMES": "Wii games",
"ERROR": "Error",
"ERROR_INVALID_SOURCE_GAME": "The source game path is invalid",
"GAME_DIRECTORY_DESTINATION": "Game Directory Destination",
"SELECT_DESTINATION_GAME": "Select the destination game",
"WARNING": "Warning",
"WARNING_DESTINATION_GAME_NOT_WRITABLE": "The destination game path is not writable",
"INSTALL": "Install",
"ERROR_INVALID_DESTINATION_GAME": "The destination game path is invalid",
"WARNING_LOW_SPACE_CONTINUE": "The space left on the drive is low. Continue ?",
"INSTALLATION_COMPLETED": "Installation Completed",
"INSTALLATION_FINISHED_WITH_SUCCESS": "The installation ended successfully !",
"OPEN_MYSTUFF_SETTINGS": "Open MyStuff settings",
"THREADS_USAGE": "Threads usage",
"USE": "Use",
"THREADS": "threads",
"MESSAGE_FROM_MOD_AUTHOR": "Message from the author",
"GLOBAL_MOD_SETTINGS": "Global mod settings",
"SPECIFIC_MOD_SETTINGS": "Specific mod settings",
"CONFIGURE_MYSTUFF_PATCH": "Configure the MyStuff patchs",
"DISABLED": "Disabled",
"ENABLED": "Enabled",
"NEW_PROFILE": "New Profile",
"DELETE_PROFILE": "Delete Profile",
"ADD_MYSTUFF": "Add MyStuff patch",
"REMOVE_MYSTUFF": "Remove MyStuff patch",
"MYSTUFF_PROFILE_ALREADY_EXIST": "This MyStuff profile already exist !",
"MYSTUFF_PROFILE_FORBIDDEN_NAME": "This MyStuff profile name can't be used !",
"SELECT_MYSTUFF": "Select MyStuff patch",
"TYPE_PREVIEW_WINDOW": "Type of preview window",
"NOT_FOUND": "not found",
"TYPE_MOD_SETTINGS": "Type of mod settings",
"BMG_LAYER_MODE": "bmg layer mode",
"IS_NOT_IMPLEMENTED": "is not implemented",
"IMAGE_LAYER_TYPE": "image layer type",
"OPERATION": "Operation",
"PATH": "Path",
"MODE": "Mode",
"SOURCE": "Source",
"OUTSIDE_ALLOWED_RANGE": "outside of allowed range",
"IN_PATCH": "in patch",
"INSTALLING_PATCH": "Installing the patch",
"PATCH_TITLE": "Patch",
"PRE-PATCH_TITLE": "Pre-patch",
"PATCHING": "Patching",
"FORBIDDEN_TRACK_ATTRIBUTE": "Forbidden track attribute",
"FORBIDDEN_ARENA_ATTRIBUTE": "Forbidden arena attribute",
"EXTRACTING_AUTOADD_FILES": "Extracting autoadd files...",
"EXTRACTING_ORIGINAL_TRACKS": "Extracting original tracks...",
"INSTALLING_MYSTUFF": "Installing MyStuff",
"INSTALLING_ALL_MYSTUFF_PATCHS": "Installing all the mystuff patchs",
"PREPARING": "Preparing",
"SPECIAL_FILE": "special file",
"REPACKING": "Repacking",
"ALL_ARCHIVES": "all archives",
"INSTALLING_ALL": "Installing all",
"CONVERTING_GAME_TO": "Converting game to",
"DELETING_EXTRACTED_GAME": "Deleting the extracted game...",
"NOT_MKW_GAME": "Not a Mario Kart Wii game",
"GAME_ALREADY_MODDED": "This game is already modded",
"COPYING_GAME": "Copying Game...",
"EXTRACTING": "Extracting",
"ESTIMATED_TIME_REMAINING": "estimated time remaining",
"CHANGING_GAME_METADATA": "Changing game metadata...",
"EXTRACTION": "Extraction",
"MYSTUFF": "MyStuff",
"PREPARING_FILES": "Preparing files",
"PRE-PATCHING": "Pre-Patching",
"CONVERTING_TO_GAME_FILE": "Converting to game file",
"CANNOT_FIND_COLOR": "Can't find color",
"NORMALIZING_TRACKS": "Normalizing tracks",
"INVALID_MACRO": "Invalid macro",
"INVALID_AST_TYPE": "Invalid ast type",
"MAGIC_ATTRIBUTE_ARE_FORBIDDEN": "Magic attribute are forbidden",
"CANNOT_SET_ATTRIBUTE": "Can't set value of attribute",
"CANNOT_SET_ENVIRONMENT": "Can't set value of environment",
"CANNOT_SET_ARGUMENT": "Can't set value of argument",
"CALLING_FUNCTION_NOT_ALLOWED": "Calling this function is not allowed",
"FORBIDDEN_SYNTAX": "Forbidden syntax",
"MAGIC_METHOD_FORBIDDEN": "Magic method are not allowed",
"CANNOT_GET_ERROR_MESSAGE": "Can't get the error message",
"RAISED": "raised",
"CANNOT_FIND_TOOL": "Can't find tool",
"IN_TOOLS_DIRECTORY": "in the tools directory.",
"CANNOT_EXTRACT_A_DIRECTORY": "Can't extract a directory",
"CANNOT_FIND_SLOT": "Can't find slot",
"FORBIDDEN_TRACKGROUP_ATTRIBUTE": "Forbidden TrackGroup attribute",
"SAFE_EVAL_ERROR": "Safe eval error",
"TEMPLATE_USED": "Template used",
"MORE_IN_ERROR_LOG": "More information in the error.log file",
"COPY_FUNCTION_FORBIDDEN": "Copying functions is forbidden",
"GET_METHOD_FORBIDDEN": "Using getattr on a method is forbidden",
"CAN_ONLY_CALL_METHOD_OF_CONSTANT": "You can only call methods on constant",
"CAN_ONLY_CALL_FUNCTION_IN_ENV": "You can only call function from the environment",
"ENABLE_DEVELOPER_MODE": "Enable the developer mode",
"TESTING_MOD_SETTINGS": "Test mod settings",
"SETTINGS_FILE": "Settings file",
"EXPORT_SETTINGS": "Export settings",
"IMPORT_SETTINGS": "Import settings",
"PREPARING_RIIVOLUTION": "Preparing for Riivolution",
"CONVERTING_TO_RIIVOLUTION": "Converting to Riivolution patch",
"PATCHS": "patchs",
"PRE-PATCHS": "pre-patchs",
"CALCULATING_HASH_FOR": "Calculating hash for",
"WARNING_NOT_ROOT": "The application require root permission. You should start the application with 'sudo'. Continue anyway ?",
"WARNING_INSTALLER_PERMISSION": "The application need writing and execution permissions to its own files. You can use 'sudo chmod 777 -R <installer-path>' to fix that. Continue anyway ?",
"WARNING_EMPTY_CACHE": "By emptying the cache, you will gain %.2fGB, but reinstalling a mod will be take way longer. Continue ?",
"EMPTY_CACHE": "Empty the cache"
}
"name": "English",
"translation": {
"TITLE_INSTALL": "MKWF-Install",
"TITLE_MOD_SETTINGS": "Mod Settings",
"TITLE_MYSTUFF_SETTINGS": "MyStuff Settings",
"MENU_LANGUAGE_SELECTION": "Language",
"MENU_ADVANCED": "Advanced",
"MENU_ADVANCED_MYSTUFF": "MyStuff settings",
"MENU_ADVANCED_THREADS": "Thread usage",
"MENU_ADVANCED_THREADS_SELECTION": "Use %i threads",
"MENU_ADVANCED_EMPTY_CACHE": "Empty the cache",
"MENU_ADVANCED_DEVELOPER_MODE": "Enable developer settings",
"MENU_HELP": "Help",
"PART_EXTRACTION": "Extraction",
"PART_PRE_RIIVOLUTION": "Pre-Riivolution",
"PART_MYSTUFF": "MyStuff",
"PART_PREPARING_FILES": "Preparing files",
"PART_PREPATCH": "Pre-Patch",
"PART_LECODE": "LE-CODE",
"PART_PATCH": "Patch",
"PART_RIIVOLUTION": "Riivolution",
"PART_CONVERSION": "Conversion",
"TEXT_SOURCE_GAME": "Source Game",
"TEXT_GAME_DESTINATION": "Game Destination",
"TEXT_INSTALL": "Install",
"TEXT_SELECT_SOURCE_GAME": "Select a source game",
"TEXT_SELECT_GAME_DESTINATION": "Select the game destination",
"TEXT_WII_GAMES": "Wii games",
"TEXT_INSTALLATION_COMPLETED": "Installation Completed",
"TEXT_INSTALLATION_FINISHED_SUCCESSFULLY": "The installation finished successfully !",
"TEXT_MESSAGE_FROM_AUTHOR": "Message from the author",
"TEXT_MOD_GLOBAL_SETTINGS": "Global settings",
"TEXT_MOD_SPECIFIC_SETTINGS": "Specific settings",
"TEXT_MOD_TEST_SETTINGS": "Test settings",
"TEXT_DISABLED": "Disabled",
"TEXT_ENABLED": "Enabled",
"TEXT_IMPORT_SETTINGS": "Import settings",
"TEXT_EXPORT_SETTINGS": "Export settings",
"TEXT_SETTINGS_FILE": "Settings file",
"TEXT_NEW_PROFILE": "New profile",
"TEXT_DELETE_PROFILE": "Delete profile",
"TEXT_ADD_MYSTUFF": "Add MyStuff",
"TEXT_REMOVE_MYSTUFF": "Remove MyStuff",
"TEXT_SELECT_MYSTUFF": "Select MyStuff",
"TEXT_COPYING_GAME": "Copying game",
"TEXT_EXTRACTING_GAME": "Extracting game - %i%% (estimated time remaining : %s)",
"TEXT_CHANGING_GAME_METADATA": "Changing game metadata",
"TEXT_EXTRACTING_AUTOADD": "Extracting autoadd files",
"TEXT_EXTRACTING_ORIGINAL_TRACKS": "Extracting original tracks \"%s\"",
"TEXT_INSTALLING_MYSTUFF": "Installing MyStuff \"%s\"",
"TEXT_PREPARING_SPECIAL_FILE": "Preparing special file \"%s\"",
"TEXT_PREPARING_MAIN_DOL": "Preparing main.dol",
"TEXT_REPACKING_ARCHIVE": "Repacking archive \"%s\"",
"TEXT_PATCHING_LECODE": "Patching LECODE.bin",
"TEXT_CONVERT_GAME_TO": "Converting game to %s",
"TEXT_DELETING_EXTRACTED_GAME": "Deleting extracted game",
"TEXT_CONVERTING_TO_RIIVOLUTION": "Converting to Riivolution",
"TEXT_CALCULATING_HASH": "Calculating hash for \"%s\"",
"TEXT_NORMALIZING_TRACKS": "Normalizing tracks :\n%s",
"TEXT_PATCHING": "Patching \"%s\"",
"WARNING": "Warning",
"WARNING_EMPTY_CACHE": "By emptying the cache, you will gain %.2fGB, but reinstalling a mod will be take way longer. Continue ?",
"WARNING_DESTINATION_NOT_WRITABLE": "The game destination directory is not writable. Continue anyway ?",
"WARNING_LOW_SPACE_CONTINUE": "Low space remaining on disk %s (%.2fGB). Continue anyway ?",
"WARNING_NOT_ROOT": "Root permissions are required. You should start the application with 'sudo'. Continue anyway ?",
"WARNING_INSTALLER_PERMISSION": "Writing and execution permissions are required. You can use 'sudo chmod 777 -R <installer-path>' to fix that. Continue anyway ?",
"ERROR": "Error",
"ERROR_SEE_LOGS": "See error.log for more information",
"ERROR_INVALID_SOURCE_GAME": "Invalid source game : \"%s\"",
"ERROR_INVALID_GAME_DESTINATION": "Invalid game destination : \"%s\"",
"ERROR_MYSTUFF_PROFILE_ALREADY_EXIST": "The MyStuff profile \"%s\" already exist !",
"ERROR_MYSTUFF_PROFILE_FORBIDDEN_NAME": "The MyStuff profile name \"%s\" is forbidden !",
"ERROR_NOT_MKW_GAME": "This game is not a Mario Kart Wii game : \"%s\"",
"ERROR_GAME_ALREADY_MODDED": "This game is already modded : \"%s\"",
"ERROR_PATH_OUTSIDE_RANGE": "Path \"%s\" is outside allowed range : \"%s\"",
"ERROR_PREVIEW_WINDOW_NOT_FOUND": "Preview window type \"%s\" not found",
"ERROR_CANNOT_FIND_COLOR": "Can't find color : \"%s\"",
"ERROR_CANNOT_FIND_SLOT": "Can't find slot : \"%s\"",
"ERROR_MOD_SETTINGS_NOT_FOUND": "Mod settings type \"%s\" not found",
"ERROR_FORBIDDEN_TRACK_ATTRIBUTE": "Forbidden track attribute : \"%s\"",
"ERROR_FORBIDDEN_ARENA_ATTRIBUTE": "Forbidden arena attribute : \"%s\"",
"ERROR_FORBIDDEN_TRACKGROUP_ATTRIBUTE": "Forbidden track group attribute : \"%s\"",
"ERROR_SAFEEVAL": "Safe eval error (template used : \"%s\")",
"ERROR_INVALID_MACRO": "Invalid macro : \"%s\"",
"ERROR_INVALID_AST_TYPE": "Invalid AST type : \"%s\"",
"ERROR_FORBIDDEN_MAGIC_ATTRIBUTE": "Magic attribute are forbidden : \"%s\"",
"ERROR_FORBIDDEN_MAGIC_METHOD": "Magic method are forbidden : \"%s\"",
"ERROR_GETTING_METHOD_FORBIDDEN": "Getting method is forbidden : \"%s\"",
"ERROR_CANNOT_SET_ATTRIBUTE": "Setting attribute is forbidden : \"%s\"",
"ERROR_CANNOT_SET_ENVIRONMENT": "Setting environment variable is forbidden : \"%s\"",
"ERROR_CANNOT_SET_ARGUMENT": "Setting argument variable is forbidden : \"%s\"",
"ERROR_CAN_ONLY_CALL_CONSTANT_METHOD": "Only calling method of constant is allowed",
"ERROR_CAN_ONLY_CALL_ENV_FUNCTION": "You only can call function from the environment",
"ERROR_FORBIDDEN_SYNTAX": "Forbidden syntax : \"%s\"",
"ERROR_FUNCTION_COPY_FORBIDDEN": "Copying a function is forbidden",
"ERROR_CANNOT_GET_ERROR_MESSAGE": "Can't get the error message",
"ERROR_WT": "%s raised %i :\n%s",
"ERROR_CANNOT_FIND_TOOL": "Can't find tool \"%s\" in the tools directory",
"ERROR_CANNOT_EXTRACT_DIRECTORY": "Directory can't be extracted",
"ERROR_PATCH_MODE_NOT_IMPLEMENTED": "Patch mode \"%s\" is not implemented (in patch : \"%s\")",
"ERROR_SOURCE_NOT_IMPLEMENTED": "Source \"%s\" is not implemented (in patch : \"%s\")",
"ERROR_OPERATION_NOT_IMPLEMENTED": "Operation \"%s\" is not implemented",
"ERROR_BMG_LAYER_MODE_NOT_IMPLEMENTED": "Bmg layer mode \"%s\" not implemented",
"ERROR_IMAGE_LAYER_TYPE_NOT_IMPLEMENTED": "Image layer type \"%s\" not implemented"
}
}

View file

@ -1,125 +1,112 @@
{
"name": "Français",
"translation": {
"INSTALLER_TITLE": "MKWF-Install",
"LANGUAGE_SELECTION": "Langue",
"TRACK_FILTER": "filtrer les courses",
"ADVANCED_CONFIGURATION": "Avancée",
"HELP": "Aide",
"name": "Français",
"translation": {
"TITLE_INSTALL": "MKWF-Install",
"TITLE_MOD_SETTINGS": "Paramètres du mod",
"TITLE_MYSTUFF_SETTINGS": "Paramètres MyStuff",
"INVALID_SOURCE_PATH": "Chemin invalide pour le jeu source",
"DISCORD": "Discord",
"GITHUB WIKI": "Wiki Github",
"READTHEDOCS": "ReadTheDocs",
"ORIGINAL_GAME_FILE": "Fichier du jeu original",
"SELECT_SOURCE_GAME": "Sélectionner le jeu source",
"WII GAMES": "jeux Wii",
"ERROR": "Erreur",
"ERROR_INVALID_SOURCE_GAME": "Chemin invalide pour le jeu source",
"GAME_DIRECTORY_DESTINATION": "Dossier de destination du jeu",
"SELECT_DESTINATION_GAME": "Sélectionner le dossier de destination du jeu",
"WARNING": "Attention",
"WARNING_DESTINATION_GAME_NOT_WRITABLE": "Le chemin de destination n'est pas modifiable",
"INSTALL": "Installer",
"ERROR_INVALID_DESTINATION_GAME": "Chemin invalide pour la destination du jeu",
"WARNING_LOW_SPACE_CONTINUE": "L'espace restant sur le disque dur est faible. Continuer ?",
"INSTALLATION_COMPLETED": "Installation Complété",
"INSTALLATION_FINISHED_WITH_SUCCESS": "L'installation s'est terminé avec succès !",
"OPEN_MYSTUFF_SETTINGS": "Ouvrir les paramètres MyStuff",
"THREADS_USAGE": "Utilisation des Threads",
"USE": "Utiliser",
"THREADS": "threads",
"MESSAGE_FROM_MOD_AUTHOR": "Message de l'auteur",
"GLOBAL_MOD_SETTINGS": "Paramètre global de mod",
"SPECIFIC_MOD_SETTINGS": "Paramètre spécifique de mod",
"CONFIGURE_MYSTUFF_PATCH": "Configurer les patchs MyStuff",
"DISABLED": "Désactiver",
"ENABLED": "Activer",
"NEW_PROFILE": "Nouveau profil",
"DELETE_PROFILE": "Retirer un Profil",
"ADD_MYSTUFF": "Ajouter un patch MyStuff",
"REMOVE_MYSTUFF": "Retirer un patch MyStuff",
"MYSTUFF_PROFILE_ALREADY_EXIST": "Ce profil MyStuff existe déjà !",
"MYSTUFF_PROFILE_FORBIDDEN_NAME": "Ce nom de profil MyStuff ne peut pas être utilisé !",
"SELECT_MYSTUFF": "Sélectionner un patch MyStuff",
"TYPE_PREVIEW_WINDOW": "Type de fenêtre de prévisualisation",
"NOT_FOUND": "introuvable",
"TYPE_MOD_SETTINGS": "Type de paramètre de mod",
"BMG_LAYER_MODE": "mode de couche bmg",
"IS_NOT_IMPLEMENTED": "n'est pas implémenté",
"IMAGE_LAYER_TYPE": "type de couche d'image",
"OPERATION": "Opération",
"PATH": "Chemin",
"MODE": "Mode",
"SOURCE": "Source",
"OUTSIDE_ALLOWED_RANGE": "En dehors de la portée autorisée",
"IN_PATCH": "dans le patch",
"INSTALLING_PATCH": "Installation du patch",
"PATCH_TITLE": "Patch",
"PRE-PATCH_TITLE": "Pre-patch",
"PATCHING": "Patch de",
"FORBIDDEN_TRACK_ATTRIBUTE": "Attribut de course interdit",
"FORBIDDEN_ARENA_ATTRIBUTE": "Attribut d'arène interdit",
"EXTRACTING_AUTOADD_FILES": "Extraction des fichiers autoadd...",
"EXTRACTING_ORIGINAL_TRACKS": "Extraction des courses originelles...",
"INSTALLING_MYSTUFF": "Installation de MyStuff",
"INSTALLING_ALL_MYSTUFF_PATCHS": "Installation de tous les patchs MyStuff",
"PREPARING": "Preparation",
"SPECIAL_FILE": "fichier spécial",
"REPACKING": "Ré-archivage",
"ALL_ARCHIVES": "toutes les archives",
"INSTALLING_ALL": "Installation de tous",
"CONVERTING_GAME_TO": "Conversion du jeu en",
"DELETING_EXTRACTED_GAME": "Suppression du jeu extrait...",
"NOT_MKW_GAME": "N'est pas un jeu Mario Kart Wii",
"GAME_ALREADY_MODDED": "Ce jeu est déjà moddée",
"COPYING_GAME": "Copie du jeu...",
"EXTRACTING": "Extraction",
"ESTIMATED_TIME_REMAINING": "temps restant estimé",
"CHANGING_GAME_METADATA": "Changement des métadonnées du jeu...",
"EXTRACTION": "Extraction",
"MYSTUFF": "MyStuff",
"PREPARING_FILES": "Preparation des fichiers",
"PRE-PATCHING": "Pre-Patching",
"CONVERTING_TO_GAME_FILE": "Conversion en fichier de jeu",
"CANNOT_FIND_COLOR": "Impossible de trouver la couleur",
"NORMALIZING_TRACKS": "Normalisation des courses",
"INVALID_MACRO": "Macro invalide",
"INVALID_AST_TYPE": "Type d'Ast invalide",
"MAGIC_ATTRIBUTE_ARE_FORBIDDEN": "Les attributs magique sont interdit",
"CANNOT_SET_ATTRIBUTE": "Impossible de changer la valeur de l'attribut",
"CANNOT_SET_ENVIRONMENT": "Impossible de changer la valeur de l'environnement",
"CANNOT_SET_ARGUMENT": "Impossible de changer la valeur de l'argument",
"CALLING_FUNCTION_NOT_ALLOWED": "Appeler cette fonction n'est pas autorisé",
"FORBIDDEN_SYNTAX": "Syntax interdite",
"MAGIC_METHOD_FORBIDDEN": "Les méthodes magique ne sont pas autorisé",
"CANNOT_GET_ERROR_MESSAGE": "Impossible d'obtenir le message d'erreur",
"RAISED": "à levé",
"CANNOT_FIND_TOOL": "Impossible de trouver l'outil",
"IN_TOOLS_DIRECTORY": "dans le dossier des outils.",
"CANNOT_EXTRACT_A_DIRECTORY": "Impossible d'extraire un dossier",
"CANNOT_FIND_SLOT": "Impossible de trouver le slot",
"FORBIDDEN_TRACKGROUP_ATTRIBUTE": "Attribut de groupe de course interdit",
"SAFE_EVAL_ERROR": "Erreur lors d'un safe eval",
"TEMPLATE_USED": "Modèle utilisé",
"MORE_IN_ERROR_LOG": "Plus d'information dans le fichier error.log",
"COPY_FUNCTION_FORBIDDEN": "Impossible de copier une fonction",
"GET_METHOD_FORBIDDEN": "Impossible d'utiliser getattr sur une méthode",
"CAN_ONLY_CALL_METHOD_OF_CONSTANT": "Vous ne pouvez appeler que des méthodes sur des constantes",
"CAN_ONLY_CALL_FUNCTION_IN_ENV": "Vous ne pouvez appeler que des fonctions dans l'environnement",
"ENABLE_DEVELOPER_MODE": "Activer le mode développeur",
"TESTING_MOD_SETTINGS": "Paramètre de test",
"SETTINGS_FILE": "Fichier de paramètres",
"EXPORT_SETTINGS": "Exporter les paramètres",
"IMPORT_SETTINGS": "Importer les paramètres",
"PREPARING_RIIVOLUTION": "Préparation pour Riivolution",
"CONVERTING_TO_RIIVOLUTION": "Conversion en patch Riivolution",
"PATCHS": "patchs",
"PRE-PATCHS": "pre-patchs",
"CALCULATING_HASH_FOR": "Calcule du hash pour",
"WARNING_NOT_ROOT": "Cette application nécessite les permissions root. Vous devriez lancer l'application avec 'sudo'. Continuer quand même ?",
"WARNING_INSTALLER_PERMISSION": "Cette application nécessite les permissions d'écriture et d'exécution sur ses propres fichiers. Vous pouvez utiliser 'sudo chmod 777 -R <installer-path>' pour corriger cela. Continuer quand même ?",
"WARNING_EMPTY_CACHE": "En vidant le cache, vous allez gagner %.2fGo, mais réinstaller un mod sera beaucoup plus long. Continuer ?",
"EMPTY_CACHE": "Vider le cache"
}
"MENU_LANGUAGE_SELECTION": "Langue",
"MENU_ADVANCED": "Avancée",
"MENU_ADVANCED_MYSTUFF": "Paramètres MyStuff",
"MENU_ADVANCED_THREADS": "Utilisation des threads",
"MENU_ADVANCED_THREADS_SELECTION": "Utiliser %i threads",
"MENU_ADVANCED_EMPTY_CACHE": "Vider le cache",
"MENU_ADVANCED_DEVELOPER_MODE": "Activer les paramètres développeur",
"MENU_HELP": "Aide",
"PART_EXTRACTION": "Extraction",
"PART_PRE_RIIVOLUTION": "Pre-Riivolution",
"PART_MYSTUFF": "MyStuff",
"PART_PREPARING_FILES": "Preparation des fichiers",
"PART_PREPATCH": "Pre-Patch",
"PART_LECODE": "LE-CODE",
"PART_PATCH": "Patch",
"PART_RIIVOLUTION": "Riivolution",
"PART_CONVERSION": "Conversion",
"TEXT_SOURCE_GAME": "Jeu Source",
"TEXT_GAME_DESTINATION": "Destination du Jeu",
"TEXT_INSTALL": "Installer",
"TEXT_SELECT_SOURCE_GAME": "Sélectionner un jeu source",
"TEXT_SELECT_GAME_DESTINATION": "Sélectionner la destination du jeu",
"TEXT_WII_GAMES": "Jeux Wii",
"TEXT_INSTALLATION_COMPLETED": "Installation Complétée",
"TEXT_INSTALLATION_FINISHED_SUCCESSFULLY": "L'installation s'est terminée avec succès !",
"TEXT_MESSAGE_FROM_AUTHOR": "Message de l'auteur",
"TEXT_MOD_GLOBAL_SETTINGS": "Paramètres globaux",
"TEXT_MOD_SPECIFIC_SETTINGS": "Paramètres spécifique",
"TEXT_MOD_TEST_SETTINGS": "Paramètres de test",
"TEXT_DISABLED": "Désactivé",
"TEXT_ENABLED": "Activé",
"TEXT_IMPORT_SETTINGS": "Importer des paramètres",
"TEXT_EXPORT_SETTINGS": "Exporter des paramètres",
"TEXT_SETTINGS_FILE": "Fichier paramètres",
"TEXT_NEW_PROFILE": "Nouveau profil",
"TEXT_DELETE_PROFILE": "Supprimer le profil",
"TEXT_ADD_MYSTUFF": "Ajouter MyStuff",
"TEXT_REMOVE_MYSTUFF": "Retirer MyStuff",
"TEXT_SELECT_MYSTUFF": "Sélectionner un MyStuff",
"TEXT_COPYING_GAME": "Copie du jeu",
"TEXT_EXTRACTING_GAME": "Extraction du jeu - %i%% (temps restant estimé : %s)",
"TEXT_CHANGING_GAME_METADATA": "Changement des métadonnées du jeu",
"TEXT_EXTRACTING_AUTOADD": "Extraction des fichiers autoadd",
"TEXT_EXTRACTING_ORIGINAL_TRACKS": "Extraction des courses originales \"%s\"",
"TEXT_INSTALLING_MYSTUFF": "Installation des MyStuff \"%s\"",
"TEXT_PREPARING_SPECIAL_FILE": "Preparion du fichier spécial \"%s\"",
"TEXT_PREPARING_MAIN_DOL": "Preparation de main.dol",
"TEXT_REPACKING_ARCHIVE": "Archivage de \"%s\"",
"TEXT_PATCHING_LECODE": "Patch de LECODE.bin",
"TEXT_CONVERT_GAME_TO": "Conversion du jeu en %s",
"TEXT_DELETING_EXTRACTED_GAME": "Suppression du jeu extrait",
"TEXT_CONVERTING_TO_RIIVOLUTION": "Conversion en Riivolution",
"TEXT_CALCULATING_HASH": "Calcul du hash pour \"%s\"",
"TEXT_NORMALIZING_TRACKS": "Normalisation des courses :\n%s",
"TEXT_PATCHING": "Patch de \"%s\"",
"WARNING": "Attention",
"WARNING_EMPTY_CACHE": "En vidant le cache, vous allez gagner %.2fGB, mais reinstaller un mod prendra beaucoup plus de temps. Continuer ?",
"WARNING_DESTINATION_NOT_WRITABLE": "Le dossier de destination du jeu ne peut pas être modifié. Continuer quand même ?",
"WARNING_LOW_SPACE_CONTINUE": "Espace faible sur le disque %s (%.2fGB). Continuer quand même ?",
"WARNING_NOT_ROOT": "Permissions administrateur requise. Vous devriez lancer l'application avec 'sudo'. Continuer quand même ?",
"WARNING_INSTALLER_PERMISSION": "Les permissions d'écriture et d'exécution sont requise. Vous pouvez utiliser 'sudo chmod 777 -R <chemin-de-l-installer>' pour corriger cela. Continuer quand même ?",
"ERROR": "Erreur",
"ERROR_SEE_LOGS": "Voir le fichier error.log pour plus d'information",
"ERROR_INVALID_SOURCE_GAME": "Jeu source invalide : \"%s\"",
"ERROR_INVALID_GAME_DESTINATION": "Destination de jeu invalide : \"%s\"",
"ERROR_MYSTUFF_PROFILE_ALREADY_EXIST": "Le profil MyStuff \"%s\" existe déjà !",
"ERROR_MYSTUFF_PROFILE_FORBIDDEN_NAME": "Le nom de profil MyStuff \"%s\" est interdit !",
"ERROR_NOT_MKW_GAME": "Ce jeu n'est pas un jeu Mario Kart Wii : \"%s\"",
"ERROR_GAME_ALREADY_MODDED": "Ce jeu est déjà moddé : \"%s\"",
"ERROR_PATH_OUTSIDE_RANGE": "Le chemin \"%s\" est en dehors de la portée autorisée : \"%s\"",
"ERROR_PREVIEW_WINDOW_NOT_FOUND": "Type de fenêtre de prévisualisation \"%s\" introuvable",
"ERROR_CANNOT_FIND_COLOR": "Impossible de trouver la couleur : \"%s\"",
"ERROR_CANNOT_FIND_SLOT": "Impossible de trouver le slot : \"%s\"",
"ERROR_MOD_SETTINGS_NOT_FOUND": "Type de paramètre de mod \"%s\" introuvable",
"ERROR_FORBIDDEN_TRACK_ATTRIBUTE": "Attribut de course interdit : \"%s\"",
"ERROR_FORBIDDEN_ARENA_ATTRIBUTE": "Attribut d'arène interdit : \"%s\"",
"ERROR_FORBIDDEN_TRACKGROUP_ATTRIBUTE": "Attribut de groupe de course interdit : \"%s\"",
"ERROR_SAFEEVAL": "Erreur de safe eval (modèle utilisé : \"%s\")",
"ERROR_INVALID_MACRO": "Macro invalide : \"%s\"",
"ERROR_INVALID_AST_TYPE": "Type d'AST invalide : \"%s\"",
"ERROR_FORBIDDEN_MAGIC_ATTRIBUTE": "Les attributs magiques sont interdit : \"%s\"",
"ERROR_FORBIDDEN_MAGIC_METHOD": "Les méthodes magiques sont interdites : \"%s\"",
"ERROR_GETTING_METHOD_FORBIDDEN": "Récupérer une méthode est interdit : \"%s\"",
"ERROR_CANNOT_SET_ATTRIBUTE": "Changer un attribut est interdit : \"%s\"",
"ERROR_CANNOT_SET_ENVIRONMENT": "Changer une variable d'environnement est interdit : \"%s\"",
"ERROR_CANNOT_SET_ARGUMENT": "Changer une variable d'argument est interdit : \"%s\"",
"ERROR_CAN_ONLY_CALL_CONSTANT_METHOD": "Seulement appeler la méthode d'une constante est autorisé",
"ERROR_CAN_ONLY_CALL_ENV_FUNCTION": "Seulement appeler une fonction de l'environnement est autorisé",
"ERROR_FORBIDDEN_SYNTAX": "Syntaxe interdite : \"%s\"",
"ERROR_FUNCTION_COPY_FORBIDDEN": "Copier une fonction est interdit",
"ERROR_CANNOT_GET_ERROR_MESSAGE": "Impossible de récupérer le message d'erreur",
"ERROR_WT": "%s à généré l'erreur %i :\n%s",
"ERROR_CANNOT_FIND_TOOL": "Impossible de trouver l'outil \"%s\" dans le dossier tools",
"ERROR_CANNOT_EXTRACT_DIRECTORY": "Un dossier ne peut pas être extrait",
"ERROR_PATCH_MODE_NOT_IMPLEMENTED": "Le mode de Patch \"%s\" n'est pas implémenté (du patch : \"%s\")",
"ERROR_SOURCE_NOT_IMPLEMENTED": "La source \"%s\" n'est pas implémenté (du patch : \"%s\")",
"ERROR_OPERATION_NOT_IMPLEMENTED": "L'opération \"%s\" n'est pas implémenté",
"ERROR_BMG_LAYER_MODE_NOT_IMPLEMENTED": "Le mode de couche bmg \"%s\" n'est pas implémenté",
"ERROR_IMAGE_LAYER_TYPE_NOT_IMPLEMENTED": "Le type de couche d'image \"%s\" n'est pas implémenté"
}
}

View file

@ -29,8 +29,8 @@ def better_gui_error(func: Callable) -> Callable:
exc_split = exc.splitlines()
if len(exc_split) > 10:
# if the traceback is too long, only keep the 5 first and 5 last lines of the traceback
exc_split = exc_split[:5] + ["..."] + exc_split[-5:] + ["", "", _("MORE_IN_ERROR_LOG")]
exc_split = exc_split[:5] + ["..."] + exc_split[-5:] + ["", "", _("ERROR_SEE_LOGS")]
messagebox.showerror(_("Error"), "\n".join(exc_split))
messagebox.showerror(_("ERROR"), "\n".join(exc_split))
return wrapper

View file

@ -23,16 +23,6 @@ import os
from source.mkw.collection.Extension import Extension
class SourceGameError(Exception):
def __init__(self, path: Path | str):
super().__init__(_(f"ERROR_INVALID_SOURCE_GAME", " : ", path))
class DestinationGameError(Exception):
def __init__(self, path: Path | str):
super().__init__(_("ERROR_INVALID_DESTINATION_GAME", " : ", path))
class InstallerState(enum.Enum):
IDLE = 0
INSTALLING = 1
@ -50,7 +40,7 @@ class Window(tkinter.Tk):
self.source_path = tkinter.StringVar()
self.destination_path = tkinter.StringVar()
self.title(_("INSTALLER_TITLE"))
self.title(_("TITLE_INSTALL"))
self.resizable(False, False)
self.icon = tkinter.PhotoImage(file="./assets/icon.png")
@ -136,7 +126,7 @@ class Menu(tkinter.Menu):
super().__init__(master, tearoff=False)
self.root = master.root
master.add_cascade(label=_("LANGUAGE_SELECTION"), menu=self)
master.add_cascade(label=_("MENU_LANGUAGE_SELECTION"), menu=self)
self.lang_variable = tkinter.StringVar(value=self.root.options.language.get())
@ -155,19 +145,19 @@ class Menu(tkinter.Menu):
super().__init__(master, tearoff=False)
self.root = master.root
master.add_cascade(label=_("ADVANCED_CONFIGURATION"), menu=self)
master.add_cascade(label=_("MENU_ADVANCED"), menu=self)
self.add_command(
label=_("OPEN_MYSTUFF_SETTINGS"),
label=_("MENU_ADVANCED_MYSTUFF"),
command=lambda: mystuff.Window(self.root.mod_config, self.root.options)
)
self.threads_used = self.ThreadsUsed(self)
self.add_command(label=_("EMPTY_CACHE"), command=self.empty_cache)
self.add_command(label=_("MENU_ADVANCED_EMPTY_CACHE"), command=self.empty_cache)
self.add_separator()
self.variable_developer_mode = tkinter.BooleanVar(value=self.root.options.developer_mode.get())
self.add_checkbutton(
label=_("ENABLE_DEVELOPER_MODE"),
label=_("MENU_ADVANCED_DEVELOPER_MODE"),
variable=self.variable_developer_mode,
command=lambda: self.root.options.developer_mode.set(self.variable_developer_mode.get())
)
@ -184,14 +174,14 @@ class Menu(tkinter.Menu):
def __init__(self, master: tkinter.Menu):
super().__init__(master, tearoff=False)
self.root = master.root
master.add_cascade(label=_("THREADS_USAGE"), menu=self)
master.add_cascade(label=_("MENU_ADVANCED_THREADS"), menu=self)
self.variable = tkinter.IntVar(value=self.root.options.threads.get())
for i in [1, 2, 4, 8, 12, 16]:
self.add_radiobutton(
label=_("USE", f" {i} ", "THREADS"),
label=_("MENU_ADVANCED_THREADS_SELECTION") % i,
value=i,
variable=self.variable,
command=(lambda amount: (lambda: self.root.options.threads.set(amount)))(i),
@ -203,12 +193,12 @@ class Menu(tkinter.Menu):
super().__init__(master, tearoff=False)
self.root = master.root
master.add_cascade(label=_("HELP"), menu=self)
master.add_cascade(label=_("MENU_HELP"), menu=self)
self.menu_id = self.master.index(tkinter.END)
self.add_command(label=_("DISCORD"), command=lambda: webbrowser.open(discord_url))
self.add_command(label=_("GITHUB WIKI"), command=lambda: webbrowser.open(github_wiki_url))
self.add_command(label=_("READTHEDOCS"), command=lambda: webbrowser.open(readthedocs_url))
self.add_command(label="Discord", command=lambda: webbrowser.open(discord_url))
self.add_command(label="GitHub", command=lambda: webbrowser.open(github_wiki_url))
self.add_command(label="ReadTheDocs", command=lambda: webbrowser.open(readthedocs_url))
def set_installation_state(self, state: InstallerState) -> bool:
"""
@ -237,7 +227,7 @@ class Menu(tkinter.Menu):
# Select game frame
class SourceGame(ttk.LabelFrame):
def __init__(self, master: tkinter.Tk):
super().__init__(master, text=_("ORIGINAL_GAME_FILE"))
super().__init__(master, text=_("TEXT_SOURCE_GAME"))
self.root = master.root
self.columnconfigure(1, weight=1)
@ -254,8 +244,8 @@ class SourceGame(ttk.LabelFrame):
:return:
"""
raw_path = tkinter.filedialog.askopenfilename(
title=_("SELECT_SOURCE_GAME"),
filetypes=[(_("WII GAMES"), "*.iso *.ciso *.wbfs *.dol")],
title=_("TEXT_SELECT_SOURCE_GAME"),
filetypes=[(_("TEXT_WII_GAMES"), "*.iso *.ciso *.wbfs *.dol")],
)
# if the user didn't select any file, return None
@ -298,7 +288,7 @@ class SourceGame(ttk.LabelFrame):
# Select game destination frame
class DestinationGame(ttk.LabelFrame):
def __init__(self, master: tkinter.Tk):
super().__init__(master, text=_("GAME_DIRECTORY_DESTINATION"))
super().__init__(master, text=_("TEXT_GAME_DESTINATION"))
self.root = master.root
self.columnconfigure(1, weight=1)
@ -320,7 +310,7 @@ class DestinationGame(ttk.LabelFrame):
:return:
"""
raw_path = tkinter.filedialog.askdirectory(
title=_("SELECT_DESTINATION_GAME"),
title=_("TEXT_SELECT_GAME_DESTINATION"),
)
# if the user didn't select any directory, return None
@ -333,7 +323,7 @@ class DestinationGame(ttk.LabelFrame):
def set_path(self, path: Path):
if not os.access(path, os.W_OK):
messagebox.showwarning(_("WARNING"), _("WARNING_DESTINATION_GAME_NOT_WRITABLE"))
messagebox.showwarning(_("WARNING"), _("WARNING_DESTINATION_NOT_WRITABLE"))
self.entry.delete(0, tkinter.END)
self.entry.insert(0, str(path.absolute()))
@ -355,7 +345,7 @@ class DestinationGame(ttk.LabelFrame):
# Install button
class ButtonInstall(ttk.Button):
def __init__(self, master: tkinter.Tk):
super().__init__(master, text=_("INSTALL"), command=self.install)
super().__init__(master, text=_("TEXT_INSTALL"), command=self.install)
self.root = master.root
@threaded
@ -364,29 +354,35 @@ class ButtonInstall(ttk.Button):
try:
self.root.set_state(InstallerState.INSTALLING)
# check if the user entered a source path
# check if the user entered a source path. If the string is ".", then the user didn't input any path
source_path = Path(self.root.source_path.get())
if not source_path.exists(): raise SourceGameError(source_path)
if str(source_path) == ".":
messagebox.showerror(_("ERROR"), _("ERROR_INVALID_SOURCE_GAME"))
if not source_path.exists() or str(source_path) == ".":
messagebox.showerror(_("ERROR"), _("ERROR_INVALID_SOURCE_GAME") % source_path)
return
# check if the user entered a destination path
# check if the user entered a destination path. If the string is ".", then the user didn't input any path
destination_path = Path(self.root.destination_path.get())
if not destination_path.exists(): raise DestinationGameError(destination_path)
if str(destination_path) == ".":
messagebox.showerror(_("ERROR"), _("ERROR_INVALID_DESTINATION_GAME"))
if not destination_path.exists() or str(destination_path) == ".":
messagebox.showerror(_("ERROR"), _("ERROR_INVALID_GAME_DESTINATION") % source_path)
return
available_space_local = shutil.disk_usage(".").free
available_space_destination = shutil.disk_usage(destination_path).free
# if there is no more space on the installer drive, show a warning
if shutil.disk_usage(".").free < minimum_space_available:
if not messagebox.askokcancel(_("WARNING"), _("WARNING_LOW_SPACE_CONTINUE")):
if available_space_local < minimum_space_available:
if not messagebox.askokcancel(
_("WARNING"),
_("WARNING_LOW_SPACE_CONTINUE") % (Path(".").resolve().drive, available_space_local/Go)
):
return
# if there is no more space on the destination drive, show a warning
elif shutil.disk_usage(destination_path).free < minimum_space_available:
if not messagebox.askokcancel(_("WARNING"), _("WARNING_LOW_SPACE_CONTINUE")):
return
elif available_space_destination < minimum_space_available:
if not messagebox.askokcancel(
_("WARNING"),
_("WARNING_LOW_SPACE_CONTINUE") % (destination_path.resolve().drive, available_space_destination/Go)
): return
if system == "lin64": # if linux
if os.getuid() != 0: # if the user is not root
@ -417,9 +413,9 @@ class ButtonInstall(ttk.Button):
)
messagebox.showinfo(
_("INSTALLATION_COMPLETED"),
f"{_('INSTALLATION_FINISHED_WITH_SUCCESS')}" + (
f"\n{_('MESSAGE_FROM_MOD_AUTHOR')} :\n\n{message}" if message != "" else ""
_("TEXT_INSTALLATION_COMPLETED"),
f"{_('TEXT_INSTALLATION_FINISHED_SUCCESSFULLY')}" + (
f"\n{_('TEXT_MESSAGE_FROM_AUTHOR')} :\n\n{message}" if message != "" else ""
)
)

View file

@ -21,6 +21,7 @@ class Window(tkinter.Toplevel):
self.resizable(False, False)
self.grab_set()
self.title(_("TITLE_MOD_SETTINGS"))
self.rowconfigure(1, weight=1)
self.columnconfigure(1, weight=1)
@ -32,19 +33,19 @@ class Window(tkinter.Toplevel):
self.frame_global_settings = FrameSettings(
self.panel_window,
_("GLOBAL_MOD_SETTINGS"),
_("TEXT_MOD_GLOBAL_SETTINGS"),
self.mod_config.global_settings
)
self.frame_specific_settings = FrameSettings(
self.panel_window,
_("SPECIFIC_MOD_SETTINGS"),
_("TEXT_MOD_SPECIFIC_SETTINGS"),
self.mod_config.specific_settings
)
if self.options.developer_mode.get():
self.frame_testing_settings = FrameTesting(
self.panel_window,
_("TESTING_MOD_SETTINGS")
_("TEXT_MOD_TEST_SETTINGS")
)
self.frame_action = FrameAction(self)
@ -143,10 +144,10 @@ class FrameAction(ttk.Frame):
super().__init__(master)
self.root = master.root
self.button_import_settings = ttk.Button(self, text=_("IMPORT_SETTINGS"), width=20,
self.button_import_settings = ttk.Button(self, text=_("TEXT_IMPORT_SETTINGS"), width=20,
command=self.import_settings)
self.button_import_settings.grid(row=1, column=1)
self.button_export_settings = ttk.Button(self, text=_("EXPORT_SETTINGS"), width=20,
self.button_export_settings = ttk.Button(self, text=_("TEXT_EXPORT_SETTINGS"), width=20,
command=self.export_settings)
self.button_export_settings.grid(row=1, column=2)
@ -156,8 +157,8 @@ class FrameAction(ttk.Frame):
"""
path = filedialog.askopenfilename(
title=_("IMPORT_SETTINGS"),
filetypes=[(_("SETTINGS_FILE"), f"*{SETTINGS_FILE_EXTENSION}")]
title=_("TEXT_IMPORT_SETTINGS"),
filetypes=[(_("TEXT_SETTINGS_FILE"), f"*{SETTINGS_FILE_EXTENSION}")]
)
# si le fichier n'a pas été choisi, ignore
@ -178,8 +179,8 @@ class FrameAction(ttk.Frame):
"""
path = filedialog.asksaveasfilename(
title=_("EXPORT_SETTINGS"),
filetypes=[(_("SETTINGS_FILE"), f"*{SETTINGS_FILE_EXTENSION}")]
title=_("TEXT_EXPORT_SETTINGS"),
filetypes=[(_("TEXT_SETTINGS_FILE"), f"*{SETTINGS_FILE_EXTENSION}")]
)
# si le fichier n'a pas été choisi, ignore

View file

@ -24,11 +24,11 @@ class Window(tkinter.Toplevel):
self.mod_config = mod_config
self.options = options
self.title(_("CONFIGURE_MYSTUFF_PATCH"))
self.title(_("TITLE_MYSTUFF_SETTINGS"))
self.resizable(False, False)
self.grab_set() # the others window will be disabled, keeping only this one activated
self.disabled_text: str = _("<", "DISABLED", ">")
self.disabled_text: str = f"<{_('TEXT_DISABLED')}>"
self.frame_profile = ttk.Frame(self)
self.frame_profile.grid(row=1, column=1, sticky="NEWS")
@ -40,14 +40,14 @@ class Window(tkinter.Toplevel):
self.button_new_profile = ttk.Button(
self.frame_profile,
text=_("NEW_PROFILE"),
text=_("TEXT_NEW_PROFILE"),
command=self.new_profile
)
self.button_new_profile.grid(row=1, column=2, sticky="NEWS")
self.button_delete_profile = ttk.Button(
self.frame_profile,
text=_("DELETE_PROFILE"),
text=_("TEXT_DELETE_PROFILE"),
command=self.delete_profile
)
self.button_delete_profile.grid(row=1, column=3, sticky="NEWS")
@ -76,14 +76,14 @@ class Window(tkinter.Toplevel):
self.button_add_mystuff_path = ttk.Button(
self.frame_mystuff_paths_action,
text=_("ADD_MYSTUFF"),
text=_("TEXT_ADD_MYSTUFF"),
command=self.add_mystuff_path
)
self.button_add_mystuff_path.grid(row=1, column=1)
self.button_remove_mystuff_path = ttk.Button(
self.frame_mystuff_paths_action,
text=_("REMOVE_MYSTUFF"),
text=_("TEXT_REMOVE_MYSTUFF"),
command=self.remove_mystuff_path
)
self.button_remove_mystuff_path.grid(row=1, column=2)
@ -138,12 +138,12 @@ class Window(tkinter.Toplevel):
profile_name: str = self.combobox_profile.get()
if profile_name in mystuff_packs:
messagebox.showerror(_("ERROR"), _("MYSTUFF_PROFILE_ALREADY_EXIST"))
messagebox.showerror(_("ERROR"), _("ERROR_MYSTUFF_PROFILE_ALREADY_EXIST") % profile_name)
return
for banned_char in "<>":
if banned_char in profile_name:
messagebox.showerror(_("ERROR"), _("MYSTUFF_PROFILE_FORBIDDEN_NAME"))
messagebox.showerror(_("ERROR"), _("ERROR_MYSTUFF_PROFILE_FORBIDDEN_NAME") % profile_name)
return
mystuff_packs[profile_name] = {"paths": []}
@ -167,7 +167,7 @@ class Window(tkinter.Toplevel):
Add a new path to the currently selected MyStuff profile
"""
if (mystuff_path := filedialog.askdirectory(title=_("SELECT_MYSTUFF"), mustexist=True)) is None: return
if (mystuff_path := filedialog.askdirectory(title=_("TEXT_SELECT_MYSTUFF"), mustexist=True)) == "": return
mystuff_path = Path(mystuff_path)
mystuff_packs = self.root.options.mystuff_packs.get()

View file

@ -9,7 +9,7 @@ if TYPE_CHECKING:
class InvalidPreviewWindowName(Exception):
def __init__(self, name: str):
super().__init__(_("TYPE_PREVIEW_WINDOW", " '", name, "' ", "NOT_FOUND"))
super().__init__(_("ERROR_PREVIEW_WINDOW_NOT_FOUND") % name)
class AbstractPreviewWindow(tkinter.Toplevel, ABC):

View file

@ -5,6 +5,7 @@ from pathlib import Path
from typing import Generator, IO, TYPE_CHECKING
from source import file_block_size
from source.mkw import PathOutsideAllowedRange
from source.mkw.ModConfig import ModConfig
from source.mkw.Patch.Patch import Patch
from source.mkw.collection.Extension import Extension
@ -21,11 +22,6 @@ if TYPE_CHECKING:
RIIVOLUTION_FOLDER_NAME: str = "riivolution"
class PathOutsideMod(Exception):
def __init__(self, forbidden_path: Path, allowed_range: Path):
super().__init__(_("PATH", ' "', forbidden_path, '" ', "OUTSIDE_ALLOWED_RANGE", ' "', allowed_range, '" '))
class ExtractedGame:
"""
Class that represents an extracted game
@ -41,7 +37,7 @@ class ExtractedGame:
Extract all the autoadd files from the game to destination_path
:param destination_path: directory where the autoadd files will be extracted
"""
yield Progress(description=_("EXTRACTING_AUTOADD_FILES"), determinate=False)
yield Progress(description=_("TEXT_EXTRACTING_AUTOADD"), determinate=False)
szs.autoadd(self.path / "files/Race/Course/", destination_path)
def extract_original_tracks(self, destination_path: "Path | str") -> Generator[Progress, None, None]:
@ -54,14 +50,13 @@ class ExtractedGame:
original_tracks: list[Path] = list((self.path / "files/Race/Course/").glob("*.szs"))
yield Progress(
description=_("EXTRACTING_ORIGINAL_TRACKS"),
determinate=True,
max_step=len(original_tracks),
set_step=0,
)
for track_file in original_tracks:
yield Progress(description=_("EXTRACTING_ORIGINAL_TRACKS", " (", track_file.name, ") ..."), step=1)
yield Progress(description=_("TEXT_EXTRACTING_ORIGINAL_TRACKS") % track_file.name, step=1)
if not (destination_path / track_file.name).exists(): track_file.rename(destination_path / track_file.name)
else: track_file.unlink()
@ -73,7 +68,7 @@ class ExtractedGame:
:mystuff_path: path to the MyStuff directory
:return:
"""
yield Progress(description=_("INSTALLING_MYSTUFF", ' "', mystuff_path, '" ...'))
yield Progress(description=_("TEXT_INSTALLING_MYSTUFF") % mystuff_path)
mystuff_path = Path(mystuff_path)
mystuff_rootfiles: dict[str, Path] = {}
@ -90,12 +85,7 @@ class ExtractedGame:
Install multiple mystuff patch
:param mystuff_paths: paths to all the mystuff patch
"""
yield Progress(
description=_("INSTALLING_ALL_MYSTUFF_PATCHS"),
determinate=True,
max_step=len(mystuff_paths),
set_step=0
)
yield Progress(determinate=True, max_step=len(mystuff_paths), set_step=0)
for mystuff_path in mystuff_paths:
yield Progress(step=1)
@ -106,7 +96,7 @@ class ExtractedGame:
Prepare special files for the patch
:return: the special files dict
"""
yield Progress(description=_("PREPARING", " ct_icons ", "SPECIAL_FILE", "..."), determinate=False)
yield Progress(description=_("TEXT_PREPARING_SPECIAL_FILE") % "ct_icons", determinate=False)
ct_icons = BytesIO()
mod_config.get_full_cticon().save(ct_icons, format="PNG")
ct_icons.seek(0)
@ -116,7 +106,7 @@ class ExtractedGame:
"""
Prepare main.dol and StaticR.rel files (clean them and add lecode)
"""
yield Progress(description="Preparing main.dol...", determinate=False)
yield Progress(description=_("TEXT_PREPARING_MAIN_DOL"), determinate=False)
StrPath(self.path / "sys/main.dol").patch(clean_dol=True, add_lecode=True)
def recreate_all_szs(self) -> Generator[Progress, None, None]:
@ -125,7 +115,6 @@ class ExtractedGame:
"""
all_extracted_szs: list[Path] = list(filter(lambda path: path.is_dir(), self.path.rglob("*.d")))
yield Progress(
description=_("REPACKING", " ", "ALL_ARCHIVES"),
determinate=True,
max_step=len(all_extracted_szs),
set_step=0,
@ -134,7 +123,7 @@ class ExtractedGame:
for extracted_szs in all_extracted_szs:
# for every directory that end with a .d in the extracted game, recreate the szs
yield Progress(
description=_("REPACKING", ' "', extracted_szs.relative_to(self.path), '"'),
description=_("TEXT_REPACKING_ARCHIVE") % extracted_szs.relative_to(self.path),
step=1
)
@ -150,7 +139,7 @@ class ExtractedGame:
:param cache_directory: Path to the cache
:param mod_config: mod configuration
"""
yield Progress(description=_("PATCHING", " LECODE.bin"))
yield Progress(description=_("TEXT_PATCHING_LECODE"))
cache_directory = Path(cache_directory)
cttracks_directory = Path(cttracks_directory)
ogtracks_directory = Path(ogtracks_directory)
@ -161,7 +150,7 @@ class ExtractedGame:
lpar_dir: Path = mod_config.path.parent / "_LPAR/"
lpar: Path = lpar_dir / mod_config.multiple_safe_eval(mod_config.lpar_template)()
if not lpar.is_relative_to(lpar_dir): raise PathOutsideMod(lpar, lpar_dir)
if not lpar.is_relative_to(lpar_dir): raise PathOutsideAllowedRange(lpar, lpar_dir)
for lecode_file in (self.path / "files/rel/").glob("lecode-*.bin"):
lec.patch(
@ -178,12 +167,16 @@ class ExtractedGame:
for all the subdirectory named by the patch_directory_name, apply the patch
:param mod_config: the mod to install
"""
# yield an empty dict so that if nothing is yielded by the Patch, still is considered a generator
yield Progress()
patch_directories: list[Path] = []
for part_directory in mod_config.get_mod_directory().glob("[!_]*"):
for patch_directory in part_directory.glob(patch_directory_name):
yield from Patch(patch_directory, mod_config, self._special_file).install(self)
patch_directories.append(patch_directory)
yield Progress(determinate=True, max_step=len(patch_directories)+1, set_step=0)
for patch_directory in patch_directories:
yield Progress(step=1)
yield from Patch(patch_directory, mod_config, self._special_file).install(self)
def install_all_prepatch(self, mod_config: ModConfig) -> Generator[Progress, None, None]:
"""
@ -191,7 +184,6 @@ class ExtractedGame:
Used before the lecode patch is applied
:param mod_config: the mod to install
"""
yield Progress(description=_("INSTALLING_ALL", " ", "PRE-PATCHS", "..."), determinate=False)
yield from self._install_all_patch(mod_config, "_PREPATCH/")
def install_all_patch(self, mod_config: ModConfig) -> Generator[Progress, None, None]:
@ -200,7 +192,6 @@ class ExtractedGame:
Used after the lecode patch is applied
:param mod_config: the mod to install
"""
yield Progress(description=_("INSTALLING_ALL", " ", "PATCHS", "..."), determinate=False)
yield from self._install_all_patch(mod_config, "_PATCH/")
def convert_to(self, output_type: Extension) -> Generator[Progress, None, wit.WITPath | None]:
@ -209,7 +200,7 @@ class ExtractedGame:
:param output_type: path to the destination of the game
:output_type: format of the destination game
"""
yield Progress(description=_("CONVERTING_GAME_TO", " ", output_type.name), determinate=False)
yield Progress(description=_("TEXT_CONVERT_GAME_TO") % output_type.name, determinate=False)
if output_type == Extension.FST: return
destination_file = self.path.with_suffix(self.path.suffix + output_type.value)
@ -225,7 +216,7 @@ class ExtractedGame:
destination_file=destination_file,
)
yield Progress(description=_("DELETING_EXTRACTED_GAME"), determinate=False)
yield Progress(description=_("TEXT_DELETING_EXTRACTED_GAME"), determinate=False)
shutil.rmtree(self.path)
return converted_game
@ -241,7 +232,7 @@ class ExtractedGame:
game_files: list[Path] = list(filter(lambda file: file.is_file(), self.path.rglob("*")))
yield Progress(
description=_("CONVERTING_TO_RIIVOLUTION"),
description=_("TEXT_CONVERTING_TO_RIIVOLUTION"),
determinate=True,
max_step=len(game_files),
set_step=0,
@ -316,7 +307,7 @@ class ExtractedGame:
rel_path: str = str(fp.relative_to(self.path))
yield Progress(
description=_(f"CALCULATING_HASH_FOR", ' "', rel_path, '"'),
description=_("TEXT_CALCULATING_HASH") % rel_path,
step=1
)

View file

@ -13,14 +13,12 @@ from source.translation import translate as _
class NotMKWGameError(Exception):
def __init__(self, path: "Path | str"):
path = Path(path)
super().__init__(_("NOT_MKW_GAME", ' : "', path.name, '"'))
super().__init__(_("ERROR_NOT_MKW_GAME") % Path(path).name)
class NotVanillaError(Exception):
def __init__(self, path: "Path | str"):
path = Path(path)
super().__init__(_("GAME_ALREADY_MODDED", ' : "', path.name, '"'))
super().__init__(_("ERROR_GAME_ALREADY_MODDED") % Path(path).name)
class Game:
@ -49,7 +47,7 @@ class Game:
gen = self.wit_path.progress_extract_all(dest)
if self.wit_path.extension == Extension.FST:
for __ in gen: yield Progress(description=_("COPYING_GAME"), determinate=False)
for __ in gen: yield Progress(description=_("TEXT_COPYING_GAME"), determinate=False)
try: next(gen)
except StopIteration as e: return e.value
@ -57,17 +55,20 @@ class Game:
yield Progress(determinate=True, max_step=100)
for gen_data in gen:
percentage: int = gen_data["percentage"]
estimation: str = gen_data["estimation"] if gen_data["estimation"] is not None else "-:--"
yield Progress(
description=_("EXTRACTING", " - ", gen_data["percentage"], "% - (", "ESTIMATED_TIME_REMAINING", ": "
f'{gen_data["estimation"] if gen_data["estimation"] is not None else "-:--"})'),
set_step=gen_data["percentage"],
description=_("TEXT_EXTRACTING_GAME") % (percentage, estimation),
set_step=percentage,
)
try: next(gen)
except StopIteration as e:
return e.value
def edit(self, mod_config: ModConfig) -> Generator[Progress, None, None]:
yield Progress(description=_("CHANGING_GAME_METADATA"), determinate=False)
yield Progress(description=_("TEXT_CHANGING_GAME_METADATA"), determinate=False)
self.wit_path.edit(
name=mod_config.name,
game_id=self.wit_path.id[:4] + mod_config.variant
@ -120,23 +121,23 @@ class Game:
if not self.is_vanilla(): raise NotVanillaError(self.wit_path.path)
# extract the game
yield Progress(title=_("EXTRACTION"), set_part=1)
yield Progress(title=_("PART_EXTRACTION"), set_part=1)
yield from self.extract(extracted_game.path)
# Get the original file hash map for comparaison with the post-patched game
yield Progress(title=_("PREPARING_RIIVOLUTION"), set_part=2)
yield Progress(title=_("PART_PRE_RIIVOLUTION"), set_part=2)
riivolution_original_hash_map: dict[str, str] | None = None
if output_type.is_riivolution():
riivolution_original_hash_map = yield from extracted_game.get_hash_map()
# install mystuff
yield Progress(title=_("MYSTUFF"), set_part=3)
yield Progress(title=_("PART_MYSTUFF"), set_part=3)
mystuff_packs = options.mystuff_packs.get()
mystuff_data = mystuff_packs.get(options.mystuff_pack_selected.get())
if mystuff_data is not None: yield from extracted_game.install_multiple_mystuff(mystuff_data["paths"])
# prepare the cache
yield Progress(title=_("PREPARING_FILES"), set_part=4)
yield Progress(title=_("PART_PREPARING_FILES"), set_part=4)
yield from extracted_game.extract_autoadd(cache_autoadd_directory)
yield from extracted_game.extract_original_tracks(cache_ogtracks_directory)
yield from mod_config.normalize_all_tracks(
@ -149,10 +150,10 @@ class Game:
yield from extracted_game.prepare_special_file(mod_config)
# prepatch the game
yield Progress(title=_("PRE-PATCH_TITLE"), set_part=5)
yield from extracted_game.install_all_prepatch(mod_config) # PROGRESS
yield Progress(title=_("PART_PREPATCH"), set_part=5)
yield from extracted_game.install_all_prepatch(mod_config)
yield Progress(title="LE-CODE", set_part=6)
yield Progress(title=_("PART_LECODE"), set_part=6)
yield from extracted_game.patch_lecode( # PROGRESS
mod_config,
cache_directory,
@ -160,17 +161,17 @@ class Game:
cache_ogtracks_directory,
)
yield Progress(title=_("PATCH_TITLE"), set_part=7)
yield from extracted_game.install_all_patch(mod_config) # PROGRESS
yield Progress(title=_("PART_PATCH"), set_part=7)
yield from extracted_game.install_all_patch(mod_config)
yield from extracted_game.recreate_all_szs()
if output_type.is_riivolution():
yield Progress(title=_("CONVERTING_TO_RIIVOLUTION"), set_part=8)
yield Progress(title=_("PART_RIIVOLUTION"), set_part=8)
yield from extracted_game.convert_to_riivolution(mod_config, riivolution_original_hash_map)
else:
# convert the extracted game into a file
yield Progress(title=_("CONVERTING_TO_GAME_FILE"), set_part=8)
yield Progress(title=_("PART_CONVERSION"), set_part=8)
converted_game: WITPath = yield from extracted_game.convert_to(output_type)
if converted_game is not None: yield from Game(converted_game.path).edit(mod_config)

View file

@ -422,7 +422,7 @@ class ModConfig:
nonlocal normalize_threads
yield Progress(
description=_("NORMALIZING_TRACKS", " :\n" + "\n".join(thread['name'] for thread in normalize_threads))
description=_("TEXT_NORMALIZING_TRACKS") % "\n".join(thread['name'] for thread in normalize_threads)
)
normalize_threads = list(filter(lambda thread: thread["thread"].is_alive(), normalize_threads))
@ -433,7 +433,6 @@ class ModConfig:
)
yield Progress(
description=_("NORMALIZING_TRACKS"),
determinate=True,
max_step=len(all_arenas_tracks)+1,
set_step=0,

View file

@ -9,7 +9,7 @@ if TYPE_CHECKING:
class InvalidSettingsType(Exception):
def __init__(self, settings_type: str):
super().__init__(_("TYPE_MOD_SETTINGS", " '", settings_type, "' ", "NOT_FOUND"))
super().__init__(_("ERROR_MOD_SETTINGS_NOT_FOUND") % settings_type)
class AbstractModSettings(ABC):

View file

@ -17,9 +17,9 @@ class Boolean(AbstractModSettings):
super().tkinter_show(master, checkbox)
variable = self.tkinter_variable(tkinter.BooleanVar)
radiobutton_on = ttk.Radiobutton(master, text=_("DISABLED"), variable=variable, value=False)
radiobutton_on = ttk.Radiobutton(master, text=_("TEXT_DISABLED"), variable=variable, value=False)
radiobutton_on.grid(row=1, column=1, sticky="E")
radiobutton_off = ttk.Radiobutton(master, text=_("ENABLED"), variable=variable, value=True)
radiobutton_off = ttk.Radiobutton(master, text=_("TEXT_ENABLED"), variable=variable, value=True)
radiobutton_off.grid(row=1, column=2, sticky="E")
self.tkinter_bind(master, checkbox)

View file

@ -29,7 +29,7 @@ class Patch:
:param extracted_game: the extracted game
"""
from source.mkw.Patch.PatchDirectory import PatchDirectory
yield Progress(description=_("INSTALLING_PATCH"), determinate=False)
yield Progress()
# take all the files in the root directory, and patch them into the game.
# Patch is not directly applied to the root to avoid custom configuration

View file

@ -1,7 +1,8 @@
from pathlib import Path
from typing import Generator, TYPE_CHECKING
from source.mkw.Patch import PathOutsidePatch, InvalidPatchMode
from source.mkw import PathOutsideAllowedRange
from source.mkw.Patch import InvalidPatchMode
from source.mkw.Patch.PatchObject import PatchObject
from source.progress import Progress
from source.translation import translate as _
@ -28,7 +29,7 @@ class PatchDirectory(PatchObject):
"""
patch a subdirectory of the game with the PatchDirectory
"""
yield Progress(description=_("PATCHING", ' "', game_subpath.relative_to(extracted_game.path), '"'))
yield Progress(description=_("TEXT_PATCHING") % game_subpath.relative_to(extracted_game.path))
# check if the directory should be patched
if not self.is_enabled(extracted_game): return
@ -47,7 +48,7 @@ class PatchDirectory(PatchObject):
for game_subfile in game_subpath.parent.glob(self.configuration["match_regex"]):
# disallow patching files outside of the game
if not game_subfile.relative_to(extracted_game.path):
raise PathOutsidePatch(game_subfile, extracted_game.path)
raise PathOutsideAllowedRange(game_subfile, extracted_game.path)
# patch the game with the subpatch
# if the subfile is a szs archive, replace it with a .d extension

View file

@ -2,7 +2,8 @@ from io import BytesIO
from pathlib import Path
from typing import Generator, IO, TYPE_CHECKING
from source.mkw.Patch import PathOutsidePatch, InvalidPatchMode, InvalidSourceMode
from source.mkw import PathOutsideAllowedRange
from source.mkw.Patch import InvalidPatchMode, InvalidSourceMode
from source.mkw.Patch.PatchOperation import AbstractPatchOperation
from source.mkw.Patch.PatchObject import PatchObject
from source.progress import Progress
@ -82,7 +83,7 @@ class PatchFile(PatchObject):
"""
patch a subfile of the game with the PatchFile
"""
yield Progress(description=translate("PATCHING", ' "', game_subpath.relative_to(extracted_game.path), '"'))
yield Progress(description=translate("TEXT_PATCHING") % game_subpath.relative_to(extracted_game.path))
# translate is not renamed "_" here because it is used to drop useless value in unpacking
# check if the file should be patched
@ -119,9 +120,9 @@ class PatchFile(PatchObject):
for game_subfile in game_subpath.parent.glob(self.configuration["match_regex"]):
# disallow patching files outside of the game
if not game_subfile.relative_to(extracted_game.path):
raise PathOutsidePatch(game_subfile, extracted_game.path)
raise PathOutsideAllowedRange(game_subfile, extracted_game.path)
yield Progress(description=translate("PATCHING", " ", game_subfile))
yield Progress(description=translate("TEXT_PATCHING") % game_subfile)
# if the source is the game, then recalculate the content for every game subfile
if self.configuration["source"] == "game":

View file

@ -11,7 +11,7 @@ if TYPE_CHECKING:
class InvalidBmgLayerMode(Exception):
def __init__(self, layer_mode: str):
super().__init__(_("BMG_LAYER_MODE", ' "', layer_mode, '" ', "IS_NOT_IMPLEMENTED"))
super().__init__(_("ERROR_BMG_LAYER_MODE_NOT_IMPLEMENTED") % layer_mode)
class AbstractLayer(ABC):

View file

@ -2,8 +2,8 @@ from typing import TYPE_CHECKING
from PIL import Image
from source.mkw import PathOutsideAllowedRange
from source.mkw.Patch.PatchOperation.ImageEditor import AbstractLayer
from source.mkw.Patch import PathOutsidePatch
if TYPE_CHECKING:
from source.mkw.Patch import Patch
@ -27,7 +27,7 @@ class ImageLayer(AbstractLayer):
# check if the path is outside of the allowed directory
layer_image_path = patch.path / self.image_path
if not layer_image_path.is_relative_to(patch.path):
raise PathOutsidePatch(layer_image_path, patch.path)
raise PathOutsideAllowedRange(layer_image_path, patch.path)
# load the image that will be pasted
layer_image = Image.open(layer_image_path.resolve()) \

View file

@ -3,7 +3,7 @@ from typing import TYPE_CHECKING
from PIL import ImageFont, ImageDraw, Image
from source.mkw.Patch import PathOutsidePatch
from source.mkw import PathOutsideAllowedRange
from source.mkw.Patch.PatchOperation.ImageEditor import AbstractLayer
if TYPE_CHECKING:
@ -33,7 +33,7 @@ class TextLayer(AbstractLayer):
if self.font_path is not None:
font_image_path = patch.path / self.font_path
if not font_image_path.is_relative_to(patch.path):
raise PathOutsidePatch(font_image_path, patch.path)
raise PathOutsideAllowedRange(font_image_path, patch.path)
else:
font_image_path = None

View file

@ -12,7 +12,7 @@ if TYPE_CHECKING:
class InvalidImageLayerType(Exception):
def __init__(self, layer_type: str):
super().__init__(_("IMAGE_LAYER_TYPE", ' "', layer_type, '" ', "IS_NOT_IMPLEMENTED"))
super().__init__(_("ERROR_IMAGE_LAYER_TYPE_NOT_IMPLEMENTED") % layer_type)
class AbstractLayer(ABC):

View file

@ -2,7 +2,7 @@ from io import BytesIO
from pathlib import Path
from typing import IO, TYPE_CHECKING
from source.mkw.Patch import PathOutsidePatch
from source.mkw import PathOutsideAllowedRange
from source.mkw.Patch.PatchOperation import AbstractPatchOperation
from source.wt import wstrt
@ -31,7 +31,7 @@ class StrEditor(AbstractPatchOperation):
for section in self.sections if self.sections is not None else []:
section_path = patch.path / section
if not section_path.is_relative_to(patch.path):
raise PathOutsidePatch(section_path, patch.path)
raise PathOutsideAllowedRange(section_path, patch.path)
checked_sections += section_path
# for every file in the sections, check if they are inside the patch.

View file

@ -9,7 +9,7 @@ if TYPE_CHECKING:
class InvalidPatchOperation(Exception):
def __init__(self, operation: str):
super().__init__(_("OPERATION", ' "', operation, '" ', "IS_NOT_IMPLEMENTED"))
super().__init__(_("ERROR_OPERATION_NOT_IMPLEMENTED") % operation)
class AbstractPatchOperation(ABC):

View file

@ -1,4 +1,3 @@
from pathlib import Path
from typing import TYPE_CHECKING
from source.translation import translate as _
@ -7,18 +6,11 @@ if TYPE_CHECKING:
from source.mkw.Patch.PatchObject import PatchObject
class PathOutsidePatch(Exception):
def __init__(self, forbidden_path: Path, allowed_range: Path):
super().__init__(_("PATH", ' "', forbidden_path, '" ', "OUTSIDE_ALLOWED_RANGE", ' "', {allowed_range}, '" '))
class InvalidPatchMode(Exception):
def __init__(self, patch: "PatchObject", mode: str):
super().__init__(_("MODE", ' "', mode, '" ', "IS_NOT_IMPLEMENTED",
"(", "IN_PATCH", ' : "', patch.full_path, '")'))
super().__init__(_("ERROR_PATCH_MODE_NOT_IMPLEMENTED") % (mode, patch.full_path))
class InvalidSourceMode(Exception):
def __init__(self, patch: "PatchObject", source: str):
super().__init__(_("SOURCE", ' "', source, '" ', "IS_NOT_IMPLEMENTED",
"(", "IN_PATCH", ' : "', patch.full_path, '")'))
super().__init__(_("ERROR_SOURCE_NOT_IMPLEMENTED") % (source, patch.full_path))

View file

@ -11,7 +11,7 @@ if TYPE_CHECKING:
class TrackForbiddenCustomAttribute(Exception):
def __init__(self, attribute_name: str):
super().__init__(_("FORBIDDEN_TRACK_ATTRIBUTE", " : ", repr(attribute_name)))
super().__init__(_("ERROR_FORBIDDEN_TRACK_ATTRIBUTE") % attribute_name)
class AbstractTrack(ABC):
@ -35,7 +35,6 @@ class AbstractTrack(ABC):
for key, value in kwargs.items():
# if the attribute start with __, this is a magic attribute, and it should not be modified
if "__" in key: raise TrackForbiddenCustomAttribute(key)
# TODO: check potential security issue with setattr and already implemented method and attribute
setattr(self, key, value)
def __repr__(self):

View file

@ -12,7 +12,7 @@ if TYPE_CHECKING:
class ArenaForbiddenCustomAttribute(Exception):
def __init__(self, attribute_name: str):
super().__init__(_("FORBIDDEN_ARENA_ATTRIBUTE", " : ", repr(attribute_name)))
super().__init__(_("ERROR_FORBIDDEN_ARENA_ATTRIBUTE") % attribute_name)
class Arena(RealArenaTrack):

View file

@ -1,10 +1,5 @@
from typing import TYPE_CHECKING
from source.mkw.Track.AbstractTrack import AbstractTrack
if TYPE_CHECKING:
from source.mkw.ModConfig import ModConfig
class DefaultTrack(AbstractTrack):
def repr_format(self, template: str) -> str:

View file

@ -11,7 +11,7 @@ if TYPE_CHECKING:
class TrackGroupForbiddenCustomAttribute(Exception):
def __init__(self, attribute_name: str):
super().__init__(_("FORBIDDEN_TRACKGROUP_ATTRIBUTE", " : ", repr(attribute_name)))
super().__init__(_("ERROR_FORBIDDEN_TRACKGROUP_ATTRIBUTE") % attribute_name)
class TrackGroup:
@ -61,7 +61,7 @@ class TrackGroup:
return the ctfile of the track group
:return: ctfile
"""
ctfile = f'T T11; T11; 0x02; "-"; "info"; "-"\n'
ctfile = f'T T11; T11; 0x02; "-"; "-"; "-"\n'
for track in self.get_tracks():
ctfile += track.get_ctfile(template=template, hidden=True)

View file

@ -1 +1,12 @@
from typing import TYPE_CHECKING
from source.translation import translate as _
if TYPE_CHECKING:
from pathlib import Path
Tag = str
class PathOutsideAllowedRange(Exception):
def __init__(self, forbidden_path: "Path", allowed_range: "Path"):
super().__init__(_("ERROR_PATH_OUTSIDE_RANGE") % (forbidden_path, allowed_range))

View file

@ -5,7 +5,7 @@ from source.translation import translate as _
class ColorNotFound(Exception):
def __init__(self, color_data: any):
super().__init__(_("CANNOT_FIND_COLOR", ' "', color_data, '"'))
super().__init__(_("ERROR_CANNOT_FIND_COLOR") % color_data)
@dataclass(init=True, slots=True)

View file

@ -4,7 +4,7 @@ from source.translation import translate as _
class SlotNotFound(Exception):
def __init__(self, slot_data: any):
super().__init__(_("CANNOT_FIND_SLOT", ' "', slot_data, '" '))
super().__init__(_("ERROR_CANNOT_FIND_SLOT") % slot_data)
@dataclass(init=True, slots=True, frozen=True, repr=True)

View file

@ -6,7 +6,7 @@ from source.utils import restart_program
class OptionLoadingError(Exception):
def __init__(self):
super().__init__(f"An error occured while loading options. Try deleting the option.json file.")
super().__init__(f"An error occurred while loading options. Try deleting the option.json file.")
class Option:

View file

@ -4,7 +4,7 @@ from source.translation import translate as _
class BetterSafeEvalError(Exception):
def __init__(self, template: str):
super().__init__(_("SAFE_EVAL_ERROR", " (", "TEMPLATE_USED", " : ", repr(template), ")"))
super().__init__(_("ERROR_SAFEEVAL") % template)
def better_safe_eval_error(func: Callable, template: str):

View file

@ -12,7 +12,7 @@ MACRO_START, MACRO_END = "##", "##"
class NotImplementedMacro(Exception):
def __init__(self, macro: str):
super().__init__(_("INVALID_MACRO", ' : "', macro, '"'))
super().__init__(_("ERROR_INVALID_MACRO") % macro)
def replace_macro(template: str, macros: dict[str, "TemplateSafeEval"]) -> str:

View file

@ -60,7 +60,7 @@ def safe_eval(template: "TemplateSafeEval", env: "Env" = None, macros: dict[str,
# convert the template to an ast expression
stmt: ast.stmt = ast.parse(template).body[0]
if not isinstance(stmt, ast.Expr):
raise SafeEvalException(_("INVALID_AST_TYPE", ' : "', type(stmt).__name__, '"'))
raise SafeEvalException(_("ERROR_INVALID_AST_TYPE") % type(stmt).__name__)
# check every node for disabled expression
for node in ast.walk(stmt):
@ -70,20 +70,20 @@ def safe_eval(template: "TemplateSafeEval", env: "Env" = None, macros: dict[str,
case ast.Attribute:
# ban all magical function, disabling the __class__.__bases__[0] ... tricks
if "__" in node.attr:
raise SafeEvalException(_("MAGIC_ATTRIBUTE_ARE_FORBIDDEN", ' : "', node.attr, '"'))
raise SafeEvalException(_("ERROR_FORBIDDEN_MAGIC_ATTRIBUTE") % node.attr)
# ban modification to environment
if isinstance(node.ctx, ast.Store):
raise SafeEvalException(_("CANNOT_SET_ATTRIBUTE", ' : "', node.attr, '"'))
raise SafeEvalException(_("ERROR_CANNOT_SET_ATTRIBUTE") % node.attr)
# when accessing any variable
case ast.Name:
# ban modification to environment, but allow custom variable to be changed
if isinstance(node.ctx, ast.Store):
if node.id in globals_ | locals_:
raise SafeEvalException(_("CANNOT_SET_ENVIRONMENT", ' : "', node.id, '"'))
raise SafeEvalException(_("ERROR_CANNOT_SET_ENVIRONMENT") % node.id)
elif node.id in args:
raise SafeEvalException(_("CANNOT_SET_ARGUMENT", ' : "', node.id, '"'))
raise SafeEvalException(_("ERROR_CANNOT_SET_ARGUMENT") % node.id)
# when assigning a value with ":="
case ast.NamedExpr:
@ -96,13 +96,14 @@ def safe_eval(template: "TemplateSafeEval", env: "Env" = None, macros: dict[str,
case ast.Call:
if isinstance(node.func, ast.Attribute): # if this is a method
if not isinstance(node.func.value, ast.Constant): # if the method is not on a constant
raise SafeEvalException(_("CAN_ONLY_CALL_METHOD_OF_CONSTANT"))
raise SafeEvalException(_("ERROR_CAN_ONLY_CALL_CONSTANT_METHOD"))
elif isinstance(node.func, ast.Name): # if this is a direct function call
if node.func.id not in globals_ | locals_: # if the function is not in env
raise SafeEvalException(_("CAN_ONLY_CALL_FUNCTION_IN_ENV"))
raise SafeEvalException(_("ERROR_CAN_ONLY_CALL_ENV_FUNCTION"))
else: raise SafeEvalException(_("CAN_ONLY_CALL_FUNCTION_IN_ENV")) # else don't allow the function call
else: # else don't allow the function call
raise SafeEvalException(_("ERROR_CAN_ONLY_CALL_ENV_FUNCTION"))
# Forbidden type. Some of them can't be accessed with the eval mode, but just in case, still ban them
case (
@ -117,7 +118,7 @@ def safe_eval(template: "TemplateSafeEval", env: "Env" = None, macros: dict[str,
# comprehension are extremely dangerous since their can associate value
ast.ListComp | ast.SetComp | ast.DictComp | ast.GeneratorExp
):
raise SafeEvalException(_("FORBIDDEN_SYNTAX", ' : "', type(node).__name__, '"'))
raise SafeEvalException(_("ERROR_FORBIDDEN_SYNTAX") % type(node).__name__)
# embed the whole expression into a lambda expression
stmt.value = ast.Lambda(

View file

@ -36,9 +36,9 @@ class safe_function:
"""
Same as normal getattr, but magic attribute are banned
"""
if "__" in name: raise Exception(_("MAGIC_METHOD_FORBIDDEN", ' : "', name, '"'))
if "__" in name: raise Exception(_("ERROR_FORBIDDEN_MAGIC_METHOD") % name)
attr = getattr(obj, name, default)
if callable(attr): raise Exception(_("GET_METHOD_FORBIDDEN", ' : "', name, '"'))
if callable(attr): raise Exception(_("ERROR_GETTING_METHOD_FORBIDDEN") % name)
return attr
@staticmethod
@ -63,5 +63,5 @@ class safe_function:
:param obj: the object to copy
:return: the copied object
"""
if callable(obj): raise Exception(_("COPY_FUNCTION_FORBIDDEN", ' : "', obj.__name__, '"'))
if callable(obj): raise Exception(_("ERROR_FUNCTION_COPY_FORBIDDEN") % obj.__name__)
return copy.deepcopy(obj)

View file

@ -19,17 +19,13 @@ def load_language(language: str):
self._language_data = json.loads(Path(f"./assets/language/{language}.json").read_text(encoding="utf8"))
def translate(*text) -> str:
def translate(text) -> str:
"""
Translate a text to the loaded language.
:param text: list of text to translate
:return: translated text
"""
return "".join([
self._language_data.get("translation", {}).get(word, word) if isinstance(word, str)
else str(word)
for word in text
])
return self._language_data.get("translation", {}).get(text, text)
def translate_external(mod_config: "ModConfig", language: str, message_texts: dict[str, str], default: str = "") -> str:

View file

@ -21,15 +21,15 @@ class WTError(Exception):
check=True,
**subprocess_kwargs
).stdout.decode()
except subprocess.CalledProcessError as e:
error = _("- ", "CANNOT_GET_ERROR_MESSAGE", " -")
except subprocess.CalledProcessError:
error = _("ERROR_CANNOT_GET_ERROR_MESSAGE")
super().__init__(_(tools_path, " ", "RAISED", " ", return_code, ":\n", error, "\n"))
super().__init__(_("ERROR_WT") % (tools_path, return_code, error))
class MissingWTError(Exception):
def __init__(self, tool_name: str):
super().__init__(_("CANNOT_FIND_TOOL", ' "', tool_name, '" ', "IN_TOOLS_DIRECTORY"))
super().__init__(_("ERROR_CANNOT_FIND_TOOL") % tool_name)
try: tools_szs_dir = next(tools_dir.glob("./szs*/")) / system

View file

@ -192,7 +192,7 @@ class SZSSubPath:
:param dest: destination path
:return: the extracted file path
"""
if self.is_dir(): raise ValueError(_("CANNOT_EXTRACT_A_DIRECTORY"))
if self.is_dir(): raise ValueError(_("ERROR_CANNOT_EXTRACT_DIRECTORY"))
dest: Path = Path(dest)
if dest.is_dir(): dest /= self.basename()

View file

@ -202,7 +202,7 @@ class WITSubPath:
if self.wit_path.extension == Extension.FST:
# if flat is used, extract the file / dir into the destination directory, without subdirectory
if flat:
os.makedirs(dest, exist_ok=True)
dest.mkdir(parents=True, exist_ok=True)
# if we are extracting a directory, we need to extract every file recursively
if self.is_dir():
for file in (self._get_fst_path()).rglob("*"):