2026-03-07 02:59:07 -05:00
|
|
|
|
#!/usr/bin/env python3
|
2026-03-11 23:15:07 -04:00
|
|
|
|
# © 2021-2026 TechnoLibre (http://www.technolibre.ca)
|
2026-03-07 02:59:07 -05:00
|
|
|
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
|
|
|
|
|
|
|
|
|
|
|
|
import os
|
|
|
|
|
|
import re
|
|
|
|
|
|
|
2026-03-10 03:45:11 -04:00
|
|
|
|
ENV_VAR_FILE = "./env_var.sh"
|
2026-03-07 02:59:07 -05:00
|
|
|
|
|
|
|
|
|
|
_current_lang = None
|
|
|
|
|
|
|
|
|
|
|
|
TRANSLATIONS = {
|
|
|
|
|
|
# Main menu
|
|
|
|
|
|
"Importation success!": {
|
|
|
|
|
|
"fr": "L'importation est un succès!",
|
|
|
|
|
|
"en": "Importation success!",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Opening TODO ...": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Ouverture de TODO en cours ...",
|
|
|
|
|
|
"en": "Opening TODO ...",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"=> Enter your choice by number and press Enter!": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "=> Entre tes directives par son chiffre et fait Entrée!",
|
|
|
|
|
|
"en": "=> Enter your choice by number and press Enter!",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Command:": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Commande :",
|
|
|
|
|
|
"en": "Command:",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Execute": {
|
[ADD] todo: QEMU/KVM menu
Deploy, list, test, resize, delete and clean up VMs. Adds the SSH
configuration with recursive ProxyJump, port forwarding, and registration
of the QEMU hosts in virt-manager.
--- FR ---
Déployer, lister, tester, redimensionner, supprimer et nettoyer des VM.
Ajoute la configuration SSH avec ProxyJump récursif, la redirection de port
et l'enregistrement des hôtes QEMU dans virt-manager.
Assisted-by: Claude Opus 4.8
2026-08-07 03:22:26 -04:00
|
|
|
|
"fr": "🧰 Exécution",
|
|
|
|
|
|
"en": "🧰 Execute",
|
2026-03-07 02:59:07 -05:00
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Install": {
|
[ADD] todo: QEMU/KVM menu
Deploy, list, test, resize, delete and clean up VMs. Adds the SSH
configuration with recursive ProxyJump, port forwarding, and registration
of the QEMU hosts in virt-manager.
--- FR ---
Déployer, lister, tester, redimensionner, supprimer et nettoyer des VM.
Ajoute la configuration SSH avec ProxyJump récursif, la redirection de port
et l'enregistrement des hôtes QEMU dans virt-manager.
Assisted-by: Claude Opus 4.8
2026-08-07 03:22:26 -04:00
|
|
|
|
"fr": "📦 Installation",
|
|
|
|
|
|
"en": "📦 Install",
|
2026-03-07 02:59:07 -05:00
|
|
|
|
},
|
2026-08-10 03:10:50 -04:00
|
|
|
|
"Assistant": {
|
|
|
|
|
|
"fr": "🤖 Assistant",
|
|
|
|
|
|
"en": "🤖 Assistant",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Fork - Open TODO in a new tab": {
|
[ADD] todo: QEMU/KVM menu
Deploy, list, test, resize, delete and clean up VMs. Adds the SSH
configuration with recursive ProxyJump, port forwarding, and registration
of the QEMU hosts in virt-manager.
--- FR ---
Déployer, lister, tester, redimensionner, supprimer et nettoyer des VM.
Ajoute la configuration SSH avec ProxyJump récursif, la redirection de port
et l'enregistrement des hôtes QEMU dans virt-manager.
Assisted-by: Claude Opus 4.8
2026-08-07 03:22:26 -04:00
|
|
|
|
"fr": "🔀 Fork - Ouvre TODO dans une nouvelle tabulation",
|
|
|
|
|
|
"en": "🔀 Fork - Open TODO in a new tab",
|
2026-03-07 02:59:07 -05:00
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Quit": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Quitter",
|
|
|
|
|
|
"en": "Quit",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Command not found !": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Commande non trouvée !",
|
|
|
|
|
|
"en": "Command not found !",
|
|
|
|
|
|
},
|
2026-08-07 03:22:26 -04:00
|
|
|
|
"Back": {
|
|
|
|
|
|
"fr": "🔙 Retour",
|
|
|
|
|
|
"en": "🔙 Back",
|
|
|
|
|
|
},
|
|
|
|
|
|
# Execute submenu
|
|
|
|
|
|
"Run - Execute and install an instance": {
|
|
|
|
|
|
"fr": "🏃 Run - Exécuter et installer une instance",
|
|
|
|
|
|
"en": "🏃 Run - Execute and install an instance",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Automation - Demonstration of developed features": {
|
|
|
|
|
|
"fr": "🦾 Automatisation - Demonstration des fonctions développées",
|
|
|
|
|
|
"en": "🦾 Automation - Demonstration of developed features",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Update - Update all developed staging source code": {
|
|
|
|
|
|
"fr": "🔃 Mise à jour - Update all developed staging source code",
|
|
|
|
|
|
"en": "🔃 Update - Update all developed staging source code",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Code - Developer tools": {
|
|
|
|
|
|
"fr": "💻 Code - Outil pour développeur",
|
|
|
|
|
|
"en": "💻 Code - Developer tools",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Doc - Documentation search": {
|
|
|
|
|
|
"fr": "📖 Doc - Recherche de documentation",
|
|
|
|
|
|
"en": "📖 Doc - Documentation search",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Database - Database tools": {
|
|
|
|
|
|
"fr": "💾 Database - Outils sur les bases de données",
|
|
|
|
|
|
"en": "💾 Database - Database tools",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Process - Execution tools": {
|
|
|
|
|
|
"fr": "📟 Process - Outils sur les executions",
|
|
|
|
|
|
"en": "📟 Process - Execution tools",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Config - Configuration file management": {
|
|
|
|
|
|
"fr": "🔧 Config - Traitement du fichier de configuration",
|
|
|
|
|
|
"en": "🔧 Config - Configuration file management",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Network - Network tools": {
|
|
|
|
|
|
"fr": "📡 Réseau - Outil réseautique",
|
|
|
|
|
|
"en": "📡 Network - Network tools",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Security - Dependency security audit": {
|
|
|
|
|
|
"fr": "🔒 Sécurité - Audit de sécurité des dépendances",
|
|
|
|
|
|
"en": "🔒 Security - Dependency security audit",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"RTK - CLI proxy to reduce LLM token consumption": {
|
2026-03-12 02:51:01 -04:00
|
|
|
|
"fr": "RTK - Proxy CLI pour réduire la consommation de tokens LLM",
|
|
|
|
|
|
"en": "RTK - CLI proxy to reduce LLM token consumption",
|
|
|
|
|
|
},
|
2026-08-07 03:22:26 -04:00
|
|
|
|
"Language - Change language / Changer la langue": {
|
|
|
|
|
|
"fr": "🌍 Langue - Changer la langue / Change language",
|
|
|
|
|
|
"en": "🌍 Language - Change language / Changer la langue",
|
|
|
|
|
|
},
|
|
|
|
|
|
# Deploy section
|
|
|
|
|
|
"Deploy - Deploy ERPLibre locally": {
|
|
|
|
|
|
"fr": "🚀 Déploiement - Déployer ERPLibre localement",
|
|
|
|
|
|
"en": "🚀 Deploy - Deploy ERPLibre locally",
|
|
|
|
|
|
},
|
2026-03-12 05:40:22 -04:00
|
|
|
|
"Deploy ERPLibre to a local directory!": {
|
|
|
|
|
|
"fr": "Déployer ERPLibre dans un répertoire local!",
|
|
|
|
|
|
"en": "Deploy ERPLibre to a local directory!",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Clone ERPLibre locally (git clone)": {
|
[ADD] todo: QEMU/KVM menu
Deploy, list, test, resize, delete and clean up VMs. Adds the SSH
configuration with recursive ProxyJump, port forwarding, and registration
of the QEMU hosts in virt-manager.
--- FR ---
Déployer, lister, tester, redimensionner, supprimer et nettoyer des VM.
Ajoute la configuration SSH avec ProxyJump récursif, la redirection de port
et l'enregistrement des hôtes QEMU dans virt-manager.
Assisted-by: Claude Opus 4.8
2026-08-07 03:22:26 -04:00
|
|
|
|
"fr": "📥 Cloner ERPLibre localement (git clone)",
|
|
|
|
|
|
"en": "📥 Clone ERPLibre locally (git clone)",
|
2026-03-12 05:40:22 -04:00
|
|
|
|
},
|
|
|
|
|
|
"Target directory path (default: ~/erplibre): ": {
|
|
|
|
|
|
"fr": "Chemin du répertoire cible (défaut: ~/erplibre) : ",
|
|
|
|
|
|
"en": "Target directory path (default: ~/erplibre): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Directory already exists: ": {
|
|
|
|
|
|
"fr": "Le répertoire existe déjà : ",
|
|
|
|
|
|
"en": "Directory already exists: ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Cloning ERPLibre...": {
|
|
|
|
|
|
"fr": "Clonage d'ERPLibre en cours...",
|
|
|
|
|
|
"en": "Cloning ERPLibre...",
|
|
|
|
|
|
},
|
|
|
|
|
|
"ERPLibre cloned successfully to: ": {
|
|
|
|
|
|
"fr": "ERPLibre cloné avec succès dans : ",
|
|
|
|
|
|
"en": "ERPLibre cloned successfully to: ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Error cloning ERPLibre: ": {
|
|
|
|
|
|
"fr": "Erreur lors du clonage d'ERPLibre : ",
|
|
|
|
|
|
"en": "Error cloning ERPLibre: ",
|
|
|
|
|
|
},
|
2026-03-22 22:28:24 -04:00
|
|
|
|
"Configure sshfs": {
|
[ADD] todo: QEMU/KVM menu
Deploy, list, test, resize, delete and clean up VMs. Adds the SSH
configuration with recursive ProxyJump, port forwarding, and registration
of the QEMU hosts in virt-manager.
--- FR ---
Déployer, lister, tester, redimensionner, supprimer et nettoyer des VM.
Ajoute la configuration SSH avec ProxyJump récursif, la redirection de port
et l'enregistrement des hôtes QEMU dans virt-manager.
Assisted-by: Claude Opus 4.8
2026-08-07 03:22:26 -04:00
|
|
|
|
"fr": "📁 Configurer sshfs",
|
|
|
|
|
|
"en": "📁 Configure sshfs",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Local": {
|
|
|
|
|
|
"fr": "💻 Local",
|
|
|
|
|
|
"en": "💻 Local",
|
|
|
|
|
|
},
|
|
|
|
|
|
"SSH (remote host)": {
|
|
|
|
|
|
"fr": "SSH (hôte distant)",
|
|
|
|
|
|
"en": "SSH (remote host)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Remote & services": {
|
|
|
|
|
|
"fr": "🌐 Distant & services",
|
|
|
|
|
|
"en": "🌐 Remote & services",
|
|
|
|
|
|
},
|
|
|
|
|
|
"SSH (remote host)...": {
|
|
|
|
|
|
"fr": "🔐 SSH (hôte distant)…",
|
|
|
|
|
|
"en": "🔐 SSH (remote host)...",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Deploy ERPLibre to a remote host over SSH!": {
|
|
|
|
|
|
"fr": "Déployer ERPLibre sur un hôte distant via SSH !",
|
|
|
|
|
|
"en": "Deploy ERPLibre to a remote host over SSH!",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Virtualization & notifications": {
|
|
|
|
|
|
"fr": "Virtualisation & notifications",
|
|
|
|
|
|
"en": "Virtualization & notifications",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Development": {
|
|
|
|
|
|
"fr": "🧰 Développement",
|
|
|
|
|
|
"en": "🧰 Development",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Data": {
|
|
|
|
|
|
"fr": "📊 Données",
|
|
|
|
|
|
"en": "📊 Data",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Sources & documentation": {
|
|
|
|
|
|
"fr": "📚 Sources & documentation",
|
|
|
|
|
|
"en": "📚 Sources & documentation",
|
|
|
|
|
|
},
|
|
|
|
|
|
"AI & automation": {
|
|
|
|
|
|
"fr": "🧠 IA & automatisation",
|
|
|
|
|
|
"en": "🧠 AI & automation",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Deployment, network & security": {
|
|
|
|
|
|
"fr": "🌐 Déploiement, réseau & sécurité",
|
|
|
|
|
|
"en": "🌐 Deployment, network & security",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Preferences": {
|
|
|
|
|
|
"fr": "🎨 Préférences",
|
|
|
|
|
|
"en": "🎨 Preferences",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Deployment": {
|
|
|
|
|
|
"fr": "🚀 Déploiement",
|
|
|
|
|
|
"en": "🚀 Deployment",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Manage": {
|
|
|
|
|
|
"fr": "🛠 Gérer",
|
|
|
|
|
|
"en": "🛠 Manage",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Catalog": {
|
|
|
|
|
|
"fr": "📚 Catalogue",
|
|
|
|
|
|
"en": "📚 Catalog",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Open the console on a VM": {
|
|
|
|
|
|
"fr": "🖥 Ouvrir la console d'une VM",
|
|
|
|
|
|
"en": "🖥 Open the console on a VM",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Backup": {
|
|
|
|
|
|
"fr": "Sauvegarde",
|
|
|
|
|
|
"en": "Backup",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Restore": {
|
|
|
|
|
|
"fr": "Restauration",
|
|
|
|
|
|
"en": "Restore",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Danger zone": {
|
|
|
|
|
|
"fr": "Zone dangereuse",
|
|
|
|
|
|
"en": "Danger zone",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Setup": {
|
|
|
|
|
|
"fr": "Installation",
|
|
|
|
|
|
"en": "Setup",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Status": {
|
|
|
|
|
|
"fr": "Statut",
|
|
|
|
|
|
"en": "Status",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Optimize": {
|
|
|
|
|
|
"fr": "Optimisation",
|
|
|
|
|
|
"en": "Optimize",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Generate": {
|
|
|
|
|
|
"fr": "Génération",
|
|
|
|
|
|
"en": "Generate",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Advanced": {
|
|
|
|
|
|
"fr": "Avancé",
|
|
|
|
|
|
"en": "Advanced",
|
|
|
|
|
|
},
|
|
|
|
|
|
"To leave the console, press Ctrl+] (then Enter).": {
|
|
|
|
|
|
"fr": "Pour quitter la console, appuyez sur Ctrl+] (puis Entrée).",
|
|
|
|
|
|
"en": "To leave the console, press Ctrl+] (then Enter).",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Default login (if set at deploy): erplibre / erplibre": {
|
|
|
|
|
|
"fr": "Login par défaut (si défini au déploiement) : "
|
|
|
|
|
|
"erplibre / erplibre",
|
|
|
|
|
|
"en": "Default login (if set at deploy): erplibre / erplibre",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Console password for 'erplibre' (default: erplibre): ": {
|
|
|
|
|
|
"fr": "Mot de passe console pour « erplibre » (défaut : erplibre) : ",
|
|
|
|
|
|
"en": "Console password for 'erplibre' (default: erplibre): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Console/SSH login:": {
|
|
|
|
|
|
"fr": "Connexion console/SSH :",
|
|
|
|
|
|
"en": "Console/SSH login:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Default login:": {
|
|
|
|
|
|
"fr": "Login par défaut :",
|
|
|
|
|
|
"en": "Default login:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Add this VM to ~/.ssh/config? (y/N): ": {
|
|
|
|
|
|
"fr": "Ajouter cette VM à ~/.ssh/config ? (o/N, défaut : non) : ",
|
|
|
|
|
|
"en": "Add this VM to ~/.ssh/config? (y/N, default: no): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Add each VM to ~/.ssh/config? (Y/n): ": {
|
|
|
|
|
|
"fr": "Ajouter chaque VM à ~/.ssh/config ? (O/n, défaut : oui) : ",
|
|
|
|
|
|
"en": "Add each VM to ~/.ssh/config? (Y/n, default: yes): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Waiting for the VM IP (DHCP lease)...": {
|
|
|
|
|
|
"fr": "Attente de l'IP de la VM (bail DHCP)...",
|
|
|
|
|
|
"en": "Waiting for the VM IP (DHCP lease)...",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No IP yet; add it later once the VM has booted.": {
|
|
|
|
|
|
"fr": "Pas encore d'IP ; à ajouter plus tard une fois la VM démarrée.",
|
|
|
|
|
|
"en": "No IP yet; add it later once the VM has booted.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Added to ~/.ssh/config:": {
|
|
|
|
|
|
"fr": "Ajouté à ~/.ssh/config :",
|
|
|
|
|
|
"en": "Added to ~/.ssh/config:",
|
2026-03-22 22:28:24 -04:00
|
|
|
|
},
|
|
|
|
|
|
"SSH address input method": {
|
|
|
|
|
|
"fr": "Méthode de saisie de l'adresse SSH",
|
|
|
|
|
|
"en": "SSH address input method",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Manual entry": {
|
|
|
|
|
|
"fr": "Saisie manuelle",
|
|
|
|
|
|
"en": "Manual entry",
|
|
|
|
|
|
},
|
|
|
|
|
|
"From ~/.ssh/config": {
|
|
|
|
|
|
"fr": "Depuis ~/.ssh/config",
|
|
|
|
|
|
"en": "From ~/.ssh/config",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Your choice (1/2): ": {
|
|
|
|
|
|
"fr": "Votre choix (1/2) : ",
|
|
|
|
|
|
"en": "Your choice (1/2): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No SSH hosts found in ~/.ssh/config": {
|
|
|
|
|
|
"fr": "Aucun hôte SSH trouvé dans ~/.ssh/config",
|
|
|
|
|
|
"en": "No SSH hosts found in ~/.ssh/config",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Select SSH host number: ": {
|
|
|
|
|
|
"fr": "Numéro de l'hôte SSH à sélectionner : ",
|
|
|
|
|
|
"en": "Select SSH host number: ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Invalid selection!": {
|
|
|
|
|
|
"fr": "Sélection invalide!",
|
|
|
|
|
|
"en": "Invalid selection!",
|
|
|
|
|
|
},
|
|
|
|
|
|
"SSH host (e.g.: user@192.168.1.100): ": {
|
|
|
|
|
|
"fr": "Hôte SSH (ex: user@192.168.1.100) : ",
|
|
|
|
|
|
"en": "SSH host (e.g.: user@192.168.1.100): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Mounting sshfs on: ": {
|
|
|
|
|
|
"fr": "Montage sshfs sur : ",
|
|
|
|
|
|
"en": "Mounting sshfs on: ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Mounted on: ": {
|
|
|
|
|
|
"fr": "Monté sur : ",
|
|
|
|
|
|
"en": "Mounted on: ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"To unmount: ": {
|
|
|
|
|
|
"fr": "Pour démonter : ",
|
|
|
|
|
|
"en": "To unmount: ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Error mounting sshfs: ": {
|
|
|
|
|
|
"fr": "Erreur lors du montage sshfs : ",
|
|
|
|
|
|
"en": "Error mounting sshfs: ",
|
|
|
|
|
|
},
|
2026-03-12 02:51:01 -04:00
|
|
|
|
# RTK (Rust Token Killer)
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Manage RTK (Rust Token Killer) for token optimization!": {
|
2026-03-12 02:51:01 -04:00
|
|
|
|
"fr": "Gérer RTK (Rust Token Killer) pour optimiser les tokens!",
|
|
|
|
|
|
"en": "Manage RTK (Rust Token Killer) for token optimization!",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Install RTK": {
|
2026-03-12 02:51:01 -04:00
|
|
|
|
"fr": "Installer RTK",
|
|
|
|
|
|
"en": "Install RTK",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Check RTK version": {
|
2026-03-12 02:51:01 -04:00
|
|
|
|
"fr": "Vérifier la version de RTK",
|
|
|
|
|
|
"en": "Check RTK version",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Show cumulative token savings": {
|
2026-03-12 02:51:01 -04:00
|
|
|
|
"fr": "Afficher les économies de tokens cumulées",
|
|
|
|
|
|
"en": "Show cumulative token savings",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Discover optimization opportunities": {
|
2026-03-12 02:51:01 -04:00
|
|
|
|
"fr": "Identifier les opportunités d'optimisation",
|
|
|
|
|
|
"en": "Discover optimization opportunities",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Initialize global auto-rewrite hook": {
|
2026-03-12 02:51:01 -04:00
|
|
|
|
"fr": "Initialiser le hook auto-rewrite global",
|
|
|
|
|
|
"en": "Initialize global auto-rewrite hook",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Check RTK status": {
|
2026-03-12 02:51:01 -04:00
|
|
|
|
"fr": "Vérifier le statut de RTK",
|
|
|
|
|
|
"en": "Check RTK status",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"RTK is not installed. Use option 1 to install it.": {
|
2026-03-12 02:51:01 -04:00
|
|
|
|
"fr": "RTK n'est pas installé. Utilisez l'option 1 pour l'installer.",
|
|
|
|
|
|
"en": "RTK is not installed. Use option 1 to install it.",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"RTK is installed, version: ": {
|
2026-03-12 02:51:01 -04:00
|
|
|
|
"fr": "RTK est installé, version : ",
|
|
|
|
|
|
"en": "RTK is installed, version: ",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Global auto-rewrite hook: active": {
|
2026-03-12 02:51:01 -04:00
|
|
|
|
"fr": "Hook auto-rewrite global : actif",
|
|
|
|
|
|
"en": "Global auto-rewrite hook: active",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Global auto-rewrite hook: inactive": {
|
2026-03-12 02:51:01 -04:00
|
|
|
|
"fr": "Hook auto-rewrite global : inactif",
|
|
|
|
|
|
"en": "Global auto-rewrite hook: inactive",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Installation method:": {
|
2026-03-12 02:51:01 -04:00
|
|
|
|
"fr": "Méthode d'installation :",
|
|
|
|
|
|
"en": "Installation method:",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"curl - Automatic install script": {
|
2026-03-12 02:51:01 -04:00
|
|
|
|
"fr": "curl - Script d'installation automatique",
|
|
|
|
|
|
"en": "curl - Automatic install script",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"brew - Homebrew (macOS/Linux)": {
|
2026-03-12 02:51:01 -04:00
|
|
|
|
"fr": "brew - Homebrew (macOS/Linux)",
|
|
|
|
|
|
"en": "brew - Homebrew (macOS/Linux)",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"cargo - Build from source (Rust required)": {
|
2026-03-12 02:51:01 -04:00
|
|
|
|
"fr": "cargo - Compilation depuis les sources (Rust requis)",
|
|
|
|
|
|
"en": "cargo - Build from source (Rust required)",
|
|
|
|
|
|
},
|
2026-03-07 02:59:07 -05:00
|
|
|
|
# Prompts and messages
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Enter your password: ": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Entrez votre mot de passe : ",
|
|
|
|
|
|
"en": "Enter your password: ",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Write your question ": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Écrit moi ta question ",
|
|
|
|
|
|
"en": "Write your question ",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Do you want a new instance?": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Voulez-vous une nouvelle instance?",
|
|
|
|
|
|
"en": "Do you want a new instance?",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"SSH port-forwarding": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "SSH port-forwarding",
|
|
|
|
|
|
"en": "SSH port-forwarding",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Network performance request per second": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Performance réseau en requêtes par seconde",
|
|
|
|
|
|
"en": "Network performance request per second",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Setup queue job for parallelism": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Configurer la file d'attente pour l'exécution parallèle",
|
|
|
|
|
|
"en": "Setup queue job for parallelism",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Choose your database": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Choisir sa base de données",
|
|
|
|
|
|
"en": "Choose your database",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Development update": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Mise à jour du développement",
|
|
|
|
|
|
"en": "Development update",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"What do you need for development?": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Qu'avez-vous de besoin pour développer?",
|
|
|
|
|
|
"en": "What do you need for development?",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Looking for documentation?": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Vous cherchez de la documentation?",
|
|
|
|
|
|
"en": "Looking for documentation?",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Make changes to databases!": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Faites des modifications sur les bases de données!",
|
|
|
|
|
|
"en": "Make changes to databases!",
|
|
|
|
|
|
},
|
2026-06-25 02:25:40 -04:00
|
|
|
|
# Database drop / erase
|
|
|
|
|
|
"Erase a database": {
|
|
|
|
|
|
"fr": "Effacer une base de données",
|
|
|
|
|
|
"en": "Erase a database",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Erase a database — irreversible operation!": {
|
|
|
|
|
|
"fr": "Effacer une base de données — opération irréversible !",
|
|
|
|
|
|
"en": "Erase a database — irreversible operation!",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Erase ALL databases (make db_drop_all)": {
|
|
|
|
|
|
"fr": "Effacer TOUTES les bases de données (make db_drop_all)",
|
|
|
|
|
|
"en": "Erase ALL databases (make db_drop_all)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Erase a single database": {
|
|
|
|
|
|
"fr": "Effacer une seule base de données",
|
|
|
|
|
|
"en": "Erase a single database",
|
|
|
|
|
|
},
|
|
|
|
|
|
"You are about to erase ALL databases. This cannot be undone.": {
|
|
|
|
|
|
"fr": (
|
|
|
|
|
|
"Tu es sur le point d'effacer TOUTES les bases de données."
|
|
|
|
|
|
" C'est irréversible."
|
|
|
|
|
|
),
|
|
|
|
|
|
"en": "You are about to erase ALL databases. This cannot be undone.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"You are about to erase the database '{database}'. This cannot be undone.": {
|
|
|
|
|
|
"fr": (
|
|
|
|
|
|
"Tu es sur le point d'effacer la base de données « {database} »."
|
|
|
|
|
|
" C'est irréversible."
|
|
|
|
|
|
),
|
|
|
|
|
|
"en": (
|
|
|
|
|
|
"You are about to erase the database '{database}'."
|
|
|
|
|
|
" This cannot be undone."
|
|
|
|
|
|
),
|
|
|
|
|
|
},
|
|
|
|
|
|
"Type 'oui' to confirm (default: no): ": {
|
|
|
|
|
|
"fr": "Tape « oui » pour confirmer (défaut : non) : ",
|
|
|
|
|
|
"en": "Type 'oui'/'yes' to confirm (default: no): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Database deletion cancelled.": {
|
|
|
|
|
|
"fr": "Effacement de la base de données annulé.",
|
|
|
|
|
|
"en": "Database deletion cancelled.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No database selected.": {
|
|
|
|
|
|
"fr": "Aucune base de données sélectionnée.",
|
|
|
|
|
|
"en": "No database selected.",
|
|
|
|
|
|
},
|
[ADD] todo: QEMU/KVM menu
Deploy, list, test, resize, delete and clean up VMs. Adds the SSH
configuration with recursive ProxyJump, port forwarding, and registration
of the QEMU hosts in virt-manager.
--- FR ---
Déployer, lister, tester, redimensionner, supprimer et nettoyer des VM.
Ajoute la configuration SSH avec ProxyJump récursif, la redirection de port
et l'enregistrement des hôtes QEMU dans virt-manager.
Assisted-by: Claude Opus 4.8
2026-08-07 03:22:26 -04:00
|
|
|
|
"Cannot list the databases (exit code): ": {
|
|
|
|
|
|
"fr": "Impossible de lister les bases (code de retour) : ",
|
|
|
|
|
|
"en": "Cannot list the databases (exit code): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Is PostgreSQL running?": {
|
|
|
|
|
|
"fr": "PostgreSQL est-il démarré ?",
|
|
|
|
|
|
"en": "Is PostgreSQL running?",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No database on this PostgreSQL server.": {
|
|
|
|
|
|
"fr": "Aucune base sur ce serveur PostgreSQL.",
|
|
|
|
|
|
"en": "No database on this PostgreSQL server.",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Manage execution processes!": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Manipuler les processus d'exécution!",
|
|
|
|
|
|
"en": "Manage execution processes!",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Manage ERPLibre and Odoo configuration!": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Manipuler la configuration ERPLibre et Odoo!",
|
|
|
|
|
|
"en": "Manage ERPLibre and Odoo configuration!",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Network tools!": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Outil réseautique!",
|
|
|
|
|
|
"en": "Network tools!",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Dependency security audit!": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Audit de securite des dépendances!",
|
|
|
|
|
|
"en": "Dependency security audit!",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"The Bash script failed with return code": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Le script Bash a échoué avec le code de retour",
|
|
|
|
|
|
"en": "The Bash script failed with return code",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"No installed environment found. Install an Odoo version first.": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Aucun environnement installe trouve. Installez d'abord une version d'Odoo.",
|
|
|
|
|
|
"en": "No installed environment found. Install an Odoo version first.",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Choose an environment for the audit:": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Choisir un environnement pour l'audit :",
|
|
|
|
|
|
"en": "Choose an environment for the audit:",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Select: ": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Sélection : ",
|
|
|
|
|
|
"en": "Select: ",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Error, cannot understand value": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Erreur, impossible de comprendre la valeur",
|
|
|
|
|
|
"en": "Error, cannot understand value",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Dependencies file not found: ": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Fichier de dépendances introuvable : ",
|
|
|
|
|
|
"en": "Dependencies file not found: ",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Execution: ": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Execution : ",
|
|
|
|
|
|
"en": "Execution: ",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Current": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Actuel",
|
|
|
|
|
|
"en": "Current",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Default": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Défaut",
|
|
|
|
|
|
"en": "Default",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Reboot TODO ...": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Reboot TODO ...",
|
|
|
|
|
|
"en": "Reboot TODO ...",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"pip-audit - Check vulnerabilities on Python environments": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "pip-audit - Verifier les vulnérabilités sur les environnements Python",
|
|
|
|
|
|
"en": "pip-audit - Check vulnerabilities on Python environments",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Will execute:": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Va exécuter :",
|
|
|
|
|
|
"en": "Will execute:",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Choose a version:": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Choisir une version :",
|
|
|
|
|
|
"en": "Choose a version:",
|
|
|
|
|
|
},
|
2026-03-30 14:46:56 -04:00
|
|
|
|
"Install type:": {
|
|
|
|
|
|
"fr": "Type d'installation :",
|
|
|
|
|
|
"en": "Install type:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Standard install (without extra modules)": {
|
|
|
|
|
|
"fr": "Installation standard (sans modules extra)",
|
|
|
|
|
|
"en": "Standard install (without extra modules)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Install with extra modules (CybroOdoo - large, slow)": {
|
|
|
|
|
|
"fr": "Installation avec modules extra (CybroOdoo - gros, lent)",
|
|
|
|
|
|
"en": "Install with extra modules (CybroOdoo - large, slow)",
|
|
|
|
|
|
},
|
2026-03-07 02:59:07 -05:00
|
|
|
|
# todo.json translatable prompt_descriptions
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Test - Minimal base instance": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Test - Instance de base minimale",
|
|
|
|
|
|
"en": "Test - Minimal base instance",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Open RobotLibre 🤖 minimal": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Ouvrir RobotLibre 🤖 minimal",
|
|
|
|
|
|
"en": "Open RobotLibre 🤖 minimal",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Open RobotLibre 🤖 with search enabled": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Ouvrir RobotLibre 🤖 en activant la recherche",
|
|
|
|
|
|
"en": "Open RobotLibre 🤖 with search enabled",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Open ERPLibre with TODO 🤖": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Ouvrir ERPLibre avec TODO 🤖",
|
|
|
|
|
|
"en": "Open ERPLibre with TODO 🤖",
|
|
|
|
|
|
},
|
2026-08-07 03:22:26 -04:00
|
|
|
|
"Update all erplibre_base on database test": {
|
|
|
|
|
|
"fr": "🔄 Mise à jour de tous les erplibre_base sur la base de données test",
|
|
|
|
|
|
"en": "🔄 Update all erplibre_base on database test",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Show code status": {
|
|
|
|
|
|
"fr": "🔍 Afficher le statut du code",
|
|
|
|
|
|
"en": "🔍 Show code status",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Stash all code": {
|
|
|
|
|
|
"fr": "📦 Remiser tout le code",
|
|
|
|
|
|
"en": "📦 Stash all code",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Format modified code": {
|
|
|
|
|
|
"fr": "🎨 Formater le code modifié",
|
|
|
|
|
|
"en": "🎨 Format modified code",
|
|
|
|
|
|
},
|
2026-03-07 02:59:07 -05:00
|
|
|
|
# todo.py hardcoded prompt_descriptions
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Mobile - Compile and run software": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Mobile - Compiler et exécuter le logiciel",
|
|
|
|
|
|
"en": "Mobile - Compile and run software",
|
|
|
|
|
|
},
|
2026-08-07 03:22:26 -04:00
|
|
|
|
"Upgrade Odoo - Migration Database": {
|
|
|
|
|
|
"fr": "🚚 Mise à jour Odoo - Migration de base de données",
|
|
|
|
|
|
"en": "🚚 Upgrade Odoo - Migration Database",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Upgrade Poetry - Dependency of Odoo": {
|
|
|
|
|
|
"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",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Migration module coverage": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Couverture de migration des modules",
|
|
|
|
|
|
"en": "Migration module coverage",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"What change between version": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Quels changements entre les versions",
|
|
|
|
|
|
"en": "What change between version",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"OCA guidelines": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Directives OCA",
|
|
|
|
|
|
"en": "OCA guidelines",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"OCA migration Odoo 19 milestone": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Migration OCA Odoo 19 - Jalons",
|
|
|
|
|
|
"en": "OCA migration Odoo 19 milestone",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Download database to create backup (.zip)": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Télécharger une base de données pour créer une sauvegarde (.zip)",
|
|
|
|
|
|
"en": "Download database to create backup (.zip)",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Restore from backup (.zip)": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Restaurer a partir d'une sauvegarde (.zip)",
|
|
|
|
|
|
"en": "Restore from backup (.zip)",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Create backup (.zip)": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Créer une sauvegarde (.zip)",
|
|
|
|
|
|
"en": "Create backup (.zip)",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Kill Odoo process from actual port": {
|
2026-03-09 16:34:43 -04:00
|
|
|
|
"fr": "Terminer le processus Odoo du port actuel",
|
|
|
|
|
|
"en": "Kill Odoo process from actual port",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Kill git daemon server process": {
|
2026-03-09 16:34:43 -04:00
|
|
|
|
"fr": "Terminer le processus du serveur git daemon",
|
|
|
|
|
|
"en": "Kill git daemon server process",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Git daemon process killed.": {
|
2026-03-09 16:34:43 -04:00
|
|
|
|
"fr": "Processus git daemon terminé.",
|
|
|
|
|
|
"en": "Git daemon process killed.",
|
2026-03-07 02:59:07 -05:00
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Generate all configuration": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Générer toute la configuration",
|
|
|
|
|
|
"en": "Generate all configuration",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Generate from pre-configuration": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Générer a partir de la pre-configuration",
|
|
|
|
|
|
"en": "Generate from pre-configuration",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Generate from backup file": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Générer a partir d'un fichier de sauvegarde",
|
|
|
|
|
|
"en": "Generate from backup file",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Generate from database": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Générer a partir de la base de données",
|
|
|
|
|
|
"en": "Generate from database",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"base": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "base",
|
|
|
|
|
|
"en": "base",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"base + code_generator": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "base + code_generator",
|
|
|
|
|
|
"en": "base + code_generator",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"base + image_db": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "base + image_db",
|
|
|
|
|
|
"en": "base + image_db",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"all": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "tout",
|
|
|
|
|
|
"en": "all",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Debug todo.py": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Débogage todo.py",
|
|
|
|
|
|
"en": "Debug todo.py",
|
|
|
|
|
|
},
|
2026-08-07 03:22:26 -04:00
|
|
|
|
# Test section
|
|
|
|
|
|
"Test - Test an Odoo module": {
|
|
|
|
|
|
"fr": "🧪 Test - Tester un module Odoo",
|
|
|
|
|
|
"en": "🧪 Test - Test an Odoo module",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Test an Odoo module on a temporary database!": {
|
2026-03-08 15:53:51 -04:00
|
|
|
|
"fr": "Tester un module Odoo sur une base de données temporaire!",
|
|
|
|
|
|
"en": "Test an Odoo module on a temporary database!",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Test a module": {
|
2026-03-08 15:53:51 -04:00
|
|
|
|
"fr": "Tester un module",
|
|
|
|
|
|
"en": "Test a module",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Test a module with code coverage": {
|
2026-03-08 15:53:51 -04:00
|
|
|
|
"fr": "Tester un module avec couverture de code",
|
|
|
|
|
|
"en": "Test a module with code coverage",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"ERPLibre unit tests": {
|
2026-03-10 01:44:10 -04:00
|
|
|
|
"fr": "Tests unitaires ERPLibre",
|
|
|
|
|
|
"en": "ERPLibre unit tests",
|
|
|
|
|
|
},
|
2026-08-10 03:10:50 -04:00
|
|
|
|
"Mail unit tests": {
|
|
|
|
|
|
"fr": "Tests unitaires courriel",
|
|
|
|
|
|
"en": "Mail unit tests",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Analyse unit tests": {
|
|
|
|
|
|
"fr": "Tests unitaires analyse",
|
|
|
|
|
|
"en": "Analyse unit tests",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Running unit tests": {
|
2026-03-10 01:44:10 -04:00
|
|
|
|
"fr": "Exécution des tests unitaires",
|
|
|
|
|
|
"en": "Running unit tests",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"All unit tests passed": {
|
2026-03-10 01:44:10 -04:00
|
|
|
|
"fr": "Tous les tests unitaires ont réussi",
|
|
|
|
|
|
"en": "All unit tests passed",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Some unit tests failed, exit code": {
|
2026-03-10 01:44:10 -04:00
|
|
|
|
"fr": "Des tests unitaires ont échoué, code de sortie",
|
|
|
|
|
|
"en": "Some unit tests failed, exit code",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Module name to test: ": {
|
2026-03-08 15:53:51 -04:00
|
|
|
|
"fr": "Nom du module à tester : ",
|
|
|
|
|
|
"en": "Module name to test: ",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Temporary database name (default: test_todo_tmp): ": {
|
2026-03-08 15:53:51 -04:00
|
|
|
|
"fr": "Nom de la base de données temporaire (défaut: test_todo_tmp) : ",
|
|
|
|
|
|
"en": "Temporary database name (default: test_todo_tmp): ",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Extra modules to install (comma-separated, empty for none): ": {
|
2026-03-08 15:53:51 -04:00
|
|
|
|
"fr": "Modules supplémentaires à installer (séparés par des virgules, vide pour aucun) : ",
|
|
|
|
|
|
"en": "Extra modules to install (comma-separated, empty for none): ",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Log level (default: test): ": {
|
2026-03-08 15:53:51 -04:00
|
|
|
|
"fr": "Niveau de log (défaut: test) : ",
|
|
|
|
|
|
"en": "Log level (default: test): ",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Creating temporary database": {
|
2026-03-08 15:53:51 -04:00
|
|
|
|
"fr": "Création de la base de données temporaire",
|
|
|
|
|
|
"en": "Creating temporary database",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Installing modules": {
|
2026-03-08 15:53:51 -04:00
|
|
|
|
"fr": "Installation des modules",
|
|
|
|
|
|
"en": "Installing modules",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Running tests": {
|
2026-03-08 15:53:51 -04:00
|
|
|
|
"fr": "Exécution des tests",
|
|
|
|
|
|
"en": "Running tests",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Cleaning up temporary database": {
|
2026-03-08 15:53:51 -04:00
|
|
|
|
"fr": "Suppression de la base de données temporaire",
|
|
|
|
|
|
"en": "Cleaning up temporary database",
|
|
|
|
|
|
},
|
2026-08-07 03:22:26 -04:00
|
|
|
|
"Keep the temporary database? (y/N): ": {
|
|
|
|
|
|
"fr": "Conserver la base de données temporaire? (o/N, défaut : non) : ",
|
|
|
|
|
|
"en": "Keep the temporary database? (y/N, default: no): ",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Database kept": {
|
2026-03-08 15:53:51 -04:00
|
|
|
|
"fr": "Base de données conservée",
|
|
|
|
|
|
"en": "Database kept",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Tests completed successfully!": {
|
2026-03-08 15:53:51 -04:00
|
|
|
|
"fr": "Tests terminés avec succès!",
|
|
|
|
|
|
"en": "Tests completed successfully!",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Tests failed with return code": {
|
2026-03-08 15:53:51 -04:00
|
|
|
|
"fr": "Les tests ont échoué avec le code de retour",
|
|
|
|
|
|
"en": "Tests failed with return code",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Module name is required!": {
|
2026-03-08 15:53:51 -04:00
|
|
|
|
"fr": "Le nom du module est requis!",
|
|
|
|
|
|
"en": "Module name is required!",
|
|
|
|
|
|
},
|
2026-08-07 03:22:26 -04:00
|
|
|
|
# Git section
|
|
|
|
|
|
"Git - Git tools": {
|
|
|
|
|
|
"fr": "🌿 Git - Outils Git",
|
|
|
|
|
|
"en": "🌿 Git - Git tools",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Git management tools!": {
|
2026-03-09 16:34:43 -04:00
|
|
|
|
"fr": "Outils de gestion Git!",
|
|
|
|
|
|
"en": "Git management tools!",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Local git server": {
|
2026-03-09 16:34:43 -04:00
|
|
|
|
"fr": "Serveur git local",
|
|
|
|
|
|
"en": "Local git server",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Manage local git repository server!": {
|
2026-03-09 16:34:43 -04:00
|
|
|
|
"fr": "Gérer le serveur de dépôts git local!",
|
|
|
|
|
|
"en": "Manage local git repository server!",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Deploy a local git server (~/.git-server)": {
|
2026-03-09 16:34:43 -04:00
|
|
|
|
"fr": "Déployer un serveur git local (~/.git-server)",
|
|
|
|
|
|
"en": "Deploy a local git server (~/.git-server)",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Deploy a production git server (/srv/git, root required)": {
|
2026-03-09 16:34:43 -04:00
|
|
|
|
"fr": "Déployer un serveur git production (/srv/git, root requis)",
|
|
|
|
|
|
"en": "Deploy a production git server (/srv/git, root required)",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Starting git server deployment...": {
|
2026-03-09 16:34:43 -04:00
|
|
|
|
"fr": "Démarrage du déploiement du serveur git...",
|
|
|
|
|
|
"en": "Starting git server deployment...",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Local mode (~/.git-server)": {
|
2026-03-09 16:34:43 -04:00
|
|
|
|
"fr": "Mode local (~/.git-server)",
|
|
|
|
|
|
"en": "Local mode (~/.git-server)",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Production mode (/srv/git, root required)": {
|
2026-03-09 16:34:43 -04:00
|
|
|
|
"fr": "Mode production (/srv/git, root requis)",
|
|
|
|
|
|
"en": "Production mode (/srv/git, root required)",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Run all (init + remote + push + serve)": {
|
2026-03-09 16:34:43 -04:00
|
|
|
|
"fr": "Tout exécuter (init + remote + push + serve)",
|
|
|
|
|
|
"en": "Run all (init + remote + push + serve)",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Init - Create bare repos": {
|
2026-03-09 16:34:43 -04:00
|
|
|
|
"fr": "Init - Créer les bare repos",
|
|
|
|
|
|
"en": "Init - Create bare repos",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Remote - Add local remotes": {
|
2026-03-09 16:34:43 -04:00
|
|
|
|
"fr": "Remote - Ajouter les remotes locaux",
|
|
|
|
|
|
"en": "Remote - Add local remotes",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Push - Push to local server": {
|
2026-03-09 16:34:43 -04:00
|
|
|
|
"fr": "Push - Pousser vers le serveur local",
|
|
|
|
|
|
"en": "Push - Push to local server",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Serve - Start git daemon": {
|
2026-03-09 16:34:43 -04:00
|
|
|
|
"fr": "Serve - Démarrer le daemon git",
|
|
|
|
|
|
"en": "Serve - Start git daemon",
|
|
|
|
|
|
},
|
2026-03-12 04:04:52 -04:00
|
|
|
|
# Git remote add
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Add a remote to a local repository": {
|
2026-03-12 04:04:52 -04:00
|
|
|
|
"fr": "Ajouter un remote vers un dépôt local",
|
|
|
|
|
|
"en": "Add a remote to a local repository",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Remote name (default: localhost): ": {
|
2026-03-12 04:04:52 -04:00
|
|
|
|
"fr": "Nom du remote (défaut: localhost) : ",
|
|
|
|
|
|
"en": "Remote name (default: localhost): ",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Repository address (e.g.: git://192.168.1.100/my-repo.git): ": {
|
2026-03-12 04:04:52 -04:00
|
|
|
|
"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): ",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Repository address is required!": {
|
2026-03-12 04:04:52 -04:00
|
|
|
|
"fr": "L'adresse du dépôt est requise!",
|
|
|
|
|
|
"en": "Repository address is required!",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Remote added successfully!": {
|
2026-03-12 04:04:52 -04:00
|
|
|
|
"fr": "Remote ajouté avec succès!",
|
|
|
|
|
|
"en": "Remote added successfully!",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Error adding remote: ": {
|
2026-03-12 04:04:52 -04:00
|
|
|
|
"fr": "Erreur lors de l'ajout du remote : ",
|
|
|
|
|
|
"en": "Error adding remote: ",
|
|
|
|
|
|
},
|
|
|
|
|
|
# Git config vim
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Configure git local editor to vim": {
|
2026-03-12 04:04:52 -04:00
|
|
|
|
"fr": "Configuration git local par vim",
|
|
|
|
|
|
"en": "Configure git local editor to vim",
|
|
|
|
|
|
},
|
2026-03-22 22:21:16 -04:00
|
|
|
|
"Generate git patch to /tmp": {
|
|
|
|
|
|
"fr": "Générer une patch git dans /tmp",
|
|
|
|
|
|
"en": "Generate git patch to /tmp",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Git editor configured to vim successfully!": {
|
2026-03-12 04:04:52 -04:00
|
|
|
|
"fr": "Éditeur git configuré sur vim avec succès!",
|
|
|
|
|
|
"en": "Git editor configured to vim successfully!",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Error during configuration: ": {
|
2026-03-12 04:04:52 -04:00
|
|
|
|
"fr": "Erreur lors de la configuration : ",
|
|
|
|
|
|
"en": "Error during configuration: ",
|
|
|
|
|
|
},
|
|
|
|
|
|
# GPT code - Claude automation
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Add an automation with Claude in todo.py": {
|
2026-03-12 04:04:52 -04:00
|
|
|
|
"fr": "Ajouter une automatisation avec Claude dans todo.py",
|
|
|
|
|
|
"en": "Add an automation with Claude in todo.py",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Description of the command to add: ": {
|
2026-03-12 04:04:52 -04:00
|
|
|
|
"fr": "Description de la commande à ajouter : ",
|
|
|
|
|
|
"en": "Description of the command to add: ",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Bash command to execute: ": {
|
2026-03-12 04:04:52 -04:00
|
|
|
|
"fr": "Commande bash à exécuter : ",
|
|
|
|
|
|
"en": "Bash command to execute: ",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Menu section (git/code/config/network/process): ": {
|
2026-03-12 04:04:52 -04:00
|
|
|
|
"fr": "Section du menu (git/code/config/network/process) : ",
|
|
|
|
|
|
"en": "Menu section (git/code/config/network/process): ",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Automation added successfully in todo.json!": {
|
2026-03-12 04:04:52 -04:00
|
|
|
|
"fr": "Automatisation ajoutée avec succès dans todo.json!",
|
|
|
|
|
|
"en": "Automation added successfully in todo.json!",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Error adding automation: ": {
|
2026-03-12 04:04:52 -04:00
|
|
|
|
"fr": "Erreur lors de l'ajout de l'automatisation : ",
|
|
|
|
|
|
"en": "Error adding automation: ",
|
|
|
|
|
|
},
|
[ADD] make: add SSH remote deployment targets and todo.py integration
Enable deploying and managing ERPLibre on remote servers via SSH
directly from make and the interactive todo.py CLI, since only
local deployment was previously supported.
- New conf/make.ssh.Makefile with 11 targets: ssh_check, ssh_push,
ssh_install, ssh_run, ssh_stop, ssh_restart, ssh_status, ssh_logs,
ssh_make, ssh_install_systemd, ssh_install_nginx
- Variables: SSH_HOST (required), SSH_USER, SSH_PORT, SSH_KEY,
SSH_PATH, SSH_TARGET, SSH_DOMAIN, SSH_ADMIN_EMAIL
- Execute > Deploy menu extended with 11 SSH options in todo.py
- All strings translated fr/en in todo_i18n.py
Generated by Claude Code 2.1.101 model claude-sonnet-4-6
Co-Authored-By: Mathieu Benoit <mathben@technolibre.ca>
2026-04-10 22:53:07 -04:00
|
|
|
|
# SSH Deploy section
|
|
|
|
|
|
"Deploy - Deploy ERPLibre via SSH": {
|
|
|
|
|
|
"fr": "Déploiement - Déployer ERPLibre via SSH",
|
|
|
|
|
|
"en": "Deploy - Deploy ERPLibre via SSH",
|
|
|
|
|
|
},
|
|
|
|
|
|
"SSH deployment tools!": {
|
|
|
|
|
|
"fr": "Outils de déploiement SSH!",
|
|
|
|
|
|
"en": "SSH deployment tools!",
|
|
|
|
|
|
},
|
|
|
|
|
|
"SSH - Check connection": {
|
[ADD] todo: QEMU/KVM menu
Deploy, list, test, resize, delete and clean up VMs. Adds the SSH
configuration with recursive ProxyJump, port forwarding, and registration
of the QEMU hosts in virt-manager.
--- FR ---
Déployer, lister, tester, redimensionner, supprimer et nettoyer des VM.
Ajoute la configuration SSH avec ProxyJump récursif, la redirection de port
et l'enregistrement des hôtes QEMU dans virt-manager.
Assisted-by: Claude Opus 4.8
2026-08-07 03:22:26 -04:00
|
|
|
|
"fr": "🔌 SSH - Vérifier la connexion",
|
|
|
|
|
|
"en": "🔌 SSH - Check connection",
|
[ADD] make: add SSH remote deployment targets and todo.py integration
Enable deploying and managing ERPLibre on remote servers via SSH
directly from make and the interactive todo.py CLI, since only
local deployment was previously supported.
- New conf/make.ssh.Makefile with 11 targets: ssh_check, ssh_push,
ssh_install, ssh_run, ssh_stop, ssh_restart, ssh_status, ssh_logs,
ssh_make, ssh_install_systemd, ssh_install_nginx
- Variables: SSH_HOST (required), SSH_USER, SSH_PORT, SSH_KEY,
SSH_PATH, SSH_TARGET, SSH_DOMAIN, SSH_ADMIN_EMAIL
- Execute > Deploy menu extended with 11 SSH options in todo.py
- All strings translated fr/en in todo_i18n.py
Generated by Claude Code 2.1.101 model claude-sonnet-4-6
Co-Authored-By: Mathieu Benoit <mathben@technolibre.ca>
2026-04-10 22:53:07 -04:00
|
|
|
|
},
|
|
|
|
|
|
"SSH - Sync files (rsync)": {
|
[ADD] todo: QEMU/KVM menu
Deploy, list, test, resize, delete and clean up VMs. Adds the SSH
configuration with recursive ProxyJump, port forwarding, and registration
of the QEMU hosts in virt-manager.
--- FR ---
Déployer, lister, tester, redimensionner, supprimer et nettoyer des VM.
Ajoute la configuration SSH avec ProxyJump récursif, la redirection de port
et l'enregistrement des hôtes QEMU dans virt-manager.
Assisted-by: Claude Opus 4.8
2026-08-07 03:22:26 -04:00
|
|
|
|
"fr": "🔄 SSH - Synchroniser les fichiers (rsync)",
|
|
|
|
|
|
"en": "🔄 SSH - Sync files (rsync)",
|
[ADD] make: add SSH remote deployment targets and todo.py integration
Enable deploying and managing ERPLibre on remote servers via SSH
directly from make and the interactive todo.py CLI, since only
local deployment was previously supported.
- New conf/make.ssh.Makefile with 11 targets: ssh_check, ssh_push,
ssh_install, ssh_run, ssh_stop, ssh_restart, ssh_status, ssh_logs,
ssh_make, ssh_install_systemd, ssh_install_nginx
- Variables: SSH_HOST (required), SSH_USER, SSH_PORT, SSH_KEY,
SSH_PATH, SSH_TARGET, SSH_DOMAIN, SSH_ADMIN_EMAIL
- Execute > Deploy menu extended with 11 SSH options in todo.py
- All strings translated fr/en in todo_i18n.py
Generated by Claude Code 2.1.101 model claude-sonnet-4-6
Co-Authored-By: Mathieu Benoit <mathben@technolibre.ca>
2026-04-10 22:53:07 -04:00
|
|
|
|
},
|
|
|
|
|
|
"SSH - Install ERPLibre": {
|
[ADD] todo: QEMU/KVM menu
Deploy, list, test, resize, delete and clean up VMs. Adds the SSH
configuration with recursive ProxyJump, port forwarding, and registration
of the QEMU hosts in virt-manager.
--- FR ---
Déployer, lister, tester, redimensionner, supprimer et nettoyer des VM.
Ajoute la configuration SSH avec ProxyJump récursif, la redirection de port
et l'enregistrement des hôtes QEMU dans virt-manager.
Assisted-by: Claude Opus 4.8
2026-08-07 03:22:26 -04:00
|
|
|
|
"fr": "📦 SSH - Installer ERPLibre",
|
|
|
|
|
|
"en": "📦 SSH - Install ERPLibre",
|
[ADD] make: add SSH remote deployment targets and todo.py integration
Enable deploying and managing ERPLibre on remote servers via SSH
directly from make and the interactive todo.py CLI, since only
local deployment was previously supported.
- New conf/make.ssh.Makefile with 11 targets: ssh_check, ssh_push,
ssh_install, ssh_run, ssh_stop, ssh_restart, ssh_status, ssh_logs,
ssh_make, ssh_install_systemd, ssh_install_nginx
- Variables: SSH_HOST (required), SSH_USER, SSH_PORT, SSH_KEY,
SSH_PATH, SSH_TARGET, SSH_DOMAIN, SSH_ADMIN_EMAIL
- Execute > Deploy menu extended with 11 SSH options in todo.py
- All strings translated fr/en in todo_i18n.py
Generated by Claude Code 2.1.101 model claude-sonnet-4-6
Co-Authored-By: Mathieu Benoit <mathben@technolibre.ca>
2026-04-10 22:53:07 -04:00
|
|
|
|
},
|
|
|
|
|
|
"SSH - Start Odoo": {
|
[ADD] todo: QEMU/KVM menu
Deploy, list, test, resize, delete and clean up VMs. Adds the SSH
configuration with recursive ProxyJump, port forwarding, and registration
of the QEMU hosts in virt-manager.
--- FR ---
Déployer, lister, tester, redimensionner, supprimer et nettoyer des VM.
Ajoute la configuration SSH avec ProxyJump récursif, la redirection de port
et l'enregistrement des hôtes QEMU dans virt-manager.
Assisted-by: Claude Opus 4.8
2026-08-07 03:22:26 -04:00
|
|
|
|
"fr": "🟢 SSH - Démarrer Odoo",
|
|
|
|
|
|
"en": "🟢 SSH - Start Odoo",
|
[ADD] make: add SSH remote deployment targets and todo.py integration
Enable deploying and managing ERPLibre on remote servers via SSH
directly from make and the interactive todo.py CLI, since only
local deployment was previously supported.
- New conf/make.ssh.Makefile with 11 targets: ssh_check, ssh_push,
ssh_install, ssh_run, ssh_stop, ssh_restart, ssh_status, ssh_logs,
ssh_make, ssh_install_systemd, ssh_install_nginx
- Variables: SSH_HOST (required), SSH_USER, SSH_PORT, SSH_KEY,
SSH_PATH, SSH_TARGET, SSH_DOMAIN, SSH_ADMIN_EMAIL
- Execute > Deploy menu extended with 11 SSH options in todo.py
- All strings translated fr/en in todo_i18n.py
Generated by Claude Code 2.1.101 model claude-sonnet-4-6
Co-Authored-By: Mathieu Benoit <mathben@technolibre.ca>
2026-04-10 22:53:07 -04:00
|
|
|
|
},
|
|
|
|
|
|
"SSH - Stop Odoo": {
|
[ADD] todo: QEMU/KVM menu
Deploy, list, test, resize, delete and clean up VMs. Adds the SSH
configuration with recursive ProxyJump, port forwarding, and registration
of the QEMU hosts in virt-manager.
--- FR ---
Déployer, lister, tester, redimensionner, supprimer et nettoyer des VM.
Ajoute la configuration SSH avec ProxyJump récursif, la redirection de port
et l'enregistrement des hôtes QEMU dans virt-manager.
Assisted-by: Claude Opus 4.8
2026-08-07 03:22:26 -04:00
|
|
|
|
"fr": "🔴 SSH - Arrêter Odoo",
|
|
|
|
|
|
"en": "🔴 SSH - Stop Odoo",
|
[ADD] make: add SSH remote deployment targets and todo.py integration
Enable deploying and managing ERPLibre on remote servers via SSH
directly from make and the interactive todo.py CLI, since only
local deployment was previously supported.
- New conf/make.ssh.Makefile with 11 targets: ssh_check, ssh_push,
ssh_install, ssh_run, ssh_stop, ssh_restart, ssh_status, ssh_logs,
ssh_make, ssh_install_systemd, ssh_install_nginx
- Variables: SSH_HOST (required), SSH_USER, SSH_PORT, SSH_KEY,
SSH_PATH, SSH_TARGET, SSH_DOMAIN, SSH_ADMIN_EMAIL
- Execute > Deploy menu extended with 11 SSH options in todo.py
- All strings translated fr/en in todo_i18n.py
Generated by Claude Code 2.1.101 model claude-sonnet-4-6
Co-Authored-By: Mathieu Benoit <mathben@technolibre.ca>
2026-04-10 22:53:07 -04:00
|
|
|
|
},
|
|
|
|
|
|
"SSH - Restart Odoo": {
|
[ADD] todo: QEMU/KVM menu
Deploy, list, test, resize, delete and clean up VMs. Adds the SSH
configuration with recursive ProxyJump, port forwarding, and registration
of the QEMU hosts in virt-manager.
--- FR ---
Déployer, lister, tester, redimensionner, supprimer et nettoyer des VM.
Ajoute la configuration SSH avec ProxyJump récursif, la redirection de port
et l'enregistrement des hôtes QEMU dans virt-manager.
Assisted-by: Claude Opus 4.8
2026-08-07 03:22:26 -04:00
|
|
|
|
"fr": "🔁 SSH - Redémarrer Odoo",
|
|
|
|
|
|
"en": "🔁 SSH - Restart Odoo",
|
[ADD] make: add SSH remote deployment targets and todo.py integration
Enable deploying and managing ERPLibre on remote servers via SSH
directly from make and the interactive todo.py CLI, since only
local deployment was previously supported.
- New conf/make.ssh.Makefile with 11 targets: ssh_check, ssh_push,
ssh_install, ssh_run, ssh_stop, ssh_restart, ssh_status, ssh_logs,
ssh_make, ssh_install_systemd, ssh_install_nginx
- Variables: SSH_HOST (required), SSH_USER, SSH_PORT, SSH_KEY,
SSH_PATH, SSH_TARGET, SSH_DOMAIN, SSH_ADMIN_EMAIL
- Execute > Deploy menu extended with 11 SSH options in todo.py
- All strings translated fr/en in todo_i18n.py
Generated by Claude Code 2.1.101 model claude-sonnet-4-6
Co-Authored-By: Mathieu Benoit <mathben@technolibre.ca>
2026-04-10 22:53:07 -04:00
|
|
|
|
},
|
|
|
|
|
|
"SSH - Service status": {
|
[ADD] todo: QEMU/KVM menu
Deploy, list, test, resize, delete and clean up VMs. Adds the SSH
configuration with recursive ProxyJump, port forwarding, and registration
of the QEMU hosts in virt-manager.
--- FR ---
Déployer, lister, tester, redimensionner, supprimer et nettoyer des VM.
Ajoute la configuration SSH avec ProxyJump récursif, la redirection de port
et l'enregistrement des hôtes QEMU dans virt-manager.
Assisted-by: Claude Opus 4.8
2026-08-07 03:22:26 -04:00
|
|
|
|
"fr": "📊 SSH - Statut du service",
|
|
|
|
|
|
"en": "📊 SSH - Service status",
|
[ADD] make: add SSH remote deployment targets and todo.py integration
Enable deploying and managing ERPLibre on remote servers via SSH
directly from make and the interactive todo.py CLI, since only
local deployment was previously supported.
- New conf/make.ssh.Makefile with 11 targets: ssh_check, ssh_push,
ssh_install, ssh_run, ssh_stop, ssh_restart, ssh_status, ssh_logs,
ssh_make, ssh_install_systemd, ssh_install_nginx
- Variables: SSH_HOST (required), SSH_USER, SSH_PORT, SSH_KEY,
SSH_PATH, SSH_TARGET, SSH_DOMAIN, SSH_ADMIN_EMAIL
- Execute > Deploy menu extended with 11 SSH options in todo.py
- All strings translated fr/en in todo_i18n.py
Generated by Claude Code 2.1.101 model claude-sonnet-4-6
Co-Authored-By: Mathieu Benoit <mathben@technolibre.ca>
2026-04-10 22:53:07 -04:00
|
|
|
|
},
|
|
|
|
|
|
"SSH - View logs": {
|
[ADD] todo: QEMU/KVM menu
Deploy, list, test, resize, delete and clean up VMs. Adds the SSH
configuration with recursive ProxyJump, port forwarding, and registration
of the QEMU hosts in virt-manager.
--- FR ---
Déployer, lister, tester, redimensionner, supprimer et nettoyer des VM.
Ajoute la configuration SSH avec ProxyJump récursif, la redirection de port
et l'enregistrement des hôtes QEMU dans virt-manager.
Assisted-by: Claude Opus 4.8
2026-08-07 03:22:26 -04:00
|
|
|
|
"fr": "📜 SSH - Voir les logs",
|
|
|
|
|
|
"en": "📜 SSH - View logs",
|
[ADD] make: add SSH remote deployment targets and todo.py integration
Enable deploying and managing ERPLibre on remote servers via SSH
directly from make and the interactive todo.py CLI, since only
local deployment was previously supported.
- New conf/make.ssh.Makefile with 11 targets: ssh_check, ssh_push,
ssh_install, ssh_run, ssh_stop, ssh_restart, ssh_status, ssh_logs,
ssh_make, ssh_install_systemd, ssh_install_nginx
- Variables: SSH_HOST (required), SSH_USER, SSH_PORT, SSH_KEY,
SSH_PATH, SSH_TARGET, SSH_DOMAIN, SSH_ADMIN_EMAIL
- Execute > Deploy menu extended with 11 SSH options in todo.py
- All strings translated fr/en in todo_i18n.py
Generated by Claude Code 2.1.101 model claude-sonnet-4-6
Co-Authored-By: Mathieu Benoit <mathben@technolibre.ca>
2026-04-10 22:53:07 -04:00
|
|
|
|
},
|
|
|
|
|
|
"SSH - Run make target": {
|
[ADD] todo: QEMU/KVM menu
Deploy, list, test, resize, delete and clean up VMs. Adds the SSH
configuration with recursive ProxyJump, port forwarding, and registration
of the QEMU hosts in virt-manager.
--- FR ---
Déployer, lister, tester, redimensionner, supprimer et nettoyer des VM.
Ajoute la configuration SSH avec ProxyJump récursif, la redirection de port
et l'enregistrement des hôtes QEMU dans virt-manager.
Assisted-by: Claude Opus 4.8
2026-08-07 03:22:26 -04:00
|
|
|
|
"fr": "🧰 SSH - Exécuter une cible make",
|
|
|
|
|
|
"en": "🧰 SSH - Run make target",
|
[ADD] make: add SSH remote deployment targets and todo.py integration
Enable deploying and managing ERPLibre on remote servers via SSH
directly from make and the interactive todo.py CLI, since only
local deployment was previously supported.
- New conf/make.ssh.Makefile with 11 targets: ssh_check, ssh_push,
ssh_install, ssh_run, ssh_stop, ssh_restart, ssh_status, ssh_logs,
ssh_make, ssh_install_systemd, ssh_install_nginx
- Variables: SSH_HOST (required), SSH_USER, SSH_PORT, SSH_KEY,
SSH_PATH, SSH_TARGET, SSH_DOMAIN, SSH_ADMIN_EMAIL
- Execute > Deploy menu extended with 11 SSH options in todo.py
- All strings translated fr/en in todo_i18n.py
Generated by Claude Code 2.1.101 model claude-sonnet-4-6
Co-Authored-By: Mathieu Benoit <mathben@technolibre.ca>
2026-04-10 22:53:07 -04:00
|
|
|
|
},
|
|
|
|
|
|
"SSH - Install systemd service": {
|
[ADD] todo: QEMU/KVM menu
Deploy, list, test, resize, delete and clean up VMs. Adds the SSH
configuration with recursive ProxyJump, port forwarding, and registration
of the QEMU hosts in virt-manager.
--- FR ---
Déployer, lister, tester, redimensionner, supprimer et nettoyer des VM.
Ajoute la configuration SSH avec ProxyJump récursif, la redirection de port
et l'enregistrement des hôtes QEMU dans virt-manager.
Assisted-by: Claude Opus 4.8
2026-08-07 03:22:26 -04:00
|
|
|
|
"fr": "🧩 SSH - Installer le service systemd",
|
|
|
|
|
|
"en": "🧩 SSH - Install systemd service",
|
[ADD] make: add SSH remote deployment targets and todo.py integration
Enable deploying and managing ERPLibre on remote servers via SSH
directly from make and the interactive todo.py CLI, since only
local deployment was previously supported.
- New conf/make.ssh.Makefile with 11 targets: ssh_check, ssh_push,
ssh_install, ssh_run, ssh_stop, ssh_restart, ssh_status, ssh_logs,
ssh_make, ssh_install_systemd, ssh_install_nginx
- Variables: SSH_HOST (required), SSH_USER, SSH_PORT, SSH_KEY,
SSH_PATH, SSH_TARGET, SSH_DOMAIN, SSH_ADMIN_EMAIL
- Execute > Deploy menu extended with 11 SSH options in todo.py
- All strings translated fr/en in todo_i18n.py
Generated by Claude Code 2.1.101 model claude-sonnet-4-6
Co-Authored-By: Mathieu Benoit <mathben@technolibre.ca>
2026-04-10 22:53:07 -04:00
|
|
|
|
},
|
|
|
|
|
|
"SSH - Configure nginx + SSL": {
|
[ADD] todo: QEMU/KVM menu
Deploy, list, test, resize, delete and clean up VMs. Adds the SSH
configuration with recursive ProxyJump, port forwarding, and registration
of the QEMU hosts in virt-manager.
--- FR ---
Déployer, lister, tester, redimensionner, supprimer et nettoyer des VM.
Ajoute la configuration SSH avec ProxyJump récursif, la redirection de port
et l'enregistrement des hôtes QEMU dans virt-manager.
Assisted-by: Claude Opus 4.8
2026-08-07 03:22:26 -04:00
|
|
|
|
"fr": "🔒 SSH - Configurer nginx + SSL",
|
|
|
|
|
|
"en": "🔒 SSH - Configure nginx + SSL",
|
[ADD] make: add SSH remote deployment targets and todo.py integration
Enable deploying and managing ERPLibre on remote servers via SSH
directly from make and the interactive todo.py CLI, since only
local deployment was previously supported.
- New conf/make.ssh.Makefile with 11 targets: ssh_check, ssh_push,
ssh_install, ssh_run, ssh_stop, ssh_restart, ssh_status, ssh_logs,
ssh_make, ssh_install_systemd, ssh_install_nginx
- Variables: SSH_HOST (required), SSH_USER, SSH_PORT, SSH_KEY,
SSH_PATH, SSH_TARGET, SSH_DOMAIN, SSH_ADMIN_EMAIL
- Execute > Deploy menu extended with 11 SSH options in todo.py
- All strings translated fr/en in todo_i18n.py
Generated by Claude Code 2.1.101 model claude-sonnet-4-6
Co-Authored-By: Mathieu Benoit <mathben@technolibre.ca>
2026-04-10 22:53:07 -04:00
|
|
|
|
},
|
|
|
|
|
|
"Remote host (user@hostname or hostname): ": {
|
|
|
|
|
|
"fr": "Hôte distant (user@hostname ou hostname) : ",
|
|
|
|
|
|
"en": "Remote host (user@hostname or hostname): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"SSH user (default: erplibre): ": {
|
|
|
|
|
|
"fr": "Utilisateur SSH (défaut: erplibre) : ",
|
|
|
|
|
|
"en": "SSH user (default: erplibre): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"SSH port (default: 22): ": {
|
|
|
|
|
|
"fr": "Port SSH (défaut: 22) : ",
|
|
|
|
|
|
"en": "SSH port (default: 22): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"SSH key path (default: ~/.ssh/id_rsa, empty for none): ": {
|
|
|
|
|
|
"fr": "Chemin de la clé SSH (défaut: ~/.ssh/id_rsa, vide pour aucune) : ",
|
|
|
|
|
|
"en": "SSH key path (default: ~/.ssh/id_rsa, empty for none): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Remote path (default: ~/erplibre_deploy_2): ": {
|
|
|
|
|
|
"fr": "Chemin distant (défaut: ~/erplibre_deploy_2) : ",
|
|
|
|
|
|
"en": "Remote path (default: ~/erplibre_deploy_2): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Make target to run remotely: ": {
|
|
|
|
|
|
"fr": "Cible make à exécuter à distance : ",
|
|
|
|
|
|
"en": "Make target to run remotely: ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Domain name (e.g.: example.com): ": {
|
|
|
|
|
|
"fr": "Nom de domaine (ex: example.com) : ",
|
|
|
|
|
|
"en": "Domain name (e.g.: example.com): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Admin email for SSL certificate: ": {
|
|
|
|
|
|
"fr": "Email administrateur pour le certificat SSL : ",
|
|
|
|
|
|
"en": "Admin email for SSL certificate: ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"SSH host is required!": {
|
|
|
|
|
|
"fr": "L'hôte SSH est requis!",
|
|
|
|
|
|
"en": "SSH host is required!",
|
|
|
|
|
|
},
|
2026-03-07 02:59:07 -05:00
|
|
|
|
# Language selection
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Choose language / Choisir la langue": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Choisir la langue / Choose language",
|
|
|
|
|
|
"en": "Choose language / Choisir la langue",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"French": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Francais",
|
|
|
|
|
|
"en": "French",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"English": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Anglais",
|
|
|
|
|
|
"en": "English",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Language changed to: English": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Langue changée pour : Francais",
|
|
|
|
|
|
"en": "Language changed to: English",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"TODO execution time": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Temps d'exécution TODO",
|
|
|
|
|
|
"en": "TODO execution time",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Keyboard interrupt": {
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"fr": "Interruption clavier",
|
|
|
|
|
|
"en": "Keyboard interrupt",
|
|
|
|
|
|
},
|
2026-08-07 03:22:26 -04:00
|
|
|
|
# GPT code section
|
|
|
|
|
|
"GPT code - AI assistant tools": {
|
|
|
|
|
|
"fr": "🤖 GPT code - Outils d'assistant IA",
|
|
|
|
|
|
"en": "🤖 GPT code - AI assistant tools",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"AI assistant tools for development!": {
|
2026-03-10 02:05:53 -04:00
|
|
|
|
"fr": "Outils d'assistant IA pour le développement!",
|
|
|
|
|
|
"en": "AI assistant tools for development!",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Configure Claude Code configurations": {
|
2026-03-12 04:37:19 -04:00
|
|
|
|
"fr": "Configurer les configurations Claude Code",
|
|
|
|
|
|
"en": "Configure Claude Code configurations",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Commit - OCA/Odoo commit command": {
|
2026-03-12 04:37:19 -04:00
|
|
|
|
"fr": "Commit - Commande de commit OCA/Odoo",
|
|
|
|
|
|
"en": "Commit - OCA/Odoo commit command",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Todo Add Command - Add a command to todo.py menu": {
|
2026-03-12 04:37:19 -04:00
|
|
|
|
"fr": "Todo Add Command - Ajouter une commande au menu todo.py",
|
|
|
|
|
|
"en": "Todo Add Command - Add a command to todo.py menu",
|
2026-03-10 02:05:53 -04:00
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Enter your full name: ": {
|
2026-03-10 02:05:53 -04:00
|
|
|
|
"fr": "Entrez votre nom complet : ",
|
|
|
|
|
|
"en": "Enter your full name: ",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Enter your email: ": {
|
2026-03-10 02:05:53 -04:00
|
|
|
|
"fr": "Entrez votre courriel : ",
|
|
|
|
|
|
"en": "Enter your email: ",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Deploy Claude Code commands!": {
|
2026-03-12 04:37:19 -04:00
|
|
|
|
"fr": "Déployer les commandes Claude Code!",
|
|
|
|
|
|
"en": "Deploy Claude Code commands!",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Show installed custom commands": {
|
2026-03-12 04:37:19 -04:00
|
|
|
|
"fr": "Afficher les commandes personnalisées installées",
|
|
|
|
|
|
"en": "Show installed custom commands",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"No custom commands found in ~/.claude/commands/": {
|
2026-03-12 04:37:19 -04:00
|
|
|
|
"fr": "Aucune commande personnalisée trouvée dans ~/.claude/commands/",
|
|
|
|
|
|
"en": "No custom commands found in ~/.claude/commands/",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Claude Code custom commands:": {
|
2026-03-12 04:37:19 -04:00
|
|
|
|
"fr": "Commandes personnalisées Claude Code :",
|
|
|
|
|
|
"en": "Claude Code custom commands:",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"File already exists: ": {
|
2026-03-12 04:37:19 -04:00
|
|
|
|
"fr": "Le fichier existe déjà : ",
|
|
|
|
|
|
"en": "File already exists: ",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Do you want to overwrite the file? (y/Y): ": {
|
2026-03-12 04:37:19 -04:00
|
|
|
|
"fr": "Voulez-vous écraser le fichier? (y/Y) : ",
|
|
|
|
|
|
"en": "Do you want to overwrite the file? (y/Y): ",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Nothing to do.": {
|
2026-03-12 04:37:19 -04:00
|
|
|
|
"fr": "Rien à faire.",
|
|
|
|
|
|
"en": "Nothing to do.",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"File created successfully: ": {
|
2026-03-12 04:37:19 -04:00
|
|
|
|
"fr": "Fichier créé avec succès : ",
|
|
|
|
|
|
"en": "File created successfully: ",
|
|
|
|
|
|
},
|
2026-03-12 05:31:45 -04:00
|
|
|
|
"Error creating file: ": {
|
2026-03-10 02:05:53 -04:00
|
|
|
|
"fr": "Erreur lors de la création du fichier : ",
|
|
|
|
|
|
"en": "Error creating file: ",
|
|
|
|
|
|
},
|
2026-04-11 00:13:56 -04:00
|
|
|
|
# NTFY section
|
|
|
|
|
|
"Deploy - Install NTFY notification server": {
|
[ADD] todo: QEMU/KVM menu
Deploy, list, test, resize, delete and clean up VMs. Adds the SSH
configuration with recursive ProxyJump, port forwarding, and registration
of the QEMU hosts in virt-manager.
--- FR ---
Déployer, lister, tester, redimensionner, supprimer et nettoyer des VM.
Ajoute la configuration SSH avec ProxyJump récursif, la redirection de port
et l'enregistrement des hôtes QEMU dans virt-manager.
Assisted-by: Claude Opus 4.8
2026-08-07 03:22:26 -04:00
|
|
|
|
"fr": "🔔 Déployer - Installer le serveur de notifications NTFY",
|
|
|
|
|
|
"en": "🔔 Deploy - Install NTFY notification server",
|
2026-04-11 00:13:56 -04:00
|
|
|
|
},
|
|
|
|
|
|
"Deploy a local NTFY push notification server (Ubuntu/Arch)": {
|
|
|
|
|
|
"fr": "Déployer un serveur local de notifications NTFY (Ubuntu/Arch)",
|
|
|
|
|
|
"en": "Deploy a local NTFY push notification server (Ubuntu/Arch)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"NTFY server port (default: 8080): ": {
|
|
|
|
|
|
"fr": "Port du serveur NTFY (défaut : 8080) : ",
|
|
|
|
|
|
"en": "NTFY server port (default: 8080): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"NTFY base URL": {
|
|
|
|
|
|
"fr": "URL de base NTFY",
|
|
|
|
|
|
"en": "NTFY base URL",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Installing NTFY server (requires sudo)...": {
|
|
|
|
|
|
"fr": "Installation du serveur NTFY (sudo requis)...",
|
|
|
|
|
|
"en": "Installing NTFY server (requires sudo)...",
|
|
|
|
|
|
},
|
|
|
|
|
|
"NTFY server installed and started successfully!": {
|
|
|
|
|
|
"fr": "Serveur NTFY installé et démarré avec succès!",
|
|
|
|
|
|
"en": "NTFY server installed and started successfully!",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Error installing NTFY server: ": {
|
|
|
|
|
|
"fr": "Erreur lors de l'installation du serveur NTFY : ",
|
|
|
|
|
|
"en": "Error installing NTFY server: ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"NTFY install script not found: ": {
|
|
|
|
|
|
"fr": "Script d'installation NTFY introuvable : ",
|
|
|
|
|
|
"en": "NTFY install script not found: ",
|
|
|
|
|
|
},
|
[ADD] todo: QEMU/KVM menu
Deploy, list, test, resize, delete and clean up VMs. Adds the SSH
configuration with recursive ProxyJump, port forwarding, and registration
of the QEMU hosts in virt-manager.
--- FR ---
Déployer, lister, tester, redimensionner, supprimer et nettoyer des VM.
Ajoute la configuration SSH avec ProxyJump récursif, la redirection de port
et l'enregistrement des hôtes QEMU dans virt-manager.
Assisted-by: Claude Opus 4.8
2026-08-07 03:22:26 -04:00
|
|
|
|
# QEMU / KVM (libvirt) VM deployment
|
|
|
|
|
|
"QEMU/KVM - Deploy an Ubuntu VM (libvirt)": {
|
|
|
|
|
|
"fr": "💻 QEMU/KVM - Déployer une VM Ubuntu (libvirt)",
|
|
|
|
|
|
"en": "💻 QEMU/KVM - Deploy an Ubuntu VM (libvirt)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Deploy a QEMU/KVM virtual machine (libvirt)!": {
|
|
|
|
|
|
"fr": "Déployer une machine virtuelle QEMU/KVM (libvirt) !",
|
|
|
|
|
|
"en": "Deploy a QEMU/KVM virtual machine (libvirt)!",
|
|
|
|
|
|
},
|
|
|
|
|
|
"QEMU deploy script not found: ": {
|
|
|
|
|
|
"fr": "Script de déploiement QEMU introuvable : ",
|
|
|
|
|
|
"en": "QEMU deploy script not found: ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Deploy a new VM": {
|
|
|
|
|
|
"fr": "Déployer une nouvelle VM",
|
|
|
|
|
|
"en": "Deploy a new VM",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Preview a deployment (dry-run, no sudo)": {
|
|
|
|
|
|
"fr": "🔍 Prévisualiser un déploiement (dry-run, sans sudo)",
|
|
|
|
|
|
"en": "🔍 Preview a deployment (dry-run, no sudo)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Download a cloud image only": {
|
|
|
|
|
|
"fr": "⬇ Télécharger seulement une image cloud",
|
|
|
|
|
|
"en": "⬇ Download a cloud image only",
|
|
|
|
|
|
},
|
|
|
|
|
|
"List VMs (virsh list --all)": {
|
|
|
|
|
|
"fr": "📋 Lister les VM (virsh list --all)",
|
|
|
|
|
|
"en": "📋 List VMs (virsh list --all)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Show a VM IP address": {
|
|
|
|
|
|
"fr": "🌐 Afficher l'adresse IP d'une VM",
|
|
|
|
|
|
"en": "🌐 Show a VM IP address",
|
|
|
|
|
|
},
|
|
|
|
|
|
"List available images and specs": {
|
|
|
|
|
|
"fr": "🗂 Lister les images disponibles et leurs specs",
|
|
|
|
|
|
"en": "🗂 List available images and specs",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Distribution:": {
|
|
|
|
|
|
"fr": "Distribution :",
|
|
|
|
|
|
"en": "Distribution:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Choice (number or name, default: ubuntu): ": {
|
|
|
|
|
|
"fr": "Choix (numéro ou nom, défaut : ubuntu) : ",
|
|
|
|
|
|
"en": "Choice (number or name, default: ubuntu): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Invalid selection, using ubuntu": {
|
|
|
|
|
|
"fr": "Sélection invalide, utilisation d'ubuntu",
|
|
|
|
|
|
"en": "Invalid selection, using ubuntu",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Version for": {
|
|
|
|
|
|
"fr": "Version des",
|
|
|
|
|
|
"en": "Version for",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Versions for": {
|
|
|
|
|
|
"fr": "Versions des",
|
|
|
|
|
|
"en": "Versions of",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Architecture:": {
|
|
|
|
|
|
"fr": "Architecture :",
|
|
|
|
|
|
"en": "Architecture:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"native": {
|
|
|
|
|
|
"fr": "native",
|
|
|
|
|
|
"en": "native",
|
|
|
|
|
|
},
|
|
|
|
|
|
"IBM Z — emulated, slow on x86; Ubuntu only": {
|
|
|
|
|
|
"fr": "IBM Z — émulé, lent sur x86 ; Ubuntu uniquement",
|
|
|
|
|
|
"en": "IBM Z — emulated, slow on x86; Ubuntu only",
|
|
|
|
|
|
},
|
|
|
|
|
|
"IBM Z — emulated, slow; Ubuntu only": {
|
|
|
|
|
|
"fr": "IBM Z — émulé, lent ; Ubuntu uniquement",
|
|
|
|
|
|
"en": "IBM Z — emulated, slow; Ubuntu only",
|
|
|
|
|
|
},
|
|
|
|
|
|
"ARM 64-bit — emulated, slow": {
|
|
|
|
|
|
"fr": "ARM 64 bits — émulé, lent",
|
|
|
|
|
|
"en": "ARM 64-bit — emulated, slow",
|
|
|
|
|
|
},
|
|
|
|
|
|
"emulated, slow": {
|
|
|
|
|
|
"fr": "émulé, lent",
|
|
|
|
|
|
"en": "emulated, slow",
|
|
|
|
|
|
},
|
|
|
|
|
|
"This architecture is emulated (TCG): boot and install are"
|
|
|
|
|
|
" much slower than the native one.": {
|
|
|
|
|
|
"fr": "Cette architecture est émulée (TCG) : le boot et l'installation"
|
|
|
|
|
|
" sont bien plus lents que l'architecture native.",
|
|
|
|
|
|
"en": "This architecture is emulated (TCG): boot and install are"
|
|
|
|
|
|
" much slower than the native one.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"images for this arch only exist for:": {
|
|
|
|
|
|
"fr": "images pour cette architecture disponibles seulement pour :",
|
|
|
|
|
|
"en": "images for this arch only exist for:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"All supported architectures": {
|
|
|
|
|
|
"fr": "Toutes les architectures supportées",
|
|
|
|
|
|
"en": "All supported architectures",
|
|
|
|
|
|
},
|
|
|
|
|
|
"installation": {
|
|
|
|
|
|
"fr": "installation",
|
|
|
|
|
|
"en": "installation",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Resources per VM (x1 = catalog minimum):": {
|
|
|
|
|
|
"fr": "Ressources par VM (x1 = minimum catalogue) :",
|
|
|
|
|
|
"en": "Resources per VM (x1 = catalog minimum):",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Host:": {
|
|
|
|
|
|
"fr": "Hôte :",
|
|
|
|
|
|
"en": "Host:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"free": {
|
|
|
|
|
|
"fr": "libres",
|
|
|
|
|
|
"en": "free",
|
|
|
|
|
|
},
|
|
|
|
|
|
"free RAM unknown": {
|
|
|
|
|
|
"fr": "RAM libre inconnue",
|
|
|
|
|
|
"en": "free RAM unknown",
|
|
|
|
|
|
},
|
|
|
|
|
|
"> host free RAM": {
|
|
|
|
|
|
"fr": "> RAM libre de l'hôte",
|
|
|
|
|
|
"en": "> host free RAM",
|
|
|
|
|
|
},
|
|
|
|
|
|
"total RAM": {
|
|
|
|
|
|
"fr": "RAM totale",
|
|
|
|
|
|
"en": "total RAM",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Choice (1-4, default 1):": {
|
|
|
|
|
|
"fr": "Choix (1-4, défaut 1) :",
|
|
|
|
|
|
"en": "Choice (1-4, default 1):",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Choice (1-5, default 1):": {
|
|
|
|
|
|
"fr": "Choix (1-5, défaut 1) :",
|
|
|
|
|
|
"en": "Choice (1-5, default 1):",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Custom - set vCPU, RAM and disk": {
|
|
|
|
|
|
"fr": "Personnalisé — choisir vCPU, RAM et disque",
|
|
|
|
|
|
"en": "Custom — set vCPU, RAM and disk",
|
|
|
|
|
|
},
|
|
|
|
|
|
"custom": {
|
|
|
|
|
|
"fr": "personnalisé",
|
|
|
|
|
|
"en": "custom",
|
|
|
|
|
|
},
|
|
|
|
|
|
"varies": {
|
|
|
|
|
|
"fr": "varié",
|
|
|
|
|
|
"en": "varies",
|
|
|
|
|
|
},
|
|
|
|
|
|
"vCPU per VM, blank = keep": {
|
|
|
|
|
|
"fr": "vCPU par VM, vide = garder",
|
|
|
|
|
|
"en": "vCPU per VM, blank = keep",
|
|
|
|
|
|
},
|
|
|
|
|
|
"New vCPU count, blank = keep": {
|
|
|
|
|
|
"fr": "Nouveau nombre de vCPU, vide = garder",
|
|
|
|
|
|
"en": "New vCPU count, blank = keep",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Invalid vCPU count.": {
|
|
|
|
|
|
"fr": "Nombre de vCPU invalide.",
|
|
|
|
|
|
"en": "Invalid vCPU count.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"More vCPU than host cores": {
|
|
|
|
|
|
"fr": "Plus de vCPU que de cœurs sur l'hôte",
|
|
|
|
|
|
"en": "More vCPU than host cores",
|
|
|
|
|
|
},
|
|
|
|
|
|
"> host cores": {
|
|
|
|
|
|
"fr": "> cœurs de l'hôte",
|
|
|
|
|
|
"en": "> host cores",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Total vCPU (all running):": {
|
|
|
|
|
|
"fr": "vCPU total (toutes démarrées) :",
|
|
|
|
|
|
"en": "Total vCPU (all running):",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Deploy VM(s) (one or many)": {
|
|
|
|
|
|
"fr": "🚀 Déployer une ou plusieurs VM",
|
|
|
|
|
|
"en": "🚀 Deploy VM(s) (one or many)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Deploy ERPLibre VM(s)!": {
|
|
|
|
|
|
"fr": "Déployer une ou plusieurs VM ERPLibre !",
|
|
|
|
|
|
"en": "Deploy ERPLibre VM(s)!",
|
|
|
|
|
|
},
|
|
|
|
|
|
"VM names (default = auto):": {
|
|
|
|
|
|
"fr": "Noms des VM (défaut = auto) :",
|
|
|
|
|
|
"en": "VM names (default = auto):",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Rename which VMs? (numbers, comma-separated; blank = none): ": {
|
|
|
|
|
|
"fr": "Renommer quelles VM ? (numéros séparés par des virgules ; "
|
|
|
|
|
|
"vide = aucune) : ",
|
|
|
|
|
|
"en": "Rename which VMs? (numbers, comma-separated; blank = none): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"->": {
|
|
|
|
|
|
"fr": "->",
|
|
|
|
|
|
"en": "->",
|
|
|
|
|
|
},
|
|
|
|
|
|
"VMs (default = no change):": {
|
|
|
|
|
|
"fr": "VM (défaut = aucune modification) :",
|
|
|
|
|
|
"en": "VMs (default = no change):",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Modify which VMs? (numbers, comma-separated; blank = none): ": {
|
|
|
|
|
|
"fr": "Modifier quelles VM ? (numéros séparés par des virgules ; "
|
|
|
|
|
|
"vide = aucune) : ",
|
|
|
|
|
|
"en": "Modify which VMs? (numbers, comma-separated; blank = none): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"new name (blank = keep):": {
|
|
|
|
|
|
"fr": "nouveau nom (vide = garder) :",
|
|
|
|
|
|
"en": "new name (blank = keep):",
|
|
|
|
|
|
},
|
|
|
|
|
|
"New disk size in G, blank = keep": {
|
|
|
|
|
|
"fr": "Nouvelle taille disque en G, vide = garder",
|
|
|
|
|
|
"en": "New disk size in G, blank = keep",
|
|
|
|
|
|
},
|
|
|
|
|
|
"New RAM in MB, blank = keep": {
|
|
|
|
|
|
"fr": "Nouvelle RAM en Mo, vide = garder",
|
|
|
|
|
|
"en": "New RAM in MB, blank = keep",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Duplicate names detected; keeping as entered.": {
|
|
|
|
|
|
"fr": "Noms en double détectés ; conservés tels quels.",
|
|
|
|
|
|
"en": "Duplicate names detected; keeping as entered.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Preview (dry-run):": {
|
|
|
|
|
|
"fr": "Aperçu (dry-run) :",
|
|
|
|
|
|
"en": "Preview (dry-run):",
|
|
|
|
|
|
},
|
|
|
|
|
|
"(includes emulated architectures — some VMs are slow)": {
|
|
|
|
|
|
"fr": "(inclut des architectures émulées — certaines VM sont lentes)",
|
|
|
|
|
|
"en": "(includes emulated architectures — some VMs are slow)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"install monitoring": {
|
|
|
|
|
|
"fr": "suivi d'installation",
|
|
|
|
|
|
"en": "install monitoring",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Navigation telemetry (TUI)": {
|
|
|
|
|
|
"fr": "📊 Télémétrie de navigation (TUI)",
|
|
|
|
|
|
"en": "📊 Navigation telemetry (TUI)",
|
|
|
|
|
|
},
|
2026-08-07 03:22:26 -04:00
|
|
|
|
"Configuration": {
|
|
|
|
|
|
"fr": "⚙ Configuration",
|
|
|
|
|
|
"en": "⚙ Configuration",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Interface": {
|
|
|
|
|
|
"fr": "Interface",
|
|
|
|
|
|
"en": "Interface",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Maintenance": {
|
|
|
|
|
|
"fr": "Maintenance",
|
|
|
|
|
|
"en": "Maintenance",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Language / Langue": {
|
|
|
|
|
|
"fr": "🌐 Langue / Language",
|
|
|
|
|
|
"en": "🌐 Language / Langue",
|
|
|
|
|
|
},
|
|
|
|
|
|
"QEMU deployment interface": {
|
|
|
|
|
|
"fr": "🖥 Interface de déploiement QEMU",
|
|
|
|
|
|
"en": "🖥 QEMU deployment interface",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Display while deploying": {
|
|
|
|
|
|
"fr": "📜 Affichage pendant le déploiement",
|
|
|
|
|
|
"en": "📜 Display while deploying",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Ask every time": {
|
|
|
|
|
|
"fr": "Demander à chaque fois",
|
|
|
|
|
|
"en": "Ask every time",
|
|
|
|
|
|
},
|
|
|
|
|
|
"TUI form": {
|
|
|
|
|
|
"fr": "Formulaire TUI",
|
|
|
|
|
|
"en": "TUI form",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Classic questions (line by line)": {
|
|
|
|
|
|
"fr": "Questions classiques (ligne par ligne)",
|
|
|
|
|
|
"en": "Classic questions (line by line)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"CLI output (easy to copy)": {
|
|
|
|
|
|
"fr": "Sortie CLI (facile à copier)",
|
|
|
|
|
|
"en": "CLI output (easy to copy)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"TUI, collapsible blocks per VM": {
|
|
|
|
|
|
"fr": "TUI, blocs repliables par VM",
|
|
|
|
|
|
"en": "TUI, collapsible blocks per VM",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Choice (number, blank = keep):": {
|
|
|
|
|
|
"fr": "Choix (numéro, vide = garder) :",
|
|
|
|
|
|
"en": "Choice (number, blank = keep):",
|
|
|
|
|
|
},
|
[ADD] todo: QEMU/KVM menu
Deploy, list, test, resize, delete and clean up VMs. Adds the SSH
configuration with recursive ProxyJump, port forwarding, and registration
of the QEMU hosts in virt-manager.
--- FR ---
Déployer, lister, tester, redimensionner, supprimer et nettoyer des VM.
Ajoute la configuration SSH avec ProxyJump récursif, la redirection de port
et l'enregistrement des hôtes QEMU dans virt-manager.
Assisted-by: Claude Opus 4.8
2026-08-07 03:22:26 -04:00
|
|
|
|
"SSH port forwarding (open Odoo in the browser)": {
|
|
|
|
|
|
"fr": "🔌 Redirection de port SSH (ouvrir Odoo dans le navigateur)",
|
|
|
|
|
|
"en": "🔌 SSH port forwarding (open Odoo in the browser)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"SSH port forwarding": {
|
|
|
|
|
|
"fr": "Redirection de port SSH",
|
|
|
|
|
|
"en": "SSH port forwarding",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Host (number or name):": {
|
|
|
|
|
|
"fr": "Hôte (numéro ou nom) :",
|
|
|
|
|
|
"en": "Host (number or name):",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Remote port (default:": {
|
|
|
|
|
|
"fr": "Port distant (défaut :",
|
|
|
|
|
|
"en": "Remote port (default:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Local port (default:": {
|
|
|
|
|
|
"fr": "Port local (défaut :",
|
|
|
|
|
|
"en": "Local port (default:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"virsh is missing: libvirt is not installed here.": {
|
|
|
|
|
|
"fr": "virsh est absent : libvirt n'est pas installé ici.",
|
|
|
|
|
|
"en": "virsh is missing: libvirt is not installed here.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Every VM command will fail until it is.": {
|
|
|
|
|
|
"fr": "Toutes les commandes VM échoueront tant que ce sera le cas.",
|
|
|
|
|
|
"en": "Every VM command will fail until it is.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Install the QEMU/libvirt tools now? (Y/n): ": {
|
|
|
|
|
|
"fr": "Installer les outils QEMU/libvirt maintenant ? "
|
|
|
|
|
|
"(O/n, défaut : oui) : ",
|
|
|
|
|
|
"en": "Install the QEMU/libvirt tools now? (Y/n, default: yes): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"libvirt is available.": {
|
|
|
|
|
|
"fr": "libvirt est disponible.",
|
|
|
|
|
|
"en": "libvirt is available.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"virsh still missing; a reboot may be required.": {
|
|
|
|
|
|
"fr": "virsh toujours absent ; un redémarrage est peut-être requis.",
|
|
|
|
|
|
"en": "virsh still missing; a reboot may be required.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No SSH public key found in ~/.ssh.": {
|
|
|
|
|
|
"fr": "Aucune clé publique SSH trouvée dans ~/.ssh.",
|
|
|
|
|
|
"en": "No SSH public key found in ~/.ssh.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Without one the VMs start with no SSH access.": {
|
|
|
|
|
|
"fr": "Sans elle, les VM démarrent sans accès SSH.",
|
|
|
|
|
|
"en": "Without one the VMs start with no SSH access.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Generate one now? (Y/n): ": {
|
|
|
|
|
|
"fr": "En générer une maintenant ? (O/n, défaut : oui) : ",
|
|
|
|
|
|
"en": "Generate one now? (Y/n, default: yes): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Checking the remote port...": {
|
|
|
|
|
|
"fr": "Vérification du port distant...",
|
|
|
|
|
|
"en": "Checking the remote port...",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Nothing is listening on port": {
|
|
|
|
|
|
"fr": "Rien n'écoute sur le port",
|
|
|
|
|
|
"en": "Nothing is listening on port",
|
|
|
|
|
|
},
|
|
|
|
|
|
"of": {
|
|
|
|
|
|
"fr": "de",
|
|
|
|
|
|
"en": "of",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Start the service there, or continue anyway.": {
|
|
|
|
|
|
"fr": "Démarrer le service là-bas, ou continuer quand même.",
|
|
|
|
|
|
"en": "Start the service there, or continue anyway.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Continue anyway? (y/N): ": {
|
|
|
|
|
|
"fr": "Continuer quand même ? (o/N, défaut : non) : ",
|
|
|
|
|
|
"en": "Continue anyway? (y/N, default: no): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Could not probe the remote port; going on.": {
|
|
|
|
|
|
"fr": "Impossible de sonder le port distant ; on continue.",
|
|
|
|
|
|
"en": "Could not probe the remote port; going on.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Local port already in use:": {
|
|
|
|
|
|
"fr": "Port local déjà occupé :",
|
|
|
|
|
|
"en": "Local port already in use:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Try anyway? (y/N): ": {
|
|
|
|
|
|
"fr": "Essayer quand même ? (o/N, défaut : non) : ",
|
|
|
|
|
|
"en": "Try anyway? (y/N, default: no): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Local port differs from the remote one.": {
|
|
|
|
|
|
"fr": "Le port local diffère du port distant.",
|
|
|
|
|
|
"en": "Local port differs from the remote one.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Odoo redirects using web.base.url; check it": {
|
|
|
|
|
|
"fr": "Odoo redirige d'après web.base.url ; vérifier qu'il",
|
|
|
|
|
|
"en": "Odoo redirects using web.base.url; check it",
|
|
|
|
|
|
},
|
|
|
|
|
|
"matches http://localhost:": {
|
|
|
|
|
|
"fr": "vaut bien http://localhost:",
|
|
|
|
|
|
"en": "matches http://localhost:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Ctrl+C closes the tunnel.": {
|
|
|
|
|
|
"fr": "Ctrl+C referme le tunnel.",
|
|
|
|
|
|
"en": "Ctrl+C closes the tunnel.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Tunnel closed.": {
|
|
|
|
|
|
"fr": "Tunnel refermé.",
|
|
|
|
|
|
"en": "Tunnel closed.",
|
|
|
|
|
|
},
|
2026-08-07 03:22:26 -04:00
|
|
|
|
"SSH configuration (~/.ssh/config, ProxyJump)": {
|
|
|
|
|
|
"fr": "🔑 Configuration SSH (~/.ssh/config, ProxyJump)",
|
|
|
|
|
|
"en": "🔑 SSH configuration (~/.ssh/config, ProxyJump)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"SSH configuration for QEMU VMs": {
|
|
|
|
|
|
"fr": "Configuration SSH des VM QEMU",
|
|
|
|
|
|
"en": "SSH configuration for QEMU VMs",
|
|
|
|
|
|
},
|
[ADD] todo: QEMU/KVM menu
Deploy, list, test, resize, delete and clean up VMs. Adds the SSH
configuration with recursive ProxyJump, port forwarding, and registration
of the QEMU hosts in virt-manager.
--- FR ---
Déployer, lister, tester, redimensionner, supprimer et nettoyer des VM.
Ajoute la configuration SSH avec ProxyJump récursif, la redirection de port
et l'enregistrement des hôtes QEMU dans virt-manager.
Assisted-by: Claude Opus 4.8
2026-08-07 03:22:26 -04:00
|
|
|
|
"Where should the machines come from?": {
|
|
|
|
|
|
"fr": "D'où viennent les machines à configurer ?",
|
|
|
|
|
|
"en": "Where should the machines come from?",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Local QEMU VMs (virsh)": {
|
|
|
|
|
|
"fr": "VM QEMU locales (virsh)",
|
|
|
|
|
|
"en": "Local QEMU VMs (virsh)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Hosts from ~/.ssh/config": {
|
|
|
|
|
|
"fr": "Hôtes de ~/.ssh/config",
|
|
|
|
|
|
"en": "Hosts from ~/.ssh/config",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Type a host or an IP": {
|
|
|
|
|
|
"fr": "Saisir un hôte ou une IP",
|
|
|
|
|
|
"en": "Type a host or an IP",
|
|
|
|
|
|
},
|
|
|
|
|
|
"~/.ssh/config holds no host.": {
|
|
|
|
|
|
"fr": "~/.ssh/config ne contient aucun hôte.",
|
|
|
|
|
|
"en": "~/.ssh/config holds no host.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Which hosts? (numbers, comma-separated; blank = all): ": {
|
|
|
|
|
|
"fr": "Quels hôtes ? (numéros séparés par des virgules ; "
|
|
|
|
|
|
"vide = tous) : ",
|
|
|
|
|
|
"en": "Which hosts? (numbers, comma-separated; blank = all): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Host or IP:": {
|
|
|
|
|
|
"fr": "Hôte ou IP :",
|
|
|
|
|
|
"en": "Host or IP:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"User": {
|
|
|
|
|
|
"fr": "Utilisateur",
|
|
|
|
|
|
"en": "User",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Name for ~/.ssh/config": {
|
|
|
|
|
|
"fr": "Nom pour ~/.ssh/config",
|
|
|
|
|
|
"en": "Name for ~/.ssh/config",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Depth (1 = these machines only, default:": {
|
|
|
|
|
|
"fr": "Profondeur (1 = ces machines seulement, défaut :",
|
|
|
|
|
|
"en": "Depth (1 = these machines only, default:",
|
|
|
|
|
|
},
|
2026-08-07 03:22:26 -04:00
|
|
|
|
"Which VMs? (numbers, comma-separated; blank = all): ": {
|
|
|
|
|
|
"fr": "Quelles VM ? (numéros séparés par des virgules ; "
|
|
|
|
|
|
"vide = toutes) : ",
|
|
|
|
|
|
"en": "Which VMs? (numbers, comma-separated; blank = all): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Depth (default:": {
|
|
|
|
|
|
"fr": "Profondeur (défaut :",
|
|
|
|
|
|
"en": "Depth (default:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"entry": {
|
|
|
|
|
|
"fr": "entrée",
|
|
|
|
|
|
"en": "entry",
|
|
|
|
|
|
},
|
|
|
|
|
|
"written": {
|
|
|
|
|
|
"fr": "écrite(s)",
|
|
|
|
|
|
"en": "written",
|
|
|
|
|
|
},
|
|
|
|
|
|
"skipped": {
|
|
|
|
|
|
"fr": "ignorée(s)",
|
|
|
|
|
|
"en": "skipped",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Level": {
|
|
|
|
|
|
"fr": "Niveau",
|
|
|
|
|
|
"en": "Level",
|
|
|
|
|
|
},
|
|
|
|
|
|
"machines to probe": {
|
|
|
|
|
|
"fr": "machines à sonder",
|
|
|
|
|
|
"en": "machines to probe",
|
|
|
|
|
|
},
|
[ADD] todo: QEMU/KVM menu
Deploy, list, test, resize, delete and clean up VMs. Adds the SSH
configuration with recursive ProxyJump, port forwarding, and registration
of the QEMU hosts in virt-manager.
--- FR ---
Déployer, lister, tester, redimensionner, supprimer et nettoyer des VM.
Ajoute la configuration SSH avec ProxyJump récursif, la redirection de port
et l'enregistrement des hôtes QEMU dans virt-manager.
Assisted-by: Claude Opus 4.8
2026-08-07 03:22:26 -04:00
|
|
|
|
"SSH refused the identity.": {
|
|
|
|
|
|
"fr": "SSH a refusé l'identité.",
|
|
|
|
|
|
"en": "SSH refused the identity.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Existing key:": {
|
|
|
|
|
|
"fr": "Clé existante :",
|
|
|
|
|
|
"en": "Existing key:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Deploy it on this host (ssh-copy-id)? (Y/n): ": {
|
|
|
|
|
|
"fr": "La déployer sur cet hôte (ssh-copy-id) ? "
|
|
|
|
|
|
"(O/n, défaut : oui) : ",
|
|
|
|
|
|
"en": "Deploy it on this host (ssh-copy-id)? (Y/n, default: yes): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No SSH key in ~/.ssh.": {
|
|
|
|
|
|
"fr": "Aucune clé SSH dans ~/.ssh.",
|
|
|
|
|
|
"en": "No SSH key in ~/.ssh.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Create one and deploy it? (Y/n): ": {
|
|
|
|
|
|
"fr": "En créer une et la déployer ? (O/n, défaut : oui) : ",
|
|
|
|
|
|
"en": "Create one and deploy it? (Y/n, default: yes): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"access refused": {
|
|
|
|
|
|
"fr": "accès refusé",
|
|
|
|
|
|
"en": "access refused",
|
|
|
|
|
|
},
|
|
|
|
|
|
"timed out": {
|
|
|
|
|
|
"fr": "délai dépassé",
|
|
|
|
|
|
"en": "timed out",
|
|
|
|
|
|
},
|
|
|
|
|
|
"unreachable": {
|
|
|
|
|
|
"fr": "injoignable",
|
|
|
|
|
|
"en": "unreachable",
|
|
|
|
|
|
},
|
|
|
|
|
|
"no QEMU/libvirt here": {
|
|
|
|
|
|
"fr": "pas de QEMU/libvirt ici",
|
|
|
|
|
|
"en": "no QEMU/libvirt here",
|
|
|
|
|
|
},
|
[IMP] qemu: a VM one can reach, and a first boot that does not stall
The monitor froze the address given at launch, so it lost the VM as soon as
cloud-init renamed the host and DHCP handed out another lease. It now
re-resolves at each attempt, in the views too, and reads virsh without sudo
— « sudo -n » fails in a detached session with no tty.
First boot also stopped paying for what it does not need: the guest agent
leaves cloud-init, snapd and locale-gen go, apt takes the fastest mirror.
The timezone follows the host. And when KVM is missing, the deployment says
so before the wait instead of being mysteriously fifteen times slower.
--- FR ---
Le suivi figeait l'adresse connue au lancement : il perdait donc la VM dès
que cloud-init posait le vrai nom d'hôte et que DHCP donnait un autre bail.
Il la ré-résout désormais à chaque tentative, dans les vues aussi, et lit
virsh sans sudo — « sudo -n » échoue dans une session détachée, sans tty.
Le premier démarrage cesse aussi de payer l'inutile : l'agent invité sort
de cloud-init, snapd et locale-gen disparaissent, apt prend le miroir le
plus rapide. Le fuseau suit l'hôte. Et faute de KVM, le déploiement le dit
avant l'attente, au lieu d'être quinze fois plus lent sans raison visible.
Assisted-by: Claude Opus 5
2026-08-10 03:10:50 -04:00
|
|
|
|
"virsh present but not accessible": {
|
|
|
|
|
|
"fr": "virsh présent mais inaccessible (droits)",
|
|
|
|
|
|
"en": "virsh present but not accessible (permissions)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Add the user to the libvirt group there:": {
|
|
|
|
|
|
"fr": "Ajoutez l'utilisateur au groupe libvirt là-bas :",
|
|
|
|
|
|
"en": "Add the user to the libvirt group there:",
|
|
|
|
|
|
},
|
[ADD] todo: QEMU/KVM menu
Deploy, list, test, resize, delete and clean up VMs. Adds the SSH
configuration with recursive ProxyJump, port forwarding, and registration
of the QEMU hosts in virt-manager.
--- FR ---
Déployer, lister, tester, redimensionner, supprimer et nettoyer des VM.
Ajoute la configuration SSH avec ProxyJump récursif, la redirection de port
et l'enregistrement des hôtes QEMU dans virt-manager.
Assisted-by: Claude Opus 4.8
2026-08-07 03:22:26 -04:00
|
|
|
|
"QEMU present, no VM": {
|
|
|
|
|
|
"fr": "QEMU présent, aucune VM",
|
|
|
|
|
|
"en": "QEMU present, no VM",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Create the SSH key if missing and deploy it? (Y/n): ": {
|
|
|
|
|
|
"fr": "Créer la clé SSH si absente et la déployer ? "
|
|
|
|
|
|
"(O/n, défaut : oui) : ",
|
|
|
|
|
|
"en": "Create the SSH key if missing and deploy it? "
|
|
|
|
|
|
"(Y/n, default: yes): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Generating an ed25519 SSH key": {
|
|
|
|
|
|
"fr": "Génération d'une clé SSH ed25519",
|
|
|
|
|
|
"en": "Generating an ed25519 SSH key",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Cannot generate the key": {
|
|
|
|
|
|
"fr": "Impossible de générer la clé",
|
|
|
|
|
|
"en": "Cannot generate the key",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Deploying the key on": {
|
|
|
|
|
|
"fr": "Déploiement de la clé sur",
|
|
|
|
|
|
"en": "Deploying the key on",
|
|
|
|
|
|
},
|
|
|
|
|
|
"host": {
|
|
|
|
|
|
"fr": "hôte",
|
|
|
|
|
|
"en": "host",
|
|
|
|
|
|
},
|
|
|
|
|
|
"key already accepted": {
|
|
|
|
|
|
"fr": "clé déjà acceptée",
|
|
|
|
|
|
"en": "key already accepted",
|
|
|
|
|
|
},
|
|
|
|
|
|
"deployed": {
|
|
|
|
|
|
"fr": "déployée(s)",
|
|
|
|
|
|
"en": "deployed",
|
|
|
|
|
|
},
|
|
|
|
|
|
"already there": {
|
|
|
|
|
|
"fr": "déjà en place",
|
|
|
|
|
|
"en": "already there",
|
|
|
|
|
|
},
|
|
|
|
|
|
"virt-manager detected": {
|
|
|
|
|
|
"fr": "virt-manager détecté",
|
|
|
|
|
|
"en": "virt-manager detected",
|
|
|
|
|
|
},
|
|
|
|
|
|
"virt-manager: every connection already there": {
|
|
|
|
|
|
"fr": "virt-manager : toutes les connexions sont déjà là",
|
|
|
|
|
|
"en": "virt-manager: every connection already there",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Add the missing connections to virt-manager? (Y/n): ": {
|
|
|
|
|
|
"fr": "Ajouter les connexions manquantes à virt-manager ? "
|
|
|
|
|
|
"(O/n, défaut : oui) : ",
|
|
|
|
|
|
"en": "Add the missing connections to virt-manager? "
|
|
|
|
|
|
"(Y/n, default: yes): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Restart virt-manager to see the new connections": {
|
|
|
|
|
|
"fr": "Redémarrer virt-manager pour voir les nouvelles connexions",
|
|
|
|
|
|
"en": "Restart virt-manager to see the new connections",
|
|
|
|
|
|
},
|
|
|
|
|
|
"the names apply live": {
|
|
|
|
|
|
"fr": "les noms s'appliquent à chaud",
|
|
|
|
|
|
"en": "the names apply live",
|
|
|
|
|
|
},
|
2026-08-07 03:22:26 -04:00
|
|
|
|
"SSH hosts written": {
|
|
|
|
|
|
"fr": "Hôtes SSH écrits",
|
|
|
|
|
|
"en": "SSH hosts written",
|
|
|
|
|
|
},
|
|
|
|
|
|
"via": {
|
|
|
|
|
|
"fr": "via",
|
|
|
|
|
|
"en": "via",
|
|
|
|
|
|
},
|
[ADD] migration: see and repair the website COW views
A copy-on-write view freezes the module view it came from; the module moves
on and the upgrade dies hours later on a missing anchor. These tools
predict, snapshot, diff, neutralize and reset them. The migration screen
gains the real state of each step, replay from any of them, and statistics.
--- FR ---
Une vue copy-on-write fige la vue de module dont elle vient ; le module
évolue et la mise à niveau meurt des heures plus tard sur un point
d'ancrage absent. Ces outils les prévoient, photographient, comparent,
neutralisent et réinitialisent. L'écran de migration gagne l'état réel de
chaque étape, la reprise depuis n'importe laquelle, et des statistiques.
Assisted-by: Claude Opus 5
2026-08-10 03:10:50 -04:00
|
|
|
|
"Odoo migration interface": {
|
|
|
|
|
|
"fr": "🚚 Interface de la migration Odoo",
|
|
|
|
|
|
"en": "🚚 Odoo migration interface",
|
|
|
|
|
|
},
|
|
|
|
|
|
"modules of the list have no code in the active Odoo:": {
|
|
|
|
|
|
"fr": "modules de la liste n'ont plus de code dans l'Odoo actif :",
|
|
|
|
|
|
"en": "modules of the list have no code in the active Odoo:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Odoo cannot uninstall a module whose code is gone;": {
|
|
|
|
|
|
"fr": "Odoo ne peut pas désinstaller un module dont le code a"
|
|
|
|
|
|
" disparu ;",
|
|
|
|
|
|
"en": "Odoo cannot uninstall a module whose code is gone;",
|
|
|
|
|
|
},
|
|
|
|
|
|
"one of them fails the whole uninstall command.": {
|
|
|
|
|
|
"fr": "un seul d'entre eux fait échouer toute la désinstallation.",
|
|
|
|
|
|
"en": "one of them fails the whole uninstall command.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"are present and can be uninstalled:": {
|
|
|
|
|
|
"fr": "sont présents et peuvent être désinstallés :",
|
|
|
|
|
|
"en": "are present and can be uninstalled:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No module of the list is present.": {
|
|
|
|
|
|
"fr": "Aucun module de la liste n'est présent.",
|
|
|
|
|
|
"en": "No module of the list is present.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Uninstall the present ones, skip the missing": {
|
|
|
|
|
|
"fr": "Désinstaller les présents, ignorer les manquants",
|
|
|
|
|
|
"en": "Uninstall the present ones, skip the missing",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Try the whole list anyway (it will fail)": {
|
|
|
|
|
|
"fr": "Tout tenter quand même (ça échouera)",
|
|
|
|
|
|
"en": "Try the whole list anyway (it will fail)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Uninstall nothing, continue": {
|
|
|
|
|
|
"fr": "Ne rien désinstaller, continuer",
|
|
|
|
|
|
"en": "Uninstall nothing, continue",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Nothing uninstalled.": {
|
|
|
|
|
|
"fr": "Rien de désinstallé.",
|
|
|
|
|
|
"en": "Nothing uninstalled.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Check the COW views that drifted": {
|
|
|
|
|
|
"fr": "Vérifier les vues COW en retard sur leur vue module",
|
|
|
|
|
|
"en": "Check the COW views that drifted",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Tool not found": {
|
|
|
|
|
|
"fr": "Outil introuvable",
|
|
|
|
|
|
"en": "Tool not found",
|
|
|
|
|
|
},
|
|
|
|
|
|
"To reset one of them onto its module view:": {
|
|
|
|
|
|
"fr": "Pour réinitialiser l'une d'elles sur sa vue module :",
|
|
|
|
|
|
"en": "To reset one of them onto its module view:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Read the diff first: a copy can hold a customisation.": {
|
|
|
|
|
|
"fr": "Lire le diff d'abord : une copie peut porter une"
|
|
|
|
|
|
" personnalisation.",
|
|
|
|
|
|
"en": "Read the diff first: a copy can hold a customisation.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Migration statistics (read-only)": {
|
|
|
|
|
|
"fr": "📊 Statistiques de la migration (lecture seule)",
|
|
|
|
|
|
"en": "📊 Migration statistics (read-only)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Choice (0-3, default 1): ": {
|
|
|
|
|
|
"fr": "Choix (0-3, défaut 1) : ",
|
|
|
|
|
|
"en": "Choice (0-3, default 1): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Migration statistics": {
|
|
|
|
|
|
"fr": "Statistiques de la migration",
|
|
|
|
|
|
"en": "Migration statistics",
|
|
|
|
|
|
},
|
|
|
|
|
|
"elapsed": {
|
|
|
|
|
|
"fr": "écoulé",
|
|
|
|
|
|
"en": "elapsed",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Level reached": {
|
|
|
|
|
|
"fr": "Niveau atteint",
|
|
|
|
|
|
"en": "Level reached",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Modules": {
|
|
|
|
|
|
"fr": "Modules",
|
|
|
|
|
|
"en": "Modules",
|
|
|
|
|
|
},
|
|
|
|
|
|
"modules": {
|
|
|
|
|
|
"fr": "modules",
|
|
|
|
|
|
"en": "modules",
|
|
|
|
|
|
},
|
|
|
|
|
|
"At the start": {
|
|
|
|
|
|
"fr": "Au départ",
|
|
|
|
|
|
"en": "At the start",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Removed in total": {
|
|
|
|
|
|
"fr": "Supprimés au total",
|
|
|
|
|
|
"en": "Removed in total",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Reported missing": {
|
|
|
|
|
|
"fr": "Signalés manquants",
|
|
|
|
|
|
"en": "Reported missing",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Duplicated": {
|
|
|
|
|
|
"fr": "En double",
|
|
|
|
|
|
"en": "Duplicated",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Migration fixes": {
|
|
|
|
|
|
"fr": "Correctifs de migration",
|
|
|
|
|
|
"en": "Migration fixes",
|
|
|
|
|
|
},
|
|
|
|
|
|
"COW views": {
|
|
|
|
|
|
"fr": "Vues COW",
|
|
|
|
|
|
"en": "COW views",
|
|
|
|
|
|
},
|
|
|
|
|
|
"views": {
|
|
|
|
|
|
"fr": "vues",
|
|
|
|
|
|
"en": "views",
|
|
|
|
|
|
},
|
|
|
|
|
|
"no snapshot": {
|
|
|
|
|
|
"fr": "aucun instantané",
|
|
|
|
|
|
"en": "no snapshot",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Journal": {
|
|
|
|
|
|
"fr": "Journal",
|
|
|
|
|
|
"en": "Journal",
|
|
|
|
|
|
},
|
|
|
|
|
|
"commands": {
|
|
|
|
|
|
"fr": "commandes",
|
|
|
|
|
|
"en": "commands",
|
|
|
|
|
|
},
|
|
|
|
|
|
"recorded decisions": {
|
|
|
|
|
|
"fr": "décisions annotées",
|
|
|
|
|
|
"en": "recorded decisions",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Removed modules, with their reason": {
|
|
|
|
|
|
"fr": "Modules supprimés, avec leur justification",
|
|
|
|
|
|
"en": "Removed modules, with their reason",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Removed modules, comma-separated (copy)": {
|
|
|
|
|
|
"fr": "Modules supprimés, séparés par des virgules (à copier)",
|
|
|
|
|
|
"en": "Removed modules, comma-separated (copy)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"COW views: snapshots and differences": {
|
|
|
|
|
|
"fr": "Vues COW : instantanés et différences",
|
|
|
|
|
|
"en": "COW views: snapshots and differences",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Recorded decisions (journal)": {
|
|
|
|
|
|
"fr": "Décisions annotées (journal)",
|
|
|
|
|
|
"en": "Recorded decisions (journal)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Executed commands (last 30)": {
|
|
|
|
|
|
"fr": "Commandes exécutées (30 dernières)",
|
|
|
|
|
|
"en": "Executed commands (last 30)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"nothing recorded": {
|
|
|
|
|
|
"fr": "rien d'annoté",
|
|
|
|
|
|
"en": "nothing recorded",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Need two snapshots to diff.": {
|
|
|
|
|
|
"fr": "Il faut deux instantanés pour comparer.",
|
|
|
|
|
|
"en": "Need two snapshots to diff.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Diff which two? (e.g. 1,2 — blank to skip)": {
|
|
|
|
|
|
"fr": "Comparer lesquels ? (ex. 1,2 — vide pour passer)",
|
|
|
|
|
|
"en": "Diff which two? (e.g. 1,2 — blank to skip)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No migration in progress to resume.": {
|
|
|
|
|
|
"fr": "Aucune migration en cours à reprendre.",
|
|
|
|
|
|
"en": "No migration in progress to resume.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Quit without doing anything": {
|
|
|
|
|
|
"fr": "Quitter sans rien faire",
|
|
|
|
|
|
"en": "Quit without doing anything",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Version bumps": {
|
|
|
|
|
|
"fr": "Montées de version",
|
|
|
|
|
|
"en": "Version bumps",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Step": {
|
|
|
|
|
|
"fr": "Étape",
|
|
|
|
|
|
"en": "Step",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Detail": {
|
|
|
|
|
|
"fr": "Détail",
|
|
|
|
|
|
"en": "Detail",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Enter on a step or a version = replay from there": {
|
|
|
|
|
|
"fr": "Entrée sur une étape ou une version = rejouer depuis là",
|
|
|
|
|
|
"en": "Enter on a step or a version = replay from there",
|
|
|
|
|
|
},
|
|
|
|
|
|
"New migration": {
|
|
|
|
|
|
"fr": "Nouvelle migration",
|
|
|
|
|
|
"en": "New migration",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Keep the zip only": {
|
|
|
|
|
|
"fr": "Garder seulement le zip",
|
|
|
|
|
|
"en": "Keep the zip only",
|
|
|
|
|
|
},
|
2026-08-07 03:22:26 -04:00
|
|
|
|
"Reset all preferences": {
|
|
|
|
|
|
"fr": "🧹 Réinitialiser toutes les préférences",
|
|
|
|
|
|
"en": "🧹 Reset all preferences",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Preferences reset": {
|
|
|
|
|
|
"fr": "Préférences réinitialisées",
|
|
|
|
|
|
"en": "Preferences reset",
|
|
|
|
|
|
},
|
[ADD] todo: QEMU/KVM menu
Deploy, list, test, resize, delete and clean up VMs. Adds the SSH
configuration with recursive ProxyJump, port forwarding, and registration
of the QEMU hosts in virt-manager.
--- FR ---
Déployer, lister, tester, redimensionner, supprimer et nettoyer des VM.
Ajoute la configuration SSH avec ProxyJump récursif, la redirection de port
et l'enregistrement des hôtes QEMU dans virt-manager.
Assisted-by: Claude Opus 4.8
2026-08-07 03:22:26 -04:00
|
|
|
|
"Interface:": {
|
|
|
|
|
|
"fr": "Interface :",
|
|
|
|
|
|
"en": "Interface:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"(change the default in TODO > Configuration)": {
|
|
|
|
|
|
"fr": "(changer le défaut dans TODO > Configuration)",
|
|
|
|
|
|
"en": "(change the default in TODO > Configuration)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Loading (VM list, branches)...": {
|
|
|
|
|
|
"fr": "Chargement (liste des VM, branches)...",
|
|
|
|
|
|
"en": "Loading (VM list, branches)...",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Architecture": {
|
|
|
|
|
|
"fr": "Architecture",
|
|
|
|
|
|
"en": "Architecture",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Resources per VM": {
|
|
|
|
|
|
"fr": "Ressources par VM",
|
|
|
|
|
|
"en": "Resources per VM",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Install ERPLibre": {
|
|
|
|
|
|
"fr": "Installer ERPLibre",
|
|
|
|
|
|
"en": "Install ERPLibre",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Production (/opt, confined)": {
|
|
|
|
|
|
"fr": "Production (/opt, confiné)",
|
|
|
|
|
|
"en": "Production (/opt, confined)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Monitoring dashboard": {
|
|
|
|
|
|
"fr": "Tableau de bord de suivi",
|
|
|
|
|
|
"en": "Monitoring dashboard",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Add each VM to ~/.ssh/config": {
|
|
|
|
|
|
"fr": "Ajouter chaque VM à ~/.ssh/config",
|
|
|
|
|
|
"en": "Add each VM to ~/.ssh/config",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Parallelism": {
|
|
|
|
|
|
"fr": "Parallélisme",
|
|
|
|
|
|
"en": "Parallelism",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Name": {
|
|
|
|
|
|
"fr": "Nom",
|
|
|
|
|
|
"en": "Name",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Distro": {
|
|
|
|
|
|
"fr": "Distro",
|
|
|
|
|
|
"en": "Distro",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Version": {
|
|
|
|
|
|
"fr": "Version",
|
|
|
|
|
|
"en": "Version",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Arch": {
|
|
|
|
|
|
"fr": "Archi",
|
|
|
|
|
|
"en": "Arch",
|
|
|
|
|
|
},
|
|
|
|
|
|
"vCPU": {
|
|
|
|
|
|
"fr": "vCPU",
|
|
|
|
|
|
"en": "vCPU",
|
|
|
|
|
|
},
|
|
|
|
|
|
"RAM (MB)": {
|
|
|
|
|
|
"fr": "RAM (Mo)",
|
|
|
|
|
|
"en": "RAM (MB)",
|
|
|
|
|
|
},
|
2026-08-11 08:36:13 -04:00
|
|
|
|
"cloud-init still running after 20 min - install starts anyway"
|
|
|
|
|
|
" (it waits for cloud-init first)": {
|
|
|
|
|
|
"fr": "cloud-init tourne encore apres 20 min - l'installation demarre"
|
|
|
|
|
|
" quand meme (elle attend d'abord cloud-init)",
|
|
|
|
|
|
"en": "cloud-init still running after 20 min - install starts anyway"
|
|
|
|
|
|
" (it waits for cloud-init first)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Waiting for cloud-init to finish (up to 15 min)": {
|
|
|
|
|
|
"fr": "Attente de la fin de cloud-init (jusqu'a 15 min)",
|
|
|
|
|
|
"en": "Waiting for cloud-init to finish (up to 15 min)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"cloud-init:": {
|
|
|
|
|
|
"fr": "cloud-init :",
|
|
|
|
|
|
"en": "cloud-init:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"WARNING libvirt unreachable: the IP will not be refreshed"
|
|
|
|
|
|
" (libvirt group? re-login required)": {
|
|
|
|
|
|
"fr": "ATTENTION libvirt injoignable : l'IP ne sera pas rafraichie"
|
|
|
|
|
|
" (groupe libvirt ? reconnexion necessaire)",
|
|
|
|
|
|
"en": "WARNING libvirt unreachable: the IP will not be refreshed"
|
|
|
|
|
|
" (libvirt group? re-login required)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"DHCP lease moved:": {
|
|
|
|
|
|
"fr": "bail DHCP deplace :",
|
|
|
|
|
|
"en": "DHCP lease moved:",
|
|
|
|
|
|
},
|
[ADD] tui qemu: resume an installation already running
Installs run detached (setsid -f): closing the terminal does not stop
them, but it lost the only view onto them. With no way to resume, the
only way out was deleting the VMs and starting over.
Picking "Deploy" now looks at the latest run: if VMs there still lack an
exit marker, it offers to reopen its monitoring instead of starting
another. Time since the last write is shown, a dead run being otherwise
indistinguishable from a live one.
Only the latest run is examined: an old one left without a marker would
flag a phantom install forever. Dry-run creates nothing, so it does not
ask. "Reopen monitoring" moves to the Deployment section, where one
looks for it.
--- FR ---
Les installs partent détachées (setsid -f) : fermer le terminal ne les
arrête pas, mais faisait perdre la seule vue dessus. Sans moyen de
reprendre, la seule issue était d'effacer les VM et de recommencer.
Choisir « Déployer » regarde donc le dernier run : s'il lui reste des VM
sans marqueur de sortie, il propose de rouvrir son suivi plutôt que d'en
lancer un autre. Le silence depuis la dernière écriture est affiché, un
run mort n'étant pas distinguable autrement d'un run vivant.
Seul le dernier run est examiné : un run ancien laissé sans marqueur
signalerait éternellement une install fantôme. L'aperçu ne crée rien, il
ne pose pas la question. « Rouvrir le suivi » rejoint la section
Déploiement, où on le cherche.
Assisted-by: Claude Opus 5
2026-08-11 07:52:52 -04:00
|
|
|
|
"An install is still running:": {
|
|
|
|
|
|
"fr": "Une installation est encore en cours :",
|
|
|
|
|
|
"en": "An install is still running:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"VM(s) in progress": {
|
|
|
|
|
|
"fr": "VM en cours",
|
|
|
|
|
|
"en": "VM(s) in progress",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Last activity:": {
|
|
|
|
|
|
"fr": "Dernière activité il y a :",
|
|
|
|
|
|
"en": "Last activity:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Reopen that monitoring": {
|
|
|
|
|
|
"fr": "Rouvrir ce suivi",
|
|
|
|
|
|
"en": "Reopen that monitoring",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Deploy anyway (new run)": {
|
|
|
|
|
|
"fr": "Déployer quand même (nouveau run)",
|
|
|
|
|
|
"en": "Deploy anyway (new run)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Choice (number, blank = reopen): ": {
|
|
|
|
|
|
"fr": "Choix (numéro, vide = rouvrir) : ",
|
|
|
|
|
|
"en": "Choice (number, blank = reopen): ",
|
|
|
|
|
|
},
|
[ADD] tui qemu: choose mise or pyenv at deploy time
The interpreter provider is now chosen when creating a VM, in both
interfaces. "mise" installs it in the VM and lays down a precompiled
CPython; "pyenv" keeps today's build-from-source.
The question is only asked where it means something: mise publishes
binaries for amd64 and arm64 only. An all-s390x fleet never sees it, a
mixed fleet sees it along with the architectures that will fall back to
pyenv -- said before deploying, not discovered in a log an hour later.
mise goes into /usr/local/bin rather than ~/.local/bin: the remote command
runs through "ssh host 'command'", where neither ~/.profile nor ~/.bashrc
is read. Choosing "mise" exports "auto", not "mise", so a failed install
can still fall back to pyenv.
The unavailability notice was translated as "pyenv required", which said
the opposite of what happens: pyenv is the fallback, not a demand.
--- FR ---
Le fournisseur d'interpréteur se choisit désormais à la création d'une VM,
dans les deux interfaces. « mise » l'installe dans la VM et pose un
CPython précompilé ; « pyenv » garde la compilation d'aujourd'hui.
La question n'est posée que là où elle a un sens : mise ne publie de
binaires que pour amd64 et arm64. Un parc tout s390x ne la voit jamais, un
parc mixte la voit avec les architectures qui retomberont sur pyenv — dit
avant de déployer, pas découvert dans un log une heure plus tard.
mise va dans /usr/local/bin plutôt que ~/.local/bin : la commande distante
passe par « ssh hôte 'commande' », où ni ~/.profile ni ~/.bashrc ne sont
lus. Choisir « mise » exporte « auto », pas « mise », pour qu'une
installation ratée puisse encore retomber sur pyenv.
L'avis d'indisponibilité était traduit par « pyenv exigé », qui disait
l'inverse de ce qui se passe : pyenv est le repli, pas une exigence.
Assisted-by: Claude Opus 5
2026-08-11 22:11:23 -04:00
|
|
|
|
"Python interpreter:": {
|
|
|
|
|
|
"fr": "Interpreteur Python :",
|
|
|
|
|
|
"en": "Python interpreter:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mise (precompiled, faster)": {
|
|
|
|
|
|
"fr": "mise (precompile, plus rapide)",
|
|
|
|
|
|
"en": "mise (precompiled, faster)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"pyenv (compiles from source)": {
|
|
|
|
|
|
"fr": "pyenv (compile depuis les sources)",
|
|
|
|
|
|
"en": "pyenv (compiles from source)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Choice (number, blank = mise): ": {
|
|
|
|
|
|
"fr": "Choix (numero, vide = mise) : ",
|
|
|
|
|
|
"en": "Choice (number, blank = mise): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mise has no binary for:": {
|
|
|
|
|
|
"fr": "mise ne publie pas de binaire pour :",
|
|
|
|
|
|
"en": "mise has no binary for:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"those VMs use pyenv": {
|
|
|
|
|
|
"fr": "ces VM utiliseront pyenv",
|
|
|
|
|
|
"en": "those VMs use pyenv",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Installing mise (precompiled Python)": {
|
|
|
|
|
|
"fr": "Installation de mise (Python precompile)",
|
|
|
|
|
|
"en": "Installing mise (precompiled Python)",
|
|
|
|
|
|
},
|
[ADD] tui qemu: graphical VMs, with a GNOME desktop
VMs could only be servers. A server-or-graphical choice joins both
interfaces, installing GNOME along with remote access to it.
Packages come from the remote command, not cloud-init: their 1 to 2 GB
would stretch an already long boot there while leaving no trace in the
monitoring, and group installs do not go through it. The desktop
therefore does not depend on ERPLibre -- a VM may be wanted graphical
and bare.
Each distribution has its own names, taken from the source: Arch has no
xrdp in its official repositories and takes TigerVNC. The SPICE display
is set only on amd64 and arm64; s390x does expose virtio-gpu-ccw, but
nothing guarantees its kernel's DRM driver, whereas remote desktop works
everywhere.
--- FR ---
Les VM ne pouvaient être que des serveurs. Un choix serveur ou graphique
s'ajoute aux deux interfaces, et pose GNOME avec son accès distant.
Les paquets viennent de la commande distante, pas de cloud-init : leurs
1 à 2 Go y allongeraient un démarrage déjà long sans laisser de trace
dans le suivi, et les installations par groupe n'y passent pas. Le bureau
ne dépend donc pas d'ERPLibre — une VM peut être voulue graphique et nue.
Chaque distribution a ses noms, relevés à la source : Arch n'a pas xrdp
dans ses dépôts officiels et prend TigerVNC. L'écran virtuel SPICE n'est
posé que sur amd64 et arm64 ; s390x expose bien virtio-gpu-ccw, mais rien
ne garantit le pilote DRM de son noyau, alors que le bureau distant, lui,
marche partout.
Assisted-by: Claude Opus 5
2026-08-11 09:49:17 -04:00
|
|
|
|
"VM type:": {
|
|
|
|
|
|
"fr": "Type de VM :",
|
|
|
|
|
|
"en": "VM type:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Server (no graphical interface)": {
|
|
|
|
|
|
"fr": "Serveur (sans interface graphique)",
|
|
|
|
|
|
"en": "Server (no graphical interface)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Graphical (server + GNOME desktop)": {
|
|
|
|
|
|
"fr": "Graphique (serveur + bureau GNOME)",
|
|
|
|
|
|
"en": "Graphical (server + GNOME desktop)",
|
|
|
|
|
|
},
|
[UPD] tui qemu: the plan shows each VM type
The server-or-desktop choice only showed on the left, in the form. The
table on the right, the one read before launching, said nothing about it:
two visually identical plans could produce different VMs.
The Status column, empty until now for a VM to create, therefore carries
the type, and the totals line repeats it. An already defined VM keeps its
collision message: it is not touched, showing a desktop there would
suggest one is about to be installed on it.
--- FR ---
Le choix serveur ou bureau ne se voyait qu'à gauche, dans le formulaire.
Le tableau de droite, celui qu'on relit avant de lancer, n'en disait
rien : deux plans identiques à l'écran pouvaient produire des VM
différentes.
La colonne Statut, vide jusqu'ici pour une VM à créer, porte donc le
type, et la ligne de totaux le rappelle. Une VM déjà définie garde son
message de collision : elle n'est pas retouchée, lui afficher un bureau
laisserait croire qu'on va lui en poser un.
Assisted-by: Claude Opus 5
2026-08-11 18:55:52 -04:00
|
|
|
|
"server": {
|
|
|
|
|
|
"fr": "serveur",
|
|
|
|
|
|
"en": "server",
|
|
|
|
|
|
},
|
[UPD] support: drop Ubuntu 20.04/22.04, add AlmaLinux and Rocky
Ubuntu 20.04 and 22.04 leave EVERY architecture, not just s390x. pikepdf
needs qpdf 12.2, whose build requires C++20, while focal ships GCC 9 and
publishes no g++-10 for s390x at all. Python 3.8, node 10, cargo 0.67 and
OpenSSL 1.1.1 each had a workaround; the pile of them did not. 18.04
follows, already off the lists. The refusal lands before any apt, this
script also serving existing machines.
AlmaLinux 9 and 10, Rocky 9 and 10 join the catalog on all four
architectures: the twelve "latest" URLs were opened, with no index to
parse unlike Fedora. They would have booted unreachable though -- the
cloud-config forced "groups: users, sudo", but the RHEL family has no
sudo group, only wheel, and an unknown group makes useradd fail, hence no
password and no key. The very trap already known for Debian, repeated
elsewhere. Host side, EPEL and CRB are enabled: without them most -devel
packages are missing, silently.
The server / graphical choice gains Cinnamon, the Linux Mint desktop,
from the distribution's own repositories. Mint's repository is set aside:
plain HTTP, and i386/amd64 only, which would rule out arm64 and s390x.
Along the way, dnf now installs an ENVIRONMENT rather than a group --
"gnome-desktop" brings gdm and gnome-shell but not base-x, hence no X
server.
--- FR ---
Ubuntu 20.04 et 22.04 partent de TOUTES les architectures, pas seulement
de s390x. pikepdf réclame qpdf 12.2, dont la compilation exige C++20,
quand focal livre GCC 9 et ne publie même pas de g++-10 pour s390x.
Python 3.8, node 10, cargo 0.67 et OpenSSL 1.1.1 avaient chacun leur
contournement ; leur accumulation, non. 18.04 suit, déjà hors des
listes. Le refus tombe avant tout apt, ce script servant aussi les
machines existantes.
AlmaLinux 9 et 10, Rocky 9 et 10 entrent au catalogue, sur les quatre
architectures : les douze URL « latest » ont été ouvertes, aucun index à
analyser contrairement à Fedora. Elles auraient pourtant démarré
inaccessibles — le cloud-config imposait « groups: users, sudo », or la
famille RHEL n'a pas de groupe sudo mais wheel, et un groupe inconnu fait
échouer useradd, donc ni mot de passe ni clé. C'est le piège déjà connu
pour Debian, reproduit ailleurs. Côté hôte, EPEL et CRB sont activés :
sans eux la plupart des -devel manquent, en silence.
Le choix serveur / graphique gagne Cinnamon, le bureau de Linux Mint,
depuis les dépôts de la distribution. Le dépôt de Mint lui-même est
écarté : il est en HTTP nu et ne publie que i386 et amd64, ce qui
exclurait arm64 et s390x. Au passage, dnf installe désormais un
ENVIRONNEMENT et non un groupe — « gnome-desktop » apporte gdm et
gnome-shell mais pas base-x, donc pas de serveur X.
Assisted-by: Claude Opus 5
2026-08-11 18:46:11 -04:00
|
|
|
|
"Graphical (server + desktop):": {
|
|
|
|
|
|
"fr": "Graphique (serveur + bureau) :",
|
|
|
|
|
|
"en": "Graphical (server + desktop):",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Installing the desktop (long):": {
|
|
|
|
|
|
"fr": "Installation du bureau (long) :",
|
|
|
|
|
|
"en": "Installing the desktop (long):",
|
|
|
|
|
|
},
|
[ADD] tui qemu: graphical VMs, with a GNOME desktop
VMs could only be servers. A server-or-graphical choice joins both
interfaces, installing GNOME along with remote access to it.
Packages come from the remote command, not cloud-init: their 1 to 2 GB
would stretch an already long boot there while leaving no trace in the
monitoring, and group installs do not go through it. The desktop
therefore does not depend on ERPLibre -- a VM may be wanted graphical
and bare.
Each distribution has its own names, taken from the source: Arch has no
xrdp in its official repositories and takes TigerVNC. The SPICE display
is set only on amd64 and arm64; s390x does expose virtio-gpu-ccw, but
nothing guarantees its kernel's DRM driver, whereas remote desktop works
everywhere.
--- FR ---
Les VM ne pouvaient être que des serveurs. Un choix serveur ou graphique
s'ajoute aux deux interfaces, et pose GNOME avec son accès distant.
Les paquets viennent de la commande distante, pas de cloud-init : leurs
1 à 2 Go y allongeraient un démarrage déjà long sans laisser de trace
dans le suivi, et les installations par groupe n'y passent pas. Le bureau
ne dépend donc pas d'ERPLibre — une VM peut être voulue graphique et nue.
Chaque distribution a ses noms, relevés à la source : Arch n'a pas xrdp
dans ses dépôts officiels et prend TigerVNC. L'écran virtuel SPICE n'est
posé que sur amd64 et arm64 ; s390x expose bien virtio-gpu-ccw, mais rien
ne garantit le pilote DRM de son noyau, alors que le bureau distant, lui,
marche partout.
Assisted-by: Claude Opus 5
2026-08-11 09:49:17 -04:00
|
|
|
|
"Choice (number, blank = server): ": {
|
|
|
|
|
|
"fr": "Choix (numero, vide = serveur) : ",
|
|
|
|
|
|
"en": "Choice (number, blank = server): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Installing the GNOME desktop (long)": {
|
|
|
|
|
|
"fr": "Installation du bureau GNOME (long)",
|
|
|
|
|
|
"en": "Installing the GNOME desktop (long)",
|
|
|
|
|
|
},
|
[ADD] install: openSUSE support, mirrors and packages
Tumbleweed is the only catalog entry whose qpdf already clears the pikepdf
threshold: 12.3.2 against 12.2 required, so the half-hour qpdf build under
s390x emulation never runs there. It is also the only family foreign to
both RHEL and Debian, and it brings zypper, absent from the repository:
wired into the install_dev.sh dispatch, a dependency script, the remote
bootstrap and the desktop block.
Four SUSE traps, all met on real machines. Global options precede the
subcommand, and misplaced ones abort the call; without
"--auto-agree-with-licenses" zypper waits for an answer nobody gives in a
detached install. A compat package that PROVIDES another under a different
name makes zypper raise a conflict and drop the whole batch, so only what
nothing already provides is requested. pkg-config no longer exists as an
RPM, only as a capability. And a single mirror, unreachable one day, sent
everyone back to Europe -- three are probed in order now.
CentOS Stream is dropped again: it served as a canary but 10 duplicates
what Alma and Rocky already cover.
--- FR ---
Tumbleweed est la seule entrée du catalogue dont qpdf franchit déjà le
seuil de pikepdf : 12.3.2 contre 12.2 exigé, si bien que la demi-heure de
compilation de qpdf sous émulation s390x ne s'y déclenche jamais. C'est
aussi la seule famille étrangère à RHEL comme à Debian, et elle apporte
zypper, absent du dépôt : câblé dans l'aiguillage d'install_dev.sh, un
script de dépendances, l'amorçage distant et le bloc bureau.
Quatre pièges SUSE, tous rencontrés sur machine réelle. Les options
globales précèdent la sous-commande, mal placées elles arrêtent l'appel ;
sans « --auto-agree-with-licenses » zypper attend une réponse que personne
ne donne dans une installation détachée. Un paquet compat qui FOURNIT un
autre sous un nom différent fait lever un conflit à zypper, qui abandonne
le lot entier : on ne demande donc que ce que rien ne fournit déjà.
pkg-config n'existe plus comme RPM, seulement comme capacité. Et un miroir
unique, injoignable un jour, renvoyait tout le monde en Europe — trois
sont désormais sondés dans l'ordre.
CentOS Stream repart : il servait de canari, mais 10 double ce qu'Alma et
Rocky couvrent déjà.
Assisted-by: Claude Opus 5
2026-08-11 20:38:12 -04:00
|
|
|
|
"openSUSE mirror:": {
|
|
|
|
|
|
"fr": "miroir openSUSE :",
|
|
|
|
|
|
"en": "openSUSE mirror:",
|
|
|
|
|
|
},
|
[ADD] tui qemu: graphical VMs, with a GNOME desktop
VMs could only be servers. A server-or-graphical choice joins both
interfaces, installing GNOME along with remote access to it.
Packages come from the remote command, not cloud-init: their 1 to 2 GB
would stretch an already long boot there while leaving no trace in the
monitoring, and group installs do not go through it. The desktop
therefore does not depend on ERPLibre -- a VM may be wanted graphical
and bare.
Each distribution has its own names, taken from the source: Arch has no
xrdp in its official repositories and takes TigerVNC. The SPICE display
is set only on amd64 and arm64; s390x does expose virtio-gpu-ccw, but
nothing guarantees its kernel's DRM driver, whereas remote desktop works
everywhere.
--- FR ---
Les VM ne pouvaient être que des serveurs. Un choix serveur ou graphique
s'ajoute aux deux interfaces, et pose GNOME avec son accès distant.
Les paquets viennent de la commande distante, pas de cloud-init : leurs
1 à 2 Go y allongeraient un démarrage déjà long sans laisser de trace
dans le suivi, et les installations par groupe n'y passent pas. Le bureau
ne dépend donc pas d'ERPLibre — une VM peut être voulue graphique et nue.
Chaque distribution a ses noms, relevés à la source : Arch n'a pas xrdp
dans ses dépôts officiels et prend TigerVNC. L'écran virtuel SPICE n'est
posé que sur amd64 et arm64 ; s390x expose bien virtio-gpu-ccw, mais rien
ne garantit le pilote DRM de son noyau, alors que le bureau distant, lui,
marche partout.
Assisted-by: Claude Opus 5
2026-08-11 09:49:17 -04:00
|
|
|
|
"Remote desktop:": {
|
|
|
|
|
|
"fr": "Bureau distant :",
|
|
|
|
|
|
"en": "Remote desktop:",
|
|
|
|
|
|
},
|
2026-08-11 08:59:10 -04:00
|
|
|
|
"One run per install": {
|
|
|
|
|
|
"fr": "une execution par installation",
|
|
|
|
|
|
"en": "One run per install",
|
|
|
|
|
|
},
|
|
|
|
|
|
"limit to host cores": {
|
|
|
|
|
|
"fr": "limiter au nombre de coeurs de l'hote",
|
|
|
|
|
|
"en": "limit to host cores",
|
|
|
|
|
|
},
|
[ADD] tui qemu: free values for vCPU, RAM and disk
The CLI already took a typed value -- a letter picks a suggestion, a
digit stands for itself. The form, though, locked the custom profile
into its three dropdowns.
Each now gets a final "free value..." entry revealing an input below it.
An invalid entry overwrites nothing: the resource falls back to the
catalog value instead of freezing the view.
The 3 vCPU preset was missing too, between 2 and 4; it comes from the
same constant, so both interfaces gain it together.
--- FR ---
La CLI acceptait déjà une valeur tapée — une lettre choisit une
suggestion, un chiffre vaut pour lui-même. Le formulaire, lui, enfermait
le profil personnalisé dans ses trois listes déroulantes.
Chacune reçoit donc un dernier choix, « valeur libre… », qui révèle une
saisie sous elle. Une entrée invalide n'écrase rien : la ressource
retombe sur celle du catalogue plutôt que de bloquer la vue.
Le préréglage 3 vCPU manquait aussi, entre 2 et 4 ; il vient de la même
constante, donc les deux interfaces l'offrent ensemble.
Assisted-by: Claude Opus 5
2026-08-11 06:57:49 -04:00
|
|
|
|
"free value…": {
|
|
|
|
|
|
"fr": "valeur libre…",
|
|
|
|
|
|
"en": "free value…",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Disk (e.g. 250G, 1.5T)": {
|
|
|
|
|
|
"fr": "Disque (ex. 250G, 1,5T)",
|
|
|
|
|
|
"en": "Disk (e.g. 250G, 1.5T)",
|
|
|
|
|
|
},
|
[ADD] todo: QEMU/KVM menu
Deploy, list, test, resize, delete and clean up VMs. Adds the SSH
configuration with recursive ProxyJump, port forwarding, and registration
of the QEMU hosts in virt-manager.
--- FR ---
Déployer, lister, tester, redimensionner, supprimer et nettoyer des VM.
Ajoute la configuration SSH avec ProxyJump récursif, la redirection de port
et l'enregistrement des hôtes QEMU dans virt-manager.
Assisted-by: Claude Opus 4.8
2026-08-07 03:22:26 -04:00
|
|
|
|
"all archs": {
|
|
|
|
|
|
"fr": "toutes",
|
|
|
|
|
|
"en": "all",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Tick what to deploy": {
|
|
|
|
|
|
"fr": "Cocher ce qu'on veut déployer",
|
|
|
|
|
|
"en": "Tick what to deploy",
|
|
|
|
|
|
},
|
|
|
|
|
|
"F7 main versions · F6 all": {
|
|
|
|
|
|
"fr": "F7 versions principales · F6 tout",
|
|
|
|
|
|
"en": "F7 main versions · F6 all",
|
|
|
|
|
|
},
|
|
|
|
|
|
"exists - skipped": {
|
|
|
|
|
|
"fr": "existe — ignorée",
|
|
|
|
|
|
"en": "exists - skipped",
|
|
|
|
|
|
},
|
|
|
|
|
|
"orphan disk - will FAIL": {
|
|
|
|
|
|
"fr": "disque orphelin — ÉCHOUERA",
|
|
|
|
|
|
"en": "orphan disk - will FAIL",
|
|
|
|
|
|
},
|
|
|
|
|
|
"press F5 again to confirm": {
|
|
|
|
|
|
"fr": "F5 à nouveau pour confirmer",
|
|
|
|
|
|
"en": "press F5 again to confirm",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Deploy": {
|
|
|
|
|
|
"fr": "Déployer",
|
|
|
|
|
|
"en": "Deploy",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Edit VM": {
|
|
|
|
|
|
"fr": "Éditer la VM",
|
|
|
|
|
|
"en": "Edit VM",
|
|
|
|
|
|
},
|
[ADD] tui qemu: set every VM in place, type included
VM type was global: the whole fleet as servers, or all of them GNOME. It
now lives on the VM, all the way down -- the creation flag, and one remote
command per machine at install time, where a single one served them all.
The right pane is no longer a table but a row of widgets per VM: vCPU,
RAM, disk and type, each with its usual values and a free entry. The
left-hand fields become the shared default, which the screen now says. The
scope selector and the F2 modal go away: given two ways to do the same
thing, keep the visible one.
Three traps came out of it, and the tests lock them. Widget ids carry a
RANK, and the rank shifts when an entry is ticked, so an event from an
already destroyed widget applied to the VM that took its place -- rows now
carry a generation, marked BEFORE mounting, since mount_all empties the
pending children and marking after it was a race. The x1..x4 profile no
longer reached any VM. And a total of zero never said it had counted
nothing.
--- FR ---
Le type de VM était global : tout le parc en serveur, ou tout en GNOME. Il
vit désormais sur la VM, jusqu'au bout — le drapeau de création, et une
commande distante par machine à l'installation, là où une seule les
servait toutes.
Le panneau de droite n'est plus un tableau mais une rangée de widgets par
VM : vCPU, RAM, disque et type, chacun avec ses valeurs usuelles et une
saisie libre. Les champs de gauche deviennent le défaut commun, ce que
l'écran dit maintenant. Le sélecteur de portée et la modale F2 partent :
entre deux façons de faire la même chose, on garde la visible.
Trois pièges en sont sortis, et les tests les verrouillent. Les
identifiants de widgets portent un RANG, et le rang se décale quand on
coche une entrée : un événement émis par un widget déjà détruit
s'appliquait à la VM qui avait pris sa place — les rangées portent
maintenant une génération, marquée AVANT le montage, car mount_all vide
les enfants en attente et marquer après était une course. Le profil x1..x4
n'atteignait plus aucune VM. Et un total à zéro ne disait pas qu'il
n'avait rien compté.
Assisted-by: Claude Opus 5
2026-08-12 02:46:41 -04:00
|
|
|
|
"Server": {
|
|
|
|
|
|
"fr": "Serveur",
|
|
|
|
|
|
"en": "Server",
|
|
|
|
|
|
},
|
|
|
|
|
|
"VM type (default):": {
|
|
|
|
|
|
"fr": "Type de VM (défaut) :",
|
|
|
|
|
|
"en": "VM type (default):",
|
|
|
|
|
|
},
|
|
|
|
|
|
"These values are the default for every VM;": {
|
|
|
|
|
|
"fr": "Ces valeurs sont le défaut de chaque VM ;",
|
|
|
|
|
|
"en": "These values are the default for every VM;",
|
|
|
|
|
|
},
|
|
|
|
|
|
"adjust any of them per VM on the right.": {
|
|
|
|
|
|
"fr": "ajustez-les VM par VM dans la vue de droite.",
|
|
|
|
|
|
"en": "adjust any of them per VM on the right.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Application store:": {
|
|
|
|
|
|
"fr": "Magasin d'applications :",
|
|
|
|
|
|
"en": "Application store:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Application store (graphical Ubuntu VMs):": {
|
|
|
|
|
|
"fr": "Magasin d'applications (VM Ubuntu graphiques) :",
|
|
|
|
|
|
"en": "Application store (graphical Ubuntu VMs):",
|
|
|
|
|
|
},
|
|
|
|
|
|
"deb only (epiphany-browser)": {
|
|
|
|
|
|
"fr": "deb uniquement (epiphany-browser)",
|
|
|
|
|
|
"en": "deb only (epiphany-browser)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Flatpak tooling, no Flathub": {
|
|
|
|
|
|
"fr": "outillage Flatpak, sans Flathub",
|
|
|
|
|
|
"en": "Flatpak tooling, no Flathub",
|
|
|
|
|
|
},
|
|
|
|
|
|
"snap (Ubuntu default, Firefox)": {
|
|
|
|
|
|
"fr": "snap (défaut Ubuntu, Firefox)",
|
|
|
|
|
|
"en": "snap (Ubuntu default, Firefox)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"snap needs the store; slow under emulation.": {
|
|
|
|
|
|
"fr": "snap exige le store ; lent sous émulation.",
|
|
|
|
|
|
"en": "snap needs the store; slow under emulation.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No graphical VM on a snap-based distro.": {
|
|
|
|
|
|
"fr": "Aucune VM graphique sur une distribution à snap.",
|
|
|
|
|
|
"en": "No graphical VM on a snap-based distro.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Resources — applied to ALL VMs": {
|
|
|
|
|
|
"fr": "Ressources — appliquées à TOUTES les VM",
|
|
|
|
|
|
"en": "Resources — applied to ALL VMs",
|
|
|
|
|
|
},
|
|
|
|
|
|
"The profile and these fields change EVERY VM.": {
|
|
|
|
|
|
"fr": "Le profil et ces champs changent TOUTES les VM.",
|
|
|
|
|
|
"en": "The profile and these fields change EVERY VM.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"A VM edited on the right (marked) keeps its own.": {
|
|
|
|
|
|
"fr": "Une VM modifiée à droite (marquée ✎) garde les siennes.",
|
|
|
|
|
|
"en": "A VM edited on the right (marked) keeps its own.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Rename the VM": {
|
|
|
|
|
|
"fr": "Renommer la VM",
|
|
|
|
|
|
"en": "Rename the VM",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Rename": {
|
|
|
|
|
|
"fr": "Renommer",
|
|
|
|
|
|
"en": "Rename",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Empty = back to the automatic name:": {
|
|
|
|
|
|
"fr": "Vide = revenir au nom automatique :",
|
|
|
|
|
|
"en": "Empty = back to the automatic name:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Invalid name: letters, digits, hyphens.": {
|
|
|
|
|
|
"fr": "Nom invalide : lettres, chiffres et traits d'union.",
|
|
|
|
|
|
"en": "Invalid name: letters, digits, hyphens.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"default; a VM may differ, see its line": {
|
|
|
|
|
|
"fr": "défaut ; une VM peut s'en écarter, voir sa ligne",
|
|
|
|
|
|
"en": "default; a VM may differ, see its line",
|
|
|
|
|
|
},
|
|
|
|
|
|
"varies, see each line": {
|
|
|
|
|
|
"fr": "varie, voir chaque ligne",
|
|
|
|
|
|
"en": "varies, see each line",
|
|
|
|
|
|
},
|
|
|
|
|
|
"From your workstation:": {
|
|
|
|
|
|
"fr": "Depuis votre poste :",
|
|
|
|
|
|
"en": "From your workstation:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"then point your client at": {
|
|
|
|
|
|
"fr": "puis pointez votre client sur",
|
|
|
|
|
|
"en": "then point your client at",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Remote desktop tunnel (VNC/RDP through SSH)": {
|
|
|
|
|
|
"fr": "🖥 Tunnel bureau distant (VNC/RDP par SSH)",
|
|
|
|
|
|
"en": "🖥 Remote desktop tunnel (VNC/RDP through SSH)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Remote desktop tunnel": {
|
|
|
|
|
|
"fr": "Tunnel vers le bureau distant",
|
|
|
|
|
|
"en": "Remote desktop tunnel",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Which VM?": {"fr": "Quelle VM ?", "en": "Which VM?"},
|
|
|
|
|
|
"No IP for this VM; is it running?": {
|
|
|
|
|
|
"fr": "Pas d'IP pour cette VM ; tourne-t-elle ?",
|
|
|
|
|
|
"en": "No IP for this VM; is it running?",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Run this on YOUR workstation:": {
|
|
|
|
|
|
"fr": "À lancer sur VOTRE poste :",
|
|
|
|
|
|
"en": "Run this on YOUR workstation:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"(through the ProxyJump already in ~/.ssh/config)": {
|
|
|
|
|
|
"fr": "(par le ProxyJump déjà dans ~/.ssh/config)",
|
|
|
|
|
|
"en": "(through the ProxyJump already in ~/.ssh/config)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No ~/.ssh/config entry; see SSH configuration.": {
|
|
|
|
|
|
"fr": "Aucune entrée ~/.ssh/config ; voir Configuration SSH.",
|
|
|
|
|
|
"en": "No ~/.ssh/config entry; see SSH configuration.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Not in an SSH session: check the host address.": {
|
|
|
|
|
|
"fr": "Hors session SSH : vérifiez l'adresse de l'hôte.",
|
|
|
|
|
|
"en": "Not in an SSH session: check the host address.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"The tunnel stays open as long as that ssh runs.": {
|
|
|
|
|
|
"fr": "Le tunnel reste ouvert tant que ce ssh tourne.",
|
|
|
|
|
|
"en": "The tunnel stays open as long as that ssh runs.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No VM defined.": {"fr": "Aucune VM définie.", "en": "No VM defined."},
|
|
|
|
|
|
"No host in ~/.ssh/config and no local VM.": {
|
|
|
|
|
|
"fr": "Aucun hôte dans ~/.ssh/config et aucune VM locale.",
|
|
|
|
|
|
"en": "No host in ~/.ssh/config and no local VM.",
|
[ADD] tui qemu: customise one VM without touching the others
The three resource fields applied to the whole fleet. With a single
machine needing 16 G, the other eight got it too. A scope selector
settles it: all VMs, or the row targeted in the plan. The x1..x4 profile
stays global, multiplying what each image asks for; under "selected VM"
the fields are absolute, hence active even outside the custom profile.
The F2 modal already existed but was undiscoverable, and its cursor was
unusable: the table never held focus. It stays, the toggle now focuses
the table, F4 returns a VM to the shared profile, and the plan marks
customised rows with a ✎ -- without it, two rows with different
resources have no explanation on screen.
One defect found along the way: clear() reset the cursor to the top on
every redraw, and the plan recomputes on every keystroke. You picked
debian, typed the value, the table redrew, and the next entry landed on
ubuntu with nothing to show for it.
--- FR ---
Les trois champs de ressources s'appliquaient à tout le parc. Une seule
machine ayant besoin de 16 G, il fallait les donner aux huit autres. Un
sélecteur de portée tranche : toutes les VM, ou la ligne visée dans le
plan. Le profil x1..x4 reste global, il multiplie ce que demande chaque
image ; en portée « une seule » les champs valent en absolu, et sont donc
actifs même hors profil personnalisé.
La modale F2 existait déjà mais restait introuvable, et son curseur était
inutilisable : le tableau n'avait jamais le focus. Elle demeure, la
bascule lui donne le focus, F4 rend une VM au profil commun, et le plan
marque d'un ✎ ce qui a été personnalisé — sans marque, deux lignes aux
ressources différentes n'ont aucune explication à l'écran.
Un défaut au passage : « clear() » ramenait le curseur en tête à chaque
redessin, et le plan se recalcule à chaque frappe. On choisissait debian,
on tapait la valeur, le tableau se redessinait, et la saisie suivante
partait sur ubuntu sans que rien ne le montre.
Assisted-by: Claude Opus 5
2026-08-12 01:30:11 -04:00
|
|
|
|
},
|
[ADD] tui qemu: set every VM in place, type included
VM type was global: the whole fleet as servers, or all of them GNOME. It
now lives on the VM, all the way down -- the creation flag, and one remote
command per machine at install time, where a single one served them all.
The right pane is no longer a table but a row of widgets per VM: vCPU,
RAM, disk and type, each with its usual values and a free entry. The
left-hand fields become the shared default, which the screen now says. The
scope selector and the F2 modal go away: given two ways to do the same
thing, keep the visible one.
Three traps came out of it, and the tests lock them. Widget ids carry a
RANK, and the rank shifts when an entry is ticked, so an event from an
already destroyed widget applied to the VM that took its place -- rows now
carry a generation, marked BEFORE mounting, since mount_all empties the
pending children and marking after it was a race. The x1..x4 profile no
longer reached any VM. And a total of zero never said it had counted
nothing.
--- FR ---
Le type de VM était global : tout le parc en serveur, ou tout en GNOME. Il
vit désormais sur la VM, jusqu'au bout — le drapeau de création, et une
commande distante par machine à l'installation, là où une seule les
servait toutes.
Le panneau de droite n'est plus un tableau mais une rangée de widgets par
VM : vCPU, RAM, disque et type, chacun avec ses valeurs usuelles et une
saisie libre. Les champs de gauche deviennent le défaut commun, ce que
l'écran dit maintenant. Le sélecteur de portée et la modale F2 partent :
entre deux façons de faire la même chose, on garde la visible.
Trois pièges en sont sortis, et les tests les verrouillent. Les
identifiants de widgets portent un RANG, et le rang se décale quand on
coche une entrée : un événement émis par un widget déjà détruit
s'appliquait à la VM qui avait pris sa place — les rangées portent
maintenant une génération, marquée AVANT le montage, car mount_all vide
les enfants en attente et marquer après était une course. Le profil x1..x4
n'atteignait plus aucune VM. Et un total à zéro ne disait pas qu'il
n'avait rien compté.
Assisted-by: Claude Opus 5
2026-08-12 02:46:41 -04:00
|
|
|
|
"local VM": {"fr": "VM locale", "en": "local VM"},
|
|
|
|
|
|
"Remote desktop kind:": {
|
|
|
|
|
|
"fr": "Type de bureau distant :",
|
|
|
|
|
|
"en": "Remote desktop kind:",
|
[ADD] tui qemu: customise one VM without touching the others
The three resource fields applied to the whole fleet. With a single
machine needing 16 G, the other eight got it too. A scope selector
settles it: all VMs, or the row targeted in the plan. The x1..x4 profile
stays global, multiplying what each image asks for; under "selected VM"
the fields are absolute, hence active even outside the custom profile.
The F2 modal already existed but was undiscoverable, and its cursor was
unusable: the table never held focus. It stays, the toggle now focuses
the table, F4 returns a VM to the shared profile, and the plan marks
customised rows with a ✎ -- without it, two rows with different
resources have no explanation on screen.
One defect found along the way: clear() reset the cursor to the top on
every redraw, and the plan recomputes on every keystroke. You picked
debian, typed the value, the table redrew, and the next entry landed on
ubuntu with nothing to show for it.
--- FR ---
Les trois champs de ressources s'appliquaient à tout le parc. Une seule
machine ayant besoin de 16 G, il fallait les donner aux huit autres. Un
sélecteur de portée tranche : toutes les VM, ou la ligne visée dans le
plan. Le profil x1..x4 reste global, il multiplie ce que demande chaque
image ; en portée « une seule » les champs valent en absolu, et sont donc
actifs même hors profil personnalisé.
La modale F2 existait déjà mais restait introuvable, et son curseur était
inutilisable : le tableau n'avait jamais le focus. Elle demeure, la
bascule lui donne le focus, F4 rend une VM au profil commun, et le plan
marque d'un ✎ ce qui a été personnalisé — sans marque, deux lignes aux
ressources différentes n'ont aucune explication à l'écran.
Un défaut au passage : « clear() » ramenait le curseur en tête à chaque
redessin, et le plan se recalcule à chaque frappe. On choisissait debian,
on tapait la valeur, le tableau se redessinait, et la saisie suivante
partait sur ubuntu sans que rien ne le montre.
Assisted-by: Claude Opus 5
2026-08-12 01:30:11 -04:00
|
|
|
|
},
|
[ADD] tui qemu: set every VM in place, type included
VM type was global: the whole fleet as servers, or all of them GNOME. It
now lives on the VM, all the way down -- the creation flag, and one remote
command per machine at install time, where a single one served them all.
The right pane is no longer a table but a row of widgets per VM: vCPU,
RAM, disk and type, each with its usual values and a free entry. The
left-hand fields become the shared default, which the screen now says. The
scope selector and the F2 modal go away: given two ways to do the same
thing, keep the visible one.
Three traps came out of it, and the tests lock them. Widget ids carry a
RANK, and the rank shifts when an entry is ticked, so an event from an
already destroyed widget applied to the VM that took its place -- rows now
carry a generation, marked BEFORE mounting, since mount_all empties the
pending children and marking after it was a race. The x1..x4 profile no
longer reached any VM. And a total of zero never said it had counted
nothing.
--- FR ---
Le type de VM était global : tout le parc en serveur, ou tout en GNOME. Il
vit désormais sur la VM, jusqu'au bout — le drapeau de création, et une
commande distante par machine à l'installation, là où une seule les
servait toutes.
Le panneau de droite n'est plus un tableau mais une rangée de widgets par
VM : vCPU, RAM, disque et type, chacun avec ses valeurs usuelles et une
saisie libre. Les champs de gauche deviennent le défaut commun, ce que
l'écran dit maintenant. Le sélecteur de portée et la modale F2 partent :
entre deux façons de faire la même chose, on garde la visible.
Trois pièges en sont sortis, et les tests les verrouillent. Les
identifiants de widgets portent un RANG, et le rang se décale quand on
coche une entrée : un événement émis par un widget déjà détruit
s'appliquait à la VM qui avait pris sa place — les rangées portent
maintenant une génération, marquée AVANT le montage, car mount_all vide
les enfants en attente et marquer après était une course. Le profil x1..x4
n'atteignait plus aucune VM. Et un total à zéro ne disait pas qu'il
n'avait rien compté.
Assisted-by: Claude Opus 5
2026-08-12 02:46:41 -04:00
|
|
|
|
"already defined, not counted": {
|
|
|
|
|
|
"fr": "déjà définie(s), non comptée(s)",
|
|
|
|
|
|
"en": "already defined, not counted",
|
[ADD] tui qemu: customise one VM without touching the others
The three resource fields applied to the whole fleet. With a single
machine needing 16 G, the other eight got it too. A scope selector
settles it: all VMs, or the row targeted in the plan. The x1..x4 profile
stays global, multiplying what each image asks for; under "selected VM"
the fields are absolute, hence active even outside the custom profile.
The F2 modal already existed but was undiscoverable, and its cursor was
unusable: the table never held focus. It stays, the toggle now focuses
the table, F4 returns a VM to the shared profile, and the plan marks
customised rows with a ✎ -- without it, two rows with different
resources have no explanation on screen.
One defect found along the way: clear() reset the cursor to the top on
every redraw, and the plan recomputes on every keystroke. You picked
debian, typed the value, the table redrew, and the next entry landed on
ubuntu with nothing to show for it.
--- FR ---
Les trois champs de ressources s'appliquaient à tout le parc. Une seule
machine ayant besoin de 16 G, il fallait les donner aux huit autres. Un
sélecteur de portée tranche : toutes les VM, ou la ligne visée dans le
plan. Le profil x1..x4 reste global, il multiplie ce que demande chaque
image ; en portée « une seule » les champs valent en absolu, et sont donc
actifs même hors profil personnalisé.
La modale F2 existait déjà mais restait introuvable, et son curseur était
inutilisable : le tableau n'avait jamais le focus. Elle demeure, la
bascule lui donne le focus, F4 rend une VM au profil commun, et le plan
marque d'un ✎ ce qui a été personnalisé — sans marque, deux lignes aux
ressources différentes n'ont aucune explication à l'écran.
Un défaut au passage : « clear() » ramenait le curseur en tête à chaque
redessin, et le plan se recalcule à chaque frappe. On choisissait debian,
on tapait la valeur, le tableau se redessinait, et la saisie suivante
partait sur ubuntu sans que rien ne le montre.
Assisted-by: Claude Opus 5
2026-08-12 01:30:11 -04:00
|
|
|
|
},
|
[ADD] tui qemu: set every VM in place, type included
VM type was global: the whole fleet as servers, or all of them GNOME. It
now lives on the VM, all the way down -- the creation flag, and one remote
command per machine at install time, where a single one served them all.
The right pane is no longer a table but a row of widgets per VM: vCPU,
RAM, disk and type, each with its usual values and a free entry. The
left-hand fields become the shared default, which the screen now says. The
scope selector and the F2 modal go away: given two ways to do the same
thing, keep the visible one.
Three traps came out of it, and the tests lock them. Widget ids carry a
RANK, and the rank shifts when an entry is ticked, so an event from an
already destroyed widget applied to the VM that took its place -- rows now
carry a generation, marked BEFORE mounting, since mount_all empties the
pending children and marking after it was a race. The x1..x4 profile no
longer reached any VM. And a total of zero never said it had counted
nothing.
--- FR ---
Le type de VM était global : tout le parc en serveur, ou tout en GNOME. Il
vit désormais sur la VM, jusqu'au bout — le drapeau de création, et une
commande distante par machine à l'installation, là où une seule les
servait toutes.
Le panneau de droite n'est plus un tableau mais une rangée de widgets par
VM : vCPU, RAM, disque et type, chacun avec ses valeurs usuelles et une
saisie libre. Les champs de gauche deviennent le défaut commun, ce que
l'écran dit maintenant. Le sélecteur de portée et la modale F2 partent :
entre deux façons de faire la même chose, on garde la visible.
Trois pièges en sont sortis, et les tests les verrouillent. Les
identifiants de widgets portent un RANG, et le rang se décale quand on
coche une entrée : un événement émis par un widget déjà détruit
s'appliquait à la VM qui avait pris sa place — les rangées portent
maintenant une génération, marquée AVANT le montage, car mount_all vide
les enfants en attente et marquer après était une course. Le profil x1..x4
n'atteignait plus aucune VM. Et un total à zéro ne disait pas qu'il
n'avait rien compté.
Assisted-by: Claude Opus 5
2026-08-12 02:46:41 -04:00
|
|
|
|
"RAM: 2048 or 8G": {"fr": "RAM : 2048 ou 8G", "en": "RAM: 2048 or 8G"},
|
[ADD] tui qemu: F9 dumps the form state, widgets and model
A per-VM setting appears not to be taken into account, and nothing on
screen tells which link failed: the gap may be between the widget and
the model, or between the model and the spec. A screenshot shows
neither.
F9 writes both side by side into ~/.erplibre/deploy-form-dump.txt:
profile, shared values, overrides, locks, row generation, then VM by VM
what the list DISPLAYS against what the model HOLDS, and finally the
spec that would go to deployment.
The file can be pasted into a message, unlike an image, and it answers
the question on its own: if the list shows 16384 while the model says
1024, the defect is in the intake; if they agree and the created VM
differs, it is downstream, in deploy_qemu.
--- FR ---
Un réglage par VM ne semble pas pris en compte, et rien dans ce que
l'écran montre ne permet de trancher : l'écart peut être entre le widget
et le modèle, ou entre le modèle et la spec. Une capture d'écran ne dit
ni l'un ni l'autre.
F9 écrit les deux côte à côte dans ~/.erplibre/deploy-form-dump.txt :
profil, valeurs communes, surcharges, verrous, génération des rangées,
puis VM par VM ce que la liste AFFICHE contre ce que le modèle CONTIENT,
et enfin la spec qui partirait au déploiement.
Le fichier se recopie dans un message, contrairement à une image, et il
répond seul à la question : si la liste montre 16384 et que le modèle
dit 1024, le défaut est dans la prise en compte ; s'ils s'accordent et
que la VM créée diffère, il est en aval, dans deploy_qemu.
Assisted-by: Claude Opus 5
2026-08-13 04:51:27 -04:00
|
|
|
|
"Diagnostic dump": {"fr": "Diagnostic", "en": "Diagnostic dump"},
|
|
|
|
|
|
"State written to": {"fr": "État écrit dans", "en": "State written to"},
|
[ADD] tui qemu: set every VM in place, type included
VM type was global: the whole fleet as servers, or all of them GNOME. It
now lives on the VM, all the way down -- the creation flag, and one remote
command per machine at install time, where a single one served them all.
The right pane is no longer a table but a row of widgets per VM: vCPU,
RAM, disk and type, each with its usual values and a free entry. The
left-hand fields become the shared default, which the screen now says. The
scope selector and the F2 modal go away: given two ways to do the same
thing, keep the visible one.
Three traps came out of it, and the tests lock them. Widget ids carry a
RANK, and the rank shifts when an entry is ticked, so an event from an
already destroyed widget applied to the VM that took its place -- rows now
carry a generation, marked BEFORE mounting, since mount_all empties the
pending children and marking after it was a race. The x1..x4 profile no
longer reached any VM. And a total of zero never said it had counted
nothing.
--- FR ---
Le type de VM était global : tout le parc en serveur, ou tout en GNOME. Il
vit désormais sur la VM, jusqu'au bout — le drapeau de création, et une
commande distante par machine à l'installation, là où une seule les
servait toutes.
Le panneau de droite n'est plus un tableau mais une rangée de widgets par
VM : vCPU, RAM, disque et type, chacun avec ses valeurs usuelles et une
saisie libre. Les champs de gauche deviennent le défaut commun, ce que
l'écran dit maintenant. Le sélecteur de portée et la modale F2 partent :
entre deux façons de faire la même chose, on garde la visible.
Trois pièges en sont sortis, et les tests les verrouillent. Les
identifiants de widgets portent un RANG, et le rang se décale quand on
coche une entrée : un événement émis par un widget déjà détruit
s'appliquait à la VM qui avait pris sa place — les rangées portent
maintenant une génération, marquée AVANT le montage, car mount_all vide
les enfants en attente et marquer après était une course. Le profil x1..x4
n'atteignait plus aucune VM. Et un total à zéro ne disait pas qu'il
n'avait rien compté.
Assisted-by: Claude Opus 5
2026-08-12 02:46:41 -04:00
|
|
|
|
"Reset VM": {
|
|
|
|
|
|
"fr": "Réinit. VM",
|
|
|
|
|
|
"en": "Reset VM",
|
[ADD] tui qemu: customise one VM without touching the others
The three resource fields applied to the whole fleet. With a single
machine needing 16 G, the other eight got it too. A scope selector
settles it: all VMs, or the row targeted in the plan. The x1..x4 profile
stays global, multiplying what each image asks for; under "selected VM"
the fields are absolute, hence active even outside the custom profile.
The F2 modal already existed but was undiscoverable, and its cursor was
unusable: the table never held focus. It stays, the toggle now focuses
the table, F4 returns a VM to the shared profile, and the plan marks
customised rows with a ✎ -- without it, two rows with different
resources have no explanation on screen.
One defect found along the way: clear() reset the cursor to the top on
every redraw, and the plan recomputes on every keystroke. You picked
debian, typed the value, the table redrew, and the next entry landed on
ubuntu with nothing to show for it.
--- FR ---
Les trois champs de ressources s'appliquaient à tout le parc. Une seule
machine ayant besoin de 16 G, il fallait les donner aux huit autres. Un
sélecteur de portée tranche : toutes les VM, ou la ligne visée dans le
plan. Le profil x1..x4 reste global, il multiplie ce que demande chaque
image ; en portée « une seule » les champs valent en absolu, et sont donc
actifs même hors profil personnalisé.
La modale F2 existait déjà mais restait introuvable, et son curseur était
inutilisable : le tableau n'avait jamais le focus. Elle demeure, la
bascule lui donne le focus, F4 rend une VM au profil commun, et le plan
marque d'un ✎ ce qui a été personnalisé — sans marque, deux lignes aux
ressources différentes n'ont aucune explication à l'écran.
Un défaut au passage : « clear() » ramenait le curseur en tête à chaque
redessin, et le plan se recalcule à chaque frappe. On choisissait debian,
on tapait la valeur, le tableau se redessinait, et la saisie suivante
partait sur ubuntu sans que rien ne le montre.
Assisted-by: Claude Opus 5
2026-08-12 01:30:11 -04:00
|
|
|
|
},
|
[ADD] todo: QEMU/KVM menu
Deploy, list, test, resize, delete and clean up VMs. Adds the SSH
configuration with recursive ProxyJump, port forwarding, and registration
of the QEMU hosts in virt-manager.
--- FR ---
Déployer, lister, tester, redimensionner, supprimer et nettoyer des VM.
Ajoute la configuration SSH avec ProxyJump récursif, la redirection de port
et l'enregistrement des hôtes QEMU dans virt-manager.
Assisted-by: Claude Opus 4.8
2026-08-07 03:22:26 -04:00
|
|
|
|
"Preview": {
|
|
|
|
|
|
"fr": "Aperçu",
|
|
|
|
|
|
"en": "Preview",
|
|
|
|
|
|
},
|
|
|
|
|
|
"All": {
|
|
|
|
|
|
"fr": "Tout",
|
|
|
|
|
|
"en": "All",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Main versions": {
|
|
|
|
|
|
"fr": "Versions principales",
|
|
|
|
|
|
"en": "Main versions",
|
|
|
|
|
|
},
|
|
|
|
|
|
"None": {
|
|
|
|
|
|
"fr": "Rien",
|
|
|
|
|
|
"en": "None",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Cancel": {
|
|
|
|
|
|
"fr": "Annuler",
|
|
|
|
|
|
"en": "Cancel",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Apply": {
|
|
|
|
|
|
"fr": "Appliquer",
|
|
|
|
|
|
"en": "Apply",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Close": {
|
|
|
|
|
|
"fr": "Fermer",
|
|
|
|
|
|
"en": "Close",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Copy log": {
|
|
|
|
|
|
"fr": "Copier le log",
|
|
|
|
|
|
"en": "Copy log",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Copy all logs": {
|
|
|
|
|
|
"fr": "Copier tous les logs",
|
|
|
|
|
|
"en": "Copy all logs",
|
|
|
|
|
|
},
|
|
|
|
|
|
"all logs": {
|
|
|
|
|
|
"fr": "tous les logs",
|
|
|
|
|
|
"en": "all logs",
|
|
|
|
|
|
},
|
|
|
|
|
|
"c copy log · C copy all · q quit": {
|
|
|
|
|
|
"fr": "c copier le log · C copier tout · q quitter",
|
|
|
|
|
|
"en": "c copy log · C copy all · q quit",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Clipboard": {
|
|
|
|
|
|
"fr": "Presse-papiers",
|
|
|
|
|
|
"en": "Clipboard",
|
|
|
|
|
|
},
|
|
|
|
|
|
"chars": {
|
|
|
|
|
|
"fr": "car.",
|
|
|
|
|
|
"en": "chars",
|
|
|
|
|
|
},
|
|
|
|
|
|
"tail only, log was truncated": {
|
|
|
|
|
|
"fr": "fin seulement, log tronqué",
|
|
|
|
|
|
"en": "tail only, log was truncated",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Needs an OSC 52 capable terminal.": {
|
|
|
|
|
|
"fr": "Nécessite un terminal compatible OSC 52.",
|
|
|
|
|
|
"en": "Needs an OSC 52 capable terminal.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Nothing to copy.": {
|
|
|
|
|
|
"fr": "Rien à copier.",
|
|
|
|
|
|
"en": "Nothing to copy.",
|
|
|
|
|
|
},
|
2026-08-07 03:22:26 -04:00
|
|
|
|
"TODO navigation telemetry": {
|
|
|
|
|
|
"fr": "TODO — télémétrie de navigation",
|
|
|
|
|
|
"en": "TODO navigation telemetry",
|
|
|
|
|
|
},
|
|
|
|
|
|
"navigations": {
|
|
|
|
|
|
"fr": "navigations",
|
|
|
|
|
|
"en": "navigations",
|
|
|
|
|
|
},
|
|
|
|
|
|
"menus": {
|
|
|
|
|
|
"fr": "menus",
|
|
|
|
|
|
"en": "menus",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Telemetry reset.": {
|
|
|
|
|
|
"fr": "Télémétrie réinitialisée.",
|
|
|
|
|
|
"en": "Telemetry reset.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"tree from code": {
|
|
|
|
|
|
"fr": "arbre issu du code",
|
|
|
|
|
|
"en": "tree from code",
|
|
|
|
|
|
},
|
|
|
|
|
|
"visited paths only": {
|
|
|
|
|
|
"fr": "chemins visités seulement",
|
|
|
|
|
|
"en": "visited paths only",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Enter = run, F3 = view": {
|
|
|
|
|
|
"fr": "Entrée = exécuter, F3 = vue",
|
|
|
|
|
|
"en": "Enter = run, F3 = view",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Enter = run, F3/F4 = views": {
|
|
|
|
|
|
"fr": "Entrée = exécuter, F3/F4 = vues",
|
|
|
|
|
|
"en": "Enter = run, F3/F4 = views",
|
|
|
|
|
|
},
|
|
|
|
|
|
"view": {
|
|
|
|
|
|
"fr": "vue",
|
|
|
|
|
|
"en": "view",
|
|
|
|
|
|
},
|
[ADD] todo: QEMU/KVM menu
Deploy, list, test, resize, delete and clean up VMs. Adds the SSH
configuration with recursive ProxyJump, port forwarding, and registration
of the QEMU hosts in virt-manager.
--- FR ---
Déployer, lister, tester, redimensionner, supprimer et nettoyer des VM.
Ajoute la configuration SSH avec ProxyJump récursif, la redirection de port
et l'enregistrement des hôtes QEMU dans virt-manager.
Assisted-by: Claude Opus 4.8
2026-08-07 03:22:26 -04:00
|
|
|
|
"avg": {
|
|
|
|
|
|
"fr": "moy",
|
|
|
|
|
|
"en": "avg",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Last install:": {
|
|
|
|
|
|
"fr": "Dernière install :",
|
|
|
|
|
|
"en": "Last install:",
|
|
|
|
|
|
},
|
2026-08-07 03:22:26 -04:00
|
|
|
|
"No command found.": {
|
|
|
|
|
|
"fr": "Aucune commande trouvée.",
|
|
|
|
|
|
"en": "No command found.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Back to telemetry (r) or quit (Enter)? ": {
|
|
|
|
|
|
"fr": "Revenir à la télémétrie (r) ou quitter (Entrée) ? ",
|
|
|
|
|
|
"en": "Back to telemetry (r) or quit (Enter)? ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"F2 system · F3/F4 views · Enter run": {
|
|
|
|
|
|
"fr": "F2 système · F3/F4 vues · Entrée exécute",
|
|
|
|
|
|
"en": "F2 system · F3/F4 views · Enter run",
|
|
|
|
|
|
},
|
|
|
|
|
|
"State": {"fr": "État", "en": "State"},
|
|
|
|
|
|
"uptime": {"fr": "actif depuis", "en": "uptime"},
|
|
|
|
|
|
"load": {"fr": "charge", "en": "load"},
|
|
|
|
|
|
"cores": {"fr": "cœurs", "en": "cores"},
|
|
|
|
|
|
"Memory": {"fr": "Mémoire", "en": "Memory"},
|
|
|
|
|
|
"Disk": {"fr": "Disque", "en": "Disk"},
|
|
|
|
|
|
"Network": {"fr": "Réseau", "en": "Network"},
|
|
|
|
|
|
"Battery": {"fr": "Batterie", "en": "Battery"},
|
|
|
|
|
|
"Temperature": {"fr": "Température", "en": "Temperature"},
|
|
|
|
|
|
"lm-sensors absent — press i to install": {
|
|
|
|
|
|
"fr": "lm-sensors absent — appuyez sur i pour installer",
|
|
|
|
|
|
"en": "lm-sensors absent — press i to install",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Sensors already available.": {
|
|
|
|
|
|
"fr": "Capteurs déjà disponibles.",
|
|
|
|
|
|
"en": "Sensors already available.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Sensors now available.": {
|
|
|
|
|
|
"fr": "Capteurs désormais disponibles.",
|
|
|
|
|
|
"en": "Sensors now available.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Still no temperature (reboot/modprobe may be needed).": {
|
|
|
|
|
|
"fr": "Toujours pas de température (redémarrage/modprobe requis ?).",
|
|
|
|
|
|
"en": "Still no temperature (reboot/modprobe may be needed).",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Unknown package manager for lm-sensors.": {
|
|
|
|
|
|
"fr": "Gestionnaire de paquets inconnu pour lm-sensors.",
|
|
|
|
|
|
"en": "Unknown package manager for lm-sensors.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Proposed install command:": {
|
|
|
|
|
|
"fr": "Commande d'installation proposée :",
|
|
|
|
|
|
"en": "Proposed install command:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Install lm-sensors now? (y/N): ": {
|
|
|
|
|
|
"fr": "Installer lm-sensors maintenant ? (o/N) : ",
|
|
|
|
|
|
"en": "Install lm-sensors now? (y/N): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Command failed: ": {
|
|
|
|
|
|
"fr": "Échec de la commande : ",
|
|
|
|
|
|
"en": "Command failed: ",
|
|
|
|
|
|
},
|
[ADD] migration: see and repair the website COW views
A copy-on-write view freezes the module view it came from; the module moves
on and the upgrade dies hours later on a missing anchor. These tools
predict, snapshot, diff, neutralize and reset them. The migration screen
gains the real state of each step, replay from any of them, and statistics.
--- FR ---
Une vue copy-on-write fige la vue de module dont elle vient ; le module
évolue et la mise à niveau meurt des heures plus tard sur un point
d'ancrage absent. Ces outils les prévoient, photographient, comparent,
neutralisent et réinitialisent. L'écran de migration gagne l'état réel de
chaque étape, la reprise depuis n'importe laquelle, et des statistiques.
Assisted-by: Claude Opus 5
2026-08-10 03:10:50 -04:00
|
|
|
|
"Textual is required for this screen.": {
|
|
|
|
|
|
"fr": "Textual est nécessaire pour cet écran.",
|
|
|
|
|
|
"en": "Textual is required for this screen.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Install it now? (Y/n): ": {
|
|
|
|
|
|
"fr": "L'installer maintenant ? (O/n, défaut : oui) : ",
|
|
|
|
|
|
"en": "Install it now? (Y/n, default: yes): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Textual is installed.": {
|
|
|
|
|
|
"fr": "Textual est installé.",
|
|
|
|
|
|
"en": "Textual is installed.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Installation finished but textual is still missing.": {
|
|
|
|
|
|
"fr": "Installation terminée mais textual reste introuvable.",
|
|
|
|
|
|
"en": "Installation finished but textual is still missing.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"pip exited with": {
|
|
|
|
|
|
"fr": "pip a retourné",
|
|
|
|
|
|
"en": "pip exited with",
|
|
|
|
|
|
},
|
2026-08-07 03:22:26 -04:00
|
|
|
|
"Your distribution may package it as python3-textual.": {
|
|
|
|
|
|
"fr": "Votre distribution le fournit peut-être en python3-textual.",
|
|
|
|
|
|
"en": "Your distribution may package it as python3-textual.",
|
|
|
|
|
|
},
|
[ADD] todo: QEMU/KVM menu
Deploy, list, test, resize, delete and clean up VMs. Adds the SSH
configuration with recursive ProxyJump, port forwarding, and registration
of the QEMU hosts in virt-manager.
--- FR ---
Déployer, lister, tester, redimensionner, supprimer et nettoyer des VM.
Ajoute la configuration SSH avec ProxyJump récursif, la redirection de port
et l'enregistrement des hôtes QEMU dans virt-manager.
Assisted-by: Claude Opus 4.8
2026-08-07 03:22:26 -04:00
|
|
|
|
"Resize a VM disk": {
|
|
|
|
|
|
"fr": "📐 Redimensionner le disque d'une VM",
|
|
|
|
|
|
"en": "📐 Resize a VM disk",
|
|
|
|
|
|
},
|
|
|
|
|
|
"VM name to resize: ": {
|
|
|
|
|
|
"fr": "Nom de la VM à redimensionner : ",
|
|
|
|
|
|
"en": "VM name to resize: ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"VM not found.": {
|
|
|
|
|
|
"fr": "VM introuvable.",
|
|
|
|
|
|
"en": "VM not found.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Main disk not found for this VM.": {
|
|
|
|
|
|
"fr": "Disque principal introuvable pour cette VM.",
|
|
|
|
|
|
"en": "Main disk not found for this VM.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Current disk:": {
|
|
|
|
|
|
"fr": "Disque actuel :",
|
|
|
|
|
|
"en": "Current disk:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Current virtual size:": {
|
|
|
|
|
|
"fr": "Taille virtuelle actuelle :",
|
|
|
|
|
|
"en": "Current virtual size:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"VM state:": {
|
|
|
|
|
|
"fr": "État de la VM :",
|
|
|
|
|
|
"en": "VM state:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Could not read current disk size; aborting.": {
|
|
|
|
|
|
"fr": "Impossible de lire la taille actuelle du disque ; abandon.",
|
|
|
|
|
|
"en": "Could not read current disk size; aborting.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Host free space:": {
|
|
|
|
|
|
"fr": "Espace libre hôte :",
|
|
|
|
|
|
"en": "Host free space:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"max sustainable total (before host full):": {
|
|
|
|
|
|
"fr": "max soutenable au total (avant hôte plein) :",
|
|
|
|
|
|
"en": "max sustainable total (before host full):",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Beyond host capacity by ~%.1f G — overcommit.": {
|
|
|
|
|
|
"fr": "Dépasse la capacité de l'hôte de ~%.1f G — surallocation.",
|
|
|
|
|
|
"en": "Beyond host capacity by ~%.1f G — overcommit.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"The qcow2 is thin: fine until the VM fills it, then the "
|
|
|
|
|
|
"host disk runs out. Max sustainable: ~%.1f G.": {
|
|
|
|
|
|
"fr": "Le qcow2 est creux : OK tant que la VM ne remplit pas, sinon "
|
|
|
|
|
|
"l'hôte tombe à court. Max soutenable : ~%.1f G.",
|
|
|
|
|
|
"en": "The qcow2 is thin: fine until the VM fills it, then the "
|
|
|
|
|
|
"host disk runs out. Max sustainable: ~%.1f G.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Safe shrink needs libguestfs (virt-resize).": {
|
|
|
|
|
|
"fr": "La réduction sûre nécessite libguestfs (virt-resize).",
|
|
|
|
|
|
"en": "Safe shrink needs libguestfs (virt-resize).",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Install libguestfs-tools now? (y/N): ": {
|
|
|
|
|
|
"fr": "Installer libguestfs-tools maintenant ? (o/N) : ",
|
|
|
|
|
|
"en": "Install libguestfs-tools now? (y/N): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Shrink aborted; disk left intact.": {
|
|
|
|
|
|
"fr": "Réduction annulée ; disque laissé intact.",
|
|
|
|
|
|
"en": "Shrink aborted; disk left intact.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Could not detect the partition to shrink; aborting.": {
|
|
|
|
|
|
"fr": "Partition à réduire introuvable ; abandon.",
|
|
|
|
|
|
"en": "Could not detect the partition to shrink; aborting.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Missing tools for safe shrink:": {
|
|
|
|
|
|
"fr": "Outils manquants pour la réduction sûre :",
|
|
|
|
|
|
"en": "Missing tools for safe shrink:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Backing up the disk before shrinking…": {
|
|
|
|
|
|
"fr": "Sauvegarde du disque avant réduction…",
|
|
|
|
|
|
"en": "Backing up the disk before shrinking…",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Back up the disk before shrinking? (Y/n): ": {
|
|
|
|
|
|
"fr": "Sauvegarder le disque avant réduction ? (O/n, défaut : oui) : ",
|
|
|
|
|
|
"en": "Back up the disk before shrinking? (Y/n, default: yes): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No backup: a failure could leave the disk broken.": {
|
|
|
|
|
|
"fr": "Sans sauvegarde : un échec pourrait rendre le disque inutilisable.",
|
|
|
|
|
|
"en": "No backup: a failure could leave the disk broken.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Disk safely shrunk.": {
|
|
|
|
|
|
"fr": "Disque réduit en toute sécurité.",
|
|
|
|
|
|
"en": "Disk safely shrunk.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No backup to restore; run fsck on the disk before use.": {
|
|
|
|
|
|
"fr": "Pas de sauvegarde à restaurer ; lancez fsck avant utilisation.",
|
|
|
|
|
|
"en": "No backup to restore; run fsck on the disk before use.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"A disk backup was kept:": {
|
|
|
|
|
|
"fr": "Une sauvegarde du disque a été conservée :",
|
|
|
|
|
|
"en": "A disk backup was kept:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Delete this backup now? (y/N): ": {
|
|
|
|
|
|
"fr": "Effacer cette sauvegarde maintenant ? (o/N) : ",
|
|
|
|
|
|
"en": "Delete this backup now? (y/N): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Backup deleted.": {
|
|
|
|
|
|
"fr": "Sauvegarde effacée.",
|
|
|
|
|
|
"en": "Backup deleted.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Backup kept (delete later via Clean up QEMU).": {
|
|
|
|
|
|
"fr": "Sauvegarde conservée (à effacer plus tard via Nettoyer QEMU).",
|
|
|
|
|
|
"en": "Backup kept (delete later via Clean up QEMU).",
|
|
|
|
|
|
},
|
|
|
|
|
|
"disk backup (resize)": {
|
|
|
|
|
|
"fr": "sauvegarde de disque (redim.)",
|
|
|
|
|
|
"en": "disk backup (resize)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Backup failed; aborting.": {
|
|
|
|
|
|
"fr": "Échec de la sauvegarde ; abandon.",
|
|
|
|
|
|
"en": "Backup failed; aborting.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Could not attach the disk (nbd); aborting.": {
|
|
|
|
|
|
"fr": "Impossible d'attacher le disque (nbd) ; abandon.",
|
|
|
|
|
|
"en": "Could not attach the disk (nbd); aborting.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Only ext2/3/4 can be shrunk safely; aborting.": {
|
|
|
|
|
|
"fr": "Seul ext2/3/4 peut être réduit en sécurité ; abandon.",
|
|
|
|
|
|
"en": "Only ext2/3/4 can be shrunk safely; aborting.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Target size too small for this layout; aborting.": {
|
|
|
|
|
|
"fr": "Taille cible trop petite pour cette disposition ; abandon.",
|
|
|
|
|
|
"en": "Target size too small for this layout; aborting.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Not enough used-space margin to shrink; aborting.": {
|
|
|
|
|
|
"fr": "Espace utilisé trop proche de la cible ; abandon.",
|
|
|
|
|
|
"en": "Not enough used-space margin to shrink; aborting.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Shrinking guest ext filesystem": {
|
|
|
|
|
|
"fr": "Réduction du système de fichiers ext invité",
|
|
|
|
|
|
"en": "Shrinking guest ext filesystem",
|
|
|
|
|
|
},
|
|
|
|
|
|
"resize2fs failed; reverting.": {
|
|
|
|
|
|
"fr": "resize2fs a échoué ; restauration.",
|
|
|
|
|
|
"en": "resize2fs failed; reverting.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Internal size check failed; reverting.": {
|
|
|
|
|
|
"fr": "Contrôle de taille interne échoué ; restauration.",
|
|
|
|
|
|
"en": "Internal size check failed; reverting.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Shrinking the partition…": {
|
|
|
|
|
|
"fr": "Réduction de la partition…",
|
|
|
|
|
|
"en": "Shrinking the partition…",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Partition rewrite failed; reverting.": {
|
|
|
|
|
|
"fr": "Réécriture de la partition échouée ; restauration.",
|
|
|
|
|
|
"en": "Partition rewrite failed; reverting.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Shrinking the qcow2 container…": {
|
|
|
|
|
|
"fr": "Réduction du conteneur qcow2…",
|
|
|
|
|
|
"en": "Shrinking the qcow2 container…",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Container shrink failed; reverting.": {
|
|
|
|
|
|
"fr": "Réduction du conteneur échouée ; restauration.",
|
|
|
|
|
|
"en": "Container shrink failed; reverting.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Restoring the original disk from backup…": {
|
|
|
|
|
|
"fr": "Restauration du disque d'origine depuis la sauvegarde…",
|
|
|
|
|
|
"en": "Restoring the original disk from backup…",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Shrinking guest FS + partition via virt-resize": {
|
|
|
|
|
|
"fr": "Réduction du FS invité + partition via virt-resize",
|
|
|
|
|
|
"en": "Shrinking guest FS + partition via virt-resize",
|
|
|
|
|
|
},
|
|
|
|
|
|
"virt-resize failed; original disk left intact.": {
|
|
|
|
|
|
"fr": "virt-resize a échoué ; disque d'origine laissé intact.",
|
|
|
|
|
|
"en": "virt-resize failed; original disk left intact.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Disk safely shrunk. Backup kept at:": {
|
|
|
|
|
|
"fr": "Disque réduit en toute sécurité. Sauvegarde conservée :",
|
|
|
|
|
|
"en": "Disk safely shrunk. Backup kept at:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"click to expand": {
|
|
|
|
|
|
"fr": "cliquer pour déplier",
|
|
|
|
|
|
"en": "click to expand",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No error detected.": {
|
|
|
|
|
|
"fr": "Aucune erreur détectée.",
|
|
|
|
|
|
"en": "No error detected.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Test a VM (open Odoo in a CLI browser)": {
|
|
|
|
|
|
"fr": "🧪 Tester une VM (ouvrir Odoo dans un navigateur CLI)",
|
|
|
|
|
|
"en": "🧪 Test a VM (open Odoo in a CLI browser)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Reopen install monitoring (last run / history)": {
|
|
|
|
|
|
"fr": "📈 Rouvrir le suivi d'installation (dernier run / historique)",
|
|
|
|
|
|
"en": "📈 Reopen install monitoring (last run / history)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No install run found in history.": {
|
|
|
|
|
|
"fr": "Aucun run d'installation dans l'historique.",
|
|
|
|
|
|
"en": "No install run found in history.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Install runs (most recent first):": {
|
|
|
|
|
|
"fr": "Runs d'installation (du plus récent) :",
|
|
|
|
|
|
"en": "Install runs (most recent first):",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Choice (number, blank = last): ": {
|
|
|
|
|
|
"fr": "Choix (numéro, vide = le dernier) : ",
|
|
|
|
|
|
"en": "Choice (number, blank = last): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Invalid selection.": {
|
|
|
|
|
|
"fr": "Sélection invalide.",
|
|
|
|
|
|
"en": "Invalid selection.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Resolving VM IP...": {
|
|
|
|
|
|
"fr": "Résolution de l'IP de la VM…",
|
|
|
|
|
|
"en": "Resolving VM IP...",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No IP found for this VM.": {
|
|
|
|
|
|
"fr": "Aucune IP trouvée pour cette VM.",
|
|
|
|
|
|
"en": "No IP found for this VM.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No CLI browser installed. Which to install?": {
|
|
|
|
|
|
"fr": "Aucun navigateur CLI installé. Lequel installer ?",
|
|
|
|
|
|
"en": "No CLI browser installed. Which to install?",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Which browser to install?": {
|
|
|
|
|
|
"fr": "Quel navigateur installer ?",
|
|
|
|
|
|
"en": "Which browser to install?",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Install another browser": {
|
|
|
|
|
|
"fr": "Installer un autre navigateur",
|
|
|
|
|
|
"en": "Install another browser",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Choice (number, blank = w3m): ": {
|
|
|
|
|
|
"fr": "Choix (numéro, vide = w3m) : ",
|
|
|
|
|
|
"en": "Choice (number, blank = w3m): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Unknown package manager; install it manually.": {
|
|
|
|
|
|
"fr": "Gestionnaire de paquets inconnu ; installez-le manuellement.",
|
|
|
|
|
|
"en": "Unknown package manager; install it manually.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Install now? (y/N): ": {
|
|
|
|
|
|
"fr": "Installer maintenant ? (o/N) : ",
|
|
|
|
|
|
"en": "Install now? (y/N): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Which browser to view the page?": {
|
|
|
|
|
|
"fr": "Quel navigateur pour voir la page ?",
|
|
|
|
|
|
"en": "Which browser to view the page?",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Choice (number, blank = first): ": {
|
|
|
|
|
|
"fr": "Choix (numéro, vide = le premier) : ",
|
|
|
|
|
|
"en": "Choice (number, blank = first): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Page may not have loaded: Odoo not started on :8069, "
|
|
|
|
|
|
"or network/firewall.": {
|
|
|
|
|
|
"fr": "La page ne s'est peut-être pas affichée : Odoo n'est pas "
|
|
|
|
|
|
"démarré sur :8069, ou réseau/pare-feu.",
|
|
|
|
|
|
"en": "Page may not have loaded: Odoo not started on :8069, "
|
|
|
|
|
|
"or network/firewall.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"errors": {
|
|
|
|
|
|
"fr": "erreurs",
|
|
|
|
|
|
"en": "errors",
|
|
|
|
|
|
},
|
|
|
|
|
|
"warnings": {
|
|
|
|
|
|
"fr": "avertissements",
|
|
|
|
|
|
"en": "warnings",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Esc to close": {
|
|
|
|
|
|
"fr": "Échap pour fermer",
|
|
|
|
|
|
"en": "Esc to close",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No running VM to pause.": {
|
|
|
|
|
|
"fr": "Aucune VM en cours à mettre en pause.",
|
|
|
|
|
|
"en": "No running VM to pause.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No paused VM to resume.": {
|
|
|
|
|
|
"fr": "Aucune VM en pause à reprendre.",
|
|
|
|
|
|
"en": "No paused VM to resume.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"resumed": {
|
|
|
|
|
|
"fr": "reprise(s)",
|
|
|
|
|
|
"en": "resumed",
|
|
|
|
|
|
},
|
|
|
|
|
|
"SSH grow failed; trying the guest agent.": {
|
|
|
|
|
|
"fr": "Échec SSH ; tentative via l'agent invité.",
|
|
|
|
|
|
"en": "SSH grow failed; trying the guest agent.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No IP; trying the guest agent (no network).": {
|
|
|
|
|
|
"fr": "Pas d'IP ; tentative via l'agent invité (sans réseau).",
|
|
|
|
|
|
"en": "No IP; trying the guest agent (no network).",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Guest filesystem grown via guest agent.": {
|
|
|
|
|
|
"fr": "FS invité étendu via l'agent invité.",
|
|
|
|
|
|
"en": "Guest filesystem grown via guest agent.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Guest agent grow failed; falling back to console.": {
|
|
|
|
|
|
"fr": "Échec de l'agent invité ; repli sur la console.",
|
|
|
|
|
|
"en": "Guest agent grow failed; falling back to console.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Guest agent unavailable; falling back to serial console.": {
|
|
|
|
|
|
"fr": "Agent invité indisponible ; repli sur la console série.",
|
|
|
|
|
|
"en": "Guest agent unavailable; falling back to serial console.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Running via guest agent (no network)…": {
|
|
|
|
|
|
"fr": "Exécution via l'agent invité (sans réseau)…",
|
|
|
|
|
|
"en": "Running via guest agent (no network)…",
|
|
|
|
|
|
},
|
|
|
|
|
|
"SSH grow failed; falling back to serial console.": {
|
|
|
|
|
|
"fr": "Échec de l'extension via SSH ; repli sur la console série.",
|
|
|
|
|
|
"en": "SSH grow failed; falling back to serial console.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No IP; falling back to serial console.": {
|
|
|
|
|
|
"fr": "Pas d'IP ; repli sur la console série.",
|
|
|
|
|
|
"en": "No IP; falling back to serial console.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Serial console fallback. Log in, then paste:": {
|
|
|
|
|
|
"fr": "Repli console série. Connectez-vous, puis collez :",
|
|
|
|
|
|
"en": "Serial console fallback. Log in, then paste:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Open the serial console now? (y/N): ": {
|
|
|
|
|
|
"fr": "Ouvrir la console série maintenant ? (o/N) : ",
|
|
|
|
|
|
"en": "Open the serial console now? (y/N): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"The VM must be off. Shut it down and retry? (y/N): ": {
|
|
|
|
|
|
"fr": "La VM doit être éteinte. L'éteindre et réessayer ? (o/N) : ",
|
|
|
|
|
|
"en": "The VM must be off. Shut it down and retry? (y/N): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"VM is still not off; aborting.": {
|
|
|
|
|
|
"fr": "La VM n'est toujours pas éteinte ; abandon.",
|
|
|
|
|
|
"en": "VM is still not off; aborting.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Waiting for the VM to shut down...": {
|
|
|
|
|
|
"fr": "Attente de l'arrêt de la VM…",
|
|
|
|
|
|
"en": "Waiting for the VM to shut down...",
|
|
|
|
|
|
},
|
2026-08-07 03:22:26 -04:00
|
|
|
|
"timeout": {
|
|
|
|
|
|
"fr": "délai max",
|
|
|
|
|
|
"en": "timeout",
|
|
|
|
|
|
},
|
|
|
|
|
|
"shutting down": {
|
|
|
|
|
|
"fr": "arrêt en cours",
|
|
|
|
|
|
"en": "shutting down",
|
|
|
|
|
|
},
|
|
|
|
|
|
"remaining": {
|
|
|
|
|
|
"fr": "restantes",
|
|
|
|
|
|
"en": "remaining",
|
|
|
|
|
|
},
|
[ADD] todo: QEMU/KVM menu
Deploy, list, test, resize, delete and clean up VMs. Adds the SSH
configuration with recursive ProxyJump, port forwarding, and registration
of the QEMU hosts in virt-manager.
--- FR ---
Déployer, lister, tester, redimensionner, supprimer et nettoyer des VM.
Ajoute la configuration SSH avec ProxyJump récursif, la redirection de port
et l'enregistrement des hôtes QEMU dans virt-manager.
Assisted-by: Claude Opus 4.8
2026-08-07 03:22:26 -04:00
|
|
|
|
"VM is off.": {
|
|
|
|
|
|
"fr": "VM éteinte.",
|
|
|
|
|
|
"en": "VM is off.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"The VM was shut down for the resize.": {
|
|
|
|
|
|
"fr": "La VM a été éteinte pour le redimensionnement.",
|
|
|
|
|
|
"en": "The VM was shut down for the resize.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Start the VM now? (y/N): ": {
|
|
|
|
|
|
"fr": "Démarrer la VM maintenant ? (o/N) : ",
|
|
|
|
|
|
"en": "Start the VM now? (y/N): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Graceful shutdown timed out. Force off (destroy)? (y/N): ": {
|
|
|
|
|
|
"fr": "Arrêt gracieux trop long. Forcer l'arrêt (destroy) ? (o/N) : ",
|
|
|
|
|
|
"en": "Graceful shutdown timed out. Force off (destroy)? (y/N): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Show advanced info (vCPU, RAM, disk)? (y/N): ": {
|
|
|
|
|
|
"fr": "Afficher les infos avancées (vCPU, RAM, disque) ? (o/N) : ",
|
|
|
|
|
|
"en": "Show advanced info (vCPU, RAM, disk)? (y/N): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"What do you want to do?": {
|
|
|
|
|
|
"fr": "Que voulez-vous faire ?",
|
|
|
|
|
|
"en": "What do you want to do?",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Advanced info (vCPU, RAM, disk)": {
|
|
|
|
|
|
"fr": "Infos avancées (vCPU, RAM, disque)",
|
|
|
|
|
|
"en": "Advanced info (vCPU, RAM, disk)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Change the state of one or more VMs": {
|
|
|
|
|
|
"fr": "Changer l'état d'une ou plusieurs VM",
|
|
|
|
|
|
"en": "Change the state of one or more VMs",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Enter": {
|
|
|
|
|
|
"fr": "Entrée",
|
|
|
|
|
|
"en": "Enter",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Nothing": {
|
|
|
|
|
|
"fr": "Rien",
|
|
|
|
|
|
"en": "Nothing",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Choice: ": {
|
|
|
|
|
|
"fr": "Choix : ",
|
|
|
|
|
|
"en": "Choice: ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Available VMs:": {
|
|
|
|
|
|
"fr": "VM disponibles :",
|
|
|
|
|
|
"en": "Available VMs:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"VMs to change (comma-separated): ": {
|
|
|
|
|
|
"fr": "VM à modifier (séparées par des virgules) : ",
|
|
|
|
|
|
"en": "VMs to change (comma-separated): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Unknown VM(s):": {
|
|
|
|
|
|
"fr": "VM inconnue(s) :",
|
|
|
|
|
|
"en": "Unknown VM(s):",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Target state:": {
|
|
|
|
|
|
"fr": "État cible :",
|
|
|
|
|
|
"en": "Target state:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Target environment?": {
|
|
|
|
|
|
"fr": "Environnement cible ?",
|
|
|
|
|
|
"en": "Target environment?",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Development (~/git/erplibre, SELinux relaxed)": {
|
|
|
|
|
|
"fr": "Développement (~/git/erplibre, SELinux relâché)",
|
|
|
|
|
|
"en": "Development (~/git/erplibre, SELinux relaxed)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Production (/opt/erplibre, SELinux enforced)": {
|
|
|
|
|
|
"fr": "Production (/opt/erplibre, SELinux confiné)",
|
|
|
|
|
|
"en": "Production (/opt/erplibre, SELinux enforced)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Choice (1-2, default 1): ": {
|
|
|
|
|
|
"fr": "Choix (1-2, défaut 1) : ",
|
|
|
|
|
|
"en": "Choice (1-2, default 1): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Open (start)": {
|
|
|
|
|
|
"fr": "Ouvrir (démarrer)",
|
|
|
|
|
|
"en": "Open (start)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Close (shut down)": {
|
|
|
|
|
|
"fr": "Fermer (éteindre)",
|
|
|
|
|
|
"en": "Close (shut down)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"start": {
|
|
|
|
|
|
"fr": "démarrer",
|
|
|
|
|
|
"en": "start",
|
|
|
|
|
|
},
|
|
|
|
|
|
"shut down": {
|
|
|
|
|
|
"fr": "éteindre",
|
|
|
|
|
|
"en": "shut down",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Apply:": {
|
|
|
|
|
|
"fr": "Appliquer :",
|
|
|
|
|
|
"en": "Apply:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Confirm for real? (y/N): ": {
|
|
|
|
|
|
"fr": "Confirmer pour de vrai ? (o/N) : ",
|
|
|
|
|
|
"en": "Confirm for real? (y/N): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Storage": {
|
|
|
|
|
|
"fr": "Stockage",
|
|
|
|
|
|
"en": "Storage",
|
|
|
|
|
|
},
|
|
|
|
|
|
"total": {
|
|
|
|
|
|
"fr": "au total",
|
|
|
|
|
|
"en": "total",
|
|
|
|
|
|
},
|
|
|
|
|
|
"used": {
|
|
|
|
|
|
"fr": "utilisés",
|
|
|
|
|
|
"en": "used",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Enter +NG to grow, -NG to shrink, or NG for a target size "
|
|
|
|
|
|
"(e.g. +20G, -10G, 60G).": {
|
|
|
|
|
|
"fr": "Entrez +NG pour agrandir, -NG pour réduire, ou NG pour une "
|
|
|
|
|
|
"taille cible (ex. +20G, -10G, 60G).",
|
|
|
|
|
|
"en": "Enter +NG to grow, -NG to shrink, or NG for a target size "
|
|
|
|
|
|
"(e.g. +20G, -10G, 60G).",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Resize: ": {
|
|
|
|
|
|
"fr": "Redimensionner : ",
|
|
|
|
|
|
"en": "Resize: ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Invalid size.": {
|
|
|
|
|
|
"fr": "Taille invalide.",
|
|
|
|
|
|
"en": "Invalid size.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No change.": {
|
|
|
|
|
|
"fr": "Aucun changement.",
|
|
|
|
|
|
"en": "No change.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"New virtual size:": {
|
|
|
|
|
|
"fr": "Nouvelle taille virtuelle :",
|
|
|
|
|
|
"en": "New virtual size:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"SHRINKING is DANGEROUS: the guest filesystem is NOT shrunk. "
|
|
|
|
|
|
"Data beyond the new size is LOST. Shrink the guest FS FIRST, "
|
|
|
|
|
|
"and only then shrink here.": {
|
|
|
|
|
|
"fr": "RÉDUIRE est DANGEREUX : le système de fichiers invité n'est "
|
|
|
|
|
|
"PAS réduit. Les données au-delà de la nouvelle taille sont PERDUES. "
|
|
|
|
|
|
"Réduisez d'ABORD le FS invité, puis seulement ici.",
|
|
|
|
|
|
"en": "SHRINKING is DANGEROUS: the guest filesystem is NOT shrunk. "
|
|
|
|
|
|
"Data beyond the new size is LOST. Shrink the guest FS FIRST, "
|
|
|
|
|
|
"and only then shrink here.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Shut the VM off before shrinking (virsh shutdown).": {
|
|
|
|
|
|
"fr": "Éteignez la VM avant de réduire (virsh shutdown).",
|
|
|
|
|
|
"en": "Shut the VM off before shrinking (virsh shutdown).",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Type y to confirm you understand the risk (y/N): ": {
|
|
|
|
|
|
"fr": "Tapez o pour confirmer que vous comprenez le risque "
|
|
|
|
|
|
"(o/N, défaut : non) : ",
|
|
|
|
|
|
"en": "Type y to confirm you understand the risk (y/N): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Resize failed (see error above).": {
|
|
|
|
|
|
"fr": "Échec du redimensionnement (voir l'erreur ci-dessus).",
|
|
|
|
|
|
"en": "Resize failed (see error above).",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Virtual disk resized.": {
|
|
|
|
|
|
"fr": "Disque virtuel redimensionné.",
|
|
|
|
|
|
"en": "Virtual disk resized.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Grow the guest filesystem now (over SSH)? (y/N): ": {
|
|
|
|
|
|
"fr": "Étendre le système de fichiers invité maintenant (via SSH) ? "
|
|
|
|
|
|
"(o/N, défaut : non) : ",
|
|
|
|
|
|
"en": "Grow the guest filesystem now (over SSH)? (y/N): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No IP; grow the guest FS manually once booted.": {
|
|
|
|
|
|
"fr": "Pas d'IP ; étendez le FS invité manuellement après le boot.",
|
|
|
|
|
|
"en": "No IP; grow the guest FS manually once booted.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"completed": {
|
|
|
|
|
|
"fr": "terminées",
|
|
|
|
|
|
"en": "completed",
|
|
|
|
|
|
},
|
|
|
|
|
|
"deleted": {
|
|
|
|
|
|
"fr": "effacée",
|
|
|
|
|
|
"en": "deleted",
|
|
|
|
|
|
},
|
|
|
|
|
|
"paused": {
|
|
|
|
|
|
"fr": "pause",
|
|
|
|
|
|
"en": "paused",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Waiting for the VM to start (boot + cloud-init)": {
|
|
|
|
|
|
"fr": "En attente du démarrage de la VM (boot + cloud-init)",
|
|
|
|
|
|
"en": "Waiting for the VM to start (boot + cloud-init)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"(an emulated architecture can be slow; this is normal)": {
|
|
|
|
|
|
"fr": "(une architecture émulée peut être lente ; c'est normal)",
|
|
|
|
|
|
"en": "(an emulated architecture can be slow; this is normal)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"VM ready - starting the ERPLibre install": {
|
|
|
|
|
|
"fr": "VM prête — installation ERPLibre en cours",
|
|
|
|
|
|
"en": "VM ready - starting the ERPLibre install",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Choice (number or name, blank = native):": {
|
|
|
|
|
|
"fr": "Choix (numéro ou nom, vide = native) :",
|
|
|
|
|
|
"en": "Choice (number or name, blank = native):",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Pick exact versions (comma-separated list)": {
|
|
|
|
|
|
"fr": "Choisir des versions précises (liste séparée par des virgules)",
|
|
|
|
|
|
"en": "Pick exact versions (comma-separated list)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Selection (numbers, 'all', 'principal' or 'granulaire',"
|
|
|
|
|
|
" default: all): ": {
|
|
|
|
|
|
"fr": "Sélection (numéros, « all », « principal » ou « granulaire »,"
|
|
|
|
|
|
" défaut : all) : ",
|
|
|
|
|
|
"en": "Selection (numbers, 'all', 'principal' or 'granulaire',"
|
|
|
|
|
|
" default: all): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"All versions:": {
|
|
|
|
|
|
"fr": "Toutes les versions :",
|
|
|
|
|
|
"en": "All versions:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Selection (comma-separated numbers): ": {
|
|
|
|
|
|
"fr": "Sélection (numéros séparés par des virgules) : ",
|
|
|
|
|
|
"en": "Selection (comma-separated numbers): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"s390x images only exist for:": {
|
|
|
|
|
|
"fr": "images s390x disponibles seulement pour :",
|
|
|
|
|
|
"en": "s390x images only exist for:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"ignored:": {
|
|
|
|
|
|
"fr": "ignoré :",
|
|
|
|
|
|
"en": "ignored:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"IBM Z — emulated, slow on x86": {
|
|
|
|
|
|
"fr": "IBM Z — émulé, lent sur x86",
|
|
|
|
|
|
"en": "IBM Z — emulated, slow on x86",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Choice (number or name, blank = amd64):": {
|
|
|
|
|
|
"fr": "Choix (numéro ou nom, vide = amd64) :",
|
|
|
|
|
|
"en": "Choice (number or name, blank = amd64):",
|
|
|
|
|
|
},
|
|
|
|
|
|
"s390x is emulated (TCG): boot and install are much "
|
|
|
|
|
|
"slower than x86.": {
|
|
|
|
|
|
"fr": "s390x est émulé (TCG) : le boot et l'installation sont bien "
|
|
|
|
|
|
"plus lents que x86.",
|
|
|
|
|
|
"en": "s390x is emulated (TCG): boot and install are much "
|
|
|
|
|
|
"slower than x86.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Choice (number or version, blank = default):": {
|
|
|
|
|
|
"fr": "Choix (numéro ou version, vide = défaut) :",
|
|
|
|
|
|
"en": "Choice (number or version, blank = default):",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Invalid selection, using": {
|
|
|
|
|
|
"fr": "Sélection invalide, utilisation de",
|
|
|
|
|
|
"en": "Invalid selection, using",
|
|
|
|
|
|
},
|
|
|
|
|
|
"select all": {
|
|
|
|
|
|
"fr": "tout sélectionner",
|
|
|
|
|
|
"en": "select all",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Delete VM(s)": {
|
[ADD] qemu: build the tunnel to the remote desktop
A "Remote desktop tunnel" entry under SSH configuration. It lists the
targets, resolves them, and composes the full command -- nothing to fill
in.
todo.py runs on the libvirt HOST; the tunnel starts from the workstation.
It cannot open it, but it alone knows the VM's private IP, its port and
the address through which it was reached. That last one comes from
SSH_CONNECTION, whose third field is exactly the server address used --
far safer than a "hostname" that may resolve to nothing outside.
When ~/.ssh/config already carries the entry, the tunnel takes it:
"ssh -N -L 5902:localhost:5901 <vm>". ProxyJump makes the route and
"localhost" means the VM's OWN loopback, so the tunnel survives an IP
change. That config is also the right source of targets, not the local
libvirt: a graphical VM is often nested, and virsh would only ever list
the orchestrator.
Two details: the hypervisor console needs VNC bound to the loopback, as
"listen=none" opens no socket at all; and two menu entries had no icon.
--- FR ---
Une entrée « Tunnel bureau distant » sous Configuration SSH. Elle liste
les cibles, les résout, et compose la commande complète — rien à remplir.
todo.py tourne sur l'HÔTE libvirt ; le tunnel, lui, part du poste de
travail. Il ne peut donc pas l'ouvrir, mais il est le seul à connaître
l'IP privée de la VM, son port et l'adresse par laquelle on l'a joint.
Cette dernière vient de SSH_CONNECTION, dont le troisième champ est
exactement l'adresse serveur utilisée — bien plus sûr qu'un « hostname »
qui peut ne rien résoudre depuis l'extérieur.
Quand ~/.ssh/config porte déjà l'entrée, le tunnel l'emprunte :
« ssh -N -L 5902:localhost:5901 <vm> ». Le ProxyJump fait la route et
« localhost » désigne le bouclage DE LA VM, si bien que le tunnel survit à
un changement d'IP. Ce fichier est aussi la bonne source de cibles, pas le
libvirt local : une VM graphique est souvent imbriquée, et virsh ne
listerait jamais que l'orchestrateur.
Deux détails : la console de l'hyperviseur exige un VNC sur la boucle
locale, « listen=none » n'ouvrant aucun socket ; et deux entrées de menu
n'avaient pas d'icône.
Assisted-by: Claude Opus 5
2026-08-13 03:40:22 -04:00
|
|
|
|
"fr": "🗑 Effacer une ou plusieurs VM",
|
|
|
|
|
|
"en": "🗑 Delete VM(s)",
|
[ADD] todo: QEMU/KVM menu
Deploy, list, test, resize, delete and clean up VMs. Adds the SSH
configuration with recursive ProxyJump, port forwarding, and registration
of the QEMU hosts in virt-manager.
--- FR ---
Déployer, lister, tester, redimensionner, supprimer et nettoyer des VM.
Ajoute la configuration SSH avec ProxyJump récursif, la redirection de port
et l'enregistrement des hôtes QEMU dans virt-manager.
Assisted-by: Claude Opus 4.8
2026-08-07 03:22:26 -04:00
|
|
|
|
},
|
|
|
|
|
|
"No VM found.": {
|
|
|
|
|
|
"fr": "Aucune VM trouvée.",
|
|
|
|
|
|
"en": "No VM found.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Select VMs to delete:": {
|
|
|
|
|
|
"fr": "Sélectionner les VM à effacer :",
|
|
|
|
|
|
"en": "Select VMs to delete:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Selection (numbers, or 'all'): ": {
|
|
|
|
|
|
"fr": "Sélection (numéros, ou « all ») : ",
|
|
|
|
|
|
"en": "Selection (numbers, or 'all'): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Also delete disk images (qcow2 + seed ISO)? (y/N): ": {
|
|
|
|
|
|
"fr": "Effacer aussi les disques (qcow2 + seed ISO) ? (o/N, défaut : non) : ",
|
|
|
|
|
|
"en": "Also delete disk images (qcow2 + seed ISO)? (y/N, default: no): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Will delete:": {
|
|
|
|
|
|
"fr": "Sera effacé :",
|
|
|
|
|
|
"en": "Will delete:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"disk images and seed ISOs": {
|
|
|
|
|
|
"fr": "les disques et les seed ISO",
|
|
|
|
|
|
"en": "disk images and seed ISOs",
|
|
|
|
|
|
},
|
|
|
|
|
|
"disks kept": {
|
|
|
|
|
|
"fr": "disques conservés",
|
|
|
|
|
|
"en": "disks kept",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Confirm deletion? (y/N): ": {
|
|
|
|
|
|
"fr": "Confirmer l'effacement ? (o/N, défaut : non) : ",
|
|
|
|
|
|
"en": "Confirm deletion? (y/N, default: no): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Deletion done.": {
|
|
|
|
|
|
"fr": "Effacement terminé.",
|
|
|
|
|
|
"en": "Deletion done.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Clean up QEMU (orphan files)": {
|
|
|
|
|
|
"fr": "🧹 Nettoyer QEMU (fichiers orphelins)",
|
|
|
|
|
|
"en": "🧹 Clean up QEMU (orphan files)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Scanning for orphan QEMU files...": {
|
|
|
|
|
|
"fr": "Recherche des fichiers QEMU orphelins...",
|
|
|
|
|
|
"en": "Scanning for orphan QEMU files...",
|
|
|
|
|
|
},
|
|
|
|
|
|
"orphan disk": {
|
|
|
|
|
|
"fr": "disque orphelin",
|
|
|
|
|
|
"en": "orphan disk",
|
|
|
|
|
|
},
|
|
|
|
|
|
"orphan seed": {
|
|
|
|
|
|
"fr": "seed orphelin",
|
|
|
|
|
|
"en": "orphan seed",
|
|
|
|
|
|
},
|
|
|
|
|
|
"partial download": {
|
|
|
|
|
|
"fr": "téléchargement interrompu",
|
|
|
|
|
|
"en": "partial download",
|
|
|
|
|
|
},
|
|
|
|
|
|
"orphan UEFI nvram": {
|
|
|
|
|
|
"fr": "nvram UEFI orpheline",
|
|
|
|
|
|
"en": "orphan UEFI nvram",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No orphan files found.": {
|
|
|
|
|
|
"fr": "Aucun fichier orphelin trouvé.",
|
|
|
|
|
|
"en": "No orphan files found.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Orphan files:": {
|
|
|
|
|
|
"fr": "Fichiers orphelins :",
|
|
|
|
|
|
"en": "Orphan files:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Total:": {
|
|
|
|
|
|
"fr": "Total :",
|
|
|
|
|
|
"en": "Total:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"files": {
|
|
|
|
|
|
"fr": "fichiers",
|
|
|
|
|
|
"en": "files",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Delete these orphan files? (y/N): ": {
|
|
|
|
|
|
"fr": "Effacer ces fichiers orphelins ? (o/N, défaut : non) : ",
|
|
|
|
|
|
"en": "Delete these orphan files? (y/N, default: no): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Cleanup done.": {
|
|
|
|
|
|
"fr": "Nettoyage terminé.",
|
|
|
|
|
|
"en": "Cleanup done.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Cached base images (reusable):": {
|
|
|
|
|
|
"fr": "Images de base en cache (réutilisables) :",
|
|
|
|
|
|
"en": "Cached base images (reusable):",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Also delete the cached base images? (y/N): ": {
|
|
|
|
|
|
"fr": "Effacer aussi les images de base en cache ? (o/N, défaut : non) : ",
|
|
|
|
|
|
"en": "Also delete the cached base images? (y/N, default: no): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"All cached base images (reusable):": {
|
|
|
|
|
|
"fr": "Toutes les images de base en cache (réutilisables) :",
|
|
|
|
|
|
"en": "All cached base images (reusable):",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Delete ALL cached base images? (y/N): ": {
|
|
|
|
|
|
"fr": "Effacer TOUTES les images de base en cache ? (o/N, défaut : non) : ",
|
|
|
|
|
|
"en": "Delete ALL cached base images? (y/N, default: no): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Ghost domains (defined but disk missing):": {
|
|
|
|
|
|
"fr": "Domaines fantômes (définis, disque manquant) :",
|
|
|
|
|
|
"en": "Ghost domains (defined but disk missing):",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Undefine these ghost domains? (y/N): ": {
|
|
|
|
|
|
"fr": "Supprimer la définition de ces domaines ? (o/N, défaut : non) : ",
|
|
|
|
|
|
"en": "Undefine these ghost domains? (y/N, default: no): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Stale codename-named Ubuntu images (duplicates):": {
|
|
|
|
|
|
"fr": "Images Ubuntu nommées par codename (doublons) :",
|
|
|
|
|
|
"en": "Stale codename-named Ubuntu images (duplicates):",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Delete these duplicate images? (y/N): ": {
|
|
|
|
|
|
"fr": "Effacer ces images en double ? (o/N, défaut : non) : ",
|
|
|
|
|
|
"en": "Delete these duplicate images? (y/N, default: no): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Orphan ~/.ssh/config entries:": {
|
|
|
|
|
|
"fr": "Entrées ~/.ssh/config orphelines :",
|
|
|
|
|
|
"en": "Orphan ~/.ssh/config entries:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Remove these ~/.ssh/config entries? (y/N): ": {
|
|
|
|
|
|
"fr": "Retirer ces entrées de ~/.ssh/config ? (o/N, défaut : non) : ",
|
|
|
|
|
|
"en": "Remove these ~/.ssh/config entries? (y/N, default: no): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Stale DHCP leases (no matching VM):": {
|
|
|
|
|
|
"fr": "Baux DHCP périmés (aucune VM correspondante) :",
|
|
|
|
|
|
"en": "Stale DHCP leases (no matching VM):",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Clear these stale leases? (y/N): ": {
|
|
|
|
|
|
"fr": "Effacer ces baux périmés ? (o/N, défaut : non) : ",
|
|
|
|
|
|
"en": "Clear these stale leases? (y/N, default: no): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Deploy ERPLibre infra (one minimal VM per image)": {
|
|
|
|
|
|
"fr": "Déployer l'infra ERPLibre (une VM minimale par image)",
|
|
|
|
|
|
"en": "Deploy ERPLibre infra (one minimal VM per image)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Deploy ERPLibre infra: one minimal VM per image": {
|
|
|
|
|
|
"fr": "Déployer l'infra ERPLibre : une VM minimale par image",
|
|
|
|
|
|
"en": "Deploy ERPLibre infra: one minimal VM per image",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Cannot load QEMU catalog: ": {
|
|
|
|
|
|
"fr": "Impossible de charger le catalogue QEMU : ",
|
|
|
|
|
|
"en": "Cannot load QEMU catalog: ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Distributions:": {
|
|
|
|
|
|
"fr": "Distributions :",
|
|
|
|
|
|
"en": "Distributions:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Whole catalog (every version)": {
|
|
|
|
|
|
"fr": "Tout le catalogue (chaque version)",
|
|
|
|
|
|
"en": "Whole catalog (every version)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"The main version of each distro (marked *)": {
|
|
|
|
|
|
"fr": "La version principale de chaque distro (marquée *)",
|
|
|
|
|
|
"en": "The main version of each distro (marked *)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Selection (numbers, 'all' or 'principal', default: all): ": {
|
|
|
|
|
|
"fr": "Sélection (numéros, « all » ou « principal », défaut : all) : ",
|
|
|
|
|
|
"en": "Selection (numbers, 'all' or 'principal', default: all): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Selection (numbers, or 'all', default: all): ": {
|
|
|
|
|
|
"fr": "Sélection (numéros, ou « all », défaut : all) : ",
|
|
|
|
|
|
"en": "Selection (numbers, or 'all', default: all): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Nothing selected.": {
|
|
|
|
|
|
"fr": "Rien de sélectionné.",
|
|
|
|
|
|
"en": "Nothing selected.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Deployment plan": {
|
|
|
|
|
|
"fr": "Plan de déploiement",
|
|
|
|
|
|
"en": "Deployment plan",
|
|
|
|
|
|
},
|
|
|
|
|
|
"disk": {
|
|
|
|
|
|
"fr": "disque",
|
|
|
|
|
|
"en": "disk",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Total RAM (all running):": {
|
|
|
|
|
|
"fr": "RAM totale (toutes actives) :",
|
|
|
|
|
|
"en": "Total RAM (all running):",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Total virtual disk (thin qcow2):": {
|
|
|
|
|
|
"fr": "Disque virtuel total (qcow2 thin) :",
|
|
|
|
|
|
"en": "Total virtual disk (thin qcow2):",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Host RAM available:": {
|
|
|
|
|
|
"fr": "RAM disponible de l'hôte :",
|
|
|
|
|
|
"en": "Host RAM available:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Total RAM exceeds host free RAM: not all VMs will run at once.": {
|
|
|
|
|
|
"fr": "La RAM totale dépasse la RAM libre de l'hôte : les VM ne "
|
|
|
|
|
|
"tourneront pas toutes en même temps.",
|
|
|
|
|
|
"en": "Total RAM exceeds host free RAM: not all VMs will run at once.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Install ERPLibre into ~/git/erplibre on each VM? (Y/n): ": {
|
|
|
|
|
|
"fr": "Installer ERPLibre dans ~/git/erplibre sur chaque VM ? "
|
|
|
|
|
|
"(O/n, défaut : oui) : ",
|
|
|
|
|
|
"en": "Install ERPLibre into ~/git/erplibre on each VM? "
|
|
|
|
|
|
"(Y/n, default: yes): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Deploy these VMs now? (Y/n): ": {
|
|
|
|
|
|
"fr": "Déployer ces VM maintenant ? (O/n, défaut : oui) : ",
|
|
|
|
|
|
"en": "Deploy these VMs now? (Y/n, default: yes): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Discard everything and start over? (y/N): ": {
|
|
|
|
|
|
"fr": "Tout abandonner et recommencer ? (o/N, défaut : non) : ",
|
|
|
|
|
|
"en": "Discard everything and start over? (y/N, default: no): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Final review before deployment": {
|
|
|
|
|
|
"fr": "Récapitulatif final avant déploiement",
|
|
|
|
|
|
"en": "Final review before deployment",
|
|
|
|
|
|
},
|
|
|
|
|
|
"VMs to create:": {
|
|
|
|
|
|
"fr": "VM à créer :",
|
|
|
|
|
|
"en": "VMs to create:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Existing, left untouched:": {
|
|
|
|
|
|
"fr": "Existantes, laissées intactes :",
|
|
|
|
|
|
"en": "Existing, left untouched:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"ERPLibre install:": {
|
|
|
|
|
|
"fr": "Installation ERPLibre :",
|
|
|
|
|
|
"en": "ERPLibre install:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"profile": {
|
|
|
|
|
|
"fr": "profil",
|
|
|
|
|
|
"en": "profile",
|
|
|
|
|
|
},
|
|
|
|
|
|
"branch": {
|
|
|
|
|
|
"fr": "branche",
|
|
|
|
|
|
"en": "branch",
|
|
|
|
|
|
},
|
|
|
|
|
|
"production (/opt, confined)": {
|
|
|
|
|
|
"fr": "production (/opt, confiné)",
|
|
|
|
|
|
"en": "production (/opt, confined)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"development (~/git)": {
|
|
|
|
|
|
"fr": "développement (~/git)",
|
|
|
|
|
|
"en": "development (~/git)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"no": {
|
|
|
|
|
|
"fr": "non",
|
|
|
|
|
|
"en": "no",
|
|
|
|
|
|
},
|
|
|
|
|
|
"SSH key:": {
|
|
|
|
|
|
"fr": "Clé SSH :",
|
|
|
|
|
|
"en": "SSH key:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"~/.ssh/config:": {
|
|
|
|
|
|
"fr": "~/.ssh/config :",
|
|
|
|
|
|
"en": "~/.ssh/config:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"one entry per VM": {
|
|
|
|
|
|
"fr": "une entrée par VM",
|
|
|
|
|
|
"en": "one entry per VM",
|
|
|
|
|
|
},
|
|
|
|
|
|
"untouched": {
|
|
|
|
|
|
"fr": "inchangé",
|
|
|
|
|
|
"en": "untouched",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Parallelism:": {
|
|
|
|
|
|
"fr": "Parallélisme :",
|
|
|
|
|
|
"en": "Parallelism:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"at a time": {
|
|
|
|
|
|
"fr": "à la fois",
|
|
|
|
|
|
"en": "at a time",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Nothing to create - every VM already exists.": {
|
|
|
|
|
|
"fr": "Rien à créer — toutes les VM existent déjà.",
|
|
|
|
|
|
"en": "Nothing to create — every VM already exists.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Name collisions detected": {
|
|
|
|
|
|
"fr": "Collisions de noms détectées",
|
|
|
|
|
|
"en": "Name collisions detected",
|
|
|
|
|
|
},
|
|
|
|
|
|
"VM already defined - SKIPPED, nothing overwritten": {
|
|
|
|
|
|
"fr": "VM déjà définie — IGNORÉE, rien ne sera écrasé",
|
|
|
|
|
|
"en": "VM already defined — SKIPPED, nothing overwritten",
|
|
|
|
|
|
},
|
|
|
|
|
|
"disk present without VM - deployment will FAIL": {
|
|
|
|
|
|
"fr": "disque présent sans VM — le déploiement ÉCHOUERA",
|
|
|
|
|
|
"en": "disk present without VM - deployment will FAIL",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Remove it by hand, or rename the VM.": {
|
|
|
|
|
|
"fr": "L'effacer à la main, ou renommer la VM.",
|
|
|
|
|
|
"en": "Remove it by hand, or rename the VM.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Continue despite these collisions? (y/N): ": {
|
|
|
|
|
|
"fr": "Continuer malgré ces collisions ? (o/N, défaut : non) : ",
|
|
|
|
|
|
"en": "Continue despite these collisions? (y/N, default: no): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Resolving VM IPs (parallel, emulated boot is slow)...": {
|
|
|
|
|
|
"fr": "Résolution des IP des VM (en parallèle, le démarrage émulé "
|
|
|
|
|
|
"est lent)...",
|
|
|
|
|
|
"en": "Resolving VM IPs (parallel, emulated boot is slow)...",
|
|
|
|
|
|
},
|
|
|
|
|
|
"no IP": {
|
|
|
|
|
|
"fr": "pas d'IP",
|
|
|
|
|
|
"en": "no IP",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Cloning ERPLibre on each VM": {
|
|
|
|
|
|
"fr": "Clonage d'ERPLibre sur chaque VM",
|
|
|
|
|
|
"en": "Cloning ERPLibre on each VM",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Installing ERPLibre on each VM": {
|
|
|
|
|
|
"fr": "Installation d'ERPLibre sur chaque VM",
|
|
|
|
|
|
"en": "Installing ERPLibre on each VM",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Install ERPLibre into ~/git/erplibre on this VM? (y/N): ": {
|
|
|
|
|
|
"fr": "Installer ERPLibre dans ~/git/erplibre sur cette VM ? (o/N, défaut : non) : ",
|
|
|
|
|
|
"en": "Install ERPLibre into ~/git/erplibre on this VM? (y/N, default: no): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"installing ERPLibre": {
|
|
|
|
|
|
"fr": "installation d'ERPLibre",
|
|
|
|
|
|
"en": "installing ERPLibre",
|
|
|
|
|
|
},
|
|
|
|
|
|
"no IP obtained, ERPLibre install skipped.": {
|
|
|
|
|
|
"fr": "aucune IP obtenue, installation ERPLibre ignorée.",
|
|
|
|
|
|
"en": "no IP obtained, ERPLibre install skipped.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"waiting for SSH...": {
|
|
|
|
|
|
"fr": "attente du SSH...",
|
|
|
|
|
|
"en": "waiting for SSH...",
|
|
|
|
|
|
},
|
|
|
|
|
|
"SSH not reachable, ERPLibre install skipped.": {
|
|
|
|
|
|
"fr": "SSH injoignable, installation ERPLibre ignorée.",
|
|
|
|
|
|
"en": "SSH not reachable, ERPLibre install skipped.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"What to install on the VM(s)?": {
|
|
|
|
|
|
"fr": "Que veut-on installer sur la/les VM ?",
|
|
|
|
|
|
"en": "What to install on the VM(s)?",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Choice (number, blank = Odoo 18): ": {
|
|
|
|
|
|
"fr": "Choix (numéro, vide = Odoo 18) : ",
|
|
|
|
|
|
"en": "Choice (number, blank = Odoo 18): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"ERPLibre + all Odoo versions": {
|
|
|
|
|
|
"fr": "ERPLibre + toutes les versions Odoo",
|
|
|
|
|
|
"en": "ERPLibre + all Odoo versions",
|
|
|
|
|
|
},
|
|
|
|
|
|
"ERPLibre only (no Odoo)": {
|
|
|
|
|
|
"fr": "ERPLibre seulement (sans Odoo)",
|
|
|
|
|
|
"en": "ERPLibre only (no Odoo)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"ERPLibre mobile (home)": {
|
|
|
|
|
|
"fr": "ERPLibre mobile (home)",
|
|
|
|
|
|
"en": "ERPLibre mobile (home)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"ERPLibre Deployment (+ QEMU + dev)": {
|
|
|
|
|
|
"fr": "ERPLibre Déploiement (+ QEMU + dev)",
|
|
|
|
|
|
"en": "ERPLibre Deployment (+ QEMU + dev)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Interactive monitoring dashboard? (y/N): ": {
|
|
|
|
|
|
"fr": "Suivi interactif (dashboard) ? (O/n, défaut : oui) : ",
|
|
|
|
|
|
"en": "Interactive monitoring dashboard? (Y/n, default: yes): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"resolving IP...": {
|
|
|
|
|
|
"fr": "résolution de l'IP...",
|
|
|
|
|
|
"en": "resolving IP...",
|
|
|
|
|
|
},
|
|
|
|
|
|
"no IP, skipped.": {
|
|
|
|
|
|
"fr": "pas d'IP, ignorée.",
|
|
|
|
|
|
"en": "no IP, skipped.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No VM to install.": {
|
|
|
|
|
|
"fr": "Aucune VM à installer.",
|
|
|
|
|
|
"en": "No VM to install.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Opening the interactive monitor...": {
|
|
|
|
|
|
"fr": "Ouverture du suivi interactif...",
|
|
|
|
|
|
"en": "Opening the interactive monitor...",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Monitor closed. Installs keep running in the background.": {
|
|
|
|
|
|
"fr": "Suivi fermé. Les installations continuent en arrière-plan.",
|
|
|
|
|
|
"en": "Monitor closed. Installs keep running in the background.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Logs:": {
|
|
|
|
|
|
"fr": "Logs :",
|
|
|
|
|
|
"en": "Logs:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Read the logs:": {
|
|
|
|
|
|
"fr": "Lire les logs :",
|
|
|
|
|
|
"en": "Read the logs:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Log files:": {
|
|
|
|
|
|
"fr": "Fichiers de log :",
|
|
|
|
|
|
"en": "Log files:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Parallel deployments (default:": {
|
|
|
|
|
|
"fr": "Déploiements en parallèle (défaut :",
|
|
|
|
|
|
"en": "Parallel deployments (default:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"VMs": {
|
|
|
|
|
|
"fr": "VM",
|
|
|
|
|
|
"en": "VMs",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Deploy summary:": {
|
|
|
|
|
|
"fr": "Bilan déploiement :",
|
|
|
|
|
|
"en": "Deploy summary:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"IPs resolved:": {
|
|
|
|
|
|
"fr": "IP résolues :",
|
|
|
|
|
|
"en": "IPs resolved:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"still waiting for": {
|
|
|
|
|
|
"fr": "encore en attente de",
|
|
|
|
|
|
"en": "still waiting for",
|
|
|
|
|
|
},
|
|
|
|
|
|
"TOTAL summary": {
|
|
|
|
|
|
"fr": "Sommaire TOTAL",
|
|
|
|
|
|
"en": "TOTAL summary",
|
|
|
|
|
|
},
|
|
|
|
|
|
"VMs deployed:": {
|
|
|
|
|
|
"fr": "VM déployées :",
|
|
|
|
|
|
"en": "VMs deployed:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"total incl. existing:": {
|
|
|
|
|
|
"fr": "total avec existantes :",
|
|
|
|
|
|
"en": "total incl. existing:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Total time:": {
|
|
|
|
|
|
"fr": "Temps total :",
|
|
|
|
|
|
"en": "Total time:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Deploying": {
|
|
|
|
|
|
"fr": "Déploiement de",
|
|
|
|
|
|
"en": "Deploying",
|
|
|
|
|
|
},
|
|
|
|
|
|
"parallel jobs:": {
|
|
|
|
|
|
"fr": "tâches parallèles :",
|
|
|
|
|
|
"en": "parallel jobs:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"ERPLibre infra deployment done.": {
|
|
|
|
|
|
"fr": "Déploiement de l'infra ERPLibre terminé.",
|
|
|
|
|
|
"en": "ERPLibre infra deployment done.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Manage with:": {
|
|
|
|
|
|
"fr": "Gérer avec :",
|
|
|
|
|
|
"en": "Manage with:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Fetching ERPLibre branch list...": {
|
|
|
|
|
|
"fr": "Récupération de la liste des branches ERPLibre...",
|
|
|
|
|
|
"en": "Fetching ERPLibre branch list...",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Branch (default:": {
|
|
|
|
|
|
"fr": "Branche (défaut :",
|
|
|
|
|
|
"en": "Branch (default:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Branches:": {
|
|
|
|
|
|
"fr": "Branches :",
|
|
|
|
|
|
"en": "Branches:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Choice (number or name, default:": {
|
|
|
|
|
|
"fr": "Choix (numéro ou nom, défaut :",
|
|
|
|
|
|
"en": "Choice (number or name, default:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"no IP obtained, ERPLibre clone skipped.": {
|
|
|
|
|
|
"fr": "aucune IP obtenue, clonage ERPLibre ignoré.",
|
|
|
|
|
|
"en": "no IP obtained, ERPLibre clone skipped.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"cloning ERPLibre": {
|
|
|
|
|
|
"fr": "clonage d'ERPLibre",
|
|
|
|
|
|
"en": "cloning ERPLibre",
|
|
|
|
|
|
},
|
|
|
|
|
|
"VM name (required): ": {
|
|
|
|
|
|
"fr": "Nom de la VM (requis) : ",
|
|
|
|
|
|
"en": "VM name (required): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"VM name is required!": {
|
|
|
|
|
|
"fr": "Le nom de la VM est requis !",
|
|
|
|
|
|
"en": "VM name is required!",
|
|
|
|
|
|
},
|
|
|
|
|
|
"VM name: ": {
|
|
|
|
|
|
"fr": "Nom de la VM : ",
|
|
|
|
|
|
"en": "VM name: ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"VM name or ID: ": {
|
|
|
|
|
|
"fr": "Nom ou ID de la VM : ",
|
|
|
|
|
|
"en": "VM name or ID: ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"VM name or ID (or 'all'): ": {
|
|
|
|
|
|
"fr": "Nom ou ID de la VM (ou « all ») : ",
|
|
|
|
|
|
"en": "VM name or ID (or 'all'): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"RAM in MB (blank = version minimum): ": {
|
|
|
|
|
|
"fr": "RAM en Mo (vide = minimum de la version) : ",
|
|
|
|
|
|
"en": "RAM in MB (blank = version minimum): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"vCPUs (default: 2): ": {
|
|
|
|
|
|
"fr": "vCPU (défaut : 2) : ",
|
|
|
|
|
|
"en": "vCPUs (default: 2): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Disk size (blank = version min; e.g. 30G, 1T): ": {
|
|
|
|
|
|
"fr": "Taille du disque (vide = min. version ; ex. 30G, 1T) : ",
|
|
|
|
|
|
"en": "Disk size (blank = version min; e.g. 30G, 1T): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"VM name (default: ": {
|
|
|
|
|
|
"fr": "Nom de la VM (défaut : ",
|
|
|
|
|
|
"en": "VM name (default: ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Deployment failed (see the error above).": {
|
|
|
|
|
|
"fr": "Échec du déploiement (voir l'erreur ci-dessus).",
|
|
|
|
|
|
"en": "Deployment failed (see the error above).",
|
|
|
|
|
|
},
|
|
|
|
|
|
"SSH public key path": {
|
|
|
|
|
|
"fr": "Chemin de la clé publique SSH",
|
|
|
|
|
|
"en": "SSH public key path",
|
|
|
|
|
|
},
|
[IMP] qemu: a VM one can reach, and a first boot that does not stall
The monitor froze the address given at launch, so it lost the VM as soon as
cloud-init renamed the host and DHCP handed out another lease. It now
re-resolves at each attempt, in the views too, and reads virsh without sudo
— « sudo -n » fails in a detached session with no tty.
First boot also stopped paying for what it does not need: the guest agent
leaves cloud-init, snapd and locale-gen go, apt takes the fastest mirror.
The timezone follows the host. And when KVM is missing, the deployment says
so before the wait instead of being mysteriously fifteen times slower.
--- FR ---
Le suivi figeait l'adresse connue au lancement : il perdait donc la VM dès
que cloud-init posait le vrai nom d'hôte et que DHCP donnait un autre bail.
Il la ré-résout désormais à chaque tentative, dans les vues aussi, et lit
virsh sans sudo — « sudo -n » échoue dans une session détachée, sans tty.
Le premier démarrage cesse aussi de payer l'inutile : l'agent invité sort
de cloud-init, snapd et locale-gen disparaissent, apt prend le miroir le
plus rapide. Le fuseau suit l'hôte. Et faute de KVM, le déploiement le dit
avant l'attente, au lieu d'être quinze fois plus lent sans raison visible.
Assisted-by: Claude Opus 5
2026-08-10 03:10:50 -04:00
|
|
|
|
"Timezone": {
|
|
|
|
|
|
"fr": "Fuseau horaire",
|
|
|
|
|
|
"en": "Timezone",
|
|
|
|
|
|
},
|
|
|
|
|
|
"virsh cannot reach qemu:///system without sudo.": {
|
|
|
|
|
|
"fr": "virsh ne peut pas joindre qemu:///system sans sudo.",
|
|
|
|
|
|
"en": "virsh cannot reach qemu:///system without sudo.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"The install monitor runs detached and cannot type a": {
|
|
|
|
|
|
"fr": "Le suivi d'installation tourne détaché et ne peut pas saisir",
|
|
|
|
|
|
"en": "The install monitor runs detached and cannot type a",
|
|
|
|
|
|
},
|
|
|
|
|
|
"password: it would lose the VM when its lease moves.": {
|
|
|
|
|
|
"fr": "de mot de passe : il perdrait la VM au changement de bail.",
|
|
|
|
|
|
"en": "password: it would lose the VM when its lease moves.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"You are in the libvirt group, but this session": {
|
|
|
|
|
|
"fr": "Vous êtes dans le groupe libvirt, mais cette session est",
|
|
|
|
|
|
"en": "You are in the libvirt group, but this session",
|
|
|
|
|
|
},
|
|
|
|
|
|
"predates it. Log out and back in, or run:": {
|
|
|
|
|
|
"fr": "antérieure. Reconnectez-vous, ou lancez :",
|
|
|
|
|
|
"en": "predates it. Log out and back in, or run:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Group is active, so the cause is elsewhere:": {
|
|
|
|
|
|
"fr": "Le groupe est actif : la cause est donc ailleurs.",
|
|
|
|
|
|
"en": "Group is active, so the cause is elsewhere:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Add your user to the libvirt group?": {
|
|
|
|
|
|
"fr": "Ajouter votre utilisateur au groupe libvirt ?",
|
|
|
|
|
|
"en": "Add your user to the libvirt group?",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Run it now? (Y/n): ": {
|
|
|
|
|
|
"fr": "L'exécuter maintenant ? (O/n) : ",
|
|
|
|
|
|
"en": "Run it now? (Y/n): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Command failed.": {
|
|
|
|
|
|
"fr": "La commande a échoué.",
|
|
|
|
|
|
"en": "Command failed.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Added. Log out and back in for it to take effect,": {
|
|
|
|
|
|
"fr": "Ajouté. Reconnectez-vous pour que ça prenne effet,",
|
|
|
|
|
|
"en": "Added. Log out and back in for it to take effect,",
|
|
|
|
|
|
},
|
|
|
|
|
|
"or start a new shell with: newgrp libvirt": {
|
|
|
|
|
|
"fr": "ou ouvrez un shell neuf avec : newgrp libvirt",
|
|
|
|
|
|
"en": "or start a new shell with: newgrp libvirt",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Timezone for the VMs": {
|
|
|
|
|
|
"fr": "Fuseau horaire des VM",
|
|
|
|
|
|
"en": "Timezone for the VMs",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Locale for the VMs": {
|
|
|
|
|
|
"fr": "Locale des VM",
|
|
|
|
|
|
"en": "Locale for the VMs",
|
|
|
|
|
|
},
|
|
|
|
|
|
"KVM is unavailable: the VMs will be EMULATED.": {
|
|
|
|
|
|
"fr": "KVM indisponible : les VM seront ÉMULÉES.",
|
|
|
|
|
|
"en": "KVM is unavailable: the VMs will be EMULATED.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"A boot then takes 10-15 min, not under a minute.": {
|
|
|
|
|
|
"fr": "Un démarrage prend alors 10 à 15 min, pas moins d'une minute.",
|
|
|
|
|
|
"en": "A boot then takes 10-15 min, not under a minute.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Cause: /dev/kvm is missing. This host is itself a VM": {
|
|
|
|
|
|
"fr": "Cause : /dev/kvm est absent. Cet hôte est lui-même une VM",
|
|
|
|
|
|
"en": "Cause: /dev/kvm is missing. This host is itself a VM",
|
|
|
|
|
|
},
|
|
|
|
|
|
"whose hypervisor does not expose nested virtualization.": {
|
|
|
|
|
|
"fr": "dont l'hyperviseur n'expose pas la virtualisation imbriquée.",
|
|
|
|
|
|
"en": "whose hypervisor does not expose nested virtualization.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"To fix it ON THE PARENT HYPERVISOR, not here:": {
|
|
|
|
|
|
"fr": "Pour corriger, SUR L'HYPERVISEUR PARENT, pas ici :",
|
|
|
|
|
|
"en": "To fix it ON THE PARENT HYPERVISOR, not here:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"then set this VM to the host-passthrough CPU mode and": {
|
|
|
|
|
|
"fr": "puis donner à cette VM le mode CPU « host-passthrough », et",
|
|
|
|
|
|
"en": "then set this VM to the host-passthrough CPU mode and",
|
|
|
|
|
|
},
|
|
|
|
|
|
"stop it and start it again - a reboot is not enough.": {
|
|
|
|
|
|
"fr": "l'arrêter puis la redémarrer — un reboot ne suffit pas.",
|
|
|
|
|
|
"en": "stop it and start it again - a reboot is not enough.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Without access to that hypervisor, nothing to do here.": {
|
|
|
|
|
|
"fr": "Sans accès à cet hyperviseur, il n'y a rien à régler ici.",
|
|
|
|
|
|
"en": "Without access to that hypervisor, nothing to do here.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Usual cause: this host is itself a VM without nested": {
|
|
|
|
|
|
"fr": "Cause habituelle : cet hôte est lui-même une VM sans",
|
|
|
|
|
|
"en": "Usual cause: this host is itself a VM without nested",
|
|
|
|
|
|
},
|
|
|
|
|
|
"virtualization. Check: systemd-detect-virt": {
|
|
|
|
|
|
"fr": "virtualisation imbriquée. Vérifier : systemd-detect-virt",
|
|
|
|
|
|
"en": "virtualization. Check: systemd-detect-virt",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Unknown timezone, keeping": {
|
|
|
|
|
|
"fr": "Fuseau inconnu, on garde",
|
|
|
|
|
|
"en": "Unknown timezone, keeping",
|
|
|
|
|
|
},
|
[ADD] todo: QEMU/KVM menu
Deploy, list, test, resize, delete and clean up VMs. Adds the SSH
configuration with recursive ProxyJump, port forwarding, and registration
of the QEMU hosts in virt-manager.
--- FR ---
Déployer, lister, tester, redimensionner, supprimer et nettoyer des VM.
Ajoute la configuration SSH avec ProxyJump récursif, la redirection de port
et l'enregistrement des hôtes QEMU dans virt-manager.
Assisted-by: Claude Opus 4.8
2026-08-07 03:22:26 -04:00
|
|
|
|
"none": {
|
|
|
|
|
|
"fr": "aucune",
|
|
|
|
|
|
"en": "none",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No SSH key found. Set a password instead? (Y/n): ": {
|
|
|
|
|
|
"fr": "Aucune clé SSH trouvée. Définir un mot de passe ? (O/n) : ",
|
|
|
|
|
|
"en": "No SSH key found. Set a password instead? (Y/n): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Overwrite existing VM disk if present? (y/N): ": {
|
|
|
|
|
|
"fr": "Écraser le disque de la VM s'il existe ? (o/N, défaut : non) : ",
|
|
|
|
|
|
"en": "Overwrite existing VM disk if present? (y/N, default: no): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Verify SHA256 after download? (y/N): ": {
|
|
|
|
|
|
"fr": "Vérifier le SHA256 après téléchargement ? (o/N, défaut : non) : ",
|
|
|
|
|
|
"en": "Verify SHA256 after download? (y/N, default: no): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"QEMU - Sample dry-run (demo-vm, Ubuntu 24.04)": {
|
[ADD] qemu: build the tunnel to the remote desktop
A "Remote desktop tunnel" entry under SSH configuration. It lists the
targets, resolves them, and composes the full command -- nothing to fill
in.
todo.py runs on the libvirt HOST; the tunnel starts from the workstation.
It cannot open it, but it alone knows the VM's private IP, its port and
the address through which it was reached. That last one comes from
SSH_CONNECTION, whose third field is exactly the server address used --
far safer than a "hostname" that may resolve to nothing outside.
When ~/.ssh/config already carries the entry, the tunnel takes it:
"ssh -N -L 5902:localhost:5901 <vm>". ProxyJump makes the route and
"localhost" means the VM's OWN loopback, so the tunnel survives an IP
change. That config is also the right source of targets, not the local
libvirt: a graphical VM is often nested, and virsh would only ever list
the orchestrator.
Two details: the hypervisor console needs VNC bound to the loopback, as
"listen=none" opens no socket at all; and two menu entries had no icon.
--- FR ---
Une entrée « Tunnel bureau distant » sous Configuration SSH. Elle liste
les cibles, les résout, et compose la commande complète — rien à remplir.
todo.py tourne sur l'HÔTE libvirt ; le tunnel, lui, part du poste de
travail. Il ne peut donc pas l'ouvrir, mais il est le seul à connaître
l'IP privée de la VM, son port et l'adresse par laquelle on l'a joint.
Cette dernière vient de SSH_CONNECTION, dont le troisième champ est
exactement l'adresse serveur utilisée — bien plus sûr qu'un « hostname »
qui peut ne rien résoudre depuis l'extérieur.
Quand ~/.ssh/config porte déjà l'entrée, le tunnel l'emprunte :
« ssh -N -L 5902:localhost:5901 <vm> ». Le ProxyJump fait la route et
« localhost » désigne le bouclage DE LA VM, si bien que le tunnel survit à
un changement d'IP. Ce fichier est aussi la bonne source de cibles, pas le
libvirt local : une VM graphique est souvent imbriquée, et virsh ne
listerait jamais que l'orchestrateur.
Deux détails : la console de l'hyperviseur exige un VNC sur la boucle
locale, « listen=none » n'ouvrant aucun socket ; et deux entrées de menu
n'avaient pas d'icône.
Assisted-by: Claude Opus 5
2026-08-13 03:40:22 -04:00
|
|
|
|
"fr": "🧰 QEMU - Exemple dry-run (demo-vm, Ubuntu 24.04)",
|
|
|
|
|
|
"en": "🧰 QEMU - Sample dry-run (demo-vm, Ubuntu 24.04)",
|
[ADD] todo: QEMU/KVM menu
Deploy, list, test, resize, delete and clean up VMs. Adds the SSH
configuration with recursive ProxyJump, port forwarding, and registration
of the QEMU hosts in virt-manager.
--- FR ---
Déployer, lister, tester, redimensionner, supprimer et nettoyer des VM.
Ajoute la configuration SSH avec ProxyJump récursif, la redirection de port
et l'enregistrement des hôtes QEMU dans virt-manager.
Assisted-by: Claude Opus 4.8
2026-08-07 03:22:26 -04:00
|
|
|
|
},
|
|
|
|
|
|
# QEMU - statistics screen
|
|
|
|
|
|
"Statistics (installs, durations, VMs)": {
|
|
|
|
|
|
"fr": "📊 Statistiques (installations, durées, VM)",
|
|
|
|
|
|
"en": "📊 Statistics (installs, durations, VMs)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"QEMU statistics": {"fr": "Statistiques QEMU", "en": "QEMU statistics"},
|
|
|
|
|
|
"No installation recorded yet.": {
|
|
|
|
|
|
"fr": "Aucune installation enregistrée pour l'instant.",
|
|
|
|
|
|
"en": "No installation recorded yet.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Installations": {"fr": "Installations", "en": "Installations"},
|
|
|
|
|
|
"Total": {"fr": "Total", "en": "Total"},
|
|
|
|
|
|
"succeeded": {"fr": "réussies", "en": "succeeded"},
|
|
|
|
|
|
"failed": {"fr": "échouées", "en": "failed"},
|
|
|
|
|
|
"Period": {"fr": "Période", "en": "Period"},
|
|
|
|
|
|
"days": {"fr": "jours", "en": "days"},
|
|
|
|
|
|
"Median duration": {"fr": "Durée médiane", "en": "Median duration"},
|
|
|
|
|
|
"min": {"fr": "min", "en": "min"},
|
|
|
|
|
|
"max": {"fr": "max", "en": "max"},
|
|
|
|
|
|
"Cumulated time": {"fr": "Temps cumulé", "en": "Cumulated time"},
|
|
|
|
|
|
"By distribution": {"fr": "Par distribution", "en": "By distribution"},
|
|
|
|
|
|
"By version": {"fr": "Par version", "en": "By version"},
|
|
|
|
|
|
"By architecture": {"fr": "Par architecture", "en": "By architecture"},
|
|
|
|
|
|
"Virtual machines": {
|
|
|
|
|
|
"fr": "Machines virtuelles",
|
|
|
|
|
|
"en": "Virtual machines",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Defined": {"fr": "Définies", "en": "Defined"},
|
|
|
|
|
|
"running": {"fr": "en cours", "en": "running"},
|
|
|
|
|
|
"stopped": {"fr": "arrêtées", "en": "stopped"},
|
|
|
|
|
|
"Disk used": {"fr": "Disque utilisé", "en": "Disk used"},
|
|
|
|
|
|
"image": {"fr": "image", "en": "image"},
|
|
|
|
|
|
"failure": {"fr": "échec", "en": "failure"},
|
|
|
|
|
|
"Reset the statistics": {
|
|
|
|
|
|
"fr": "Réinitialiser les statistiques",
|
|
|
|
|
|
"en": "Reset the statistics",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Nothing to reset.": {"fr": "Rien à effacer.", "en": "Nothing to reset."},
|
|
|
|
|
|
"Erase": {"fr": "Effacer", "en": "Erase"},
|
|
|
|
|
|
"recorded runs": {"fr": "runs enregistrés", "en": "recorded runs"},
|
|
|
|
|
|
"runs erased": {"fr": "runs effacés", "en": "runs erased"},
|
|
|
|
|
|
"Cancelled.": {"fr": "Annulé.", "en": "Cancelled."},
|
[ADD] migration: see and repair the website COW views
A copy-on-write view freezes the module view it came from; the module moves
on and the upgrade dies hours later on a missing anchor. These tools
predict, snapshot, diff, neutralize and reset them. The migration screen
gains the real state of each step, replay from any of them, and statistics.
--- FR ---
Une vue copy-on-write fige la vue de module dont elle vient ; le module
évolue et la mise à niveau meurt des heures plus tard sur un point
d'ancrage absent. Ces outils les prévoient, photographient, comparent,
neutralisent et réinitialisent. L'écran de migration gagne l'état réel de
chaque étape, la reprise depuis n'importe laquelle, et des statistiques.
Assisted-by: Claude Opus 5
2026-08-10 03:10:50 -04:00
|
|
|
|
# Database migration - resume menu
|
|
|
|
|
|
"Migration in progress": {
|
|
|
|
|
|
"fr": "Migration en cours",
|
|
|
|
|
|
"en": "Migration in progress",
|
|
|
|
|
|
},
|
|
|
|
|
|
"File": {"fr": "Fichier", "en": "File"},
|
|
|
|
|
|
"Database": {"fr": "Base", "en": "Database"},
|
|
|
|
|
|
"Target": {"fr": "Cible", "en": "Target"},
|
|
|
|
|
|
"Started": {"fr": "Démarrée", "en": "Started"},
|
|
|
|
|
|
"Steps": {"fr": "Étapes", "en": "Steps"},
|
|
|
|
|
|
"Prepare the environment": {
|
|
|
|
|
|
"fr": "Préparer l'environnement",
|
|
|
|
|
|
"en": "Prepare the environment",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Restore and neutralize the database": {
|
|
|
|
|
|
"fr": "Restaurer et neutraliser la base",
|
|
|
|
|
|
"en": "Restore and neutralize the database",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Update all addons": {
|
|
|
|
|
|
"fr": "Mettre à jour tous les modules",
|
|
|
|
|
|
"en": "Update all addons",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Clean up before data migration": {
|
|
|
|
|
|
"fr": "Nettoyer avant la migration des données",
|
|
|
|
|
|
"en": "Clean up before data migration",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Upgrade version by version (OpenUpgrade)": {
|
|
|
|
|
|
"fr": "Monter de version en version (OpenUpgrade)",
|
|
|
|
|
|
"en": "Upgrade version by version (OpenUpgrade)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"not started": {"fr": "non démarrée", "en": "not started"},
|
|
|
|
|
|
"done": {"fr": "terminée", "en": "done"},
|
|
|
|
|
|
"partially done": {"fr": "partielle", "en": "partially done"},
|
|
|
|
|
|
"version bumps migrated": {
|
|
|
|
|
|
"fr": "montées de version faites",
|
|
|
|
|
|
"en": "version bumps migrated",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Continue where it stopped": {
|
|
|
|
|
|
"fr": "Continuer là où ça s'est arrêté",
|
|
|
|
|
|
"en": "Continue where it stopped",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Replay from that step": {
|
|
|
|
|
|
"fr": "Reprendre à partir de cette étape",
|
|
|
|
|
|
"en": "Replay from that step",
|
|
|
|
|
|
},
|
|
|
|
|
|
"erases the progression of that step and the next ones": {
|
|
|
|
|
|
"fr": "efface la progression de cette étape et des suivantes",
|
|
|
|
|
|
"en": "erases the progression of that step and the next ones",
|
|
|
|
|
|
},
|
|
|
|
|
|
"New migration, erase everything": {
|
|
|
|
|
|
"fr": "Nouvelle migration, tout effacer",
|
|
|
|
|
|
"en": "New migration, erase everything",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Keep the zip only, ask every question again": {
|
|
|
|
|
|
"fr": "Garder seulement le zip, reposer toutes les questions",
|
|
|
|
|
|
"en": "Keep the zip only, ask every question again",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Your choice": {"fr": "Votre choix", "en": "Your choice"},
|
|
|
|
|
|
"Unknown choice, continuing where it stopped": {
|
|
|
|
|
|
"fr": "Choix inconnu, on continue là où ça s'est arrêté",
|
|
|
|
|
|
"en": "Unknown choice, continuing where it stopped",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Replaying from step": {
|
|
|
|
|
|
"fr": "Reprise à partir de l'étape",
|
|
|
|
|
|
"en": "Replaying from step",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Replay the upgrade from version N": {
|
|
|
|
|
|
"fr": "Reprendre la montée à partir de la version N",
|
|
|
|
|
|
"en": "Replay the upgrade from version N",
|
|
|
|
|
|
},
|
|
|
|
|
|
"rebuilds the intermediate database": {
|
|
|
|
|
|
"fr": "recrée la base intermédiaire",
|
|
|
|
|
|
"en": "rebuilds the intermediate database",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Unknown version": {"fr": "Version inconnue", "en": "Unknown version"},
|
|
|
|
|
|
"The progression file is invalid, ignoring it": {
|
|
|
|
|
|
"fr": "Le fichier de progression est invalide, on l'ignore",
|
|
|
|
|
|
"en": "The progression file is invalid, ignoring it",
|
|
|
|
|
|
},
|
[ADD] analyse: inspect an Odoo database without restoring it
A read-only toolkit answering « what is in this database, and what will
break on a version bump »: schema size and tables no model claims,
customised views compared with their module source, and the x_ fields
Studio left behind. It reads a backup zip directly, so a 40 GB dump needs
no restore, and it reaches every answer from the TODO menu.
--- FR ---
Une boîte à outils en lecture seule qui répond à « qu'y a-t-il dans cette
base, et qu'est-ce qui cassera à la montée de version » : taille du schéma
et tables qu'aucun modèle ne réclame, vues personnalisées comparées à leur
source, champs x_ laissés par Studio. Elle lit un zip de sauvegarde tel
quel, donc sans restaurer, et tout s'atteint depuis le menu TODO.
Assisted-by: Claude Opus 5
2026-08-10 03:10:50 -04:00
|
|
|
|
# --- Menu Execute › Analyse ---
|
|
|
|
|
|
# L'émoji vit dans la valeur, jamais dans la clé. Deux clés distinctes,
|
|
|
|
|
|
# comme « Data » / « Database » : l'entrée de menu porte l'émoji,
|
|
|
|
|
|
# l'étiquette du fil d'Ariane est nue — _todo_telemetry_tui traduit le
|
|
|
|
|
|
# dernier segment du chemin, et un émoji y détonnerait.
|
|
|
|
|
|
"Analyse - Odoo database analysis": {
|
|
|
|
|
|
"fr": "🔬 Analyse - Analyse de base de données Odoo",
|
|
|
|
|
|
"en": "🔬 Analyse - Odoo database analysis",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Analyse": {"fr": "Analyse", "en": "Analysis"},
|
|
|
|
|
|
"Analyse a database, without ever writing to it!": {
|
|
|
|
|
|
"fr": "Analyser une base, sans jamais y écrire !",
|
|
|
|
|
|
"en": "Analyse a database, without ever writing to it!",
|
|
|
|
|
|
},
|
|
|
|
|
|
# Les icônes du sous-menu Analyse. Elles sont réservées d'avance pour la
|
|
|
|
|
|
# famille entière — 🧱 index, 🎈 ballonnement, 📎 pièces jointes,
|
|
|
|
|
|
# 🔗 ir_model_data — pour qu'ajouter un outil ne demande pas d'en
|
|
|
|
|
|
# rechercher une libre, et que deux outils voisins ne se ressemblent pas.
|
|
|
|
|
|
"Structure": {"fr": "🗄 Structure", "en": "🗄 Structure"},
|
|
|
|
|
|
"Tables and database size": {
|
|
|
|
|
|
"fr": "📏 Tables et poids de la base",
|
|
|
|
|
|
"en": "📏 Tables and database size",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Customisation": {"fr": "🎭 Personnalisation", "en": "🎭 Customisation"},
|
|
|
|
|
|
"Customised views, website copies included": {
|
|
|
|
|
|
"fr": "🖼 Vues personnalisées, copies de site web comprises",
|
|
|
|
|
|
"en": "🖼 Customised views, website copies included",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Analysis failed: ": {
|
|
|
|
|
|
"fr": "L'analyse a échoué : ",
|
|
|
|
|
|
"en": "Analysis failed: ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Full list and JSON output:": {
|
|
|
|
|
|
"fr": "Liste complète et sortie JSON :",
|
|
|
|
|
|
"en": "Full list and JSON output:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"A backup holds no registry, so nothing was compared with the module"
|
|
|
|
|
|
" source. The classification above needs none; only the differences do."
|
|
|
|
|
|
" Restore it, or run this on the database.": {
|
|
|
|
|
|
"fr": "Une sauvegarde n'a pas de registre, donc rien n'a été comparé"
|
|
|
|
|
|
" avec la source du module. Le classement ci-dessus n'en a pas besoin"
|
|
|
|
|
|
" ; seuls les écarts en ont. Restaurez-la, ou lancez ceci sur la base.",
|
|
|
|
|
|
"en": "A backup holds no registry, so nothing was compared with the"
|
|
|
|
|
|
" module source. The classification above needs none; only the"
|
|
|
|
|
|
" differences do. Restore it, or run this on the database.",
|
|
|
|
|
|
},
|
[ADD] migration: keep the previous log when a run restarts
The progression log lives at one path and the next migration writes over it.
Restarting therefore erased everything known about the run before — which
steps passed, which modules were missing, how long each took — and that is
exactly what someone looks for after having had to restart.
Both answers that discard the progression now copy it aside first, named after
the origin database and the moment of the copy, so two attempts on the same
database do not cover each other and a file moved out of its folder still says
what it is. Partial replays keep the log, so they archive nothing.
It lands under private/, not beside the original: reinstalling wipes
.venv.erplibre/ and would take the history with it.
Two copies in the same second overwrote each other in silence — undoing the
very loss this prevents. The second one is numbered now, and the test that
had skipped that case asserts it.
Checked on the real 51 KB log from the VM: 18 states copied unchanged, the
name carries the database and the timestamp, and a log with no state at all is
not archived. 12 tests.
--- FR ---
Le journal de progression vit à un seul endroit et la migration suivante écrit
par-dessus. Recommencer effaçait donc tout ce qu'on savait de la tentative
précédente — quels paliers étaient passés, quels modules manquaient, combien
de temps chacun avait pris — soit exactement ce qu'on cherche après avoir dû
recommencer.
Les deux réponses qui jettent la progression la copient désormais d'abord,
sous un nom portant la base d'origine et l'instant de la copie, pour que deux
tentatives sur la même base ne se recouvrent pas et qu'un fichier sorti de son
dossier se décrive encore. Une reprise partielle garde le journal, donc
n'archive rien.
La copie va sous private/, pas à côté de l'originale : une réinstallation
efface .venv.erplibre/ et emporterait l'historique avec elle.
Deux copies dans la même seconde s'écrasaient en silence — annulant la perte
même qu'on évite. La seconde est numérotée, et le test qui sautait ce cas
l'affirme.
Vérifié sur le vrai journal de 51 Ko de la VM : 18 états copiés à l'identique,
le nom porte la base et l'horodatage, et un journal sans aucun état n'est pas
archivé. 12 tests.
Assisted-by: Claude Opus 5
2026-08-12 00:34:17 -04:00
|
|
|
|
"Could not archive the previous log": {
|
|
|
|
|
|
"fr": "Impossible d'archiver le journal précédent",
|
|
|
|
|
|
"en": "Could not archive the previous log",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Previous migration log kept in": {
|
|
|
|
|
|
"fr": "Journal de la migration précédente conservé dans",
|
|
|
|
|
|
"en": "Previous migration log kept in",
|
|
|
|
|
|
},
|
[FIX] migration: updating the addons early left step 2 « not started »
A database coming from an old version is updated before the neutralization —
the tool offers it at the very start. That work IS step 2, done earlier, but
it was only recorded as state_1_update_all while the screen reads state_2_*.
So the step stayed « not started » right after running.
The costly half was invisible. Step 2 skipped the work through a local
variable, which is False again on a resume: with only the old flag written,
resuming a migration re-ran update_addons_all on an already-updated database.
Hours, for nothing. The check now reads the written trace, not the session.
The screen says when it happened rather than just « done », since the moment
is exactly what raised the doubt. Logs written before this fix read correctly
without being touched — the early flag alone is recognised.
Checked on the real log from the VM: step 2 goes from « not started » to
« done early, before the neutralization », the four other steps unchanged.
10 tests; removing the fix makes the resume one fail.
--- FR ---
Une base venant d'une vieille version se met à jour avant la neutralisation —
l'outil le propose au tout début. Ce travail EST l'étape 2, faite plus tôt,
mais il n'était enregistré que sous state_1_update_all quand l'écran lit
state_2_*. L'étape restait donc « non démarrée » juste après avoir tourné.
La moitié coûteuse était invisible. L'étape 2 sautait le travail grâce à une
variable locale, remise à False à la reprise : avec seulement l'ancien drapeau
écrit, reprendre une migration relançait update_addons_all sur une base déjà à
jour. Des heures, pour rien. Le contrôle lit maintenant la trace écrite, pas
la session.
L'écran dit quand cela a eu lieu plutôt que « terminée », le moment étant
justement la source du doute. Les journaux écrits avant ce correctif se lisent
correctement sans être modifiés — le drapeau précoce seul est reconnu.
Vérifié sur le vrai journal de la VM : l'étape 2 passe de « non démarrée » à
« faite plus tôt, avant la neutralisation », les quatre autres inchangées.
10 tests ; retirer le correctif fait tomber celui de la reprise.
Assisted-by: Claude Opus 5
2026-08-12 00:43:16 -04:00
|
|
|
|
"done early, before the neutralization": {
|
|
|
|
|
|
"fr": "faite plus tôt, avant la neutralisation",
|
|
|
|
|
|
"en": "done early, before the neutralization",
|
|
|
|
|
|
},
|
[IMP] migration: say when the COW question is actually asked
An early check warns, hours before the bump, that a copy will break it. It
then says « arbitrate BEFORE launching the migration » and hands over a raw
UPDATE — and no question follows. Read on a resumed run it looks like the
question already went by and was skipped, which is exactly how it was read.
It now says the migration asks at the bump itself, and shows what each copy
holds before the answer. The manual UPDATE stays, but after the two commands
that do it reversibly: telling someone to hand-write SQL when --restore exists
is offering the sharper tool first.
--- FR ---
Un contrôle précoce prévient, des heures avant le palier, qu'une copie va le
casser. Il dit ensuite « arbitrez AVANT de lancer la migration » et livre un
UPDATE brut — et aucune question ne suit. Lu au cours d'une reprise, on croit
que la question est passée et a été sautée, ce qui est exactement la lecture
qui en a été faite.
Il dit maintenant que la migration pose la question au palier lui-même, en
montrant ce que chaque copie contient avant qu'on réponde. L'UPDATE manuel
reste, mais après les deux commandes qui le font de façon réversible : dire à
quelqu'un d'écrire du SQL à la main quand --restore existe, c'est tendre
l'outil le plus coupant en premier.
Assisted-by: Claude Opus 5
2026-08-12 01:36:47 -04:00
|
|
|
|
"compare each website copy with the module view it shadows": {
|
|
|
|
|
|
"fr": "comparer chaque copie de site web à la vue de module qu'elle"
|
|
|
|
|
|
" masque",
|
|
|
|
|
|
"en": "compare each website copy with the module view it shadows",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Compare the website copies with the view they shadow": {
|
|
|
|
|
|
"fr": "🪞 Comparer les copies de site web à la vue qu'elles masquent",
|
|
|
|
|
|
"en": "🪞 Compare the website copies with the view they shadow",
|
|
|
|
|
|
},
|
|
|
|
|
|
"website copies compared with their module view,": {
|
|
|
|
|
|
"fr": "copies de site web comparées à leur vue de module,",
|
|
|
|
|
|
"en": "website copies compared with their module view,",
|
|
|
|
|
|
},
|
|
|
|
|
|
"differ.": {"fr": "diffèrent.", "en": "differ."},
|
[ADD] migration: act on the COW copies when they are announced
Step 2 announced the copies the next bump will break, invited to
arbitrate, then moved on: the question came at the bump, tens of minutes
later. Yet looking writes nothing, and neutralizing here covers every bump
-- each bump database is a clone of this one.
The announcement is now a prompt: view what a copy holds, why it breaks,
full screen, or neutralize right away. Both prompts build their command in
one place, so they cannot drift apart.
Full screen means a real terminal: the viewer was started without one and
drew nothing, the pager exiting at once on a pipe.
--- FR ---
L'étape 2 annonçait les copies que le palier suivant casserait, invitait à
arbitrer, puis passait : la question venait au palier, des dizaines de
minutes plus tard. Or regarder n'écrit rien, et neutraliser ici vaut pour
tous les paliers — chaque base de palier est un clone de celle-ci.
L'annonce est désormais une invite : voir ce que porte une copie, pourquoi
elle casse, en plein écran, ou neutraliser tout de suite. Les deux invites
composent leur commande au même endroit, elles ne peuvent donc pas
diverger.
Le plein écran suppose un vrai terminal : la vue était lancée sans, et ne
dessinait rien, le pagineur sortant aussitôt sur un tube.
Assisted-by: Claude Opus 5
2026-08-13 03:12:09 -04:00
|
|
|
|
"What do you want to do with these COW copies?": {
|
|
|
|
|
|
"fr": "Que voulez-vous faire de ces copies COW ?",
|
|
|
|
|
|
"en": "What do you want to do with these COW copies?",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Enter = decide at the version bump": {
|
|
|
|
|
|
"fr": "Entrée = décider au palier de version",
|
|
|
|
|
|
"en": "Enter = decide at the version bump",
|
|
|
|
|
|
},
|
|
|
|
|
|
"what each copy holds": {
|
|
|
|
|
|
"fr": "ce que chaque copie porte",
|
|
|
|
|
|
"en": "what each copy holds",
|
|
|
|
|
|
},
|
|
|
|
|
|
"why it breaks": {
|
|
|
|
|
|
"fr": "pourquoi ça casse",
|
|
|
|
|
|
"en": "why it breaks",
|
|
|
|
|
|
},
|
|
|
|
|
|
"full screen": {
|
|
|
|
|
|
"fr": "plein écran",
|
|
|
|
|
|
"en": "full screen",
|
|
|
|
|
|
},
|
|
|
|
|
|
"neutralize now, reversible": {
|
|
|
|
|
|
"fr": "neutraliser maintenant, réversible",
|
|
|
|
|
|
"en": "neutralize now, reversible",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Undo with": {
|
|
|
|
|
|
"fr": "Annuler avec",
|
|
|
|
|
|
"en": "Undo with",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Nothing decided": {
|
|
|
|
|
|
"fr": "Rien de décidé",
|
|
|
|
|
|
"en": "Nothing decided",
|
|
|
|
|
|
},
|
|
|
|
|
|
"the migration will ask again at the version bump.": {
|
|
|
|
|
|
"fr": "la migration reposera la question au palier de version.",
|
|
|
|
|
|
"en": "the migration will ask again at the version bump.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Target version directory not found": {
|
|
|
|
|
|
"fr": "Répertoire de la version cible introuvable",
|
|
|
|
|
|
"en": "Target version directory not found",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No website COW view changes shape in": {
|
|
|
|
|
|
"fr": "Aucune vue COW de site ne change de forme dans",
|
|
|
|
|
|
"en": "No website COW view changes shape in",
|
|
|
|
|
|
},
|
|
|
|
|
|
"website COW view(s) will break when moving to": {
|
|
|
|
|
|
"fr": "vue(s) COW de site casseront au passage à",
|
|
|
|
|
|
"en": "website COW view(s) will break when moving to",
|
|
|
|
|
|
},
|
|
|
|
|
|
"the copy keeps an arch whose shape no longer matches what": {
|
|
|
|
|
|
"fr": "la copie garde une arch dont la forme ne correspond plus à ce que",
|
|
|
|
|
|
"en": "the copy keeps an arch whose shape no longer matches what",
|
|
|
|
|
|
},
|
|
|
|
|
|
"the target module view expects.": {
|
|
|
|
|
|
"fr": "la vue module cible attend.",
|
|
|
|
|
|
"en": "the target module view expects.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"The migration offers to neutralize them, and shows what": {
|
|
|
|
|
|
"fr": "La migration propose de les neutraliser, et montre ce que",
|
|
|
|
|
|
"en": "The migration offers to neutralize them, and shows what",
|
|
|
|
|
|
},
|
|
|
|
|
|
"each copy holds before you answer. To look now:": {
|
|
|
|
|
|
"fr": "chaque copie porte avant que vous répondiez. Pour regarder maintenant :",
|
|
|
|
|
|
"en": "each copy holds before you answer. To look now:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"reversible with --restore": {
|
|
|
|
|
|
"fr": "réversible avec --restore",
|
|
|
|
|
|
"en": "reversible with --restore",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Or by hand. To neutralize a copy, rename its key": {
|
|
|
|
|
|
"fr": "Ou à la main. Pour neutraliser une copie, renommer sa clé",
|
|
|
|
|
|
"en": "Or by hand. To neutralize a copy, rename its key",
|
|
|
|
|
|
},
|
|
|
|
|
|
"an unmatched key is never paired with the": {
|
|
|
|
|
|
"fr": "une clé sans correspondance n'est jamais appariée à la",
|
|
|
|
|
|
"en": "an unmatched key is never paired with the",
|
|
|
|
|
|
},
|
|
|
|
|
|
"module view, so the copy never receives the new": {
|
|
|
|
|
|
"fr": "vue module, la copie ne reçoit donc jamais le nouveau",
|
|
|
|
|
|
"en": "module view, so the copy never receives the new",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Setting active=false alone is NOT enough:": {
|
|
|
|
|
|
"fr": "Mettre active=false seul ne suffit PAS :",
|
|
|
|
|
|
"en": "Setting active=false alone is NOT enough:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"an inactive copy keeping the same key still shadows it.": {
|
|
|
|
|
|
"fr": "une copie inactive gardant la même clé la masque toujours.",
|
|
|
|
|
|
"en": "an inactive copy keeping the same key still shadows it.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"COW view(s) belong to a module absent from": {
|
|
|
|
|
|
"fr": "vue(s) COW appartiennent à un module absent de",
|
|
|
|
|
|
"en": "COW view(s) belong to a module absent from",
|
|
|
|
|
|
},
|
|
|
|
|
|
"COW view(s) are pages or records made in the website": {
|
|
|
|
|
|
"fr": "vue(s) COW sont des pages ou des enregistrements faits dans",
|
|
|
|
|
|
"en": "COW view(s) are pages or records made in the website",
|
|
|
|
|
|
},
|
|
|
|
|
|
"editor (no module view of that name): not at risk.": {
|
|
|
|
|
|
"fr": "l'éditeur de site (aucune vue module de ce nom) : hors de danger.",
|
|
|
|
|
|
"en": "editor (no module view of that name): not at risk.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Use -v to list them.": {
|
|
|
|
|
|
"fr": "Utiliser -v pour les lister.",
|
|
|
|
|
|
"en": "Use -v to list them.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No view with this prefix on": {
|
|
|
|
|
|
"fr": "Aucune vue avec ce préfixe sur",
|
|
|
|
|
|
"en": "No view with this prefix on",
|
|
|
|
|
|
},
|
|
|
|
|
|
"archived COW view(s) on": {
|
|
|
|
|
|
"fr": "vue(s) COW archivées sur",
|
|
|
|
|
|
"en": "archived COW view(s) on",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Their arch is intact. Restore them all with --restore,": {
|
|
|
|
|
|
"fr": "Leur arch est intacte. Toutes les restaurer avec --restore,",
|
|
|
|
|
|
"en": "Their arch is intact. Restore them all with --restore,",
|
|
|
|
|
|
},
|
|
|
|
|
|
"once the module view they shadow has the right shape.": {
|
|
|
|
|
|
"fr": "une fois que la vue module qu'elles masquent a la bonne forme.",
|
|
|
|
|
|
"en": "once the module view they shadow has the right shape.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"COW view(s) restored on": {
|
|
|
|
|
|
"fr": "vue(s) COW restaurées sur",
|
|
|
|
|
|
"en": "COW view(s) restored on",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No website COW view to neutralize.": {
|
|
|
|
|
|
"fr": "Aucune vue COW de site à neutraliser.",
|
|
|
|
|
|
"en": "No website COW view to neutralize.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"website COW view(s) would break the bump": {
|
|
|
|
|
|
"fr": "vue(s) COW de site casseraient le palier",
|
|
|
|
|
|
"en": "website COW view(s) would break the bump",
|
|
|
|
|
|
},
|
|
|
|
|
|
"to": {
|
|
|
|
|
|
"fr": "vers",
|
|
|
|
|
|
"en": "to",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Dry-run. Add --apply to rename their key to": {
|
|
|
|
|
|
"fr": "Simulation. Ajouter --apply pour renommer leur clé en",
|
|
|
|
|
|
"en": "Dry-run. Add --apply to rename their key to",
|
|
|
|
|
|
},
|
|
|
|
|
|
"and deactivate them. Reversible": {
|
|
|
|
|
|
"fr": "et les désactiver. Réversible",
|
|
|
|
|
|
"en": "and deactivate them. Reversible",
|
|
|
|
|
|
},
|
|
|
|
|
|
"with --restore; the arch stays in database.": {
|
|
|
|
|
|
"fr": "avec --restore ; l'arch reste en base.",
|
|
|
|
|
|
"en": "with --restore; the arch stays in database.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"COW view(s) neutralized (key prefixed with": {
|
|
|
|
|
|
"fr": "vue(s) COW neutralisées (clé préfixée par",
|
|
|
|
|
|
"en": "COW view(s) neutralized (key prefixed with",
|
|
|
|
|
|
},
|
|
|
|
|
|
"deactivated). The arch is kept as an": {
|
|
|
|
|
|
"fr": "désactivées). L'arch est conservée comme",
|
|
|
|
|
|
"en": "deactivated). The arch is kept as an",
|
|
|
|
|
|
},
|
|
|
|
|
|
"archive; use --restore to undo.": {
|
|
|
|
|
|
"fr": "archive ; utiliser --restore pour annuler.",
|
|
|
|
|
|
"en": "archive; use --restore to undo.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"target inherits, copy holds a standalone template": {
|
|
|
|
|
|
"fr": "la cible hérite, la copie porte un modèle autonome",
|
|
|
|
|
|
"en": "target inherits, copy holds a standalone template",
|
|
|
|
|
|
},
|
|
|
|
|
|
"target is a root view, copy holds inheritance specs": {
|
|
|
|
|
|
"fr": "la cible est une vue racine, la copie porte des specs d'héritage",
|
|
|
|
|
|
"en": "target is a root view, copy holds inheritance specs",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No module view carries this key, so there is nothing to": {
|
|
|
|
|
|
"fr": "Aucune vue module ne porte cette clé, il n'y a donc rien à",
|
|
|
|
|
|
"en": "No module view carries this key, so there is nothing to",
|
|
|
|
|
|
},
|
|
|
|
|
|
"compare: this copy is a page made in the website editor.": {
|
|
|
|
|
|
"fr": "comparer : cette copie est une page faite dans l'éditeur de site.",
|
|
|
|
|
|
"en": "compare: this copy is a page made in the website editor.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"The copy is identical to the module view.": {
|
|
|
|
|
|
"fr": "La copie est identique à la vue module.",
|
|
|
|
|
|
"en": "The copy is identical to the module view.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"line(s) added": {
|
|
|
|
|
|
"fr": "ligne(s) ajoutée(s)",
|
|
|
|
|
|
"en": "line(s) added",
|
|
|
|
|
|
},
|
|
|
|
|
|
"removed": {
|
|
|
|
|
|
"fr": "retirée(s)",
|
|
|
|
|
|
"en": "removed",
|
|
|
|
|
|
},
|
|
|
|
|
|
"this is what neutralizing gives up.": {
|
|
|
|
|
|
"fr": "c'est ce que la neutralisation abandonne.",
|
|
|
|
|
|
"en": "this is what neutralizing gives up.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"the module no longer declares this template": {
|
|
|
|
|
|
"fr": "le module ne déclare plus ce modèle",
|
|
|
|
|
|
"en": "the module no longer declares this template",
|
|
|
|
|
|
},
|
|
|
|
|
|
"inheritance specs (needs inherit_id)": {
|
|
|
|
|
|
"fr": "des specs d'héritage (exige inherit_id)",
|
|
|
|
|
|
"en": "inheritance specs (needs inherit_id)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"a standalone template": {
|
|
|
|
|
|
"fr": "un modèle autonome",
|
|
|
|
|
|
"en": "a standalone template",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Odoo changing the shape of its own template is NOT the": {
|
|
|
|
|
|
"fr": "Qu'Odoo change la forme de son propre modèle n'est PAS le",
|
|
|
|
|
|
"en": "Odoo changing the shape of its own template is NOT the",
|
|
|
|
|
|
},
|
|
|
|
|
|
"problem: on a database without a copy, the module upgrade": {
|
|
|
|
|
|
"fr": "problème : sur une base sans copie, la mise à jour du module",
|
|
|
|
|
|
"en": "problem: on a database without a copy, the module upgrade",
|
|
|
|
|
|
},
|
|
|
|
|
|
"rewrites the view and nothing breaks. It breaks here because": {
|
|
|
|
|
|
"fr": "réécrit la vue et rien ne casse. Ça casse ici parce qu'une",
|
|
|
|
|
|
"en": "rewrites the view and nothing breaks. It breaks here because",
|
|
|
|
|
|
},
|
|
|
|
|
|
"a COPY exists and froze the old shape — the copy follows the": {
|
|
|
|
|
|
"fr": "COPIE existe et a figé l'ancienne forme — la copie suit le",
|
|
|
|
|
|
"en": "a COPY exists and froze the old shape — the copy follows the",
|
|
|
|
|
|
},
|
|
|
|
|
|
"module and becomes an extension, while still holding a": {
|
|
|
|
|
|
"fr": "module et devient une extension, tout en portant encore un",
|
|
|
|
|
|
"en": "module and becomes an extension, while still holding a",
|
|
|
|
|
|
},
|
|
|
|
|
|
"standalone template. Odoo then applies that template as an": {
|
|
|
|
|
|
"fr": "modèle autonome. Odoo applique alors ce modèle comme une",
|
|
|
|
|
|
"en": "standalone template. Odoo then applies that template as an",
|
|
|
|
|
|
},
|
|
|
|
|
|
"inheritance spec and stops on « cannot be located in parent": {
|
|
|
|
|
|
"fr": "spec d'héritage et s'arrête sur « cannot be located in parent",
|
|
|
|
|
|
"en": "inheritance spec and stops on « cannot be located in parent",
|
|
|
|
|
|
},
|
|
|
|
|
|
"view ».": {
|
|
|
|
|
|
"fr": "view ».",
|
|
|
|
|
|
"en": "view ».",
|
|
|
|
|
|
},
|
|
|
|
|
|
"This copy has no module view of that name: it is a page": {
|
|
|
|
|
|
"fr": "Cette copie n'a aucune vue module de ce nom : c'est une page",
|
|
|
|
|
|
"en": "This copy has no module view of that name: it is a page",
|
|
|
|
|
|
},
|
|
|
|
|
|
"made in the website editor, and nothing holds its content.": {
|
|
|
|
|
|
"fr": "faite dans l'éditeur de site, et rien d'autre ne porte son contenu.",
|
|
|
|
|
|
"en": "made in the website editor, and nothing holds its content.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"This copy is IDENTICAL to the module view it shadows: it": {
|
|
|
|
|
|
"fr": "Cette copie est IDENTIQUE à la vue module qu'elle masque : elle",
|
|
|
|
|
|
"en": "This copy is IDENTICAL to the module view it shadows: it",
|
|
|
|
|
|
},
|
|
|
|
|
|
"holds no customization, so neutralizing it loses nothing.": {
|
|
|
|
|
|
"fr": "ne porte aucune personnalisation, la neutraliser ne perd rien.",
|
|
|
|
|
|
"en": "holds no customization, so neutralizing it loses nothing.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"This copy differs from the module view by": {
|
|
|
|
|
|
"fr": "Cette copie diffère de la vue module de",
|
|
|
|
|
|
"en": "This copy differs from the module view by",
|
|
|
|
|
|
},
|
|
|
|
|
|
"line(s):": {
|
|
|
|
|
|
"fr": "ligne(s) :",
|
|
|
|
|
|
"en": "line(s):",
|
|
|
|
|
|
},
|
|
|
|
|
|
"that is the customization, and all neutralizing gives up.": {
|
|
|
|
|
|
"fr": "c'est la personnalisation, et tout ce que la neutralisation abandonne.",
|
|
|
|
|
|
"en": "that is the customization, and all neutralizing gives up.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Run without --shape to read it.": {
|
|
|
|
|
|
"fr": "Lancer sans --shape pour la lire.",
|
|
|
|
|
|
"en": "Run without --shape to read it.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No website COW view is at risk.": {
|
|
|
|
|
|
"fr": "Aucune vue COW de site n'est en danger.",
|
|
|
|
|
|
"en": "No website COW view is at risk.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No COW copy has drifted from its module view.": {
|
|
|
|
|
|
"fr": "Aucune copie COW n'a dérivé de sa vue module.",
|
|
|
|
|
|
"en": "No COW copy has drifted from its module view.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"COW copy(ies) drifted from their module": {
|
|
|
|
|
|
"fr": "copie(s) COW ont dérivé de leur vue",
|
|
|
|
|
|
"en": "COW copy(ies) drifted from their module",
|
|
|
|
|
|
},
|
|
|
|
|
|
"view in": {
|
|
|
|
|
|
"fr": "module dans",
|
|
|
|
|
|
"en": "view in",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Odoo surfaces this when the module view is rewritten (a": {
|
|
|
|
|
|
"fr": "Odoo le fait apparaître quand la vue module est réécrite (un",
|
|
|
|
|
|
"en": "Odoo surfaces this when the module view is rewritten (a",
|
|
|
|
|
|
},
|
|
|
|
|
|
"version bump) or when the page is rendered.": {
|
|
|
|
|
|
"fr": "palier de version) ou quand la page est rendue.",
|
|
|
|
|
|
"en": "version bump) or when the page is rendered.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"NO module view with this key": {
|
|
|
|
|
|
"fr": "AUCUNE vue module avec cette clé",
|
|
|
|
|
|
"en": "NO module view with this key",
|
|
|
|
|
|
},
|
|
|
|
|
|
"child": {
|
|
|
|
|
|
"fr": "enfant",
|
|
|
|
|
|
"en": "child",
|
|
|
|
|
|
},
|
|
|
|
|
|
"cannot apply": {
|
|
|
|
|
|
"fr": "ne peut s'appliquer",
|
|
|
|
|
|
"en": "cannot apply",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Nothing changed. Re-run with --reset <key> --apply to": {
|
|
|
|
|
|
"fr": "Rien n'a changé. Relancer avec --reset <clé> --apply pour",
|
|
|
|
|
|
"en": "Nothing changed. Re-run with --reset <key> --apply to",
|
|
|
|
|
|
},
|
|
|
|
|
|
"reset a copy onto its module view.": {
|
|
|
|
|
|
"fr": "réinitialiser une copie sur sa vue module.",
|
|
|
|
|
|
"en": "reset a copy onto its module view.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"no module view to reset onto, skipped.": {
|
|
|
|
|
|
"fr": "aucune vue module sur laquelle réinitialiser, ignorée.",
|
|
|
|
|
|
"en": "no module view to reset onto, skipped.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"dry-run": {
|
|
|
|
|
|
"fr": "simulation",
|
|
|
|
|
|
"en": "dry-run",
|
|
|
|
|
|
},
|
|
|
|
|
|
"would reset": {
|
|
|
|
|
|
"fr": "réinitialiserait",
|
|
|
|
|
|
"en": "would reset",
|
|
|
|
|
|
},
|
|
|
|
|
|
"onto": {
|
|
|
|
|
|
"fr": "sur",
|
|
|
|
|
|
"en": "onto",
|
|
|
|
|
|
},
|
|
|
|
|
|
"reset": {
|
|
|
|
|
|
"fr": "réinitialisé",
|
|
|
|
|
|
"en": "reset",
|
|
|
|
|
|
},
|
|
|
|
|
|
"previous arch saved to": {
|
|
|
|
|
|
"fr": "arch précédente sauvegardée dans",
|
|
|
|
|
|
"en": "previous arch saved to",
|
|
|
|
|
|
},
|
|
|
|
|
|
"copy(ies) reset. Re-apply any real customisation": {
|
|
|
|
|
|
"fr": "copie(s) réinitialisée(s). Réappliquer toute vraie personnalisation",
|
|
|
|
|
|
"en": "copy(ies) reset. Re-apply any real customisation",
|
|
|
|
|
|
},
|
|
|
|
|
|
"as an INHERITING view, not a copy, so it cannot go stale": {
|
|
|
|
|
|
"fr": "comme une vue HÉRITANTE, pas une copie, pour qu'elle ne périme plus",
|
|
|
|
|
|
"en": "as an INHERITING view, not a copy, so it cannot go stale",
|
|
|
|
|
|
},
|
|
|
|
|
|
"again.": {
|
|
|
|
|
|
"fr": "à l'avenir.",
|
|
|
|
|
|
"en": "again.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"lxml is required to resolve the xpath expressions.": {
|
|
|
|
|
|
"fr": "lxml est requis pour résoudre les expressions xpath.",
|
|
|
|
|
|
"en": "lxml is required to resolve the xpath expressions.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"COW view(s) recorded in": {
|
|
|
|
|
|
"fr": "vue(s) COW enregistrées dans",
|
|
|
|
|
|
"en": "COW view(s) recorded in",
|
|
|
|
|
|
},
|
|
|
|
|
|
"COW view(s) disappeared": {
|
|
|
|
|
|
"fr": "vue(s) COW ont disparu",
|
|
|
|
|
|
"en": "COW view(s) disappeared",
|
|
|
|
|
|
},
|
|
|
|
|
|
"COW view(s) appeared": {
|
|
|
|
|
|
"fr": "vue(s) COW sont apparues",
|
|
|
|
|
|
"en": "COW view(s) appeared",
|
|
|
|
|
|
},
|
|
|
|
|
|
"arch rewritten": {
|
|
|
|
|
|
"fr": "arch réécrite",
|
|
|
|
|
|
"en": "arch rewritten",
|
|
|
|
|
|
},
|
|
|
|
|
|
"COW view(s) changed": {
|
|
|
|
|
|
"fr": "vue(s) COW ont changé",
|
|
|
|
|
|
"en": "COW view(s) changed",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No change on the website COW views.": {
|
|
|
|
|
|
"fr": "Aucun changement sur les vues COW de site.",
|
|
|
|
|
|
"en": "No change on the website COW views.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Welcome to the Odoo database upgrade with ERPLibre": {
|
|
|
|
|
|
"fr": "Bienvenue dans la mise à niveau de base de données Odoo avec ERPLibre",
|
|
|
|
|
|
"en": "Welcome to the Odoo database upgrade with ERPLibre",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Select the zip file of your database backup.": {
|
|
|
|
|
|
"fr": "Sélectionnez le fichier zip de votre sauvegarde de base de données.",
|
|
|
|
|
|
"en": "Select the zip file of your database backup.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Give the path of the file, or empty to use a file": {
|
|
|
|
|
|
"fr": "Donnez le chemin du fichier, ou rien pour utiliser un",
|
|
|
|
|
|
"en": "Give the path of the file, or empty to use a file",
|
|
|
|
|
|
},
|
|
|
|
|
|
"browser, or type": {
|
|
|
|
|
|
"fr": "navigateur de fichiers, ou tapez",
|
|
|
|
|
|
"en": "browser, or type",
|
|
|
|
|
|
},
|
|
|
|
|
|
"to download from production": {
|
|
|
|
|
|
"fr": "pour télécharger depuis la production",
|
|
|
|
|
|
"en": "to download from production",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Cannot retrieve the database from remote, please retry the migration.": {
|
|
|
|
|
|
"fr": "Impossible de récupérer la base distante, veuillez relancer la migration.",
|
|
|
|
|
|
"en": "Cannot retrieve the database from remote, please retry the migration.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Open file": {
|
|
|
|
|
|
"fr": "Ouverture du fichier",
|
|
|
|
|
|
"en": "Open file",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Detected Odoo CE version": {
|
|
|
|
|
|
"fr": "Version Odoo CE détectée",
|
|
|
|
|
|
"en": "Detected Odoo CE version",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Which version do you want to upgrade to?": {
|
|
|
|
|
|
"fr": "Vers quelle version voulez-vous migrer ?",
|
|
|
|
|
|
"en": "Which version do you want to upgrade to?",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Search the Odoo version": {
|
|
|
|
|
|
"fr": "Recherche de la version Odoo",
|
|
|
|
|
|
"en": "Search the Odoo version",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Find the right environment, read the .zip file": {
|
|
|
|
|
|
"fr": "Recherche du bon environnement, lecture du fichier .zip",
|
|
|
|
|
|
"en": "Find the right environment, read the .zip file",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Would you like to install": {
|
|
|
|
|
|
"fr": "Voulez-vous installer",
|
|
|
|
|
|
"en": "Would you like to install",
|
|
|
|
|
|
},
|
|
|
|
|
|
"You need an installed system before": {
|
|
|
|
|
|
"fr": "Il faut un système installé avant de",
|
|
|
|
|
|
"en": "You need an installed system before",
|
|
|
|
|
|
},
|
|
|
|
|
|
"continuing, check your Odoo installation.": {
|
|
|
|
|
|
"fr": "continuer, vérifiez votre installation Odoo.",
|
|
|
|
|
|
"en": "continuing, check your Odoo installation.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Install the environment if missing": {
|
|
|
|
|
|
"fr": "Installation de l'environnement s'il manque",
|
|
|
|
|
|
"en": "Install the environment if missing",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Search missing modules": {
|
|
|
|
|
|
"fr": "Recherche des modules manquants",
|
|
|
|
|
|
"en": "Search missing modules",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Install the missing modules, search for them or": {
|
|
|
|
|
|
"fr": "Installer les modules manquants, les chercher ou",
|
|
|
|
|
|
"en": "Install the missing modules, search for them or",
|
|
|
|
|
|
},
|
|
|
|
|
|
"ask to uninstall them (can break data)": {
|
|
|
|
|
|
"fr": "demander à les désinstaller (peut casser des données)",
|
|
|
|
|
|
"en": "ask to uninstall them (can break data)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Cannot set up the environment to begin.": {
|
|
|
|
|
|
"fr": "Impossible de préparer l'environnement pour commencer.",
|
|
|
|
|
|
"en": "Cannot set up the environment to begin.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Missing module": {
|
|
|
|
|
|
"fr": "Module manquant",
|
|
|
|
|
|
"en": "Missing module",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Duplicate module": {
|
|
|
|
|
|
"fr": "Module en double",
|
|
|
|
|
|
"en": "Duplicate module",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Missing or duplicate module detected at init,": {
|
|
|
|
|
|
"fr": "Module manquant ou en double détecté à l'init,",
|
|
|
|
|
|
"en": "Missing or duplicate module detected at init,",
|
|
|
|
|
|
},
|
|
|
|
|
|
"do you want to continue?": {
|
|
|
|
|
|
"fr": "voulez-vous continuer ?",
|
|
|
|
|
|
"en": "do you want to continue?",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Missing or duplicate module detected at init, do": {
|
|
|
|
|
|
"fr": "Module manquant ou en double détecté à l'init,",
|
|
|
|
|
|
"en": "Missing or duplicate module detected at init, do",
|
|
|
|
|
|
},
|
|
|
|
|
|
"you want to continue?": {
|
|
|
|
|
|
"fr": "voulez-vous continuer ?",
|
|
|
|
|
|
"en": "you want to continue?",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Which database name do you want to work with?": {
|
|
|
|
|
|
"fr": "Avec quel nom de base voulez-vous travailler ?",
|
|
|
|
|
|
"en": "Which database name do you want to work with?",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Ignore the database neutralization": {
|
|
|
|
|
|
"fr": "Ignorer la neutralisation de la base",
|
|
|
|
|
|
"en": "Ignore the database neutralization",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Working with database": {
|
|
|
|
|
|
"fr": "Travail sur la base",
|
|
|
|
|
|
"en": "Working with database",
|
|
|
|
|
|
},
|
|
|
|
|
|
"will copy": {
|
|
|
|
|
|
"fr": "va copier",
|
|
|
|
|
|
"en": "will copy",
|
|
|
|
|
|
},
|
|
|
|
|
|
"a file already exists, do you want to": {
|
|
|
|
|
|
"fr": "un fichier existe déjà, voulez-vous",
|
|
|
|
|
|
"en": "a file already exists, do you want to",
|
|
|
|
|
|
},
|
|
|
|
|
|
"continue?": {
|
|
|
|
|
|
"fr": "continuer ?",
|
|
|
|
|
|
"en": "continue?",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Restore the database": {
|
|
|
|
|
|
"fr": "Restauration de la base",
|
|
|
|
|
|
"en": "Restore the database",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Update all addons before neutralizing (already": {
|
|
|
|
|
|
"fr": "Mettre à jour tous les modules avant la neutralisation (déjà",
|
|
|
|
|
|
"en": "Update all addons before neutralizing (already",
|
|
|
|
|
|
},
|
|
|
|
|
|
"neutralized by Odoo if supported)": {
|
|
|
|
|
|
"fr": "neutralisée par Odoo si pris en charge)",
|
|
|
|
|
|
"en": "neutralized by Odoo if supported)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Do you need to upgrade before neutralizing the": {
|
|
|
|
|
|
"fr": "Faut-il mettre à jour avant de neutraliser la",
|
|
|
|
|
|
"en": "Do you need to upgrade before neutralizing the",
|
|
|
|
|
|
},
|
|
|
|
|
|
"database? Press enter to ignore": {
|
|
|
|
|
|
"fr": "base ? Entrée pour ignorer",
|
|
|
|
|
|
"en": "database? Press enter to ignore",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Update the database before neutralizing, by module": {
|
|
|
|
|
|
"fr": "Mise à jour de la base avant neutralisation, par module",
|
|
|
|
|
|
"en": "Update the database before neutralizing, by module",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Neutralize the database": {
|
|
|
|
|
|
"fr": "Neutralisation de la base",
|
|
|
|
|
|
"en": "Neutralize the database",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Modules to uninstall before the migration": {
|
|
|
|
|
|
"fr": "Modules à désinstaller avant la migration",
|
|
|
|
|
|
"en": "Modules to uninstall before the migration",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Uninstall modules": {
|
|
|
|
|
|
"fr": "Désinstallation des modules",
|
|
|
|
|
|
"en": "Uninstall modules",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Install modules": {
|
|
|
|
|
|
"fr": "Installation des modules",
|
|
|
|
|
|
"en": "Install modules",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Go to Settings / Technical / Cleanup... / Purge and": {
|
|
|
|
|
|
"fr": "Aller dans Configuration / Technique / Nettoyage... / Purger et",
|
|
|
|
|
|
"en": "Go to Settings / Technical / Cleanup... / Purge and",
|
|
|
|
|
|
},
|
|
|
|
|
|
"purge the obsolete modules": {
|
|
|
|
|
|
"fr": "purger les modules obsolètes",
|
|
|
|
|
|
"en": "purge the obsolete modules",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Press enter to continue step 3": {
|
|
|
|
|
|
"fr": "Entrée pour continuer l'étape 3",
|
|
|
|
|
|
"en": "Press enter to continue step 3",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Inspect zip": {
|
|
|
|
|
|
"fr": "Inspection du zip",
|
|
|
|
|
|
"en": "Inspect zip",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Import database from zip": {
|
|
|
|
|
|
"fr": "Import de la base depuis le zip",
|
|
|
|
|
|
"en": "Import database from zip",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Succeed update all addons": {
|
|
|
|
|
|
"fr": "Mise à jour de tous les modules",
|
|
|
|
|
|
"en": "Succeed update all addons",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Clean up database before data migration": {
|
|
|
|
|
|
"fr": "Nettoyage de la base avant la migration des données",
|
|
|
|
|
|
"en": "Clean up database before data migration",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Upgrade version with OpenUpgrade": {
|
|
|
|
|
|
"fr": "Montée de version avec OpenUpgrade",
|
|
|
|
|
|
"en": "Upgrade version with OpenUpgrade",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Cleaning up database after upgrade": {
|
|
|
|
|
|
"fr": "Nettoyage de la base après la montée de version",
|
|
|
|
|
|
"en": "Cleaning up database after upgrade",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Migration finished": {
|
|
|
|
|
|
"fr": "Migration terminée",
|
|
|
|
|
|
"en": "Migration finished",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Clone to Odoo": {
|
|
|
|
|
|
"fr": "Clonage vers Odoo",
|
|
|
|
|
|
"en": "Clone to Odoo",
|
|
|
|
|
|
},
|
|
|
|
|
|
"FAILED (status": {
|
|
|
|
|
|
"fr": "ÉCHEC (statut",
|
|
|
|
|
|
"en": "FAILED (status",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Stopping:": {
|
|
|
|
|
|
"fr": "Arrêt :",
|
|
|
|
|
|
"en": "Stopping:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"is not usable.": {
|
|
|
|
|
|
"fr": "n'est pas utilisable.",
|
|
|
|
|
|
"en": "is not usable.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Clone Odoo": {
|
|
|
|
|
|
"fr": "Clonage Odoo",
|
|
|
|
|
|
"en": "Clone Odoo",
|
|
|
|
|
|
},
|
|
|
|
|
|
"nothing": {
|
|
|
|
|
|
"fr": "rien",
|
|
|
|
|
|
"en": "nothing",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Switch Odoo": {
|
|
|
|
|
|
"fr": "Bascule Odoo",
|
|
|
|
|
|
"en": "Switch Odoo",
|
|
|
|
|
|
},
|
|
|
|
|
|
"done with update": {
|
|
|
|
|
|
"fr": "terminée avec mise à jour",
|
|
|
|
|
|
"en": "done with update",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Module upgrade Odoo": {
|
|
|
|
|
|
"fr": "Mise à jour des modules Odoo",
|
|
|
|
|
|
"en": "Module upgrade Odoo",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Database upgrade Odoo": {
|
|
|
|
|
|
"fr": "Migration de la base Odoo",
|
|
|
|
|
|
"en": "Database upgrade Odoo",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Modules to uninstall before Odoo": {
|
|
|
|
|
|
"fr": "Modules à désinstaller avant Odoo",
|
|
|
|
|
|
"en": "Modules to uninstall before Odoo",
|
|
|
|
|
|
},
|
|
|
|
|
|
"to redo the command": {
|
|
|
|
|
|
"fr": "pour refaire la commande",
|
|
|
|
|
|
"en": "to redo the command",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Error detected, press enter to continue or": {
|
|
|
|
|
|
"fr": "Erreur détectée, Entrée pour continuer ou",
|
|
|
|
|
|
"en": "Error detected, press enter to continue or",
|
|
|
|
|
|
},
|
|
|
|
|
|
"to stop": {
|
|
|
|
|
|
"fr": "pour arrêter",
|
|
|
|
|
|
"en": "to stop",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Clone done for Odoo": {
|
|
|
|
|
|
"fr": "Clonage terminé pour Odoo",
|
|
|
|
|
|
"en": "Clone done for Odoo",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Clone already done for Odoo": {
|
|
|
|
|
|
"fr": "Clonage déjà fait pour Odoo",
|
|
|
|
|
|
"en": "Clone already done for Odoo",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Switch already done for Odoo": {
|
|
|
|
|
|
"fr": "Bascule déjà faite pour Odoo",
|
|
|
|
|
|
"en": "Switch already done for Odoo",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Switch done with update for Odoo": {
|
|
|
|
|
|
"fr": "Bascule terminée avec mise à jour pour Odoo",
|
|
|
|
|
|
"en": "Switch done with update for Odoo",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Module upgrade done for Odoo": {
|
|
|
|
|
|
"fr": "Mise à jour des modules terminée pour Odoo",
|
|
|
|
|
|
"en": "Module upgrade done for Odoo",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Module upgrade already done for Odoo": {
|
|
|
|
|
|
"fr": "Mise à jour des modules déjà faite pour Odoo",
|
|
|
|
|
|
"en": "Module upgrade already done for Odoo",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Database upgrade done for Odoo": {
|
|
|
|
|
|
"fr": "Migration de la base terminée pour Odoo",
|
|
|
|
|
|
"en": "Database upgrade done for Odoo",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Database upgrade already done for Odoo": {
|
|
|
|
|
|
"fr": "Migration de la base déjà faite pour Odoo",
|
|
|
|
|
|
"en": "Database upgrade already done for Odoo",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Cloning to Odoo": {
|
|
|
|
|
|
"fr": "Clonage vers Odoo",
|
|
|
|
|
|
"en": "Cloning to Odoo",
|
|
|
|
|
|
},
|
|
|
|
|
|
"from": {
|
|
|
|
|
|
"fr": "depuis",
|
|
|
|
|
|
"en": "from",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Duplicate module in Odoo": {
|
|
|
|
|
|
"fr": "Module en double dans Odoo",
|
|
|
|
|
|
"en": "Duplicate module in Odoo",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Duplicate module error detected, handle it": {
|
|
|
|
|
|
"fr": "Erreur de module en double détectée, la traiter",
|
|
|
|
|
|
"en": "Duplicate module error detected, handle it",
|
|
|
|
|
|
},
|
|
|
|
|
|
"manually then press enter to continue.": {
|
|
|
|
|
|
"fr": "manuellement puis Entrée pour continuer.",
|
|
|
|
|
|
"en": "manually then press enter to continue.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Missing module error detected, missing in": {
|
|
|
|
|
|
"fr": "Erreur de module manquant détectée, manquant dans",
|
|
|
|
|
|
"en": "Missing module error detected, missing in",
|
|
|
|
|
|
},
|
|
|
|
|
|
"All of the list above": {
|
|
|
|
|
|
"fr": "Toute la liste ci-dessus",
|
|
|
|
|
|
"en": "All of the list above",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Add an extra custom one": {
|
|
|
|
|
|
"fr": "Ajouter un module personnalisé",
|
|
|
|
|
|
"en": "Add an extra custom one",
|
|
|
|
|
|
},
|
|
|
|
|
|
"List the missing modules to delete,": {
|
|
|
|
|
|
"fr": "Énumérer les modules manquants à supprimer,",
|
|
|
|
|
|
"en": "List the missing modules to delete,",
|
|
|
|
|
|
},
|
|
|
|
|
|
"separated by commas. The others will be": {
|
|
|
|
|
|
"fr": "séparés par des virgules. Les autres seront",
|
|
|
|
|
|
"en": "separated by commas. The others will be",
|
|
|
|
|
|
},
|
|
|
|
|
|
"migrated": {
|
|
|
|
|
|
"fr": "migrés",
|
|
|
|
|
|
"en": "migrated",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Migration fix for Odoo": {
|
|
|
|
|
|
"fr": "Correctif de migration pour Odoo",
|
|
|
|
|
|
"en": "Migration fix for Odoo",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Migration fix done for Odoo": {
|
|
|
|
|
|
"fr": "Correctif de migration terminé pour Odoo",
|
|
|
|
|
|
"en": "Migration fix done for Odoo",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No migration fix to run for Odoo": {
|
|
|
|
|
|
"fr": "Aucun correctif de migration à exécuter pour Odoo",
|
|
|
|
|
|
"en": "No migration fix to run for Odoo",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Migration fix already done for Odoo": {
|
|
|
|
|
|
"fr": "Correctif de migration déjà fait pour Odoo",
|
|
|
|
|
|
"en": "Migration fix already done for Odoo",
|
|
|
|
|
|
},
|
|
|
|
|
|
"List the module names to delete,": {
|
|
|
|
|
|
"fr": "Énumérer les noms de modules à supprimer,",
|
|
|
|
|
|
"en": "List the module names to delete,",
|
|
|
|
|
|
},
|
|
|
|
|
|
"separated by commas": {
|
|
|
|
|
|
"fr": "séparés par des virgules",
|
|
|
|
|
|
"en": "separated by commas",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Missing module error": {
|
|
|
|
|
|
"fr": "Erreur de module manquant",
|
|
|
|
|
|
"en": "Missing module error",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Duplicate module error": {
|
|
|
|
|
|
"fr": "Erreur de module en double",
|
|
|
|
|
|
"en": "Duplicate module error",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Module error": {
|
|
|
|
|
|
"fr": "Erreur de module",
|
|
|
|
|
|
"en": "Module error",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Please validate the commits after the code": {
|
|
|
|
|
|
"fr": "Veuillez valider les commits après la migration",
|
|
|
|
|
|
"en": "Please validate the commits after the code",
|
|
|
|
|
|
},
|
|
|
|
|
|
"migration.": {
|
|
|
|
|
|
"fr": "du code.",
|
|
|
|
|
|
"en": "migration.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"To show the repo status": {
|
|
|
|
|
|
"fr": "Pour afficher l'état des dépôts",
|
|
|
|
|
|
"en": "To show the repo status",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Please validate this path in config.conf": {
|
|
|
|
|
|
"fr": "Veuillez valider ce chemin dans config.conf",
|
|
|
|
|
|
"en": "Please validate this path in config.conf",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Database migration to Odoo": {
|
|
|
|
|
|
"fr": "Migration de la base vers Odoo",
|
|
|
|
|
|
"en": "Database migration to Odoo",
|
|
|
|
|
|
},
|
|
|
|
|
|
"is now half migrated: replaying the command on": {
|
|
|
|
|
|
"fr": "est maintenant à moitié migrée : rejouer la commande dessus",
|
|
|
|
|
|
"en": "is now half migrated: replaying the command on",
|
|
|
|
|
|
},
|
|
|
|
|
|
"it would never recover, so it is NOT offered.": {
|
|
|
|
|
|
"fr": "ne s'en remettrait jamais, ce n'est donc PAS proposé.",
|
|
|
|
|
|
"en": "it would never recover, so it is NOT offered.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"The clone step has been reset. Fix the": {
|
|
|
|
|
|
"fr": "L'étape de clonage a été réinitialisée. Corrigez la",
|
|
|
|
|
|
"en": "The clone step has been reset. Fix the",
|
|
|
|
|
|
},
|
|
|
|
|
|
"cause, then relaunch the migration and answer": {
|
|
|
|
|
|
"fr": "cause, puis relancez la migration et répondez",
|
|
|
|
|
|
"en": "cause, then relaunch the migration and answer",
|
|
|
|
|
|
},
|
|
|
|
|
|
"continue": {
|
|
|
|
|
|
"fr": "continuer",
|
|
|
|
|
|
"en": "continue",
|
|
|
|
|
|
},
|
|
|
|
|
|
"will be dropped and rebuilt from the previous": {
|
|
|
|
|
|
"fr": "sera supprimée et reconstruite depuis la version",
|
|
|
|
|
|
"en": "will be dropped and rebuilt from the previous",
|
|
|
|
|
|
},
|
|
|
|
|
|
"version before retrying.": {
|
|
|
|
|
|
"fr": "précédente avant de réessayer.",
|
|
|
|
|
|
"en": "version before retrying.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Do you want to upgrade all": {
|
|
|
|
|
|
"fr": "Voulez-vous tout mettre à jour",
|
|
|
|
|
|
"en": "Do you want to upgrade all",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Press y/Y to upgrade all addons of the": {
|
|
|
|
|
|
"fr": "y/Y pour mettre à jour tous les modules de la",
|
|
|
|
|
|
"en": "Press y/Y to upgrade all addons of the",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Open the server with Selenium": {
|
|
|
|
|
|
"fr": "Ouvrir le serveur avec Selenium",
|
|
|
|
|
|
"en": "Open the server with Selenium",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Do you want to test this upgrade? Choose": {
|
|
|
|
|
|
"fr": "Voulez-vous tester cette migration ? Choisissez",
|
|
|
|
|
|
"en": "Do you want to test this upgrade? Choose",
|
|
|
|
|
|
},
|
|
|
|
|
|
"or press enter to ignore it": {
|
|
|
|
|
|
"fr": "ou Entrée pour ignorer",
|
|
|
|
|
|
"en": "or press enter to ignore it",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Press enter to continue": {
|
|
|
|
|
|
"fr": "Entrée pour continuer",
|
|
|
|
|
|
"en": "Press enter to continue",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Re-update i18n, purge the data and the tables": {
|
|
|
|
|
|
"fr": "Remise à jour de l'i18n, purge des données et des tables",
|
|
|
|
|
|
"en": "Re-update i18n, purge the data and the tables",
|
|
|
|
|
|
},
|
|
|
|
|
|
"except mail_test and mail_test_full": {
|
|
|
|
|
|
"fr": "sauf mail_test et mail_test_full",
|
|
|
|
|
|
"en": "except mail_test and mail_test_full",
|
|
|
|
|
|
},
|
|
|
|
|
|
"A backup can be created": {
|
|
|
|
|
|
"fr": "Une sauvegarde peut être créée",
|
|
|
|
|
|
"en": "A backup can be created",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Press y/Y or type filename.zip to export, or": {
|
|
|
|
|
|
"fr": "y/Y ou un nom fichier.zip pour exporter, ou",
|
|
|
|
|
|
"en": "Press y/Y or type filename.zip to export, or",
|
|
|
|
|
|
},
|
|
|
|
|
|
"enter to continue": {
|
|
|
|
|
|
"fr": "Entrée pour continuer",
|
|
|
|
|
|
"en": "enter to continue",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Test the migration, press y/Y": {
|
|
|
|
|
|
"fr": "Tester la migration, y/Y",
|
|
|
|
|
|
"en": "Test the migration, press y/Y",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Documentation for this version": {
|
|
|
|
|
|
"fr": "Documentation de cette version",
|
|
|
|
|
|
"en": "Documentation for this version",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Command not found": {
|
|
|
|
|
|
"fr": "Commande non trouvée",
|
|
|
|
|
|
"en": "Command not found",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Execute command": {
|
|
|
|
|
|
"fr": "Exécution de la commande",
|
|
|
|
|
|
"en": "Execute command",
|
|
|
|
|
|
},
|
2026-08-13 04:21:24 -04:00
|
|
|
|
"Textual is missing from this interpreter:": {
|
|
|
|
|
|
"fr": "Textual manque à cet interpréteur :",
|
|
|
|
|
|
"en": "Textual is missing from this interpreter:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Run it with": {
|
|
|
|
|
|
"fr": "Le lancer avec",
|
|
|
|
|
|
"en": "Run it with",
|
|
|
|
|
|
},
|
[ADD] addons: uninstall a theme the way Odoo removes one
There was no counterpart to install_addons_theme.sh, so themes were
removed with a plain --uninstall. That takes out the module, not the
theme: it skips _theme_remove(), whose first act is
_reset_default_config() — the call that writes font-number and its
three neighbours into user_values.scss.
Measured on a real 12 -> 13 migration: web.assets_frontend stopped on
« Undefined variable: $o-theme-font-number ». Odoo 12 defined it in
option_font_body_*, dropped in 13.0; only the theme still redefined
it, so removing the theme exposed a frozen 2020 customization.
theme_leftover.py then reports what unloading does not take — 15
attachments on the measured database. It deletes nothing: their
content may be the only trace of a customization.
--- FR ---
[ADD] addons : désinstaller un thème comme Odoo le retire
install_addons_theme.sh n'avait pas de symétrique : on retirait donc
les thèmes par un --uninstall nu. Cela enlève le module, pas le thème
— cela saute _theme_remove(), dont le premier geste est
_reset_default_config(), l'appel qui écrit font-number et ses trois
voisines dans user_values.scss.
Mesuré sur une vraie migration 12 → 13 : web.assets_frontend s'arrête
sur « Undefined variable: $o-theme-font-number ». Odoo 12 la
définissait dans option_font_body_*, supprimés en 13.0 ; seul le thème
la redéfinissait, et le retirer a mis à nu un SCSS figé en 2020.
theme_leftover.py signale ensuite ce que le déchargement ne prend pas
— 15 pièces jointes sur la base mesurée. Il ne supprime rien : leur
contenu peut être la seule trace d'une personnalisation.
Assisted-by: Claude Opus 5
(cherry picked from commit 16a7b1e1333752e2ef603d66f47374a22590d665)
2026-08-13 05:56:06 -04:00
|
|
|
|
"No leftover for theme": {
|
|
|
|
|
|
"fr": "Aucun reste pour le thème",
|
|
|
|
|
|
"en": "No leftover for theme",
|
|
|
|
|
|
},
|
|
|
|
|
|
"attachment(s) still under": {
|
|
|
|
|
|
"fr": "pièce(s) jointe(s) encore sous",
|
|
|
|
|
|
"en": "attachment(s) still under",
|
|
|
|
|
|
},
|
|
|
|
|
|
"view(s) whose key still names it": {
|
|
|
|
|
|
"fr": "vue(s) dont la clé le nomme encore",
|
|
|
|
|
|
"en": "view(s) whose key still names it",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Nothing was deleted: their content may be the only trace": {
|
|
|
|
|
|
"fr": "Rien n'a été supprimé : leur contenu peut être la seule trace",
|
|
|
|
|
|
"en": "Nothing was deleted: their content may be the only trace",
|
|
|
|
|
|
},
|
|
|
|
|
|
"of a customization. Read before removing.": {
|
|
|
|
|
|
"fr": "d'une personnalisation. Lire avant de retirer.",
|
|
|
|
|
|
"en": "of a customization. Read before removing.",
|
|
|
|
|
|
},
|
[ADD] migration: offer to remove the themes before the first bump
A theme carries view copies and SCSS through every version bump, and a
bump can rename what they rely on. Removing it first drops a whole
family of failures, and it can be put back afterwards.
Asked in step 1, before OpenUpgrade runs, and only when a theme is
actually installed. Default is no: removing a theme changes how a site
looks, and a migration does not decide that for its owner.
theme_default is not offered — it IS the absence of a theme.
--- FR ---
[ADD] migration : proposer de retirer les thèmes avant le premier palier
Un thème traîne des copies de vues et des SCSS à travers chaque palier
de version, et un palier peut renommer ce dont ils dépendent. Le
retirer d'abord enlève une famille entière de pannes, et se refait
ensuite.
Posée à l'étape 1, avant OpenUpgrade, et seulement s'il y a vraiment un
thème installé. Par défaut non : retirer un thème change l'apparence
d'un site, et ce n'est pas à une migration de trancher cela à la place
de son propriétaire. theme_default n'est pas proposé — il EST
l'absence de thème.
Assisted-by: Claude Opus 5
(cherry picked from commit dcbee87a6b920408c315f7354a096b0ecfabcf06)
2026-08-15 02:36:28 -04:00
|
|
|
|
"Installed theme(s) on": {
|
|
|
|
|
|
"fr": "Thème(s) installé(s) sur",
|
|
|
|
|
|
"en": "Installed theme(s) on",
|
|
|
|
|
|
},
|
|
|
|
|
|
"A theme carries view copies and SCSS through every": {
|
|
|
|
|
|
"fr": "Un thème traîne des copies de vues et des SCSS à travers chaque",
|
|
|
|
|
|
"en": "A theme carries view copies and SCSS through every",
|
|
|
|
|
|
},
|
|
|
|
|
|
"version bump, and a bump can rename what they rely on.": {
|
|
|
|
|
|
"fr": "palier de version, et un palier peut renommer ce dont ils dépendent.",
|
|
|
|
|
|
"en": "version bump, and a bump can rename what they rely on.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Uninstall them properly before migrating?": {
|
|
|
|
|
|
"fr": "Les désinstaller proprement avant de migrer ?",
|
|
|
|
|
|
"en": "Uninstall them properly before migrating?",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Kept. Nothing was uninstalled.": {
|
|
|
|
|
|
"fr": "Conservés. Rien n'a été désinstallé.",
|
|
|
|
|
|
"en": "Kept. Nothing was uninstalled.",
|
|
|
|
|
|
},
|
[ADD] migration: predict which customized SCSS the next bump breaks
Customized SCSS lives in ir_attachment, frozen the day it is written,
still using the variables of that version. A bump can rename them:
website declared $o-theme-font-number in 12.0 and replaced the whole
mechanism in 13.0, so a 2020 customization stopped the frontend
bundle. Same shape as a stale COW view.
Reading the stored SCSS against the target sources answers before the
bump and names the attachment. Booting Odoo and opening a page answers
after, with « Style error » and no name — on a page a broken frontend
bundle is exactly what keeps you from reaching.
Measured: on the pre-bump database, targeting 13.0, it predicts the
failure; against its own 12.0 sources it reports nothing. That noise
floor is what makes it usable — the first version cried wolf on six
mixin parameters and named @include arguments.
--- FR ---
[ADD] migration : prédire quel SCSS personnalisé le palier va casser
Un SCSS personnalisé vit dans ir_attachment, figé le jour où il est
écrit, employant encore les variables de cette version-là. Un palier
peut les renommer : website déclarait $o-theme-font-number en 12.0 et a
remplacé le mécanisme en 13.0, arrêtant le bundle sur une
personnalisation de 2020. Même forme qu'une vue COW périmée.
Lire le SCSS stocké contre les sources cibles répond AVANT le palier et
nomme la pièce jointe. Démarrer Odoo et ouvrir une page répond après,
par « Style error » sans nom — et un bundle cassé est justement ce qui
empêche d'atteindre la page.
Mesuré : sur la base d'avant le palier, cible 13.0, il prédit la
panne ; contre ses propres sources 12.0 il ne signale rien. Ce bruit de
fond nul est ce qui le rend utilisable — la première version criait à
tort sur six paramètres de mixin et arguments nommés.
Assisted-by: Claude Opus 5
(cherry picked from commit c6611ee1431cfee94e321536d20052b8cca3ec10)
2026-08-15 03:21:25 -04:00
|
|
|
|
"No customized SCSS is at risk in": {
|
|
|
|
|
|
"fr": "Aucun SCSS personnalisé n'est en danger dans",
|
|
|
|
|
|
"en": "No customized SCSS is at risk in",
|
|
|
|
|
|
},
|
|
|
|
|
|
"customized SCSS use(s) a variable that": {
|
|
|
|
|
|
"fr": "SCSS personnalisé(s) utilisent une variable que",
|
|
|
|
|
|
"en": "customized SCSS use(s) a variable that",
|
|
|
|
|
|
},
|
|
|
|
|
|
"no longer defines: the bundle will not compile.": {
|
|
|
|
|
|
"fr": "ne définit plus : le bundle ne compilera pas.",
|
|
|
|
|
|
"en": "no longer defines: the bundle will not compile.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Each one is a copy frozen on an older version. Dropping it": {
|
|
|
|
|
|
"fr": "Chacun est une copie figée sur une version antérieure. L'abandonner",
|
|
|
|
|
|
"en": "Each one is a copy frozen on an older version. Dropping it",
|
|
|
|
|
|
},
|
|
|
|
|
|
"restores the module file:": {
|
|
|
|
|
|
"fr": "rend le fichier du module :",
|
|
|
|
|
|
"en": "restores the module file:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Read it first: what it holds beyond the stale variable is": {
|
|
|
|
|
|
"fr": "Le lire d'abord : ce qu'il porte au-delà de la variable périmée est",
|
|
|
|
|
|
"en": "Read it first: what it holds beyond the stale variable is",
|
|
|
|
|
|
},
|
|
|
|
|
|
"a real customization, to re-apply as a small file.": {
|
|
|
|
|
|
"fr": "une vraie personnalisation, à réappliquer en petit fichier.",
|
|
|
|
|
|
"en": "a real customization, to re-apply as a small file.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Read the customizations above before continuing.": {
|
|
|
|
|
|
"fr": "Lire les personnalisations ci-dessus avant de continuer.",
|
|
|
|
|
|
"en": "Read the customizations above before continuing.",
|
|
|
|
|
|
},
|
[ADD] migration: read the stale SCSS, then fix it, without leaving the tool
The report named the attachment and printed a command to paste. Deciding
« reset it » still meant accepting to lose one did not know what: the
diff against the module file is the only thing resetting gives up.
The tool now shows it and asks. --diff prints it, --tui browses it,
--apply resets without asking; with no flag and a terminal it asks, and
looking does not answer — the prompt comes back after each read.
--apply writes the copies under private/ first: reset_asset deletes the
attachment, so without that the customized lines would be nowhere.
The migration runs it on the real terminal, or none of this would be
reachable from there — the same pipe that was closing the TUI.
--- FR ---
[ADD] migration : lire le SCSS périmé, puis le corriger, sans sortir
Le rapport nommait la pièce jointe et imprimait une commande à coller.
Répondre « réinitialise » revenait encore à accepter de perdre on ne
sait quoi : l'écart avec le fichier du module est la seule chose que la
réinitialisation abandonne.
L'outil le montre et pose la question. --diff l'imprime, --tui le
parcourt, --apply réinitialise sans demander ; sans drapeau et devant
un terminal il demande, et regarder ne répond pas — l'invite revient
après chaque lecture. --apply écrit d'abord les copies sous private/ :
reset_asset supprime la pièce jointe, sans quoi les lignes
personnalisées ne seraient plus nulle part.
La migration le lance sur le vrai terminal, sinon rien de tout cela n'y
serait atteignable — le tube même qui fermait la TUI.
Assisted-by: Claude Opus 5
(cherry picked from commit 9e6c88622a74f7be408a8b1c523838eeadda1da3)
2026-08-15 03:41:56 -04:00
|
|
|
|
"Missing:": {
|
|
|
|
|
|
"fr": "Manquantes :",
|
|
|
|
|
|
"en": "Missing:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No module file of that name in": {
|
|
|
|
|
|
"fr": "Aucun fichier de module de ce nom dans",
|
|
|
|
|
|
"en": "No module file of that name in",
|
|
|
|
|
|
},
|
|
|
|
|
|
"the target no longer ships it, so there is nothing to": {
|
|
|
|
|
|
"fr": "la cible ne le livre plus, il n'y a donc rien sur quoi",
|
|
|
|
|
|
"en": "the target no longer ships it, so there is nothing to",
|
|
|
|
|
|
},
|
|
|
|
|
|
"fall back on. Read the copy before dropping it.": {
|
|
|
|
|
|
"fr": "retomber. Lire la copie avant de l'abandonner.",
|
|
|
|
|
|
"en": "fall back on. Read the copy before dropping it.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"The copy is identical to the module file.": {
|
|
|
|
|
|
"fr": "La copie est identique au fichier du module.",
|
|
|
|
|
|
"en": "The copy is identical to the module file.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"line(s): that is what resetting gives up.": {
|
|
|
|
|
|
"fr": "ligne(s) : c'est ce que la réinitialisation abandonne.",
|
|
|
|
|
|
"en": "line(s): that is what resetting gives up.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"What do you want to do with these customizations?": {
|
|
|
|
|
|
"fr": "Que voulez-vous faire de ces personnalisations ?",
|
|
|
|
|
|
"en": "What do you want to do with these customizations?",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Enter = nothing": {
|
|
|
|
|
|
"fr": "Entrée = rien",
|
|
|
|
|
|
"en": "Enter = nothing",
|
|
|
|
|
|
},
|
|
|
|
|
|
"what the copy changed": {
|
|
|
|
|
|
"fr": "ce que la copie a changé",
|
|
|
|
|
|
"en": "what the copy changed",
|
|
|
|
|
|
},
|
|
|
|
|
|
"reset them onto the module file": {
|
|
|
|
|
|
"fr": "les réinitialiser sur le fichier du module",
|
|
|
|
|
|
"en": "reset them onto the module file",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Full screen view unavailable.": {
|
|
|
|
|
|
"fr": "Vue plein écran indisponible.",
|
|
|
|
|
|
"en": "Full screen view unavailable.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Saved before resetting": {
|
|
|
|
|
|
"fr": "Sauvegardé avant réinitialisation",
|
|
|
|
|
|
"en": "Saved before resetting",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Reset failed, nothing was changed.": {
|
|
|
|
|
|
"fr": "La réinitialisation a échoué, rien n'a été modifié.",
|
|
|
|
|
|
"en": "Reset failed, nothing was changed.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Reset done.": {
|
|
|
|
|
|
"fr": "Réinitialisation faite.",
|
|
|
|
|
|
"en": "Reset done.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"This copy uses variables that": {
|
|
|
|
|
|
"fr": "Cette copie utilise des variables que",
|
|
|
|
|
|
"en": "This copy uses variables that",
|
|
|
|
|
|
},
|
|
|
|
|
|
"no longer defines": {
|
|
|
|
|
|
"fr": "ne définit plus",
|
|
|
|
|
|
"en": "no longer defines",
|
|
|
|
|
|
},
|
|
|
|
|
|
"The copy was written against an older version and frozen": {
|
|
|
|
|
|
"fr": "La copie a été écrite contre une version antérieure et figée",
|
|
|
|
|
|
"en": "The copy was written against an older version and frozen",
|
|
|
|
|
|
},
|
|
|
|
|
|
"there. The module has since renamed what it relies on.": {
|
|
|
|
|
|
"fr": "là. Le module a depuis renommé ce dont elle dépend.",
|
|
|
|
|
|
"en": "there. The module has since renamed what it relies on.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Resetting restores": {
|
|
|
|
|
|
"fr": "La réinitialisation rend",
|
|
|
|
|
|
"en": "Resetting restores",
|
|
|
|
|
|
},
|
|
|
|
|
|
"(nothing: the target no longer ships this file)": {
|
|
|
|
|
|
"fr": "(rien : la cible ne livre plus ce fichier)",
|
|
|
|
|
|
"en": "(nothing: the target no longer ships this file)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Reset command copied.": {
|
|
|
|
|
|
"fr": "Commande de réinitialisation copiée.",
|
|
|
|
|
|
"en": "Reset command copied.",
|
|
|
|
|
|
},
|
[FIX] migration: reset the stale SCSS after the bump, not before
The fix was offered while the checkout was still on the previous
version. Answering « a » ran reset_asset in an Odoo 12 shell, which has
no web_editor.assets: KeyError, nothing changed, and the migration went
on to break at the next bump — measured on a real run.
Predicting early is right; fixing early is not. The early call is now
--report-only, and a second call comes after the bump, on the upgraded
database, where the checkout can do it. The tool also refuses on its
own: it reads the checkout sources for reset_asset rather than trusting
a version number, and says when to come back.
--- FR ---
[FIX] migration : réinitialiser le SCSS périmé après le palier, pas avant
La correction était proposée alors que le checkout était encore sur la
version précédente. Répondre « a » lançait reset_asset dans un shell
Odoo 12, sans web_editor.assets : KeyError, rien de modifié, et la
migration continuait jusqu'à casser au palier suivant. Mesuré.
Prédire tôt est juste ; corriger tôt ne l'est pas. L'appel précoce est
désormais --report-only, et un second vient après le palier, sur la
base montée de version, là où le checkout sait le faire. L'outil refuse
aussi de lui-même : il cherche reset_asset dans les sources du checkout
plutôt que de se fier à un numéro, et dit quand revenir.
Assisted-by: Claude Opus 5
(cherry picked from commit 13d4d0a33c63efcbafb5fb29313a5377ba5f508e)
2026-08-15 16:34:26 -04:00
|
|
|
|
"Resetting needs Odoo 13.0 or later; this checkout is on": {
|
|
|
|
|
|
"fr": "La réinitialisation exige Odoo 13.0 ou plus ; ce checkout est sur",
|
|
|
|
|
|
"en": "Resetting needs Odoo 13.0 or later; this checkout is on",
|
|
|
|
|
|
},
|
|
|
|
|
|
"The prediction stands, the fix does not: run it again once": {
|
|
|
|
|
|
"fr": "La prédiction tient, pas la correction : relancer une fois",
|
|
|
|
|
|
"en": "The prediction stands, the fix does not: run it again once",
|
|
|
|
|
|
},
|
|
|
|
|
|
"the bump is done, on the upgraded database.": {
|
|
|
|
|
|
"fr": "le palier passé, sur la base montée de version.",
|
|
|
|
|
|
"en": "the bump is done, on the upgraded database.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Applying it from here fails with": {
|
|
|
|
|
|
"fr": "L'appliquer d'ici échoue sur",
|
|
|
|
|
|
"en": "Applying it from here fails with",
|
|
|
|
|
|
},
|
|
|
|
|
|
"and changes nothing.": {
|
|
|
|
|
|
"fr": "et ne change rien.",
|
|
|
|
|
|
"en": "and changes nothing.",
|
|
|
|
|
|
},
|
2026-08-16 03:39:37 -04:00
|
|
|
|
"Reset one of them onto its module view": {
|
|
|
|
|
|
"fr": "Réinitialiser l'une d'elles sur sa vue module",
|
|
|
|
|
|
"en": "Reset one of them onto its module view",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Drifted COW copies": {
|
|
|
|
|
|
"fr": "Copies COW ayant dérivé",
|
|
|
|
|
|
"en": "Drifted COW copies",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Which one(s) to reset onto the module view?": {
|
|
|
|
|
|
"fr": "Laquelle ou lesquelles réinitialiser sur la vue module ?",
|
|
|
|
|
|
"en": "Which one(s) to reset onto the module view?",
|
|
|
|
|
|
},
|
|
|
|
|
|
"numbers separated by commas, a = all, empty =": {
|
|
|
|
|
|
"fr": "numéros séparés par des virgules, a = toutes, vide =",
|
|
|
|
|
|
"en": "numbers separated by commas, a = all, empty =",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Kept. Nothing was reset.": {
|
|
|
|
|
|
"fr": "Conservées. Rien n'a été réinitialisé.",
|
|
|
|
|
|
"en": "Kept. Nothing was reset.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Unknown choice, nothing was reset.": {
|
|
|
|
|
|
"fr": "Choix inconnu, rien n'a été réinitialisé.",
|
|
|
|
|
|
"en": "Unknown choice, nothing was reset.",
|
|
|
|
|
|
},
|
[FIX] addons: stop calling a leftover report a failed command
« Command returned error code: 1 » followed a theme that was removed
correctly. 1 is this toolkit's « there are findings », but the report
was the uninstaller's last command, so its code became the script's;
and the COW check was still read through the capturing executor, which
announces any non-zero code as an error.
The leftovers can now be dealt with instead of only listed: keep by
default, or delete after their content is written to private/. Listing
fifteen attachments and stopping there meant composing an unlink() by
hand, mid-migration, from identifiers read off a screen.
--- FR ---
[FIX] addons : cesser d'appeler « erreur » un rapport de restes
« Command returned error code: 1 » suivait un thème correctement
retiré. 1 veut dire « il y a des constats » dans cet outillage, mais le
rapport était la dernière commande du désinstalleur, donc son code
devenait celui du script ; et la vérification COW passait encore par
l'exécuteur qui capture, lequel annonce tout code non nul comme une
erreur.
Les restes peuvent désormais être traités, pas seulement listés :
garder par défaut, ou effacer après écriture de leur contenu sous
private/. Lister quinze pièces jointes et s'arrêter là revenait à faire
composer un unlink() à la main, en pleine migration.
Assisted-by: Claude Opus 5
(cherry picked from commit 304ce32b2680e3d01e38c3224bb85c2798cd6f53)
2026-08-16 05:33:28 -04:00
|
|
|
|
"Delete these leftovers, or keep them?": {
|
|
|
|
|
|
"fr": "Effacer ces restes, ou les garder ?",
|
|
|
|
|
|
"en": "Delete these leftovers, or keep them?",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Enter = keep": {
|
|
|
|
|
|
"fr": "Entrée = garder",
|
|
|
|
|
|
"en": "Enter = keep",
|
|
|
|
|
|
},
|
|
|
|
|
|
"delete, after saving them": {
|
|
|
|
|
|
"fr": "effacer, après les avoir sauvegardés",
|
|
|
|
|
|
"en": "delete, after saving them",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Kept. Nothing was deleted.": {
|
|
|
|
|
|
"fr": "Conservés. Rien n'a été effacé.",
|
|
|
|
|
|
"en": "Kept. Nothing was deleted.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Saved before deleting": {
|
|
|
|
|
|
"fr": "Sauvegardé avant effacement",
|
|
|
|
|
|
"en": "Saved before deleting",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Deletion failed, nothing was removed.": {
|
|
|
|
|
|
"fr": "L'effacement a échoué, rien n'a été retiré.",
|
|
|
|
|
|
"en": "Deletion failed, nothing was removed.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"attachment(s) deleted.": {
|
|
|
|
|
|
"fr": "pièce(s) jointe(s) effacée(s).",
|
|
|
|
|
|
"en": "attachment(s) deleted.",
|
|
|
|
|
|
},
|
[ADD] migration: request every public URL before calling it done
A migration can load every module, log nothing, and still serve 500s on
pages nobody thought to open. Measured on the real one: 2 of 33 public
URLs failed — a blog post and /contactus — after a bump the log called
successful.
The list is the sitemap, what Odoo publishes for search engines. It
cannot be read from odoo-bin shell: enumerate_pages() asks for
http.root.get_db_router(request.db) and raises « object unbound »
without a real request. So the server is started on its own port,
asked, and always stopped — a forgotten one holds the port and fails
the next bump.
Offered before the Selenium prompt, default no: it boots a server and
can take minutes.
--- FR ---
[ADD] migration : interroger chaque URL publique avant de conclure
Une migration peut charger tous ses modules, ne rien écrire au journal,
et servir quand même des 500 sur des pages que personne n'ouvre.
Mesuré : 2 URL publiques sur 33 échouaient — un billet de blogue et
/contactus — après un palier que le journal disait réussi.
La liste est le sitemap, celle qu'Odoo publie pour les moteurs. Elle ne
se lit pas depuis odoo-bin shell : enumerate_pages() réclame
http.root.get_db_router(request.db) et lève « object unbound » sans
requête réelle. Le serveur est donc démarré sur son propre port,
interrogé, et toujours arrêté — un serveur oublié tient le port et fait
échouer le palier suivant.
Proposé avant l'invite Selenium, par défaut non : cela démarre un
serveur et peut durer.
Assisted-by: Claude Opus 5
(cherry picked from commit 277e85b3d06173beeb92be792467751b31ac0696)
2026-08-16 05:50:00 -04:00
|
|
|
|
"Request every public URL of this database now?": {
|
|
|
|
|
|
"fr": "Interroger maintenant toutes les URL publiques de cette base ?",
|
|
|
|
|
|
"en": "Request every public URL of this database now?",
|
|
|
|
|
|
},
|
|
|
|
|
|
"The sitemap listed no URL: nothing was tested.": {
|
|
|
|
|
|
"fr": "Le sitemap n'a listé aucune URL : rien n'a été testé.",
|
|
|
|
|
|
"en": "The sitemap listed no URL: nothing was tested.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"public URL(s) answered without error.": {
|
|
|
|
|
|
"fr": "URL publique(s) ont répondu sans erreur.",
|
|
|
|
|
|
"en": "public URL(s) answered without error.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"public URL(s) failed": {
|
|
|
|
|
|
"fr": "URL publique(s) ont échoué",
|
|
|
|
|
|
"en": "public URL(s) failed",
|
|
|
|
|
|
},
|
|
|
|
|
|
"no answer": {
|
|
|
|
|
|
"fr": "aucune réponse",
|
|
|
|
|
|
"en": "no answer",
|
|
|
|
|
|
},
|
|
|
|
|
|
"A page listed for search engines that does not answer is": {
|
|
|
|
|
|
"fr": "Une page listée pour les moteurs de recherche qui ne répond pas est",
|
|
|
|
|
|
"en": "A page listed for search engines that does not answer is",
|
|
|
|
|
|
},
|
|
|
|
|
|
"a page your visitors do not reach either.": {
|
|
|
|
|
|
"fr": "une page que vos visiteurs n'atteignent pas non plus.",
|
|
|
|
|
|
"en": "a page your visitors do not reach either.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"The server never answered on": {
|
|
|
|
|
|
"fr": "Le serveur n'a jamais répondu sur",
|
|
|
|
|
|
"en": "The server never answered on",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Could not read": {
|
|
|
|
|
|
"fr": "Impossible de lire",
|
|
|
|
|
|
"en": "Could not read",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Starting Odoo on": {
|
|
|
|
|
|
"fr": "Démarrage d'Odoo sur",
|
|
|
|
|
|
"en": "Starting Odoo on",
|
|
|
|
|
|
},
|
|
|
|
|
|
"port": {
|
|
|
|
|
|
"fr": "port",
|
|
|
|
|
|
"en": "port",
|
|
|
|
|
|
},
|
[ADD] migration: name the views behind a failing URL, and offer the reset
The smoke test stopped at « these URLs answer 500 ». Turning that into a
fix meant reading an id out of a traceback and translating it to a key
by hand, mid-migration — the copy where a character goes missing.
It now reads the error context Odoo logs — [view_id: N, parent_id: M],
the one line it does not translate — resolves the parent to its key,
offers the reset numbered with « all », and re-requests the failing URLs
afterwards. Applying without re-asking would be calling it fixed
without having seen it answer.
Two defects found while measuring, both mine. ./run.sh is a bash
wrapper: terminate() killed it and left odoo-bin holding the port, six
orphans in six runs, each later run silently querying the first one's
server. And the log was read before stopping, so only the 24 startup
lines existed — hence « no view in cause » on pages that named one.
--- FR ---
[ADD] migration : nommer les vues derrière une URL en échec, et corriger
Le test s'arrêtait à « ces URL répondent 500 ». En faire un correctif
demandait de relever un id dans une trace et de le traduire en clé à la
main, en pleine migration — la recopie où un caractère se perd.
Il lit désormais le contexte qu'Odoo journalise — [view_id: N,
parent_id: M], la seule ligne qu'il ne traduit pas —, résout le parent
en clé, propose la réinitialisation numérotée avec « toutes », et
redemande ensuite les URL en échec. Appliquer sans redemander, ce
serait déclarer réparé sans l'avoir vu répondre.
Deux défauts trouvés en mesurant, tous deux à moi. ./run.sh est une
enveloppe bash : terminate() la tuait et laissait odoo-bin tenir le
port, six orphelins en six essais, chaque essai suivant interrogeant
sans le savoir le serveur du premier. Et le journal était lu avant
l'arrêt, donc seules les 24 lignes de démarrage existaient — d'où
« aucune vue en cause » sur des pages qui en nommaient une.
Assisted-by: Claude Opus 5
(cherry picked from commit 30fbcd1aee20c4028ea6b24bd04eb518f0587ffe)
2026-08-16 06:18:45 -04:00
|
|
|
|
"parent view(s) in cause": {
|
|
|
|
|
|
"fr": "vue(s) parente(s) en cause",
|
|
|
|
|
|
"en": "parent view(s) in cause",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Those parents are copies frozen on an older version;": {
|
|
|
|
|
|
"fr": "Ces parents sont des copies figées sur une version antérieure ;",
|
|
|
|
|
|
"en": "Those parents are copies frozen on an older version;",
|
|
|
|
|
|
},
|
|
|
|
|
|
"resetting them onto the module view is the fix": {
|
|
|
|
|
|
"fr": "les réinitialiser sur la vue module est le correctif",
|
|
|
|
|
|
"en": "resetting them onto the module view is the fix",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No parent view named: nothing to offer.": {
|
|
|
|
|
|
"fr": "Aucune vue parente nommée : rien à proposer.",
|
|
|
|
|
|
"en": "No parent view named: nothing to offer.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Copies to reset onto their module view": {
|
|
|
|
|
|
"fr": "Copies à réinitialiser sur leur vue module",
|
|
|
|
|
|
"en": "Copies to reset onto their module view",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Which one(s) to reset?": {
|
|
|
|
|
|
"fr": "Laquelle ou lesquelles réinitialiser ?",
|
|
|
|
|
|
"en": "Which one(s) to reset?",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Re-checked the": {
|
|
|
|
|
|
"fr": "Revérification des",
|
|
|
|
|
|
"en": "Re-checked the",
|
|
|
|
|
|
},
|
|
|
|
|
|
"failing URL(s) after the reset": {
|
|
|
|
|
|
"fr": "URL en échec après la réinitialisation",
|
|
|
|
|
|
"en": "failing URL(s) after the reset",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Something already listens on port": {
|
|
|
|
|
|
"fr": "Quelque chose écoute déjà sur le port",
|
|
|
|
|
|
"en": "Something already listens on port",
|
|
|
|
|
|
},
|
|
|
|
|
|
"it would be tested instead of this database.": {
|
|
|
|
|
|
"fr": "il serait testé à la place de cette base.",
|
|
|
|
|
|
"en": "it would be tested instead of this database.",
|
|
|
|
|
|
},
|
[ADD] migration: Enter targets the highest supported Odoo version
Migrating means going all the way, and that choice had to be typed
every time. Enter did nothing visible: click.prompt without a default
re-asks on an empty line without printing anything, so a default added
without `default=""` would never have been reached and nothing would
have said so.
The highest is computed, not read off the end of the list: display
order is not a guarantee, and « 9.0 » sorts after « 18.0 » as a string.
The prompt announces it — a default nobody sees is a default nobody
uses. No choice at all means no default rather than a crash.
--- FR ---
[ADD] migration : Entrée vise la version Odoo la plus élevée
Migrer, c'est aller au bout, et ce choix devait être tapé chaque fois.
Entrée ne faisait rien de visible : click.prompt sans défaut redemande
sur une ligne vide sans rien afficher, si bien qu'un défaut ajouté sans
`default=""` n'aurait jamais été atteint, sans que rien ne le signale.
La plus haute est calculée, pas lue au bout de la liste : l'ordre
d'affichage n'est pas une garantie, et « 9.0 » se trie après « 18.0 »
en chaînes. L'invite l'annonce — un défaut qu'on ne montre pas est un
défaut que personne n'utilise. Aucun choix possible ne donne aucun
défaut plutôt qu'une erreur.
Assisted-by: Claude Opus 5
(cherry picked from commit f0861363f496cdc3c0bb02216d30dd0aa8d3697b)
2026-08-16 07:11:43 -04:00
|
|
|
|
"Enter =": {
|
|
|
|
|
|
"fr": "Entrée =",
|
|
|
|
|
|
"en": "Enter =",
|
|
|
|
|
|
},
|
[FIX] migration: reset the copy that is actually stale, and say when a key misses
Answering « all » reset one copy of two and reported success. Two
defects behind it, both mine.
A requested key matching no finding did nothing, silently: the tool
returned early on « nothing drifted » and never looked. Detection is
differential — it only sees a copy whose CHILD breaks — so a stale copy
without children escapes it. A key can now be reset outside detection,
an unknown one is named, and the exit code is 2.
And the culprit is not always the parent. On /contactus the parent was
identical to its module view; the CHILD held the stale arch. Both are
proposed now, parent first — it is the more common case.
Measured on the migration: 33 of 33 public URLs answer.
--- FR ---
[FIX] migration : réinitialiser la copie vraiment périmée, et signaler une clé sans objet
Répondre « toutes » réinitialisait une copie sur deux et annonçait un
succès. Deux défauts derrière, tous deux à moi.
Une clé demandée ne correspondant à aucun constat ne faisait rien, en
silence : l'outil sortait sur « rien n'a dérivé » sans jamais chercher.
La détection est différentielle — elle ne voit qu'une copie dont un
ENFANT casse — donc une copie périmée sans enfant lui échappe. Une clé
peut désormais être réinitialisée hors détection, une clé inconnue est
nommée, et le code de sortie vaut 2.
Et le coupable n'est pas toujours le parent. Sur /contactus, le parent
était identique à sa vue module ; c'est l'ENFANT qui portait l'arch
périmée. Les deux sont proposés, le parent d'abord — le cas le plus
fréquent.
Mesuré sur la migration : 33 URL publiques sur 33 répondent.
Assisted-by: Claude Opus 5
2026-08-17 04:49:32 -04:00
|
|
|
|
"No COW copy carries this key": {
|
|
|
|
|
|
"fr": "Aucune copie COW ne porte cette clé",
|
|
|
|
|
|
"en": "No COW copy carries this key",
|
|
|
|
|
|
},
|
|
|
|
|
|
"already identical to the module view.": {
|
|
|
|
|
|
"fr": "déjà identique à la vue module.",
|
|
|
|
|
|
"en": "already identical to the module view.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Odoo surfaces this when the module view is rewritten": {
|
|
|
|
|
|
"fr": "Odoo le fait apparaître quand la vue module est réécrite",
|
|
|
|
|
|
"en": "Odoo surfaces this when the module view is rewritten",
|
|
|
|
|
|
},
|
|
|
|
|
|
"(a version bump) or when the page is rendered.": {
|
|
|
|
|
|
"fr": "(un palier de version) ou quand la page est rendue.",
|
|
|
|
|
|
"en": "(a version bump) or when the page is rendered.",
|
|
|
|
|
|
},
|
2026-08-17 05:13:13 -04:00
|
|
|
|
"Before starting: what already answers, and what": {
|
|
|
|
|
|
"fr": "Avant de commencer : ce qui répond déjà, et ce qui",
|
|
|
|
|
|
"en": "Before starting: what already answers, and what",
|
|
|
|
|
|
},
|
|
|
|
|
|
"does not. A page broken now will still be broken": {
|
|
|
|
|
|
"fr": "ne répond pas. Une page cassée maintenant le sera encore",
|
|
|
|
|
|
"en": "does not. A page broken now will still be broken",
|
|
|
|
|
|
},
|
|
|
|
|
|
"after — and that is not the migration.": {
|
|
|
|
|
|
"fr": "après — et ce ne sera pas la migration.",
|
|
|
|
|
|
"en": "after — and that is not the migration.",
|
|
|
|
|
|
},
|
[ADD] migration: browse the drifted COW copies full screen
The text report puts a thousand lines between the two things that decide:
what the copy holds that the module view does not — all a reset gives up
— and which child no longer finds its anchor, which is why anything
breaks. Space switches between them, « c » copies the reset command.
Offered as [4] on the error prompt, next to check and reset, and run on
the real terminal: a full screen started through the capturing executor
falls back to the text report with nothing to tell the two apart.
The diff is rendered in one place, shared with the command line. Two
renderings would drift without anything saying so.
--- FR ---
[ADD] migration : parcourir les copies COW dérivées en plein écran
Le rapport texte met mille lignes entre les deux choses qui décident :
ce que la copie porte et que la vue module n'a pas — tout ce qu'une
réinitialisation abandonne — et quel enfant ne trouve plus son ancrage,
la raison pour laquelle quoi que ce soit casse. L'espace bascule, « c »
copie la commande de réparation.
Proposé en [4] à l'invite d'erreur, à côté de vérifier et réinitialiser,
et lancé sur le vrai terminal : un plein écran passé par l'exécuteur qui
capture retombe sur le rapport texte, sans rien qui les distingue.
Le diff est rendu à un seul endroit, partagé avec la ligne de commande.
Deux rendus dériveraient sans que rien ne le signale.
Assisted-by: Claude Opus 5
2026-08-17 06:29:25 -04:00
|
|
|
|
"No child fails on this copy: it drifted without": {
|
|
|
|
|
|
"fr": "Aucun enfant n'échoue sur cette copie : elle a dérivé sans",
|
|
|
|
|
|
"en": "No child fails on this copy: it drifted without",
|
|
|
|
|
|
},
|
|
|
|
|
|
"breaking anything yet.": {
|
|
|
|
|
|
"fr": "encore rien casser.",
|
|
|
|
|
|
"en": "breaking anything yet.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"These children no longer find their anchor in the copy": {
|
|
|
|
|
|
"fr": "Ces enfants ne trouvent plus leur point d'ancrage dans la copie",
|
|
|
|
|
|
"en": "These children no longer find their anchor in the copy",
|
|
|
|
|
|
},
|
|
|
|
|
|
"The anchor exists in the module view": {
|
|
|
|
|
|
"fr": "L'ancrage existe dans la vue module",
|
|
|
|
|
|
"en": "The anchor exists in the module view",
|
|
|
|
|
|
},
|
|
|
|
|
|
"but not in this copy, frozen on an older version.": {
|
|
|
|
|
|
"fr": "mais pas dans cette copie, figée sur une version antérieure.",
|
|
|
|
|
|
"en": "but not in this copy, frozen on an older version.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Resetting restores the module arch; the customization the": {
|
|
|
|
|
|
"fr": "Réinitialiser rend l'arch du module ; la personnalisation que",
|
|
|
|
|
|
"en": "Resetting restores the module arch; the customization the",
|
|
|
|
|
|
},
|
|
|
|
|
|
"copy carried is saved first, and is yours to re-apply as an": {
|
|
|
|
|
|
"fr": "la copie portait est sauvegardée d'abord, à vous de la réappliquer en",
|
|
|
|
|
|
"en": "copy carried is saved first, and is yours to re-apply as an",
|
|
|
|
|
|
},
|
|
|
|
|
|
"INHERITING view with its own key.": {
|
|
|
|
|
|
"fr": "vue HÉRITANTE avec sa propre clé.",
|
|
|
|
|
|
"en": "INHERITING view with its own key.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No module view carries this key: nothing to reset": {
|
|
|
|
|
|
"fr": "Aucune vue module ne porte cette clé : rien sur quoi",
|
|
|
|
|
|
"en": "No module view carries this key: nothing to reset",
|
|
|
|
|
|
},
|
|
|
|
|
|
"onto.": {
|
|
|
|
|
|
"fr": "réinitialiser.",
|
|
|
|
|
|
"en": "onto.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Browse the differences full screen": {
|
|
|
|
|
|
"fr": "Parcourir les différences en plein écran",
|
|
|
|
|
|
"en": "Browse the differences full screen",
|
|
|
|
|
|
},
|
[ADD] migration: run the OCA database cleanup before testing the pages
The eight purges are not independent: purging a model frees the columns
that referenced it, purging a table frees the data pointing at it. One
pass is never enough, so the requested order runs again until a full
pass repairs nothing new.
Refusals are expected — a foreign key still holds, a module says no.
Purging a list at once loses everything to the first one, so each entry
purges inside its own savepoint: a refusal rolls back that entry alone.
What one pass could not take, the next may, once its neighbours are
gone. Leftovers are reported as a warning: a database can carry some
that nothing removes, and stopping there would help no one.
It refuses a version mismatch. Measured while building it: a shell on
Odoo 14 opened against a 17.0 database went rewriting ir_model before
dying on a jsonb it did not know. An older Odoo does not merely fail on
a newer database — it writes on the way.
--- FR ---
[ADD] migration : lancer le nettoyage OCA avant de tester les pages
Les huit purges ne sont pas indépendantes : purger un modèle libère les
colonnes qui le référençaient, purger une table libère les données qui
la visaient. Une passe ne suffit jamais, l'ordre demandé est donc rejoué
jusqu'à ce qu'une passe entière ne répare plus rien.
Les refus sont attendus — une clé étrangère tient, un module dit non.
Purger d'un bloc perdrait tout au premier : chaque entrée passe dans son
propre point de reprise, un refus n'emporte que la sienne. Ce qu'une
passe n'a pu prendre, la suivante le peut, une fois les voisines
parties. Les restes sont un avertissement : une base peut en porter que
rien ne retire, et s'arrêter là n'aiderait personne.
Il refuse une version qui ne correspond pas. Mesuré en le construisant :
un shell en Odoo 14 ouvert sur une base 17.0 est parti réécrire ir_model
avant de mourir sur un jsonb inconnu de lui. Un Odoo plus ancien
n'échoue pas simplement sur une base plus récente — il écrit en chemin.
Assisted-by: Claude Opus 5
2026-08-17 09:05:03 -04:00
|
|
|
|
"The checkout is on Odoo": {
|
|
|
|
|
|
"fr": "Le checkout est sur Odoo",
|
|
|
|
|
|
"en": "The checkout is on Odoo",
|
|
|
|
|
|
},
|
|
|
|
|
|
"and the database on": {
|
|
|
|
|
|
"fr": "et la base sur",
|
|
|
|
|
|
"en": "and the database on",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Opening it with the wrong version writes to it before": {
|
|
|
|
|
|
"fr": "L'ouvrir avec la mauvaise version y écrit avant",
|
|
|
|
|
|
"en": "Opening it with the wrong version writes to it before",
|
|
|
|
|
|
},
|
|
|
|
|
|
"failing. Switch the checkout first.": {
|
|
|
|
|
|
"fr": "d'échouer. Basculer le checkout d'abord.",
|
|
|
|
|
|
"en": "failing. Switch the checkout first.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Database cleanup on": {
|
|
|
|
|
|
"fr": "Nettoyage de la base",
|
|
|
|
|
|
"en": "Database cleanup on",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Cleaning": {
|
|
|
|
|
|
"fr": "Nettoyage de",
|
|
|
|
|
|
"en": "Cleaning",
|
|
|
|
|
|
},
|
|
|
|
|
|
"pass": {
|
|
|
|
|
|
"fr": "passe",
|
|
|
|
|
|
"en": "pass",
|
|
|
|
|
|
},
|
|
|
|
|
|
"purged": {
|
|
|
|
|
|
"fr": "purgés",
|
|
|
|
|
|
"en": "purged",
|
|
|
|
|
|
},
|
|
|
|
|
|
"purged;": {
|
|
|
|
|
|
"fr": "purgés ;",
|
|
|
|
|
|
"en": "purged;",
|
|
|
|
|
|
},
|
|
|
|
|
|
"could not be, and are left as they are": {
|
|
|
|
|
|
"fr": "n'ont pas pu l'être, et restent en l'état",
|
|
|
|
|
|
"en": "could not be, and are left as they are",
|
|
|
|
|
|
},
|
|
|
|
|
|
"entries purged, nothing left.": {
|
|
|
|
|
|
"fr": "entrées purgées, rien ne reste.",
|
|
|
|
|
|
"en": "entries purged, nothing left.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"no such wizard in this version, skipped.": {
|
|
|
|
|
|
"fr": "pas d'assistant pour cela dans cette version, ignoré.",
|
|
|
|
|
|
"en": "no such wizard in this version, skipped.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"A database can carry leftovers nothing can remove.": {
|
|
|
|
|
|
"fr": "Une base peut porter des restes que rien ne peut retirer.",
|
|
|
|
|
|
"en": "A database can carry leftovers nothing can remove.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"This is a warning, not a failure.": {
|
|
|
|
|
|
"fr": "C'est un avertissement, pas un échec.",
|
|
|
|
|
|
"en": "This is a warning, not a failure.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"The cleanup produced no report.": {
|
|
|
|
|
|
"fr": "Le nettoyage n'a produit aucun rapport.",
|
|
|
|
|
|
"en": "The cleanup produced no report.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Unreadable report": {
|
|
|
|
|
|
"fr": "Rapport illisible",
|
|
|
|
|
|
"en": "Unreadable report",
|
|
|
|
|
|
},
|
|
|
|
|
|
"obsolete models": {
|
|
|
|
|
|
"fr": "modèles obsolètes",
|
|
|
|
|
|
"en": "obsolete models",
|
|
|
|
|
|
},
|
|
|
|
|
|
"obsolete modules": {
|
|
|
|
|
|
"fr": "modules obsolètes",
|
|
|
|
|
|
"en": "obsolete modules",
|
|
|
|
|
|
},
|
|
|
|
|
|
"obsolete columns": {
|
|
|
|
|
|
"fr": "colonnes obsolètes",
|
|
|
|
|
|
"en": "obsolete columns",
|
|
|
|
|
|
},
|
|
|
|
|
|
"obsolete tables": {
|
|
|
|
|
|
"fr": "tables obsolètes",
|
|
|
|
|
|
"en": "obsolete tables",
|
|
|
|
|
|
},
|
|
|
|
|
|
"obsolete data entries": {
|
|
|
|
|
|
"fr": "entrées de données obsolètes",
|
|
|
|
|
|
"en": "obsolete data entries",
|
|
|
|
|
|
},
|
|
|
|
|
|
"obsolete menu entries": {
|
|
|
|
|
|
"fr": "entrées de menu obsolètes",
|
|
|
|
|
|
"en": "obsolete menu entries",
|
|
|
|
|
|
},
|
|
|
|
|
|
"missing indexes": {
|
|
|
|
|
|
"fr": "index manquants",
|
|
|
|
|
|
"en": "missing indexes",
|
|
|
|
|
|
},
|
|
|
|
|
|
"obsolete properties": {
|
|
|
|
|
|
"fr": "propriétés obsolètes",
|
|
|
|
|
|
"en": "obsolete properties",
|
|
|
|
|
|
},
|
|
|
|
|
|
"entries would be purged": {
|
|
|
|
|
|
"fr": "entrées seraient purgées",
|
|
|
|
|
|
"en": "entries would be purged",
|
|
|
|
|
|
},
|
|
|
|
|
|
"nothing was changed": {
|
|
|
|
|
|
"fr": "rien n'a été modifié",
|
|
|
|
|
|
"en": "nothing was changed",
|
|
|
|
|
|
},
|
[ADD] migration: make Enter mean the answer you always give
Auto-run promised to "take the default after five seconds", but every
default was EMPTY: it took nothing. Worse, half the prompts of a
migration are asked by separate processes — the theme uninstaller, the
stale-SCSS detector, the smoke test. They knew nothing of auto-run and
waited forever for a keystroke that never came.
The countdown now lives in one file and travels through the ENVIRONMENT,
the only channel a fork shares. Enter takes the default everywhere, not
only under auto-run: a prompt that prints (Y/n) and does otherwise is
worse than no prompt. Every text was rewritten to say what Enter does,
and every default kept an explicit way out.
Two defaults now write. Both are tenable only because the backup runs
first, and a test locks that ORDER rather than trusting a promise.
--- FR ---
[ADD] migration : faire d'Entrée la réponse qu'on donne toujours
L'auto-exécution promettait « le défaut après cinq secondes », mais tous
les défauts étaient VIDES : elle ne prenait rien. Pire, la moitié des
invites d'une migration sont posées par d'autres processus — le
désinstalleur de thème, le détecteur de SCSS figé, le test de fumée. Ils
ignoraient l'auto-exécution et attendaient sans fin une frappe.
Le compte à rebours tient désormais dans un seul fichier et voyage par
l'ENVIRONNEMENT, le seul canal qu'un fork partage. Entrée vaut le défaut
partout, pas seulement en auto : une invite qui affiche (Y/n) et fait
l'inverse est pire que pas d'invite. Chaque texte dit ce que fait Entrée,
et chaque défaut garde une issue explicite.
Deux défauts écrivent. Ils ne tiennent que parce que la sauvegarde passe
d'abord, et un test verrouille cet ORDRE plutôt qu'une promesse.
Assisted-by: Claude Opus 5
2026-08-17 20:47:17 -04:00
|
|
|
|
"Enter = 1": {
|
|
|
|
|
|
"fr": "Entrée = 1",
|
|
|
|
|
|
"en": "Enter = 1",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Enter = all, n = none": {
|
|
|
|
|
|
"fr": "Entrée = tous, n = aucun",
|
|
|
|
|
|
"en": "Enter = all, n = none",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Enter = delete, after saving them": {
|
|
|
|
|
|
"fr": "Entrée = effacer, après les avoir sauvegardés",
|
|
|
|
|
|
"en": "Enter = delete, after saving them",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Enter = neutralize now, reversible": {
|
|
|
|
|
|
"fr": "Entrée = neutraliser maintenant, réversible",
|
|
|
|
|
|
"en": "Enter = neutralize now, reversible",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Enter = reset them onto the module file": {
|
|
|
|
|
|
"fr": "Entrée = les réinitialiser sur le fichier du module",
|
|
|
|
|
|
"en": "Enter = reset them onto the module file",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Enter upgrades all addons of the": {
|
|
|
|
|
|
"fr": "Entrée met à jour tous les modules de la",
|
|
|
|
|
|
"en": "Enter upgrades all addons of the",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Open it at EVERY version bump, stop asking": {
|
|
|
|
|
|
"fr": "L'ouvrir à CHAQUE palier de version, ne plus demander",
|
|
|
|
|
|
"en": "Open it at EVERY version bump, stop asking",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Testing every bump, as chosen earlier.": {
|
|
|
|
|
|
"fr": "Test à chaque palier, comme choisi plus tôt.",
|
|
|
|
|
|
"en": "Testing every bump, as chosen earlier.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"numbers separated by commas, Enter = all, n =": {
|
|
|
|
|
|
"fr": "numéros séparés par des virgules, Entrée = toutes, n =",
|
|
|
|
|
|
"en": "numbers separated by commas, Enter = all, n =",
|
|
|
|
|
|
},
|
|
|
|
|
|
"decide at the version bump": {
|
|
|
|
|
|
"fr": "décider au palier de version",
|
|
|
|
|
|
"en": "decide at the version bump",
|
|
|
|
|
|
},
|
|
|
|
|
|
"keep": {
|
|
|
|
|
|
"fr": "garder",
|
|
|
|
|
|
"en": "keep",
|
|
|
|
|
|
},
|
[ADD] migration: open every app as the neutralization test user
The public smoke test opens what a visitor reaches. It says nothing about
the back office, which is where a migration does most of its damage: a
field dropped from a model but still named in a form, a module installed
in the database whose code no longer ships with the target version. None
of it stops the module loading — it stops the day someone opens the app.
Neutralizing installs a `test` / `test` login carrying the SYSTEM user's
groups, and it survives the module's uninstall. So the tool checks for
that user rather than trusting a flag, and it rides the server the public
pass already started: booting Odoo is what costs minutes, not requests.
Measured on a real 18.0 database of 25 apps, which found four defects of
mine: web_search_read changed signature in 17, a bare 404 hides an
unregistered model, embedded sub-views are not the parent's fields, and
an empty psql result meant two different things.
--- FR ---
[ADD] migration : ouvrir chaque application avec l'utilisateur test
Le test de fumée public ouvre ce qu'un visiteur atteint. Il ne dit rien du
back-office, où une migration fait pourtant l'essentiel de ses dégâts : un
champ retiré du modèle mais toujours nommé dans un formulaire, un module
installé en base dont le code n'accompagne plus la version cible. Rien de
cela n'arrête le chargement ; cela arrête le jour où l'on ouvre l'appli.
La neutralisation pose un compte `test` / `test` portant les groupes du
superutilisateur, et il survit à la désinstallation du module. L'outil
vérifie donc cet utilisateur plutôt qu'un drapeau, et réutilise le serveur
déjà démarré : c'est le démarrage qui coûte, pas les requêtes.
Mesuré sur une vraie base 18.0 de 25 applications, ce qui a révélé quatre
défauts de mon fait.
Assisted-by: Claude Opus 5
2026-08-17 22:16:40 -04:00
|
|
|
|
"The server did not serve the login page.": {
|
|
|
|
|
|
"fr": "Le serveur n'a pas servi la page de connexion.",
|
|
|
|
|
|
"en": "The server did not serve the login page.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Wrong login or password for user": {
|
|
|
|
|
|
"fr": "Identifiant ou mot de passe refusé pour l'utilisateur",
|
|
|
|
|
|
"en": "Wrong login or password for user",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Could not read the menus": {
|
|
|
|
|
|
"fr": "Impossible de lire les menus",
|
|
|
|
|
|
"en": "Could not read the menus",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Apps opened as the test user": {
|
|
|
|
|
|
"fr": "Applications ouvertes avec l'utilisateur test",
|
|
|
|
|
|
"en": "Apps opened as the test user",
|
|
|
|
|
|
},
|
|
|
|
|
|
"not openable this way (client or server": {
|
|
|
|
|
|
"fr": "non ouvrables ainsi (action client ou",
|
|
|
|
|
|
"en": "not openable this way (client or server",
|
|
|
|
|
|
},
|
|
|
|
|
|
"action): nothing to render.": {
|
|
|
|
|
|
"fr": "serveur) : rien à rendre.",
|
|
|
|
|
|
"en": "action): nothing to render.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"opened with an empty domain: theirs": {
|
|
|
|
|
|
"fr": "ouvertes avec un domaine vide : le leur",
|
|
|
|
|
|
"en": "opened with an empty domain: theirs",
|
|
|
|
|
|
},
|
|
|
|
|
|
"needs the browser to evaluate it.": {
|
|
|
|
|
|
"fr": "demande le navigateur pour être évalué.",
|
|
|
|
|
|
"en": "needs the browser to evaluate it.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Every app opened its first page.": {
|
|
|
|
|
|
"fr": "Chaque application a ouvert sa première page.",
|
|
|
|
|
|
"en": "Every app opened its first page.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Apps that failed to open": {
|
|
|
|
|
|
"fr": "Applications qui n'ont pas pu s'ouvrir",
|
|
|
|
|
|
"en": "Apps that failed to open",
|
|
|
|
|
|
},
|
|
|
|
|
|
"reading the action": {
|
|
|
|
|
|
"fr": "lecture de l'action",
|
|
|
|
|
|
"en": "reading the action",
|
|
|
|
|
|
},
|
|
|
|
|
|
"rendering the views": {
|
|
|
|
|
|
"fr": "rendu des vues",
|
|
|
|
|
|
"en": "rendering the views",
|
|
|
|
|
|
},
|
|
|
|
|
|
"loading the first records": {
|
|
|
|
|
|
"fr": "chargement des premiers enregistrements",
|
|
|
|
|
|
"en": "loading the first records",
|
|
|
|
|
|
},
|
|
|
|
|
|
"These are back-office failures: the public smoke test": {
|
|
|
|
|
|
"fr": "Ce sont des échecs du back-office : le test de fumée public",
|
|
|
|
|
|
"en": "These are back-office failures: the public smoke test",
|
|
|
|
|
|
},
|
|
|
|
|
|
"cannot see them.": {
|
|
|
|
|
|
"fr": "ne peut pas les voir.",
|
|
|
|
|
|
"en": "cannot see them.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Could not log in as": {
|
|
|
|
|
|
"fr": "Connexion impossible en tant que",
|
|
|
|
|
|
"en": "Could not log in as",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No": {
|
|
|
|
|
|
"fr": "Aucun",
|
|
|
|
|
|
"en": "No",
|
|
|
|
|
|
},
|
|
|
|
|
|
"user in": {
|
|
|
|
|
|
"fr": "utilisateur dans",
|
|
|
|
|
|
"en": "user in",
|
|
|
|
|
|
},
|
|
|
|
|
|
"the database was not neutralized, nothing to browse.": {
|
|
|
|
|
|
"fr": "la base n'a pas été neutralisée, rien à parcourir.",
|
|
|
|
|
|
"en": "the database was not neutralized, nothing to browse.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Back office not browsed": {
|
|
|
|
|
|
"fr": "Back-office non parcouru",
|
|
|
|
|
|
"en": "Back office not browsed",
|
|
|
|
|
|
},
|
|
|
|
|
|
"no test user: the database was not neutralized": {
|
|
|
|
|
|
"fr": "pas d'utilisateur test : la base n'a pas été neutralisée",
|
|
|
|
|
|
"en": "no test user: the database was not neutralized",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Could not tell whether the": {
|
|
|
|
|
|
"fr": "Impossible de savoir si l'utilisateur",
|
|
|
|
|
|
"en": "Could not tell whether the",
|
|
|
|
|
|
},
|
|
|
|
|
|
"user exists in": {
|
|
|
|
|
|
"fr": "existe dans",
|
|
|
|
|
|
"en": "user exists in",
|
|
|
|
|
|
},
|
|
|
|
|
|
"could not tell whether the test user exists": {
|
|
|
|
|
|
"fr": "impossible de savoir si l'utilisateur test existe",
|
|
|
|
|
|
"en": "could not tell whether the test user exists",
|
|
|
|
|
|
},
|
|
|
|
|
|
"listing the model fields": {
|
|
|
|
|
|
"fr": "inventaire des champs du modèle",
|
|
|
|
|
|
"en": "listing the model fields",
|
|
|
|
|
|
},
|
|
|
|
|
|
"model absent from the running registry": {
|
|
|
|
|
|
"fr": "modèle absent du registre en cours",
|
|
|
|
|
|
"en": "model absent from the running registry",
|
|
|
|
|
|
},
|
|
|
|
|
|
"installed in the database, but its module code": {
|
|
|
|
|
|
"fr": "installé dans la base, mais le code de son module",
|
|
|
|
|
|
"en": "installed in the database, but its module code",
|
|
|
|
|
|
},
|
|
|
|
|
|
"is not in this addons path.": {
|
|
|
|
|
|
"fr": "n'est pas dans cet addons path.",
|
|
|
|
|
|
"en": "is not in this addons path.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Views naming a field the model no longer has": {
|
|
|
|
|
|
"fr": "Vues nommant un champ que le modèle n'a plus",
|
|
|
|
|
|
"en": "Views naming a field the model no longer has",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Public pages, then the back office as the test user": {
|
|
|
|
|
|
"fr": "Les pages publiques, puis le back-office avec l'utilisateur test",
|
|
|
|
|
|
"en": "Public pages, then the back office as the test user",
|
|
|
|
|
|
},
|
|
|
|
|
|
"if the database was neutralized.": {
|
|
|
|
|
|
"fr": "si la base a été neutralisée.",
|
|
|
|
|
|
"en": "if the database was neutralized.",
|
|
|
|
|
|
},
|
[ADD] migration: open /my as the test user, and blame the right request
The portal is a third rendering, neither the public site nor the back
office: QWeb frontend with counters that each query their own model. The
sitemap does not list /my — the page needs a session — and no RPC call
goes through it. A migration can break it with nothing else noticing.
Measured on a real database: /my answered 500 on a user field a module no
longer defines, while every public page and sixteen apps were fine.
Two guards the run proved necessary. Requested without a session, /my
redirects to the login form and answers 200: counting that as a success
would announce a healthy portal never seen. And the production error page
shows no traceback, so the reason comes from the server log — attributed
by PATH, because the portal is checked first and taking the last trace
blamed it for an app's failure. A wrong cause costs more than none.
--- FR ---
[ADD] migration : ouvrir /my avec l'utilisateur test, et accuser la bonne requête
Le portail est un troisième rendu, ni le site public ni le back-office :
du QWeb frontend, avec ses compteurs qui interrogent chacun leur modèle.
Le sitemap ne liste pas /my — la page demande une session — et aucun appel
RPC n'y passe. Une migration peut le casser sans que rien ne le dise.
Mesuré sur une vraie base : /my rendait 500 sur un champ utilisateur qu'un
module ne définit plus, alors que les pages publiques et seize
applications allaient bien.
Deux garde-fous que l'essai a rendus nécessaires. Sans session, /my
redirige vers le formulaire de connexion et rend 200 : le compter pour une
réussite annoncerait un portail jamais vu. Et la page d'erreur de
production ne montre aucune trace : la raison vient du journal, attribuée
par CHEMIN — le portail est interrogé en premier, et prendre la dernière
trace l'accusait de la panne d'une application.
Assisted-by: Claude Opus 5
2026-08-17 22:48:40 -04:00
|
|
|
|
"Portal": {
|
|
|
|
|
|
"fr": "Portail",
|
|
|
|
|
|
"en": "Portal",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Portal pages opened as the test user": {
|
|
|
|
|
|
"fr": "Pages du portail ouvertes avec l'utilisateur test",
|
|
|
|
|
|
"en": "Portal pages opened as the test user",
|
|
|
|
|
|
},
|
|
|
|
|
|
"not signed in": {
|
|
|
|
|
|
"fr": "pas connecté",
|
|
|
|
|
|
"en": "not signed in",
|
|
|
|
|
|
},
|
|
|
|
|
|
"the page served was the login form": {
|
|
|
|
|
|
"fr": "la page servie était le formulaire de connexion",
|
|
|
|
|
|
"en": "the page served was the login form",
|
|
|
|
|
|
},
|
|
|
|
|
|
"server log": {
|
|
|
|
|
|
"fr": "journal du serveur",
|
|
|
|
|
|
"en": "server log",
|
|
|
|
|
|
},
|
[FIX] database_cleanup: purge the batch, and say what you are doing
Seventeen minutes with no output looks exactly like an infinite loop. It
was not one: measured on the running process, zero lock waits and queries
changing every sample. It was working, silently, one module every thirty
seconds.
Both halves were mine. Purging line by line — added so one refusal could
not sink a category — calls button_immediate_uninstall() per line, and
that reloads the WHOLE registry: 5984 modules re-read each time. The
batch is now purged in one call, exactly as the OCA wizard intends, and
the per-line isolation only costs when a refusal actually happens.
And the tool captured its child's output, so nothing showed before the
end. It now relays each step as it arrives, with elapsed seconds, and a
timer kills the process group when the deadline passes.
--- FR ---
[FIX] database_cleanup : purger le lot, et dire ce qu'on fait
Dix-sept minutes sans une ligne ressemblent trait pour trait à une boucle
infinie. Ce n'en était pas une : mesuré sur le processus vivant, zéro
verrou en attente et des requêtes qui changeaient à chaque instantané. Il
travaillait, en silence, à raison d'un module toutes les trente secondes.
Les deux moitiés étaient de mon fait. Purger ligne par ligne — ajouté
pour qu'un refus n'emporte pas la catégorie — appelle
button_immediate_uninstall() par ligne, ce qui recharge TOUT le registre :
5984 modules relus à chaque fois. Le lot est désormais purgé en un seul
appel, comme le module OCA le prévoit, et l'isolement ne coûte que
lorsqu'un refus survient vraiment.
Et l'outil capturait la sortie de son enfant : rien avant la fin. Il
relaie maintenant chaque étape, temps écoulé compris.
Assisted-by: Claude Opus 5
2026-08-17 23:19:04 -04:00
|
|
|
|
"Purging modules reloads the whole registry: on a big": {
|
|
|
|
|
|
"fr": "Purger les modules recharge tout le registre : sur un grand",
|
|
|
|
|
|
"en": "Purging modules reloads the whole registry: on a big",
|
|
|
|
|
|
},
|
|
|
|
|
|
"addons path this takes minutes, not seconds.": {
|
|
|
|
|
|
"fr": "addons path cela prend des minutes, pas des secondes.",
|
|
|
|
|
|
"en": "addons path this takes minutes, not seconds.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Took": {
|
|
|
|
|
|
"fr": "Durée :",
|
|
|
|
|
|
"en": "Took",
|
|
|
|
|
|
},
|
|
|
|
|
|
"seconds.": {
|
|
|
|
|
|
"fr": "secondes.",
|
|
|
|
|
|
"en": "seconds.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"pass": {
|
|
|
|
|
|
"fr": "passe",
|
|
|
|
|
|
"en": "pass",
|
|
|
|
|
|
},
|
|
|
|
|
|
"The cleanup was still running after": {
|
|
|
|
|
|
"fr": "Le nettoyage tournait encore après",
|
|
|
|
|
|
"en": "The cleanup was still running after",
|
|
|
|
|
|
},
|
|
|
|
|
|
"seconds and was stopped.": {
|
|
|
|
|
|
"fr": "secondes et a été arrêté.",
|
|
|
|
|
|
"en": "seconds and was stopped.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"the whole batch": {
|
|
|
|
|
|
"fr": "le lot entier",
|
|
|
|
|
|
"en": "the whole batch",
|
|
|
|
|
|
},
|
[FIX] migration: repair, replay, and know when to stop
The failure that stops an upgrade is almost always the same one — a COW
copy left behind on the previous version — and the repair is known. So
Enter now repairs, and a repair that actually changed something replays
the command by itself.
A default that acts must know when to stop, and here it must twice over.
Repairing when there is nothing left to repair, then offering it again,
loops without end: measured, « no COW copy has drifted » over and over.
And a repair that does not help would replay the command forever. Three
attempts, then the prompt says plainly that this one needs a developer.
The turn counter is deliberately redundant with that logic: both guard
the same failure, but the counter holds even if the logic is broken one
day by accident. An endless loop in an unattended migration costs a
night.
Also: the smoke tool forced `ask=input` on its own prompt, which
short-circuited auto-run — the question waited for a keystroke nobody
was there to give.
--- FR ---
[FIX] migration : réparer, rejouer, et savoir s'arrêter
L'échec qui arrête une migration est presque toujours le même — une copie
COW restée sur la version d'avant — et la réparation est connue. Entrée
répare donc, et une réparation qui a vraiment changé quelque chose rejoue
la commande d'elle-même.
Un défaut qui agit doit savoir s'arrêter, et ici deux fois plutôt qu'une.
Réparer quand il n'y a plus rien à réparer, puis le reproposer, boucle
sans fin : mesuré, « Aucune copie COW n'a dérivé », encore et encore. Et
une réparation qui ne suffit pas rejouerait indéfiniment. Trois
tentatives, puis l'invite dit qu'il faut un développeur.
Le compteur de tours double volontairement cette logique : il tient même
si elle est cassée un jour par mégarde.
Assisted-by: Claude Opus 5
2026-08-18 00:08:31 -04:00
|
|
|
|
"Still failing after": {
|
|
|
|
|
|
"fr": "Échoue toujours après",
|
|
|
|
|
|
"en": "Still failing after",
|
|
|
|
|
|
},
|
|
|
|
|
|
"attempts: this one needs a developer.": {
|
|
|
|
|
|
"fr": "tentatives : celle-ci demande un développeur.",
|
|
|
|
|
|
"en": "attempts: this one needs a developer.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Error detected. Choose, or ctrl+c to": {
|
|
|
|
|
|
"fr": "Erreur détectée. Choisissez, ou ctrl+c pour",
|
|
|
|
|
|
"en": "Error detected. Choose, or ctrl+c to",
|
|
|
|
|
|
},
|
|
|
|
|
|
"stop": {
|
|
|
|
|
|
"fr": "arrêter",
|
|
|
|
|
|
"en": "stop",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Too many turns on this prompt: moving on.": {
|
|
|
|
|
|
"fr": "Trop de tours sur cette invite : on passe.",
|
|
|
|
|
|
"en": "Too many turns on this prompt: moving on.",
|
|
|
|
|
|
},
|
[FIX] migration: the module-upgrade step no longer waits forever
Six « press to continue » prompts on the module-code migration were bare
input() calls. Auto-run never reached them, so it stopped there and said
nothing — the question had been asked, after all. They now go through the
timed reader, where Enter and the countdown mean the same thing.
They were English too, on a path the rest of which speaks the system
language.
The guard that forbids a bare input() only looked at a handful of
methods, and internal_module_upgrade was not among them — which is
exactly how these six survived the first sweep. It is now covered, and
the module menu's own prompts are documented as deliberately blocking:
they ask for a module name, a path, a version, and « » after five
seconds would be a wrong answer, not a convenience.
--- FR ---
[FIX] migration : l'étape de migration de code n'attend plus indéfiniment
Six invites « appuyez pour continuer » de la migration de code étaient des
input() nus. L'auto-exécution ne les atteignait pas : elle s'arrêtait là
sans rien dire — la question avait bien été posée. Elles passent
désormais par le lecteur temporisé, où Entrée et le compte à rebours font
la même chose.
Elles étaient aussi en anglais, sur un chemin dont tout le reste parle la
langue du système.
Le garde-fou qui interdit les input() nus ne regardait qu'une poignée de
méthodes, et internal_module_upgrade n'en faisait pas partie — c'est
ainsi que ces six-là avaient survécu. Il la couvre maintenant, et les
invites du menu de modules sont documentées comme bloquantes à dessein :
elles demandent un nom, un chemin, une version.
Assisted-by: Claude Opus 5
2026-08-18 02:36:44 -04:00
|
|
|
|
"Please validate git commit on repos, press to": {
|
|
|
|
|
|
"fr": "Veuillez valider les commits sur les dépôts, appuyez pour",
|
|
|
|
|
|
"en": "Please validate git commit on repos, press to",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Check the migrated code, press to continue": {
|
|
|
|
|
|
"fr": "Vérifiez le code migré, appuyez pour continuer",
|
|
|
|
|
|
"en": "Check the migrated code, press to continue",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Please validate the repo is ready to run": {
|
|
|
|
|
|
"fr": "Veuillez valider que le dépôt est prêt pour",
|
|
|
|
|
|
"en": "Please validate the repo is ready to run",
|
|
|
|
|
|
},
|
|
|
|
|
|
"press to continue": {
|
|
|
|
|
|
"fr": "appuyez pour continuer",
|
|
|
|
|
|
"en": "press to continue",
|
|
|
|
|
|
},
|
[FIX] migration: a skipped back office must not read as a healthy one
The back-office pass was already there, and it works. What did not work
was the way it declined: one discreet line at the end of a long report,
saying « the database was not neutralized ». On six databases of a real
migration the test user is present up to the 15 bump and GONE at 17 and
18 — so the pass stopped silently exactly where a migration does the most
damage, and said something that was not even true.
The migration knows what it neutralized, so it now asks for the back
office by name. A missing test user on a database it neutralized is a
finding, printed loudly and counted as a failure. A missing tool is too:
returning None made the whole pass vanish without a word.
And it says UP FRONT which passes will run, on that database, by name.
--- FR ---
[FIX] migration : un back-office sauté ne doit pas se lire comme un sain
La passe back-office était déjà là et elle fonctionne. Ce qui ne
fonctionnait pas, c'est sa façon de renoncer : une ligne discrète en fin
d'un long rapport, disant « la base n'a pas été neutralisée ». Sur les six
bases d'une vraie migration, l'utilisateur test est présent jusqu'au
palier 15 et ABSENT en 17 et 18 — la passe s'arrêtait donc sans bruit là
où une migration fait le plus de dégâts, en disant quelque chose de faux.
La migration sait ce qu'elle a neutralisé : elle réclame désormais le
back-office. Un utilisateur test manquant sur une base qu'elle a
neutralisée est une trouvaille, affichée fort et comptée comme un échec.
Un outil absent aussi : rendre None faisait disparaître la passe entière.
Et elle annonce AVANT de lancer ce qui sera parcouru.
Assisted-by: Claude Opus 5
2026-08-19 03:21:25 -04:00
|
|
|
|
"smoke_internal_ui.py is missing": {
|
|
|
|
|
|
"fr": "smoke_internal_ui.py est absent",
|
|
|
|
|
|
"en": "smoke_internal_ui.py is missing",
|
|
|
|
|
|
},
|
|
|
|
|
|
"the test user is gone from a neutralized database: the back office was NOT checked": {
|
|
|
|
|
|
"fr": "l'utilisateur test a disparu d'une base neutralisée : le back-office n'a PAS été vérifié",
|
|
|
|
|
|
"en": "the test user is gone from a neutralized database: the back office was NOT checked",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Back office NOT browsed": {
|
|
|
|
|
|
"fr": "Back-office NON parcouru",
|
|
|
|
|
|
"en": "Back office NOT browsed",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Public pages, then the back office and /my as the": {
|
|
|
|
|
|
"fr": "Les pages publiques, puis le back-office et /my avec",
|
|
|
|
|
|
"en": "Public pages, then the back office and /my as the",
|
|
|
|
|
|
},
|
|
|
|
|
|
"test user (neutralized database).": {
|
|
|
|
|
|
"fr": "l'utilisateur test (base neutralisée).",
|
|
|
|
|
|
"en": "test user (neutralized database).",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Public pages only:": {
|
|
|
|
|
|
"fr": "Pages publiques seulement :",
|
|
|
|
|
|
"en": "Public pages only:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"was not neutralized, so there is no test user to": {
|
|
|
|
|
|
"fr": "n'a pas été neutralisée, il n'y a donc pas d'utilisateur test pour",
|
|
|
|
|
|
"en": "was not neutralized, so there is no test user to",
|
|
|
|
|
|
},
|
|
|
|
|
|
"sign in with.": {
|
|
|
|
|
|
"fr": "s'y connecter.",
|
|
|
|
|
|
"en": "sign in with.",
|
|
|
|
|
|
},
|
[ADD] migration: « t » shows where the migration stands
A migration crosses six bumps, runs hundreds of commands and lasts hours.
The journal said what had been LAUNCHED; it never said what came of it.
Three hours in, one reads two hundred command lines without knowing which
one failed, nor what the smoke test concluded.
« v » could not carry this: it already means « view the differences » in
three prompts, and a letter meaning two things is worse than a letter
meaning nothing. « t » was free, and it is the same everywhere — one
shared string carries both shortcuts, so no prompt can drift.
Looking is not answering: the same question comes back afterwards.
What was missing was the data. Failures and tool verdicts are now
recorded with the step they happened in, bounded so a progression file
cannot grow without end. A tool rerun after a repair keeps its LAST
verdict and the count of its runs — showing both without distinction
would read a repair as a lasting failure.
--- FR ---
[ADD] migration : « t » montre où en est la migration
Une migration traverse six paliers, lance des centaines de commandes et
dure des heures. Le journal disait ce qui avait été LANCÉ, jamais ce que
cela avait donné. Trois heures plus tard on relit deux cents lignes sans
savoir laquelle a échoué, ni ce que le test de fumée a conclu.
« v » ne pouvait pas porter cela : il veut déjà dire « voir les
différences » dans trois invites, et une lettre qui signifie deux choses
est pire qu'une lettre qui ne signifie rien. « t » était libre, et il est
le même partout — une seule chaîne porte les deux raccourcis.
Regarder n'est pas répondre : la même question revient ensuite.
Ce qui manquait, c'étaient les données. Les échecs et les verdicts
d'outils sont désormais retenus avec l'étape où ils se sont produits.
Assisted-by: Claude Opus 5
2026-08-19 03:53:56 -04:00
|
|
|
|
"Migration state": {
|
|
|
|
|
|
"fr": "État de la migration",
|
|
|
|
|
|
"en": "Migration state",
|
|
|
|
|
|
},
|
|
|
|
|
|
"started": {
|
|
|
|
|
|
"fr": "démarrée",
|
|
|
|
|
|
"en": "started",
|
|
|
|
|
|
},
|
|
|
|
|
|
"last written": {
|
|
|
|
|
|
"fr": "dernière écriture",
|
|
|
|
|
|
"en": "last written",
|
|
|
|
|
|
},
|
|
|
|
|
|
"current step": {
|
|
|
|
|
|
"fr": "étape courante",
|
|
|
|
|
|
"en": "current step",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Test results": {
|
|
|
|
|
|
"fr": "Résultat des tests",
|
|
|
|
|
|
"en": "Test results",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No tool has run yet.": {
|
|
|
|
|
|
"fr": "Aucun outil n'a encore tourné.",
|
|
|
|
|
|
"en": "No tool has run yet.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Commands that failed": {
|
|
|
|
|
|
"fr": "Commandes en échec",
|
|
|
|
|
|
"en": "Commands that failed",
|
|
|
|
|
|
},
|
|
|
|
|
|
"What was done, step by step": {
|
|
|
|
|
|
"fr": "Ce qui a été fait, étape par étape",
|
|
|
|
|
|
"en": "What was done, step by step",
|
|
|
|
|
|
},
|
|
|
|
|
|
"runs": {
|
|
|
|
|
|
"fr": "passages",
|
|
|
|
|
|
"en": "runs",
|
|
|
|
|
|
},
|
|
|
|
|
|
"nothing to report": {
|
|
|
|
|
|
"fr": "rien à signaler",
|
|
|
|
|
|
"en": "nothing to report",
|
|
|
|
|
|
},
|
|
|
|
|
|
"findings to look at": {
|
|
|
|
|
|
"fr": "des trouvailles à regarder",
|
|
|
|
|
|
"en": "findings to look at",
|
|
|
|
|
|
},
|
|
|
|
|
|
"the tool itself failed": {
|
|
|
|
|
|
"fr": "l'outil lui-même a échoué",
|
|
|
|
|
|
"en": "the tool itself failed",
|
|
|
|
|
|
},
|
|
|
|
|
|
"unknown result": {
|
|
|
|
|
|
"fr": "résultat inconnu",
|
|
|
|
|
|
"en": "unknown result",
|
|
|
|
|
|
},
|
|
|
|
|
|
"before the first step": {
|
|
|
|
|
|
"fr": "avant la première étape",
|
|
|
|
|
|
"en": "before the first step",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No migration in progress.": {
|
|
|
|
|
|
"fr": "Aucune migration en cours.",
|
|
|
|
|
|
"en": "No migration in progress.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"show the migration state": {
|
|
|
|
|
|
"fr": "afficher l'état de la migration",
|
|
|
|
|
|
"en": "show the migration state",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Nothing to show yet.": {
|
|
|
|
|
|
"fr": "Rien à montrer pour l'instant.",
|
|
|
|
|
|
"en": "Nothing to show yet.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"(b = go back a step, t = show the migration state)": {
|
|
|
|
|
|
"fr": "(b = revenir à une étape précédente,"
|
|
|
|
|
|
" t = afficher l'état de la migration)",
|
|
|
|
|
|
"en": "(b = go back a step, t = show the migration state)",
|
|
|
|
|
|
},
|
[ADD] migration: reach the state screen from the statistics too
The statistics screen answers « what was removed, and why ». The state
screen answers « where are we, and what broke ». Two neighbouring
questions asked at the same moment: separating them by a menu entry
rather than by two commands to remember is what makes them usable.
Same letter as the prompts. One that changes meaning from one screen to
the next is not learned, it is looked up — and nobody looks it up.
Adding it surfaced two ways that screen could not survive auto-run. The
interface question is the FIRST one asked after auto-run is switched on,
and it was a bare input(): an unattended migration stopped there before
it had begun. The statistics menu was one too. And the state screen
writes the progression before reading it — but the statistics screen runs
before the progression is even loaded, so that write failed on an
attribute that does not exist yet.
--- FR ---
[ADD] migration : atteindre l'état depuis les statistiques aussi
L'écran de statistiques répond « qu'a-t-on supprimé, et pourquoi ». Celui
de l'état répond « où en est-on, et qu'est-ce qui a cassé ». Deux
questions voisines, posées au même moment : les séparer par une entrée de
menu plutôt que par deux commandes à retenir est ce qui les rend
utilisables.
La même lettre que dans les invites. Une lettre qui change de sens d'un
écran à l'autre ne s'apprend pas, elle se cherche — et personne ne la
cherche.
L'ajout a révélé deux façons dont cet écran ne survivait pas au mode
auto : la question d'interface, PREMIÈRE posée après l'activation, était
un input() nu, et le menu des statistiques aussi. Et l'écran d'état écrit
la progression avant de la lire, alors que les statistiques s'ouvrent
avant même qu'elle ne soit chargée.
Assisted-by: Claude Opus 5
2026-08-19 04:05:03 -04:00
|
|
|
|
"Migration state (full screen)": {
|
|
|
|
|
|
"fr": "État de la migration (plein écran)",
|
|
|
|
|
|
"en": "Migration state (full screen)",
|
|
|
|
|
|
},
|
[FIX] migration: Enter exports the backup at the end
After several hours of migration the archive is what one wanted anyway,
and the prompt asked for it with an empty default: pressing Enter — or
letting auto-run answer — walked past the only artifact of the whole run.
The three-way answer moved into a function of its own, because the third
way is the one that traps. « n » refuses, « y » or Enter takes the
timestamped name, and EVERYTHING ELSE is a filename. So « non.zip » is a
file, not a refusal: comparing the whole answer rather than its first
letter is what keeps it one.
--- FR ---
[FIX] migration : Entrée exporte la sauvegarde à la fin
Au bout de plusieurs heures de migration, l'archive est ce qu'on voulait
de toute façon, et l'invite la proposait avec un défaut vide : appuyer sur
Entrée — ou laisser l'auto-exécution répondre — passait à côté du seul
artefact de toute la course.
La réponse à trois sens est devenue une fonction à part, car c'est le
troisième qui piège. « n » refuse, « y » ou Entrée prend le nom horodaté,
et TOUT LE RESTE est un nom de fichier. « non.zip » est donc un fichier et
non un refus : comparer la réponse entière plutôt que sa première lettre
est ce qui le garantit.
Assisted-by: Claude Opus 5
2026-08-19 04:20:13 -04:00
|
|
|
|
"Export a backup?": {
|
|
|
|
|
|
"fr": "Exporter une sauvegarde ?",
|
|
|
|
|
|
"en": "Export a backup?",
|
|
|
|
|
|
},
|
|
|
|
|
|
"or type filename.zip": {
|
|
|
|
|
|
"fr": "ou tapez nomfichier.zip",
|
|
|
|
|
|
"en": "or type filename.zip",
|
|
|
|
|
|
},
|
[FIX] smoke: judge the back office AFTER the repair, and say why it failed
No timeout was hit and no test was skipped. The back office failed for the
same reason the site did: the COW copy of website.submenu breaks
/web/login exactly as it breaks the public pages — both go through the
same layout. The pass simply ran BEFORE the reset, and unlike the URLs it
was never looked at again. It is now, on the same second server.
« Session expired » described the consequence and hid the cause. The login
page returned 500 and nothing said so, so one looks at the password. The
status of that page, and a missing CSRF token, are now named.
Diagnosing this, I started Odoo 18 on a 17 database and got 500 on all
thirty-seven URLs and on /web/login: a report that says the site is
entirely broken when nothing is. database_cleanup already refused that;
the smoke tools now share the same guard rather than reimplementing it.
--- FR ---
[FIX] smoke : juger le back-office APRÈS la réparation, et dire pourquoi
Aucun délai n'a été atteint et aucun test n'a été sauté. Le back-office a
échoué pour la raison même qui cassait le site : la copie COW de
website.submenu casse /web/login comme elle casse les pages publiques —
les deux passent par le même gabarit. La passe tournait avant la
réinitialisation et, contrairement aux URL, n'était jamais revue.
« Session expired » décrivait la conséquence et cachait la cause : la page
de connexion rendait 500 sans que rien ne le dise, et l'on cherchait du
côté du mot de passe.
En diagnostiquant, j'ai lancé un Odoo 18 sur une base 17 : 500 partout, un
rapport qui déclare le site entièrement cassé quand rien ne l'est.
database_cleanup refusait déjà cela ; les outils de fumée partagent
désormais sa garde au lieu d'en écrire une seconde.
Assisted-by: Claude Opus 5
2026-08-19 04:53:43 -04:00
|
|
|
|
"The login page itself failed": {
|
|
|
|
|
|
"fr": "La page de connexion elle-même a échoué",
|
|
|
|
|
|
"en": "The login page itself failed",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No CSRF token on the login page": {
|
|
|
|
|
|
"fr": "Aucun jeton CSRF sur la page de connexion",
|
|
|
|
|
|
"en": "No CSRF token on the login page",
|
|
|
|
|
|
},
|
|
|
|
|
|
"after the reset": {
|
|
|
|
|
|
"fr": "après la réinitialisation",
|
|
|
|
|
|
"en": "after the reset",
|
|
|
|
|
|
},
|
|
|
|
|
|
"The differential detection found nothing; resetting": {
|
|
|
|
|
|
"fr": "La détection différentielle n'a rien trouvé ; réinitialisation",
|
|
|
|
|
|
"en": "The differential detection found nothing; resetting",
|
|
|
|
|
|
},
|
|
|
|
|
|
"the requested key(s) anyway.": {
|
|
|
|
|
|
"fr": "de la ou des clés demandées malgré tout.",
|
|
|
|
|
|
"en": "the requested key(s) anyway.",
|
|
|
|
|
|
},
|
[ADD] migration state: l, p, r and the two resize keys
Four keys, and one bug behind them. « l » separates the commands from the
server log: the first says what was LAUNCHED, the second what it
ANSWERED, and an update writing tens of thousands of lines buries three
commands. « p » hides the summary panel, « - » and « + » move the split,
« r » re-reads the disk — the migration writes while one watches, and
closing the screen to see the next bump is what one ended up doing.
The « missing logs » had two causes, both real. A tail was shown without
saying it was one; it now names how many lines are hidden. And the first
two steps run before the database is named, so their logs landed under
« sans-nom » — outside the migration they belong to, invisible from the
screen. They are brought back the moment the name is known, appended
never overwritten.
The step 4 bug was already fixed: its log held all six bumps because only
print_step opened one, and the bump loop never calls it.
--- FR ---
[ADD] état de migration : l, p, r et les deux touches de taille
Quatre touches, et un défaut derrière. « l » sépare les commandes du
journal : la première dit ce qui a été LANCÉ, le second ce que cela a
RÉPONDU, et une mise à jour qui écrit des dizaines de milliers de lignes
enterre trois commandes. « p » cache le panneau de résumé, « - » et « + »
déplacent la séparation, « r » relit le disque — la migration écrit
pendant qu'on regarde.
Les « logs manquants » avaient deux causes. On montrait une fin sans dire
que c'en était une ; le nombre de lignes cachées est désormais nommé. Et
les deux premières étapes tournent avant que la base ne soit nommée :
leurs journaux atterrissaient sous « sans-nom », hors de la migration à
laquelle ils appartiennent. Ils la rejoignent dès que le nom est connu.
Assisted-by: Claude Opus 5
2026-08-19 06:23:20 -04:00
|
|
|
|
"lines": {
|
|
|
|
|
|
"fr": "lignes",
|
|
|
|
|
|
"en": "lines",
|
|
|
|
|
|
},
|
|
|
|
|
|
"press l to show": {
|
|
|
|
|
|
"fr": "l pour afficher",
|
|
|
|
|
|
"en": "press l to show",
|
|
|
|
|
|
},
|
|
|
|
|
|
"last": {
|
|
|
|
|
|
"fr": "dernières",
|
|
|
|
|
|
"en": "last",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Refresh": {
|
|
|
|
|
|
"fr": "Rafraîchir",
|
|
|
|
|
|
"en": "Refresh",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Logs": {
|
|
|
|
|
|
"fr": "Journaux",
|
|
|
|
|
|
"en": "Logs",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Panel": {
|
|
|
|
|
|
"fr": "Panneau",
|
|
|
|
|
|
"en": "Panel",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Wider": {
|
|
|
|
|
|
"fr": "Élargir",
|
|
|
|
|
|
"en": "Wider",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Narrower": {
|
|
|
|
|
|
"fr": "Rétrécir",
|
|
|
|
|
|
"en": "Narrower",
|
|
|
|
|
|
},
|
2026-08-19 06:37:14 -04:00
|
|
|
|
"finished": {
|
|
|
|
|
|
"fr": "terminée",
|
|
|
|
|
|
"en": "finished",
|
|
|
|
|
|
},
|
|
|
|
|
|
"duration": {
|
|
|
|
|
|
"fr": "durée",
|
|
|
|
|
|
"en": "duration",
|
|
|
|
|
|
},
|
[ADD] migration: announce the countdown, and cycle three panel states
Every prompt now carries « ⏱5s » before the question. One that looks like
it waits forever does not invite you to step away, which is the whole
point of auto-run. The same clock reports the answer afterwards: it
announces the wait, then says what it decided.
Enter on the auto-run question already meant no; it now says so, with an
explicit default instead of a deduction. And the flag is cleared before
asking — an ERPLIBRE_AUTO_EXECUTE inherited from the shell would have
answered yes on someone's behalf, and that answer commits every later one.
« p » cycles three states rather than toggling one: a plain toggle freed
four lines, while what takes the room is the left COLUMN — on a server log
every character of width counts. All → no summary → detail only, and back.
The state is named in the sub-title, which stays visible in all three: a
panel that vanishes without a word reads as a broken screen.
--- FR ---
[ADD] migration : annoncer le compte à rebours, et trois états de panneaux
Chaque invite porte désormais « ⏱5s » devant la question. Une invite qui a
l'air d'attendre indéfiniment n'invite pas à s'absenter, et c'est pourtant
tout l'intérêt du mode auto. Le même chronomètre rend compte ensuite.
Entrée sur la question d'auto-exécution voulait déjà dire non ; elle le
DIT maintenant, par un défaut explicite plutôt qu'une déduction. Et le
drapeau est effacé avant de demander : une variable héritée du shell
aurait répondu « oui » à la place de quelqu'un.
« p » enchaîne trois états au lieu d'en basculer un : ce qui prend la
place, c'est la colonne de gauche. Tout → sans résumé → détail seul, et
l'on revient. L'état est nommé dans le sous-titre.
Assisted-by: Claude Opus 5
2026-08-19 06:57:19 -04:00
|
|
|
|
"Panels": {
|
|
|
|
|
|
"fr": "Panneaux",
|
|
|
|
|
|
"en": "Panels",
|
|
|
|
|
|
},
|
|
|
|
|
|
"summary + list + detail": {
|
|
|
|
|
|
"fr": "résumé + liste + détail",
|
|
|
|
|
|
"en": "summary + list + detail",
|
|
|
|
|
|
},
|
|
|
|
|
|
"list + detail": {
|
|
|
|
|
|
"fr": "liste + détail",
|
|
|
|
|
|
"en": "list + detail",
|
|
|
|
|
|
},
|
|
|
|
|
|
"detail only": {
|
|
|
|
|
|
"fr": "détail seul",
|
|
|
|
|
|
"en": "detail only",
|
|
|
|
|
|
},
|
[ADD] analyse: what a migration gained and lost, step by step
A migration leaves one database per step and they all still exist, so the
tool compares them side by side rather than replaying anything. Six Odoo
starts would cost an hour, write to the databases and need the checkout
switched at each step; the same inspection in SQL takes under half a
second per database and touches nothing. Measured on a real migration:
seven databases surveyed in 3.8 seconds.
It reports what each step gained and lost — modules, models, views, menus,
attachments — and ends on the comparison of the ENDS, which is not the sum
of the steps: a module dropped at 15 and put back at 17 lost nothing, and
adding the steps would count it twice.
The signal that matters is rows. A module removed is visible; a table
going from four thousand rows to zero is visible nowhere.
Two rename heuristics were tried and rejected on real data before the one
that holds. Row count alone paired an account tag table with dms_directory
— both had seven rows. A shared five-letter word paired two cleanup
tables. Overall name similarity separates them. And a renamed table stays
in the loss list, annotated: removing it was the real danger, since a
wrong pairing would have hidden a genuine loss.
--- FR ---
[ADD] analyse : ce qu'une migration a gagné et perdu, palier par palier
Une migration laisse une base par palier et elles existent toutes encore :
l'outil les compare côte à côte plutôt que de rejouer quoi que ce soit.
Six démarrages d'Odoo coûteraient une heure et écriraient dans les bases ;
la même inspection en SQL prend moins d'une demi-seconde par base. Mesuré
sur une vraie migration : sept bases inspectées en 3,8 secondes.
Le bilan final compare les EXTRÉMITÉS, ce qui n'est pas la somme des
paliers : un module retiré en 15 puis remis en 17 n'a rien perdu.
Le signal qui compte, ce sont les lignes. Un module en moins se voit ; une
table qui passe de quatre mille lignes à zéro ne se voit nulle part.
Deux rapprochements de renommage ont été essayés et rejetés sur des
données réelles. Et une table renommée reste dans la liste des pertes,
annotée : l'en retirer était le vrai danger.
Assisted-by: Claude Opus 5
2026-08-19 08:21:12 -04:00
|
|
|
|
"errors in the log": {
|
|
|
|
|
|
"fr": "erreurs dans le journal",
|
|
|
|
|
|
"en": "errors in the log",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Migration quality, step by step": {
|
|
|
|
|
|
"fr": "Qualité de la migration, palier par palier",
|
|
|
|
|
|
"en": "Migration quality, step by step",
|
|
|
|
|
|
},
|
|
|
|
|
|
"database not found": {
|
|
|
|
|
|
"fr": "base introuvable",
|
|
|
|
|
|
"en": "database not found",
|
|
|
|
|
|
},
|
|
|
|
|
|
"attachment files missing from the filestore": {
|
|
|
|
|
|
"fr": "fichiers de pièces jointes absents du filestore",
|
|
|
|
|
|
"en": "attachment files missing from the filestore",
|
|
|
|
|
|
},
|
|
|
|
|
|
"From start to finish": {
|
|
|
|
|
|
"fr": "Du départ à l'arrivée",
|
|
|
|
|
|
"en": "From start to finish",
|
|
|
|
|
|
},
|
|
|
|
|
|
"not comparable: a database is missing": {
|
|
|
|
|
|
"fr": "non comparable : une base manque",
|
|
|
|
|
|
"en": "not comparable: a database is missing",
|
|
|
|
|
|
},
|
|
|
|
|
|
"probable table rename(s)": {
|
|
|
|
|
|
"fr": "renommage(s) de table probable(s)",
|
|
|
|
|
|
"en": "probable table rename(s)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"table(s) lost rows": {
|
|
|
|
|
|
"fr": "table(s) ont perdu des lignes",
|
|
|
|
|
|
"en": "table(s) lost rows",
|
|
|
|
|
|
},
|
|
|
|
|
|
"attachments": {
|
|
|
|
|
|
"fr": "pièces jointes",
|
|
|
|
|
|
"en": "attachments",
|
|
|
|
|
|
},
|
|
|
|
|
|
"probably renamed to": {
|
|
|
|
|
|
"fr": "probablement renommée en",
|
|
|
|
|
|
"en": "probably renamed to",
|
|
|
|
|
|
},
|
|
|
|
|
|
"steps": {
|
|
|
|
|
|
"fr": "paliers",
|
|
|
|
|
|
"en": "steps",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Migration": {
|
[FIX] todo analyse: give the migration entry its icon
An entry with no icon, in a menu where every other one has one, reads as
unfinished. The section gets 🚚 and the entry 📐 — the same symbol the
quality report already wears in its own title, because it is the same
thing.
The spacing follows a convention this menu keeps without stating it: an
icon of « neutral » width — 🗄, 🖼 — occupies one column in a terminal
and takes TWO spaces to line up, while a wide one — 📏, 📐 — takes one.
Getting it wrong shifts the line, and nothing says so until it is under
your eyes. A test now states it.
--- FR ---
[FIX] todo analyse : donner son icône à l'entrée de migration
Une entrée sans icône, dans un menu où toutes les autres en ont une, se
lit comme une entrée inachevée. La section reçoit 🚚 et l'entrée 📐 — le
symbole que le rapport de qualité porte déjà dans son propre titre,
puisque c'est la même chose.
L'espacement suit une convention que ce menu tient sans la dire : une
icône de largeur « neutre » — 🗄, 🖼 — s'affiche sur une colonne dans un
terminal et prend DEUX espaces pour s'aligner, une icône large — 📏, 📐 —
un seul. Se tromper décale la ligne, et rien ne le signale avant de
l'avoir sous les yeux. Un test l'énonce désormais.
Assisted-by: Claude Opus 5
2026-08-20 02:08:15 -04:00
|
|
|
|
"fr": "🚚 Migration",
|
|
|
|
|
|
"en": "🚚 Migration",
|
[ADD] analyse: what a migration gained and lost, step by step
A migration leaves one database per step and they all still exist, so the
tool compares them side by side rather than replaying anything. Six Odoo
starts would cost an hour, write to the databases and need the checkout
switched at each step; the same inspection in SQL takes under half a
second per database and touches nothing. Measured on a real migration:
seven databases surveyed in 3.8 seconds.
It reports what each step gained and lost — modules, models, views, menus,
attachments — and ends on the comparison of the ENDS, which is not the sum
of the steps: a module dropped at 15 and put back at 17 lost nothing, and
adding the steps would count it twice.
The signal that matters is rows. A module removed is visible; a table
going from four thousand rows to zero is visible nowhere.
Two rename heuristics were tried and rejected on real data before the one
that holds. Row count alone paired an account tag table with dms_directory
— both had seven rows. A shared five-letter word paired two cleanup
tables. Overall name similarity separates them. And a renamed table stays
in the loss list, annotated: removing it was the real danger, since a
wrong pairing would have hidden a genuine loss.
--- FR ---
[ADD] analyse : ce qu'une migration a gagné et perdu, palier par palier
Une migration laisse une base par palier et elles existent toutes encore :
l'outil les compare côte à côte plutôt que de rejouer quoi que ce soit.
Six démarrages d'Odoo coûteraient une heure et écriraient dans les bases ;
la même inspection en SQL prend moins d'une demi-seconde par base. Mesuré
sur une vraie migration : sept bases inspectées en 3,8 secondes.
Le bilan final compare les EXTRÉMITÉS, ce qui n'est pas la somme des
paliers : un module retiré en 15 puis remis en 17 n'a rien perdu.
Le signal qui compte, ce sont les lignes. Un module en moins se voit ; une
table qui passe de quatre mille lignes à zéro ne se voit nulle part.
Deux rapprochements de renommage ont été essayés et rejetés sur des
données réelles. Et une table renommée reste dans la liste des pertes,
annotée : l'en retirer était le vrai danger.
Assisted-by: Claude Opus 5
2026-08-19 08:21:12 -04:00
|
|
|
|
},
|
|
|
|
|
|
"Quality of a migration, step by step": {
|
[FIX] todo analyse: give the migration entry its icon
An entry with no icon, in a menu where every other one has one, reads as
unfinished. The section gets 🚚 and the entry 📐 — the same symbol the
quality report already wears in its own title, because it is the same
thing.
The spacing follows a convention this menu keeps without stating it: an
icon of « neutral » width — 🗄, 🖼 — occupies one column in a terminal
and takes TWO spaces to line up, while a wide one — 📏, 📐 — takes one.
Getting it wrong shifts the line, and nothing says so until it is under
your eyes. A test now states it.
--- FR ---
[FIX] todo analyse : donner son icône à l'entrée de migration
Une entrée sans icône, dans un menu où toutes les autres en ont une, se
lit comme une entrée inachevée. La section reçoit 🚚 et l'entrée 📐 — le
symbole que le rapport de qualité porte déjà dans son propre titre,
puisque c'est la même chose.
L'espacement suit une convention que ce menu tient sans la dire : une
icône de largeur « neutre » — 🗄, 🖼 — s'affiche sur une colonne dans un
terminal et prend DEUX espaces pour s'aligner, une icône large — 📏, 📐 —
un seul. Se tromper décale la ligne, et rien ne le signale avant de
l'avoir sous les yeux. Un test l'énonce désormais.
Assisted-by: Claude Opus 5
2026-08-20 02:08:15 -04:00
|
|
|
|
"fr": "📐 Qualité d'une migration, palier par palier",
|
|
|
|
|
|
"en": "📐 Quality of a migration, step by step",
|
[ADD] analyse: what a migration gained and lost, step by step
A migration leaves one database per step and they all still exist, so the
tool compares them side by side rather than replaying anything. Six Odoo
starts would cost an hour, write to the databases and need the checkout
switched at each step; the same inspection in SQL takes under half a
second per database and touches nothing. Measured on a real migration:
seven databases surveyed in 3.8 seconds.
It reports what each step gained and lost — modules, models, views, menus,
attachments — and ends on the comparison of the ENDS, which is not the sum
of the steps: a module dropped at 15 and put back at 17 lost nothing, and
adding the steps would count it twice.
The signal that matters is rows. A module removed is visible; a table
going from four thousand rows to zero is visible nowhere.
Two rename heuristics were tried and rejected on real data before the one
that holds. Row count alone paired an account tag table with dms_directory
— both had seven rows. A shared five-letter word paired two cleanup
tables. Overall name similarity separates them. And a renamed table stays
in the loss list, annotated: removing it was the real danger, since a
wrong pairing would have hidden a genuine loss.
--- FR ---
[ADD] analyse : ce qu'une migration a gagné et perdu, palier par palier
Une migration laisse une base par palier et elles existent toutes encore :
l'outil les compare côte à côte plutôt que de rejouer quoi que ce soit.
Six démarrages d'Odoo coûteraient une heure et écriraient dans les bases ;
la même inspection en SQL prend moins d'une demi-seconde par base. Mesuré
sur une vraie migration : sept bases inspectées en 3,8 secondes.
Le bilan final compare les EXTRÉMITÉS, ce qui n'est pas la somme des
paliers : un module retiré en 15 puis remis en 17 n'a rien perdu.
Le signal qui compte, ce sont les lignes. Un module en moins se voit ; une
table qui passe de quatre mille lignes à zéro ne se voit nulle part.
Deux rapprochements de renommage ont été essayés et rejetés sur des
données réelles. Et une table renommée reste dans la liste des pertes,
annotée : l'en retirer était le vrai danger.
Assisted-by: Claude Opus 5
2026-08-19 08:21:12 -04:00
|
|
|
|
},
|
|
|
|
|
|
"Quality": {
|
|
|
|
|
|
"fr": "Qualité",
|
|
|
|
|
|
"en": "Quality",
|
|
|
|
|
|
},
|
[FIX] migration state: open the quality screen in its own process
Pressing « k » crashed with « asyncio.run() cannot be called from a
running event loop ». `suspend()` hands the terminal over but does NOT
stop the loop, and `app.run()` calls `asyncio.run()` underneath. A
subprocess has its own loop and paints the terminal it was given.
My test asserted that suspend() was CALLED, and that structure was
correct — the launch inside it was not. Structure is not behaviour, and
the test that would have caught this is the one that presses the key.
It exists now, and a guard refuses the nested start with a sentence
instead of forty lines of traceback.
The screen module also lost its shebang: it has no main, so the line
claimed something that was not true, and the permission rule was right
to say so.
--- FR ---
[FIX] état de migration : ouvrir l'écran de qualité dans son processus
« k » plantait sur « asyncio.run() cannot be called from a running event
loop ». `suspend()` rend le terminal mais n'arrête PAS la boucle, et
`app.run()` appelle `asyncio.run()` en dessous. Un sous-processus a sa
propre boucle.
Mon test vérifiait que suspend() était APPELÉ — et cette structure était
juste ; c'est le lancement à l'intérieur qui ne l'était pas. La structure
n'est pas le comportement, et le test qui aurait attrapé cela est celui
qui presse la touche. Il existe désormais, et un garde-fou refuse le
lancement imbriqué par une phrase plutôt que par quarante lignes de trace.
Le module d'écran perd aussi son shebang : sans `main`, cette ligne
annonçait une intention qui n'existait pas.
Assisted-by: Claude Opus 5
2026-08-19 10:52:49 -04:00
|
|
|
|
"Already inside a running screen: open it in its own": {
|
|
|
|
|
|
"fr": "Déjà dans un écran en cours : l'ouvrir dans son propre",
|
|
|
|
|
|
"en": "Already inside a running screen: open it in its own",
|
|
|
|
|
|
},
|
|
|
|
|
|
"process instead.": {
|
|
|
|
|
|
"fr": "processus.",
|
|
|
|
|
|
"en": "process instead.",
|
|
|
|
|
|
},
|
[ADD] migration quality: name the missing files, and show every delta
« 254 attachment files missing » does not say which. « m » now lists
them, grouped by model AND FIELD — the field is the most useful thing in
the row, because it says which field lost its image. On a real migration
it splits into 234 res.country/image flags, 13 payment.provider logos,
and a handful of scattered attachments including one 255 kB event photo:
the first two are module data a reinstall restores, the last is not, and
the raw list put them on the same footing.
The metadata is read ONLY when asked. One more query per database would
lengthen a survey that runs in four seconds, for something one looks at
by asking for it.
And every figure of a step now carries its change against the previous
one. « 2283 views » is a number; « +61 » is information. The first step
gets none: inventing a zero there would claim a comparison that does not
exist.
--- FR ---
[ADD] qualité de migration : nommer les fichiers absents, et montrer les écarts
« 254 fichiers de pièces jointes absents » ne dit pas lesquels. « m » en
donne la liste, groupée par modèle ET PAR CHAMP — le champ est le
renseignement le plus utile de la ligne, car il dit lequel a perdu son
image. Sur une vraie migration : 234 drapeaux res.country/image, 13 logos
de fournisseurs de paiement, et une poignée de pièces jointes éparses dont
une photo d'événement de 255 ko. Les deux premiers groupes sont des
données de module qu'une réinstallation restaure, le dernier non.
Les métadonnées ne sont lues QU'À LA DEMANDE : une requête de plus par
base allongerait un parcours qui tient en quatre secondes.
Et chaque chiffre d'un palier porte son écart avec le précédent. « 2283
vues » est un nombre, « +61 » est une information. Le premier palier n'en
a pas : y inventer un zéro annoncerait une comparaison qui n'existe pas.
Assisted-by: Claude Opus 5
2026-08-20 01:52:35 -04:00
|
|
|
|
"Missing files": {
|
|
|
|
|
|
"fr": "Fichiers absents",
|
|
|
|
|
|
"en": "Missing files",
|
|
|
|
|
|
},
|
|
|
|
|
|
"every attachment file is present": {
|
|
|
|
|
|
"fr": "tous les fichiers de pièces jointes sont présents",
|
|
|
|
|
|
"en": "every attachment file is present",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Could not read their metadata.": {
|
|
|
|
|
|
"fr": "Impossible de lire leurs métadonnées.",
|
|
|
|
|
|
"en": "Could not read their metadata.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"by model and field": {
|
|
|
|
|
|
"fr": "par modèle et par champ",
|
|
|
|
|
|
"en": "by model and field",
|
|
|
|
|
|
},
|
|
|
|
|
|
"one by one": {
|
|
|
|
|
|
"fr": "un par un",
|
|
|
|
|
|
"en": "one by one",
|
|
|
|
|
|
},
|
|
|
|
|
|
"press m to list them": {
|
|
|
|
|
|
"fr": "m pour en voir la liste",
|
|
|
|
|
|
"en": "press m to list them",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Pick a step to see its missing files.": {
|
|
|
|
|
|
"fr": "Choisissez un palier pour voir ses fichiers absents.",
|
|
|
|
|
|
"en": "Pick a step to see its missing files.",
|
|
|
|
|
|
},
|
[ADD] migration quality: tell Odoo's own redesigns from real losses
« 81 tables lost rows » buried the questions worth asking. The largest of
them — ir_translation, 32 984 rows — is a redesign Odoo made in 16, when
translations moved into jsonb columns. Putting redesigns and real losses
on the same footing is the surest way not to see the second kind.
A map now names what Odoo moves or retires, and the report splits the
list in two: unexplained first, then what Odoo did on purpose. On a real
12 → 18 the split is 57 and 14, and the partition is exact — an explained
loss is annotated, never removed, because removing it was the one thing
that could hide a real one.
Every entry was VERIFIED on that migration by counting both sides, not
written from memory: crm_lead_tag 6 → crm_tag +6, mail_channel 26 →
discuss_channel +26, account_invoice 651 → account_move +441. A map
written from memory would explain losses that are not losses.
And a merge whose target gained nothing says so: the explanation does not
hold, and that is exactly what the map was meant to surface.
--- FR ---
[ADD] qualité de migration : distinguer les refontes d'Odoo des vraies pertes
« 81 tables ont perdu des lignes » enterrait les questions qui valaient la
peine. La plus grosse — ir_translation, 32 984 lignes — est une refonte
d'Odoo 16, quand les traductions sont passées en colonnes jsonb.
Une carte nomme désormais ce qu'Odoo déplace ou retire, et le rapport
sépare la liste en deux : l'inexpliqué d'abord, puis ce qu'Odoo a fait
exprès. Sur une vraie 12 → 18 : 57 et 14, et la partition est exacte —
une perte expliquée est annotée, jamais retirée.
Chaque entrée a été VÉRIFIÉE sur cette migration en comptant les deux
côtés : crm_lead_tag 6 → crm_tag +6, mail_channel 26 → discuss_channel
+26, account_invoice 651 → account_move +441.
Et une fusion dont la table d'accueil n'a rien reçu le dit : l'explication
ne tient pas, et c'est précisément ce que la carte devait révéler.
Assisted-by: Claude Opus 5
2026-08-20 02:42:18 -04:00
|
|
|
|
"table(s) lost rows, unexplained": {
|
|
|
|
|
|
"fr": "table(s) ont perdu des lignes, sans explication",
|
|
|
|
|
|
"en": "table(s) lost rows, unexplained",
|
|
|
|
|
|
},
|
[ADD] migration quality: explain ir_property and tracking values
ir_property never empties, it GROWS — 211 rows in 12, 457 in 17 — then
vanishes in 18. Its fields are jsonb columns there, verified on
res_partner.property_payment_term_id.
mail_tracking_value is a REAL loss, not a rename: `field` goes from
varchar to a foreign key in 14, and what would not resolve was deleted.
1528 of the 2336 belonged to the removed agile module; 706 more survived
under a new name. New kind "pruned" so the report cannot call a living
table retired.
--- FR ---
ir_property ne se vide jamais, elle GROSSIT — 211 lignes en 12, 457 en
17 — puis disparaît en 18. Ses champs y sont des colonnes jsonb, vérifié
sur res_partner.property_payment_term_id.
mail_tracking_value est une VRAIE perte, pas un renommage : `field`
passe de varchar à clé étrangère en 14, et ce qui ne se résolvait pas a
été supprimé. 1528 des 2336 venaient du module agile retiré ; 706 autres
ont survécu sous un autre nom. Nouveau genre « pruned » pour que le
rapport ne déclare pas retirée une table bien vivante.
Assisted-by: Claude Opus 5
2026-08-21 03:31:28 -04:00
|
|
|
|
"table(s) explained by an Odoo change": {
|
|
|
|
|
|
"fr": "table(s) expliquées par un changement d'Odoo",
|
|
|
|
|
|
"en": "table(s) explained by an Odoo change",
|
|
|
|
|
|
},
|
2026-08-21 05:00:38 -04:00
|
|
|
|
"Cannot read the database: ": {
|
|
|
|
|
|
"fr": "Lecture impossible de la base : ",
|
|
|
|
|
|
"en": "Cannot read the database: ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Cannot read the package file: ": {
|
|
|
|
|
|
"fr": "Lecture impossible du fichier de packages : ",
|
|
|
|
|
|
"en": "Cannot read the package file: ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Modules missing from the default package": {
|
|
|
|
|
|
"fr": "📦 Modules absents du package par défaut",
|
|
|
|
|
|
"en": "📦 Modules missing from the default package",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Show every entry": {
|
|
|
|
|
|
"fr": "Tout afficher",
|
|
|
|
|
|
"en": "Show every entry",
|
|
|
|
|
|
},
|
|
|
|
|
|
"List the known packages": {
|
|
|
|
|
|
"fr": "Lister les packages connus",
|
|
|
|
|
|
"en": "List the known packages",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Already repaired: the group exists.": {
|
|
|
|
|
|
"fr": "Déjà réparé : le groupe existe.",
|
|
|
|
|
|
"en": "Already repaired: the group exists.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"DMS access groups:": {
|
|
|
|
|
|
"fr": "Groupes d'accès DMS :",
|
|
|
|
|
|
"en": "DMS access groups:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"DMS documents in the database": {
|
|
|
|
|
|
"fr": "Documents DMS dans la base",
|
|
|
|
|
|
"en": "DMS documents in the database",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No DMS user to test visibility with.": {
|
|
|
|
|
|
"fr": "Aucun utilisateur DMS pour éprouver la visibilité.",
|
|
|
|
|
|
"en": "No DMS user to test visibility with.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Nothing written. Re-run with --apply.": {
|
|
|
|
|
|
"fr": "Rien d'écrit. Relancer avec --apply.",
|
|
|
|
|
|
"en": "Nothing written. Re-run with --apply.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Now visible to": {
|
|
|
|
|
|
"fr": "Désormais visibles pour",
|
|
|
|
|
|
"en": "Now visible to",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Visible to": {
|
|
|
|
|
|
"fr": "Visibles pour",
|
|
|
|
|
|
"en": "Visible to",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Would create one access group over": {
|
|
|
|
|
|
"fr": "Créerait un groupe d'accès sur",
|
|
|
|
|
|
"en": "Would create one access group over",
|
|
|
|
|
|
},
|
|
|
|
|
|
"file(s)": {
|
|
|
|
|
|
"fr": "fichier(s)",
|
|
|
|
|
|
"en": "file(s)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"folder(s)": {
|
|
|
|
|
|
"fr": "dossier(s)",
|
|
|
|
|
|
"en": "folder(s)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"root folder(s)": {
|
|
|
|
|
|
"fr": "dossier(s) racine",
|
|
|
|
|
|
"en": "root folder(s)",
|
|
|
|
|
|
},
|
2026-08-21 05:25:23 -04:00
|
|
|
|
"No DMS in this database, nothing to repair.": {
|
|
|
|
|
|
"fr": "Pas de DMS dans cette base, rien à réparer.",
|
|
|
|
|
|
"en": "No DMS in this database, nothing to repair.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Every one of them is visible to someone.": {
|
|
|
|
|
|
"fr": "Chacun est visible par quelqu'un.",
|
|
|
|
|
|
"en": "Every one of them is visible to someone.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No internal user to test visibility with.": {
|
|
|
|
|
|
"fr": "Aucun utilisateur interne pour éprouver la visibilité.",
|
|
|
|
|
|
"en": "No internal user to test visibility with.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"The data is there; a global rule hides all of it.": {
|
|
|
|
|
|
"fr": "Les données sont là ; une règle globale les masque entièrement.",
|
|
|
|
|
|
"en": "The data is there; a global rule hides all of it.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"checked against": {
|
|
|
|
|
|
"fr": "éprouvés avec",
|
|
|
|
|
|
"en": "checked against",
|
|
|
|
|
|
},
|
|
|
|
|
|
"internal user(s)": {
|
|
|
|
|
|
"fr": "utilisateur(s) interne(s)",
|
|
|
|
|
|
"en": "internal user(s)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"model(s) nobody can see a single row of": {
|
|
|
|
|
|
"fr": "modèle(s) dont personne ne voit une seule ligne",
|
|
|
|
|
|
"en": "model(s) nobody can see a single row of",
|
|
|
|
|
|
},
|
|
|
|
|
|
"model(s) with a global rule and some data,": {
|
|
|
|
|
|
"fr": "modèle(s) avec une règle globale et des données,",
|
|
|
|
|
|
"en": "model(s) with a global rule and some data,",
|
|
|
|
|
|
},
|
|
|
|
|
|
"row(s)": {
|
|
|
|
|
|
"fr": "ligne(s)",
|
|
|
|
|
|
"en": "row(s)",
|
|
|
|
|
|
},
|
[ADD] migration quality: lay OpenUpgrade's declared changes over the real ones
The report counted + and -. A field that stops being stored is neither:
it loses its column and keeps its existence. Read as a loss it cries
wolf every step; ignored it would mask a real one. OpenUpgrade already
knows, in the upgrade_analysis.txt files the checkout carries -- the
same source the OCA coverage pages are generated from, read locally, at
the checkout's exact version.
Three categories keep the signal from drowning: a field whose model went
away is not a second finding (544 of them on one real step), a field of
a module OpenUpgrade does not analyse was never its business, and the
rest is grouped by field name -- __last_update counted itself 391 times.
--- FR ---
Le rapport comptait des + et des -. Un champ qui cesse d'être stocké
n'est ni l'un ni l'autre : il perd sa colonne et garde son existence. Lu
comme une perte, il alarme à chaque palier ; ignoré, il en masquerait
une vraie. OpenUpgrade le sait déjà, dans les upgrade_analysis.txt que
porte le checkout -- la source des pages de couverture de l'OCA, lue
localement, à la version exacte.
Trois catégories évitent que le signal se noie : un champ dont le modèle
a disparu n'est pas une trouvaille de plus (544 sur un palier réel), un
champ d'un module non analysé n'a jamais relevé d'OpenUpgrade, et le
reste est groupé par nom -- __last_update se comptait 391 fois.
Assisted-by: Claude Opus 5
2026-08-21 05:55:54 -04:00
|
|
|
|
"Against OpenUpgrade": {
|
|
|
|
|
|
"fr": "Face à OpenUpgrade",
|
|
|
|
|
|
"en": "Against OpenUpgrade",
|
|
|
|
|
|
},
|
|
|
|
|
|
"NOT declared by OpenUpgrade": {
|
|
|
|
|
|
"fr": "NON déclarés par OpenUpgrade",
|
|
|
|
|
|
"en": "NOT declared by OpenUpgrade",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No OpenUpgrade analysis for this step.": {
|
|
|
|
|
|
"fr": "Pas d'analyse OpenUpgrade pour ce palier.",
|
|
|
|
|
|
"en": "No OpenUpgrade analysis for this step.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"became a per-company jsonb column": {
|
|
|
|
|
|
"fr": "passés en colonne jsonb par société",
|
|
|
|
|
|
"en": "became a per-company jsonb column",
|
|
|
|
|
|
},
|
|
|
|
|
|
"computed now — the field remains, the column does not": {
|
|
|
|
|
|
"fr": "devenus calculés — le champ demeure, la colonne non",
|
|
|
|
|
|
"en": "computed now — the field remains, the column does not",
|
|
|
|
|
|
},
|
|
|
|
|
|
"core module(s) analysed": {
|
|
|
|
|
|
"fr": "module(s) cœur analysé(s)",
|
|
|
|
|
|
"en": "core module(s) analysed",
|
|
|
|
|
|
},
|
|
|
|
|
|
"declared obsolete": {
|
|
|
|
|
|
"fr": "déclarés obsolètes",
|
|
|
|
|
|
"en": "declared obsolete",
|
|
|
|
|
|
},
|
|
|
|
|
|
"declared removed": {
|
|
|
|
|
|
"fr": "déclarés supprimés",
|
|
|
|
|
|
"en": "declared removed",
|
|
|
|
|
|
},
|
|
|
|
|
|
"field(s) lost": {
|
|
|
|
|
|
"fr": "champ(s) perdu(s)",
|
|
|
|
|
|
"en": "field(s) lost",
|
|
|
|
|
|
},
|
|
|
|
|
|
"model(s) lost": {
|
|
|
|
|
|
"fr": "modèle(s) perdu(s)",
|
|
|
|
|
|
"en": "model(s) lost",
|
|
|
|
|
|
},
|
|
|
|
|
|
"moved to another module": {
|
|
|
|
|
|
"fr": "déplacés vers un autre module",
|
|
|
|
|
|
"en": "moved to another module",
|
|
|
|
|
|
},
|
|
|
|
|
|
"renamed by Odoo": {
|
|
|
|
|
|
"fr": "renommés par Odoo",
|
|
|
|
|
|
"en": "renamed by Odoo",
|
|
|
|
|
|
},
|
|
|
|
|
|
"their model went away too": {
|
|
|
|
|
|
"fr": "leur modèle a disparu aussi",
|
|
|
|
|
|
"en": "their model went away too",
|
|
|
|
|
|
},
|
|
|
|
|
|
"in a module OpenUpgrade does not analyse": {
|
|
|
|
|
|
"fr": "dans un module qu'OpenUpgrade n'analyse pas",
|
|
|
|
|
|
"en": "in a module OpenUpgrade does not analyse",
|
|
|
|
|
|
},
|
|
|
|
|
|
"model(s)": {
|
|
|
|
|
|
"fr": "modèle(s)",
|
|
|
|
|
|
"en": "model(s)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"other field name(s)": {
|
|
|
|
|
|
"fr": "autre(s) nom(s) de champ",
|
|
|
|
|
|
"en": "other field name(s)",
|
|
|
|
|
|
},
|
2026-08-21 05:00:38 -04:00
|
|
|
|
"Census": {
|
|
|
|
|
|
"fr": "Recensement",
|
|
|
|
|
|
"en": "Census",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Compared against nothing — census only.": {
|
|
|
|
|
|
"fr": "Comparé à rien — recensement seul.",
|
|
|
|
|
|
"en": "Compared against nothing — census only.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Default package:": {
|
|
|
|
|
|
"fr": "Package par défaut :",
|
|
|
|
|
|
"en": "Default package:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Every suggested module is installed.": {
|
|
|
|
|
|
"fr": "Tous les modules suggérés sont installés.",
|
|
|
|
|
|
"en": "Every suggested module is installed.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Installed modules by author": {
|
|
|
|
|
|
"fr": "Modules installés, par auteur",
|
|
|
|
|
|
"en": "Installed modules by author",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Modules of": {
|
|
|
|
|
|
"fr": "Modules de",
|
|
|
|
|
|
"en": "Modules of",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No default package known for this version": {
|
|
|
|
|
|
"fr": "Aucun package par défaut connu pour cette version",
|
|
|
|
|
|
"en": "No default package known for this version",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Odoo": {
|
|
|
|
|
|
"fr": "Odoo",
|
|
|
|
|
|
"en": "Odoo",
|
|
|
|
|
|
},
|
|
|
|
|
|
"absent from the addons path — sync the repo first": {
|
|
|
|
|
|
"fr": "hors du chemin des addons — synchroniser le dépôt d'abord",
|
|
|
|
|
|
"en": "absent from the addons path — sync the repo first",
|
|
|
|
|
|
},
|
|
|
|
|
|
"also needs": {
|
|
|
|
|
|
"fr": "réclame aussi",
|
|
|
|
|
|
"en": "also needs",
|
|
|
|
|
|
},
|
|
|
|
|
|
"half-way — finish the pending update": {
|
|
|
|
|
|
"fr": "à mi-chemin — finir la mise à jour en attente",
|
|
|
|
|
|
"en": "half-way — finish the pending update",
|
|
|
|
|
|
},
|
|
|
|
|
|
"installed": {
|
|
|
|
|
|
"fr": "déjà en place",
|
|
|
|
|
|
"en": "installed",
|
|
|
|
|
|
},
|
|
|
|
|
|
"known": {
|
|
|
|
|
|
"fr": "connus",
|
|
|
|
|
|
"en": "known",
|
|
|
|
|
|
},
|
|
|
|
|
|
"known to the database — install it": {
|
|
|
|
|
|
"fr": "dans la base — à installer",
|
|
|
|
|
|
"en": "known to the database — install it",
|
|
|
|
|
|
},
|
|
|
|
|
|
"present but broken — fix the dependency": {
|
|
|
|
|
|
"fr": "en panne de dépendance — à réparer",
|
|
|
|
|
|
"en": "present but broken — fix the dependency",
|
|
|
|
|
|
},
|
|
|
|
|
|
"suggested module(s)": {
|
|
|
|
|
|
"fr": "module(s) suggéré(s)",
|
|
|
|
|
|
"en": "suggested module(s)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"suggested module(s) not installed": {
|
|
|
|
|
|
"fr": "module(s) suggéré(s) non installé(s)",
|
|
|
|
|
|
"en": "suggested module(s) not installed",
|
|
|
|
|
|
},
|
|
|
|
|
|
"unknown author": {
|
|
|
|
|
|
"fr": "auteur inconnu",
|
|
|
|
|
|
"en": "unknown author",
|
|
|
|
|
|
},
|
[ADD] migration quality: explain ir_property and tracking values
ir_property never empties, it GROWS — 211 rows in 12, 457 in 17 — then
vanishes in 18. Its fields are jsonb columns there, verified on
res_partner.property_payment_term_id.
mail_tracking_value is a REAL loss, not a rename: `field` goes from
varchar to a foreign key in 14, and what would not resolve was deleted.
1528 of the 2336 belonged to the removed agile module; 706 more survived
under a new name. New kind "pruned" so the report cannot call a living
table retired.
--- FR ---
ir_property ne se vide jamais, elle GROSSIT — 211 lignes en 12, 457 en
17 — puis disparaît en 18. Ses champs y sont des colonnes jsonb, vérifié
sur res_partner.property_payment_term_id.
mail_tracking_value est une VRAIE perte, pas un renommage : `field`
passe de varchar à clé étrangère en 14, et ce qui ne se résolvait pas a
été supprimé. 1528 des 2336 venaient du module agile retiré ; 706 autres
ont survécu sous un autre nom. Nouveau genre « pruned » pour que le
rapport ne déclare pas retirée une table bien vivante.
Assisted-by: Claude Opus 5
2026-08-21 03:31:28 -04:00
|
|
|
|
"rows dropped, the table remains": {
|
|
|
|
|
|
"fr": "lignes supprimées, la table demeure",
|
|
|
|
|
|
"en": "rows dropped, the table remains",
|
[ADD] migration quality: tell Odoo's own redesigns from real losses
« 81 tables lost rows » buried the questions worth asking. The largest of
them — ir_translation, 32 984 rows — is a redesign Odoo made in 16, when
translations moved into jsonb columns. Putting redesigns and real losses
on the same footing is the surest way not to see the second kind.
A map now names what Odoo moves or retires, and the report splits the
list in two: unexplained first, then what Odoo did on purpose. On a real
12 → 18 the split is 57 and 14, and the partition is exact — an explained
loss is annotated, never removed, because removing it was the one thing
that could hide a real one.
Every entry was VERIFIED on that migration by counting both sides, not
written from memory: crm_lead_tag 6 → crm_tag +6, mail_channel 26 →
discuss_channel +26, account_invoice 651 → account_move +441. A map
written from memory would explain losses that are not losses.
And a merge whose target gained nothing says so: the explanation does not
hold, and that is exactly what the map was meant to surface.
--- FR ---
[ADD] qualité de migration : distinguer les refontes d'Odoo des vraies pertes
« 81 tables ont perdu des lignes » enterrait les questions qui valaient la
peine. La plus grosse — ir_translation, 32 984 lignes — est une refonte
d'Odoo 16, quand les traductions sont passées en colonnes jsonb.
Une carte nomme désormais ce qu'Odoo déplace ou retire, et le rapport
sépare la liste en deux : l'inexpliqué d'abord, puis ce qu'Odoo a fait
exprès. Sur une vraie 12 → 18 : 57 et 14, et la partition est exacte —
une perte expliquée est annotée, jamais retirée.
Chaque entrée a été VÉRIFIÉE sur cette migration en comptant les deux
côtés : crm_lead_tag 6 → crm_tag +6, mail_channel 26 → discuss_channel
+26, account_invoice 651 → account_move +441.
Et une fusion dont la table d'accueil n'a rien reçu le dit : l'explication
ne tient pas, et c'est précisément ce que la carte devait révéler.
Assisted-by: Claude Opus 5
2026-08-20 02:42:18 -04:00
|
|
|
|
},
|
|
|
|
|
|
"retired from the database": {
|
|
|
|
|
|
"fr": "retirée de la base",
|
|
|
|
|
|
"en": "retired from the database",
|
|
|
|
|
|
},
|
|
|
|
|
|
"but it gained nothing": {
|
|
|
|
|
|
"fr": "mais elle n'a rien reçu",
|
|
|
|
|
|
"en": "but it gained nothing",
|
|
|
|
|
|
},
|
|
|
|
|
|
"there": {
|
|
|
|
|
|
"fr": "là-bas",
|
|
|
|
|
|
"en": "there",
|
|
|
|
|
|
},
|
[ADD] migration quality: biggest losses first, and the full lists behind « d »
Fifty-seven losses are read from the top, not in alphabetical order. They
are now sorted by rows LOST, not by rows there before: a table of ten
thousand that drops two matters less than one of a thousand that empties.
« d » cycles the pane through the complete lists — modules, models,
fields, COW copies, tables — and back to the summary. The summary cuts at
eight entries and is right to; but when one is looking for whether ONE
module survived, the truncated list does not answer, and that is exactly
when it is needed.
Two inventories were missing and both matter. FIELDS: a lost field is a
lost column of data, finer than a model, which can survive emptied of
half of its own. COW COPIES by key: it is the key one resets, and by the
key one finds them again across versions. On a real 12 → 18 they read
11 036 → 15 069 fields and 64 → 131 copies, of which 28 disappeared —
customisations nothing was reporting until now.
One mode replaces two flags: « show missing files » and « show the model
list » cannot both be true, and two booleans let that impossible state be
written. The mode is named in the sub-title, because a pane that changes
without saying why reads as a broken screen.
--- FR ---
[ADD] qualité de migration : les plus grosses pertes d'abord, les listes derrière « d »
Cinquante-sept pertes se lisent par le haut. Elles sont triées sur le
volume PERDU, non sur le volume présent avant : une table de dix mille qui
en perd deux compte moins qu'une de mille qui se vide.
« d » fait défiler le panneau à travers les listes entières — modules,
modèles, champs, copies COW, tables — puis revient au résumé.
Deux inventaires manquaient. Les CHAMPS : un champ perdu est une colonne
de données perdue. Les COPIES COW par leur clé : sur une vraie 12 → 18,
28 ont disparu — des personnalisations que rien ne signalait.
Un seul mode remplace deux drapeaux, qui laissaient écrire un état
impossible. Il est nommé dans le sous-titre.
Assisted-by: Claude Opus 5
2026-08-20 03:47:02 -04:00
|
|
|
|
"lost": {
|
|
|
|
|
|
"fr": "perdus",
|
|
|
|
|
|
"en": "lost",
|
|
|
|
|
|
},
|
|
|
|
|
|
"gained": {
|
|
|
|
|
|
"fr": "gagnés",
|
|
|
|
|
|
"en": "gained",
|
|
|
|
|
|
},
|
|
|
|
|
|
"fields": {
|
|
|
|
|
|
"fr": "champs",
|
|
|
|
|
|
"en": "fields",
|
|
|
|
|
|
},
|
|
|
|
|
|
"cow": {
|
|
|
|
|
|
"fr": "copies COW",
|
|
|
|
|
|
"en": "cow",
|
|
|
|
|
|
},
|
|
|
|
|
|
"tables": {
|
|
|
|
|
|
"fr": "tables",
|
|
|
|
|
|
"en": "tables",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Details": {
|
|
|
|
|
|
"fr": "Détails",
|
|
|
|
|
|
"en": "Details",
|
|
|
|
|
|
},
|
|
|
|
|
|
"summary": {
|
|
|
|
|
|
"fr": "résumé",
|
|
|
|
|
|
"en": "summary",
|
|
|
|
|
|
},
|
[ADD] migration: run the OCA database cleanup before testing the pages
The eight purges are not independent: purging a model frees the columns
that referenced it, purging a table frees the data pointing at it. One
pass is never enough, so the requested order runs again until a full
pass repairs nothing new.
Refusals are expected — a foreign key still holds, a module says no.
Purging a list at once loses everything to the first one, so each entry
purges inside its own savepoint: a refusal rolls back that entry alone.
What one pass could not take, the next may, once its neighbours are
gone. Leftovers are reported as a warning: a database can carry some
that nothing removes, and stopping there would help no one.
It refuses a version mismatch. Measured while building it: a shell on
Odoo 14 opened against a 17.0 database went rewriting ir_model before
dying on a jsonb it did not know. An older Odoo does not merely fail on
a newer database — it writes on the way.
--- FR ---
[ADD] migration : lancer le nettoyage OCA avant de tester les pages
Les huit purges ne sont pas indépendantes : purger un modèle libère les
colonnes qui le référençaient, purger une table libère les données qui
la visaient. Une passe ne suffit jamais, l'ordre demandé est donc rejoué
jusqu'à ce qu'une passe entière ne répare plus rien.
Les refus sont attendus — une clé étrangère tient, un module dit non.
Purger d'un bloc perdrait tout au premier : chaque entrée passe dans son
propre point de reprise, un refus n'emporte que la sienne. Ce qu'une
passe n'a pu prendre, la suivante le peut, une fois les voisines
parties. Les restes sont un avertissement : une base peut en porter que
rien ne retire, et s'arrêter là n'aiderait personne.
Il refuse une version qui ne correspond pas. Mesuré en le construisant :
un shell en Odoo 14 ouvert sur une base 17.0 est parti réécrire ir_model
avant de mourir sur un jsonb inconnu de lui. Un Odoo plus ancien
n'échoue pas simplement sur une base plus récente — il écrit en chemin.
Assisted-by: Claude Opus 5
2026-08-17 09:05:03 -04:00
|
|
|
|
"Clean the database before testing the pages?": {
|
|
|
|
|
|
"fr": "Nettoyer la base avant de tester les pages ?",
|
|
|
|
|
|
"en": "Clean the database before testing the pages?",
|
|
|
|
|
|
},
|
[ADD] migration: an auto-run that takes the default after five seconds
A migration asks dozens of questions whose answer is almost always the
one offered, and someone has to stand there pressing Enter for hours.
Asked before the version choice — so it covers that choice too, now
that it has a default — and off by default: it decides in your place.
Every prompt of the migration goes through one reader, seventeen of
them. A prompt left as a bare input() would know nothing of auto mode
and would block the run with nothing to say why; a test reads the
function and rejects any that appears.
click.prompt had to go: it cannot hand back control after a delay, so
auto would have stopped at the first question. select() rather than a
thread or an alarm — a thread would leave a blocked input() behind it,
stealing the next keystroke.
--- FR ---
[ADD] migration : une auto-exécution qui prend le défaut après cinq secondes
Une migration pose des dizaines de questions dont la réponse est presque
toujours celle proposée, et il faut rester devant à taper Entrée pendant
des heures. Posée avant le choix de version — donc elle vaut aussi pour
lui, maintenant qu'il a un défaut — et éteinte par défaut : elle décide
à votre place.
Toutes les invites de la migration passent par une seule lecture,
dix-sept d'un coup. Une invite laissée en input() nu ne saurait rien du
mode auto et bloquerait la migration sans rien dire ; un test lit la
fonction et refuse toute apparition.
click.prompt devait partir : il ne sait pas rendre la main après un
délai, l'auto se serait arrêté à la première question. select() plutôt
qu'un fil ou une alarme — un fil laisserait un input() bloqué derrière
lui, qui volerait la frappe suivante.
Assisted-by: Claude Opus 5
2026-08-17 17:35:31 -04:00
|
|
|
|
"Auto-run: take the default answer after": {
|
|
|
|
|
|
"fr": "Auto-exécution : prendre la réponse par défaut après",
|
|
|
|
|
|
"en": "Auto-run: take the default answer after",
|
|
|
|
|
|
},
|
|
|
|
|
|
"seconds?": {
|
|
|
|
|
|
"fr": "secondes ?",
|
|
|
|
|
|
"en": "seconds?",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Auto-run on: each prompt waits": {
|
|
|
|
|
|
"fr": "Auto-exécution activée : chaque invite attend",
|
|
|
|
|
|
"en": "Auto-run on: each prompt waits",
|
|
|
|
|
|
},
|
|
|
|
|
|
"seconds, then takes its default.": {
|
|
|
|
|
|
"fr": "secondes, puis prend son défaut.",
|
|
|
|
|
|
"en": "seconds, then takes its default.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"(default)": {
|
|
|
|
|
|
"fr": "(défaut)",
|
|
|
|
|
|
"en": "(default)",
|
|
|
|
|
|
},
|
[FIX] migration: the cleanup installs its own module, and survives a refusal
Three defects, all mine, all found on a real run.
Creating a wizard could fail and leave the transaction ABORTED. The next
name read died on it, outside any guard, and the whole script stopped
with no report at all — only a traceback. Creating and reading the names
now happen inside the savepoint, and a report is printed whatever
happens: knowing what was done matters more than the trace of what
broke.
« No orphaned models found » is a UserError: the module signals the
EMPTY by raising. Counting it as a failure made a healthy database look
broken, four warnings out of five kinds.
The module was installed at step 3, after being used at step 2, so the
first run had no wizard at all and answered « nothing to do ». It is now
installed by the tool itself, and step 3 no longer asks to redo by hand
what has just been done automatically.
--- FR ---
[FIX] migration : le nettoyage pose son module, et survit à un refus
Trois défauts, tous à moi, tous trouvés sur une vraie exécution.
Créer un assistant pouvait échouer en laissant la transaction AVORTÉE.
La lecture de nom suivante mourait dessus, hors de tout garde, et le
script s'arrêtait sans aucun rapport — juste une trace. La création et
la lecture des noms sont désormais dans le point de reprise, et un
rapport est imprimé quoi qu'il arrive : savoir ce qui a été fait vaut
mieux que la trace de ce qui a cassé.
« No orphaned models found » est une UserError : le module signale le
VIDE en levant. Le compter comme un échec faisait passer une base saine
pour cassée, quatre avertissements sur cinq catégories.
Le module était installé à l'étape 3, après avoir servi à l'étape 2 : le
premier passage n'avait donc aucun assistant et répondait « rien à
faire ». L'outil le pose lui-même, et l'étape 3 ne demande plus de
refaire à la main ce qui vient d'être fait.
Assisted-by: Claude Opus 5
2026-08-17 17:45:04 -04:00
|
|
|
|
"database_cleanup is not installed on this base;": {
|
|
|
|
|
|
"fr": "database_cleanup n'est pas installé sur cette base ;",
|
|
|
|
|
|
"en": "database_cleanup is not installed on this base;",
|
|
|
|
|
|
},
|
|
|
|
|
|
"installing it first.": {
|
|
|
|
|
|
"fr": "installation préalable.",
|
|
|
|
|
|
"en": "installing it first.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Could not install database_cleanup.": {
|
|
|
|
|
|
"fr": "Impossible d'installer database_cleanup.",
|
|
|
|
|
|
"en": "Could not install database_cleanup.",
|
|
|
|
|
|
},
|
[IMP] migration: say when the COW question is actually asked
An early check warns, hours before the bump, that a copy will break it. It
then says « arbitrate BEFORE launching the migration » and hands over a raw
UPDATE — and no question follows. Read on a resumed run it looks like the
question already went by and was skipped, which is exactly how it was read.
It now says the migration asks at the bump itself, and shows what each copy
holds before the answer. The manual UPDATE stays, but after the two commands
that do it reversibly: telling someone to hand-write SQL when --restore exists
is offering the sharper tool first.
--- FR ---
Un contrôle précoce prévient, des heures avant le palier, qu'une copie va le
casser. Il dit ensuite « arbitrez AVANT de lancer la migration » et livre un
UPDATE brut — et aucune question ne suit. Lu au cours d'une reprise, on croit
que la question est passée et a été sautée, ce qui est exactement la lecture
qui en a été faite.
Il dit maintenant que la migration pose la question au palier lui-même, en
montrant ce que chaque copie contient avant qu'on réponde. L'UPDATE manuel
reste, mais après les deux commandes qui le font de façon réversible : dire à
quelqu'un d'écrire du SQL à la main quand --restore existe, c'est tendre
l'outil le plus coupant en premier.
Assisted-by: Claude Opus 5
2026-08-12 01:36:47 -04:00
|
|
|
|
"Nothing to decide yet": {
|
|
|
|
|
|
"fr": "Rien à décider pour l'instant",
|
|
|
|
|
|
"en": "Nothing to decide yet",
|
|
|
|
|
|
},
|
|
|
|
|
|
"the migration will offer to neutralize these copies": {
|
|
|
|
|
|
"fr": "la migration proposera de neutraliser ces copies",
|
|
|
|
|
|
"en": "the migration will offer to neutralize these copies",
|
|
|
|
|
|
},
|
|
|
|
|
|
"at the version bump itself, showing what each one holds.": {
|
|
|
|
|
|
"fr": "au moment du palier lui-même, en montrant ce que chacune"
|
|
|
|
|
|
" contient.",
|
|
|
|
|
|
"en": "at the version bump itself, showing what each one holds.",
|
|
|
|
|
|
},
|
[ADD] migration: go back a step from a prompt, and name the DB
Realising at a prompt that an earlier step deserved another answer had one
way out: Ctrl+C. That leaves the progression as it stands and makes you find
the resume screen again to rewind. « b » does it properly — it shows the
steps, rewinds the state, writes it, and says what to relaunch.
Cancelling that must not stop the migration, which is the trap: it returns to
the same prompt, exactly where you were. Removing that guard makes two tests
fail.
The COW warning printed « -d DB -t odooXX.0 » for commands meant to be pasted.
It knows both values, so it prints them, and offers --shape as well since that
is half the answer.
Checked on the four outcomes: a normal answer passes through, « b » then a
step rewinds and stops, cancelling and an unknown step both continue. 15 tests.
--- FR ---
S'apercevoir à une invite qu'une étape antérieure méritait un autre choix
n'avait qu'une issue : Ctrl+C. Cela laisse la progression telle quelle et
oblige à retrouver l'écran de reprise pour rembobiner. « b » le fait
proprement — il montre les étapes, rembobine l'état, l'écrit, et dit quoi
relancer.
Y renoncer ne doit pas arrêter la migration, et c'est le piège : on revient à
la même invite, exactement là où l'on était. Retirer ce garde-fou fait tomber
deux tests.
L'avertissement COW affichait « -d DB -t odooXX.0 » pour des commandes faites
pour être collées. Il connaît les deux valeurs, donc il les écrit, et propose
aussi --shape puisque c'est la moitié de la réponse.
Vérifié sur les quatre issues : une réponse normale passe, « b » puis une
étape rembobine et arrête, annuler et un choix inconnu continuent. 15 tests.
Assisted-by: Claude Opus 5
2026-08-12 01:47:44 -04:00
|
|
|
|
"Press to continue": {
|
|
|
|
|
|
"fr": "Appuyez pour continuer",
|
|
|
|
|
|
"en": "Press to continue",
|
|
|
|
|
|
},
|
|
|
|
|
|
"(b = go back to a previous step)": {
|
|
|
|
|
|
"fr": "(b = revenir à une étape précédente)",
|
|
|
|
|
|
"en": "(b = go back to a previous step)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Neutralize database, press to continue": {
|
|
|
|
|
|
"fr": "Neutraliser la base, appuyez pour continuer",
|
|
|
|
|
|
"en": "Neutralize database, press to continue",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Replay from which step? (empty to cancel)": {
|
|
|
|
|
|
"fr": "Rejouer à partir de quelle étape ? (vide pour annuler)",
|
|
|
|
|
|
"en": "Replay from which step? (empty to cancel)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Rewound.": {"fr": "Rembobiné.", "en": "Rewound."},
|
|
|
|
|
|
"Relaunch the migration to resume from there.": {
|
|
|
|
|
|
"fr": "Relancez la migration pour reprendre à partir de là.",
|
|
|
|
|
|
"en": "Relaunch the migration to resume from there.",
|
|
|
|
|
|
},
|
[ADD] migration: offer « go back to a step » on the resume screen
The screen could already replay from any step -- Enter on a row does it --
but that lived in a hint line under four named buttons. A capability you
have to guess is not offered, and it was reported as missing. It now has a
key, a button and a message, like the four others.
The b key added earlier went to the line-by-line prompts during the run,
not to this screen. Same letter on both, so going back is one key wherever
you are.
Two defects behind it. Replaying the chosen step actually SKIPPED it,
resuming at the next one. And a replayed step did not rewind the variables
it owns, so the second run started from the first one's leftovers.
Checked in a simulated terminal: b focuses the table, two arrows and Enter
return step 2, and the button focuses without choosing.
--- FR ---
L'écran savait déjà rejouer depuis n'importe quelle étape — Entrée sur une
rangée le fait — mais cela vivait dans une ligne d'indication sous quatre
boutons nommés. Une capacité qu'il faut deviner n'est pas offerte, et elle
a été signalée manquante. Elle a maintenant une touche, un bouton et un
message, comme les quatre autres.
La touche b ajoutée plus tôt s'adressait aux invites ligne à ligne pendant
l'exécution, pas à cet écran. Même lettre sur les deux : revenir en
arrière est une seule touche, où qu'on soit.
Deux défauts derrière cela. Rejouer l'étape choisie la SAUTAIT en fait,
reprenant à la suivante. Et une étape rejouée ne remettait pas à zéro les
variables qui lui appartiennent, si bien que la seconde exécution partait
des restes de la première.
Vérifié en terminal simulé : b donne le focus au tableau, deux flèches et
Entrée ramènent l'étape 2, et le bouton met le focus sans choisir.
Assisted-by: Claude Opus 5
2026-08-12 01:58:38 -04:00
|
|
|
|
"Go back to a step": {
|
|
|
|
|
|
"fr": "Revenir à une étape",
|
|
|
|
|
|
"en": "Go back to a step",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Choose a step, Enter replays from there.": {
|
|
|
|
|
|
"fr": "Choisissez une étape, Entrée rejoue à partir de là.",
|
|
|
|
|
|
"en": "Choose a step, Enter replays from there.",
|
|
|
|
|
|
},
|
[ADD] analyse: inspect an Odoo database without restoring it
A read-only toolkit answering « what is in this database, and what will
break on a version bump »: schema size and tables no model claims,
customised views compared with their module source, and the x_ fields
Studio left behind. It reads a backup zip directly, so a 40 GB dump needs
no restore, and it reaches every answer from the TODO menu.
--- FR ---
Une boîte à outils en lecture seule qui répond à « qu'y a-t-il dans cette
base, et qu'est-ce qui cassera à la montée de version » : taille du schéma
et tables qu'aucun modèle ne réclame, vues personnalisées comparées à leur
source, champs x_ laissés par Studio. Elle lit un zip de sauvegarde tel
quel, donc sans restaurer, et tout s'atteint depuis le menu TODO.
Assisted-by: Claude Opus 5
2026-08-10 03:10:50 -04:00
|
|
|
|
# --- script/analyse : vues personnalisées ---
|
|
|
|
|
|
"Customised views": {
|
|
|
|
|
|
"fr": "Vues personnalisées",
|
|
|
|
|
|
"en": "Customised views",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Views": {"fr": "Vues", "en": "Views"},
|
|
|
|
|
|
"why": {"fr": "pourquoi", "en": "why"},
|
|
|
|
|
|
"more": {"fr": "de plus", "en": "more"},
|
|
|
|
|
|
"From an installed theme": {
|
|
|
|
|
|
"fr": "Posée par un thème installé",
|
|
|
|
|
|
"en": "From an installed theme",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Website copy (COW)": {
|
|
|
|
|
|
"fr": "Copie de site web (COW)",
|
|
|
|
|
|
"en": "Website copy (COW)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Made with Studio": {"fr": "Faite avec Studio", "en": "Made with Studio"},
|
|
|
|
|
|
"Imported or exported": {
|
|
|
|
|
|
"fr": "Importée ou exportée",
|
|
|
|
|
|
"en": "Imported or exported",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Created from the interface": {
|
|
|
|
|
|
"fr": "Créée depuis l'interface",
|
|
|
|
|
|
"en": "Created from the interface",
|
|
|
|
|
|
},
|
|
|
|
|
|
"From a module, flagged as touched": {
|
|
|
|
|
|
"fr": "D'un module, signalée comme retouchée",
|
|
|
|
|
|
"en": "From a module, flagged as touched",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Straight from a module": {
|
|
|
|
|
|
"fr": "Telle quelle depuis un module",
|
|
|
|
|
|
"en": "Straight from a module",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Every view comes straight from a module.": {
|
|
|
|
|
|
"fr": "Toutes les vues viennent telles quelles d'un module.",
|
|
|
|
|
|
"en": "Every view comes straight from a module.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Views that did not come straight from a module": {
|
|
|
|
|
|
"fr": "Vues qui ne viennent pas telles quelles d'un module",
|
|
|
|
|
|
"en": "Views that did not come straight from a module",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Website copies are user data: Odoo copies a view instead of editing it."
|
|
|
|
|
|
" Whether they will survive the next version is another question, and"
|
|
|
|
|
|
" these tools answer it:": {
|
|
|
|
|
|
"fr": "Les copies de site web sont des données utilisateur : Odoo"
|
|
|
|
|
|
" copie une vue au lieu de la modifier. Savoir si elles survivront à"
|
|
|
|
|
|
" la prochaine version est une autre question, et ces outils y"
|
|
|
|
|
|
" répondent :",
|
|
|
|
|
|
"en": "Website copies are user data: Odoo copies a view instead of"
|
|
|
|
|
|
" editing it. Whether they will survive the next version is another"
|
|
|
|
|
|
" question, and these tools answer it:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Flags say a view was touched, not how. They are incomplete both ways: a"
|
|
|
|
|
|
" direct SQL write does not set arch_updated, and reset_arch clears it."
|
|
|
|
|
|
" Comparing with the module source is what settles it.": {
|
|
|
|
|
|
"fr": "Les drapeaux disent qu'une vue a été touchée, pas comment. Ils"
|
|
|
|
|
|
" sont incomplets dans les deux sens : un write SQL direct n'arme pas"
|
|
|
|
|
|
" arch_updated, et reset_arch l'efface. Seule la comparaison avec la"
|
|
|
|
|
|
" source du module tranche.",
|
|
|
|
|
|
"en": "Flags say a view was touched, not how. They are incomplete"
|
|
|
|
|
|
" both ways: a direct SQL write does not set arch_updated, and"
|
|
|
|
|
|
" reset_arch clears it. Comparing with the module source is what"
|
|
|
|
|
|
" settles it.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"List the views of an Odoo database that did not come straight from a"
|
|
|
|
|
|
" module, website copies included (read-only).": {
|
|
|
|
|
|
"fr": "Lister les vues d'une base Odoo qui ne viennent pas telles"
|
|
|
|
|
|
" quelles d'un module, copies de site web comprises (lecture seule).",
|
|
|
|
|
|
"en": "List the views of an Odoo database that did not come straight"
|
|
|
|
|
|
" from a module, website copies included (read-only).",
|
|
|
|
|
|
},
|
|
|
|
|
|
"only show this category": {
|
|
|
|
|
|
"fr": "n'afficher que cette catégorie",
|
|
|
|
|
|
"en": "only show this category",
|
|
|
|
|
|
},
|
|
|
|
|
|
"how many views to show (default: 20)": {
|
|
|
|
|
|
"fr": "nombre de vues à afficher (défaut : 20)",
|
|
|
|
|
|
"en": "how many views to show (default: 20)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"list every view": {
|
|
|
|
|
|
"fr": "afficher toutes les vues",
|
|
|
|
|
|
"en": "list every view",
|
|
|
|
|
|
},
|
|
|
|
|
|
"which views to compare (default: flagged)": {
|
|
|
|
|
|
"fr": "quelles vues comparer (défaut : celles qui sont signalées)",
|
|
|
|
|
|
"en": "which views to compare (default: flagged)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"From a module, silently drifted": {
|
|
|
|
|
|
"fr": "D'un module, dérivée en silence",
|
|
|
|
|
|
"en": "From a module, silently drifted",
|
|
|
|
|
|
},
|
|
|
|
|
|
"In --scope all, a difference is a lead, not a verdict:"
|
|
|
|
|
|
" read_arch_from_file returns the raw file, while the database holds the"
|
|
|
|
|
|
" arch AFTER load-time processing. Measured on a freshly installed 18.0"
|
|
|
|
|
|
" database, 160 of its 974 views already differ this way.": {
|
|
|
|
|
|
"fr": "En --scope all, un écart est une piste, pas un verdict :"
|
|
|
|
|
|
" read_arch_from_file rend le fichier brut, alors que la base porte"
|
|
|
|
|
|
" l'arch APRÈS traitement au chargement. Mesuré sur une base 18.0"
|
|
|
|
|
|
" fraîchement installée, 160 de ses 974 vues diffèrent déjà ainsi.",
|
|
|
|
|
|
"en": "In --scope all, a difference is a lead, not a verdict:"
|
|
|
|
|
|
" read_arch_from_file returns the raw file, while the database holds"
|
|
|
|
|
|
" the arch AFTER load-time processing. Measured on a freshly installed"
|
|
|
|
|
|
" 18.0 database, 160 of its 974 views already differ this way.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Compare with the module source? (Y/n): ": {
|
|
|
|
|
|
"fr": "Comparer avec la source du module ? (O/n) : ",
|
|
|
|
|
|
"en": "Compare with the module source? (Y/n): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
# --- Menu Analyse : les actions « aller plus loin ». Ce qui était
|
|
|
|
|
|
# conseillé en options de ligne de commande est devenu des entrées :
|
|
|
|
|
|
# dire « utilisez -v » à quelqu'un qui est dans un menu, c'est lui
|
|
|
|
|
|
# demander d'en sortir pour obtenir ce que le menu pouvait offrir.
|
|
|
|
|
|
"Browse the differences (TUI)": {
|
|
|
|
|
|
"fr": "🖥 Naviguer dans les écarts (plein écran)",
|
|
|
|
|
|
"en": "🖥 Browse the differences (TUI)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Compare every view (slower, noisier)": {
|
|
|
|
|
|
"fr": "🔬 Comparer toutes les vues (plus long, plus bruyant)",
|
|
|
|
|
|
"en": "🔬 Compare every view (slower, noisier)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Compare first, then browse.": {
|
|
|
|
|
|
"fr": "Comparez d'abord, vous naviguerez ensuite.",
|
|
|
|
|
|
"en": "Compare first, then browse.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Compare the flagged views with the module source": {
|
|
|
|
|
|
"fr": "🔍 Comparer les vues signalées avec la source du module",
|
|
|
|
|
|
"en": "🔍 Compare the flagged views with the module source",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Count rows exactly (full scan)": {
|
|
|
|
|
|
"fr": "🔢 Compter les lignes exactement (balayage complet)",
|
|
|
|
|
|
"en": "🔢 Count rows exactly (full scan)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Counting rows exactly, one scan per table…": {
|
|
|
|
|
|
"fr": "Comptage exact des lignes, un balayage par table…",
|
|
|
|
|
|
"en": "Counting rows exactly, one scan per table…",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Export as JSON": {"fr": "💾 Exporter en JSON", "en": "💾 Export as JSON"},
|
|
|
|
|
|
"Go further": {"fr": "🔎 Aller plus loin", "en": "🔎 Go further"},
|
|
|
|
|
|
"Loading the Odoo registry, this takes a moment…": {
|
|
|
|
|
|
"fr": "Chargement du registre Odoo, cela prend un moment…",
|
|
|
|
|
|
"en": "Loading the Odoo registry, this takes a moment…",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No view differs from its module source.": {
|
|
|
|
|
|
"fr": "Aucune vue ne diffère de la source de son module.",
|
|
|
|
|
|
"en": "No view differs from its module source.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Show every table": {
|
|
|
|
|
|
"fr": "📜 Afficher toutes les tables",
|
|
|
|
|
|
"en": "📜 Show every table",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Show every view": {
|
|
|
|
|
|
"fr": "📜 Afficher toutes les vues",
|
|
|
|
|
|
"en": "📜 Show every view",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Written to: ": {"fr": "Écrit dans : ", "en": "Written to: "},
|
|
|
|
|
|
"Flags say a view was touched, not how: only comparing with the module"
|
|
|
|
|
|
" source settles it.": {
|
|
|
|
|
|
"fr": "Les drapeaux disent qu'une vue a été touchée, pas comment :"
|
|
|
|
|
|
" seule la comparaison avec la source du module tranche.",
|
|
|
|
|
|
"en": "Flags say a view was touched, not how: only comparing with the"
|
|
|
|
|
|
" module source settles it.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"blocking": {"fr": "bloquants", "en": "blocking"},
|
|
|
|
|
|
"Studio and hand-made x_ fields": {
|
|
|
|
|
|
"fr": "🧩 Champs x_ (Studio et faits à la main)",
|
|
|
|
|
|
"en": "🧩 Studio and hand-made x_ fields",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Show every field": {
|
|
|
|
|
|
"fr": "📜 Afficher tous les champs",
|
|
|
|
|
|
"en": "📜 Show every field",
|
|
|
|
|
|
},
|
|
|
|
|
|
"A database": {"fr": "💾 Une base de données", "en": "💾 A database"},
|
|
|
|
|
|
"A backup .zip, without restoring it": {
|
|
|
|
|
|
"fr": "🗜 Une sauvegarde .zip, sans la restaurer",
|
|
|
|
|
|
"en": "🗜 A backup .zip, without restoring it",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Path to the backup .zip (empty to cancel): ": {
|
|
|
|
|
|
"fr": "Chemin de la sauvegarde .zip (vide pour annuler) : ",
|
|
|
|
|
|
"en": "Path to the backup .zip (empty to cancel): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No such file: ": {"fr": "Fichier introuvable : ", "en": "No such file: "},
|
|
|
|
|
|
"from a backup": {"fr": "depuis une sauvegarde", "en": "from a backup"},
|
|
|
|
|
|
"Odoo backup .zip to inspect, without restoring it": {
|
|
|
|
|
|
"fr": "sauvegarde Odoo .zip à examiner, sans la restaurer",
|
|
|
|
|
|
"en": "Odoo backup .zip to inspect, without restoring it",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Not an Odoo backup (no manifest.json): ": {
|
|
|
|
|
|
"fr": "Pas une sauvegarde Odoo (aucun manifest.json) : ",
|
|
|
|
|
|
"en": "Not an Odoo backup (no manifest.json): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Cannot read the backup: ": {
|
|
|
|
|
|
"fr": "Lecture impossible de la sauvegarde : ",
|
|
|
|
|
|
"en": "Cannot read the backup: ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"This backup holds no dump.sql: ": {
|
|
|
|
|
|
"fr": "Cette sauvegarde ne contient aucun dump.sql : ",
|
|
|
|
|
|
"en": "This backup holds no dump.sql: ",
|
|
|
|
|
|
},
|
|
|
|
|
|
# --- script/analyse : champs et modèles hors module ---
|
|
|
|
|
|
"Nothing declares these in a file, so no module will "
|
|
|
|
|
|
"recreate them. What a version upgrade keeps is what "
|
|
|
|
|
|
"someone carried over.": {
|
|
|
|
|
|
"fr": "Rien ne les déclare dans un fichier, donc aucun module "
|
|
|
|
|
|
"ne les recréera. Ce qu'une montée de version conserve "
|
|
|
|
|
|
"est ce que quelqu'un a reporté.",
|
|
|
|
|
|
"en": "Nothing declares these in a file, so no module will "
|
|
|
|
|
|
"recreate them. What a version upgrade keeps is what "
|
|
|
|
|
|
"someone carried over.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"A stored field without its column stops the registry "
|
|
|
|
|
|
"from loading, so the upgrade will not even start. Settle "
|
|
|
|
|
|
"these before anything else.": {
|
|
|
|
|
|
"fr": "Un champ stocké sans sa colonne empêche le registre de "
|
|
|
|
|
|
"charger, donc la montée de version ne démarrera même "
|
|
|
|
|
|
"pas. Réglez cela avant tout le reste.",
|
|
|
|
|
|
"en": "A stored field without its column stops the registry "
|
|
|
|
|
|
"from loading, so the upgrade will not even start. Settle "
|
|
|
|
|
|
"these before anything else.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Use -v to list them all, --json for the raw data.": {
|
|
|
|
|
|
"fr": "Utilisez -v pour tout afficher, --json pour la donnée "
|
|
|
|
|
|
"brute.",
|
|
|
|
|
|
"en": "Use -v to list them all, --json for the raw data.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"List the fields and models added outside a module — "
|
|
|
|
|
|
"Studio or by hand (read-only).": {
|
|
|
|
|
|
"fr": "Lister les champs et modèles ajoutés hors module — "
|
|
|
|
|
|
"Studio ou faits à la main (lecture seule).",
|
|
|
|
|
|
"en": "List the fields and models added outside a module — "
|
|
|
|
|
|
"Studio or by hand (read-only).",
|
|
|
|
|
|
},
|
|
|
|
|
|
"how many to show (default: 30)": {
|
|
|
|
|
|
"fr": "nombre à afficher (défaut : 30)",
|
|
|
|
|
|
"en": "how many to show (default: 30)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"list every one": {"fr": "tout afficher", "en": "list every one"},
|
|
|
|
|
|
"Studio": {"fr": "Studio", "en": "Studio"},
|
|
|
|
|
|
"Made by hand": {"fr": "Fait à la main", "en": "Made by hand"},
|
|
|
|
|
|
"Declared by a module": {
|
|
|
|
|
|
"fr": "Déclaré par un module",
|
|
|
|
|
|
"en": "Declared by a module",
|
|
|
|
|
|
},
|
|
|
|
|
|
"stored, but its column is missing": {
|
|
|
|
|
|
"fr": "stocké, mais sa colonne manque",
|
|
|
|
|
|
"en": "stored, but its column is missing",
|
|
|
|
|
|
},
|
|
|
|
|
|
"points at a model that no longer exists": {
|
|
|
|
|
|
"fr": "pointe vers un modèle qui n'existe plus",
|
|
|
|
|
|
"en": "points at a model that no longer exists",
|
|
|
|
|
|
},
|
|
|
|
|
|
"its model no longer exists": {
|
|
|
|
|
|
"fr": "son modèle n'existe plus",
|
|
|
|
|
|
"en": "its model no longer exists",
|
|
|
|
|
|
},
|
|
|
|
|
|
"its table could not be resolved": {
|
|
|
|
|
|
"fr": "sa table n'a pas pu être résolue",
|
|
|
|
|
|
"en": "its table could not be resolved",
|
|
|
|
|
|
},
|
|
|
|
|
|
"origin": {"fr": "provenance", "en": "origin"},
|
|
|
|
|
|
"Fields added outside a module": {
|
|
|
|
|
|
"fr": "Champs ajoutés hors module",
|
|
|
|
|
|
"en": "Fields added outside a module",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Custom fields": {"fr": "Champs personnalisés", "en": "Custom fields"},
|
|
|
|
|
|
"Custom models": {"fr": "Modèles personnalisés", "en": "Custom models"},
|
|
|
|
|
|
"No field or model was added outside a module.": {
|
|
|
|
|
|
"fr": "Aucun champ ni modèle n'a été ajouté hors module.",
|
|
|
|
|
|
"en": "No field or model was added outside a module.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Blocking": {"fr": "Bloquant", "en": "Blocking"},
|
|
|
|
|
|
"To carry over by hand": {
|
|
|
|
|
|
"fr": "À reporter à la main",
|
|
|
|
|
|
"en": "To carry over by hand",
|
|
|
|
|
|
},
|
|
|
|
|
|
# --- script/analyse : comparaison et navigation des écarts ---
|
|
|
|
|
|
"Differences only": {
|
|
|
|
|
|
"fr": "Écarts seuls",
|
|
|
|
|
|
"en": "Differences only",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Ignore indentation": {
|
|
|
|
|
|
"fr": "Ignorer l'indentation",
|
|
|
|
|
|
"en": "Ignore indentation",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Copy": {
|
|
|
|
|
|
"fr": "Copier",
|
|
|
|
|
|
"en": "Copy",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Reset command": {
|
|
|
|
|
|
"fr": "Commande de réinitialisation",
|
|
|
|
|
|
"en": "Reset command",
|
|
|
|
|
|
},
|
|
|
|
|
|
"module (file)": {
|
|
|
|
|
|
"fr": "module (fichier)",
|
|
|
|
|
|
"en": "module (file)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"database": {
|
|
|
|
|
|
"fr": "base",
|
|
|
|
|
|
"en": "database",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Difference copied.": {
|
|
|
|
|
|
"fr": "Écart copié.",
|
|
|
|
|
|
"en": "Difference copied.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No Odoo configuration file found.": {
|
|
|
|
|
|
"fr": "Aucun fichier de configuration Odoo trouvé.",
|
|
|
|
|
|
"en": "No Odoo configuration file found.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"odoo_bin.sh not found.": {
|
|
|
|
|
|
"fr": "odoo_bin.sh est introuvable.",
|
|
|
|
|
|
"en": "odoo_bin.sh not found.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"The Odoo shell returned no result: ": {
|
|
|
|
|
|
"fr": "Le shell Odoo n'a rien renvoyé : ",
|
|
|
|
|
|
"en": "The Odoo shell returned no result: ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"The Odoo shell exceeded the timeout (s): ": {
|
|
|
|
|
|
"fr": "Le shell Odoo a dépassé le délai imparti (s) : ",
|
|
|
|
|
|
"en": "The Odoo shell exceeded the timeout (s): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Unreadable JSON from the Odoo shell: ": {
|
|
|
|
|
|
"fr": "JSON illisible en sortie du shell Odoo : ",
|
|
|
|
|
|
"en": "Unreadable JSON from the Odoo shell: ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"To restore a view to what its module declares — this "
|
|
|
|
|
|
"WRITES to the database, so read the difference first:": {
|
|
|
|
|
|
"fr": "Pour rendre à une vue ce que déclare son module — ceci "
|
|
|
|
|
|
"ÉCRIT dans la base, lisez donc l'écart d'abord :",
|
|
|
|
|
|
"en": "To restore a view to what its module declares — this "
|
|
|
|
|
|
"WRITES to the database, so read the difference first:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"compare with the module source (opens an Odoo shell)": {
|
|
|
|
|
|
"fr": "comparer avec la source du module (ouvre un shell Odoo)",
|
|
|
|
|
|
"en": "compare with the module source (opens an Odoo shell)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"fail if the comparison could not be made": {
|
|
|
|
|
|
"fr": "échouer si la comparaison n'a pas pu être faite",
|
|
|
|
|
|
"en": "fail if the comparison could not be made",
|
|
|
|
|
|
},
|
|
|
|
|
|
"browse the differences in a full-screen view": {
|
|
|
|
|
|
"fr": "naviguer dans les écarts en plein écran",
|
|
|
|
|
|
"en": "browse the differences in a full-screen view",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Flags say a view was touched, not how. They are incomplete "
|
|
|
|
|
|
"both ways: a direct SQL write does not set arch_updated, "
|
|
|
|
|
|
"and reset_arch clears it. Comparing with the module source "
|
|
|
|
|
|
"is what settles it — add --diff.": {
|
|
|
|
|
|
"fr": "Les drapeaux disent qu'une vue a été touchée, pas comment. "
|
|
|
|
|
|
"Ils sont incomplets dans les deux sens : un write SQL "
|
|
|
|
|
|
"direct n'arme pas arch_updated, et reset_arch l'efface. "
|
|
|
|
|
|
"Seule la comparaison avec la source du module tranche — "
|
|
|
|
|
|
"ajoutez --diff.",
|
|
|
|
|
|
"en": "Flags say a view was touched, not how. They are incomplete "
|
|
|
|
|
|
"both ways: a direct SQL write does not set arch_updated, "
|
|
|
|
|
|
"and reset_arch clears it. Comparing with the module source "
|
|
|
|
|
|
"is what settles it — add --diff.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Database is Odoo": {
|
|
|
|
|
|
"fr": "La base est en Odoo",
|
|
|
|
|
|
"en": "Database is Odoo",
|
|
|
|
|
|
},
|
|
|
|
|
|
"checkout is": {
|
|
|
|
|
|
"fr": "le checkout est en",
|
|
|
|
|
|
"en": "checkout is",
|
|
|
|
|
|
},
|
|
|
|
|
|
"views were flagged but hold exactly what their module "
|
|
|
|
|
|
"declares: only the comparison could tell.": {
|
|
|
|
|
|
"fr": "vues étaient signalées mais portent exactement ce que "
|
|
|
|
|
|
"déclare leur module : seule la comparaison pouvait le "
|
|
|
|
|
|
"dire.",
|
|
|
|
|
|
"en": "views were flagged but hold exactly what their module "
|
|
|
|
|
|
"declares: only the comparison could tell.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No reference arch, so nothing was compared: ": {
|
|
|
|
|
|
"fr": "Aucune arch de référence, donc rien n'a été comparé : ",
|
|
|
|
|
|
"en": "No reference arch, so nothing was compared: ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Not a terminal: showing the text report instead.": {
|
|
|
|
|
|
"fr": "Pas un terminal : affichage du rapport texte à la place.",
|
|
|
|
|
|
"en": "Not a terminal: showing the text report instead.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"To restore this view to what its module declares:": {
|
|
|
|
|
|
"fr": "Pour rendre à cette vue ce que déclare son module :",
|
|
|
|
|
|
"en": "To restore this view to what its module declares:",
|
|
|
|
|
|
},
|
|
|
|
|
|
# --- script/analyse : erreurs du socle ---
|
|
|
|
|
|
"Invalid database name: ": {
|
|
|
|
|
|
"fr": "Nom de base invalide : ",
|
|
|
|
|
|
"en": "Invalid database name: ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Cannot read from the database: ": {
|
|
|
|
|
|
"fr": "Lecture impossible dans la base : ",
|
|
|
|
|
|
"en": "Cannot read from the database: ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"is not an Odoo database.": {
|
|
|
|
|
|
"fr": "n'est pas une base Odoo.",
|
|
|
|
|
|
"en": "is not an Odoo database.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"psql is not installed or not in PATH.": {
|
|
|
|
|
|
"fr": "psql n'est pas installé, ou absent du PATH.",
|
|
|
|
|
|
"en": "psql is not installed or not in PATH.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Query exceeded the timeout (s): ": {
|
|
|
|
|
|
"fr": "La requête a dépassé le délai imparti (s) : ",
|
|
|
|
|
|
"en": "Query exceeded the timeout (s): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Unreadable JSON from psql: ": {
|
|
|
|
|
|
"fr": "JSON illisible en sortie de psql : ",
|
|
|
|
|
|
"en": "Unreadable JSON from psql: ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"shared library, nothing to run here.": {
|
|
|
|
|
|
"fr": "bibliothèque partagée, rien à lancer ici.",
|
|
|
|
|
|
"en": "shared library, nothing to run here.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Runnable tools in this directory:": {
|
|
|
|
|
|
"fr": "Outils exécutables de ce répertoire :",
|
|
|
|
|
|
"en": "Runnable tools in this directory:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No analysis tool here yet.": {
|
|
|
|
|
|
"fr": "Aucun outil d'analyse ici pour l'instant.",
|
|
|
|
|
|
"en": "No analysis tool here yet.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"From the menu:": {"fr": "Depuis le menu :", "en": "From the menu:"},
|
|
|
|
|
|
"in the dump": {"fr": "dans le dump", "en": "in the dump"},
|
|
|
|
|
|
"Weight in the dump": {
|
|
|
|
|
|
"fr": "Poids dans le dump",
|
|
|
|
|
|
"en": "Weight in the dump",
|
|
|
|
|
|
},
|
|
|
|
|
|
# --- script/analyse : poids du schéma ---
|
|
|
|
|
|
"Schema analysis": {
|
|
|
|
|
|
"fr": "Analyse du schéma",
|
|
|
|
|
|
"en": "Schema analysis",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Database size": {
|
|
|
|
|
|
"fr": "Poids de la base",
|
|
|
|
|
|
"en": "Database size",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Tables": {"fr": "Tables", "en": "Tables"},
|
|
|
|
|
|
"Models": {"fr": "Modèles", "en": "Models"},
|
|
|
|
|
|
"rows": {"fr": "lignes", "en": "rows"},
|
|
|
|
|
|
"Models without table": {
|
|
|
|
|
|
"fr": "Modèles sans table",
|
|
|
|
|
|
"en": "Models without table",
|
|
|
|
|
|
},
|
|
|
|
|
|
"abstract models have none, by design": {
|
|
|
|
|
|
"fr": "les modèles abstraits n'en ont pas, par construction",
|
|
|
|
|
|
"en": "abstract models have none, by design",
|
|
|
|
|
|
},
|
|
|
|
|
|
"ir_model_relation is absent, so m2m tables cannot be told apart from"
|
|
|
|
|
|
" orphans: the list below is unreliable.": {
|
|
|
|
|
|
"fr": "ir_model_relation est absente : impossible de distinguer une"
|
|
|
|
|
|
" table m2m d'une orpheline, la liste ci-dessous n'est pas fiable.",
|
|
|
|
|
|
"en": "ir_model_relation is absent, so m2m tables cannot be told apart"
|
|
|
|
|
|
" from orphans: the list below is unreliable.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Heaviest tables": {
|
|
|
|
|
|
"fr": "Tables les plus lourdes",
|
|
|
|
|
|
"en": "Heaviest tables",
|
|
|
|
|
|
},
|
|
|
|
|
|
"All tables, heaviest first": {
|
|
|
|
|
|
"fr": "Toutes les tables, de la plus lourde à la plus légère",
|
|
|
|
|
|
"en": "All tables, heaviest first",
|
|
|
|
|
|
},
|
|
|
|
|
|
"use -v to list them all": {
|
|
|
|
|
|
"fr": "utilisez -v pour toutes les afficher",
|
|
|
|
|
|
"en": "use -v to list them all",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Every table belongs to an installed model.": {
|
|
|
|
|
|
"fr": "Chaque table appartient à un modèle installé.",
|
|
|
|
|
|
"en": "Every table belongs to an installed model.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Orphan tables": {
|
|
|
|
|
|
"fr": "Tables orphelines",
|
|
|
|
|
|
"en": "Orphan tables",
|
|
|
|
|
|
},
|
|
|
|
|
|
"No installed model claims these tables. They are usually left over from"
|
|
|
|
|
|
" modules uninstalled without DROP TABLE, and every version upgrade"
|
|
|
|
|
|
" carries them along.": {
|
|
|
|
|
|
"fr": "Aucun modèle installé ne réclame ces tables. Ce sont le plus"
|
|
|
|
|
|
" souvent des reliquats de modules désinstallés sans DROP TABLE, que"
|
|
|
|
|
|
" chaque montée de version transporte avec elle.",
|
|
|
|
|
|
"en": "No installed model claims these tables. They are usually left"
|
|
|
|
|
|
" over from modules uninstalled without DROP TABLE, and every version"
|
|
|
|
|
|
" upgrade carries them along.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Check what they hold before dropping anything.": {
|
|
|
|
|
|
"fr": "Regardez ce qu'elles contiennent avant de supprimer quoi que ce"
|
|
|
|
|
|
" soit.",
|
|
|
|
|
|
"en": "Check what they hold before dropping anything.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Row counts are estimates from the last ANALYZE. Use --exact for real"
|
|
|
|
|
|
" counts, at the cost of one full scan per table.": {
|
|
|
|
|
|
"fr": "Les nombres de lignes sont estimés d'après le dernier ANALYZE."
|
|
|
|
|
|
" Utilisez --exact pour un comptage réel, au prix d'un balayage"
|
|
|
|
|
|
" complet par table.",
|
|
|
|
|
|
"en": "Row counts are estimates from the last ANALYZE. Use --exact for"
|
|
|
|
|
|
" real counts, at the cost of one full scan per table.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Report the size of an Odoo database and the tables no installed model"
|
|
|
|
|
|
" claims (read-only).": {
|
|
|
|
|
|
"fr": "Rapporter le poids d'une base Odoo et les tables qu'aucun"
|
|
|
|
|
|
" modèle installé ne réclame (lecture seule).",
|
|
|
|
|
|
"en": "Report the size of an Odoo database and the tables no installed"
|
|
|
|
|
|
" model claims (read-only).",
|
|
|
|
|
|
},
|
|
|
|
|
|
"database to inspect": {
|
|
|
|
|
|
"fr": "base à examiner",
|
|
|
|
|
|
"en": "database to inspect",
|
|
|
|
|
|
},
|
|
|
|
|
|
"count rows exactly: one full scan per table": {
|
|
|
|
|
|
"fr": "compter les lignes exactement : un balayage complet par table",
|
|
|
|
|
|
"en": "count rows exactly: one full scan per table",
|
|
|
|
|
|
},
|
|
|
|
|
|
"how many tables to show (default: 20)": {
|
|
|
|
|
|
"fr": "nombre de tables à afficher (défaut : 20)",
|
|
|
|
|
|
"en": "how many tables to show (default: 20)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"list every table": {
|
|
|
|
|
|
"fr": "afficher toutes les tables",
|
|
|
|
|
|
"en": "list every table",
|
|
|
|
|
|
},
|
|
|
|
|
|
"output JSON": {"fr": "sortie JSON", "en": "output JSON"},
|
|
|
|
|
|
"path to an Odoo config file": {
|
|
|
|
|
|
"fr": "chemin d'un fichier de configuration Odoo",
|
|
|
|
|
|
"en": "path to an Odoo config file",
|
|
|
|
|
|
},
|
2026-08-10 03:10:50 -04:00
|
|
|
|
# Courriel
|
|
|
|
|
|
"mail_menu": {
|
|
|
|
|
|
"fr": "Courriel - Lire et envoyer du courriel",
|
|
|
|
|
|
"en": "Mail - Read and send email",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_ai_question": {
|
|
|
|
|
|
"fr": "Question IA - Poser une question à un modèle",
|
|
|
|
|
|
"en": "AI question - Ask a model a question",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_open_tui": {
|
|
|
|
|
|
"fr": "Ouvrir le client courriel (TUI)",
|
|
|
|
|
|
"en": "Open the mail client (TUI)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_accounts_menu": {"fr": "Comptes", "en": "Accounts"},
|
|
|
|
|
|
"mail_sync_now": {
|
|
|
|
|
|
"fr": "Synchroniser maintenant",
|
|
|
|
|
|
"en": "Synchronise now",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_cache_menu": {"fr": "Cache", "en": "Cache"},
|
|
|
|
|
|
"mail_account_list": {"fr": "Lister les comptes", "en": "List accounts"},
|
|
|
|
|
|
"mail_account_add": {"fr": "Ajouter un compte", "en": "Add an account"},
|
|
|
|
|
|
"mail_account_delete": {
|
|
|
|
|
|
"fr": "Supprimer un compte",
|
|
|
|
|
|
"en": "Delete an account",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_account_template": {
|
|
|
|
|
|
"fr": "Générer un modèle accounts.json",
|
|
|
|
|
|
"en": "Generate an accounts.json template",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_account_test": {
|
|
|
|
|
|
"fr": "Tester la connexion d'un compte",
|
|
|
|
|
|
"en": "Test an account connection",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_cache_default_mode": {
|
|
|
|
|
|
"fr": "Mode de cache par défaut",
|
|
|
|
|
|
"en": "Default cache mode",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_cache_account_mode": {
|
|
|
|
|
|
"fr": "Mode de cache d'un compte",
|
|
|
|
|
|
"en": "Cache mode of one account",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_cache_size_purge": {
|
|
|
|
|
|
"fr": "Taille du cache et purge",
|
|
|
|
|
|
"en": "Cache size and purge",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_no_account": {
|
|
|
|
|
|
"fr": "Aucun compte configuré. Ajoutez-en un d'abord.",
|
|
|
|
|
|
"en": "No account configured. Add one first.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_ask_name": {
|
|
|
|
|
|
"fr": "Nom court du compte : ",
|
|
|
|
|
|
"en": "Short account name: ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_ask_email": {"fr": "Adresse courriel : ", "en": "Email address: "},
|
|
|
|
|
|
"mail_ask_display_name": {
|
|
|
|
|
|
"fr": "Nom affiché (facultatif) : ",
|
|
|
|
|
|
"en": "Display name (optional): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_ask_preset": {"fr": "Fournisseur : ", "en": "Provider: "},
|
|
|
|
|
|
"mail_ask_password": {"fr": "Mot de passe : ", "en": "Password: "},
|
|
|
|
|
|
"mail_ask_imap_host": {"fr": "Serveur IMAP : ", "en": "IMAP server: "},
|
|
|
|
|
|
"mail_ask_smtp_host": {"fr": "Serveur SMTP : ", "en": "SMTP server: "},
|
|
|
|
|
|
"mail_ask_account": {"fr": "Quel compte ? ", "en": "Which account? "},
|
|
|
|
|
|
"mail_ask_mode": {
|
|
|
|
|
|
"fr": "Mode (clear / encrypted / ephemeral) : ",
|
|
|
|
|
|
"en": "Mode (clear / encrypted / ephemeral): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_app_password_note": {
|
|
|
|
|
|
"fr": "Ce fournisseur exige un mot de passe d'application.",
|
|
|
|
|
|
"en": "This provider requires an app password.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_account_saved": {"fr": "Compte enregistré.", "en": "Account saved."},
|
|
|
|
|
|
"mail_account_save": {"fr": "Enregistrer", "en": "Save"},
|
|
|
|
|
|
"mail_account_missing_fields": {
|
|
|
|
|
|
"fr": "Nom, adresse et mot de passe sont requis.",
|
|
|
|
|
|
"en": "Name, address and password are required.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_account_add_unavailable": {
|
|
|
|
|
|
"fr": "Ajout de compte indisponible dans ce contexte.",
|
|
|
|
|
|
"en": "Adding an account is unavailable in this context.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_account_deleted": {
|
|
|
|
|
|
"fr": "Compte supprimé.",
|
|
|
|
|
|
"en": "Account deleted.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_connection_ok": {
|
|
|
|
|
|
"fr": "Connexion réussie.",
|
|
|
|
|
|
"en": "Connection succeeded.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_connection_failed": {
|
|
|
|
|
|
"fr": "Connexion échouée :",
|
|
|
|
|
|
"en": "Connection failed:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_template_written": {
|
|
|
|
|
|
"fr": "Modèle écrit dans",
|
|
|
|
|
|
"en": "Template written to",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_purge_confirm": {
|
|
|
|
|
|
"fr": "Effacer tout le cache de ce compte ? (o/N) ",
|
|
|
|
|
|
"en": "Erase this account's whole cache? (y/N) ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_purged": {"fr": "Cache effacé.", "en": "Cache erased."},
|
|
|
|
|
|
"mail_no_vault": {
|
|
|
|
|
|
"fr": "Aucun coffre disponible : installez pykeepass ou déverrouillez un trousseau système.",
|
|
|
|
|
|
"en": "No vault available: install pykeepass or unlock a system keyring.",
|
|
|
|
|
|
},
|
2026-08-10 03:10:50 -04:00
|
|
|
|
# Coffre KeePass, côté infrastructure partagée (`kdbx_manager`) : ces
|
|
|
|
|
|
# messages servent à TOUT le CLI, pas seulement au courriel.
|
|
|
|
|
|
"kdbx_vault_is": {
|
|
|
|
|
|
"fr": "Coffre KeePass :",
|
|
|
|
|
|
"en": "KeePass vault:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"kdbx_ask_password": {
|
|
|
|
|
|
"fr": "Mot de passe du coffre (vide pour abandonner) : ",
|
|
|
|
|
|
"en": "Vault password (empty to give up): ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"kdbx_wrong_password": {
|
|
|
|
|
|
"fr": "Mot de passe incorrect pour ce coffre KeePass.",
|
|
|
|
|
|
"en": "Wrong password for this KeePass vault.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"kdbx_give_up": {
|
|
|
|
|
|
"fr": "Coffre non ouvert : on abandonne.",
|
|
|
|
|
|
"en": "Vault not opened: giving up.",
|
|
|
|
|
|
},
|
2026-08-10 03:10:50 -04:00
|
|
|
|
"mail_kdbx_none_configured": {
|
|
|
|
|
|
"fr": "Aucun fichier kdbx n'est configuré.",
|
|
|
|
|
|
"en": "No kdbx file is configured.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_kdbx_menu_create": {
|
|
|
|
|
|
"fr": "Créer un nouveau fichier .kdbx",
|
|
|
|
|
|
"en": "Create a new .kdbx file",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_kdbx_menu_choose": {
|
|
|
|
|
|
"fr": "Choisir un fichier existant",
|
|
|
|
|
|
"en": "Choose an existing file",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_kdbx_menu_cancel": {"fr": "Annuler", "en": "Cancel"},
|
|
|
|
|
|
"mail_kdbx_ask_choice": {"fr": "Votre choix : ", "en": "Your choice: "},
|
|
|
|
|
|
"mail_kdbx_ask_path_new": {
|
|
|
|
|
|
"fr": "Chemin du nouveau fichier kdbx",
|
|
|
|
|
|
"en": "Path for the new kdbx file",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_kdbx_ask_path_existing": {
|
|
|
|
|
|
"fr": "Chemin du fichier kdbx existant : ",
|
|
|
|
|
|
"en": "Path to the existing kdbx file: ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_kdbx_ask_password": {
|
|
|
|
|
|
"fr": "Mot de passe du coffre : ",
|
|
|
|
|
|
"en": "Vault password: ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_kdbx_ask_password_confirm": {
|
|
|
|
|
|
"fr": "Confirmez le mot de passe : ",
|
|
|
|
|
|
"en": "Confirm the password: ",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_kdbx_password_mismatch": {
|
|
|
|
|
|
"fr": "Les mots de passe ne correspondent pas.",
|
|
|
|
|
|
"en": "Passwords do not match.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_kdbx_path_not_found": {
|
|
|
|
|
|
"fr": "Ce fichier n'existe pas :",
|
|
|
|
|
|
"en": "This file does not exist:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_kdbx_created": {
|
|
|
|
|
|
"fr": "Fichier kdbx créé :",
|
|
|
|
|
|
"en": "Kdbx file created:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_kdbx_path_recorded": {
|
|
|
|
|
|
"fr": "Fichier kdbx configuré :",
|
|
|
|
|
|
"en": "Kdbx file configured:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_no_password_stored": {
|
|
|
|
|
|
"fr": "Aucun mot de passe enregistré pour ce compte.",
|
|
|
|
|
|
"en": "No password stored for this account.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_install_textual": {
|
|
|
|
|
|
"fr": "Installez textual pour le client courriel (pip).",
|
|
|
|
|
|
"en": "Install textual for the mail client (pip).",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_accounts": {"fr": "Comptes", "en": "Accounts"},
|
|
|
|
|
|
"mail_search": {"fr": "Rechercher…", "en": "Search…"},
|
|
|
|
|
|
"mail_search_clear": {
|
|
|
|
|
|
"fr": "Effacer la recherche",
|
|
|
|
|
|
"en": "Clear search",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_from": {"fr": "De :", "en": "From:"},
|
|
|
|
|
|
"mail_to": {"fr": "À :", "en": "To:"},
|
|
|
|
|
|
"mail_cc": {"fr": "Cc :", "en": "Cc:"},
|
|
|
|
|
|
"mail_subject": {"fr": "Objet :", "en": "Subject:"},
|
|
|
|
|
|
"mail_date": {"fr": "Date", "en": "Date"},
|
|
|
|
|
|
"mail_send": {"fr": "Envoyer", "en": "Send"},
|
|
|
|
|
|
"mail_attachments": {"fr": "Pièces jointes :", "en": "Attachments:"},
|
|
|
|
|
|
"mail_attachments_paths": {
|
|
|
|
|
|
"fr": "Pièces jointes (chemins séparés par des points-virgules)",
|
|
|
|
|
|
"en": "Attachments (semicolon-separated paths)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_browse": {"fr": "Parcourir…", "en": "Browse…"},
|
|
|
|
|
|
"mail_browse_failed": {
|
|
|
|
|
|
"fr": "Sélecteur de fichiers impossible :",
|
|
|
|
|
|
"en": "File browser unavailable:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_no_subject": {"fr": "(sans objet)", "en": "(no subject)"},
|
|
|
|
|
|
"mail_body_needs_network": {
|
|
|
|
|
|
"fr": "Corps non téléchargé — connexion requise.",
|
|
|
|
|
|
"en": "Body not downloaded — connection required.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_body_error": {
|
|
|
|
|
|
"fr": "Lecture du corps impossible :",
|
|
|
|
|
|
"en": "Cannot read the body:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_flag_error": {
|
|
|
|
|
|
"fr": "Drapeau non transmis au serveur :",
|
|
|
|
|
|
"en": "Flag not sent to the server:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_syncing": {"fr": "Synchronisation de", "en": "Synchronising"},
|
|
|
|
|
|
"mail_new_messages": {"fr": "nouveaux messages", "en": "new messages"},
|
|
|
|
|
|
"mail_errors": {"fr": "erreurs", "en": "errors"},
|
|
|
|
|
|
"mail_folders_resynced": {
|
|
|
|
|
|
"fr": "dossiers resynchronisés (UIDVALIDITY changé) :",
|
|
|
|
|
|
"en": "folders resynchronised (UIDVALIDITY changed):",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_offline_cannot_send": {
|
|
|
|
|
|
"fr": "Compte hors ligne : envoi impossible.",
|
|
|
|
|
|
"en": "Account offline: cannot send.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_sent_to": {"fr": "Envoyé à", "en": "Sent to"},
|
|
|
|
|
|
"mail_sent_not_filed": {
|
|
|
|
|
|
"fr": "envoyé, mais pas classé dans Envoyés",
|
|
|
|
|
|
"en": "sent, but not filed in Sent",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_nothing_to_reply_to": {
|
|
|
|
|
|
"fr": "Aucun message sélectionné.",
|
|
|
|
|
|
"en": "No message selected.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_nothing_to_forward": {
|
|
|
|
|
|
"fr": "Aucun message à transférer.",
|
|
|
|
|
|
"en": "No message to forward.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_attachment_not_found": {
|
|
|
|
|
|
"fr": "Pièce jointe introuvable.",
|
|
|
|
|
|
"en": "Attachment not found.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_no_attachment": {
|
|
|
|
|
|
"fr": "Ce message n'a pas de pièce jointe.",
|
|
|
|
|
|
"en": "This message has no attachment.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_save_failed": {
|
|
|
|
|
|
"fr": "Enregistrement impossible :",
|
|
|
|
|
|
"en": "Cannot save:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_saved_to": {"fr": "Enregistré dans", "en": "Saved to"},
|
|
|
|
|
|
"mail_log_binding": {"fr": "Journal", "en": "Log"},
|
|
|
|
|
|
"mail_log_close": {"fr": "Fermer", "en": "Close"},
|
|
|
|
|
|
"mail_log_tail_heading": {
|
|
|
|
|
|
"fr": "Journal (fin) :",
|
|
|
|
|
|
"en": "Log (tail):",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_log_errors_heading": {
|
|
|
|
|
|
"fr": "Erreurs de synchronisation (session en cours) :",
|
|
|
|
|
|
"en": "Sync errors (current session):",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_log_missing": {
|
|
|
|
|
|
"fr": "journal introuvable",
|
|
|
|
|
|
"en": "log not found",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_log_empty": {"fr": "journal vide", "en": "log empty"},
|
|
|
|
|
|
"mail_log_unreadable": {
|
|
|
|
|
|
"fr": "journal illisible",
|
|
|
|
|
|
"en": "log unreadable",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_log_no_errors": {
|
|
|
|
|
|
"fr": "Aucune erreur de synchronisation dans cette session.",
|
|
|
|
|
|
"en": "No sync errors in this session.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_layout_binding": {"fr": "Vue", "en": "View"},
|
|
|
|
|
|
"mail_layout_switched": {"fr": "Disposition :", "en": "Layout:"},
|
|
|
|
|
|
"mail_layout_columns": {"fr": "Colonnes", "en": "Columns"},
|
|
|
|
|
|
"mail_layout_split": {"fr": "Partagée", "en": "Split"},
|
|
|
|
|
|
"mail_layout_stacked": {"fr": "Empilée", "en": "Stacked"},
|
|
|
|
|
|
"mail_pane_grow_binding": {"fr": "Agrandir volet", "en": "Grow pane"},
|
|
|
|
|
|
"mail_pane_shrink_binding": {
|
|
|
|
|
|
"fr": "Rétrécir volet",
|
|
|
|
|
|
"en": "Shrink pane",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_pane_reset_binding": {
|
|
|
|
|
|
"fr": "Tailles par défaut",
|
|
|
|
|
|
"en": "Reset sizes",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_pane_reset_done": {
|
|
|
|
|
|
"fr": "Tailles des volets réinitialisées.",
|
|
|
|
|
|
"en": "Pane sizes reset.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_pane_splitter_tooltip": {
|
|
|
|
|
|
"fr": "Glisser pour redimensionner",
|
|
|
|
|
|
"en": "Drag to resize",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_fullscreen_binding": {"fr": "Plein écran", "en": "Full screen"},
|
|
|
|
|
|
# -- Libellés des raccourcis de MailApp (suffixe _binding) -------------
|
|
|
|
|
|
# Ce sont les descriptions des `Binding` de `MailApp` : elles s'affichent
|
|
|
|
|
|
# au pied d'écran ET, depuis la tâche 26, dans la fenêtre d'aide (`h`),
|
|
|
|
|
|
# qui les lit directement dans `MailApp.BINDINGS`. Le français est repris
|
|
|
|
|
|
# MOT POUR MOT de ce qui était écrit en dur avant cette tâche — le pied
|
|
|
|
|
|
# d'écran d'un utilisateur francophone ne change pas.
|
|
|
|
|
|
"mail_quit_binding": {"fr": "Quitter", "en": "Quit"},
|
|
|
|
|
|
"mail_sync_current_binding": {"fr": "Sync", "en": "Sync"},
|
|
|
|
|
|
"mail_sync_all_binding": {"fr": "Sync tout", "en": "Sync all"},
|
|
|
|
|
|
"mail_back_binding": {"fr": "Retour", "en": "Back"},
|
|
|
|
|
|
"mail_search_binding": {"fr": "Rechercher", "en": "Search"},
|
|
|
|
|
|
"mail_mark_seen_binding": {"fr": "Lu", "en": "Read"},
|
|
|
|
|
|
"mail_mark_unseen_binding": {"fr": "Non lu", "en": "Unread"},
|
|
|
|
|
|
"mail_save_attachment_binding": {
|
|
|
|
|
|
"fr": "Enregistrer PJ",
|
|
|
|
|
|
"en": "Save attachment",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_compose_binding": {"fr": "Écrire", "en": "Compose"},
|
|
|
|
|
|
"mail_reply_binding": {"fr": "Répondre", "en": "Reply"},
|
|
|
|
|
|
"mail_reply_all_binding": {"fr": "Répondre à tous", "en": "Reply all"},
|
|
|
|
|
|
"mail_forward_binding": {"fr": "Transférer", "en": "Forward"},
|
|
|
|
|
|
"mail_add_account_binding": {
|
|
|
|
|
|
"fr": "Nouveau compte",
|
|
|
|
|
|
"en": "New account",
|
|
|
|
|
|
},
|
|
|
|
|
|
# -- Fenêtre d'aide (touche h) -----------------------------------------
|
|
|
|
|
|
# La liste des touches n'est PAS ici : elle est engendrée depuis
|
|
|
|
|
|
# `MailApp.BINDINGS` (voir `HelpScreen`), avec les libellés ci-dessus.
|
|
|
|
|
|
# Seul ce qu'une liste de touches ne peut pas dire est rédigé ici.
|
|
|
|
|
|
"mail_help_binding": {"fr": "Aide", "en": "Help"},
|
|
|
|
|
|
"mail_help_close": {"fr": "Fermer", "en": "Close"},
|
|
|
|
|
|
"mail_help_title": {
|
|
|
|
|
|
"fr": "Aide — client courriel",
|
|
|
|
|
|
"en": "Help — mail client",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_help_keys_heading": {
|
|
|
|
|
|
"fr": "Raccourcis clavier :",
|
|
|
|
|
|
"en": "Keyboard shortcuts:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_help_notes_heading": {"fr": "Bon à savoir :", "en": "Good to know:"},
|
|
|
|
|
|
"mail_help_mouse": {
|
|
|
|
|
|
"fr": (
|
|
|
|
|
|
"Souris : glisser une barre entre deux volets les redimensionne."
|
|
|
|
|
|
" Au clavier, + et - font de même sur le volet qui a le focus, et"
|
|
|
|
|
|
" 0 remet les tailles par défaut. Les tailles sont retenues par"
|
|
|
|
|
|
" disposition."
|
|
|
|
|
|
),
|
|
|
|
|
|
"en": (
|
|
|
|
|
|
"Mouse: drag a bar between two panes to resize them. From the"
|
|
|
|
|
|
" keyboard, + and - do the same to the focused pane, and 0 resets"
|
|
|
|
|
|
" the sizes. Sizes are remembered per layout."
|
|
|
|
|
|
),
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_help_layouts": {
|
|
|
|
|
|
"fr": (
|
|
|
|
|
|
"Dispositions : v passe de colonnes à partagée, puis empilée, puis"
|
|
|
|
|
|
" revient à colonnes."
|
|
|
|
|
|
),
|
|
|
|
|
|
"en": (
|
|
|
|
|
|
"Layouts: v cycles columns, split, stacked, then back to columns."
|
|
|
|
|
|
),
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_help_sync": {
|
|
|
|
|
|
"fr": (
|
|
|
|
|
|
"Synchronisation : r synchronise le compte du dossier sélectionné"
|
|
|
|
|
|
" (tous ses dossiers), R synchronise tous les comptes. La"
|
|
|
|
|
|
" synchronisation automatique ne tourne QUE tant que le client est"
|
|
|
|
|
|
" ouvert, à l'intervalle mail_refresh_sec (300 s par défaut ; 0 la"
|
|
|
|
|
|
" désactive)."
|
|
|
|
|
|
),
|
|
|
|
|
|
"en": (
|
|
|
|
|
|
"Sync: r syncs the account of the selected folder (all its"
|
|
|
|
|
|
" folders), R syncs every account. The automatic refresh runs ONLY"
|
|
|
|
|
|
" while the client is open, at the mail_refresh_sec interval (300 s"
|
|
|
|
|
|
" by default; 0 disables it)."
|
|
|
|
|
|
),
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_help_files": {
|
|
|
|
|
|
"fr": (
|
|
|
|
|
|
"Fichiers : le journal est dans ~/.erplibre/mail.log (la touche l"
|
|
|
|
|
|
" en montre la fin), les comptes dans"
|
|
|
|
|
|
" ~/.erplibre/mail/accounts.json. Les mots de passe n'y sont JAMAIS"
|
|
|
|
|
|
" écrits : ils vivent dans le coffre kdbx ou le trousseau du"
|
|
|
|
|
|
" système."
|
|
|
|
|
|
),
|
|
|
|
|
|
"en": (
|
|
|
|
|
|
"Files: the log lives in ~/.erplibre/mail.log (the l key shows its"
|
|
|
|
|
|
" tail), the accounts in ~/.erplibre/mail/accounts.json. Passwords"
|
|
|
|
|
|
" are NEVER written there: they live in the kdbx vault or in the"
|
|
|
|
|
|
" system keyring."
|
|
|
|
|
|
),
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_help_close_hint": {
|
|
|
|
|
|
"fr": "Échap ferme cette fenêtre.",
|
|
|
|
|
|
"en": "Esc closes this window.",
|
|
|
|
|
|
},
|
|
|
|
|
|
# -- Exceptions internes au paquet courriel (préfixe mail_err_) --------
|
|
|
|
|
|
# Label traduit, données dynamiques (chemins, texte serveur, valeurs de
|
|
|
|
|
|
# config) concaténées crues : jamais de traduction d'un message serveur
|
|
|
|
|
|
# ou d'un chemin de fichier.
|
|
|
|
|
|
#
|
|
|
|
|
|
# Quelques messages ont la donnée dynamique AU MILIEU de la phrase : ils
|
|
|
|
|
|
# sont donc assemblés à partir de deux clés (voire trois), dans un ordre
|
|
|
|
|
|
# FIXE codé au site d'appel plutôt que par une seule clé avec un
|
|
|
|
|
|
# emplacement — mail_err_unknown_security + mail_err_expected,
|
|
|
|
|
|
# mail_err_key_wrong_length + mail_err_octets_unit,
|
|
|
|
|
|
# mail_err_mode_prefix + mail_err_mode_requires_key(_no_vault),
|
|
|
|
|
|
# mail_err_imap_connection_prefix / mail_err_smtp_connection_prefix +
|
|
|
|
|
|
# mail_err_connection_refused_suffix, et mail_err_keyring_plaintext +
|
|
|
|
|
|
# mail_err_keyring_plaintext_hint. L'ordre des mots vit donc dans le
|
|
|
|
|
|
# code, pas dans les chaînes traduisibles : une langue à l'ordre des
|
|
|
|
|
|
# mots différent devra remplacer ces paires par une convention à
|
|
|
|
|
|
# emplacement (ex. `.format()`), pas par une simple concaténation.
|
|
|
|
|
|
"mail_err_envelope_too_short": {
|
|
|
|
|
|
"fr": "enveloppe trop courte",
|
|
|
|
|
|
"en": "envelope too short",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_err_sealed_in_clear_mode": {
|
|
|
|
|
|
"fr": "donnée chiffrée lue en mode clair : la clé du compte manque",
|
|
|
|
|
|
"en": "encrypted data read in clear mode: the account key is missing",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_err_unknown_envelope": {
|
|
|
|
|
|
"fr": "enveloppe inconnue :",
|
|
|
|
|
|
"en": "unknown envelope:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_err_key_wrong_length": {
|
|
|
|
|
|
"fr": "la clé doit faire",
|
|
|
|
|
|
"en": "the key must be",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_err_octets_unit": {"fr": "octets", "en": "bytes"},
|
|
|
|
|
|
"mail_err_cryptography_not_installed": {
|
|
|
|
|
|
"fr": "le paquet cryptography n'est pas installé",
|
|
|
|
|
|
"en": "the cryptography package is not installed",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_err_decrypt_refused": {
|
|
|
|
|
|
"fr": "déchiffrement refusé : clé fausse ou donnée altérée",
|
|
|
|
|
|
"en": "decryption refused: wrong key or corrupted data",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_err_envelope_unreadable": {
|
|
|
|
|
|
"fr": "enveloppe illisible :",
|
|
|
|
|
|
"en": "unreadable envelope:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_err_mode_prefix": {"fr": "le mode", "en": "mode"},
|
|
|
|
|
|
"mail_err_mode_requires_key": {
|
|
|
|
|
|
"fr": "exige une clé",
|
|
|
|
|
|
"en": "requires a key",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_err_unknown_cache_mode": {
|
|
|
|
|
|
"fr": "mode de cache inconnu :",
|
|
|
|
|
|
"en": "unknown cache mode:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_err_file_already_exists": {
|
|
|
|
|
|
"fr": "le fichier existe déjà :",
|
|
|
|
|
|
"en": "the file already exists:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_err_invalid_secret_ref": {
|
|
|
|
|
|
"fr": "référence de secret invalide :",
|
|
|
|
|
|
"en": "invalid secret reference:",
|
|
|
|
|
|
},
|
|
|
|
|
|
# Notes par fournisseur. Affichées à l'ajout d'un compte ET juste avant
|
|
|
|
|
|
# de redemander un mot de passe après un refus : elles doivent donner
|
|
|
|
|
|
# l'adresse EXACTE, pas un chemin de menu — Google et Apple déplacent
|
|
|
|
|
|
# régulièrement ces pages, et Google cache la sienne.
|
|
|
|
|
|
"mail_preset_note_gmail": {
|
|
|
|
|
|
"fr": (
|
|
|
|
|
|
"Générez-le sur https://myaccount.google.com/apppasswords"
|
|
|
|
|
|
" (16 caractères, les espaces sont acceptés). La validation en"
|
|
|
|
|
|
" deux étapes doit être active, sinon la page est vide."
|
|
|
|
|
|
),
|
|
|
|
|
|
"en": (
|
|
|
|
|
|
"Generate one at https://myaccount.google.com/apppasswords"
|
|
|
|
|
|
" (16 characters, spaces are accepted). Two-step verification"
|
|
|
|
|
|
" must be on, otherwise the page is empty."
|
|
|
|
|
|
),
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_preset_note_outlook": {
|
|
|
|
|
|
"fr": (
|
|
|
|
|
|
"Générez-le sur https://account.microsoft.com/security."
|
|
|
|
|
|
" Microsoft ferme l'authentification simple sur les comptes"
|
|
|
|
|
|
" grand public : sans mot de passe d'application, il faudra"
|
|
|
|
|
|
" OAuth (phase 2, non implémentée)."
|
|
|
|
|
|
),
|
|
|
|
|
|
"en": (
|
|
|
|
|
|
"Generate one at https://account.microsoft.com/security."
|
|
|
|
|
|
" Microsoft is closing basic authentication on consumer"
|
|
|
|
|
|
" accounts: without an app password this needs OAuth (phase 2,"
|
|
|
|
|
|
" not implemented)."
|
|
|
|
|
|
),
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_preset_note_icloud": {
|
|
|
|
|
|
"fr": (
|
|
|
|
|
|
"Générez-le sur https://account.apple.com, section « Connexion"
|
|
|
|
|
|
" et sécurité ». L'authentification à deux facteurs doit être"
|
|
|
|
|
|
" active."
|
|
|
|
|
|
),
|
|
|
|
|
|
"en": (
|
|
|
|
|
|
'Generate one at https://account.apple.com, under "Sign-In and'
|
|
|
|
|
|
' Security". Two-factor authentication must be on.'
|
|
|
|
|
|
),
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_preset_note_generic": {
|
|
|
|
|
|
"fr": "Saisissez les serveurs de votre fournisseur.",
|
|
|
|
|
|
"en": "Enter your provider's servers.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_ask_app_password": {
|
|
|
|
|
|
"fr": "Mot de passe d'application : ",
|
|
|
|
|
|
"en": "App password: ",
|
|
|
|
|
|
},
|
2026-08-10 03:10:50 -04:00
|
|
|
|
"mail_err_password_not_ascii": {
|
|
|
|
|
|
"fr": (
|
|
|
|
|
|
"le mot de passe contient un caractère non ASCII, que ce client"
|
|
|
|
|
|
" IMAP ne sait pas transmettre — il n'a pas été envoyé au"
|
|
|
|
|
|
" serveur"
|
|
|
|
|
|
),
|
|
|
|
|
|
"en": (
|
|
|
|
|
|
"the password contains a non-ASCII character this IMAP client"
|
|
|
|
|
|
" cannot transmit — it was never sent to the server"
|
|
|
|
|
|
),
|
|
|
|
|
|
},
|
2026-08-10 03:10:50 -04:00
|
|
|
|
"mail_err_no_kdbx_configured": {
|
|
|
|
|
|
"fr": "aucun fichier kdbx configuré",
|
|
|
|
|
|
"en": "no kdbx file configured",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_err_kdbx_unreadable": {
|
|
|
|
|
|
"fr": "le fichier kdbx n'a pas pu être ouvert",
|
|
|
|
|
|
"en": "the kdbx file could not be opened",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_err_no_vault_available": {
|
|
|
|
|
|
"fr": "aucun coffre disponible : ni kdbx, ni trousseau système",
|
|
|
|
|
|
"en": "no vault available: neither kdbx nor system keyring",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_err_keyring_plaintext": {
|
|
|
|
|
|
"fr": (
|
|
|
|
|
|
"le trousseau du système écrirait le mot de passe en clair"
|
|
|
|
|
|
" (backend"
|
|
|
|
|
|
),
|
|
|
|
|
|
"en": (
|
|
|
|
|
|
"the system keyring would store the password in plaintext"
|
|
|
|
|
|
" (backend"
|
|
|
|
|
|
),
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_err_keyring_plaintext_hint": {
|
|
|
|
|
|
"fr": "Utilisez un fichier kdbx, ou déverrouillez un vrai trousseau.",
|
|
|
|
|
|
"en": "Use a kdbx file, or unlock a real keyring.",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_err_unknown_security": {
|
|
|
|
|
|
"fr": "sécurité inconnue :",
|
|
|
|
|
|
"en": "unknown security:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_err_expected": {"fr": "(attendu", "en": "(expected"},
|
|
|
|
|
|
"mail_err_account_needs_name": {
|
|
|
|
|
|
"fr": "un compte doit avoir un nom",
|
|
|
|
|
|
"en": "an account must have a name",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_err_invalid_account_name": {
|
|
|
|
|
|
"fr": "nom de compte invalide :",
|
|
|
|
|
|
"en": "invalid account name:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_err_account_name_reason": {
|
|
|
|
|
|
"fr": "(il sert de nom de dossier et de référence de coffre)",
|
|
|
|
|
|
"en": "(it is used as a folder name and vault reference)",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_err_account_unreadable": {
|
|
|
|
|
|
"fr": "compte illisible :",
|
|
|
|
|
|
"en": "unreadable account:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_err_unknown_preset": {
|
|
|
|
|
|
"fr": "préréglage inconnu :",
|
|
|
|
|
|
"en": "unknown preset:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_err_not_valid_json": {
|
|
|
|
|
|
"fr": "n'est pas du JSON valide :",
|
|
|
|
|
|
"en": "is not valid JSON:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_err_should_contain_json_object": {
|
|
|
|
|
|
"fr": "devrait contenir un objet JSON",
|
|
|
|
|
|
"en": "should contain a JSON object",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_err_duplicate_account_names": {
|
|
|
|
|
|
"fr": "noms de compte en double :",
|
|
|
|
|
|
"en": "duplicate account names:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_err_already_exists_relaunch": {
|
|
|
|
|
|
"fr": "existe déjà — relancez avec l'option de remplacement",
|
|
|
|
|
|
"en": "already exists — rerun with the overwrite option",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_err_symlink_refused": {
|
|
|
|
|
|
"fr": "est un lien symbolique : cache refusé",
|
|
|
|
|
|
"en": "is a symlink: cache refused",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_err_owned_by_other_user": {
|
|
|
|
|
|
"fr": "appartient à un autre utilisateur : cache refusé",
|
|
|
|
|
|
"en": "belongs to another user: cache refused",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_err_cache_unreadable": {
|
|
|
|
|
|
"fr": "cache illisible, purgez-le et resynchronisez :",
|
|
|
|
|
|
"en": "unreadable cache, purge it and resynchronise:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_err_mode_requires_key_no_vault": {
|
|
|
|
|
|
"fr": "exige une clé : aucun coffre fourni",
|
|
|
|
|
|
"en": "requires a key: no vault provided",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_err_cache_not_open": {
|
|
|
|
|
|
"fr": "cache non ouvert : appelez open() d'abord",
|
|
|
|
|
|
"en": "cache not open: call open() first",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_err_unknown_folder_fields": {
|
|
|
|
|
|
"fr": "champs de dossier inconnus :",
|
|
|
|
|
|
"en": "unknown folder fields:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_err_unterminated_ampersand": {
|
|
|
|
|
|
"fr": "séquence & non terminée",
|
|
|
|
|
|
"en": "unterminated & sequence",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_err_server_replied": {
|
|
|
|
|
|
"fr": ": le serveur a répondu",
|
|
|
|
|
|
"en": ": the server replied",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_err_no_body_for_uid": {
|
|
|
|
|
|
"fr": "aucun corps rendu pour l'UID",
|
|
|
|
|
|
"en": "no body returned for UID",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_err_imap_connection_prefix": {
|
|
|
|
|
|
"fr": "connexion IMAP à",
|
|
|
|
|
|
"en": "IMAP connection to",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_err_smtp_connection_prefix": {
|
|
|
|
|
|
"fr": "connexion SMTP à",
|
|
|
|
|
|
"en": "SMTP connection to",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_err_connection_refused_suffix": {
|
|
|
|
|
|
"fr": "refusée :",
|
|
|
|
|
|
"en": "refused:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_err_message_needs_recipient": {
|
|
|
|
|
|
"fr": "un message doit avoir au moins un destinataire",
|
|
|
|
|
|
"en": "a message must have at least one recipient",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_err_attachment_missing": {
|
|
|
|
|
|
"fr": "pièce jointe introuvable :",
|
|
|
|
|
|
"en": "attachment not found:",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_err_no_recipient_nothing_sent": {
|
|
|
|
|
|
"fr": "aucun destinataire : rien n'a été envoyé",
|
|
|
|
|
|
"en": "no recipient: nothing was sent",
|
|
|
|
|
|
},
|
|
|
|
|
|
"mail_err_send_refused": {"fr": "envoi refusé :", "en": "send refused:"},
|
2026-03-07 02:59:07 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-03-10 04:07:38 -04:00
|
|
|
|
def get_lang() -> str:
|
2026-03-07 02:59:07 -05:00
|
|
|
|
global _current_lang
|
|
|
|
|
|
if _current_lang is not None:
|
|
|
|
|
|
return _current_lang
|
|
|
|
|
|
|
|
|
|
|
|
# 1. Check env_var.sh file
|
2026-03-10 03:45:11 -04:00
|
|
|
|
if os.path.exists(ENV_VAR_FILE):
|
2026-03-07 02:59:07 -05:00
|
|
|
|
try:
|
2026-03-10 03:45:11 -04:00
|
|
|
|
with open(ENV_VAR_FILE) as f:
|
2026-03-07 02:59:07 -05:00
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-03-10 04:07:38 -04:00
|
|
|
|
def set_lang(lang: str) -> None:
|
2026-03-07 02:59:07 -05:00
|
|
|
|
global _current_lang
|
|
|
|
|
|
_current_lang = lang
|
|
|
|
|
|
|
|
|
|
|
|
# Persist to env_var.sh
|
2026-03-10 03:45:11 -04:00
|
|
|
|
if os.path.exists(ENV_VAR_FILE):
|
2026-03-07 02:59:07 -05:00
|
|
|
|
try:
|
2026-03-10 03:45:11 -04:00
|
|
|
|
with open(ENV_VAR_FILE) as f:
|
2026-03-07 02:59:07 -05:00
|
|
|
|
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"
|
|
|
|
|
|
|
2026-03-10 03:45:11 -04:00
|
|
|
|
with open(ENV_VAR_FILE, "w") as f:
|
2026-03-07 02:59:07 -05:00
|
|
|
|
f.write(content)
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-03-10 04:07:38 -04:00
|
|
|
|
def lang_is_configured() -> bool:
|
2026-03-07 02:59:07 -05:00
|
|
|
|
"""Check if a language has been explicitly set."""
|
2026-03-10 03:45:11 -04:00
|
|
|
|
if os.path.exists(ENV_VAR_FILE):
|
2026-03-07 02:59:07 -05:00
|
|
|
|
try:
|
2026-03-10 03:45:11 -04:00
|
|
|
|
with open(ENV_VAR_FILE) as f:
|
2026-03-07 02:59:07 -05:00
|
|
|
|
content = f.read()
|
|
|
|
|
|
return bool(re.search(r"^EL_LANG=", content, re.MULTILINE))
|
|
|
|
|
|
except OSError:
|
|
|
|
|
|
pass
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-03-10 04:07:38 -04:00
|
|
|
|
def t(key: str) -> str:
|
2026-03-07 02:59:07 -05:00
|
|
|
|
entry = TRANSLATIONS.get(key)
|
|
|
|
|
|
if entry is None:
|
|
|
|
|
|
return key
|
|
|
|
|
|
lang = get_lang()
|
|
|
|
|
|
return entry.get(lang, entry.get("fr", key))
|