export default {
  // Navigation
  nav: {
    printers: 'Imprimantes',
    archives: 'Archives',
    queue: 'File d\'attente d\'impression',
    stats: 'Statistiques',
    profiles: 'Profils',
    maintenance: 'Maintenance',
    projects: 'Projets',
    inventory: 'Filament',
    files: 'Gestionnaire de fichiers',
    makerworld: 'MakerWorld',
    notifications: 'Notifications',
    settings: 'Paramètres',
    system: 'Système',
    collapseSidebar: 'Réduire la barre latérale',
    expandSidebar: 'Développer la barre latérale',
    update: 'Mise à jour',
    updateAvailable: 'Mise à jour disponible : v{{version}}',
    updateAvailableBanner: 'La version {{version}} est disponible !',
    viewUpdate: 'Voir la mise à jour',
    viewOnGithub: 'Voir sur GitHub',
    keyboardShortcuts: 'Raccourcis clavier (?)',
    switchToLight: 'Passer au mode clair',
    switchToDark: 'Passer au mode sombre',
    smartSwitches: 'Interrupteurs intelligents',
    logout: 'Déconnexion',
  },

  // Common
  common: {
    save: 'Enregistrer',
    saving: 'Enregistrement...',
    cancel: 'Annuler',
    delete: 'Supprimer',
    edit: 'Modifier',
    add: 'Ajouter',
    close: 'Fermer',
    confirm: 'Confirmer',
    loading: 'Chargement...',
    error: 'Erreur',
    errorLoading: 'Erreur de chargement',
    retry: 'Réessayer',
    success: 'Succès',
    warning: 'Avertissement',
    enabled: 'Activé',
    disabled: 'Désactivé',
    yes: 'Oui',
    no: 'Non',
    on: 'On',
    off: 'Désactivé',
    all: 'Tous',
    none: 'Aucun',
    search: 'Rechercher',
    filter: 'Filtrer',
    sort: 'Trier',
    refresh: 'Actualiser',
    download: 'Télécharger',
    upload: 'Téléverser',
    uploading: 'Téléversement...',
    uploadFailed: 'Échec du téléversement',
    actions: 'Actions',
    status: 'Statut',
    name: 'Nom',
    description: 'Description',
    date: 'Date',
    time: 'Heure',
    hours: 'heures',
    minutes: 'minutes',
    seconds: 'secondes',
    days: 'jours',
    enable: 'Activer',
    disable: 'Désactiver',
    permissions: 'Autorisations',
    noPrinters: 'Aucune imprimante configurée',
    noData: 'Aucune donnée disponible',
    linkNotFound: 'Lien non trouvé',
    required: 'Requis',
    optional: 'Optionnel',
    dismiss: 'Ignorer',
    apply: 'Appliquer',
    reset: 'Réinitialiser',
    export: 'Exporter',
    import: 'Importer',
    clear: 'Effacer',
    selectAll: 'Tout sélectionner',
    deselectAll: 'Tout désélectionner',
    noChange: '— Aucun changement —',
    unchanged: 'Inchangé',
    unassigned: 'Non assigné',
    unknown: 'Inconnu',
    unknownError: 'Erreur inconnue',
    today: 'Aujourd\'hui',
    tomorrow: 'Demain',
    asap: 'Dès que possible',
    overdue: 'En retard',
    now: 'Maintenant',
    collapse: 'Réduire',
    expand: 'Développer',
    viewArchive: 'Voir l\'archive',
    viewInFileManager: 'Voir dans le gestionnaire de fichiers',
    addedBy: 'Ajouté par {{username}}',
    prints: 'impressions',
    more: '+{{count}} de plus',
    ascending: 'Croissant',
    descending: 'Décroissant',
    back: 'Retour',
    copy: 'Copier',
    copied: 'Copié !',
    printer: 'Imprimante',
    remove: 'Retirer',
    type: 'Type',
    print: 'Imprimer',
    rename: 'Renommer',
    move: 'Déplacer',
    create: 'Créer',
    duplicate: 'Dupliquer',
    left: 'Gauche',
    right: 'Droite',
  },

  // Printers page
  printers: {
    title: 'Imprimantes',
    addPrinter: 'Ajouter une imprimante',
    editPrinter: 'Modifier l\'imprimante',
    deletePrinter: 'Supprimer l\'imprimante',
    printerName: 'Nom de l\'imprimante',
    serialNumber: 'Numéro de série',
    ipAddress: 'Adresse IP / Nom d\'hôte',
    accessCode: 'Code d\'accès',
    model: 'Modèle',
    nozzleCount: 'Nombre de buses',
    autoArchive: 'Auto-archivage',
    status: {
      available: 'Disponible',
      idle: 'Inactif',
      printing: 'Impression en cours',
      paused: 'En pause',
      offline: 'Hors ligne',
      problem: 'Problème',
      error: 'Erreur',
      finished: 'Terminé',
      unknown: 'Inconnu',
    },
    temperatures: {
      nozzle: 'Buse',
      bed: 'Plateau',
      chamber: 'Chambre',
    },
    progress: '{{percent}}% terminé',
    timeRemaining: '{{time}} restant',
    deleteConfirm: 'Êtes-vous sûr de vouloir supprimer "{{name}}" ?',
    maintenanceOk: 'Maintenance OK',
    maintenanceWarning: '{{count}} avertissement',
    maintenanceWarning_plural: '{{count}} avertissements',
    maintenanceDue: '{{count}} échéance',
    maintenanceDue_plural: '{{count}} échéances',
    // Sort options
    sort: {
      name: 'Nom',
      status: 'Statut',
      model: 'Modèle',
      location: 'Emplacement',
      ascending: 'Tri croissant',
      descending: 'Tri décroissant',
    },
    // Card size
    cardSize: {
      small: 'Petites cartes',
      medium: 'Cartes moyennes',
      large: 'Grandes cartes',
      extraLarge: 'Très grandes cartes',
    },
    // Controls
    hideOffline: 'Masquer hors ligne',
    nextAvailable: 'Prochaine disponible',
    powerOn: 'Allumer',
    offlinePrintersWithPlugs: 'Imprimantes hors ligne avec prises connectées',
    noPrintersConfigured: 'Aucune imprimante configurée pour le moment',
    search: 'Rechercher des imprimantes...',
    noSearchResults: 'Aucune imprimante ne correspond à votre recherche ou à vos filtres',
    filter: {
      allStatuses: 'Tous les statuts',
      allLocations: 'Tous les emplacements',
    },
    toolbar: {
      filters: 'Filtres',
      view: 'Vue',
      actions: 'Actions',
    },
    // Printer card
    readyToPrint: 'Prête à imprimer',
    external: 'Externe',
    extL: 'Ext-L',
    extR: 'Ext-R',
    deleteArchives: 'Supprimer les archives d\'impression',
    noLabel: 'Pas d\'étiquette',
    printPreview: 'Aperçu avant impression',
    width: 'Largeur',
    height: 'Hauteur',
    noObjectsFound: 'Aucun objet trouvé',
    objectsLoadedOnPrintStart: 'Les objets sont chargés au début de l\'impression',
    willBeSkipped: 'Sera sauté',
    name: 'Nom',
    serialCannotBeChanged: 'Le numéro de série ne peut pas être modifié',
    locationHelp: 'Utilisé pour grouper les imprimantes et filtrer la file d\'attente',
    // WiFi signal strength
    wifiSignal: {
      veryWeak: 'Très faible',
      weak: 'Faible',
      fair: 'Moyen',
      good: 'Bon',
      excellent: 'Excellent',
    },
    // Maintenance
    maintenanceUpToDate: 'Maintenance à jour - Cliquez pour voir',
    // Chamber light
    chamberLightOn: 'Allumer la lumière de la chambre',
    chamberLightOff: 'Éteindre la lumière de la chambre',
    // Files
    files: 'Fichiers',
    browseFiles: 'Parcourir les fichiers de l\'imprimante',
    // Smart plug
    autoOffAfterPrint: 'Extinction auto après impression',
    autoOffExecuted: 'Extinction auto exécutée - rallumez pour réinitialiser',
    // HMS errors
    hmsErrors: 'Erreurs HMS',
    viewHmsErrors: 'Voir {{count}} erreur(s) HMS',
    // Actions
    resume: 'Reprendre',
    pause: 'Pause',
    stop: 'Arrêter',
    camera: 'Caméra',
    skipObject: 'Sauter l\'objet',
    reconnect: 'Reconnecter',
    forceRefresh: 'Forcer l\'actualisation',
    forceRefreshSuccess: 'Actualisation demandée',
    mqttDebug: 'Débogage MQTT',
    printerInformation: 'Informations imprimante',
    copyToClipboard: 'Copier',
    copied: 'Copié !',
    state: 'État',
    wifiSignalLabel: 'Signal WiFi',
    developerMode: 'Mode développeur',
    enabled: 'Activé',
    disabled: 'Désactivé',
    addedOn: 'Ajoutée le',
    sdCard: 'Carte SD',
    inserted: 'Insérée',
    notInserted: 'Non insérée',
    totalPrintHours: 'Heures d\'impression',
    activeNozzle: 'Active : buse {{nozzle}}',
    nozzleRack: 'Rack à buses',
    nozzleDocked: 'Rangée',
    nozzleMounted: 'Montée',
    nozzleActive: 'Actif',
    nozzleIdle: 'Inactive',
    nozzleDiameter: 'Diamètre',
    nozzleType: 'Type',
    nozzleStatus: 'Statut',
    nozzleFilament: 'Filament',
    nozzleWear: 'Usure',
    nozzleMaxTemp: 'Temp Max',
    nozzleSerial: 'Série',
    nozzleHardenedSteel: 'Acier Trempé',
    nozzleStainlessSteel: 'Acier Inoxydable',
    nozzleTungstenCarbide: 'Carbure de Tungstène',
    nozzleFlow: 'Débit',
    nozzleHighFlow: 'Haut débit',
    nozzleStandardFlow: 'Standard',
    // Firmware
    firmwareUpdate: 'Mise à jour Firmware',
    firmwareInstructions: 'Sur l\'écran de l\'imprimante, allez dans',
    firmwareNav: 'Naviguez vers',
    settings: 'Paramètres',
    firmware: 'Micrologiciel',
    // Discovery
    discoverPrinters: 'Découvrir les imprimantes',
    searching: 'Recherche...',
    manualEntry: 'Saisie manuelle',
    addFromCloud: 'Ajouter depuis le Cloud',
    // Toast messages
    toast: {
      printerDeleted: 'Imprimante supprimée',
      missingSpoolAssignment: 'Impression démarrée sur {{printer}}. Attribution de bobine manquante pour : {{slots}}',
      printerAdded: 'Imprimante ajoutée',
      printerUpdated: 'Imprimante mise à jour',
      failedToDelete: 'Échec de la suppression',
      failedToAdd: 'Échec de l\'ajout',
      connectionFailedNotAdded: 'Connexion à l\'imprimante impossible. Vérifiez l\'IP, le numéro de série et le code d\'accès, et confirmez que le mode LAN-only est activé. L\'imprimante n\'a pas été ajoutée.',
      failedToUpdate: 'Échec de la mise à jour',
      commandSent: 'Commande envoyée',
      failedToSendCommand: 'Échec de l\'envoi de la commande',
      turnedOn: '{{name}} allumée',
      failedToPowerOn: 'Échec de l\'allumage de {{name}}',
      scriptTriggered: 'Script déclenché',
      printStopped: 'Impression arrêtée',
      printPaused: 'Impression en pause',
      printResumed: 'Impression reprise',
      referenceDeleted: 'Référence supprimée',
      detectionAreaSaved: 'Zone de détection enregistrée',
      failedToRunScript: 'Échec du script',
      failedToStopPrint: 'Échec de l\'arrêt',
      failedToPausePrint: 'Échec de la mise en pause',
      failedToResumePrint: 'Échec de la reprise',
      failedToControlChamberLight: 'Échec du contrôle de la lumière',
      failedToSetSpeed: 'Échec du réglage de la vitesse',
      failedToUpdateSetting: 'Échec de mise à jour du paramètre',
      failedToSkipObjects: 'Échec du saut d\'objets',
      failedToRereadRfid: 'Échec lecture RFID',
      failedToCheckPlate: 'Échec vérification plateau',
      failedToUpdateLabel: 'Échec mise à jour étiquette',
      failedToDeleteReference: 'Échec suppression référence',
      failedToSaveDetectionArea: 'Échec enregistrement zone',
      plateCheckEnabled: 'Vérification plateau activée',
      plateCheckDisabled: 'Vérification plateau désactivée',
      calibrationSaved: 'Calibration enregistrée !',
      calibrationFailed: 'Échec de la calibration',
      rfidRereadInitiated: 'Lecture RFID initiée',
      loadInitiated: 'Chargement du filament…',
      unloadInitiated: 'Déchargement du filament…',
      failedToLoad: 'Échec du chargement du filament',
      failedToUnload: 'Échec du déchargement du filament',
    },
    // Connection status
    connection: {
      connected: 'Connecté',
      offline: 'Hors ligne',
    },
    plateStatus: {
      markCleared: 'Marquer le plateau comme dégagé',
      cleared: 'Plateau dégagé',
      notCleared: 'Plateau non dégagé',
      inUse: 'Plateau en cours d\'utilisation',
    },
    // Queue info
    queue: {
      inQueue: '{{count}} impression en file',
      inQueue_plural: '{{count}} impressions en file',
    },
    // Controls section
    controls: 'Contrôles',
    // RFID
    rfid: {
      reread: 'Relire RFID',
    },
    // AMS load/unload (#891)
    ams: {
      load: 'Charger',
      unload: 'Décharger',
    },
    bedJog: {
      title: 'Déplacer le plateau',
      bed: 'Plateau',
      step: 'Pas (mm)',
      up: 'Monter le plateau',
      down: 'Descendre le plateau',
      disabledWhilePrinting: 'Désactivé pendant l\'impression',
      notHomedTitle: 'Imprimante non référencée',
      notHomedMessage: 'L\'imprimante n\'a pas été référencée depuis la dernière impression. Lancez la référence automatique d\'abord pour un positionnement sûr (parque la tête d\'outil, puis référence X, Y et Z), ou déplacez quand même — les butées logicielles seront ignorées.',
      homeZ: 'Référence automatique',
      moveAnyway: 'Déplacer quand même',
      homingStarted: 'Référencement automatique en cours…',
    },
    // Permissions
    permission: {
      noAdd: 'Pas d\'autorisation pour ajouter',
      noEdit: 'Pas d\'autorisation pour modifier',
      noDelete: 'Pas d\'autorisation pour supprimer',
      noControl: 'Pas d\'autorisation pour contrôler',
      noFiles: 'Pas d\'autorisation pour les fichiers',
      noAmsRfid: 'Pas d\'autorisation pour le RFID',
      noSmartPlugControl: 'Pas d\'autorisation pour les prises',
      noCamera: 'Pas d\'autorisation pour les caméras',
    },
    // Add/Edit modal
    modal: {
      addTitle: 'Ajouter une imprimante',
      editTitle: 'Modifier l\'imprimante',
      myPrinter: 'Mon imprimante',
      selectModel: 'Choisir un modèle...',
      locationGroup: 'Emplacement / Groupe (optionnel)',
      locationPlaceholder: 'ex: Atelier, Bureau',
      autoArchiveLabel: 'Auto-archiver les impressions terminées',
      fromPrinterSettings: 'Depuis les paramètres imprimante',
      modelOptional: 'Modèle (optionnel)',
      saveChanges: 'Enregistrer les modifications',
    },
    // Skip objects
    skipObjects: {
      tooltip: 'Sauter des objets',
      onlyWhilePrinting: 'Sauter (uniquement pendant l\'impression)',
      requiresMultiple: 'Sauter (nécessite 2+ objets)',
      title: 'Sauter des objets',
      matchIdsInfo: 'Faites correspondre les IDs avec l\'écran de l\'imprimante',
      printerShowsIds: 'L\'écran affiche les IDs des objets sur le plateau',
      skipSelected: 'Sauter la sélection',
      skipping: 'Saut en cours...',
      noObjectsSelected: 'Aucun objet sélectionné',
      selectObjectsToSkip: 'Sélectionnez les objets à ignorer',
      skipped: 'sauté',
      objectsSkipped: 'Objets sautés',
      activeCount: '{{count}} actifs',
      waitForLayer: 'Attendez la couche 2 pour sauter des objets (actuelle : {{layer}})',
      skip: 'Sauter',
      confirmTitle: 'Sauter l\'objet ?',
      confirmMessage: 'Voulez-vous vraiment sauter "{{name}}" ? Cette action est irréversible.',
    },
    // Confirm modals
    confirm: {
      deleteTitle: 'Supprimer l\'imprimante',
      deleteMessage: 'Supprimer "{{name}}" ? Cela retirera tous les paramètres de connexion.',
      deleteArchivesNote: 'Tout l\'historique sera définitivement supprimé.',
      keepArchivesNote: 'L\'historique sera conservé mais plus associé à cette imprimante.',
      stopTitle: 'Arrêter l\'impression',
      stopMessage: 'Arrêter l\'impression sur "{{name}}" ?',
      stopButton: 'Arrêter',
      pauseTitle: 'Mettre en pause',
      pauseMessage: 'Mettre en pause l\'impression sur "{{name}}" ?',
      pauseButton: 'Pause',
      resumeTitle: 'Reprendre l\'impression',
      resumeMessage: 'Reprendre l\'impression sur "{{name}}" ?',
      resumeButton: 'Reprendre',
      powerOnTitle: 'Allumer l\'imprimante',
      powerOnMessage: 'Allumer "{{name}}" ?',
      powerOnButton: 'Allumer',
      powerOffTitle: 'Éteindre l\'imprimante',
      powerOffMessage: 'Éteindre "{{name}}" ?',
      powerOffWarning: 'ATTENTION : "{{name}}" imprime ! L\'éteindre maintenant peut endommager l\'imprimante.',
      powerOffButton: 'Éteindre',
      haToggleTitle: 'Basculer "{{name}}"',
      haToggleMessage: 'Basculer l\'entité Home Assistant {{entity}} ? Cela peut couper l\'alimentation si elle est actuellement activée.',
      haToggleWarning: 'ATTENTION : "{{name}}" imprime ! Basculer {{entity}} peut couper l\'alimentation et interrompre l\'impression. Continuer ?',
      haToggleButton: 'Basculer',
    },
    // Bulk actions
    bulk: {
      select: 'Sélectionner',
      selectAll: 'Tout sélectionner',
      selectByLocation: 'Sélectionner par emplacement',
      selected: '{{count}} sélectionné(s)',
      actions: {
        stop: 'Arrêter',
        pause: 'Pause',
        resume: 'Reprendre',
        clearPlate: 'Vider le plateau',
        clearHMS: 'Effacer les notifications',
      },
      confirm: {
        stopTitle: 'Arrêter {{count}} impressions',
        stopMessage: 'Cela annulera les impressions actives sur {{count}} imprimante(s). Cette action est irréversible.',
        stopButton: 'Tout arrêter',
        pauseTitle: 'Mettre en pause {{count}} impressions',
        pauseMessage: 'Cela mettra en pause les impressions actives sur {{count}} imprimante(s).',
        pauseButton: 'Tout mettre en pause',
        clearPlateTitle: 'Vider {{count}} plateaux',
        clearPlateMessage: 'Cela videra le plateau sur {{count}} imprimante(s) et pourrait lancer les travaux en file d\'attente.',
        clearPlateButton: 'Tout vider',
      },
      success: '{{action}} terminé sur {{count}} imprimante(s)',
      partial: '{{succeeded}} réussi(s), {{failed}} échoué(s)',
      noneApplicable: 'Aucune imprimante sélectionnée n\'est dans le bon état pour cette action',
      selectByState: 'Sélectionner par état',
    },
    // Discovery
    discovery: {
      title: 'Découvrir les imprimantes',
      searching: 'Recherche...',
      scanning: 'Scan en cours...',
      scanProgress: 'Scan... {{scanned}}/{{total}}',
      foundPrinters: '{{count}} imprimante(s) trouvée(s)',
      noPrintersFound: 'Aucune imprimante trouvée',
      noPrintersFoundSubnet: 'Aucune imprimante dans ce sous-réseau.',
      noPrintersFoundNetwork: 'Aucune imprimante sur le réseau.',
      allConfigured: 'Toutes les imprimantes trouvées sont déjà configurées.',
      alreadyAdded: 'Déjà ajoutée',
      select: 'Sélectionner',
      manualEntry: 'Saisie manuelle',
      addFromCloud: 'Ajouter depuis le Cloud',
      subnetToScan: 'Sous-réseau à scanner',
      dockerNote: 'Docker détecté. Entrez le sous-réseau en notation CIDR. Nécessite network_mode: host.',
      scanSubnet: 'Scanner le sous-réseau',
      discoverNetwork: 'Découvrir sur le réseau',
      scanningSubnet: 'Scan du sous-réseau pour imprimantes Bambu...',
      scanningNetwork: 'Scan du réseau...',
      serialRequired: 'Série requis',
      unknown: 'Inconnu',
      failedToStart: 'Échec du démarrage de la découverte',
    },
    // AMS Drying
    drying: {
      start: 'Démarrer le séchage',
      stop: 'Arrêter le séchage',
      temperature: 'Température',
      duration: 'Durée',
      hours: 'heures',
      timeRemaining: '{{time}} restant',
      active: 'Séchage',
      notSupported: 'Séchage non pris en charge',
      powerRequired: 'Brancher l\'adaptateur secteur AMS pour activer le séchage',
      startingDrying: 'Démarrage du séchage...',
      stoppingDrying: 'Arrêt du séchage...',
      rotateTray: 'Tourner la bobine pendant le séchage',
    },
    // Filaments section
    filaments: 'Filaments',
    // Camera
    openCameraOverlay: 'Ouvrir la caméra en superposition',
    openCameraWindow: 'Ouvrir la caméra dans une fenêtre',
    // Firmware
    firmwareUpdateAvailable: 'Mise à jour firmware : {{current}} → {{latest}}',
    firmwareUpToDate: 'Firmware {{version}} — À jour',
    firmwareUpdateButton: 'Mettre à jour',
    // Plate detection
    plateDetection: {
      noPermission: 'Pas d\'autorisation de modification',
      enabledClick: 'Vérification activée - Cliquez pour désactiver',
      disabledClick: 'Vérification désactivée - Cliquez pour activer',
      manageCalibration: 'Gérer la calibration de détection',
      calibrationRequired: 'Calibration requise',
      calibrationInstructions: 'Videz le plateau, puis cliquez sur Calibrer.',
      calibrationDescription: 'Capture une image de référence du plateau vide.',
      calibrationTip: 'Conseil : Stockez jusqu\'à 5 références. Le système utilise la meilleure correspondance.',
      plateEmpty: 'Le plateau semble vide',
      objectsDetected: 'Objets détectés sur le plateau',
      confidence: 'Confiance',
      difference: 'Différence',
      analysisPreview: 'Aperçu de l\'analyse :',
      analysisLegend: 'Cadre vert = zone, Rouge = différences',
      savedReferences: 'Références ({{count}}/{{max}})',
      deleteReference: 'Supprimer la référence',
      labelPlaceholder: 'Étiquette...',
      clickToEdit: '{{label}} - Modifier',
      clickToAddLabel: 'Ajouter une étiquette',
    },
    // Speed
    speed: {
      title: 'Vitesse d\'impression',
      silent: 'Silencieux (50%)',
      standard: 'Standard (100%)',
      sport: 'Sport (124%)',
      ludicrous: 'Ludicrous (166%)',
    },
    airduct: {
      title: 'Mode conduit d\'air',
      cooling: 'Refroidissement',
      heating: 'Chauffage',
    },
    noSdCard: 'Pas de SD',
    door: {
      open: 'Ouverte',
      closed: 'Fermée',
    },
    // Fans
    fans: {
      partCooling: 'Ventilateur pièce',
      auxiliary: 'Ventilateur auxiliaire',
      chamber: 'Ventilateur chambre',
    },
    // HMS errors
    clickToViewHmsErrors: 'Cliquez pour voir les erreurs HMS',
    estimatedCompletion: 'Fin estimée',
    plateNumber: 'Plaque {{number}}',
    slotOptions: 'Options du slot',
    // AMS hover popup
    amsPopup: {
      friendlyName: 'Nom AMS',
      friendlyNamePlaceholder: 'ex. Nom convivial AMS',
      serialNumber: 'Numéro de série',
      firmwareVersion: 'Micrologiciel',
      save: 'Enregistrer',
      clear: 'Effacer',
      noEditPermission: 'Vous n\'avez pas la permission de renommer les unités AMS',
    },
    // Firmware modal
    firmwareModal: {
      title: 'Mise à jour Firmware',
      titleUpToDate: 'Infos Firmware',
      currentVersion: 'Actuelle :',
      latestVersion: 'Dernière :',
      releaseNotes: 'Notes de version',
      checkingPrereqs: 'Vérification des prérequis...',
      sdCardReady: 'Carte SD prête. Cliquez pour téléverser.',
      uploadedSuccess: 'Firmware téléversé !',
      applyInstructions: 'Pour appliquer sur l\'imprimante :',
      step1: 'Sur l\'écran, allez dans Paramètres',
      step2: 'Allez dans Firmware',
      step3: 'Sélectionnez "Mettre à jour depuis carte SD"',
      step4: 'Prévoyez 10-20 minutes',
      done: 'Terminé',
      starting: 'Démarrage...',
      uploadFirmware: 'Téléverser le Firmware',
      uploadFailed: 'Échec du téléversement : {{error}}',
      uploadedToast: 'Firmware téléversé ! Lancez la mise à jour sur l\'écran.',
      availableVersions: 'Versions disponibles',
      usable: 'Utilisable',
      unavailable: 'Indisponible',
      installed: 'Installé',
      newerBadge: 'plus récent',
      olderBadge: 'plus ancien',
      currentBadge: 'actuel',
    },
    accessCodePlaceholder: 'Laissez vide pour garder l\'actuel',
    // ROI editor
    roi: {
      title: 'Zone de détection (ROI)',
      xStart: 'Début X',
      yStart: 'Début Y',
      width: 'Largeur',
      height: 'Hauteur',
      instruction: 'Ajustez le cadre vert pour cibler le plateau.',
    },
    developerModeWarning: 'Le mode développeur LAN n\'est pas activé sur : {{names}}. Certaines fonctionnalités peuvent ne pas fonctionner.',
    howToEnable: 'Comment activer',
    incompatibleFile: 'Ce fichier a été tranché pour {{slicedFor}}, mais cette imprimante est une {{printerModel}}',
    dropNotPrintable: 'Seuls les fichiers .gcode et .gcode.3mf peuvent être imprimés',
    dropToPrint: 'Déposer pour imprimer',
    cannotPrint: 'Imprimante occupée',
  },

  // Archives page
  archives: {
    title: 'Archives d\'impression',
    searchPlaceholder: 'Chercher dans les archives...',
    filterByPrinter: 'Par imprimante',
    filterByStatus: 'Par statut',
    sortBy: 'Trier par',
    sortNewest: 'Plus récent',
    sortOldest: 'Plus ancien',
    sortName: 'Nom',
    sortDuration: 'Durée',
    sortLargest: 'Plus volumineux',
    sortSmallest: 'Plus léger',
    sortSize: 'Taille',
    noArchives: 'Aucune archive trouvée',
    noArchivesSearch: 'Aucune archive ne correspond',
    originalPrintNotVisible: 'Impression d\'origine non visible - essayez d\'effacer les filtres',
    noArchivesYet: 'Pas encore d\'archive',
    prints: 'impressions',
    pagination: {
      showing: 'Affichage',
      to: 'à',
      of: 'sur',
      show: 'Afficher',
      page: 'Page',
      all: 'Tout',
    },
    loadingArchives: 'Chargement...',
    releaseToUpload: 'Relâcher pour téléverser',
    showAll: 'Tout afficher',
    showFavoritesOnly: 'Favoris uniquement',
    gridView: 'Grille',
    listView: 'Liste',
    calendarView: 'Calendrier',
    logView: 'Journal d\'impression',
    manageTags: 'Gérer les tags',
    showFailedPrints: 'Afficher les échecs',
    hideFailedPrints: 'Masquer les échecs',
    hideDuplicates: 'Masquer les doublons',
    viewOriginalPrint: 'Cliquez pour afficher l\'impression originale (#{{id}})',
    printTime: 'Temps d\'impression',
    filamentUsed: 'Filament utilisé',
    cost: 'Coût',
    reprint: 'Réimprimer',
    preview: 'Aperçu',
    deleteArchive: 'Supprimer l\'archive',
    deleteConfirm: 'Supprimer cette archive ?',
    favorite: 'Favori',
    unfavorite: 'Retirer des favoris',
    viewDetails: 'Détails',
    status: {
      completed: 'Réussi',
      failed: 'Échoué',
      stopped: 'Arrêté',
    },
    toast: {
      source3mfAttached: 'Source 3MF attachée : {{filename}}',
      failedUploadSource3mf: 'Échec téléversement 3MF',
      source3mfRemoved: 'Source 3MF retirée',
      failedRemoveSource3mf: 'Échec retrait 3MF',
      f3dAttached: 'F3D attaché : {{filename}}',
      failedUploadF3d: 'Échec téléversement F3D',
      f3dRemoved: 'F3D retiré',
      failedRemoveF3d: 'Échec retrait F3D',
      timelapseAttached: 'Timelapse attaché : {{filename}}',
      timelapseAlreadyAttached: 'Timelapse déjà présent',
      noMatchingTimelapse: 'Pas de timelapse correspondant',
      failedScanTimelapse: 'Échec scan timelapse',
      failedAttachTimelapse: 'Échec attache timelapse',
      timelapseRemoved: 'Timelapse supprimé',
      failedRemoveTimelapse: 'Échec de la suppression du timelapse',
      timelapseUploaded: 'Timelapse importé : {{filename}}',
      failedUploadTimelapse: 'Échec de l\'importation du timelapse',
      archiveDeleted: 'Archive supprimée',
      failedDeleteArchive: 'Échec suppression',
      addedToFavorites: 'Ajouté aux favoris',
      removedFromFavorites: 'Retiré des favoris',
      projectUpdated: 'Projet mis à jour',
      failedUpdateProject: 'Échec mise à jour projet',
      linkCopied: 'Lien copié',
      failedCopyLink: 'Échec copie lien',
      photoDeleted: 'Photo supprimée',
      failedDeletePhoto: 'Échec suppression photo',
      failedDeleteArchives: 'Échec suppression archives',
      failedUpdateFavorites: 'Échec mise à jour favoris',
      exportDownloaded: 'Export téléchargé',
      exportFailed: 'Échec export',
    },
    menu: {
      print: 'Imprimer',
      schedule: 'Planifier',
      openInBambuStudio: 'Ouvrir dans le Slicer',
      slice: 'Découper',
      externalLink: 'Lien externe',
      viewOnMakerWorld: 'Voir sur MakerWorld',
      preview3d: 'Aperçu 3D',
      viewTimelapse: 'Voir le Timelapse',
      scanForTimelapse: 'Scanner pour Timelapse',
      uploadTimelapse: 'Importer un timelapse',
      removeTimelapse: 'Supprimer le timelapse',
      downloadSource3mf: 'Télécharger Source 3MF',
      uploadSource3mf: 'Téléverser Source 3MF',
      replaceSource3mf: 'Remplacer Source 3MF',
      removeSource3mf: 'Retirer Source 3MF',
      uploadF3d: 'Téléverser F3D',
      replaceF3d: 'Remplacer F3D',
      downloadF3d: 'Télécharger F3D',
      removeF3d: 'Retirer F3D',
      download: 'Télécharger',
      copyDownloadLink: 'Copier lien de téléchargement',
      qrCode: 'Code QR',
      viewPhotos: 'Voir les photos',
      viewPhotosCount: 'Voir les photos ({{count}})',
      projectPage: 'Page du Projet',
      addToFavorites: 'Ajouter aux favoris',
      removeFromFavorites: 'Retirer des favoris',
      edit: 'Modifier',
      printLog: 'Journal d\'impression',
      goToProject: 'Aller au Projet : {{name}}',
      addToProject: 'Ajouter au Projet',
      removeFromProject: 'Retirer du Projet',
      loading: 'Chargement...',
      noProjectsAvailable: 'Aucun projet disponible',
      searchProjects: 'Rechercher des projets…',
      select: 'Sélectionner',
      deselect: 'Désélectionner',
      delete: 'Supprimer',
    },
    permission: {
      noReprint: 'Pas d\'autorisation de réimpression',
      noAddToQueue: 'Pas d\'autorisation pour la file',
      noUpdateArchives: 'Pas d\'autorisation de mise à jour',
      noUploadFiles: 'Pas d\'autorisation de téléversement',
      noDownload: 'Pas d\'autorisation de téléchargement',
      noCopyLink: 'Pas d\'autorisation de copie lien',
      noDelete: 'Pas d\'autorisation de suppression',
      noCreate: 'Pas d\'autorisation de création',
    },
    platePicker: {
      title: 'Choisir une plaque à prévisualiser',
      hint: 'Cette archive contient plusieurs plaques. Sélectionnez-en une pour l\'ouvrir dans la visionneuse GCode.',
      plateLabel: 'Plaque {{index}}',
      objectCount: '{{count}} objet',
      objectCount_plural: '{{count}} objets',
      noGcode: 'Cette archive ne contient pas de G-code généré à prévisualiser. Ouvrez-la d\'abord dans Bambu Studio pour la trancher.',
    },
    card: {
      previousPlate: 'Plateau précédent',
      nextPlate: 'Plateau suivant',
      plateNumber: 'Plateau {{index}}',
      moreOptions: 'Clic droit pour plus d\'options',
      addToFavorites: 'Ajouter aux favoris',
      removeFromFavorites: 'Retirer des favoris',
      cancelled: 'annulé',
      failed: 'échoué',
      duplicate: 'doublon',
      duplicateTitle: 'Ce modèle a déjà été imprimé',
      openSource3mf: 'Ouvrir 3MF dans Bambu Studio (clic droit pour plus)',
      downloadF3d: 'Télécharger fichier Fusion 360',
      viewTimelapse: 'Voir timelapse',
      viewPhoto: 'Voir 1 photo',
      viewPhotos: 'Voir {{count}} photos',
      openFolder: 'Ouvrir le dossier : {{name}}',
      slicedFile: 'Fichier découpé - prêt',
      sourceFile: 'Fichier source uniquement - pas de mapping AMS',
      gcode: 'GCODE',
      source: 'SOURCE',
      project: 'Projet : {{name}}',
      runsBadge: '{{count}} impressions',
      runsBadgeTitle: '{{count}} impressions au total — {{successful}} réussies, {{failed}} échouées. Cliquez pour voir le journal complet.',
      estimated: 'Estimé : {{time}}',
      actual: 'Réel : {{time}}',
      accuracy: 'Précision : {{percent}}%',
      filament: '{{weight}} g',
      layer: '{{count}} couche',
      layers: '{{count}} couches',
      object: '{{count}} objet',
      objects: '{{count}} objets',
      slicedFor: 'Découpé pour {{model}}',
      uploadedBy: 'Téléversé par',
      noPermissionReprint: 'Pas d\'autorisation de réimpression',
      noFileForReprint: 'Aucun fichier 3MF disponible — le fichier n\'a pas pu être téléchargé depuis l\'imprimante lors de l\'enregistrement',
      noPermissionEdit: 'Pas d\'autorisation de modification',
      noPermissionDelete: 'Pas d\'autorisation de suppression',
      reprint: 'Réimprimer',
      schedulePrint: 'Planifier',
      schedule: 'Planifier',
      openInBambuStudio: 'Ouvrir dans le Slicer',
      openInBambuStudioToSlice: 'Ouvrir dans le Slicer pour découper',
      slice: 'Découper',
      externalLink: 'Lien externe',
      makerWorld: 'MakerWorld : {{designer}}',
      viewProject: 'Voir projet',
      noExternalLink: 'Aucun lien externe',
      preview3d: 'Aperçu 3D',
      download: 'Télécharger',
      edit: 'Modifier',
      delete: 'Supprimer',
    },
    runLog: {
      title: 'Journal d\'impression',
      modalTitle: 'Journal d\'impression — {{name}}',
      modalTitleFallback: 'cette archive',
      empty: 'Aucun événement d\'impression enregistré pour cette archive.',
      col: {
        date: 'Date',
        status: 'Statut',
        duration: 'Durée',
        filament: 'Filament',
        cost: 'Coût',
      },
      status: {
        completed: 'Terminé',
        failed: 'Échoué',
        cancelled: 'Annulé',
        stopped: 'Arrêté',
        skipped: 'Ignoré',
        printing: 'Impression en cours',
      },
    },
    modal: {
      deleteArchive: 'Supprimer l\'archive',
      deleteConfirm: 'Supprimer "{{name}}" ? Cette action est irréversible.',
      deleteButton: 'Supprimer',
      deletePurgeStats: 'Retirer également cette impression des Quick Stats (filament, temps, coût, énergie)',
      removeSource3mf: 'Retirer Source 3MF',
      removeSource3mfConfirm: 'Retirer le fichier 3MF de "{{name}}" ?',
      removeButton: 'Retirer',
      removeF3d: 'Retirer F3D',
      removeF3dConfirm: 'Retirer le fichier Fusion 360 de "{{name}}" ?',
      removeTimelapse: 'Supprimer le timelapse',
      removeTimelapseConfirm: 'Êtes-vous sûr de vouloir supprimer la vidéo timelapse de "{{name}}" ?',
      timelapse: '{{name}} – Timelapse',
      selectTimelapse: 'Choisir un Timelapse',
      selectTimelapseDesc: 'Sélectionnez manuellement le timelapse :',
      deleteArchives: 'Supprimer les archives',
      deleteArchivesConfirm: 'Supprimer {{count}} archive(s) ?',
      deleteCount: 'Supprimer {{count}}',
    },
    page: {
      title: 'Archives',
      printsCount: '{{filtered}} sur {{total}} impressions',
      dropFilesHere: 'Déposez les fichiers .3mf ici',
      releaseToUpload: 'Relâcher pour téléverser',
      only3mfSupported: 'Seuls les fichiers .3mf sont supportés',
      close: 'Fermer',
      selected: '{{count}} sélectionnés',
      selectAll: 'Tout sélectionner',
      tags: 'Étiquettes',
      project: 'Projet',
      favorite: 'Favori',
      delete: 'Supprimer',
      toggledFavorites: 'Favoris mis à jour pour {{count}} archive(s)',
      failedUpdateFavorites: 'Échec mise à jour favoris',
      archivesDeleted: '{{count}} archive(s) supprimée(s)',
      failedDeleteArchives: 'Échec suppression',
      photoDeleted: 'Photo supprimée',
      failedDeletePhoto: 'Échec suppression photo',
    },
    list: {
      name: 'Nom',
      printer: 'Imprimante',
      date: 'Date',
      size: 'Taille',
      actions: 'Actions',
      hasTimelapse: 'A un timelapse',
    },
    log: {
      date: 'Date',
      printName: 'Nom de l\'impression',
      printer: 'Imprimante',
      user: 'Utilisateur',
      status: 'Statut',
      duration: 'Durée',
      filament: 'Filament',
      allPrinters: 'Toutes les imprimantes',
      allUsers: 'Tous les utilisateurs',
      allStatuses: 'Tous les statuts',
      cancelled: 'Annulé',
      skipped: 'Ignoré',
      dateFrom: 'Du',
      dateTo: 'Au',
      noEntries: 'Aucune entrée de journal trouvée',
      showing: '{{count}} sur {{total}} entrées',
      rowsPerPage: 'Lignes',
      page: 'Page',
      prev: 'Préc.',
      next: 'Suiv.',
      clearLog: 'Effacer le journal',
      clearLogTitle: 'Effacer le journal d\'impression',
      clearLogConfirm: 'Toutes les entrées du journal d\'impression seront supprimées définitivement. Les archives et les éléments de file d\'attente ne sont pas affectés. Cette action est irréversible. Êtes-vous sûr ?',
      clearLogButton: 'Tout effacer',
      cleared: '{{count}} entrées de journal effacées',
      clearFailed: 'Échec de l\'effacement du journal d\'impression',
    },
  },

  // Queue page
  queue: {
    title: 'File d\'attente',
    subtitle: 'Gérez vos travaux d\'impression',
    addToQueue: 'Ajouter à la file',
    // Print modal
    print: 'Imprimer',
    reprint: 'Réimprimer',
    schedulePrint: 'Planifier',
    editQueueItem: 'Modifier l\'élément',
    printToPrinters: 'Imprimer sur {{count}} imprimantes',
    queueToPrinters: 'Ajouter à la file pour {{count}} imprimantes',
    queueSelectedPlates: 'Ajouter {{count}} plaques à la file',
    selectAllPlates: 'Sélectionner les {{count}} plaques',
    deselectAll: 'Tout désélectionner',
    printQueued: 'Impression ajoutée à la file',
    itemsQueued: '{{count}} éléments ajoutés à la file',
    sending: 'Envoi...',
    sendingProgress: 'Envoi {{current}}/{{total}}...',
    adding: 'Ajout...',
    addingProgress: 'Ajout {{current}}/{{total}}...',
    savingProgress: 'Enregistrement {{current}}/{{total}}...',
    clearQueue: 'Vider la file',
    clearHistory: 'Effacer l\'historique',
    emptyQueue: 'La file est vide',
    position: 'Position',
    scheduledTime: 'Heure prévue',
    moveUp: 'Monter',
    moveDown: 'Descendre',
    startNow: 'Démarrer maintenant',
    printingInProgress: 'Impression en cours...',
    viewArchive: 'Voir l\'archive',
    viewInFileManager: 'Voir dans le gestionnaire',
    itemCount: '{{count}} élément',
    itemCount_plural: '{{count}} éléments',
    dragToReorder: 'Glisser pour réordonner (ASAP uniquement)',
    reorderHint: 'La position n\'affecte que les éléments ASAP.',
    sjf: {
      label: 'SJF',
      tooltip: 'Travail le plus court en premier — le planificateur priorise les impressions plus courtes',
    },
    addedBy: 'Ajouté par {{name}}',
    nextInQueue: 'Prochain en file',
    clearPlateSuccess: 'Plateau vidé — prêt pour l\'impression suivante',
    plateNumber: 'Plateau {{index}}',
    // Batch / quantity
    quantity: 'Quantité',
    quantityHint: 'Crée {{count}} éléments de file d\'attente',
    activeBatches: 'Lots actifs',
    batchProgress: '{{completed}} sur {{total}} terminés',
    cancelBatch: 'Annuler les restants',
    batchCancelled: 'Éléments restants du lot annulés',
    cancelBatchConfirmTitle: 'Annuler le lot',
    cancelBatchConfirmMessage: 'Annuler tous les éléments en attente restants dans ce lot ?',
    batch: 'Lot',
    // Sections
    sections: {
      currentlyPrinting: 'En cours',
      queued: 'En attente',
      history: 'Historique',
    },
    // Status
    status: {
      pending: 'En attente',
      waiting: 'En attente',
      printing: 'Impression',
      paused: 'En pause',
      completed: 'Terminé',
      failed: 'Échoué',
      skipped: 'Sauté',
      cancelled: 'Annulé',
    },
    // Summary cards
    summary: {
      printing: 'Impressions',
      queued: 'En attente',
      totalTime: 'Temps total estimé',
      totalWeight: 'Poids total estimé',
      history: 'Historique',
    },
    // Filters
    filter: {
      allPrinters: 'Toutes les imprimantes',
      unassigned: 'Non assigné',
      allStatus: 'Tous les statuts',
      allLocations: 'Tous les emplacements',
      any: 'Tout',
    },
    // Sort
    sort: {
      byPosition: 'Par position',
      byName: 'Par nom',
      byPrinter: 'Par imprimante',
      bySchedule: 'Par planification',
      byDate: 'Par date',
      ascendingOldest: 'Croissant (plus vieux)',
      descendingNewest: 'Décroissant (plus récent)',
    },
    // Badges
    badges: {
      staged: 'Préparé',
      requiresPrevious: 'Nécessite succès précédent',
      autoPowerOff: 'Extinction auto',
      gcodeInjection: 'G-code',
    },
    // Empty state
    empty: {
      title: 'Aucune impression prévue',
      description: 'Planifiez depuis les Archives ou glissez des fichiers ici.',
    },
    // Time
    time: {
      asap: 'Dès que possible',
      overdue: 'En retard',
      now: 'Maintenant',
      lessThanMinute: 'Dans moins d\'une minute',
      inMinutes: 'Dans {{count}} min',
      inHours: 'Dans {{count}} heures',
    },
    // Actions
    actions: {
      stopPrint: 'Arrêter',
      startPrint: 'Démarrer',
      requeue: 'Remettre en file',
    },
    // Bulk edit
    bulkEdit: {
      title: 'Modifier {{count}} élément',
      title_plural: 'Modifier {{count}} éléments',
      description: 'Seuls les changements seront appliqués.',
      printer: 'Imprimante',
      noChange: '— Aucun changement —',
      queueOptions: 'Options de file',
      staged: 'Préparé (manuel)',
      autoPowerOff: 'Extinction auto après',
      requirePrevious: 'Requiert succès précédent',
      printOptions: 'Options d\'impression',
      bedLevelling: 'Nivellement plateau',
      flowCalibration: 'Calibration débit',
      vibrationCalibration: 'Vibration (Input Shaper)',
      layerInspection: 'Inspection 1ère couche',
      timelapse: 'Time-lapse',
      useAms: 'Utiliser AMS',
      applyChanges: 'Appliquer',
      selectAll: 'Tout sélectionner',
      deselectAll: 'Tout désélectionner',
      selected: '{{count}} sélectionnés',
      editSelected: 'Modifier la sélection',
      cancelSelected: 'Annuler la sélection',
    },
    // Confirmations
    confirm: {
      cancelTitle: 'Annuler l\'impression prévue',
      cancelMessage: 'Annuler "{{name}}" ?',
      stopTitle: 'Arrêter l\'impression',
      stopMessage: 'Arrêter "{{name}}" sur l\'imprimante ?',
      removeTitle: 'Retirer de l\'historique',
      removeMessage: 'Retirer "{{name}}" de l\'historique ?',
      clearHistoryTitle: 'Effacer l\'historique',
      clearHistoryMessage: 'Retirer les {{count}} éléments ?',
      cancelButton: 'Annuler l\'impression',
      stopButton: 'Arrêter l\'impression',
      thisPrint: 'cette impression',
      thisItem: 'cet élément',
    },
    // Toast messages
    toast: {
      cancelled: 'Élément annulé',
      cancelFailed: 'Échec annulation',
      removed: 'Élément retiré',
      removeFailed: 'Échec retrait',
      stopped: 'Impression arrêtée',
      stopFailed: 'Échec arrêt',
      released: 'Libéré dans la file',
      startFailed: 'Échec démarrage',
      reorderFailed: 'Échec réorganisation',
      historyCleared: '{{count}} éléments effacés',
      clearHistoryFailed: 'Échec effacement',
      updateFailed: 'Échec mise à jour',
      bulkCancelled: '{{count}} éléments annulés',
      bulkCancelFailed: 'Échec annulation',
    },
    // Timeline view
    timeline: {
      listView: 'Liste',
      timelineView: 'Chronologie',
      unassigned: 'Non attribué',
      noData: 'Aucune impression planifiée pour ce jour',
      allDoneBy: 'Toutes les impressions terminées vers {{time}}',
      staged: 'En attente',
      filterAll: 'Tout afficher',
      filterPrinting: 'En cours',
      filterQueued: 'En file',
      time: {
        anyMoment: 'imminent',
        minutesLeft: '{{minutes}}m restantes',
        hoursLeft: '{{hours}}h restantes',
        hoursMinutesLeft: '{{hours}}h {{minutes}}m restantes',
      },
      day: {
        previous: 'Jour précédent',
        next: 'Jour suivant',
        today: 'Aujourd\'hui',
      },
    },
    // Permissions
    permissions: {
      noStopPrint: 'Pas d\'autorisation d\'arrêt',
      noStartPrint: 'Pas d\'autorisation de démarrage',
      noEdit: 'Pas d\'autorisation de modification',
      noCancel: 'Pas d\'autorisation d\'annulation',
      noRequeue: 'Pas d\'autorisation de remise en file',
      noRemove: 'Pas d\'autorisation de retrait',
      noClearHistory: 'Pas d\'autorisation historique',
      noEditItems: 'Pas d\'autorisation modification groupée',
      noCancelItems: 'Pas d\'autorisation annulation groupée',
    },
  },

  backgroundDispatch: {
    unknownFile: 'Fichier inconnu',
    unknownPrinter: 'Imprimante inconnue',
    startingPrints: 'Démarrage des impressions',
    progressSummary: '{{complete}}/{{total}} terminés • Envoyés : {{dispatched}} • En cours : {{processing}}',
    expandDetails: 'Expand dispatch details',
    collapseDetails: 'Collapse dispatch details',
    dismissToast: 'Ignorer la notification d\'envoi',
    cancelDispatchJob: 'Annuler l\'envoi',
    cancel: 'Annuler',
    cancelling: 'Annulation…',
    awaitingPrinter: 'En attente de l\'imprimante…',
    status: {
      dispatched: 'Dispatché',
      processing: 'Traitement',
      completed: 'Terminé',
      failed: 'Échoué',
      cancelled: 'Annulé',
    },
    toast: {
      cancellingUpload: 'Cancelling upload...',
      cancelled: 'Envoi annulé',
      cancelFailed: 'Échec de l\'annulation de l\'envoi',
      completeWithFailures: 'Envoi en arrière-plan terminé : {{completed}} réussi(s), {{failed}} échoué(s)',
      completeSuccess: 'Envoi en arrière-plan terminé : {{completed}} réussi(s)',
      printStartedRemaining: '{{completed}} impression(s) lancée(s), {{remaining}} en cours d\'envoi...',
    },
  },

  // Statistics page
  stats: {
    title: 'Statistiques',
    subtitle: 'Glissez les widgets pour réorganiser. Cliquez sur l\'œil pour masquer.',
    overview: 'Vue d\'ensemble',
    totalPrints: 'Total impressions',
    successRate: 'Taux de succès',
    totalPrintTime: 'Temps total d\'impression',
    printTime: 'Temps d\'impression',
    totalFilament: 'Total filament utilisé',
    filamentUsed: 'Filament utilisé',
    filamentCost: 'Coût filament',
    totalCost: 'Coût total',
    energyUsed: 'Énergie consommée',
    energyCost: 'Coût énergie',
    energyWarmingUpTooltip: 'Le suivi énergétique collecte encore des instantanés horaires. Les totaux par période deviendront précis dès qu’un instantané existera avant la plage sélectionnée. Les premières valeurs peuvent être sous-estimées.',
    averagePrintTime: 'Temps moyen par impression',
    printsPerDay: 'Impressions par jour',
    byPrinter: 'Par imprimante',
    printsByPrinter: 'Impressions par imprimante',
    byMaterial: 'Par matériau',
    byMonth: 'Par mois',
    last7Days: '7 derniers jours',
    last30Days: '30 derniers jours',
    last90Days: '90 derniers jours',
    allTime: 'Tout le temps',
    // Widgets
    quickStats: 'Stats rapides',
    printActivity: 'Activité d\'impression',
    filamentTypes: 'Types de filament',
    filamentTrends: 'Tendances filament',
    failureAnalysis: 'Analyse des échecs',
    timeAccuracy: 'Précision du temps',
    successful: 'Succès :',
    failed: 'Échecs :',
    perfectEstimate: '100% = estimation parfaite',
    noTimeAccuracyData: 'Pas encore de données de précision',
    noFilamentData: 'Aucune donnée de filament',
    noPrinterData: 'Aucune donnée d\'imprimante',
    noPrintData: 'Aucune donnée d\'impression',
    noPrintDataLast30Days: 'Aucune impression ces 30 derniers jours',
    failureReasons: 'Raisons des échecs',
    topFailureReasons: 'Top raisons d\'échec',
    failedPrintsCount: '{{failed}} / {{total}} impressions ont échoué',
    lastWeekRate: 'Semaine dernière : {{rate}}%',
    // Actions
    resetLayout: 'Réinitialiser la mise en page',
    recalculateCosts: 'Recalculer les coûts',
    recalculateCostsHint: 'Recalcule les coûts avec les prix actuels des filaments',
    exportStats: 'Exporter les stats',
    exportAsCsv: 'Exporter en CSV',
    exportAsExcel: 'Exporter en Excel',
    hiddenCount: '{{count}} Masqués',
    // Toast
    exportDownloaded: 'Export téléchargé',
    exportFailed: 'Échec export',
    layoutReset: 'Mise en page réinitialisée',
    recalculatedCosts: 'Coûts recalculés pour {{count}} archives',
    recalculateFailed: 'Échec du calcul',
    // Loading
    loadingStats: 'Chargement des statistiques...',
    // Permissions
    noPermissionResetLayout: 'Pas d\'autorisation de réinitialisation',
    noPermissionRecalculate: 'Pas d\'autorisation de recalcul',
    noPrintDataInRange: 'Aucune donnée dans la période sélectionnée',
    periodFilament: 'Filament utilisé',
    periodCost: 'Coût',
    avgPerPrint: 'Moy. par impression',
    usageOverTime: 'Utilisation dans le temps',
    filamentByWeight: 'Poids',
    printDuration: 'Durée d\'impression',
    printerUtilization: 'Utilisation imprimante',
    filamentSuccess: 'Succès par matériau',
    printHabits: 'Habitudes d\'impression',
    printTimeOfDay: 'Heure d\'impression',
    colorDistribution: 'Distribution des couleurs',
    noColorData: 'Aucune donnée de couleur disponible',
    records: 'Enregistrements',
    longestPrint: 'Plus longue impression',
    heaviestPrint: 'Plus lourde impression',
    mostExpensivePrint: 'Plus chère',
    busiestDay: 'Jour le plus actif',
    successStreak: 'Série de succès',
    streakPrint: 'impression consécutive',
    streakPrints: '{{count}} impressions consécutives',
    printerStats: 'Stats imprimante',
    hours: 'heures',
    avgPrints: 'Moy. impressions',
    noArchiveData: 'Aucune donnée d\'impression disponible',
    filamentByTime: 'Temps',
    avgWeight: 'Moy. poids',
    avgTime: 'Moy. temps',
    filamentByPrints: 'Impressions',
    timeframe: {
      today: 'Aujourd\'hui',
      'this-week': 'Cette semaine',
      'this-month': 'Ce mois',
      'last-7': '7 derniers jours',
      'last-30': '30 derniers jours',
      'last-90': '90 derniers jours',
      'this-year': 'Cette année',
      'all-time': 'Tout',
      custom: 'Personnalisé',
      from: 'Du',
      to: 'Au',
    },
    allUsers: 'Tous les utilisateurs',
    noUser: 'Aucun utilisateur (Système)',
    filterByUser: 'Filtrer par utilisateur',
  },

  // Maintenance page
  maintenance: {
    title: 'Maintenance',
    overview: 'Vue d\'ensemble',
    allOk: 'Toute la maintenance est à jour',
    dueCount: '{{count}} tâche à faire',
    dueCount_plural: '{{count}} tâches à faire',
    warningCount: '{{count}} avertissement',
    warningCount_plural: '{{count}} avertissements',
    totalPrintTime: 'Temps d\'impression total',
    nextMaintenance: 'Prochaine maintenance',
    nothingDue: 'Rien de prévu',
    tasks: 'Tâches',
    lastPerformed: 'Dernière réalisation',
    interval: 'Intervalle',
    hoursRemaining: '{{hours}}h restantes',
    hoursOverdue: '{{hours}}h de retard',
    markDone: 'Marquer comme fait',
    performMaintenance: 'Effectuer la maintenance',
    history: 'Historique',
    noHistory: 'Aucun historique',
    editPrintHours: 'Modifier les heures d\'impression',
    currentHours: 'Heures actuelles',
    // Tabs
    statusTab: 'Statut',
    settingsTab: 'Paramètres',
    // Status
    overdueCount: '{{count}} en retard',
    dueSoonCount: '{{count}} bientôt',
    dueSoon: 'Bientôt',
    allGood: 'Tout va bien',
    overdueBy: 'Retard de {{duration}}',
    dueIn: 'Dans {{duration}}',
    timeLeft: '{{duration}} restant',
    // Duration formats
    day: '1 jour',
    days: '{{count}} jours',
    week: '1 semaine',
    weeks: '{{count}} semaines',
    month: '1 mois',
    months: '{{count}} mois',
    year: '1 an',
    // Settings
    maintenanceTypes: 'Types de maintenance',
    maintenanceTypesDescription: 'Types système et tâches personnalisées',
    addCustomType: 'Ajouter un type',
    restoreDefaults: 'Restaurer les tâches par défaut',
    intervalType: 'Type d\'intervalle',
    intervalValue: 'Intervalle ({{type}})',
    icon: 'Icône',
    documentationLink: 'Lien documentation (optionnel)',
    assignToPrinters: 'Assigner aux imprimantes',
    selectAtLeastOnePrinter: 'Sélectionnez au moins une imprimante',
    addType: 'Ajouter le type',
    custom: 'Personnalisé',
    printHours: 'Heures d\'impression',
    calendarDays: 'Jours calendaires',
    exampleName: 'ex: Remplacement filtre HEPA',
    viewDocumentation: 'Voir documentation',
    timeBasedInterval: 'Intervalle temporel',
    // Interval overrides
    intervalOverrides: 'Exceptions d\'intervalle',
    intervalOverridesDescription: 'Intervalles spécifiques par imprimante',
    // Printer assignment
    assignedToPrinters: 'Assigné aux imprimantes :',
    noPrintersAssigned: 'Aucune imprimante assignée',
    addPrinterShort: 'Ajouter :',
    printersAssignedClick: '{{count}} imprimante(s) assignée(s) - gérer',
    removeFromPrinter: 'Retirer de cette imprimante',
    // Types
    types: {
      lubricateCarbonRods: 'Lubrifier les tiges carbone',
      lubricateRails: 'Lubrifier les rails linéaires',
      cleanNozzle: 'Nettoyer la buse / hotend',
      checkBelts: 'Vérifier la tension des courroies',
      cleanBuildPlate: 'Nettoyer le plateau',
      checkExtruder: 'Vérifier les engrenages de l\'extrudeur',
      checkCooling: 'Vérifier les ventilateurs',
      generalInspection: 'Inspection générale',
      cleanCarbonRods: 'Nettoyer les tiges carbone',
      lubricateSteelRods: 'Lubrifier les tiges en acier',
      cleanSteelRods: 'Nettoyer les tiges en acier',
      cleanLinearRails: 'Nettoyer les rails linéaires',
      checkPtfeTube: 'Vérifier le tube PTFE',
      replaceHepaFilter: 'Remplacer le filtre HEPA',
      replaceCarbonFilter: 'Remplacer le filtre charbon',
      lubricateLeftNozzleRail: 'Lubrifier le rail de buse gauche',
    },
    // Toast
    maintenanceComplete: 'Maintenance marquée comme faite',
    typeUpdated: 'Type mis à jour',
    typeDeleted: 'Type supprimé',
    defaultsRestored: '{{count}} tâche(s) par défaut restaurée(s)',
    printHoursUpdated: 'Heures mises à jour',
    printerAssigned: 'Imprimante assignée',
    printerRemoved: 'Imprimante retirée',
    // Confirmation
    deleteTypeConfirm: 'Supprimer "{{name}}" ?',
    deleteSystemTypeTitle: 'Supprimer la tâche de maintenance par défaut ?',
    deleteSystemTypeMessage: 'Êtes-vous sûr de vouloir supprimer la tâche de maintenance par défaut "{{name}}" ?',
    // Permissions
    noPermissionUpdate: 'Pas d\'autorisation de mise à jour',
    noPermissionPerform: 'Pas d\'autorisation d\'action',
    noPermissionEditTypes: 'Pas d\'autorisation de modification types',
    noPermissionDeleteTypes: 'Pas d\'autorisation de suppression types',
    noPermissionEditHours: 'Pas d\'autorisation de modification heures',
    noPermissionRemovePrinter: 'Pas d\'autorisation retrait imprimante',
    noPermissionAssignPrinter: 'Pas d\'autorisation assignation',
    noPermissionEditIntervals: 'Pas d\'autorisation modification intervalles',
    // Configure link
    configureSettings: 'Configurer types et intervalles',
  },

  // Settings page
  settings: {
    title: 'Paramètres',
    general: 'Général',
    // Tab names
    tabs: {
      general: 'Général',
      smartPlugs: 'Prises connectées',
      notifications: 'Notifications',
      queue: 'Flux de travail',
      filament: 'Filament',
      network: 'Réseau',
      apiKeys: 'Clés API',
      virtualPrinter: 'Imprimante virtuelle',
      failureDetection: 'Détection d\'échec',
      users: 'Authentification',
      backup: 'Sauvegarde',
      emailAuth: 'Authentification Email',
      ldap: 'LDAP',
      twoFa: 'Authentification 2FA',
      oidc: 'SSO / OIDC',
      security: 'Sécurité',
      spoolbuddy: 'SpoolBuddy',
    },
    ldap: {
      title: 'Authentification LDAP',
      enabledDesc: "L'authentification LDAP est activée",
      disabledDesc: "L'authentification LDAP est désactivée",
      disabledHint: 'Configurez et enregistrez les paramètres LDAP ci-dessous, puis activez.',
      enabled: 'Authentification LDAP activée',
      disabled: 'Authentification LDAP désactivée',
      feature1: 'Les utilisateurs peuvent se connecter avec leurs identifiants LDAP',
      feature2: "Le compte administrateur local reste disponible en secours",
      feature3: 'Les groupes LDAP sont associés aux groupes BamBuddy à la connexion',
      serverConfig: 'Configuration du serveur LDAP',
      serverUrl: 'URL du serveur',
      serverUrlHint: 'Utilisez ldap:// pour standard ou ldaps:// pour les connexions SSL',
      security: 'Sécurité',
      securityHint: 'StartTLS met à niveau une connexion simple vers TLS. LDAPS utilise TLS dès le début.',
      bindDn: 'DN de liaison (compte de service)',
      bindPassword: 'Mot de passe de liaison',
      searchBase: 'DN de base de recherche',
      userFilter: 'Filtre de recherche utilisateur',
      userFilterHint: "{username} est remplacé par le nom d'utilisateur. Utilisez (uid={username}) pour OpenLDAP.",
      advanced: 'Avancé',
      autoProvision: 'Provisionnement automatique',
      autoProvisionHint: 'Créer automatiquement un compte BamBuddy lors de la première connexion LDAP',
      defaultGroup: 'Groupe par défaut',
      defaultGroupNone: '— Aucun (pas de repli) —',
      defaultGroupHint: "Groupe de repli attribué lorsqu'un utilisateur LDAP s'authentifie mais n'est dans aucun groupe LDAP mappé. Laissez vide pour laisser les utilisateurs non mappés sans autorisations.",
      groupMapping: 'Mappage de groupes (JSON)',
      groupMappingHint: 'Associer les DN de groupes LDAP aux groupes BamBuddy. Groupes disponibles : ',
      testConnection: 'Tester la connexion',
      settingsSaved: 'Paramètres LDAP enregistrés',
      errors: {
        serverRequired: "L'URL du serveur LDAP est requise",
        searchBaseRequired: 'Le DN de base de recherche est requis',
        enableAuthFirst: "Activez d'abord l'authentification",
        configureLdapFirst: "Enregistrez d'abord les paramètres LDAP",
      },
    },
    // Email settings
    email: {
      smtpSettings: 'Configuration SMTP',
      smtpHost: 'Serveur SMTP',
      smtpPort: 'Port SMTP',
      security: 'Sécurité',
      authentication: 'Authentification',
      username: 'Utilisateur',
      password: 'Mot de passe',
      fromEmail: 'Email expéditeur',
      fromName: 'Nom expéditeur',
      testConnection: 'Tester la connexion SMTP',
      testRecipient: 'Email test destinataire',
      sendTest: 'Envoyer email test',
      sending: 'Envoi...',
      save: 'Enregistrer les paramètres',
      saving: 'Enregistrement...',
      advancedAuth: 'Authentification avancée',
      advancedAuthEnabled: 'L\'authentification avancée est activée',
      advancedAuthEnabledDesc: 'La gestion des utilisateurs par email est active. Les nouveaux utilisateurs recevront un mot de passe auto-généré.',
      advancedAuthDisabled: 'Authentification avancée désactivée',
      advancedAuthDisabledDesc: 'Activez pour les fonctionnalités liées à l\'email (mot de passe oublié, etc).',
      enable: 'Activer',
      disable: 'Désactiver',
      feature1: 'Génération auto et envoi de mots de passe par email',
      feature2: 'Connexion par utilisateur ou email',
      feature3: 'Réinitialisation mot de passe oublié disponible',
      feature4: 'Réinitialisation admin par email',
      // Error messages
      errors: {
        requiredFields: 'Remplissez tous les champs requis',
        usernameRequired: 'L\'utilisateur est requis pour l\'authentification',
        enterTestEmail: 'Entrez une adresse email de test',
        smtpServerAndEmail: 'Serveur et expéditeur requis pour le test',
        usernamePasswordRequired: 'Utilisateur et mot de passe requis pour l\'auth',
        configureSmtpFirst: 'Configurez et testez le SMTP d\'abord',
        enableAuthFirst: 'Veuillez d\'abord activer l\'authentification pour utiliser les fonctionnalités basées sur le courrier électronique.',
      },
      // Success messages
      success: {
        settingsSaved: 'Paramètres SMTP enregistrés',
      },
      // Security options
      securityOptions: {
        starttls: 'STARTTLS (Port 587)',
        ssl: 'SSL/TLS (Port 465)',
        none: 'Aucun (Port 25)',
      },
      // Authentication options
      authOptions: {
        enabled: 'Activée',
        disabled: 'Désactivée',
      },
    },
    appearance: 'Apparence',
    notifications: 'Notifications',
    smartPlugs: 'Prises connectées',
    spoolman: 'Spoolman',
    updates: 'Mises à jour',
    language: 'Langue',
    languageDescription: 'Choisissez votre langue',
    theme: 'Thème',
    themeLight: 'Clair',
    themeDark: 'Sombre',
    themeSystem: 'Système',
    defaultView: 'Vue par défaut',
    defaultViewDescription: 'Page affichée au démarrage',
    checkForUpdates: 'Vérifier les mises à jour',
    autoUpdate: 'Mise à jour auto',
    currentVersion: 'Version actuelle',
    latestVersion: 'Dernière version',
    upToDate: 'Bambuddy est à jour',
    updateAvailable: 'Mise à jour disponible',
    // Notifications
    notificationLanguage: 'Langue des notifications',
    notificationLanguageDescription: 'Langue pour les notifications push',
    bedCooledThreshold: 'Seuil de refroidissement du plateau',
    bedCooledThresholdDescription: 'Température en dessous de laquelle le plateau est considéré comme refroidi',
    userNotificationsEnabled: 'Notifications utilisateur',
    userNotificationsEnabledDescription: "Active le menu de notifications utilisateur et les notifications par e-mail pour les événements d'impression. Nécessite l'authentification avancée.",
    userNotificationsDisabledHint: "Activez l'authentification avancée pour utiliser les notifications utilisateur.",
    notificationProviders: 'Fournisseurs de notifications',
    addProvider: 'Ajouter un fournisseur',
    editProvider: 'Modifier le fournisseur',
    providerType: 'Type de fournisseur',
    testNotification: 'Tester la notification',
    testSuccess: 'Notification de test envoyée',
    testFailed: 'Échec de l\'envoi du test',
    quietHours: 'Heures de silence',
    quietHoursDescription: 'Ne pas déranger pendant ces heures',
    quietHoursStart: 'Début',
    quietHoursEnd: 'Fin',
    events: {
      title: 'Événements de notification',
      printStart: 'Impression démarrée',
      printComplete: 'Impression terminée',
      printFailed: 'Impression échouée',
      printStopped: 'Impression arrêtée',
      printProgress: 'Jalons de progression',
      printProgressDescription: 'Notifier à 25%, 50%, 75%',
      printerOffline: 'Imprimante hors ligne',
      printerError: 'Erreur imprimante',
      filamentLow: 'Filament bas',
      maintenanceDue: 'Maintenance due',
      maintenanceDueDescription: 'Notifier quand une tâche est due',
    },
    // Smart Plugs
    smartPlug: {
      title: 'Prises connectées',
      add: 'Ajouter une prise',
      edit: 'Modifier la prise',
      name: 'Nom',
      ipAddress: 'Adresse IP',
      linkedPrinter: 'Imprimante liée',
      autoOn: 'Allumage auto',
      autoOnDescription: 'Allumer au début de l\'impression',
      autoOff: 'Extinction auto',
      autoOffDescription: 'Éteindre après l\'impression',
      offDelay: 'Délai d\'extinction',
      offDelayMinutes: 'Minutes après fin',
      offDelayTemp: 'Quand la buse est sous',
      currentState: 'État actuel',
      turnOn: 'Allumer',
      turnOff: 'Éteindre',
    },
    // Filament Tracking Mode
    filamentTracking: 'Suivi de Filament',
    filamentTrackingDesc: 'Choisissez comment suivre vos bobines. Utilisez l\'inventaire intégré ou connectez un serveur Spoolman.',
    filamentChecks: 'Vérifications du filament',
    disableFilamentWarnings: 'Désactiver les avertissements de filament',
    disableFilamentWarningsDesc: 'Ne pas afficher les avertissements de filament insuffisant lors de l\'impression ou de la mise en file d\'attente',
    preferLowestFilament: 'Préférer le filament le plus bas',
    preferLowestFilamentDesc: 'Lorsque plusieurs bobines correspondent, utiliser celle avec le moins de filament restant',
    trackingModeBuiltIn: 'Inventaire Intégré',
    trackingModeBuiltInDesc: 'Correspondance RFID et suivi de consommation inclus',
    trackingModeSpoolmanDesc: 'Serveur de gestion externe',
    builtInFeatureRfid: 'Détecte auto les bobines RFID Bambu Lab dans l\'AMS',
    builtInFeatureUsage: 'Suit la consommation par impression',
    builtInFeatureCatalog: 'Gère bobines, couleurs et profils facteur K',
    builtInFeatureThirdParty: 'Les bobines tierces peuvent être assignées aux bobines d\'inventaire',
    amsSyncButton: 'Synchroniser les poids depuis l\'AMS',
    amsSyncTitle: 'Synchroniser les poids des bobines depuis l\'AMS',
    amsSyncMessage: 'Tous les poids des bobines de l\'inventaire seront écrasés par les valeurs actuelles de l\'AMS des imprimantes connectées. Utilisez ceci pour récupérer des données de poids corrompues. Les imprimantes doivent être en ligne.',
    amsSyncing: 'Synchronisation...',
    amsSyncSuccess: '{{synced}} bobine(s) synchronisée(s), {{skipped}} ignorée(s)',
    amsSyncError: 'Échec de la synchronisation des poids depuis l\'AMS',
    spoolmanAmsSyncButton: 'Synchroniser les poids Spoolman depuis l\'AMS',
    spoolmanAmsSyncTitle: 'Synchroniser les poids des bobines Spoolman depuis l\'AMS',
    spoolmanAmsSyncMessage: 'Cela mettra à jour les poids des bobines Spoolman en fonction des valeurs de remplissage AMS actuelles des imprimantes connectées. Les imprimantes doivent être en ligne.',
    spoolmanAmsSyncing: 'Synchronisation...',
    spoolmanAmsSyncSuccess: '{{synced}} bobine(s) synchronisée(s), {{skipped}} ignorée(s)',
    spoolmanAmsSyncError: 'Échec de la synchronisation des poids Spoolman depuis l\'AMS',
    spoolmanAmsSyncErrorUnreachable: 'Échec de la synchronisation (Spoolman inaccessible)',
    spoolmanAmsSyncErrorNotConfigured: 'Échec de la synchronisation (Spoolman non configuré)',
    spoolmanNotConfigured: 'Spoolman non configuré',
    // Spoolman filament catalog section in spool catalog settings
    spoolmanFilamentCatalogTitle: 'Catalogue de filaments Spoolman',
    spoolmanFilamentCatalogDesc: 'Noms de filaments et poids à vide depuis votre instance Spoolman. Le nom et le poids bobine sont modifiables ici ; toutes les autres propriétés sont gérées directement dans Spoolman.',
    // Spoolman settings
    spoolmanUrl: 'URL Spoolman',
    spoolmanUrlHint: 'URL de votre serveur Spoolman (ex: http://localhost:7912)',
    spoolmanConnected: 'Connecté',
    spoolmanDisconnected: 'Déconnecté',
    status: 'Statut',
    connect: 'Connecter',
    disconnect: 'Déconnecter',
    howSyncWorks: 'Fonctionnement de la Sync',
    syncInfoRfidOnly: 'Seules les bobines officielles RFID sont synchronisées',
    syncInfoAutoCreate: 'Les bobines sont créées dans Spoolman à la 1ère sync',
    syncInfoThirdPartySkipped: 'Les bobines tierces ou rechargées sont ignorées',
    linkingExistingSpools: 'Lier des bobines existantes',
    linkingExistingSpoolsDesc: 'Pour lier une bobine Spoolman, survolez un slot AMS et cliquez sur "Lier à Spoolman".',
    syncMode: 'Mode de Sync',
    syncModeAuto: 'Automatique',
    syncModeManual: 'Manuel uniquement',
    syncModeAutoDesc: 'Sync auto lors de changements AMS',
    syncModeManualDesc: 'Sync uniquement sur déclenchement manuel',
    syncAmsData: 'Synchroniser AMS',
    syncAmsDataDesc: 'Synchroniser manuellement les données vers Spoolman',
    allPrinters: 'Toutes les imprimantes',
    // Default printer
    noDefaultPrinter: 'Aucune par défaut (demander à chaque fois)',
    // Sidebar
    sidebarOrder: 'Ordre de la barre latérale',
    // Camera
    saveThumbnails: 'Enregistrer les vignettes',
    captureFinishPhoto: 'Prendre une photo à la fin',
    noPrintersConfigured: 'Aucune imprimante configurée',
    // Archive settings
    archiveMode: {
      always: 'Toujours créer une archive',
      never: 'Ne jamais créer d\'archive',
      ask: 'Demander à chaque fois',
    },
    // Updates
    checkForUpdatesLabel: 'Vérifier les mises à jour',
    checkPrinterFirmware: 'Vérifier le firmware imprimante',
    includeBetaUpdates: 'Inclure les versions bêta',
    includeBetaUpdatesDesc: 'Notifier des versions bêta et préliminaires lors de la vérification des mises à jour',
    // Queue
    enableRetry: 'Activer la rétentative',
    // Home Assistant
    homeAssistantDescription: 'Contrôler les prises via Home Assistant',
    environmentManagedLabel: '(Géré par l\'environnement)',
    autoEnabledViaEnv: 'Activé via variables d\'environnement',
    urlFromEnvReadOnly: 'Valeur HA_URL (lecture seule)',
    tokenFromEnvReadOnly: 'Valeur HA_TOKEN (lecture seule)',
    // MQTT
    mqttConnectedTo: 'Connecté à',
    // Prometheus
    prometheusDescription: 'Exposer les données au format Prometheus',
    // Smart plugs empty state
    noSmartPlugsTitle: 'Aucune prise configurée',
    noSmartPlugsDescription: 'Ajoutez une prise Tasmota pour suivre l\'énergie et automatiser.',
    // Notifications empty state
    noProvidersTitle: 'Aucun fournisseur configuré',
    noProvidersDescription: 'Ajoutez un fournisseur pour recevoir des alertes.',
    noTemplatesAvailable: 'Aucun modèle dispo. Redémarrez pour les générer.',
    // API permissions
    apiPermissionView: 'Voir statut et file',
    apiPermissionEdit: 'Gérer la file d\'attente',
    // API keys
    apiKeysEmptyTitle: 'Aucune clé API',
    apiKeysEmptyDescription: 'Créez une clé pour vos intégrations.',
    // Users
    noUsersFound: 'Aucun utilisateur trouvé',
    noGroupsFound: 'Aucun groupe trouvé',
    noGroupsAvailable: 'Aucun groupe disponible',
    passwordsDoNotMatch: 'Les mots de passe ne correspondent pas',
    systemGroupWarning: 'Les noms des groupes système sont fixes',
    // Auth disabled
    authDisabledTitle: 'Authentification désactivée',
    authDisabledFeature1: 'Requis pour accéder au système',
    authDisabledFeature2: 'Gestion multi-utilisateurs et groupes',
    authDisabledFeature3: 'Plus de 50 permissions granulaires',
    // User deletion
    userHasCreated: 'Cet utilisateur a créé :',
    userItemsQuestion: 'Que faire de ces éléments ?',
    deleteUserConfirm: 'Supprimer cet utilisateur ?',
    actionCannotBeUndone: 'Cette action est irréversible.',
    // Smart plugs
    addFirstSmartPlug: 'Ajoutez votre première prise',
    // Notifications
    providers: 'Fournisseurs',
    log: 'Journal',
    testAll: 'Tout tester',
    testResults: 'Résultats du test',
    testPassedCount: '{{count}} succès',
    testFailedCount: '{{count}} échecs',
    messageTemplates: 'Modèles de message',
    messageTemplatesDescription: 'Personnalisez les messages par événement.',
    // API Keys section
    apiKeys: 'Clés API',
    apiKeysDescription: 'Créez des clés pour webhooks et API.',
    createKey: 'Créer une clé',
    apiKeyCreated: 'Clé API créée avec succès',
    apiKeyCopyWarning: 'Copiez cette clé maintenant - elle ne sera plus affichée !',
    useInApiBrowser: 'Utiliser dans l\'explorateur API',
    createNewApiKey: 'Nouvelle clé API',
    keyName: 'Nom de la clé',
    keyNamePlaceholder: 'ex: Home Assistant, OctoPrint',
    readStatus: 'Lire le statut',
    readStatusDescription: 'Voir les imprimantes et la file',
    manageQueue: 'Gérer la file',
    manageQueueDescription: 'Ajouter/retirer des éléments',
    controlPrinter: 'Contrôler l\'imprimante',
    controlPrinterDescription: 'Pause, reprise, arrêt',
    cloudAccess: 'Autoriser l\'accès cloud',
    cloudAccessDescription: 'Lit les préréglages et filaments Bambu Cloud en votre nom. Nécessite d\'être connecté à Bambu Cloud.',
    cloudBadge: 'Cloud',
    updateEnergyCost: 'Mettre à jour le tarif électrique',
    updateEnergyCostDescription: 'Autoriser cette clé à POSTer un nouveau tarif électrique par kWh sur /settings/electricity-price. Utile pour les automatisations Home Assistant à tarif dynamique (Tibber, Octopus, etc.). Il s\'agit du seul champ de paramètres modifiable via clé API.',
    energyCostBadge: 'Énergie',
    legacyKey: 'Hérité',
    legacyKeyTooltip: 'Créé avant la propriété par utilisateur ; recréez pour l\'accès cloud',
    unnamedKey: 'Clé sans nom',
    lastUsed: 'Dernière utilisation',
    read: 'Lecture',
    control: 'Contrôle',
    createFirstKey: 'Créez votre première clé',
    webhookEndpoints: 'Points de terminaison Webhook',
    webhookApiKeyHint: 'Utilisez la clé dans l\'en-tête X-API-Key.',
    webhook: {
      getAllStatus: 'Tous les statuts',
      getSpecificStatus: 'Statut spécifique',
      addToQueue: 'Ajouter à la file',
      pausePrint: 'Pause',
      resumePrint: 'Reprise',
      stopPrint: 'Arrêt',
    },
    apiBrowser: 'Explorateur API',
    apiBrowserDescription: 'Testez les endpoints API.',
    apiKeyForTesting: 'Clé API pour test',
    apiKeyPlaceholder: 'Collez votre clé pour tester...',
    apiKeyHint: 'Sera envoyée via l\'en-tête X-API-Key.',
    deleteApiKeyTitle: 'Supprimer la clé API',
    deleteApiKeyMessage: 'Les intégrations utilisant cette clé cesseront de fonctionner.',
    deleteKey: 'Supprimer la clé',
    // Filament tab
    amsDisplayThresholds: 'Seuils d\'affichage AMS',
    amsThresholdsDescription: 'Seuils de couleur pour humidité et température.',
    humidity: 'Humidité',
    goodGreen: 'Bon (vert)',
    fairOrange: 'Moyen (orange)',
    aboveFairBad: 'Au-dessus = rouge (mauvais)',
    fairAlsoDryingThreshold: 'Ce seuil est aussi utilisé pour déclencher le séchage automatique',
    temperature: 'Température',
    goodBlue: 'Bon (bleu)',
    aboveFairHot: 'Au-dessus = rouge (chaud)',
    historyRetention: 'Rétention d\'historique',
    keepSensorHistory: 'Garder l\'historique pendant',
    historyRetentionDescription: 'Les anciennes données seront supprimées.',
    defaultPrintOptions: 'Options d\'impression par défaut',
    defaultPrintOptionsDescription: 'Définir les valeurs par défaut des options d\'impression. Modifiables dans la boîte de dialogue d\'impression.',
    defaultBedLevelling: 'Nivellement du lit',
    defaultBedLevellingDesc: 'Niveler automatiquement le lit avant l\'impression',
    defaultFlowCali: 'Calibration du flux',
    defaultFlowCaliDesc: 'Calibrer le flux d\'extrusion',
    defaultVibrationCali: 'Calibration des vibrations',
    defaultVibrationCaliDesc: 'Réduire les artefacts de ringing',
    defaultLayerInspect: 'Inspection première couche',
    defaultLayerInspectDesc: 'Inspection IA de la première couche',
    defaultTimelapse: 'Time-lapse',
    defaultTimelapseDesc: 'Enregistrer une vidéo timelapse',
    staggeredStart: 'Démarrage échelonné',
    staggeredStartDescription: 'Taille de groupe et intervalle par défaut lors de l\'échelonnement des démarrages de lots multi-imprimantes. Modifiable par lot dans la fenêtre d\'impression.',
    plateClear: 'Confirmation de plateau libre',
    requirePlateClear: 'Exiger la confirmation de plateau libre',
    requirePlateClearDescription: 'Lorsque cette option est activée, le planificateur attend une confirmation de plateau libre par imprimante avant de lancer les impressions en file d\'attente sur les imprimantes ayant terminé. La désactiver masque également le badge d\'état du plateau et le bouton « Marquer le plateau comme dégagé » sur les cartes d\'imprimante.',
    gcodeInjection: 'Injection de G-code',
    gcodeInjectionDescription: 'Configurez du G-code personnalisé à injecter au début et/ou à la fin des impressions pour les systèmes d\'auto-impression comme Farmloop, SwapMod, AutoClear et Printflow 3D. Les snippets sont configurés par modèle d\'imprimante et appliqués lorsque « Injecter le G-code » est activé sur un élément de file d\'attente.',
    gcodeInjectionNoPrinters: 'Aucune imprimante trouvée. Ajoutez des imprimantes pour configurer les snippets G-code.',
    gcodeStartLabel: 'G-code de début',
    gcodeEndLabel: 'G-code de fin',
    gcodeStartPlaceholder: 'G-code ajouté avant le début de l\'impression...',
    gcodeEndPlaceholder: 'G-code ajouté après la fin de l\'impression...',
    staggerGroupSize: 'Taille du groupe',
    staggerGroupSizeHelp: 'Imprimantes à démarrer simultanément par groupe',
    staggerInterval: 'Intervalle (minutes)',
    staggerIntervalHelp: 'Délai entre chaque démarrage de groupe',
    queueDrying: 'Séchage automatique',
    queueDryingDescription: 'Sécher automatiquement le filament AMS lorsque l\'imprimante est inactive entre les impressions. Utilise le seuil d\'humidité ci-dessus.',
    queueDryingEnabled: 'Activer le séchage automatique',
    queueDryingEnabledDescription: 'Démarrer le séchage AMS automatiquement lorsque l\'imprimante est inactive et l\'humidité dépasse le seuil',
    queueDryingBlock: 'Attendre la fin du séchage',
    queueDryingBlockDescription: 'Bloquer la file d\'attente jusqu\'à la fin du séchage. Désactivé, les impressions sont prioritaires.',
    ambientDryingEnabled: 'Séchage ambiant',
    ambientDryingEnabledDescription: 'Sécher automatiquement le filament sur les imprimantes inactives lorsque l\'humidité dépasse le seuil, même sans impressions en file.',
    dryingPresets: 'Préréglages de séchage',
    dryingPresetsDescription: 'Température et durée par type de filament. AMS 2 Pro utilise des températures plus basses, AMS-HT supporte des températures plus élevées.',
    dryingFilament: 'Filament',
    printModal: 'Fenêtre d\'impression',
    expandCustomMapping: 'Étendre le mapping personnalisé par défaut',
    expandCustomMappingDescription: 'Affiche le mapping AMS par imprimante étendu.',
    // User management
    authentication: 'Authentification',
    authEnabledDescription: 'L\'instance est sécurisée',
    authDisabledDescription: 'Activez pour restreindre l\'accès',
    authDisabledMessage: 'Activez l\'authentification pour gérer comptes et permissions.',
    enableAuthentication: 'Activer l\'authentification',
    currentUser: 'Utilisateur actuel',
    changePassword: 'Changer le mot de passe',
    admin: 'Administrateur',
    users: 'Utilisateurs',
    addUser: 'Ajouter un utilisateur',
    groups: 'Groupes',
    addGroup: 'Ajouter un groupe',
    system: 'Système',
    noDescription: 'Pas de description',
    userCount: '{{count}} utilisateurs',
    permissionCount: '{{count}} permissions',
    createUser: 'Créer un utilisateur',
    username: 'Nom d\'utilisateur',
    enterUsername: 'Entrez l\'utilisateur',
    password: 'Mot de passe',
    enterPassword: 'Entrez le mot de passe',
    passwordRequirements: 'Au moins 8 caractères, avec une majuscule, une minuscule, un chiffre et un caractère spécial.',
    confirmPassword: 'Confirmer le mot de passe',
    confirmPasswordPlaceholder: 'Confirmez le mot de passe',
    // Title tooltips
    viewReleaseOnGitHub: 'Voir la version sur GitHub',
    turnAllPlugsOn: 'Tout allumer',
    turnAllPlugsOff: 'Tout éteindre',
    // Modal: Clear logs
    clearNotificationLogs: 'Effacer les journaux',
    clearLogsMessage: 'Efface définitivement les logs de plus de 30 jours.',
    clearLogs: 'Effacer les logs',
    // Modal: Reset UI
    resetUiPreferences: 'Réinitialiser l\'UI',
    resetUiPreferencesMessage: 'Réinitialise l\'ordre, thème, widgets, etc. N\'affecte pas vos données.',
    resetPreferences: 'Réinitialiser',
    // Modal: Delete group
    deleteGroupTitle: 'Supprimer le groupe',
    deleteGroupMessage: 'Les utilisateurs de ce groupe perdront ces permissions.',
    deleteGroup: 'Supprimer le groupe',
    // Modal: Disable auth
    disableAuthenticationTitle: 'Désactiver l\'authentification',
    disableAuthenticationMessage: 'Instance accessible sans connexion. Les comptes sont conservés.',
    disableAuthentication: 'Désactiver',
    // Additional settings
    configureBambuddy: 'Configurer Bambuddy',
    systemDefault: 'Défaut système',
    archiveSettings: 'Réglages Archives',
    newWindow: 'Nouvelle fenêtre',
    embeddedOverlay: 'Superposition intégrée',
    preferredSlicer: 'Slicer préféré',
    preferredSlicerDescription: 'Application pour ouvrir les fichiers',
    orcaslicerKnownIssuesWarning: 'OrcaSlicer 2.3.2 / 2.4.0-dev ont des bugs CLI connus qui bloquent le découpage de nombreux 3MF créés par Bambu — voir les issues amont #12426 (segfault sur fichiers multi-extrudeurs peints) et #13386 (rejet de validation stricte des plages de paramètres). Bambu Studio est recommandé jusqu\'à ce que les correctifs amont arrivent.',
    useSlicerApi: 'Utiliser l\'API Slicer',
    useSlicerApiDescription: 'Lorsque activé, les actions "Slice" ouvrent le slicer intégré et appellent le sidecar slicer-API. Lorsque désactivé (par défaut), elles redirigent vers le slicer desktop via le schéma URI.',
    slicerCard: 'Slicer',
    orcaslicerApiUrl: 'OrcaSlicer sidecar URL',
    bambuStudioApiUrl: 'Bambu Studio sidecar URL',
    slicerApiUrlDescription: 'URL du conteneur sidecar slicer-API. Laisser vide pour utiliser les variables d\'environnement SLICER_API_URL / BAMBU_STUDIO_API_URL.',
    slicerBundles: {
      title: 'Bundles de slicer',
      description: 'Importez un Printer Preset Bundle (.bbscfg) exporté depuis BambuStudio (Fichier → Exporter → Exporter le bundle de préréglages → "Printer preset bundle"). Une fois importé, les requêtes de découpage peuvent sélectionner les préréglages du bundle par nom sans re-téléverser le triplet de profils JSON.',
      uploadButton: 'Téléverser le bundle',
      uploading: 'Téléversement…',
      loading: 'Chargement des bundles…',
      empty: 'Aucun bundle importé pour le moment.',
      summary: '{{processCount}} processus · {{filamentCount}} préréglages de filament',
      delete: 'Supprimer',
      uploadSuccess: '{{name}} importé',
      uploadError: 'Échec du téléversement du bundle : {{message}}',
      deleteSuccess: 'Bundle supprimé',
      deleteError: 'Échec de la suppression du bundle : {{message}}',
      confirmDeleteTitle: 'Supprimer ce bundle ?',
      confirmDeleteMessage: 'Les requêtes de découpage référençant "{{name}}" échoueront jusqu\'à la réimportation du bundle.',
    },
    externalCameras: 'Caméras externes',
    costTracking: 'Suivi des coûts',
    printsOnly: 'Impressions uniquement',
    totalConsumption: 'Consommation totale',
    dataManagement: 'Gestion des données',
    storageUsage: 'Utilisation du stockage',
    storageUsageDescription: 'Répartition de l\'utilisation des données par catégorie',
    storageUsageTotal: 'Total',
    storageUsageErrors: 'Erreurs',
    storageUsageOtherBreakdown: 'Autre (inclut ressources statiques, scripts et fichiers de configuration)',
    storageUsageSystem: 'Système',
    storageUsageData: 'Données',
    storageUsageUnavailable: 'Informations d\'utilisation du stockage non disponibles',
    clearNotificationLogsDescription: 'Supprimer logs de plus de 30 jours',
    resetUiPreferencesDescription: 'Réinitialise thèmes et affichage sans toucher aux données.',
    enableHomeAssistant: 'Activer Home Assistant',
    enableMqtt: 'Activer MQTT',
    useTls: 'Utiliser TLS',
    enableMetricsEndpoint: 'Activer l\'endpoint Metrics',
    availableMetrics: 'Metrics disponibles',
    editUser: 'Modifier l\'utilisateur',
    deleteUserTitle: 'Supprimer l\'utilisateur',
    groupName: 'Nom du groupe',
    // Placeholders
    leaveEmptyForAnonymous: 'Vide pour anonyme',
    leaveEmptyForNoAuth: 'Vide si pas d\'auth',
    enterNewPassword: 'Nouveau mot de passe',
    confirmNewPassword: 'Confirmer nouveau mdp',
    enterGroupName: 'Entrez le nom du groupe',
    enterDescriptionOptional: 'Description (optionnel)',
    enterCurrentPassword: 'Mdp actuel',
    enterNewPasswordMin6: 'Nouveau mdp (min 6 char)',
    toast: {
      keyCopied: 'Clé copiée',
      copyFailed: 'Échec copie',
      keyAddedToBrowser: 'Clé ajoutée à l\'explorateur',
      clearLogsFailed: 'Échec effacement logs',
      uiPreferencesReset: 'Préférences UI réinitialisées. Rafraîchissement...',
      authDisabled: 'Authentification désactivée',
      authDisableFailed: 'Échec désactivation',
      apiKeyCreated: 'Clé API créée',
      apiKeyDeleted: 'Clé API supprimée',
      userCreated: 'Utilisateur créé',
      userUpdated: 'Utilisateur mis à jour',
      userDeleted: 'Utilisateur supprimé',
      groupCreated: 'Groupe créé',
      groupUpdated: 'Groupe mis à jour',
      groupDeleted: 'Groupe supprimé',
      fillRequiredFields: 'Remplissez les champs requis',
      passwordsDoNotMatch: 'Les mots de passe ne correspondent pas',
      passwordTooShort: 'Le mot de passe doit contenir au moins 8 caractères',
      passwordNeedsUppercase: 'Le mot de passe doit contenir au moins une majuscule',
      passwordNeedsLowercase: 'Le mot de passe doit contenir au moins une minuscule',
      passwordNeedsDigit: 'Le mot de passe doit contenir au moins un chiffre',
      passwordNeedsSpecial: 'Le mot de passe doit contenir au moins un caractère spécial',
      enterGroupName: 'Entrez un nom de groupe',
      settingsSaved: 'Paramètres enregistrés',
      noPermissionUpdate: "Vous n'avez pas l'autorisation de modifier les paramètres",
      cameraSettingsSaved: 'Réglages caméra enregistrés',
      enterCameraUrl: 'Entrez une URL caméra',
      passwordChanged: 'Mot de passe modifié',
      connectionFailed: 'Échec connexion',
      testFailed: 'Échec test',
      cameraConnected: 'Caméra connectée {{resolution}}',
    },
    testConnection: 'Tester la connexion',
    catalog: {
      spoolCatalog: 'Catalogue de Bobines',
      spoolCatalogDescription: 'Poids des bobines vides par marque/type. Utilisé pour le calcul auto du poids restant.',
      searchCatalog: 'Chercher dans le catalogue...',
      addNewEntry: 'Nouvelle Entrée',
      namePlaceholder: 'Nom (ex: Bambu Lab - Plastique)',
      weight: 'Poids',
      type: 'Type',
      default: 'Défaut',
      custom: 'Perso',
      noMatch: 'Aucune entrée correspondante',
      empty: 'Catalogue vide',
      deleteEntry: 'Supprimer l\'entrée',
      deleteConfirm: 'Supprimer "{{name}}" ?',
      resetCatalog: 'Réinitialiser le catalogue',
      resetConfirm: 'Réinitialiser aux valeurs par défaut ? Vos entrées personnalisées seront supprimées.',
      loadFailed: 'Échec chargement catalogue',
      nameWeightRequired: 'Nom et poids requis',
      entryAdded: 'Entrée ajoutée',
      addFailed: 'Échec ajout',
      entryUpdated: 'Entrée mise à jour',
      updateFailed: 'Échec mise à jour',
      entryDeleted: 'Entrée supprimée',
      deleteFailed: 'Échec suppression',
      resetSuccess: 'Catalogue réinitialisé',
      resetFailed: 'Échec réinitialisation',
      exported: '{{count}} entrées exportées',
      imported: '{{added}} entrées importées ({{skipped}} ignorées)',
      importFailed: 'Échec import : format JSON invalide',
      exportTooltip: 'Exporter en JSON',
      importTooltip: 'Importer depuis JSON',
      resetTooltip: 'Réinitialiser par défaut',
      selectedCount: '{{count}} sélectionnés',
      deleteSelected: 'Supprimer la sélection',
      bulkDeleteConfirm: 'Supprimer {{count}} entrées ?',
      bulkDeleted: '{{count}} entrées supprimées',
      bulkDeleteFailed: 'Échec de la suppression',
      material: 'Matériau',
      spoolWeight: 'Poids bobine',
      color: 'Couleur',
      updateSpoolWeight: 'Mettre à jour le poids bobine',
      filamentUpdated: 'Filament mis à jour',
      filamentUpdateFailed: 'Échec de la mise à jour du filament',
      filamentUpdateInvalid: 'Données de filament invalides',
      keepExistingSpoolWeight: 'Conserver l\'ancien poids pour les bobines existantes',
      keepExistingSpoolWeightDesc: 'Les bobines déjà créées avec ce type de filament conservent l\'ancien poids à vide. Les nouvelles bobines utilisent la valeur mise à jour.',
      applyToAllSpools: 'Appliquer à toutes les bobines',
      applyToAllSpoolsDesc: 'Tous les calculs de poids pour ce type de filament utilisent immédiatement le nouveau poids à vide.',
    },
    colorCatalog: {
      title: 'Catalogue de Couleurs',
      description: 'Couleurs de filament par fabricant. Utilisé pour la recherche auto lors de l\'ajout de bobines.',
      searchColors: 'Chercher couleurs...',
      allManufacturers: 'Tous les fabricants',
      addNewColor: 'Nouvelle Couleur',
      manufacturer: 'Fabricant',
      colorName: 'Nom couleur',
      hex: 'Hex',
      materialOptional: 'Matériau (optionnel)',
      showing: 'Affichage {{filtered}} sur {{total}} couleurs',
      noMatch: 'Aucune couleur correspondante',
      empty: 'Catalogue vide',
      deleteColor: 'Supprimer couleur',
      deleteConfirm: 'Supprimer "{{name}}" ?',
      resetCatalog: 'Réinitialiser catalogue couleurs',
      resetConfirm: 'Réinitialiser aux valeurs par défaut ?',
      sync: 'Synchro.',
      starting: 'Démarrage...',
      syncTooltip: 'Sync depuis FilamentColors.xyz (2000+ couleurs, peut être long)',
      loadFailed: 'Échec chargement catalogue couleurs',
      fieldsRequired: 'Fabricant, nom et code Hex requis',
      colorAdded: 'Couleur ajoutée',
      addFailed: 'Échec ajout',
      colorUpdated: 'Couleur mise à jour',
      updateFailed: 'Échec mise à jour',
      colorDeleted: 'Couleur supprimée',
      deleteFailed: 'Échec suppression',
      resetSuccess: 'Catalogue réinitialisé',
      resetFailed: 'Échec réinitialisation',
      syncUpToDate: 'Déjà à jour ({{count}} couleurs vérifiées)',
      syncComplete: '{{added}} couleurs ajoutées ({{skipped}} déjà présentes)',
      syncError: 'Erreur de sync',
      syncFailed: 'Échec sync FilamentColors.xyz',
      exported: '{{count}} couleurs exportées',
      imported: '{{added}} couleurs importées ({{skipped}} ignorées)',
      importFailed: 'Échec import : format JSON invalide',
      selectedCount: '{{count}} sélectionnés',
      deleteSelected: 'Supprimer la sélection',
      bulkDeleteConfirm: 'Supprimer {{count}} couleurs ?',
      bulkDeleted: '{{count}} couleurs supprimées',
      bulkDeleteFailed: 'Échec de la suppression des couleurs',
    },
    // General tab
    dateFormat: 'Format de date',
    dateFormatUs: 'US (MM/JJ/AAAA)',
    dateFormatEu: 'EU (JJ/MM/AAAA)',
    dateFormatIso: 'ISO (AAAA-MM-JJ)',
    timeFormat: 'Format horaire',
    timeFormat12: '12 heures (3:30 PM)',
    timeFormat24: '24 heures (15:30)',
    defaultPrinter: 'Imprimante par défaut',
    defaultPrinterDescription: 'Présélectionner cette imprimante pour les téléversements, réimpressions et autres opérations.',
    slicerBambuStudio: 'Bambu Studio',
    slicerOrcaSlicer: 'OrcaSlicer',
    sidebarOrderDescription: 'Glissez les éléments dans la barre latérale pour réorganiser. Réinitialiser l\'ordre par défaut ici.',
    setDefault: 'Définir par défaut',
    sidebarOrderSetDefaultHint: 'Définir par défaut applique l\'ordre actuel du menu aux utilisateurs qui n\'ont pas encore personnalisé le leur.',
    sidebarDefaultSet: 'L\'ordre du menu par défaut a été défini.',
    sidebarDefaultCleared: 'Ordre du menu par défaut effacé.',
    sidebarDefaultFailed: 'Échec de la définition de l\'ordre du menu par défaut.',
    reset: 'Réinitialiser',
    darkMode: 'Mode sombre',
    lightMode: 'Mode clair',
    active: '(actif)',
    background: 'Arrière-plan',
    accent: 'Accent',
    style: 'Style',
    bgNeutral: 'Neutre',
    bgWarm: 'Chaud',
    bgCool: 'Froid',
    bgOled: 'OLED Noir',
    bgSlate: 'Bleu ardoise',
    bgForest: 'Vert forêt',
    accentGreen: 'Vert',
    accentTeal: 'Sarcelle',
    accentBlue: 'Bleu',
    accentOrange: 'Orange',
    accentPurple: 'Violet',
    accentRed: 'Rouge',
    styleClassic: 'Classique',
    styleGlow: 'Lumineux',
    styleVibrant: 'Vif',
    themeToggleHint: 'Basculer entre le mode sombre et clair avec l\'icône soleil/lune dans la barre latérale.',
    autoArchivePrints: 'Archiver automatiquement les impressions',
    autoArchiveDescription: 'Sauvegarder automatiquement les fichiers 3MF à la fin des impressions',
    saveThumbnailsDescription: 'Extraire et sauvegarder les images d\'aperçu des fichiers 3MF',
    captureFinishPhotoDescription: 'Prendre une photo avec la caméra de l\'imprimante à la fin de l\'impression',
    ffmpegNotInstalled: 'ffmpeg non installé',
    ffmpegRequired: 'La capture caméra nécessite ffmpeg. Installez-le via <brew>brew install ffmpeg</brew> (macOS) ou <apt>apt install ffmpeg</apt> (Linux).',
    camera: 'Caméra',
    cameraViewMode: 'Mode d\'affichage caméra',
    cameraOverlayDescription: 'La caméra s\'ouvre dans un overlay redimensionnable sur l\'écran principal',
    cameraWindowDescription: 'La caméra s\'ouvre dans une fenêtre de navigateur séparée',
    externalCamerasDescription: 'Configurer des caméras externes pour remplacer la caméra intégrée. Supporte les flux MJPEG, RTSP, snapshots HTTP et caméras USB (V4L2). Lorsqu\'activée, la caméra externe est utilisée pour la vue en direct et les photos de fin.',
    cameraPlaceholderUsb: 'Chemin du périphérique (/dev/video0)',
    cameraPlaceholderUrl: 'URL caméra (rtsp://... ou http://...)',
    cameraTypeMjpeg: 'Flux MJPEG',
    cameraTypeRtsp: 'Flux RTSP',
    cameraTypeSnapshot: 'Snapshot HTTP',
    cameraTypeUsb: 'Caméra USB (V4L2)',
    cameraSnapshotUrl: 'URL de capture (facultatif)',
    cameraSnapshotUrlPlaceholder: 'http://192.168.1.61:1984/api/frame.jpeg?src=printer',
    cameraSnapshotUrlHelp: 'URL d\'image unique utilisée pour les miniatures de notification, photos de fin, frames de timelapse par couche et détection de plaque. Le timelapse et la détection de plaque nécessitent chacun leur propre bascule par imprimante — cette URL est juste la source d\'image qu\'ils utilisent lorsqu\'activés. Laisser vide pour capturer depuis le flux en direct ci-dessus. Utile pour go2rtc (/api/frame.jpeg) et caméras IP avec un point de terminaison de capture dédié.',
    cameraRotation: 'Rotation',
    test: 'Tester',
    connected: 'Connecté',
    disconnected: 'Déconnecté',
    currency: 'Devise',
    defaultFilamentCost: 'Coût filament par défaut (par kg)',
    electricityCost: 'Coût électricité par kWh',
    energyDisplayMode: 'Mode d\'affichage énergie',
    energyModePrintDescription: 'Le tableau de bord affiche la somme de l\'énergie utilisée pendant les impressions',
    energyModeTotalDescription: 'Le tableau de bord affiche l\'énergie totale des prises connectées',
    fileManager: 'Gestionnaire de fichiers',
    createArchiveEntry: 'Créer une entrée d\'archive lors de l\'impression',
    createArchiveEntryDescription: 'Lors de l\'impression depuis le gestionnaire de fichiers, créer optionnellement une entrée d\'archive',
    lowDiskSpaceWarning: 'Avertissement espace disque faible',
    lowDiskSpaceDescription: 'Afficher un avertissement lorsque l\'espace disque libre descend sous ce seuil',
    printerFirmware: 'Firmware imprimante',
    checkFirmwareDescription: 'Vérifier les mises à jour firmware de Bambu Lab',
    bambuddySoftware: 'Logiciel Bambuddy',
    autoCheckDescription: 'Vérifier automatiquement les nouvelles versions au démarrage',
    checkNow: 'Vérifier maintenant',
    updateAvailableVersion: 'Mise à jour disponible : v{{version}}',
    releaseNotes: 'Notes de version',
    updateViaDocker: 'Mettre à jour via Docker Compose :',
    updateViaHomeAssistant: 'Les mises à jour sont gérées par le superviseur Home Assistant. Ouvrez Paramètres → Modules complémentaires → Bambuddy dans Home Assistant pour installer la nouvelle version.',
    installUpdate: 'Installer la mise à jour',
    latestVersionRunning: 'Vous utilisez la dernière version',
    failedToCheckUpdates: 'Échec de la vérification des mises à jour : {{error}}',
    backupRestore: 'Sauvegarde & Restauration',
    backupRestoreDescription: 'Exporter/importer les paramètres et configurer la sauvegarde GitHub',
    goToBackup: 'Aller à la sauvegarde',
    externalUrl: 'URL externe',
    externalUrlDescription: 'L\'URL externe où Bambuddy est accessible. Utilisée pour les images de notification et les intégrations externes.',
    bambuddyUrl: 'URL Bambuddy',
    externalUrlHint: 'Inclure le protocole et le port (ex : http://192.168.1.100:8000)',
    ftpRetry: 'Réessai FTP',
    ftpRetryDescription: 'Réessayer les opérations FTP lorsque le WiFi de l\'imprimante est instable. S\'applique aux téléchargements 3MF, uploads d\'impression, téléchargements timelapse et mises à jour firmware.',
    autoRetryDescription: 'Réessayer automatiquement les opérations FTP échouées',
    retryAttempts: 'Tentatives de réessai',
    retryDelay: 'Délai de réessai',
    connectionTimeout: 'Délai de connexion',
    time_one: '{{count}} fois',
    time_other: '{{count}} fois',
    second_one: '{{count}} seconde',
    second_other: '{{count}} secondes',
    nSeconds: '{{count}} secondes',
    increaseForWeakWifi: 'Augmenter pour les imprimantes avec un WiFi faible',
    homeAssistant: 'Home Assistant',
    homeAssistantFullDescription: 'Se connecter à Home Assistant pour contrôler les prises connectées via l\'API REST HA. Supporte les entités switch, light, input_boolean et script.',
    homeAssistantUrl: 'URL Home Assistant',
    longLivedAccessToken: 'Token d\'accès longue durée',
    haTokenHint: 'Créer un token dans HA : Profil → Tokens d\'accès longue durée → Créer un token',
    connectionSuccessful: 'Connexion réussie',
    connectionFailed: 'Connexion échouée',
    haConnectionSuccess: 'Connexion à Home Assistant réussie.',
    haConnectionFailed: 'Échec de la connexion à Home Assistant.',
    mqttPublishing: 'Publication MQTT',
    mqttDescription: 'Publier les événements BamBuddy vers un broker MQTT externe pour l\'intégration avec Node-RED, Home Assistant et d\'autres systèmes d\'automatisation.',
    mqttEnableDescription: 'Publier les événements vers un broker MQTT externe',
    brokerHostname: 'Nom d\'hôte du broker',
    port: 'Port',
    usernameOptional: 'Nom d\'utilisateur (optionnel)',
    passwordOptional: 'Mot de passe (optionnel)',
    topicPrefix: 'Préfixe de topic',
    topicPrefixHint: 'Les topics seront : {{prefix}}/printers/<serial>/status, etc.',
    prometheusMetrics: 'Métriques Prometheus',
    prometheusEndpointDescription: 'Exposer les métriques imprimante sur <code>/api/v1/metrics</code> pour la surveillance Prometheus/Grafana.',
    bearerTokenOptional: 'Token Bearer (optionnel)',
    bearerTokenHint: 'Si défini, les requêtes doivent inclure <code>Authorization: Bearer <token></code>',
    metricsConnectionStatus: 'État de connexion',
    metricsPrinterState: 'État imprimante (idle/printing/etc)',
    metricsPrintProgress: 'Progression impression 0-100%',
    metricsBedTemp: 'Température du plateau',
    metricsNozzleTemp: 'Température de la buse',
    metricsPrintsTotal: 'Total impressions par résultat',
    metricsMore: '...et plus (couches, ventilateurs, file d\'attente, utilisation filament)',
    smartPlugsDescription: 'Connecter des prises connectées (Tasmota ou Home Assistant) pour automatiser le contrôle de l\'alimentation et suivre la consommation d\'énergie de vos imprimantes.',
    allOn: 'Tout allumer',
    allOff: 'Tout éteindre',
    addSmartPlug: 'Ajouter une prise',
    energySummary: 'Résumé énergétique',
    currentPower: 'Puissance actuelle',
    plugsOnline: '{{reachable}}/{{total}} prises en ligne',
    today: 'Aujourd\'hui',
    yesterday: 'Hier',
    total: 'Total',
    enablePlugsForSummary: 'Activer les prises pour voir le résumé énergétique',
    addNotificationProvider: 'Ajouter',
    systemBadge: '(Système)',
    creating: 'Création...',
    changing: 'Modification...',
    deleteUserAndItems: 'Supprimer l\'utilisateur ET ses éléments',
    deleteUserKeepItems: 'Supprimer l\'utilisateur, garder les éléments (deviennent sans propriétaire)',
    ok: 'OK',

    // 2FA settings
    twoFa: {
      totpTitle: 'Application Authenticator (TOTP)',
      totpDesc: 'Utilisez une application comme Google Authenticator, Aegis ou Authy.',
      emailOtpTitle: 'OTP par e-mail',
      emailOtpDesc: 'Envoyez un code à usage unique à {{email}} lors de la connexion.',
      emailOtpNoEmail: 'Ajoutez une adresse e-mail à votre compte pour activer cette méthode.',
      addEmailFirst: 'Votre compte n\'a pas d\'adresse e-mail. Demandez à un administrateur d\'en ajouter une.',
      setupTotp: 'Configurer l\'application Authenticator',
      setupAuthApp: 'Configurer l\'application Authenticator',
      setupInstructions: 'Scannez le QR code avec votre application authenticator, puis confirmez avec un code.',
      manualEntry: 'Impossible de scanner ? Entrez ce secret manuellement :',
      scannedContinue: 'Code scanné — continuer',
      enterCodeToConfirm: 'Entrez le code à 6 chiffres de votre application authenticator pour confirmer.',
      activate: 'Activer',
      disableTotp: 'Désactiver l\'Authenticator',
      disableConfirmHint: 'Entrez un code TOTP valide ou un code de secours pour désactiver l\'authenticator.',
      totpDisabled: 'Application Authenticator désactivée.',
      emailOtpEnabled: 'OTP par e-mail activé.',
      emailOtpDisabled: 'OTP par e-mail désactivé.',
      smtpRequired: 'Veuillez d\'abord configurer et tester les paramètres SMTP.',
      invalidCode: 'Code invalide. Veuillez réessayer.',
      enableEmailOtp: 'Activer OTP par e-mail',
      disableEmailOtp: 'Désactiver OTP par e-mail',
      emailSetupEnterCode: 'Un code de vérification a été envoyé à votre adresse e-mail. Entrez-le ci-dessous pour confirmer que vous possédez cette boîte de réception.',
      verifyAndEnable: 'Vérifier et activer',
      emailDisablePasswordHint: 'Entrez le mot de passe de votre compte pour confirmer la désactivation de l\'OTP par e-mail.',
      passwordPlaceholder: 'Entrez votre mot de passe',
      backupCodesTitle: 'Sauvegardez vos codes de secours',
      backupCodesWarning: 'Conservez ces codes en lieu sûr. Chaque code ne peut être utilisé qu\'une seule fois et ne sera plus affiché.',
      backupCodesRemaining: '{{count}} codes de secours restants',
      savedCodes: 'Codes sauvegardés',
      regenBackup: 'Régénérer les codes de secours',
      regenBackupHint: 'Entrez votre code TOTP actuel pour générer 10 nouveaux codes de secours. Tous les codes existants seront invalidés.',
      newBackupCodes: 'Nouveaux codes de secours',
      linkedAccounts: 'Comptes SSO liés',
      linkedAccountsDesc: 'Ces fournisseurs d\'identité externes sont liés à votre compte.',
      oidcUnlinked: 'Compte dissocié.',
    },

    // OIDC provider settings
    oidc: {
      title: 'Fournisseurs SSO / OIDC',
      desc: 'Configurez des fournisseurs OpenID Connect pour l\'authentification unique.',
      addProvider: 'Ajouter un fournisseur',
      newProvider: 'Nouveau fournisseur',
      empty: 'Aucun fournisseur OIDC configuré.',
      created: 'Fournisseur créé.',
      updated: 'Fournisseur mis à jour.',
      deleted: 'Fournisseur supprimé.',
      refreshIcon: 'Actualiser l\'icône',
      removeIcon: 'Supprimer l\'icône',
      iconRefreshed: 'Icône actualisée.',
      iconRemoved: 'Icône supprimée.',
      iconFetchFailed: 'Impossible de récupérer l\'icône depuis l\'URL du fournisseur.',
      deleteTitle: 'Supprimer le fournisseur',
      deleteMessage: 'Supprimer "{{name}}" ? Tous les comptes liés seront déconnectés.',
      form: {
        name: 'Nom d\'affichage',
        issuerUrl: 'URL de l\'émetteur',
        clientId: 'ID client',
        clientSecret: 'Secret client',
        scopes: 'Portées',
        iconUrl: 'URL de l\'icône (optionnel)',
        enabled: 'Activé',
        autoCreate: 'Créer les utilisateurs automatiquement',
        autoCreateDesc: 'Crée automatiquement un compte local lors de la première connexion.',
        autoLink: 'Lier automatiquement les comptes existants',
        autoLinkDesc: 'Lie les comptes locaux existants par e-mail lors de la première connexion.',
        secretHint: 'laisser vide pour conserver',
        secretPlaceholder: 'nouveau secret',
        emailClaim: 'Claim e-mail',
        emailClaimDesc: "Claim JWT utilisé comme identité e-mail. Utiliser 'preferred_username' ou 'upn' pour Azure Entra ID (qui n'envoie pas email_verified). Utiliser uniquement des noms de claims de confiance.",
        emailClaimPlaceholder: 'e-mail',
        emailClaimCustomClaimAutoLinkWarning: 'Les revendications personnalisées sont sûres pour la liaison auto uniquement lorsque la valeur est administrée par le tenant (par ex. Azure Entra ID upn / preferred_username). N\'activez pas la liaison auto si votre IdP permet aux utilisateurs de s\'auto-attribuer cette revendication.',
        requireEmailVerified: 'Exiger la vérification e-mail',
        requireEmailVerifiedDesc: "N'accepter le claim e-mail que si le fournisseur le marque comme vérifié.",
        requireEmailVerifiedWarning: "Avertissement : l'e-mail sera accepté sans vérification. À utiliser uniquement avec des fournisseurs de confiance.",
        requireEmailVerifiedAutoLink: 'Désactiver le lien automatique d\'abord pour modifier ce paramètre.',
        defaultGroup: 'Groupe par défaut',
        defaultGroupDesc: 'Groupe attribué aux utilisateurs créés automatiquement. Repli sur Viewers si non défini.',
        defaultGroupViewersFallback: 'Viewers (par défaut)',
      },
    },

    // TODO: translate encryption keys
    encryption: {
      title: 'Statut du chiffrement MFA',
      enabledFromEnv: 'Chiffrement au repos activé (clé depuis la variable d\'environnement MFA_ENCRYPTION_KEY)',
      enabledFromFile: 'Chiffrement au repos activé (clé chargée depuis le répertoire de données)',
      enabledGenerated: 'Chiffrement au repos activé avec une clé auto-générée',
      notConfigured: 'Chiffrement au repos non configuré',
      notConfiguredDesc: 'Les secrets TOTP et client_secrets OIDC sont stockés en clair. Définissez MFA_ENCRYPTION_KEY ou redémarrez Bambuddy avec un répertoire de données accessible en écriture pour en générer un automatiquement.',
      allEncrypted: 'Tous les secrets MFA sont chiffrés au repos.',
      legacyRowsLabel: 'Lignes héritées en clair',
      encryptedRowsLabel: 'Lignes chiffrées',
      legacyRowsWarning: '{{count}} ligne(s) héritée(s) en clair détectée(s). Réenregistrez le fournisseur OIDC ou réinscrivez l\'application d\'authentification de l\'utilisateur pour migrer vers le stockage chiffré.',
      backupHint: 'La clé auto-générée est stockée dans DATA_DIR/.mfa_encryption_key et incluse dans les ZIP de sauvegarde locale. Conservez vos sauvegardes en sécurité ou définissez MFA_ENCRYPTION_KEY explicitement.',
      decryptionBrokenTitle: 'Clé de chiffrement manquante',
      decryptionBrokenError: '{{count}} enregistrement(s) chiffré(s) ne peuvent pas être déchiffrés car la clé de chiffrement n\'est plus disponible. Restaurez le MFA_ENCRYPTION_KEY précédent ou DATA_DIR/.mfa_encryption_key pour récupérer.',
      migrationErrorWarning: "{{count}} ligne(s) ancienne(s) n'ont pas pu être rechiffrée(s) au démarrage. Vérifiez les journaux du serveur et redémarrez Bambuddy pour réessayer.",
    },


    spoolbuddy: {
      infoTitle: 'Périphériques SpoolBuddy',
      infoBody: 'Les bornes SpoolBuddy s\'enregistrent automatiquement via heartbeat. Désinscrivez ici un appareil qui n\'est plus utilisé ou un doublon obsolète laissé par un crash du daemon.',
      duplicatesTitle: '{{count}} appareils enregistrés',
      duplicatesBody: 'Seul le premier appareil enregistré est utilisé par l\'interface du kiosk. Si l\'un de ces appareils est un doublon obsolète issu d\'un crash, désinscrivez-le — un appareil en ligne se ré-enregistrera automatiquement au prochain heartbeat.',
      empty: 'Aucun appareil SpoolBuddy enregistré pour le moment.',
      online: 'En ligne',
      offline: 'Hors ligne',
      unregister: 'Désinscrire',
      unregisterSuccess: 'Appareil désinscrit',
      unregisterError: 'Échec de la désinscription',
      confirmTitle: 'Désinscrire l\'appareil SpoolBuddy ?',
      confirmBody: 'Cela retirera "{{hostname}}" ({{deviceId}}) de la base de données. Si l\'appareil est en ligne, il se ré-enregistrera automatiquement au prochain heartbeat.',
      ipAddress: 'Adresse IP',
      firmware: 'Micrologiciel',
      lastSeen: 'Dernière activité',
      daemonUptime: 'Uptime du daemon',
      systemUptime: 'Uptime système',
      never: 'jamais',
      nfc: 'NFC',
      scale: 'Balance',
      cpuTemp: 'Temp. CPU',
      memory: 'Mémoire',
      disk: 'Disque',
      update: 'Mettre à jour',
      updateConfirmTitle: 'Mettre à jour le daemon Spoolbuddy ?',
      updateConfirmBody: 'Déclencher une mise à jour logicielle sur "{{hostname}}" ? Le daemon redémarrera une fois la mise à jour appliquée.',
      restartBrowser: 'Redémarrer le navigateur',
      restartBrowserConfirmTitle: 'Redémarrer le navigateur du kiosk ?',
      restartBrowserConfirmBody: 'Redémarrer le navigateur du kiosk sur "{{hostname}}" ? L\'écran sera momentanément vide.',
      restartDaemon: 'Redémarrer le daemon',
      restartDaemonConfirmTitle: 'Redémarrer le daemon Spoolbuddy ?',
      restartDaemonConfirmBody: 'Redémarrer le daemon Spoolbuddy sur "{{hostname}}" ? L\'appareil sera hors ligne quelques secondes.',
      reboot: 'Redémarrer',
      rebootConfirmTitle: 'Redémarrer l\'appareil ?',
      rebootConfirmBody: 'Redémarrer "{{hostname}}" ? L\'appareil sera hors ligne pendant environ une minute.',
      shutdown: 'Éteindre',
      shutdownConfirmTitle: 'Éteindre l\'appareil ?',
      shutdownConfirmBody: 'Éteindre "{{hostname}}" ? Vous aurez besoin d\'un accès physique pour le rallumer.',
      commandConfirm: 'Confirmer',
      commandQueued: 'Commande en file d\'attente',
      commandError: 'Échec de l\'envoi de la commande',
    },
  },

  // Notifications (for push notifications)
  notification: {
    printStarted: {
      title: 'Impression démarrée',
      body: '{{printer}} : {{filename}} commence l\'impression',
    },
    printCompleted: {
      title: 'Impression terminée',
      body: '{{printer}} : {{filename}} a réussi',
    },
    printFailed: {
      title: 'Impression échouée',
      body: '{{printer}} : {{filename}} a échoué',
    },
    printStopped: {
      title: 'Impression arrêtée',
      body: '{{printer}} : {{filename}} a été arrêtée',
    },
    printProgress: {
      title: 'Progression d\'impression',
      body: '{{printer}} : {{filename}} est à {{percent}}%',
    },
    printerOffline: {
      title: 'Imprimante hors ligne',
      body: '{{printer}} est déconnectée',
    },
    printerError: {
      title: 'Erreur imprimante',
      body: '{{printer}} : {{error}}',
    },
    filamentLow: {
      title: 'Filament bas',
      body: '{{printer}} : Le filament est presque vide',
    },
    maintenanceDue: {
      title: 'Maintenance due',
      body: '{{printer}} : {{items}} demandent votre attention',
    },
  },

  // Errors
  errors: {
    generic: 'Une erreur est survenue',
    networkError: 'Erreur réseau. Vérifiez votre connexion.',
    notFound: 'Non trouvé',
    unauthorized: 'Non autorisé',
    serverError: 'Erreur serveur',
    validationError: 'Vérifiez vos saisies',
    printerConnectionFailed: 'Échec connexion imprimante',
    saveFailed: 'Échec enregistrement',
    deleteFailed: 'Échec suppression',
    loadFailed: 'Échec chargement',
  },

  // HMS Errors modal
  hmsErrors: {
    title: 'Erreurs - {{name}}',
    noErrors: 'Aucune erreur',
    viewOnWiki: 'Voir sur le Wiki Bambu Lab',
    clearInstructions: 'Effacez les erreurs sur l\'imprimante pour les retirer ici.',
    clearErrors: 'Effacer les erreurs',
    clearSuccess: 'Erreurs HMS effacées',
    clearFailed: 'Échec de l\'effacement des erreurs HMS',
  },

  // MQTT Debug modal
  mqttDebug: {
    title: 'Journal de débogage MQTT',
    searchPlaceholder: 'Chercher topic ou message...',
    noMessages: 'Aucun message enregistré',
    startLoggingHint: 'Cliquez sur "Démarrer" pour capturer les messages MQTT',
    noMessagesMatch: 'Aucun message ne correspond',
    adjustFilterHint: 'Ajustez votre recherche',
    incoming: 'Entrant',
    outgoing: 'Sortant',
    loggingStopped: 'Enregistrement arrêté',
    loggingActive: 'Enregistrement actif - rafraîchissement auto',
    startLogging: 'Démarrer',
    stopLogging: 'Arrêter',
    clearLog: 'Effacer le journal',
    topic: 'Sujet',
    timestamp: 'Horodatage',
    direction: 'Direction',
    all: 'Tous',
  },

  // Printer File Manager modal (printer internal storage)
  printerFiles: {
    title: 'Gestionnaire de fichiers',
    storageUsed: 'Utilisé :',
    storageFree: 'Libre :',
    filterPlaceholder: 'Filtrer les fichiers...',
    deleteButton: 'Supprimer',
    deleteFiles: 'Supprimer {{count}} fichiers',
    deleteFileConfirm: 'Supprimer "{{name}}" ?',
    deleteFilesConfirm: 'Supprimer les {{count}} fichiers sélectionnés ?',
    noFiles: 'Aucun fichier sur l\'imprimante',
    loadingFiles: 'Chargement...',
    failedToLoad: 'Échec chargement fichiers',
    toast: {
      filesDeleted: '{{count}} fichier(s) supprimé(s)',
      deleteFailed: 'Échec suppression : {{error}}',
    },
  },

  // Confirmations
  confirm: {
    delete: 'Voulez-vous vraiment supprimer cet élément ?',
    unsavedChanges: 'Changements non sauvegardés. Voulez-vous quitter ?',
    clearQueue: 'Voulez-vous vraiment vider la file d\'attente ?',
  },

  // Login page
  login: {
    title: 'Connexion Bambuddy',
    subtitle: 'Connectez-vous à votre compte',
    username: 'Utilisateur',
    usernamePlaceholder: 'Entrez votre utilisateur',
    usernameOrEmail: 'Utilisateur ou Email',
    usernameOrEmailPlaceholder: 'Utilisateur ou @ Email',
    password: 'Mot de passe',
    passwordPlaceholder: 'Entrez votre mot de passe',
    signIn: 'Se connecter',
    signingIn: 'Connexion...',
    rememberMe: 'Se souvenir de moi',
    forgotPassword: 'Mot de passe oublié ?',
    loginSuccess: 'Connecté avec succès',
    loginFailed: 'Échec de connexion',
    enterCredentials: 'Entrez vos identifiants',
    enterEmail: 'Veuillez entrer votre adresse e-mail',
    oidcLoginFailed: 'Échec de la connexion OIDC',
    oidcErrors: {
      providerError: "Le fournisseur d'identité a renvoyé une erreur",
      missingParameters: 'Il manque des paramètres requis dans le callback OIDC',
      invalidState: "L'état OIDC est invalide ou a déjà été utilisé",
      stateExpired: 'La session OIDC a expiré — veuillez réessayer',
      providerNotFound: 'Fournisseur OIDC introuvable',
      discoveryFailed: 'Impossible de récupérer le document de découverte OIDC',
      invalidDiscovery: 'Le document de découverte OIDC est invalide',
      networkError: "Erreur réseau lors de l'échange de jeton OIDC",
      badResponse: "Réponse inattendue lors de l'échange de jeton OIDC",
      noIdToken: "Le fournisseur OIDC n'a pas renvoyé de jeton d'identité",
      validationFailed: 'La validation du jeton OIDC a échoué',
      nonceMismatch: 'Le nonce OIDC ne correspond pas — possible attaque par rejeu',
      missingSubClaim: 'Le jeton OIDC est dépourvu de la revendication sub',
      noLinkedAccount: 'Aucun compte local est lié à cette identité OIDC',
      accountInactive: 'Votre compte est inactif',
      userResolutionFailed: 'Impossible de résoudre votre compte',
      internalError: 'Une erreur interne est survenue lors de la connexion OIDC',
      tokenExchangeFailed: "L'échange de jeton OIDC a échoué",
    },
    forgotPasswordTitle: 'Mot de passe oublié',
    forgotPasswordMessage: 'Contactez votre administrateur pour réinitialiser votre accès.',
    forgotPasswordEmailMessage: 'Entrez votre email pour recevoir un nouveau mot de passe.',
    emailAddress: 'Adresse Email',
    emailPlaceholder: 'votre.email@exemple.com',
    cancel: 'Annuler',
    sending: 'Envoi...',
    sendResetEmail: 'Envoyer l\'email',
    howToReset: 'Comment réinitialiser :',
    resetStep1: 'Contactez votre admin Bambuddy',
    resetStep2: 'Demandez une réinitialisation dans la Gestion Utilisateurs',
    resetStep3: 'Il vous donnera un mot de passe temporaire',
    resetStep4: 'Connectez-vous et changez-le dans les Paramètres',
    gotIt: 'Compris',
    twoFA: {
      title: 'Authentification à deux facteurs',
      subtitle: 'Votre compte est protégé par la 2FA. Saisissez le code de vérification ci-dessous.',
      methodAuthenticator: "Application d'authentification",
      methodEmail: 'Code par e-mail',
      methodBackup: 'Code de récupération',
      instructionsTotp: "Ouvrez votre application d'authentification et saisissez le code à 6 chiffres pour Bambuddy.",
      instructionsEmail: 'Un code à 6 chiffres a été envoyé à votre adresse e-mail. Il est valable 10 minutes.',
      instructionsEmailNotSent: 'Cliquez ci-dessous pour recevoir un code de vérification par e-mail.',
      instructionsBackup: "Saisissez l'un de vos codes de récupération à 8 caractères. Chaque code ne peut être utilisé qu'une seule fois.",
      sendCodeButton: 'Envoyer le code par e-mail',
      sendingCode: 'Envoi en cours...',
      resendCode: 'Renvoyer le code',
      codeLabel: 'Code de vérification',
      backupCodeLabel: 'Code de récupération',
      codePlaceholder: '000000',
      backupCodePlaceholder: 'XXXXXXXX',
      verifyButton: 'Vérifier',
      verifyingButton: 'Vérification en cours...',
      backToLogin: '← Retour à la connexion',
      orContinueWith: 'ou continuer avec',
      signInWith: 'Se connecter avec {{provider}}',
      enterCode: 'Veuillez entrer le code de vérification',
      sendCodeFailed: 'Échec de l\'envoi du code de vérification',
      invalidCode: 'Code invalide. Veuillez réessayer.',
    },

    resetPassword: {
      title: 'Définir un nouveau mot de passe',
      subtitle: 'Saisissez et confirmez votre nouveau mot de passe ci-dessous.',
      newPassword: 'Nouveau mot de passe',
      newPasswordPlaceholder: 'Au moins 8 caractères',
      confirmPassword: 'Confirmer le mot de passe',
      confirmPasswordPlaceholder: 'Répéter le nouveau mot de passe',
      saving: 'Enregistrement…',
      submit: 'Définir le mot de passe',
      backToLogin: 'Retour à la connexion',
      passwordsDoNotMatch: 'Les mots de passe ne correspondent pas',
      passwordTooShort: 'Le mot de passe doit contenir au moins 8 caractères',
      resetFailed: 'Échec de la réinitialisation. Le lien a peut-être expiré.',
    },
  },

  // Setup page
  setup: {
    title: 'Configuration Bambuddy',
    subtitle: 'Configurez l\'authentification',
    enableAuth: 'Activer l\'authentification',
    adminAccount: 'Compte Admin',
    adminAccountDesc: 'Si des admins existent, ils seront utilisés. Sinon, créez-en un ci-dessous.',
    adminUsername: 'Utilisateur Admin',
    adminPassword: 'Mot de passe Admin',
    optionalIfAdminExists: '(optionnel si admin existe)',
    adminUsernamePlaceholder: 'Nom admin (optionnel)',
    adminPasswordPlaceholder: 'Mdp admin (optionnel)',
    confirmPassword: 'Confirmer mdp',
    confirmPasswordPlaceholder: 'Confirmez mdp admin',
    settingUp: 'Configuration...',
    completeSetup: 'Terminer la configuration',
    toast: {
      authEnabledAdminCreated: 'Authentification activée et admin créé',
      authEnabledExistingAdmins: 'Authentification activée avec admins existants',
      setupCompleted: 'Configuration terminée',
      enterBothCredentials: 'Entrez les deux ou laissez vide pour utiliser les admins existants',
      passwordsDoNotMatch: 'Les mots de passe ne correspondent pas',
      passwordTooShort: 'Minimum 6 caractères',
    },
  },

  // Password change
  changePassword: {
    title: 'Changer le mot de passe',
    currentPassword: 'Mot de passe actuel',
    currentPasswordPlaceholder: 'Entrez mdp actuel',
    newPassword: 'Nouveau mot de passe',
    newPasswordPlaceholder: 'Nouveau mdp (min 6 char)',
    confirmPassword: 'Confirmer nouveau mdp',
    confirmPasswordPlaceholder: 'Confirmez nouveau mdp',
    passwordsDoNotMatch: 'Les mots de passe ne correspondent pas',
    passwordTooShort: 'Minimum 6 caractères',
    changing: 'Changement...',
    success: 'Mot de passe modifié',
    failed: 'Échec modification',
  },

  // Plate detection alert
  plateAlert: {
    title: 'Impression en Pause !',
    message: 'Objets détectés sur le plateau. L\'impression a été suspendue automatiquement. Videz le plateau avant de reprendre.',
    understand: 'J\'ai compris',
  },

  // Camera page
  camera: {
    title: 'Vue Caméra',
    invalidPrinterId: 'ID imprimante invalide',
    live: 'Direct',
    snapshot: 'Instantané',
    restartStream: 'Redémarrer le flux',
    refreshSnapshot: 'Rafraîchir l\'image',
    fullscreen: 'Plein écran',
    exitFullscreen: 'Quitter plein écran',
    connectingToCamera: 'Connexion caméra...',
    capturingSnapshot: 'Capture...',
    connectionLost: 'Connexion perdue',
    connectionFailed: 'Échec connexion caméra',
    reconnecting: 'Reconnexion dans {{countdown}}s... (essai {{attempt}}/{{max}})',
    reconnectNow: 'Reconnexion immédiate',
    cameraUnavailable: 'Caméra indisponible',
    cameraUnavailableDesc: 'Vérifiez que l\'imprimante est allumée.',
    noCamera: 'Aucune caméra disponible',
    retry: 'Réessayer',
    cameraStream: 'Flux caméra',
    zoomOut: 'Zoom arrière',
    zoomIn: 'Zoom avant',
    resetZoom: 'Réinitialiser zoom',
    recording: 'Enregistrement',
    startRecording: 'Démarrer l\'enregistrement',
    stopRecording: 'Arrêter l\'enregistrement',
    chamberLight: 'Basculer lumière chambre',
    unavailable: 'Caméra indisponible',
    diagnose: {
      button: 'Diagnostic',
      modalTitle: 'Diagnostic de la caméra',
      running: 'Diagnostic en cours...',
      runFailed: 'Impossible d\'exécuter le diagnostic : {{error}}',
      retry: 'Relancer',
      stage: {
        tcp_reachable: 'Accessibilité réseau',
        first_frame: 'Capture d\'image',
        live_stream_active: 'Flux en direct actif',
      },
      summary: {
        all_ok: 'La caméra fonctionne. Toutes les étapes du diagnostic ont réussi.',
        live_stream_active_healthy: 'La caméra diffuse actuellement avec des images récentes — aucun test nécessaire.',
        printer_unreachable: 'L\'imprimante n\'est pas joignable. Vérifiez l\'adresse IP, la connexion réseau et que l\'imprimante est allumée.',
        camera_port_closed: 'L\'imprimante répond mais le port de la caméra est fermé. Assurez-vous que le mode LAN et le mode développeur sont activés dans les paramètres de l\'imprimante.',
        no_frame: 'Connexion à la caméra établie mais aucune image reçue. Réessayez ou vérifiez que la caméra est activée dans les paramètres de l\'imprimante.',
        unknown_failure: 'Le diagnostic de la caméra a échoué pour une raison inconnue. Consultez le journal de support pour plus de détails.',
      },
      meta: {
        protocol: 'Protocole',
        port: 'Port',
        profile: 'Profil',
      },
    },
  },

  // Groups management
  groups: {
    title: 'Gestion des Groupes',
    subtitle: 'Gérez les permissions pour le contrôle d\'accès',
    backToSettings: 'Retour aux paramètres',
    createGroup: 'Créer un groupe',
    noPermission: 'Accès refusé.',
    system: 'Système',
    noDescription: 'Pas de description',
    usersCount: '{{count}} utilisateurs',
    permissionsCount: '{{count}} permissions',
    edit: 'Modifier',
    delete: 'Supprimer',
    toast: {
      created: 'Groupe créé',
      updated: 'Groupe mis à jour',
      deleted: 'Groupe supprimé',
      enterGroupName: 'Entrez un nom de groupe',
    },
    modal: {
      editGroup: 'Modifier le groupe',
      createGroup: 'Créer un groupe',
      cancel: 'Annuler',
      saving: 'Enregistrement...',
      creating: 'Création...',
      saveChanges: 'Enregistrer',
    },
    form: {
      groupName: 'Nom du groupe',
      groupNamePlaceholder: 'Nom du groupe',
      systemGroupWarning: 'Les groupes système sont fixes',
      description: 'Description',
      descriptionPlaceholder: 'Description (optionnel)',
      permissions: 'Permissions ({{count}} sélectionnées)',
    },
    deleteModal: {
      title: 'Supprimer le groupe',
      message: 'Les utilisateurs de ce groupe perdront ces permissions.',
      confirm: 'Supprimer',
    },
    editor: {
      title: 'Modifier le groupe',
      createTitle: 'Créer un groupe',
      search: 'Rechercher des permissions...',
      selectAll: 'Tout sélectionner',
      clearAll: 'Tout désélectionner',
      permissionsSelected: '{{count}} sélectionnée(s)',
      noResults: 'Aucune permission ne correspond à votre recherche',
    },
  },

  // Users management
  users: {
    title: 'Gestion des Utilisateurs',
    subtitle: 'Gérez les accès à Bambuddy',
    backToSettings: 'Retour aux paramètres',
    createUser: 'Créer un utilisateur',
    noPermission: 'Accès refusé.',
    admin: 'Administrateur',
    noGroups: 'Aucun groupe',
    active: 'Actif',
    inactive: 'Inactif',
    edit: 'Modifier',
    delete: 'Supprimer',
    system: 'Système',
    noGroupsAvailable: 'Aucun groupe disponible',
    table: {
      username: 'Utilisateur',
      groups: 'Groupes',
      status: 'Statut',
      actions: 'Actions',
    },
    toast: {
      created: 'Utilisateur créé',
      updated: 'Utilisateur mis à jour',
      deleted: 'Utilisateur supprimé',
      fillRequired: 'Remplissez les champs requis',
      passwordsDoNotMatch: 'Les mots de passe ne correspondent pas',
      passwordTooShort: 'Minimum 6 caractères',
      ldapProvisioned: 'Utilisateur LDAP "{{username}}" provisionné',
    },
    modal: {
      createUser: 'Créer utilisateur',
      editUser: 'Modifier utilisateur',
      cancel: 'Annuler',
      creating: 'Création...',
      saving: 'Enregistrement...',
      saveChanges: 'Enregistrer',
      advancedAuthSubtitle: 'avec Authentification Avancée',
      // Manual LDAP provisioning (#1298) — English fallbacks
      tabsAriaLabel: 'Source utilisateur',
      localTab: 'Local',
      ldapTab: 'LDAP',
      ldapSearchLabel: 'Rechercher dans l\'annuaire',
      ldapSearchPlaceholder: 'Tapez un nom d\'utilisateur, un nom ou un e-mail...',
      ldapMinChars: 'Tapez au moins 2 caractères pour rechercher',
      ldapTypeToSearch: 'Commencez à taper pour rechercher dans l\'annuaire LDAP',
      ldapSearching: 'Recherche dans l\'annuaire...',
      ldapNoResults: 'Aucun utilisateur correspondant dans l\'annuaire',
      ldapSearchError: 'Échec de la recherche dans l\'annuaire. Vérifiez l\'état du serveur LDAP.',
      ldapAlreadyProvisioned: 'Déjà provisionné',
      ldapSelectedLabel: 'Sélectionné',
      ldapProvision: 'Provisionner l\'utilisateur',
      ldapProvisioning: 'Provisionnement...',
      ldapErrorProvision: 'Échec du provisionnement. Vérifiez l\'état du serveur LDAP et réessayez.',
    },
    form: {
      username: 'Utilisateur',
      usernamePlaceholder: 'Nom utilisateur',
      email: 'E-mail',
      emailPlaceholder: 'utilisateur@exemple.com',
      password: 'Mot de passe',
      passwordPlaceholder: 'Mot de passe',
      confirmPassword: 'Confirmer mdp',
      confirmPasswordPlaceholder: 'Confirmez mdp',
      newPasswordPlaceholder: 'Nouveau mdp',
      confirmNewPasswordPlaceholder: 'Confirmez nouveau mdp',
      leaveBlankToKeep: 'Laissez vide pour conserver l\'actuel',
      groups: 'Groupes',
      optional: 'optionnel',
      autoGeneratedPassword: 'Un mot de passe sera généré et envoyé par email.',
      passwordManagedByAdvancedAuth: 'Géré par Auth Avancée. Utilisez "Réinitialiser" pour envoyer un nouveau mdp par email.',
      resetPassword: 'Réinitialiser le mot de passe',
      resettingPassword: 'Réinitialisation...',
    },
    deleteModal: {
      title: 'Supprimer utilisateur',
      message: 'Cette action est irréversible.',
      confirm: 'Supprimer',
    },
  },

  // Stream overlay
  streamOverlay: {
    title: 'Superposition Flux',
    invalidPrinterId: 'ID invalide',
    cameraStream: 'Flux caméra',
    progress: 'Progression',
    eta: 'Fin estimée',
    printerIdle: 'Imprimante inactive',
    printerOffline: 'Imprimante hors ligne',
    status: {
      printing: 'Impression',
      paused: 'En pause',
      finished: 'Terminée',
      failed: 'Échouée',
      idle: 'Inactive',
      unknown: 'Inconnue',
    },
  },

  // Profiles
  profiles: {
    title: 'Profils',
    subtitle: 'Gérez vos presets slicer et calibrations Pressure Advance',
    tabs: {
      cloud: 'Profils Cloud',
      local: 'Profils Locaux',
      kprofiles: 'Profils K',
    },
    localProfiles: {
      title: 'Profils Locaux',
      subtitle: 'Gérez vos presets OrcaSlicer',
      import: 'Importer Profils',
      importDesc: 'Déposez les fichiers .bbscfg, .bbsflmt, .orca_filament, .zip ou .json',
      importing: 'Importation...',
      search: 'Chercher un preset...',
      noPresets: 'Aucun preset local',
      badge: 'Local',
      edit: 'Modifier',
      delete: 'Supprimer',
      cancel: 'Annuler',
      deleteConfirmTitle: 'Supprimer Preset',
      deleteConfirm: 'Supprimer définitivement ce preset ?',
      source: 'Source',
      inheritsFrom: 'Hérite de',
      filamentType: 'Type',
      vendor: 'Vendeur',
      compatiblePrinters: 'Imprimantes',
      nozzleTemp: 'Temp Buse',
      cost: 'Coût',
      density: 'Densité',
      pressureAdvance: 'Pressure Advance',
      filament: 'Filament',
      process: 'Processus',
      printer: 'Imprimante',
      toast: {
        importSuccess: '{{count}} profil(s) importé(s)',
        importSkipped: '{{count}} profil(s) ignoré(s) (doublons)',
        importError: '{{count}} erreur(s) d\'import',
        deleted: 'Preset supprimé',
        updated: 'Preset mis à jour',
      },
    },
    connectedAs: 'Connecté en tant que',
    logout: 'Déconnexion',
    noLogoutPermission: 'Pas d\'autorisation de déconnexion',
    failedToLoad: 'Échec chargement profils',
    retry: 'Réessayer',
    time: {
      justNow: 'À l\'instant',
      minsAgo: 'Il y a {{count}}m',
      hoursAgo: 'Il y a {{count}}h',
      daysAgo: 'Il y a {{count}}j',
    },
    toast: {
      loggedOut: 'Déconnecté',
    },
    login: {
      title: 'Connexion Bambu Cloud',
      subtitle: 'Synchronisez vos presets slicer',
      email: 'E-mail',
      password: 'Mot de passe',
      region: 'Région',
      regionGlobal: 'Global',
      regionChina: 'Chine',
      verificationCode: 'Code de vérification',
      totpCode: 'Code Authenticator',
      checkEmail: 'Code envoyé à {{email}}',
      enterTotpHint: 'Entrez le code 2FA',
      accessToken: 'Jeton d\'accès (Access Token)',
      accessTokenHint: 'Collez votre jeton Bambu Cloud. Les comptes de la région Chine doivent passer par ici (liés à un numéro de téléphone — pas de connexion par e-mail). Le wiki explique comment récupérer le jeton depuis les cookies MakerWorld.',
      back: 'Retour',
      loginButton: 'Connexion',
      verifyButton: 'Vérifier',
      setTokenButton: 'Définir Jeton',
      useToken: 'Utiliser jeton d\'accès',
      useEmail: 'Connexion par email',
      toast: {
        loggedIn: 'Connecté avec succès',
        codeSent: 'Code envoyé par email',
        enterTotp: 'Entrez le code Authenticator',
        tokenSet: 'Jeton défini',
      },
    },
    presets: {
      myPreset: 'Mon preset (modifiable)',
      duplicate: 'Dupliquer',
      editable: 'Modifiable',
      failedToLoadDetails: 'Échec détails preset',
      deleteConfirm: 'Supprimer ce preset ?',
      deleteWarning: 'Ceci supprimera "{{name}}" de Bambu Cloud définitivement.',
      noDuplicatePermission: 'Pas d\'autorisation duplication',
      noEditPermission: 'Pas d\'autorisation modification',
      noDeletePermission: 'Pas d\'autorisation suppression',
      types: {
        filament: 'Preset filament',
        printer: 'Preset imprimante',
        process: 'Preset processus',
      },
      toast: {
        deleted: 'Preset supprimé',
        created: 'Preset créé',
        updated: 'Preset mis à jour',
        duplicated: 'Preset dupliqué',
        fieldAdded: 'Champ "{{key}}" ajouté',
        exported: 'Preset exporté',
      },
      baseLabel: 'Base : {{name}}',
      currentLabel: 'Actuel : {{name}}',
      newPreset: 'Nouveau Preset',
      editPreset: 'Modifier Preset',
      duplicatePreset: 'Dupliquer Preset',
      createNewPreset: 'Créer un nouveau Preset',
      customizeSettings: 'Personnalisez vos réglages',
      compareWithBase: 'Comparer avec la base',
      compare: 'Comparer',
      // CreatePresetModal - Basic Info
      basePreset: 'Preset de base',
      selectBasePreset: 'Choisir preset de base...',
      presetName: 'Nom du preset',
      myCustomPreset: 'Mon preset personnalisé',
      inheritsFrom: 'Hérite de',
      dropJsonToImport: 'Glissez JSON pour importer',
      // CreatePresetModal - Tabs
      tabs: {
        common: 'Commun',
        allFields: 'Tous les champs',
      },
      // CreatePresetModal - All Fields Tab
      availableFields: 'Champs disponibles',
      searchFieldsPlaceholder: 'Chercher un champ...',
      noMatchingFields: 'Aucun champ trouvé',
      allFieldsAdded: 'Tous les champs sont ajoutés',
      addCustomField: 'Ajouter un champ personnalisé',
      yourOverrides: 'Vos modifications',
      noOverridesYet: 'Aucune modification',
      clickFieldsToAdd: 'Cliquez à gauche pour ajouter',
      saveAsTemplate: 'Enregistrer comme modèle',
      jsonTip: 'Conseil : Glissez un .json pour importer les réglages',
    },
    cloudView: {
      searchPlaceholder: 'Chercher presets...',
      templates: 'Modèles',
      refresh: 'Rafraîchir',
      newPreset: 'Nouveau Preset',
      clearFilters: 'Effacer filtres',
      // Compare mode
      compareMode: 'Mode Comparaison',
      selectAnotherPreset: 'Choisir un autre preset {{type}}',
      clickTwoPresets: 'Choisissez deux presets de même type',
      selectFirst: '1. Sélectionner premier',
      selectSecond: '2. Sélectionner second',
      compareNow: 'Comparer maintenant',
      // Status row
      lastSynced: 'Synchronisé :',
      showingCount: '{{showing}} sur {{total}} presets',
      noPresetsFound: 'Aucun preset trouvé',
      // Column headers
      columns: {
        filament: 'Filament',
        process: 'Processus',
        printer: 'Imprimante',
      },
      noFilamentPresets: 'Pas de preset filament',
      noProcessPresets: 'Pas de preset processus',
      noPrinterPresets: 'Pas de preset imprimante',
      // Filters
      filters: {
        type: 'Type',
        owner: 'Propriétaire',
        printer: 'Imprimante',
        nozzle: 'Buse',
        filament: 'Filament',
        layer: 'Couche',
        all: 'Tous',
        myPresets: 'Mes Presets',
        builtIn: 'Inclus',
        process: 'Processus',
      },
      // Permissions
      noTemplatesPermission: 'Pas d\'autorisation modèles',
      noRefreshPermission: 'Pas d\'autorisation rafraîchissement',
      noCreatePermission: 'Pas d\'autorisation création',
    },
    templates: {
      title: 'Modèles rapides',
      noTemplates: 'Aucun modèle',
      createFirst: 'Créez-en depuis l\'éditeur de preset',
      typeFilter: 'Type :',
      deleteTitle: 'Supprimer modèle',
      deleteWarning: 'Action irréversible',
      deleteConfirm: 'Supprimer "{{name}}" ?',
      namePlaceholder: 'Nom du modèle',
      descriptionPlaceholder: 'Description',
      settingsJson: 'Paramètres (JSON)',
      fieldsCount: '{{count}} champs',
      shownInModals: 'Visible dans fenêtres',
      hiddenInModals: 'Masqué dans fenêtres',
      apply: 'Appliquer',
      toast: {
        deleted: 'Modèle supprimé',
        updated: 'Modèle mis à jour',
        created: 'Modèle créé',
        applied: 'Modèle appliqué',
      },
    },
  },

  // Support/Debug
  support: {
    debugLoggingActive: 'Débogage actif',
    manageLogs: 'Gérer',
    collectItem7: 'Connectivité et versions firmware',
    collectItem8: 'Statut intégrations (Spoolman, MQTT, HA)',
    collectItem9: 'Interfaces réseau (sous-réseaux)',
    collectItem10: 'Versions packages Python',
    collectItem11: 'Santé base de données',
    collectItem12: 'Détails environnement Docker',
  },

  // File manager
  fileManager: {
    title: 'Gestionnaire de fichiers',
    subtitle: 'Organisez vos fichiers d\'impression',
    uploadFiles: 'Téléverser fichiers',
    newFolder: 'Nouveau dossier',
    folderName: 'Nom du dossier',
    folderNamePlaceholder: 'ex: Pièces Utiles',
    renameFile: 'Renommer fichier',
    renameFolder: 'Renommer dossier',
    moveFiles: 'Déplacer {{count}} fichier(s)',
    rootNoFolder: 'Racine (aucun dossier)',
    current: 'actuel',
    linkFolder: 'Lier le dossier',
    linkFolderDescription: 'Lier "{{name}}" à un projet ou archive.',
    project: 'Projet',
    archive: 'Archive',
    noProjectsFound: 'Aucun projet trouvé',
    noArchivesFound: 'Aucune archive trouvée',
    unlink: 'Délier',
    link: 'Lier',
    dragDropFiles: 'Glissez les fichiers ici',
    dropFilesHere: 'Déposez ici',
    orClickToBrowse: 'ou cliquez pour parcourir',
    allFileTypesSupported: 'Tous types supportés. ZIP extraits.',
    zipFilesDetected: 'ZIP détectés',
    zipExtractOptions: 'Choix de structure pour ZIP :',
    preserveZipStructure: 'Garder structure ZIP',
    createFolderFromZip: 'Dossier par nom du ZIP',
    stlThumbnailGeneration: 'Vignettes STL',
    zipMayContainStl: 'Extraction vignettes possible pour STL.',
    thumbnailsCanBeGenerated: 'Génération vignettes (peut être long).',
    generateThumbnailsForStl: 'Générer vignettes STL',
    threemfDetected: 'Fichiers 3MF détectés',
    threemfExtractionInfo: 'Réglages extraits auto du 3MF.',
    willBeExtracted: 'Sera extrait',
    filesExtracted: '{{count}} fichiers extraits',
    uploadComplete: 'Terminé : {{succeeded}} succès',
    uploadFailed: 'Échec du téléversement',
    zipFilesFailed: '{{count}} fichiers échoués',
    uploading: 'Téléversement...',
    changeLink: 'Modifier lien...',
    linkTo: 'Lier à...',
    linkToProjectOrArchive: 'Lier à projet ou archive',
    addToQueue: 'Ajouter à la file',
    schedulePrint: 'Planifier',
    generateThumbnail: 'Générer vignette',
    generateThumbnails: 'Générer vignettes',
    generateThumbnailsForMissing: 'Vignettes STL manquantes',
    gridView: 'Grille',
    listView: 'Liste',
    lowDiskSpaceWarning: 'Espace disque faible',
    lowDiskSpaceDetails: '{{free}} libre sur {{total}}. Seuil : {{threshold}} Go.',
    files: 'Fichiers',
    folders: 'Dossiers',
    size: 'Taille',
    free: 'Libre',
    allFiles: 'Tous les fichiers',
    wrap: 'Retour ligne',
    enableTextWrapping: 'Activer retour ligne',
    disableTextWrapping: 'Désactiver retour ligne',
    collapse: 'Réduire',
    collapseFoldersByDefault: 'Réduire les dossiers par défaut',
    expandFoldersByDefault: 'Développer les dossiers par défaut',
    dragToResizeTooltip: 'Glisser pour redimensionner, double-clic reset',
    searchFiles: 'Chercher fichiers...',
    allTypes: 'Tous types',
    prints: 'Impressions',
    ascending: 'Croissant',
    descending: 'Décroissant',
    resultsCount: '{{showing}} sur {{total}} fichiers',
    selectAll: 'Tout sélectionner',
    deselectAll: 'Tout désélectionner',
    selected: '{{count}} sélectionnés',
    adding: 'Ajout...',
    loadingFiles: 'Chargement...',
    folderIsEmpty: 'Dossier vide',
    noFilesYet: 'Aucun fichier',
    folderEmptyDescription: 'Téléversez ou déplacez des fichiers ici.',
    noFilesDescription: 'Téléversez des fichiers pour organiser.',
    noMatchingFiles: 'Aucun fichier correspondant',
    noMatchingFilesDescription: 'Ajustez votre recherche.',
    clearFilters: 'Effacer filtres',
    printedCount: 'Imprimé {{count}}x',
    uploadedBy: 'Téléversé par',
    deleteFolder: 'Supprimer dossier',
    deleteFile: 'Supprimer fichier',
    deleteFilesCount: 'Supprimer {{count}} fichiers',
    deleteFolderConfirm: 'Supprimer le dossier et son contenu ?',
    deleteFileConfirm: 'Supprimer ce fichier ?',
    deleteFilesConfirm: 'Supprimer {{count}} fichiers définitivement ?',
    deleting: 'Suppression...',
    noPermissionRenameFolder: 'Pas d\'autorisation renommage',
    noPermissionLinkFolder: 'Pas d\'autorisation lien',
    noPermissionDeleteFolder: 'Pas d\'autorisation suppression dossier',
    noPermissionPrint: 'Pas d\'autorisation impression',
    noPermissionSlice: 'Vous n\'avez pas la permission de découper des fichiers',
    noPermissionAddToQueue: 'Pas d\'autorisation file',
    noPermissionDownload: 'Pas d\'autorisation téléchargement',
    noPermissionRenameFile: 'Pas d\'autorisation renommage fichier',
    noPermissionGenerateThumbnail: 'Pas d\'autorisation vignettes',
    noPermissionDeleteFile: 'Pas d\'autorisation suppression fichier',
    noPermissionCreateFolder: 'Pas d\'autorisation nouveau dossier',
    noPermissionUpload: 'Pas d\'autorisation téléversement',
    noPermissionMoveFiles: 'Pas d\'autorisation déplacement',
    noPermissionDeleteFiles: 'Pas d\'autorisation suppression groupée',
    // External folder
    linkExternal: 'Lier externe',
    linkExternalFolder: 'Lier un dossier externe',
    linkExternalFolderDescription: 'Monter un répertoire hôte (NAS, USB, partage réseau) dans le gestionnaire de fichiers. Les fichiers ne sont pas copiés — ils sont lus directement depuis le chemin d\'origine.',
    externalFolderNamePlaceholder: 'ex. Impressions NAS',
    externalPath: 'Chemin hôte',
    externalPathHelp: 'Chemin absolu du répertoire sur l\'hôte Docker. Doit être monté en bind dans le conteneur.',
    readOnly: 'Lecture seule',
    readOnlyHelp: 'empêche les téléversements et suppressions',
    showHiddenFiles: 'Afficher les fichiers cachés (fichiers point)',
    externalFolder: 'Dossier externe',
    scanFolder: 'Scanner',
    toast: {
      folderCreated: 'Dossier créé',
      folderDeleted: 'Dossier supprimé',
      fileDeleted: 'Fichier supprimé',
      filesDeleted: '{{count}} fichiers supprimés',
      filesMoved: 'Fichiers déplacés',
      folderLinked: 'Dossier lié',
      folderUnlinked: 'Dossier délié',
      externalFolderLinked: 'Dossier externe lié et scanné',
      folderScanned: 'Scan terminé : {{added}} ajoutés, {{removed}} supprimés',
      addedToQueue: '{{count}} fichier(s) ajouté(s)',
      addedToQueuePartial: '{{added}} ajoutés, {{failed}} échecs',
      failedToAddToQueue: 'Échec ajout file : {{error}}',
      fileRenamed: 'Fichier renommé',
      folderRenamed: 'Dossier renommé',
      thumbnailsGenerated: '{{count}} vignette(s) générée(s)',
      thumbnailsGeneratedPartial: '{{succeeded}} succès, {{failed}} échecs',
      noStlMissingThumbnails: 'Aucun STL sans vignette',
      failedToGenerateThumbnails: 'Échec vignettes : {{error}}',
      thumbnailGenerated: 'Vignette générée',
      failedToGenerateThumbnail: 'Échec vignette : {{error}}',
    },
  },

  // Projects
  projects: {
    title: 'Projets',
    subtitle: 'Suivez vos projets d\'impression 3D',
    newProject: 'Nouveau Projet',
    editProject: 'Modifier Projet',
    deleteProject: 'Supprimer Projet',
    projectName: 'Nom du Projet',
    description: 'Description',
    noProjects: 'Aucun projet',
    noProjectsFiltered: 'Aucun projet {{status}}',
    noProjectsFilteredHelp: 'Vous n\'avez aucun projet "{{status}}". Les projets apparaîtront ici lorsque leur statut changera.',
    createFirst: 'Créez votre premier projet pour organiser vos builds.',
    createFirstButton: 'Créer votre premier projet',
    create: 'Créer',
    files: 'Fichiers',
    prints: 'Impressions',
    plates: 'plateaux',
    parts: 'pièces',
    lastModified: 'Modifié le',
    deleteConfirm: 'Supprimer ce projet ? Les archives seront déliées mais conservées.',
    addFiles: 'Ajouter fichiers',
    removeFile: 'Retirer fichier',
    viewDetails: 'Détails',
    // Modal fields
    namePlaceholder: 'ex: Build Voron 2.4',
    descriptionPlaceholder: 'Description optionnelle...',
    urlLabel: 'URL',
    urlPlaceholder: 'https://makerworld.com/...',
    urlInvalid: "L'URL doit commencer par http:// ou https://",
    openExternalUrl: 'Ouvrir l’URL du projet',
    coverImageLabel: 'Photo de couverture',
    coverImageAlt: 'Photo de couverture du projet',
    coverImageUpload: 'Téléverser',
    coverImageReplace: 'Remplacer',
    coverImageRemove: 'Supprimer',
    color: 'Couleur',
    targetPlates: 'Plateaux cibles',
    targetPlatesPlaceholder: 'ex: 25',
    targetPlatesHelp: 'Nombre total de jobs',
    targetParts: 'Pièces cibles',
    targetPartsPlaceholder: 'ex: 150',
    targetPartsHelp: 'Nombre total d\'objets',
    tagsLabel: 'Tags (séparés par virgules)',
    tagsPlaceholder: 'ex: voron, cadeau',
    dueDate: 'Échéance',
    priority: 'Priorité',
    priorityLow: 'Basse',
    priorityNormal: 'Normale',
    priorityHigh: 'Haute',
    priorityUrgent: 'Urgente',
    // Status
    statusActive: 'Actif',
    statusCompleted: 'Terminé',
    statusArchived: 'Archivé',
    done: 'Fait',
    completed: 'terminé',
    failed: 'échoué',
    inQueue: 'en file',
    noPrintsYet: 'Aucune impression',
    // Footer stats
    printJobs: 'Jobs (plateaux)',
    partsPrinted: 'Pièces imprimées',
    failedParts: 'Pièces échouées',
    // Actions
    import: 'Importer',
    export: 'Exporter',
    importProject: 'Importer projet',
    exportAll: 'Exporter tous les projets',
    loading: 'Chargement des projets...',
    // Permissions
    noEditPermission: 'Pas d\'autorisation de modification',
    noDeletePermission: 'Pas d\'autorisation de suppression',
    noCreatePermission: 'Pas d\'autorisation de création',
    noImportPermission: 'Pas d\'autorisation d\'import',
    noExportPermission: 'Pas d\'autorisation d\'export',
    // Toast
    toast: {
      created: 'Projet créé',
      updated: 'Projet mis à jour',
      deleted: 'Projet supprimé',
      imported: 'Projet importé',
      multipleImported: '{{count}} projets importés',
      importFailed: 'Échec d\'import',
      exported: 'Projets exportés (métadonnées)',
    },
  },

  // Project detail page
  projectDetail: {
    notFound: 'Projet non trouvé',
    backToProjects: 'Retour aux Projets',
    export: 'Exporter',
    exportProject: 'Exporter projet',
    noExportPermission: 'Pas d\'autorisation export',
    noEditPermission: 'Pas d\'autorisation modification',
    partOf: 'Fait partie de :',
    priorityLabel: 'Priorité :',
    noPrints: 'Aucune impression dans ce projet',
    status: {
      active: 'Actif',
      completed: 'Terminé',
      archived: 'Archivé',
    },
    priority: {
      low: 'Basse',
      normal: 'Normale',
      high: 'Haute',
      urgent: 'Urgente',
    },
    dueDate: {
      overdue: 'En retard',
      today: 'Aujourd\'hui',
      daysLeft: '{{count}} jours restants',
    },
    progress: {
      platesProgress: 'Progression Plateaux',
      partsProgress: 'Progression Pièces',
      printJobs: 'jobs d\'impression',
      parts: 'pièces',
      percentComplete: '{{percent}}% terminé',
      remaining: '{{count}} restant(s)',
    },
    stats: {
      printJobs: 'Jobs d\'Impression',
      total: 'total',
      failed: '{{count}} échecs',
      partsPrinted: '{{count}} pièces imprimées',
      printTime: 'Temps d\'Impression',
      filamentUsed: 'Filament utilisé',
    },
    cost: {
      title: 'Suivi des coûts',
      filamentCost: 'Coût Filament',
      energy: 'Énergie',
      totalCost: 'Coût Total',
      total: 'Total',
      includesBom: 'BOM incluse',
      budget: 'Budget',
      remaining: 'Restant',
    },
    subProjects: {
      title: 'Sous-projets ({{count}})',
    },
    notes: {
      title: 'Notes',
      noEditPermission: 'Pas d\'autorisation modification',
      placeholder: 'Ajouter des notes...',
      empty: 'Aucune note. Cliquez sur modifier.',
    },
    files: {
      title: 'Fichiers',
      linkFolders: 'Liez des dossiers depuis le gestionnaire',
      forQuickAccess: 'pour un accès rapide.',
      fileCount: '{{count}} fichier(s)',
      empty: 'Aucun dossier lié.',
      noFiles: 'Aucun fichier dans ce dossier.',
      print: 'Imprimer maintenant',
      addToQueue: 'Ajouter à la file',
    },
    bom: {
      title: 'BOM (Liste matériel)',
      acquired: '{{completed}}/{{total}} acquis',
      showAll: 'Tout afficher',
      hideDone: 'Masquer acquis',
      addPart: 'Ajouter matériel',
      noAddPermission: 'Pas d\'autorisation ajout',
      partNamePlaceholder: 'Nom (ex: Vis M3x8)',
      partName: 'Nom de pièce',
      qty: 'Qté',
      price: 'Prix ({{currency}})',
      sourcingUrlPlaceholder: 'Lien d\'achat (optionnel)',
      remarksPlaceholder: 'Remarques (optionnel)',
      deletePart: 'Supprimer pièce',
      deleteConfirm: 'Supprimer "{{name}}" ?',
      noUpdatePermission: 'Pas d\'autorisation mise à jour',
      noEditPermission: 'Pas d\'autorisation modification',
      noDeletePermission: 'Pas d\'autorisation suppression',
      totalCost: 'Coût total :',
      empty: 'BOM vide. Ajoutez du matériel ou de l\'électronique.',
    },
    timeline: {
      title: 'Historique d\'activité',
      empty: 'Aucune activité.',
    },
    template: {
      saveAsTemplate: 'Enregistrer comme modèle',
      noCreatePermission: 'Pas d\'autorisation modèle',
    },
    queue: {
      title: 'File d\'attente',
      viewAll: 'Tout voir',
      printing: '{{count}} en cours',
      queued: '{{count}} en file',
    },
    prints: {
      title: 'Impressions ({{count}})',
    },
    toast: {
      projectUpdated: 'Projet mis à jour',
      partAdded: 'Pièce ajoutée',
      partRemoved: 'Pièce retirée',
      exportFailed: 'Échec export',
      projectExported: 'Projet exporté',
      templateCreated: 'Modèle créé',
    },
  },

  // System info
  system: {
    title: 'Informations Système',
    version: 'Version',
    uptime: 'Temps de fonctionnement',
    cpuUsage: 'Utilisation CPU',
    memoryUsage: 'Utilisation RAM',
    diskUsage: 'Utilisation Disque',
    networkInfo: 'Infos Réseau',
    logs: 'Journaux',
    debugMode: 'Mode Débogage',
    enableDebug: 'Activer débogage',
    disableDebug: 'Désactiver débogage',
    downloadLogs: 'Télécharger logs',
    clearLogs: 'Effacer logs',
    dockerInfo: 'Infos Docker',
    containerName: 'Nom conteneur',
    imageName: 'Nom image',
    platform: 'Plateforme',
    architecture: 'Architecture',
  },

  // Library (K Profiles)
  library: {
    title: 'Bibliothèque Filament',
    addFilament: 'Ajouter Filament',
    editFilament: 'Modifier Filament',
    deleteFilament: 'Supprimer Filament',
    vendor: 'Vendeur',
    material: 'Matériau',
    color: 'Couleur',
    kFactor: 'Facteur K',
    temperature: 'Température',
    noFilaments: 'Bibliothèque vide',
    deleteConfirm: 'Supprimer ce filament ?',
    importFromPrinter: 'Importer de l\'imprimante',
    exportToFile: 'Exporter vers fichier',
  },

  // Slice (slicer-API integration via SliceModal)
  slice: {
    title: 'Slicer le modèle',
    action: 'Slicer',
    slicing: 'Découpage…',
    printer: 'Profil d\'imprimante',
    process: 'Profil de processus',
    filament: 'Profil de filament',
    filamentSlot: 'Filament {{index}} – {{type}}',
    selectPreset: '— Sélectionner un préréglage —',
    loadingPresets: 'Chargement des préréglages…',
    analyzingPlateFilaments: 'Analyse des filaments de la plaque…',
    analyzingPlateFilamentsHint: 'Exécution d\'un découpage d\'aperçu pour identifier les emplacements AMS utilisés. Mis en cache après — la réouverture est instantanée.',
    previewToast: 'Analyse de {{name}} – {{elapsed}}',
    previewWithProgress: 'Analyse de {{name}} – {{stage}} ({{percent}} %) – {{elapsed}}',
    notUsedByPlate: '— non utilisé par cette plaque',
    printerMismatch: 'Ce 3MF a été découpé pour {{source}}, mais vous avez choisi {{target}}. La CLI du slicer ne peut pas re-découper un 3MF pour une autre imprimante — ouvrez la source dans Bambu Studio, changez d\'imprimante et réexportez.',
    noPresetsForSlot: 'Aucun préréglage disponible',
    presetsLoadFailed: 'Échec du chargement des préréglages. Ouvrez Paramètres → Profils pour les importer d\'abord.',
    allPresetsRequired: 'Tous les préréglages doivent être sélectionnés',
    enqueuing: 'Envoi du travail de découpage…',
    queued: 'En file d\'attente…',
    failed: 'Échec du découpage. Vérifiez les journaux du sidecar.',
    startedToast: 'Découpage de {{name}} en arrière-plan…',
    queuedToast: 'En attente : {{name}} – {{elapsed}}',
    runningToast: 'Découpage {{name}} – {{elapsed}}',
    runningWithProgress: '{{name}} – {{stage}} ({{percent}} %) – {{elapsed}}',
    completedToast: '{{name}} découpé',
    failedToast: 'Échec du découpage de {{name}} : {{detail}}',
    tier: {
      local: 'Importé',
      cloud: 'Cloud',
      standard: 'Standard',
    },
    cloud: {
      notAuthenticated: 'Connectez-vous à Bambu Cloud (Paramètres → Profils → Cloud) pour voir vos préréglages cloud.',
      expired: 'Session Bambu Cloud expirée – reconnectez-vous pour actualiser vos préréglages cloud.',
      unreachable: 'Bambu Cloud est inaccessible. Les préréglages locaux et standards fonctionnent encore.',
    },
    bedType: {
      label: 'Plateau d\'impression',
      auto: 'Auto (utiliser le préréglage de processus)',
      coolPlate: 'Cool Plate',
      coolPlateSuperTack: 'Cool Plate SuperTack',
      engineering: 'Engineering Plate',
      highTemp: 'High Temp Plate',
      texturedPEI: 'Textured PEI Plate',
      smoothPEI: 'Smooth PEI Plate',
    },
  },

  // Spoolman
  spoolman: {
    title: 'Intégration Spoolman',
    enabled: 'Spoolman Activé',
    url: 'URL Spoolman',
    connected: 'Connecté',
    disconnected: 'Non Connecté',
    testConnection: 'Tester connexion',
    sync: 'Synchroniser',
    syncing: 'Sync...',
    lastSync: 'Dernière Sync',
    linkToSpoolman: 'Lier à Spoolman',
    openInSpoolman: 'Ouvrir Spoolman',
    unlinkSpool: 'Délier bobine',
    unlinkConfirmTitle: 'Désassigner la bobine ?',
    unlinkConfirmMessage: 'Cela retirera la bobine de cet emplacement. Les données de la bobine elles-mêmes resteront inchangées.',
    selectSpool: 'Choisir bobine',
    noUnlinkedSpools: 'Pas de bobine libre',
    linkSuccess: 'Bobine assignée avec succès',
    linkFailed: 'Échec de l’assignation',
    unlinkSuccess: 'Bobine désassignée avec succès',
    unlinkFailed: 'Échec du désassignement',
    linkedSpool: 'Bobine assignée',
    spoolId: 'ID Bobine',
    fillSourceLabel: '(Spoolman)',
    weight: 'Poids',
    remaining: 'Restant',
    disableWeightSync: 'Désactiver Sync poids estimé AMS',
    disableWeightSyncDesc: 'Ne pas utiliser les estimations AMS. Utile si vous préférez le suivi Spoolman.',
    reportPartialUsage: 'Rapporter consommation partielle pour échecs',
    reportPartialUsageDesc: 'Si l\'impression échoue, rapporte le filament consommé selon les couches.',
  },

  // Inventory
  inventory: {
    title: 'Inventaire de Bobines',
    subtitle: 'Gérez vos bobines',
    spoolmanMixedContentTitle: 'Spoolman ne peut pas se charger en HTTPS — contenu mixte bloqué par votre navigateur',
    spoolmanMixedContentBody: 'Bambuddy est servi en HTTPS (via votre reverse proxy), mais votre URL Spoolman est encore en HTTP. Les navigateurs bloquent le contenu mixte pour des raisons de sécurité, donc l\'interface Spoolman intégrée ne peut pas s\'afficher. Spoolman doit être accessible en HTTPS.',
    spoolmanMixedContentFixReverseProxy: 'Placez Spoolman derrière le même reverse proxy que Bambuddy (Traefik / Nginx / Caddy) en HTTPS, puis mettez à jour l\'URL Spoolman dans les Paramètres avec la nouvelle adresse HTTPS.',
    spoolmanMixedContentFixOpenNewTab: 'Alternative : ouvrez Spoolman dans un nouvel onglet en HTTP — les règles de contenu mixte ne s\'appliquent qu\'aux cadres intégrés, un onglet autonome fonctionne.',
    spoolmanOpenInNewTab: 'Ouvrir Spoolman dans un nouvel onglet',
    labels: {
      title: 'Imprimer étiquettes de bobines',
      selectedCount: '{{count}} sélectionné',
      pickSpools: 'Choisissez les bobines pour lesquelles imprimer des étiquettes :',
      searchPlaceholder: 'Rechercher par nom, marque ou #ID',
      filterByMaterial: 'Matériau :',
      allMaterials: 'Tout',
      selectVisible: 'Sélectionner tout visible ({{count}})',
      deselectVisible: 'Désélectionner visible',
      clearAll: 'Tout effacer',
      noSpoolsToShow: 'Aucune bobine à afficher. Ajustez votre filtre et réessayez.',
      noMatches: 'Aucune bobine ne correspond à la recherche ou au filtre actuel.',
      printOne: 'Imprimer l\'étiquette de cette bobine',
      printLabels: 'Imprimer les étiquettes…',
      bulkTitle: 'Choisissez les bobines à étiqueter parmi les {{count}} affichées',
      noSpoolsTitle: 'Aucune bobine à étiqueter',
      error: 'Impossible de générer les étiquettes : {{msg}}',
      sortBy: {
        label: 'Trier :',
        id: 'Par ID',
        color: 'Par couleur',
      },
      templates: {
        ams: {
          label: 'AMS holder (30 × 15 mm)',
          hint: 'Une étiquette par page ; convient au support d\'étiquettes AMS populaire.',
        },
        box40x30: {
          label: 'Étiquette boîte (40 × 30 mm)',
          hint: 'Une étiquette par page ; taille de rouleau DK/Brother courante, idéale pour étiqueter sachets de filament et bacs de stockage.',
        },
        box: {
          label: 'Box label (62 × 29 mm)',
          hint: 'Une étiquette par page ; dimensionnée pour Brother PT/QL et petites étiquettes Dymo.',
        },
        averyL7160: {
          label: 'Avery L7160 — A4 sheet (38.1 × 63.5 mm × 21)',
          hint: 'Format de feuille UE ; 21 étiquettes par page A4.',
        },
        avery5160: {
          label: 'Avery 5160 — US Letter sheet (25.4 × 66.7 mm × 30)',
          hint: 'Format de feuille US ; 30 étiquettes par page Letter.',
        },
      },
    },
    addSpool: 'Ajouter Bobine',
    editSpool: 'Modifier Bobine',
    copySpool: 'Copier Bobine',
    material: 'Matériau',
    selectMaterial: 'Choisir matériau...',
    subtype: 'Sous-type',
    brand: 'Marque',
    searchBrand: 'Chercher marque...',
    useCustomBrand: 'Utiliser "{{brand}}"',
    useCustomMaterial: 'Utiliser un matériau personnalisé : {{material}}',
    colorName: 'Nom de couleur',
    colorNamePlaceholder: 'Blanc Jade, Rouge Feu...',
    color: 'Couleur',
    hexColor: 'Code Hex',
    pickColor: 'Choisir couleur perso',
    labelWeight: 'Poids net',
    coreWeight: 'Poids bobine vide',
    searchSpoolWeight: 'Chercher poids bobine...',
    weightUsed: 'Consommé',
    currentWeight: 'Poids restant',
    measuredWeight: 'Poids mesuré',
    spoolName: 'Bobine',
    costPerKg: 'Coût par kg',
    storageLocation: 'Emplacement de stockage',
    storageLocationPlaceholder: 'ex. Étagère A, Tiroir 1',
    openInInventory: "Ouvrir dans l'inventaire",
    measuredWeightError: 'Le poids mesuré doit être entre {{min}}g et {{max}}g.',
    slicerFilament: 'Filament Slicer',
    slicerFilamentName: 'Nom du Preset Slicer',
    slicerPreset: 'Preset Slicer',
    searchPresets: 'Chercher presets...',
    selectedPreset: 'Sélectionné',
    noPresetsFound: 'Aucun preset trouvé',
    tempOverrides: 'Exceptions Température',
    note: 'Note',
    notePlaceholder: 'Notes additionnelles sur cette bobine...',
    // Per-spool category + low-stock threshold override (#729)
    category: 'Catégorie',
    categoryPlaceholder: 'ex. Production, Prototype, Client A',
    categoryNone: 'Sans catégorie',
    storageLocationNone: 'Aucun emplacement défini',
    lowStockThresholdOverride: 'Seuil bas (cette bobine)',
    lowStockThresholdOverrideHelp: 'Laisser vide pour utiliser le seuil global ({{global}} %).',
    clearRfid: 'Effacer le tag RFID',
    rfidCleared: 'Tag RFID effacé',
    archive: 'Archiver',
    restore: 'Restaurer',
    noSpools: 'Aucune bobine. Ajoutez votre première bobine pour commencer.',
    noAvailableSpools: 'Aucune bobine disponible. Ajoutez une bobine à votre inventaire ou désaffectez-en une d\'un autre emplacement.',
    kProfiles: 'Profils K',
    addKProfile: 'Ajouter K-Profile',
    assignSpool: 'Assigner Bobine',
    unassignSpool: 'Désassigner',
    assignSuccess: 'Bobine assignée et slot AMS configuré',
    assignFailed: 'Échec assignation',
    selectSpool: 'Choisir une bobine pour ce slot',
    assigned: 'Assigné',
    assigning: 'Assignation...',
    searchSpools: 'Chercher bobines...',
    showAllSpools: 'Afficher toutes les bobines',
    spoolmanSpools: 'Bobines Spoolman',
    allMaterials: 'Tous Matériaux',
    filterByBrand: 'Filtrer par marque...',
    showArchived: 'Afficher archivées',
    quickAdd: 'Ajout rapide (Stock)',
    quantity: 'Quantité',
    stock: 'Stock',
    configured: 'Configuré',
    spoolsCreated: '{{count}} bobines créées',
    spoolsPartiallyCreated: '{{created}} sur {{total}} bobines créées (certaines ont échoué)',
    spoolCreated: 'Bobine créée',
    spoolUpdated: 'Bobine mise à jour',
    spoolDeleted: 'Bobine supprimée',
    deepLinkSpoolNotFound: 'Bobine introuvable',
    deepLinkFetchFailed: 'Impossible de charger la bobine — réessayez',
    spoolArchived: 'Bobine archivée',
    spoolRestored: 'Bobine restaurée',
    kProfileSaveFailed: 'Les paramètres de profil K n\'ont pas pu être enregistrés',
    syncWeightSpoolNotFound: 'Bobine introuvable — elle a peut-être été supprimée',
    syncWeightSpoolmanUnreachable: 'Spoolman est inaccessible — réessayez plus tard',
    syncWeightFailed: 'Échec de la synchronisation du poids',
    spoolmanUnreachable: 'Spoolman est inaccessible — veuillez réessayer plus tard',
    deleteSpoolNotFound: 'Bobine introuvable — elle a peut-être déjà été supprimée',
    deleteFailed: 'Impossible de supprimer la bobine',
    archiveSpoolNotFound: 'Bobine introuvable — elle a peut-être déjà été supprimée',
    archiveFailed: 'Impossible d\'archiver la bobine',
    restoreSpoolNotFound: 'Bobine introuvable — elle a peut-être déjà été supprimée',
    restoreFailed: 'Impossible de restaurer la bobine',
    saveFailed: 'Impossible d\'enregistrer les modifications',
    tagClearFailed: 'Impossible de supprimer le tag',
    deleteConfirm: 'Supprimer définitivement cette bobine ?',
    archiveConfirm: 'Voulez-vous vraiment archiver cette bobine ?',
    advancedSettings: 'Paramètres Avancés',
    // Tabs
    filamentInfoTab: 'Infos Filament',
    paProfileTab: 'Profil PA',
    filamentInfo: 'Filament',
    additional: 'Additionnel',
    // Cloud
    loadingPresets: 'Chargement des presets cloud...',
    cloudConnected: 'Cloud connecté',
    cloudNotConnected: 'Cloud déconnecté (valeurs par défaut)',
    // Colors
    recentColors: 'Récentes',
    searchColors: 'Chercher couleurs...',
    searchResults: 'Résultats',
    allColors: 'Toutes',
    commonColors: 'Communes',
    showLess: 'Moins',
    showAll: 'Toutes',
    noColorsFound: 'Aucune couleur correspondante',
    noResults: 'Aucun résultat',
    // Multi-color gradient + visual effect (#1154) — English fallback.
    extraColorsLabel: 'Couleurs supplémentaires',
    extraColorsPlaceholder: 'EC984C,#6CD4BC,A66EB9,D87694',
    extraColorsHint: 'Collez 2 à 8 stops hex, séparés par des virgules. Affiché en dégradé.',
    extraColorsInvalid: 'Hex non valides ignorés : {{tokens}}',
    colorEffectLabel: 'Effet',
    colorEffect: {
      none: 'Aucun',
      sparkle: 'Pailleté',
      wood: 'Bois',
      marble: 'Marbre',
      glow: 'Lueur',
      matte: 'Mat',
      silk: 'Soie',
      galaxy: 'Galaxie',
      rainbow: 'Arc-en-ciel',
      metal: 'Métal',
      translucent: 'Translucide',
      gradient: 'Dégradé',
      dualColor: 'Bicolore',
      triColor: 'Tricolore',
      multicolor: 'Multicolore',
    },
    // PA Profiles
    selectMaterialFirst: 'Veuillez choisir un matériau dans l\'onglet Infos Filament.',
    noPrintersConfigured: 'Ajoutez une imprimante pour utiliser les profils PA.',
    matchingFilter: 'Correspondant',
    anyBrand: 'Toute marque',
    anyVariant: 'Toute variante',
    autoSelect: 'Auto-sélection',
    matches: 'correspondances',
    match: 'correspondance',
    noMatches: 'Aucune correspondance',
    connected: 'Connecté',
    offline: 'Hors ligne',
    printerOffline: 'Imprimante hors ligne. Connectez-vous pour voir les profils.',
    noKProfilesMatch: 'Aucun profil K ne correspond au filament.',
    leftNozzle: 'Buse Gauche',
    rightNozzle: 'Buse Droite',
    profilesSelected: 'profil(s) de calibration sélectionné(s)',
    // Stats & enhanced table
    totalInventory: 'Total Inventaire',
    totalConsumed: 'Total Consommé',
    byMaterial: 'Par Matériau',
    inPrinter: 'Dans Imprimante',
    lowStock: 'Stock Bas',
    sinceTracking: 'Depuis le début du suivi',
    resetUsage: 'Réinitialiser l\'usage à 0',
    resetUsageTooltip: 'Remettre à zéro le compteur de grammes consommés pour cette bobine',
    resetUsageConfirm: 'Remettre à 0 le compteur de grammes consommés de cette bobine ? Les futures impressions repartiront de zéro. La bobine, son calcul de poids restant et vos paramètres ne sont pas modifiés.',
    resetAllUsage: 'Réinitialiser l\'usage de toutes les bobines',
    resetAllUsageTooltip: 'Remettre à zéro le compteur de grammes consommés sur chaque bobine active',
    resetAllUsageConfirm: 'Remettre à 0 le compteur de grammes consommés sur les {{count}} bobines actives ? Cela efface la valeur « Total Consommé » pour que les futures impressions repartent de zéro. Les bobines et les poids restants ne sont pas modifiés.',
    usageReset: 'Usage de la bobine remis à 0',
    allUsageReset: '{{count}} bobine(s) réinitialisée(s)',
    resetUsageFailed: 'Échec de la réinitialisation de l\'usage',
    loadedInAms: 'Chargé dans AMS/Ext',
    remaining: 'Restant',
    weightCheck: 'Vérification poids',
    lastWeighed: 'Dernière pesée',
    neverWeighed: 'Jamais pesé',
    search: 'Chercher bobines...',
    showing: 'Affichage',
    to: 'à',
    of: 'sur',
    show: 'Voir',
    spools: 'bobines',
    spool: 'bobine',
    page: 'Page',
    noSpoolsMatch: 'Aucun résultat trouvé',
    noSpoolsMatchDesc: 'Ajustez votre recherche ou vos filtres.',
    active: 'Actif',
    archived: 'Archivé',
    all: 'Tous',
    used: 'Occasion',
    new: 'Neuf',
    clearFilters: 'Effacer filtres',
    table: 'Tableau',
    cards: 'Cartes',
    net: 'Réseau',
    // Grouping
    groupSimilar: 'Grouper',
    groupedSpools: '{{count}} bobines identiques',
    groupedRows: 'lignes',
    // Column config
    columns: 'Colonnes',
    configureColumns: 'Configurer Colonnes',
    configureColumnsDesc: 'Glissez pour ordonner ou utilisez les flèches. Cliquez sur l\'œil pour masquer.',
    visible: 'visible',
    reset: 'Réinitialiser',
    cancel: 'Annuler',
    applyChanges: 'Appliquer',
    moveUp: 'Monter',
    moveDown: 'Descendre',
    hideColumn: 'Masquer',
    showColumn: 'Afficher',
    // Tag linking
    linkToSpool: 'Lier à une Bobine',
    tagLinked: 'Tag lié à la bobine',
    tagLinkFailed: 'Échec lien tag',
    tagAlreadyLinked: 'Tag déjà lié à une autre bobine',
    unknownTag: 'Tag RFID inconnu détecté',
    // Usage history
    usageHistory: 'Historique de Consommation',
    noUsageHistory: 'Aucune consommation enregistrée',
    printName: 'Nom Impression',
    weightConsumed: 'Poids consommé',
    clearHistory: 'Effacer',
    historyCleared: 'Historique effacé',
    fillSourceLabel: '(Inv)',
    lowStockThresholdError: 'Le seuil doit être compris entre 0.1 et 99.9',
    assignMismatchTitle: 'Incompatibilité de matériau',
    assignMismatchMessage: 'Le matériau de la bobine sélectionnée "{{spoolMaterial}}" ne correspond pas au matériau du plateau "{{trayMaterial}}" pour {{location}}. Assigner quand même ?',
    assignMismatchConfirm: 'Assigner quand même',
    assignPartialMismatchMessage: 'Le matériau de la bobine "{{spoolMaterial}}" est similaire, mais ne correspond pas exactement à "{{trayMaterial}}" dans {{location}}. Voulez-vous continuer ?',
    assignProfileMismatchMessage: 'Le profil de la bobine "{{spoolProfile}}" ne correspond pas au profil du plateau "{{trayProfile}}" dans {{location}}. Voulez-vous continuer ?',
    // Spoolman filament catalog picker
    spoolmanFilamentCatalog: 'Catalogue de filaments Spoolman',
    pickFromSpoolmanCatalog: 'Choisir dans le catalogue Spoolman…',
    spoolmanFilamentSelected: 'Filament sélectionné depuis le catalogue Spoolman',
    spoolmanFilamentUnlinked: 'Lien avec le catalogue de filaments supprimé',
    noSpoolmanFilaments: 'Aucun filament trouvé dans le catalogue Spoolman',
    spoolmanFilamentColorSwatch: 'Couleur du filament',
    spoolWeightManagedBySpoolman: 'Le poids de la bobine vide est géré par type de filament dans Spoolman',
    spoolmanCatalogLoadFailed: 'Impossible de charger le catalogue Spoolman',
  },

  // Timelapse
  timelapse: {
    title: 'Time-lapse',
    create: 'Créer Timelapse',
    download: 'Télécharger',
    delete: 'Supprimer',
    preview: 'Aperçu',
    frameRate: 'Images/sec',
    quality: 'Qualité',
    processing: 'Traitement...',
    noTimelapses: 'Aucun timelapse',
  },

  // AMS
  ams: {
    title: 'AMS',
    slot: 'Emplacement',
    empty: 'Vide',
    emptySlot: 'Slot vide',
    unknown: 'Inconnu',
    humidity: 'Humidité',
    temperature: 'Température',
    filamentType: 'Type filament',
    filamentColor: 'Couleur',
    remaining: 'Restant',
    history: 'Historique AMS',
    noHistory: 'Aucun historique',
    configureSlot: 'Configurer Slot',
    externalSpool: 'Bobine externe',
    profile: 'Profil',
    kFactor: 'Facteur K',
    fill: 'Remplir',
    configure: 'Configurer',
    used: 'utilisé',
    remainingUnit: 'restant',
  },

  // Print modal
  printModal: {
    title: 'Lancer l\'impression',
    selectPrinter: 'Choisir l\'imprimante',
    selectPlate: 'Choisir le plateau',
    filamentMapping: 'Mapping Filament',
    totalCost: 'Coût total :',
    slotRemainingShort: ' - {{grams}}g rest.',
    printSettings: 'Réglages d\'impression',
    bedLeveling: 'Nivellement plateau',
    flowCalibration: 'Calibration débit',
    vibrationCalibration: 'Vibration (Input Shaper)',
    layerInspection: 'Inspection 1ère couche',
    timelapse: 'Time-lapse',
    startPrint: 'Démarrer',
    addToQueue: 'Ajouter à la file',
    cancel: 'Annuler',
    noPrintersAvailable: 'Aucune imprimante disponible',
    printerBusy: 'L\'imprimante est occupée',
    printerOffline: 'L\'imprimante est hors ligne',
    sameTypeDifferentColor: 'Même type, couleur différente',
    filamentTypeNotLoaded: 'Type de filament non chargé',
    openCalendar: 'Ouvrir calendrier',
    leftNozzle: 'G',
    rightNozzle: 'D',
    leftNozzleTooltip: 'Buse gauche',
    rightNozzleTooltip: 'Buse droite',
    filamentOverride: 'Remplacement de filament',
    filamentOverrideHint: 'Remplacez optionnellement les filaments pour l\'affectation par modèle. Le planificateur utilisera vos filaments sélectionnés au lieu des valeurs 3MF d\'origine.',
    originalFilament: 'Original',
    overrideWith: 'Remplacer par',
    resetToOriginal: 'Revenir à l\'original',
    insufficientFilamentTitle: 'Filament insuffisant',
    insufficientFilamentMessage: 'Certaines bobines assignées ont moins de filament restant que nécessaire pour cette impression :',
    insufficientFilamentLine: '{{printer}} - {{slot}} : nécessite {{required}}g, restant {{remaining}}g',
    printAnyway: 'Imprimer quand même',
    forceColorMatch: 'Forcer correspondance des couleurs',
    staggerPrinterStarts: 'Échelonner démarrages',
    staggerGroupSize: 'Taille du groupe',
    staggerInterval: 'Intervalle (min)',
    staggerPreview: '{{printers}} imprimantes → {{groups}} groupes de {{size}}, démarrage toutes les {{interval}} min',
    staggerLastGroup: 'dernier groupe : {{count}}',
    staggerTotal: 'total : {{minutes}} min',
    staggerToPrinters: 'Échelonner sur {{count}} imprimantes',
    gcodeInjection: 'Injecter le G-code auto-impression',
  },

  // Backup
  backup: {
    includesEncryptionKey: 'Les sauvegardes locales incluent le fichier de clé de chiffrement MFA (DATA_DIR/.mfa_encryption_key), donc un ZIP de sauvegarde est autonome. Traitez le ZIP comme sensible — quiconque possède le fichier peut déchiffrer les secrets client OIDC et TOTP qu\'il contient.',
    title: 'Sauvegarde & Restauration',
    createBackup: 'Créer Sauvegarde',
    restoreBackup: 'Restaurer Sauvegarde',
    restoreDescription: 'Remplace les données par un fichier de sauvegarde',
    downloadBackup: 'Télécharger Sauvegarde',
    uploadBackup: 'Téléverser Sauvegarde',
    lastBackup: 'Dernière sauvegarde',
    autoBackup: 'Sauvegarde auto',
    backupNow: 'Sauvegarder maintenant',
    restoreWarning: 'Attention : Écrase TOUTES les données actuelles.',
    includeArchives: 'Inclure Archives',
    includeSettings: 'Inclure Paramètres',
    includeProfiles: 'Inclure Profils',
    backupSuccess: 'Sauvegarde réussie',
    restoreSuccess: 'Restauration réussie',
    backupFailed: 'Échec sauvegarde',
    restoreFailed: 'Échec restauration',
    restoreNote: 'L\'imprimante virtuelle sera arrêtée pendant la restauration',

    // GitHub Backup
    githubBackup: 'Sauvegarde GitHub',
    enabled: 'Activé',
    cloudLoginRequired: 'Connexion Bambu Cloud requise. Connectez-vous sous Profils → Profils Cloud pour activer la sauvegarde GitHub.',
    cloudLoginRequiredShort: 'Connexion Cloud requise',
    githubDescription: 'Synchronisez automatiquement vos profils vers un dépôt GitHub privé pour la sauvegarde et l\'historique des versions.',
    repoIsPrivate: 'Le dépôt est privé — la sauvegarde peut s\'y faire en toute sécurité.',
    repoIsPublicWarning: 'Le dépôt est PUBLIC. Les sauvegardes Bambuddy contiennent les identifiants MQTT, les jetons Home Assistant, les jetons Prometheus, votre adresse Bambu Cloud, et les codes d\'accès des imprimantes via les K-profils. L\'enregistrement est bloqué tant que le dépôt n\'est pas passé en privé dans les paramètres de votre hébergeur.',
    repoVisibilityUnknown: 'Impossible de déterminer la visibilité du dépôt. Bambuddy refuse toute sauvegarde vers un dépôt non confirmé privé ; l\'enregistrement sera bloqué.',
    repositoryUrl: 'URL du dépôt',
    repoUrlPlaceholderGitHub: 'https://github.com/username/repo-name',
	repoUrlPlaceholderGitea: 'https://gitea.example.com/username/repo-name',
    repoUrlPlaceholderForgejo: 'https://forgejo.example.com/username/repo-name',
    repoUrlPlaceholderGitLab: 'https://gitlab.com/username/repo-name',
    allowInsecureHttp: 'Autoriser HTTP non sécurisé',
    allowInsecureHttpHint: 'Activer pour les instances auto-hébergées sur des réseaux privés sans TLS',
    personalAccessToken: 'Jeton d\'accès personnel',
    tokenSaved: '(enregistré)',
    enterNewToken: 'Entrez un nouveau jeton pour mettre à jour',
    tokenHint: 'Jeton à granularité fine avec permission de lecture/écriture du contenu',
    branch: 'Branche',
    provider: 'Fournisseur Git',
    providerGitHub: 'GitHub',
	providerGitLab: 'GitLab',
	providerGitea: 'Gitea',
    providerForgejo: 'Forgejo',
    manualOnly: 'Manuel uniquement',
    hourly: 'Toutes les heures',
    daily: 'Quotidien',
    weekly: 'Hebdomadaire',
    includeInBackup: 'Inclure dans la sauvegarde',
    kProfiles: 'K-Profils',
    kProfilesDescription: 'Calibration de l\'avance de pression des imprimantes connectées',
    noPrintersConnected: 'Aucune imprimante connectée',
    printersConnected: '{{connected}}/{{total}} connectées',
    cloudProfiles: 'Profils Cloud',
    cloudProfilesDescription: 'Préréglages de filament, imprimante et processus depuis Bambu Cloud',
    appSettings: 'Paramètres de l\'application',
    appSettingsDescription: 'Configuration Bambuddy (base de données complète)',
    spoolInventory: 'Inventaire des bobines',
    spoolInventoryDescription: 'Bobines de filament, historique d\'utilisation et suivi des coûts',
    printArchives: 'Archives d\'impression',
    printArchivesDescription: 'Métadonnées de l\'historique d\'impression (pas de fichiers gcode/3MF)',
    lastBackupAt: 'Dernière sauvegarde :',
    noBackupsYet: 'Aucune sauvegarde pour l\'instant',
    next: 'Prochaine :',
    startingBackup: 'Démarrage de la sauvegarde...',
    test: 'Tester',
    enableBackup: 'Activer la sauvegarde',
    testConnection: 'Tester la connexion',
    enterRepoUrl: 'Entrez l\'URL du dépôt',
    enterRepoAndToken: 'Entrez l\'URL du dépôt et le jeton d\'accès',
    repoRequired: 'L\'URL du dépôt est requise',
    tokenRequired: 'Le jeton d\'accès est requis',
    githubBackupEnabled: 'Sauvegarde GitHub activée',
    tokenUpdated: 'Jeton mis à jour',
    settingsSaved: 'Paramètres enregistrés',
    failedToSave: 'Échec de l\'enregistrement : {{message}}',
    backupCompleteFiles: 'Sauvegarde terminée - {{count}} fichiers mis à jour',
    backupSkippedNoChanges: 'Sauvegarde ignorée - aucun changement',
    backupFailed2: 'Échec de la sauvegarde : {{message}}',
    clearedLogs: '{{count}} journaux supprimés',
    failedToClearLogs: 'Échec de la suppression des journaux : {{message}}',

    // History
    history: 'Historique',
    clear: 'Effacer',
    date: 'Date',
    status: 'Statut',
    commit: 'Commit',

    // Local Backup
    localBackup: 'Sauvegarde locale',
    localBackupDescription: 'Créez une sauvegarde complète de vos données Bambuddy incluant la base de données, les archives, les téléchargements et tous les fichiers.',
    downloadBackupLabel: 'Télécharger la sauvegarde',
    completeBackupZip: 'Sauvegarde complète : base de données + tous les fichiers (ZIP)',
    download: 'Télécharger',
    preparingBackup: 'Préparation de la sauvegarde...',
    creatingArchive: 'Création de l\'archive de sauvegarde... Cela peut prendre un moment pour les archives volumineuses.',
    downloadingFile: 'Téléchargement du fichier de sauvegarde...',
    backupDownloaded: 'Sauvegarde téléchargée avec succès',
    failedToCreateBackup: 'Échec de la création de la sauvegarde : {{message}}',
    restore: 'Restaurer',
    restoreReplacesAll: 'La restauration remplace toutes les données.',
    restoreReplacesAllDetail: 'Votre base de données et vos fichiers actuels seront complètement remplacés. Un redémarrage est nécessaire après la restauration.',
    restoreConfirmTitle: 'Restaurer la sauvegarde',
    restoreConfirmMessage: 'Êtes-vous sûr de vouloir restaurer depuis "{{filename}}" ? Cela remplacera complètement votre base de données et tous vos fichiers. L\'application devra être redémarrée après la restauration.',
    restoreConfirmButton: 'Restaurer la sauvegarde',
    uploadingFile: 'Téléchargement du fichier de sauvegarde...',
    backupRestoredRestart: 'Sauvegarde restaurée. Veuillez redémarrer Bambuddy.',
    failedToRestore: 'Échec de la restauration. Veuillez vérifier le format du fichier.',
    reloadNow: 'Recharger maintenant',
    creatingBackup: 'Création de la sauvegarde',
    restoringBackup: 'Restauration de la sauvegarde',
    preparing: 'Préparation...',
    processing: 'Traitement...',
    doNotClosePage: 'Veuillez ne pas fermer cette page ni naviguer ailleurs. Cette opération peut prendre plusieurs minutes pour les sauvegardes volumineuses.',

    // RestoreModal
    restoring: 'Restauration...',
    restoreComplete: 'Restauration terminée',
    restoreFailed2: 'Échec de la restauration',
    importSettings: 'Importer les paramètres depuis un fichier de sauvegarde',
    pleaseWaitRestoring: 'Veuillez patienter pendant la restauration de vos données',
    selectBackupFile: 'Cliquez pour sélectionner un fichier de sauvegarde (.json ou .zip)',
    duplicateHandling: 'Comment fonctionne la gestion des doublons :',
    matchPrinters: 'Imprimantes',
    matchPrintersBy: 'correspondance par numéro de série',
    matchSmartPlugs: 'Prises connectées',
    matchSmartPlugsBy: 'correspondance par adresse IP',
    matchNotificationProviders: 'Fournisseurs de notifications',
    matchNotificationProvidersBy: 'correspondance par nom',
    matchFilaments: 'Filaments',
    matchFilamentsBy: 'correspondance par nom + type + marque',
    matchArchives: 'Archives',
    matchArchivesBy: 'correspondance par hash de contenu (toujours ignoré)',
    matchPendingUploads: 'Téléchargements en attente',
    matchPendingUploadsBy: 'correspondance par nom de fichier',
    matchSettingsTemplates: 'Paramètres et modèles',
    matchSettingsTemplatesBy: 'toujours écrasés',
    replaceExisting: 'Remplacer les données existantes',
    keepExisting: 'Conserver les données existantes',
    overwriteDescription: 'Écraser les éléments qui existent déjà avec les données de sauvegarde',
    keepDescription: 'Restaurer uniquement les éléments qui n\'existent pas encore',
    overwriteCaution: 'Attention :',
    overwriteWarning: 'L\'écrasement remplacera vos configurations actuelles par les données de la sauvegarde. Les codes d\'accès des imprimantes ne sont jamais écrasés pour des raisons de sécurité.',
    cancel: 'Annuler',
    processingBackup: 'Traitement du fichier de sauvegarde...',
    itemsRestored: 'Éléments restaurés',
    itemsSkipped: 'Éléments ignorés',
    restored: 'Restaurés',
    skippedAlreadyExist: 'Ignorés (existent déjà)',
    filesCategory: 'Fichiers (3MF, miniatures, etc.)',
    andMore: '...et {{count}} de plus',
    newApiKeysGenerated: 'Nouvelles clés API générées',
    keysShownOnce: 'Ces clés ne sont affichées qu\'une seule fois. Copiez-les maintenant !',
    copy: 'Copier',
    noDataFound: 'Aucune donnée à restaurer n\'a été trouvée dans le fichier de sauvegarde.',
    close: 'Fermer',

    // Scheduled local backups (#884)
    scheduledBackup: 'Sauvegardes planifiées',
    scheduledBackupDescription: 'Crée automatiquement des instantanés de sauvegarde selon un planning. Le répertoire de sortie peut être monté sur un NAS ou un stockage externe.',
    frequency: 'Fréquence',
    backupTime: 'Temps',
    retention: 'Rétention',
    retentionDescription: 'Nombre de sauvegardes à conserver',
    outputPath: 'Chemin de sortie',
    outputPathPlaceholder: 'Par défaut : {{path}}',
    outputPathDescription: 'Laisser vide pour l\'emplacement par défaut',
    runNow: 'Exécuter maintenant',
    backupFiles: 'Fichiers de sauvegarde',
    noScheduledBackups: 'Aucune sauvegarde',
    deleteBackup: 'Supprimer',
    deleteBackupConfirm: 'Supprimer ce fichier de sauvegarde ?',
    backupRunning: 'Sauvegarde en cours...',
    scheduledBackupComplete: 'Sauvegarde terminée avec succès',
    scheduledBackupFailed: 'Échec de la sauvegarde',
    nextBackup: 'Prochaine sauvegarde',
    backupSize: 'Taille',
    utc: 'UTC',
    defaultPathLabel: 'Par défaut :',

    // Category labels
    categories: {
      settings: 'Paramètres',
      notification_providers: 'Fournisseurs de notifications',
      notification_templates: 'Modèles de notifications',
      smart_plugs: 'Prises connectées',
      printers: 'Imprimantes',
      filaments: 'Filaments',
      maintenance_types: 'Types de maintenance',
      archives: 'Archives',
      projects: 'Projets',
      pending_uploads: 'Téléchargements en attente',
      external_links: 'Liens externes',
      api_keys: 'Clés API',
    },
  },

  // Tags
  tags: {
    title: 'Étiquettes',
    addTag: 'Ajouter Tag',
    editTag: 'Modifier Tag',
    deleteTag: 'Supprimer Tag',
    tagName: 'Nom du Tag',
    tagColor: 'Couleur du Tag',
    noTags: 'Aucun tag',
    deleteConfirm: 'Supprimer ce tag ?',
    manageTags: 'Gérer les Tags',
  },

  // Upload modal (archives)
  uploadModal: {
    title: 'Téléverser des fichiers 3MF',
    dragDrop: 'Glissez les fichiers .3mf ici',
    or: 'ou',
    browseFiles: 'Parcourir',
    extractionInfo: 'Le modèle d\'imprimante est extrait des métadonnées 3MF.',
    uploaded: 'téléversé',
    failed: 'échoué',
    uploading: 'En cours...',
    upload: 'Téléverser',
    uploadFailed: 'Échec du téléversement',
  },

  // Edit archive modal
  // Edit Archive Modal
  editArchive: {
    title: 'Modifier l\'archive',
    name: 'Nom',
    namePlaceholder: 'Nom de l\'impression',
    printer: 'Imprimante',
    noPrinter: 'Aucune imprimante',
    project: 'Projet',
    noProject: 'Aucun projet',
    itemsPrinted: 'Nombre de pièces',
    itemsPrintedHelp: 'Nombre d\'objets produits',
    notes: 'Notes',
    notesPlaceholder: 'Notes sur l\'impression...',
    externalLink: 'Lien externe',
    externalLinkPlaceholder: 'https://...',
    externalLinkHelp: 'Lien vers Printables, Thingiverse, etc.',
    tags: 'Étiquettes',
    tagsPlaceholder: 'Ajouter des tags...',
    addMoreTags: 'Plus de tags...',
    matchingTags: 'Correspondant à "{{query}}"',
    existingTags: 'Tags existants',
    clickToAdd: '(cliquer pour ajouter)',
    status: 'Statut',
    failureReason: 'Raison de l\'échec',
    selectReason: 'Choisir raison...',
    photos: 'Photos du résultat',
    photosHelp: 'Cliquez sur + pour ajouter des photos',
    printResult: 'Résultat d\'impression',
    saving: 'Enregistrement...',
    // Failure reasons
    failureReasons: {
      adhesionFailure: 'Défaut d\'adhésion',
      spaghettiDetached: 'Spaghetti / Détaché',
      layerShift: 'Décalage de couche',
      cloggedNozzle: 'Buse bouchée',
      filamentRunout: 'Filament fini',
      warping: 'Warping (Déformation)',
      stringing: 'Stringing (Cheveux d\'ange)',
      underExtrusion: 'Sous-extrusion',
      powerFailure: 'Coupure courant',
      userCancelled: 'Annulé par l\'utilisateur',
      other: 'Autre',
    },
    // Archive statuses
    statuses: {
      completed: 'Réussie',
      failed: 'Échouée',
      aborted: 'Annulée',
      printing: 'Impression',
    },
  },

  // K-Profiles
  kProfiles: {
    title: 'Profils K',
    noPrintersConfigured: 'Aucune imprimante configurée',
    addPrinterInSettings: 'Ajoutez une imprimante pour gérer les K-profiles',
    noActivePrinters: 'Aucune imprimante active',
    enablePrinterConnection: 'Activez la connexion pour voir les K-profiles',
    loadingProfiles: 'Chargement des K-Profiles...',
    printerOffline: 'Imprimante hors ligne',
    printerOfflineDesc: 'L\'imprimante doit être allumée.',
    noMatchingProfiles: 'Aucun profil correspondant',
    noMatchingProfilesDesc: 'Ajustez votre recherche',
    noKProfiles: 'Aucun K-Profile',
    noKProfilesDesc: 'Aucun profil trouvé pour une buse de {{diameter}}mm',
    createFirstProfile: 'Créer le premier profil',
    // Controls
    printer: 'Imprimante',
    nozzle: 'Buse',
    refresh: 'Rafraîchir',
    addProfile: 'Ajouter Profil',
    export: 'Exporter',
    import: 'Importer',
    select: 'Choisir',
    selectAll: 'Tout sélectionner',
    delete: 'Supprimer',
    // Filters
    searchPlaceholder: 'Nom ou filament...',
    allExtruders: 'Tous les extrudeurs',
    leftOnly: 'Gauche uniquement',
    rightOnly: 'Droite uniquement',
    allFlow: 'Tout débit',
    hfOnly: 'HF uniquement',
    sOnly: 'S uniquement',
    sortName: 'Tri : Nom',
    sortKValue: 'Tri : Valeur K',
    sortFilament: 'Tri : Filament',
    // Dual extruder labels
    leftExtruder: 'Extrudeur gauche',
    rightExtruder: 'Extrudeur droit',
    // Modal
    modal: {
      addTitle: 'Ajouter K-Profile',
      editTitle: 'Modifier K-Profile',
      profileName: 'Nom du profil',
      profileNamePlaceholder: 'ex: Mon profil PLA',
      kValue: 'Valeur K',
      kValuePlaceholder: '0.020',
      kValueHelp: 'Plage type : 0.01-0.06 (PLA), 0.02-0.10 (PETG)',
      filament: 'Filament',
      selectFilament: 'Choisir filament...',
      noFilamentsHelp: 'Créez d\'abord un profil dans Bambu Studio.',
      flowType: 'Type de débit',
      highFlow: 'Haut Débit (HF)',
      standard: 'Standard',
      nozzleSize: 'Taille buse',
      extruder: 'Extrudeur',
      extruders: 'Extrudeurs',
      left: 'Gauche',
      right: 'Droite',
      notes: 'Notes (locales)',
      notesPlaceholder: 'Notes sur ce profil...',
      notesHelp: 'Enregistré dans Bambuddy, pas sur l\'imprimante',
      syncing: 'Sync avec l\'imprimante...',
      savingExtruder: 'Sauvegarde extrudeur {{current}}/{{total}}...',
      pleaseWait: 'Patientez...',
    },
    // Delete confirmation
    deleteConfirm: {
      title: 'Supprimer profil',
      cannotUndo: 'Action irréversible',
      message: 'Supprimer "{{name}}" de l\'imprimante ?',
    },
    // Bulk delete
    bulkDelete: {
      title: 'Supprimer les profils',
      cannotUndo: 'Action irréversible',
      message: 'Supprimer les {{count}} profils de l\'imprimante ?',
    },
    // Toast
    toast: {
      profileSaved: 'Profil K enregistré',
      profilesSaved: 'Profil K enregistré sur {{count}} extrudeur(s)',
      selectAtLeastOneExtruder: 'Sélectionnez un extrudeur',
      profileDeleted: 'Profil K supprimé',
      profilesDeleted: '{{count}} profils supprimés',
      exportedProfiles: '{{count}} profils exportés',
      importedProfiles: '{{count}} sur {{total}} profils importés',
      noProfilesToExport: 'Rien à exporter',
      invalidFileFormat: 'Format invalide',
      failedToParseImport: 'Échec analyse fichier',
      failedToSaveBatch: 'Échec enregistrement groupé',
      noteSaved: 'Note enregistrée',
      failedToSaveNote: 'Échec note',
    },
    // Permissions
    permission: {
      noRead: 'Pas d\'autorisation lecture',
      noCreate: 'Pas d\'autorisation création',
      noUpdate: 'Pas d\'autorisation mise à jour',
      noDelete: 'Pas d\'autorisation suppression',
      noExport: 'Pas d\'autorisation export',
      noImport: 'Pas d\'autorisation import',
    },
  },

  // Virtual Printer
  virtualPrinter: {
    title: 'Imprimante Virtuelle',
    running: 'En cours',
    stopped: 'Arrêtée',
    description: {
      default: 'Active une imprimante qui apparaît dans Bambu Studio. Les fichiers envoyés sont archivés sans impression.',
      proxy: 'Active un proxy qui relaie le trafic vers une imprimante réelle, permettant l\'impression à distance.',
    },
    enable: {
      title: 'Activer l\'imprimante virtuelle',
      visibleInSlicer: 'Visible comme "Bambuddy" dans le Slicer',
      proxyingTo: 'Proxy vers {{name}}',
      notActive: 'Inactive',
    },
    model: {
      title: 'Modèle d\'imprimante',
      description: 'Choisissez le modèle à émuler.',
      restartWarning: 'Changer le modèle redémarrera le service',
    },
    accessCode: {
      title: 'Code d\'accès',
      isSet: 'Code défini',
      notSet: 'Code requis pour activer',
      placeholder: 'Code 8 char',
      placeholderChange: 'Entrez nouveau code',
      hint: 'Exactement 8 caractères. Sert à l\'auth du Slicer.',
      charCount: '({{count}}/8)',
    },
    targetPrinter: {
      title: 'Imprimante cible',
      configured: 'Cible configurée',
      notConfigured: 'Imprimante requise pour mode Proxy',
      placeholder: 'Choisir imprimante...',
      hint: 'L\'imprimante doit être en mode LAN.',
      noPrinters: 'Ajoutez une imprimante réelle d\'abord.',
    },
    remoteInterface: {
      title: 'Exception Interface Réseau',
      configured: 'Override actif',
      optional: 'Optionnel - si IP auto est fausse (VPN, Docker, multi-NIC).',
      placeholder: 'Auto (défaut)...',
      hint: 'Force l\'IP annoncée via SSDP.',
    },
    mode: {
      title: 'Mode',
      archive: 'Archiver',
      archiveDesc: 'Archive immédiatement',
      review: 'Revue',
      reviewDesc: 'Attendre revue avant archive',
      queue: 'File',
      queueDesc: 'Archiver et ajouter à la file',
      proxy: 'Proxy',
      proxyDesc: 'Relais vers imprimante réelle',
    },
    autoDispatch: {
      title: 'Lancement automatique',
      description: 'Lancer automatiquement les impressions ajoutées à la file. Désactivé, les impressions attendent un lancement manuel.',
    },
    queueForceColorMatch: {
      title: 'Forcer la correspondance des couleurs',
      description: 'Refuser l\'envoi vers une imprimante qui n\'a pas exactement le type de filament et la couleur chargés. Désactivé par défaut — sans cela, la file d\'attente utilise uniquement la correspondance par modèle et peut choisir une imprimante avec la mauvaise couleur.',
    },
    tailscaleDisabled: {
      title: 'Intégration Tailscale',
      description: 'Activez pour marquer ce VP comme exposé via Tailscale. Affiche l\'adresse Tailscale de l\'hôte pour que vous sachiez quelle IP coller dans le slicer. L\'étape d\'import CA est inchangée — cette bascule n\'a aucun effet sur les certificats.',
    },
    setupRequired: {
      title: 'Configuration requise',
      description: 'Nécessite des réglages système (ports, pare-feu).',
      readGuide: 'Lire le guide de configuration',
    },
    howItWorks: {
      title: 'Fonctionnement',
      step1: 'Sur le même LAN, les imprimantes virtuelles apparaissent automatiquement dans votre slicer (Bambu Studio / OrcaSlicer). Depuis d\'autres réseaux, ajoutez-les manuellement par adresse IP et code d\'accès.',
      step2: 'En mode Archive, Revue et File d\'attente, utilisez le bouton "Envoyer" dans votre slicer pour envoyer des fichiers 3MF à Bambuddy. Le slicer affichera "Impression réussie" — le fichier est stocké, pas imprimé.',
      step3: 'En mode Proxy, l\'imprimante virtuelle relaie tout le trafic vers une vraie imprimante — les impressions démarrent immédiatement comme en connexion directe.',
    },
    status: {
      title: 'Détails du statut',
      printerName: 'Nom',
      model: 'Modèle',
      serialNumber: 'Série',
      mode: 'Mode',
      pendingFiles: 'Fichiers en attente',
      targetPrinter: 'Cible',
      ftpPort: 'Port FTP',
      mqttPort: 'Port MQTT',
      ftpConnections: 'Connexions FTP',
      mqttConnections: 'Connexions MQTT',
    },
    toast: {
      updated: 'Réglages virtuels mis à jour',
      failedToUpdate: 'Échec mise à jour',
      copyFailed: 'Échec de la copie — veuillez sélectionner le texte manuellement',
      accessCodeRequired: 'Code d\'accès requis',
      targetPrinterRequired: 'Imprimante cible requise',
      bindIpRequired: 'Veuillez d\'abord définir une adresse IP',
      accessCodeEmpty: 'Le code ne peut pas être vide',
      accessCodeLength: 'Le code doit faire 8 caractères',
      created: 'Imprimante virtuelle créée',
      failedToCreate: 'Échec de la création de l\'imprimante virtuelle',
      deleted: 'Imprimante virtuelle supprimée',
      failedToDelete: 'Échec de la suppression de l\'imprimante virtuelle',
    },
    list: {
      title: 'Imprimantes virtuelles',
      add: 'Ajouter',
      addFirst: 'Ajouter une imprimante virtuelle',
      empty: 'Aucune imprimante virtuelle configurée. Ajoutez-en une pour commencer.',
    },
    bindIp: {
      title: 'Interface réseau',
      placeholder: 'Sélectionner interface...',
      hint: 'Interface réseau sur laquelle cette imprimante virtuelle écoute. Doit être unique par imprimante.',
    },
    proxy: {
      accessCodeHint: 'En mode proxy, utilisez le code d\'accès de l\'imprimante cible dans le slicer. La connexion est transmise de manière transparente à l\'imprimante réelle.',
    },
    addDialog: {
      title: 'Ajouter une imprimante virtuelle',
      name: 'Nom',
      hint: 'Vous pourrez configurer le code d\'accès, l\'imprimante cible et d\'autres paramètres après la création.',
      create: 'Créer',
    },
    deleteConfirm: {
      title: 'Supprimer l\'imprimante virtuelle',
      message: 'Êtes-vous sûr de vouloir supprimer "{{name}}" ? Cela arrêtera tous les services de cette imprimante.',
    },
    archiveNameSource: {
      title: 'Source du nom d\'archive',
      description: 'Choisissez comment les nouvelles archives sont nommées lorsque les fichiers arrivent via l\'imprimante virtuelle. "Métadonnées" utilise le titre intégré par le slicer dans le 3MF (par défaut). "Nom de fichier" utilise le nom envoyé par Bambu Studio en FTP — pratique si vous avez renommé le job dans la boîte "envoyer à l\'imprimante".',
      metadata: 'Métadonnées',
      filename: 'Nom de fichier',
    },
  },

  // Model Viewer
  modelViewer: {
    openInSlicer: 'Ouvrir dans le Slicer',
    tabs: {
      model: 'Modèle 3D',
      gcode: 'Aperçu G-code',
    },
    notAvailable: 'indisponible',
    notSliced: 'pas découpé',
    plates: 'Plateaux',
    allPlates: 'Tous les plateaux',
    plateNumber: 'Plateau {{number}}',
    plateCount: '{{count}} plateau',
    plateCount_other: '{{count}} plateaux',
    objectCount: '{{count}} objet',
    objectCount_other: '{{count}} objets',
    filamentCount: '{{count}} filament',
    filamentCount_other: '{{count}} filaments',
    eta: 'Fin {{minutes}} min',
    noPreview: 'Aucun aperçu pour ce fichier',
    pagination: {
      pageOf: 'Page {{current}} sur {{total}}',
      prev: 'Préc',
      next: 'Suiv',
    },
    errors: {
      failedToLoad: 'Échec chargement fichier',
      noMeshes: 'Aucun maillage trouvé dans le 3MF',
      unsupportedFormat: 'Format non supporté',
    },
  },

  // Maintenance type descriptions (built-in)
  maintenanceDescriptions: {
    lubricateCarbonRods: 'Appliquer du lubrifiant sur les tiges carbone pour un mouvement fluide',
    lubricateRails: 'Appliquer du lubrifiant sur les rails linéaires',
    cleanNozzle: 'Nettoyer buse et hotend anti-bouchage',
    checkBelts: 'Tension des courroies pour la précision',
    cleanBuildPlate: 'Nettoyage plateau pour l\'adhésion',
    checkExtruder: 'Usure des engrenages de l\'extrudeur',
    checkCooling: 'Bon fonctionnement des ventilateurs',
    generalInspection: 'Inspection générale de la machine',
    cleanCarbonRods: 'Nettoyer les tiges carbone (friction)',
    lubricateSteelRods: 'Appliquer du lubrifiant sur les tiges en acier pour un mouvement fluide',
    cleanSteelRods: 'Nettoyer les tiges en acier (friction)',
    cleanLinearRails: 'Essuyer les rails linéaires (poussière/débris)',
    checkPtfeTube: 'Usure ou dommage du tube PTFE',
    replaceHepaFilter: 'Filtre HEPA pour la qualité de l\'air',
    replaceCarbonFilter: 'Filtre charbon actif (odeurs)',
    lubricateLeftNozzleRail: 'Lubrifier le rail de buse gauche (Série H2)',
  },

  // Smart Plugs
  smartPlugs: {
    offline: 'Hors ligne',
    admin: 'Administrateur',
    openPlugAdminPage: 'Page admin de la prise',
    deleteSmartPlug: 'Supprimer la prise',
    turnOnSmartPlug: 'Allumer la prise',
    turnOffSmartPlug: 'Éteindre la prise',
    turnOn: 'Allumer',
    turnOff: 'Éteindre',
    addSmartPlug: {
      scanningNetwork: 'Scan réseau...',
      chooseEntity: 'Choisir une entité...',
      connectionFailed: 'Échec connexion',
      searchEntities: 'Chercher entités...',
      searchPowerSensors: 'Capteurs puissance...',
      searchEnergySensors: 'Capteurs énergie...',
      placeholders: {
        plugName: 'Prise Salon',
        mqttStateOnValue: 'ON, true, 1',
        mqttSameAsPower: 'Identique au topic puissance, ou différent',
      },
    },
    // SmartPlugCard
    linkedTo: 'Lié à :',
    monitorOnly: 'Surveillance uniquement',
    alerts: 'Alertes',
    scheduleOn: 'À {{time}}',
    scheduleOff: 'Désactivé {{time}}',
    on: 'On',
    off: 'Désactivé',
    power: 'Puissance',
    kwhToday: 'kWh Aujourd\'hui',
    settings: 'Paramètres',
    automationSettings: 'Paramètres d\'automatisation',
    showInSwitchbar: 'Afficher dans la barre de commutateurs',
    quickAccessSidebar: 'Accès rapide depuis la barre latérale',
    enabled: 'Activé',
    enableAutomation: 'Activer l\'automatisation pour cette prise',
    autoOn: 'Auto Activé',
    autoOnDescription: 'Allumer au démarrage de l\'impression',
    autoOff: 'Auto Désactivé',
    autoOffDescription: 'Éteindre à la fin de l\'impression (unique)',
    autoOffPersistent: 'Garder activé',
    autoOffPersistentDescription: 'Rester activé entre les impressions au lieu d\'une seule fois',
    autoOffAfterDrying: 'Arrêt auto après séchage',
    autoOffAfterDryingDescription: 'Éteindre à la fin du séchage de l\'AMS',
    delayAfterDryingMinutes: 'Délai après séchage (minutes)',
    turnOffDelayMode: 'Mode de délai d\'extinction',
    time: 'Temps',
    temp: 'Temp.',
    delayMinutes: 'Délai (minutes)',
    tempThreshold: 'Seuil de température (°C)',
    tempThresholdDescription: 'S\'éteint lorsque la buse refroidit en dessous de cette température',
    edit: 'Modifier',
    deleteConfirm: 'Êtes-vous sûr de vouloir supprimer "{{name}}" ? Cette action est irréversible.',
    turnOnConfirm: 'Êtes-vous sûr de vouloir allumer "{{name}}" ?',
    turnOffConfirm: 'Êtes-vous sûr de vouloir éteindre "{{name}}" ? Cela coupera l\'alimentation de l\'appareil connecté.',
    failedToTurn: 'Impossible de {{action}} "{{name}}"',
    unknown: 'Inconnu',
    // AddSmartPlugModal
    addTitle: 'Ajouter une prise connectée',
    editTitle: 'Modifier la prise connectée',
    stopScanning: 'Arrêter le scan',
    discoverTasmota: 'Découvrir les appareils Tasmota',
    foundDevices: '{{count}} appareil(s) trouvé(s) - cliquez pour sélectionner :',
    noDevicesFound: 'Aucun appareil Tasmota trouvé sur votre réseau',
    haNotConfigured: 'Home Assistant n\'est pas configuré. Configurez-le dans',
    haSettingsPath: 'Paramètres → Réseau → Home Assistant',
    selectEntity: 'Sélectionner l\'entité *',
    ipAddress: 'Adresse IP *',
    nameLabel: 'Nom *',
    username: 'Nom d\'utilisateur',
    password: 'Mot de passe',
    authHint: 'Laissez vide si votre appareil Tasmota ne nécessite pas d\'authentification',
    linkToPrinter: 'Lier à l\'imprimante',
    noPrinter: 'Pas d\'imprimante (contrôle manuel uniquement)',
    linkingDescription: 'La liaison permet l\'allumage/extinction automatique au début/fin de l\'impression',
    powerAlerts: 'Alertes de puissance',
    alertAbove: 'Alerte si au-dessus (W)',
    alertBelow: 'Alerte si en dessous (W)',
    alertDescription: 'Recevoir une notification lorsque la consommation dépasse ces seuils. Laisser vide pour désactiver cette direction.',
    dailySchedule: 'Planification quotidienne',
    turnOnAt: 'Allumer à',
    turnOffAt: 'Éteindre à',
    scheduleDescription: 'Allumer/éteindre automatiquement la prise à ces heures chaque jour. Laisser vide pour ignorer cette action.',
    showOnPrinterCard: 'Afficher sur la carte imprimante',
    displayOnPrinterCard: 'Afficher le bouton sur la carte imprimante',
    connectedResult: 'Connecté !',
    deviceLabel: 'Appareil : {{name}} - ',
    stateLabel: 'État : {{state}}',
    test: 'Tester',
    delete: 'Supprimer',
    save: 'Enregistrer',
    add: 'Ajouter',
    cancel: 'Annuler',
    failedToStartScan: 'Impossible de démarrer le scan',
    nameRequired: 'Le nom est requis',
    entityRequired: 'L\'entité est requise pour les prises Home Assistant',
    mqttTopicRequired: 'Au moins un topic MQTT doit être configuré pour la puissance, l\'énergie ou la surveillance d\'état',
    loadingEntities: 'Chargement des entités...',
    loading: 'Chargement...',
    failedToLoadEntities: 'Échec du chargement des entités : {{error}}',
    noEntitiesMatching: 'Aucune entité trouvée correspondant à "{{search}}"',
    noEntitiesAvailable: 'Aucune entité disponible',
    searchingEntities: 'Recherche de toutes les entités ({{count}} trouvées)',
    showingEntities: 'Affichage switch, light, input_boolean ({{count}} disponibles)',
    energyMonitoringOptional: 'Surveillance énergétique (Optionnel)',
    energyMonitoringHint: 'Recherchez et sélectionnez les capteurs fournissant des données de puissance/énergie.',
    powerSensorW: 'Capteur de puissance (W)',
    energyTodayKwh: 'Énergie aujourd\'hui (kWh)',
    totalEnergyKwh: 'Énergie totale (kWh)',
    noMatchingSensors: 'Aucun capteur correspondant',
    none: 'Aucun',
    mqttNotConfigured: 'Broker MQTT non configuré. Définissez l\'adresse du broker dans',
    mqttSettingsPath: 'Paramètres → Réseau → Publication MQTT',
    mqttNotConfiguredSuffix: '(vous n\'avez pas besoin d\'activer la publication, remplissez simplement les détails du broker).',
    mqttMonitorOnlyDescription: 'Les prises MQTT reçoivent les données de puissance/énergie via un abonnement MQTT. Le contrôle on/off n\'est pas disponible - utilisez votre broker MQTT ou système domotique.',
    powerMonitoring: 'Surveillance de puissance',
    energyMonitoring: 'Surveillance énergétique',
    stateMonitoring: 'Surveillance d\'état',
    optional: 'optionnel',
    topic: 'Sujet',
    jsonPath: 'Chemin JSON',
    multiplier: 'Multiplicateur',
    onValue: 'Valeur ON',
    mqttPowerHint: 'Le chemin JSON extrait la valeur du payload JSON (ex: "power_l1"). Laisser vide si le topic publie des valeurs numériques brutes.\nUtiliser le multiplicateur 0.001 pour mW→W, 1000 pour kW→W.',
    mqttEnergyHint: 'Le chemin JSON extrait la valeur du payload JSON. Laisser vide pour les valeurs brutes.\nUtiliser le multiplicateur 0.001 pour Wh→kWh, 1000 pour MWh→kWh.',
    mqttStateHint: 'Le chemin JSON extrait la valeur du payload JSON. Laisser vide pour les valeurs brutes.\nValeur ON : la chaîne exacte signifiant "ON". Laisser vide pour la détection auto (ON, true, 1).',
    // REST smart plug
    restControl: 'Contrôle',
    restOnUrl: 'URL d\'activation',
    restOffUrl: 'URL de désactivation',
    restOnBody: 'Corps de requête ON',
    restOffBody: 'Corps de requête OFF',
    restMethod: 'Méthode HTTP',
    restHeaders: 'Custom Headers (JSON)',
    restStatusUrl: 'URL de statut',
    restStatusPath: 'Chemin JSON état',
    restStatusOnValue: 'Valeur ON',
    restPowerUrl: 'URL de puissance',
    restPowerPath: 'Chemin JSON puissance',
    restPowerMultiplier: 'Multiplicateur de puissance',
    restEnergyUrl: 'URL d\'énergie',
    restEnergyPath: 'Chemin JSON énergie',
    restEnergyMultiplier: 'Multiplicateur d\'énergie',
    restUrlRequired: 'Au moins une URL (ON ou OFF) est requise pour les prises REST',
    restHeadersHint: 'par ex. {"Authorization": "Bearer your-token"}',
    restBodyHint: 'par ex. ON, {"state": "on"}',
    restStatusHint: 'URL pour interroger l\'état actuel',
    restPathHint: 'par ex. state ou data.power.status',
    restPowerUrlHint: 'URL séparée pour les données de puissance (utilise l\'URL de statut si vide)',
    restEnergyUrlHint: 'URL séparée pour les données d\'énergie (utilise l\'URL de statut si vide)',
    restEnergyHint: 'Chaque valeur peut utiliser sa propre URL ou se rabattre sur l\'URL de statut. Utilisez les multiplicateurs pour la conversion d\'unités (ex : 0.001 pour convertir Wh en kWh).',
    testConnection: 'Tester la connexion',
    connectionSuccess: 'Connexion réussie',
    noSwitchesInSwitchbar: 'Aucun commutateur dans la barre',
    enableSwitchbarHint: 'Activez "Afficher dans la barre de commutateurs" dans Paramètres > Smart Plugs',
  },

  // Notifications
  notifications: {
    // Provider types
    providerTypes: {
      callmebot: 'CallMeBot/WhatsApp',
      ntfy: 'ntfy',
      pushover: 'Pushover',
      telegram: 'Telegram',
      email: 'E-mail',
      discord: 'Discord',
      webhook: 'Webhook',
      homeassistant: 'Home Assistant',
    },
    // Provider descriptions
    providerDescriptions: {
      email: 'Notifications par e-mail SMTP',
      telegram: 'Notifications via un bot Telegram',
      discord: 'Envoyer vers un canal Discord via webhook',
      ntfy: 'Notifications push gratuites, auto-hébergeables',
      pushover: 'Notifications push simples et fiables',
      callmebot: 'Notifications WhatsApp gratuites via CallMeBot',
      webhook: 'POST HTTP générique vers n\'importe quelle URL',
      homeassistant: 'Notifications persistantes dans le tableau de bord Home Assistant',
    },
    // NotificationProviderCard
    lastSuccess: 'Dernier : {{date}}',
    error: 'Erreur',
    printer: 'Imprimante :',
    allPrinters: 'Toutes les imprimantes',
    sendTestNotification: 'Envoyer une notification de test',
    eventSettings: 'Paramètres des événements',
    enabled: 'Activé',
    sendFromProvider: 'Envoyer des notifications depuis ce fournisseur',
    // Event categories
    printEvents: 'Événements d\'impression',
    printerStatus: 'État de l\'imprimante',
    amsAlarms: 'Alarmes AMS',
    amsHtAlarms: 'Alarmes AMS-HT',
    printQueue: 'File d\'attente d\'impression',
    // Event tags (badges)
    start: 'Début',
    plateCheck: 'Vérification du plateau',
    complete: 'Terminé',
    failed: 'Échoué',
    stopped: 'Arrêté',
    progress: 'Progression',
    offline: 'Hors ligne',
    lowFilament: 'Filament bas',
    maintenance: 'Maintenance',
    amsHumidity: 'Humidité AMS',
    amsTemp: 'Temp. AMS',
    amsHtHumidity: 'Humidité AMS-HT',
    amsHtTemp: 'Temp. AMS-HT',
    bedCooled: 'Plateau refroidi',
    firstLayer: 'Première couche',
    quiet: 'Silencieux',
    digest: 'Résumé {{time}}',
    // Event labels (expanded settings)
    printStarted: 'Impression démarrée',
    plateNotEmpty: 'Plateau non vide',
    plateNotEmptyDescription: 'Objets détectés avant l\'impression',
    printCompleted: 'Impression terminée',
    bedCooledLabel: 'Plateau refroidi',
    bedCooledDescription: 'Plateau refroidi sous le seuil après l\'impression',
    firstLayerCompleteLabel: 'Première couche terminée',
    firstLayerCompleteDescription: 'Notification avec photo après la première couche',
    missingSpoolAssignmentLabel: 'Affectation de bobine manquante',
    missingSpoolAssignmentDescription: 'Notifier quand une impression démarre et que des bacs requis n\'ont pas de bobine assignée',
    printFailed: 'Impression échouée',
    printStopped: 'Impression arrêtée',
    progressMilestones: 'Jalons de progression',
    progressMilestonesDescription: 'Notifier à 25 %, 50 %, 75 %',
    printerOffline: 'Imprimante hors ligne',
    printerError: 'Erreur de l\'imprimante',
    lowFilamentLabel: 'Filament bas',
    maintenanceDue: 'Maintenance requise',
    maintenanceDueDescription: 'Notifier lorsqu\'une maintenance est nécessaire',
    amsHumidityHigh: 'Humidité AMS élevée',
    amsHumidityHighDescription: 'L\'humidité de l\'AMS standard dépasse le seuil',
    amsTemperatureHigh: 'Température AMS élevée',
    amsTemperatureHighDescription: 'La température de l\'AMS standard dépasse le seuil',
    amsHtHumidityHigh: 'Humidité AMS-HT élevée',
    amsHtHumidityHighDescription: 'L\'humidité de l\'AMS-HT dépasse le seuil',
    amsHtTemperatureHigh: 'Température AMS-HT élevée',
    amsHtTemperatureHighDescription: 'La température de l\'AMS-HT dépasse le seuil',
    // Inventory stock alert events
    inventoryAlerts: 'Alertes de stock',
    stockReorderAlert: 'Alerte de réapprovisionnement',
    stockReorderAlertDescription: 'Le SKU a atteint son point de réapprovisionnement',
    stockBreakAlert: 'Alerte de rupture de stock',
    stockBreakAlertDescription: 'Le stock sera épuisé avant l\'arrivée du réapprovisionnement',
    // Queue events
    jobAdded: 'Tâche ajoutée',
    jobAddedDescription: 'Tâche ajoutée à la file d\'attente',
    jobAssigned: 'Tâche assignée',
    jobAssignedDescription: 'Tâche basée sur le modèle assignée à l\'imprimante',
    jobStarted: 'Tâche démarrée',
    jobStartedDescription: 'La tâche de la file a commencé l\'impression',
    jobWaiting: 'Tâche en attente',
    jobWaitingDescription: 'Tâche en attente de filament ou imprimante',
    jobSkipped: 'Tâche ignorée',
    jobSkippedDescription: 'Tâche ignorée (échec précédent)',
    jobFailed: 'Tâche échouée',
    jobFailedDescription: 'La tâche n\'a pas pu démarrer',
    queueComplete: 'File d\'attente terminée',
    queueCompleteDescription: 'Toutes les tâches de la file sont terminées',
    // Quiet hours
    quietHours: 'Heures silencieuses',
    noNotificationsDuring: 'Aucune notification pendant ces heures',
    editProviderToChangeQuietHours: 'Modifier le fournisseur pour changer les heures silencieuses',
    // Daily digest
    dailyDigest: 'Résumé quotidien',
    batchNotifications: 'Regrouper les notifications en un seul résumé quotidien',
    sendAt: 'Envoyer à {{time}}',
    editProviderToChangeDigestTime: 'Modifier le fournisseur pour changer l\'heure du résumé',
    // Actions
    edit: 'Modifier',
    deleteProvider: 'Supprimer le fournisseur de notifications',
    deleteConfirm: 'Êtes-vous sûr de vouloir supprimer « {{name}} » ? Cette action est irréversible.',
    delete: 'Supprimer',
    // AddNotificationModal
    addTitle: 'Ajouter un fournisseur de notifications',
    editTitle: 'Modifier le fournisseur de notifications',
    nameLabel: 'Nom *',
    namePlaceholder: 'Mes notifications',
    providerTypeLabel: 'Type de fournisseur *',
    configuration: 'Configuration',
    testConfiguration: 'Tester la configuration',
    printerFilter: 'Filtre d\'imprimante',
    onlyFromPrinter: 'Envoyer uniquement les notifications pour les événements de cette imprimante',
    quietHoursDnd: 'Heures silencieuses (Ne pas déranger)',
    quietStart: 'Début',
    quietEnd: 'Fin',
    dailyDigestLabel: 'Résumé quotidien',
    sendDigestAt: 'Envoyer le résumé à',
    digestCollected: 'Les événements seront collectés et envoyés en un seul résumé à cette heure',
    notificationEvents: 'Événements de notification',
    progressPercent: '(25 %, 50 %, 75 %)',
    bedCooledAfterPrint: '(après la fin de l\'impression)',
    // Per-event ntfy priority (#990)
    eventPriority: {
      sectionTitle: 'Priorité ntfy',
      helpNtfy: 'Choisissez une priorité pour chaque événement activé. ntfy l\'utilise pour faire remonter les alertes (son, visibilité, comportement push). Les niveaux non définis ici utilisent la valeur par défaut du serveur ntfy.',
      min: 'Min.',
      low: 'Basse',
      default: 'Par défaut',
      high: 'Haute',
      urgent: 'Urgente',
    },
    cancel: 'Annuler',
    save: 'Enregistrer',
    add: 'Ajouter',
    nameRequired: 'Le nom est requis',
    fieldRequired: '{{field}} est requis',
    // Config field labels
    phoneNumber: 'Numéro de téléphone',
    apiKey: 'Clé API',
    serverUrl: 'URL du serveur',
    topic: 'Sujet',
    authToken: 'Jeton d\'authentification',
    userKey: 'Clé utilisateur',
    appToken: 'Jeton d\'application',
    priority: 'Priorité',
    botToken: 'Jeton du bot',
    chatId: 'ID du chat',
    smtpServer: 'Serveur SMTP',
    smtpPort: 'Port SMTP',
    security: 'Sécurité',
    authentication: 'Authentification',
    username: 'Nom d\'utilisateur',
    password: 'Mot de passe',
    fromEmail: 'E-mail expéditeur',
    toEmail: 'E-mail destinataire',
    webhookUrl: 'URL du webhook',
    payloadFormat: 'Format du payload',
    authorization: 'Autorisation',
    titleFieldName: 'Nom du champ titre',
    messageFieldName: 'Nom du champ message',
    // NotificationTemplateEditor
    editTemplate: 'Modifier le modèle : {{name}}',
    titleLabel: 'Titre',
    bodyLabel: 'Corps',
    titlePlaceholder: 'Titre de la notification...',
    bodyPlaceholder: 'Corps de la notification...',
    availableVariables: 'Variables disponibles',
    clickToInsert: 'Cliquer pour insérer à la position du curseur dans le corps',
    livePreview: 'Aperçu en direct',
    hide: 'Masquer',
    show: 'Afficher',
    loadingPreview: 'Chargement de l\'aperçu...',
    enterTemplateContent: 'Saisir le contenu du modèle pour voir l\'aperçu',
    titlePreview: 'Titre :',
    bodyPreview: 'Corps :',
    resetToDefault: 'Réinitialiser par défaut',
    titleRequired: 'Le titre est requis',
    bodyRequired: 'Le corps est requis',
    // NotificationLogViewer
    notificationLog: 'Journal des notifications',
    showFailedOnly: 'Échecs uniquement',
    last24Hours: 'Dernières 24 heures',
    last7Days: '7 derniers jours',
    last30Days: '30 derniers jours',
    last90Days: '90 derniers jours',
    justNow: 'À l\'instant',
    noFailedNotifications: 'Aucune notification échouée',
    noNotificationsLogged: 'Aucune notification enregistrée',
    unknownProvider: 'Fournisseur inconnu',
    logTitle: 'Titre',
    logMessage: 'Message',
    logError: 'Erreur',
    logProvider: 'Fournisseur : {{type}}',
    logTime: 'Heure : {{time}}',
    refresh: 'Actualiser',
    clearOld: 'Purger les anciens',
    statsSummary: '{{days}} derniers jours :',
    statsNotifications: 'notifications',
    statsSent: '{{count}} envoyées',
    statsFailed: '{{count}} échouées',
    // Event type labels (for log viewer)
    eventTypes: {
      print_start: 'Impression démarrée',
      print_complete: 'Impression terminée',
      print_failed: 'Impression échouée',
      print_stopped: 'Impression arrêtée',
      print_progress: 'Progression',
      printer_offline: 'Imprimante hors ligne',
      printer_error: 'Erreur de l\'imprimante',
      filament_low: 'Filament bas',
      maintenance_due: 'Maintenance requise',
      test: 'Tester',
    },
    // User email notification preferences
    userEmail: {
      title: 'Notifications',
      emailNotifications: 'Notifications par e-mail',
      emailNotificationsDesc: "Recevez des notifications par e-mail pour vos propres travaux d'impression. Les e-mails sont envoyés via les paramètres SMTP configurés dans l'authentification avancée.",
      sendingTo: 'Les notifications seront envoyées à',
      noEmailWarning: "Votre compte n'a pas d'adresse e-mail. Contactez un administrateur pour en ajouter une.",
      printJobNotifications: "Notifications de travaux d'impression",
      printJobNotificationsDesc: "Choisissez quels événements déclenchent des notifications par e-mail pour les travaux d'impression que vous soumettez.",
      printJobStarts: "Démarrage du travail d'impression",
      printJobStartsDesc: "Être notifié quand votre travail d'impression commence.",
      printJobFinishes: "Fin du travail d'impression",
      printJobFinishesDesc: "Être notifié quand votre travail d'impression se termine avec succès.",
      printErrors: "Erreurs d'impression",
      printErrorsDesc: "Être notifié quand votre travail d'impression échoue ou rencontre une erreur.",
      printJobStops: "Travail d'impression arrêté",
      printJobStopsDesc: "Être notifié quand votre travail d'impression est annulé ou arrêté.",
      saveSuccess: 'Préférences de notification sauvegardées.',
      saveError: 'Impossible de sauvegarder les préférences de notification.',
    },
  },

  // Rich Text Editor
  richTextEditor: {
    bold: 'Gras',
    italic: 'Italique',
    underline: 'Souligné',
    bulletList: 'Liste à puces',
    numberedList: 'Liste numérotée',
    alignLeft: 'Aligner à gauche',
    alignCenter: 'Centrer',
    alignRight: 'Aligner à droite',
    addLink: 'Ajouter lien',
    removeLink: 'Retirer lien',
  },

  // External Links
  externalLinks: {
    noLinksConfigured: 'Aucun lien externe configuré',
    deleteLink: 'Supprimer lien',
    removeCustomIcon: 'Retirer icône personnalisée',
    openInNewTab: 'Ouvrir dans un nouvel onglet',
    placeholders: {
      linkName: 'Mon Lien',
    },
  },

  // Keyboard Shortcuts Modal
  keyboardShortcuts: {
    title: 'Raccourcis Clavier',
    navigation: 'Navigation',
    archivesSection: 'Archives',
    kProfilesSection: 'Profils K',
    generalSection: 'Général',
    shortcuts: {
      goToPrinters: 'Aller aux Imprimantes',
      goToArchives: 'Aller aux Archives',
      goToQueue: 'Aller à la File',
      goToStats: 'Aller aux Stats',
      goToProfiles: 'Aller aux Profils Cloud',
      goToSettings: 'Aller aux Paramètres',
      focusSearch: 'Focus recherche',
      openUploadModal: 'Ouvrir téléversement',
      clearSelection: 'Tout déselectionner',
      contextMenu: 'Menu contextuel (cartes)',
      refreshProfiles: 'Rafraîchir profils',
      newProfile: 'Nouveau profil',
      exitSelectionMode: 'Quitter mode sélection',
      showHelp: 'Afficher cette aide',
    },
    footer: 'Échap ou clic extérieur pour fermer',
  },

  // Notification Log
  notificationLog: {
    title: 'Journal de Notification',
    events: {
      printStarted: 'Début impression',
      printComplete: 'Fin impression',
      printFailed: 'Échec impression',
      printStopped: 'Arrêt impression',
      progress: 'Progression',
      printerOffline: 'Hors ligne',
      printerError: 'Erreur',
      lowFilament: 'Filament bas',
      maintenanceDue: 'Maintenance',
      test: 'Tester',
    },
    timeAgo: {
      justNow: 'À l\'instant',
      minutesAgo: 'Il y a {{minutes}}m',
      hoursAgo: 'Il y a {{hours}}h',
    },
  },

  // Restore/Backup Modal
  restoreBackup: {
    title: 'Restaurer Sauvegarde',
    restoring: 'Restauration...',
    restoreComplete: 'Restauration terminée',
    restoreFailed: 'Échec restauration',
    importSettings: 'Importer les réglages d\'un fichier',
    pleaseWait: 'Patientez pendant la restauration',
    clickToSelect: 'Fichier .json ou .zip',
    howDuplicateHandling: 'Gestion des doublons :',
    categories: {
      printers: 'Imprimantes',
      smartPlugs: 'Prises',
      notificationProviders: 'Fournisseurs',
      filaments: 'Filaments',
      archives: 'Archives',
      pendingUploads: 'Téléversements en attente',
      settingsTemplates: 'Réglages & Modèles',
    },
    matchingInfo: {
      printers: 'par numéro de série',
      smartPlugs: 'par adresse IP',
      notificationProviders: 'par nom',
      filaments: 'par nom+type+marque',
      archives: 'par empreinte numérique (hash)',
      pendingUploads: 'par nom de fichier',
      settingsTemplates: 'toujours écrasés',
    },
    replaceExisting: 'Remplacer existant',
    keepExisting: 'Garder existant',
    replaceDescription: 'Écrase les doublons avec la sauvegarde',
    keepDescription: 'Ne restaure que les éléments absents',
    caution: 'Attention :',
    cautionText: 'L\'écrasement remplacera vos réglages. Les codes d\'accès imprimantes sont exclus par sécurité.',
    itemsRestored: 'Éléments restaurés',
    itemsSkipped: 'Éléments ignorés',
    restored: 'Restaurés',
    skipped: 'Ignorés (déjà présents)',
    filesLabel: 'Fichiers (3MF, vignettes, etc.)',
    newApiKeysGenerated: 'Nouvelles clés API générées',
    newApiKeysWarning: 'Copiées maintenant, elles ne seront plus visibles !',
    processingBackup: 'Traitement du fichier...',
    noDataFound: 'Aucune donnée trouvée dans le fichier.',
    failedToRestore: 'Échec restaure. Vérifiez le format.',
  },

  // Backup Export Modal
  backupExport: {
    title: 'Exporter Sauvegarde',
    selectData: 'Données à inclure',
    selectAll: 'Tout sélectionner',
    selectNone: 'Ne rien sélectionner',
    categoryDescriptions: {
      settings: 'Langue, thèmes, préférences',
      notifications: 'ntfy, Pushover, Discord, etc.',
      templates: 'Modèles de messages personnalisés',
      smartPlugs: 'Configuration des prises Tasmota',
      externalLinks: 'Liens externes de la barre latérale',
      printers: 'Infos imprimantes (codes d\'accès exclus par défaut)',
      plateDetection: 'Images références des plateaux vides',
      filaments: 'Types et coûts filaments',
      maintenance: 'Plannings de maintenance personnalisés',
      archives: 'Données impressions + fichiers (3MF, vignettes, etc.)',
      projects: 'Projets, BOM, pièces jointes',
      pendingUploads: 'En attente revue virtuelle',
      apiKeys: 'Clés Webhook (nouvelles clés générées à l\'import)',
    },
    requiresPrinters: 'Nécessite sélection des imprimantes',
    zipFileWarning: 'Fichier ZIP créé.',
    zipFileDescription: 'Contient tous les médias. Peut être volumineux.',
    includeAccessCodes: 'Inclure Codes d\'accès',
    includeAccessCodesDescription: 'Pour migrer vers une autre machine',
    includeAccessCodesWarning: 'Codes en texte clair. Sécurisez ce fichier !',
    categoriesSelected: '{{selectedCount}} catégories choisies',
  },

  // Pending Uploads Panel
  pendingUploads: {
    placeholders: {
      notes: 'Notes sur l\'impression...',
    },
    discardUpload: 'Rejeter',
    archiveAllUploads: 'Tout archiver',
    discardAllUploads: 'Tout rejeter',
    archive: 'Archiver',
    timeAgo: {
      justNow: 'À l\'instant',
      minutesAgo: 'Il y a {{minutes}}m',
      hoursAgo: 'Il y a {{hours}}h',
      daysAgo: 'Il y a {{days}}j',
    },
  },

  // API Browser
  apiBrowser: {
    placeholders: {
      requestBody: 'Corps JSON...',
      searchEndpoints: 'Chercher endpoints...',
    },
  },

  // Configure AMS Slot Modal
  configureAmsSlot: {
    title: 'Configurer le slot AMS',
    slotConfigured: 'Slot configuré !',
    configuringSlot: 'Configuration du slot :',
    slotLabel: '{{ams}} – Emplacement {{slot}}',
    searchPresets: 'Chercher presets...',
    colorPlaceholder: 'Nom couleur ou hex (ex: brown, FF8800)',
    clearCustomColor: 'Effacer couleur perso',
    noCloudPresets: 'Profils Cloud absents. Connectez-vous.',
    noPresetsAvailable: 'Aucun preset disponible. Connectez-vous à Bambu Cloud ou importez des profils locaux.',
    noMatchingPresets: 'Aucun profil trouvé.',
    custom: 'Perso',
    builtin: 'Inclus',
    settingsSentToPrinter: 'Réglages envoyés',
    filamentProfile: 'Profil Filament',
    kProfileLabel: 'Profil K (Pressure Advance)',
    filteringFor: 'Filtrage pour : {{material}}',
    noKProfile: 'Pas de profil K (utiliser défaut 0.020)',
    noMatchingKProfiles: 'Aucun profil K trouvé. K=0.020 par défaut sera utilisé.',
    selectFilamentFirst: 'Sélectionnez d\'abord un profil filament',
    kFromCalibration: 'K={{value}} de la calibration imprimante',
    customColorLabel: 'Couleur personnalisée (optionnel)',
    presetColors: 'Couleurs {{name}} :',
    showLessColors: 'Moins de couleurs',
    showMoreColors: 'Plus de couleurs',
    clear: 'Effacer',
    hexLabel: 'Hex : #{{hex}}',
    resetting: 'Réinitialisation...',
    resetSlot: 'Réinitialiser le slot',
    cancel: 'Annuler',
    configuring: 'Configuration...',
    configureSlot: 'Configurer le slot',
  },

  // Git Backup Settings
  githubBackup: {
    title: 'Sauvegarde Git',
    history: 'Historique',
    downloadBackup: 'Télécharger',
    restoreBackup: 'Restaurer',
    noBackupsYet: 'Aucune sauvegarde',
  },

  // Email Settings
  emailSettings: {
    placeholders: {
      fromName: 'Bambuddy',
    },
  },

  // Tag Management Modal
  tagManagement: {
    searchTags: 'Chercher tags...',
    renameTag: 'Renommer tag',
    deleteTag: 'Supprimer tag',
  },

  // Notification Template Editor
  notificationTemplates: {
    placeholders: {
      title: 'Titre notification...',
      body: 'Message notification...',
    },
  },

  // Batch Tag Modal
  batchTag: {
    placeholders: {
      newTag: 'Nouveau tag...',
    },
  },

  // Photo Gallery Modal
  photoGallery: {
    deletePhoto: 'Supprimer photo',
  },

  // Filament Hover Card
  filamentHoverCard: {
    copySpoolUuid: 'Copier UUID bobine',
  },

  // K Profiles View
  kProfilesView: {
    hasNote: 'A une note',
    copyProfile: 'Copier profil',
  },

  // Layout/Navigation
  layout: {
    openMenu: 'Ouvrir menu',
    noPermissionSystemInfo: 'Pas d\'autorisation système',
  },

  // Dashboard
  dashboard: {
    dragToReorder: 'Glisser pour réorganiser',
    hideWidget: 'Masquer widget',
  },

  // Notification Provider Card
  notificationProviderCard: {
    deleteNotificationProvider: 'Supprimer fournisseur',
  },

  // File Manager Modal
  fileManagerModal: {
    closeFileManager: 'Fermer gestionnaire',
    sortFiles: 'Trier fichiers',
    goToParentFolder: 'Dossier parent',
    threeView: 'Vue 3D',
  },

  // Embedded Camera Viewer
  embeddedCameraViewer: {
    refreshStream: 'Actualiser flux',
    close: 'Fermer',
    zoomOut: 'Zoom -',
    resetZoom: 'Réinitialiser le zoom',
    zoomIn: 'Zoom +',
    dragToResize: 'Glisser pour dimension',
  },

  // Timelapse Viewer
  timelapseViewer: {
    skipBack5s: '-5s',
    skipForward5s: '+5s',
  },

  // Notification Providers
  notificationProviders: {
    descriptions: {
      email: 'Notifications par email SMTP',
      telegram: 'Via bot Telegram',
      discord: 'Via webhook Discord',
      ntfy: 'Push auto-hébergé (ntfy)',
      pushover: 'Push fiable (Pushover)',
      callmebot: 'WhatsApp gratuit via CallMeBot',
      webhook: 'Requête HTTP POST personnalisée',
    },
  },

  // Log Viewer
  logViewer: {
    searchPlaceholder: 'Message ou nom...',
    noLogEntries: 'Aucune entrée journal',
  },

  // Switchbar Popover
  switchbarPopover: {
    noSwitchesInSwitchbar: 'Aucun interrupteur',
  },

  // Project Page Modal
  projectPageModal: {
    placeholders: {
      title: 'Titre',
      designer: 'Designer',
      license: 'Licence',
      description: 'Description...',
      profileTitle: 'Titre du profil',
      profileDescription: 'Description du profil...',
    },
  },

  // Spoolman Settings
  spoolmanSettings: {},

  // Time
  time: {
    unknown: '-',
    waiting: 'En attente',
    justNow: 'À l\'instant',
    now: 'Maintenant',
    minsAgo: 'il y a {{count}}m',
    inMins: 'dans {{count}}m',
    hoursAgo: 'il y a {{count}}h',
    inHours: 'dans {{count}}h',
    daysAgo: 'il y a {{count}}j',
    inDays: 'dans {{count}}j',
  },

  // SpoolBuddy Kiosk
  spoolbuddy: {
    nav: {
      dashboard: 'Tableau de bord',
      ams: 'AMS',
      inventory: 'Inventaire',
      writeTag: 'Écrire',
      settings: 'Paramètres',
    },
    status: {
      nfcReady: 'NFC prêt',
      nfcOff: 'NFC désactivé',
      offline: 'Hors ligne',
      online: 'En ligne',
      noPrinters: 'Aucune imprimante',
      deviceOffline: 'Appareil hors ligne',
      waitingConnection: 'En attente de connexion...',
      systemReady: 'Système prêt',
      status: 'Statut',
    },
    dashboard: {
      readyToScan: 'Prêt à scanner',
      idleMessage: 'Placez une bobine sur la balance pour l\'identifier',
      nfcHint: 'Le tag NFC sera lu automatiquement',
      device: 'Appareil',
      syncWeight: 'Sync. poids',
      weightSynced: 'Synchronisé !',
      unknownTag: 'Tag inconnu',
      newTag: 'Nouveau tag détecté',
      onScale: 'sur la balance',
      linkSpool: 'Lier à une bobine',
      linkTagTitle: 'Lier le tag à une bobine',
      linkTag: 'Lier le tag',
      selectSpool: 'Sélectionnez une bobine à lier à ce tag :',
      noUntagged: 'Aucune bobine sans tag trouvée',
      tagDetected: 'Tag détecté',
      noTag: 'Pas de tag',
      tagId: 'Étiquette',
      grossWeight: 'Poids brut',
      spoolSize: 'Taille bobine',
      close: 'Fermer',
      currentSpool: 'Bobine actuelle',
      plateReady: 'Plateau prêt : {{name}}',
      plateReadyLabel: 'Plateaux à libérer',
      plateClearAction: 'Libérer',
      plateClearedToast: 'Plateau marqué comme libéré',
      plateClearFailed: 'Impossible de marquer le plateau comme libéré',
    },
    modal: {
      spoolDetected: 'Bobine détectée',
      assignToAms: 'Assigner à l\'AMS',
      syncWeight: 'Synchroniser le poids',
      weightSynced: 'Synchronisé !',
      syncing: 'Synchronisation...',
      newTagDetected: 'Nouveau tag détecté',
      addToInventory: 'Ajouter à l\'inventaire',
      assignToAmsTitle: 'Assigner à l\'AMS',
      selectSlot: 'Sélectionner un emplacement',
      assign: 'Assigner',
      assigning: 'Attribution...',
      assignSuccess: 'Assigné !',
      assignPendingInsert: 'Assigné. Le slot sera configuré lors de l\'insertion de la bobine.',
      assignError: 'Échec de l\'attribution de la bobine. Veuillez réessayer.',
      noPrinterSelected: 'Sélectionner une imprimante...',
      noAmsDetected: 'Aucun AMS détecté sur cette imprimante',
      slot: 'Emplacement',
    },
    weight: {
      noReading: 'Pas de lecture',
      stable: 'Stable',
      measuring: 'Mesure...',
      tare: 'Tarer',
      calibrate: 'Calibrer',
    },
    spool: {
      remaining: 'Restant',
      material: 'Matériau',
      brand: 'Marque',
      color: 'Couleur',
      coreWeight: 'Noyau',
      labelWeight: 'Étiquette',
      scaleWeight: 'Balance',
      netWeight: 'Réseau',
      lastUsed: 'Dernière utilisation',
    },
    ams: {
      noData: 'Aucun AMS détecté',
      connectAms: 'Connectez un AMS pour voir les slots',
      noPrinter: 'Aucune imprimante sélectionnée',
      selectPrinter: 'Sélectionnez une imprimante dans la barre supérieure',
      printerDisconnected: 'Imprimante déconnectée',
      humidity: 'Humidité',
      level: 'Niveau',
      active: 'Actif',
      slot: 'Emplacement',
      empty: 'Vide',
    },
    inventory: {
      search: 'Rechercher des bobines...',
      empty: 'Aucune bobine dans l\'inventaire',
      noResults: 'Aucune bobine correspondante',
      spools: 'bobines',
      addSpool: 'Ajouter une bobine',
    },
    settings: {
      // Tabs
      tabDevice: 'Appareil',
      tabDisplay: 'Affichage',
      tabScale: 'Balance',
      tabUpdates: 'Mises à jour',
      // Device tab
      nfcReader: 'Lecteur NFC',
      type: 'Type',
      connection: 'Connexion',
      notConnected: 'N/D',
      deviceInfo: 'Info appareil',
      hostname: 'Hôte',
      uptime: 'Temps de fonctionnement',
      // Display tab
      brightness: 'Luminosité',
      saved: 'Enregistré',
      noBacklight: 'Aucun rétroéclairage DSI détecté. Le contrôle de luminosité nécessite un écran DSI.',
      screenBlank: 'Délai d\'extinction',
      screenBlankDesc: 'L\'écran s\'éteint après inactivité. Touchez pour réveiller.',
      displayNote: 'La luminosité est appliquée comme filtre logiciel.',
      // Scale tab
      scaleCalibration: 'Calibration de la balance',
      currentWeight: 'Poids actuel',
      tareOffset: 'Tare',
      calFactor: 'Facteur',
      knownWeight: 'Poids connu',
      calStep1: 'Retirez tout de la balance et appuyez sur Mettre à zéro.',
      calStep2: 'Placez le poids connu sur la balance.',
      setZero: 'Mettre à zéro',
      calibrateNow: 'Calibrer',
      calibrated: 'Calibré',
      tareSet: 'Commande de tare envoyée. En attente de l\'appareil...',
      tareFailed: 'Échec de l\'envoi de la commande de tare',
      zeroSet: 'Point zéro défini. Placez le poids connu sur la balance.',
      calibrationDone: 'Calibration terminée !',
      calibrationFailed: 'Échec de la calibration',
      lastCalibrated: 'Dernière calibration',
      stable: 'Stable',
      settling: 'Stabilisation...',
      firmware: 'Micrologiciel',
      scale: 'Balance',
      noDevice: 'Aucun appareil SpoolBuddy trouvé',
      // Updates tab
      daemonVersion: 'Version du daemon',
      currentVersion: 'Actuelle',
      versionPending: 'En attente du daemon...',
      checking: 'Vérification...',
      checkUpdates: 'Vérifier les mises à jour',
      updateAvailable: 'Mise à jour disponible',
      updateInstructions: 'Mise à jour via SSH : exécutez le script d\'installation SpoolBuddy.',
      upToDate: 'À jour',
      includeBeta: 'Inclure les versions bêta',
      systemConfig: 'Backend & authentification',
      backendUrl: 'URL du backend Bambuddy',
      apiToken: 'Jeton d\'API',
      apiTokenPlaceholder: 'Saisir un jeton d\'API',
      saveConfig: 'Enregistrer la configuration',
      systemQueued: 'Configuration mise en file.',
      systemFieldsRequired: 'L\'URL du backend est obligatoire.',
      nfcDiagnostic: 'Diagnostic NFC',
      scaleDiagnostic: 'Diagnostic balance',
      readTagDiagnostic: 'Diagnostic lecture de tag',
      testNfc: 'Tester le lecteur',
      testScale: 'Tester la précision',
      testReadTag: 'Lire un tag',
    },
    writeTag: {
      tabExisting: 'Bobine existante',
      tabNew: 'Nouvelle bobine',
      tabReplace: 'Remplacer le tag',
      searchPlaceholder: 'Rechercher par matériau, couleur, marque...',
      noUntaggedSpools: 'Aucune bobine sans tag',
      noTaggedSpools: 'Aucune bobine avec tag',
      selectSpool: 'Sélectionnez une bobine, puis placez un NTAG sur le lecteur',
      placeTag: 'Placez un NTAG sur le lecteur',
      tagReady: 'Tag détecté — prêt à écrire',
      writeTag: 'Écrire le tag',
      replaceTag: 'Remplacer le tag',
      writing: 'Écriture du tag...',
      waiting: 'En attente de SpoolBuddy...',
      writeSuccess: 'Tag écrit avec succès !',
      writeFailed: 'Échec de l\'écriture',
      queueFailed: 'Impossible de mettre en file la commande d\'écriture',
      tryAgain: 'Réessayer',
      cancel: 'Annuler',
      replaceWarning: 'L\'ancien tag sera dissocié. Le nouveau tag le remplacera.',
      deviceOffline: 'SpoolBuddy est hors ligne',
      material: 'Matériau',
      colorName: 'Nom de la couleur',
      color: 'Couleur',
      brand: 'Marque',
      weight: 'Poids (g)',
      createSpool: 'Créer la bobine',
      creating: 'Création...',
      spoolCreated: 'Bobine créée ! Prêt à écrire.',
      createFailed: 'Impossible de créer la bobine',
      incompleteDataWarning: 'Tag écrit avec des données Spoolman incomplètes',
    },
    quickMenu: {
      printerPower: 'Alimentation imprimante',
      systemControls: 'Système',
      restartDaemon: 'Redémarrer le daemon',
      restartBrowser: 'Redémarrer le navigateur',
      reboot: 'Redémarrer',
      shutdown: 'Éteindre',
      swipeToClose: 'Glisser vers le bas pour fermer',
      confirmTitle: 'Confirmer',
      confirmShutdown: 'Êtes-vous sûr de vouloir éteindre le SpoolBuddy ? Vous aurez besoin d\'un accès physique pour le rallumer.',
      confirmReboot: 'Êtes-vous sûr de vouloir redémarrer le SpoolBuddy ?',
      confirmRestartDaemon: 'Redémarrer le daemon SpoolBuddy ? Le NFC et la balance seront temporairement indisponibles.',
      confirmRestartBrowser: 'Redémarrer le navigateur kiosque ? L\'écran sera brièvement noir.',
      confirm: 'Confirmer',
      confirmPlugOn: 'Allumer {{name}} ?',
      confirmPlugOff: 'Éteindre {{name}} ?',
      turnOn: 'Allumer',
      turnOff: 'Éteindre',
    },
  },

  bugReport: {
    title: 'Signaler un bug',
    description: 'Description',
    descriptionPlaceholder: 'Qu\'est-ce qui n\'a pas fonctionné ? Veuillez décrire le problème...',
    email: 'E-mail (optionnel)',
    emailPlaceholder: 'votre@email.fr',
    emailPrivacy: 'Si fourni, votre e-mail sera inclus dans une section repliée de l\'issue GitHub pour que le mainteneur puisse vous contacter.',
    screenshot: 'Capture d\'écran',
    uploadOrPaste: 'Télécharger, coller ou glisser une image',
    dataCollectedSummary: 'Quelles données sont incluses dans le rapport ?',
    dataIncluded: 'Inclus :',
    dataIncludedList: 'Version de l\'app, OS, architecture, version Python, statistiques de base de données (compteurs uniquement), modèles d\'imprimantes, nombre de buses, versions firmware, état de connexion, état des intégrations (Spoolman, MQTT, HA), paramètres non sensibles, nombre d\'interfaces réseau, détails Docker, versions des dépendances.',
    dataNeverIncluded: 'Jamais inclus :',
    dataNeverIncludedList: 'Noms d\'imprimantes, numéros de série, codes d\'accès, mots de passe, adresses IP, adresses e-mail, clés API, tokens, URLs de webhook, noms d\'hôtes ou noms d\'utilisateurs.',
    submit: 'Envoyer',
    startLogging: 'Lancer la journalisation',
    stepEnableLogging: 'Journalisation activée',
    stepReproduce: 'Reproduisez le problème',
    stepStopLogging: 'Arrêter & envoyer',
    stopAndSubmit: 'Arrêter & Envoyer',
    maxDuration: 'Arrêt auto après {{minutes}} min',
    stoppingLogs: 'Collecte des journaux & envoi...',
    submitting: 'Envoi du rapport de bug...',
    submitSuccess: 'Rapport de bug envoyé avec succès !',
    submitFailed: 'Échec de l\'envoi du rapport de bug',
    thankYou: 'Merci !',
    submitted: 'Votre rapport de bug a été soumis.',
    viewIssue: 'Voir l\'issue',
    unexpectedError: 'Une erreur inattendue est survenue',
  },
  failureDetection: {
    title: 'Détection d\'échec par IA',
    description: 'Surveille les impressions via une API ML Obico auto-hébergée et agit automatiquement sur les échecs détectés.',
    mlUrl: 'URL de l\'API ML Obico',
    mlUrlHint: 'URL de base de votre conteneur Obico ml_api auto-hébergé (ex. http://192.168.1.10:3333).',
    test: 'Tester',
    testSuccess: 'API ML accessible et fonctionnelle.',
    testFailed: 'Impossible d\'atteindre l\'API ML.',
    sensitivity: 'Sensibilité',
    sensitivityLow: 'Basse (moins de faux positifs)',
    sensitivityMedium: 'Moyenne (équilibrée)',
    sensitivityHigh: 'Haute (détection précoce, plus de faux positifs)',
    sensitivityHint: 'Ajuste les seuils de confiance qui déclenchent les avertissements et les échecs.',
    action: 'Action sur échec détecté',
    actionNotify: 'Notifier uniquement',
    actionPause: 'Mettre en pause',
    actionPauseOff: 'Pause et couper l\'alimentation',
    pollInterval: 'Intervalle de vérification (secondes)',
    pollIntervalHint: 'Fréquence de vérification de chaque imprimante pendant l\'impression. Minimum 5s, maximum 120s.',
    externalUrlMissing: 'L\'URL externe n\'est pas définie.',
    externalUrlHint: 'L\'API ML récupère la capture caméra par URL. Définissez l\'URL externe dans les paramètres généraux pour que le conteneur API ML puisse atteindre Bambuddy.',
    perPrinterTitle: 'Imprimantes surveillées',
    perPrinterHint: 'Choisissez quelles imprimantes le service de détection surveille.',
    monitorAll: 'Surveiller toutes les imprimantes connectées',
    statusTitle: 'Statut',
    serviceRunning: 'Service en cours d\'exécution',
    thresholds: 'Seuils bas / haut',
    activePrinters: 'Impressions actives',
    noActivePrints: 'Aucune impression en cours.',
    historyTitle: 'Détections récentes',
    noHistory: 'Aucune détection pour le moment.',
  },
  makerworld: {
    title: 'MakerWorld',
    description: 'Collez une URL de modèle MakerWorld pour l\'importer et l\'imprimer directement depuis Bambuddy — sans passer par l\'application Bambu Handy.',
    pasteUrlHeader: 'Importer depuis MakerWorld',
    pasteUrlPlaceholder: 'https://makerworld.com/en/models/… ou collez n\'importe quel lien MakerWorld',
    resolveButton: 'Résoudre',
    signInRequiredTitle: 'Connexion Bambu Cloud requise pour télécharger',
    signInRequiredBody: 'Vous pouvez consulter les détails du modèle anonymement, mais MakerWorld nécessite un compte Bambu Cloud pour télécharger les fichiers 3MF.',
    openCloudSettings: 'Ouvrir les paramètres Cloud',
    untitledModel: 'Modèle sans titre',
    byCreator: 'par {{name}}',
    downloadsCount: '{{count}} téléchargements',
    licensePrefix: 'Licence',
    alreadyImported: 'Déjà dans la bibliothèque',
    openOnMakerworld: 'Ouvrir sur MakerWorld',
    alreadyInLibrary: 'Ce modèle est déjà dans votre bibliothèque — retrouvez-le dans Gestionnaire de fichiers → MakerWorld',
    importSuccess: '{{filename}} importé — enregistré dans Gestionnaire de fichiers → MakerWorld',
    platesHeader: 'Plateaux ({{count}})',
    plateDefaultName: 'Plateau {{n}}',
    materialCount: '{{count}} filaments',
    amsRequired: 'AMS requis',
    slicedFor: 'Découpé pour {{printer}}',
    alsoCompatible: 'Également marqué compatible : {{printers}}',
    importToLibrary: 'Enregistrer',
    sliceIn: 'Enregistrer et découper dans {{slicer}}',
    disclaimer: 'L\'intégration MakerWorld utilise des points de terminaison API documentés par la communauté. Bambuddy n\'est ni affilié ni approuvé par MakerWorld ou Bambu Lab.',
    lastImportSuccess: 'Importé dans votre bibliothèque',
    lastImportAlreadyInLibrary: 'Déjà dans votre bibliothèque',
    viewInLibrary: 'Voir dans le Gestionnaire de fichiers',
    openInBambuStudio: 'Ouvrir dans Bambu Studio',
    openInOrcaSlicer: 'Ouvrir dans OrcaSlicer',
    importTo: 'Importer dans le Gestionnaire de fichiers',
    recentImportsHeader: 'Imports récents',
    phaseResolving: 'Résolution',
    phaseDownloading: 'Téléchargement',
    folderAuto: 'MakerWorld (par défaut)',
    importAll: 'Tout importer',
    importAllProgress: 'Importation de {{current}}/{{total}}',
    openGallery: 'Ouvrir la galerie d\'images',
    galleryPrev: 'Image précédente',
    galleryNext: 'Image suivante',
    deleteImport: 'Retirer de la bibliothèque',
    importDeleting: 'Suppression…',
    importDeleted: 'Retiré de la bibliothèque',
    confirmDelete: 'Retirer {{filename}} de la bibliothèque ? Le fichier local est supprimé, mais la plaque peut être ré-importée depuis MakerWorld.',
    errors: {
      resolveFailed: 'Impossible de résoudre cette URL MakerWorld.',
      downloadFailed: 'Le téléchargement a échoué. Veuillez réessayer.',
      deleteFailed: 'Impossible de retirer le fichier de la bibliothèque.',
    },
  },
  gcodeViewer: {
    back: 'Retour',
    backToArchives: 'Retour aux archives d\'impression',
    backToFiles: 'Retour au gestionnaire de fichiers',
  },
  libraryTrash: {
    title: 'Corbeille',
    headerButton: 'Corbeille',
    headerTooltip: 'Voir les fichiers déplacés dans la corbeille',
    backToFiles: 'Retour au gestionnaire de fichiers',
    subtitleAdmin: 'Les fichiers supprimés restent ici pendant {{days}} jours, puis sont supprimés automatiquement. Cette vue affiche la corbeille de tous les utilisateurs.',
    subtitleUser: 'Les fichiers supprimés restent ici pendant {{days}} jours, puis sont supprimés automatiquement.',
    loading: 'Chargement de la corbeille…',
    loadError: 'Impossible de charger la corbeille.',
    empty: 'La corbeille est vide.',
    summary: '{{count}} fichiers · {{size}}',
    emptyTrash: 'Vider la corbeille',
    restore: 'Restaurer',
    purgeNow: 'Supprimer maintenant',
    autoPurgeIn: 'Suppression automatique dans {{when}}',
    days: 'jours',
    retentionLabel: 'Supprimer automatiquement après',
    selectAll: 'Tout sélectionner',
    selectOne: 'Sélectionner {{filename}}',
    selectionCount: '{{count}} sélectionné(s)',
    bulkRestore: 'Restaurer la sélection',
    bulkPurge: 'Supprimer la sélection',
    col: {
      filename: 'Fichier',
      folder: 'Dossier',
      size: 'Taille',
      deleted: 'Mis à la corbeille',
      autoPurge: 'Suppression auto.',
      owner: 'Propriétaire',
      actions: 'Actions',
    },
    confirm: {
      purgeTitle: 'Supprimer définitivement ?',
      purgeBody: '{{filename}} sera supprimé du disque et ne pourra pas être restauré.',
      emptyTitle: 'Vider la corbeille ?',
      emptyBody: 'Tous les {{count}} fichiers seront supprimés du disque. Cette action est irréversible.',
      bulkPurgeTitle: 'Supprimer définitivement les fichiers sélectionnés ?',
      bulkPurgeBody: 'Les {{count}} fichiers sélectionnés seront supprimés du disque et ne pourront pas être restaurés.',
      cta: 'Supprimer définitivement',
    },
    toast: {
      restored: 'Fichier restauré.',
      restoreFailed: 'Impossible de restaurer le fichier.',
      purged: 'Fichier supprimé définitivement.',
      purgeFailed: 'Impossible de supprimer le fichier.',
      emptied: '{{count}} fichier(s) supprimé(s) de la corbeille.',
      emptyFailed: 'Impossible de vider la corbeille.',
      retentionSaved: 'Suppression automatique réglée sur {{days}} jours.',
      retentionFailed: 'Impossible d\'enregistrer le réglage de rétention.',
      bulkRestored: '{{count}} fichier(s) restauré(s).',
      bulkPurged: '{{count}} fichier(s) supprimé(s).',
    },
  },
  libraryPurge: {
    title: 'Purger les anciens fichiers',
    headerButton: 'Purger',
    headerTooltip: 'Déplacer en masse les anciens fichiers vers la corbeille',
    description: 'Les fichiers plus anciens que le seuil seront déplacés vers la corbeille. Les dossiers externes sont ignorés. Vous pouvez restaurer depuis la corbeille avant la suppression automatique.',
    ageLabel: 'Déplacer les fichiers plus anciens que',
    days: 'jours',
    includeNeverPrinted: 'Inclure les fichiers jamais imprimés',
    effectsTitle: 'Ce qui se passe lorsque vous cliquez sur Purger',
    effect1: 'Les fichiers correspondants sont déplacés vers la corbeille — pas encore supprimés du disque.',
    effect2: 'Vous pouvez les restaurer depuis la corbeille jusqu\'à l\'expiration de la fenêtre de rétention.',
    effect3: 'Après la période de rétention, le balayeur de corbeille les supprime définitivement.',
    effect4: 'Les fichiers dans les dossiers externes (liés) sont ignorés — Bambuddy ne supprime jamais d\'octets qu\'il ne possède pas.',    previewLoading: 'Vérification du nombre de fichiers concernés…',
    previewFailed: 'Impossible de prévisualiser la purge.',
    previewSummary: '{{count}} fichiers · {{size}} seraient déplacés vers la corbeille',
    andMore: '…et {{count}} de plus',
    warning: 'Les fichiers sont supprimés en douceur — vous pouvez les restaurer depuis la corbeille jusqu\'à l\'expiration de la période de rétention.',
    confirmCta: 'Déplacer {{count}} vers la corbeille',
    purging: 'Déplacement vers la corbeille…',
    toast: {
      success: '{{count}} fichier(s) déplacé(s) vers la corbeille.',
      failed: 'Impossible de purger les fichiers.',
    },
  },
  libraryAutoPurge: {
    enableLabel: 'Purge automatique des anciens fichiers',
    enableDescription: 'Exécute la purge administrateur une fois par jour. Les fichiers sont d\'abord déplacés vers la corbeille — ils ne sont pas supprimés immédiatement.',
    ageLabel: 'Purger automatiquement les fichiers plus anciens que',
    ageDescription: 'Minimum 7 jours, maximum 10 ans. Utilise la même règle d\'ancienneté que le bouton Purger manuel.',
    days: 'jours',
    includeNeverPrinted: 'Inclure les fichiers jamais imprimés',
    saveFailed: 'Impossible d\'enregistrer les paramètres de purge automatique.',
  },
  archivePurge: {
    headerButton: 'Purger les anciens',
    headerTooltip: 'Suppression groupée d\'anciennes archives',
    title: 'Purger les anciennes archives',
    description: 'Effacer l\'ancien historique d\'impression. Chaque archive vieillit selon sa dernière impression terminée — réimprimer une archive remet son âge à zéro, donc le travail actif n\'est jamais purgé.',
    ageLabel: 'Supprimer les archives non imprimées depuis',
    days: 'jours',
    effectsTitle: 'Ce qui se passe lorsque vous cliquez sur Purger',
    effect1: 'Chaque archive correspondante est masquée des listes et ses fichiers sont retirés du disque (3MF, miniature, timelapse, 3MF source, F3D, photos).',
    effect2: 'La ligne d\'archive reste en base pour que les statistiques rapides conservent la contribution en filament, temps, coût et énergie — même comportement que la suppression individuelle par défaut.',
    effect3: 'Cochez « Aussi retirer des statistiques » ci-dessous pour également supprimer la contribution des statistiques rapides (équivalent à l\'option de la suppression individuelle). Ce chemin est irréversible.',
    effect4: 'Réimprimer une archive remet son compteur à zéro, les archives actives sont donc protégées.',
    purgeStatsLabel: 'Aussi retirer des statistiques',
    purgeStatsHint: 'Supprime les archives correspondantes des statistiques rapides (filament, temps, coût, énergie). Sans cette option, les statistiques conservent chaque contribution et seuls les fichiers quittent le disque.',
    previewLoading: 'Vérification du nombre d\'archives correspondantes…',
    previewFailed: 'Impossible de prévisualiser la purge.',
    previewSummary: '{{count}} archives · {{size}} seraient retirées',
    andMore: '…et {{count}} de plus',
    warning: 'Les fichiers sont retirés du disque et ne peuvent pas être restaurés. Téléchargez ou marquez comme favori avant de continuer.',
    confirmCta: 'Retirer {{count}} archive(s)',
    purging: 'Retrait en cours…',
    toast: {
      success: '{{count}} archive(s) retirée(s).',
      failed: 'Impossible de purger les archives.',
    },
  },
  archiveAutoPurge: {
    enableLabel: 'Purger auto. les anciennes archives',
    enableDescription: 'Une fois par jour, masque des listes les archives non imprimées dans le seuil et retire leurs fichiers du disque. Réimprimer remet le compteur à zéro.',
    ageLabel: 'Suppression auto. des archives non imprimées depuis',
    ageDescription: 'Minimum 7 jours, maximum 10 ans. Basé sur la dernière impression terminée — réimprimer remet l\'âge à zéro. Retire 3MF, miniature, timelapse, 3MF source, F3D et photos.',
    days: 'jours',
    purgeStatsLabel: 'Aussi retirer des statistiques',
    purgeStatsDescription: 'Lorsqu\'activé, le nettoyage quotidien supprime aussi la contribution de chaque archive purgée des statistiques rapides (filament, temps, coût, énergie). Désactivé par défaut — les statistiques conservent la contribution et seuls les fichiers quittent le disque.',
    runNow: 'Purger les archives maintenant',
    saveFailed: 'Impossible d\'enregistrer les paramètres de purge automatique.',
  },
  cameraTokens: {
    title: 'Jetons API caméra',
    navTitle: 'Jetons API caméra',
    description:
      'Jetons longue durée pour intégrer le flux caméra dans Home Assistant, Frigate, kiosques ou tout autre outil nécessitant une URL stable. Chaque jeton est limité au flux caméra et peut être révoqué à tout moment.',
    loading: 'Chargement…',
    confirmRevoke: {
      title: 'Révoquer ce jeton ?',
      body: 'Tout appareil utilisant "{{name}}" perdra l\'accès immédiatement. Action irréversible.',
      cancel: 'Annuler',
      confirm: 'Révoquer',
    },
    create: {
      title: 'Créer un nouveau jeton',
      nameLabel: 'Nom du jeton',
      namePlaceholder: 'par ex. Home Assistant',
      daysLabel: 'Jours avant expiration',
      submit: 'Créer',
      hint:
        'Durée de vie maximale 365 jours. La valeur du jeton n\'est affichée qu\'une seule fois — copiez-la maintenant.',
    },
    created: {
      title: 'Jeton créé – copiez-le maintenant',
      warning:
        'C\'est la seule fois où ce jeton sera visible. Après la fermeture de ce dialogue, vous ne pourrez plus jamais le voir.',
      copy: 'Copier',
      dismiss: 'Je l\'ai enregistré',
    },
    list: {
      myTitle: 'Mes jetons',
      allTitle: 'Tous les utilisateurs (vue admin)',
      empty: 'Aucun jeton pour le moment.',
      name: 'Nom',
      owner: 'Propriétaire',
      prefix: 'Préfixe',
      created: 'Créé',
      expires: 'Expire',
      lastUsed: 'Dernière utilisation',
      revoke: 'Révoquer',
      expired: 'Expiré',
    },
    toast: {
      created: 'Jeton créé',
      createFailed: 'Échec de la création du jeton',
      revoked: 'Jeton révoqué',
      revokeFailed: 'Échec de la révocation du jeton',
      loadFailed: 'Échec du chargement des jetons',
      copied: 'Copié dans le presse-papiers',
      copyFailed: 'Échec de la copie – sélectionnez et copiez manuellement',
    },
  },

  // Forecast & Inventory Intelligence
  forecast: {
    title: 'Prévision des stocks',
    noSpools: 'Aucune bobine active trouvée. Ajoutez des bobines à votre inventaire pour voir les données de prévision.',
    noUsageData: 'Aucune donnée d\'utilisation disponible — impossible de projeter la chronologie des stocks.',
    sku: 'SKU',
    // Table headers
    material: 'Matériau',
    stock: 'Stock',
    dailyRate: 'Cadence',
    daysLeft: 'Jours restants',
    emptyBy: 'Épuisé le',
    reorderBy: 'Réapprovisionner avant le',
    actions: 'Actions',
    // Rate tier badges
    trend: 'Tendance',
    estimated: 'Est.',
    noData: 'Aucune donnée',
    // Timeframe
    timeframe: 'Période',
    // Chart
    chartTitle: 'Prévision du stock — Top 5 matériaux',
    dashedLinesROP: 'Lignes pointillées = points de réapprovisionnement',
    stockLevel: 'Niveau de stock',
    reorderPoint: 'Point de réapprovisionnement',
    safetyMargin: 'Marge de sécurité',
    trendLegend: 'Tendance (basée sur l\'historique, niveau de service 95 %)',
    estimatedLegend: 'Estimée (delta de poids)',
    noDataLegend: 'Aucune donnée',
    ropLabel: 'PR',
    ssLabel: 'SS',
    safetyStockLegend: 'Stock de sécurité',
    stockArrivalLegend: 'Arrivée du stock',
    stockoutLegend: 'Rupture de stock',
    // Alerts toolbar
    alertCount_one: '{{count}} alerte',
    alertCount_other: '{{count}} alertes',
    order: 'Commander',
    // Settings
    globalLeadTime: 'Délai global',
    globalLeadTimeHint: 'Délai global minimum — utilisé dans le calcul du point de réapprovisionnement pour tous les SKUs',
    save: 'Enregistrer',
    cancel: 'Annuler',
    settingsSaved: 'Paramètres enregistrés',
    failedSaveSettings: 'Échec de l\'enregistrement des paramètres',
    globalLeadTimeSaved: 'Délai global enregistré',
    skuLeadTimeOverride: 'Délai spécifique au SKU',
    skuLeadTimeHint: '0 = utiliser le délai global. Définir >0 pour remplacer ce SKU.',
    safetyMarginLabel: 'Marge de sécurité',
    effectiveLeadTime: 'Délai effectif',
    effectiveLeadTimeHint: 'max(global {{global}}j, SKU {{sku}}j)',
    reorderPointHint: 'd̄ × LT + safety margin — commander quand le stock atteint ce niveau',
    safetyMarginHint: 'Stock de sécurité statistique (z=1,65 × σ × √LT) + tampon défini par l\'utilisateur',
    safetyMarginHintDays: 'Tampon ajouté par-dessus le stock de sécurité statistique.{{approx}}',
    safetyMarginHintDaysApprox: ' ≈ {{g}}g au taux actuel.',
    safetyMarginHintG: 'Tampon de poids fixe ajouté par-dessus le stock de sécurité statistique.{{approx}}',
    safetyMarginHintGApprox: ' ≈ {{days}}j au taux actuel.',
    individualSpools: 'Bobines individuelles',
    labelWeight: 'Étiquette',
    spoolCount_one: '{{count}} bobine',
    spoolCount_other: '{{count}} bobines',
    // Alerts
    stockBreakRisk: 'Risque de rupture de stock',
    stockBreakBefore: 'Rupture de stock avant réapprovisionnement',
    stockBreakDetail: '{{days}}j restants, délai {{lt}}j.',
    reorderNow: 'Réapprovisionner maintenant',
    reorderTriggerPassed: 'La date de déclenchement {{date}} est passée.',
    // Shopping list
    shoppingList: 'Liste d\'achats',
    shoppingListItems_one: '({{count}} article)',
    shoppingListItems_other: '({{count}} articles)',
    shoppingListEmpty: 'La liste d\'achats est vide. Cliquez sur l\'icône panier d\'une ligne pour ajouter des articles.',
    addToCart: 'Ajouter à la liste d\'achats',
    alertsSnoozed: 'Désactiver les alertes pour ce SKU',
    alertsEnabled: 'Activer les alertes pour ce SKU',
    addedToCart: 'Ajouté à la liste de courses',
    failedAddItem: 'Impossible d\'ajouter l\'article',
    listView: 'Liste',
    logisticsView: 'Logistique',
    qty: 'Qté',
    weight: 'Poids',
    leadTime: 'Délai',
    expectedRestock: 'Réapprovisionnement prévu',
    status: 'Statut',
    note: 'Note',
    pending: 'En attente',
    purchased: 'Commandé',
    received: 'Reçu',
    markPurchased: 'Marquer comme commandé',
    markReceived: 'Marquer comme reçu — ajoute les bobines au stock',
    resetToPending: 'Remettre en attente',
    remove: 'Supprimer',
    clearAll: 'Tout effacer',
    downloadCsv: 'CSV',
    // Add to cart modal
    addToCartTitle: 'Ajouter à la liste de courses',
    byQuantity: 'Par quantité',
    byDuration: 'Par durée',
    numberOfSpools: 'Nombre de bobines',
    lastHowManyDays: 'Combien de jours doit-il durer ?',
    noUsageQty: 'Pas de données d\'utilisation — quantité définie à 1.',
    noteOptional: 'Note (optionnel)',
    notePlaceholder: 'ex. pour le projet X, urgent…',
    addNSpools_one: 'Ajouter {{count}} bobine',
    addNSpools_other: 'Ajouter {{count}} bobines',
    // Cart logistics
    onArrival: 'À la livraison',
    stockBreakIn: 'Rupture de stock dans {{days}}j.',
    stockRunsOutBefore: 'Le stock s\'épuise avant la fin du délai de {{lt}}j.',
    atRate: 'À {{rate}}g/jour, vous avez besoin de',
    moreSpools_one: '{{count}} bobine supplémentaire',
    moreSpools_other: '{{count}} bobines supplémentaires',
    bridgeGap: 'pour combler l\'écart.',
    // Permissions
    noReadAccess: 'Vous n\'avez pas la permission de consulter les prévisions de stock.',
    noWriteAccess: 'Vous n\'avez pas la permission de modifier les paramètres de prévision.',
  },
};
