export default {
  // Navigation
  nav: {
    printers: 'Impresoras',
    archives: 'Archivos',
    queue: 'Cola de impresión',
    stats: 'Estadísticas',
    profiles: 'Perfiles',
    maintenance: 'Mantenimiento',
    projects: 'Proyectos',
    inventory: 'Filamento',
    finance: 'Finanzas',
    files: 'Gestor de archivos',
    makerworld: 'MakerWorld',
    notifications: 'Notificaciones',
    settings: 'Ajustes',
    system: 'Sistema',
    collapseSidebar: 'Contraer barra lateral',
    expandSidebar: 'Expandir barra lateral',
    update: 'Actualizar',
    updateAvailable: 'Actualización disponible: v{{version}}',
    updateAvailableBanner: '¡La versión {{version}} está disponible!',
    viewUpdate: 'Ver actualización',
    viewOnGithub: 'Ver en GitHub',
    keyboardShortcuts: 'Atajos de teclado (?)',
    switchToLight: 'Cambiar a modo claro',
    switchToDark: 'Cambiar a modo oscuro',
    switchToSystem: 'Cambiar a modo del sistema',
    smartSwitches: 'Interruptores inteligentes',
    logout: 'Cerrar sesión',
    installApp: 'Instalar app',
    installAppSuccess: 'Bambuddy se ha instalado',
  },

  // Common
  common: {
    plusNMore: '+{{count}} más',
    save: 'Guardar',
    saving: 'Guardando...',
    cancel: 'Cancelar',
    delete: 'Eliminar',
    edit: 'Editar',
    add: 'Añadir',
    close: 'Cerrar',
    confirm: 'Confirmar',
    loading: 'Cargando...',
    error: 'Error',
    errorLoading: 'Error al cargar los datos',
    retry: 'Reintentar',
    success: 'Éxito',
    warning: 'Advertencia',
    enabled: 'Activado',
    disabled: 'Desactivado',
    yes: 'Sí',
    no: 'No',
    on: 'Encendido',
    off: 'Apagado',
    all: 'Todos',
    none: 'Ninguno',
    search: 'Buscar',
    filter: 'Filtrar',
    sort: 'Ordenar',
    refresh: 'Actualizar',
    download: 'Descargar',
    upload: 'Subir',
    uploading: 'Subiendo...',
    uploadFailed: 'Error al subir',
    actions: 'Acciones',
    status: 'Estado',
    name: 'Nombre',
    description: 'Descripción',
    date: 'Fecha',
    time: 'Hora',
    hours: 'horas',
    minutes: 'minutos',
    seconds: 'segundos',
    days: 'días',
    enable: 'Activar',
    disable: 'Desactivar',
    permissions: 'Permisos',
    noPrinters: 'No hay impresoras configuradas',
    noData: 'No hay datos disponibles',
    linkNotFound: 'Enlace no encontrado',
    required: 'Obligatorio',
    optional: 'Opcional',
    dismiss: 'Descartar',
    apply: 'Aplicar',
    reset: 'Restablecer',
    export: 'Exportar',
    import: 'Importar',
    clear: 'Borrar',
    selectAll: 'Seleccionar todo',
    deselectAll: 'Deseleccionar todo',
    noChange: '— Sin cambios —',
    unchanged: 'Sin cambios',
    unassigned: 'Sin asignar',
    unknown: 'Desconocido',
    unknownError: 'Error desconocido',
    today: 'Hoy',
    tomorrow: 'Mañana',
    asap: 'Lo antes posible',
    overdue: 'Atrasado',
    now: 'Ahora',
    collapse: 'Contraer',
    expand: 'Expandir',
    previous: 'Anterior',
    next: 'Siguiente',
    viewArchive: 'Ver archivo',
    viewInFileManager: 'Ver en el gestor de archivos',
    addedBy: 'Añadido por {{username}}',
    prints: 'impresiones',
    more: '+{{count}} más',
    ascending: 'Ascendente',
    descending: 'Descendente',
    back: 'Atrás',
    copy: 'Copiar',
    copied: '¡Copiado!',
    printer: 'Impresora',
    remove: 'Quitar',
    type: 'Tipo',
    print: 'Imprimir',
    rename: 'Renombrar',
    move: 'Mover',
    create: 'Crear',
    duplicate: 'Duplicar',
    left: 'Izquierda',
    right: 'Derecha',
    user: 'Usuario',
    run: 'Ejecutar',
    running: 'Ejecutando...',
  },

  finance: {
    allTypes: 'Todos los tipos',
    allCostCenters: 'Todos los centros de costes',
    title: 'Finanzas',
    subtitle: 'Monedero, transacciones personales y centros de costes',
    noAccess: 'No tienes permiso para ver los datos financieros.',
    personalView: 'Vista personal',
    adminView: 'Vista de administración',
    createCostCenter: 'Crear centro de costes',
    adjustWallet: 'Ajustar monedero',
    addManualPrint: 'Añadir impresión manual',
    manageMembers: 'Gestionar miembros del centro de costes',
    currentBalance: 'Saldo personal',
    transactions: 'Transacciones',
    personalTransactions: 'Transacciones personales',
    costCenters: 'Centros de costes',
    availableForPrinting: 'Disponible para asignar impresiones',
    costCenterName: 'Nombre',
    budgetType: 'Tipo de presupuesto',
    monthlyBudget: 'Presupuesto mensual',
    totalBudget: 'Presupuesto total',
    noBudget: 'Sin presupuesto',
    create: 'Crear',
    selectUser: 'Seleccionar usuario',
    transactionType: 'Tipo',
    amount: 'Importe',
    noCostCenter: 'Sin centro de costes',
    descriptionOptional: 'Descripción (opcional)',
    applyAdjustment: 'Aplicar ajuste',
    memberCanPrint: 'El miembro puede imprimir',
    addMember: 'Añadir miembro',
    canPrint: 'Puede imprimir',
    noMembers: 'No hay miembros asignados.',
    editCostCenter: 'Editar centro de costes',
    myCostCenters: 'Mis centros de costes',
    costCentersHint: 'Revisa los límites presupuestarios y mantén los costes bajo control',
    noCostCenters: 'No se encontraron centros de costes.',
    owner: 'Propietario',
    balance: 'Saldo de cuenta',
    unlimited: 'Ilimitado',
    budgetPolicyHint: 'Los saldos de cuenta registran los costes. La impresión solo está limitada por el presupuesto del centro de costes seleccionado; sin presupuesto, es ilimitada.',
    budget: 'Presupuesto',
    shared: 'Compartido',
    cannotEditPrivateCostCenter: 'Los centros de costes privados no se pueden editar aquí',
    recentTransactions: 'Transacciones recientes',
    transactionsHint: 'Filtrar por tipo y centro de costes',
    first: 'Primera',
    prev: 'Anterior',
    next: 'Siguiente',
    last: 'Última',
    pageNumberOf: 'Página {{page}} de {{total}}',
    noTransactions: 'No hay transacciones disponibles.',
    noTransactionsForFilter: 'Ninguna transacción coincide con los filtros seleccionados.',
    costCenter: 'Centro de costes',
    balanceAfter: 'Saldo posterior',
    userWithId: 'Usuario n.º {{id}}',
    partial: 'Parcial',
    editTransaction: 'Editar transacción',
    selectCostCenter: 'Seleccionar centro de costes...',
    costCenterRequired: 'Selecciona un centro de costes',
    amountExample: 'p. ej., 4,00',
    manualAdjustmentExample: 'p. ej., Ajuste manual',
    userRequired: 'Selecciona un usuario',
    amountInvalid: 'El importe debe ser un número válido',
    createdCostCenter: 'Centro de costes creado',
    createCostCenterFailed: 'No se pudo crear el centro de costes',
    transactionDeleted: 'Transacción eliminada',
    deleteTransactionFailed: 'No se pudo eliminar la transacción',
    transactionEdited: 'Transacción actualizada y libro mayor recalculado',
    editTransactionFailed: 'No se pudo editar la transacción',
    manualPrintCreated: 'Cargo de impresión manual añadido y libro mayor recalculado',
    manualPrintFailed: 'No se pudo crear la impresión manual',
    memberSaved: 'Miembro guardado',
    memberSaveFailed: 'No se pudo guardar el miembro',
    memberRemoved: 'Miembro eliminado',
    memberRemoveFailed: 'No se pudo eliminar el miembro',
    costCenterNameRequired: 'El nombre del centro de costes es obligatorio',
    costCenterUpdated: 'Centro de costes actualizado',
    costCenterUpdateFailed: 'No se pudo actualizar el centro de costes',
    confirmDeleteCostCenter: '¿Eliminar el centro de costes «{{name}}»?',
    costCenterDeleted: 'Centro de costes eliminado',
    costCenterDeleteFailed: 'No se pudo eliminar el centro de costes',
    deleteTransactionConfirm: '¿Eliminar esta transacción? Los saldos se recalcularán automáticamente.',
    deposit: 'Ingreso',
    withdraw: 'Retirada',
    printCharge: 'Cargo de impresión',
    deleteTransaction: 'Eliminar transacción',
  },

  // Printers page
  printers: {
    addPreflight: {
      checking: 'Comprobando la conexión...',
      warning: 'Algunas comprobaciones de conexión fallaron. Esta impresora podría aparecer como desconectada. Revisa las comprobaciones de abajo, soluciona lo que puedas o guárdala de todos modos.',
      back: 'Atrás',
      saveAnyway: 'Guardar de todos modos',
    },
    title: 'Impresoras',
    addPrinter: 'Añadir impresora',
    editPrinter: 'Editar impresora',
    deletePrinter: 'Eliminar impresora',
    printerName: 'Nombre de la impresora',
    serialNumber: 'Número de serie',
    ipAddress: 'Dirección IP / Nombre de host',
    accessCode: 'Código de acceso',
    model: 'Modelo',
    nozzleCount: 'Número de boquillas',
    autoArchive: 'Archivado automático',
    status: {
      available: 'Disponible',
      idle: 'Inactiva',
      printing: 'Imprimiendo',
      paused: 'En pausa',
      offline: 'Desconectada',
      problem: 'Problema',
      error: 'Error',
      finished: 'Finalizada',
      unknown: 'Desconocido',
    },
    temperatures: {
      nozzle: 'Boquilla',
      bed: 'Cama',
      chamber: 'Cámara',
    },
    heaterHistory: {
      title: 'Historial de calentadores',
      openLabel: 'Ver historial de calentadores',
      nozzle: 'Boquilla',
      nozzle2: 'Boquilla 2',
      bed: 'Cama',
      chamber: 'Cámara',
      error: 'No se pudo cargar el historial',
      empty: 'Aún no hay datos registrados',
    },
    progress: '{{percent}}% completado',
    timeRemaining: '{{time}} restante',
    deleteConfirm: '¿Está seguro de que desea eliminar "{{name}}"?',
    maintenanceOk: 'Mantenimiento correcto',
    maintenanceWarning: '{{count}} advertencia',
    maintenanceWarning_plural: '{{count}} advertencias',
    maintenanceDue: '{{count}} pendiente',
    maintenanceDue_plural: '{{count}} pendientes',
    // Sort options
    sort: {
      name: 'Nombre',
      status: 'Estado',
      model: 'Modelo',
      location: 'Ubicación',
      eta: 'Tiempo restante',
      ascending: 'Orden ascendente',
      descending: 'Orden descendente',
    },
    // Card size
    cardSize: {
      small: 'Tarjetas pequeñas',
      medium: 'Tarjetas medianas',
      large: 'Tarjetas grandes',
      extraLarge: 'Tarjetas extragrandes',
    },
    pageView: {
      openCamWallPage: 'Abrir el muro de cámaras como página',
      cards: 'Tarjetas',
      camWall: 'Muro de cámaras',
    },
    camWall: {
      page: {
        tokenRejected:
          'Este enlace del muro de cámaras ya no es válido. Es posible que el token haya caducado o se haya revocado.',
        loadFailed: 'No se han podido cargar las impresoras.',
      },
      noPrinters: 'No hay impresoras que mostrar',
      noSignal: 'Sin señal',
      live: 'En vivo',
      snap: 'Foto',
      off: 'Inactivo',
      summary: '{{live}} en vivo, {{snap}} fotos, {{total}} en total',
      layer: 'Capa {{cur}}/{{total}}',
      timeLeft: 'quedan {{time}}',
      statusMode: {
        off: 'Apagado',
        compact: 'Compacto',
        full: 'Completo',
      },
      settings: {
        title: 'Ajustes del muro de cámaras',
        maxLive: 'Máx. transmisiones en vivo',
        maxLiveHint: 'Cuántos mosaicos transmiten en vivo a la vez. Los demás se actualizan como fotos.',
        snapshotInterval: 'Intervalo de fotos (segundos)',
        snapshotIntervalHint: 'Con qué frecuencia los mosaicos no en vivo obtienen una nueva foto.',
        statusOverlay: 'Superposición de estado',
        statusOverlayHint: 'Compacto: solo insignia de estado. Completo: + progreso, capa, tiempo restante.',
      },
    },
    // Controls
    hideOffline: 'Ocultar desconectadas',
    nextAvailable: 'Próxima disponible',
    powerOn: 'Encender',
    offlinePrintersWithPlugs: 'Impresoras desconectadas con enchufes inteligentes',
    noPrintersConfigured: 'Aún no hay impresoras configuradas',
    search: 'Buscar impresoras...',
    noSearchResults: 'Ninguna impresora coincide con su búsqueda o filtros',
    filter: {
      allStatuses: 'Todos los estados',
      allLocations: 'Todas las ubicaciones',
    },
    toolbar: {
      filters: 'Filtros',
      view: 'Vista',
      actions: 'Acciones',
    },
    // Printer card
    readyToPrint: 'Lista para imprimir',
    external: 'Externo',
    extL: 'Ext-L',
    extR: 'Ext-R',
    deleteArchives: 'Eliminar archivos de impresión',
    noLabel: 'Sin etiqueta',
    printPreview: 'Vista previa de impresión',
    width: 'Anchura',
    height: 'Altura',
    noObjectsFound: 'No se encontraron objetos',
    objectsLoadedOnPrintStart: 'Los objetos se cargan al iniciar una impresión',
    willBeSkipped: 'Se omitirá',
    name: 'Nombre',
    serialCannotBeChanged: 'El número de serie no se puede cambiar',
    locationHelp: 'Se usa para agrupar impresoras y filtrar trabajos en cola',
    // WiFi signal strength
    wifiSignal: {
      veryWeak: 'Muy débil',
      weak: 'Débil',
      fair: 'Aceptable',
      good: 'Buena',
      excellent: 'Excelente',
    },
    // Maintenance
    maintenanceUpToDate: 'Todo el mantenimiento al día - Haga clic para ver',
    maintenance: {
      title: 'En mantenimiento',
      subtitle: 'Esta impresora está en pausa — sin conexión, no elegible para la cola y sin enviar notificaciones.',
      pillLabel: 'Mantenimiento',
      exitButton: 'Salir de mantenimiento',
      menuEnter: 'Entrar en modo mantenimiento',
      menuExit: 'Salir del modo mantenimiento',
      toastEntered: '{{name}} ahora está en modo mantenimiento',
      toastExited: '{{name}} vuelve a estar en línea',
      confirmMidPrintTitle: '¿Entrar en modo mantenimiento durante una impresión?',
      confirmMidPrintMessage: '{{name}} está imprimiendo actualmente. Entrar en modo mantenimiento desconectará MQTT y detendrá el seguimiento del progreso y las notificaciones de finalización para este trabajo. ¿Continuar?',
      editFieldLabel: 'Modo mantenimiento',
      editFieldHelp: 'Cuando está activado, esta impresora se pausa de MQTT, despacho de cola y notificaciones — útil para reparaciones, instalaciones paralelas de Bambuddy o suspensión temporal.',
    },
    // Chamber light
    chamberLightOn: 'Encender luz de la cámara',
    chamberLightOff: 'Apagar luz de la cámara',
    // Files
    files: 'Archivos',
    browseFiles: 'Explorar archivos de la impresora',
    // Smart plug
    autoOffAfterPrint: 'Apagado automático tras la impresión',
    autoOffExecuted: 'Se ejecutó el apagado automático - encienda la impresora para restablecer',
    // HMS errors
    hmsErrors: 'Errores HMS',
    viewHmsErrors: 'Ver {{count}} error(es) HMS',
    // Actions
    resume: 'Reanudar',
    pause: 'Pausar',
    stop: 'Detener',
    camera: 'Cámara',
    skipObject: 'Omitir objeto',
    reconnect: 'Reconectar',
    forceRefresh: 'Forzar actualización',
    forceRefreshSuccess: 'Actualización solicitada',
    mqttDebug: 'Depuración MQTT',
    printerInformation: 'Información de la impresora',
    copyToClipboard: 'Copiar',
    copied: '¡Copiado!',
    state: 'Estado',
    wifiSignalLabel: 'Señal Wi-Fi',
    developerMode: 'Modo desarrollador',
    enabled: 'Activado',
    disabled: 'Desactivado',
    addedOn: 'Añadida',
    sdCard: 'Tarjeta SD',
    inserted: 'Insertada',
    notInserted: 'No insertada',
    totalPrintHours: 'Horas de impresión',
    activeNozzle: 'Activa: boquilla {{nozzle}}',
    nozzleRack: 'Soporte de boquillas',
    nozzleDocked: 'Acoplada',
    nozzleMounted: 'Montada',
    nozzleActive: 'Activa',
    nozzleIdle: 'Inactiva',
    nozzleDiameter: 'Diámetro',
    nozzleType: 'Tipo',
    nozzleStatus: 'Estado',
    nozzleFilament: 'Filamento',
    nozzleWear: 'Desgaste',
    nozzleMaxTemp: 'Temp. máx.',
    nozzleSerial: 'Serie',
    nozzleHardenedSteel: 'Acero endurecido',
    nozzleStainlessSteel: 'Acero inoxidable',
    nozzleTungstenCarbide: 'Carburo de tungsteno',
    nozzleFlow: 'Flujo',
    nozzleHighFlow: 'Flujo alto',
    nozzleStandardFlow: 'Estándar',
    // Firmware
    firmwareUpdate: 'Actualización de firmware',
    firmwareInstructions: 'En la pantalla táctil de la impresora, vaya a',
    firmwareNav: 'Navegue hasta',
    settings: 'Ajustes',
    firmware: 'Firmware',
    // Discovery
    discoverPrinters: 'Detectar impresoras',
    searching: 'Buscando...',
    manualEntry: 'Entrada manual',
    addFromCloud: 'Añadir desde la nube',
    // Toast messages
    toast: {
      printerDeleted: 'Impresora eliminada',
      missingSpoolAssignment: 'Impresión iniciada en {{printer}}. Falta la asignación de bobina para: {{slots}}',
      killSwitchTriggered: 'El interruptor de seguridad de facturación detuvo una impresión no autorizada en {{printer}}: {{filename}}',
      billingChargeFailed: 'La facturación de {{filename}} en {{printer}} ha fallado. Se conservó la reserva de presupuesto; revisa los registros del servidor.',
      assignmentVerified: 'Filamento cargado en la ranura {{slot}} ({{printer}})',
      assignmentVerifiedNoKprofile: 'Ranura {{slot}} en {{printer}} cargada, pero no se aplicó el perfil de calibración de flujo (perfil K)',
      assignmentNotConfirmed: 'No se pudo confirmar la asignación de la ranura {{slot}} en {{printer}}: revisa la ranura AMS',
      printerAdded: 'Impresora añadida',
      printerUpdated: 'Impresora actualizada',
      failedToDelete: 'Error al eliminar la impresora',
      failedToAdd: 'Error al añadir la impresora',
      connectionFailedNotAdded: 'No se pudo conectar con la impresora. Verifique la IP, el número de serie y el código de acceso, y confirme que el modo solo LAN está activado. La impresora no se añadió.',
      failedToUpdate: 'Error al actualizar la impresora',
      commandSent: 'Comando enviado',
      failedToSendCommand: 'Error al enviar el comando',
      turnedOn: '{{name}} encendida',
      failedToPowerOn: 'Error al encender {{name}}',
      scriptTriggered: 'Script ejecutado',
      printStopped: 'Impresión detenida',
      printPaused: 'Impresión en pausa',
      printResumed: 'Impresión reanudada',
      referenceDeleted: 'Referencia eliminada',
      detectionAreaSaved: 'Área de detección guardada',
      failedToRunScript: 'Error al ejecutar el script',
      failedToStopPrint: 'Error al detener la impresión',
      failedToPausePrint: 'Error al pausar la impresión',
      failedToResumePrint: 'Error al reanudar la impresión',
      failedToControlChamberLight: 'Error al controlar la luz de la cámara',
      failedToSetSpeed: 'Error al establecer la velocidad de impresión',
      failedToUpdateSetting: 'Error al actualizar el ajuste',
      failedToSkipObjects: 'Error al omitir objetos',
      failedToRereadRfid: 'Error al releer el RFID',
      failedToCheckPlate: 'Error al comprobar la cama',
      failedToUpdateLabel: 'Error al actualizar la etiqueta',
      failedToDeleteReference: 'Error al eliminar la referencia',
      failedToSaveDetectionArea: 'Error al guardar el área de detección',
      plateCheckEnabled: 'Comprobación de cama activada',
      plateCheckDisabled: 'Comprobación de cama desactivada',
      calibrationSaved: '¡Calibración guardada!',
      calibrationFailed: 'Error en la calibración',
      rfidRereadInitiated: 'Relectura de RFID iniciada',
      loadInitiated: 'Cargando filamento…',
      unloadInitiated: 'Descargando filamento…',
      failedToLoad: 'Error al cargar el filamento',
      failedToUnload: 'Error al descargar el filamento',
    },
    // Connection status
    connection: {
      connected: 'Conectada',
      offline: 'Desconectada',
    },
    plateStatus: {
      markCleared: 'Marcar cama como despejada',
      cleared: 'Cama despejada',
      notCleared: 'Cama no despejada',
      inUse: 'Cama en uso',
    },
    // Queue info
    queue: {
      inQueue: '{{count}} impresión en cola',
      inQueue_plural: '{{count}} impresiones en cola',
    },
    // Controls section
    controls: 'Controles',
    // RFID
    rfid: {
      reread: 'Releer RFID',
    },
    // AMS load/unload (#891)
    ams: {
      load: 'Cargar',
      unload: 'Descargar',
    },
    bedJog: {
      limitWarning: 'Los límites de recorrido no se aplican en los movimientos manuales: un error del firmware de Bambu ignora los finales de carrera por software en los comandos remotos. Muévelo con cuidado para evitar colisiones.',
      title: 'Controles de movimiento',
      bed: 'Cama',
      step: 'Paso (mm)',
      up: 'Subir la cama',
      down: 'Bajar la cama',
      disabledWhilePrinting: 'Desactivado durante la impresión',
      notHomedTitle: 'La impresora no está en posición de origen',
      notHomedMessage: 'La impresora no se ha llevado a su posición de origen desde la última impresión. Ejecute el autoorigen primero para un posicionamiento seguro (estaciona el cabezal y luego lleva X, Y y Z al origen), o mueva de todos modos — los finales de carrera por software se omitirán.',
      homeZ: 'Autoorigen',
      moveAnyway: 'Mover de todos modos',
      homingStarted: 'Llevando la impresora al origen…',
    },
    // Permissions
    permission: {
      noAdd: 'No tiene permiso para añadir impresoras',
      noEdit: 'No tiene permiso para editar impresoras',
      noDelete: 'No tiene permiso para eliminar impresoras',
      noControl: 'No tiene permiso para controlar impresoras',
      noFiles: 'No tiene permiso para acceder a los archivos de la impresora',
      noAmsRfid: 'No tiene permiso para releer el RFID del AMS',
      noSmartPlugControl: 'No tiene permiso para controlar enchufes inteligentes',
      noCamera: 'No tiene permiso para ver las cámaras',
    },
    // Add/Edit modal
    modal: {
      addTitle: 'Añadir impresora',
      editTitle: 'Editar impresora',
      myPrinter: 'Mi impresora',
      selectModel: 'Seleccionar modelo...',
      locationGroup: 'Ubicación / Grupo (opcional)',
      locationPlaceholder: 'p. ej., Taller, Oficina, Sótano',
      autoArchiveLabel: 'Archivar automáticamente las impresiones completadas',
      fromPrinterSettings: 'Desde los ajustes de la impresora',
      modelOptional: 'Modelo (opcional)',
      saveChanges: 'Guardar cambios',
    },
    // Skip objects
    skipObjects: {
      tooltip: 'Omitir objetos',
      onlyWhilePrinting: 'Omitir objetos (solo durante la impresión)',
      requiresMultiple: 'Omitir objetos (requiere 2 o más objetos)',
      title: 'Omitir objetos',
      matchIdsInfo: 'Haga coincidir los ID con la pantalla de su impresora',
      printerShowsIds: 'La pantalla de la impresora muestra los ID de los objetos en la cama de impresión',
      skipSelected: 'Omitir seleccionados',
      skipping: 'Omitiendo...',
      noObjectsSelected: 'No hay objetos seleccionados',
      selectObjectsToSkip: 'Seleccione los objetos que desea omitir de la impresión actual',
      skipped: 'omitidos',
      objectsSkipped: 'Objetos omitidos',
      activeCount: '{{count}} activos',
      waitForLayer: 'Espere a la capa 2 o superior para omitir objetos (capa actual {{layer}})',
      skip: 'Omitir',
      confirmTitle: '¿Omitir objeto?',
      confirmMessage: '¿Está seguro de que desea omitir "{{name}}"? Esto no se puede deshacer.',
      confirmAllMessage: 'Se han seleccionado todos los objetos restantes. Esto detendrá el trabajo de impresión. ¿Continuar?',
      confirmMultipleMessage: '¿Omitir {{count}} objetos seleccionados? Esto no se puede deshacer.',
    },
    // Confirm modals
    confirm: {
      deleteTitle: 'Eliminar impresora',
      deleteMessage: '¿Está seguro de que desea eliminar "{{name}}"? Esto eliminará todos los ajustes de conexión.',
      deleteArchivesNote: 'Todo el historial de impresión de esta impresora se eliminará permanentemente.',
      keepArchivesNote: 'El historial de impresión se conservará pero ya no estará asociado a esta impresora.',
      stopTitle: 'Detener impresión',
      stopMessage: '¿Está seguro de que desea detener la impresión actual en "{{name}}"? Esto cancelará el trabajo de impresión.',
      stopButton: 'Detener impresión',
      pauseTitle: 'Pausar impresión',
      pauseMessage: '¿Está seguro de que desea pausar la impresión actual en "{{name}}"?',
      pauseButton: 'Pausar impresión',
      resumeTitle: 'Reanudar impresión',
      resumeMessage: '¿Está seguro de que desea reanudar la impresión en "{{name}}"?',
      resumeButton: 'Reanudar impresión',
      powerOnTitle: 'Encender impresora',
      powerOnMessage: '¿Está seguro de que desea ENCENDER "{{name}}"?',
      powerOnButton: 'Encender',
      powerOffTitle: 'Apagar impresora',
      powerOffMessage: '¿Está seguro de que desea APAGAR "{{name}}"?',
      powerOffWarning: 'ADVERTENCIA: ¡"{{name}}" está imprimiendo actualmente! ¿Está seguro de que desea APAGARLA? Esto interrumpirá la impresión y podría dañar la impresora.',
      powerOffButton: 'Apagar',
      haToggleTitle: 'Conmutar "{{name}}"',
      haToggleMessage: '¿Conmutar la entidad de Home Assistant {{entity}}? Esto puede apagarla si está encendida actualmente.',
      haToggleWarning: 'ADVERTENCIA: ¡"{{name}}" está imprimiendo actualmente! Conmutar {{entity}} puede cortar la corriente e interrumpir la impresión. ¿Continuar?',
      haToggleButton: 'Conmutar',
    },
    // Bulk actions
    bulk: {
      select: 'Seleccionar',
      selectAll: 'Seleccionar todo',
      selectByLocation: 'Seleccionar por ubicación',
      selected: '{{count}} seleccionadas',
      actions: {
        stop: 'Detener',
        pause: 'Pausar',
        resume: 'Reanudar',
        clearPlate: 'Despejar cama',
        clearHMS: 'Borrar notificaciones',
      },
      confirm: {
        stopTitle: 'Detener {{count}} impresiones',
        stopMessage: 'Esto cancelará las impresiones activas en {{count}} impresora(s). Esta acción no se puede deshacer.',
        stopButton: 'Detener todo',
        pauseTitle: 'Pausar {{count}} impresiones',
        pauseMessage: 'Esto pausará las impresiones activas en {{count}} impresora(s).',
        pauseButton: 'Pausar todo',
        clearPlateTitle: 'Despejar {{count}} camas de impresión',
        clearPlateMessage: 'Esto despejará la cama de impresión en {{count}} impresora(s) y puede activar trabajos en cola.',
        clearPlateButton: 'Despejar todo',
      },
      success: '{{action}} completado en {{count}} impresora(s)',
      partial: '{{succeeded}} con éxito, {{failed}} con error',
      noneApplicable: 'Ninguna de las impresoras seleccionadas está en el estado adecuado para esta acción',
      selectByState: 'Seleccionar por estado',
    },
    // Discovery
    discovery: {
      title: 'Detectar impresoras',
      searching: 'Buscando...',
      scanning: 'Escaneando...',
      scanProgress: 'Escaneando... {{scanned}}/{{total}}',
      foundPrinters: 'Se encontraron {{count}} impresora(s)',
      noPrintersFound: 'No se encontraron impresoras',
      noPrintersFoundSubnet: 'No se encontraron impresoras en la subred especificada.',
      noPrintersFoundNetwork: 'No se encontraron impresoras en la red.',
      allConfigured: 'Todas las impresoras detectadas ya están configuradas.',
      alreadyAdded: 'Ya añadida',
      select: 'Seleccionar',
      manualEntry: 'Entrada manual',
      addFromCloud: 'Añadir desde la nube',
      subnetToScan: 'Subred a escanear',
      dockerNote: 'Docker detectado. Introduzca la subred de su impresora en notación CIDR. Requiere network_mode: host en docker-compose.yml.',
      scanSubnet: 'Escanear subred en busca de impresoras',
      discoverNetwork: 'Detectar impresoras en la red',
      scanningSubnet: 'Escaneando la subred en busca de impresoras Bambu...',
      scanningNetwork: 'Escaneando la red...',
      serialRequired: 'Número de serie obligatorio',
      unknown: 'Desconocido',
      failedToStart: 'Error al iniciar la detección',
      customSubnetOption: 'Subred personalizada...',
      customSubnetLabel: 'Subred personalizada (CIDR)',
      customSubnetNote: 'Usa una subred personalizada si tu impresora está en una red distinta a la de este servidor. Los puertos FTP (990) y MQTT (8883) deben ser accesibles a través del límite de enrutamiento.',
    },
    // AMS Drying
    drying: {
      start: 'Iniciar secado',
      stop: 'Detener secado',
      temperature: 'Temperatura',
      duration: 'Duración',
      hours: 'horas',
      timeRemaining: '{{time}} restante',
      active: 'Secando',
      targetSummary: '{{filament}} @ {{temp}}°C',
      notSupported: 'Secado no compatible',
      powerRequired: 'Conecte el adaptador de corriente del AMS para activar el secado',
      startingDrying: 'Iniciando el secado...',
      toastCommandSent: 'Comando de secado enviado',
      toastStopped: 'Secado detenido',
      toastNotStarted: 'La impresora aceptó el comando, pero el AMS no inició el secado. Comprueba que el adaptador de corriente del AMS esté conectado y que la impresora esté inactiva.',
      screenOnly: 'En esta impresora, el secado del AMS solo se puede controlar desde la pantalla de la propia impresora (limitación de Bambu)',
      stoppingDrying: 'Deteniendo el secado...',
      rotateTray: 'Girar la bobina durante el secado',
      rotateUnavailableReason: 'No disponible — un slot de este AMS está cargado hacia el cabezal. La bobina está bloqueada por el tubo de alimentación y no puede girar. Retira el filamento primero.',
    },
    amsBackup: {
      titleOn: 'AMS Filament Backup está ACTIVADO. Haz clic para desactivar.',
      titleOff: 'AMS Filament Backup está DESACTIVADO. Haz clic para activar.',
      titleUnknown: 'Estado de AMS Filament Backup no disponible en esta impresora.',
      toastEnabled: 'AMS Filament Backup activado',
      toastDisabled: 'AMS Filament Backup desactivado',
      modalTitle: 'AMS Filament Backup',
      modalHelp: 'Cuando el slot activo se agota, la impresora rota a slots con el mismo preset y color en este orden.',
      modalNoSlots: 'Sin filamento cargado.',
      modalNoPairs: 'No hay pares de respaldo — no hay dos slots que compartan preset y color.',
      extruderRightShort: 'D',
      extruderLeftShort: 'I',
      stateOn: 'Activado',
      stateOff: 'Desactivado',
      stateUnknown: 'No compatible con esta impresora',
    },
    activeJobSlot: {
      title: 'Este slot es el filamento {{n}} en la impresión activa',
      ariaLabel: 'Slot de impresión activa {{n}}',
    },
    expectedSlot: {
      title: 'La impresora está esperando filamento en este slot',
      ariaLabel: 'Slot de filamento esperado {{n}}',
      label: '{{ams}} · Slot {{slot}}',
      external: 'Bobina externa',
    },
    // Filaments section
    filaments: 'Filamentos',
    externalSpool: {
      hide: 'Ocultar bobina externa',
      show: 'Mostrar bobina externa',
    },
    // Camera
    openCameraOverlay: 'Abrir la cámara superpuesta',
    openCameraWindow: 'Abrir la cámara en una ventana nueva',
    // Firmware
    firmwareUpdateAvailable: 'Actualización de firmware disponible: {{current}} → {{latest}}',
    firmwareUpToDate: 'Firmware {{version}} — Actualizado',
    firmwareUpdateButton: 'Actualizar',
    // Plate detection
    plateDetection: {
      noPermission: 'No tiene permiso para actualizar impresoras',
      enabledClick: 'Comprobación de cama activada - Haga clic para desactivar',
      disabledClick: 'Comprobación de cama desactivada - Haga clic para activar',
      manageCalibration: 'Gestionar la calibración de detección de cama',
      calibrationRequired: 'Calibración necesaria',
      calibrationInstructions: 'Asegúrese de que la cama de impresión esté <strong>completamente vacía</strong> y luego haga clic en Calibrar.',
      calibrationDescription: 'La calibración captura una imagen de referencia de la cama vacía. Las comprobaciones futuras se compararán con esta referencia para detectar objetos.',
      calibrationTip: '<strong>Consejo:</strong> puede almacenar hasta 5 calibraciones para distintas camas. El sistema usa automáticamente la mejor coincidencia al comprobar.',
      plateEmpty: 'La cama parece estar vacía',
      objectsDetected: 'Objetos detectados en la cama',
      confidence: 'Confianza',
      difference: 'Diferencia',
      analysisPreview: 'Vista previa del análisis:',
      analysisLegend: 'Recuadro verde = área de detección, Superposición roja = diferencias respecto a la calibración',
      savedReferences: 'Referencias guardadas ({{count}}/{{max}})',
      deleteReference: 'Eliminar referencia',
      labelPlaceholder: 'Etiqueta...',
      clickToEdit: '{{label}} - Haga clic para editar',
      clickToAddLabel: 'Haga clic para añadir una etiqueta',
    },
    // Speed
    speed: {
      title: 'Velocidad de impresión',
      silent: 'Silencioso (50%)',
      standard: 'Estándar (100%)',
      sport: 'Sport (124%)',
      ludicrous: 'Ludicrous (166%)',
    },
    airduct: {
      title: 'Modo de conducto de aire',
      cooling: 'Refrigeración',
      heating: 'Calentamiento',
    },
    noSdCard: 'Sin SD',
    door: {
      open: 'Abierta',
      closed: 'Cerrada',
    },
    // Fans
    fans: {
      partCooling: 'Ventilador de refrigeración de piezas',
      auxiliary: 'Ventilador auxiliar',
      leftAuxiliary: 'Ventilador auxiliar izquierdo',
      exhaust: 'Extracción',
      chamber: 'Ventilador de la cámara',
    },
    // HMS errors
    clickToViewHmsErrors: 'Haga clic para ver los errores HMS',
    aiDetection: {
      safe: 'Seguro',
      warning: 'Advertencia',
      failure: 'Fallo',
      idle: 'Inactiva',
      tooltip: 'Detección de fallos por IA: {{status}} (puntuación {{score}}) - haga clic para más detalles',
      tooltipIdle: 'Detección de fallos por IA activada - la supervisión comienza con la próxima impresión - haga clic para más detalles',
      modalTitle: 'Detección de fallos por IA - {{name}}',
      currentStatus: 'Estado',
      score: 'Puntuación',
      framesAnalyzed: 'Fotogramas analizados',
      idleHint: 'Ahora mismo no se supervisa ninguna impresión. La supervisión comienza automáticamente con la próxima impresión.',
      lastError: 'Último error',
      openSettings: 'Abrir ajustes',
    },
    estimatedCompletion: 'Hora estimada de finalización',
    plateNumber: 'Cama {{number}}',
    slotOptions: 'Opciones de la ranura',
    // AMS hover popup
    amsPopup: {
      friendlyName: 'Nombre del AMS',
      friendlyNamePlaceholder: 'p. ej. Nombre descriptivo del AMS',
      serialNumber: 'Número de serie',
      firmwareVersion: 'Firmware',
      save: 'Guardar',
      clear: 'Borrar',
      noEditPermission: 'No tiene permiso para renombrar unidades AMS',
    },
    // Firmware modal
    firmwareModal: {
      title: 'Actualización de firmware',
      titleUpToDate: 'Información del firmware',
      currentVersion: 'Actual:',
      latestVersion: 'Más reciente:',
      releaseNotes: 'Notas de la versión',
      checkingPrereqs: 'Comprobando los requisitos previos...',
      sdCardReady: 'Tarjeta SD lista. Haga clic abajo para subir el firmware.',
      uploadedSuccess: '¡Firmware subido a la tarjeta SD!',
      applyInstructions: 'Para aplicar la actualización en su impresora:',
      step1: 'En la pantalla táctil de la impresora, vaya a <strong>Ajustes</strong>',
      step2: 'Navegue hasta <strong>Firmware</strong>',
      step3: 'Seleccione <strong>Actualizar desde tarjeta SD</strong>',
      step4: 'La actualización tardará entre 10 y 20 minutos',
      done: 'Hecho',
      starting: 'Iniciando...',
      uploadFirmware: 'Subir firmware',
      uploadFailed: 'Error al iniciar la subida: {{error}}',
      uploadedToast: '¡Firmware subido! Inicie la actualización desde la pantalla de la impresora.',
      availableVersions: 'Versiones disponibles',
      usable: 'Utilizable',
      unavailable: 'No disponible',
      installed: 'Instalada',
      newerBadge: 'más reciente',
      olderBadge: 'más antigua',
      currentBadge: 'actual',
    },
    accessCodePlaceholder: 'Dejar vacío para conservar el actual',
    // ROI editor
    roi: {
      title: 'Área de detección (ROI)',
      xStart: 'Inicio X',
      yStart: 'Inicio Y',
      width: 'Anchura',
      height: 'Altura',
      instruction: 'Ajuste el área de detección para centrarse en la cama de impresión. El recuadro verde de la vista previa muestra el área actual.',
    },
    developerModeWarning: 'El modo desarrollador LAN no está activado en: {{names}}. Es posible que algunas funciones no funcionen.',
    howToEnable: 'Cómo activarlo',
    incompatibleFile: 'Este archivo se laminó para {{slicedFor}}, pero esta impresora es una {{printerModel}}',
    dropNotPrintable: 'Solo se pueden imprimir archivos .gcode y .gcode.3mf',
    dropToPrint: 'Suelte para imprimir',
    cannotPrint: 'Impresora ocupada',
  },

  // Archives page
  archives: {
    title: 'Archivos de impresión',
    no3mfBanner: {
      title: 'Algunas impresiones recientes no se pudieron archivar con miniaturas',
      body: 'El slicer no dejó el archivo .gcode.3mf en la tarjeta SD de la impresora, así que Bambuddy no pudo recuperar ni la miniatura ni los metadatos del slicer. Esto suele deberse a que "Almacenar archivos enviados en almacenamiento externo" está desactivado en el slicer (pestaña Dispositivo de Bambu Studio / OrcaSlicer).',
      docsLink: 'Ver paso 4 de instalación',
      docsLinkInternalStorage: 'Por qué ocurre',
      titleInternalStorage: 'Algunas impresiones recientes se quedaron en el almacenamiento interno de la impresora',
      bodyInternalStorage: 'La impresora guardó el archivo laminado en su propio almacenamiento interno en lugar de la tarjeta, así que Bambuddy no tenía nada que leer por FTP. El firmware de la serie H2 y de la P2S hace esto, y «Guardar archivos enviados en almacenamiento externo» no lo cambia. Esas impresiones se siguen archivando con su nombre y sus tiempos, solo que sin miniatura ni metadatos del laminador.',
      titleNoExternalStorage: 'Algunas impresiones recientes no se pudieron archivar — no hay almacenamiento en la impresora',
      bodyNoExternalStorage: 'La impresora no detecta ninguna tarjeta ni memoria en su ranura, así que el archivo laminado no tenía dónde aterrizar y Bambuddy nada que leer. Inserte una y la próxima impresión se archivará por completo.',
      dismissLabel: 'Descartar este aviso',
    },
    searchPlaceholder: 'Buscar archivos...',
    filterByPrinter: 'Filtrar por impresora',
    filterByStatus: 'Filtrar por estado',
    sortBy: 'Ordenar por',
    sortNewest: 'Más recientes primero',
    sortOldest: 'Más antiguos primero',
    sortName: 'Nombre',
    sortDuration: 'Duración',
    sortLargest: 'Mayores primero',
    sortSmallest: 'Menores primero',
    sortSize: 'Tamaño',
    noArchives: 'No se encontraron archivos',
    noArchivesSearch: 'Ningún archivo coincide con su búsqueda',
    originalPrintNotVisible: 'La impresión original no está visible - pruebe a borrar los filtros',
    noArchivesYet: 'Aún no hay archivos',
    prints: 'impresiones',
    pagination: {
      showing: 'Mostrando',
      to: 'a',
      of: 'de',
      show: 'Mostrar',
      page: 'Página',
      all: 'Todos',
    },
    loadingArchives: 'Cargando archivos...',
    releaseToUpload: 'Suelte para subir',
    showAll: 'Mostrar todos',
    showFavoritesOnly: 'Mostrar solo favoritos',
    gridView: 'Vista de cuadrícula',
    listView: 'Vista de lista',
    calendarView: 'Vista de calendario',
    logView: 'Registro de impresión',
    manageTags: 'Gestionar etiquetas',
    showFailedPrints: 'Mostrar impresiones fallidas',
    hideFailedPrints: 'Ocultar impresiones fallidas',
    hideDuplicates: 'Ocultar duplicados',
    viewOriginalPrint: 'Haga clic para ver la impresión original (n.º {{id}})',
    printTime: 'Tiempo de impresión',
    filamentUsed: 'Filamento usado',
    cost: 'Coste',
    preview: 'Vista previa',
    deleteArchive: 'Eliminar archivo',
    deleteConfirm: '¿Está seguro de que desea eliminar este archivo?',
    favorite: 'Favorito',
    unfavorite: 'Quitar de favoritos',
    viewDetails: 'Ver detalles',
    status: {
      completed: 'Completada',
      failed: 'Fallida',
      stopped: 'Detenida',
    },
    toast: {
      source3mfAttached: '3MF de origen adjuntado: {{filename}}',
      failedUploadSource3mf: 'Error al subir el 3MF de origen',
      source3mfRemoved: '3MF de origen eliminado',
      failedRemoveSource3mf: 'Error al eliminar el 3MF de origen',
      f3dAttached: 'F3D adjuntado: {{filename}}',
      failedUploadF3d: 'Error al subir el F3D',
      f3dRemoved: 'F3D eliminado',
      failedRemoveF3d: 'Error al eliminar el F3D',
      timelapseAttached: 'Time-lapse adjuntado: {{filename}}',
      timelapseAlreadyAttached: 'El time-lapse ya está adjuntado',
      noMatchingTimelapse: 'No se encontró ningún time-lapse coincidente',
      failedScanTimelapse: 'Error al buscar el time-lapse',
      failedAttachTimelapse: 'Error al adjuntar el time-lapse',
      timelapseRemoved: 'Time-lapse eliminado',
      failedRemoveTimelapse: 'Error al eliminar el time-lapse',
      timelapseUploaded: 'Time-lapse subido: {{filename}}',
      failedUploadTimelapse: 'Error al subir el time-lapse',
      archiveDeleted: 'Archivo eliminado',
      failedDeleteArchive: 'Error al eliminar el archivo',
      addedToFavorites: 'Añadido a favoritos',
      removedFromFavorites: 'Quitado de favoritos',
      projectUpdated: 'Proyecto actualizado',
      failedUpdateProject: 'Error al actualizar el proyecto',
      linkCopied: 'Enlace copiado al portapapeles',
      failedCopyLink: 'Error al copiar el enlace',
      photoDeleted: 'Foto eliminada',
      failedDeletePhoto: 'Error al eliminar la foto',
      failedDeleteArchives: 'Error al eliminar los archivos',
      failedUpdateFavorites: 'Error al actualizar los favoritos',
      exportDownloaded: 'Exportación descargada',
      exportFailed: 'Error en la exportación',
    },
    menu: {
      print: 'Imprimir',
      openInBambuStudio: 'Abrir en el laminador',
      slice: 'Laminar',
      externalLink: 'Enlace externo',
      viewOnMakerWorld: 'Ver en MakerWorld',
      preview3d: 'Vista previa 3D',
      viewTimelapse: 'Ver time-lapse',
      scanForTimelapse: 'Buscar time-lapse',
      uploadTimelapse: 'Subir time-lapse',
      removeTimelapse: 'Eliminar time-lapse',
      downloadSource3mf: 'Descargar 3MF de origen',
      uploadSource3mf: 'Subir 3MF de origen',
      replaceSource3mf: 'Reemplazar 3MF de origen',
      removeSource3mf: 'Eliminar 3MF de origen',
      uploadF3d: 'Subir F3D',
      replaceF3d: 'Reemplazar F3D',
      downloadF3d: 'Descargar F3D',
      removeF3d: 'Eliminar F3D',
      download: 'Descargar',
      copyDownloadLink: 'Copiar enlace de descarga',
      qrCode: 'Código QR',
      viewPhotos: 'Ver fotos',
      viewPhotosCount: 'Ver fotos ({{count}})',
      projectPage: 'Página del proyecto',
      addToFavorites: 'Añadir a favoritos',
      removeFromFavorites: 'Quitar de favoritos',
      edit: 'Editar',
      printLog: 'Registro de impresión',
      goToProject: 'Ir al proyecto: {{name}}',
      addToProject: 'Añadir al proyecto',
      removeFromProject: 'Quitar del proyecto',
      loading: 'Cargando...',
      noProjectsAvailable: 'No hay proyectos disponibles',
      searchProjects: 'Buscar proyectos…',
      select: 'Seleccionar',
      deselect: 'Deseleccionar',
      delete: 'Eliminar',
    },
    permission: {
      noReprint: 'No tiene permiso para reimprimir este archivo',
      noAddToQueue: 'No tiene permiso para añadir a la cola',
      noUpdateArchives: 'No tiene permiso para actualizar archivos',
      noUploadFiles: 'No tiene permiso para subir archivos',
      noDownload: 'No tiene permiso para descargar archivos',
      noCopyLink: 'No tiene permiso para copiar enlaces de descarga',
      noDelete: 'No tiene permiso para eliminar este archivo',
      noEdit: 'No tiene permiso para editar esta entrada',
      noCreate: 'No tiene permiso para crear archivos',
    },
    platePicker: {
      title: 'Seleccione la cama para la vista previa',
      hint: 'Este archivo tiene varias camas. Elija una para abrirla en el visor de G-code.',
      plateLabel: 'Cama {{index}}',
      objectCount: '{{count}} objeto',
      objectCount_plural: '{{count}} objetos',
      noGcode: 'Este archivo no tiene G-code laminado para previsualizar. Ábralo en Bambu Studio para laminarlo primero.',
    },
    card: {
      previousPlate: 'Cama anterior',
      nextPlate: 'Cama siguiente',
      plateNumber: 'Cama {{index}}',
      moreOptions: 'Haga clic con el botón derecho para más opciones',
      addToFavorites: 'Añadir a favoritos',
      removeFromFavorites: 'Quitar de favoritos',
      cancelled: 'cancelada',
      failed: 'fallida',
      duplicate: 'duplicado',
      duplicateTitle: 'Este modelo ya se ha impreso anteriormente',
      openSource3mf: 'Abrir el 3MF de origen en Bambu Studio (clic derecho para más opciones)',
      downloadF3d: 'Descargar el archivo de diseño de Fusion 360',
      viewTimelapse: 'Ver time-lapse',
      viewPhoto: 'Ver 1 foto',
      viewPhotos: 'Ver {{count}} fotos',
      openFolder: 'Abrir carpeta: {{name}}',
      slicedFile: 'Archivo laminado - listo para imprimir',
      sourceFile: 'Solo archivo de origen - no hay mapeo de AMS disponible',
      gcode: 'GCODE',
      source: 'ORIGEN',
      project: 'Proyecto: {{name}}',
      runsBadge: '{{count}} impresiones',
      runsBadgeTitle: '{{count}} impresiones en total — {{successful}} con éxito, {{failed}} fallidas. Haga clic para ver el registro completo de impresión.',
      estimated: 'Estimado: {{time}}',
      actual: 'Real: {{time}}',
      accuracy: 'Precisión: {{percent}}%',
      filament: '{{weight}} g',
      layer: '{{count}} capa',
      layers: '{{count}} capas',
      object: '{{count}} objeto',
      objects: '{{count}} objetos',
      slicedFor: 'Laminado para {{model}}',
      uploadedBy: 'Subido por',
      noPermissionReprint: 'No tiene permiso para reimprimir',
      noFileForReprint: 'No hay archivo 3MF disponible — no se pudo descargar el archivo de la impresora cuando se registró la impresión',
      slicerAmsMapping: 'Mapeo de AMS guardado ({{printer}})',
      slicerAmsMappingTooltip: 'La elección de ranura AMS del slicer se guardó para {{printer}}. Los números de ranura solo significan algo en esa impresora, así que una reimpresión los reutiliza únicamente si vuelve a dirigirse a {{printer}}.',
      noPermissionEdit: 'No tiene permiso para editar archivos',
      noPermissionDelete: 'No tiene permiso para eliminar archivos',
      openInBambuStudio: 'Abrir en el laminador',
      openInBambuStudioToSlice: 'Abrir en el laminador para laminar',
      slice: 'Laminar',
      externalLink: 'Enlace externo',
      makerWorld: 'MakerWorld: {{designer}}',
      viewProject: 'Ver proyecto',
      noExternalLink: 'Sin enlace externo',
      preview3d: 'Vista previa 3D',
      download: 'Descargar',
      edit: 'Editar',
      delete: 'Eliminar',
    },
    runLog: {
      title: 'Registro de impresión',
      modalTitle: 'Registro de impresión — {{name}}',
      modalTitleFallback: 'este archivo',
      empty: 'Aún no se han registrado eventos de impresión para este archivo.',
      col: {
        date: 'Fecha',
        status: 'Estado',
        duration: 'Duración',
        filament: 'Filamento',
        cost: 'Coste',
      },
      status: {
        completed: 'Completada',
        failed: 'Fallida',
        cancelled: 'Cancelada',
        stopped: 'Detenida',
        skipped: 'Omitida',
        printing: 'Imprimiendo',
      },
    },
    modal: {
      deleteArchive: 'Eliminar archivo',
      deleteConfirm: '¿Está seguro de que desea eliminar "{{name}}"? Esta acción no se puede deshacer.',
      deleteButton: 'Eliminar',
      deletePurgeStats: 'Eliminar también esta impresión de las estadísticas rápidas (filamento, tiempo, coste, energía)',
      deleteQueueItemsWarning: 'También se eliminarán {{count}} elemento(s) de la cola vinculado(s) a este archivo.',
      deleteBlockedByPrinting: 'No se puede eliminar — {{count}} elemento(s) de la cola se están imprimiendo. Detén la impresión primero y vuelve a intentarlo.',
      removeSource3mf: 'Eliminar 3MF de origen',
      removeSource3mfConfirm: '¿Está seguro de que desea eliminar el archivo 3MF de origen de "{{name}}"? Esto eliminará el archivo de proyecto original del laminador.',
      removeButton: 'Eliminar',
      removeF3d: 'Eliminar F3D',
      removeF3dConfirm: '¿Está seguro de que desea eliminar el archivo de diseño de Fusion 360 de "{{name}}"?',
      removeTimelapse: 'Eliminar time-lapse',
      removeTimelapseConfirm: '¿Está seguro de que desea eliminar el vídeo time-lapse de "{{name}}"?',
      timelapse: '{{name}} - Time-lapse',
      selectTimelapse: 'Seleccionar time-lapse',
      selectTimelapseDesc: 'No se encontró ninguna coincidencia automática. Seleccione el time-lapse para esta impresión:',
      deleteArchives: 'Eliminar archivos',
      deleteArchivesConfirm: '¿Está seguro de que desea eliminar {{count}} archivo(s)? Esta acción no se puede deshacer.',
      deleteCount: 'Eliminar {{count}}',
    },
    page: {
      title: 'Archivos',
      printsCount: '{{filtered}} de {{total}} impresiones',
      dropFilesHere: 'Suelte aquí los archivos .3mf',
      releaseToUpload: 'Suelte para subir',
      only3mfSupported: 'Solo se admiten archivos .3mf',
      close: 'Cerrar',
      selected: '{{count}} seleccionados',
      selectAll: 'Seleccionar todo',
      tags: 'Etiquetas',
      project: 'Proyecto',
      favorite: 'Favorito',
      delete: 'Eliminar',
      toggledFavorites: 'Se cambió el estado de favorito de {{count}} archivo(s)',
      failedUpdateFavorites: 'Error al actualizar los favoritos',
      archivesDeleted: '{{count}} archivo(s) eliminado(s)',
      failedDeleteArchives: 'Error al eliminar los archivos',
      photoDeleted: 'Foto eliminada',
      failedDeletePhoto: 'Error al eliminar la foto',
    },
    list: {
      name: 'Nombre',
      printer: 'Impresora',
      date: 'Fecha',
      size: 'Tamaño',
      actions: 'Acciones',
      hasTimelapse: 'Tiene time-lapse',
    },
    log: {
      date: 'Fecha',
      printName: 'Nombre de la impresión',
      printer: 'Impresora',
      user: 'Usuario',
      status: 'Estado',
      duration: 'Duración',
      filament: 'Filamento',
      filamentUsed: 'Filamento usado',
      cost: 'Coste',
      energy: 'Energía',
      energyCost: 'Coste de energía',
      completedAt: 'Finalizado',
      columns: 'Columnas',
      sortBy: 'Ordenar por {{column}}',
      allPrinters: 'Todas las impresoras',
      allUsers: 'Todos los usuarios',
      allStatuses: 'Todos los estados',
      cancelled: 'Cancelada',
      skipped: 'Omitida',
      dateFrom: 'Desde',
      dateTo: 'Hasta',
      noEntries: 'No se encontraron entradas en el registro de impresión',
      showing: 'Mostrando {{count}} de {{total}} entradas',
      rowsPerPage: 'Filas',
      page: 'Página',
      prev: 'Anterior',
      next: 'Siguiente',
      clearLog: 'Borrar registro',
      clearLogTitle: 'Borrar el registro de impresión',
      clearLogConfirm: 'Todas las entradas del registro de impresión se eliminarán permanentemente. Los archivos y los elementos de la cola no se ven afectados. Esta acción no se puede deshacer. ¿Está seguro?',
      clearLogButton: 'Borrar todo',
      cleared: '{{count}} entradas del registro borradas',
      clearFailed: 'Error al borrar el registro de impresión',
      deleteEntryTitle: 'Eliminar entrada del registro de impresión',
      deleteEntryConfirm: 'Esta entrada se eliminará del registro y sus contribuciones de filamento, tiempo y costo desaparecerán de las estadísticas rápidas. El archivo correspondiente (si lo hay) no se ve afectado. Esta acción no se puede deshacer.',
      entryDeleted: 'Entrada del registro de impresión eliminada',
      entryDeleteFailed: 'Error al eliminar la entrada del registro de impresión',
      editEntryTitle: 'Editar entrada del registro de impresión',
      editEntryDescription: 'Clasifica esta impresión. El widget de Análisis de fallos agrupa por estos valores, por lo que las actualizaciones se reflejan en las estadísticas de inmediato.',
      entryUpdated: 'Entrada del registro actualizada',
      entryUpdateFailed: 'No se pudo actualizar la entrada del registro',
      statuses: {
        completed: 'Completada',
        failed: 'Fallida',
        stopped: 'Detenida',
        cancelled: 'Cancelada',
        skipped: 'Omitida',
      },
    },
  },

  // Sticky upload-progress toast (#1625 follow-up)
  dispatchToast: {
    untitled: 'Trabajo de impresión',
    startingPrints: 'Iniciando impresiones',
    progressSummary: '{{complete}}/{{total}} listas • Procesando: {{processing}}',
    expandDetails: 'Expandir detalles del despacho',
    collapseDetails: 'Contraer detalles del despacho',
    awaitingPrinter: 'Esperando a la impresora…',
    status: {
      processing: 'Procesando',
      completed: 'Completada',
      failed: 'Fallida',
    },
    failed: {
      generic: 'Despacho fallido',
      upload_failed: 'Fallo al subir a la impresora',
      start_command_failed: 'La impresora rechazó el comando de inicio',
    },
    dismiss: 'Cerrar',
  },

  // Pipeline Runs dashboard (#1425 PR C).
  pipelineRuns: {
    title: 'Ejecuciones de pipeline',
    loading: 'Cargando…',
    empty: 'Aún no hay ejecuciones de pipeline.',
    filter: {
      pipeline: 'Pipeline',
      status: 'Estado',
      target: 'Destino',
      all: 'Todas',
      allPipelines: 'Todas las pipelines',
      allStatus: 'Todos los estados',
      allTargets: 'Todos los destinos',
      clear: 'Limpiar filtros',
      noMatches: 'Ninguna ejecución coincide con los filtros actuales.',
    },
    totalCount_one: '{{n}} ejecución',
    totalCount_other: '{{n}} ejecuciones',
    copies: '{{n}} copias',
    failedCount: '{{n}} fallidas',
    copyN: 'Copia {{n}}',
    retryFailed: 'Reintentar fallidas',
    retryOf: 'reintento de #{{n}}',
    pagination: '{{start}}–{{end}} de {{total}}',
    toast: {
      cancelled: 'Ejecución cancelada',
      cancelFailed: 'Cancelación fallida',
      retryStarted: 'Reintento iniciado',
      retryFailed: 'Reintento fallido',
      cleared: '{{n}} ejecuciones eliminadas',
      clearFailed: 'Error al borrar',
    },
    clearLog: 'Borrar historial',
    clearConfirmTitle: '¿Borrar historial?',
    clearConfirmBody: '¿Eliminar todas las ejecuciones de pipeline completadas, fallidas, canceladas y con fallos parciales? Las ejecuciones en curso se conservan. Esto no se puede deshacer.',
    clearConfirmAction: 'Borrar',
    jobStatus: {
      pending: 'pendiente',
      awaiting_printer: 'esperando impresora',
      queued: 'en cola',
      printing: 'imprimiendo',
      completed: 'completada',
      failed: 'fallida',
      cancelled: 'cancelada',
    },
    cancelledByUser: 'Cancelado por el usuario',
  },

  // Queue page
  queue: {
    filamentShort: {
      rowBadge: 'Filamento insuficiente para la bobina asignada',
      rowTooltip: 'El planificador marco este elemento. Pulsa Reproducir para ver el deficit por slot y decidir si imprimir de todos modos.',
      confirmTitle: 'Filamento insuficiente',
      confirmIntro: 'La bobina asignada no puede cubrir al menos un slot. Imprimir de todos modos?',
      lineItem: 'Slot {{slot}}: necesita {{required}} g, quedan {{remaining}} g',
      unknown: 'desconocido',
      printAnyway: 'Imprimir de todos modos',
    },
    slicerAmsMapping: {
      rowBadge: 'Ranuras AMS guardadas para esta impresora',
      rowTooltip: 'Este archivo conserva las ranuras AMS exactas que eligió el slicer, guardadas para la impresora de este elemento. Una reimpresión en ella puede reutilizar esas bobinas en lugar de volver a emparejar por tipo y color.',
    },
    title: 'Cola de impresión',
    subtitle: 'Programe y gestione sus trabajos de impresión',
    // Print modal
    editQueueItem: 'Editar elemento de la cola',
    selectAllPlates: 'Seleccionar las {{count}} camas',
    deselectAll: 'Deseleccionar todo',
    printQueued: 'Impresión encolada',
    printQueuedWillStartWhenIdle: 'Comenzará cuando la impresora esté inactiva',
    itemsQueued: '{{count}} elementos encolados',
    sending: 'Enviando...',
    sendingProgress: 'Enviando {{current}}/{{total}}...',
    adding: 'Añadiendo...',
    addingProgress: 'Añadiendo {{current}}/{{total}}...',
    savingProgress: 'Guardando {{current}}/{{total}}...',
    clearQueue: 'Vaciar la cola',
    clearHistory: 'Borrar el historial',
    emptyQueue: 'La cola está vacía',
    position: 'Posición',
    scheduledTime: 'Hora programada',
    moveUp: 'Subir',
    moveDown: 'Bajar',
    startNow: 'Iniciar ahora',
    printingInProgress: 'Impresión en curso...',
    viewArchive: 'Ver archivo',
    viewInFileManager: 'Ver en el gestor de archivos',
    itemCount: '{{count}} elemento',
    itemCount_plural: '{{count}} elementos',
    dragToReorder: 'Arrastre para reordenar (solo elementos «lo antes posible»)',
    reorderHint: 'La posición solo afecta a los elementos «lo antes posible». Los elementos programados se ejecutan a su hora establecida.',
    sjf: {
      label: 'SJF',
      tooltip: 'Trabajo más corto primero — el planificador prioriza las impresiones más cortas',
    },
    addedBy: 'Añadido por {{name}}',
    nextInQueue: 'Siguiente en la cola',
    clearPlateSuccess: 'Cama despejada — lista para la próxima impresión',
    plateNumber: 'Cama {{index}}',
    // Batch / quantity
    quantity: 'Cantidad',
    quantityHint: 'Crea {{count}} elementos en la cola',
    plateQuantityLabel: 'Impresiones de {{plate}}',
    plateQuantityTotal_one: '{{count}} impresión en total',
    plateQuantityTotal_other: '{{count}} impresiones en total',
    batchOrders: {
      title: 'Pedidos por lotes',
      emptyTitle: 'Sin pedidos por lotes',
      emptyDescription: 'Pon en cola un archivo de varias bandejas, o más de una copia de una bandeja, y el pedido aparecerá aquí con su progreso.',
      filter: {
        active: 'Activos',
        completed: 'Completados',
        cancelled: 'Cancelados',
        all: 'Todos',
      },
      status: {
        active: 'Activo',
        completed: 'Completado',
        cancelled: 'Cancelado',
      },
      noTargets: 'Solo agrupación',
      noTargetsHint: 'Creado antes de que existieran las cantidades por bandeja, así que sigue las impresiones en cola en lugar de un objetivo.',
      dispatchRemaining: 'Encolar {{count}} restantes',
      dispatchPlate: 'Encolar restantes',
      dispatched: 'Impresiones restantes encoladas para {{name}}',
      plateProgress: '{{completed}} de {{target}} hechas',
      remaining: '{{count}} pendientes',
      failed: '{{count}} fallidas',
      printing: '{{count}} imprimiendo',
      wholeFile: 'Archivo completo',
      due: 'Vence {{date}}',
      costSoFar: '{{amount}} hasta ahora',
      costRemaining: '({{amount}} por gastar)',
    },
    activeBatches: 'Lotes activos',
    batchProgress: '{{completed}} de {{total}} completados',
    cancelBatch: 'Cancelar los restantes',
    batchCancelled: 'Elementos restantes del lote cancelados',
    cancelBatchConfirmTitle: 'Cancelar lote',
    cancelBatchConfirmMessage: '¿Cancelar todos los elementos pendientes restantes de este lote?',
    batch: {
      defaultName: 'Lote',
      label: '{{count}} elemento',
      label_plural: '{{count}} elementos',
      pendingCount: '{{count}} pendiente',
      pendingCount_plural: '{{count}} pendientes',
      expand: 'Expandir lote',
      collapse: 'Contraer lote',
      groupAsBatch: 'Agrupar como lote…',
      groupAsBatchDescription: 'Combinar los {{count}} elementos seleccionados en un único lote contraíble.',
      nameLabel: 'Nombre del lote',
      namePlaceholder: 'p. ej., Regalos del viernes',
      create: 'Crear lote',
      ungroup: 'Desagrupar',
      ungroupConfirmTitle: '¿Desagrupar lote?',
      ungroupConfirmMessage: 'Los elementos permanecerán en la cola pero ya no estarán agrupados.',
      dragGroup: 'Arrastrar grupo',
    },
    tabs: {
      batches: 'Lotes',
      queue: 'Cola',
      history: 'Historial',
      timeline: 'Cronología',
      pipelines: 'Procesos',
    },
    layout: {
      flatList: 'Lista',
      byPrinter: 'Por impresora',
      groupByPrinter: 'Agrupar por impresora',
    },
    history: {
      emptyTitle: 'Sin historial todavía',
      emptyDescription: 'Las impresiones completadas, canceladas y fallidas aparecerán aquí.',
      showMore: 'Mostrar más',
      showingCount: 'Mostrando {{shown}} de {{total}}',
    },
    dragGhost: {
      multiCount: '{{count}} elementos',
      batch: '{{name}} ({{count}} copia)',
      batch_plural: '{{name}} ({{count}} copias)',
    },
    // Sections
    sections: {
      currentlyPrinting: 'Imprimiendo actualmente',
      queued: 'En cola',
      history: 'Historial',
    },
    // Status
    status: {
      pending: 'Pendiente',
      waiting: 'En espera',
      printing: 'Imprimiendo',
      paused: 'En pausa',
      completed: 'Completada',
      failed: 'Fallida',
      skipped: 'Omitida',
      cancelled: 'Cancelada',
    },
    // Summary cards
    summary: {
      printing: 'Imprimiendo',
      queued: 'En cola',
      totalTime: 'Tiempo total de la cola',
      totalWeight: 'Peso total de la cola',
      history: 'Historial',
    },
    // Filters
    filter: {
      allPrinters: 'Todas las impresoras',
      unassigned: 'Sin asignar',
      allStatus: 'Todos los estados',
      allLocations: 'Todas las ubicaciones',
      any: 'Cualquiera',
    },
    // Sort
    sort: {
      byPosition: 'Ordenar por posición',
      byName: 'Ordenar por nombre',
      byPrinter: 'Ordenar por impresora',
      bySchedule: 'Ordenar por programación',
      byDate: 'Ordenar por fecha',
      ascendingOldest: 'Ascendente (más antiguos primero)',
      descendingNewest: 'Descendente (más recientes primero)',
    },
    // Badges
    badges: {
      staged: 'Preparado',
      requiresPrevious: 'Requiere éxito previo',
      autoPowerOff: 'Apagado automático',
      gcodeInjection: 'G-code',
    },
    // Empty state
    empty: {
      title: 'No hay impresiones programadas',
      description: 'Programe una impresión desde la página de Archivos con la opción «Programar» del menú contextual, o arrastre y suelte archivos para empezar.',
    },
    // Time
    time: {
      etaIfStartedNow: 'Hora de finalización si este trabajo comenzara ahora',
      asap: 'Lo antes posible',
      overdue: 'Atrasada',
      now: 'Ahora',
      lessThanMinute: 'En menos de un minuto',
      inMinutes: 'En {{count}} min',
      inHours: 'En {{count}} horas',
    },
    // Actions
    actions: {
      startPrint: 'Iniciar impresión',
      stopPrint: 'Detener impresión',
      requeue: 'Volver a encolar',
    },
    // Bulk edit
    bulkEdit: {
      title: 'Editar {{count}} elemento',
      title_plural: 'Editar {{count}} elementos',
      description: 'Solo se aplicarán los ajustes modificados a los elementos seleccionados.',
      printer: 'Impresora',
      noChange: '— Sin cambios —',
      queueOptions: 'Opciones de la cola',
      staged: 'Preparado (inicio manual)',
      autoPowerOff: 'Apagado automático tras la impresión',
      requirePrevious: 'Requerir éxito previo',
      printOptions: 'Opciones de impresión',
      bedLevelling: 'Nivelación de la cama',
      flowCalibration: 'Calibración del flujo',
      vibrationCalibration: 'Calibración de vibración',
      layerInspection: 'Inspección de la primera capa',
      timelapse: 'Time-lapse',
      useAms: 'Usar AMS',
      nozzleOffsetCali: 'Calibración del offset de boquillas',
      applyChanges: 'Aplicar cambios',
      selectAll: 'Seleccionar todo',
      deselectAll: 'Deseleccionar todo',
      selected: '{{count}} seleccionados',
      editSelected: 'Editar seleccionados',
      cancelSelected: 'Cancelar seleccionados',
    },
    // Confirmations
    confirm: {
      cancelTitle: 'Cancelar impresión programada',
      cancelMessage: '¿Está seguro de que desea cancelar "{{name}}"?',
      stopTitle: 'Detener impresión',
      stopMessage: '¿Está seguro de que desea detener la impresión actual "{{name}}"? Esto cancelará el trabajo de impresión en la impresora.',
      removeTitle: 'Quitar del historial',
      removeMessage: '¿Está seguro de que desea quitar "{{name}}" del historial de la cola?',
      clearHistoryTitle: 'Borrar el historial',
      clearHistoryMessage: '¿Está seguro de que desea quitar los {{count}} elemento(s) del historial?',
      cancelButton: 'Cancelar impresión',
      stopButton: 'Detener impresión',
      thisPrint: 'esta impresión',
      thisItem: 'este elemento',
    },
    // Toast messages
    toast: {
      cancelled: 'Elemento de la cola cancelado',
      cancelFailed: 'Error al cancelar el elemento',
      removed: 'Elemento de la cola eliminado',
      removeFailed: 'Error al eliminar el elemento',
      stopped: 'Impresión detenida',
      stopFailed: 'Error al detener la impresión',
      released: 'Impresión liberada a la cola',
      startFailed: 'Error al iniciar la impresión',
      reorderFailed: 'Error al reordenar la cola',
      historyCleared: 'Se borraron {{count}} elemento(s) del historial',
      clearHistoryFailed: 'Error al borrar el historial',
      updateFailed: 'Error al actualizar los elementos',
      bulkCancelled: 'Se cancelaron {{count}} elemento(s)',
      bulkCancelFailed: 'Error al cancelar los elementos',
      batchCreated: 'Lote "{{name}}" creado',
      batchCreateFailed: 'Error al crear el lote',
      batchUngrouped: '{{count}} elemento(s) desagrupado(s)',
      batchUngroupFailed: 'Error al desagrupar el lote',
      resumedAfterFailure: 'Cola reanudada — {{restored}} trabajo(s) restaurado(s) a pendientes',
      resumeAfterFailureFailed: 'Error al reanudar la cola',
    },
    resumeAfterFailure: {
      banner: '{{printer}} está bloqueado por un fallo de impresión previo — {{count}} trabajo(s) omitido(s)',
      bannerHint: 'Soluciona el problema de la impresora y reanuda para restaurar los trabajos omitidos y limpiar el bloqueo.',
      button: 'Reanudar tras fallo',
      confirmTitle: '¿Reanudar la cola tras el fallo?',
      confirmMessage: 'Restaurar {{count}} trabajo(s) omitido(s) en {{printer}} a pendientes y limpiar el bloqueo del trabajo anterior. Asegúrate de que la impresora esté lista antes de continuar.',
    },
    // Timeline view
    timeline: {
      listView: 'Lista',
      timelineView: 'Cronología',
      unassigned: 'Sin asignar',
      noData: 'No hay impresiones programadas para este día',
      nothingCommitted: 'No hay programaciones comprometidas en esta ventana. Los elementos preparados, los elementos en espera y los trabajos ASAP en impresoras inactivas no se muestran — establece una hora programada o libera un elemento preparado para verlo aquí.',
      allDoneBy: 'Todas las impresiones estimadas para las {{time}}',
      staged: 'Preparado',
      filterAll: 'Mostrar todo',
      filterPrinting: 'Imprimiendo',
      filterQueued: 'En cola',
      time: {
        anyMoment: 'en cualquier momento',
        minutesLeft: '{{minutes}} min restantes',
        hoursLeft: '{{hours}} h restantes',
        hoursMinutesLeft: '{{hours}} h {{minutes}} min restantes',
      },
      day: {
        previous: 'Día anterior',
        next: 'Día siguiente',
        today: 'Hoy',
      },
      window: {
        back12h: '12 horas atrás',
        forward12h: '12 horas adelante',
        now: 'Ahora',
      },
      printerColumnHeader: 'Impresora',
    },
    // Permissions
    permissions: {
      noStopPrint: 'No tiene permiso para detener impresiones',
      noStartPrint: 'No tiene permiso para iniciar impresiones',
      noEdit: 'No tiene permiso para editar este elemento de la cola',
      noCancel: 'No tiene permiso para cancelar este elemento de la cola',
      noRequeue: 'No tiene permiso para volver a encolar elementos',
      noRemove: 'No tiene permiso para eliminar este elemento de la cola',
      noClearHistory: 'No tiene permiso para borrar todo el historial',
      noEditItems: 'No tiene permiso para editar elementos de la cola',
      noCancelItems: 'No tiene permiso para cancelar elementos de la cola',
    },
  },

  // Statistics page
  stats: {
    title: 'Estadísticas',
    subtitle: 'Arrastre los widgets para reorganizarlos. Haga clic en el icono del ojo para ocultarlos.',
    overview: 'Resumen',
    totalPrints: 'Impresiones totales',
    successRate: 'Tasa de éxito',
    totalPrintTime: 'Tiempo total de impresión',
    printTime: 'Tiempo de impresión',
    totalFilament: 'Filamento total usado',
    filamentUsed: 'Filamento usado',
    filamentCost: 'Coste del filamento',
    totalCost: 'Coste total',
    energyUsed: 'Energía usada',
    energyCost: 'Coste de la energía',
    energyWarmingUpTooltip: 'El seguimiento de energía aún está recopilando instantáneas horarias. Los totales por rango de fechas serán precisos una vez que exista al menos una instantánea antes del rango seleccionado. Los valores iniciales pueden quedarse cortos.',
    averagePrintTime: 'Tiempo medio de impresión',
    printsPerDay: 'Impresiones por día',
    byPrinter: 'Por impresora',
    printsByPrinter: 'Impresiones por impresora',
    byMaterial: 'Por material',
    byMonth: 'Por mes',
    last7Days: 'Últimos 7 días',
    last30Days: 'Últimos 30 días',
    last90Days: 'Últimos 90 días',
    allTime: 'Todo el tiempo',
    // Widgets
    quickStats: 'Estadísticas rápidas',
    printActivity: 'Actividad de impresión',
    filamentTypes: 'Tipos de filamento',
    filamentTrends: 'Tendencias del filamento',
    failureAnalysis: 'Análisis de fallos',
    timeAccuracy: 'Precisión temporal',
    successful: 'Con éxito:',
    failed: 'Fallidas:',
    cancelled: 'Canceladas:',
    perfectEstimate: '100% = estimación perfecta',
    noTimeAccuracyData: 'Aún no hay datos de precisión temporal',
    noFilamentData: 'No hay datos de filamento disponibles',
    noPrinterData: 'No hay datos de impresora disponibles',
    noPrintData: 'No hay datos de impresión disponibles',
    noPrintDataLast30Days: 'No hay datos de impresión en los últimos 30 días',
    failureReasons: 'Motivos de fallo',
    topFailureReasons: 'Principales motivos de fallo',
    failedPrintsCount: '{{failed}} / {{total}} impresiones fallidas',
    lastWeekRate: 'Semana pasada: {{rate}}%',
    // Actions
    resetLayout: 'Restablecer disposición',
    recalculateCosts: 'Recalcular costes',
    recalculateCostsHint: 'Recalcular todos los costes de archivo usando los precios actuales del filamento',
    exportStats: 'Exportar estadísticas',
    exportAsCsv: 'Exportar como CSV',
    exportAsExcel: 'Exportar como Excel',
    hiddenCount: '{{count}} ocultos',
    // Toast
    exportDownloaded: 'Exportación descargada',
    exportFailed: 'Error en la exportación',
    layoutReset: 'Disposición restablecida',
    recalculatedCosts: 'Costes recalculados para {{count}} archivos',
    recalculateFailed: 'Error al recalcular los costes',
    // Loading
    loadingStats: 'Cargando estadísticas...',
    // Permissions
    noPermissionResetLayout: 'No tiene permiso para restablecer la disposición',
    noPermissionRecalculate: 'No tiene permiso para recalcular los costes',
    noPrintDataInRange: 'No hay datos de impresión en el rango seleccionado',
    periodFilament: 'Filamento del periodo',
    periodCost: 'Coste del periodo',
    avgPerPrint: 'Promedio por impresión',
    usageOverTime: 'Uso a lo largo del tiempo',
    energyOverTime: 'Energía a lo largo del tiempo',
    filamentByWeight: 'Peso',
    printDuration: 'Duración de la impresión',
    printerUtilization: 'Uso de la impresora',
    filamentSuccess: 'Éxito por material',
    printHabits: 'Hábitos de impresión',
    printTimeOfDay: 'Hora del día de impresión',
    colorDistribution: 'Distribución de colores',
    noColorData: 'No hay datos de color disponibles',
    records: 'Récords',
    longestPrint: 'Impresión más larga',
    heaviestPrint: 'Impresión más pesada',
    mostExpensivePrint: 'Más cara',
    busiestDay: 'Día más activo',
    successStreak: 'Racha de éxitos',
    streakPrint: 'impresión consecutiva',
    streakPrints: '{{count}} impresiones consecutivas',
    printerStats: 'Estadísticas de la impresora',
    hours: 'horas',
    avgPrints: 'Impresiones medias',
    noArchiveData: 'No hay datos de impresión disponibles',
    filamentByTime: 'Tiempo',
    avgWeight: 'Peso medio',
    avgTime: 'Tiempo medio',
    filamentByPrints: 'Impresiones',
    timeframe: {
      'today': 'Hoy',
      'this-week': 'Esta semana',
      'this-month': 'Este mes',
      'last-7': 'Últimos 7 días',
      'last-30': 'Últimos 30 días',
      'last-90': 'Últimos 90 días',
      'this-year': 'Este año',
      'all-time': 'Todo el tiempo',
      'custom': 'Rango personalizado',
      from: 'Desde',
      to: 'Hasta',
    },
    // User filter
    allUsers: 'Todos los usuarios',
    noUser: 'Sin usuario (sistema)',
    filterByUser: 'Filtrar por usuario',
  },

  // Maintenance page
  maintenance: {
    title: 'Mantenimiento',
    overview: 'Resumen',
    allOk: 'Todo el mantenimiento al día',
    dueCount: '{{count}} tarea pendiente',
    dueCount_plural: '{{count}} tareas pendientes',
    warningCount: '{{count}} advertencia',
    warningCount_plural: '{{count}} advertencias',
    totalPrintTime: 'Tiempo total de impresión',
    nextMaintenance: 'Próximo mantenimiento',
    nothingDue: 'Nada pendiente',
    tasks: 'Tareas',
    lastPerformed: 'Realizado por última vez',
    interval: 'Intervalo',
    hoursRemaining: '{{hours}} h restantes',
    hoursOverdue: '{{hours}} h de retraso',
    markDone: 'Marcar como hecho',
    performMaintenance: 'Realizar mantenimiento',
    history: 'Historial',
    noHistory: 'No hay historial de mantenimiento',
    editPrintHours: 'Editar horas de impresión',
    currentHours: 'Horas actuales',
    // Tabs
    statusTab: 'Estado',
    settingsTab: 'Ajustes',
    // Status
    overdueCount: '{{count}} atrasadas',
    dueSoonCount: '{{count}} pronto pendientes',
    dueSoon: 'Pronto pendiente',
    allGood: 'Todo correcto',
    overdueBy: 'Atrasado {{duration}}',
    dueIn: 'Pendiente en {{duration}}',
    timeLeft: '{{duration}} restante',
    // Duration formats
    day: '1 día',
    days: '{{count}} días',
    week: '1 semana',
    weeks: '{{count}} semanas',
    month: '1 mes',
    months: '{{count}} meses',
    year: '1 año',
    // Settings
    maintenanceTypes: 'Tipos de mantenimiento',
    maintenanceTypesDescription: 'Tipos del sistema y sus tareas de mantenimiento personalizadas',
    addCustomType: 'Añadir tipo personalizado',
    restoreDefaults: 'Restaurar tareas predeterminadas',
    intervalType: 'Tipo de intervalo',
    intervalValue: 'Intervalo ({{type}})',
    icon: 'Icono',
    documentationLink: 'Enlace de documentación (opcional)',
    assignToPrinters: 'Asignar a impresoras',
    selectAtLeastOnePrinter: 'Seleccione al menos una impresora',
    addType: 'Añadir tipo',
    custom: 'Personalizado',
    printHours: 'Horas de impresión',
    calendarDays: 'Días naturales',
    exampleName: 'p. ej., Reemplazar filtro HEPA',
    viewDocumentation: 'Ver documentación',
    timeBasedInterval: 'Intervalo basado en el tiempo',
    // Interval overrides
    intervalOverrides: 'Anulaciones de intervalo',
    intervalOverridesDescription: 'Personalizar intervalos para impresoras concretas',
    // Printer assignment
    assignedToPrinters: 'Asignado a las impresoras:',
    noPrintersAssigned: 'No hay impresoras asignadas',
    addPrinterShort: 'Añadir:',
    printersAssignedClick: '{{count}} impresora(s) asignada(s) - haga clic para gestionar',
    removeFromPrinter: 'Quitar de esta impresora',
    // Types
    types: {
      lubricateCarbonRods: 'Lubricar varillas de carbono',
      lubricateRails: 'Lubricar guías lineales',
      cleanNozzle: 'Limpiar boquilla/fusor',
      checkBelts: 'Comprobar la tensión de las correas',
      cleanBuildPlate: 'Limpiar la cama de impresión',
      checkExtruder: 'Comprobar los engranajes del extrusor',
      checkCooling: 'Comprobar los ventiladores de refrigeración',
      generalInspection: 'Inspección general',
      cleanCarbonRods: 'Limpiar varillas de carbono',
      lubricateSteelRods: 'Lubricar varillas de acero',
      cleanSteelRods: 'Limpiar varillas de acero',
      cleanLinearRails: 'Limpiar guías lineales',
      checkPtfeTube: 'Comprobar el tubo de PTFE',
      replaceHepaFilter: 'Reemplazar filtro HEPA',
      replaceCarbonFilter: 'Reemplazar filtro de carbono',
      lubricateLeftNozzleRail: 'Lubricar la guía de la boquilla izquierda',
    },
    // Toast
    maintenanceComplete: 'Mantenimiento marcado como completado',
    typeUpdated: 'Tipo de mantenimiento actualizado',
    typeDeleted: 'Tipo de mantenimiento eliminado',
    defaultsRestored: 'Se restauraron {{count}} tarea(s) predeterminada(s)',
    printHoursUpdated: 'Horas de impresión actualizadas',
    printerAssigned: 'Impresora asignada',
    printerRemoved: 'Impresora eliminada',
    // Confirmation
    deleteTypeConfirm: '¿Eliminar "{{name}}"?',
    deleteSystemTypeTitle: '¿Eliminar la tarea de mantenimiento predeterminada?',
    deleteSystemTypeMessage: '¿Está seguro de que desea eliminar la tarea de mantenimiento predeterminada "{{name}}"?',
    // Permissions
    noPermissionUpdate: 'No tiene permiso para actualizar elementos de mantenimiento',
    noPermissionPerform: 'No tiene permiso para realizar mantenimiento',
    noPermissionEditTypes: 'No tiene permiso para editar tipos de mantenimiento',
    noPermissionDeleteTypes: 'No tiene permiso para eliminar tipos de mantenimiento',
    noPermissionEditHours: 'No tiene permiso para editar las horas de impresión',
    noPermissionRemovePrinter: 'No tiene permiso para quitar asignaciones de impresoras',
    noPermissionAssignPrinter: 'No tiene permiso para asignar impresoras',
    noPermissionEditIntervals: 'No tiene permiso para editar intervalos',
    // Configure link
    configureSettings: 'Configurar tipos e intervalos de mantenimiento',
  },

  // Settings page
  settings: {
    sliceEngine: 'Motor de laminado',
    sliceEngineSidecar: 'Sidecar del servidor',
    sliceEngineSidecarHint: 'El laminado se ejecuta en el servidor, en el contenedor sidecar del laminador.',
    sliceEngineBrowser: 'En el navegador',
    sliceEngineBrowserHint: 'El laminado se ejecuta en este dispositivo, sin servidor.',
    title: 'Ajustes',
    general: 'General',
    // Tab names
    tabs: {
      general: 'General',
      smartPlugs: 'Enchufes inteligentes',
      notifications: 'Notificaciones',
      queue: 'Flujo de trabajo',
      queueDispatch: 'Cola y Despacho',
      queuePipelines: 'Pipelines',
      filament: 'Filamento',
      network: 'Red',
      apiKeys: 'Claves API',
      virtualPrinter: 'Impresora virtual',
      spoolbuddy: 'SpoolBuddy',
      failureDetection: 'Detección de fallos',
      users: 'Autenticación',
      backup: 'Copia de seguridad',
      emailAuth: 'Autenticación por correo',
      ldap: 'LDAP',
      twoFa: 'Autenticación de dos factores',
      oidc: 'SSO / OIDC',
      security: 'Seguridad',
    },
    spoolbuddy: {
      infoTitle: 'Dispositivos SpoolBuddy',
      infoBody: 'Los quioscos SpoolBuddy se registran automáticamente mediante una señal de latido. Anule el registro de un dispositivo aquí si ya no está en uso o si quedó un duplicado obsoleto tras el fallo de un demonio.',
      duplicatesTitle: '{{count}} dispositivos registrados',
      duplicatesBody: 'La interfaz del quiosco solo usa el primer dispositivo registrado. Si uno de estos es un duplicado obsoleto de un fallo, anule su registro — un dispositivo en línea se volverá a registrar en su próxima señal de latido.',
      empty: 'Aún no hay dispositivos SpoolBuddy registrados.',
      online: 'En línea',
      offline: 'Desconectado',
      unregister: 'Anular registro',
      unregisterSuccess: 'Registro del dispositivo anulado',
      unregisterError: 'Error al anular el registro del dispositivo',
      confirmTitle: '¿Anular el registro del dispositivo SpoolBuddy?',
      confirmBody: 'Esto eliminará "{{hostname}}" ({{deviceId}}) de la base de datos. Si el dispositivo está en línea, se volverá a registrar en su próxima señal de latido.',
      ipAddress: 'Dirección IP',
      firmware: 'Firmware',
      lastSeen: 'Visto por última vez',
      daemonUptime: 'Tiempo de actividad del demonio',
      systemUptime: 'Tiempo de actividad del sistema',
      never: 'nunca',
      nfc: 'NFC',
      scale: 'Báscula',
      cpuTemp: 'Temp. de CPU',
      cpuLoad: 'Carga de CPU',
      memory: 'Memoria',
      disk: 'Disco',
      // Device actions
      update: 'Actualizar',
      updateConfirmTitle: '¿Actualizar el demonio de Spoolbuddy?',
      updateConfirmBody: '¿Iniciar una actualización de software en "{{hostname}}"? El demonio se reiniciará una vez aplicada la actualización.',
      restartBrowser: 'Reiniciar navegador',
      restartBrowserConfirmTitle: '¿Reiniciar el navegador del quiosco?',
      restartBrowserConfirmBody: '¿Reiniciar el navegador del quiosco en "{{hostname}}"? La pantalla se quedará en negro brevemente.',
      restartDaemon: 'Reiniciar demonio',
      restartDaemonConfirmTitle: '¿Reiniciar el demonio de Spoolbuddy?',
      restartDaemonConfirmBody: '¿Reiniciar el demonio de Spoolbuddy en "{{hostname}}"? El dispositivo se desconectará durante unos segundos.',
      reboot: 'Reiniciar',
      rebootConfirmTitle: '¿Reiniciar el dispositivo?',
      rebootConfirmBody: '¿Reiniciar "{{hostname}}"? El dispositivo estará desconectado durante alrededor de un minuto.',
      shutdown: 'Apagar',
      shutdownConfirmTitle: '¿Apagar el dispositivo?',
      shutdownConfirmBody: '¿Apagar "{{hostname}}"? Necesitará acceso físico para volver a encenderlo.',
      commandConfirm: 'Confirmar',
      commandQueued: 'Comando encolado',
      commandError: 'Error al enviar el comando',
    },
    // LDAP settings
    ldap: {
      title: 'Autenticación LDAP',
      enabledDesc: 'La autenticación LDAP está activada',
      disabledDesc: 'La autenticación LDAP está desactivada',
      disabledHint: 'Configure y guarde los ajustes de LDAP a continuación y luego actívela.',
      enabled: 'Autenticación LDAP activada',
      disabled: 'Autenticación LDAP desactivada',
      feature1: 'Los usuarios pueden iniciar sesión con credenciales LDAP',
      feature2: 'La cuenta de administrador local permanece como alternativa',
      feature3: 'Los grupos LDAP se asignan a los grupos de Bambuddy al iniciar sesión',
      serverConfig: 'Configuración del servidor LDAP',
      serverUrl: 'URL del servidor',
      serverUrlHint: 'Use ldaps:// para SSL o ldap:// con StartTLS',
      security: 'Seguridad',
      securityHint: 'StartTLS actualiza una conexión sin cifrar a TLS. LDAPS usa TLS desde el principio.',
      bindDn: 'Bind DN (cuenta de servicio)',
      bindPassword: 'Contraseña de Bind',
      searchBase: 'DN base de búsqueda',
      userFilter: 'Filtro de búsqueda de usuarios',
      userFilterHint: '{username} se sustituye por el nombre de usuario de inicio de sesión. Use (uid={username}) para OpenLDAP.',
      advanced: 'Avanzado',
      autoProvision: 'Aprovisionar usuarios automáticamente',
      autoProvisionHint: 'Crear automáticamente una cuenta de Bambuddy en el primer inicio de sesión LDAP',
      defaultGroup: 'Grupo predeterminado',
      defaultGroupNone: '— Ninguno (sin alternativa) —',
      defaultGroupHint: 'Grupo alternativo asignado cuando un usuario LDAP se autentica pero no figura en ningún grupo LDAP asignado. Déjelo vacío para dejar a los usuarios sin asignar sin permisos.',
      groupMapping: 'Asignación de grupos (JSON)',
      groupMappingHint: 'Asignar los DN de grupos LDAP a los grupos de Bambuddy. Grupos disponibles: ',
      testConnection: 'Probar conexión',
      settingsSaved: 'Ajustes de LDAP guardados',
      errors: {
        serverRequired: 'La URL del servidor LDAP es obligatoria',
        searchBaseRequired: 'El DN base de búsqueda es obligatorio',
        enableAuthFirst: 'Active primero la autenticación',
        configureLdapFirst: 'Guarde primero los ajustes de LDAP',
      },
    },
    // Email settings
    email: {
      smtpSettings: 'Configuración SMTP',
      smtpHost: 'Servidor SMTP',
      smtpPort: 'Puerto SMTP',
      security: 'Seguridad',
      authentication: 'Autenticación',
      username: 'Nombre de usuario',
      password: 'Contraseña',
      fromEmail: 'Correo del remitente',
      fromName: 'Nombre del remitente',
      testConnection: 'Probar conexión SMTP',
      testRecipient: 'Correo del destinatario de prueba',
      sendTest: 'Enviar correo de prueba',
      sending: 'Enviando...',
      save: 'Guardar ajustes',
      saving: 'Guardando...',
      advancedAuth: 'Autenticación avanzada',
      advancedAuthEnabled: 'La autenticación avanzada está activada',
      advancedAuthEnabledDesc: 'Las funciones de gestión de usuarios basadas en correo están activas. Los nuevos usuarios recibirán contraseñas autogeneradas por correo, y los usuarios podrán restablecer sus contraseñas mediante la función de contraseña olvidada.',
      advancedAuthDisabled: 'La autenticación avanzada está desactivada',
      advancedAuthDisabledDesc: 'Active la autenticación avanzada para habilitar las funciones basadas en correo para la gestión de usuarios.',
      enable: 'Activar',
      disable: 'Desactivar',
      feature1: 'Las contraseñas se autogeneran y se envían por correo a los nuevos usuarios',
      feature2: 'Los usuarios pueden iniciar sesión con el nombre de usuario o el correo',
      feature3: 'La función de contraseña olvidada está disponible',
      feature4: 'Los administradores pueden restablecer las contraseñas de los usuarios por correo',
      // Error messages
      errors: {
        requiredFields: 'Rellene todos los campos obligatorios',
        usernameRequired: 'El nombre de usuario es obligatorio cuando la autenticación está activada',
        enterTestEmail: 'Introduzca una dirección de correo de prueba',
        smtpServerAndEmail: 'Rellene el servidor SMTP y el correo del remitente antes de probar',
        usernamePasswordRequired: 'El nombre de usuario y la contraseña son obligatorios cuando la autenticación está activada',
        configureSmtpFirst: 'Configure y pruebe primero los ajustes de SMTP',
        enableAuthFirst: 'Active primero la autenticación para usar las funciones basadas en correo.',
      },
      // Success messages
      success: {
        settingsSaved: 'Ajustes de SMTP guardados correctamente',
      },
      // Security options
      securityOptions: {
        starttls: 'STARTTLS (puerto 587)',
        ssl: 'SSL/TLS (puerto 465)',
        none: 'Ninguna (puerto 25)',
      },
      // Authentication options
      authOptions: {
        enabled: 'Activada',
        disabled: 'Desactivada',
      },
    },
    appearance: 'Apariencia',
    notifications: 'Notificaciones',
    smartPlugs: 'Enchufes inteligentes',
    spoolman: 'Spoolman',
    updates: 'Actualizaciones',
    language: 'Idioma',
    languageDescription: 'Seleccione su idioma preferido',
    theme: 'Tema',
    themeLight: 'Claro',
    themeDark: 'Oscuro',
    themeSystem: 'Sistema',
    defaultView: 'Vista predeterminada',
    defaultViewDescription: 'Página que se muestra al abrir la aplicación',
    checkForUpdates: 'Buscar actualizaciones',
    autoUpdate: 'Actualización automática',
    currentVersion: 'Versión actual',
    latestVersion: 'Versión más reciente',
    upToDate: 'Está actualizado',
    updateAvailable: 'Actualización disponible',
    // Notifications
    notificationLanguage: 'Idioma de las notificaciones',
    notificationLanguageDescription: 'Idioma de las notificaciones push',
    bedCooledThreshold: 'Umbral de cama enfriada',
    bedCooledThresholdDescription: 'Temperatura por debajo de la cual se considera que la cama se ha enfriado tras una impresión',
    userNotificationsEnabled: 'Notificaciones de usuario',
    userNotificationsEnabledDescription: 'Activa el menú de notificaciones de usuario y las notificaciones por correo de los eventos de los trabajos de impresión. Requiere autenticación avanzada.',
    userNotificationsDisabledHint: 'Active la autenticación avanzada para usar las notificaciones de usuario.',
    notificationProviders: 'Proveedores de notificaciones',
    addProvider: 'Añadir proveedor',
    editProvider: 'Editar proveedor',
    providerType: 'Tipo de proveedor',
    testNotification: 'Notificación de prueba',
    testSuccess: 'Notificación de prueba enviada correctamente',
    testFailed: 'Error al enviar la notificación de prueba',
    quietHours: 'Horas de silencio',
    quietHoursDescription: 'No molestar durante estas horas',
    quietHoursStart: 'Inicio',
    quietHoursEnd: 'Fin',
    events: {
      title: 'Eventos de notificación',
      printStart: 'Impresión iniciada',
      printComplete: 'Impresión completada',
      printFailed: 'Impresión fallida',
      printStopped: 'Impresión detenida',
      printProgress: 'Hitos de progreso',
      printProgressDescription: 'Notificar al 25%, 50% y 75%',
      printerOffline: 'Impresora desconectada',
      printerError: 'Error de la impresora',
      filamentLow: 'Filamento bajo',
      maintenanceDue: 'Mantenimiento pendiente',
      maintenanceDueDescription: 'Notificar cuando se necesite mantenimiento',
    },
    // Smart Plugs
    smartPlug: {
      title: 'Enchufes inteligentes',
      add: 'Añadir enchufe inteligente',
      edit: 'Editar enchufe inteligente',
      name: 'Nombre',
      ipAddress: 'Dirección IP',
      linkedPrinter: 'Impresora vinculada',
      autoOn: 'Encendido automático',
      autoOnDescription: 'Encender cuando comienza la impresión',
      autoOff: 'Apagado automático',
      autoOffDescription: 'Apagar tras completar la impresión',
      offDelay: 'Retardo de apagado',
      offDelayMinutes: 'Minutos tras la impresión',
      offDelayTemp: 'Cuando la boquilla está por debajo de la temperatura',
      currentState: 'Estado actual',
      turnOn: 'Encender',
      turnOff: 'Apagar',
    },
    // Filament Tracking Mode
    filamentTracking: 'Seguimiento del filamento',
    filamentTrackingDesc: 'Elija cómo realizar el seguimiento de sus bobinas de filamento. Puede usar el inventario integrado o conectar un servidor Spoolman externo.',
    autoAddUnknownRfid: 'Añadir automáticamente bobinas RFID desconocidas',
    autoAddUnknownRfidDesc: 'Crea automáticamente una entrada de inventario cuando se detecta una bobina con una etiqueta RFID desconocida. Desactive si registra manualmente las nuevas bobinas con antelación para evitar duplicados.',
    filamentChecks: 'Comprobaciones de filamento',
    disableFilamentWarnings: 'Desactivar las advertencias de filamento',
    disableFilamentWarningsDesc: 'No mostrar advertencias sobre filamento insuficiente al imprimir o encolar',
    preferLowestFilament: 'Preferir el filamento con menos restante',
    preferLowestFilamentDesc: 'Cuando varias bobinas coincidan, usar la que tenga menos filamento restante',
    preferLowestFilamentBackupNote: 'Solo surte efecto cuando AMS Filament Backup está activado en la impresora — de lo contrario, la impresora no puede cambiar a una segunda bobina cuando la elegida se acabe.',
    trackingModeBuiltIn: 'Inventario integrado',
    trackingModeBuiltInDesc: 'Incluye coincidencia automática por RFID y seguimiento del uso',
    trackingModeSpoolmanDesc: 'Servidor externo de gestión de filamento',
    builtInFeatureRfid: 'Detecta automáticamente las bobinas RFID de Bambu Lab en el AMS',
    builtInFeatureUsage: 'Realiza el seguimiento del consumo de filamento por impresión',
    builtInFeatureCatalog: 'Gestione bobinas, colores y perfiles de factor K',
    builtInFeatureThirdParty: 'Las bobinas de terceros se pueden asignar a bobinas del inventario',
    amsSyncButton: 'Sincronizar pesos desde el AMS',
    amsSyncTitle: 'Sincronizar los pesos de las bobinas desde el AMS',
    amsSyncMessage: 'Esto sobrescribirá todos los pesos de las bobinas del inventario con los valores actuales de % restante del AMS de las impresoras conectadas. Use esto para recuperarse de datos de peso dañados. Las impresoras deben estar en línea.',
    amsSyncing: 'Sincronizando...',
    amsSyncSuccess: '{{synced}} bobina(s) sincronizada(s), {{skipped}} omitida(s)',
    amsSyncError: 'Error al sincronizar los pesos desde el AMS',
    spoolmanAmsSyncButton: 'Sincronizar pesos de Spoolman desde el AMS',
    spoolmanAmsSyncTitle: 'Sincronizar los pesos de las bobinas de Spoolman desde el AMS',
    spoolmanAmsSyncMessage: 'Esto actualizará todos los pesos de las bobinas de Spoolman según los valores actuales de % restante del AMS de las impresoras conectadas. Las impresoras deben estar en línea.',
    spoolmanAmsSyncing: 'Sincronizando...',
    spoolmanAmsSyncSuccess: '{{synced}} bobina(s) sincronizada(s), {{skipped}} omitida(s)',
    spoolmanAmsSyncError: 'Error al sincronizar los pesos de Spoolman desde el AMS',
    spoolmanAmsSyncErrorUnreachable: 'Error al sincronizar los pesos de Spoolman (Spoolman inaccesible)',
    spoolmanAmsSyncErrorNotConfigured: 'Error al sincronizar los pesos de Spoolman (Spoolman no configurado)',
    spoolmanNotConfigured: 'Spoolman no configurado',
    // Spoolman filament catalog section in spool catalog settings
    spoolmanFilamentCatalogTitle: 'Catálogo de filamentos de Spoolman',
    spoolmanFilamentCatalogDesc: 'Nombres de filamentos y pesos de tara de su instancia de Spoolman. El nombre y el peso de la bobina se pueden editar aquí; el resto de las propiedades se gestionan directamente en Spoolman.',
    // Spoolman settings
    spoolmanUrl: 'URL de Spoolman',
    spoolmanUrlHint: 'URL de su servidor Spoolman (p. ej., http://localhost:7912)',
    spoolmanConnected: 'Conectado',
    spoolmanDisconnected: 'Desconectado',
    status: 'Estado',
    connect: 'Conectar',
    disconnect: 'Desconectar',
    howSyncWorks: 'Cómo funciona la sincronización',
    syncInfoRfidOnly: 'Solo se sincronizan las bobinas oficiales de Bambu Lab con RFID',
    syncInfoAutoCreate: 'Las bobinas nuevas se crean automáticamente en Spoolman en la primera sincronización',
    syncInfoThirdPartySkipped: 'Las bobinas que no son de Bambu Lab (de terceros, rellenadas) se omiten',
    linkingExistingSpools: 'Vincular bobinas existentes',
    linkingExistingSpoolsDesc: 'Para vincular bobinas existentes de Spoolman a su AMS, pase el cursor sobre una ranura del AMS y haga clic en "Vincular a Spoolman".',
    syncMode: 'Modo de sincronización',
    syncModeAuto: 'Automática',
    syncModeManual: 'Solo manual',
    syncModeAutoDesc: 'Los datos del AMS se sincronizan automáticamente cuando se detectan cambios',
    syncModeManualDesc: 'Sincronizar solo cuando se active manualmente',
    syncAmsData: 'Sincronizar datos del AMS',
    syncAmsDataDesc: 'Sincronizar manualmente los datos del AMS de la impresora con Spoolman',
    allPrinters: 'Todas las impresoras',
    // Default printer
    noDefaultPrinter: 'Sin predeterminada (preguntar cada vez)',
    // Sidebar
    sidebarOrder: 'Orden de la barra lateral',
    // Camera
    saveThumbnails: 'Guardar miniaturas',
    captureFinishPhoto: 'Capturar foto de finalización',
    noPrintersConfigured: 'No hay impresoras configuradas',
    // Archive settings
    archiveMode: {
      always: 'Crear siempre una entrada de archivo',
      never: 'No crear nunca una entrada de archivo',
      ask: 'Preguntar cada vez',
    },
    // Updates
    checkForUpdatesLabel: 'Buscar actualizaciones',
    checkPrinterFirmware: 'Comprobar el firmware de la impresora',
    includeBetaUpdates: 'Incluir versiones beta',
    includeBetaUpdatesDesc: 'Notificar sobre versiones beta y preliminares al buscar actualizaciones',
    localLogin: {
      disable: 'Deshabilitar el inicio de sesión local con usuario/contraseña',
      disableHint: 'Cuando se habilita, solo los proveedores SSO pueden iniciar sesión. LDAP no se ve afectado. Defina BAMBUDDY_LOCAL_LOGIN=true en el servidor para mantener una vía de recuperación.',
    },
    // Queue
    enableRetry: 'Activar reintentos',
    // Home Assistant
    homeAssistantDescription: 'Controlar enchufes inteligentes mediante Home Assistant',
    environmentManagedLabel: '(Gestionado por el entorno)',
    autoEnabledViaEnv: 'Activado automáticamente mediante variables de entorno',
    urlFromEnvReadOnly: 'Valor establecido por la variable de entorno HA_URL (solo lectura)',
    tokenFromEnvReadOnly: 'Valor establecido por la variable de entorno HA_TOKEN (solo lectura)',
    // MQTT
    mqttConnectedTo: 'Conectado a',
    // Prometheus
    prometheusDescription: 'Exponer los datos de la impresora en formato Prometheus',
    // Smart plugs empty state
    noSmartPlugsTitle: 'No hay enchufes inteligentes configurados',
    noSmartPlugsDescription: 'Añada un enchufe inteligente basado en Tasmota para realizar el seguimiento del consumo de energía y automatizar el control de la alimentación.',
    // Notifications empty state
    noProvidersTitle: 'No hay proveedores configurados',
    noProvidersDescription: 'Añada un proveedor para recibir alertas.',
    noTemplatesAvailable: 'No hay plantillas disponibles. Reinicie el backend para cargar las plantillas predeterminadas.',
    // API permissions
    apiPermissionView: 'Ver el estado de la impresora y la cola',
    apiPermissionEdit: 'Añadir y quitar elementos de la cola de impresión',
    // API keys
    apiKeysEmptyTitle: 'No hay claves API',
    apiKeysEmptyDescription: 'Cree una clave API para integrarse con servicios externos.',
    // Users
    noUsersFound: 'No se encontraron usuarios',
    noGroupsFound: 'No se encontraron grupos',
    noGroupsAvailable: 'No hay grupos disponibles',
    passwordsDoNotMatch: 'Las contraseñas no coinciden',
    systemGroupWarning: 'Los nombres de los grupos del sistema no se pueden cambiar',
    // Auth disabled
    authDisabledTitle: 'La autenticación está desactivada',
    authDisabledFeature1: 'Exigir inicio de sesión para acceder al sistema',
    authDisabledFeature2: 'Crear varios usuarios con permisos basados en grupos',
    authDisabledFeature3: 'Controlar el acceso con más de 50 permisos granulares',
    // User deletion
    userHasCreated: 'Este usuario ha creado:',
    userItemsQuestion: '¿Qué desea hacer con estos elementos?',
    deleteUserConfirm: '¿Está seguro de que desea eliminar este usuario?',
    actionCannotBeUndone: 'Esta acción no se puede deshacer.',
    // Smart plugs
    addFirstSmartPlug: 'Añada su primer enchufe inteligente',
    // Notifications
    providers: 'Proveedores',
    log: 'Registro',
    testAll: 'Probar todo',
    testResults: 'Resultados de la prueba',
    testPassedCount: '{{count}} superadas',
    testFailedCount: '{{count}} fallidas',
    messageTemplates: 'Plantillas de mensajes',
    messageTemplatesDescription: 'Personalice los mensajes de notificación para cada evento.',
    // API Keys section
    apiKeys: 'Claves API',
    apiKeysDescription: 'Cree claves API para integraciones externas y webhooks.',
    createKey: 'Crear clave',
    apiKeyCreated: 'Clave API creada correctamente',
    apiKeyCopyWarning: '¡Copie esta clave ahora; no se volverá a mostrar!',
    useInApiBrowser: 'Usar en el explorador de API',
    apiKeyQrButton: 'Código QR',
    apiKeyQrTitle: 'Escanea para configurar',
    apiKeyQrCaption: 'Escanea con tu app móvil para añadir este servidor y la clave API.',
    apiKeyQrWarning: 'Contiene tu clave API secreta: no la compartas ni hagas capturas donde otros puedan verla.',
    createNewApiKey: 'Crear nueva clave API',
    keyName: 'Nombre de la clave',
    keyNamePlaceholder: 'p. ej., Home Assistant, OctoPrint',
    readStatus: 'Leer estado',
    readStatusDescription: 'Ver el estado de la impresora, la cola, la biblioteca, los archivos de impresión y las pipelines del cortador',
    manageQueue: 'Gestionar la cola',
    manageQueueDescription: 'Añadir y quitar elementos de la cola de impresión. Junto con "Gestionar biblioteca", ejecutar pipelines del cortador',
    controlPrinter: 'Controlar la impresora',
    controlPrinterDescription: 'Pausar, reanudar y detener impresiones',
    manageLibrary: 'Gestionar biblioteca',
    manageLibraryDescription: 'Subir, renombrar, eliminar y cortar archivos de la biblioteca; importar modelos desde MakerWorld. Junto con "Gestionar la cola", ejecutar pipelines del cortador',
    manageInventory: 'Gestionar inventario',
    manageInventoryDescription: 'Crear, actualizar y eliminar bobinas y registros de inventario. Necesario para los quioscos SpoolBuddy (escaneo NFC, lecturas de balanza, comandos del sistema del quiosco).',
    manageMaintenance: 'Gestionar mantenimiento',
    manageMaintenanceDescription: 'Registrar mantenimientos completados, restablecer contadores, editar intervalos y gestionar el catálogo de tipos de mantenimiento. Ideal para automatizaciones de Home Assistant que registran "limpié la boquilla" sin conceder un control más amplio de la impresora.',
    manageArchives: 'Gestionar archivos de impresión',
    manageArchivesDescription: 'Editar y eliminar archivos de impresión, incluida la eliminación de impresiones antiguas y de su contribución a las estadísticas. Ideal para automatizaciones que depuran el historial de impresión.',
    manageProjects: 'Gestionar proyectos',
    manageProjectsDescription: 'Crear, actualizar y eliminar proyectos, y añadirles archivos de impresión. Ideal para automatizaciones que organizan las impresiones en proyectos.',
    maintenanceBadge: 'Mantenimiento',
    archivesBadge: 'Archivos',
    projectsBadge: 'Proyectos',
    libraryBadge: 'Biblioteca',
    inventoryBadge: 'Inventario',
    cloudAccess: 'Permitir el acceso a la nube',
    cloudAccessDescription: 'Leer los preajustes y filamentos de Bambu Cloud en su nombre. Requiere que haya iniciado sesión en Bambu Cloud.',
    cloudBadge: 'Nube',
    updateEnergyCost: 'Actualizar el precio de la electricidad',
    updateEnergyCostDescription: 'Permite que esta clave envíe por POST un nuevo precio de electricidad por kWh a /settings/electricity-price. Útil para las automatizaciones de tarifa dinámica de Home Assistant (Tibber, Octopus, etc.). Este es el único campo de ajustes que se puede escribir mediante una clave API.',
    energyCostBadge: 'Energía',
    legacyKey: 'Heredada',
    legacyKeyTooltip: 'Creada antes de la propiedad por usuario; vuelva a crearla para usar el acceso a la nube',
    unnamedKey: 'Clave sin nombre',
    lastUsed: 'Usada por última vez',
    read: 'Lectura',
    control: 'Control',
    createFirstKey: 'Cree su primera clave',
    webhookEndpoints: 'Puntos de conexión de webhook',
    webhookApiKeyHint: 'Use su clave API en la cabecera X-API-Key.',
    webhook: {
      getAllStatus: 'Obtener el estado de todas las impresoras',
      getSpecificStatus: 'Obtener el estado de una impresora concreta',
      addToQueue: 'Añadir a la cola de impresión',
      pausePrint: 'Pausar impresión',
      resumePrint: 'Reanudar impresión',
      stopPrint: 'Detener impresión',
    },
    apiBrowser: 'Explorador de API',
    apiBrowserDescription: 'Explore y pruebe todos los puntos de conexión de API disponibles.',
    apiKeyForTesting: 'Clave API para pruebas',
    apiKeyPlaceholder: 'Pegue aquí su clave API para probar los puntos de conexión autenticados...',
    apiKeyHint: 'Esta clave se enviará como cabecera X-API-Key en las solicitudes.',
    deleteApiKeyTitle: 'Eliminar clave API',
    deleteApiKeyMessage: '¿Está seguro de que desea eliminar esta clave API? Cualquier integración que use esta clave dejará de funcionar.',
    deleteKey: 'Eliminar clave',
    // Filament tab
    amsDisplayThresholds: 'Umbrales de visualización del AMS',
    amsThresholdsDescription: 'Configure los umbrales de color para los indicadores de humedad y temperatura del AMS.',
    humidity: 'Humedad',
    goodGreen: 'Buena (verde)',
    fairOrange: 'Aceptable (naranja)',
    aboveFairBad: 'Por encima del umbral aceptable se muestra en rojo (mala)',
    fairAlsoDryingThreshold: 'Este umbral también se usa para activar el secado automático cuando está habilitado',
    fairBelowDryingFloor: 'El secado automático no puede alcanzar este valor: un AMS indica {{floor}} % o más mientras calienta, por lo que cada ciclo terminaría y se reiniciaría al instante. Súbelo por encima del {{floor}} % si usas el secado automático.',
    temperature: 'Temperatura',
    goodBlue: 'Buena (azul)',
    aboveFairHot: 'Por encima del umbral aceptable se muestra en rojo (caliente)',
    historyRetention: 'Retención del historial',
    keepSensorHistory: 'Conservar el historial del sensor durante',
    historyRetentionDescription: 'Los datos de humedad y temperatura más antiguos se eliminarán automáticamente',
    defaultPrintOptions: 'Opciones de impresión predeterminadas',
    defaultPrintOptionsDescription: 'Establezca valores predeterminados para las opciones de impresión al iniciar nuevas impresiones. Se pueden anular por impresión en el diálogo de impresión.',
    defaultBedLevelling: 'Nivelación de la cama',
    defaultBedLevellingDesc: 'Nivelar automáticamente la cama antes de imprimir',
    defaultFlowCali: 'Calibración del flujo',
    defaultFlowCaliDesc: 'Calibrar el flujo de extrusión',
    defaultVibrationCali: 'Calibración de vibración',
    defaultVibrationCaliDesc: 'Reducir los artefactos de resonancia',
    defaultLayerInspect: 'Inspección de la primera capa',
    defaultLayerInspectDesc: 'Inspección de la primera capa por IA',
    defaultTimelapse: 'Time-lapse',
    defaultTimelapseDesc: 'Grabar vídeo time-lapse',
    defaultNozzleOffsetCali: 'Calibración del offset de boquillas',
    defaultNozzleOffsetCaliDesc: 'Calibrar los desplazamientos entre boquillas',
    tempFanPresetsTitle: 'Predefinidos de temperatura y ventilador',
    tempFanPresetsDescription: 'Personalice los valores de selección rápida en las ventanas emergentes de temperatura y velocidad del ventilador de la tarjeta de impresora. El botón Apagar siempre se muestra.',
    tempFanPresetsNozzle: 'Temperatura de la boquilla',
    tempFanPresetsBed: 'Temperatura de la cama',
    tempFanPresetsChamber: 'Temperatura de la cámara',
    tempFanPresetsFan: 'Velocidad del ventilador',
    tempFanPresetsReset: 'Restablecer valores predeterminados',
    concurrentUploadsTitle: 'Subidas simultáneas',
    concurrentUploadsDescription: 'Cuántas impresoras pueden recibir archivos de la cola al mismo tiempo. Las impresoras reciben los archivos lentamente (una impresión grande puede tardar varios minutos) y cada una espera su turno, así que en una granja grande subir este valor evita que la última impresora de un lote tenga que esperar a que terminen todas las transferencias anteriores. Bájalo si tu red o el host de Bambuddy tienen problemas con transferencias en paralelo.',
    concurrentUploadsLabel: 'Impresoras atendidas a la vez',
    concurrentUploadsHelp: '1 envía a una impresora cada vez (el comportamiento anterior). El valor predeterminado es 4.',
    staggeredStart: 'Inicio escalonado',
    staggeredStartDescription: 'Tamaño de grupo e intervalo predeterminados al escalonar los inicios de lotes en varias impresoras. Se pueden anular por lote en la ventana de impresión.',
    preheatTitle: 'Precalentamiento y Heat Soak',
    preheatDescription: 'Calienta la cama (y la cámara, si es compatible) y mantiene la temperatura antes de iniciar cada impresión en cola. Útil para filamentos técnicos (PA, ABS) en impresoras sin calentador activo de cámara — la cama calienta la cámara por radiación mientras corre el temporizador de soak. La temperatura objetivo de la cama se lee del archivo de impresión; el comportamiento de la cámara depende del modelo de impresora.',
    preheatEnabled: 'Activar precalentamiento y soak',
    preheatEnabledDesc: 'Si está desactivado, las impresiones en cola se inician de inmediato. Cada elemento puede anular por impresión.',
    preheatFilamentTargetsLabel: 'Objetivo de cámara por filamento (°C)',
    preheatFilamentTargetsHint: 'Bambuddy elige el objetivo más alto entre los slots AMS cargados; las impresiones solo PLA derivan 0 y omiten la fase de cámara automáticamente.',
    preheatFilamentTargetsReset: 'Restablecer valores predeterminados',
    preheatFilamentTargetsDefaultRow: 'Otros / sin asignar',
    preheatMaxWait: 'Espera máx. (segundos)',
    preheatMaxWaitHelp: 'Tope de la fase de calentamiento de cámara antes de pasar al soak.',
    preheatSoak: 'Soak (segundos)',
    preheatSoakHelp: 'Tiempo de retención tras alcanzar el objetivo o caducar la espera máx.',
    preheatHardwareTitle: 'Comportamiento por impresora:',
    preheatHardwareDetail: 'H2C/H2D/H2D Pro/H2S/X2D/X1E calientan activamente la cámara vía M141. X1C/P2S leen la temperatura pero solo calientan la cámara por radiación de la cama. P1S/P1P/A1/A1 Mini no tienen sensor de cámara — solo aplica el temporizador de soak.',
    preheatPerItemDesc: 'Calienta la cama y la cámara antes de iniciar esta impresión. Por defecto usa el interruptor global de Ajustes → Workflow.',
    preheatOverride_inherit: 'Heredar',
    preheatOverride_on: 'Activado',
    preheatOverride_off: 'Desactivado',
    calibrationMode_off: 'Desactivado',
    calibrationMode_on: 'Activado',
    calibrationMode_auto: 'Automático',
    preheatTargetOverride: 'Sobrescribir objetivo de cámara (°C, vacío = por filamento)',
    plateClear: 'Confirmación de cama despejada',
    requirePlateClear: 'Requerir confirmación de cama despejada',
    requirePlateClearDescription: 'Cuando está activado, el planificador espera la confirmación de cama despejada por impresora antes de iniciar impresiones en cola en impresoras con trabajos finalizados. Desactivar esto también oculta la insignia de estado de la cama y el botón "Marcar cama como despejada" en las tarjetas de impresora.',
    keepBedWarm: 'Mantener la cama caliente entre impresiones',
    keepBedWarmDesc: 'Mientras se espera la confirmación de limpieza de placa, mantiene la cama a la temperatura de mantenimiento indicada abajo para que la cámara siga caliente — o a la temperatura de cama de la siguiente impresión si es mayor. Solo aplica cuando la siguiente impresión requiere calefacción de cámara (ASA, ABS, PA, PC, etc.). Requiere la confirmación de limpieza de placa activada.',
    keepWarmBedTemp: 'Temperatura de la cama en mantenimiento (°C)',
    keepWarmBedTempHelp: 'Se usa siempre que la cama sirve para calentar la cámara: durante el mantenimiento en caliente y en el precalentado cuando el archivo no incluye temperatura de cama. Una temperatura mayor del archivo siempre tiene prioridad.',
    keepWarmMaxMinutes: 'Dejar de mantener caliente tras (minutos)',
    keepWarmMaxMinutesHelp: 'Si la placa no se limpia dentro de este tiempo, los calentadores se apagan en lugar de mantenerse encendidos indefinidamente.',
    gcodeInjection: 'Inyección de G-code',
    gcodeInjectionDescription: 'Configure G-code personalizado para inyectar al inicio o al final de las impresiones para sistemas de impresión automática como Farmloop, SwapMod, AutoClear y Printflow 3D. Los fragmentos se configuran por modelo de impresora y se aplican cuando se activa "Inyectar G-code" en un elemento de la cola.',
    gcodeInjectionNoPrinters: 'No se encontraron impresoras. Añada impresoras para configurar fragmentos de G-code.',
    gcodeStartLabel: 'G-code de inicio',
    gcodeEndLabel: 'G-code de fin',
    gcodeStartPlaceholder: 'G-code añadido antes de que comience la impresión...',
    gcodeEndPlaceholder: 'G-code añadido después de que termine la impresión...',
    staggerGroupSize: 'Tamaño de grupo',
    staggerGroupSizeHelp: 'Impresoras que se inician simultáneamente por grupo',
    staggerInterval: 'Intervalo (minutos)',
    staggerIntervalHelp: 'Retardo entre el inicio de cada grupo',
    queueDrying: 'Secado automático de la cola',
    queueDryingDescription: 'Secar automáticamente el filamento del AMS cuando la impresora está inactiva entre impresiones en cola. Usa el umbral de humedad de arriba para activar el secado.',
    queueDryingEnabled: 'Activar el secado automático',
    queueDryingEnabledDescription: 'Iniciar el secado del AMS automáticamente cuando la impresora está inactiva y la humedad supera el umbral',
    queueDryingBlock: 'Esperar a que termine el secado',
    queueDryingBlockDescription: 'Bloquear la cola de impresión hasta que termine el secado. Cuando está desactivado, las impresiones tienen prioridad sobre el secado.',
    ambientDryingEnabled: 'Secado ambiental',
    ambientDryingEnabledDescription: 'Secar automáticamente el filamento en impresoras inactivas cuando la humedad supera el umbral, incluso sin impresiones en cola.',
    printDryingEnabled: 'Continuar secado durante la impresión',
    printDryingEnabledDescription: 'Permite que el secado automático siga funcionando durante una impresión en hardware compatible (H2D, H2C, H2S, P2S, H2D Pro, X2D, X1C, A2L con firmware reciente). La temperatura de secado se limita automáticamente 5°C por debajo del preajuste en reposo para proteger las bobinas.',
    dryingPresets: 'Preajustes de secado',
    dryingPresetsDescription: 'Temperatura y duración por tipo de filamento. El AMS 2 Pro usa temperaturas más bajas; el AMS-HT admite temperaturas más altas.',
    dryingFilament: 'Filamento',
    humidityThresholds: 'Umbrales de humedad',
    humidityThresholdsDescription: 'Disparador de humedad por tipo de filamento para autosecado y alarmas. Cargas mixtas en un AMS usan el umbral más bajo.',
    humidityThresholdCol: 'Umbral',
    humidityThresholdDefault: 'Predeterminado (tipos desconocidos)',
    printModal: 'Ventana de impresión',
    expandCustomMapping: 'Expandir el mapeo personalizado de forma predeterminada',
    expandCustomMappingDescription: 'Al imprimir en varias impresoras, mostrar el mapeo de AMS por impresora expandido',
    // User management
    authentication: 'Autenticación',
    authEnabledDescription: 'Su instancia está protegida con autenticación de usuario',
    authDisabledDescription: 'Active para exigir el inicio de sesión y gestionar el acceso de los usuarios',
    authDisabledMessage: 'Active la autenticación para crear cuentas de usuario, gestionar permisos y proteger su instancia de Bambuddy.',
    enableAuthentication: 'Activar la autenticación',
    currentUser: 'Usuario actual',
    changePassword: 'Cambiar contraseña',
    admin: 'Administrador',
    users: 'Usuarios',
    addUser: 'Añadir usuario',
    groups: 'Grupos',
    addGroup: 'Añadir grupo',
    system: 'Sistema',
    noDescription: 'Sin descripción',
    userCount: '{{count}} usuarios',
    permissionCount: '{{count}} permisos',
    createUser: 'Crear usuario',
    username: 'Nombre de usuario',
    enterUsername: 'Introduzca el nombre de usuario',
    password: 'Contraseña',
    enterPassword: 'Introduzca la contraseña',
    passwordRequirements: 'Al menos 8 caracteres, con una mayúscula, una minúscula, un dígito y un carácter especial.',
    confirmPassword: 'Confirmar contraseña',
    confirmPasswordPlaceholder: 'Confirme la contraseña',
    // Title tooltips
    viewReleaseOnGitHub: 'Ver la versión en GitHub',
    turnAllPlugsOn: 'Encender todos los enchufes',
    turnAllPlugsOff: 'Apagar todos los enchufes',
    // Modal: Clear logs
    clearNotificationLogs: 'Borrar los registros de notificaciones',
    clearLogsMessage: 'Esto eliminará permanentemente todos los registros de notificaciones de más de 30 días. Esta acción no se puede deshacer.',
    clearLogs: 'Borrar registros',
    // Modal: Reset UI
    resetUiPreferences: 'Restablecer las preferencias de la interfaz',
    resetUiPreferencesMessage: 'Esto restablecerá todas las preferencias de la interfaz a sus valores predeterminados: orden de la barra lateral, tema, disposición del panel, modos de vista y preferencias de ordenación. Sus impresoras, archivos y ajustes del servidor NO se verán afectados. La página se recargará tras el restablecimiento.',
    resetPreferences: 'Restablecer preferencias',
    // Modal: Delete group
    deleteGroupTitle: 'Eliminar grupo',
    deleteGroupMessage: '¿Está seguro de que desea eliminar este grupo? Los usuarios de este grupo perderán estos permisos.',
    deleteGroup: 'Eliminar grupo',
    // Modal: Disable auth
    disableAuthenticationTitle: 'Desactivar la autenticación',
    disableAuthenticationMessage: '¿Está seguro de que desea desactivar la autenticación? Esto hará que su instancia de Bambuddy sea accesible sin iniciar sesión. Todos los usuarios permanecerán en la base de datos, pero la autenticación se desactivará.',
    disableAuthentication: 'Desactivar la autenticación',
    // Additional settings
    configureBambuddy: 'Configurar Bambuddy',
    systemDefault: 'Predeterminado del sistema',
    archiveSettings: 'Ajustes de archivado',
    newWindow: 'Ventana nueva',
    embeddedOverlay: 'Superposición integrada',
    preferredSlicer: 'Laminador preferido',
    preferredSlicerDescription: 'Laminador usado para el corte en la aplicación a través del sidecar de la API',
    openInSlicerLabel: 'Abrir en el laminador',
    openInSlicerInherit: 'Igual que el laminador de la API',
    openInSlicerDescription: 'Laminador de escritorio que usa el botón «Abrir en el laminador». Déjelo en «Igual que el laminador de la API» para heredar, o elija un laminador distinto para uso local.',
    orcaslicerKnownIssuesWarning: 'OrcaSlicer 2.3.2 / 2.4.0-dev tienen errores conocidos de la CLI que impiden laminar muchos 3MF creados con Bambu — consulte las incidencias del proyecto original #12426 (cierre inesperado en archivos multiextrusor pintados) y #13386 (rechazo por validación estricta del rango de parámetros). Se recomienda Bambu Studio hasta que lleguen las correcciones.',
    useSlicerApi: 'Usar la API del laminador',
    useSlicerApiDescription: 'Cuando está activado, las acciones de «Laminar» abren la ventana del laminador integrada en la aplicación y llaman al contenedor auxiliar de la API del laminador. Cuando está desactivado (predeterminado), delegan en el laminador de escritorio mediante un esquema de URI.',
    slicerCard: 'Laminador',
    orcaslicerApiUrl: 'OrcaSlicer sidecar URL',
    bambuStudioApiUrl: 'Bambu Studio sidecar URL',
    slicerStallTimeout: 'Tiempo de espera por inactividad del laminador (minutos)',
    slicerStallTimeoutDescription: 'Abandona un laminado tras este tiempo sin progreso del sidecar. Los modelos pesados que siguen informando progreso nunca se interrumpen, por mucho que tarden. Los sidecars que no informan progreso usan este valor como limite de tiempo total.',
    slicerApiUrlDescription: 'URL del contenedor auxiliar de la API del laminador. Déjelo en blanco para usar los valores predeterminados de las variables de entorno SLICER_API_URL / BAMBU_STUDIO_API_URL.',
    externalCameras: 'Cámaras externas',
    costTracking: 'Seguimiento de costes',
    billingEnabled: 'Activar facturación',
    billingEnabledDescription: 'Cobrar las impresiones a los usuarios y activar las funciones financieras',
    printerKillSwitch: 'Parada de impresiones no autorizadas',
    printerKillSwitchDescription: 'Detiene inmediatamente las impresiones que comiencen sin autorización.',
    financeBudgetReset: 'Reinicio mensual del presupuesto financiero',
    financeBudgetResetDay: 'Día de reinicio',
    financeBudgetResetDayHelp: 'En los meses cortos, se usa el último día del mes.',
    financeBudgetResetTimezone: 'Zona horaria del reinicio',
    financeBudgetResetTimezoneHelp: 'El inicio del período presupuestario se calcula en esta zona horaria.',
    rebuildLedger: 'Reconstruir libro mayor del monedero',
    rebuildLedgerStarted: 'Se inició la reconstrucción del libro mayor',
    rebuildLedgerConfirmTitle: '¿Reconstruir el libro mayor del monedero?',
    rebuildLedgerConfirmMessage: 'Esto reconstruirá el libro mayor para reparar saldos históricos. Ejecútalo solo si sabes lo que estás haciendo.',
    rebuildLedgerInProgress: 'Iniciando reconstrucción...',
    printsOnly: 'Solo impresiones',
    totalConsumption: 'Consumo total',
    dataManagement: 'Gestión de datos',
    storageUsage: 'Uso del almacenamiento',
    storageUsageDescription: 'Desglose del uso de datos por categoría',
    storageUsageTotal: 'Total',
    storageUsageErrors: 'Errores',
    storageUsageOtherBreakdown: 'Otros (incluye recursos estáticos, scripts y archivos de configuración)',
    storageUsageSystem: 'Sistema',
    storageUsageData: 'Datos',
    storageUsageUnavailable: 'Información de uso del almacenamiento no disponible',
    clearNotificationLogsDescription: 'Eliminar los registros de notificaciones de más de 30 días',
    resetUiPreferencesDescription: 'Restablecer el orden de la barra lateral, el tema, los modos de vista y las preferencias de disposición. Las impresoras, los archivos y los ajustes no se ven afectados.',
    enableHomeAssistant: 'Activar Home Assistant',
    enableMqtt: 'Activar MQTT',
    useTls: 'Usar TLS',
    enableMetricsEndpoint: 'Activar el punto de conexión de métricas',
    availableMetrics: 'Métricas disponibles',
    editUser: 'Editar usuario',
    deleteUserTitle: 'Eliminar usuario',
    groupName: 'Nombre del grupo',
    // Placeholders
    leaveEmptyForAnonymous: 'Dejar vacío para anónimo',
    leaveEmptyForNoAuth: 'Dejar vacío para sin autenticación',
    enterNewPassword: 'Introduzca la nueva contraseña',
    confirmNewPassword: 'Confirme la nueva contraseña',
    enterGroupName: 'Introduzca el nombre del grupo',
    enterDescriptionOptional: 'Introduzca una descripción (opcional)',
    enterCurrentPassword: 'Introduzca la contraseña actual',
    enterNewPasswordMin6: 'Introduzca la nueva contraseña (mín. 6 caracteres)',
    toast: {
      keyCopied: 'Clave copiada al portapapeles',
      copyFailed: 'Error al copiar la clave',
      keyAddedToBrowser: 'Clave añadida al explorador de API',
      clearLogsFailed: 'Error al borrar los registros',
      uiPreferencesReset: 'Preferencias de la interfaz restablecidas. Actualizando...',
      authDisabled: 'Autenticación desactivada correctamente',
      authDisableFailed: 'Error al desactivar la autenticación',
      apiKeyCreated: 'Clave API creada',
      apiKeyDeleted: 'Clave API eliminada',
      userCreated: 'Usuario creado correctamente',
      userUpdated: 'Usuario actualizado correctamente',
      userDeleted: 'Usuario eliminado correctamente',
      groupCreated: 'Grupo creado correctamente',
      groupUpdated: 'Grupo actualizado correctamente',
      groupDeleted: 'Grupo eliminado correctamente',
      fillRequiredFields: 'Rellene todos los campos obligatorios',
      passwordsDoNotMatch: 'Las contraseñas no coinciden',
      passwordTooShort: 'La contraseña debe tener al menos 8 caracteres',
      passwordNeedsUppercase: 'La contraseña debe contener al menos una letra mayúscula',
      passwordNeedsLowercase: 'La contraseña debe contener al menos una letra minúscula',
      passwordNeedsDigit: 'La contraseña debe contener al menos un dígito',
      passwordNeedsSpecial: 'La contraseña debe contener al menos un carácter especial',
      enterGroupName: 'Introduzca un nombre de grupo',
      settingsSaved: 'Ajustes guardados',
      noPermissionUpdate: 'No tiene permiso para cambiar los ajustes',
      cameraSettingsSaved: 'Ajustes de la cámara guardados',
      enterCameraUrl: 'Introduzca una URL de cámara',
      passwordChanged: 'Contraseña cambiada correctamente',
      connectionFailed: 'Error de conexión',
      testFailed: 'La prueba falló',
      cameraConnected: 'Cámara conectada{{resolution}}',
      cameraConnectedCoalesced: 'Cámara conectada{{resolution}} (compartida con una captura ya en curso)',
    },
    testConnection: 'Probar conexión',
    catalog: {
      spoolCatalog: 'Catálogo de bobinas',
      spoolCatalogDescription: 'Pesos de bobinas vacías por marca/tipo. Se usa para la búsqueda automática de peso al añadir bobinas.',
      searchCatalog: 'Buscar en el catálogo...',
      addNewEntry: 'Añadir nueva entrada',
      namePlaceholder: 'Nombre (p. ej., Bambu Lab - Plástico)',
      weight: 'Peso',
      type: 'Tipo',
      default: 'Predeterminado',
      custom: 'Personalizado',
      noMatch: 'Ninguna entrada coincide con su búsqueda',
      empty: 'No hay entradas en el catálogo',
      deleteEntry: 'Eliminar entrada',
      deleteConfirm: '¿Está seguro de que desea eliminar "{{name}}"?',
      resetCatalog: 'Restablecer catálogo',
      resetConfirm: '¿Restablecer el catálogo a los valores predeterminados? Esto eliminará todas las entradas personalizadas.',
      loadFailed: 'Error al cargar el catálogo de bobinas',
      nameWeightRequired: 'El nombre y el peso son obligatorios',
      entryAdded: 'Entrada añadida',
      addFailed: 'Error al añadir la entrada',
      entryUpdated: 'Entrada actualizada',
      updateFailed: 'Error al actualizar la entrada',
      entryDeleted: 'Entrada eliminada',
      deleteFailed: 'Error al eliminar la entrada',
      resetSuccess: 'Catálogo restablecido a los valores predeterminados',
      resetFailed: 'Error al restablecer el catálogo',
      exported: 'Se exportaron {{count}} entradas',
      imported: 'Se importaron {{added}} entradas ({{skipped}} omitidas)',
      importFailed: 'Error al importar: formato JSON no válido',
      exportTooltip: 'Exportar el catálogo a JSON',
      importTooltip: 'Importar el catálogo desde JSON',
      resetTooltip: 'Restablecer a los valores predeterminados',
      selectedCount: '{{count}} seleccionadas',
      deleteSelected: 'Eliminar seleccionadas',
      bulkDeleteConfirm: '¿Está seguro de que desea eliminar {{count}} entradas?',
      bulkDeleted: 'Se eliminaron {{count}} entradas',
      bulkDeleteFailed: 'Error al eliminar las entradas',
      material: 'Material',
      spoolWeight: 'Peso de la bobina',
      color: 'Color',
      updateSpoolWeight: 'Actualizar el peso de la bobina',
      filamentUpdated: 'Filamento actualizado',
      filamentUpdateFailed: 'Error al actualizar el filamento',
      filamentUpdateInvalid: 'Datos de filamento no válidos',
      keepExistingSpoolWeight: 'Conservar el peso anterior para las bobinas existentes',
      keepExistingSpoolWeightDesc: 'Las bobinas ya creadas con este tipo de filamento conservan el peso de tara anterior. Las bobinas nuevas usan el valor actualizado.',
      applyToAllSpools: 'Aplicar a todas las bobinas',
      applyToAllSpoolsDesc: 'Todos los cálculos de peso para este tipo de filamento usan inmediatamente el nuevo peso de tara.',
    },
    colorCatalog: {
      title: 'Catálogo de colores',
      description: 'Colores de filamento por fabricante/material. Se usa para la búsqueda automática de color al añadir bobinas.',
      searchColors: 'Buscar colores...',
      allManufacturers: 'Todos los fabricantes',
      addNewColor: 'Añadir nuevo color',
      manufacturer: 'Fabricante',
      colorName: 'Nombre del color',
      hex: 'Hex',
      materialOptional: 'Material (opcional)',
      showing: 'Mostrando {{filtered}} de {{total}} colores',
      noMatch: 'Ningún color coincide con su búsqueda',
      empty: 'No hay colores en el catálogo',
      deleteColor: 'Eliminar color',
      deleteConfirm: '¿Está seguro de que desea eliminar "{{name}}"?',
      resetCatalog: 'Restablecer el catálogo de colores',
      resetConfirm: '¿Restablecer el catálogo a los valores predeterminados? Esto eliminará todos los colores personalizados.',
      sync: 'Sincronizar',
      starting: 'Iniciando...',
      syncTooltip: 'Sincronizar desde FilamentColors.xyz (más de 2000 colores, puede tardar un minuto)',
      loadFailed: 'Error al cargar el catálogo de colores',
      fieldsRequired: 'El fabricante, el nombre del color y el color hexadecimal son obligatorios',
      colorAdded: 'Color añadido',
      addFailed: 'Error al añadir el color',
      colorUpdated: 'Color actualizado',
      updateFailed: 'Error al actualizar el color',
      colorDeleted: 'Color eliminado',
      deleteFailed: 'Error al eliminar el color',
      resetSuccess: 'Catálogo de colores restablecido a los valores predeterminados',
      resetFailed: 'Error al restablecer el catálogo',
      syncUpToDate: 'Ya está actualizado ({{count}} colores comprobados)',
      syncComplete: 'Se añadieron {{added}} colores nuevos ({{skipped}} ya existían)',
      syncError: 'Error de sincronización',
      syncFailed: 'Error al sincronizar desde FilamentColors.xyz',
      exported: 'Se exportaron {{count}} colores',
      imported: 'Se importaron {{added}} colores ({{skipped}} omitidos)',
      importFailed: 'Error al importar: formato JSON no válido',
      selectedCount: '{{count}} seleccionados',
      deleteSelected: 'Eliminar seleccionados',
      bulkDeleteConfirm: '¿Está seguro de que desea eliminar {{count}} colores?',
      bulkDeleted: 'Se eliminaron {{count}} colores',
      bulkDeleteFailed: 'Error al eliminar los colores',
    },
    // General tab
    dateFormat: 'Formato de fecha',
    dateFormatUs: 'EE. UU. (MM/DD/AAAA)',
    dateFormatEu: 'UE (DD/MM/AAAA)',
    dateFormatIso: 'ISO (AAAA-MM-DD)',
    timeFormat: 'Formato de hora',
    timeFormat12: '12 horas (3:30 PM)',
    timeFormat24: '24 horas (15:30)',
    defaultPrinter: 'Impresora predeterminada',
    defaultPrinterDescription: 'Preseleccione esta impresora para subidas, reimpresiones y otras operaciones.',
    slicerBambuStudio: 'Bambu Studio',
    slicerOrcaSlicer: 'OrcaSlicer',
    sidebarOrderDescription: 'Use Diseño de la barra lateral para reordenar elementos, restablecer la visibilidad y gestionar enlaces personalizados.',
    setDefault: 'Establecer como predeterminado',
    sidebarOrderSetDefaultHint: 'Establecer como predeterminado aplica el orden de menú actual a los usuarios que no han personalizado el suyo.',
    sidebarDefaultSet: 'Se ha establecido el orden de menú predeterminado.',
    sidebarDefaultCleared: 'Orden de menú predeterminado borrado.',
    sidebarDefaultFailed: 'Error al establecer el orden de menú predeterminado.',
    reset: 'Restablecer',
    // Appearance
    darkMode: 'Modo oscuro',
    lightMode: 'Modo claro',
    active: '(activo)',
    background: 'Fondo',
    accent: 'Acento',
    style: 'Estilo',
    bgNeutral: 'Neutro',
    bgWarm: 'Cálido',
    bgCool: 'Frío',
    bgOled: 'Negro OLED',
    bgSlate: 'Azul pizarra',
    bgForest: 'Verde bosque',
    accentGreen: 'Verde',
    accentTeal: 'Verde azulado',
    accentBlue: 'Azul',
    accentOrange: 'Naranja',
    accentPurple: 'Morado',
    accentRed: 'Rojo',
    styleClassic: 'Clásico',
    styleGlow: 'Resplandor',
    styleVibrant: 'Vibrante',
    themeToggleHint: 'Alterne entre modo oscuro, claro y sistema con el icono en la barra lateral.',
    progressInTitle: 'Progreso en la pestaña',
    progressInTitleDescription: 'Muestra el porcentaje de la impresión activa y un anillo de progreso en la pestaña del navegador.',
    // Archive
    autoArchivePrints: 'Archivar impresiones automáticamente',
    autoArchiveDescription: 'Guardar automáticamente los archivos 3MF cuando se completan las impresiones',
    saveThumbnailsDescription: 'Extraer y guardar imágenes de vista previa de los archivos 3MF',
    captureFinishPhotoDescription: 'Tomar una foto desde la cámara de la impresora cuando se completa la impresión. Bambuddy graba un breve timelapse durante la impresión para que la foto pueda obtenerse del momento previo al descenso de la cama; el archivo del timelapse se conserva si activaste el timelapse para esta impresión, de lo contrario se elimina automáticamente tras capturar la foto.',
    finishPhotoRestorePlate: 'Elevar la cama para la foto final',
    finishPhotoRestorePlateDescription: 'La impresora baja la cama unos 100 mm al terminar una impresión, dejando la pieza terminada por debajo del encuadre de la cámara. Bambuddy la vuelve a subir hasta justo encima de la última capa impresa, toma la foto y luego la baja de nuevo. Se omite si se desconoce la altura de la impresión o si hay otro trabajo en cola.',
    ffmpegNotInstalled: 'ffmpeg no instalado',
    ffmpegRequired: 'La captura de cámara requiere ffmpeg. Instálelo mediante <brew>brew install ffmpeg</brew> (macOS) o <apt>apt install ffmpeg</apt> (Linux).',
    // Camera
    camera: 'Cámara',
    cameraViewMode: 'Modo de vista de la cámara',
    cameraOverlayDescription: 'La cámara se abre en una superposición redimensionable en la pantalla principal',
    cameraWindowDescription: 'La cámara se abre en una ventana de navegador independiente',
    externalCamerasDescription: 'Configure cámaras externas para sustituir la cámara integrada de la impresora. Admite transmisiones MJPEG, RTSP, capturas HTTP y cámaras USB (V4L2). Cuando está activada, la cámara externa se usa para la vista en directo y las fotos de finalización.',
    cameraPlaceholderUsb: 'Ruta del dispositivo (/dev/video0)',
    cameraPlaceholderUrl: 'URL de la cámara (rtsp://... o http://...)',
    cameraTypeMjpeg: 'Transmisión MJPEG',
    cameraTypeRtsp: 'Transmisión RTSP',
    cameraTypeSnapshot: 'Captura HTTP',
    cameraTypeUsb: 'Cámara USB (V4L2)',
    cameraSnapshotUrl: 'URL de captura (opcional)',
    cameraSnapshotUrlPlaceholder: 'http://192.168.1.61:1984/api/frame.jpeg?src=printer',
    cameraSnapshotUrlHelp: 'URL de fotograma único usada para las miniaturas de notificaciones, las fotos de finalización, los fotogramas de time-lapse por capa y la detección de cama. El time-lapse y la detección de cama requieren cada uno su propio interruptor por impresora; esta URL es solo la fuente de imagen de la que se nutren cuando están activos. Déjelo en blanco para capturar desde la transmisión en directo de arriba. Útil para go2rtc (/api/frame.jpeg) y cámaras IP con un punto de conexión de captura dedicado.',
    cameraRotation: 'Rotación',
    test: 'Probar',
    connected: 'Conectada',
    disconnected: 'Desconectada',
    // Cost tracking
    currency: 'Moneda',
    defaultFilamentCost: 'Coste predeterminado del filamento (por kg)',
    electricityCost: 'Coste de la electricidad por kWh',
    energyDisplayMode: 'Modo de visualización de la energía',
    energyModePrintDescription: 'El panel muestra la suma de la energía usada durante las impresiones',
    energyModeTotalDescription: 'El panel muestra la energía total de los enchufes inteligentes',
    // File Manager
    fileManager: 'Gestor de archivos',
    createArchiveEntry: 'Crear una entrada de archivo al imprimir',
    createArchiveEntryDescription: 'Al imprimir desde el gestor de archivos, crear opcionalmente una entrada de archivo',
    lowDiskSpaceWarning: 'Advertencia de poco espacio en disco',
    lowDiskSpaceDescription: 'Mostrar una advertencia cuando el espacio libre en disco caiga por debajo de este umbral',
    // Updates
    printerFirmware: 'Firmware de la impresora',
    checkFirmwareDescription: 'Buscar actualizaciones de firmware de la impresora de Bambu Lab',
    bambuddySoftware: 'Software de Bambuddy',
    autoCheckDescription: 'Buscar automáticamente nuevas versiones al iniciar',
    checkNow: 'Buscar ahora',
    updateAvailableVersion: 'Actualización disponible: v{{version}}',
    releaseNotes: 'Notas de la versión',
    updateViaDocker: 'Actualizar mediante Docker Compose:',
    composeDirectory: 'Directorio de Compose',
    composeDirectoryHint: 'Directorio que contiene tu docker-compose.yml. Déjalo vacío para omitir el cd.',
    copyUpdateCommand: 'Copiar comando de actualización',
    updateViaHomeAssistant: 'Las actualizaciones las gestiona el Supervisor de Home Assistant. Abra Ajustes → Complementos → Bambuddy en Home Assistant para instalar la nueva versión.',
    updateViaWindowsInstaller: 'Las instalaciones en Windows se actualizan volviendo a ejecutar el instalador. Descarga la nueva versión abajo — tus datos, ajustes e impresoras se conservan.',
    downloadWindowsInstaller: 'Descargar instalador para v{{version}}',
    installUpdate: 'Instalar actualización',
    latestVersionRunning: 'Está ejecutando la versión más reciente',
    failedToCheckUpdates: 'Error al buscar actualizaciones: {{error}}',
    // Data Management
    backupRestore: 'Copia de seguridad y restauración',
    backupRestoreDescription: 'Exportar/importar ajustes y configurar la copia de seguridad en GitHub',
    goToBackup: 'Ir a la copia de seguridad',
    // Network tab
    externalUrl: 'URL externa',
    externalUrlDescription: 'La URL externa donde Bambuddy es accesible. Se usa para las imágenes de notificación y las integraciones externas.',
    bambuddyUrl: 'URL de Bambuddy',
    externalUrlHint: 'Incluya el protocolo y el puerto (p. ej., http://192.168.1.100:8000)',
    ftpRetry: 'Reintento de FTP',
    ftpRetryDescription: 'Reintentar las operaciones FTP cuando el Wi-Fi de la impresora no es fiable. Se aplica a las descargas de 3MF, las subidas de impresión, las descargas de time-lapse y las actualizaciones de firmware.',
    autoRetryDescription: 'Reintentar automáticamente las operaciones FTP fallidas',
    retryAttempts: 'Intentos de reintento',
    retryDelay: 'Retardo de reintento',
    connectionTimeout: 'Tiempo de espera de la conexión',
    time_one: '{{count}} vez',
    time_other: '{{count}} veces',
    second_one: '{{count}} segundo',
    second_other: '{{count}} segundos',
    nSeconds: '{{count}} segundos',
    increaseForWeakWifi: 'Aumente para impresoras con Wi-Fi débil',
    // Home Assistant
    homeAssistant: 'Home Assistant',
    homeAssistantFullDescription: 'Conéctese a Home Assistant para controlar enchufes inteligentes mediante la API REST de HA. Admite las entidades switch, light, input_boolean y script.',
    homeAssistantUrl: 'URL de Home Assistant',
    longLivedAccessToken: 'Token de acceso de larga duración',
    haTokenHint: 'Cree un token en HA: Perfil → Tokens de acceso de larga duración → Crear token',
    connectionSuccessful: 'Conexión correcta',
    connectionFailed: 'Error de conexión',
    haConnectionSuccess: 'Conexión con Home Assistant realizada correctamente.',
    haConnectionFailed: 'Error al conectar con Home Assistant.',
    // MQTT
    mqttPublishing: 'Publicación MQTT',
    mqttDescription: 'Publique los eventos de Bambuddy en un broker MQTT externo para la integración con Node-RED, Home Assistant y otros sistemas de automatización.',
    mqttEnableDescription: 'Publicar eventos en un broker MQTT externo',
    brokerHostname: 'Nombre de host del broker',
    port: 'Puerto',
    usernameOptional: 'Nombre de usuario (opcional)',
    passwordOptional: 'Contraseña (opcional)',
    topicPrefix: 'Prefijo del tema',
    topicPrefixHint: 'Los temas serán: {{prefix}}/printers/<serial>/status, etc.',
    // Prometheus
    prometheusMetrics: 'Métricas de Prometheus',
    prometheusEndpointDescription: 'Exponer las métricas de la impresora en <code>/api/v1/metrics</code> para la supervisión con Prometheus/Grafana.',
    bearerTokenOptional: 'Bearer Token (opcional)',
    bearerTokenHint: 'Si se establece, las solicitudes deben incluir <code>Authorization: Bearer <token></code>',
    metricsConnectionStatus: 'Estado de la conexión',
    metricsPrinterState: 'Estado de la impresora (inactiva/imprimiendo/etc.)',
    metricsPrintProgress: 'Progreso de la impresión 0-100%',
    metricsBedTemp: 'Temperatura de la cama',
    metricsNozzleTemp: 'Temperatura de la boquilla',
    metricsPrintsTotal: 'Total de impresiones por resultado',
    metricsMore: '...y más (capas, ventiladores, cola, uso de filamento)',
    // Smart Plugs
    smartPlugsDescription: 'Conecte enchufes inteligentes (Tasmota o Home Assistant) para automatizar el control de la alimentación y realizar el seguimiento del consumo de energía de sus impresoras.',
    allOn: 'Encender todo',
    allOff: 'Apagar todo',
    addSmartPlug: 'Añadir enchufe inteligente',
    energySummary: 'Resumen de energía',
    currentPower: 'Potencia actual',
    plugsOnline: '{{reachable}}/{{total}} enchufes en línea',
    today: 'Hoy',
    yesterday: 'Ayer',
    total: 'Total',
    enablePlugsForSummary: 'Active enchufes para ver el resumen de energía',
    addNotificationProvider: 'Añadir',
    // Users
    systemBadge: '(Sistema)',
    creating: 'Creando...',
    changing: 'Cambiando...',
    deleteUserAndItems: 'Eliminar el usuario Y sus elementos',
    deleteUserKeepItems: 'Eliminar el usuario, conservar los elementos (quedan sin propietario)',
    ok: 'Aceptar',

    // 2FA settings
    twoFa: {
      totpTitle: 'Aplicación de autenticación (TOTP)',
      totpDesc: 'Use una aplicación de autenticación como Google Authenticator, Aegis o Authy.',
      emailOtpTitle: 'OTP por correo',
      emailOtpDesc: 'Enviar un código de un solo uso a {{email}} cuando inicie sesión.',
      emailOtpNoEmail: 'Añada una dirección de correo a su cuenta para habilitar este método.',
      addEmailFirst: 'Su cuenta no tiene dirección de correo. Pida a un administrador que añada una antes de activar el OTP por correo.',
      setupTotp: 'Configurar la aplicación de autenticación',
      setupAuthApp: 'Configurar la aplicación de autenticación',
      setupInstructions: 'Escanee el código QR de abajo con su aplicación de autenticación y luego confirme con un código.',
      manualEntry: '¿No puede escanear? Introduzca este secreto manualmente:',
      scannedContinue: 'He escaneado el código — continuar',
      enterCodeToConfirm: 'Introduzca el código de 6 dígitos de su aplicación de autenticación para confirmar la configuración.',
      activate: 'Activar',
      disableTotp: 'Desactivar el autenticador',
      disableConfirmHint: 'Introduzca un código TOTP válido o un código de respaldo para desactivar el autenticador.',
      totpDisabled: 'Aplicación de autenticación desactivada.',
      emailOtpEnabled: 'OTP por correo activado.',
      emailOtpDisabled: 'OTP por correo desactivado.',
      smtpRequired: 'Configure y pruebe primero los ajustes de SMTP.',
      invalidCode: 'Código no válido. Inténtelo de nuevo.',
      enableEmailOtp: 'Activar el OTP por correo',
      disableEmailOtp: 'Desactivar el OTP por correo',
      emailSetupEnterCode: 'Se ha enviado un código de verificación a su dirección de correo. Introdúzcalo a continuación para confirmar que es el propietario de este buzón.',
      verifyAndEnable: 'Verificar y activar',
      emailDisablePasswordHint: 'Introduzca la contraseña de su cuenta para confirmar la desactivación del OTP por correo.',
      passwordPlaceholder: 'Introduzca su contraseña',
      backupCodesTitle: 'Guarde sus códigos de respaldo',
      backupCodesWarning: 'Guarde estos códigos en un lugar seguro. Cada código solo se puede usar una vez y no se volverán a mostrar.',
      backupCodesRemaining: 'Quedan {{count}} códigos de respaldo',
      savedCodes: 'He guardado mis códigos',
      regenBackup: 'Regenerar los códigos de respaldo',
      regenBackupHint: 'Introduzca su código TOTP actual para generar 10 códigos de respaldo nuevos. Todos los códigos de respaldo existentes quedarán invalidados.',
      newBackupCodes: 'Nuevos códigos de respaldo',
      linkedAccounts: 'Cuentas de SSO vinculadas',
      linkedAccountsDesc: 'Estos proveedores de identidad externos están vinculados a su cuenta.',
      oidcUnlinked: 'Cuenta desvinculada.',
    },
    // Session Policy (#1706)
    sessionPolicy: {
      title: 'Política de sesión',
      description: 'Duración máxima de sesión para nuevos inicios de sesión. Los tokens ya emitidos conservan su caducidad original.',
      preset24h: '24 horas',
      preset7d: '7 días',
      preset30d: '30 días',
      customHoursLabel: 'Duración de sesión personalizada en horas',
      hoursSuffix: 'horas',
      warning: 'Las sesiones más largas reducen la protección de cierre automático. Recomendado solo para implementaciones de usuario único en entornos de confianza.',
    },

    // OIDC provider settings
    oidc: {
      title: 'Proveedores SSO / OIDC',
      desc: 'Configure proveedores de OpenID Connect para permitir el inicio de sesión único mediante proveedores de identidad externos.',
      addProvider: 'Añadir proveedor',
      newProvider: 'Nuevo proveedor',
      empty: 'Aún no hay proveedores OIDC configurados.',
      created: 'Proveedor creado.',
      updated: 'Proveedor actualizado.',
      deleted: 'Proveedor eliminado.',
      refreshIcon: 'Actualizar icono',
      removeIcon: 'Quitar icono',
      iconRefreshed: 'Icono actualizado.',
      iconRemoved: 'Icono eliminado.',
      iconFetchFailed: 'No se pudo obtener el icono de la URL del proveedor.',
      deleteTitle: 'Eliminar proveedor',
      deleteMessage: '¿Eliminar "{{name}}"? Todas las cuentas de usuario vinculadas se desconectarán.',
      form: {
        name: 'Nombre visible',
        issuerUrl: 'URL del emisor',
        clientId: 'ID de cliente',
        clientSecret: 'Secreto de cliente',
        scopes: 'Ámbitos',
        iconUrl: 'URL del icono (opcional)',
        enabled: 'Activado',
        autoCreate: 'Crear usuarios automáticamente',
        autoCreateDesc: 'Crear automáticamente una cuenta local en el primer inicio de sesión.',
        autoLink: 'Vincular cuentas existentes automáticamente',
        autoLinkDesc: 'Vincular cuentas locales existentes haciendo coincidir el correo en el primer inicio de sesión.',
        secretHint: 'dejar en blanco para conservar el actual',
        secretPlaceholder: 'nuevo secreto',
        emailClaim: 'Claim de correo',
        emailClaimDesc: 'Claim de JWT usado como identidad de correo. Use «preferred_username» o «upn» para Azure Entra ID (que no envía email_verified). Use solo nombres de claim de confianza.',
        emailClaimPlaceholder: 'correo',
        emailClaimCustomClaimAutoLinkWarning: 'Los claims personalizados solo son seguros para la vinculación automática cuando el valor lo administra el inquilino (p. ej. upn / preferred_username de Azure Entra ID). No active la vinculación automática si su IdP permite a los usuarios autoasignarse este claim.',
        requireEmailVerified: 'Requerir correo verificado',
        requireEmailVerifiedDesc: 'Aceptar el claim de correo solo cuando el proveedor lo marca como verificado.',
        requireEmailVerifiedWarning: 'Advertencia: el correo se aceptará incluso sin verificación. Úselo solo con proveedores de confianza.',
        requireEmailVerifiedAutoLink: 'Desactive primero la vinculación automática para cambiar este ajuste.',
        defaultGroup: 'Grupo predeterminado',
        defaultGroupDesc: 'Grupo asignado a los usuarios creados automáticamente. Si no se establece, se usa Visores como alternativa.',
        defaultGroupViewersFallback: 'Visores (predeterminado)',
        autologin: 'Inicio automático',
        autologinDesc: 'Redirigir a los visitantes no autenticados directamente a este proveedor. Solo un proveedor puede llevar esta marca.',
      },
    },

    encryption: {
      title: 'Estado del cifrado de MFA',
      enabledFromEnv: 'Cifrado en reposo activado (clave de la variable de entorno MFA_ENCRYPTION_KEY)',
      enabledFromFile: 'Cifrado en reposo activado (clave cargada del directorio de datos)',
      enabledGenerated: 'Cifrado en reposo activado con una clave autogenerada',
      notConfigured: 'Cifrado en reposo no configurado',
      notConfiguredDesc: 'Los secretos TOTP y los client_secrets de OIDC se almacenan en texto plano. Establezca MFA_ENCRYPTION_KEY o reinicie Bambuddy con un directorio de datos con permisos de escritura para generar una automáticamente.',
      allEncrypted: 'Todos los secretos de MFA están cifrados en reposo.',
      legacyRowsLabel: 'Filas heredadas en texto plano',
      encryptedRowsLabel: 'Filas cifradas',
      legacyRowsWarning: 'Se detectaron {{count}} fila(s) heredada(s) en texto plano. Vuelva a guardar el proveedor OIDC o vuelva a registrar la aplicación de autenticación del usuario para migrar al almacenamiento cifrado.',
      backupHint: 'La clave autogenerada se almacena en DATA_DIR/.mfa_encryption_key y se incluye en los ZIP de copia de seguridad local. Mantenga sus copias de seguridad seguras o establezca MFA_ENCRYPTION_KEY de forma explícita.',
      decryptionBrokenTitle: 'Falta la clave de cifrado',
      decryptionBrokenError: '{{count}} registro(s) cifrado(s) no se pueden descifrar porque la clave de cifrado ya no está disponible. Restaure la MFA_ENCRYPTION_KEY anterior o DATA_DIR/.mfa_encryption_key para recuperarlos.',
      migrationErrorWarning: '{{count}} fila(s) heredada(s) no se pudieron volver a cifrar al iniciar. Revise los registros del servidor y reinicie Bambuddy para reintentarlo.',
    },


    pipelineLimits: {
      title: 'Límites de pipelines del cortador',
      maxCopiesLabel: 'Copias máximas por ejecución',
      maxCopiesDesc: 'Límite superior de copias que los operadores pueden solicitar al ejecutar una pipeline. El límite máximo del servidor es 1000.',
    },

    // Slicer Pipelines (#1425): list/edit/delete preset bundles users saved
    // from the Slice dialog. Lives in Settings → Workflow → Pipelines sub-tab.
    pipelines: {
      title: 'Pipelines del Cortador',
      subtitle: 'Paquetes reutilizables de preajustes (impresora + proceso + filamentos + tipo de placa). Guarda uno desde el diálogo Cortar y aplícalo con un clic al siguiente archivo.',
      loading: 'Cargando pipelines…',
      loadError: 'No se pudieron cargar las pipelines.',
      confirmDelete: '¿Eliminar esta pipeline? Esto no se puede deshacer.',
      staleWarning: 'Uno o más preajustes referenciados ya no existen. Vuelve a guardar esta pipeline desde el diálogo Cortar para corregirla.',
      empty: {
        title: 'Aún no hay pipelines.',
        howto: 'Abre el diálogo Cortar para cualquier archivo, elige impresora / proceso / filamentos / placa, y haz clic en "Guardar como pipeline". Tus pipelines guardadas aparecerán aquí.',
      },
      field: {
        name: 'Nombre de la pipeline',
        description: 'Descripción',
        targetPrinter: 'Impresora de destino',
        noTarget: '— Sin destino —',
        targetKind: 'Tipo de destino',
        targetKindSpecific: 'Impresora específica',
        targetKindClass: 'Clase de impresora',
        targetModelClass: 'Modelo de impresora',
        fanoutStrategy: 'Estrategia de distribución',
        fanout: {
          max_parallel: 'Máximo paralelo — distribuir entre cualquier impresora libre coincidente',
          round_robin: 'Round robin — alternar entre impresoras elegibles',
          fill_one_first: 'Llenar una primero — fijar todas las copias a una impresora',
        },
        fanoutShort: {
          max_parallel: 'paralelo',
          round_robin: 'round robin',
          fill_one_first: 'primero uno',
        },
      },
      action: {
        save: 'Guardar',
        cancel: 'Cancelar',
        rename: 'Renombrar',
        delete: 'Eliminar',
      },
      slot: {
        printer: 'Impresora',
        process: 'Proceso',
        filament: 'Filamento',
        filamentN: 'Filamento {{n}}',
        filamentAll: 'Todos los {{n}} slots',
        bed: 'Placa',
      },
      group: {
        profiles: 'Perfiles',
        filaments: 'Filamentos',
      },
      searchPlaceholder: 'Buscar pipelines…',
      filterTargetType: 'Filtrar por tipo de destino',
      filterTarget: 'Filtrar por destino',
      filter: {
        all: 'Todos los destinos',
        noTarget: 'Sin destino',
        count: '{{shown}} / {{total}}',
        noMatches: 'Ninguna pipeline coincide con los filtros actuales.',
      },
      toast: {
        saved: 'Pipeline guardada',
        saveFailed: 'Error al guardar',
        deleted: 'Pipeline eliminada',
        deleteFailed: 'Error al eliminar',
      },
      noTargetHint: 'Establece una impresora de destino para ejecutar',
      noTargetWarning: 'Establece una impresora de destino antes de ejecutar esta pipeline.',
      runs: {
        lastRun: 'Última ejecución',
        status: {
          queued: 'en cola',
          slicing: 'cortando',
          dispatching: 'enviando',
          in_progress: 'imprimiendo',
          completed: 'completada',
          failed: 'fallida',
          partial_failure: 'fallo parcial',
          cancelled: 'cancelada',
        },
      },
    },
  },

  // Notifications (for push notifications)
  notification: {
    printStarted: {
      title: 'Impresión iniciada',
      body: '{{printer}}: {{filename}} ha empezado a imprimirse',
    },
    printCompleted: {
      title: 'Impresión completada',
      body: '{{printer}}: {{filename}} se completó correctamente',
    },
    printFailed: {
      title: 'Impresión fallida',
      body: '{{printer}}: {{filename}} ha fallado',
    },
    printStopped: {
      title: 'Impresión detenida',
      body: '{{printer}}: {{filename}} se detuvo',
    },
    printProgress: {
      title: 'Progreso de la impresión',
      body: '{{printer}}: {{filename}} está al {{percent}}%',
    },
    printerOffline: {
      title: 'Impresora desconectada',
      body: '{{printer}} está desconectada',
    },
    printerError: {
      title: 'Error de la impresora',
      body: '{{printer}}: {{error}}',
    },
    filamentLow: {
      title: 'Filamento bajo',
      body: '{{printer}}: el filamento se está agotando',
    },
    maintenanceDue: {
      title: 'Mantenimiento pendiente',
      body: '{{printer}}: {{items}} necesitan atención',
    },
  },

  // Errors
  errors: {
    generic: 'Algo salió mal',
    networkError: 'Error de red. Compruebe su conexión.',
    notFound: 'No encontrado',
    unauthorized: 'No autorizado',
    serverError: 'Error del servidor',
    validationError: 'Compruebe los datos introducidos',
    printerConnectionFailed: 'Error al conectar con la impresora',
    saveFailed: 'Error al guardar los cambios',
    deleteFailed: 'Error al eliminar',
    loadFailed: 'Error al cargar los datos',
  },

  // HMS Errors modal
  hmsErrors: {
    title: 'Errores - {{name}}',
    noErrors: 'No hay errores',
    viewOnWiki: 'Ver en la wiki de Bambu Lab',
    mqttVerifyFailedRemedy: 'Activa el modo desarrollador en la impresora, reinicia la impresora y vuelve a iniciar el trabajo.',
    unknownCode: 'Código HMS desconocido — consulta la wiki de Bambu Lab para más detalles.',
    clearInstructions: 'Borre los errores en la impresora para descartarlos aquí.',
    clearErrors: 'Borrar errores',
    clearSuccess: 'Errores HMS borrados',
    clearFailed: 'Error al borrar los errores HMS',
    actionSuccess: 'Acción enviada a la impresora',
    actionFailed: 'No se pudo enviar la acción',
    runoutExpectedSlot: 'El filamento se agotó en {{ranOut}}. La impresora ahora espera filamento compatible en {{expected}}. Inserta una bobina en {{expected}} y luego selecciona Reintentar.',
    runoutExpectedSlotOnly: 'La impresora está esperando filamento compatible en {{expected}}. Inserta una bobina ahí y luego selecciona Reintentar.',
    runoutSlotUnknown: 'El filamento se agotó y la impresión está en pausa. Bambuddy no pudo determinar qué slot espera ahora la impresora — revisa la pantalla de la impresora para ver el slot solicitado.',
    actions: {
      RESUME_PRINTING: 'Reanudar impresión',
      RESUME_PRINTING_DEFECTS: 'Reanudar (defectos aceptables)',
      RESUME_PRINTING_PROBELM_SOLVED: 'Reanudar (problema resuelto)',
      STOP_PRINTING: 'Detener impresión',
      CHECK_ASSISTANT: 'Ver asistente',
      FILAMENT_EXTRUDED: 'Filamento extruido, continuar',
      RETRY_FILAMENT_EXTRUDED: 'Aún no extruido, reintentar',
      CONTINUE: 'Finalizado, continuar',
      LOAD_VIRTUAL_TRAY: 'Cargar filamento',
      OK_BUTTON: 'OK',
      FILAMENT_LOAD_RESUME: 'Filamento cargado, reanudar',
      JUMP_TO_LIVEVIEW: 'Ver en vivo',
      NO_REMINDER_NEXT_TIME: 'No recordar la próxima vez',
      REFRESH_NOZZLE: 'Volver a comprobar',
      IGNORE_NO_REMINDER_NEXT_TIME: 'Ignorar y no recordar',
      IGNORE_RESUME: 'Ignorar y reanudar',
      PROBLEM_SOLVED_RESUME: 'Problema resuelto, reanudar',
      TURN_OFF_FIRE_ALARM: 'Entendido, apagar alarma de incendio',
      RETRY_PROBLEM_SOLVED: 'Reintentar (problema resuelto)',
      CANCLE: 'Cancelar',
      STOP_DRYING: 'Detener secado',
      PROCEED: 'Continuar',
      OK_JUMP_RACK: 'OK',
      ABORT: 'Cancelar',
      DISABLE_PURIFICATION: 'Desactivar purificación para esta impresión',
      DONT_REMIND_NEXT_TIME: 'No recordarme',
      DBL_CHECK_CANCEL: 'Cancelar',
      DBL_CHECK_DONE: 'Hecho',
      DBL_CHECK_RETRY: 'Reintentar',
      DBL_CHECK_RESUME: 'Reanudar',
      DBL_CHECK_OK: 'Confirmar',
      REMOVE_CLOSE_BTN: 'Cerrar',
    },
  },

  // MQTT Debug modal
  mqttDebug: {
    title: 'Registro de depuración MQTT',
    searchPlaceholder: 'Buscar tema o carga útil...',
    noMessages: 'Aún no se han registrado mensajes',
    startLoggingHint: 'Haga clic en «Iniciar registro» para empezar a capturar mensajes MQTT',
    noMessagesMatch: 'Ningún mensaje coincide con su filtro',
    adjustFilterHint: 'Pruebe a ajustar su búsqueda o los criterios del filtro',
    incoming: 'Entrante',
    outgoing: 'Saliente',
    loggingStopped: 'Registro detenido',
    loggingActive: 'Registro activo - los mensajes se actualizarán automáticamente',
    startLogging: 'Iniciar registro',
    stopLogging: 'Detener registro',
    clearLog: 'Borrar registro',
    topic: 'Tema',
    timestamp: 'Marca de tiempo',
    direction: 'Dirección',
    all: 'Todos',
  },

  // Printer File Manager modal (printer internal storage)
  printerFiles: {
    title: 'Gestor de archivos',
    storageUsed: 'Usado:',
    storageFree: 'Libre:',
    filterPlaceholder: 'Filtrar archivos...',
    deleteButton: 'Eliminar',
    deleteFiles: 'Eliminar {{count}} archivos',
    deleteFileConfirm: '¿Eliminar "{{name}}"? Esto no se puede deshacer.',
    deleteFilesConfirm: '¿Eliminar {{count}} archivos seleccionados? Esto no se puede deshacer.',
    noFiles: 'No hay archivos en la impresora',
    loadingFiles: 'Cargando archivos...',
    failedToLoad: 'Error al cargar los archivos',
    toast: {
      filesDeleted: 'Se eliminaron {{count}} archivo(s)',
      deleteFailed: 'Error al eliminar: {{error}}',
    },
  },

  // Confirmations
  confirm: {
    delete: '¿Está seguro de que desea eliminar esto?',
    unsavedChanges: 'Tiene cambios sin guardar. ¿Está seguro de que desea salir?',
    clearQueue: '¿Está seguro de que desea vaciar la cola?',
  },

  // Login page
  login: {
    title: 'Inicio de sesión en Bambuddy',
    subtitle: 'Inicie sesión en su cuenta',
    username: 'Nombre de usuario',
    usernamePlaceholder: 'Introduzca su nombre de usuario',
    usernameOrEmail: 'Nombre de usuario o correo',
    usernameOrEmailPlaceholder: 'Nombre de usuario o @ correo',
    password: 'Contraseña',
    passwordPlaceholder: 'Introduzca su contraseña',
    signIn: 'Iniciar sesión',
    signingIn: 'Iniciando sesión...',
    rememberMe: 'Recordarme',
    forgotPassword: '¿Olvidó su contraseña?',
    autologinFailed: 'El inicio de sesión SSO automático falló. Elija un proveedor abajo para continuar.',
    localDisabledNotice: 'El inicio de sesión local está deshabilitado. Use uno de los proveedores SSO de abajo.',
    loginSuccess: 'Sesión iniciada correctamente',
    loginFailed: 'Error al iniciar sesión',
    enterCredentials: 'Introduzca el nombre de usuario y la contraseña',
    enterEmail: 'Introduzca su dirección de correo',
    oidcLoginFailed: 'Error en el inicio de sesión con OIDC',
    oidcErrors: {
      providerError: 'El proveedor de identidad devolvió un error',
      missingParameters: 'A la respuesta de OIDC le faltan parámetros obligatorios',
      invalidState: 'El estado de OIDC no es válido o ya se ha usado',
      stateExpired: 'La sesión de inicio de sesión con OIDC ha caducado — inténtelo de nuevo',
      providerNotFound: 'Proveedor OIDC no encontrado',
      discoveryFailed: 'Error al obtener el documento de descubrimiento de OIDC',
      invalidDiscovery: 'El documento de descubrimiento de OIDC no es válido',
      networkError: 'Error de red durante el intercambio de tokens de OIDC',
      badResponse: 'Respuesta inesperada durante el intercambio de tokens de OIDC',
      noIdToken: 'El proveedor OIDC no devolvió un token de ID',
      validationFailed: 'Error en la validación del token de OIDC',
      nonceMismatch: 'Discrepancia de nonce de OIDC — posible ataque de repetición',
      missingSubClaim: 'Al token de OIDC le falta el claim sub',
      noLinkedAccount: 'Ninguna cuenta local está vinculada a esta identidad de OIDC',
      accountInactive: 'Su cuenta está inactiva',
      userResolutionFailed: 'Error al resolver su cuenta',
      internalError: 'Se produjo un error interno durante el inicio de sesión con OIDC',
      tokenExchangeFailed: 'Error en el intercambio de tokens de OIDC',
    },
    forgotPasswordTitle: 'Contraseña olvidada',
    forgotPasswordMessage: 'Si ha olvidado su contraseña, póngase en contacto con el administrador de su sistema para restablecerla.',
    forgotPasswordEmailMessage: 'Introduzca su dirección de correo y le enviaremos una contraseña nueva.',
    emailAddress: 'Dirección de correo',
    emailPlaceholder: 'su.correo@ejemplo.com',
    cancel: 'Cancelar',
    sending: 'Enviando...',
    sendResetEmail: 'Enviar correo de restablecimiento',
    howToReset: 'Cómo restablecer su contraseña:',
    resetStep1: 'Póngase en contacto con su administrador de Bambuddy',
    resetStep2: 'Pídale que restablezca su contraseña en la gestión de usuarios',
    resetStep3: 'Puede establecerle una nueva contraseña temporal',
    resetStep4: 'Inicie sesión con la nueva contraseña y cámbiela en Ajustes',
    gotIt: 'Entendido',
    resetPassword: {
      title: 'Establecer nueva contrase\u00f1a',
      subtitle: 'Introduzca y confirme su nueva contrase\u00f1a a continuaci\u00f3n.',
      newPassword: 'Nueva contrase\u00f1a',
      newPasswordPlaceholder: 'Al menos 8 caracteres',
      confirmPassword: 'Confirmar contrase\u00f1a',
      confirmPasswordPlaceholder: 'Repita la nueva contrase\u00f1a',
      saving: 'Guardando\u2026',
      submit: 'Establecer nueva contrase\u00f1a',
      backToLogin: 'Volver al inicio de sesi\u00f3n',
      passwordsDoNotMatch: 'Las contrase\u00f1as no coinciden',
      passwordTooShort: 'La contrase\u00f1a debe tener al menos 8 caracteres',
      resetFailed: 'Error al restablecer la contrase\u00f1a. Es posible que el enlace haya caducado.',
    },
    twoFA: {
      title: 'Autenticación de dos factores',
      subtitle: 'Su cuenta está protegida con 2FA. Introduzca el código de verificación a continuación.',
      methodAuthenticator: 'Aplicación de autenticación',
      methodEmail: 'Código por correo',
      methodBackup: 'Código de respaldo',
      instructionsTotp: 'Abra su aplicación de autenticación e introduzca el código de 6 dígitos para Bambuddy.',
      instructionsEmail: 'Se ha enviado un código de 6 dígitos a su dirección de correo. Caduca en 10 minutos.',
      instructionsEmailNotSent: 'Haga clic en el botón de abajo para recibir un código de verificación por correo.',
      instructionsBackup: 'Introduzca uno de sus códigos de recuperación de respaldo de 8 caracteres. Cada código solo se puede usar una vez.',
      sendCodeButton: 'Enviar código por correo',
      sendingCode: 'Enviando...',
      resendCode: 'Reenviar código',
      codeLabel: 'Código de verificación',
      backupCodeLabel: 'Código de respaldo',
      codePlaceholder: '000000',
      backupCodePlaceholder: 'XXXXXXXX',
      verifyButton: 'Verificar',
      verifyingButton: 'Verificando...',
      backToLogin: '← Volver al inicio de sesión',
      orContinueWith: 'o continuar con',
      signInWith: 'Iniciar sesión con {{provider}}',
      enterCode: 'Introduzca el código de verificación',
      sendCodeFailed: 'Error al enviar el código de verificación',
      invalidCode: 'Código no válido. Inténtelo de nuevo.',
    },

  },

  // Setup page
  setup: {
    title: 'Configuración de Bambuddy',
    subtitle: 'Configure la autenticación para su instancia de Bambuddy',
    enableAuth: 'Activar la autenticación',
    adminAccount: 'Cuenta de administrador',
    adminAccountDesc: 'Si ya existen usuarios administradores, la autenticación se activará usando las cuentas de administrador existentes. Deje los campos de abajo vacíos para usar los administradores existentes, o introduzca nuevas credenciales para crear un nuevo usuario administrador.',
    adminUsername: 'Nombre de usuario del administrador',
    adminPassword: 'Contraseña del administrador',
    optionalIfAdminExists: '(opcional si existen usuarios administradores)',
    adminUsernamePlaceholder: 'Introduzca el nombre de usuario del administrador (opcional)',
    adminPasswordPlaceholder: 'Introduzca la contraseña del administrador (opcional)',
    confirmPassword: 'Confirmar contraseña',
    confirmPasswordPlaceholder: 'Confirme la contraseña del administrador',
    settingUp: 'Configurando...',
    completeSetup: 'Completar la configuración',
    toast: {
      authEnabledAdminCreated: 'Autenticación activada y usuario administrador creado',
      authEnabledExistingAdmins: 'Autenticación activada usando los usuarios administradores existentes',
      setupCompleted: 'Configuración completada',
      enterBothCredentials: 'Introduzca el nombre de usuario y la contraseña del administrador, o deje ambos vacíos para usar los usuarios administradores existentes',
      passwordsDoNotMatch: 'Las contraseñas no coinciden',
      passwordTooShort: 'La contraseña debe tener al menos 6 caracteres',
    },
  },

  // Password change
  changePassword: {
    title: 'Cambiar contraseña',
    currentPassword: 'Contraseña actual',
    currentPasswordPlaceholder: 'Introduzca la contraseña actual',
    newPassword: 'Nueva contraseña',
    newPasswordPlaceholder: 'Introduzca la nueva contraseña (mín. 6 caracteres)',
    confirmPassword: 'Confirmar nueva contraseña',
    confirmPasswordPlaceholder: 'Confirme la nueva contraseña',
    passwordsDoNotMatch: 'Las contraseñas no coinciden',
    passwordTooShort: 'La contraseña debe tener al menos 6 caracteres',
    changing: 'Cambiando...',
    success: 'Contraseña cambiada correctamente',
    failed: 'Error al cambiar la contraseña',
  },

  // Plate detection alert
  plateAlert: {
    title: '¡Impresión en pausa!',
    message: 'Se han detectado objetos en la cama de impresión. La impresión se ha pausado automáticamente. Despeje la cama y reanude la impresión.',
    understand: 'Entendido',
  },

  // Camera page
  camera: {
    title: 'Vista de la cámara',
    invalidPrinterId: 'ID de impresora no válido',
    live: 'En directo',
    snapshot: 'Captura',
    restartStream: 'Reiniciar la transmisión',
    refreshSnapshot: 'Actualizar la captura',
    fullscreen: 'Pantalla completa',
    exitFullscreen: 'Salir de la pantalla completa',
    connectingToCamera: 'Conectando con la cámara...',
    capturingSnapshot: 'Capturando imagen...',
    connectionLost: 'Conexión perdida',
    connectionFailed: 'Error de conexión con la cámara',
    reconnecting: 'Reconectando en {{countdown}} s... (intento {{attempt}}/{{max}})',
    reconnectNow: 'Reconectar ahora',
    cameraUnavailable: 'Cámara no disponible',
    cameraUnavailableDesc: 'Asegúrese de que la impresora está encendida y conectada.',
    noCamera: 'No hay cámara disponible',
    retry: 'Reintentar',
    cameraStream: 'Transmisión de la cámara',
    zoomOut: 'Alejar',
    zoomIn: 'Acercar',
    resetZoom: 'Restablecer el zoom',
    recording: 'Grabando',
    startRecording: 'Iniciar grabación',
    stopRecording: 'Detener grabación',
    chamberLight: 'Conmutar la luz de la cámara',
    unavailable: 'Cámara no disponible',
    diagnose: {
      button: 'Diagnosticar',
      modalTitle: 'Diagnóstico de la cámara',
      running: 'Ejecutando el diagnóstico...',
      runFailed: 'No se pudo ejecutar el diagnóstico: {{error}}',
      retry: 'Ejecutar de nuevo',
      stage: {
        tcp_reachable: 'Accesibilidad de la red',
        first_frame: 'Captura de fotogramas',
        live_stream_active: 'Transmisión en directo activa',
      },
      summary: {
        all_ok: 'La cámara funciona. El diagnóstico completó todas las etapas correctamente.',
        live_stream_active_healthy: 'La cámara está transmitiendo actualmente con fotogramas recientes — no se necesita ninguna prueba.',
        printer_unreachable: 'La impresora no es accesible. Compruebe la dirección IP, la conexión de red y que la impresora está encendida.',
        camera_port_closed: 'La impresora es accesible pero el puerto de la cámara está cerrado. Asegúrese de que el modo solo LAN y el modo desarrollador están activados en los ajustes de la impresora.',
        no_frame: 'Se conectó a la cámara pero no se recibieron fotogramas. Inténtelo de nuevo o compruebe que la cámara está activada en los ajustes de la impresora.',
        unknown_failure: 'El diagnóstico de la cámara falló por un motivo desconocido. Consulte el registro de soporte para más detalles.',
      },
      meta: {
        protocol: 'Protocolo',
        port: 'Puerto',
        profile: 'Perfil',
      },
    },
  },

  // Groups management
  groups: {
    title: 'Gestión de grupos',
    subtitle: 'Gestione los grupos de permisos para el control de acceso',
    backToSettings: 'Volver a Ajustes',
    createGroup: 'Crear grupo',
    noPermission: 'No tiene permiso para acceder a esta página.',
    system: 'Sistema',
    noDescription: 'Sin descripción',
    usersCount: '{{count}} usuarios',
    permissionsCount: '{{count}} permisos',
    edit: 'Editar',
    delete: 'Eliminar',
    toast: {
      created: 'Grupo creado correctamente',
      updated: 'Grupo actualizado correctamente',
      deleted: 'Grupo eliminado correctamente',
      enterGroupName: 'Introduzca un nombre de grupo',
    },
    modal: {
      editGroup: 'Editar grupo',
      createGroup: 'Crear grupo',
      cancel: 'Cancelar',
      saving: 'Guardando...',
      creating: 'Creando...',
      saveChanges: 'Guardar cambios',
    },
    form: {
      groupName: 'Nombre del grupo',
      groupNamePlaceholder: 'Introduzca el nombre del grupo',
      systemGroupWarning: 'Los nombres de los grupos del sistema no se pueden cambiar',
      description: 'Descripción',
      descriptionPlaceholder: 'Introduzca una descripción (opcional)',
      permissions: 'Permisos ({{count}} seleccionados)',
    },
    deleteModal: {
      title: 'Eliminar grupo',
      message: '¿Está seguro de que desea eliminar este grupo? Los usuarios de este grupo perderán estos permisos.',
      confirm: 'Eliminar grupo',
    },
    editor: {
      title: 'Editar grupo',
      createTitle: 'Crear grupo',
      search: 'Buscar permisos...',
      selectAll: 'Seleccionar todo',
      clearAll: 'Borrar todo',
      permissionsSelected: '{{count}} seleccionados',
      noResults: 'Ningún permiso coincide con su búsqueda',
      websocketHint: 'Necesario para las actualizaciones en vivo. Sin este permiso, la interfaz recurre al sondeo periódico.',
    },
  },

  // Users management
  users: {
    title: 'Gestión de usuarios',
    subtitle: 'Gestione los usuarios y su acceso a su instancia de Bambuddy',
    backToSettings: 'Volver a Ajustes',
    createUser: 'Crear usuario',
    noPermission: 'No tiene permiso para acceder a esta página.',
    admin: 'Administrador',
    noGroups: 'Sin grupos',
    active: 'Activo',
    inactive: 'Inactivo',
    edit: 'Editar',
    delete: 'Eliminar',
    system: 'Sistema',
    noGroupsAvailable: 'No hay grupos disponibles',
    table: {
      username: 'Nombre de usuario',
      groups: 'Grupos',
      status: 'Estado',
      actions: 'Acciones',
    },
    toast: {
      created: 'Usuario creado correctamente',
      updated: 'Usuario actualizado correctamente',
      deleted: 'Usuario eliminado correctamente',
      fillRequired: 'Rellene todos los campos obligatorios',
      passwordsDoNotMatch: 'Las contraseñas no coinciden',
      passwordTooShort: 'La contraseña debe tener al menos 6 caracteres',
      ldapProvisioned: 'Usuario LDAP "{{username}}" aprovisionado',
    },
    modal: {
      createUser: 'Crear usuario',
      editUser: 'Editar usuario',
      cancel: 'Cancelar',
      creating: 'Creando...',
      saving: 'Guardando...',
      saveChanges: 'Guardar cambios',
      advancedAuthSubtitle: 'con autenticación avanzada',
      // LDAP manual provisioning (#1298)
      tabsAriaLabel: 'Origen del usuario',
      localTab: 'Local',
      ldapTab: 'LDAP',
      ldapSearchLabel: 'Buscar en el directorio',
      ldapSearchPlaceholder: 'Escriba un nombre de usuario, nombre o correo...',
      ldapMinChars: 'Escriba al menos 2 caracteres para buscar',
      ldapTypeToSearch: 'Empiece a escribir para buscar en el directorio LDAP',
      ldapSearching: 'Buscando en el directorio...',
      ldapNoResults: 'No hay usuarios coincidentes en el directorio',
      ldapSearchError: 'Error en la búsqueda en el directorio. Compruebe el estado del servidor LDAP.',
      ldapAlreadyProvisioned: 'Ya aprovisionado',
      ldapSelectedLabel: 'Seleccionado',
      ldapProvision: 'Aprovisionar usuario',
      ldapProvisioning: 'Aprovisionando...',
      ldapErrorProvision: 'Error en el aprovisionamiento. Compruebe el estado del servidor LDAP e inténtelo de nuevo.',
    },
    form: {
      username: 'Nombre de usuario',
      usernamePlaceholder: 'Introduzca el nombre de usuario',
      email: 'Correo',
      emailPlaceholder: 'usuario@ejemplo.com',
      password: 'Contraseña',
      passwordPlaceholder: 'Introduzca la contraseña',
      confirmPassword: 'Confirmar contraseña',
      confirmPasswordPlaceholder: 'Confirme la contraseña',
      newPasswordPlaceholder: 'Introduzca la nueva contraseña',
      confirmNewPasswordPlaceholder: 'Confirme la nueva contraseña',
      leaveBlankToKeep: 'dejar en blanco para conservar la actual',
      groups: 'Grupos',
      optional: 'opcional',
      autoGeneratedPassword: 'Se generará automáticamente una contraseña segura y se enviará al usuario por correo.',
      passwordManagedByAdvancedAuth: 'La contraseña la gestiona la autenticación avanzada. Use «Restablecer contraseña» para enviar una nueva contraseña al usuario por correo.',
      resetPassword: 'Restablecer contraseña',
      resettingPassword: 'Restableciendo la contraseña...',
    },
    deleteModal: {
      title: 'Eliminar usuario',
      message: '¿Está seguro de que desea eliminar este usuario? Esta acción no se puede deshacer.',
      confirm: 'Eliminar usuario',
    },
  },

  // Stream overlay
  streamOverlay: {
    title: 'Superposición de transmisión',
    invalidPrinterId: 'ID de impresora no válido',
    cameraStream: 'Transmisión de la cámara',
    progress: 'Progreso',
    eta: 'Tiempo estimado',
    printerIdle: 'La impresora está inactiva',
    printerOffline: 'Impresora desconectada',
    builder: {
      title: 'Superposición de emisión',
      description: 'Crea la URL de una superposición de emisión: una vista de cámara a pantalla completa con los datos de impresión en directo encima, para OBS, una pantalla de pared o cualquier fuente de navegador. Elige los campos y copia la URL.',
      printer: 'Impresora',
      size: 'Tamaño del texto',
      sizeSmall: 'Pequeño',
      sizeMedium: 'Mediano',
      sizeLarge: 'Grande',
      fps: 'Fotogramas por segundo',
      fpsHint: 'Las cámaras A1 y P1 no pasan de unos 5 fps, sea cual sea el valor solicitado.',
      token: 'Token de superposición (opcional)',
      tokenHint: 'Solo hace falta si el inicio de sesión está activado: OBS no tiene sesión propia. Crea uno arriba con el ámbito Superposición de emisión.',
      tokenWarning: 'Esta URL contiene un token: cualquiera que pueda leerla verá la emisión y el nombre del archivo. Revoca el token para cortar el acceso.',
      fields: 'Campos que mostrar',
      fieldPrinter: 'Nombre de la impresora',
      fieldFilename: 'Nombre del archivo',
      fieldStatus: 'Estado',
      fieldProgress: 'Barra de progreso',
      fieldLayers: 'Número de capas',
      fieldEta: 'Tiempo restante y hora de fin',
      fieldCamera: 'Imagen de la cámara',
      chamberHint: 'La temperatura de la cámara de impresión solo aparece en modelos con sensor real: las P1 y A1 informan un valor sin sentido, así que se omite.',
      urlTitle: 'URL de la superposición',
      open: 'Abrir',
      showPreview: 'Mostrar vista previa',
      hidePreview: 'Ocultar vista previa',
      previewTitle: 'Vista previa de la superposición',
    },
    status: {
      printing: 'Imprimiendo',
      paused: 'En pausa',
      finished: 'Finalizada',
      failed: 'Fallida',
      idle: 'Inactiva',
      unknown: 'Desconocido',
    },
  },

  // Profiles
  profiles: {
    title: 'Perfiles',
    subtitle: 'Gestione sus preajustes del laminador y las calibraciones de avance de presión',
    tabs: {
      bambuCloud: 'Bambu Cloud',
      orcaCloud: 'Orca Cloud',
      local: 'Perfiles locales',
      kprofiles: 'Perfiles K',
    },
    orcaCloud: {
      connectedAs: 'Conectado como',
      connectedShort: 'Conectado a Orca Cloud',
      logout: 'Desconectar',
      noLogoutPermission: 'No tienes permiso para desconectar',
      noConnectPermission: 'No tienes permiso para conectar a Orca Cloud',
      retry: 'Reintentar',
      connectButton: 'Conectar Orca Cloud',
      connect: {
        title: 'Conectar a Orca Cloud',
        description: 'Inicia sesión en tu cuenta Orca Cloud para sincronizar tus perfiles de slicer en Bambuddy.',
      },
      device: {
        title: 'Aprueba Bambuddy en Orca Cloud',
        instruction: 'Abre Orca Cloud y aprueba este código. Bambuddy se conecta automáticamente en cuanto lo apruebes.',
        codeLabel: 'Tu código de emparejamiento',
        openButton: 'Abrir la página de aprobación de Orca Cloud',
        manualHint: 'O ve a {{url}} e introduce el código de arriba.',
        waiting: 'Esperando a que apruebes…',
        cancel: 'Cancelar',
      },
      profiles: {
        title: 'Tus perfiles de Orca Cloud ({{count}})',
        refresh: 'Actualizar',
        empty: 'Aún no hay perfiles en tu cuenta de Orca Cloud.',
      },
      toast: {
        disconnected: 'Desconectado de Orca Cloud',
      },
      errors: {
        startFailed: 'No se pudo iniciar el inicio de sesión de Orca Cloud.',
        denied: 'El emparejamiento fue rechazado en Orca Cloud.',
        expired: 'El código de emparejamiento caducó. Haz clic en Conectar para intentarlo de nuevo.',
        pollFailed: 'Se perdió la conexión mientras se esperaba la aprobación. Inténtalo de nuevo.',
      },
    },
    localProfiles: {
      title: 'Perfiles locales',
      subtitle: 'Importe y gestione preajustes del laminador desde OrcaSlicer',
      import: 'Importar perfiles',
      importDesc: 'Suelte aquí archivos .bbscfg, .bbsflmt, .orca_filament, .zip o .json',
      importing: 'Importando...',
      search: 'Buscar preajustes locales...',
      noPresets: 'Aún no hay preajustes locales',
      noSearchResults: 'Ningún preajuste coincide con su búsqueda',
      badge: 'Local',
      edit: 'Editar',
      delete: 'Eliminar',
      cancel: 'Cancelar',
      deleteConfirmTitle: 'Eliminar preajuste',
      deleteConfirm: '¿Está seguro de que desea eliminar este preajuste? Esto no se puede deshacer.',
      source: 'Origen',
      inheritsFrom: 'Hereda de',
      filamentType: 'Tipo',
      vendor: 'Proveedor',
      compatiblePrinters: 'Impresoras',
      nozzleTemp: 'Temp. de la boquilla',
      cost: 'Coste',
      density: 'Densidad',
      pressureAdvance: 'Avance de presión',
      filament: 'Filamento',
      process: 'Proceso',
      printer: 'Impresora',
      toast: {
        importSuccess: '{{count}} preajuste(s) importado(s)',
        importSkipped: '{{count}} preajuste(s) omitido(s) (duplicados)',
        importError: '{{count}} error(es) durante la importación',
        deleted: 'Preajuste eliminado',
        updated: 'Preajuste actualizado',
      },
    },
    connectedAs: 'Conectado como',
    signInExpiredTitle: 'La sesión de Bambu Cloud ha caducado',
    signInExpiredBody: 'Bambu Lab ya no acepta el token almacenado. Inicie sesión de nuevo para restaurar los perfiles en la nube, las importaciones de MakerWorld y las comprobaciones de firmware.',
    logout: 'Cerrar sesión',
    noLogoutPermission: 'No tiene permiso para cerrar sesión',
    failedToLoad: 'Error al cargar los perfiles',
    retry: 'Reintentar',
    time: {
      justNow: 'Ahora mismo',
      minsAgo: 'hace {{count}} min',
      hoursAgo: 'hace {{count}} h',
      daysAgo: 'hace {{count}} d',
    },
    toast: {
      loggedOut: 'Sesión cerrada',
    },
    login: {
      title: 'Conectarse a Bambu Cloud',
      subtitle: 'Sincronice sus preajustes del laminador entre dispositivos',
      email: 'Correo',
      password: 'Contraseña',
      region: 'Región',
      regionGlobal: 'Global',
      regionChina: 'China',
      verificationCode: 'Código de verificación',
      totpCode: 'Código del autenticador',
      checkEmail: 'Compruebe su correo ({{email}}) en busca de un código de 6 dígitos',
      enterTotpHint: 'Introduzca el código de 6 dígitos de su aplicación de autenticación',
      accessToken: 'Token de acceso',
      accessTokenHint: 'Pegue su token de acceso de Bambu Cloud. Las cuentas de la región de China deben usar esta vía (vinculada al teléfono — inicio de sesión por correo no disponible). Consulte la wiki para saber cómo obtener el token de las cookies de MakerWorld.',
      back: 'Atrás',
      loginButton: 'Iniciar sesión',
      verifyButton: 'Verificar',
      setTokenButton: 'Establecer token',
      useToken: 'Usar token de acceso en su lugar',
      captchaTitle: 'Bambu Cloud solicita un CAPTCHA',
      captchaBody: 'Bambu está exigiendo un CAPTCHA a tu red antes de aceptar un inicio de sesión, y ese desafío no se puede responder desde Bambuddy. Tu correo y tu contraseña no son el problema. El bloqueo está ligado a tu dirección IP pública y suele desaparecer solo en unas horas; reintentar repetidamente lo prolonga. Para entrar ahora, usa un token de acceso obtenido en una sesión del navegador.',
      useEmail: 'Iniciar sesión con correo en su lugar',
      toast: {
        loggedIn: 'Sesión iniciada correctamente',
        codeSent: 'Código de verificación enviado a su correo',
        enterTotp: 'Introduzca el código de su aplicación de autenticación',
        tokenSet: 'Token establecido correctamente',
      },
    },
    presets: {
      myPreset: 'Mi preajuste (editable)',
      duplicate: 'Duplicar',
      editable: 'Editable',
      failedToLoadDetails: 'Error al cargar los detalles del preajuste',
      deleteConfirm: '¿Eliminar este preajuste?',
      deleteWarning: 'Esto eliminará permanentemente "{{name}}" de Bambu Cloud. Esto no se puede deshacer.',
      noDuplicatePermission: 'No tiene permiso para duplicar preajustes',
      noEditPermission: 'No tiene permiso para editar preajustes',
      noDeletePermission: 'No tiene permiso para eliminar preajustes',
      types: {
        filament: 'Preajuste de filamento',
        printer: 'Preajuste de impresora',
        process: 'Preajuste de proceso',
      },
      toast: {
        deleted: 'Preajuste eliminado',
        created: 'Preajuste creado',
        updated: 'Preajuste actualizado',
        duplicated: 'Preajuste duplicado',
        fieldAdded: 'Campo "{{key}}" añadido',
        exported: 'Preajuste exportado',
      },
      baseLabel: 'Base: {{name}}',
      currentLabel: 'Actual: {{name}}',
      newPreset: 'Nuevo preajuste',
      editPreset: 'Editar preajuste',
      duplicatePreset: 'Duplicar preajuste',
      createNewPreset: 'Crear nuevo preajuste',
      customizeSettings: 'Personalice los ajustes de su nuevo preajuste',
      compareWithBase: 'Comparar con el preajuste base',
      compare: 'Comparar',
      // CreatePresetModal - Basic Info
      basePreset: 'Preajuste base',
      selectBasePreset: 'Seleccionar preajuste base...',
      presetName: 'Nombre del preajuste',
      myCustomPreset: 'Mi preajuste personalizado',
      inheritsFrom: 'Hereda de',
      dropJsonToImport: 'Suelte un JSON para importar',
      // CreatePresetModal - Tabs
      tabs: {
        common: 'Común',
        allFields: 'Todos los campos',
      },
      // CreatePresetModal - All Fields Tab
      availableFields: 'Campos disponibles',
      searchFieldsPlaceholder: 'Buscar campos...',
      noMatchingFields: 'No hay campos coincidentes',
      allFieldsAdded: 'Todos los campos añadidos',
      addCustomField: 'Añadir campo personalizado',
      yourOverrides: 'Sus anulaciones',
      noOverridesYet: 'Aún no hay anulaciones',
      clickFieldsToAdd: 'Haga clic en los campos de la izquierda para añadirlos',
      saveAsTemplate: 'Guardar como plantilla',
      jsonTip: 'Consejo: arrastre y suelte un archivo .json en cualquier parte de esta ventana para importar ajustes',
    },
    cloudView: {
      searchPlaceholder: 'Buscar preajustes...',
      templates: 'Plantillas',
      refresh: 'Actualizar',
      newPreset: 'Nuevo preajuste',
      clearFilters: 'Borrar filtros',
      // Compare mode
      compareMode: 'Modo de comparación',
      selectAnotherPreset: 'Seleccione otro preajuste de {{type}}',
      clickTwoPresets: 'Haga clic en dos preajustes del mismo tipo para compararlos',
      selectFirst: '1. Seleccione el primero',
      selectSecond: '2. Seleccione el segundo',
      compareNow: 'Comparar ahora',
      // Status row
      lastSynced: 'Última sincronización:',
      showingCount: 'Mostrando {{showing}} de {{total}} preajustes',
      noPresetsFound: 'No se encontraron preajustes',
      // Column headers
      columns: {
        filament: 'Filamento',
        process: 'Proceso',
        printer: 'Impresora',
      },
      noFilamentPresets: 'No hay preajustes de filamento',
      noProcessPresets: 'No hay preajustes de proceso',
      noPrinterPresets: 'No hay preajustes de impresora',
      // Filters
      filters: {
        type: 'Tipo',
        owner: 'Propietario',
        printer: 'Impresora',
        nozzle: 'Boquilla',
        filament: 'Filamento',
        layer: 'Capa',
        all: 'Todos',
        myPresets: 'Mis preajustes',
        builtIn: 'Integrados',
        process: 'Proceso',
      },
      // Permissions
      noTemplatesPermission: 'No tiene permiso para gestionar plantillas',
      noRefreshPermission: 'No tiene permiso para actualizar perfiles',
      noCreatePermission: 'No tiene permiso para crear preajustes',
    },
    templates: {
      title: 'Plantillas rápidas',
      noTemplates: 'Aún no hay plantillas',
      createFirst: 'Cree plantillas desde el editor de preajustes',
      typeFilter: 'Tipo:',
      deleteTitle: 'Eliminar plantilla',
      deleteWarning: 'Esta acción no se puede deshacer',
      deleteConfirm: '¿Está seguro de que desea eliminar "{{name}}"?',
      namePlaceholder: 'Nombre de la plantilla',
      descriptionPlaceholder: 'Descripción',
      settingsJson: 'Ajustes (JSON)',
      fieldsCount: '{{count}} campos',
      shownInModals: 'Mostrada en las ventanas',
      hiddenInModals: 'Oculta en las ventanas',
      apply: 'Aplicar',
      toast: {
        deleted: 'Plantilla eliminada',
        updated: 'Plantilla actualizada',
        created: 'Plantilla creada',
        applied: 'Plantilla aplicada',
      },
    },
  },

  // Support/Debug
  support: {
    debugLoggingActive: 'El registro de depuración está activo',
    manageLogs: 'Gestionar',
    collectItem7: 'Conectividad de la impresora y versiones de firmware',
    collectItem8: 'Estado de las integraciones (Spoolman, MQTT, HA)',
    collectItem9: 'Interfaces de red (solo subredes)',
    collectItem10: 'Versiones de los paquetes de Python',
    collectItem11: 'Comprobaciones de estado de la base de datos',
    collectItem12: 'Detalles del entorno de Docker',
    bundleGenerating: 'Generando paquete...',
    bundleStepConnection: 'Ejecutando comprobaciones de conectividad de impresoras',
    bundleStepVirtualPrinters: 'Ejecutando comprobaciones de configuración de impresoras virtuales',
    bundleStepLogScan: 'Analizando los registros recientes en busca de problemas conocidos',
    bundleStepBuild: 'Creando el archivo ZIP del paquete de soporte',
  },

  // File manager
  fileManager: {
    variants: {
      badge: '{{count}} versiones',
      groupAction: 'Agrupar como versiones',
      groupTooltip: 'Marcar estos archivos como el mismo trabajo laminado para distintas impresoras',
      grouped: '{{count}} archivos agrupados como versiones',
      printAlternatives: 'Imprimir ({{count}} alternativas)',
    },
    title: 'Gestor de archivos',
    subtitle: 'Organice y gestione sus archivos de impresión',
    uploadFiles: 'Subir archivos',
    newFolder: 'Nueva carpeta',
    folderName: 'Nombre de la carpeta',
    folderNamePlaceholder: 'p. ej., Piezas funcionales',
    renameFile: 'Renombrar archivo',
    renameFolder: 'Renombrar carpeta',
    invalidFilenameChar: 'El carácter "{{char}}" no está permitido en nombres de archivos de impresión. La tarjeta SD de la impresora rechaza: < > : " / \\ | ? *',
    moveFiles: 'Mover {{count}} archivo(s)',
    rootNoFolder: 'Raíz (sin carpeta)',
    current: 'actual',
    linkFolder: 'Vincular carpeta',
    linkFolderDescription: 'Vincule "{{name}}" a un proyecto o archivo para un acceso rápido.',
    project: 'Proyecto',
    archive: 'Archivo',
    noProjectsFound: 'No se encontraron proyectos',
    noArchivesFound: 'No se encontraron archivos',
    unlink: 'Desvincular',
    link: 'Vincular',
    dragDropFiles: 'Arrastre y suelte archivos aquí',
    dropFilesHere: 'Suelte archivos aquí',
    releaseToUpload: 'Suelte para subir',
    orClickToBrowse: 'o haga clic para examinar',
    allFileTypesSupported: 'Se admiten todos los tipos de archivo. Los archivos ZIP se extraerán.',
    zipFilesDetected: 'Archivos ZIP detectados',
    zipExtractOptions: 'Los archivos ZIP se extraerán. Elija cómo gestionar la estructura de carpetas:',
    preserveZipStructure: 'Conservar la estructura de carpetas del ZIP',
    createFolderFromZip: 'Crear una carpeta a partir del nombre del archivo ZIP',
    stlThumbnailGeneration: 'Generación de miniaturas STL',
    zipMayContainStl: 'Los archivos ZIP pueden contener archivos STL. Se pueden generar miniaturas durante la extracción.',
    thumbnailsCanBeGenerated: 'Se pueden generar miniaturas para los archivos STL. Los modelos grandes pueden tardar más en procesarse.',
    generateThumbnailsForStl: 'Generar miniaturas para los archivos STL',
    threemfDetected: 'Archivos 3MF detectados',
    threemfExtractionInfo: 'El modelo de impresora, el material, el color y los ajustes de impresión se extraerán automáticamente de los archivos 3MF.',
    willBeExtracted: 'Se extraerá',
    filesExtracted: '{{count}} archivos extraídos',
    uploadComplete: 'Subida completada: {{succeeded}} con éxito',
    uploadFailed: 'Error al subir',
    zipFilesFailed: '{{count}} archivos fallidos',
    uploading: 'Subiendo...',
    changeLink: 'Cambiar enlace...',
    linkTo: 'Vincular a...',
    linkToProjectOrArchive: 'Vincular a un proyecto o archivo',
    generateThumbnail: 'Generar miniatura',
    generateThumbnails: 'Generar miniaturas',
    generateThumbnailsForMissing: 'Generar miniaturas para los archivos STL que no las tienen',
    gridView: 'Vista de cuadrícula',
    listView: 'Vista de lista',
    lowDiskSpaceWarning: 'Advertencia de poco espacio en disco',
    lowDiskSpaceDetails: 'Solo {{free}} libres de {{total}} en total. El umbral está establecido en {{threshold}} GB en los ajustes.',
    files: 'Archivos',
    folders: 'Carpetas',
    size: 'Tamaño',
    free: 'Libre',
    allFiles: 'Todos los archivos',
    allExternal: 'Externos',
    externalIsEmpty: 'Sin archivos externos',
    externalEmptyDescription: 'Los archivos de tus carpetas externas vinculadas aparecerán aquí.',
    wrap: 'Ajustar',
    enableTextWrapping: 'Activar el ajuste de texto',
    disableTextWrapping: 'Desactivar el ajuste de texto',
    collapse: 'Contraer',
    collapseFoldersByDefault: 'Contraer las carpetas de forma predeterminada',
    expandFoldersByDefault: 'Expandir las carpetas de forma predeterminada',
    folderSort: 'Ordenar carpetas',
    folderSortByName: 'Por nombre',
    folderSortByActivity: 'Por actividad reciente',
    dragToResizeTooltip: 'Arrastre para redimensionar, doble clic para restablecer',
    searchFiles: 'Buscar archivos...',
    searchSubfoldersHint: 'Incluyendo subcarpetas',
    readme: {
      truncated: 'Truncado',
      show: 'Mostrar README',
      hide: 'Ocultar README',
      label: 'README',
    },
    tags: {
      title: 'Etiquetas',
      subtitle: 'Etiqueta archivos para filtros transversales — juguetes, apto para niños, solo PETG, lo que necesites.',
      manage: 'Etiquetas',
      manageTitle: 'Gestionar el catálogo de etiquetas',
      add: 'Nueva etiqueta',
      edit: 'Renombrar etiqueta',
      name: 'Nombre',
      fileCount: 'Archivos',
      empty: 'Aún no hay etiquetas. Crea una para empezar a etiquetar archivos.',
      noMatches: 'No hay etiquetas que coincidan.',
      createPlaceholder: 'p. ej. juguetes, apto-niños, petg',
      createButton: 'Crear',
      nameRequired: 'El nombre es obligatorio.',
      searchPlaceholder: 'Filtrar etiquetas...',
      created: 'Etiqueta creada.',
      updated: 'Etiqueta renombrada.',
      deleted: 'Etiqueta eliminada.',
      saveFailed: 'No se pudo guardar la etiqueta.',
      deleteFailed: 'No se pudo eliminar la etiqueta.',
      applyFailed: 'No se pudieron aplicar las etiquetas.',
      applyAdd: 'Añadir etiquetas',
      applyRemove: 'Quitar etiquetas',
      applyAddSuccess: 'Se añadieron {{count}} etiqueta(s) en {{files}} archivo(s).',
      applyRemoveSuccess: 'Se quitaron {{count}} etiqueta(s) en {{files}} archivo(s).',
      actionAdd: 'Añadir a los archivos seleccionados',
      actionRemove: 'Quitar de los archivos seleccionados',
      tagAction: 'Etiquetar',
      bulkTitle: 'Etiquetar {{count}} archivo(s) seleccionado(s)',
      bulkTooltip: 'Añadir o quitar etiquetas en cada archivo seleccionado.',
      noPermission: 'No tienes permiso para etiquetar archivos.',
      filterLabel: 'Filtrando por:',
      clearAll: 'Limpiar todo',
      confirmDelete: '¿Eliminar la etiqueta "{{name}}"?',
      confirmDeleteMessage: 'Esto elimina la etiqueta del catálogo. Los archivos conservan sus otras etiquetas.',
      confirmDeleteInUseMessage: 'Esta etiqueta está en {{count}} archivo(s). Al eliminarla desaparece de todos; los archivos en sí no se tocan.',
      editAria: 'Editar {{name}}',
      deleteAria: 'Eliminar {{name}}',
    },
    allTypes: 'Todos los tipos',
    prints: 'Impresiones',
    ascending: 'Ascendente',
    descending: 'Descendente',
    showModified: 'Mostrar fechas de modificación',
    hideModified: 'Ocultar fechas de modificación',
    lastModified: 'Última modificación',
    lastActivity: 'Última actividad',
    resultsCount: '{{showing}} de {{total}} archivos',
    selectAll: 'Seleccionar todo',
    deselectAll: 'Deseleccionar todo',
    selected: '{{count}} seleccionados',
    adding: 'Añadiendo...',
    loadingFiles: 'Cargando archivos...',
    folderIsEmpty: 'La carpeta está vacía',
    noFilesYet: 'Aún no hay archivos',
    folderEmptyDescription: 'Suba archivos o mueva archivos a esta carpeta para empezar.',
    noFilesDescription: 'Suba archivos para empezar a organizar sus archivos relacionados con la impresión.',
    noMatchingFiles: 'No hay archivos coincidentes',
    noMatchingFilesDescription: 'Ningún archivo coincide con su búsqueda o los criterios de filtro actuales.',
    clearFilters: 'Borrar filtros',
    printedCount: 'Impreso {{count}} veces',
    uploadedBy: 'Subido por',
    deleteFolder: 'Eliminar carpeta',
    deleteFile: 'Eliminar archivo',
    deleteFilesCount: 'Eliminar {{count}} archivos',
    deleteFolderConfirm: '¿Está seguro de que desea eliminar esta carpeta? Todos los archivos que contiene también se eliminarán.',
    deleteFileConfirm: '¿Está seguro de que desea eliminar este archivo?',
    deleteFilesConfirm: '¿Está seguro de que desea eliminar {{count}} archivos seleccionados? Esta acción no se puede deshacer.',
    deleting: 'Eliminando...',
    noPermissionRenameFolder: 'No tiene permiso para renombrar carpetas',
    noPermissionLinkFolder: 'No tiene permiso para vincular carpetas',
    noPermissionDeleteFolder: 'No tiene permiso para eliminar carpetas',
    onlyEmptyFoldersDeletable: 'Solo puede eliminar carpetas vacías',
    noPermissionPrint: 'No tiene permiso para imprimir',
    noPermissionAddToQueue: 'No tiene permiso para añadir a la cola',
    noPermissionSlice: 'No tiene permiso para laminar archivos',
    noPermissionDownload: 'No tiene permiso para descargar archivos',
    noPermissionRenameFile: 'No tiene permiso para renombrar este archivo',
    noPermissionGenerateThumbnail: 'No tiene permiso para generar miniaturas',
    noPermissionDeleteFile: 'No tiene permiso para eliminar este archivo',
    noPermissionCreateFolder: 'No tiene permiso para crear carpetas',
    noPermissionUpload: 'No tiene permiso para subir archivos',
    noPermissionMoveFiles: 'No tiene permiso para mover archivos',
    noPermissionDeleteFiles: 'No tiene permiso para eliminar archivos',
    // External folder
    linkExternal: 'Vincular externa',
    linkExternalFolder: 'Vincular carpeta externa',
    linkExternalFolderDescription: 'Monte un directorio del host (NAS, USB, recurso compartido de red) en el gestor de archivos. Los archivos no se copian — se accede a ellos directamente desde la ruta original.',
    externalFolderNamePlaceholder: 'p. ej., Impresiones del NAS',
    externalPath: 'Ruta del host',
    externalPathHelp: 'Ruta absoluta al directorio en el host de Docker. Debe estar montado por enlace en el contenedor.',
    readOnly: 'Solo lectura',
    readOnlyHelp: 'impide las subidas y eliminaciones',
    showHiddenFiles: 'Mostrar archivos ocultos (con punto)',
    externalFolder: 'Carpeta externa',
    scanFolder: 'Escanear',
    toast: {
      folderCreated: 'Carpeta creada',
      openInSlicerFailed: 'No se pudo abrir en el laminador',
      folderDeleted: 'Carpeta eliminada',
      fileDeleted: 'Archivo eliminado',
      filesDeleted: 'Se eliminaron {{count}} archivos',
      filesMoved: 'Archivos movidos',
      folderLinked: 'Carpeta vinculada',
      folderUnlinked: 'Carpeta desvinculada',
      externalFolderLinked: 'Carpeta externa vinculada y escaneada',
      folderScanned: 'Escaneo completado: {{added}} añadidos, {{removed}} eliminados',
      addedToQueue: 'Se añadieron {{count}} archivo(s) a la cola',
      addedToQueuePartial: 'Se añadieron {{added}} archivo(s), {{failed}} fallidos',
      failedToAddToQueue: 'Error al añadir los archivos: {{error}}',
      fileRenamed: 'Archivo renombrado',
      folderRenamed: 'Carpeta renombrada',
      thumbnailsGenerated: 'Se generaron {{count}} miniatura(s)',
      thumbnailsGeneratedPartial: 'Se generaron {{succeeded}} miniatura(s), {{failed}} fallidas',
      noStlMissingThumbnails: 'No hay archivos STL sin miniaturas',
      failedToGenerateThumbnails: 'Error al generar las miniaturas: {{error}}',
      thumbnailGenerated: 'Miniatura generada',
      failedToGenerateThumbnail: 'Error al generar la miniatura: {{error}}',
    },
  },

  // Projects
  projects: {
    parentLabel: 'Proyecto principal',
    parentNone: 'Ninguno (proyecto de nivel superior)',
    parentHint: 'Anida este proyecto en otro para que sus cifras se sumen en un proyecto maestro',
    partOf: 'Parte de {{name}}',
    subProjectCount: '{{count}} subproyectos',
    subProjectsOf: 'Subproyectos de {{name}}',
    title: 'Proyectos',
    subtitle: 'Organice y haga el seguimiento de sus proyectos de impresión 3D',
    newProject: 'Nuevo proyecto',
    editProject: 'Editar proyecto',
    deleteProject: 'Eliminar proyecto',
    projectName: 'Nombre del proyecto',
    description: 'Descripción',
    noProjects: 'Aún no hay proyectos',
    noProjectsFiltered: 'No hay proyectos {{status}}',
    noProjectsFilteredHelp: 'No tiene ningún proyecto {{status}}. Los proyectos aparecerán aquí cuando cambie su estado.',
    createFirst: 'Cree su primer proyecto para empezar a organizar impresiones relacionadas, hacer el seguimiento del progreso y gestionar sus construcciones.',
    createFirstButton: 'Cree su primer proyecto',
    create: 'Crear',
    files: 'Archivos',
    prints: 'Impresiones',
    plates: 'camas',
    parts: 'piezas',
    lastModified: 'Última modificación',
    deleteConfirm: '¿Está seguro de que desea eliminar este proyecto? Los archivos y los elementos de la cola se desvincularán pero no se eliminarán.',
    addFiles: 'Añadir archivos',
    removeFile: 'Quitar archivo',
    viewDetails: 'Ver detalles',
    // Modal fields
    namePlaceholder: 'p. ej., Construcción de Voron 2.4',
    descriptionPlaceholder: 'Descripción opcional...',
    urlLabel: 'URL',
    urlPlaceholder: 'https://makerworld.com/...',
    urlInvalid: 'La URL debe empezar por http:// o https://',
    openExternalUrl: 'Abrir la URL del proyecto',
    coverImageLabel: 'Foto de portada',
    coverImageAlt: 'Foto de portada del proyecto',
    coverImageUpload: 'Subir',
    coverImageReplace: 'Reemplazar',
    coverImageRemove: 'Quitar',
    color: 'Color',
    targetPlates: 'Camas objetivo',
    targetPlatesPlaceholder: 'p. ej., 25',
    targetPlatesHelp: 'Número de trabajos de impresión',
    targetParts: 'Piezas objetivo',
    targetPartsPlaceholder: 'p. ej., 150',
    targetPartsHelp: 'Total de objetos necesarios',
    targetSets: 'Copias por archivo',
    targetSetsPlaceholder: 'p. ej., 10',
    targetSetsHelp: 'Veces que debe imprimirse cada archivo imprimible',
    tagsLabel: 'Etiquetas (separadas por comas)',
    tagsPlaceholder: 'p. ej., voron, funcional, regalo',
    dueDate: 'Fecha límite',
    priority: 'Prioridad',
    priorityLow: 'Baja',
    priorityNormal: 'Normal',
    priorityHigh: 'Alta',
    priorityUrgent: 'Urgente',
    // Status
    statusActive: 'Activo',
    statusCompleted: 'Completado',
    statusArchived: 'Archivado',
    done: 'Hecho',
    completed: 'completadas',
    failed: 'fallidas',
    inQueue: 'en cola',
    noPrintsYet: 'Aún no hay impresiones',
    // Footer stats
    printJobs: 'Trabajos de impresión (camas)',
    partsPrinted: 'Piezas impresas',
    failedParts: 'Piezas fallidas',
    // Actions
    import: 'Importar',
    export: 'Exportar',
    importProject: 'Importar proyecto',
    exportAll: 'Exportar todos los proyectos',
    loading: 'Cargando proyectos...',
    // Permissions
    noEditPermission: 'No tiene permiso para editar proyectos',
    noDeletePermission: 'No tiene permiso para eliminar proyectos',
    noCreatePermission: 'No tiene permiso para crear proyectos',
    noImportPermission: 'No tiene permiso para importar proyectos',
    noExportPermission: 'No tiene permiso para exportar proyectos',
    // Toast
    toast: {
      created: 'Proyecto creado',
      updated: 'Proyecto actualizado',
      deleted: 'Proyecto eliminado',
      imported: 'Proyecto importado',
      multipleImported: '{{count}} proyectos importados',
      importFailed: 'Error en la importación',
      exported: 'Proyectos exportados (solo metadatos)',
    },
  },

  // Project detail page
  projectDetail: {
    notFound: 'Proyecto no encontrado',
    backToProjects: 'Volver a Proyectos',
    export: 'Exportar',
    exportProject: 'Exportar proyecto',
    noExportPermission: 'No tiene permiso para exportar proyectos',
    noEditPermission: 'No tiene permiso para editar proyectos',
    partOf: 'Parte de:',
    priorityLabel: 'Prioridad:',
    noPrints: 'Aún no hay impresiones en este proyecto',
    status: {
      active: 'Activo',
      completed: 'Completado',
      archived: 'Archivado',
    },
    priority: {
      low: 'Baja',
      normal: 'Normal',
      high: 'Alta',
      urgent: 'Urgente',
    },
    dueDate: {
      overdue: 'Atrasado',
      today: 'Vence hoy',
      daysLeft: 'Quedan {{count}} días',
    },
    progress: {
      platesProgress: 'Progreso de camas',
      partsProgress: 'Progreso de piezas',
      setsProgress: 'Juegos completos',
      sets: 'juegos',
      setsHint: 'Un juego está completo cuando cada archivo imprimible alcanza este número',
      printJobs: 'trabajos de impresión',
      parts: 'piezas',
      percentComplete: '{{percent}}% completado',
      remaining: '{{count}} restantes',
    },
    stats: {
      printJobs: 'Trabajos de impresión',
      total: 'total',
      failed: '{{count}} fallidos',
      partsPrinted: '{{count}} piezas impresas',
      printTime: 'Tiempo de impresión',
      filamentUsed: 'Filamento usado',
    },
    cost: {
      title: 'Seguimiento de costes',
      filamentCost: 'Coste del filamento',
      energy: 'Energía',
      totalCost: 'Coste total',
      total: 'Total',
      includesBom: 'incl. lista de materiales',
      budget: 'Presupuesto',
      remaining: 'Restante',
    },
    subProjects: {
      title: 'Subproyectos ({{count}})',
      jobs: '{{count}} trabajos',
    },
    rollup: {
      title: 'Incluyendo {{count}} subproyectos',
      progress: 'Progreso general',
      percentComplete: '{{percent}}% completado',
    },
    notes: {
      title: 'Notas',
      noEditPermission: 'No tiene permiso para editar notas',
      placeholder: 'Añada notas sobre este proyecto...',
      empty: 'Aún no hay notas. Haga clic en Editar para añadir notas.',
    },
    files: {
      title: 'Archivos',
      linkFolders: 'Vincule carpetas del gestor de archivos',
      forQuickAccess: 'a este proyecto para un acceso rápido.',
      fileCount: '{{count}} archivo(s)',
      empty: 'No hay carpetas vinculadas. Vaya al gestor de archivos y vincule una carpeta a este proyecto.',
      printedCount: 'Impreso {{count}} veces',
      progressTooltip: '{{done}} de {{target}} impresiones completadas',
      noFiles: 'No hay archivos en esta carpeta.',
    },
    bom: {
      title: 'Lista de materiales',
      acquired: '{{completed}}/{{total}} adquiridos',
      showAll: 'Mostrar todo',
      hideDone: 'Ocultar los hechos',
      addPart: 'Añadir pieza',
      noAddPermission: 'No tiene permiso para añadir piezas',
      partNamePlaceholder: 'Nombre de la pieza (p. ej., tornillos M3x8)',
      partName: 'Nombre de la pieza',
      qty: 'Cant.',
      price: 'Precio ({{currency}})',
      sourcingUrlPlaceholder: 'URL de aprovisionamiento (opcional)',
      remarksPlaceholder: 'Observaciones (opcional)',
      deletePart: 'Eliminar pieza',
      deleteConfirm: '¿Está seguro de que desea eliminar "{{name}}"?',
      noUpdatePermission: 'No tiene permiso para actualizar piezas',
      noEditPermission: 'No tiene permiso para editar piezas',
      noDeletePermission: 'No tiene permiso para eliminar piezas',
      totalCost: 'Coste total:',
      empty: 'No hay piezas en la lista de materiales. Añada hardware, componentes electrónicos u otros componentes para hacer el seguimiento de lo que hay que aprovisionar.',
    },
    timeline: {
      title: 'Cronología de actividad',
      empty: 'Aún no hay actividad.',
    },
    template: {
      saveAsTemplate: 'Guardar como plantilla',
      noCreatePermission: 'No tiene permiso para crear plantillas',
    },
    queue: {
      title: 'Cola',
      viewAll: 'Ver todo',
      printing: '{{count}} imprimiendo',
      queued: '{{count}} en cola',
    },
    prints: {
      title: 'Impresiones ({{count}})',
    },
    toast: {
      projectUpdated: 'Proyecto actualizado',
      partAdded: 'Pieza añadida',
      partRemoved: 'Pieza eliminada',
      exportFailed: 'Error en la exportación',
      projectExported: 'Proyecto exportado',
      templateCreated: 'Plantilla creada',
    },
  },

  // System info
  system: {
    title: 'Información del sistema',
    version: 'Versión',
    uptime: 'Tiempo de actividad',
    cpuUsage: 'Uso de la CPU',
    memoryUsage: 'Uso de la memoria',
    diskUsage: 'Uso del disco',
    networkInfo: 'Información de la red',
    logs: 'Registros',
    debugMode: 'Modo de depuración',
    enableDebug: 'Activar el registro de depuración',
    disableDebug: 'Desactivar el registro de depuración',
    downloadLogs: 'Descargar registros',
    clearLogs: 'Borrar registros',
    dockerInfo: 'Información de Docker',
    containerName: 'Nombre del contenedor',
    imageName: 'Nombre de la imagen',
    platform: 'Plataforma',
    architecture: 'Arquitectura',
  },

  // Sponsor surface (System page)
  sponsors: {
    sectionTitle: 'Independiente y financiado por la comunidad',
    tagline: 'Bambuddy es gratuito y seguirá siéndolo porque hay quien decide apoyarlo. Sin VC, sin dependencia de la nube.',
    viewSupporters: 'Ver patrocinadores',
    toastPrints: 'Has completado {{count}} impresiones con Bambuddy. Bambuddy sigue siendo gratuito gracias a quienes lo apoyan.',
    toastCost: 'Has rastreado {{total}} de filamento con Bambuddy. Mira quién mantiene el proyecto independiente.',
    toastArchives: '{{count}} impresiones archivadas con Bambuddy. Mira quién lo mantiene independiente.',
    toastAnniversary: '¡Un año con Bambuddy! Mira quién mantiene el proyecto independiente.',
    toastVersionUpdate: 'Actualizado a v{{version}}. Bambuddy sigue siendo gratuito gracias a quienes lo apoyan.',
    toastBusiness: '¿Usas Bambuddy en {{count}} impresoras? Existe un plan de soporte para equipos: correcciones prioritarias, facturación y contacto directo con el responsable del proyecto.',
    businessCta: 'Bambuddy para empresas',
    businessTitle: 'Bambuddy para empresas',
    businessTagline: 'Estás gestionando {{count}} impresoras. Hay soporte prioritario, licencias comerciales y facturación disponibles para equipos y granjas de impresión.',
  },

  // Library (K Profiles)
  library: {
    title: 'Biblioteca de filamentos',
    addFilament: 'Añadir filamento',
    editFilament: 'Editar filamento',
    deleteFilament: 'Eliminar filamento',
    vendor: 'Proveedor',
    material: 'Material',
    color: 'Color',
    kFactor: 'Factor K',
    temperature: 'Temperatura',
    noFilaments: 'No hay filamentos en la biblioteca',
    deleteConfirm: '¿Está seguro de que desea eliminar este filamento?',
    importFromPrinter: 'Importar desde la impresora',
    exportToFile: 'Exportar a un archivo',
    runWithPipeline: {
      actionLabel: 'Ejecutar con pipeline',
      noPermission: 'No tienes permiso para ejecutar pipelines',
      modalTitle: 'Ejecutar con pipeline',
      confirmTitle: 'Confirmar ejecución',
      confirmIntro: 'El pre-vuelo encontró problemas con esta ejecución',
      sourceHint: 'Origen',
      pipelineHint: 'Pipeline',
      targetHint: 'Destino',
      pipelineListAria: 'Pipelines disponibles',
      runAnyway: 'Ejecutar de todos modos',
      loading: 'Cargando…',
      empty: 'Aún no hay pipelines guardadas. Abre el diálogo Cortar y haz clic en "Guardar como pipeline" para crear una.',
      noTarget: 'Sin impresora de destino',
      noTargetMessage: 'Esta pipeline no tiene impresora de destino. Ábrela en Ajustes para elegir una.',
      copies: 'Copias',
      copiesHint: 'máx {{n}}',
      classTarget: 'Cualquier {{model}}',
      toast: {
        started: 'Ejecución de pipeline iniciada',
        failed: 'No se pudo iniciar la ejecución',
      },
      issue: {
        printerNotSet: 'Sin impresora de destino en esta pipeline.',
        printerNotFound: 'La impresora de destino ya no existe.',
        printerDisabled: 'La impresora de destino está deshabilitada.',
        printerOffline: 'La impresora de destino está desconectada.',
        filamentType: 'Slot de filamento {{slot}}: esperado {{expected}}, AMS tiene {{actual}}',
        filamentColor: 'Slot de filamento {{slot}}: color distinto (esperado {{expected}}, AMS tiene {{actual}})',
        amsSlotMissing: 'Slot AMS {{slot}} no disponible en esta impresora',
        filamentUnverified: 'Slot de filamento {{slot}} proviene de un preset de nube / estándar y no se pudo verificar estáticamente.',
        noClassMatches: 'Ninguna impresora en esta instalación coincide con la clase de modelo objetivo de la pipeline ({{expected}}).',
        classNotSet: 'El destino de la pipeline es una clase de impresora pero no se eligió ningún modelo.',
      },
    },
  },

  // Slice (slicer-API integration via SliceModal)
  slicerSettings: {
    presetValuesOutdatedSidecar: 'Se muestran los valores predeterminados del laminador: tu sidecar es más antiguo que esta función y no puede informar los valores de un perfil. Actualiza la imagen del sidecar para verlos. Todo lo que no cambies seguirá usando el perfil.',
    presetValuesNotConfigured: 'Se muestran los valores predeterminados del laminador: no hay ningún sidecar configurado, así que no se pueden leer los valores de un perfil. Todo lo que no cambies seguirá usando el perfil.',
    presetValuesSidecarUnavailable: 'Se muestran los valores predeterminados del laminador: el sidecar no respondió, así que no se pueden leer los valores de un perfil. Todo lo que no cambies seguirá usando el perfil.',
    presetValuesUnavailable: 'Se muestran los valores predeterminados del laminador: no se pudieron leer los del perfil seleccionado. Todo lo que no cambies seguirá usando el perfil.',
    filamentDefault: 'Predeterminado',
    fromFile: 'del archivo',
    fromFileHint: 'El diseñador cambió esto en el archivo de origen. Su valor es {{value}}.',
    fromFilePrinterCoupled: 'impresora del diseñador',
    fromFilePrinterCoupledHint: 'Ajustado para la impresora para la que se diseñó este archivo: en la tuya puede ser incorrecto o estar fuera de rango.',
    useFromFile: 'Usar el valor del archivo de origen para {{option}}',
    otherFromFile: 'Otros ajustes de este archivo',
    loading: 'Cargando ajustes del laminador…',
    mode: {
      simple: 'Simple',
      advanced: 'Avanzado',
      expert: 'Experto',
    },
    searchPlaceholder: 'Buscar ajustes',
    resetAll: 'Restablecer {{count}}',
    resetOption: 'Restablecer al valor predeterminado',
    noMatches: 'Ningún ajuste coincide con esta búsqueda.',
  },
  slice: {
    filamentSlotUnset: 'sin definir',
    processSettingsEmbedded: 'No se usa mientras «Usar los ajustes integrados del archivo» está activo: los ajustes del propio archivo rigen este corte.',
    processSettingsInactive: 'Inactivo',
    presetsHidden: '{{count}} ocultos',
    showAllPresets: 'Mostrar todos',
    showFewerPresets: 'Mostrar menos',
    processSettings: 'Ajustes de proceso',
    processSettingsHint: 'Ajusta el perfil seleccionado para este corte. Todo lo que no toques se mantiene como lo define el perfil.',
    processSettingsChanged: '{{count}} cambiados',
    processSettingsUnchanged: 'Valores del perfil',
    title: 'Laminar modelo',
    action: 'Laminar',
    actionAll: 'Laminar las {{count}} bandejas',
    actionAllTitle: 'Laminar todas las bandejas en una salida única de varias bandejas (un solo archivo). La selección de filamento cubre cada hueco que defina el proyecto.',
    allPlatesToggle: 'Laminar las {{count}} bandejas',
    slicing: 'Laminando…',
    printer: 'Perfil de impresora',
    process: 'Perfil de proceso',
    filament: 'Perfil de filamento',
    filamentSlot: 'Filamento {{index}} ({{type}})',
    selectPreset: '— Seleccione un preajuste —',
    loadingPresets: 'Cargando preajustes…',
    analyzingPlateFilaments: 'Analizando los filamentos de la cama…',
    analyzingPlateFilamentsHint: 'Ejecutando un laminado de vista previa para descubrir qué ranuras del AMS usa esta cama. Después se almacena en caché — volver a abrir es instantáneo.',
    previewToast: 'Analizando {{name}} — {{elapsed}}',
    previewWithProgress: 'Analizando {{name}} — {{stage}} ({{percent}}%) — {{elapsed}}',
    notUsedByPlate: '— no usado por esta cama',
    noPresetsForSlot: 'No hay preajustes disponibles',
    otherPrinters: 'Otras impresoras',
    presetsLoadFailed: 'Error al cargar los preajustes. Abra Ajustes → Perfiles para importarlos primero.',
    refreshPresets: 'Actualizar',
    refreshPresetsTitle: 'Actualizar preajustes — recuperar los listados más recientes de la nube y los paquetes (úselo tras eliminar un preajuste en Bambu Studio o Bambu Handy)',
    allPresetsRequired: 'Deben seleccionarse todos los preajustes',
    useEmbedded: 'Usar la configuración incorporada del archivo',
    useEmbeddedHint: 'Laminar tal como lo configuró el diseñador (perímetros, relleno, filamento) en lugar de los perfiles de arriba. Disponible porque tu impresora coincide con la del archivo.',
    autoOrient: 'Orientar los objetos automáticamente',
    autoOrientHint: 'El laminador gira cada objeto hacia su mejor cara de impresión antes de laminar. Sustituye la orientación del archivo.',
    autoArrange: 'Organizar automáticamente en la base',
    autoArrangeHint: 'El laminador coloca los objetos para que dejen de solaparse. Sustituye la disposición del archivo.',
    enqueuing: 'Enviando el trabajo de laminado…',
    queued: 'En cola…',
    failed: 'Error al laminar. Consulte los registros del contenedor auxiliar del laminador.',
    startedToast: 'Laminando {{name}} en segundo plano…',
    queuedToast: 'En cola: {{name}} — {{elapsed}}',
    runningToast: 'Laminando {{name}} — {{elapsed}}',
    runningWithProgress: '{{name}} — {{stage}} ({{percent}}%) — {{elapsed}}',
    runningWithProgressMultiPlate: 'Bandeja {{plateIndex}} de {{plateCount}} • {{name}} — {{stage}} ({{percent}}%) — {{elapsed}}',
    completedToast: '{{name}} laminado',
    externalWriteFallbackToast: 'Guardado en la biblioteca interna: no se pudo escribir en la carpeta externa',
    failedTitle: 'Error al laminar',
    failedToast: 'Error al laminar {{name}}: {{detail}}',
    tier: {
      local: 'Importado',
      cloud: 'Bambu Cloud',
      orcaCloud: 'Orca Cloud',
      standard: 'Estándar',
    },
    cloud: {
      notAuthenticated: 'Inicie sesión en Bambu Cloud (Ajustes → Perfiles → Bambu Cloud) para ver sus preajustes de la nube.',
      expired: 'La sesión de Bambu Cloud ha caducado — inicie sesión de nuevo para actualizar sus preajustes de la nube.',
      unreachable: 'Bambu Cloud no es accesible en este momento. Los preajustes locales y estándar siguen funcionando.',
    },
    orcaCloud: {
      notAuthenticated: 'Inicia sesión en Orca Cloud (Perfiles → Orca Cloud) para ver tus preajustes Orca.',
      expired: 'Sesión de Orca Cloud caducada — inicia sesión de nuevo para actualizar tus preajustes Orca.',
      unreachable: 'Orca Cloud no está disponible ahora. Otros preajustes siguen funcionando.',
    },
    bedType: {
      label: 'Cama de impresión',
      auto: 'Automático (usar el preajuste de proceso)',
      coolPlate: 'Cool Plate',
      coolPlateSuperTack: 'Cool Plate SuperTack',
      engineering: 'Engineering Plate',
      highTemp: 'High Temp Plate',
      texturedPEI: 'Textured PEI Plate',
      smoothPEI: 'Smooth PEI Plate',
    },
    // Slicer Pipelines (#1425) — apply a saved bundle or save the current pick.
    pipelines: {
      label: 'Pipeline',
      applyAria: 'Aplicar pipeline',
      applyPrompt: 'Aplicar pipeline…',
      empty: 'Sin pipelines guardadas',
      saveButton: 'Guardar como pipeline',
      saveTitle: 'Guardar la selección actual de los cuatro ajustes como pipeline reutilizable',
      namePlaceholder: 'Nombre de la pipeline',
      nameAria: 'Nuevo nombre de pipeline',
      toast: {
        applied: '"{{name}}" aplicada',
        saved: 'Pipeline guardada',
        saveFailed: 'Error al guardar',
      },
    },
  },

  // Spoolman
  spoolman: {
    title: 'Integración con Spoolman',
    enabled: 'Spoolman activado',
    url: 'URL de Spoolman',
    connected: 'Conectado',
    disconnected: 'No conectado',
    testConnection: 'Probar conexión',
    sync: 'Sincronizar',
    syncing: 'Sincronizando...',
    lastSync: 'Última sincronización',
    linkToSpoolman: 'Vincular a Spoolman',
    openInSpoolman: 'Abrir en Spoolman',
    unlinkSpool: 'Desvincular bobina',
    unlinkConfirmTitle: '¿Desasignar la bobina?',
    unlinkConfirmMessage: 'Esto quitará la bobina de esta ranura. Los datos de la bobina en sí permanecerán sin cambios.',
    selectSpool: 'Seleccionar bobina',
    noUnlinkedSpools: 'No hay bobinas sin asignar disponibles',
    linkSuccess: 'Bobina asignada correctamente',
    linkFailed: 'Error al asignar la bobina',
    unlinkSuccess: 'Bobina desasignada correctamente',
    unlinkFailed: 'Error al desasignar la bobina',
    linkedSpool: 'Bobina asignada',
    spoolId: 'ID de la bobina',
    fillSourceLabel: '(Spoolman)',
    weight: 'Peso',
    remaining: 'Restante',
    disableWeightSync: 'Desactivar la sincronización del peso estimado del AMS',
    disableWeightSyncDesc: 'No actualizar la capacidad restante a partir de las estimaciones del AMS. Use esto si prefiere el seguimiento del uso de Spoolman a las estimaciones del AMS basadas en porcentajes. Las bobinas nuevas seguirán usando la estimación del AMS como su peso inicial.',
    reportPartialUsage: 'Informar del uso parcial en las impresiones fallidas',
    reportPartialUsageDesc: 'Cuando una impresión falla o se cancela, informar del filamento estimado usado hasta ese punto según el progreso de las capas.',
  },

  locations: {
    title: 'Ubicaciones de almacenamiento',
    subtitle: 'Gestione estantes, cajones y otros lugares físicos para sus bobinas',
    add: 'Añadir ubicación',
    addShort: 'Añadir',
    edit: 'Editar ubicación',
    name: 'Nombre',
    spools: 'Bobinas',
    empty: 'Aún no hay ubicaciones de almacenamiento. Cree su primer estante o cajón.',
    manage: 'Ubicaciones',
    createPlaceholder: 'p. ej. Estante A, Cajón 1',
    nameRequired: 'El nombre de la ubicación es obligatorio',
    created: 'Ubicación creada',
    updated: 'Ubicación actualizada',
    deleted: 'Ubicación eliminada',
    saveFailed: 'No se pudo guardar la ubicación',
    deleteFailed: 'No se pudo eliminar la ubicación',
    deleteBlocked: 'Retire todas las bobinas de esta ubicación antes de eliminarla',
    confirmDelete: '¿Eliminar «{{name}}»?',
    confirmDeleteMessage: 'Esta ubicación se eliminará del catálogo. Mueva las bobinas primero.',
  },

  // Inventory
  inventory: {
    title: 'Inventario de bobinas',
    subtitle: 'Gestione sus bobinas',
    addToInventory: 'Añadir al inventario',
    addToInventoryPending: 'Añadiendo...',
    addToInventorySuccess: 'Bobina añadida al inventario',
    addToInventoryFailed: 'No se pudo añadir la bobina al inventario',
    unknownSpoolTitle: 'Nuevo filamento detectado',
    unknownSpoolMessage: 'Se ha detectado una bobina con una etiqueta RFID desconocida en {{location}}. ¿Añadirla a su inventario ahora?',
    unknownSpoolSlot: 'Ranura',
    bulk: {
      selectAllVisible: 'Seleccionar todo lo visible',
      selectRow: 'Seleccionar fila',
      selectGroup: 'Seleccionar grupo',
      selectionCount: '{{count}} seleccionadas',
      edit: 'Editar',
      printLabels: 'Imprimir etiquetas',
      resetUsage: 'Restablecer uso',
      restore: 'Restaurar',
      archive: 'Archivar',
      delete: 'Eliminar',
      clearSelection: 'Limpiar selección',
      editTitle: 'Edición masiva de bobinas',
      editSubtitle: 'Se aplica a {{count}} bobinas seleccionadas. Solo se actualizan los campos marcados.',
      editHint: 'Escriba en un campo para marcarlo para actualización — solo se envían las filas marcadas. Los campos vacíos no cambian las bobinas (vaciar campos solo se hace por bobina).',
      useCustom: 'Usar "{{value}}"',
      toggleField: 'Activar la actualización de este campo',
      changeCount: 'Se actualizarán {{count}} campos.',
      applyPending: 'Aplicando...',
      applyButton: 'Aplicar a {{count}} bobinas',
      deleteTitle: 'Eliminar bobinas seleccionadas',
      archiveTitle: 'Archivar bobinas seleccionadas',
      restoreTitle: 'Restaurar bobinas seleccionadas',
      resetUsageTitle: 'Restablecer uso de bobinas seleccionadas',
      deleteMessage: '¿Eliminar permanentemente {{count}} bobinas? Esto no se puede deshacer.',
      archiveMessage: '¿Archivar {{count}} bobinas? Se pueden restaurar más tarde.',
      restoreMessage: '¿Restaurar {{count}} bobinas archivadas?',
      resetUsageMessage: '¿Restablecer el contador "Total consumido" en {{count}} bobinas? El peso restante se conserva.',
      updateSuccess: '{{count}} bobinas actualizadas',
      updateFailed: 'Actualización masiva fallida',
      updatePartial: '{{ok}} bobinas actualizadas, {{failed}} fallidas',
      updateAllFailed: 'Las {{count}} actualizaciones fallaron — la selección se mantiene para reintentar',
      deleteSuccess: '{{count}} bobinas eliminadas',
      deleteFailed: 'Eliminación masiva fallida',
      deletePartial: '{{ok}} bobinas eliminadas, {{failed}} fallidas',
      deleteAllFailed: 'Las {{count}} eliminaciones fallaron — la selección se mantiene para reintentar',
      archiveSuccess: '{{count}} bobinas archivadas',
      archiveFailed: 'Archivado masivo fallido',
      archivePartial: '{{ok}} bobinas archivadas, {{failed}} fallidas',
      archiveAllFailed: 'Los {{count}} archivados fallaron — la selección se mantiene para reintentar',
      restoreSuccess: '{{count}} bobinas restauradas',
      restoreFailed: 'Restauración masiva fallida',
      restorePartial: '{{ok}} bobinas restauradas, {{failed}} fallidas',
      restoreAllFailed: 'Las {{count}} restauraciones fallaron — la selección se mantiene para reintentar',
      invalidHex: 'Introduzca 6 caracteres hex (RRGGBB) u 8 (RRGGBBAA). De lo contrario el campo no se aplicará.',
    },
    spoolmanMixedContentTitle: 'Spoolman no se puede cargar por HTTPS — contenido mixto bloqueado por su navegador',
    spoolmanMixedContentBody: 'Bambuddy se sirve por HTTPS (mediante su proxy inverso), pero su URL de Spoolman sigue siendo HTTP sin cifrar. Los navegadores bloquean el contenido mixto por seguridad, por lo que la interfaz integrada de Spoolman no se puede mostrar. Spoolman debe ser accesible por HTTPS para que esto funcione.',
    spoolmanMixedContentFixReverseProxy: 'Ponga Spoolman tras el mismo proxy inverso que Bambuddy (Traefik / Nginx / Caddy) con HTTPS y luego actualice la URL de Spoolman en Ajustes a la nueva dirección HTTPS.',
    spoolmanMixedContentFixOpenNewTab: 'Como solución alternativa, abra Spoolman en una pestaña nueva del navegador por HTTP — las reglas de contenido mixto solo se aplican a los marcos integrados, por lo que una pestaña independiente sí funciona.',
    spoolmanOpenInNewTab: 'Abrir Spoolman en una pestaña nueva',
    labels: {
      title: 'Imprimir etiquetas de bobinas',
      selectedCount: '{{count}} seleccionadas',
      pickSpools: 'Elija para qué bobinas imprimir etiquetas:',
      monochrome: 'Monocromo (impresora en blanco y negro)',
      monochromeHint: 'Elimina la muestra de color y amplía el texto',
      searchPlaceholder: 'Buscar nombre, marca o n.º de ID',
      filterByMaterial: 'Material:',
      allMaterials: 'Todos',
      selectVisible: 'Seleccionar todas las visibles ({{count}})',
      deselectVisible: 'Deseleccionar las visibles',
      clearAll: 'Borrar todo',
      noSpoolsToShow: 'No hay bobinas que mostrar. Ajuste su filtro e inténtelo de nuevo.',
      noMatches: 'Ninguna bobina coincide con la búsqueda o el filtro actuales.',
      printOne: 'Imprimir etiqueta para esta bobina',
      printLabels: 'Imprimir etiquetas…',
      bulkTitle: 'Elija las bobinas para las que imprimir etiquetas de las {{count}} mostradas actualmente',
      noSpoolsTitle: 'No hay bobinas que etiquetar',
      error: 'No se pudieron generar las etiquetas: {{msg}}',
      sortBy: {
        label: 'Ordenar:',
        id: 'Por ID',
        color: 'Por color',
      },
      templates: {
        amsHolderSmall: {
          label: 'Soporte de AMS — pequeño (74 × 33 mm)',
          hint: 'Una etiqueta por página; coincide con la etiqueta imprimible del modelo 752566 de MakerWorld (AMS Filament Label Holder).',
        },
        amsHolderLarge: {
          label: 'Soporte de AMS — grande (75 × 55 mm)',
          hint: 'Una etiqueta por página; encaja en la variante con inserto de cartulina del AMS Filament Label Holder. Lo bastante amplia para muestra de color, marca, material, ID y código QR.',
        },
        box40x30: {
          label: 'Etiqueta de caja (40 × 30 mm)',
          hint: 'Una etiqueta por página; tamaño de rollo DK/Brother habitual, adecuado para etiquetas de bolsas de filamento y cajas de almacenamiento.',
        },
        box: {
          label: 'Box label (62 × 29 mm)',
          hint: 'Una etiqueta por página; dimensionada para las etiquetas pequeñas de Brother PT/QL y Dymo.',
        },
        averyL7160: {
          label: 'Avery L7160 — A4 sheet (38.1 × 63.5 mm × 21)',
          hint: 'Hojas de tamaño UE; 21 etiquetas por página A4.',
        },
        avery5160: {
          label: 'Avery 5160 — US Letter sheet (25.4 × 66.7 mm × 30)',
          hint: 'Hojas de tamaño EE. UU.; 30 etiquetas por página Letter.',
        },
      },
    },
    csv: {
      importButton: 'Importar CSV',
      exportButton: 'Exportar CSV',
      modalTitle: 'Importar bobinas desde CSV',
      selectFile: 'Elige un archivo CSV o arrástralo aquí',
      dragHint: 'Encabezado: material (obligatorio), brand, subtype, color_name, rgba, …',
      parsing: 'Leyendo archivo…',
      previewError: 'No se pudo leer el archivo CSV',
      validCount: '{{count}} válidas',
      errorCount: '{{count}} con error',
      skippedCount: '{{count}} omitidas',
      colRow: 'Fila',
      colStatus: 'Estado',
      colColor: 'Color',
      colorResolved: 'Color rellenado desde el catálogo',
      colorCrossMaterial: 'Color tomado de un material diferente — sin coincidencia exacta en el catálogo',
      duplicateExisting: 'Ya existe una bobina con este material, marca y color — se importará igualmente como una bobina nueva',
      spoolmanHint: 'En modo Spoolman, usa la importación/exportación CSV integrada de Spoolman.',
      importValidRows: 'Importar {{count}} filas válidas',
      noValidRows: 'Sin filas válidas',
      importing: 'Importando…',
      importSuccess: '{{count}} bobinas importadas',
      importError: 'Error al importar',
      exportError: 'Error al exportar',
    },
    addSpool: 'Añadir bobina',
    editSpool: 'Editar bobina',
    copySpool: 'Copiar bobina',
    material: 'Material',
    selectMaterial: 'Seleccionar material...',
    subtype: 'Subtipo',
    brand: 'Marca',
    searchBrand: 'Buscar marca...',
    useCustomBrand: 'Usar "{{brand}}"',
    useCustomMaterial: 'Usar material personalizado: {{material}}',
    suggestedOptions: 'Sugeridos',
    allOptions: 'Todos',
    colorName: 'Nombre del color',
    colorNamePlaceholder: 'Blanco jade, Rojo fuego...',
    color: 'Color',
    hexColor: 'Color hexadecimal',
    pickColor: 'Elegir color personalizado',
    labelWeight: 'Peso de la etiqueta',
    coreWeight: 'Peso de la bobina vacía',
    searchSpoolWeight: 'Buscar peso de la bobina...',
    weightUsed: 'Usado',
    currentWeight: 'Peso restante',
    measuredWeight: 'Peso medido',
    spoolName: 'Bobina',
    costPerKg: 'Coste por kg',
    storageLocation: 'Ubicación de almacenamiento',
    storageLocationPlaceholder: 'p. ej. Estante A, Cajón 1',
    openInInventory: 'Abrir en el inventario',
    measuredWeightError: 'El peso medido debe estar entre {{min}} g y {{max}} g.',
    slicerFilament: 'Filamento del laminador',
    slicerFilamentName: 'Nombre del preajuste del laminador',
    slicerPreset: 'Preajuste del laminador',
    searchPresets: 'Buscar preajustes de filamento...',
    selectedPreset: 'Seleccionado',
    noPresetsFound: 'No se encontraron preajustes',
    tempOverrides: 'Anulaciones de temperatura',
    note: 'Nota',
    notePlaceholder: 'Cualquier nota adicional sobre esta bobina...',
    // Per-spool category + low-stock threshold override (#729)
    category: 'Categoría',
    categoryPlaceholder: 'p. ej. Producción, Prototipo, Cliente A',
    categoryNone: 'Sin categoría',
    // #1400: storage-location filter chip
    storageLocationNone: 'Sin ubicación establecida',
    lowStockThresholdOverride: 'Umbral de existencias bajas (esta bobina)',
    lowStockThresholdOverrideHelp: 'Déjelo en blanco para usar el umbral global ({{global}}%).',
    // RFID button rename
    clearRfid: 'Borrar etiqueta RFID',
    rfidCleared: 'Etiqueta RFID borrada',
    archive: 'Archivar',
    restore: 'Restaurar',
    noSpools: 'Aún no hay bobinas. Añada su primera bobina para empezar.',
    noAvailableSpools: 'No hay bobinas disponibles. Añada una bobina a su inventario o desasigne una de otra ranura primero.',
    kProfiles: 'Perfiles K',
    addKProfile: 'Añadir perfil K',
    assignSpool: 'Asignar bobina',
    unassignSpool: 'Desasignar',
    assignSuccess: 'Bobina asignada y ranura del AMS configurada',
    assignPendingInsert: 'Asignada. La ranura se configurará cuando inserte la bobina.',
    assignFailed: 'Error al asignar la bobina',
    selectSpool: 'Seleccione una bobina para asignar a esta ranura',
    assigned: 'Asignada',
    assigning: 'Asignando...',
    searchSpools: 'Buscar bobinas...',
    showAllSpools: 'Mostrar todas las bobinas',
    spoolmanSpools: 'Bobinas de Spoolman',
    allMaterials: 'Todos los materiales',
    filterByBrand: 'Filtrar por marca...',
    showArchived: 'Mostrar archivadas',
    quickAdd: 'Añadir rápido (existencias)',
    quantity: 'Cantidad',
    stock: 'Existencias',
    configured: 'Configurada',
    spoolsCreated: '{{count}} bobinas creadas',
    spoolsPartiallyCreated: '{{created}} de {{total}} bobinas creadas (algunas fallaron)',
    spoolCreated: 'Bobina creada',
    spoolUpdated: 'Bobina actualizada',
    spoolDeleted: 'Bobina eliminada',
    deepLinkSpoolNotFound: 'Bobina no encontrada',
    deepLinkFetchFailed: 'No se pudo cargar la bobina — inténtelo de nuevo',
    spoolArchived: 'Bobina archivada',
    spoolRestored: 'Bobina restaurada',
    kProfileSaveFailed: 'No se pudieron guardar los ajustes del perfil K',
    syncWeightSpoolNotFound: 'Bobina no encontrada — es posible que se haya eliminado',
    syncWeightSpoolmanUnreachable: 'Spoolman no es accesible — inténtelo más tarde',
    syncWeightFailed: 'Error al sincronizar el peso',
    spoolmanUnreachable: 'Spoolman no es accesible — inténtelo de nuevo más tarde',
    deleteSpoolNotFound: 'Bobina no encontrada — es posible que ya se haya eliminado',
    deleteFailed: 'Error al eliminar la bobina',
    archiveSpoolNotFound: 'Bobina no encontrada — es posible que ya se haya eliminado',
    archiveFailed: 'Error al archivar la bobina',
    restoreSpoolNotFound: 'Bobina no encontrada — es posible que ya se haya eliminado',
    restoreFailed: 'Error al restaurar la bobina',
    saveFailed: 'Error al guardar los cambios',
    tagClearFailed: 'Error al borrar la etiqueta',
    deleteConfirm: '¿Está seguro de que desea eliminar esta bobina? Esto no se puede deshacer.',
    archiveConfirm: '¿Está seguro de que desea archivar esta bobina?',
    advancedSettings: 'Ajustes avanzados',
    // Tabs
    filamentInfoTab: 'Información del filamento',
    paProfileTab: 'Perfil PA',
    filamentInfo: 'Filamento',
    additional: 'Adicional',
    // Cloud
    loadingPresets: 'Cargando preajustes de la nube...',
    cloudConnected: 'Nube conectada',
    cloudNotConnected: 'Nube no conectada (usando los valores predeterminados)',
    // Colors
    recentColors: 'Recientes',
    searchColors: 'Buscar colores...',
    searchResults: 'Resultados de búsqueda',
    allColors: 'Todos los colores',
    commonColors: 'Colores comunes',
    showLess: 'Mostrar menos',
    showAll: 'Mostrar todos',
    noColorsFound: 'Ningún color coincide con su búsqueda',
    noResults: 'No se encontraron coincidencias',
    // Multi-color gradient + visual effect (#1154)
    extraColorsLabel: 'Colores adicionales',
    extraColorsPlaceholder: 'EC984C,#6CD4BC,A66EB9,D87694',
    extraColorsHint: 'Pegue de 2 a 8 paradas hexadecimales, separadas por comas. Se muestra como un degradado.',
    extraColorsInvalid: 'Se ignoró el hexadecimal no válido: {{tokens}}',
    colorEffectLabel: 'Efecto',
    colorEffect: {
      none: 'Ninguno',
      // Surface effects
      sparkle: 'Destellos',
      wood: 'Madera',
      marble: 'Mármol',
      glow: 'Brillante en la oscuridad',
      matte: 'Mate',
      // Sheen / finish variants
      silk: 'Seda',
      galaxy: 'Galaxia',
      rainbow: 'Arcoíris',
      metal: 'Metal',
      translucent: 'Translúcido',
      // Multi-color structural variants
      gradient: 'Degradado',
      dualColor: 'Bicolor',
      triColor: 'Tricolor',
      multicolor: 'Multicolor',
    },
    // PA Profiles
    selectMaterialFirst: 'Seleccione primero un material en la pestaña de información del filamento.',
    noPrintersConfigured: 'No hay impresoras configuradas. Añada impresoras para usar perfiles PA.',
    matchingFilter: 'Coincidentes',
    anyBrand: 'Cualquier marca',
    anyVariant: 'Cualquier variante',
    autoSelect: 'Selección automática',
    matches: 'coincidencias',
    match: 'coincidencia',
    noMatches: 'Sin coincidencias',
    connected: 'Conectada',
    offline: 'Desconectada',
    printerOffline: 'La impresora está desconectada. Conéctela para ver los perfiles de calibración.',
    noKProfilesMatch: 'Ningún perfil K coincide con el filamento seleccionado.',
    leftNozzle: 'Boquilla izquierda',
    rightNozzle: 'Boquilla derecha',
    profilesSelected: 'perfil(es) de calibración seleccionado(s)',
    // Stats & enhanced table
    totalInventory: 'Inventario total',
    totalConsumed: 'Total consumido',
    byMaterial: 'Por material',
    inPrinter: 'En la impresora',
    lowStock: 'Existencias bajas',
    sinceTracking: 'Desde que comenzó el seguimiento',
    resetConsumedCounter: 'Restablecer contador',
    resetConsumedCounterTooltip: 'Poner a cero el contador de gramos consumidos de esta bobina. El peso restante no cambia.',
    resetConsumedCounterConfirm: '¿Restablecer a 0 el contador de gramos consumidos de esta bobina? Las impresiones futuras volverán a contar desde cero. La bobina en sí, su cálculo de peso restante y sus ajustes no cambian.',
    resetAllConsumedCounters: 'Restablecer todos los contadores',
    resetAllConsumedCountersTooltip: 'Poner a cero el contador de gramos consumidos de todas las bobinas. Los pesos restantes no cambian.',
    resetAllConsumedCountersConfirm: '¿Restablecer a 0 el contador de gramos consumidos de las {{count}} bobinas (incluidas las archivadas)? Esto borra la estadística «Total consumido» para que las impresiones futuras cuenten desde cero. Las bobinas y los pesos restantes no cambian.',
    consumedCounterReset: 'Contador restablecido',
    allConsumedCountersReset: 'Contador restablecido para {{count}} bobina(s)',
    resetConsumedCounterFailed: 'Error al restablecer el contador',
    loadedInAms: 'Cargada en AMS/ext.',
    remaining: 'Restante',
    weightCheck: 'Comprobación de peso',
    lastWeighed: 'Pesada por última vez',
    neverWeighed: 'Nunca pesada',
    search: 'Buscar bobinas...',
    showing: 'Mostrando',
    to: 'a',
    of: 'de',
    show: 'Mostrar',
    spools: 'bobinas',
    spool: 'bobina',
    page: 'Página',
    noSpoolsMatch: 'No se encontraron resultados',
    noSpoolsMatchDesc: 'Pruebe a ajustar su búsqueda o sus filtros para encontrar lo que busca.',
    active: 'Activas',
    archived: 'Archivadas',
    all: 'Todas',
    used: 'Usada',
    new: 'Nueva',
    clearFilters: 'Borrar filtros',
    table: 'Tabla',
    cards: 'Tarjetas',
    net: 'Neto',
    // Grouping
    groupSimilar: 'Agrupar',
    groupedSpools: '{{count}} bobinas idénticas',
    groupedRows: 'filas',
    // Column config
    columns: 'Columnas',
    configureColumns: 'Configurar columnas',
    configureColumnsDesc: 'Arrastre para reordenar las columnas o use las flechas. Conmute la visibilidad con el icono del ojo.',
    visible: 'visibles',
    reset: 'Restablecer',
    cancel: 'Cancelar',
    applyChanges: 'Aplicar cambios',
    moveUp: 'Subir',
    moveDown: 'Bajar',
    hideColumn: 'Ocultar columna',
    showColumn: 'Mostrar columna',
    // Tag linking
    linkToSpool: 'Vincular a bobina',
    tagLinked: 'Etiqueta vinculada a la bobina',
    tagLinkFailed: 'Error al vincular la etiqueta',
    tagAlreadyLinked: 'La etiqueta ya está vinculada a otra bobina',
    unknownTag: 'Se detectó una etiqueta RFID desconocida',
    // Usage history
    usageHistory: 'Historial de uso',
    noUsageHistory: 'Aún no se ha registrado ningún uso',
    printName: 'Nombre de la impresión',
    weightConsumed: 'Peso consumido',
    clearHistory: 'Borrar',
    historyCleared: 'Historial de uso borrado',
    fillSourceLabel: '(Inv.)',
    lowStockThresholdError: 'El umbral debe estar entre 0,1 y 99,9',
    assignMismatchTitle: 'Discrepancia de material',
    assignMismatchMessage: 'El material de la bobina seleccionada "{{spoolMaterial}}" no coincide con el material de la bandeja "{{trayMaterial}}" de {{location}}. ¿Asignar de todos modos?',
    assignMismatchConfirm: 'Asignar de todos modos',
    assignPartialMismatchMessage: 'El material de la bobina "{{spoolMaterial}}" es similar pero no coincide exactamente con "{{trayMaterial}}" en {{location}}. ¿Desea continuar?',
    assignProfileMismatchMessage: 'El perfil de la bobina "{{spoolProfile}}" no coincide con el perfil de la bandeja "{{trayProfile}}" en {{location}}. ¿Desea continuar?',
    assignReconfigureNote: 'La ranura del AMS se reconfigurará para usar el perfil de la bobina.',
    // Spoolman filament catalog picker
    spoolmanFilamentCatalog: 'Catálogo de filamentos de Spoolman',
    pickFromSpoolmanCatalog: 'Elegir del catálogo de Spoolman…',
    spoolmanFilamentSelected: 'Filamento seleccionado del catálogo de Spoolman',
    spoolmanFilamentUnlinked: 'Vínculo del catálogo de filamentos borrado',
    noSpoolmanFilaments: 'No se encontraron filamentos en el catálogo de Spoolman',
    spoolmanFilamentColorSwatch: 'Color del filamento',
    spoolWeightManagedBySpoolman: 'El peso de la bobina vacía se gestiona por tipo de filamento en Spoolman',
    spoolmanCatalogLoadFailed: 'Error al cargar el catálogo de filamentos de Spoolman',
  },

  // Timelapse
  timelapse: {
    title: 'Time-lapse',
    create: 'Crear time-lapse',
    download: 'Descargar',
    delete: 'Eliminar',
    preview: 'Vista previa',
    frameRate: 'Velocidad de fotogramas',
    quality: 'Calidad',
    processing: 'Procesando...',
    noTimelapses: 'No hay time-lapses disponibles',
  },

  // AMS
  ams: {
    title: 'AMS',
    slot: 'Ranura',
    empty: 'Vacía',
    emptySlot: 'Ranura vacía',
    slotEmpty: 'Vacía',
    slotUnconfigured: '?',
    emptySlotReset: 'No hay filamento asignado',
    unknown: 'Desconocido',
    humidity: 'Humedad',
    temperature: 'Temperatura',
    filamentType: 'Tipo de filamento',
    filamentColor: 'Color',
    remaining: 'Restante',
    history: 'Historial del AMS',
    noHistory: 'No hay historial disponible',
    configureSlot: 'Configurar ranura',
    externalSpool: 'Bobina externa',
    profile: 'Perfil',
    kFactor: 'Factor K',
    fill: 'Rellenar',
    configure: 'Configurar',
    used: 'usado',
    remainingUnit: 'restante',
  },

  // Print modal
  printModal: {
    variants: {
      editNote: 'Estas alternativas se fijaron al poner el trabajo en cola. Cancela y vuelve a encolar para cambiarlas.',
      title: 'Alternativas de impresora',
      help: 'Un trabajo, un puesto en la cola. La primera impresora compatible que quede libre imprime su archivo.',
      unknownModel: 'Modelo desconocido',
      plateFor: 'Placa para {{filename}}',
      moveUp: 'Subir',
      moveDown: 'Bajar',
      queued: 'En cola con {{count}} alternativas',
    },
    selectPrinter: 'Seleccionar impresora',
    selectPlate: 'Seleccionar cama',
    filamentMapping: 'Mapeo de filamentos',
    useArchiveMapping: 'Mapeo',
    useArchiveMappingTooltip: 'Selecciona todas las ranuras a partir del mapeo de AMS guardado con este archivo (del slicer), en lugar de emparejar por tipo/color.',
    clickToChangeSlot: 'Haga clic para cambiar la asignación de ranura',
    reRead: 'Releer',
    plateN: 'Cama {{n}}',
    plateFilamentsUnreadable: 'No se han podido leer los filamentos de una cama seleccionada, por lo que no se puede asignar. Deselecciónala para encolar las demás.',
    totalCost: 'Coste total:',
    slotRemainingShort: ' - quedan {{grams}} g',
    printSettings: 'Ajustes de impresión',
    bedLeveling: 'Nivelación de la cama',
    flowCalibration: 'Calibración del flujo',
    vibrationCalibration: 'Calibración de vibración',
    layerInspection: 'Inspección de la primera capa',
    timelapse: 'Time-lapse',
    cancel: 'Cancelar',
    noPrintersAvailable: 'No hay impresoras disponibles',
    printerBusy: 'La impresora está ocupada',
    printerOffline: 'La impresora está desconectada',
    sameTypeDifferentColor: 'Mismo tipo, color distinto',
    filamentTypeNotLoaded: 'Tipo de filamento no cargado',
    whenToPrint: 'Cuándo imprimir',
    asap: 'Lo antes posible',
    queue: 'Cola',
    schedule: 'Programar',
    dateTime: 'Fecha y hora',
    invalidDateTime: 'Introduzca una fecha y hora válidas',
    openCalendar: 'Abrir calendario',
    requireManualStart: 'Requerir inicio manual',
    requirePreviousSuccess: 'Iniciar solo si la impresión anterior se completó correctamente',
    autoOffAfter: 'Apagar la impresora al terminar',
    helpAsap: 'La impresión se añadirá al principio de la cola y comenzará en cuanto haya una impresora elegible inactiva.',
    helpSchedule: 'La impresión comenzará a la hora programada si la impresora está inactiva. Si está ocupada, esperará hasta que esté disponible.',
    helpQueue: 'La impresión se añadirá al final de la cola.',
    leftNozzle: 'I',
    rightNozzle: 'D',
    leftNozzleTooltip: 'Boquilla izquierda',
    rightNozzleTooltip: 'Boquilla derecha',
    rackPosition: 'Posición del carro',
    rackPositionTooltip: 'Qué boquilla del carro imprime este filamento. Las posiciones se numeran como en la impresora.',
    rackEmptyPosition: 'Esta posición del carro está vacía',
    rackWrongNozzle: 'Tiene una boquilla {{has}}; este filamento necesita {{needs}}',
    filamentOverride: 'Anulación de filamento',
    filamentOverrideHint: 'Anule opcionalmente los filamentos para la asignación basada en el modelo. El planificador comparará con los filamentos que ha seleccionado en lugar de los valores originales del 3MF.',
    originalFilament: 'Original',
    overrideWith: 'Anular con',
    resetToOriginal: 'Restablecer al original',
    insufficientFilamentTitle: 'No hay suficiente filamento',
    waitingForAmsStatus: 'Esperando el estado del AMS de {{printer}}…',
    insufficientFilamentMessage: 'Algunas bobinas asignadas tienen menos filamento restante del que necesita esta impresión:',
    insufficientFilamentLine: '{{printer}} - {{slot}}: necesita {{required}} g, restante {{remaining}} g',
    insufficientFilamentLinePooled: '{{printer}} - {{slot}}: necesita {{required}} g, {{remaining}} g disponibles entre bobinas equivalentes (AMS Filament Backup)',
    printAnyway: 'Imprimir de todos modos',
    forceColorMatch: 'Forzar la coincidencia de color',
    staggerPrinterStarts: 'Escalonar los inicios de las impresoras',
    staggerGroupSize: 'Tamaño de grupo',
    staggerInterval: 'Intervalo (min)',
    staggerPreview: '{{printers}} impresoras → {{groups}} grupos de {{size}}, iniciando cada {{interval}} min',
    staggerLastGroup: 'último grupo: {{count}}',
    staggerTotal: 'total: {{minutes}} min',
    staggerToPrinters: 'Escalonar en {{count}} impresoras',
    gcodeInjection: 'Inyectar G-code de impresión automática',
    insufficientBudget: 'Presupuesto insuficiente',
    unlimitedNoBudget: 'Ilimitado: no se ha establecido ningún límite de presupuesto.',
    noPrintableCostCenters: 'No hay ningún centro de costes activo disponible para imprimir. Pide a un administrador que te conceda acceso de impresión.',
  },

  // Backup
  backup: {
    includesEncryptionKey: 'Las copias de seguridad locales incluyen el archivo de clave de cifrado de MFA (DATA_DIR/.mfa_encryption_key) para que un ZIP de copia de seguridad sea autónomo. Trate el ZIP como información sensible — cualquiera que tenga el archivo puede descifrar los secretos de cliente de OIDC y los secretos TOTP que contiene.',
    title: 'Copia de seguridad y restauración',
    createBackup: 'Crear copia de seguridad',
    restoreBackup: 'Restaurar copia de seguridad',
    restoreDescription: 'Reemplazar todos los datos desde un archivo de copia de seguridad',
    downloadBackup: 'Descargar copia de seguridad',
    uploadBackup: 'Subir copia de seguridad',
    lastBackup: 'Última copia de seguridad',
    autoBackup: 'Copia de seguridad automática',
    backupNow: 'Hacer copia de seguridad ahora',
    restoreWarning: 'Advertencia: restaurar una copia de seguridad sobrescribirá todos los datos actuales.',
    includeArchives: 'Incluir archivos',
    includeSettings: 'Incluir ajustes',
    includeProfiles: 'Incluir perfiles',
    backupSuccess: 'Copia de seguridad creada correctamente',
    restoreSuccess: 'Copia de seguridad restaurada correctamente',
    backupFailed: 'Error en la copia de seguridad',
    restoreFailed: 'Error en la restauración',
    restoreNote: 'La impresora virtual se detendrá durante la restauración',

    // GitHub Backup
    githubBackup: 'Copia de seguridad en Git',
    enabled: 'Activada',
    cloudLoginRequired: 'Se requiere iniciar sesión en Bambu Cloud. Inicie sesión en Perfiles → Perfiles en la nube para activar la copia de seguridad en GitHub.',
    cloudLoginRequiredShort: 'Se requiere iniciar sesión en la nube',
    githubDescription: 'Sincronice automáticamente sus perfiles con un repositorio privado de GitHub para la copia de seguridad y el historial de versiones.',
    repoIsPrivate: 'El repositorio es privado — es seguro hacer copias de seguridad en él.',
    repoIsPublicWarning: 'El repositorio es PÚBLICO. Las copias de seguridad de Bambuddy incluyen credenciales de MQTT, tokens de Home Assistant, tokens de Prometheus, su correo de Bambu Cloud y los códigos de acceso de las impresoras mediante los perfiles K. El guardado está bloqueado hasta que haga el repositorio privado en los ajustes de su proveedor.',
    repoVisibilityUnknown: 'No se pudo determinar la visibilidad del repositorio. Bambuddy se niega a hacer copias de seguridad en cualquier cosa que no se confirme como privada; el guardado se bloqueará.',
    repositoryUrl: 'URL del repositorio',
    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: 'Permitir HTTP no seguro',
    allowInsecureHttpHint: 'Actívelo para instancias autoalojadas en redes privadas sin TLS',
    personalAccessToken: 'Token de acceso personal',
    tokenSaved: '(guardado)',
    enterNewToken: 'Introduzca un nuevo token para actualizar',
    tokenHintGitHub: 'Token de granularidad fina con acceso de lectura y escritura a Contents, o token clásico con el ámbito repo.',
    tokenHintGitLab: 'Token con el ámbito api, o read_repository junto con write_repository.',
    tokenHintGitea: 'Token con el ámbito write:repository.',
    tokenHintForgejo: 'Token con el ámbito write:repository. Basta con un token limitado a este único repositorio.',
    branch: 'Rama',
    provider: 'Proveedor de Git',
    providerGitHub: 'GitHub',
    providerGitLab: 'GitLab',
	providerGitea: 'Gitea',
    providerForgejo: 'Forgejo',
    manualOnly: 'Solo manual',
    hourly: 'Cada hora',
    daily: 'Diaria',
    weekly: 'Semanal',
    includeInBackup: 'Incluir en la copia de seguridad',
    kProfiles: 'Perfiles K',
    kProfilesDescription: 'Calibración de avance de presión de las impresoras conectadas',
    noPrintersConnected: 'No hay impresoras conectadas',
    printersConnected: '{{connected}}/{{total}} conectadas',
    cloudProfiles: 'Perfiles en la nube',
    cloudProfilesDescription: 'Preajustes de filamento, impresora y proceso de Bambu Cloud y Orca Cloud',
    cloudProfilesAccounts: 'Cuentas conectadas — Bambu Cloud: {{bambu}}, Orca Cloud: {{orca}}',
    appSettings: 'Ajustes de la aplicación',
    appSettingsDescription: 'Configuración de Bambuddy (base de datos completa)',
    spoolInventory: 'Inventario de bobinas',
    spoolInventoryDescription: 'Bobinas de filamento, historial de uso y seguimiento de costes',
    printArchives: 'Archivos de impresión',
    printArchivesDescription: 'Metadatos del historial de impresión (sin archivos gcode/3MF)',
    lastBackupAt: 'Última copia de seguridad:',
    noBackupsYet: 'Aún no hay copias de seguridad',
    next: 'Siguiente:',
    startingBackup: 'Iniciando la copia de seguridad...',
    test: 'Probar',
    enableBackup: 'Activar la copia de seguridad',
    testConnection: 'Probar conexión',
    enterRepoUrl: 'Introduzca la URL del repositorio',
    enterRepoAndToken: 'Introduzca la URL del repositorio y el token de acceso',
    repoRequired: 'La URL del repositorio es obligatoria',
    tokenRequired: 'El token de acceso es obligatorio',
    githubBackupEnabled: 'Copia de seguridad en GitHub activada',
    tokenUpdated: 'Token actualizado',
    settingsSaved: 'Ajustes guardados',
    failedToSave: 'Error al guardar: {{message}}',
    backupCompleteFiles: 'Copia de seguridad completada - {{count}} archivos actualizados',
    backupSkippedNoChanges: 'Copia de seguridad omitida - sin cambios',
    backupFailed2: 'Error en la copia de seguridad: {{message}}',
    clearedLogs: 'Se borraron {{count}} registros',
    failedToClearLogs: 'Error al borrar los registros: {{message}}',

    // Restore from Git backup (#2656)
    restoreFromGit: {
      button: 'Restaurar desde Git',
      title: 'Restaurar desde copia de Git',
      subtitle: 'Elige un commit y qué se debe restaurar',
      commitLabel: 'Commit de la copia',
      latestCommit: 'Última copia (punta de la rama)',
      categoriesLabel: 'Qué restaurar',
      inspecting: 'Leyendo el contenido de la copia...',
      itemCount: '{{count}} en la copia',
      overwriteLabel: 'Sobrescribir entradas existentes',
      overwriteOn: 'Las entradas existentes se actualizarán desde la copia.',
      overwriteOff: 'Solo se añaden las entradas que falten; las existentes no se modifican.',
      selectedCount: '{{count}} seleccionados',
      restoring: 'Restaurando...',
      confirmTitle: '¿Restaurar desde la copia?',
      confirmMessage: 'Las categorías seleccionadas se restaurarán desde este commit. Se añaden las entradas que falten y las existentes se mantienen igual.',
      confirmMessageOverwrite: 'Las categorías seleccionadas se restaurarán desde este commit y se sobrescribirán las entradas que ya existan localmente. Esto no se puede deshacer.',
      kprofilesOverwriteCaveat: 'Los perfiles K son la excepción: escribir una ranura siempre reemplaza la calibración en la impresora.',
      tally: '{{restored}} restaurados, {{skipped}} omitidos, {{failed}} fallidos',
      reloadHint: 'Recarga Bambuddy para que los datos restaurados aparezcan en todas partes.',
      partialHint: 'Las categorías indicadas arriba se completaron y están guardadas. Las que faltan no llegaron a ejecutarse.',
      failed: 'La restauración ha fallado.',
      loadFailed: 'No se pudo leer el repositorio de copias de seguridad.',
      details: {
        notPresent: 'No está presente en este commit de la copia',
        unreadableJson: 'JSON ilegible: {{paths}}',
        settingsNoPayload: 'No hay ajustes en los datos',
        settingsCredentialsWillSkip: 'Se omitirán {{count}} claves con aspecto de credencial',
        settingsCompanionWillSkip: 'Se omitirán {{count}} claves con aspecto de credencial y {{companion}} interruptores que dependen de ellas quedarán desactivados',
        settingsCompanionOnlyWillSkip: '{{companion}} interruptores quedarán desactivados - la credencial que necesita cada uno no se puede restaurar desde una copia de seguridad',
        spoolsUsageCount: 'incluidos {{count}} registros de consumo',
        archivesMetadataOnly: 'Solo metadatos - los archivos 3MF y las miniaturas no están en una copia de Git',
        kprofilesPrinterCount: 'en {{count}} impresoras',
      },
      notes: {
        noData: 'No hay datos de este tipo en esta copia de seguridad',
        archivesPrinterMissing: 'Algunos archivos hacían referencia a impresoras que ya no existen - enlace eliminado',
        archivesProjectMissing: 'Algunos archivos hacían referencia a proyectos que ya no existen - enlace eliminado',
        archivesOwnerCleared: 'Algunos archivos hacían referencia a usuarios que ya no existen - se ha borrado el propietario, por lo que solo son visibles para usuarios con el permiso archives:read_all hasta que un administrador los reasigne',
        archivesOwnerUnmatched: 'Algunos archivos indican un propietario que no existe en esta instancia - se ha borrado el propietario en lugar de deducirlo del id de usuario de la copia de seguridad, por lo que solo son visibles para usuarios con el permiso archives:read_all hasta que un administrador los reasigne',
        archivesOwnerUnknown: 'Algunos archivos se restauraron sin propietario - esta copia de seguridad no registra ninguno, por lo que solo son visibles para usuarios con el permiso archives:read_all hasta que un administrador los reasigne',
        archivesUndeleted: 'Los archivos eliminados desde la copia vuelven a estar visibles - la sobrescritura estaba activada',
        archivesMetadataOnly: 'Los archivos restaurados solo contienen metadatos - los ficheros 3MF y las miniaturas no están en una copia de Git',
        spoolUsageUnresolved: '{{count}} registros de consumo omitidos - su bobina no está en la lista de bobinas de esta copia, así que no hay nada a lo que asociarlos.',
        spoolUsageUnlinked: '{{count}} registros de consumo restaurados sin su enlace al historial de impresión - selecciona Archivos de impresión junto con Inventario de bobinas para conservarlo.',
        spoolTagKept: '{{count}} etiquetas de bobina se han dejado como estaban - la copia habría borrado una etiqueta escaneada desde entonces, o la habría movido a una segunda bobina.',
        settingsCredentialsSkipped: '{{count}} claves con aspecto de credencial omitidas - vuelve a introducir los secretos manualmente',
        settingsAuthSkipped: '{{count}} ajustes de autenticación omitidos - cámbialos en Ajustes > Autenticación para que sigan aplicándose las comprobaciones de bloqueo',
        settingsCompanionSkipped: '{{keys}} se han dejado desactivados - la credencial que cada uno necesita no puede restaurarse desde una copia y esta instancia no tiene ninguna guardada, así que activarlos dejaría la integración sin autenticación',
        settingsMqttRelayFailed: 'Ajustes MQTT restaurados, pero no se pudo reconectar el relé - reinicia Bambuddy',
        kprofilesAlwaysOverwrite: 'Los perfiles K siempre sobrescriben la ranura correspondiente en la impresora',
        kprofilesAckUnreliable: 'Una impresora que no responde sigue contando como restaurada - verifica los perfiles en la impresora',
        kprofilesPrinterMissing: 'No hay ninguna impresora con el número de serie {{serial}} - omitido',
        kprofilesPrinterOffline: '{{printer}} ({{serial}}) no está conectada - omitido',
        kprofilesUnknownNozzle: 'Diámetro de boquilla inesperado {{nozzle}} para {{serial}} - enviado tal cual',
        kprofilesUnmatched: '{{count}} perfiles para {{nozzle}} no tenían equivalente en {{printer}} - añadidos como perfiles nuevos',
        kprofilesSendFailed: 'No se pudieron enviar los perfiles de {{nozzle}} a {{printer}} ({{serial}})',
        kprofilesRefused: '{{printer}} ({{serial}}) rechazó los perfiles de {{nozzle}}: {{reason}}',
        kprofilesStepFailed: 'No se pudo completar el paso de los perfiles K - {{reason}}. Lo que se restauró antes sigue guardado.',
      },
    },

    // History
    history: 'Historial',
    clear: 'Borrar',
    date: 'Fecha',
    status: 'Estado',
    trigger: 'Tipo',
    triggers: {
      manual: 'Copia (manual)',
      scheduled: 'Copia (programada)',
      restore: 'Restauración',
    },
    commit: 'Confirmación',

    // Local Backup
    localBackup: 'Copia de seguridad local',
    localBackupDescription: 'Cree una copia de seguridad completa de sus datos de Bambuddy, incluida la base de datos, los archivos, las subidas y todos los archivos.',
    downloadBackupLabel: 'Descargar copia de seguridad',
    completeBackupZip: 'Copia de seguridad completa: base de datos + todos los archivos (ZIP)',
    download: 'Descargar',
    preparingBackup: 'Preparando la copia de seguridad...',
    creatingArchive: 'Creando el archivo de copia de seguridad... Esto puede tardar un rato para archivos grandes.',
    downloadingFile: 'Descargando el archivo de copia de seguridad...',
    backupDownloaded: 'Copia de seguridad descargada correctamente',
    failedToCreateBackup: 'Error al crear la copia de seguridad: {{message}}',
    restore: 'Restaurar',
    restoreReplacesAll: 'La restauración reemplaza todos los datos.',
    restoreReplacesAllDetail: 'Su base de datos y archivos actuales se reemplazarán por completo. Se requiere un reinicio tras la restauración.',
    restoreConfirmTitle: 'Restaurar copia de seguridad',
    restoreConfirmMessage: '¿Está seguro de que desea restaurar desde "{{filename}}"? Esto reemplazará por completo su base de datos actual y todos los archivos. La aplicación deberá reiniciarse tras la restauración.',
    restoreConfirmButton: 'Restaurar copia de seguridad',
    uploadingFile: 'Subiendo el archivo de copia de seguridad...',
    backupRestoredRestart: 'Copia de seguridad restaurada. Reinicie Bambuddy.',
    failedToRestore: 'Error al restaurar la copia de seguridad. Compruebe el formato del archivo.',
    reloadNow: 'Recargar ahora',
    creatingBackup: 'Creando copia de seguridad',
    restoringBackup: 'Restaurando copia de seguridad',
    preparing: 'Preparando...',
    processing: 'Procesando...',
    doNotClosePage: 'No cierre esta página ni navegue a otra. Esta operación puede tardar varios minutos para copias de seguridad grandes.',

    // RestoreModal
    restoring: 'Restaurando...',
    restoreComplete: 'Restauración completada',
    restoreFailed2: 'Error en la restauración',
    importSettings: 'Importar ajustes desde un archivo de copia de seguridad',
    pleaseWaitRestoring: 'Espere mientras se restauran sus datos',
    selectBackupFile: 'Haga clic para seleccionar el archivo de copia de seguridad (.json o .zip)',
    duplicateHandling: 'Cómo funciona la gestión de duplicados:',
    matchPrinters: 'Impresoras',
    matchPrintersBy: 'coincidencia por número de serie',
    matchSmartPlugs: 'Enchufes inteligentes',
    matchSmartPlugsBy: 'coincidencia por dirección IP',
    matchNotificationProviders: 'Proveedores de notificaciones',
    matchNotificationProvidersBy: 'coincidencia por nombre',
    matchFilaments: 'Filamentos',
    matchFilamentsBy: 'coincidencia por nombre + tipo + marca',
    matchArchives: 'Archivos',
    matchArchivesBy: 'coincidencia por hash del contenido (siempre se omiten)',
    matchPendingUploads: 'Subidas pendientes',
    matchPendingUploadsBy: 'coincidencia por nombre de archivo',
    matchSettingsTemplates: 'Ajustes y plantillas',
    matchSettingsTemplatesBy: 'siempre se sobrescriben',
    replaceExisting: 'Reemplazar los datos existentes',
    keepExisting: 'Conservar los datos existentes',
    overwriteDescription: 'Sobrescribir los elementos que ya existen con los datos de la copia de seguridad',
    keepDescription: 'Restaurar solo los elementos que aún no existen',
    overwriteCaution: 'Precaución:',
    overwriteWarning: 'Sobrescribir reemplazará sus configuraciones actuales con los datos de la copia de seguridad. Los códigos de acceso de las impresoras nunca se sobrescriben por seguridad.',
    cancel: 'Cancelar',
    processingBackup: 'Procesando el archivo de copia de seguridad...',
    itemsRestored: 'Elementos restaurados',
    itemsSkipped: 'Elementos omitidos',
    restored: 'Restaurados',
    skippedAlreadyExist: 'Omitidos (ya existen)',
    filesCategory: 'Archivos (3MF, miniaturas, etc.)',
    andMore: '...y {{count}} más',
    newApiKeysGenerated: 'Nuevas claves API generadas',
    keysShownOnce: 'Estas claves solo se muestran una vez. ¡Cópielas ahora!',
    copy: 'Copiar',
    noDataFound: 'No se encontraron datos para restaurar en el archivo de copia de seguridad.',
    close: 'Cerrar',

    // Scheduled local backups (#884)
    scheduledBackup: 'Copias de seguridad programadas',
    scheduledBackupDescription: 'Cree automáticamente instantáneas de copia de seguridad según una programación. El directorio de salida se puede montar en un NAS o en almacenamiento externo.',
    frequency: 'Frecuencia',
    backupTime: 'Hora',
    retention: 'Retención',
    retentionDescription: 'Número de copias de seguridad que conservar',
    outputPath: 'Ruta de salida',
    outputPathPlaceholder: 'Predeterminada: {{path}}',
    outputPathDescription: 'Déjelo vacío para la ubicación predeterminada',
    runNow: 'Ejecutar ahora',
    backupFiles: 'Archivos de copia de seguridad',
    noScheduledBackups: 'Aún no hay copias de seguridad',
    deleteBackup: 'Eliminar',
    deleteBackupConfirm: '¿Eliminar este archivo de copia de seguridad?',
    backupRunning: 'Copia de seguridad en curso...',
    scheduledBackupComplete: 'Copia de seguridad completada correctamente',
    scheduledBackupFailed: 'Error en la copia de seguridad',
    nextBackup: 'Próxima copia de seguridad',
    backupSize: 'Tamaño',
    localTimeHint: 'Hora local ({{tz}})',
    defaultPathLabel: 'Predeterminada:',
    // Backup output-path probe (#2544)
    pathCheck: {
      title: 'Bambuddy no puede escribir en este directorio',
      howToFix: 'Cómo solucionarlo:',
      sandboxed: 'El servicio de Bambuddy no puede escribir en {{path}}. Su unidad de systemd se ejecuta con ProtectSystem=strict, lo que hace que todo directorio fuera de los de instalación, datos y registros sea de solo lectura para el servicio, incluso uno en el que usted sí puede escribir desde su propia shell.',
      read_only: '{{path}} está en un sistema de archivos de solo lectura.',
      permission_denied: 'Bambuddy no tiene permiso para escribir en {{path}}. Compruebe el propietario y los permisos del directorio.',
      no_space: 'El sistema de archivos que contiene {{path}} está lleno.',
      not_a_directory: '{{path}} existe pero no es un directorio.',
      missing: '{{path}} no existe y no se pudo crear.',
      error: 'Bambuddy no puede escribir en {{path}}.',
      ephemeralTitle: 'Estas copias de seguridad no sobrevivirán a la recreación del contenedor',
      container_ephemeral: '{{path}} está dentro del contenedor de Bambuddy, no en el host. Las copias escritas ahí se pierden cuando se recrea el contenedor. Monte el directorio desde el host:',
    },

    // Category labels
    categories: {
      settings: 'Ajustes',
      notification_providers: 'Proveedores de notificaciones',
      notification_templates: 'Plantillas de notificaciones',
      smart_plugs: 'Enchufes inteligentes',
      printers: 'Impresoras',
      filaments: 'Filamentos',
      maintenance_types: 'Tipos de mantenimiento',
      archives: 'Archivos',
      projects: 'Proyectos',
      pending_uploads: 'Subidas pendientes',
      external_links: 'Enlaces externos',
      api_keys: 'Claves API',
    },
  },

  // Tags
  tags: {
    title: 'Etiquetas',
    addTag: 'Añadir etiqueta',
    editTag: 'Editar etiqueta',
    deleteTag: 'Eliminar etiqueta',
    tagName: 'Nombre de la etiqueta',
    tagColor: 'Color de la etiqueta',
    noTags: 'No hay etiquetas',
    deleteConfirm: '¿Está seguro de que desea eliminar esta etiqueta?',
    manageTags: 'Gestionar etiquetas',
  },

  // Upload modal (archives)
  uploadModal: {
    title: 'Subir archivos 3MF',
    dragDrop: 'Arrastre y suelte archivos .3mf aquí',
    or: 'o',
    browseFiles: 'Examinar archivos',
    extractionInfo: 'El modelo de impresora se extraerá automáticamente de los metadatos del archivo 3MF.',
    uploaded: 'subidos',
    failed: 'fallidos',
    uploading: 'Subiendo...',
    upload: 'Subir',
    uploadFailed: 'Error al subir',
  },

  // Edit archive modal
  // Edit Archive Modal
  editArchive: {
    title: 'Editar archivo',
    name: 'Nombre',
    namePlaceholder: 'Nombre de la impresión',
    printer: 'Impresora',
    noPrinter: 'Sin impresora',
    project: 'Proyecto',
    noProject: 'Sin proyecto',
    itemsPrinted: 'Elementos impresos',
    itemsPrintedHelp: 'Número de elementos producidos en este trabajo de impresión',
    notes: 'Notas',
    notesPlaceholder: 'Añada notas sobre esta impresión...',
    externalLink: 'Enlace externo',
    externalLinkPlaceholder: 'https://printables.com/model/...',
    externalLinkHelp: 'Enlace a Printables, Thingiverse u otra fuente',
    tags: 'Etiquetas',
    tagsPlaceholder: 'Añada etiquetas...',
    addMoreTags: 'Añada más etiquetas...',
    matchingTags: 'Coincidentes con "{{query}}"',
    existingTags: 'Etiquetas existentes',
    clickToAdd: '(haga clic para añadir)',
    status: 'Estado',
    failureReason: 'Motivo del fallo',
    selectReason: 'Seleccionar motivo...',
    photos: 'Fotos del resultado impreso',
    photosHelp: 'Haga clic en + para añadir fotos de su resultado impreso',
    printResult: 'Resultado de la impresión',
    saving: 'Guardando...',
    // Failure reasons
    failureReasons: {
      adhesionFailure: 'Fallo de adhesión',
      spaghettiDetached: 'Espagueti / Desprendido',
      layerShift: 'Desplazamiento de capa',
      cloggedNozzle: 'Boquilla obstruida',
      filamentRunout: 'Agotamiento del filamento',
      warping: 'Alabeo',
      stringing: 'Hilos',
      underExtrusion: 'Subextrusión',
      powerFailure: 'Corte de corriente',
      userCancelled: 'Cancelada por el usuario',
      other: 'Otro',
    },
    // Archive statuses
    statuses: {
      completed: 'Completada',
      failed: 'Fallida',
      aborted: 'Cancelada',
      printing: 'Imprimiendo',
    },
  },

  // K-Profiles
  kProfiles: {
    title: 'Perfiles K',
    noPrintersConfigured: 'No hay impresoras configuradas',
    addPrinterInSettings: 'Añada una impresora en Ajustes para gestionar perfiles K',
    noActivePrinters: 'No hay impresoras activas',
    enablePrinterConnection: 'Active la conexión de una impresora para ver sus perfiles K',
    loadingProfiles: 'Cargando perfiles K...',
    printerOffline: 'Impresora desconectada',
    printerOfflineDesc: 'La impresora seleccionada no está conectada. Enciéndala para ver los perfiles K.',
    noMatchingProfiles: 'No hay perfiles coincidentes',
    noMatchingProfilesDesc: 'Ningún perfil coincide con sus criterios de búsqueda',
    noKProfiles: 'No hay perfiles K',
    noKProfilesDesc: 'No se encontraron perfiles de avance de presión para la boquilla de {{diameter}} mm',
    createFirstProfile: 'Crear el primer perfil',
    // Controls
    printer: 'Impresora',
    nozzle: 'Boquilla',
    refresh: 'Actualizar',
    addProfile: 'Añadir perfil',
    export: 'Exportar',
    import: 'Importar',
    select: 'Seleccionar',
    selectAll: 'Seleccionar todo',
    delete: 'Eliminar',
    // Filters
    searchPlaceholder: 'Buscar por nombre o filamento...',
    allExtruders: 'Todos los extrusores',
    leftOnly: 'Solo izquierdo',
    rightOnly: 'Solo derecho',
    allFlow: 'Todos los flujos',
    hfOnly: 'Solo flujo alto',
    sOnly: 'Solo estándar',
    sortName: 'Ordenar: nombre',
    sortKValue: 'Ordenar: valor K',
    sortFilament: 'Ordenar: filamento',
    // Dual extruder labels
    leftExtruder: 'Extrusor izquierdo',
    rightExtruder: 'Extrusor derecho',
    // Modal
    modal: {
      addTitle: 'Añadir perfil K',
      editTitle: 'Editar perfil K',
      profileName: 'Nombre del perfil',
      profileNamePlaceholder: 'Mi perfil de PLA',
      kValue: 'Valor K',
      kValuePlaceholder: '0,020',
      kValueHelp: 'Rango típico: 0,01 - 0,06 para PLA, 0,02 - 0,10 para PETG',
      filament: 'Filamento',
      selectFilament: 'Seleccionar filamento...',
      source: {
        local: 'Importado',
        orcaCloud: 'Orca Cloud',
        bambuCloud: 'Bambu Cloud',
        builtin: 'Integrado',
      },
      noFilamentsHelp: 'No hay filamentos disponibles. Inicia sesión en Bambu Cloud o importa ajustes en Perfiles → Perfiles locales.',
      searchFilaments: 'Buscar filamentos...',
      noFilamentMatches: 'Ningún filamento coincide con esa búsqueda',
      flowType: 'Tipo de flujo',
      highFlow: 'Flujo alto',
      standard: 'Estándar',
      nozzleSize: 'Tamaño de la boquilla',
      extruder: 'Extrusor',
      extruders: 'Extrusores',
      left: 'Izquierdo',
      right: 'Derecho',
      notes: 'Notas (almacenadas localmente)',
      notesPlaceholder: 'Añada notas sobre este perfil...',
      notesHelp: 'Las notas se guardan en Bambuddy, no en la impresora',
      syncing: 'Sincronizando con la impresora...',
      savingExtruder: 'Guardando en el extrusor {{current}}/{{total}}...',
      pleaseWait: 'Espere',
    },
    // Delete confirmation
    deleteConfirm: {
      title: 'Eliminar perfil',
      cannotUndo: 'Esto no se puede deshacer',
      message: '¿Está seguro de que desea eliminar "{{name}}" de la impresora?',
    },
    // Bulk delete
    bulkDelete: {
      title: 'Eliminar perfiles',
      cannotUndo: 'Esto no se puede deshacer',
      message: '¿Está seguro de que desea eliminar {{count}} perfiles seleccionados de la impresora?',
    },
    // Toast
    toast: {
      profileSaved: 'Perfil K guardado',
      profilesSaved: 'Perfil K guardado en {{count}} extrusores',
      selectAtLeastOneExtruder: 'Seleccione al menos un extrusor',
      selectFilament: 'Selecciona primero un filamento',
      filamentNotResolvable: 'No hay ID de filamento Bambu para {{name}}: la impresora no puede guardar un perfil',
      profileDeleted: 'Perfil K eliminado',
      profilesDeleted: 'Se eliminaron {{count}} perfiles',
      exportedProfiles: 'Se exportaron {{count}} perfiles',
      importedProfiles: 'Se importaron {{count}} de {{total}} perfiles',
      noProfilesToExport: 'No hay perfiles que exportar',
      invalidFileFormat: 'Formato de archivo no válido',
      failedToParseImport: 'Error al analizar el archivo de importación',
      failedToSaveBatch: 'Error al guardar los perfiles K',
      noteSaved: 'Nota guardada',
      failedToSaveNote: 'Error al guardar la nota',
    },
    // Permissions
    permission: {
      noRead: 'No tiene permiso para actualizar perfiles',
      noCreate: 'No tiene permiso para añadir perfiles',
      noUpdate: 'No tiene permiso para actualizar perfiles K',
      noDelete: 'No tiene permiso para eliminar perfiles K',
      noExport: 'No tiene permiso para exportar perfiles',
      noImport: 'No tiene permiso para importar perfiles',
    },
  },

  // Virtual Printer
  virtualPrinter: {
    title: 'Impresora virtual',
    running: 'En ejecución',
    stopped: 'Detenida',
    description: {
      default: 'Active una impresora virtual que aparece en Bambu Studio y OrcaSlicer. Los archivos enviados a esta impresora se archivarán directamente sin imprimir.',
      proxy: 'Active un proxy que retransmite el tráfico del laminador a una impresora real, permitiendo la impresión remota a través de cualquier red.',
    },
    enable: {
      title: 'Activar la impresora virtual',
      visibleInSlicer: 'Visible como "Bambuddy" en la detección del laminador',
      proxyingTo: 'Haciendo de proxy hacia {{name}}',
      notActive: 'No activa',
    },
    model: {
      title: 'Modelo de impresora',
      description: 'Seleccione qué modelo de impresora emular.',
      restartWarning: 'Cambiar el modelo reiniciará la impresora virtual',
    },
    accessCode: {
      title: 'Código de acceso',
      isSet: 'El código de acceso está establecido',
      notSet: 'No hay código de acceso establecido - necesario para activarla',
      placeholder: 'Introduzca un código de 8 caracteres',
      placeholderChange: 'Introduzca un nuevo código para cambiarlo',
      hint: 'Debe tener exactamente 8 caracteres. Los laminadores lo usan para autenticarse.',
      charCount: '({{count}}/8)',
      inheritedFromTarget: 'Heredado de la impresora de destino',
      derivedFromTargetHint: 'Usa el código de acceso de la impresora de destino. El puente reenvía la autenticación del laminador a la impresora real, por lo que los códigos deben coincidir — edita el código en los ajustes de la impresora para cambiarlo.',
      reveal: 'Mostrar código de acceso',
      hide: 'Ocultar código de acceso',
    },
    targetPrinter: {
      title: 'Impresora de destino',
      configured: 'Destino del proxy configurado',
      notConfigured: 'No hay impresora de destino seleccionada - necesaria para el modo proxy',
      placeholder: 'Seleccionar una impresora...',
      hint: 'Seleccione la impresora a la que retransmitir el tráfico del laminador. La impresora debe estar en modo LAN.',
      noPrinters: 'No hay impresoras configuradas. Añada primero una impresora para usar el modo proxy.',
    },
    remoteInterface: {
      title: 'Anulación de la interfaz de red',
      configured: 'Anulación de la interfaz activa',
      optional: 'Opcional - úsela si la IP detectada automáticamente es incorrecta (p. ej. varias tarjetas de red, Docker, VPN)',
      placeholder: 'Detección automática (predeterminada)...',
      hint: 'Anula la dirección IP anunciada mediante SSDP y usada en el certificado TLS. Útil cuando Bambuddy tiene varias interfaces de red.',
    },
    mode: {
      title: 'Modo',
      archive: 'Archivar',
      archiveDesc: 'Archivar los archivos inmediatamente',
      review: 'Revisar',
      reviewDesc: 'Revisar antes de archivar',
      queue: 'Encolar',
      queueDesc: 'Archivar y añadir a la cola',
      proxy: 'Proxy',
      proxyDesc: 'Retransmitir a una impresora real',
    },
    autoDispatch: {
      title: 'Envío automático',
      description: 'Iniciar automáticamente las impresiones al añadirlas a la cola. Cuando está desactivado, las impresiones esperan al envío manual.',
    },
    queueForceColorMatch: {
      title: 'Forzar la coincidencia de color',
      description: 'Negarse a enviar a una impresora que no tiene cargados el tipo y el color exactos de filamento. Desactivado de forma predeterminada — sin esto, la cola usa la coincidencia solo por modelo y puede elegir una impresora con el color equivocado cargado.',
    },
    saveAmsMapping: {
      title: 'Guardar mapeo de AMS',
      description: 'Guarda en el archivo la selección de ranura AMS propia del slicer (del comando MQTT project_file), de modo que una reimpresión posterior reutilice el mismo carrete físico en lugar de volver a derivarlo del tipo/color del archivo. Desactivado de forma predeterminada.',
    },
    gcodeInjection: {
      title: 'Inyección de G-code',
      description: 'Aplica los fragmentos de G-code configurados por modelo en Ajustes a los trabajos de esta IV. Desactivado de forma predeterminada.',
    },
    tailscaleDisabled: {
      title: 'Integración con Tailscale',
      description: 'Actívelo para marcar esta IV como expuesta a través de Tailscale. Muestra la dirección de Tailscale del host para que sepa qué IP pegar en el laminador. El paso de importación de la CA no cambia — este interruptor no tiene ningún efecto sobre los certificados.',
    },
    setupRequired: {
      title: 'Configuración necesaria',
      description: 'La función de impresora virtual requiere configuración adicional del sistema antes de funcionar. Esto incluye el reenvío de puertos, las reglas del cortafuegos y los ajustes específicos de la plataforma.',
      readGuide: 'Lea la guía de configuración antes de activarla',
    },
    archiveNameSource: {
      title: 'Origen del nombre del archivo',
      description: 'Elija cómo se nombran los archivos nuevos cuando llegan a través de la impresora virtual. «Metadatos» usa el título incrustado por el laminador del 3MF (predeterminado). «Nombre de archivo» usa el nombre de archivo que Bambu Studio envió por FTP. Nota: Bambu Studio sobrescribe el nombre que escribe en el diálogo «enviar a la impresora» con el campo Título del 3MF cuando existe, por lo que ambos modos suelen producir la misma cadena.',
      metadata: 'Metadatos',
      filename: 'Nombre de archivo',
    },
    caCert: {
      title: 'Certificado del laminador',
      description: 'Las impresoras virtuales usan un certificado TLS firmado por la CA de Bambuddy. Importe este certificado de CA en el almacén de confianza de su laminador una sola vez para que acepte la conexión — sin necesidad de copiarlo desde la línea de comandos.',
      copy: 'Copiar',
      copied: 'Copiado',
      download: 'Descargar',
      fingerprint: 'SHA-256',
    },
    howItWorks: {
      title: 'Cómo funciona',
      step1: 'En la misma LAN, las impresoras virtuales aparecen automáticamente en su laminador (Bambu Studio / OrcaSlicer) mediante detección. Desde otras redes, añádalas manualmente por dirección IP y código de acceso.',
      step2: 'En los modos Archivar, Revisar y Encolar, use el botón «Enviar» de su laminador para subir archivos 3MF a Bambuddy. El laminador mostrará «Impresión correcta» — el archivo se almacena, no se imprime.',
      step3: 'En el modo Proxy, la impresora virtual retransmite todo el tráfico a una impresora real — las impresiones comienzan inmediatamente como si estuviera conectada directamente.',
    },
    status: {
      title: 'Detalles del estado',
      printerName: 'Nombre de la impresora',
      model: 'Modelo',
      serialNumber: 'Número de serie',
      mode: 'Modo',
      pendingFiles: 'Archivos pendientes',
      targetPrinter: 'Impresora de destino',
      ftpPort: 'Puerto FTP',
      mqttPort: 'Puerto MQTT',
      ftpConnections: 'Conexiones FTP',
      mqttConnections: 'Conexiones MQTT',
    },
    toast: {
      updated: 'Ajustes de la impresora virtual actualizados',
      failedToUpdate: 'Error al actualizar los ajustes',
      copyFailed: 'Error al copiar — pruebe a seleccionar el texto manualmente',
      accessCodeRequired: 'Establezca primero un código de acceso',
      targetPrinterRequired: 'Seleccione primero una impresora de destino',
      bindIpRequired: 'Establezca primero una IP de enlace',
      accessCodeEmpty: 'El código de acceso no puede estar vacío',
      accessCodeLength: 'El código de acceso debe tener exactamente 8 caracteres',
      targetCodeChangedRebind: 'El código de acceso ahora coincide con la nueva impresora de destino. Vuelve a añadir este dispositivo en tu laminador para usar el nuevo código.',
      created: 'Impresora virtual creada',
      failedToCreate: 'Error al crear la impresora virtual',
      deleted: 'Impresora virtual eliminada',
      failedToDelete: 'Error al eliminar la impresora virtual',
    },
    list: {
      title: 'Impresoras virtuales',
      add: 'Añadir',
      addFirst: 'Añadir impresora virtual',
      empty: 'No hay impresoras virtuales configuradas. Añada una para empezar.',
    },
    bindIp: {
      title: 'Interfaz de enlace',
      placeholder: 'Seleccionar interfaz...',
      hint: 'Interfaz de red a la que se enlaza esta impresora virtual. Debe ser única por impresora.',
    },
    proxy: {
      accessCodeHint: 'En el modo proxy, use el código de acceso de su impresora de destino en el laminador. La conexión se reenvía de forma transparente a la impresora real.',
    },
    addDialog: {
      title: 'Añadir impresora virtual',
      name: 'Nombre',
      hint: 'Puede configurar el código de acceso, la impresora de destino y otros ajustes después de crearla.',
      create: 'Crear',
    },
    deleteConfirm: {
      title: 'Eliminar impresora virtual',
      message: '¿Está seguro de que desea eliminar "{{name}}"? Esto detendrá todos los servicios de esta impresora.',
    },
  },

  // Model Viewer
  modelViewer: {
    openInSlicer: 'Abrir en el laminador',
    openInSlicerWith: 'Abrir en {{slicer}}',
    moreSlicerOptions: 'Más opciones de laminador',
    openInSlicerFailed: 'No se pudo abrir en el laminador',
    tabs: {
      model: 'Modelo 3D',
    },
    notAvailable: 'no disponible',
    plates: 'Camas',
    allPlates: 'Todas las camas',
    plateNumber: 'Cama {{number}}',
    plateCount: '{{count}} cama',
    plateCount_other: '{{count}} camas',
    objectCount: '{{count}} objeto',
    objectCount_other: '{{count}} objetos',
    filamentCount: '{{count}} filamento',
    filamentCount_other: '{{count}} filamentos',
    eta: 'Tiempo estimado {{minutes}} min',
    noPreview: 'No hay vista previa disponible para este archivo',
    pagination: {
      pageOf: 'Página {{current}} de {{total}}',
      prev: 'Anterior',
      next: 'Siguiente',
    },
    errors: {
      failedToLoad: 'Error al cargar el archivo',
      noMeshes: 'No se encontraron mallas en el archivo 3MF',
      unsupportedFormat: 'Formato de archivo no compatible',
    },
  },

  // Maintenance type descriptions (built-in)
  maintenanceDescriptions: {
    lubricateCarbonRods: 'Aplique lubricante a las varillas de carbono para un movimiento suave',
    lubricateRails: 'Aplique lubricante a las guías lineales para un movimiento suave',
    cleanNozzle: 'Limpie el fusor y la boquilla para evitar obstrucciones',
    checkBelts: 'Verifique la tensión de las correas para impresiones precisas',
    cleanBuildPlate: 'Limpie la cama de impresión para una mejor adhesión',
    checkExtruder: 'Inspeccione los engranajes del extrusor en busca de desgaste',
    checkCooling: 'Asegúrese de que los ventiladores de refrigeración funcionan correctamente',
    generalInspection: 'Inspección general de la impresora',
    cleanCarbonRods: 'Limpie las varillas de carbono para reducir la fricción',
    lubricateSteelRods: 'Aplique lubricante a las varillas de acero para un movimiento suave',
    cleanSteelRods: 'Limpie las varillas de acero para reducir la fricción',
    cleanLinearRails: 'Limpie las guías lineales para eliminar el polvo y los residuos',
    checkPtfeTube: 'Inspeccione el tubo de PTFE en busca de desgaste o daños',
    replaceHepaFilter: 'Reemplace el filtro HEPA para la calidad del aire',
    replaceCarbonFilter: 'Reemplace el filtro de carbón activado',
    lubricateLeftNozzleRail: 'Lubrique la guía de la boquilla izquierda (serie H2)',
  },

  // Smart Plugs
  haSensors: {
    label: 'Sensores',
    unavailable: 'No disponible',
    blocksPrints: '{{entity}} — retiene las impresiones mientras haya alerta',
    states: {
      open: 'Abierto',
      closed: 'Cerrado',
      unlocked: 'Desbloqueado',
      locked: 'Bloqueado',
      detected: 'Detectado',
      clear: 'Despejado',
      wet: 'Húmedo',
      dry: 'Seco',
      problem: 'Problema',
      ok: 'OK',
      running: 'En marcha',
      stopped: 'Detenido',
      on: 'Encendido',
      off: 'Apagado',
    },
    sectionTitle: 'Sensores de Home Assistant',
    add: 'Añadir sensor',
    addTitle: 'Añadir sensor de Home Assistant',
    editTitle: 'Editar sensor de Home Assistant',
    empty: 'Aún no hay sensores. Vincula un contacto de puerta o un termómetro de Home Assistant para mostrarlo en la tarjeta de la impresora.',
    unknownPrinter: 'Impresora desconocida',
    badgeBlocks: 'Retiene impresiones',
    badgeNotifies: 'Notifica',
    badgeHidden: 'Oculto en la tarjeta',
    printer: 'Impresora',
    entity: 'Entidad',
    searchPlaceholder: 'Buscar entidades...',
    noEntities: 'No hay entidades coincidentes',
    name: 'Nombre visible',
    alertWhen: 'Alertar cuando',
    alertNever: 'Nunca — solo mostrar',
    alertAbove: 'Por encima de',
    alertBelow: 'Por debajo de',
    alertHint: 'El estado de alerta resalta el sensor en la tarjeta de la impresora y habilita las opciones de abajo.',
    showOnCard: 'Mostrar en la tarjeta de la impresora',
    notifyOnAlert: 'Enviar una notificación cuando empiece la alerta',
    blockPrint: 'Retener las impresiones en cola mientras haya alerta',
    blockPrintHint: 'Los trabajos siguen en cola y arrancan solos cuando el sensor se despeja. Se ignora mientras Home Assistant no esté accesible.',
    toast: {
      created: 'Sensor añadido',
      updated: 'Sensor guardado',
      deleted: 'Sensor eliminado',
    },
    error: {
      pickEntity: 'Elige una entidad de Home Assistant',
      nameRequired: 'Introduce un nombre visible',
      printerRequired: 'Elige una impresora',
      alertRequired: 'Define primero una condición de alerta',
    },
  },
  smartPlugs: {
    offline: 'Desconectado',
    admin: 'Administración',
    openPlugAdminPage: 'Abrir la página de administración del enchufe',
    deleteSmartPlug: 'Eliminar enchufe inteligente',
    turnOnSmartPlug: 'Encender el enchufe inteligente',
    turnOffSmartPlug: 'Apagar el enchufe inteligente',
    turnOn: 'Encender',
    turnOff: 'Apagar',
    addSmartPlug: {
      scanningNetwork: 'Escaneando la red...',
      chooseEntity: 'Elija una entidad...',
      connectionFailed: 'Error de conexión',
      searchEntities: 'Buscar entidades...',
      searchPowerSensors: 'Buscar sensores de potencia...',
      searchEnergySensors: 'Buscar sensores de energía...',
      placeholders: {
        plugName: 'Enchufe del salón',
        mqttStateOnValue: 'ON, true, 1',
        mqttSameAsPower: 'Igual que el tema de potencia, o distinto',
      },
    },
    // SmartPlugCard
    linkedTo: 'Vinculado a:',
    monitorOnly: 'Solo supervisión',
    alerts: 'Alertas',
    scheduleOn: 'Encendido a las {{time}}',
    scheduleOff: 'Apagado a las {{time}}',
    on: 'Encendido',
    off: 'Apagado',
    power: 'Potencia',
    kwhToday: 'kWh hoy',
    settings: 'Ajustes',
    automationSettings: 'Ajustes de automatización',
    showInSwitchbar: 'Mostrar en la barra de interruptores',
    quickAccessSidebar: 'Acceso rápido desde la barra lateral',
    enabled: 'Activada',
    enableAutomation: 'Activar la automatización para este enchufe',
    autoOn: 'Encendido automático',
    autoOnDescription: 'Encender cuando comienza la impresión',
    autoOff: 'Apagado automático',
    autoOffDescription: 'Apagar cuando se completa la impresión (una sola vez)',
    autoOffPersistent: 'Mantener activado',
    autoOffPersistentDescription: 'Permanecer activado entre impresiones en lugar de una sola vez',
    controlsPrinterPower: 'Alimenta la impresora',
    controlsPrinterPowerDescription: 'Desactívalo si este enchufe solo alimenta un accesorio (ventilador de filtro, luces). De lo contrario, apagarlo marca la impresora como desconectada.',
    autoOffAfterDrying: 'Apagado automático tras el secado',
    autoOffAfterDryingDescription: 'Apagar cuando se completa el secado del AMS',
    delayAfterDryingMinutes: 'Retardo de secado (minutos)',
    turnOffDelayMode: 'Modo de retardo de apagado',
    time: 'Tiempo',
    temp: 'Temp.',
    delayMinutes: 'Retardo (minutos)',
    tempThreshold: 'Umbral de temperatura (°C)',
    tempThresholdDescription: 'Se apaga cuando la boquilla se enfría por debajo de esta temperatura',
    edit: 'Editar',
    deleteConfirm: '¿Está seguro de que desea eliminar "{{name}}"? Esto no se puede deshacer.',
    turnOnConfirm: '¿Está seguro de que desea encender "{{name}}"?',
    turnOffConfirm: '¿Está seguro de que desea apagar "{{name}}"? Esto cortará la corriente al dispositivo conectado.',
    failedToTurn: 'Error al {{action}} "{{name}}"',
    unknown: 'Desconocido',
    // AddSmartPlugModal
    addTitle: 'Añadir enchufe inteligente',
    editTitle: 'Editar enchufe inteligente',
    stopScanning: 'Detener escaneo',
    discoverTasmota: 'Detectar dispositivos Tasmota',
    foundDevices: 'Se encontraron {{count}} dispositivo(s) - haga clic para seleccionar:',
    noDevicesFound: 'No se encontraron dispositivos Tasmota en su red',
    haNotConfigured: 'Home Assistant no está configurado. Configúrelo en',
    haSettingsPath: 'Ajustes → Red → Home Assistant',
    selectEntity: 'Seleccionar entidad *',
    ipAddress: 'Dirección IP *',
    nameLabel: 'Nombre *',
    username: 'Nombre de usuario',
    password: 'Contraseña',
    authHint: 'Déjelo vacío si su dispositivo Tasmota no requiere autenticación',
    linkToPrinter: 'Vincular a una impresora',
    noPrinter: 'Sin impresora (solo control manual)',
    linkingDescription: 'La vinculación activa el encendido/apagado automático cuando las impresiones comienzan/terminan',
    powerAlerts: 'Alertas de potencia',
    alertAbove: 'Alertar si supera (W)',
    alertBelow: 'Alertar si está por debajo (W)',
    alertDescription: 'Reciba una notificación cuando el consumo de energía cruce estos umbrales. Déjelo vacío para desactivar esa dirección.',
    dailySchedule: 'Programación diaria',
    turnOnAt: 'Encender a las',
    turnOffAt: 'Apagar a las',
    scheduleDescription: 'Encender/apagar automáticamente el enchufe a estas horas cada día. Déjelo vacío para omitir esa acción.',
    showOnPrinterCard: 'Mostrar en la tarjeta de la impresora',
    displayOnPrinterCard: 'Mostrar el botón en la tarjeta de la impresora',
    connectedResult: '¡Conectado!',
    deviceLabel: 'Dispositivo: {{name}} - ',
    stateLabel: 'Estado: {{state}}',
    test: 'Probar',
    delete: 'Eliminar',
    save: 'Guardar',
    add: 'Añadir',
    cancel: 'Cancelar',
    failedToStartScan: 'Error al iniciar el escaneo',
    nameRequired: 'El nombre es obligatorio',
    entityRequired: 'La entidad es obligatoria para los enchufes de Home Assistant',
    mqttTopicRequired: 'Debe configurarse al menos un tema MQTT para la supervisión de potencia, energía o estado',
    loadingEntities: 'Cargando entidades...',
    loading: 'Cargando...',
    failedToLoadEntities: 'Error al cargar las entidades: {{error}}',
    noEntitiesMatching: 'No se encontraron entidades que coincidan con "{{search}}"',
    noEntitiesAvailable: 'No hay entidades disponibles',
    searchingEntities: 'Buscando en todas las entidades ({{count}} encontradas)',
    showingEntities: 'Mostrando switch, light, input_boolean ({{count}} disponibles)',
    energyMonitoringOptional: 'Supervisión de energía (opcional)',
    energyMonitoringHint: 'Busque y seleccione sensores que proporcionen datos de potencia/energía.',
    powerSensorW: 'Sensor de potencia (W)',
    energyTodayKwh: 'Energía de hoy (kWh)',
    totalEnergyKwh: 'Energía total (kWh)',
    noMatchingSensors: 'No hay sensores coincidentes',
    none: 'Ninguno',
    mqttNotConfigured: 'El broker MQTT no está configurado. Establezca la dirección del broker en',
    mqttSettingsPath: 'Ajustes → Red → Publicación MQTT',
    mqttNotConfiguredSuffix: '(no necesita activar la publicación, solo rellenar los datos del broker).',
    mqttMonitorOnlyDescription: 'Los enchufes MQTT reciben los datos de potencia/energía mediante una suscripción MQTT. El control de encendido/apagado no está disponible - use su broker MQTT o sistema de domótica.',
    powerMonitoring: 'Supervisión de potencia',
    energyMonitoring: 'Supervisión de energía',
    stateMonitoring: 'Supervisión de estado',
    optional: 'opcional',
    topic: 'Tema',
    jsonPath: 'Ruta JSON',
    multiplier: 'Multiplicador',
    onValue: 'Valor de ON',
    mqttPowerHint: 'La ruta JSON extrae el valor de la carga útil JSON (p. ej., "power_l1"). Déjelo vacío si el tema publica valores numéricos sin procesar.\nUse el multiplicador 0,001 para mW→W, 1000 para kW→W.',
    mqttEnergyHint: 'La ruta JSON extrae el valor de la carga útil JSON. Déjelo vacío para valores sin procesar.\nUse el multiplicador 0,001 para Wh→kWh, 1000 para MWh→kWh.',
    mqttStateHint: 'La ruta JSON extrae el valor de la carga útil JSON. Déjelo vacío para valores sin procesar.\nValor de ON: la cadena exacta que significa «ON». Déjelo vacío para la detección automática (ON, true, 1).',
    // REST smart plug
    restControl: 'Control',
    restOnUrl: 'URL de encendido',
    restOffUrl: 'URL de apagado',
    restOnBody: 'Cuerpo de la solicitud de encendido',
    restOffBody: 'Cuerpo de la solicitud de apagado',
    restMethod: 'Método HTTP',
    restHeaders: 'Cabeceras personalizadas (JSON)',
    restStatusUrl: 'URL de estado',
    restStatusPath: 'Ruta JSON del estado',
    restStatusOnValue: 'Valor de ON',
    restPowerUrl: 'URL de potencia',
    restPowerPath: 'Ruta JSON de la potencia',
    restPowerMultiplier: 'Multiplicador de potencia',
    restEnergyUrl: 'URL de energía',
    restEnergyPath: 'Ruta JSON de energía (hoy)',
    restEnergyTotalPath: 'Ruta JSON de energía (contador total)',
    restEnergyTotalMultiplier: 'Multiplicador del contador total',
    restEnergyTotalPathHint: 'p. ej. aenergy.total',
    restEnergyTotalHint:
      'Muchos enchufes — todos los Shelly entre ellos — solo informan de un contador acumulado que nunca se reinicia. Va aquí, no en el campo de arriba: leído como el consumo de hoy nunca se pondría a cero a medianoche, y Ayer y Total seguirían vacíos. Bambuddy deduce Hoy y Ayer a partir de él, lo que requiere uno o dos días de lecturas. Un Shelly informa en vatios-hora, así que usa un multiplicador de 0.001.',
    restEnergyMultiplier: 'Multiplicador de energía',
    restUrlRequired: 'Se requiere al menos una URL (de encendido o apagado) para los enchufes REST',
    restHeadersHint: 'p. ej. {"Authorization": "Bearer your-token"}',
    restBodyHint: 'p. ej. ON, {"state": "on"}',
    restStatusHint: 'URL para sondear el estado actual',
    restPathHint: 'p. ej. state o data.power.status',
    restPowerUrlHint: 'URL independiente para los datos de potencia (usa la URL de estado si está vacía)',
    restEnergyUrlHint: 'URL independiente para los datos de energía (usa la URL de estado si está vacía)',
    restEnergyHint: 'Cada valor puede usar su propia URL o recurrir a la URL de estado. Use multiplicadores para la conversión de unidades (p. ej. 0,001 para convertir Wh en kWh).',
    testConnection: 'Probar conexión',
    connectionSuccess: 'Conexión correcta',
    noSwitchesInSwitchbar: 'No hay interruptores en la barra de interruptores',
    enableSwitchbarHint: 'Active «Mostrar en la barra de interruptores» en Ajustes > Enchufes inteligentes',
  },

  // Notifications
  notifications: {
    // Provider types
    providerTypes: {
      callmebot: 'CallMeBot/WhatsApp',
      ntfy: 'ntfy',
      pushover: 'Pushover',
      telegram: 'Telegram',
      email: 'Correo',
      discord: 'Discord',
      webhook: 'Webhook',
      homeassistant: 'Home Assistant',
      bark: 'Bark',
    },
    // Provider descriptions
    providerDescriptions: {
      email: 'Notificaciones por correo SMTP',
      telegram: 'Notificaciones mediante un bot de Telegram',
      discord: 'Enviar a un canal de Discord mediante un webhook',
      ntfy: 'Notificaciones push gratuitas y autoalojables',
      pushover: 'Notificaciones push sencillas y fiables',
      callmebot: 'Notificaciones de WhatsApp gratuitas mediante CallMeBot',
      webhook: 'POST HTTP genérico a cualquier URL',
      homeassistant: 'Notificaciones persistentes en el panel de Home Assistant',
      bark: 'Notificaciones push de iOS mediante Bark (autoalojable)',
    },
    // NotificationProviderCard
    lastSuccess: 'Última: {{date}}',
    error: 'Error',
    printer: 'Impresora:',
    allPrinters: 'Todas las impresoras',
    sendTestNotification: 'Enviar notificación de prueba',
    eventSettings: 'Ajustes de eventos',
    enabled: 'Activado',
    sendFromProvider: 'Enviar notificaciones desde este proveedor',
    // Event categories
    printEvents: 'Eventos de impresión',
    printerStatus: 'Estado de la impresora',
    amsAlarms: 'Alarmas del AMS',
    amsHtAlarms: 'Alarmas del AMS-HT',
    printQueue: 'Cola de impresión',
    // Event tags (badges)
    start: 'Inicio',
    plateCheck: 'Comprobación de cama',
    complete: 'Completada',
    failed: 'Fallida',
    stopped: 'Detenida',
    progress: 'Progreso',
    offline: 'Desconectada',
    lowFilament: 'Filamento bajo',
    maintenance: 'Mantenimiento',
    amsHumidity: 'Humedad del AMS',
    amsTemp: 'Temp. del AMS',
    amsDryingSuspended: 'Secado suspendido',
    amsHtHumidity: 'Humedad del AMS-HT',
    amsHtTemp: 'Temp. del AMS-HT',
    bedCooled: 'Cama enfriada',
    firstLayer: 'Primera capa',
    quiet: 'Silencio',
    digest: 'Resumen {{time}}',
    // Event labels (expanded settings)
    printStarted: 'Impresión iniciada',
    plateNotEmpty: 'Cama no vacía',
    plateNotEmptyDescription: 'Objetos detectados antes de la impresión',
    plateClearRequired: 'Confirmar cama libre',
    plateClearRequiredDescription: 'Impresión terminada, la cola espera confirmación',
    plateClear: 'Cama libre',
    printCompleted: 'Impresión completada',
    bedCooledLabel: 'Cama enfriada',
    bedCooledDescription: 'La cama se enfrió por debajo del umbral tras la impresión',
    firstLayerCompleteLabel: 'Primera capa completada',
    firstLayerCompleteDescription: 'Notificar con una captura cuando termina la primera capa',
    missingSpoolAssignmentLabel: 'Falta la asignación de bobina',
    billingChargeFailedLabel: 'Error de facturación',
    billingChargeFailedDescription: 'Notificar cuando no se puedan registrar los costes de impresión',
    missingSpoolAssignmentDescription: 'Notificar cuando la impresión comienza y las bandejas necesarias no tienen ninguna bobina asignada',
    printFailed: 'Impresión fallida',
    printStopped: 'Impresión detenida',
    progressMilestones: 'Hitos de progreso',
    progressMilestonesDescription: 'Notificar al 25%, 50% y 75%',
    printerOffline: 'Impresora desconectada',
    printerError: 'Error de la impresora',
    aiFailureDetection: 'Detección de fallos por IA',
    aiFailureDetectionDescription: 'Notificar cuando la IA de Obico detecte un posible fallo de impresión',
    lowFilamentLabel: 'Filamento bajo',
    maintenanceDue: 'Mantenimiento pendiente',
    maintenanceDueDescription: 'Notificar cuando se necesite mantenimiento',
    amsHumidityHigh: 'Humedad alta del AMS',
    amsHumidityHighDescription: 'La humedad del AMS normal supera el umbral',
    amsTemperatureHigh: 'Temperatura alta del AMS',
    amsTemperatureHighDescription: 'La temperatura del AMS normal supera el umbral',
    amsDryingSuspendedTitle: 'Secado automático suspendido',
    amsDryingSuspendedDescription: 'El secado automático se rindió con una unidad AMS porque la humedad nunca bajó del umbral',
    amsHtHumidityHigh: 'Humedad alta del AMS-HT',
    amsHtHumidityHighDescription: 'La humedad del AMS-HT supera el umbral',
    amsHtTemperatureHigh: 'Temperatura alta del AMS-HT',
    amsHtTemperatureHighDescription: 'La temperatura del AMS-HT supera el umbral',
    // Inventory stock alert events
    inventoryAlerts: 'Alertas de inventario',
    stockReorderAlert: 'Alerta de reabastecimiento',
    stockReorderAlertDescription: 'El SKU ha alcanzado su punto de reabastecimiento',
    stockBreakAlert: 'Alerta de rotura de existencias',
    stockBreakAlertDescription: 'Las existencias se agotarán antes de que llegue el reabastecimiento',
    // Queue events
    jobAdded: 'Trabajo añadido',
    jobAddedDescription: 'Trabajo añadido a la cola',
    jobAssigned: 'Trabajo asignado',
    jobAssignedDescription: 'Trabajo basado en el modelo asignado a una impresora',
    jobStarted: 'Trabajo iniciado',
    jobStartedDescription: 'El trabajo de la cola empezó a imprimirse',
    jobWaiting: 'Trabajo en espera',
    jobWaitingDescription: 'Trabajo a la espera de filamento o impresora',
    jobSkipped: 'Trabajo omitido',
    jobSkippedDescription: 'Trabajo omitido (el anterior falló)',
    jobFailed: 'Trabajo fallido',
    jobFailedDescription: 'El trabajo no pudo iniciarse',
    queueComplete: 'Cola completada',
    queueCompleteDescription: 'Todos los trabajos de la cola han terminado',
    // Quiet hours
    quietHours: 'Horas de silencio',
    noNotificationsDuring: 'No hay notificaciones durante estas horas',
    editProviderToChangeQuietHours: 'Edite el proveedor para cambiar las horas de silencio',
    // Daily digest
    dailyDigest: 'Resumen diario',
    batchNotifications: 'Agrupar las notificaciones en un único resumen diario',
    sendAt: 'Enviar a las {{time}}',
    editProviderToChangeDigestTime: 'Edite el proveedor para cambiar la hora del resumen',
    // Actions
    edit: 'Editar',
    deleteProvider: 'Eliminar proveedor de notificaciones',
    deleteConfirm: '¿Está seguro de que desea eliminar "{{name}}"? Esto no se puede deshacer.',
    delete: 'Eliminar',
    // AddNotificationModal
    addTitle: 'Añadir proveedor de notificaciones',
    editTitle: 'Editar proveedor de notificaciones',
    nameLabel: 'Nombre *',
    namePlaceholder: 'Mis notificaciones',
    providerTypeLabel: 'Tipo de proveedor *',
    configuration: 'Configuración',
    testConfiguration: 'Probar configuración',
    printerFilter: 'Filtro de impresora',
    onlyFromPrinter: 'Enviar notificaciones solo para los eventos de esta impresora',
    quietHoursDnd: 'Horas de silencio (no molestar)',
    quietStart: 'Inicio',
    quietEnd: 'Fin',
    dailyDigestLabel: 'Resumen diario',
    sendDigestAt: 'Enviar el resumen a las',
    digestCollected: 'Los eventos se recopilarán y se enviarán como un único resumen a esta hora',
    notificationEvents: 'Eventos de notificación',
    progressPercent: '(25%, 50%, 75%)',
    bedCooledAfterPrint: '(después de completar la impresión)',
    haSensorAlert: 'Alerta de sensor',
    haSensorAlertDescription: '(un sensor de Home Assistant vinculado requiere atención)',
    // Per-event ntfy priority (#990)
    eventPriority: {
      sectionTitle: 'Prioridad de ntfy',
      helpNtfy: 'Elija una prioridad para cada evento activado. ntfy las usa para escalar las alertas (sonido, visibilidad, comportamiento de las notificaciones push). Los niveles no establecidos aquí usan el valor predeterminado del servidor ntfy.',
      min: 'Mínima',
      low: 'Baja',
      default: 'Predeterminada',
      high: 'Alta',
      urgent: 'Urgente',
    },
    cancel: 'Cancelar',
    save: 'Guardar',
    add: 'Añadir',
    nameRequired: 'El nombre es obligatorio',
    fieldRequired: '{{field}} es obligatorio',
    haDataInvalid: 'El campo Data debe ser un objeto JSON válido, p. ej. {"priority": "high", "ttl": 0}',
    // Config field labels
    phoneNumber: 'Número de teléfono',
    apiKey: 'Clave API',
    serverUrl: 'URL del servidor',
    topic: 'Tema',
    authToken: 'Token de autenticación',
    userKey: 'Clave de usuario',
    appToken: 'Token de la aplicación',
    priority: 'Prioridad',
    pushoverRetry: 'Reintento de emergencia (s)',
    pushoverExpire: 'Expiración de emergencia (s)',
    botToken: 'Token del bot',
    chatId: 'ID del chat',
    telegramThreadId: 'ID del tema del foro',
    telegramThreadIdHelp: 'Opcional. Envía a un único tema de un grupo de foro: el último número del enlace del tema (t.me/c/.../25). Déjalo vacío para el tema General.',
    telegramThreadIdInvalid: 'El ID del tema del foro debe ser un número.',
    smtpServer: 'Servidor SMTP',
    smtpPort: 'Puerto SMTP',
    security: 'Seguridad',
    authentication: 'Autenticación',
    username: 'Nombre de usuario',
    password: 'Contraseña',
    fromEmail: 'Correo del remitente',
    toEmail: 'Correo del destinatario',
    webhookUrl: 'URL del webhook',
    payloadFormat: 'Formato de la carga útil',
    authorization: 'Autorización',
    titleFieldName: 'Nombre del campo del título',
    messageFieldName: 'Nombre del campo del mensaje',
    // NotificationTemplateEditor
    editTemplate: 'Editar plantilla: {{name}}',
    titleLabel: 'Título',
    bodyLabel: 'Cuerpo',
    titlePlaceholder: 'Título de la notificación...',
    bodyPlaceholder: 'Cuerpo de la notificación...',
    availableVariables: 'Variables disponibles',
    clickToInsert: 'Haga clic para insertar en la posición del cursor en el cuerpo',
    livePreview: 'Vista previa en directo',
    hide: 'Ocultar',
    show: 'Mostrar',
    loadingPreview: 'Cargando la vista previa...',
    enterTemplateContent: 'Introduzca el contenido de la plantilla para ver la vista previa',
    titlePreview: 'Título:',
    bodyPreview: 'Cuerpo:',
    resetToDefault: 'Restablecer al valor predeterminado',
    titleRequired: 'El título es obligatorio',
    bodyRequired: 'El cuerpo es obligatorio',
    // NotificationLogViewer
    notificationLog: 'Registro de notificaciones',
    showFailedOnly: 'Solo fallidas',
    last24Hours: 'Últimas 24 horas',
    last7Days: 'Últimos 7 días',
    last30Days: 'Últimos 30 días',
    last90Days: 'Últimos 90 días',
    justNow: 'Ahora mismo',
    noFailedNotifications: 'No hay notificaciones fallidas',
    noNotificationsLogged: 'No hay notificaciones registradas',
    unknownProvider: 'Proveedor desconocido',
    logTitle: 'Título',
    logMessage: 'Mensaje',
    logError: 'Error',
    logProvider: 'Proveedor: {{type}}',
    logTime: 'Hora: {{time}}',
    refresh: 'Actualizar',
    clearOld: 'Borrar antiguas',
    statsSummary: 'Últimos {{days}} días:',
    statsNotifications: 'notificaciones',
    statsSent: '{{count}} enviadas',
    statsFailed: '{{count}} fallidas',
    // Event type labels (for log viewer)
    eventTypes: {
      print_start: 'Impresión iniciada',
      print_complete: 'Impresión completada',
      print_failed: 'Impresión fallida',
      print_stopped: 'Impresión detenida',
      print_progress: 'Progreso',
      printer_offline: 'Impresora desconectada',
      printer_error: 'Error de la impresora',
      filament_low: 'Filamento bajo',
      maintenance_due: 'Mantenimiento pendiente',
      test: 'Prueba',
    },
    // User email notification preferences
    userEmail: {
      title: 'Notificaciones',
      emailNotifications: 'Notificaciones por correo',
      emailNotificationsDesc: 'Reciba notificaciones por correo de sus propios trabajos de impresión. Los correos se envían usando los ajustes de SMTP del sistema configurados en la autenticación avanzada.',
      sendingTo: 'Las notificaciones se enviarán a',
      noEmailWarning: 'Su cuenta no tiene una dirección de correo. Póngase en contacto con un administrador para añadir una.',
      printJobNotifications: 'Notificaciones de trabajos de impresión',
      printJobNotificationsDesc: 'Elija qué eventos activan las notificaciones por correo para los trabajos de impresión que envíe.',
      printJobStarts: 'El trabajo de impresión comienza',
      printJobStartsDesc: 'Reciba una notificación cuando su trabajo de impresión comience.',
      printJobFinishes: 'El trabajo de impresión termina',
      printJobFinishesDesc: 'Reciba una notificación cuando su trabajo de impresión se complete correctamente.',
      printErrors: 'Errores de impresión',
      printErrorsDesc: 'Reciba una notificación cuando su trabajo de impresión falle o encuentre un error.',
      printJobStops: 'El trabajo de impresión se detiene',
      printJobStopsDesc: 'Reciba una notificación cuando su trabajo de impresión se cancele o se detenga.',
      saveSuccess: 'Preferencias de notificación guardadas.',
      saveError: 'Error al guardar las preferencias de notificación.',
    },
  },

  // Rich Text Editor
  richTextEditor: {
    bold: 'Negrita',
    italic: 'Cursiva',
    underline: 'Subrayado',
    bulletList: 'Lista con viñetas',
    numberedList: 'Lista numerada',
    alignLeft: 'Alinear a la izquierda',
    alignCenter: 'Centrar',
    alignRight: 'Alinear a la derecha',
    addLink: 'Añadir enlace',
    removeLink: 'Quitar enlace',
  },

  // External Links
  externalLinks: {
    title: 'Enlaces de la barra lateral',
    sidebarLayout: 'Barra lateral',
    sidebarLayoutDescription: 'Muestra u oculta páginas integradas, añade enlaces externos y arrastra elementos para reordenar la navegación lateral.',
    systemPages: 'Páginas de Bambuddy',
    externalLinks: 'Enlaces externos',
    visibleInSidebar: 'Visible en la barra lateral',
    hiddenFromSidebar: 'Oculto en la barra lateral',
    requiredInSidebar: 'Obligatorio en la barra lateral',
    hidePage: 'Ocultar página',
    showPage: 'Mostrar página',
    settingsCannotBeHidden: 'Ajustes no se puede ocultar',
    noLinksConfigured: 'No hay enlaces externos configurados',
    deleteLink: 'Eliminar enlace',
    removeCustomIcon: 'Quitar el icono personalizado',
    openInNewTab: 'Abrir en una pestaña nueva',
    placeholders: {
      linkName: 'Mi enlace',
    },
  },

  // Keyboard Shortcuts Modal
  keyboardShortcuts: {
    title: 'Atajos de teclado',
    navigation: 'Navegación',
    archivesSection: 'Archivos',
    kProfilesSection: 'Perfiles K',
    generalSection: 'General',
    shortcuts: {
      goToPrinters: 'Ir a Impresoras',
      goToArchives: 'Ir a Archivos',
      goToQueue: 'Ir a la cola',
      goToStats: 'Ir a Estadísticas',
      goToProfiles: 'Ir a Perfiles en la nube',
      goToSettings: 'Ir a Ajustes',
      focusSearch: 'Enfocar la búsqueda',
      openUploadModal: 'Abrir la ventana de subida',
      clearSelection: 'Borrar la selección / quitar el foco del campo',
      contextMenu: 'Menú contextual en las tarjetas',
      refreshProfiles: 'Actualizar perfiles',
      newProfile: 'Nuevo perfil',
      exitSelectionMode: 'Salir del modo de selección',
      showHelp: 'Mostrar esta ayuda',
    },
    footer: 'Pulse Esc o haga clic fuera para cerrar',
  },

  // Notification Log
  notificationLog: {
    title: 'Registro de notificaciones',
    events: {
      printStarted: 'Impresión iniciada',
      printComplete: 'Impresión completada',
      printFailed: 'Impresión fallida',
      printStopped: 'Impresión detenida',
      progress: 'Progreso',
      printerOffline: 'Impresora desconectada',
      printerError: 'Error de la impresora',
      lowFilament: 'Filamento bajo',
      maintenanceDue: 'Mantenimiento pendiente',
      test: 'Prueba',
    },
    timeAgo: {
      justNow: 'Ahora mismo',
      minutesAgo: 'hace {{minutes}} min',
      hoursAgo: 'hace {{hours}} h',
    },
  },

  // Restore/Backup Modal
  restoreBackup: {
    title: 'Restaurar copia de seguridad',
    restoring: 'Restaurando...',
    restoreComplete: 'Restauración completada',
    restoreFailed: 'Error en la restauración',
    importSettings: 'Importar ajustes desde un archivo de copia de seguridad',
    pleaseWait: 'Espere mientras se restauran sus datos',
    clickToSelect: 'Haga clic para seleccionar el archivo de copia de seguridad (.json o .zip)',
    howDuplicateHandling: 'Cómo funciona la gestión de duplicados:',
    categories: {
      printers: 'Impresoras',
      smartPlugs: 'Enchufes inteligentes',
      notificationProviders: 'Proveedores de notificaciones',
      filaments: 'Filamentos',
      archives: 'Archivos',
      pendingUploads: 'Subidas pendientes',
      settingsTemplates: 'Ajustes y plantillas',
    },
    matchingInfo: {
      printers: 'coincidencia por número de serie',
      smartPlugs: 'coincidencia por dirección IP',
      notificationProviders: 'coincidencia por nombre',
      filaments: 'coincidencia por nombre + tipo + marca',
      archives: 'coincidencia por hash del contenido',
      pendingUploads: 'coincidencia por nombre de archivo',
      settingsTemplates: 'siempre se sobrescriben',
    },
    replaceExisting: 'Reemplazar los datos existentes',
    keepExisting: 'Conservar los datos existentes',
    replaceDescription: 'Sobrescribir los elementos que ya existen con los datos de la copia de seguridad',
    keepDescription: 'Restaurar solo los elementos que aún no existen',
    caution: 'Precaución:',
    cautionText: 'Sobrescribir reemplazará sus configuraciones actuales con los datos de la copia de seguridad. Los códigos de acceso de las impresoras nunca se sobrescriben por seguridad.',
    itemsRestored: 'Elementos restaurados',
    itemsSkipped: 'Elementos omitidos',
    restored: 'Restaurados',
    skipped: 'Omitidos (ya existen)',
    filesLabel: 'Archivos (3MF, miniaturas, etc.)',
    newApiKeysGenerated: 'Nuevas claves API generadas',
    newApiKeysWarning: 'Estas claves solo se muestran una vez. ¡Cópielas ahora!',
    processingBackup: 'Procesando el archivo de copia de seguridad...',
    noDataFound: 'No se encontraron datos para restaurar en el archivo de copia de seguridad.',
    failedToRestore: 'Error al restaurar la copia de seguridad. Compruebe el formato del archivo.',
  },

  // Backup Export Modal
  backupExport: {
    title: 'Exportar copia de seguridad',
    selectData: 'Seleccione los datos que incluir',
    selectAll: 'Seleccionar todo',
    selectNone: 'No seleccionar nada',
    categoryDescriptions: {
      settings: 'Idioma, tema, preferencias de actualización',
      notifications: 'ntfy, Pushover, Discord, etc.',
      templates: 'Plantillas de mensajes personalizadas',
      smartPlugs: 'Configuraciones de enchufes Tasmota',
      externalLinks: 'Enlaces de la barra lateral a servicios externos',
      printers: 'Información de las impresoras (códigos de acceso excluidos)',
      plateDetection: 'Imágenes de referencia de la cama vacía',
      filaments: 'Tipos y costes de filamento',
      maintenance: 'Programaciones de mantenimiento personalizadas',
      archives: 'Todos los datos de impresión + archivos (3MF, miniaturas, fotos)',
      projects: 'Proyectos, elementos de la lista de materiales y adjuntos',
      pendingUploads: 'Subidas de la impresora virtual pendientes de revisión',
      apiKeys: 'Claves API de webhook (se generan claves nuevas al importar)',
    },
    requiresPrinters: 'Requiere que se seleccionen las impresoras',
    zipFileWarning: 'Se creará un archivo ZIP.',
    zipFileDescription: 'Incluye todos los archivos 3MF, las miniaturas, los time-lapses y las fotos. Esto puede tardar un rato y dar como resultado un archivo grande.',
    includeAccessCodes: 'Incluir los códigos de acceso',
    includeAccessCodesDescription: 'Para transferir a otra máquina',
    includeAccessCodesWarning: 'Los códigos de acceso se incluirán en texto plano. ¡Mantenga este archivo de copia de seguridad seguro!',
    categoriesSelected: '{{selectedCount}} categorías seleccionadas',
  },

  // Pending Uploads Panel
  pendingUploads: {
    placeholders: {
      notes: 'Añada notas sobre esta impresión...',
    },
    discardUpload: 'Descartar subida',
    archiveAllUploads: 'Archivar todas las subidas',
    discardAllUploads: 'Descartar todas las subidas',
    archive: 'Archivar',
    timeAgo: {
      justNow: 'Ahora mismo',
      minutesAgo: 'hace {{minutes}} min',
      hoursAgo: 'hace {{hours}} h',
      daysAgo: 'hace {{days}} d',
    },
  },

  // API Browser
  apiBrowser: {
    placeholders: {
      requestBody: 'Cuerpo de la solicitud JSON...',
      searchEndpoints: 'Buscar puntos de conexión...',
    },
  },

  // Configure AMS Slot Modal
  configureAmsSlot: {
    title: 'Configurar la ranura del AMS',
    slotConfigured: '¡Ranura configurada!',
    configuringSlot: 'Configurando la ranura:',
    slotLabel: '{{ams}} Ranura {{slot}}',
    searchPresets: 'Buscar preajustes...',
    colorPlaceholder: 'Nombre del color o hexadecimal (p. ej., marrón, FF8800)',
    clearCustomColor: 'Borrar el color personalizado',
    noCloudPresets: 'No hay preajustes de la nube. Inicie sesión en Bambu Cloud para sincronizar.',
    noPresetsAvailable: 'No hay preajustes disponibles. Inicie sesión en Bambu Cloud o importe perfiles locales.',
    noMatchingPresets: 'No se encontraron preajustes coincidentes.',
    custom: 'Personalizado',
    builtin: 'Integrado',
    orcaCloud: 'Orca Cloud',
    bambuCloud: 'Bambu Cloud',
    settingsSentToPrinter: 'Ajustes enviados a la impresora',
    filamentProfile: 'Perfil de filamento',
    kProfileLabel: 'Perfil K (avance de presión)',
    filteringFor: 'Filtrando por: {{material}}',
    noKProfile: 'Sin perfil K (usar el predeterminado 0,020)',
    noMatchingKProfiles: 'No se encontraron perfiles K coincidentes. Se usará el K=0,020 predeterminado.',
    otherKProfiles: 'Otros perfiles K en esta impresora',
    selectFilamentFirst: 'Seleccione primero un perfil de filamento',
    kFromCalibration: 'K={{value}} de la calibración de la impresora',
    customColorLabel: 'Color personalizado (opcional)',
    presetColors: 'Colores de {{name}}:',
    showLessColors: 'Mostrar menos colores',
    showMoreColors: 'Mostrar más colores',
    clear: 'Borrar',
    hexLabel: 'Hex: #{{hex}}',
    resetting: 'Restableciendo...',
    resetSlot: 'Restablecer ranura',
    cancel: 'Cancelar',
    configuring: 'Configurando...',
    configureSlot: 'Configurar ranura',
  },

  // Git Backup Settings
  githubBackup: {
    title: 'Copia de seguridad en Git',
    history: 'Historial',
    downloadBackup: 'Descargar copia de seguridad',
    restoreBackup: 'Restaurar copia de seguridad',
    noBackupsYet: 'Aún no hay copias de seguridad',
  },

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

  // Tag Management Modal
  tagManagement: {
    searchTags: 'Buscar etiquetas...',
    renameTag: 'Renombrar etiqueta',
    deleteTag: 'Eliminar etiqueta',
  },

  // Notification Template Editor
  notificationTemplates: {
    placeholders: {
      title: 'Título de la notificación...',
      body: 'Cuerpo de la notificación...',
    },
  },

  // Batch Tag Modal
  batchTag: {
    placeholders: {
      newTag: 'Introduzca una nueva etiqueta...',
    },
  },

  // Photo Gallery Modal
  photoGallery: {
    deletePhoto: 'Eliminar foto',
  },

  // Filament Hover Card
  filamentHoverCard: {
    copySpoolUuid: 'Copiar el UUID de la bobina',
  },

  // K Profiles View
  kProfilesView: {
    hasNote: 'Tiene nota',
    copyProfile: 'Copiar perfil',
  },

  // Layout/Navigation
  layout: {
    openMenu: 'Abrir menú',
    noPermissionSystemInfo: 'No tiene permiso para ver la información del sistema',
  },

  // Dashboard
  dashboard: {
    dragToReorder: 'Arrastre para reordenar',
    hideWidget: 'Ocultar widget',
  },

  // Notification Provider Card
  notificationProviderCard: {
    deleteNotificationProvider: 'Eliminar proveedor de notificaciones',
  },

  // File Manager Modal
  fileManagerModal: {
    closeFileManager: 'Cerrar el gestor de archivos',
    sortFiles: 'Ordenar archivos',
    goToParentFolder: 'Ir a la carpeta superior',
    threeView: 'Vista 3D',
  },

  // Embedded Camera Viewer
  embeddedCameraViewer: {
    refreshStream: 'Actualizar la transmisión',
    close: 'Cerrar',
    zoomOut: 'Alejar',
    resetZoom: 'Restablecer el zoom',
    zoomIn: 'Acercar',
    dragToResize: 'Arrastre para redimensionar',
  },

  // Timelapse Viewer
  timelapseViewer: {
    skipBack5s: 'Retroceder 5 s',
    skipForward5s: 'Avanzar 5 s',
  },

  // Notification Providers
  notificationProviders: {
    descriptions: {
      email: 'Notificaciones por correo SMTP',
      telegram: 'Notificaciones mediante un bot de Telegram',
      discord: 'Enviar a un canal de Discord mediante un webhook',
      ntfy: 'Notificaciones push gratuitas y autoalojables',
      pushover: 'Notificaciones push sencillas y fiables',
      callmebot: 'Notificaciones de WhatsApp gratuitas mediante CallMeBot',
      webhook: 'POST HTTP genérico a cualquier URL',
    },
  },

  // Log Viewer
  logViewer: {
    searchPlaceholder: 'Buscar mensaje o nombre del registrador...',
    noLogEntries: 'No se encontraron entradas de registro',
  },

  // Switchbar Popover
  switchbarPopover: {
    noSwitchesInSwitchbar: 'No hay interruptores en la barra de interruptores',
  },

  // Project Page Modal
  projectPageModal: {
    placeholders: {
      title: 'Título',
      designer: 'Diseñador',
      license: 'Licencia',
      description: 'Introduzca una descripción...',
      profileTitle: 'Título del perfil',
      profileDescription: 'Descripción del perfil...',
    },
  },

  // Spoolman Settings
  spoolmanSettings: {},

  // Time
  time: {
    unknown: '-',
    waiting: 'En espera',
    justNow: 'Ahora mismo',
    now: 'Ahora',
    minsAgo: 'hace {{count}} min',
    inMins: 'en {{count}} min',
    hoursAgo: 'hace {{count}} h',
    inHours: 'en {{count}} h',
    daysAgo: 'hace {{count}} d',
    inDays: 'en {{count}} d',
  },

  // SpoolBuddy Kiosk
  spoolbuddy: {
    nav: {
      dashboard: 'Panel',
      ams: 'AMS',
      inventory: 'Inventario',
      writeTag: 'Escribir',
      settings: 'Ajustes',
    },
    status: {
      nfcReady: 'NFC listo',
      nfcOff: 'NFC apagado',
      offline: 'Desconectado',
      online: 'En línea',
      noPrinters: 'No hay impresoras',
      deviceOffline: 'Dispositivo desconectado',
      waitingConnection: 'Esperando la conexión del dispositivo...',
      systemReady: 'Sistema listo',
      status: 'Estado',
    },
    dashboard: {
      readyToScan: 'Listo para escanear',
      idleMessage: 'Coloque una bobina en la báscula para identificarla',
      nfcHint: 'La etiqueta NFC se leerá automáticamente',
      device: 'Dispositivo',
      syncWeight: 'Sincronizar peso',
      weightSynced: '¡Sincronizado!',
      unknownTag: 'Etiqueta desconocida',
      newTag: 'Nueva etiqueta detectada',
      onScale: 'en la báscula',
      linkSpool: 'Vincular a bobina',
      linkTagTitle: 'Vincular la etiqueta a una bobina',
      linkTag: 'Vincular etiqueta',
      selectSpool: 'Seleccione una bobina a la que vincular esta etiqueta:',
      noUntagged: 'No se encontraron bobinas sin etiquetas',
      tagDetected: 'Etiqueta detectada',
      noTag: 'Sin etiqueta',
      tagId: 'Etiqueta',
      grossWeight: 'Peso bruto',
      spoolSize: 'Tamaño de la bobina',
      close: 'Cerrar',
      currentSpool: 'Bobina actual',
      plateReady: 'Cama lista: {{name}}',
      plateReadyLabel: 'Camas listas para despejar',
      plateClearAction: 'Despejar',
      plateClearedToast: 'Cama marcada como despejada',
      plateClearFailed: 'No se pudo marcar la cama como despejada',
    },
    modal: {
      spoolDetected: 'Bobina detectada',
      assignToAms: 'Asignar al AMS',
      syncWeight: 'Sincronizar peso',
      weightSynced: '¡Sincronizado!',
      syncing: 'Sincronizando...',
      newTagDetected: 'Nueva etiqueta detectada',
      addToInventory: 'Añadir al inventario',
      assignToAmsTitle: 'Asignar al AMS',
      selectSlot: 'Seleccione una ranura',
      assign: 'Asignar',
      assigning: 'Asignando...',
      assignSuccess: '¡Asignada!',
      assignPendingInsert: 'Asignada. La ranura se configurará cuando inserte la bobina.',
      assignError: 'Error al asignar la bobina. Inténtelo de nuevo.',
      noPrinterSelected: 'Seleccionar una impresora...',
      noAmsDetected: 'No se detectó ningún AMS en esta impresora',
      slot: 'Ranura',
    },
    weight: {
      noReading: 'Sin lectura',
      stable: 'Estable',
      measuring: 'Midiendo...',
      tare: 'Tara',
      calibrate: 'Calibrar',
    },
    spool: {
      remaining: 'Restante',
      material: 'Material',
      brand: 'Marca',
      color: 'Color',
      coreWeight: 'Núcleo',
      labelWeight: 'Etiqueta',
      scaleWeight: 'Báscula',
      netWeight: 'Neto',
      lastUsed: 'Usada por última vez',
    },
    ams: {
      noData: 'No se detectó ningún AMS',
      connectAms: 'Conecte un AMS para ver las ranuras de filamento',
      noPrinter: 'No hay impresora seleccionada',
      selectPrinter: 'Seleccione una impresora en la barra superior',
      printerDisconnected: 'Impresora desconectada',
      humidity: 'Humedad',
      level: 'Nivel',
      active: 'Activa',
      slot: 'Ranura',
      empty: 'Vacía',
    },
    inventory: {
      search: 'Buscar bobinas...',
      empty: 'No hay bobinas en el inventario',
      noResults: 'No hay bobinas coincidentes',
      spools: 'bobinas',
      addSpool: 'Añadir bobina',
    },
    settings: {
      // Tabs
      tabDevice: 'Dispositivo',
      tabDisplay: 'Pantalla',
      tabScale: 'Báscula',
      tabUpdates: 'Actualizaciones',
      // Device tab
      nfcReader: 'Lector NFC',
      type: 'Tipo',
      connection: 'Conexión',
      notConnected: 'N/D',
      deviceInfo: 'Información del dispositivo',
      hostname: 'Host',
      uptime: 'Tiempo de actividad',
      systemConfig: 'Backend y autenticación',
      backendUrl: 'URL del backend de Bambuddy',
      apiToken: 'Token de API',
      apiTokenPlaceholder: 'Introduzca el token de API',
      saveConfig: 'Guardar configuración',
      systemQueued: 'Configuración encolada.',
      nfcDiagnostic: 'Diagnóstico de NFC',
      scaleDiagnostic: 'Diagnóstico de la báscula',
      readTagDiagnostic: 'Diagnóstico de lectura de etiqueta',
      testNfc: 'Probar el lector',
      testScale: 'Probar la precisión',
      testReadTag: 'Leer etiqueta',
      systemFieldsRequired: 'La URL del backend es obligatoria.',
      // Display tab
      brightness: 'Brillo',
      saved: 'Guardado',
      noBacklight: 'No se detectó retroiluminación DSI. El control del brillo requiere una pantalla DSI.',
      screenBlank: 'Tiempo de espera para apagar la pantalla',
      screenBlankDesc: 'La pantalla se apaga tras la inactividad. Toque para activarla.',
      displayNote: 'El brillo se aplica como un filtro de software.',
      // Scale tab
      scaleCalibration: 'Calibración de la báscula',
      currentWeight: 'Peso actual',
      tareOffset: 'Tara',
      calFactor: 'Factor',
      knownWeight: 'Peso conocido',
      calStep1: 'Retire todos los elementos de la báscula y pulse Poner a cero.',
      calStep2: 'Coloque el peso conocido en la báscula.',
      setZero: 'Poner a cero',
      calibrateNow: 'Calibrar',
      calibrated: 'Calibrada',
      tareSet: 'Comando de tara enviado. Esperando al dispositivo...',
      tareComplete: '¡Tara completada!',
      tareTimedOut: 'Tara agotó el tiempo de espera — ¿está activo el daemon de SpoolBuddy?',
      tareFailed: 'Error al enviar el comando de tara',
      zeroSet: 'Punto cero establecido. Coloque el peso conocido en la báscula.',
      calibrationDone: '¡Calibración completada!',
      calibrationFailed: 'Error en la calibración',
      lastCalibrated: 'Calibrada por última vez',
      stable: 'Estable',
      settling: 'Estabilizando...',
      firmware: 'Firmware',
      scale: 'Báscula',
      noDevice: 'No se encontró ningún dispositivo SpoolBuddy',
      // Updates tab
      daemonVersion: 'Versión del demonio',
      currentVersion: 'Actual',
      versionPending: 'Esperando al demonio...',
      checking: 'Comprobando...',
      checkUpdates: 'Buscar actualizaciones',
      updateAvailable: 'Actualización disponible',
      updateInstructions: 'Actualice mediante SSH: ejecute el script de instalación de SpoolBuddy para actualizar.',
      upToDate: 'Actualizado',
      includeBeta: 'Incluir versiones beta',
    },
    writeTag: {
      tabExisting: 'Bobina existente',
      tabNew: 'Bobina nueva',
      tabReplace: 'Reemplazar etiqueta',
      searchPlaceholder: 'Buscar por material, color, marca...',
      noUntaggedSpools: 'No hay bobinas sin etiquetas',
      noTaggedSpools: 'No hay bobinas con etiquetas',
      selectSpool: 'Seleccione una bobina y luego coloque una etiqueta NTAG en blanco en el lector',
      placeTag: 'Coloque una etiqueta NTAG en el lector',
      tagReady: 'Etiqueta detectada — lista para escribir',
      writeTag: 'Escribir etiqueta',
      replaceTag: 'Reemplazar etiqueta',
      writing: 'Escribiendo la etiqueta...',
      waiting: 'Esperando a SpoolBuddy...',
      writeSuccess: '¡Etiqueta escrita correctamente!',
      writeFailed: 'Error al escribir',
      queueFailed: 'Error al encolar el comando de escritura',
      tryAgain: 'Inténtelo de nuevo',
      cancel: 'Cancelar',
      replaceWarning: 'La etiqueta antigua se desvinculará. La nueva etiqueta la reemplazará.',
      deviceOffline: 'SpoolBuddy está desconectado',
      material: 'Material',
      colorName: 'Nombre del color',
      color: 'Color',
      brand: 'Marca',
      weight: 'Peso (g)',
      createSpool: 'Crear bobina',
      creating: 'Creando...',
      spoolCreated: '¡Bobina creada! Lista para escribir.',
      createFailed: 'Error al crear la bobina',
      incompleteDataWarning: 'Etiqueta escrita con datos de Spoolman incompletos',
    },
    quickMenu: {
      printerPower: 'Alimentación de la impresora',
      systemControls: 'Sistema',
      restartDaemon: 'Reiniciar demonio',
      restartBrowser: 'Reiniciar navegador',
      reboot: 'Reiniciar',
      shutdown: 'Apagar',
      swipeToClose: 'Deslice hacia abajo para cerrar',
      confirmTitle: 'Confirmar',
      confirmShutdown: '¿Está seguro de que desea apagar el SpoolBuddy? Necesitará acceso físico para volver a encenderlo.',
      confirmReboot: '¿Está seguro de que desea reiniciar el SpoolBuddy?',
      confirmRestartDaemon: '¿Reiniciar el demonio de SpoolBuddy? El NFC y la báscula no estarán disponibles temporalmente.',
      confirmRestartBrowser: '¿Reiniciar el navegador del quiosco? La pantalla se quedará en negro brevemente.',
      confirm: 'Confirmar',
      confirmPlugOn: '¿Encender {{name}}?',
      confirmPlugOff: '¿Apagar {{name}}?',
      turnOn: 'Encender',
      turnOff: 'Apagar',
    },
  },

  diagnostic: {
    modalTitle: 'Diagnóstico de conexión — {{name}}',
    running: 'Ejecutando el diagnóstico...',
    runningElapsed: 'Ejecutando el diagnóstico... ({{elapsed}}s)',
    waitingForReportHint: 'Esperando a que la impresora publique un informe de estado — puede tardar hasta {{max}} segundos.',
    runFailed: 'No se pudo ejecutar el diagnóstico: {{error}}',
    retry: 'Ejecutar de nuevo',
    runButton: 'Ejecutar diagnóstico',
    sectionTitle: 'Diagnóstico de conexión',
    sectionDescription: 'Compruebe por qué una impresora no se conecta o no imprime — accesibilidad de los puertos, modo desarrollador LAN, modo de red de Docker y credenciales.',
    noPrinters: 'No hay impresoras configuradas.',
    overall: {
      ok: 'No se encontraron problemas — la conexión de la impresora parece correcta.',
      warnings: 'La impresora debería funcionar, pero algunas cosas necesitan atención.',
      problems: 'Se encontraron problemas que explican por qué la impresora no se conecta o no imprime.',
    },
    check: {
      port_mqtt: {
        title: 'Puerto de control (MQTT 8883)',
        pass: 'Accesible — la impresora acepta conexiones de control.',
        fail: 'El puerto 8883 no es accesible. La impresora está apagada, en una dirección IP distinta, o un cortafuegos lo está bloqueando. Verifique la IP de la impresora y que nada bloquee el puerto 8883.',
      },
      port_ftps: {
        title: 'Puerto de transferencia de archivos (FTPS 990)',
        pass: 'Accesible — el envío de archivos de impresión funcionará.',
        warn: 'El puerto 990 no es accesible. La supervisión puede seguir funcionando, pero el envío de impresiones a la impresora fallará. Asegúrese de que el puerto 990 no esté bloqueado.',
        warn_no_tls: 'El puerto 990 está abierto, pero el servicio de archivos de la impresora no completa el protocolo de enlace TLS. No se pueden obtener los archivos de impresión, las portadas ni los timelapses, por lo que los archivos quedan vacíos. Reinicie la impresora — desbloquear el puerto no servirá.',
      },
      external_storage: {
        title: 'Almacenar archivos enviados en almacenamiento externo (paso 4 de instalación)',
        pass: 'La impresora informa que esta opción está activada — los archivos enviados se guardarán en la tarjeta SD y los archivos tendrán miniaturas y metadatos del slicer.',
        fail: 'La impresora informa que esta opción está desactivada. Active "Almacenar archivos enviados en almacenamiento externo" — en firmware reciente (P2S 01.02 / Bambu Studio 2.6+) el conmutador está en los Ajustes de Impresión de la impresora; en versiones anteriores está en la pestaña Dispositivo de Bambu Studio / OrcaSlicer. Sin esta opción, cada impresión archivada queda sin miniatura ni metadatos del slicer.',
        skip: 'No comprobado — se necesita una conexión MQTT activa. En slicers más antiguos donde este ajuste solo existe en el slicer, la impresora no lo reporta, así que esta comprobación pasa aunque la opción esté desactivada — verifique el paso 4 de la instalación manualmente.',
        skip_unsupported_model: 'Este modelo tiene ranura SD pero no hay forma de activar la opción — el firmware actual de la serie P1 no muestra el interruptor en Bambu Studio y la impresora no tiene pantalla. Aquí no hay nada que arreglar; a las impresiones archivadas pueden faltarles miniaturas y metadatos del slicer hasta que Bambu Lab lo admita por firmware.',
        fail_no_media: 'La opción está activada, pero la impresora no detecta ninguna tarjeta ni memoria en su ranura, así que los archivos enviados no tienen dónde ir. Inserte una e imprima de nuevo — hasta entonces, cada impresión archivada carecerá de miniatura y de metadatos del laminador.',
        warn_internal_storage: 'La opción está activada y hay almacenamiento presente, pero esta impresora aun así guardó la última impresión en su almacenamiento interno, que Bambuddy no puede leer. El firmware de la serie H2 y de la P2S hace esto y ningún ajuste lo cambia. Las impresiones se archivan con su nombre y sus tiempos, pero sin miniatura ni metadatos del laminador.',
      },
      port_rtsps: {
        title: 'Puerto de la cámara ({{protocol}} {{port}})',
        pass: 'Accesible — la transmisión de la cámara funcionará.',
        warn: 'El puerto {{port}} no es accesible. La vista de la cámara en directo no funcionará. Esto no afecta a la impresión.',
      },
      network_mode: {
        title: 'Modo de red de Docker',
        pass: 'Ejecutándose en modo de red de host.',
        warn: 'Bambuddy se está ejecutando en red de tipo bridge de Docker. La detección de impresoras y la impresora virtual necesitan el modo de red de host — vuelva a crear el contenedor con "network_mode: host".',
        skip: 'No se está ejecutando en Docker — no aplicable.',
      },
      subnet: {
        title: 'Subred de la red',
        pass: 'La impresora y Bambuddy están en la misma subred.',
        warn: 'La impresora ({{printer_ip}}) y Bambuddy ({{host_ip}}) están en subredes distintas. Es posible que no se alcancen entre sí a menos que se configure el enrutamiento entre las subredes.',
        skip: 'No se pudo determinar la subred — omitido.',
      },
      mqtt_auth: {
        title: 'Credenciales de la impresora',
        pass: 'La impresora aceptó la conexión.',
        fail: 'La impresora es accesible pero Bambuddy no está conectado a ella. Lo más probable es que el código de acceso o el número de serie sean incorrectos — el código de acceso cambia cada vez que se conmuta el modo Solo LAN o el modo desarrollador, así que vuelva a copiarlo de la pantalla de la impresora. Una impresora que se está reiniciando, o que ya alcanzó su límite de conexiones simultáneas, se ve igual.',
        fail_auth_rejected: 'La impresora rechazó las credenciales de Bambuddy. El código de acceso o el número de serie son incorrectos — el código de acceso cambia cada vez que se conmuta el modo Solo LAN o el modo desarrollador, así que vuelva a copiarlo de la pantalla de la impresora y guárdelo en la configuración de la impresora.',
        skip: 'No comprobado — no se pudo alcanzar la impresora.',
      },
      developer_mode: {
        title: 'Modo desarrollador LAN',
        pass: 'El modo desarrollador está activado.',
        fail: 'El modo desarrollador está DESACTIVADO en la impresora. Actívelo en los ajustes de LAN de la impresora — y confirme con Aceptar. Sin él, las impresiones no comenzarán.',
        skip: 'No se pudo comprobar — requiere una conexión activa con la impresora.',
      },
      printer_publishing: {
        title: 'La impresora publica estado',
        pass: 'La impresora está publicando actualizaciones de estado — AMS, filamentos y perfiles K se reflejarán correctamente en el slicer.',
        fail: 'El broker MQTT aceptó la conexión, pero la impresora no ha publicado ningún informe de estado. Casi siempre se trata de un número de serie incorrecto o con mayúsculas/minúsculas mal escritas — el topic device/<serial>/report distingue entre mayúsculas y minúsculas. Revise el número de serie en los ajustes de la impresora comparándolo con la pantalla del aparato.',
        skip: 'No se pudo comprobar — requiere una conexión activa con la impresora.',
      },
    },
  },

  systemHealth: {
    sectionTitle: 'Estado del sistema',
    sectionDescription: 'Analiza los registros recientes en busca de problemas conocidos que normalmente puedes resolver tú mismo, antes de que se conviertan en un ticket de soporte.',
    rescan: 'Volver a analizar',
    clean: 'No se encontraron problemas conocidos en las últimas {{times}} entradas de registro.',
    logUnavailable: 'El registro en archivo está desactivado, por lo que no se pueden analizar los registros. Activa el registro en archivo para usar esta comprobación.',
    learnMore: 'Cómo solucionarlo',
    fixLabel: 'Solución:',
    occurrences: 'Visto {{times}}× — última vez a las {{lastSeen}}',
    category: {
      layer8: 'Puedes solucionarlo',
      environment: 'Entorno',
      bug: 'Por favor, repórtalo',
    },
    signature: {
      'ftp-auth-rejected': {
        name: 'La impresora rechazó el código de acceso',
        cause: 'La impresora rechazó el inicio de sesión de transferencia de archivos. El código de acceso es incorrecto o cambió tras activar el Modo Desarrollador.',
        fix: 'Vuelve a copiar el código de acceso desde la pantalla de la impresora (ajustes de LAN) y actualízalo en los ajustes de la impresora en Bambuddy.',
      },
      'ftp-connection-timeout': {
        name: 'Se agotó el tiempo de la conexión de transferencia de archivos',
        cause: 'Bambuddy no pudo alcanzar el puerto de transferencia de archivos de la impresora (FTPS 990). El puerto está bloqueado, o la impresora está apagada o en otra subred.',
        fix: 'Asegúrate de que nada bloquee el puerto 990 entre Bambuddy y la impresora, y de que ambos estén en la misma red.',
      },
      'ftp-ssl-error': {
        name: 'Falló el protocolo de enlace seguro de transferencia de archivos',
        cause: 'El servicio de archivos de la impresora respondió en el puerto 990 sin TLS. Su servidor de archivos se ha bloqueado: es un fallo de la impresora, no del cortafuegos ni del firmware.',
        fix: 'Reinicia la impresora. Hasta entonces no se pueden descargar archivos de impresión, portadas ni timelapses; la impresión en sí no se ve afectada.',
      },
      'bambu-cloud-captcha': {
        name: 'Bambu Cloud solicita un CAPTCHA',
        cause: 'La protección antiabuso de Bambu está verificando esta red, por lo que ningún inicio de sesión en Bambu Cloud puede completarse. Depende de la dirección IP pública, no de tu cuenta ni de esta instalación.',
        fix: 'Espera: suele resolverse en unas horas y los intentos repetidos lo prolongan. Mientras tanto, conéctate con un token de acceso obtenido en una sesión del navegador.',
      },
      'mqtt-connection-flapping': {
        name: 'La conexión con la impresora se cae continuamente',
        cause: 'La conexión de control (MQTT 8883) se desconecta y reconecta repetidamente, normalmente por una red débil o un puerto parcialmente bloqueado.',
        fix: 'Comprueba la señal Wi-Fi en la impresora, usa preferentemente una conexión por cable y asegúrate de que el puerto 8883 sea accesible de forma fiable.',
      },
      'camera-connection-refused': {
        name: 'Transmisión de cámara inaccesible',
        cause: 'No se pudo acceder a la cámara en vivo en el puerto RTSPS 322. El puerto está bloqueado, o la cámara o la vista en vivo por LAN están desactivadas en la impresora.',
        fix: 'Activa la cámara y la vista en vivo por LAN en la impresora y asegúrate de que el puerto 322 no esté bloqueado. Esto no afecta a la impresión.',
      },
      'database-locked': {
        name: 'Conflictos de escritura en la base de datos',
        cause: 'La base de datos SQLite presenta errores "database is locked" bajo carga, algo habitual al usar varias impresoras a la vez.',
        fix: 'Cambia Bambuddy a una base de datos PostgreSQL externa. Consulta la guía de PostgreSQL en la documentación.',
      },
    },
  },

  vpDiagnostic: {
    title: 'Comprobación de configuración — {{name}}',
    runButton: 'Ejecutar comprobación de configuración',
    running: 'Ejecutando comprobación de configuración...',
    runFailed: 'No se pudo ejecutar la comprobación de configuración: {{error}}',
    retry: 'Volver a comprobar',
    overall: {
      ok: 'Todas las comprobaciones se superaron — esta impresora virtual está configurada correctamente.',
      warnings: 'La impresora virtual debería funcionar, pero algunas cosas requieren atención.',
      problems: 'Se encontraron problemas que explican por qué el laminador no puede ver ni usar esta impresora virtual.',
    },
    check: {
      enabled: {
        title: 'Impresora virtual habilitada',
        fail: 'Esta impresora virtual está apagada. Actívela para que sea detectable.',
      },
      running: {
        title: 'Servicios en ejecución',
        fail: 'La impresora virtual está habilitada pero sus servicios no se están ejecutando. Revise el registro de Bambuddy — normalmente los detiene un conflicto de IP de enlace o un error de permisos.',
      },
      bind_interface: {
        title: 'Interfaz de red de enlace',
        fail: 'La interfaz de enlace no está configurada o ya no existe en este host. Elija una interfaz actual en el menú "Interfaz de enlace".',
      },
      access_code: {
        title: 'Código de acceso configurado',
        fail: 'No hay ningún código de acceso configurado. Al laminador se le debe dar el mismo código de acceso de 8 caracteres que configura aquí.',
      },
      target_printer: {
        title: 'Impresora de destino',
        fail: 'No hay ninguna impresora de destino seleccionada. El modo proxy necesita una impresora real a la que reenviar.',
        warn: 'La impresora de destino está desconectada ahora mismo — el reenvío se reanudará cuando vuelva a conectarse.',
      },
      port_ftps: {
        title: 'Servicio de subida de archivos (puerto {{port}})',
        fail: 'No hay nada escuchando en el puerto {{port}} de la IP de enlace, por lo que el laminador no puede subir archivos. La causa habitual es un conflicto de puerto en esta interfaz.',
      },
      port_mqtt: {
        title: 'Servicio de control (puerto {{port}})',
        fail: 'No hay nada escuchando en el puerto {{port}} de la IP de enlace, por lo que el laminador no puede conectarse ni mostrar el estado.',
      },
      port_bind: {
        title: 'Servicio de detección (puerto {{port}})',
        fail: 'No hay nada escuchando en el puerto {{port}} de la IP de enlace, por lo que falla el protocolo de detección del laminador.',
      },
      privileged_ports: {
        title: 'Vinculación a puertos privilegiados',
        fail: 'El puerto {{port}} está por debajo de 1024 y este servicio no tiene permiso para vincularlo, por eso no hay nada escuchando arriba. Añade AmbientCapabilities=CAP_NET_BIND_SERVICE a /etc/systemd/system/bambuddy.service y reinicia, o ejecuta "sudo setcap cap_net_bind_service=+ep $(readlink -f $(which python3))". En Docker, añade cap_add: [NET_BIND_SERVICE].',
      },
      certificate: {
        title: 'Certificado TLS',
        pass: 'Certificado listo. Asegúrese de que el certificado de CA de Bambuddy (arriba) esté importado en el almacén de confianza de su laminador.',
        fail: 'Falta el certificado TLS de esta impresora virtual. Compruebe que el directorio de datos de Bambuddy tenga permisos de escritura.',
      },
    },
  },

  bugReport: {
    logHealthSummary: 'Se encontraron problemas conocidos en tus registros',
    logHealthIntro: 'Los registros recientes coinciden con problemas conocidos. Revisa las soluciones de abajo: resolverlas podría arreglar tu problema sin un informe de error. Aun así, puedes enviar un informe abajo.',
    title: 'Informar de un error',
    description: 'Descripción',
    descriptionPlaceholder: '¿Qué salió mal? Describa el problema...',
    email: 'Correo (opcional)',
    emailPlaceholder: 'su@correo.com',
    emailPrivacy: 'Si lo proporciona, su correo se incluirá en una sección plegada de la incidencia de GitHub para que el mantenedor pueda hacer un seguimiento.',
    screenshot: 'Captura de pantalla',
    uploadOrPaste: 'Suba, pegue o arrastre una imagen',
    dataCollectedSummary: '¿Qué datos se incluyen en el informe?',
    dataIncluded: 'Incluidos:',
    dataIncludedList: 'Versión de la aplicación, sistema operativo, arquitectura, versión de Python, estadísticas de la base de datos (solo recuentos), modelos de impresora, número de boquillas, versiones de firmware, estado de la conectividad, estado de las integraciones (Spoolman, MQTT, HA), ajustes no sensibles, número de interfaces de red, detalles de Docker, versiones de las dependencias.',
    dataNeverIncluded: 'Nunca incluidos:',
    dataNeverIncludedList: 'Nombres de impresora, números de serie, códigos de acceso, contraseñas, direcciones IP, direcciones de correo, claves API, tokens, URL de webhook, nombres de host o nombres de usuario.',
    submit: 'Enviar',
    startLogging: 'Iniciar el registro de depuración',
    stepEnableLogging: 'Registro de depuración activado',
    stepReproduce: 'Reproduzca el problema ahora',
    stepStopLogging: 'Detener y enviar el informe',
    stopAndSubmit: 'Detener y enviar',
    maxDuration: 'Se detiene automáticamente tras {{minutes}} min',
    stoppingLogs: 'Recopilando registros y enviando...',
    submitting: 'Enviando el informe de error...',
    submittingStepConnection: 'Ejecutando comprobaciones de conectividad de impresoras',
    submittingStepVirtualPrinters: 'Ejecutando comprobaciones de configuración de impresoras virtuales',
    submittingStepLogScan: 'Analizando los registros recientes en busca de problemas conocidos',
    submittingStepSubmit: 'Enviando el informe a GitHub',
    submitSuccess: '¡Informe de error enviado correctamente!',
    submitFailed: 'Error al enviar el informe de error',
    diagnosticChecking: 'Comprobando las conexiones de las impresoras...',
    diagnosticHealthy: 'La comprobación de conexión se superó — no se encontraron problemas en sus impresoras.',
    diagnosticSummary: '{{problems}} de {{total}} impresoras tienen problemas de conexión',
    diagnosticIntro: 'Una o más impresoras tienen un problema de conexión que puede estar causando su problema. Despliegue una impresora a continuación para ver la solución — resolverla podría solucionar el problema sin un informe de error. Aún puede enviar un informe a continuación.',
    thankYou: '¡Gracias!',
    submitted: 'Su informe de error se ha enviado.',
    viewIssue: 'Ver incidencia',
    unexpectedError: 'Se produjo un error inesperado',
  },
  failureDetection: {
    title: 'Detección de fallos por IA',
    description: 'Supervise las impresiones con una API de ML de Obico autoalojada y actúe automáticamente ante los fallos detectados.',
    mlUrl: 'URL de la API de ML de Obico',
    mlUrlHint: 'URL base de su contenedor ml_api de Obico autoalojado (p. ej. http://192.168.1.10:3333).',
    mlToken: 'Token de la API de ML (opcional)',
    mlTokenPlaceholder: 'Déjelo vacío si el servidor funciona sin ML_API_TOKEN',
    mlTokenHint: 'Debe coincidir con la variable de entorno ML_API_TOKEN de su contenedor ml_api de Obico. Déjelo vacío si el contenedor funciona sin token.',
    test: 'Probar',
    testSuccess: 'API de ML accesible y correcta.',
    testSuccessTokenUnknown: 'API de ML accesible y correcta. No se pudo comprobar el token.',
    testFailed: 'No se pudo alcanzar la API de ML.',
    sensitivity: 'Sensibilidad',
    sensitivityLow: 'Baja (menos falsos positivos)',
    sensitivityMedium: 'Media (equilibrada)',
    sensitivityHigh: 'Alta (detección temprana, más falsos positivos)',
    sensitivityHint: 'Ajusta los umbrales de confianza que activan las advertencias y los fallos.',
    action: 'Acción ante un fallo detectado',
    actionNotify: 'Solo notificar',
    actionPause: 'Pausar la impresión',
    actionPauseOff: 'Pausar y cortar la corriente',
    pollInterval: 'Intervalo de sondeo (segundos)',
    pollIntervalHint: 'Con qué frecuencia comprobar cada impresora mientras imprime. Mínimo 5 s, máximo 120 s.',
    externalUrlMissing: 'La URL externa no está establecida.',
    externalUrlHint: 'La API de ML obtiene la captura de la cámara por URL. Establezca la URL externa en los ajustes generales para que el contenedor de la API de ML pueda alcanzar Bambuddy.',
    perPrinterTitle: 'Impresoras supervisadas',
    perPrinterHint: 'Elija qué impresoras vigila el servicio de detección.',
    monitorAll: 'Supervisar todas las impresoras conectadas',
    statusTitle: 'Estado',
    serviceRunning: 'Servicio en ejecución',
    thresholds: 'Umbrales bajo / alto',
    activePrinters: 'Impresiones activas',
    noActivePrints: 'No hay impresiones en curso actualmente.',
    historyTitle: 'Detecciones recientes',
    noHistory: 'Aún no hay detecciones.',
  },

  makerworld: {
    title: 'MakerWorld',
    description: 'Pegue la URL de un modelo de MakerWorld para importarlo e imprimirlo directamente desde Bambuddy — sin tener que salir a la aplicación Bambu Handy.',
    pasteUrlHeader: 'Importar desde MakerWorld',
    pasteUrlPlaceholder: 'https://makerworld.com/en/models/… o pegue cualquier enlace de MakerWorld',
    resolveButton: 'Resolver',
    signInRequiredTitle: 'Se requiere iniciar sesión en Bambu Cloud para descargar',
    signInRequiredBody: 'Puede explorar los detalles del modelo de forma anónima, pero MakerWorld requiere una cuenta de Bambu Cloud para descargar archivos 3MF.',
    signInExpiredTitle: 'La sesión de Bambu Cloud ha caducado',
    signInExpiredBody: 'Sigue conectado a Bambuddy, pero Bambu Lab ha dejado de aceptar el token almacenado, por lo que las descargas fallarán. Inicie sesión de nuevo en Bambu Cloud.',
    openCloudSettings: 'Abrir los ajustes de la nube',
    untitledModel: 'Modelo sin título',
    byCreator: 'de {{name}}',
    downloadsCount: '{{count}} descargas',
    licensePrefix: 'Licencia',
    alreadyImported: 'Ya está en la biblioteca',
    openOnMakerworld: 'Abrir en MakerWorld',
    alreadyInLibrary: 'Este modelo ya está en su biblioteca — encuéntrelo en Gestor de archivos → MakerWorld',
    importSuccess: '{{filename}} importado — guardado en Gestor de archivos → MakerWorld',
    platesHeader: 'Camas ({{count}})',
    plateDefaultName: 'Cama {{n}}',
    materialCount: '{{count}} filamentos',
    amsRequired: 'AMS necesario',
    slicedFor: 'Laminado para {{printer}}',
    alsoCompatible: 'También marcado como compatible: {{printers}}',
    importToLibrary: 'Guardar',
    sliceIn: 'Guardar y laminar en {{slicer}}',
    disclaimer: 'La integración con MakerWorld usa puntos de conexión de API documentados por la comunidad. Bambuddy no está afiliado a MakerWorld ni a Bambu Lab ni cuenta con su respaldo.',
    lastImportSuccess: 'Importado a su biblioteca',
    lastImportAlreadyInLibrary: 'Ya está en su biblioteca',
    viewInLibrary: 'Ver en el gestor de archivos',
    openInBambuStudio: 'Abrir en Bambu Studio',
    openInOrcaSlicer: 'Abrir en OrcaSlicer',
    importTo: 'Importar al gestor de archivos',
    recentImportsHeader: 'Importaciones recientes',
    phaseResolving: 'Resolviendo',
    phaseDownloading: 'Descargando',
    folderAuto: 'MakerWorld (predeterminada)',
    importAll: 'Importar todo',
    importAllProgress: 'Importando {{current}}/{{total}}',
    openGallery: 'Abrir la galería de imágenes',
    galleryPrev: 'Imagen anterior',
    galleryNext: 'Imagen siguiente',
    deleteImport: 'Quitar de la biblioteca',
    importDeleting: 'Quitando…',
    importDeleted: 'Quitado de la biblioteca',
    confirmDelete: '¿Quitar {{filename}} de la biblioteca? Esto elimina el archivo local, pero la cama se puede volver a importar desde MakerWorld.',
    errors: {
      resolveFailed: 'No se pudo resolver esa URL de MakerWorld.',
      downloadFailed: 'Error en la descarga. Inténtelo de nuevo.',
      deleteFailed: 'No se pudo quitar el archivo de la biblioteca.',
    },
  },
  gcodeViewer: {
    filamentSlot: 'Filamento {{n}}',
    loading: 'Leyendo la trayectoria…',
    loadFailed: 'No se pudo cargar el G-code de este archivo.',
    showTravel: 'Movimientos en vacío',
    topLayer: 'Capa superior',
    bottomLayer: 'Capa inferior',
    noSource: 'No se indicó ningún archivo para previsualizar.',
    view: {
      filament: 'Filamento',
      feature: 'Elemento',
      height: 'Altura',
      width: 'Ancho',
    },
    feature: {
      wall: 'Paredes',
      sparseInfill: 'Relleno disperso',
      solidInfill: 'Relleno sólido',
      bridge: 'Puente / voladizo',
      support: 'Soporte',
      skirt: 'Falda / borde',
      gapFill: 'Relleno de huecos',
      ironing: 'Alisado',
      primeTower: 'Torre de purga',
    },
    back: 'Atrás',
    backToArchives: 'Volver a los archivos de impresión',
    backToFiles: 'Volver al gestor de archivos',
    plates: 'Camas',
    plateN: 'Cama {{n}}',
  },
  libraryTrash: {
    title: 'Papelera',
    headerButton: 'Papelera',
    headerTooltip: 'Ver los archivos movidos a la papelera',
    backToFiles: 'Volver al gestor de archivos',
    subtitleAdmin: 'Los archivos eliminados permanecen aquí durante {{days}} días y luego se eliminan automáticamente. Esta vista muestra los archivos en la papelera de todos los usuarios.',
    subtitleUser: 'Los archivos eliminados permanecen aquí durante {{days}} días y luego se eliminan automáticamente.',
    loading: 'Cargando la papelera…',
    loadError: 'No se pudo cargar la papelera.',
    empty: 'La papelera está vacía.',
    summary: '{{count}} archivos · {{size}}',
    emptyTrash: 'Vaciar la papelera',
    restore: 'Restaurar',
    purgeNow: 'Eliminar ahora',
    autoPurgeIn: 'Se elimina automáticamente en {{when}}',
    days: 'días',
    retentionLabel: 'Eliminar automáticamente tras',
    selectAll: 'Seleccionar todo',
    selectOne: 'Seleccionar {{filename}}',
    selectionCount: '{{count}} seleccionados',
    bulkRestore: 'Restaurar los seleccionados',
    bulkPurge: 'Eliminar los seleccionados',
    col: {
      filename: 'Archivo',
      folder: 'Carpeta',
      size: 'Tamaño',
      deleted: 'Movido a la papelera',
      autoPurge: 'Se elimina automáticamente',
      owner: 'Propietario',
      actions: 'Acciones',
    },
    confirm: {
      purgeTitle: '¿Eliminar permanentemente?',
      purgeBody: '{{filename}} se eliminará del disco y no se podrá restaurar.',
      emptyTitle: '¿Vaciar la papelera?',
      emptyBody: 'Los {{count}} archivos se eliminarán del disco. Esto no se puede deshacer.',
      bulkPurgeTitle: '¿Eliminar permanentemente los archivos seleccionados?',
      bulkPurgeBody: 'Los {{count}} archivos seleccionados se eliminarán del disco y no se podrán restaurar.',
      cta: 'Eliminar permanentemente',
    },
    toast: {
      restored: 'Archivo restaurado.',
      restoreFailed: 'No se pudo restaurar el archivo.',
      purged: 'Archivo eliminado permanentemente.',
      purgeFailed: 'No se pudo eliminar el archivo.',
      emptied: 'Se eliminaron {{count}} archivo(s) de la papelera.',
      emptyFailed: 'No se pudo vaciar la papelera.',
      retentionSaved: 'Eliminación automática establecida en {{days}} días.',
      retentionFailed: 'No se pudo guardar el ajuste de retención.',
      bulkRestored: 'Se restauraron {{count}} archivo(s).',
      bulkPurged: 'Se eliminaron {{count}} archivo(s).',
    },
  },
  libraryPurge: {
    title: 'Purgar archivos antiguos',
    headerButton: 'Purgar antiguos',
    headerTooltip: 'Mover en bloque los archivos antiguos a la papelera',
    description: 'Limpie de un solo paso los archivos antiguos de su biblioteca. Los archivos con un historial de impresión envejecen según su fecha de última impresión; los archivos que nunca se imprimieron envejecen según su fecha de subida.',
    ageLabel: 'Mover los archivos de más de',
    days: 'días',
    includeNeverPrinted: 'Incluir los archivos que nunca se han impreso',
    effectsTitle: 'Qué ocurre cuando hace clic en Purgar',
    effect1: 'Los archivos coincidentes se mueven a la papelera — aún no se eliminan del disco.',
    effect2: 'Puede restaurarlos de la papelera en cualquier momento hasta que expire el periodo de retención.',
    effect3: 'Tras la retención, el barrido de la papelera los elimina permanentemente del disco.',
    effect4: 'Los archivos de carpetas externas (vinculadas) se omiten — Bambuddy nunca elimina bytes que no le pertenecen.',
    previewLoading: 'Comprobando cuántos archivos coinciden…',
    previewFailed: 'No se pudo previsualizar la purga.',
    previewSummary: '{{count}} archivos · {{size}} se moverían a la papelera',
    andMore: '…y {{count}} más',
    warning: 'Los archivos en la papelera siguen contando para el almacenamiento hasta que expire el periodo de retención. Vacíe la papelera después para liberar el disco de inmediato.',
    confirmCta: 'Mover {{count}} a la papelera',
    purging: 'Moviendo a la papelera…',
    toast: {
      success: 'Se movieron {{count}} archivo(s) a la papelera.',
      failed: 'No se pudieron purgar los archivos.',
    },
  },
  libraryAutoPurge: {
    enableLabel: 'Purgar automáticamente los archivos antiguos',
    enableDescription: 'Ejecuta la purga de administración una vez al día. Los archivos van primero a la papelera — no se eliminan de inmediato.',
    ageLabel: 'Purgar automáticamente los archivos de más de',
    ageDescription: 'Mínimo 7 días, máximo 10 años. Usa la misma regla de antigüedad que el botón de purga manual.',
    days: 'días',
    includeNeverPrinted: 'Incluir los archivos que nunca se han impreso',
    saveFailed: 'No se pudieron guardar los ajustes de purga automática.',
  },
  archivePurge: {
    headerButton: 'Purgar antiguos',
    headerTooltip: 'Eliminar en bloque los archivos antiguos',
    title: 'Purgar archivos antiguos',
    description: 'Limpie el historial de impresión antiguo. Cada archivo envejece según la finalización de su impresión más reciente — reimprimir un archivo actualiza su antigüedad, por lo que el trabajo activo nunca se purga.',
    ageLabel: 'Eliminar los archivos no impresos en los últimos',
    days: 'días',
    effectsTitle: 'Qué ocurre cuando hace clic en Purgar',
    effect1: 'Cada archivo coincidente se oculta de los listados y sus archivos se eliminan del disco (3MF, miniatura, time-lapse, 3MF de origen, archivo de diseño F3D, fotos).',
    effect2: 'La fila del archivo permanece en la base de datos para que las estadísticas rápidas conserven la contribución de filamento, tiempo, coste y energía — igual que el comportamiento predeterminado de la eliminación de un solo archivo.',
    effect3: 'Marque «Eliminar también de las estadísticas» abajo para descartar también la contribución a las estadísticas rápidas (coincide con la opción de eliminación de un solo archivo). Esa vía es irreversible.',
    effect4: 'Reimprimir un archivo actualiza su reloj de antigüedad, por lo que los archivos que aún usa están a salvo.',
    purgeStatsLabel: 'Eliminar también de las estadísticas',
    purgeStatsHint: 'Descarta los archivos coincidentes de las estadísticas rápidas (filamento, tiempo, coste, energía). Sin esto, las estadísticas rápidas conservan todas las contribuciones y solo los archivos abandonan el disco.',
    previewLoading: 'Comprobando cuántos archivos coinciden…',
    previewFailed: 'No se pudo previsualizar la purga.',
    previewSummary: '{{count}} archivos · {{size}} se eliminarían',
    andMore: '…y {{count}} más',
    warning: 'Los archivos se eliminan del disco y no se pueden restaurar. Descargue o marque como favorito todo lo que desee conservar antes de continuar.',
    confirmCta: 'Eliminar {{count}} archivo(s)',
    purging: 'Eliminando…',
    toast: {
      success: 'Se eliminaron {{count}} archivo(s).',
      failed: 'No se pudieron purgar los archivos.',
    },
  },
  archiveAutoPurge: {
    enableLabel: 'Purgar automáticamente los archivos antiguos',
    enableDescription: 'Una vez al día, oculta los archivos de los listados y elimina sus archivos del disco cuando no se han impreso dentro del umbral. Reimprimir un archivo reinicia el reloj.',
    ageLabel: 'Eliminar automáticamente los archivos no impresos en los últimos',
    ageDescription: 'Mínimo 7 días, máximo 10 años. Se basa en la finalización de la impresión más reciente — reimprimir un archivo actualiza su antigüedad. Elimina el 3MF, la miniatura, el time-lapse, el 3MF de origen, el F3D y las fotos.',
    days: 'días',
    purgeStatsLabel: 'Eliminar también de las estadísticas',
    purgeStatsDescription: 'Cuando está activado, el barrido diario también descarta cada archivo purgado de las estadísticas rápidas (filamento, tiempo, coste, energía). Desactivado de forma predeterminada — las estadísticas rápidas conservan la contribución, solo los archivos abandonan el disco.',
    runNow: 'Purgar los archivos ahora',
    saveFailed: 'No se pudieron guardar los ajustes de purga automática.',
  },
  cameraTokens: {
    scope: {
      camera_stream: 'Transmisión de cámara',
      camwall: 'Muro de cámaras',
      overlay: 'Superposición de streaming',
    },
    title: 'Tokens de API de la cámara',
    navTitle: 'Tokens de API de la cámara',
    description:
      'Tokens de larga duración para incrustar la transmisión de la cámara en Home Assistant, Frigate, pantallas en modo quiosco o cualquier otra herramienta que necesite una dirección estable. El alcance se elige al crearlos; un token se puede revocar en cualquier momento.',
    loading: 'Cargando…',
    confirmRevoke: {
      title: '¿Revocar este token?',
      body: 'Cualquier dispositivo que use "{{name}}" perderá el acceso de inmediato. Esto no se puede deshacer.',
      cancel: 'Cancelar',
      confirm: 'Revocar',
    },
    create: {
      scopeLabel: 'Alcance',
      hintCameraStream:
        'Un token de transmisión de cámara solo puede obtener transmisiones e instantáneas. Úsalo para Home Assistant, Frigate o cualquier cosa que incruste una sola cámara.',
      hintCamWall:
        'Un token de muro de cámaras abre /camwall en una pantalla sin iniciar sesión. Puede ver el nombre y el estado de cada impresora, y sus transmisiones de cámara. No puede ver nombres de archivo, direcciones ni códigos de acceso.',
      hintOverlay:
        'Un token de superposición de streaming abre /overlay/{printerId} en una pantalla sin iniciar sesión, para OBS o cualquier transmisión en vivo. Puede ver la transmisión de la cámara de una impresora y su estado de impresión en vivo, incluido el nombre de archivo que aparece en pantalla. No puede ver direcciones ni códigos de acceso.',
      title: 'Crear nuevo token',
      nameLabel: 'Nombre del token',
      namePlaceholder: 'p. ej. Home Assistant',
      daysLabel: 'Días hasta la caducidad',
      submit: 'Crear',
      hint:
        'La vida útil máxima es de 365 días. El valor del token se muestra solo una vez al crearlo — cópielo ahora.',
    },
    created: {
      camWallUrlTitle: 'Dirección del muro de cámaras para esta pantalla',
      camWallUrlHint:
        'Abre esta dirección en la pantalla. Cualquiera que pueda leerla puede ver el muro, así que trátala como una llave: revoca el token para dejar la pantalla sin acceso.',
      overlayUrlTitle: 'Dirección de superposición para OBS',
      overlayUrlHint:
        'Agrega esto como Fuente de navegador en OBS. Cambia el número de /overlay/1 por el número de tu impresora (de su dirección en la página Impresoras). Cualquiera que pueda leer la dirección puede ver la transmisión, así que trátala como una llave: revoca el token para cortar el acceso.',
      title: 'Token creado — cópielo ahora',
      warning:
        'Esta es la única vez que este token estará visible. Después de cerrar este diálogo no podrá volver a verlo nunca.',
      copy: 'Copiar',
      dismiss: 'Lo he guardado',
    },
    list: {
      scope: 'Alcance',
      myTitle: 'Mis tokens',
      allTitle: 'Todos los usuarios (vista de administración)',
      empty: 'Aún no hay tokens.',
      name: 'Nombre',
      owner: 'Propietario',
      prefix: 'Prefijo',
      created: 'Creado',
      expires: 'Caduca',
      lastUsed: 'Usado por última vez',
      revoke: 'Revocar',
      expired: 'Caducado',
    },
    toast: {
      created: 'Token creado',
      createFailed: 'Error al crear el token',
      revoked: 'Token revocado',
      revokeFailed: 'Error al revocar el token',
      loadFailed: 'Error al cargar los tokens',
      copied: 'Copiado al portapapeles',
      copyFailed: 'Error al copiar — selecciónelo y cópielo manualmente',
    },
  },
  // Forecast & Inventory Intelligence
  forecast: {
    title: 'Previsión',
    noSpools: 'No se encontraron bobinas activas. Añada bobinas a su inventario para ver los datos de previsión.',
    noUsageData: 'No hay datos de uso disponibles — no se puede proyectar la cronología de existencias.',
    // Table headers
    sku: 'SKU',
    material: 'Material',
    stock: 'Existencias',
    dailyRate: 'Tasa',
    daysLeft: 'Días restantes',
    emptyBy: 'Se agota el',
    reorderBy: 'Reabastecer antes del',
    actions: 'Acciones',
    // Rate tier badges
    trend: 'Tendencia',
    estimated: 'Est.',
    noData: 'Sin datos',
    // Timeframe
    timeframe: 'Periodo',
    // Chart
    chartTitle: 'Existencias previstas — Los 5 materiales principales',
    dashedLinesROP: 'Líneas discontinuas = puntos de reabastecimiento',
    stockLevel: 'Nivel de existencias',
    reorderPoint: 'Punto de reabastecimiento',
    safetyMargin: 'Margen de seguridad',
    // Legend labels
    trendLegend: 'Tendencia (basada en el historial, nivel de servicio del 95%)',
    estimatedLegend: 'Estimada (diferencia de peso)',
    noDataLegend: 'Sin datos',
    ropLabel: 'PR',
    ssLabel: 'ES',
    safetyStockLegend: 'Existencias de seguridad',
    stockArrivalLegend: 'Llegada de existencias',
    stockoutLegend: 'Rotura de existencias',
    // Alerts toolbar
    alertCount_one: '{{count}} alerta',
    alertCount_other: '{{count}} alertas',
    order: 'Pedir',
    // Settings
    save: 'Guardar',
    cancel: 'Cancelar',
    settingsSaved: 'Ajustes guardados',
    failedSaveSettings: 'Error al guardar los ajustes',
    globalLeadTimeSaved: 'Plazo de entrega global guardado',
    globalLeadTime: 'Plazo de entrega global',
    globalLeadTimeHint: 'Mínimo del plazo de entrega global — se usa en el cálculo del punto de reabastecimiento para todos los SKU',
    skuLeadTimeOverride: 'Anulación del plazo de entrega del SKU',
    skuLeadTimeHint: '0 = usar el plazo de entrega global. Establezca >0 para anularlo para este SKU.',
    safetyMarginLabel: 'Margen de seguridad',
    effectiveLeadTime: 'Plazo de entrega efectivo',
    effectiveLeadTimeHint: 'máx(global {{global}} d, SKU {{sku}} d)',
    reorderPointHint: 'd̄ × PE + margen de seguridad — pida cuando las existencias alcancen este nivel',
    safetyMarginHint: 'Existencias de seguridad estadísticas (z=1,65 × σ × √PE) + margen definido por el usuario',
    safetyMarginHintDays: 'Margen añadido sobre las existencias de seguridad estadísticas.{{approx}}',
    safetyMarginHintDaysApprox: ' ≈ {{g}} g a la tasa actual.',
    safetyMarginHintG: 'Margen de peso fijo añadido sobre las existencias de seguridad estadísticas.{{approx}}',
    safetyMarginHintGApprox: ' ≈ {{days}} d a la tasa actual.',
    individualSpools: 'Bobinas individuales',
    labelWeight: 'Etiqueta',
    spoolCount_one: '{{count}} bobina',
    spoolCount_other: '{{count}} bobinas',
    // Alerts
    stockBreakRisk: 'Riesgo de rotura de existencias',
    stockBreakDetail: 'Quedan {{days}} d, plazo de entrega {{lt}} d.',
    stockBreakBefore: 'Rotura de existencias antes del reabastecimiento',
    reorderNow: 'Reabastecer ahora',
    reorderTriggerPassed: 'La fecha de activación {{date}} ha pasado.',
    // Shopping list
    shoppingList: 'Lista de la compra',
    shoppingListItems_one: '({{count}} artículo)',
    shoppingListItems_other: '({{count}} artículos)',
    shoppingListEmpty: 'La lista de la compra está vacía. Haga clic en el icono del carrito de cualquier fila para añadir artículos.',
    addToCart: 'Añadir a la lista de la compra',
    alertsSnoozed: 'Silenciar las alertas de este SKU',
    alertsEnabled: 'Reactivar las alertas de este SKU',
    addedToCart: 'Añadido a la lista de la compra',
    failedAddItem: 'Error al añadir el artículo',
    listView: 'Lista',
    logisticsView: 'Logística',
    qty: 'Cant.',
    weight: 'Peso',
    leadTime: 'Plazo de entrega',
    expectedRestock: 'Reabastecimiento previsto',
    status: 'Estado',
    note: 'Nota',
    pending: 'Pendiente',
    purchased: 'Comprado',
    received: 'Recibido',
    markPurchased: 'Marcar como comprado',
    markReceived: 'Marcar como recibido — añade bobinas al inventario de existencias',
    resetToPending: 'Restablecer a pendiente',
    remove: 'Quitar',
    clearAll: 'Borrar todo',
    downloadCsv: 'CSV',
    // Add to cart modal
    addToCartTitle: 'Añadir a la lista de la compra',
    byQuantity: 'Por cantidad',
    byDuration: 'Por duración',
    numberOfSpools: 'Número de bobinas',
    lastHowManyDays: '¿Cuántos días debería durar?',
    noUsageQty: 'No hay datos de uso — cantidad establecida en 1.',
    noteOptional: 'Nota (opcional)',
    notePlaceholder: 'p. ej. para el proyecto X, urgente…',
    addNSpools_one: 'Añadir {{count}} bobina',
    addNSpools_other: 'Añadir {{count}} bobinas',
    // Cart logistics
    onArrival: 'A la llegada',
    stockBreakIn: 'Rotura de existencias en {{days}} d.',
    stockRunsOutBefore: 'Las existencias se agotan antes de que transcurra el plazo de entrega de {{lt}} d.',
    atRate: 'A {{rate}} g/día necesita',
    moreSpools_one: '{{count}} bobina más',
    moreSpools_other: '{{count}} bobinas más',
    bridgeGap: 'para cubrir el déficit.',
    // Permissions
    noReadAccess: 'No tiene permiso para ver las previsiones de inventario.',
    noWriteAccess: 'No tiene permiso para modificar los ajustes de previsión.',
  },
};
