erplibre/script/todo/todo_i18n.py
Mathieu Benoit 91c835d57e [IMP] todo: add Claude configs submenu and command listing
Refactor single commit setup into a generic deployment
mechanism for Claude commands, allowing easy addition of
new commands. Add todo_add_command template and an option
to list installed custom commands with their dates.

Generated by Claude Code 2.1.74 model claude-opus-4-6

Co-Authored-By: Mathieu Benoit <mathben@technolibre.ca>
2026-03-14 23:26:31 -04:00

810 lines
25 KiB
Python

#!/usr/bin/env python3
# © 2021-2026 TechnoLibre (http://www.technolibre.ca)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
import os
import re
ENV_VAR_FILE = "./env_var.sh"
_current_lang = None
TRANSLATIONS = {
# Main menu
"Importation success!": {
"fr": "L'importation est un succès!",
"en": "Importation success!",
},
"opening": {
"fr": "Ouverture de TODO en cours ...",
"en": "Opening TODO ...",
},
"enter_directives": {
"fr": "=> Entre tes directives par son chiffre et fait Entrée!",
"en": "=> Enter your choice by number and press Enter!",
},
"command": {
"fr": "Commande :",
"en": "Command:",
},
"menu_execute": {
"fr": "Exécution",
"en": "Execute",
},
"menu_install": {
"fr": "Installation",
"en": "Install",
},
"menu_question": {
"fr": "Question",
"en": "Question",
},
"menu_fork": {
"fr": "Fork - Ouvre TODO dans une nouvelle tabulation",
"en": "Fork - Open TODO in a new tab",
},
"menu_quit": {
"fr": "Quitter",
"en": "Quit",
},
"cmd_not_found": {
"fr": "Commande non trouvée !",
"en": "Command not found !",
},
"back": {
"fr": "Retour",
"en": "Back",
},
# Execute submenu
"menu_run": {
"fr": "Run - Exécuter et installer une instance",
"en": "Run - Execute and install an instance",
},
"menu_automation": {
"fr": "Automatisation - Demonstration des fonctions développées",
"en": "Automation - Demonstration of developed features",
},
"menu_update": {
"fr": "Mise à jour - Update all developed staging source code",
"en": "Update - Update all developed staging source code",
},
"menu_code": {
"fr": "Code - Outil pour développeur",
"en": "Code - Developer tools",
},
"menu_doc": {
"fr": "Doc - Recherche de documentation",
"en": "Doc - Documentation search",
},
"menu_database": {
"fr": "Database - Outils sur les bases de données",
"en": "Database - Database tools",
},
"menu_process": {
"fr": "Process - Outils sur les executions",
"en": "Process - Execution tools",
},
"menu_config": {
"fr": "Config - Traitement du fichier de configuration",
"en": "Config - Configuration file management",
},
"menu_network": {
"fr": "Réseau - Outil réseautique",
"en": "Network - Network tools",
},
"menu_security": {
"fr": "Sécurité - Audit de sécurité des dépendances",
"en": "Security - Dependency security audit",
},
"menu_rtk": {
"fr": "RTK - Proxy CLI pour réduire la consommation de tokens LLM",
"en": "RTK - CLI proxy to reduce LLM token consumption",
},
"menu_lang": {
"fr": "Langue - Changer la langue / Change language",
"en": "Language - Change language / Changer la langue",
},
# RTK (Rust Token Killer)
"rtk_manage": {
"fr": "Gérer RTK (Rust Token Killer) pour optimiser les tokens!",
"en": "Manage RTK (Rust Token Killer) for token optimization!",
},
"rtk_install": {
"fr": "Installer RTK",
"en": "Install RTK",
},
"rtk_version": {
"fr": "Vérifier la version de RTK",
"en": "Check RTK version",
},
"rtk_gain": {
"fr": "Afficher les économies de tokens cumulées",
"en": "Show cumulative token savings",
},
"rtk_discover": {
"fr": "Identifier les opportunités d'optimisation",
"en": "Discover optimization opportunities",
},
"rtk_init_global": {
"fr": "Initialiser le hook auto-rewrite global",
"en": "Initialize global auto-rewrite hook",
},
"rtk_status": {
"fr": "Vérifier le statut de RTK",
"en": "Check RTK status",
},
"rtk_not_installed": {
"fr": "RTK n'est pas installé. Utilisez l'option 1 pour l'installer.",
"en": "RTK is not installed. Use option 1 to install it.",
},
"rtk_installed_version": {
"fr": "RTK est installé, version : ",
"en": "RTK is installed, version: ",
},
"rtk_hook_active": {
"fr": "Hook auto-rewrite global : actif",
"en": "Global auto-rewrite hook: active",
},
"rtk_hook_inactive": {
"fr": "Hook auto-rewrite global : inactif",
"en": "Global auto-rewrite hook: inactive",
},
"rtk_install_method": {
"fr": "Méthode d'installation :",
"en": "Installation method:",
},
"rtk_install_curl": {
"fr": "curl - Script d'installation automatique",
"en": "curl - Automatic install script",
},
"rtk_install_brew": {
"fr": "brew - Homebrew (macOS/Linux)",
"en": "brew - Homebrew (macOS/Linux)",
},
"rtk_install_cargo": {
"fr": "cargo - Compilation depuis les sources (Rust requis)",
"en": "cargo - Build from source (Rust required)",
},
# Prompts and messages
"enter_password": {
"fr": "Entrez votre mot de passe : ",
"en": "Enter your password: ",
},
"ia_prompt": {
"fr": "Écrit moi ta question ",
"en": "Write your question ",
},
"new_instance_confirm": {
"fr": "Voulez-vous une nouvelle instance?",
"en": "Do you want a new instance?",
},
"ssh_port_forwarding": {
"fr": "SSH port-forwarding",
"en": "SSH port-forwarding",
},
"network_performance_request_per_second": {
"fr": "Performance réseau en requêtes par seconde",
"en": "Network performance request per second",
},
"setup_queue_job_for_parallelism": {
"fr": "Configurer la file d'attente pour l'exécution parallèle",
"en": "Setup queue job for parallelism",
},
"choose_database": {
"fr": "Choisir sa base de données",
"en": "Choose your database",
},
"update_dev": {
"fr": "Mise à jour du développement",
"en": "Development update",
},
"code_need": {
"fr": "Qu'avez-vous de besoin pour développer?",
"en": "What do you need for development?",
},
"doc_search": {
"fr": "Vous cherchez de la documentation?",
"en": "Looking for documentation?",
},
"db_modify": {
"fr": "Faites des modifications sur les bases de données!",
"en": "Make changes to databases!",
},
"process_manage": {
"fr": "Manipuler les processus d'exécution!",
"en": "Manage execution processes!",
},
"config_manage": {
"fr": "Manipuler la configuration ERPLibre et Odoo!",
"en": "Manage ERPLibre and Odoo configuration!",
},
"network_tools": {
"fr": "Outil réseautique!",
"en": "Network tools!",
},
"security_audit": {
"fr": "Audit de securite des dépendances!",
"en": "Dependency security audit!",
},
"script_failed": {
"fr": "Le script Bash a échoué avec le code de retour",
"en": "The Bash script failed with return code",
},
"no_env_installed": {
"fr": "Aucun environnement installe trouve. Installez d'abord une version d'Odoo.",
"en": "No installed environment found. Install an Odoo version first.",
},
"choose_env_audit": {
"fr": "Choisir un environnement pour l'audit :",
"en": "Choose an environment for the audit:",
},
"selection": {
"fr": "Sélection : ",
"en": "Select: ",
},
"error_value": {
"fr": "Erreur, impossible de comprendre la valeur",
"en": "Error, cannot understand value",
},
"dep_file_not_found": {
"fr": "Fichier de dépendances introuvable : ",
"en": "Dependencies file not found: ",
},
"execution": {
"fr": "Execution : ",
"en": "Execution: ",
},
"current": {
"fr": "Actuel",
"en": "Current",
},
"default": {
"fr": "Défaut",
"en": "Default",
},
"reboot_todo": {
"fr": "Reboot TODO ...",
"en": "Reboot TODO ...",
},
"pip_audit_desc": {
"fr": "pip-audit - Verifier les vulnérabilités sur les environnements Python",
"en": "pip-audit - Check vulnerabilities on Python environments",
},
"will_execute": {
"fr": "Va exécuter :",
"en": "Will execute:",
},
"choose_version": {
"fr": "Choisir une version :",
"en": "Choose a version:",
},
"error_cannot_understand": {
"fr": "Erreur, impossible de comprendre la valeur",
"en": "Error, cannot understand value",
},
# todo.json translatable prompt_descriptions
"json_instance_test": {
"fr": "Test - Instance de base minimale",
"en": "Test - Minimal base instance",
},
"json_robot_minimal": {
"fr": "Ouvrir RobotLibre 🤖 minimal",
"en": "Open RobotLibre 🤖 minimal",
},
"json_robot_search": {
"fr": "Ouvrir RobotLibre 🤖 en activant la recherche",
"en": "Open RobotLibre 🤖 with search enabled",
},
"json_open_erplibre_todo": {
"fr": "Ouvrir ERPLibre avec TODO 🤖",
"en": "Open ERPLibre with TODO 🤖",
},
"json_update_erplibre_base_test": {
"fr": "Mise à jour de tous les erplibre_base sur la base de données test",
"en": "Update all erplibre_base on database test",
},
"json_show_code_status": {
"fr": "Afficher le statut du code",
"en": "Show code status",
},
"json_stash_all_code": {
"fr": "Remiser tout le code",
"en": "Stash all code",
},
"json_format_modified_code": {
"fr": "Formater le code modifié",
"en": "Format modified code",
},
# todo.py hardcoded prompt_descriptions
"mobile_compile_run": {
"fr": "Mobile - Compiler et exécuter le logiciel",
"en": "Mobile - Compile and run software",
},
"upgrade_odoo_migration": {
"fr": "Mise à jour Odoo - Migration de base de données",
"en": "Upgrade Odoo - Migration Database",
},
"upgrade_poetry_dependency": {
"fr": "Mise à jour Poetry - Dépendances d'Odoo",
"en": "Upgrade Poetry - Dependency of Odoo",
},
"open_shell": {
"fr": "Ouvrir le SHELL",
"en": "Open SHELL",
},
"upgrade_module": {
"fr": "Mise à jour de module",
"en": "Upgrade Module",
},
"debug": {
"fr": "Débogage",
"en": "Debug",
},
"migration_module_coverage": {
"fr": "Couverture de migration des modules",
"en": "Migration module coverage",
},
"what_change_between_version": {
"fr": "Quels changements entre les versions",
"en": "What change between version",
},
"oca_guidelines": {
"fr": "Directives OCA",
"en": "OCA guidelines",
},
"oca_migration_odoo_19": {
"fr": "Migration OCA Odoo 19 - Jalons",
"en": "OCA migration Odoo 19 milestone",
},
"download_db_backup": {
"fr": "Télécharger une base de données pour créer une sauvegarde (.zip)",
"en": "Download database to create backup (.zip)",
},
"restore_from_backup": {
"fr": "Restaurer a partir d'une sauvegarde (.zip)",
"en": "Restore from backup (.zip)",
},
"create_backup": {
"fr": "Créer une sauvegarde (.zip)",
"en": "Create backup (.zip)",
},
"kill_process_port": {
"fr": "Terminer le processus Odoo du port actuel",
"en": "Kill Odoo process from actual port",
},
"kill_git_daemon": {
"fr": "Terminer le processus du serveur git daemon",
"en": "Kill git daemon server process",
},
"kill_git_daemon_done": {
"fr": "Processus git daemon terminé.",
"en": "Git daemon process killed.",
},
"generate_all_config": {
"fr": "Générer toute la configuration",
"en": "Generate all configuration",
},
"generate_from_preconfig": {
"fr": "Générer a partir de la pre-configuration",
"en": "Generate from pre-configuration",
},
"generate_from_backup": {
"fr": "Générer a partir d'un fichier de sauvegarde",
"en": "Generate from backup file",
},
"generate_from_database": {
"fr": "Générer a partir de la base de données",
"en": "Generate from database",
},
"preconfig_base": {
"fr": "base",
"en": "base",
},
"preconfig_base_code_generator": {
"fr": "base + code_generator",
"en": "base + code_generator",
},
"preconfig_base_image_db": {
"fr": "base + image_db",
"en": "base + image_db",
},
"preconfig_all": {
"fr": "tout",
"en": "all",
},
"debug_todo_py": {
"fr": "Débogage todo.py",
"en": "Debug todo.py",
},
# Test section
"menu_test": {
"fr": "Test - Tester un module Odoo",
"en": "Test - Test an Odoo module",
},
"test_description": {
"fr": "Tester un module Odoo sur une base de données temporaire!",
"en": "Test an Odoo module on a temporary database!",
},
"test_run_module": {
"fr": "Tester un module",
"en": "Test a module",
},
"test_run_module_coverage": {
"fr": "Tester un module avec couverture de code",
"en": "Test a module with code coverage",
},
"test_run_unit_tests": {
"fr": "Tests unitaires ERPLibre",
"en": "ERPLibre unit tests",
},
"test_unit_running": {
"fr": "Exécution des tests unitaires",
"en": "Running unit tests",
},
"test_unit_success": {
"fr": "Tous les tests unitaires ont réussi",
"en": "All unit tests passed",
},
"test_unit_failed": {
"fr": "Des tests unitaires ont échoué, code de sortie",
"en": "Some unit tests failed, exit code",
},
"test_enter_module_name": {
"fr": "Nom du module à tester : ",
"en": "Module name to test: ",
},
"test_db_name": {
"fr": "Nom de la base de données temporaire (défaut: test_todo_tmp) : ",
"en": "Temporary database name (default: test_todo_tmp): ",
},
"test_install_extra_modules": {
"fr": "Modules supplémentaires à installer (séparés par des virgules, vide pour aucun) : ",
"en": "Extra modules to install (comma-separated, empty for none): ",
},
"test_log_level": {
"fr": "Niveau de log (défaut: test) : ",
"en": "Log level (default: test): ",
},
"test_creating_db": {
"fr": "Création de la base de données temporaire",
"en": "Creating temporary database",
},
"test_installing_modules": {
"fr": "Installation des modules",
"en": "Installing modules",
},
"test_running": {
"fr": "Exécution des tests",
"en": "Running tests",
},
"test_cleaning_db": {
"fr": "Suppression de la base de données temporaire",
"en": "Cleaning up temporary database",
},
"test_keep_db": {
"fr": "Conserver la base de données temporaire? (o/N) : ",
"en": "Keep the temporary database? (y/N): ",
},
"test_db_kept": {
"fr": "Base de données conservée",
"en": "Database kept",
},
"test_success": {
"fr": "Tests terminés avec succès!",
"en": "Tests completed successfully!",
},
"test_failed": {
"fr": "Les tests ont échoué avec le code de retour",
"en": "Tests failed with return code",
},
"test_module_required": {
"fr": "Le nom du module est requis!",
"en": "Module name is required!",
},
# Git section
"menu_git": {
"fr": "Git - Outils Git",
"en": "Git - Git tools",
},
"git_manage": {
"fr": "Outils de gestion Git!",
"en": "Git management tools!",
},
"git_local_server": {
"fr": "Serveur git local",
"en": "Local git server",
},
"git_repo_manage": {
"fr": "Gérer le serveur de dépôts git local!",
"en": "Manage local git repository server!",
},
"git_repo_deploy_local": {
"fr": "Déployer un serveur git local (~/.git-server)",
"en": "Deploy a local git server (~/.git-server)",
},
"git_repo_deploy_production": {
"fr": "Déployer un serveur git production (/srv/git, root requis)",
"en": "Deploy a production git server (/srv/git, root required)",
},
"git_repo_deploy_starting": {
"fr": "Démarrage du déploiement du serveur git...",
"en": "Starting git server deployment...",
},
"git_mode_local": {
"fr": "Mode local (~/.git-server)",
"en": "Local mode (~/.git-server)",
},
"git_mode_production": {
"fr": "Mode production (/srv/git, root requis)",
"en": "Production mode (/srv/git, root required)",
},
"git_action_all": {
"fr": "Tout exécuter (init + remote + push + serve)",
"en": "Run all (init + remote + push + serve)",
},
"git_action_init": {
"fr": "Init - Créer les bare repos",
"en": "Init - Create bare repos",
},
"git_action_remote": {
"fr": "Remote - Ajouter les remotes locaux",
"en": "Remote - Add local remotes",
},
"git_action_push": {
"fr": "Push - Pousser vers le serveur local",
"en": "Push - Push to local server",
},
"git_action_serve": {
"fr": "Serve - Démarrer le daemon git",
"en": "Serve - Start git daemon",
},
# Git remote add
"git_add_remote": {
"fr": "Ajouter un remote vers un dépôt local",
"en": "Add a remote to a local repository",
},
"git_add_remote_name_prompt": {
"fr": "Nom du remote (défaut: localhost) : ",
"en": "Remote name (default: localhost): ",
},
"git_add_remote_url_prompt": {
"fr": "Adresse du dépôt (ex: git://192.168.1.100/mon-repo.git) : ",
"en": "Repository address (e.g.: git://192.168.1.100/my-repo.git): ",
},
"git_add_remote_url_required": {
"fr": "L'adresse du dépôt est requise!",
"en": "Repository address is required!",
},
"git_add_remote_success": {
"fr": "Remote ajouté avec succès!",
"en": "Remote added successfully!",
},
"git_add_remote_error": {
"fr": "Erreur lors de l'ajout du remote : ",
"en": "Error adding remote: ",
},
# Git config vim
"git_config_vim": {
"fr": "Configuration git local par vim",
"en": "Configure git local editor to vim",
},
"git_config_vim_success": {
"fr": "Éditeur git configuré sur vim avec succès!",
"en": "Git editor configured to vim successfully!",
},
"git_config_vim_error": {
"fr": "Erreur lors de la configuration : ",
"en": "Error during configuration: ",
},
# GPT code - Claude automation
"gpt_code_claude_add_automation": {
"fr": "Ajouter une automatisation avec Claude dans todo.py",
"en": "Add an automation with Claude in todo.py",
},
"gpt_code_claude_add_automation_prompt": {
"fr": "Description de la commande à ajouter : ",
"en": "Description of the command to add: ",
},
"gpt_code_claude_add_automation_cmd_prompt": {
"fr": "Commande bash à exécuter : ",
"en": "Bash command to execute: ",
},
"gpt_code_claude_add_automation_section_prompt": {
"fr": "Section du menu (git/code/config/network/process) : ",
"en": "Menu section (git/code/config/network/process): ",
},
"gpt_code_claude_add_automation_success": {
"fr": "Automatisation ajoutée avec succès dans todo.json!",
"en": "Automation added successfully in todo.json!",
},
"gpt_code_claude_add_automation_error": {
"fr": "Erreur lors de l'ajout de l'automatisation : ",
"en": "Error adding automation: ",
},
# Language selection
"lang_prompt": {
"fr": "Choisir la langue / Choose language",
"en": "Choose language / Choisir la langue",
},
"lang_french": {
"fr": "Francais",
"en": "French",
},
"lang_english": {
"fr": "Anglais",
"en": "English",
},
"lang_changed": {
"fr": "Langue changée pour : Francais",
"en": "Language changed to: English",
},
"execution_time": {
"fr": "Temps d'exécution TODO",
"en": "TODO execution time",
},
"keyboard_interrupt": {
"fr": "Interruption clavier",
"en": "Keyboard interrupt",
},
# GPT code section
"menu_gpt_code": {
"fr": "GPT code - Outils d'assistant IA",
"en": "GPT code - AI assistant tools",
},
"gpt_code_manage": {
"fr": "Outils d'assistant IA pour le développement!",
"en": "AI assistant tools for development!",
},
"gpt_code_claude_configs": {
"fr": "Configurer les configurations Claude Code",
"en": "Configure Claude Code configurations",
},
"gpt_code_claude_commit": {
"fr": "Commit - Commande de commit OCA/Odoo",
"en": "Commit - OCA/Odoo commit command",
},
"gpt_code_claude_todo_add_command": {
"fr": "Todo Add Command - Ajouter une commande au menu todo.py",
"en": "Todo Add Command - Add a command to todo.py menu",
},
"gpt_code_enter_name": {
"fr": "Entrez votre nom complet : ",
"en": "Enter your full name: ",
},
"gpt_code_enter_email": {
"fr": "Entrez votre courriel : ",
"en": "Enter your email: ",
},
"gpt_code_claude_configs_manage": {
"fr": "Déployer les commandes Claude Code!",
"en": "Deploy Claude Code commands!",
},
"gpt_code_claude_list_commands": {
"fr": "Afficher les commandes personnalisées installées",
"en": "Show installed custom commands",
},
"gpt_code_claude_no_commands": {
"fr": "Aucune commande personnalisée trouvée dans ~/.claude/commands/",
"en": "No custom commands found in ~/.claude/commands/",
},
"gpt_code_claude_list_header": {
"fr": "Commandes personnalisées Claude Code :",
"en": "Claude Code custom commands:",
},
"gpt_code_claude_list_total": {
"fr": "Total :",
"en": "Total:",
},
"gpt_code_cmd_exists": {
"fr": "Le fichier existe déjà : ",
"en": "File already exists: ",
},
"gpt_code_cmd_overwrite": {
"fr": "Voulez-vous écraser le fichier? (y/Y) : ",
"en": "Do you want to overwrite the file? (y/Y): ",
},
"gpt_code_cmd_nothing_to_do": {
"fr": "Rien à faire.",
"en": "Nothing to do.",
},
"gpt_code_cmd_created": {
"fr": "Fichier créé avec succès : ",
"en": "File created successfully: ",
},
"gpt_code_cmd_error": {
"fr": "Erreur lors de la création du fichier : ",
"en": "Error creating file: ",
},
"gpt_code_commit_exists": {
"fr": "Le fichier ~/.claude/commands/commit.md existe déjà. Aucune action effectuée.",
"en": "File ~/.claude/commands/commit.md already exists. No action taken.",
},
"gpt_code_commit_created": {
"fr": "Fichier ~/.claude/commands/commit.md créé avec succès!",
"en": "File ~/.claude/commands/commit.md created successfully!",
},
"gpt_code_commit_error": {
"fr": "Erreur lors de la création du fichier : ",
"en": "Error creating file: ",
},
}
def get_lang() -> str:
global _current_lang
if _current_lang is not None:
return _current_lang
# 1. Check env_var.sh file
if os.path.exists(ENV_VAR_FILE):
try:
with open(ENV_VAR_FILE) as f:
content = f.read()
match = re.search(
r'^EL_LANG=["\']?(\w+)["\']?', content, re.MULTILINE
)
if match:
lang = match.group(1)
if lang in ("fr", "en"):
_current_lang = lang
return _current_lang
except OSError:
pass
# 2. Check env var
env_lang = os.environ.get("EL_LANG")
if env_lang in ("fr", "en"):
_current_lang = env_lang
return _current_lang
# 3. Default
_current_lang = "fr"
return _current_lang
def set_lang(lang: str) -> None:
global _current_lang
_current_lang = lang
# Persist to env_var.sh
if os.path.exists(ENV_VAR_FILE):
try:
with open(ENV_VAR_FILE) as f:
content = f.read()
except OSError:
return
new_line = f'EL_LANG="{lang}"'
if re.search(r"^EL_LANG=", content, re.MULTILINE):
content = re.sub(
r'^EL_LANG=["\']?\w*["\']?',
new_line,
content,
count=1,
flags=re.MULTILINE,
)
else:
content = content.rstrip("\n") + "\n" + new_line + "\n"
with open(ENV_VAR_FILE, "w") as f:
f.write(content)
def lang_is_configured() -> bool:
"""Check if a language has been explicitly set."""
if os.path.exists(ENV_VAR_FILE):
try:
with open(ENV_VAR_FILE) as f:
content = f.read()
return bool(re.search(r"^EL_LANG=", content, re.MULTILINE))
except OSError:
pass
return False
def t(key: str) -> str:
entry = TRANSLATIONS.get(key)
if entry is None:
return key
lang = get_lang()
return entry.get(lang, entry.get("fr", key))