diff --git a/apps/desktop/src/i18n/catalog.ts b/apps/desktop/src/i18n/catalog.ts index f35c957076db..371d652992e6 100644 --- a/apps/desktop/src/i18n/catalog.ts +++ b/apps/desktop/src/i18n/catalog.ts @@ -1,5 +1,6 @@ import { ar } from './ar' import { en } from './en' +import { es } from './es' import { ja } from './ja' import type { Locale, Translations } from './types' import { zh } from './zh' @@ -10,5 +11,6 @@ export const TRANSLATIONS: Record = { zh, 'zh-hant': zhHant, ja, - ar + ar, + es } diff --git a/apps/desktop/src/i18n/es.ts b/apps/desktop/src/i18n/es.ts new file mode 100644 index 000000000000..924199e8b61f --- /dev/null +++ b/apps/desktop/src/i18n/es.ts @@ -0,0 +1,3153 @@ +import { defineLocale } from './define-locale' + +export const es = defineLocale({ + common: { + apply: 'Aplicar', + back: 'Volver', + save: 'Guardar', + saving: 'Guardando…', + cancel: 'Cancelar', + change: 'Cambiar', + choose: 'Elegir', + clear: 'Limpiar', + close: 'Cerrar', + collapse: 'Contraer', + confirm: 'Confirmar', + connect: 'Conectar', + connecting: 'Conectando', + continue: 'Continuar', + copied: 'Copiado', + copy: 'Copiar', + copyFailed: 'Error al copiar', + delete: 'Eliminar', + docs: 'Documentación', + done: 'Hecho', + error: 'Error', + expand: 'Expandir', + failed: 'Fallido', + formatJson: 'Formatear JSON', + free: 'Gratis', + loading: 'Cargando…', + notSet: 'No configurado', + refresh: 'Actualizar', + remove: 'Quitar', + replace: 'Reemplazar', + retry: 'Reintentar', + run: 'Ejecutar', + send: 'Enviar', + set: 'Establecer', + skip: 'Omitir', + update: 'Actualizar', + tryHint: term => `Prueba “${term}”`, + on: 'Activado', + off: 'Desactivado' + }, + + fileMenu: { + revealFinder: 'Mostrar en Finder', + revealExplorer: 'Mostrar en el Explorador de archivos', + revealFileManager: 'Abrir carpeta contenedora', + revealInSidebar: 'Mostrar en el árbol de archivos', + copyPath: 'Copiar ruta', + copyRelativePath: 'Copiar ruta relativa', + rename: 'Renombrar…', + delete: 'Eliminar', + renameTitle: 'Renombrar', + renameLabel: 'Nuevo nombre', + deleteTitle: name => `¿Eliminar ${name}?`, + deleteBody: 'Se moverá a la Papelera — puedes restaurarlo desde allí.', + pathCopied: 'Ruta copiada' + }, + + boot: { + ready: 'Hermes Desktop está listo', + desktopBootFailedWithMessage: message => `Fallo al iniciar el escritorio: ${message}`, + steps: { + connectingGateway: 'Conectando con la puerta de enlace del escritorio', + loadingSettings: 'Cargando ajustes de Hermes', + loadingSessions: 'Cargando sesiones recientes', + startingDesktopConnection: 'Iniciando conexión del escritorio', + startingHermesDesktop: 'Iniciando Hermes Desktop…' + }, + errors: { + backgroundExited: 'El proceso en segundo plano de Hermes ha salido.', + backgroundExitedDuringStartup: 'El proceso en segundo plano de Hermes ha salido durante el inicio.', + backendStopped: 'Backend detenido', + desktopBootFailed: 'Fallo al iniciar el escritorio', + gatewayConnectionLost: 'Conexión con la puerta de enlace perdida', + gatewaySignInRequired: 'Se requiere inicio de sesión en la puerta de enlace', + ipcBridgeUnavailable: 'El puente IPC del escritorio no está disponible.' + }, + failure: { + title: 'No se pudo iniciar Hermes', + description: 'La puerta de enlace en segundo plano no arrancó. Prueba uno de los pasos de recuperación a continuación. Nada de esto borra tus chats ni ajustes.', + remoteTitle: 'Se requiere inicio de sesión en la puerta de enlace remota', + remoteDescription: 'Tu sesión de puerta de enlace remota ha expirado. Inicia sesión de nuevo para reconectar. Nada de esto borra tus chats ni ajustes.', + retry: 'Reintentar', + repairInstall: 'Reparar instalación', + useLocalGateway: 'Usar puerta de enlace local', + gatewaySettings: 'Ajustes de la puerta de enlace', + back: 'Volver', + openLogs: 'Abrir registros', + repairHint: 'La reparación vuelve a ejecutar el instalador y puede tardar unos minutos en una máquina nueva.', + remoteSignInHint: signInLabel => `Cierra la sesión del navegador remoto guardado y abre ${signInLabel}. Usa la puerta de enlace local para cambiar al backend incluido.`, + signOutAndSignIn: 'Cerrar sesión e iniciar sesión', + remoteFailureHint: 'Comprueba la URL de la puerta de enlace y el inicio de sesión en Ajustes de la puerta de enlace, o cambia a la puerta de enlace local.', + hideRecentLogs: 'Ocultar registros recientes', + showRecentLogs: 'Mostrar registros recientes', + signedInTitle: 'Sesión iniciada', + signedInMessage: 'Reconectando con la puerta de enlace remota…', + signInIncompleteTitle: 'Inicio de sesión incompleto', + signInIncompleteMessage: 'La ventana de inicio de sesión se cerró antes de completar la autenticación.', + signInFailed: 'Falló el inicio de sesión', + signInToRemoteGateway: 'Iniciar sesión en la puerta de enlace remota', + signInWithProvider: provider => `Iniciar sesión con ${provider}`, + identityProvider: 'tu proveedor de identidad' + } + }, + + language: { + label: 'Idioma', + description: 'Elige el idioma para la interfaz del escritorio.', + saving: 'Guardando idioma…', + saveError: 'Fallo al actualizar el idioma', + switchTo: 'Cambiar idioma', + searchPlaceholder: 'Buscar idiomas…', + noResults: 'No se encontraron idiomas' + }, + + titlebar: { + hideSidebar: 'Ocultar barra lateral', + showSidebar: 'Mostrar barra lateral', + search: 'Buscar', + searchTitle: 'Buscar sesiones, vistas y acciones', + swapSidebarSides: 'Intercambiar barras laterales', + hideRightSidebar: 'Ocultar barra lateral derecha', + showRightSidebar: 'Mostrar barra lateral derecha', + muteHaptics: 'Silenciar hápticos', + unmuteHaptics: 'Activar hápticos', + openSettings: 'Abrir ajustes', + openStarmap: 'Abrir mapa de memoria', + enterHud: 'Modo HUD', + exitHud: 'Salir del modo HUD', + layoutEditor: 'Editor de disposición', + layoutEditorTitle: 'Editor de disposición — -clic restablece la disposición' + }, + + findInPage: { + next: 'Siguiente coincidencia', + previous: 'Coincidencia anterior' + }, + + remoteDisplayBanner: { + message: reason => `Renderizado por software activo — detectada pantalla remota (${reason}). Aceleración GPU desactivada para evitar parpadeos.` + }, + + billingBlock: { + titleNous: 'Sin créditos de Nous', + titleProvider: provider => `Sin créditos — ${provider}`, + fallbackMessage: 'Tu cuenta no tiene créditos. Añade créditos para continuar.', + openBilling: 'Abrir facturación', + addCredits: 'Añadir créditos', + dismiss: 'Descartar' + }, + + notifications: { + region: 'Notificaciones', + hide: 'Ocultar', + show: 'Mostrar', + more: count => `${count} notificaciones más`, + clearAll: 'Limpiar todo', + dismiss: 'Descartar notificación', + details: 'Detalles', + copyDetail: 'Copiar detalle', + copyDetailFailed: 'No se pudo copiar el detalle de la notificación', + backendOutOfDateTitle: 'Backend desactualizado', + backendOutOfDateMessage: + 'Tu backend de Hermes es más antiguo que esta versión de escritorio y puede no funcionar correctamente. Actualiza para alinearlos.', + installMethodUnsupportedTitle: 'Método de instalación no soportado', + updateHermes: 'Actualizar Hermes', + updateReadyTitle: 'Actualización lista', + updateReadyMessage: count => + `${count} cambio${count === 1 ? '' : 's'} nuevo${count === 1 ? '' : 's'} disponible${count === 1 ? '' : 's'}.`, + seeWhatsNew: 'Ver novedades', + errors: { + elevenLabsNeedsKey: 'ElevenLabs STT necesita ELEVENLABS_API_KEY.', + elevenLabsRejectedKey: 'ElevenLabs rechazó la clave API (401).', + diskFull: 'Disco lleno — libera espacio y vuelve a intentarlo.', + gatewayAuthFailed: 'Falló la autenticación de la puerta de enlace — comprueba tu API_SERVER_KEY.', + methodNotAllowed: + 'El backend de escritorio rechazó esa petición (405 Method Not Allowed). Intenta reiniciar Hermes Desktop.', + microphonePermission: 'Se denegó el permiso del micrófono.', + openaiRejectedApiKey: 'OpenAI rechazó la clave API.', + openaiRejectedApiKeyWithStatus: status => `OpenAI rechazó la clave API (${status} invalid_api_key).`, + openaiTtsNeedsKey: 'OpenAI TTS necesita VOICE_TOOLS_OPENAI_KEY u OPENAI_API_KEY.' + }, + voice: { + configureSpeechToText: 'Configura speech-to-text para usar el modo voz.', + couldNotStartSession: 'No se pudo iniciar la sesión de voz', + microphoneAccessDenied: 'Acceso al micrófono denegado.', + microphoneConstraintsUnsupported: 'Las restricciones del micrófono no son compatibles con este dispositivo.', + microphoneFailed: 'Fallo del micrófono', + microphoneInUse: 'El micrófono ya está en uso por otra app.', + microphonePermissionDenied: 'Se denegó el permiso del micrófono.', + microphoneStartFailed: 'No se pudo iniciar la grabación del micrófono.', + microphoneUnsupported: 'Este entorno no soporta grabación de micrófono.', + noMicrophone: 'No se encontró ningún micrófono.', + noSpeechDetected: 'No se detectó voz', + playbackFailed: 'Falló la reproducción de voz', + recordingFailed: 'Falló la grabación de voz', + sayStopToEnd: phrase => `Di "${phrase}" para terminar el chat de voz.`, + transcriptionFailed: 'Falló la transcripción de voz', + transcriptionUnavailable: 'La transcripción de voz no está disponible aún.', + tryRecordingAgain: 'Intenta grabar de nuevo.', + unavailable: 'Voz no disponible' + }, + native: { + approvalTitle: 'Se necesita aprobación', + approveAction: 'Aprobar', + rejectAction: 'Rechazar', + inputTitle: 'Se necesita entrada', + inputBody: 'Hermes está esperando tu respuesta.', + turnDoneTitle: 'Hermes terminó', + turnDoneBody: '', + turnErrorTitle: 'Turno fallido', + backgroundDoneTitle: 'Tarea en segundo plano terminada', + backgroundFailedTitle: 'Tarea en segundo plano fallida', + creditsTitle: 'Créditos' + } + }, + + keybinds: { + title: 'Atajos de teclado', + subtitle: open => `Haz clic en un atajo para cambiarlo · ${open} reabre este panel.`, + search: 'Buscar atajos…', + rebind: 'Cambiar', + reset: 'Restablecer a valores predeterminados', + resetAll: 'Restablecer todo', + pressKey: 'Pulsa una tecla…', + set: 'establecer', + conflictWith: label => `También asignado a “${label}”`, + categories: { + composer: 'Compositor', + profiles: 'Perfiles', + session: 'Sesión', + navigation: 'Navegación', + view: 'Vista' + }, + actions: { + 'keybinds.openPanel': 'Abrir atajos de teclado', + 'nav.commandPalette': 'Abrir paleta de comandos', + 'nav.commandCenter': 'Abrir centro de comandos', + 'nav.settings': 'Abrir ajustes', + 'nav.profiles': 'Abrir perfiles', + 'nav.skills': 'Abrir habilidades', + 'nav.messaging': 'Abrir mensajería', + 'nav.artifacts': 'Abrir artefactos', + 'nav.cron': 'Abrir tareas programadas', + 'nav.agents': 'Abrir agentes', + 'session.new': 'Nueva sesión', + 'session.newTab': 'Nueva pestaña de sesión', + 'session.newWindow': 'Nueva ventana', + 'session.next': 'Siguiente sesión', + 'session.prev': 'Sesión anterior', + 'session.slot.1': 'Cambiar a sesión reciente 1', + 'session.slot.2': 'Cambiar a sesión reciente 2', + 'session.slot.3': 'Cambiar a sesión reciente 3', + 'session.slot.4': 'Cambiar a sesión reciente 4', + 'session.slot.5': 'Cambiar a sesión reciente 5', + 'session.slot.6': 'Cambiar a sesión reciente 6', + 'session.slot.7': 'Cambiar a sesión reciente 7', + 'session.slot.8': 'Cambiar a sesión reciente 8', + 'session.slot.9': 'Cambiar a sesión reciente 9', + 'session.focusSearch': 'Buscar sesiones', + 'session.togglePin': 'Fijar / desfijar sesión actual', + 'workspace.newWorktree': 'Nuevo worktree', + 'workspace.openFolder': 'Abrir carpeta como proyecto', + 'composer.focus': 'Centrar en el compositor', + 'composer.modelPicker': 'Abrir selector de modelo', + 'composer.voice': 'Iniciar / detener conversación por voz', + 'view.toggleSidebar': 'Alternar barra lateral de sesiones', + 'view.toggleRightSidebar': 'Alternar explorador de archivos', + 'view.toggleReview': 'Alternar panel de revisión', + 'view.toggleStatusbar': 'Alternar barra de estado', + 'view.showFiles': 'Mostrar explorador de archivos', + 'view.toggleHud': 'Alternar modo HUD', + 'hud.snapToPointer': 'Mover HUD al puntero (global, mientras el HUD esté abierto)', + 'view.showTerminal': 'Alternar terminal', + 'view.newTerminal': 'Nueva terminal', + 'view.nextTerminal': 'Siguiente terminal', + 'view.prevTerminal': 'Terminal anterior', + 'view.closeTerminal': 'Cerrar terminal', + 'view.terminalSelection': 'Enviar selección de terminal al compositor', + 'view.terminalCopy': 'Copiar selección de terminal', + 'view.terminalPaste': 'Pegar en terminal', + 'view.closeTab': 'Cerrar pestaña', + 'view.reopenTab': 'Reabrir pestaña cerrada', + 'view.flipPanes': 'Intercambiar barras laterales', + 'view.findInPage': 'Buscar en la página', + 'view.findNext': 'Siguiente coincidencia', + 'view.findPrevious': 'Coincidencia anterior', + 'appearance.toggleMode': 'Alternar claro / oscuro', + 'profile.default': 'Cambiar al perfil predeterminado', + 'profile.switch.1': 'Cambiar al perfil 1', + 'profile.switch.2': 'Cambiar al perfil 2', + 'profile.switch.3': 'Cambiar al perfil 3', + 'profile.switch.4': 'Cambiar al perfil 4', + 'profile.switch.5': 'Cambiar al perfil 5', + 'profile.switch.6': 'Cambiar al perfil 6', + 'profile.switch.7': 'Cambiar al perfil 7', + 'profile.switch.8': 'Cambiar al perfil 8', + 'profile.switch.9': 'Cambiar al perfil 9', + 'profile.switch.10': 'Cambiar al perfil 10', + 'profile.switch.11': 'Cambiar al perfil 11', + 'profile.switch.12': 'Cambiar al perfil 12', + 'profile.switch.13': 'Cambiar al perfil 13', + 'profile.switch.14': 'Cambiar al perfil 14', + 'profile.switch.15': 'Cambiar al perfil 15', + 'profile.switch.16': 'Cambiar al perfil 16', + 'profile.switch.17': 'Cambiar al perfil 17', + 'profile.switch.18': 'Cambiar al perfil 18', + 'profile.next': 'Siguiente perfil', + 'profile.prev': 'Perfil anterior', + 'profile.toggleAll': 'Alternar vista de todos los perfiles', + 'profile.create': 'Crear perfil', + 'composer.send': 'Enviar mensaje', + 'composer.newline': 'Insertar salto de línea', + 'composer.steer': 'Redirigir el turno en curso', + 'composer.queue': 'Poner mensaje en cola', + 'composer.sendQueued': 'Enviar siguiente turno en cola', + 'composer.mention': 'Referenciar archivos, carpetas, URLs', + 'composer.slash': 'Paleta de comandos con barra', + 'composer.help': 'Ayuda rápida', + 'composer.history': 'Navegar ventana emergente / historial', + 'composer.cancel': 'Cerrar ventana emergente · cancelar ejecución' + } + }, + settings: { + closeSettings: 'Cerrar ajustes', + exportConfig: 'Exportar configuración', + importConfig: 'Importar configuración', + resetToDefaults: 'Restablecer a valores predeterminados', + resetConfirm: '¿Restablecer todos los ajustes a los valores predeterminados de Hermes?', + exportFailed: 'Fallo al exportar', + resetFailed: 'Fallo al restablecer', + nav: { + providers: 'Proveedores', + providerAccounts: 'Cuentas', + providerApiKeys: 'Claves API', + providerCustomEndpoints: 'Endpoints personalizados', + gateway: 'Puerta de enlace', + apiKeys: 'Herramientas y claves', + keybinds: 'Atajos de teclado', + keysTools: 'Herramientas', + keysSettings: 'Ajustes', + mcp: 'MCP', + archivedChats: 'Chats archivados', + about: 'Acerca de', + billing: 'Facturación', + notifications: 'Notificaciones', + plugins: 'Complementos' + }, + sections: { + model: 'Modelo', + chat: 'Chat', + appearance: 'Apariencia', + workspace: 'Espacio de trabajo', + safety: 'Seguridad', + memory: 'Memoria y contexto', + voice: 'Voz', + advanced: 'Avanzado' + }, + searchPlaceholder: { + about: 'Acerca de Hermes Desktop', + config: 'Buscar ajustes…', + gateway: 'Conexión de puerta de enlace…', + keys: 'Buscar claves API…', + mcp: 'Buscar servidores MCP…', + sessions: 'Buscar sesiones archivadas…' + }, + modeOptions: { + light: { label: 'Claro', description: 'Superficies de escritorio brillantes' }, + dark: { label: 'Oscuro', description: 'Espacio de trabajo de bajo deslumbramiento' }, + system: { label: 'Sistema', description: 'Seguir la apariencia del sistema operativo' } + }, + plugins: { + title: 'Complementos de escritorio', + blurb: + 'Incluidos o colocados en la carpeta de complementos del escritorio. Desactívalos para descargarlos en vivo.', + count: n => `${n} instalado${n === 1 ? '' : 's'}`, + openFolder: 'Abrir carpeta de complementos', + rescan: 'Volver a escanear', + reveal: 'Mostrar en el administrador de archivos', + enable: 'Activar', + disable: 'Desactivar', + failed: 'fallido', + empty: 'Aún no hay complementos de escritorio instalados.', + kinds: { bundled: 'incluido', disk: 'en disco', runtime: 'en tiempo de ejecución' }, + agent: { + title: 'Complementos de agente', + blurb: + 'Se ejecutan en el backend de Hermes — herramientas, habilidades, servidores MCP, hooks y comandos de barra. Los portables son paquetes de Complementos de agente (habilidades + paquetes MCP que también funcionan en otros agentes). Los interruptores se aplican a sesiones nuevas.', + empty: 'Aún no hay complementos de agente instalados.', + loadFailed: 'No se pudieron cargar los complementos de agente', + portable: 'portable', + search: 'Buscar complementos…', + noMatches: 'Ningún complemento coincide con tu búsqueda.', + toggleFailed: name => `No se pudo alternar ${name}`, + updateBackendToManage: 'Actualiza el backend de Hermes para gestionar este complemento desde el escritorio.', + sources: { bundled: 'incluido', user: 'usuario', git: 'git', project: 'proyecto', entrypoint: 'pip' } + } + }, + notifications: { + title: 'Notificaciones', + intro: 'Notificaciones del sistema operativo (no avisos dentro de la app). Por dispositivo.', + enableAll: 'Activar notificaciones', + enableAllDesc: 'Desactivado silencia todas las notificaciones siguientes.', + focusedHint: 'Las alertas de finalización solo se disparan mientras Hermes está en segundo plano.', + kinds: { + approval: { + label: 'Se necesita aprobación', + description: 'Un comando espera que lo apruebes o rechaces.' + }, + input: { + label: 'Se necesita entrada', + description: 'Hermes hizo una pregunta o necesita una contraseña o secreto.' + }, + turnDone: { + label: 'Respuesta lista', + description: 'Un turno terminó mientras Hermes estaba en segundo plano.' + }, + turnError: { + label: 'Turno fallido', + description: 'Errores de turnos en segundo plano.' + }, + backgroundDone: { + label: 'Tarea en segundo plano terminada', + description: 'Un comando de terminal en segundo plano se completó.' + }, + credits: { + label: 'Alertas de crédito', + description: 'El acceso a créditos está pausado o restablecido.' + }, + plugin: { + label: 'Notificaciones de complementos', + description: 'Un complemento de escritorio envió una notificación mientras Hermes estaba en segundo plano.' + } + }, + test: 'Enviar notificación de prueba', + testTitle: 'Hermes', + testBody: 'Las notificaciones funcionan.', + testSent: + 'Prueba enviada. Si no aparece nada, comprueba los permisos de notificación de tu sistema operativo y el modo No molestar.', + testUnsupported: 'Este sistema no soporta notificaciones nativas.', + completionSoundTitle: 'Sonido de finalización', + completionSoundDesc: 'Se reproduce cuando termina un turno del agente. Elige un ajuste preestablecido y pruébalo aquí.', + completionSoundPreview: 'Vista previa' + }, + appearance: { + title: 'Apariencia', + intro: 'Solo escritorio. El modo es el brillo; el tema es la paleta y el marco del chat.', + colorMode: 'Modo de color', + colorModeDesc: 'Elige un modo fijo o deja que Hermes siga tu configuración del sistema.', + toolViewTitle: 'Visualización de llamadas a herramientas', + toolViewDesc: 'Producto oculta los datos brutos de las herramientas; Técnico muestra entrada/salida completas.', + uiScaleTitle: 'Escala de interfaz', + uiScaleDesc: percent => + `Escala el texto y los controles de toda la app. Cmd/Ctrl con +, - y 0 también funciona. Actual: ${percent}%.`, + terminalFontTitle: 'Fuente de terminal', + terminalFontDesc: + 'Elige una fuente instalada para las terminales del escritorio. Las Nerd Fonts renderizan Powerlevel10k y los iconos de shell; déjalo vacío para usar la JetBrains Mono incluida.', + terminalFontPlaceholder: 'MesloLGS NF o una pila de fuentes CSS', + terminalFontPreview: 'Vista previa de glifos', + terminalFontReset: 'Usar la predeterminada', + translucencyTitle: 'Translucidez de ventana', + translucencyDesc: 'Ver tu escritorio a través de toda la ventana. Solo macOS y Windows.', + backdropTitle: 'Fondo del chat', + backdropDesc: 'La tenue imagen de estatua detrás de la conversación.', + reactionsTitle: 'Reacciones a mensajes', + reactionsDesc: 'Reacciones emoji estilo iMessage — reacciona a los mensajes, y Hermes puede reaccionar a los tuyos.', + embedsTitle: 'Vistas previas integradas', + embedsDesc: + 'Las vistas previas enriquecidas se cargan de sitios de terceros (YouTube, X, …). Preguntar muestra un marcador hasta que lo permitas; Siempre las carga automáticamente; Desactivado mantiene enlaces simples.', + embedsAsk: 'Preguntar', + embedsAlways: 'Siempre', + embedsOff: 'Desactivado', + embedsReset: count => `Restablecer ${count} servicio${count === 1 ? '' : 's'} permitido${count === 1 ? '' : 's'}`, + product: 'Producto', + productDesc: 'Actividad de herramientas amigable con resúmenes concisos.', + technical: 'Técnico', + technicalDesc: 'Incluye argumentos/resultados brutos de herramientas y detalles de bajo nivel.', + themeTitle: 'Tema', + themeDesc: 'Solo paletas de escritorio. El modo seleccionado se aplica encima.', + themeProfileNote: profile => `Guardado para el perfil ${profile} — cada perfil mantiene su propio tema.`, + installTitle: 'Instalar desde VS Code', + installDesc: + 'Pega un id de extensión del Marketplace (ej. dracula-theme.theme-dracula) para convertir su tema de color en una paleta de escritorio.', + installPlaceholder: 'editor.extensión', + installButton: 'Instalar', + installing: 'Instalando…', + installError: 'No se pudo instalar ese tema.', + installed: name => `Instalado “${name}”.`, + removeTheme: 'Quitar tema', + importedBadge: 'Importado', + pet: { + title: 'Mascota', + intro: + 'Adopta una mascota animada petdex que flota sobre la app y reacciona a lo que hace Hermes — corre mientras las herramientas se ejecutan, celebra al tener éxito y se enfada con los errores.', + restartHint: + 'Las mascotas necesitan un reinicio rápido — la app en ejecución se inició antes de añadir esta función. Cierra y vuelve a abrir Hermes, y vuelve aquí.', + on: 'Activada', + off: 'Desactivada', + scaleTitle: 'Tamaño', + scaleDesc: 'Redimensiona la mascota flotante. Se aplica en todas partes al instante.', + roamTitle: 'Deambular', + roamDesc: 'Deja que la mascota deambule por la ventana sola cuando está inactiva.', + chooseTitle: 'Elegir mascota', + chooseDesc: 'Elegir una la instala (si es necesario) y la hace activa.', + searchPlaceholder: 'Buscar mascotas…', + unreachable: 'No se pudo acceder a la galería de petdex. Comprueba tu conexión y vuelve a abrir esta página.', + noMatch: query => `Ninguna mascota coincide con "${query}".`, + installedTag: 'instalada', + generatedTag: 'Generada', + countCapped: (cap, total) => `Mostrando ${cap} de ${total} — escribe para filtrar.`, + count: n => `${n} mascota${n === 1 ? '' : 's'}.`, + uninstall: name => `Desinstalar ${name}`, + delete: name => `Eliminar ${name}`, + deleteTitle: name => `¿Eliminar ${name}?`, + deleteBody: 'Esto elimina la mascota permanentemente — no se puede reinstalar.', + deleteConfirm: 'Eliminar', + rename: name => `Renombrar ${name}`, + renameTitle: 'Renombrar mascota', + renamePlaceholder: 'Pon nombre a tu mascota', + renameSave: 'Guardar', + exportPet: name => `Exportar ${name}`, + adoptFailed: slug => `No se pudo adoptar ${slug}`, + uninstallFailed: slug => `No se pudo desinstalar ${slug}`, + renameFailed: slug => `No se pudo renombrar ${slug}`, + exportFailed: slug => `No se pudo exportar ${slug}`, + noneAvailable: 'No hay mascotas disponibles para activar ahora mismo.', + turnOnFailed: 'No se pudo activar la mascota.', + turnOffFailed: 'No se pudo desactivar la mascota.' + } + }, + fieldLabels: { + model: 'Modelo predeterminado', + modelContextLength: 'Ventana de contexto', + fallbackProviders: 'Modelos de respaldo', + toolsets: 'Conjuntos de herramientas activados', + timezone: 'Zona horaria', + 'display.personality': 'Personalidad', + 'display.showReasoning': 'Bloques de razonamiento', + 'desktop.repoScanEnabled': 'Detección automática de repositorios', + 'desktop.repoScanRoots': 'Raíces de detección de repositorios', + 'desktop.repoScanExcludePaths': 'Rutas de repositorio excluidas', + 'agent.maxTurns': 'Máximo de pasos del agente', + 'agent.imageInputMode': 'Adjuntos de imagen', + 'agent.apiMaxRetries': 'Reintentos de API', + 'agent.serviceTier': 'Nivel de servicio', + 'agent.toolUseEnforcement': 'Cumplimiento del uso de herramientas', + 'terminal.cwd': 'Directorio de trabajo', + 'terminal.backend': 'Backend de ejecución', + 'terminal.timeout': 'Tiempo de espera de comandos', + 'terminal.persistentShell': 'Shell persistente', + 'terminal.envPassthrough': 'Paso de variables de entorno', + 'terminal.dockerImage': 'Imagen de Docker', + 'terminal.singularityImage': 'Imagen de Singularity', + 'terminal.modalImage': 'Imagen de Modal', + 'terminal.daytonaImage': 'Imagen de Daytona', + fileReadMaxChars: 'Límite de lectura de archivos', + 'toolOutput.maxBytes': 'Límite de salida de terminal', + 'toolOutput.maxLines': 'Límite de páginas de archivos', + 'toolOutput.maxLineLength': 'Límite de longitud de línea', + 'codeExecution.mode': 'Modo de ejecución de código', + 'approvals.mode': 'Modo de aprobación', + 'approvals.timeout': 'Tiempo de espera de aprobación', + 'approvals.mcpReloadConfirm': 'Confirmar recargas de MCP', + commandAllowlist: 'Lista de comandos permitidos', + 'security.redactSecrets': 'Ocultar secretos', + 'security.allowPrivateUrls': 'Permitir URLs privadas', + 'browser.allowPrivateUrls': 'URLs privadas del navegador', + 'browser.autoLocalForPrivateUrls': 'Navegador local para URLs privadas', + 'checkpoints.enabled': 'Puntos de control de archivos', + 'checkpoints.maxSnapshots': 'Límite de puntos de control', + 'voice.recordKey': 'Atajo de voz', + 'voice.maxRecordingSeconds': 'Duración máxima de grabación', + 'voice.autoTts': 'Leer respuestas en voz alta', + 'stt.enabled': 'Voz a texto', + 'stt.echoTranscripts': 'Reenviar transcripciones', + 'stt.provider': 'Proveedor de voz a texto', + 'stt.local.model': 'Modelo de transcripción local', + 'stt.local.language': 'Idioma de transcripción', + 'stt.openai.model': 'Modelo STT de OpenAI', + 'stt.groq.model': 'Modelo STT de Groq', + 'stt.mistral.model': 'Modelo STT de Mistral', + 'stt.elevenlabs.modelId': 'Modelo STT de ElevenLabs', + 'stt.elevenlabs.languageCode': 'Idioma de ElevenLabs', + 'stt.elevenlabs.tagAudioEvents': 'Etiquetar eventos de audio', + 'stt.elevenlabs.diarize': 'Diarización de hablantes', + 'tts.provider': 'Proveedor de texto a voz', + 'tts.edge.voice': 'Voz de Edge', + 'tts.openai.model': 'Modelo TTS de OpenAI', + 'tts.openai.voice': 'Voz de OpenAI', + 'tts.elevenlabs.voiceId': 'Voz de ElevenLabs', + 'tts.elevenlabs.modelId': 'Modelo de ElevenLabs', + 'tts.xai.voiceId': 'Voz de xAI (Grok)', + 'tts.xai.language': 'Idioma de xAI', + 'tts.xai.speed': 'Velocidad de reproducción de xAI', + 'tts.xai.autoSpeechTags': 'Etiquetas de voz automáticas de xAI', + 'tts.xai.optimizeStreamingLatency': 'Optimización de latencia de streaming de xAI', + 'tts.xai.sampleRate': 'Frecuencia de muestreo de xAI', + 'tts.xai.bitRate': 'Tasa de bits de xAI', + 'tts.minimax.model': 'Modelo TTS de MiniMax', + 'tts.minimax.voiceId': 'Voz de MiniMax', + 'tts.mistral.model': 'Modelo TTS de Mistral', + 'tts.mistral.voiceId': 'Voz de Mistral', + 'tts.gemini.model': 'Modelo TTS de Gemini', + 'tts.gemini.voice': 'Voz de Gemini', + 'tts.neutts.model': 'Modelo de NeuTTS', + 'tts.neutts.device': 'Dispositivo de NeuTTS', + 'tts.kittentts.model': 'Modelo de KittenTTS', + 'tts.kittentts.voice': 'Voz de KittenTTS', + 'tts.piper.voice': 'Voz de Piper', + 'tts.deepinfra.model': 'Modelo TTS de DeepInfra', + 'tts.deepinfra.voice': 'Voz de DeepInfra', + 'memory.memoryEnabled': 'Memoria persistente', + 'memory.userProfileEnabled': 'Perfil de usuario', + 'memory.memoryCharLimit': 'Presupuesto de memoria', + 'memory.userCharLimit': 'Presupuesto de perfil', + 'memory.provider': 'Proveedor de memoria', + 'context.engine': 'Motor de contexto', + 'compression.enabled': 'Compresión automática', + 'compression.threshold': 'Umbral de compresión', + 'compression.targetRatio': 'Objetivo de compresión', + 'compression.protectLastN': 'Mensajes recientes protegidos', + 'delegation.model': 'Modelo de subagente', + 'delegation.provider': 'Proveedor de subagente', + 'delegation.maxIterations': 'Límite de turnos del subagente', + 'delegation.maxConcurrentChildren': 'Subagentes en paralelo', + 'delegation.childTimeoutSeconds': 'Tiempo de espera del subagente', + 'delegation.reasoningEffort': 'Esfuerzo de razonamiento del subagente', + 'updates.nonInteractiveLocalChanges': 'Cambios locales al actualizar desde la app' + }, + fieldDescriptions: { + model: 'Se usa para chats nuevos a menos que elijas otro modelo en el compositor.', + modelContextLength: 'Déjalo en 0 para usar la ventana de contexto detectada del modelo seleccionado.', + fallbackProviders: 'Entradas proveedor:modelo de respaldo para intentar si falla el modelo predeterminado.', + 'display.personality': 'Estilo de asistente predeterminado para sesiones nuevas.', + 'display.showReasoning': 'Mostrar secciones de razonamiento cuando el backend las proporcione.', + 'desktop.repoScanEnabled': 'Escanear carpetas locales en busca de repositorios Git para mostrar en Proyectos.', + 'desktop.repoScanRoots': 'Carpetas a escanear. Déjalo vacío para escanear tu directorio de inicio.', + 'desktop.repoScanExcludePaths': + 'Carpetas y sus subcarpetas que se omitirán durante la detección de repositorios.', + timezone: 'Identificador de zona horaria IANA. Vacío usa la zona horaria del sistema.', + 'agent.imageInputMode': 'Controla cómo se envían los adjuntos de imagen al modelo.', + 'agent.maxTurns': 'Límite superior de turnos de llamadas a herramientas antes de que Hermes detenga una ejecución.', + 'terminal.cwd': 'Carpeta de proyecto predeterminada para el trabajo con herramientas y terminal.', + 'terminal.persistentShell': 'Mantener el estado del shell entre comandos cuando el backend lo soporta.', + 'terminal.envPassthrough': 'Variables de entorno a pasar a la ejecución de herramientas.', + 'terminal.dockerImage': 'Imagen de contenedor usada cuando el backend de ejecución es Docker.', + 'terminal.singularityImage': 'Imagen usada cuando el backend de ejecución es Singularity.', + 'terminal.modalImage': 'Imagen usada cuando el backend de ejecución es Modal.', + 'terminal.daytonaImage': 'Imagen usada cuando el backend de ejecución es Daytona.', + 'codeExecution.mode': 'Con qué rigor se limita la ejecución de código al proyecto actual.', + fileReadMaxChars: 'Máximo de caracteres que Hermes puede leer de una sola petición de archivo.', + 'approvals.mode': 'Cómo maneja Hermes los comandos que necesitan aprobación explícita.', + 'approvals.timeout': 'Cuánto esperan las peticiones de aprobación antes de agotar el tiempo.', + 'security.redactSecrets': 'Ocultar los secretos detectados del contenido visible por el modelo cuando sea posible.', + 'checkpoints.enabled': 'Crear instantáneas de reversión antes de editar archivos.', + 'memory.memoryEnabled': 'Guardar memorias duraderas que puedan ayudar a sesiones futuras.', + 'memory.userProfileEnabled': 'Mantener un perfil compacto de las preferencias del usuario.', + 'context.engine': 'Estrategia para gestionar conversaciones largas cerca del límite de contexto.', + 'compression.enabled': 'Resumir el contexto más antiguo cuando las conversaciones se hacen grandes.', + 'voice.autoTts': 'Hablar automáticamente las respuestas del asistente.', + 'tts.xai.voiceId': 'ID de voz de xAI (ej. eve) o un ID de voz personalizado.', + 'tts.xai.language': 'Código de idioma hablado (ej. en, es) o "auto" para autodetección.', + 'tts.xai.speed': 'Velocidad de reproducción. 0.7 = más lenta, 1.0 = normal, 1.5 = más rápida.', + 'tts.xai.autoSpeechTags': + 'Deja que un LLM inserte etiquetas de audio expresivas ([laughing], [sighs]) en el guion antes de la síntesis.', + 'tts.xai.optimizeStreamingLatency': 'Equilibrio entre latencia y calidad. 0 = mejor calidad, 2 = menor latencia.', + 'tts.xai.sampleRate': 'Frecuencia de muestreo de audio en Hz. Más alta = mejor calidad, archivos más grandes.', + 'tts.xai.bitRate': 'Tasa de bits MP3 en bps. Solo se aplica cuando el códec es mp3.', + 'tts.neutts.device': 'Dispositivo de inferencia local para NeuTTS.', + 'stt.enabled': 'Activar la transcripción de voz local o respaldada por proveedor.', + 'stt.echoTranscripts': 'Publicar la transcripción cruda 🎙️ de los mensajes de voz de vuelta en el chat.', + 'stt.elevenlabs.languageCode': 'Código de idioma ISO-639-3 opcional. Vacío deja que ElevenLabs lo autodetecte.', + 'updates.nonInteractiveLocalChanges': + 'Cuando Hermes se actualice desde la app (sin aviso de terminal), conservar los cambios locales del código (stash) o descartarlos (discard). Las actualizaciones por terminal siempre preguntan.' + }, + gateway: { + loading: 'Cargando ajustes de la puerta de enlace...', + unavailableTitle: 'Ajustes de la puerta de enlace no disponibles', + unavailableDesc: 'El puente IPC del escritorio no expone los ajustes de la puerta de enlace.', + title: 'Conexión de la puerta de enlace', + envOverride: 'anulación por entorno', + intro: + 'Local por defecto. Usa remoto cuando esta app deba manejar un backend de Hermes en otra parte. Anulaciones por perfil abajo.', + appliesTo: 'Se aplica a', + allProfiles: 'Todos los perfiles', + defaultConnection: 'Conexión predeterminada para cada perfil que no tenga su propia anulación.', + profileConnection: profile => + `Conexión usada solo cuando “${profile}” es el perfil activo. Elige Usar puerta de enlace predeterminada para quitar su anulación.`, + envOverrideTitle: 'Las variables de entorno están controlando esta sesión del escritorio.', + envOverrideDesc: + 'Desactiva HERMES_DESKTOP_REMOTE_URL y HERMES_DESKTOP_REMOTE_TOKEN para usar el ajuste guardado abajo.', + modeTitle: 'Modo de conexión', + localTitle: 'Puerta de enlace local', + localDesc: 'Inicia un backend privado de Hermes en localhost. Es el predeterminado y funciona sin conexión.', + inheritTitle: 'Usar puerta de enlace predeterminada', + inheritDesc: 'Quita la anulación de este perfil y usa la conexión predeterminada.', + remoteTitle: 'Puerta de enlace remota', + remoteDesc: 'Conecta este escritorio a un backend de Hermes remoto.', + remoteAuthHint: + 'Las puertas de enlace alojadas usan OAuth o usuario y contraseña; las autoalojadas pueden usar un token de sesión.', + cloudTitle: 'Hermes Cloud', + cloudDesc: 'Inicia sesión una vez en Hermes Cloud y elige entre los agentes de tu cuenta — sin URL que pegar.', + cloudSignInTitle: 'Hermes Cloud', + cloudSignIn: 'Iniciar sesión en Hermes Cloud', + cloudSignedIn: 'Sesión iniciada en Hermes Cloud', + cloudNeedsSignIn: 'Inicia sesión en Hermes Cloud para descubrir los agentes de tu cuenta.', + cloudSignedInDesc: 'Has iniciado sesión. Elige un agente abajo; la sesión se refresca automáticamente.', + cloudAgentsTitle: 'Tus agentes', + cloudOrgPickerTitle: 'Elige una organización', + cloudOrgSelect: 'Seleccionar', + cloudOrgChange: 'Cambiar organización', + cloudOrgRole: role => `Rol: ${role}`, + cloudLoadingAgents: 'Cargando tus agentes…', + cloudNoAgents: { + before: 'No se encontraron agentes en esta cuenta. Crea uno en el ', + linkText: 'portal de Nous', + after: ', y luego refresca.' + }, + cloudRefresh: 'Actualizar', + cloudConnect: 'Conectar', + cloudConnecting: 'Conectando…', + cloudDiscoverFailed: 'No se pudieron cargar tus agentes de Hermes Cloud', + cloudConnectFailed: 'No se pudo conectar a ese agente', + cloudSignInFailed: 'Falló el inicio de sesión en Hermes Cloud', + cloudSignedOutTitle: 'Sesión cerrada de Hermes Cloud', + cloudSignedOutMessage: 'Se limpió la sesión de Hermes Cloud.', + cloudConnectedTitle: 'Conectado', + cloudConnectedPill: 'Conectado', + cloudConnectedTo: name => `Conectado a ${name}.`, + cloudAgentProvisioning: 'Aprovisionando…', + cloudStatusLabel: status => `Estado: ${status}`, + remoteUrlTitle: 'URL remota', + remoteUrlDesc: 'URL base para el backend del panel remoto. Se admiten prefijos de ruta, por ejemplo /hermes.', + probing: 'Comprobando cómo se autentica esta puerta de enlace…', + probeError: + 'Todavía no se puede alcanzar esta puerta de enlace. Comprueba la URL — el método de autenticación aparecerá cuando responda.', + signedIn: 'Sesión iniciada', + signIn: 'Iniciar sesión', + signOut: 'Cerrar sesión', + signInWith: provider => `Iniciar sesión con ${provider}`, + authTitle: 'Autenticación', + authSignedInPassword: + 'Esta puerta de enlace usa usuario y contraseña. Has iniciado sesión; la sesión se refresca automáticamente.', + authSignedInOauth: 'Esta puerta de enlace usa OAuth. Has iniciado sesión; la sesión se refresca automáticamente.', + authNeedsPassword: + 'Esta puerta de enlace usa usuario y contraseña. Inicia sesión para autorizar esta app de escritorio.', + authNeedsOauth: provider => `Esta puerta de enlace usa OAuth. Inicia sesión con ${provider} para autorizar esta app de escritorio.`, + tokenTitle: 'Token de sesión', + tokenDesc: + 'El token de sesión del panel usado para acceso REST y WebSocket. Déjalo vacío para conservar el token guardado.', + existingToken: value => `Token existente ${value}`, + savedToken: 'guardado', + pasteSessionToken: 'Pegar token de sesión', + plainTextConfirmTitle: '¿Guardar el token de la puerta de enlace en texto plano?', + plainTextConfirmDesc: + 'No se encontró ningún servicio de llavero del sistema en esta máquina, así que el token se guardaría sin cifrar en el archivo de ajustes de conexión de la app, legible por cualquier proceso que corra como este usuario. Instala o activa GNOME Keyring o KWallet para almacenamiento cifrado.', + plainTextConfirmAction: 'Guardar como texto plano', + plainTextStoredTitle: 'Token guardado en texto plano', + plainTextStoredDesc: + 'El almacenamiento seguro no está disponible, así que el token guardado se guarda sin cifrar en el archivo de ajustes de conexión de la app en esta máquina. Instala o activa GNOME Keyring o KWallet para cifrarlo.', + testRemote: 'Probar remoto', + saveForRestart: 'Guardar para el próximo reinicio', + saveAndReconnect: 'Guardar y reconectar', + diagnostics: 'Diagnósticos', + diagnosticsDesc: 'Revela desktop.log en tu administrador de archivos — útil cuando la puerta de enlace no arranca.', + openLogs: 'Abrir registros', + incompleteTitle: 'Puerta de enlace remota incompleta', + incompleteSignIn: 'Introduce una URL remota e inicia sesión antes de cambiar a remoto.', + incompleteToken: 'Introduce una URL remota y un token de sesión antes de cambiar a remoto.', + incompleteSignInTest: 'Introduce una URL remota e inicia sesión antes de probar.', + incompleteTokenTest: 'Introduce una URL remota y un token de sesión antes de probar.', + enterUrlFirst: 'Introduce primero una URL remota.', + restartingTitle: 'Reiniciando la conexión de la puerta de enlace', + savedTitle: 'Ajustes de la puerta de enlace guardados', + restartingMessage: 'Hermes Desktop se reconectará con los ajustes guardados — la ventana permanece abierta.', + savedMessage: 'Guardado para el próximo reinicio.', + connectedTo: (baseUrl, version) => `Conectado a ${baseUrl}${version ? ` · Hermes ${version}` : ''}`, + reachableTitle: 'Puerta de enlace remota accesible', + signedOutTitle: 'Sesión cerrada', + signedOutMessage: 'Se limpió la sesión de la puerta de enlace remota.', + failedLoad: 'No se pudieron cargar los ajustes de la puerta de enlace', + signInFailed: 'Falló el inicio de sesión', + signOutFailed: 'Falló el cierre de sesión', + testFailed: 'Falló la prueba de la puerta de enlace remota', + applyFailed: 'No se pudieron aplicar los ajustes de la puerta de enlace', + saveFailed: 'No se pudieron guardar los ajustes de la puerta de enlace', + sshTitle: 'Conectar vía SSH', + sshDesc: + 'Hermes se lanza en el remoto vía SSH y se tuneliza a esta app — nada que iniciar ni exponer tú. Requiere acceso SSH basado en claves funcionando al host.', + sshTrustHint: 'La primera clave de host presentada se confía y fija; cambios posteriores fallan cerrados.', + sshHostTitle: 'Host', + sshHostDesc: 'usuario@host, o un alias de Host de ~/.ssh/config.', + sshHostPick: 'Seleccionar un host…', + sshHostPickTitle: 'Host', + sshHostPickDesc: 'Un alias de Host de ~/.ssh/config, o Personalizado para escribir uno.', + sshHostCustom: 'Personalizado (entrar manualmente)…', + sshUserTitle: 'Usuario', + sshUserDesc: 'Vacío = ~/.ssh/config o tu usuario actual.', + sshUserPlaceholder: 'desde ~/.ssh/config', + sshPortTitle: 'Puerto', + sshPortDesc: 'Vacío = 22 o el puerto de ~/.ssh/config.', + sshKeyTitle: 'Archivo de identidad', + sshKeyDesc: 'Ruta a la clave privada. Vacío = ssh-agent o ~/.ssh/config.', + sshHermesPathTitle: 'Ruta de Hermes (opcional)', + sshHermesPathDesc: 'Ruta completa al binario hermes remoto. Vacío = auto-detectar.', + sshHermesPathPlaceholder: 'auto-detectar', + sshRemoteProfileTitle: 'Perfil remoto (opcional)', + sshRemoteProfileDesc: 'Nombre del perfil en el host remoto. Vacío = usar el nombre del perfil del escritorio.', + sshTestConnection: 'Probar SSH', + sshConnect: 'Conectar', + sshButtonsHint: 'Guardar aplica en el próximo lanzamiento. Conectar reconecta ahora.', + sshReachable: (host, platform) => `Accesible: ${host} (${platform}) — Hermes encontrado`, + sshIncompleteHost: 'Introduce un host SSH antes de conectar.', + sshErrUnreachable: 'No se pudo alcanzar ese host vía SSH. Comprueba el host, el puerto y tu red.', + sshErrAuth: + 'Falló la autenticación SSH. Carga tu clave en el ssh-agent (ssh-add) o pon un IdentityFile en ~/.ssh/config — Hermes ejecuta ssh sin interacción.', + sshErrHostKey: + 'La clave de host ha CAMBIADO desde la última vez que te conectaste. Verifica que es esperado, luego ejecuta ssh-keygen -R y reconecta.', + sshErrNotInstalled: + 'Hermes no está instalado en el host remoto. Instálalo allí (curl -fsSL https://hermes-agent.nousresearch.com/install.sh | sh) o pon la ruta de Hermes.', + sshErrPlatform: + 'Plataforma remota no soportada. El modo SSH de Hermes Desktop soporta hosts remotos Linux, macOS y Windows.', + sshErrTimeout: 'Tiempo de espera de la conexión SSH agotado. El host puede estar inalcanzable o dormido.', + sshErrUpdateRequired: 'Actualiza Hermes en el host remoto antes de conectar con SSH de Desktop.', + sshErrUnknown: 'Falló la conexión SSH.' + }, + keys: { + loading: 'Cargando claves API y credenciales...', + failedLoad: 'No se pudieron cargar las claves API', + empty: 'Nada configurado en esta categoría todavía.' + }, + mcp: { + loading: 'Cargando servidores MCP...', + failedLoad: 'No se pudo cargar la configuración MCP', + nameRequiredTitle: 'Se requiere nombre', + nameRequiredMessage: 'Pon una clave de configuración a este servidor MCP.', + objectRequired: 'La configuración del servidor debe ser un objeto JSON', + invalidJson: 'JSON de MCP no válido', + saveFailed: 'Fallo al guardar', + removeFailed: 'Fallo al eliminar', + gatewayUnavailableTitle: 'Puerta de enlace no disponible', + gatewayUnavailableMessage: 'Reconecta la puerta de enlace antes de recargar MCP.', + reloadedTitle: 'Herramientas MCP recargadas', + reloadedMessage: 'Los nuevos esquemas de herramientas se aplican a turnos nuevos.', + reloadFailed: 'Fallo al recargar MCP', + savedTitle: 'Servidor MCP guardado', + savedMessage: name => `${name} se aplica tras recargar MCP.`, + newServer: 'Nuevo servidor', + reload: 'Recargar MCP', + reloading: 'Recargando...', + emptyTitle: 'Sin servidores MCP', + emptyDesc: 'Añade un servidor stdio o HTTP para exponer herramientas MCP.', + disabled: 'desactivado', + editServer: 'Editar servidor', + name: 'Nombre', + serverJson: 'JSON del servidor', + remove: 'Eliminar', + saveServer: 'Guardar servidor', + test: 'Probar conexión', + testing: 'Probando...', + testOk: count => `Conectado — ${count} herramienta${count === 1 ? '' : 's'} disponible${count === 1 ? '' : 's'}`, + testFailed: 'Fallo de conexión', + enableServer: name => `Activar ${name}`, + disableServer: name => `Desactivar ${name}`, + serverEnabled: name => `${name} activado — se aplica a sesiones nuevas.`, + serverDisabled: name => `${name} desactivado — se aplica a sesiones nuevas.`, + toggleFailed: (name, enabled) => `No se pudo ${enabled ? 'activar' : 'desactivar'} ${name}`, + tabServers: 'Servidores', + tabCatalog: 'Catálogo', + catalogLoading: 'Cargando catálogo MCP...', + catalogLoadFailed: 'No se pudo cargar el catálogo MCP', + catalogEmpty: 'No hay entradas de catálogo disponibles.', + catalogInstalled: 'Instalado', + catalogEnabled: 'Activado', + catalogNeedsInstall: 'Necesita compilación', + catalogInstall: 'Instalar', + catalogInstalling: 'Instalando...', + catalogInstallStarted: name => `Instalando ${name}... se aplica a sesiones nuevas al terminar.`, + catalogInstallFailed: name => `No se pudo instalar ${name}`, + catalogEnvPrompt: name => `${name} requiere credenciales`, + catalogEnvRequired: 'Rellena los valores requeridos antes de instalar.', + capabilitySummary: (tools, prompts, resources) => + `${[`${tools} herramientas`, ...(prompts ? [`${prompts} prompts`] : []), ...(resources ? [`${resources} recursos`] : [])].join(', ')} activado`, + statusConnecting: 'Conectando…', + statusNeedsAuth: 'Requiere autenticación', + statusError: 'Error', + statusOff: 'Desactivado', + allServers: 'Todos los servidores', + authenticatedTitle: 'Autenticado', + authenticatedMessage: (server, count) => `${server}: ${count} herramientas`, + waitingForBrowser: 'Esperando al navegador…', + authenticate: 'Autenticar', + unsavedConnect: 'Sin guardar — guarda mcp.json para conectar.', + enableTool: tool => `Activar ${tool}`, + disableTool: tool => `Desactivar ${tool}`, + noOutput: 'Sin salida todavía.' + }, + model: { + loading: 'Cargando configuración del modelo...', + appliesDesc: 'Se aplica a sesiones nuevas. Usa el selector de modelo en el compositor para cambiar el modelo del chat activo.', + provider: 'Proveedor', + model: 'Modelo', + applying: 'Aplicando...', + defaultsLabel: 'Predeterminados', + reasoning: 'Razonamiento', + reasoningOff: 'Desactivado', + defaultsFailed: 'No se pudieron guardar los valores predeterminados del modelo', + auxiliaryTitle: 'Modelos auxiliares', + resetAllToMain: 'Restablecer todos al principal', + auxiliaryDesc: 'Las tareas auxiliares se ejecutan en el modelo principal por defecto. Asigna un modelo dedicado a cualquier tarea para sobrescribir.', + setToMain: 'Establecer como principal', + change: 'Cambiar', + autoUseMain: 'auto · usar modelo principal', + providerDefault: '(predeterminado del proveedor)', + fallbackAdd: 'Añadir respaldo', + fallbackEmpty: 'Sin modelos de respaldo — se usa el modelo predeterminado a menos que falle.', + notInCatalog: "no está en la lista de modelos de este proveedor — las llamadas pueden caer en un respaldo.", + tasks: { + vision: { label: 'Visión', hint: 'Análisis de imágenes' }, + web_extract: { label: 'Extracción web', hint: 'Resumen de páginas' }, + compression: { label: 'Compresión', hint: 'Compactación de contexto' }, + skills_hub: { label: 'Hub de habilidades', hint: 'Búsqueda de habilidades' }, + approval: { label: 'Aprobación', hint: 'Auto-aprobación inteligente' }, + mcp: { label: 'MCP', hint: 'Enrutamiento de herramientas MCP' }, + title_generation: { label: 'Generación de títulos', hint: 'Títulos de sesión' }, + curator: { label: 'Curador', hint: 'Revisión de uso de habilidades' } + } + }, + providers: { + connectAccount: 'Conectar una cuenta', + haveApiKey: '¿tienes una clave API en su lugar?', + intro: 'Inicia sesión con una suscripción — sin clave API que copiar. Hermes ejecuta el inicio de sesión en el navegador por ti, aquí mismo en la app.', + connected: 'Conectado', + collapse: 'Contraer', + connectAnother: 'Conectar otro proveedor', + otherProviders: 'Otros proveedores', + disconnect: 'Desconectar', + disconnectInTerminal: 'Desconectar (ejecuta el comando de eliminación en la terminal)', + removeConfirm: provider => `¿Eliminar ${provider}?`, + removeExternalGeneric: provider => `${provider} se gestiona desde su propio CLI — elimínalo allí.`, + removeKeyManaged: provider => `${provider} está configurado desde una clave API. Elimínalo desde Claves API.`, + removeTerminalConfirm: (provider, command) => + `¿Desconectar ${provider}? Esto ejecuta "${command}" en la terminal para borrar la credencial.`, + removeTerminalRunning: provider => `Ejecutando desconexión de ${provider} en la terminal…`, + removedTitle: 'Cuenta eliminada', + removedMessage: provider => `${provider} ha sido eliminado.`, + failedRemove: provider => `No se pudo eliminar ${provider}`, + noProviderKeys: 'No hay claves API de proveedores disponibles.', + searchKeys: 'Buscar proveedores…', + noKeysMatch: 'Ningún proveedor coincide con tu búsqueda.', + localEndpoint: { + title: 'Endpoint local / personalizado', + description: 'Apunta Hermes a cualquier endpoint compatible con OpenAI (Zyphra, vLLM, llama.cpp, Ollama, etc).' + }, + loading: 'Cargando proveedores...' + }, + sessions: { + loading: 'Cargando sesiones archivadas…', + archivedTitle: 'Sesiones archivadas', + archivedIntro: 'Los chats archivados están ocultos en la barra lateral pero conservan todos sus mensajes. Ctrl/-clic en un chat en la barra lateral para archivarlo.', + emptyArchivedTitle: 'Nada archivado', + emptyArchivedDesc: 'Archiva un chat para ocultarlo aquí.', + unarchive: 'Desarchivar', + deletePermanently: 'Eliminar permanentemente', + messages: count => `${count} ${count === 1 ? 'mensaje' : 'mensajes'}`, + restored: 'Restaurado', + deleteConfirm: title => `Eliminar permanentemente "${title}"? No se puede deshacer.`, + autoArchiveTitle: 'Auto-archivar chats inactivos', + autoArchiveDesc: 'Archiva automáticamente los chats que no has tocado en un tiempo. Los chats fijados nunca se archivan, y nada se elimina — los chats archivados solo se mueven aquí.', + autoArchiveDaysLabel: 'Archivar después de', + autoArchiveDaysUnit: 'días de inactividad', + autoArchiveFailed: 'No se pudo actualizar el auto-archivo', + defaultDirTitle: 'Directorio de proyecto predeterminado', + defaultDirDesc: 'Las nuevas sesiones empiezan en esta carpeta a menos que elijas otra. Déjalo sin establecer para usar tu directorio de inicio.', + defaultDirUpdated: 'Directorio de proyecto predeterminado actualizado — inicia un nuevo chat (Ctrl/+N) para que surta efecto', + defaultsTo: label => `Predeterminado a ${label}.`, + change: 'Cambiar', + choose: 'Elegir', + clear: 'Limpiar', + notSet: 'Sin establecer', + failedLoad: 'No se pudieron cargar las sesiones archivadas', + unarchiveFailed: 'Fallo al desarchivar', + deleteFailed: 'Fallo al eliminar', + updateDirFailed: 'No se pudo actualizar el directorio predeterminado', + clearDirFailed: 'No se pudo limpiar el directorio predeterminado' + }, + toolsets: { + loadingConfig: 'Cargando configuración', + savedTitle: 'Credencial guardada', + savedMessage: key => `${key} actualizada.`, + removedTitle: 'Credencial eliminada', + removedMessage: key => `${key} eliminada.`, + failedSave: key => `No se pudo guardar ${key}`, + failedRemove: key => `No se pudo eliminar ${key}`, + failedReveal: key => `No se pudo revelar ${key}`, + removeConfirm: key => `Eliminar ${key} de .env?`, + set: 'Establecer', + notSet: 'Sin establecer', + selectedTitle: 'Proveedor seleccionado', + selectedMessage: provider => `${provider} está ahora activo.`, + failedSelect: provider => `No se pudo seleccionar ${provider}`, + failedLoad: 'No se pudo cargar la configuración de herramientas', + noProviderOptions: 'Este conjunto de herramientas no tiene opciones de proveedor — actívalo y funcionará con tu configuración actual.', + noProviders: 'No hay proveedores disponibles para este conjunto de herramientas ahora mismo.', + ready: 'Listo', + needsSignIn: 'Requiere inicio de sesión', + needsSetup: 'Configuración requerida', + activeBackend: 'Activo', + activeBackendHint: 'Este es tu backend activo', + useBackend: 'Usar este backend', + nousIncluded: 'Incluido con una suscripción de Nous — inicia sesión en Nous Portal para activar.', + nousAuthNeededTitle: 'Iniciar sesión en Nous Portal', + nousAuthNeededMessage: provider => `${provider} está guardado pero no se activará hasta que inicies sesión en Nous Portal.`, + nousAuthSignIn: 'Iniciar sesión', + nousAuthDoneTitle: 'Nous Portal conectado', + nousAuthDoneMessage: 'Tus backends de suscripción están ahora activos.', + nousAuthFailed: 'El inicio de sesión en Nous Portal no se completó', + noApiKeyRequired: 'No se requiere clave API.', + postSetupHint: step => `Este backend necesita una instalación única (${step}). Se ejecuta en esta máquina — puede tardar unos minutos.`, + postSetupInstalledHint: 'Instalado. Vuelve a ejecutar la configuración solo si algo falla.', + postSetupRun: 'Ejecutar configuración', + postSetupRerun: 'Volver a ejecutar configuración', + postSetupInstalled: 'Instalado', + postSetupRunning: 'Instalando…', + postSetupStarting: 'Iniciando…', + postSetupCompleteTitle: 'Configuración completada', + postSetupCompleteMessage: step => `${step} instalado.`, + postSetupErrorTitle: 'La configuración terminó con errores', + postSetupErrorMessage: step => `Revisa el registro de ${step}.`, + postSetupFailed: step => `Falló la ejecución de la configuración de ${step}`, + webSearchActive: backend => `Búsqueda: ${backend}`, + webExtractActive: backend => `Extracción: ${backend}`, + webCapabilityUnset: 'sin establecer', + webUseForSearch: 'Usar para Búsqueda', + webUseForExtract: 'Usar para Extracción', + webUsedForSearch: 'Backend de búsqueda', + webUsedForExtract: 'Backend de extracción', + webCapabilitySelectedMessage: (provider, capability) => `${provider} gestiona ahora web ${capability}.`, + failedSelectCapability: provider => `No se pudo establecer ${provider}`, + loadingModels: 'Cargando catálogo de modelos...', + modelSectionTitle: 'Modelo', + modelCount: count => `${count} modelo${count === 1 ? '' : 's'}`, + modelInUse: 'En uso', + modelDefault: 'predeterminado', + modelInactiveHint: 'Selecciona este backend primero para cambiar su modelo.', + modelSelectedTitle: 'Modelo seleccionado', + modelSelectedMessage: model => `${model} se aplica a sesiones nuevas.`, + failedSelectModel: model => `No se pudo seleccionar ${model}`, + terminalBackend: { + sectionTitle: 'Backend de ejecución', + loading: 'Comprobando backends de ejecución…', + failedLoad: 'No se pudieron cargar los backends de terminal', + ready: 'Listo', + needsSetup: 'Requiere configuración', + unavailable: 'No disponible', + inUse: 'En uso', + selectedTitle: 'Backend seleccionado', + selectedMessage: backend => `Los comandos de terminal ahora se ejecutan vía ${backend}. Se aplica a sesiones nuevas.`, + failedSelect: backend => `No se pudo seleccionar ${backend}`, + needsSetupHint: 'Puedes seleccionar este backend ahora — los comandos fallarán hasta que la configuración esté completa.' + } + }, + about: { + heading: 'Hermes Desktop', + version: value => `Versión ${value}`, + versionUnavailable: 'Versión no disponible', + updates: 'Actualizaciones', + checkNow: 'Buscar ahora', + checking: 'Buscando…', + seeWhatsNew: 'Ver novedades', + updateNow: 'Actualizar ahora', + releaseNotes: 'Notas de la versión', + onLatest: 'Estás en la última versión.', + installing: 'Se está instalando una actualización.', + cantUpdate: 'Esta compilación no puede actualizarse desde la app.', + cantReach: 'No pudimos contactar con el servidor de actualizaciones.', + tapCheck: 'Toca «Buscar ahora» para buscar actualizaciones.', + updateReady: count => `Una nueva actualización está lista (${count} cambio${count === 1 ? '' : 's'} incluido${count === 1 ? '' : 's'}).`, + lastChecked: age => `Última comprobación ${age}`, + justNowSuffix: ' · justo ahora', + automaticUpdates: 'Actualizaciones automáticas', + automaticUpdatesDesc: 'Hermes busca actualizaciones automáticamente en segundo plano y te avisa cuando una está lista.', + branchCommit: (branch, commit) => `Rama ${branch} · Commit ${commit}`, + never: 'nunca', + justNow: 'justo ahora', + minAgo: count => `hace ${count} min`, + hoursAgo: count => `hace ${count} horas`, + daysAgo: count => `hace ${count} días` + }, + config: { + none: 'Ninguno', + noneParen: '(ninguno)', + builtinOnly: 'Solo integrados', + notSet: 'Sin establecer', + commaSeparated: 'valores separados por comas', + searchPlaceholder: 'Buscar…', + noResults: 'No se encontraron resultados', + systemDefault: 'Valor del sistema', + loading: 'Cargando configuración de Hermes...', + emptyTitle: 'Nada que configurar', + emptyDesc: 'Esta sección no tiene ajustes modificables.', + failedLoad: 'No se pudieron cargar los ajustes', + autosaveFailed: 'Fallo el guardado automático', + imported: 'Configuración importada', + invalidJson: 'JSON de configuración no válido', + keepAwakeTitle: 'Mantener el equipo despierto', + keepAwakeDesc: 'Evita que esta máquina se duerma para que las ejecuciones largas o nocturnas sigan funcionando. La pantalla aún puede atenuarse.', + attachmentSizeTitle: 'Tamaño máximo de vista previa / carga de imagen', + attachmentSizeDesc: 'Tamaño máximo de archivo local que el escritorio cargará para vistas previas y adjuntos de imagen, en MB. El valor por defecto es 16. Los adjuntos remotos no-imagen usan un límite separado de 256 MB. Un valor muy alto carga el archivo completo en memoria y puede congelar o bloquear la app.', + attachmentSizeUnit: 'MB', + attachmentSizeLabel: 'Tamaño máximo de vista previa / carga de imagen en megabytes' + }, + quickEntry: { + enabledTitle: 'Entrada rápida', + enabledDesc: 'Invoca un pequeño compositor desde cualquier lugar con un atajo global y envía un prompt sin abrir Hermes.', + shortcutTitle: 'Atajo de entrada rápida', + shortcutDesc: 'Necesita al menos un modificador, p. ej. CommandOrControl+Shift+Espacio.', + active: 'El atajo está activo.', + takenBy: 'Otra app ya usa este atajo — elige uno diferente.', + invalidShortcut: 'No es un atajo válido. Incluye al menos una tecla modificadora.' + }, + credentials: { + pasteKey: 'Pegar clave', + pasteLabelKey: label => `Pegar clave de ${label}`, + optional: 'Opcional', + enterValueFirst: 'Introduce un valor primero.', + couldNotSave: 'No se pudo guardar la credencial.', + remove: 'Eliminar', + getKey: 'Obtener una clave', + saving: 'Guardando' + }, + envActions: { + actions: 'Acciones', + manageInKeys: 'Gestionar en Claves API', + docs: 'Documentación', + hideValue: 'Ocultar valor', + revealValue: 'Revelar valor', + replace: 'Reemplazar', + set: 'Establecer', + clear: 'Limpiar' + }, + }, + skills: { + tabSkills: 'Habilidades', + tabToolsets: 'Herramientas', + tabMcp: 'MCP', + tabHub: 'Explorar Hub', + all: 'Todo', + searchSkills: 'Buscar habilidades…', + searchToolsets: 'Buscar herramientas…', + refresh: 'Actualizar habilidades', + refreshing: 'Actualizando habilidades…', + loading: 'Cargando capacidades…', + noSkillsTitle: 'No se encontraron habilidades', + noSkillsDesc: 'Prueba una búsqueda más amplia o diferente categoría.', + noToolsetsTitle: 'No se encontraron conjuntos de herramientas', + noToolsetsDesc: 'Prueba una consulta de búsqueda más amplia.', + noDescription: 'Sin descripción.', + configured: 'Configurado', + needsKeys: 'Necesita claves', + visionModelHint: 'La visión usa tu configuración de modelos auxiliares — el modelo capaz de imágenes se elige ahí, no por proveedor aquí.', + visionModelLink: 'Elige modelo de visión en Ajustes → Modelos', + toolsetsEnabled: (enabled, total) => `${enabled}/${total} conjuntos de herramientas activados`, + configureToolset: label => `Configurar ${label}`, + toggleToolset: (label, enabled) => `Activar/desactivar conjunto de herramientas ${label} ${enabled ? 'activado' : 'desactivado'}`, + skillsLoadFailed: 'Fallo al cargar habilidades', + toolsetsRefreshFailed: 'Fallo al actualizar conjuntos de herramientas', + skillEnabled: 'Habilidad activada', + skillDisabled: 'Habilidad desactivada', + toolsetEnabled: 'Conjunto de herramientas activado', + toolsetDisabled: 'Conjunto de herramientas desactivado', + appliesToNewSessions: name => `${name} se aplica a sesiones nuevas.`, + failedToUpdate: name => `No se pudo actualizar ${name}`, + sortMostUsed: 'Más usadas', + sortAlpha: 'A–Z', + sortMostUsedDesc: ' Más usadas', + sortLeastUsedAsc: ' Menos usadas', + enableAll: 'Activar todo', + disableAll: 'Desactivar todo', + disableUnused: 'Desactivar no usadas', + bulkUpdated: count => `Actualizados ${count} ${count === 1 ? 'elemento' : 'elementos'} para sesiones nuevas.`, + bulkNoChange: 'Nada que cambiar.', + usageCount: count => `usado ${count}×`, + provenance: { + agent: 'Aprendida', + bundled: 'Integrada', + hub: 'Hub' + }, + emptyNoneFound: noun => `No se encontró ${noun}`, + emptyNothingMatches: query => `Nada coincide con "${query}".`, + emptyNoneAvailable: noun => `No hay ${noun} disponible todavía.`, + changesApplyNewSessions: 'Los cambios se aplican a sesiones nuevas.', + skillUpdated: 'Habilidad actualizada', + edit: 'Editar', + archive: 'Archivar', + skillArchivedTitle: 'Habilidad archivada', + skillArchivedMessage: 'Restaurable vía hermes curator restore.', + hub: { + searchPlaceholder: 'Buscar en el hub de habilidades', + search: 'Buscar', + searching: 'Buscando…', + connectingHubs: 'Conectando a hubs de habilidades…', + connectedHubs: 'Hubs conectados:', + featured: 'Habilidades destacadas', + landingHint: 'Busca en el hub para explorar habilidades instalables del índice oficial, GitHub y fuentes comunitarias.', + noResults: 'No se encontraron habilidades coincidentes en el hub.', + resultCount: (count, ms) => `${count} resultado${count === 1 ? '' : 's'}${ms !== null ? ` en ${ms}ms` : ''}`, + timedOut: sources => `Tiempo agotado: ${sources}`, + installed: 'Instalado', + install: 'Instalar', + installing: 'Instalando…', + uninstall: 'Desinstalar', + uninstalling: 'Desinstalando…', + updateAll: 'Actualizar instaladas', + updating: 'Actualizando…', + preview: 'Vista previa', + scan: 'Escanear', + scanning: 'Escaneando…', + close: 'Cerrar', + files: 'Archivos', + noReadme: 'Esta habilidad no tiene vista previa de SKILL.md.', + trust: { + builtin: 'integrada', + trusted: 'confiable', + community: 'comunidad' + }, + verdictSafe: 'Segura', + verdictCaution: 'Precaución', + verdictDangerous: 'Peligrosa', + policyAllow: 'Instalación permitida', + policyAsk: 'Revisar antes de instalar', + policyBlock: 'Instalación bloqueada por política', + findings: count => `${count} hallazgo${count === 1 ? '' : 's'}`, + noFindings: 'Sin hallazgos de seguridad.', + installStarted: name => `Instalando ${name}...`, + uninstallStarted: name => `Desinstalando ${name}...`, + updateStarted: 'Actualizando habilidades instaladas...', + actionFailed: 'Fallo en la acción de habilidad', + actionLog: 'Registro de acciones', + loadFailed: 'Fallo al cargar el hub de habilidades', + previewFailed: 'Fallo en la vista previa de habilidad', + scanFailed: 'Fallo en el escaneo de seguridad', + searchFailed: 'Fallo en la búsqueda del hub' + } + }, + starmap: { + title: 'Mapa de memoria', + subtitle: (nodes, clusters) => `${nodes} habilidades en ${clusters} categorías`, + close: 'Cerrar mapa de memoria', + refresh: 'Actualizar', + memory: 'Memoria', + filterAll: 'Todo', + filterUsed: 'Usadas', + filterLearned: 'Aprendidas', + viewGraph: 'Grafo', + loadFailed: 'No se pudo cargar el mapa de memoria', + loading: 'Cargando…', + emptyTitle: 'Aún no se ha aprendido nada', + emptyDesc: 'A medida que Hermes crea habilidades y memorias para tu trabajo, aparecerán aquí.', + share: 'Compartir mapa', + shareHint: 'Copia el código para compartir este mapa, o pega uno para cargarlo. Solo incluye la disposición, no tu memoria ni el texto de las habilidades.', + shareTitle: 'Importar / exportar mapa', + sharePlaceholder: 'Pega un código de mapa…', + copy: 'Copiar código del mapa', + copied: '¡Copiado!', + importMap: 'Importar un mapa', + importBtn: 'Cargar', + importEmpty: 'Pega un código de mapa para cargarlo.', + importSuccess: nodes => `Se cargó un mapa con ${nodes} ${nodes === 1 ? 'nodo' : 'nodos'}.`, + importedBadge: 'mapa importado', + resetToMine: 'Volver a mi mapa' + }, + agents: { + close: 'Cerrar agentes', + title: 'Árbol de subagentes', + subtitle: 'Actividad de subagentes en vivo para el turno actual.', + emptyTitle: 'Sin subagentes activos', + emptyDesc: 'Cuando un turno delega trabajo, los subagentes transmiten su progreso aquí.', + running: 'En ejecución', + failed: 'Fallidos', + done: 'Completados', + streaming: 'Transmitiendo', + files: 'Archivos', + moreFiles: count => `+${count} archivos más`, + delegation: index => `Delegación ${index}`, + workers: count => `${count} trabajadores`, + workersActive: count => `${count} activos`, + agentsCount: count => `${count} ${count === 1 ? 'agente' : 'agentes'}`, + activeCount: count => `${count} activos`, + failedCount: count => `${count} fallidos`, + toolsCount: count => `${count} herramientas`, + filesCount: count => `${count} archivos`, + updatedAgo: age => `actualizado ${age}`, + ageNow: 'ahora', + ageSeconds: seconds => `hace ${seconds}s`, + ageMinutes: minutes => `hace ${minutes}m`, + ageHours: hours => `hace ${hours}h`, + ageDays: days => `hace ${days}d`, + durationSeconds: seconds => `${seconds}s`, + durationMinutes: (minutes, seconds) => `${minutes}m ${seconds}s`, + tokens: value => `${value} tok` + }, + commandCenter: { + close: 'Cerrar centro de comandos', + paletteTitle: 'Paleta de comandos', + back: 'Volver', + searchPlaceholder: 'Buscar sesiones, vistas y acciones', + goTo: 'Ir a', + goToSession: 'Ir a la sesión', + branches: 'Ramas', + projects: 'Proyectos', + openFolder: 'Abrir carpeta como proyecto…', + openFolderAt: path => `Abrir carpeta como proyecto — ${path}`, + newSessionInProject: project => `Nueva sesión en ${project}`, + commands: 'Comandos', + startInBranch: branch => `Nueva conversación en ${branch}`, + commandCenter: 'Centro de comandos', + appearance: 'Apariencia', + settings: 'Ajustes', + changeTheme: 'Cambiar tema', + changeColorMode: 'Cambiar modo de color…', + pets: { + title: 'Mascotas', + placeholder: 'Buscar mascotas…', + loading: 'Cargando galería petdex…', + error: 'No se pudo acceder a la galería petdex.', + staleBackend: 'Reinicia Hermes para usar mascotas — el backend es anterior a esta función.', + empty: 'Sin mascotas coincidentes.', + turnOff: 'Apagar', + turnOn: 'Encender', + installed: 'Instalada', + generatedTag: 'Generada', + adoptFailed: 'No se pudo adoptar esa mascota.', + toggleFailed: enabled => `No se pudo ${enabled ? 'encender' : 'apagar'} la mascota.`, + noneAvailable: 'No hay mascotas disponibles — elige una abajo para instalar.' + }, + generatePet: { + title: 'Generar una mascota', + placeholder: 'Describe una mascota para generar…', + promptHint: 'Escribe una descripción y pulsa Enter para esbozar cuatro diseños.', + readyHint: 'Pulsa Enter para esbozar cuatro diseños a partir de tu descripción.', + generate: 'Generar', + generating: 'Generando…', + retry: 'Reintentar', + hatch: 'Incubar', + spawning: 'Generando…', + hatching: 'Incubando tu mascota…', + hatchingSub: 'Dándole vida…', + hatched: '¡Ha nacido!', + hatchRow: (_state, done, total) => `Dibujando el fotograma ${done} de ${total}…`, + hatchComposing: 'Ensamblando…', + hatchSaving: 'Casi listo…', + namePlaceholder: 'Pon nombre a tu mascota', + staleBackend: 'Actualiza Hermes para generar mascotas.', + backgroundHint: 'Puedes cerrar esto — Hermes te avisará cuando termine.', + slowProviderHint: 'Esto puede tardar varios minutos', + remix: 'Reinterpretar', + remixConfirmTitle: '¿Reinterpretar este diseño?', + remixConfirmBody: 'Esto genera un nuevo conjunto de diseños usando este como punto de partida. Puede tardar varios minutos.', + genericError: 'La generación falló — inténtalo de nuevo o elige una sugerencia.', + referenceImageTooLarge: 'La imagen de referencia es demasiado grande. Usa una de menos de 16 MB.', + referenceImageInvalid: 'No se pudo leer esa imagen de referencia. Prueba con PNG, JPG, WebP o GIF.', + adopt: 'Adoptar', + startOver: 'Empezar de nuevo' + }, + installTheme: { + title: 'Instalar tema…', + pageTitle: 'Instalar tema', + placeholder: 'Buscar en VS Code Marketplace...', + loading: 'Buscando en el Marketplace...', + error: 'No se pudo contactar con el Marketplace.', + empty: 'Sin temas coincidentes.', + install: 'Instalar', + installing: 'Instalando...', + installed: 'Instalado', + installs: count => `${count} instalaciones` + }, + settingsFields: 'Campos de ajustes', + mcpServers: 'Servidores MCP', + archivedChats: 'Chats archivados', + sections: { maintenance: 'Mantenimiento', sessions: 'Sesiones', system: 'Sistema', usage: 'Uso' }, + sectionDescriptions: { + maintenance: 'Diagnósticos, copias de seguridad, curador y datos de memoria', + sessions: 'Buscar y gestionar sesiones', + system: 'Estado, registros y acciones del sistema', + usage: 'Actividad de tokens, costes y habilidades a lo largo del tiempo' + }, + nav: { + newChat: { title: 'Nueva sesión', detail: 'Iniciar una sesión nueva' }, + settings: { title: 'Ajustes', detail: 'Configurar el escritorio de Hermes' }, + skills: { title: 'Capacidades', detail: 'Habilidades, herramientas y servidores MCP' }, + messaging: { title: 'Mensajería', detail: 'Configura Telegram, Slack, Discord y más' }, + artifacts: { title: 'Artefactos', detail: 'Explorar salidas generadas' } + }, + sectionEntries: { + sessions: { title: 'Panel de sesiones', detail: 'Buscar, fijar y gestionar sesiones' }, + system: { title: 'Panel del sistema', detail: 'Estado de la puerta de enlace, registros, reinicio/actualización' }, + usage: { title: 'Panel de uso', detail: 'Actividad de tokens, costes y habilidades' } + }, + providerNavigate: 'Navegar', + providerSessions: 'Sesiones', + refresh: 'Actualizar', + refreshing: 'Actualizando...', + noResults: 'No se encontraron resultados coincidentes.', + pinSession: 'Fijar sesión', + unpinSession: 'Desfijar sesión', + exportSession: 'Exportar sesión', + deleteSession: 'Eliminar sesión', + noSessions: 'Aún no hay sesiones.', + gatewayRunning: 'Puerta de enlace de mensajería en ejecución', + gatewayStopped: 'Puerta de enlace de mensajería detenida', + hermesActiveSessions: (version, count) => `Hermes ${version} · Sesiones activas ${count}`, + restartGateway: 'Reiniciar puerta de enlace', + gatewayRestartFailed: 'Fallo al reiniciar la puerta de enlace.', + updateHermes: 'Actualizar Hermes', + actionRunning: 'en ejecución', + actionDone: 'completado', + actionFailed: 'fallido', + actionStartedWaiting: 'Acción iniciada, esperando estado...', + loadingStatus: 'Cargando estado...', + recentLogs: 'Registros recientes', + noLogs: 'Aún no hay registros cargados.', + days: count => `${count}d`, + statSessions: 'Sesiones', + statApiCalls: 'Llamadas API', + statTokens: 'Tokens in/out', + statCost: 'Coste est.', + actualCost: cost => `real ${cost}`, + loadingUsage: 'Cargando uso...', + noUsage: period => `Sin uso en los últimos ${period} días.`, + retry: 'Reintentar', + dailyTokens: 'Tokens diarios', + input: 'entrada', + output: 'salida', + noDailyActivity: 'Sin actividad diaria.', + topModels: 'Modelos principales', + noModelUsage: 'Aún no hay uso de modelos.', + topSkills: 'Habilidades principales', + noSkillActivity: 'Aún no hay actividad de habilidades.', + actions: count => `${count} acciones`, + logFile: 'Archivo de registro', + logLevel: 'Nivel', + logSearchPlaceholder: 'Filtrar líneas de registro...', + maintenance: { + runOps: 'Diagnósticos', + doctor: 'Ejecutar doctor', + doctorDesc: 'Comprobar el estado de la instalación, la configuración y los proveedores', + securityAudit: 'Auditoría de seguridad', + securityAuditDesc: 'Escanear configuración y habilidades en busca de ajustes arriesgados', + backup: 'Crear copia de seguridad', + backupDesc: 'Comprimir configuración, memorias, habilidades y sesiones', + debugShare: 'Compartir diagnóstico', + debugShareDesc: 'Sube un informe depurado + registros, obtén enlaces compartibles (se eliminan automáticamente en 6h)', + debugShareRunning: 'Subiendo informe de diagnóstico...', + debugShareLinks: 'Enlaces para compartir', + debugShareFailed: 'Fallo al compartir diagnóstico', + copyLink: 'Copiar enlace', + linkCopied: 'Enlace copiado', + curator: 'Curador de habilidades', + curatorDesc: 'Revisión en segundo plano que archiva habilidades obsoletas creadas por el agente', + curatorPaused: 'En pausa', + curatorActive: 'Activo', + curatorDisabled: 'Desactivado', + curatorLastRun: when => `Última ejecución ${when}`, + curatorNeverRan: 'Nunca se ejecutó', + pause: 'Pausar', + resume: 'Reanudar', + runNow: 'Ejecutar ahora', + memoryData: 'Datos de memoria', + memoryDataDesc: 'Archivos de memoria integrados que se inyectan en cada sesión', + memoryProvider: name => `Proveedor activo: ${name}`, + builtinMemory: 'integrada', + memoryFile: 'Memoria del agente (MEMORY.md)', + userFile: 'Perfil de usuario (USER.md)', + bytes: size => size, + empty: 'vacío', + resetMemory: 'Restablecer memoria', + resetUser: 'Restablecer perfil', + resetAll: 'Restablecer ambos', + resetConfirm: target => `¿Eliminar ${target}? Esto no se puede deshacer.`, + resetDone: files => `Se eliminó ${files}.`, + resetFailed: 'Fallo al restablecer la memoria', + actionStarted: name => `${name} iniciado — siguiendo el registro...`, + actionFailed: name => `${name} no pudo iniciarse`, + running: 'En ejecución...', + viewLog: 'Registro de acciones' + } + }, + messaging: { + search: 'Buscar mensajería...', + loading: 'Cargando plataformas de mensajería...', + loadFailed: 'No se pudieron cargar las plataformas de mensajería', + states: { + connected: 'Conectado', + connecting: 'Conectando', + disabled: 'Desactivado', + fatal: 'Error', + gateway_stopped: 'Puerta de enlace de mensajería detenida', + not_configured: 'Requiere configuración', + pending_restart: 'Reinicio necesario', + retrying: 'Reintentando', + startup_failed: 'Fallo al iniciar' + }, + unknown: 'Desconocido', + hintPendingRestart: 'Reinicia la puerta de enlace desde la barra de estado para aplicar este cambio.', + hintGatewayStopped: 'Inicia la puerta de enlace desde la barra de estado para conectar.', + credentialsSet: 'Credenciales establecidas', + needsSetup: 'Requiere configuración', + gatewayStopped: 'Puerta de enlace de mensajería detenida', + getCredentials: 'Obtén tus credenciales', + openSetupGuide: 'Abrir guía de configuración', + required: 'Obligatorio', + recommended: 'Recomendado', + advanced: count => `Avanzado (${count})`, + noTokenNeeded: 'Esta plataforma no necesita token aquí. Usa la guía de configuración de arriba y luego actívala abajo.', + enabled: 'Activado', + disabled: 'Desactivado', + unsavedChanges: 'Cambios sin guardar', + saving: 'Guardando...', + saveChanges: 'Guardar cambios', + saved: 'Guardado', + replaceValue: 'Reemplazar valor actual', + openDocs: 'Abrir documentación', + clearField: key => `Limpiar ${key}`, + enableAria: name => `Activar ${name}`, + disableAria: name => `Desactivar ${name}`, + platformEnabled: name => `${name} activado`, + platformDisabled: name => `${name} desactivado`, + restartToApply: 'Este cambio surte efecto tras reiniciar la puerta de enlace.', + setupSaved: name => `Configuración de ${name} guardada`, + restartToReconnect: 'Las nuevas credenciales surten efecto tras reiniciar la puerta de enlace.', + keyCleared: key => `${key} limpiada`, + setupUpdated: name => `La configuración de ${name} se actualizó.`, + failedUpdate: name => `No se pudo actualizar ${name}`, + failedSave: name => `No se pudo guardar ${name}`, + failedClear: key => `No se pudo limpiar ${key}`, + pendingRequests: count => `Solicitudes pendientes (${count})`, + pendingAria: count => `${count} solicitud${count === 1 ? '' : 'es'} de emparejamiento pendiente${count === 1 ? '' : 's'}`, + approvedUsers: count => `Usuarios aprobados (${count})`, + approve: 'Aprobar', + approving: 'Aprobando...', + revoke: 'Revocar', + revoking: 'Revocando...', + revokeAria: name => `Revocar ${name}`, + revokeTitle: 'Revocar acceso', + revokeDesc: name => `${name} perderá el acceso y dejará de ser reconocido en su próximo mensaje.`, + approvedUser: name => `${name} aprobado`, + approvedHint: 'Se le reconoce automáticamente en su próximo mensaje.', + revokedUser: name => `${name} revocado`, + failedApprove: name => `No se pudo aprobar a ${name}`, + failedRevoke: name => `No se pudo revocar a ${name}`, + pairingLockedOut: 'Demasiados fallos de aprobación — esta plataforma está bloqueada. Inténtalo de nuevo más tarde.', + waitingSince: minutes => (minutes < 1 ? 'justo ahora' : `hace ${minutes}m`), + fieldCopy: { + TELEGRAM_BOT_TOKEN: { + label: 'Token del bot', + help: 'Crea un bot con @BotFather y pega el token que te dé.', + placeholder: 'Pegar token del bot de Telegram' + }, + TELEGRAM_ALLOWED_USERS: { + label: 'IDs de usuario de Telegram permitidos', + help: 'Recomendado. IDs numéricos separados por comas de @userinfobot. Sin esto, cualquiera puede escribir a tu bot.' + }, + TELEGRAM_PROXY: { label: 'URL del proxy', help: 'Solo necesario en redes donde Telegram esté bloqueado.' }, + DISCORD_BOT_TOKEN: { + label: 'Token del bot', + help: 'Crea una aplicación en el Portal de Desarrolladores de Discord, añade un bot y pega su token.' + }, + DISCORD_ALLOWED_USERS: { + label: 'IDs de usuario de Discord permitidos', + help: 'Recomendado. IDs de usuario de Discord separados por comas.' + }, + DISCORD_REPLY_TO_MODE: { label: 'Estilo de respuesta', help: 'first, all u off.' }, + DISCORD_ALLOW_ALL_USERS: { + label: 'Permitir todos los usuarios de Discord', + help: 'Solo desarrollo. Cuando es true, cualquiera puede escribir al bot sin lista de permitidos.' + }, + DISCORD_HOME_CHANNEL: { + label: 'ID del canal principal', + help: 'Canal donde el bot envía mensajes proactivos (salidas de cron, recordatorios).' + }, + DISCORD_HOME_CHANNEL_NAME: { + label: 'Nombre del canal principal', + help: 'Nombre mostrado del canal principal en registros y salida de estado.' + }, + BLUEBUBBLES_ALLOW_ALL_USERS: { + label: 'Permitir todos los usuarios de iMessage', + help: 'Cuando es true, omite la lista de permitidos de BlueBubbles.' + }, + MATTERMOST_ALLOW_ALL_USERS: { label: 'Permitir todos los usuarios de Mattermost' }, + MATTERMOST_HOME_CHANNEL: { label: 'Canal principal' }, + QQ_ALLOW_ALL_USERS: { label: 'Permitir todos los usuarios de QQ' }, + QQBOT_HOME_CHANNEL: { label: 'Canal principal de QQ', help: 'Canal o grupo predeterminado para entregas de cron.' }, + QQBOT_HOME_CHANNEL_NAME: { label: 'Nombre del canal principal de QQ' }, + SLACK_BOT_TOKEN: { + label: 'Token del bot de Slack', + help: 'Usa el token del bot de OAuth & Permissions tras instalar tu app de Slack.', + placeholder: 'Pegar token del bot de Slack' + }, + SLACK_APP_TOKEN: { + label: 'Token de la app de Slack', + help: 'Usa el token a nivel de app requerido para Socket Mode.', + placeholder: 'Pegar token de la app de Slack' + }, + SLACK_ALLOWED_USERS: { label: 'IDs de usuario de Slack permitidos', help: 'Recomendado. IDs de usuario de Slack separados por comas.' }, + MATTERMOST_URL: { label: 'URL del servidor', placeholder: 'https://mattermost.example.com' }, + MATTERMOST_TOKEN: { label: 'Token del bot' }, + MATTERMOST_ALLOWED_USERS: { + label: 'IDs de usuario permitidos', + help: 'Recomendado. IDs de usuario de Mattermost separados por comas.' + }, + MATRIX_HOMESERVER: { label: 'URL del homeserver', placeholder: 'https://matrix.org' }, + MATRIX_ACCESS_TOKEN: { label: 'Token de acceso' }, + MATRIX_USER_ID: { label: 'ID de usuario del bot', placeholder: '@hermes:example.org' }, + MATRIX_ALLOWED_USERS: { + label: 'IDs de usuario de Matrix permitidos', + help: 'Recomendado. IDs de usuario separados por comas en formato @usuario:servidor.' + }, + SIGNAL_HTTP_URL: { + label: 'URL del puente Signal', + placeholder: 'http://127.0.0.1:8080', + help: 'URL de un puente REST signal-cli en ejecución.' + }, + SIGNAL_ACCOUNT: { label: 'Número de teléfono', help: 'El número registrado con tu puente signal-cli.' }, + SIGNAL_ALLOWED_USERS: { label: 'Usuarios de Signal permitidos', help: 'Recomendado. Identificadores de Signal separados por comas.' }, + WHATSAPP_ENABLED: { + label: 'Activar puente de WhatsApp', + help: 'Se establece automáticamente con el interruptor de abajo. No lo toques a menos que sepas que lo necesitas.' + }, + WHATSAPP_MODE: { label: 'Modo de puente' }, + WHATSAPP_ALLOWED_USERS: { + label: 'Usuarios de WhatsApp permitidos', + help: 'Recomendado. Números de teléfono o IDs de WhatsApp separados por comas.' + } + }, + platformIntro: {} + }, + webhooks: { + search: 'Buscar webhooks...', + loading: 'Cargando webhooks...', + loadFailed: 'No se pudieron cargar los webhooks', + subscriptions: count => `Suscripciones (${count})`, + hint: 'Los cambios de suscripción se recargan en caliente una vez que el receptor está en ejecución. Las suscripciones desactivadas rechazan eventos entrantes.', + empty: 'Aún no hay suscripciones de webhook.', + disabledTitle: 'Receptor de webhooks desactivado', + disabledBody: 'Los webhooks son su propia plataforma de puerta de enlace. Actívalos aquí para aceptar eventos HTTP entrantes; los canales de chat solo se necesitan cuando una suscripción entrega a Telegram, Discord, Slack u otro canal.', + enable: 'Activar webhooks', + enabling: 'Activando...', + enabled: name => `Activado: "${name}"`, + disabled: name => `Desactivado: "${name}"`, + enableRow: 'Activar', + disableRow: 'Desactivar', + delete: 'Eliminar', + deleting: 'Eliminando...', + deleted: 'Webhook eliminado', + deleteTitle: 'Eliminar webhook', + deleteDescPrefix: 'Esto eliminará permanentemente ', + deleteDescSuffix: '. Esto no se puede deshacer.', + deleteFailed: name => `No se pudo eliminar "${name}"`, + toggleFailed: (name, enabled) => `No se pudo ${enabled ? 'activar' : 'desactivar'} "${name}"`, + newSubscription: 'Nueva suscripción', + restarting: 'Reiniciando la puerta de enlace...', + restartNeeded: 'Los webhooks están activados, pero la puerta de enlace aún necesita un reinicio antes de que el receptor pueda conectarse.', + restartGateway: 'Reiniciar puerta de enlace', + restartingGateway: 'Reiniciando...', + restartFailed: detail => `Fallo al reiniciar la puerta de enlace${detail}`, + enabledRestarting: 'Webhooks activados; reiniciando la puerta de enlace...', + all: '(todos)', + deliverOnly: 'solo entrega', + createdTitle: 'Suscripción creada', + createdSecretHint: 'Copia el secreto ahora — solo se muestra una vez.', + webhookUrl: 'URL del webhook', + secretOnce: 'Secreto (se muestra una vez)', + done: 'Hecho', + fieldName: 'Nombre', + fieldNamePlaceholder: 'p. ej. github-push', + fieldDescription: 'Descripción', + fieldDescriptionPlaceholder: 'Qué hace este webhook (opcional)', + fieldEvents: 'Eventos', + fieldEventsPlaceholder: 'separados por comas, vacío para todos', + fieldSkills: 'Habilidades', + fieldSkillsPlaceholder: 'nombres de habilidades separados por comas (opcional)', + fieldDeliver: 'Entregar a', + fieldDeliverOnly: 'Entregar solo el payload', + fieldPrompt: 'Prompt', + fieldPromptPlaceholder: 'Instrucciones para el agente cuando este webhook se dispare (opcional)', + nameRequired: 'El nombre es obligatorio', + create: 'Crear', + creating: 'Creando...', + created: 'Creado', + createFailed: detail => `No se pudo crear: ${detail}`, + copy: 'Copiar', + deliverOptions: { + log: 'Registro', + telegram: 'Telegram', + discord: 'Discord', + slack: 'Slack', + email: 'Email', + github_comment: 'Comentario de GitHub' + } + }, + profiles: { + close: 'Cerrar perfiles', + nameHint: 'Letras minúsculas, dígitos, guiones y guiones bajos. Debe empezar con una letra o dígito.', + title: 'Perfiles', + count: count => `${count} ${count === 1 ? 'perfil' : 'perfiles'}`, + search: 'Buscar perfiles...', + loading: 'Cargando perfiles...', + newProfile: 'Nuevo perfil', + importProfile: 'Importar perfil…', + exportProfile: 'Exportar perfil…', + imported: 'Perfil importado', + exported: 'Perfil exportado', + failedImport: 'No se pudo importar el perfil', + failedExport: 'No se pudo exportar el perfil', + allProfiles: 'Todos los perfiles', + showAllProfiles: 'Mostrar todos los perfiles', + switchToProfile: name => `Cambiar a ${name}`, + manageProfiles: 'Gestionar perfiles…', + actions: 'Acciones', + color: 'Color…', + colorFor: 'Color', + setColor: color => `Establecer color ${color}`, + autoColor: 'Automático', + noProfiles: 'Aún no hay perfiles.', + selectPrompt: 'Selecciona un perfil para ver sus detalles.', + refresh: 'Actualizar perfiles', + refreshing: 'Actualizando perfiles', + default: 'predeterminado', + skills: count => `${count} ${count === 1 ? 'habilidad' : 'habilidades'}`, + env: 'env', + defaultBadge: 'Predeterminado', + rename: 'Renombrar', + renameMenu: 'Renombrar…', + editSoul: 'Editar SOUL.md…', + copySetup: 'Copiar configuración', + copying: 'Copiando...', + modelLabel: 'Modelo', + skillsLabel: 'Habilidades', + notSet: 'Sin establecer', + soulDesc: 'El prompt del sistema y las instrucciones de persona integradas en este perfil.', + soulOptional: 'opcional', + soulPlaceholder: mode => `El prompt del sistema / persona para este perfil.\nDéjalo vacío para mantener el ${mode} predeterminado.`, + soulPlaceholderCloned: 'clonado', + soulPlaceholderEmpty: 'vacío', + unsavedChanges: 'Cambios sin guardar', + loadingSoul: 'Cargando SOUL.md...', + emptySoul: 'SOUL.md vacío — empieza a escribir la persona...', + saving: 'Guardando...', + saveSoul: 'Guardar SOUL.md', + deleteTitle: '¿Eliminar el perfil?', + deleteDescPrefix: 'Esto eliminará ', + deleteDescMid: ' y quitará su ', + deleteDescSuffix: ' directorio. Esto no se puede deshacer.', + deleting: 'Eliminando...', + createDesc: 'Los perfiles son entornos Hermes independientes: configuración, habilidades y SOUL.md separados.', + nameLabel: 'Nombre', + cloneFrom: 'Clonar desde', + cloneFromNone: 'Ninguno (vacío)', + cloneFromDesc: 'Copia la configuración, las habilidades y el SOUL.md del perfil fuente seleccionado.', + cloneFromDefault: 'Clonar desde el predeterminado', + cloneFromDefaultDesc: 'Copia la configuración, las habilidades y el SOUL.md de tu perfil predeterminado.', + invalidName: hint => `Nombre no válido. ${hint}`, + nameRequired: 'El nombre es obligatorio.', + creating: 'Creando...', + createAction: 'Crear perfil', + renameTitle: 'Renombrar perfil', + renameDescPrefix: 'Renombrar actualiza el directorio del perfil y cualquier script envoltorio en ', + renameDescSuffix: '.', + newNameLabel: 'Nuevo nombre', + renaming: 'Renombrando...', + created: 'Perfil creado', + renamed: 'Perfil renombrado', + deleted: 'Perfil eliminado', + setupCopied: 'Comando de configuración copiado', + soulSaved: 'SOUL.md guardado', + failedLoad: 'No se pudieron cargar los perfiles', + failedDelete: 'No se pudo eliminar el perfil', + failedCopy: 'No se pudo copiar el comando de configuración', + failedLoadSoul: 'No se pudo cargar SOUL.md', + failedSaveSoul: 'No se pudo guardar SOUL.md', + failedCreate: 'No se pudo crear el perfil', + failedRename: 'No se pudo renombrar el perfil' + }, + cron: { + close: 'Cerrar cron', + title: 'Tareas programadas', + count: count => `${count} ${count === 1 ? 'tarea' : 'tareas'}`, + modelImpact: { + title: 'Las tareas programadas requieren revisión', + message: count => + `${count} tarea${count === 1 ? '' : 's'} programada${count === 1 ? '' : 's'} se omitirá${count === 1 ? '' : 'n'} hasta que revises su configuración de modelo.`, + detailMore: (names, remaining) => `${names} y ${remaining} más`, + review: 'Revisar tareas programadas', + saveFailed: 'Hermes no guardó ese cambio de modelo.' + }, + search: 'Buscar tareas cron...', + loading: 'Cargando tareas cron...', + states: { + enabled: 'activada', + scheduled: 'programada', + running: 'en ejecución', + paused: 'en pausa', + disabled: 'desactivada', + error: 'error', + completed: 'completada' + }, + deliveryLabels: { + local: 'Este escritorio', + telegram: 'Telegram', + discord: 'Discord', + slack: 'Slack', + email: 'Email' + }, + scheduleLabels: { + daily: 'Diaria', + weekdays: 'Entre semana', + weekly: 'Semanal', + monthly: 'Mensual', + hourly: 'Cada hora', + 'every-15-minutes': 'Cada 15 minutos', + custom: 'Personalizada' + }, + scheduleHints: { + daily: 'Todos los días a las 9:00', + weekdays: 'De lunes a viernes a las 9:00', + weekly: 'Todos los lunes a las 9:00', + monthly: 'El primer día de cada mes a las 9:00', + hourly: 'Al inicio de cada hora', + 'every-15-minutes': 'Cada 15 minutos', + custom: 'Sintaxis cron o lenguaje natural' + }, + days: { + '0': 'Domingo', + '1': 'Lunes', + '2': 'Martes', + '3': 'Miércoles', + '4': 'Jueves', + '5': 'Viernes', + '6': 'Sábado', + '7': 'Domingo' + }, + dayFallback: value => `día ${value}`, + everyDayAt: time => `Todos los días a las ${time}`, + weekdaysAt: time => `Entre semana a las ${time}`, + everyDayOfWeekAt: (day, time) => `Todos los ${day} a las ${time}`, + monthlyOnDayAt: (dayOfMonth, time) => `Mensual el día ${dayOfMonth} a las ${time}`, + topOfHour: 'Al inicio de cada hora', + everyHourAt: minute => `Cada hora a las :${minute}`, + newCron: 'Nueva tarea cron', + emptyDescNew: 'Programa un prompt para que se ejecute con una expresión cron. Hermes lo ejecutará y entregará los resultados al destino que elijas.', + emptyDescSearch: 'Prueba una consulta de búsqueda más amplia.', + emptyTitleNew: 'Aún no hay tareas programadas', + emptyTitleSearch: 'Sin coincidencias', + last: 'Última:', + next: 'Próxima:', + noRuns: 'Sin ejecuciones aún', + manage: 'Gestionar', + showRuns: 'Mostrar ejecuciones', + hideRuns: 'Ocultar ejecuciones', + runHistory: 'Historial de ejecuciones', + actionsTitle: 'Acciones de la tarea cron', + resume: 'Reanudar cron', + pause: 'Pausar cron', + resumeTitle: 'Reanudar', + pauseTitle: 'Pausar', + triggerNow: 'Ejecutar ahora', + edit: 'Editar cron', + deleteTitle: '¿Eliminar la tarea cron?', + deleteDescPrefix: 'Esto eliminará ', + deleteDescSuffix: ' permanentemente. Dejará de ejecutarse de inmediato.', + deleting: 'Eliminando...', + resumed: 'Cron reanudado', + paused: 'Cron pausado', + triggered: 'Cron ejecutado', + deleted: 'Cron eliminado', + created: 'Cron creado', + updated: 'Cron actualizado', + failedLoad: 'No se pudieron cargar las tareas cron', + failedUpdate: 'No se pudo actualizar la tarea cron', + failedTrigger: 'No se pudo ejecutar la tarea cron', + failedDelete: 'No se pudo eliminar la tarea cron', + failedSave: 'No se pudo guardar la tarea cron', + editTitle: 'Editar tarea cron', + createTitle: 'Nueva tarea cron', + editDesc: 'Actualiza el horario, el prompt o el destino de entrega. Los cambios se aplican en la próxima ejecución.', + createDesc: 'Programa un prompt para que se ejecute automáticamente. Usa sintaxis cron o una frase natural como «cada 15 minutos».', + nameLabel: 'Nombre', + namePlaceholder: 'Resumen matutino', + promptLabel: 'Prompt', + promptPlaceholder: 'Resume mis hilos de Slack sin leer y envíame por email los 5 principales...', + frequencyLabel: 'Frecuencia', + deliverLabel: 'Entregar a', + deliverNeedsHomeChannel: 'establece primero un canal principal', + modelLabel: 'Modelo', + modelDefault: 'Predeterminado (modelo global)', + customScheduleLabel: 'Horario personalizado', + customPlaceholder: '0 9 * * * o entre semana a las 9', + customHint: 'Expresión cron, o frases como «cada hora» o «entre semana a las 9».', + optional: 'Opcional', + promptRequired: 'El prompt es obligatorio.', + promptScheduleRequired: 'El prompt y el horario son obligatorios.', + scheduleRequired: 'El horario es obligatorio.', + scriptOnlyEditHint: 'Tarea solo de script (sin prompt de IA). Id de tarea:', + saveChanges: 'Guardar cambios', + createAction: 'Crear cron', + tabs: { + jobs: 'Tareas', + blueprints: 'Plantillas' + }, + blueprints: { + tab: 'Plantillas', + startFrom: 'Empezar desde', + custom: 'Personalizada', + subtitle: 'Automatizaciones listas', + dialogDesc: 'Rellena los detalles y prográmala.', + scheduleIt: 'Programarla', + scheduling: 'Programando...', + scheduled: 'Plantilla programada', + loading: 'Cargando plantillas...', + failedLoad: 'No se pudieron cargar las plantillas', + emptyTitle: 'No hay plantillas disponibles', + emptyDesc: 'No hay plantillas de automatización disponibles en este backend.' + } + }, + artifacts: { + search: 'Buscar artefactos...', + refresh: 'Actualizar artefactos', + refreshing: 'Actualizando artefactos', + indexing: 'Indexando artefactos recientes de sesiones', + tabAll: 'Todo', + tabImages: 'Imágenes', + tabFiles: 'Archivos', + tabLinks: 'Enlaces', + noArtifactsTitle: 'No se encontraron artefactos', + noArtifactsDesc: 'Las imágenes generadas y las salidas de archivo aparecerán aquí a medida que las sesiones las produzcan.', + failedLoad: 'No se pudieron cargar los artefactos', + openFailed: 'Fallo al abrir', + itemsImage: 'imágenes', + itemsLink: 'enlaces', + itemsFile: 'archivos', + itemsGeneric: 'elementos', + zero: '0', + rangeOf: (start, end, total) => `${start}-${end} de ${total}`, + goToPage: (itemLabel, page) => `Ir a la página ${page} de ${itemLabel}`, + colTitleLink: 'Título del enlace', + colTitleFile: 'Nombre', + colTitleDefault: 'Título / nombre', + colLocationLink: 'URL', + colLocationFile: 'Ruta', + colLocationDefault: 'Ubicación', + colSession: 'Sesión', + kindImage: 'imagen', + kindFile: 'archivo', + kindLink: 'enlace', + chat: 'Chat', + copyUrl: 'Copiar URL', + copyPath: 'Copiar ruta' + }, + artifactCard: { + kind: { code: 'Código', html: 'Página interactiva', svg: 'Gráfico' }, + generating: lines => `Generando… ${lines} líneas`, + versionBadge: count => `${count} versiones`, + open: 'Abrir' + }, + artifactPreview: { + versionOf: (current, total) => `v${current} de ${total}`, + olderVersion: 'Versión anterior', + newerVersion: 'Versión más reciente', + latest: 'Última', + copyContent: 'Copiar contenido', + download: 'Descargar', + openInBrowser: 'Abrir en el navegador', + openInBrowserFailed: 'No se pudo abrir en el navegador', + missingTitle: 'Artefacto no disponible', + missingBody: 'Este artefacto ya no está en el registro local.' + }, + sidebar: { + nav: { + 'new-session': 'Nueva sesión', + skills: 'Capacidades', + messaging: 'Mensajería', + artifacts: 'Artefactos', + cron: 'Tareas programadas' + }, + searchAria: 'Buscar sesiones', + searchPlaceholder: 'Buscar sesiones…', + clearSearch: 'Limpiar búsqueda', + noMatch: query => `Ninguna sesión coincide con "${query}".`, + results: 'Resultados', + pinned: 'Fijadas', + sessions: 'Sesiones', + cronJobs: 'Tareas cron', + groupAriaGrouped: 'Mostrar sesiones como una sola lista', + groupAriaUngrouped: 'Agrupar sesiones por espacio de trabajo', + showProjects: 'Mostrar proyectos', + showSessions: 'Mostrar sesiones', + groupTitleGrouped: 'Desagrupar sesiones', + groupTitleUngrouped: 'Agrupar por espacio de trabajo', + allPinned: 'Todo aquí está fijado. Desfija un chat para mostrarlo en recientes.', + shiftClickHint: 'Shift-clic en un chat para fijarlo', + noWorkspace: 'Sin espacio de trabajo', + projectEmpty: 'Aún no hay sesiones', + noSessions: 'Aún no hay sesiones', + noFilterMatches: 'Ninguna sesión coincide con estos filtros', + projects: { + sectionLabel: 'Proyectos', + home: 'Inicio', + newButton: 'Nuevo proyecto', + createTitle: 'Nuevo proyecto', + createDesc: 'Pon nombre a un espacio de trabajo y añade una o más carpetas.', + renameTitle: 'Renombrar proyecto', + addFolderTitle: 'Añadir carpeta', + namePlaceholder: 'p. ej. Skunkworks', + foldersLabel: 'Carpetas', + ideaLabel: 'Idea', + ideaPlaceholder: '¿De qué trata este proyecto? (se guarda en IDEA.md)', + ideaGenerate: 'Generar idea', + ideaGenerating: 'Generando…', + ideaShuffle: 'Barajar plantillas', + noFolders: 'Aún no hay carpetas añadidas.', + addFolder: 'Añadir carpeta', + primaryBadge: 'principal', + removeFolder: 'Eliminar', + create: 'Crear', + menu: 'Acciones', + menuRename: 'Renombrar', + menuAppearance: 'Apariencia', + noColor: 'Sin color', + menuAddFolder: 'Añadir carpeta', + menuSetActive: 'Establecer activo', + menuDelete: 'Eliminar', + moveToProject: 'Mover a proyecto', + movedTo: name => `Movido a ${name}`, + moveFailed: 'No se pudo mover la sesión', + moveNoFolder: 'Ese proyecto no tiene carpeta a la que mover', + moveNoProjects: 'No hay otros proyectos', + reveal: 'Revelar en carpeta', + copyPath: 'Copiar ruta', + removeFromSidebar: 'Ocultar de la barra lateral', + createFailed: 'No se pudo crear el proyecto', + staleBackend: 'Actualiza el backend de Hermes para crear proyectos — tu backend es más antiguo que esta app de escritorio (Ajustes → Actualizaciones → Backend).', + deleteConfirm: 'Esto elimina el proyecto guardado de Hermes. Los archivos, repos git y worktrees permanecen intactos.', + startWork: 'Nuevo worktree', + newWorktreeTitle: 'Nuevo worktree', + newWorktreeDesc: 'Pon nombre a la rama para este worktree.', + branchPlaceholder: 'p. ej. mi-funcion', + branchOff: () => ({ after: '', before: 'rama desde ' }), + baseBranchPlaceholder: 'Buscar ramas…', + baseBranchNone: 'No se encontraron ramas', + startWorkFailed: 'No se pudo crear el worktree', + worktreeProjectLabel: 'Proyecto', + worktreeProjectPlaceholder: 'Buscar proyectos…', + worktreeProjectNone: 'Ningún proyecto con carpeta', + convertBranch: 'Convertir una rama…', + convertBranchTitle: 'Convertir una rama', + convertBranchDesc: 'Abrir ramas con checkout, o crear un worktree para una rama libre.', + convertBranchPlaceholder: 'Buscar ramas…', + convertBranchInstead: 'Convertir una rama existente', + branchOpenExisting: 'abrir', + branchSwitchHome: 'cambiar a inicio', + branchCreateWorktree: 'nuevo worktree', + branchTrackRemote: 'seguir remota', + branchesLoading: 'Cargando ramas…', + noBranches: 'No se encontraron ramas', + removeWorktree: 'Eliminar worktree', + removeWorktreeFailed: 'No se pudo eliminar el worktree (¿cambios sin confirmar?)', + removeWorktreeConfirm: 'Eliminarlo de git (borra el directorio del worktree; la rama se queda), o solo ocultar el carril de la barra lateral y dejar el worktree en disco.', + removeWorktreeDirty: 'Este worktree tiene cambios sin confirmar. Forzar su eliminación (descarta esos cambios), o solo ocultar el carril y conservarlo en disco.', + forceRemove: 'Forzar eliminación', + enter: label => `Abrir ${label}`, + reorder: label => `Reordenar ${label}`, + toggle: (label, open) => `${open ? 'Mostrar' : 'Ocultar'} sesiones de ${label}`, + back: 'Todos los proyectos' + }, + newSessionIn: label => `Nueva sesión en ${label}`, + showMoreIn: (count, label) => `Mostrar ${count} más en ${label}`, + loading: 'Cargando…', + loadMore: 'Cargar más', + loadCount: step => `Cargar ${step} más`, + row: { + pin: 'Fijar', + unpin: 'Desfijar', + copyId: 'Copiar ID', + export: 'Exportar', + branchFrom: 'Rama', + rename: 'Renombrar', + archive: 'Archivar', + newWindow: 'Nueva ventana', + hideTabBar: 'Ocultar barra de pestañas', + openInNewTab: 'Abrir en pestaña nueva', + openInSplit: 'Abrir en división', + copyIdFailed: 'No se pudo copiar el ID de sesión', + sessionActions: 'Acciones de sesión', + sessionRunning: 'Sesión en ejecución', + needsInput: 'Necesita tu entrada', + waitingForAnswer: 'Esperando tu respuesta', + finishedUnread: 'Terminada — sin leer', + backgroundRunning: 'Tarea en segundo plano en ejecución', + draftSession: 'Borrador — aún no se ha enviado nada', + handoffOrigin: platform => `Transferido desde ${platform}`, + ownedByProfile: profile => `Perfil: ${profile}`, + renamed: 'Renombrada', + renameFailed: 'Fallo al renombrar', + renameTitle: 'Renombrar sesión', + renameDesc: 'Déjalo vacío para limpiarlo.', + untitledPlaceholder: 'Sesión sin título', + untitledChat: id => `Chat ${id}`, + messageCount: count => `${count} ${count === 1 ? 'mensaje' : 'mensajes'}`, + todoProgress: 'Tareas completadas', + ageNow: 'ahora', + ageDay: 'd', + ageHour: 'h', + ageMin: 'm' + }, + dateDivider: { + today: 'Hoy más temprano', + yesterday: 'Ayer', + thisWeek: 'Esta semana más temprano', + lastWeek: 'La semana pasada', + thisMonth: 'Este mes más temprano' + }, + statusDivider: { + working: 'En progreso', + done: 'Completado' + } + }, + composer: { + message: 'Mensaje', + wakingProfile: profile => `Despertando ${profile}…`, + placeholderStarting: 'Iniciando Hermes...', + placeholderReconnecting: 'Reconectando con Hermes…', + placeholderFollowUp: 'Enviar seguimiento', + newSessionPlaceholders: [ + '¿Qué estamos construyendo?', + 'Dale una tarea a Hermes', + '¿Qué tienes en mente?', + 'Describe lo que necesitas', + '¿Qué deberíamos abordar?', + 'Pregunta lo que quieras', + 'Empieza con un objetivo' + ], + followUpPlaceholders: [ + 'Enviar un seguimiento', + 'Añadir más contexto', + 'Refinar la petición', + '¿Qué sigue?', + 'Sigue así', + 'Empújalo más lejos', + 'Ajusta o continúa' + ], + startVoice: 'Iniciar conversación por voz', + openDirective: 'Abrir', + queueMessage: 'Poner mensaje en cola', + steer: 'Redirigir la ejecución actual', + stop: 'Detener', + send: 'Enviar', + speaking: 'Hablando', + transcribing: 'Transcribiendo', + thinking: 'Pensando', + muted: 'Silenciado', + listening: 'Escuchando', + muteMic: 'Silenciar micrófono', + unmuteMic: 'Activar micrófono', + stopListening: 'Dejar de escuchar y enviar', + stopShort: 'Detener', + endConversation: 'Terminar conversación por voz', + endShort: 'Terminar', + stopDictation: 'Detener dictado', + transcribingDictation: 'Transcribiendo dictado', + voiceDictation: 'Dictado por voz', + speakReplies: 'Leer respuestas en voz alta', + stopSpeakingReplies: 'Dejar de leer respuestas en voz alta', + wakeWordListening: phrase => `Palabra de activación: "${phrase}" — escuchando`, + wakeWordOff: phrase => `Palabra de activación: "${phrase}" — desactivada`, + wakeWordPausedVoice: phrase => `Palabra de activación: "${phrase}" — pausada durante el chat de voz`, + lookupLoading: 'Buscando…', + lookupNoMatches: 'Sin coincidencias.', + lookupTry: 'Prueba', + lookupOr: 'o', + commonCommands: 'Comandos comunes', + hotkeys: 'Atajos de teclado', + helpFooter: 'abre el panel completo · retroceso descarta', + commandDescs: { + '/help': 'lista completa de comandos + atajos', + '/clear': 'iniciar una nueva sesión', + '/resume': 'reanudar una sesión anterior', + '/details': 'controlar el nivel de detalle del transcript', + '/copy': 'copiar la selección o el último mensaje del asistente', + '/quit': 'salir de hermes' + }, + hotkeyDescs: { + 'composer.mention': 'referenciar archivos, carpetas, urls, git', + 'composer.slash': 'paleta de comandos con barra', + 'composer.help': 'esta ayuda rápida (suprimir para descartar)', + 'composer.sendNewline': 'enviar · Shift+Enter para salto de línea', + 'composer.sendQueued': 'enviar el siguiente turno en cola', + 'keybinds.openPanel': 'todos los atajos de teclado', + 'composer.cancel': 'cerrar ventana emergente · cancelar ejecución', + 'composer.history': 'alternar ventana emergente / historial' + }, + attachUrlTitle: 'Adjuntar una URL', + attachUrlDesc: 'Hermes obtendrá la página y la incluirá como contexto para este turno.', + urlPlaceholder: 'https://example.com/post', + urlHintPre: 'Incluye la URL completa, p. ej. ', + attach: 'Adjuntar', + queued: count => `${count} En cola`, + queuedPaused: count => `${count} En cola — pausado`, + attachmentOnly: 'Turno solo de adjunto', + emptyTurn: 'Turno vacío', + attachments: count => `${count} adjunto${count === 1 ? '' : 's'}`, + editingInComposer: 'Editando en el compositor', + editingQueuedInComposer: 'Editando turno en cola en el compositor', + queueEdit: 'Editar', + queueSendNext: 'Siguiente', + queueSteer: 'Redirigir — encauzar el turno en vivo ahora', + queueSend: 'Enviar', + queueDelete: 'Eliminar', + queueResume: 'Reanudar', + queueResumeTip: 'Pausado por Detener — reanuda el envío de los turnos en cola', + queueStuckTitle: 'Mensaje en cola no enviado', + queueStuckBody: 'Un turno en cola seguía fallando al enviarse. Sigue en la cola — intenta enviarlo de nuevo.', + previewUnavailable: 'Vista previa no disponible', + previewLabel: label => `Vista previa de ${label}`, + couldNotPreview: label => `No se pudo previsualizar ${label}`, + removeAttachment: label => `Quitar ${label}`, + dictating: 'Dictando', + preparingAudio: 'Preparando audio', + speakingResponse: 'Hablando la respuesta', + readingAloud: 'Leyendo en voz alta', + themeSuggestions: 'Sugerencias de tema del escritorio', + noMatchingThemes: 'Sin temas coincidentes.', + themeTryPre: 'Prueba ', + themeTryPost: '.', + attachLabel: 'Adjuntar', + files: 'Archivos…', + folder: 'Carpeta…', + images: 'Imágenes…', + pasteImage: 'Pegar imagen', + url: 'URL…', + promptSnippets: 'Fragmentos de prompt…', + tipPre: 'Consejo: escribe ', + tipPost: ' para referenciar archivos en línea.', + snippetsTitle: 'Fragmentos de prompt', + snippetsDesc: 'Elige un prompt inicial para insertarlo en el compositor.', + dropFiles: 'Suelta archivos para adjuntar', + dropSession: 'Suelta para enlazar este chat', + mcpSuggestions: { + label: server => `Añadir ${server}`, + tip: keyword => `Sugerido porque mencionaste "${keyword}" — haz clic para conectar`, + connecting: server => `Conectando ${server}…`, + cancelTip: 'Haz clic para cancelar', + added: server => `${server} añadido`, + addedTip: 'Conectado — sus herramientas están listas en este chat', + connectFailed: server => `No se pudo conectar ${server}` + }, + skillSuggestions: { + label: skill => `Usar habilidad: ${skill}`, + tip: skill => `Mencionaste "${skill}" — haz clic para empezar con esa habilidad`, + done: skill => `Añadido /${skill}`, + doneTip: 'La habilidad se carga cuando envíes' + }, + repairSuggestions: { + label: server => `Reconectar ${server}`, + tip: server => `Una llamada a ${server} acaba de fallar con un error de conexión`, + working: server => `Reconectando ${server}…`, + workingTip: 'Haz clic para cancelar', + done: server => `${server} reconectado`, + doneTip: 'Las credenciales nuevas están activas en este chat', + failed: server => `No se pudo reconectar ${server}` + }, + cronSuggestions: { + label: 'Programar esto', + tip: phrase => `"${phrase}" suena recurrente — ejecútalo con un horario en su lugar`, + prefix: 'Configúralo como tarea programada:', + done: 'Marcado para programar', + doneTip: 'Envíalo y el agente creará la tarea' + }, + snippets: { + codeReview: { + label: 'Revisión de código', + description: 'Audita el cambio actual en busca de regresiones, casos límite omitidos y tests faltantes.', + text: 'Por favor revisa esto en busca de bugs, regresiones y tests faltantes.' + }, + implementationPlan: { + label: 'Plan de implementación', + description: 'Esboza un enfoque antes de tocar el código para que el diff se mantenga enfocado.', + text: 'Por favor haz un plan de implementación conciso antes de cambiar el código.' + }, + explainThis: { + label: 'Explícame esto', + description: 'Recorre cómo funciona el código seleccionado y enlaza los archivos clave.', + text: 'Por favor explícame cómo funciona esto y apúntame a los archivos clave.' + } + } + }, + statusStack: { + agents: 'Agentes', + background: count => `${count} En segundo plano`, + goalActive: 'Objetivo activo', + goalDone: 'Objetivo completado', + goalPaused: 'Objetivo en pausa', + goalWaiting: 'Objetivo esperando', + subagents: count => `${count} Subagente${count === 1 ? '' : 's'}`, + todos: (done, total) => `Tareas ${done}/${total}`, + running: 'En ejecución', + stop: 'Detener', + dismiss: 'Descartar', + exit: code => `salida ${code}`, + coding: { + title: 'Árbol de trabajo', + noBranch: 'Sin rama', + detached: 'desacoplado', + clean: 'Limpio', + changed: count => `${count} cambiado${count === 1 ? '' : 's'}`, + ahead: count => `${count} por delante`, + behind: count => `${count} por detrás`, + review: 'Revisar', + close: 'Cerrar', + openChanges: 'Abrir cambios', + openFile: 'Abrir archivo', + stage: 'Ensayar', + unstage: 'Desensayar', + stageAll: 'Ensayar todo', + viewAsTree: 'Ver como árbol', + viewAsList: 'Ver como lista', + revert: 'Revertir', + revertAll: 'Revertir todo', + revertConfirm: '¿Descartar los cambios de este archivo y restaurarlo al estado confirmado? Esto no se puede deshacer.', + revertAllConfirm: '¿Descartar todos los cambios y restaurar los archivos al estado confirmado? Esto no se puede deshacer.', + staged: 'Ensayados', + noChanges: 'Sin cambios', + notRepo: 'No es un repositorio git', + noDiff: 'No hay diff que mostrar', + scopeUncommitted: 'Sin confirmar', + scopeBranch: 'Rama', + scopeLastTurn: 'Último turno', + commit: 'Confirmar', + commitAndPush: 'Confirmar y enviar', + commitPlaceholder: 'Mensaje (⌘↵ para confirmar)', + generateCommitMessage: 'Generar mensaje de confirmación', + stopGenerating: 'Dejar de generar', + createPr: 'Crear PR', + openPr: 'Abrir PR', + ghMissing: 'Instala la CLI de GitHub (gh) e inicia sesión para abrir PRs', + agentShip: 'Pedir a Hermes que abra el PR', + agentShipPrompt: 'Revisa los cambios actuales, confírmalos con un mensaje de commit convencional claro, envía la rama y abre un pull request.', + newBranch: 'Nueva rama', + branchOffFrom: base => `Nueva rama desde ${base}`, + switchTo: branch => `Cambiar a ${branch}`, + switchFailed: branch => `No se pudo cambiar a ${branch}`, + worktrees: 'Worktrees' + } + }, + updates: { + stages: { + idle: 'Preparando…', + prepare: 'Preparando…', + fetch: 'Descargando…', + pull: 'Casi listo…', + pydeps: 'Terminando…', + update: 'Actualizando Hermes…', + rebuild: 'Reconstruyendo la app de escritorio…', + restart: 'Reiniciando Hermes…', + done: 'Actualización completada', + manual: 'Actualizar desde tu terminal', + guiSkew: 'Actualizar la app de escritorio', + error: 'Actualización en pausa' + }, + checking: 'Buscando actualizaciones…', + checkFailedTitle: 'No se pudieron buscar actualizaciones', + tryAgain: 'Intentar de nuevo', + notAvailableTitle: 'Actualización no disponible', + unsupportedMessage: 'Esta versión de Hermes no puede actualizarse desde la app.', + connectionRetry: 'Comprueba tu conexión e inténtalo de nuevo.', + latestBody: 'Estás ejecutando la última versión.', + latestBodyBackend: 'El backend está ejecutando la última versión.', + allSetTitle: 'Todo listo', + availableTitle: 'Nueva actualización disponible', + availableBody: 'Una nueva versión de Hermes está lista para instalar.', + availableTitleBackend: 'Actualización de backend disponible', + availableBodyBackend: 'Una versión más reciente del backend de Hermes conectado está lista para instalar.', + availableBodyNoChangelog: 'Una versión más reciente está lista. Las notas de la versión no están disponibles para este tipo de instalación.', + updateNow: 'Actualizar ahora', + maybeLater: 'Quizás más tarde', + moreChanges: count => `+ ${count} cambio${count === 1 ? '' : 's'} más incluido${count === 1 ? '' : 's'}.`, + manualTitle: 'Actualizar desde tu terminal', + manualBody: 'Instalaste Hermes desde la línea de comandos, así que las actualizaciones también se ejecutan allí. Pega esto en tu terminal:', + manualPickedUp: 'Hermes tomará la nueva versión la próxima vez que lo inicies.', + guiSkewTitle: 'Actualizar la app de escritorio', + guiSkewBody: 'El backend se actualizó, pero este paquete de la app de escritorio no cambió. Actualiza o reinstala la app de escritorio de Hermes (tu AppImage / .deb / .rpm) para que coincidan.', + copy: 'Copiar', + copied: 'Copiado', + done: 'Hecho', + applyingBody: 'El actualizador de Hermes toma el control en su propia ventana y reabre Hermes automáticamente cuando termina. Por favor, no reabras Hermes tú mismo mientras se actualiza.', + applyingBodyBackend: 'El backend remoto está aplicando la actualización y se reiniciará. Hermes se reconecta automáticamente cuando vuelva.', + applyingClose: 'Esta ventana se cerrará mientras se ejecuta la actualización, y luego Hermes se reabre solo.', + errorTitle: 'La actualización no terminó', + errorBody: 'No te preocupes — no se perdió nada. Puedes intentarlo de nuevo ahora.', + notNow: 'Ahora no', + applyStatus: { + preparing: 'Actualizando backend…', + pulling: 'Backend actualizándose…', + restarting: 'Backend reiniciándose para cargar la actualización…', + notAvailable: 'Actualización no disponible para este backend.', + failed: 'Fallo la actualización del backend.', + noReturn: 'El backend no volvió a conectarse. La actualización puede no haberse completado — comprueba el host del backend.' + } + }, + install: { + stageStates: { + pending: 'Pendiente', + running: 'Instalando', + succeeded: 'Hecho', + skipped: 'Omitido', + failed: 'Fallido' + }, + oneTimeTitle: 'Hermes necesita una instalación única', + unsupportedDesc: platform => + `La instalación automática del primer arranque no está disponible en ${platform} todavía. Abre Terminal y ejecuta el comando de abajo, y luego relanza esta app. Los lanzamientos posteriores omitirán este paso.`, + installCommand: 'Comando de instalación', + copyCommand: 'Copiar comando', + viewDocs: 'Ver documentación de instalación', + installTo: 'Se instalará en', + retryAfterRun: 'Ya lo ejecuté — reintentar', + setupChoiceTitle: 'Configurar Hermes Desktop', + setupChoiceDesc: 'Conecta esta app a una puerta de enlace Hermes que ya ejecutes, o instala Hermes localmente en este equipo.', + connectExistingTitle: 'Conectar a Hermes existente', + connectExistingShort: 'Conectar existente', + connectExistingDesc: 'Usa un backend remoto con token de sesión o inicio de sesión en navegador. No se iniciará ninguna instalación local.', + installLocalTitle: 'Instalar Hermes localmente', + installLocalDesc: 'Descarga Hermes, crea su entorno Python y ejecuta el backend en este equipo.', + localStartUnavailable: 'La instalación local no pudo iniciarse. Reinicia Hermes Desktop e inténtalo de nuevo.', + remoteSetupTitle: 'Conectar a Hermes existente', + remoteSetupDesc: 'Introduce la URL de tu puerta de enlace. Hermes Desktop detectará si necesita un token o inicio de sesión en navegador.', + remoteUrlTitle: 'URL de la puerta de enlace', + remoteUrlDesc: 'Usa la URL base de la puerta de enlace de Hermes, incluyendo https:// cuando sea remota.', + remoteUrlPlaceholder: 'https://gateway.example.com/hermes', + probing: 'Detectando autenticación de la puerta de enlace...', + probeError: 'No se pudo contactar con esa puerta de enlace de Hermes.', + identityProvider: 'tu proveedor de identidad', + authTitle: 'Autenticación', + authNeedsOauth: provider => `Inicia sesión con ${provider} antes de probar esta puerta de enlace.`, + authSignedIn: 'Inicio de sesión en navegador completado.', + connected: 'Conectado', + signIn: 'Iniciar sesión', + signInWith: provider => `Iniciar sesión con ${provider}`, + enterUrlFirst: 'Introduce primero una URL de puerta de enlace.', + signInIncomplete: 'La ventana de inicio de sesión se cerró antes de completar la autenticación.', + tokenTitle: 'Token de sesión', + tokenDesc: 'Pega el token de sesión del archivo .env de la puerta de enlace remota.', + pasteSessionToken: 'Pegar token de sesión', + incompleteSignInTest: 'Inicia sesión antes de probar esta puerta de enlace protegida por OAuth.', + incompleteTokenTest: 'Introduce un token de sesión antes de probar esta puerta de enlace.', + testConnection: 'Probar conexión', + testSucceeded: (baseUrl, version) => `Conectado a ${baseUrl}${version ? ` (${version})` : ''}.`, + applyRemote: 'Aplicar y reconectar', + backToSetup: 'Volver', + failedTitle: 'Instalación fallida', + settingUpTitle: 'Configurando Hermes Agent', + finishingTitle: 'Terminando', + failedDesc: 'Uno de los pasos de instalación falló. En Windows, esto puede pasar si otra instancia de CLI o escritorio de Hermes está en ejecución. Detén cualquier instancia de Hermes en ejecución y reintenta. Revisa los detalles abajo o el registro del escritorio para el transcript completo.', + activeDesc: 'Esta es una configuración única. El instalador de Hermes está descargando dependencias y configurando tu máquina. Los lanzamientos posteriores omitirán este paso.', + progress: (completed, total) => `${completed} de ${total} pasos completados`, + currentStage: stage => ` -- ahora: ${stage}`, + fetchingManifest: 'Obteniendo manifiesto del instalador...', + error: 'Error', + hideOutput: 'Ocultar salida del instalador', + showOutput: 'Mostrar salida del instalador', + lines: count => `${count} línea${count === 1 ? '' : 's'}`, + noOutput: 'Sin salida todavía.', + cancelling: 'Cancelando...', + cancelInstall: 'Cancelar instalación', + transcriptSaved: 'Transcript completo guardado en', + copiedOutput: '¡Copiado!', + copyOutput: 'Copiar salida', + reloadRetry: 'Recargar y reintentar' + }, + onboarding: { + headerTitle: 'Vamos a configurarte Hermes Agent', + headerDesc: 'Conecta un proveedor de modelos para empezar a chatear. La mayoría de opciones son de un clic.', + preparingInstall: 'Hermes está terminando la instalación. Esto suele tardar menos de un minuto en el primer arranque.', + starting: 'Iniciando Hermes…', + lookingUpProviders: 'Buscando proveedores...', + collapse: 'Contraer', + otherProviders: 'Otros proveedores', + haveApiKey: '¿Tienes una clave API?', + chooseLater: 'Elegiré un proveedor más tarde', + recommended: 'Recomendado', + connected: 'Conectado', + featuredPitch: 'Una suscripción, 300+ modelos punteros — la forma recomendada de ejecutar Hermes', + fireworksPitch: 'API de modelo directa — modelos punteros alojados en Fireworks', + openRouterPitch: 'Una clave, cientos de modelos — un predeterminado sólido', + apiKeyOptions: { + fireworks: { + short: 'API de modelo directa', + description: 'Acceso directo a modelos alojados por Fireworks AI.' + }, + openrouter: { + short: 'una clave, muchos modelos', + description: 'Aloja cientos de modelos detrás de una sola clave. Buen predeterminado para instalaciones nuevas.' + }, + openai: { short: 'Modelos clase GPT', description: 'Acceso directo a modelos de OpenAI.' }, + gemini: { short: 'Modelos Gemini', description: 'Acceso directo a modelos de Google Gemini.' }, + xai: { short: 'Modelos Grok', description: 'Acceso directo a modelos xAI Grok.' }, + local: { + short: 'autoalojado', + description: 'Apunta Hermes a un endpoint local o autoalojado compatible con OpenAI (vLLM, llama.cpp, Ollama, etc).' + } + }, + backToSignIn: 'Volver al inicio de sesión', + getKey: 'Obtener una clave', + replaceCurrent: 'Reemplazar valor actual', + pasteApiKey: 'Pegar clave API', + localApiKeyPlaceholder: 'Clave API (opcional — solo si tu endpoint lo requiere)', + couldNotSave: 'No se pudo guardar la credencial.', + connecting: 'Conectando', + update: 'Actualizar', + flowSubtitles: { + pkce: 'Abre tu navegador para iniciar sesión y luego continúa aquí', + device_code: 'Abre una página de verificación en tu navegador — Hermes se conecta automáticamente', + external: 'Inicia sesión una vez en tu terminal y luego vuelve a chatear' + }, + startingSignIn: provider => `Iniciando sesión para ${provider}...`, + verifyingCode: provider => `Verificando tu código con ${provider}...`, + connectedProvider: provider => `${provider} conectado`, + connectedPicking: provider => `${provider} conectado. Eligiendo un modelo predeterminado...`, + signInFailed: 'Fallo el inicio de sesión. Inténtalo de nuevo.', + pickDifferentProvider: 'Elegir un proveedor diferente', + signInWith: provider => `Iniciar sesión con ${provider}`, + openedBrowser: provider => `Abrimos ${provider} en tu navegador.`, + authorizeThere: 'Autoriza Hermes allí.', + copyAuthCode: 'Copia el código de autorización y pégalo abajo.', + pasteAuthCode: 'Pegar código de autorización', + reopenAuthPage: 'Reabrir página de autorización', + autoBrowser: provider => + `Abrimos ${provider} en tu navegador. Autoriza Hermes allí y te conectarás automáticamente — nada que copiar o pegar.`, + reopenSignInPage: 'Reabrir página de inicio de sesión', + waitingAuthorize: 'Esperando a que autorices...', + externalPending: provider => + `${provider} inicia sesión a través de su propio CLI. Ejecuta este comando en una terminal y luego vuelve y elige «He iniciado sesión»:`, + signedIn: 'He iniciado sesión', + deviceCodeOpened: provider => `Abrimos ${provider} en tu navegador. Introduce este código allí:`, + reopenVerification: 'Reabrir página de verificación', + copy: 'Copiar', + defaultModel: 'Modelo predeterminado', + freeTier: 'Nivel gratuito', + pro: 'Pro', + free: 'Gratis', + price: (input, output) => `${input} entrada / ${output} salida por Mtok`, + change: 'Cambiar', + startChatting: 'Empezar', + docs: provider => `Docs de ${provider}` + }, + modelPicker: { + title: 'Cambiar modelo', + current: 'actual:', + unknown: '(desconocido)', + search: 'Filtrar proveedores y modelos...', + noModels: 'No se encontraron modelos.', + addProvider: 'Añadir proveedor', + loadFailed: 'No se pudieron cargar los modelos', + noAuthenticatedProviders: 'No hay proveedores autenticados.', + pro: 'Pro', + proNeedsSubscription: 'Los modelos Pro necesitan una suscripción de Nous de pago.', + free: 'Gratis', + freeTier: 'Nivel gratuito', + priceTitle: 'Precio de entrada / salida por millón de tokens', + wasPrice: 'era' + }, + modelVisibility: { + title: 'Modelos', + search: 'Buscar modelos', + noAuthenticatedProviders: 'No hay proveedores autenticados.', + addProvider: 'Añadir proveedor…' + }, + shell: { + windowControls: 'Controles de ventana', + paneControls: 'Controles de panel', + appControls: 'Controles de la app', + modelMenu: { + search: 'Buscar modelos', + noModels: 'No se encontraron modelos', + editModels: 'Editar modelos…', + refreshModels: 'Actualizar modelos', + fast: 'Rápido' + }, + modelOptions: { + noOptions: 'Sin opciones para este modelo', + options: 'Opciones', + thinking: 'Pensamiento', + fast: 'Rápido', + effort: 'Esfuerzo', + minimal: 'Mínimo', + low: 'Bajo', + medium: 'Medio', + high: 'Alto', + xhigh: 'Muy alto', + max: 'Máximo', + ultra: 'Ultra', + updateFailed: 'Fallo al actualizar la opción del modelo', + fastFailed: 'Fallo al actualizar el modo rápido' + }, + gatewayMenu: { + gateway: 'Puerta de enlace', + connected: 'Conectado', + connecting: 'Conectando', + offline: 'Desconectado', + inferenceReady: 'Inferencia lista', + inferenceNotReady: 'Inferencia no lista', + checkingInference: 'Comprobando inferencia', + disconnected: 'Desconectado', + openSystem: 'Abrir panel del sistema', + connection: label => `Conexión: ${label}`, + recentActivity: 'Actividad reciente', + viewAllLogs: 'Ver todos los registros →', + messagingPlatforms: 'Plataformas de mensajería' + }, + approvalMode: { + title: 'Modo de aprobación', + ariaLabel: mode => `Modo de aprobación: ${mode}`, + manual: 'Manual', + manualDescription: 'Preguntar antes de acciones que requieran aprobación', + smart: 'Inteligente', + smartDescription: 'Evaluar acciones automáticamente y preguntar cuando sea necesario', + off: 'Desactivado', + offDescription: 'Ejecutar sin avisos de aprobación' + }, + statusbar: { + unknown: 'desconocido', + restart: 'reiniciar', + update: 'actualizar', + updateInProgress: 'Actualización en curso', + commitsBehind: (count, branch) => `${count} commit${count === 1 ? '' : 's'} por detrás de ${branch}`, + desktopVersion: version => `Hermes Desktop v${version}`, + backendVersion: version => `Backend v${version}`, + clientLabel: version => `cliente v${version}`, + connectionSsh: host => `SSH: ${host}`, + connectionRemote: host => `Remoto: ${host}`, + connectionCloud: host => `Cloud: ${host}`, + connectionCloudTooltip: host => `Hermes Cloud · ${host}`, + connectionSshTooltip: host => `SSH · ${host}`, + connectionRemoteTooltip: host => `Remoto · ${host}`, + backendLabel: version => `backend v${version}`, + commit: sha => `commit ${sha}`, + branch: branch => `rama ${branch}`, + closeCommandCenter: 'Cerrar Centro de comandos', + openCommandCenter: 'Abrir Centro de comandos', + showTerminal: 'Mostrar terminal', + hideTerminal: 'Ocultar terminal', + gateway: 'Puerta de enlace', + gatewayReady: 'lista', + gatewayNeedsSetup: 'requiere configuración', + gatewayChecking: 'comprobando', + gatewayConnecting: 'conectando', + gatewayOffline: 'desconectada', + gatewayRestarting: 'reiniciando…', + gatewayTitle: 'Puerta de enlace', + customizeTitle: 'Mostrar en la barra de estado', + hideStatusbar: 'Ocultar barra de estado', + resetStatusbar: 'Restablecer a valores predeterminados', + toggleApprovalMode: 'Aprobaciones', + toggleBackendVersion: 'Versión del backend', + toggleCommandCenter: 'Centro de comandos', + toggleContextUsage: 'Medidor de contexto', + toggleRunningTimer: 'Temporizador de turno', + toggleSessionTimer: 'Temporizador de sesión', + toggleTerminal: 'Terminal', + toggleVersion: 'Versión y actualizaciones', + toggleWorkspace: 'Espacio de trabajo', + agents: 'Agentes', + closeAgents: 'Cerrar agentes', + openAgents: 'Abrir agentes', + subagents: count => `${count} subagente${count === 1 ? '' : 's'}`, + failed: count => `${count} fallidos`, + running: count => `${count} en ejecución`, + cron: 'Cron', + openCron: 'Abrir tareas cron', + webhooks: 'Webhooks', + openWebhooks: 'Abrir webhooks', + starmap: 'Mapa de memoria', + openStarmap: 'Abrir mapa de memoria', + turnRunning: 'En ejecución', + contextUsage: 'Uso de contexto', + contextUsagePanel: { + categories: { + conversation: 'Conversación', + mcp: 'MCP', + memory: 'Memoria', + rules: 'Reglas', + skills: 'Habilidades', + subagent_definitions: 'Definiciones de subagentes', + system_prompt: 'Prompt del sistema', + tool_definitions: 'Definiciones de herramientas' + }, + empty: 'Aún no hay datos de contexto', + loading: 'Cargando desglose…', + percentFull: percent => `${percent}% Lleno`, + title: 'Uso de contexto', + tokenSummary: (used, max) => `${used} / ${max} Tokens` + }, + session: 'Sesión', + yoloOn: 'YOLO activado — autoaprobando comandos peligrosos. Shift+clic lo alterna globalmente.', + yoloOff: 'YOLO desactivado. Shift+clic lo alterna globalmente.', + modelNone: 'ninguno', + noModel: 'sin modelo', + switchModel: 'Cambiar modelo', + openModelPicker: 'Abrir selector de modelo', + modelPinned: 'fijado por ti; los chats nuevos usan este en lugar del predeterminado de Ajustes', + modelTitle: (provider, model) => `Modelo · ${provider}: ${model}`, + providerModelTitle: (provider, model) => `${provider} · ${model}` + } + }, + rightSidebar: { + aria: 'Barra lateral derecha', + panelsAria: 'Paneles de la barra lateral derecha', + files: 'Sistema de archivos', + terminal: 'Terminal', + noFolderSelected: 'Ninguna carpeta seleccionada', + changeCwdTitle: 'Cambiar directorio de trabajo', + remotePickerTitle: 'Elegir carpeta remota', + remotePickerDescription: 'Explora carpetas en el backend conectado.', + remotePickerSelect: 'Seleccionar carpeta', + folderTip: cwd => cwd, + openFolder: 'Abrir carpeta', + refreshTree: 'Actualizar árbol', + collapseAll: 'Contraer todas las carpetas', + previewUnavailable: 'Vista previa no disponible', + couldNotPreview: path => `No se pudo previsualizar ${path}`, + noProjectTitle: 'Sin proyecto', + noProjectBody: 'Abre un proyecto para explorar sus archivos y revisar cambios.', + noProjectOpen: 'Ningún proyecto abierto', + noDiffs: 'Sin diffs', + unreadableTitle: 'Ilegible', + unreadableBody: error => `No se pudo leer esta carpeta (${error}).`, + emptyTitle: 'Vacía', + emptyBody: 'Esta carpeta está vacía.', + treeErrorTitle: 'Error de árbol', + treeErrorBody: 'El árbol de archivos falló al renderizar esta carpeta.', + tryAgain: 'Intentar de nuevo', + loadingTree: 'Cargando árbol de archivos', + loadingFiles: 'Cargando archivos', + terminalHide: 'Ocultar terminal', + terminalsAria: 'Terminales', + terminalNew: 'Nueva terminal', + terminalCloseOthers: 'Cerrar otras', + terminalCloseAll: 'Cerrar todas', + addToChat: 'Añadir al chat' + }, + preview: { + tab: 'Vista previa', + closePane: 'Cerrar panel de vista previa', + loading: 'Cargando vista previa', + unavailable: 'Vista previa no disponible', + opening: 'Abriendo...', + hide: 'Ocultar', + openPreview: 'Abrir vista previa', + openInBrowser: 'Abrir en el navegador', + linkHint: '⌘/Ctrl-clic para el panel de vista previa', + sourceLineTitle: 'Clic para seleccionar · shift-clic para extender · arrastrar al compositor', + source: 'FUENTE', + renderedPreview: 'VISTA PREVIA', + diff: 'DIFF', + unknownSize: 'tamaño desconocido', + binaryTitle: 'Esto parece un archivo binario', + binaryBody: label => `Previsualizar ${label} puede mostrar texto ilegible.`, + largeTitle: 'Este archivo es grande', + largeBody: (label, size) => `${label} tiene ${size}. Hermes solo mostrará los primeros 512 KB.`, + previewAnyway: 'Previsualizar de todos modos', + truncated: 'Mostrando los primeros 512 KB.', + noInlineTitle: 'Sin vista previa en línea', + noInlineBody: mimeType => `${mimeType || 'Este tipo de archivo'} aún puede adjuntarse como contexto.`, + edit: 'Editar', + editing: 'Editando', + unsavedChanges: 'Cambios sin guardar', + saveFailed: message => `No se pudo guardar: ${message}`, + diskChangedTitle: 'El archivo cambió en disco', + diskChangedBody: 'Este archivo cambió desde que lo abriste. ¿Sobrescribirlo con tu versión, o descartar tus ediciones y recargar?', + overwrite: 'Sobrescribir', + discardReload: 'Descartar y recargar', + console: { + deselect: 'Deseleccionar entrada', + select: 'Seleccionar entrada', + copyFailed: 'No se pudo copiar la salida de consola', + copyEntry: 'Copiar esta entrada', + sendEntry: 'Enviar esta entrada al chat', + messages: count => `${count} mensajes de consola`, + resize: 'Redimensionar consola de vista previa', + title: 'Consola de vista previa', + selected: count => `${count} seleccionados`, + sendToChat: 'Enviar al chat', + copySelected: 'Copiar selección al portapapeles', + copyAll: 'Copiar todo al portapapeles', + copy: 'Copiar', + clear: 'Limpiar', + empty: 'Aún no hay mensajes de consola.', + promptHeader: 'Consola de vista previa:', + sentTitle: 'Enviado al chat', + sentMessage: count => `${count} entrada${count === 1 ? '' : 's'} de registro añadida${count === 1 ? '' : 's'} al compositor` + }, + web: { + appFailedToBoot: 'La app de vista previa no pudo iniciarse', + serverNotFound: 'Servidor no encontrado', + failedToLoad: 'La vista previa no pudo cargarse', + tryAgain: 'Intentar de nuevo', + restarting: 'Hermes se está reiniciando...', + askRestart: 'Pedir a Hermes que reinicie el servidor', + lookingRestart: taskId => `Hermes está buscando un servidor de vista previa para reiniciar (${taskId})`, + restartingTitle: 'Reiniciando servidor de vista previa', + restartingMessage: 'Hermes está trabajando en segundo plano. Observa la consola de vista previa para el progreso.', + startRestartFailed: message => `No se pudo iniciar el reinicio del servidor: ${message}`, + restartFailed: 'Fallo al reiniciar el servidor', + hideConsole: 'Ocultar consola de vista previa', + showConsole: 'Mostrar consola de vista previa', + hideDevTools: 'Ocultar DevTools de vista previa', + openDevTools: 'Abrir DevTools de vista previa', + finishedRestarting: message => `Hermes terminó de reiniciar el servidor de vista previa${message ? `: ${message}` : ''}`, + failedRestarting: message => `Fallo al reiniciar el servidor: ${message}`, + unknownError: 'error desconocido', + restartedTitle: 'Servidor de vista previa reiniciado', + reloadingNow: 'Recargando la vista previa ahora.', + restartFailedTitle: 'Fallo al reiniciar la vista previa', + restartFailedMessage: 'Hermes no pudo reiniciar el servidor.', + stillWorking: 'Hermes sigue trabajando, pero aún no ha llegado el resultado del reinicio. El comando del servidor puede estar ejecutándose en primer plano.', + workspaceReloading: 'El espacio de trabajo cambió, recargando vista previa', + fileChanged: url => `El archivo cambió, recargando vista previa: ${url}`, + filesChanged: (count, url) => `${count} cambios de archivo, recargando vista previa: ${url}`, + watchFailed: message => `No se pudo observar el archivo de vista previa: ${message}`, + moduleMimeDescription: 'Los scripts de módulo se sirven con el tipo MIME incorrecto. Esto normalmente significa que un servidor de archivos estático sirve una app Vite/React en lugar del servidor de desarrollo del proyecto.', + loadFailedConsole: (code, message) => `Fallo al cargar${code ? ` (${code})` : ''}: ${message}`, + unreachableDescription: 'No se pudo acceder a la página de vista previa.', + openTarget: url => `Abrir ${url}`, + fallbackTitle: 'Vista previa' + } + }, + zones: { + showHeader: 'Mostrar cabecera', + hideHeader: 'Ocultar cabecera', + minimize: 'Minimizar', + restore: 'Restaurar', + closeRunningTitle: '¿Cerrar pestaña en ejecución?', + closeRunningBody: 'Este chat sigue trabajando (o esperando tu entrada). Cerrar la pestaña la oculta — la sesión conserva su progreso y puede reabrirse desde la barra lateral.', + closeRunningConfirm: 'Cerrar pestaña', + reload: 'Recargar', + closeOthers: 'Cerrar otras', + closeToRight: 'Cerrar las de la derecha', + closeAll: 'Cerrar todas', + newSessionTab: 'Nueva pestaña de sesión', + pluginDisabled: pluginId => `Complemento "${pluginId}" desactivado`, + pluginDisabledBody: 'Reactívalo en Ajustes → Complementos para recuperar el panel.', + missingPane: paneId => `panel faltante: ${paneId}`, + editTitle: 'Disposiciones', + editHint: 'Elige una disposición, o arrastra paneles entre zonas.', + reset: 'Restablecer', + templates: 'Plantillas', + custom: 'Personalizada', + newGridLayout: 'Nueva disposición de cuadrícula', + saveCurrentAs: 'Guardar disposición actual como plantilla', + nameLayoutPlaceholder: 'Pon nombre a esta disposición…', + deletePreset: name => `Eliminar ${name}`, + zoneEditorTitle: 'Editor de zonas', + editorHintPre: 'clic para dividir · ', + editorHintPost: ' invierte la línea · arrastra entre zonas para fusionar · arrastra los bordes compartidos para redimensionar', + templateColumns: 'Columnas', + templateRows: 'Filas', + templateGrid: 'Cuadrícula', + templatePriority: 'Prioridad', + zoneTag: index => `zona ${index}`, + mergeZones: count => `Fusionar ${count} zonas`, + customZoneName: count => `Personalizada de ${count} zonas`, + layoutNamePlaceholder: fallback => `Nombre de disposición (${fallback})`, + saveApply: 'Guardar y aplicar', + notExpressible: 'esta disposición se entrelaza (molinete) — aún no expresable como divisiones anidadas', + zoneCount: count => `${count} zonas`, + tabCount: count => `${count} pestañas` + }, + assistant: { + thread: { + loadingSession: 'Cargando sesión', + showEarlier: 'Mostrar mensajes anteriores', + loadingResponse: 'Hermes está cargando una respuesta', + resumeWhenBackgroundDone: count => + count === 1 + ? 'Se reanudará cuando termine la tarea en segundo plano' + : `Se reanudará cuando terminen ${count} tareas en segundo plano`, + thinking: 'Pensando', + thought: 'Pensamiento', + thoughtBriefly: 'Pensó brevemente', + thoughtFor: duration => `Pensó durante ${duration}`, + today: time => `Hoy, ${time}`, + yesterday: time => `Ayer, ${time}`, + copy: 'Copiar', + refresh: 'Actualizar', + moreActions: 'Más acciones', + branchNewChat: 'Rama en chat nuevo', + react: 'Reaccionar', + dismissError: 'Descartar error', + filesChanged: count => (count === 1 ? '1 archivo cambiado' : `${count} archivos cambiados`), + reviewChanges: 'Revisar', + readAloudFailed: 'Fallo al leer en voz alta', + preparingAudio: 'Preparando audio...', + stopReading: 'Dejar de leer', + readAloud: 'Leer en voz alta', + editMessage: 'Editar mensaje', + expandMessage: 'Expandir mensaje', + scrollToBottom: 'Desplazar al final', + stop: 'Detener', + restorePrevious: 'Restaurar punto de control anterior', + restoreCheckpoint: 'Restaurar punto de control', + restoreFromHere: 'Restaurar punto de control — reejecutar desde este prompt', + restoreTitle: '¿Restaurar a este punto de control?', + restoreBody: 'Todo lo posterior a este prompt se elimina de la conversación, y el prompt se ejecuta de nuevo desde aquí.', + restoreConfirm: 'Restaurar y reejecutar', + restoreNext: 'Restaurar siguiente punto de control', + goForward: 'Avanzar', + sendEdited: 'Enviar mensaje editado', + attachingFile: 'Adjuntando…' + }, + approval: { + gatewayDisconnected: 'La puerta de enlace de Hermes no está conectada', + sendFailed: 'No se pudo enviar la respuesta de aprobación', + run: 'Ejecutar', + command: 'Comando', + moreOptions: 'Más opciones de aprobación', + allowSession: 'Permitir esta sesión', + alwaysAllowMenu: 'Permitir siempre…', + jumpToApproval: 'Aprobación necesaria', + reject: 'Rechazar', + alwaysTitle: '¿Permitir siempre este comando?', + alwaysDescription: pattern => + `Esto añade el patrón "${pattern}" a tu lista de permitidos permanente (~/.hermes/config.yaml). Hermes no volverá a preguntar por comandos como este — ni en esta sesión ni en ninguna futura.`, + alwaysAllow: 'Permitir siempre' + }, + clarify: { + notReady: 'La petición de aclaración aún no está lista', + gatewayDisconnected: 'La puerta de enlace de Hermes no está conectada', + sendFailed: 'No se pudo enviar la respuesta de aclaración', + loadingQuestion: 'Cargando pregunta…', + other: 'Otro (escribe tu respuesta)', + placeholder: 'Escribe tu respuesta…', + skip: 'Omitir', + skipped: 'Omitido', + continueLabel: 'Continuar', + lateAnswer: (question, choice) => `Re: "${question}" — mi respuesta: ${choice}`, + lateAnswerTip: 'Redactar esta respuesta como mensaje de seguimiento', + lateAnswerHint: 'Este prompt ya no está esperando. Elige una opción para redactarlo como mensaje de seguimiento.' + }, + mcpSetup: { + installTitle: server => `¿Añadir el servidor MCP ${server}?`, + enableTitle: server => `¿Activar el servidor MCP ${server}?`, + authorizeTitle: server => `¿Autorizar el servidor MCP ${server}?`, + installAction: 'Instalar', + enableAction: 'Activar', + authorizeAction: 'Autorizar', + decline: 'Ahora no', + declined: 'Rechazado', + installed: server => `${server} instalado`, + enabled: server => `${server} activado`, + authorized: server => `${server} autorizado`, + failed: server => `La configuración falló para ${server}`, + unanswered: 'Sin respuesta', + toolCount: count => (count === 1 ? '1 herramienta' : `${count} herramientas`), + notInCatalog: server => `"${server}" no está en el catálogo MCP`, + catalogSource: 'Del catálogo aprobado por Nous', + envRequired: 'Rellena primero las credenciales obligatorias', + sendFailed: 'No se pudo enviar la respuesta de configuración MCP', + reloadFailed: 'Servidor guardado, pero falló la recarga de herramientas MCP — se cargarán en la próxima sesión', + gatewayDisconnected: 'La puerta de enlace de Hermes no está conectada' + }, + tool: { + copyCode: 'Copiar código', + renderingImage: 'Renderizando imagen', + copyOutput: 'Copiar salida', + copyCommand: 'Copiar comando', + copyContent: 'Copiar contenido', + copyUrl: 'Copiar URL', + copyResults: 'Copiar resultados', + copyQuery: 'Copiar consulta', + copyFile: 'Copiar archivo', + copyPath: 'Copiar ruta', + outputAlt: 'Salida de herramienta', + rawResponse: 'Respuesta cruda', + copyActivity: 'Copiar actividad', + recoveredOne: 'Recuperado tras 1 paso fallido', + recoveredMany: count => `Recuperado tras ${count} pasos fallidos`, + failedOne: '1 paso fallido', + failedMany: count => `${count} pasos fallidos`, + statusRunning: 'En ejecución', + statusError: 'Error', + statusRecovered: 'Recuperado', + statusDone: 'Hecho', + memoryWriteNoted: 'Escritura de memoria anotada', + actions: { + read: 'Leer', + reading: 'Leyendo', + opened: 'Abierto', + opening: 'Abriendo', + failedToOpen: 'Fallo al abrir', + searched: 'Buscado', + searching: 'Buscando', + ran: 'Ejecutado', + running: 'Ejecutando', + ranCode: 'Ejecutó código', + runningCode: 'Programando' + }, + prefixes: { + browser: 'Navegador', + web: 'Web' + }, + titleTemplates: { + actionCommand: (action, command) => `${action} ${command}`, + actionQuoted: (action, value) => `${action} "${value}"`, + actionTarget: (action, target) => `${action} ${target}`, + prefixedDone: (prefix, action) => `${prefix} ${action}`, + runningPrefixedTool: (prefix, action) => `Ejecutando ${prefix.toLowerCase()} ${action.toLowerCase()}`, + runningTool: action => `Ejecutando ${action.toLowerCase()}` + }, + titles: { + browser_click: { done: 'Hizo clic en elemento de página', pending: 'Haciendo clic en elemento de página', pendingAction: 'Haciendo clic' }, + browser_fill: { done: 'Rellenó campo de formulario', pending: 'Rellenando campo de formulario', pendingAction: 'Rellenando' }, + browser_navigate: { done: 'Abrió página', pending: 'Abriendo página', pendingAction: 'Abriendo' }, + browser_snapshot: { done: 'Capturó instantánea de página', pending: 'Capturando instantánea de página', pendingAction: 'Capturando' }, + browser_take_screenshot: { done: 'Capturó captura de pantalla', pending: 'Capturando captura de pantalla', pendingAction: 'Capturando' }, + browser_type: { done: 'Escribió en la página', pending: 'Escribiendo en la página', pendingAction: 'Escribiendo' }, + clarify: { done: 'Hizo una pregunta', pending: 'Haciendo una pregunta', pendingAction: 'Preguntando' }, + cronjob: { done: 'Tarea cron', pending: 'Programando tarea cron', pendingAction: 'Programando' }, + edit_file: { done: 'Editó archivo', pending: 'Editando archivo', pendingAction: 'Editando' }, + execute_code: { done: 'Ejecutó código', pending: 'Programando', pendingAction: 'Programando' }, + image_generate: { done: 'Generó imagen', pending: 'Generando imagen', pendingAction: 'Generando' }, + list_files: { done: 'Listó archivos', pending: 'Listando archivos', pendingAction: 'Listando' }, + memory: { done: 'Guardado en memoria', pending: 'Guardando en memoria', pendingAction: 'Guardando' }, + patch: { done: 'Parcheó archivo', pending: 'Parcheando archivo', pendingAction: 'Parcheando' }, + read_file: { done: 'Leyó archivo', pending: 'Leyendo archivo', pendingAction: 'Leyendo' }, + search_files: { done: 'Buscó archivos', pending: 'Buscando archivos', pendingAction: 'Buscando' }, + session_search_recall: { done: 'Buscó historial de sesiones', pending: 'Buscando historial de sesiones', pendingAction: 'Buscando' }, + terminal: { done: 'Ejecutó comando', pending: 'Ejecutando comando', pendingAction: 'Ejecutando' }, + todo: { done: 'Actualizó tareas', pending: 'Actualizando tareas', pendingAction: 'Actualizando' }, + vision_analyze: { done: 'Analizó imagen', pending: 'Analizando imagen', pendingAction: 'Analizando' }, + web_extract: { done: 'Leyó página web', pending: 'Leyendo página web', pendingAction: 'Leyendo' }, + web_search: { done: 'Buscó en la web', pending: 'Buscando en la web', pendingAction: 'Buscando' }, + write_file: { done: 'Editó archivo', pending: 'Editando archivo', pendingAction: 'Editando' } + } + } + }, + prompts: { + gatewayDisconnected: 'La puerta de enlace de Hermes no está conectada', + sudoSendFailed: 'No se pudo enviar la contraseña sudo', + secretSendFailed: 'No se pudo enviar el secreto', + sudoTitle: 'Contraseña de administrador', + sudoDesc: 'Hermes necesita tu contraseña sudo para ejecutar un comando privilegiado. Solo se envía a tu agente local.', + sudoPlaceholder: 'contraseña sudo', + secretTitle: 'Secreto requerido', + secretDesc: 'Hermes necesita una credencial para continuar.', + secretPlaceholder: 'valor del secreto' + }, + desktop: { + audioReadFailed: 'No se pudo leer el audio grabado', + sessionUnavailable: 'Sesión no disponible', + createSessionFailed: 'No se pudo crear una nueva sesión', + promptFailed: 'Prompt fallido', + providerCredentialRequired: 'Añade una credencial de proveedor antes de enviar tu primer mensaje.', + emptySlashCommand: 'comando de barra vacío', + desktopCommands: 'Comandos del escritorio', + skillCommandsAvailable: count => `${count} comandos de habilidad disponibles.`, + warningLine: message => `aviso: ${message}`, + yoloArmed: 'YOLO armado para este chat', + yoloOff: 'YOLO desactivado', + yoloSystem: active => `YOLO ${active ? 'activado' : 'desactivado'} para esta sesión`, + yoloTitle: 'YOLO', + yoloToggleFailed: 'No se pudo alternar YOLO', + profileStatus: current => + `Perfil: ${current}. Usa /profile o el selector «Nueva sesión» para iniciar un chat en otro perfil.`, + unknownProfile: 'Perfil desconocido', + noProfileNamed: (target, available) => `No existe el perfil "${target}". Disponibles: ${available}`, + newChatsProfile: name => `Los chats nuevos usarán el perfil ${name}.`, + setProfileFailed: 'No se pudo establecer el perfil', + sttDisabled: 'El reconocimiento de voz está desactivado en los ajustes.', + stopFailed: 'Fallo al detener', + regenerateFailed: 'Fallo al regenerar', + editFailed: 'Fallo al editar', + resumeFailed: 'Fallo al reanudar', + resumeStrandedTitle: 'No se pudo cargar esta sesión', + resumeStrandedBody: 'La conexión con esta sesión falló y los reintentos automáticos se rindieron. Comprueba que la puerta de enlace esté en ejecución e inténtalo de nuevo.', + resumeRetry: 'Reintentar', + nothingToBranch: 'Nada que ramificar', + branchNeedsChat: 'Inicia o reanuda un chat antes de ramificar.', + sessionBusy: 'Sesión ocupada', + branchStopCurrent: 'Detén el turno actual antes de ramificar este chat.', + branchNoText: 'Este mensaje no tiene texto del que ramificar.', + branchTitle: n => `Borrador: Rama #${n}`, + branchFailed: 'Fallo al ramificar', + deleteFailed: 'Fallo al eliminar', + archived: 'Archivado', + archiveFailed: 'Fallo al archivar', + cwdChangeFailed: 'Fallo al cambiar el directorio de trabajo', + cwdStagedTitle: 'Directorio de trabajo preparado', + cwdStagedMessage: 'Reinicia el backend del escritorio para aplicar los cambios de cwd a esta sesión activa.', + modelSwitchFailed: 'Fallo al cambiar de modelo', + sessionExported: 'Sesión exportada', + sessionExportFailed: 'No se pudo exportar la sesión', + imageSaved: 'Imagen guardada', + downloadStarted: 'Descarga iniciada', + restartToUseSaveImage: 'Reinicia Hermes Desktop para usar Guardar imagen.', + restartToSaveImages: 'Reinicia Hermes Desktop para guardar imágenes', + imageDownloadFailed: 'Fallo al descargar la imagen', + openImage: 'Abrir imagen', + downloadImage: 'Descargar imagen', + savingImage: 'Guardando imagen', + imagePreviewFailed: 'Fallo en la vista previa de la imagen', + imageAttach: 'Adjuntar imagen', + imageWriteFailed: 'No se pudo escribir la imagen en disco.', + imageAttachFailed: 'Fallo al adjuntar la imagen', + attachImages: 'Adjuntar imágenes', + clipboard: 'Portapapeles', + noClipboardImage: 'No se encontró ninguna imagen en el portapapeles', + clipboardPasteFailed: 'Fallo al pegar del portapapeles', + dropFiles: 'Soltar archivos', + handoff: { + pickPlatform: 'Elige un destino', + success: platform => `Transferido a ${platform}. Reanuda aquí cuando quieras.`, + systemNote: platform => `↻ Transferido a ${platform} — reanuda aquí cuando quieras.`, + failed: error => `Fallo en la transferencia: ${error}`, + timedOut: 'Se agotó el tiempo esperando a la puerta de enlace. ¿Está `hermes gateway` en ejecución?' + } + }, + errors: { + genericFailure: 'Algo salió mal', + boundaryTitle: 'Algo se rompió en la interfaz', + boundaryDesc: 'La vista encontró un error inesperado. Tus chats y ajustes están a salvo.', + reloadWindow: 'Recargar ventana', + openLogs: 'Abrir registros' + }, + ui: { + search: { + clear: 'Limpiar búsqueda' + }, + pagination: { + label: 'paginación', + previous: 'Anterior', + previousAria: 'Ir a la página anterior', + next: 'Siguiente', + nextAria: 'Ir a la página siguiente' + }, + sidebar: { + title: 'Barra lateral', + description: 'Muestra la barra lateral móvil.', + toggle: open => `${open ? 'Mostrar' : 'Ocultar'} barra lateral` + } + } +}) diff --git a/apps/desktop/src/i18n/languages.ts b/apps/desktop/src/i18n/languages.ts index e4a1fb1e02eb..f866ea19191e 100644 --- a/apps/desktop/src/i18n/languages.ts +++ b/apps/desktop/src/i18n/languages.ts @@ -19,7 +19,7 @@ export const LOCALE_OPTIONS = [ }, { id: 'zh-hant', - name: '繁體中文', + name: '��體中文', englishName: 'Traditional Chinese', configValue: 'zh-hant' }, @@ -34,6 +34,12 @@ export const LOCALE_OPTIONS = [ name: 'العربية', englishName: 'Arabic', configValue: 'ar' + }, + { + id: 'es', + name: 'Español', + englishName: 'Spanish', + configValue: 'es' } ] as const satisfies readonly { configValue: string; englishName: string; id: Locale; name: string }[] @@ -79,7 +85,17 @@ const LOCALE_ALIASES: Record = { 'ar-eg': 'ar', ar_eg: 'ar', arabic: 'ar', - العربية: 'ar' + العربية: 'ar', + es: 'es', + 'es-es': 'es', + es_es: 'es', + 'es-mx': 'es', + es_mx: 'es', + 'es-419': 'es', + es_419: 'es', + espanol: 'es', + español: 'es', + spanish: 'es' } export function isLocale(value: unknown): value is Locale { diff --git a/apps/desktop/src/i18n/types.ts b/apps/desktop/src/i18n/types.ts index 4772ef6561bb..61a06744fa2b 100644 --- a/apps/desktop/src/i18n/types.ts +++ b/apps/desktop/src/i18n/types.ts @@ -5,7 +5,7 @@ // partial locales should use `defineLocale()` so missing desktop-only strings // fall back to English while new keys remain type-checked. -export type Locale = 'en' | 'zh' | 'zh-hant' | 'ja' | 'ar' +export type Locale = 'en' | 'zh' | 'zh-hant' | 'ja' | 'ar' | 'es' export type ToolTitleKey = | 'browser_click'