// Spanish dictionary. Mirror the shape of src/lang/en.ts — // missing keys fall back to English per createI18n's fallbackLocale. // // ⚠️ Las cadenas que contienen ángulos (p.ej. ``) activan la // heurística XSS de vue-i18n y generan el aviso // "Detected HTML in '…' message". Usa siempre la **forma función** // en esos casos. Ver la cabecera de en.ts para más detalle. const esMessages = { common: { downloadZip: "ZIP", downloadFailed: "Error al descargar", save: "Guardar", cancel: "Cancelar", loading: "Cargando...", close: "Cerrar", dismiss: "Descartar", add: "Añadir", remove: "Quitar", yes: "Sí", no: "No", saving: "Guardando...", saved: "Guardado", noResultsYet: "Aún no hay resultados", noImageYet: "Aún no hay imagen", sendChat: "Iniciar un chat nuevo", }, sessionTabBar: { newSession: "Nueva sesión", activeSessions: "{count} sesión activa (agente en ejecución) | {count} sesiones activas (agente en ejecución)", unreadReplies: "{count} respuesta sin leer | {count} respuestas sin leer", unreadDot: "Nueva respuesta", origin: { scheduler: "Iniciada por el programador", skill: "Iniciada por una skill", bridge: "Iniciada por un bridge", }, }, chatInput: { placeholder: "Mensaje para Claude…", send: "Enviar", stop: "Detener", runningPlaceholder: "En ejecución… pulsa Enter para poner en cola", removeBuffered: "Eliminar mensaje en cola", attachFile: "Adjuntar archivo", fileTooLarge: "El archivo es demasiado grande ({sizeMB} MB). El máximo es 30 MB.", unsupportedFileType: "Tipo de archivo no admitido. Se aceptan: imágenes, PDF, DOCX, XLSX, PPTX y archivos de texto.", attachImageFailed: "No se pudo adjuntar la imagen: {error}", stopFailed: "No se pudo detener la ejecución: {error}", dropHint: "Suelta el archivo para adjuntar", tooManyFiles: "Puedes adjuntar hasta {max} archivos a la vez.", removeAttachment: "Quitar {name}", attachmentFallbackName: "archivo adjunto", voice: { start: "Iniciar entrada de voz", stop: "Detener entrada de voz", }, }, cspViolation: { notice: "⚠ Una vista intentó cargar {host}, pero la política de seguridad de contenido lo bloqueó ({directive}). Para permitirlo, añade el host a config/csp.json, solo si confías en él.", dismiss: "Descartar", }, sessionHistoryPanel: { filters: { all: "Todas", unread: "No leídas", bookmarked: "Marcadas", longRunning: "De larga duración (24h+)", human: "Persona", scheduler: "Programador", skill: "Skill", bridge: "Bridge", }, failedToRefresh: "⚠ Error al actualizar: {error}", showingLastKnown: " — mostrando la última lista conocida.", noSessions: "Aún no hay sesiones.", noMatching: "No hay sesiones coincidentes.", running: "En ejecución", noMessages: "(sin mensajes)", openRowAria: "Abrir sesión: {preview}", rowMenuAria: "Acciones de sesión", bookmark: "Marcar", unbookmark: "Quitar marcador", delete: "Eliminar", deleteConfirm: "¿Eliminar esta sesión?\n\n{preview}\n\nEsta acción no se puede deshacer.", }, notificationBell: { notifications: "Notificaciones", activeSection: "Activas", historySection: "Historial", noActive: "Sin notificaciones activas", noHistory: "Sin actividad reciente", clearAll: "Borrar", dismiss: "Descartar", cancel: "Cancelar", showMore: "Mostrar más ({count})", showLess: "Mostrar menos", openTarget: "Abrir", expandDetails: "Expandir detalles", }, pluginDiagnostics: { title: "Problema de configuración del plugin", hostBody: 'El plugin "{plugin}" intentó registrar la clave {label} "{key}", pero está reservada por el host. La entrada del plugin se ha descartado.', intraBody: 'Los plugins "{first}" y "{second}" registran ambos el {dimension} "{key}". "{first}" lo reclamó primero, por lo que el registro de "{second}" se ignora.', }, shadowedEnv: { title: "El shell está anulando .env", body: "Definido tanto en el shell como en .env: {keys}. Prevalece el valor del shell, por lo que .env se ignora. Si editaste .env, actualiza o elimina el valor del shell y reinicia.", }, optionalDeps: { // `title` genérico conservado por compatibilidad con entradas de // historial persistidas antes de la división por motivo; las // nuevas usan `titleNotFound` / `titleNotResponding`. title: "Dependencia opcional no disponible", titleNotFound: "{command} no está instalado", titleNotResponding: "{command} no está en ejecución", notFound: "No se encontró {command} — las funciones relacionadas se han desactivado. Instala {command} y reinicia MulmoClaude para habilitarlas.", notResponding: "{command} está instalado pero no se está ejecutando — las funciones relacionadas se han desactivado. Inicia {command} y reinicia MulmoClaude para habilitarlas.", }, billingMigration: { title: "La facturación pasó a configurarse bajo demanda", body: "Las colecciones incluidas clients, worklog, invoice y profile se eliminaron de tu panel, pero tus datos están seguros e intactos. Pide configurar el seguimiento de clientes y horas y, luego, la facturación para volver a crearlas; tus registros existentes reaparecerán.", }, backendOffline: { title: "No se puede conectar con el backend", body: "Es posible que el servidor de MulmoClaude no esté en ejecución. Comprueba el servidor de desarrollo y vuelve a intentarlo.", retry: "Reintentar", }, pluginErrorBoundary: { title: "El plugin {pkg} se ha bloqueado", subtitle: "El plugin no se pudo renderizar. El error se ha registrado en la consola.", showDetails: "Mostrar detalles", hideDetails: "Ocultar detalles", retry: "Reintentar", }, remoteHostOffline: { title: "Host remoto desconectado", body: "Tu teléfono no podrá enviar a este dispositivo hasta que vuelvas a conectarte.", reconnect: "Reconectar", }, remoteHost: { title: "Host remoto", online: "Host remoto en línea", offline: "Host remoto sin conexión", uid: "uid {uid}", signIn: "Iniciar sesión con Google", connecting: "Conectando…", disconnect: "Desconectar", disconnecting: "Desconectando…", noToken: "El inicio de sesión de Google no devolvió idToken", connectFailed: "Error al conectar", disconnectFailed: "Error al desconectar", signInFailed: "Error al iniciar sesión con Google", statusFailed: "Error al cargar el estado", description: "El acceso remoto permite que un dispositivo móvil se conecte a las colecciones y feeds de este MulmoClaude.", howTo: "En tu teléfono, abre {url} e inicia sesión con la misma cuenta de Google.", customViewHint: "Para una vista compatible con móviles, pide a Claude que cree una {keyword} (no una custom view normal).", qrHint: "O escanea este código QR con la cámara de tu teléfono.", }, sidebarHeader: { newMessages: "Mensajes nuevos", home: "Ir al chat más reciente", toolCallHistory: "Historial de llamadas a herramientas", settings: "Ajustes", settingsGeminiMissing: "Ajustes — Falta la clave API de Gemini", todayJournal: "Resumen de hoy", todayJournalNotFound: "Aún no hay resumen — chatea un rato y el journal lo generará.", todayJournalLoadFailed: "Error al cargar el journal (status {status}): {error}", copyMarkdown: "Copiar la conversación como Markdown", copiedMarkdown: "¡Copiado!", }, rightSidebar: { permalink: "Enlace al mensaje seleccionado", copyPermalink: "Copiar enlace al mensaje seleccionado", copiedPermalink: "¡Copiado!", toggleSystemPrompt: "Alternar system prompt", systemPrompt: "System Prompt", availableTools: "Herramientas disponibles", toggleToolDescription: "Alternar descripción de la herramienta", toolCallHistory: "Historial de llamadas a herramientas", copyHistory: "Copiar historial de llamadas a herramientas", copiedHistory: "¡Copiado!", noToolCalls: "Aún no hay llamadas a herramientas", arguments: "Argumentos", error: "Error", result: "Resultado", running: "Ejecutando...", mcpHint: { title: (ctx: { named: (key: "server") => string }) => `Ayuda de configuración: ${ctx.named("server")}`, requiredKeys: "Claves requeridas", setupGuide: "Abrir guía de configuración", }, }, fileTreePane: { sort: "Orden:", sortByName: "Ordenar por nombre", name: "Nombre", sortByRecent: "Ordenar por fecha de modificación (más recientes primero)", recent: "Reciente", reference: "Referencia", // "RO" = Read-Only. Se mantiene la abreviatura en inglés para // renderizarse como una insignia compacta junto al label Referencia. readOnlyBadge: "RO", showSystemFiles: "Mostrar archivos del sistema", showSystemFilesTitle: "Muestra los directorios raíz internos del agente (conversations/, feeds/, etc.) además del contenido del usuario (data/, artifacts/, config/).", }, fileTree: { dropHint: "Suelta archivos aquí para guardarlos en esta carpeta", upload: { progress: "Subiendo {done} de {total}…", done: "Se guardaron {count} archivo(s)", failed: "No se pudieron guardar {count} archivo(s)", }, workspace: "(área de trabajo)", recentlyChanged: "Modificados recientemente", newFileMenuItem: "Nuevo archivo", newFileInputAria: "Nombre del nuevo archivo", newFilePlaceholder: { wikiPage: "slug-de-página", summary: "nombre-de-resumen", document: "nombre-de-documento", html: "nombre-de-página", story: "nombre-de-historia", }, newFileError: { empty: "El nombre del archivo no puede estar vacío.", unsafe: "El nombre del archivo contiene caracteres no válidos.", exists: "Ya existe un archivo llamado {filename} aquí.", saveFailed: "No se pudo crear el archivo. Inténtalo de nuevo.", }, }, lockStatusPopup: { sandboxEnabledTooltip: "Sandbox activado (Docker)", noSandboxTooltip: "Sin sandbox (Docker no encontrado)", sandboxEnabledLabel: "Sandbox activado:", sandboxEnabledBody: "Docker está en ejecución. El acceso al sistema de archivos está aislado.", noSandboxLabel: "Sin sandbox:", noSandboxBodyPrefix: "Claude puede acceder a todos los archivos del equipo. Instala", noSandboxBodySuffix: "para activar el aislamiento del sistema de archivos.", dockerDesktop: "Docker Desktop", hostCredentials: "Credenciales del host adjuntas:", credsLoading: "cargando…", sshAgent: "Agente SSH:", forwarded: "reenviado", notForwarded: "no reenviado", mountedConfigs: "Configuraciones montadas:", none: "ninguna", testIsolation: "Probar el aislamiento del sandbox:", }, settingsModal: { title: "Ajustes", version: "MulmoClaude v{version}", tabs: { gemini: "Clave API de Gemini", tools: "Herramientas permitidas", mcp: "Servidores MCP", dirs: "Directorios", refs: "Directorios de referencia", map: "Mapa", photos: "Fotos", google: "Google", model: "Modelo", voice: "Voz", chatIndex: "Índice de chat", journal: "Diario", notifications: "Web Push", skills: "Skills", roles: "Roles", quit: "Salir", }, groups: { llm: "LLM", servers: "Servidores", workspace: "Espacio de trabajo", notifications: "Notificaciones", plugins: "Plugins", management: "Gestión", server: "Servidor", }, navAriaLabel: "Secciones de ajustes", googleTab: { description: "Vincula tu cuenta de Google para que esta máquina pueda llamar a las API de Google (primero Calendar). El token de actualización se guarda solo en esta máquina y nunca se envía a ningún servidor que no sea Google.", statusLinked: "Vinculada", statusNotLinked: "No vinculada", statusPending: "Esperando a que termine el consentimiento en el navegador…", connect: "Vincular cuenta de Google", unlink: "Desvincular", unlinkConfirm: "¿Desvincular la cuenta de Google? El token guardado se revocará y se eliminará de esta máquina.", clientSecretAmbiguous: "Se encontraron varios archivos client_secret_*.json en ~/.secrets/. Conserva solo uno para que el token guardado siga emparejado con el cliente OAuth correcto.", loadError: "No se pudo cargar el estado de la vinculación con Google.", connectError: "No se pudo iniciar el flujo de autorización de Google.", unlinkError: "No se pudo desvincular la cuenta de Google.", }, mapTab: { description: "Configura la clave de la API de Google Maps que usa el plugin de mapas. La clave se guarda localmente y solo se envía a Google Maps.", apiKeyLabel: "Clave API de Google Maps", apiKeyPlaceholder: "AIza…", helperText: "Crea o copia una clave en {consoleLink}.", requiredApis: "APIs necesarias: Maps JavaScript API, Geocoding API, Places API (New), Directions API.", configured: "Configurada", notConfigured: "Sin configurar", clear: "Borrar", loadError: "Error al cargar los ajustes", saveError: "Error al guardar", }, photosTab: { description: "Controles de privacidad para las fotos recibidas por chat o por un bridge conectado. Los datos de ubicación EXIF son sensibles — desmarca para desactivar la captura automática.", autoCaptureLabel: "Capturar automáticamente la ubicación de las fotos", autoCaptureHint: "Activado: cada imagen subida con GPS en EXIF genera un sidecar de ubicación en data/locations/. Desactivado: no se captura nada automáticamente; el LLM aún puede extraer EXIF manualmente.", statusOn: "Captura automática ACTIVADA", statusOff: "Captura automática DESACTIVADA", loadError: "Error al cargar los ajustes", saveError: "Error al guardar", }, quitTab: { description: "Detén el servidor de MulmoClaude que se ejecuta en este equipo. Si lo abriste desde el icono, sigue funcionando aunque cierres esta pestaña: así se detiene sin usar una terminal.", // Message function form — skips vue-i18n's message compiler so the // literal `@` in `mulmoclaude@latest` is not parsed as a linked-message // reference (the compiler throws, and the whole tab renders as nothing). restartHint: () => "Para volver a iniciarlo, haz doble clic en el icono de MulmoClaude (o ejecuta `npx mulmoclaude@latest`).", quitLabel: "Salir de MulmoClaude", confirmBody: "El servidor se detiene y esta página deja de funcionar. Se interrumpe todo lo que esté en curso.", confirmLabel: "Salir", stopping: "Deteniendo…", stoppedTitle: "MulmoClaude se ha detenido", stoppedBody: "Puedes cerrar esta pestaña. Haz doble clic en el icono para volver a iniciarlo.", error: "No se pudo detener el servidor", }, notificationsTab: { description: "Recibe una notificación push en tus dispositivos registrados cuando termina una tarea que iniciaste aquí — útil cuando preguntas algo, te alejas y quieres saber en cuanto la respuesta esté lista.", enableLabel: "Enviar una Web Push cuando termine una tarea", enableHint: "Se activa cuando se completa un chat que iniciaste aquí. Las tareas programadas y en segundo plano no lo activan.", remoteHostNote: "Requiere la conexión RemoteHost (que proporciona el inicio de sesión) y al menos un dispositivo registrado. Si falta alguno, no hace nada.", macosRemindersLabel: "Crear un recordatorio de macOS al terminar una tarea", macosRemindersHint: "Añade la tarea terminada a tu lista de Recordatorios predeterminada. La sincronización de iCloud la refleja en tu iPhone, que es quien entrega la notificación.", macosRemindersForcedOff: "Desactivado al iniciar con --disable-macos-reminders o DISABLE_MACOS_REMINDER_NOTIFICATIONS. Quita la opción o anula la variable de entorno y reinicia para controlarlo desde aquí.", statusOn: "Web Push está ACTIVADO", statusOff: "Web Push está DESACTIVADO", loadError: "Error al cargar los ajustes", saveError: "Error al guardar", }, modelTab: { description: "Controla el esfuerzo de razonamiento que Claude Code usa en cada turno. Déjalo sin configurar para usar el valor por defecto de Claude.", effortLabel: "Esfuerzo de razonamiento", effortUnset: "(sin configurar — usar valor por defecto de Claude)", helperText: "Niveles más altos permiten más tiempo de pensamiento pero aumentan la latencia y el uso de tokens.", configured: "Esfuerzo: {level}", notConfigured: "Sin configurar", loadError: "Error al cargar los ajustes", saveError: "Error al guardar", }, voiceTab: { description: "Dicta mensajes de chat con tu voz. El audio se transcribe localmente en la máquina que ejecuta MulmoClaude; no se envía nada a ningún servicio externo.", requirements: "Disponible solo en macOS. Requiere el servidor whisper.cpp: compílalo con yarn build:whisper (consulta la sección Local Voice Input del README).", unsupported: "La entrada de voz requiere macOS con el servidor whisper.cpp instalado. No está disponible en esta máquina.", enableLabel: "Activar entrada de voz", enableHint: "Al activarlo se descarga una vez el modelo de voz (1–3 GB). Luego aparece un botón de micrófono en la entrada de chat.", modelLabel: "Modelo de voz", downloading: "Descargando modelo… {percent}%", ready: "Modelo listo", downloadError: "Error al descargar el modelo.", retry: "Reintentar", loadError: "Error al cargar los ajustes", saveError: "Error al guardar", }, chatIndexTab: { description: "Títulos y resúmenes automáticos generados por IA para tu historial de chat. Sale desactivado por defecto — las sesiones de automatización (scheduler / trabajadores del sistema) siempre se omiten aunque esté activado; las sesiones humanas solo pagan una llamada al resumidor al terminar cada turno.", modeLabel: "Modelo del índice de chat", helperText: "Haiku es más económico; Sonnet ofrece títulos más precisos en sesiones largas con cambios de tema.", mode: { off: "Desactivado", haiku: "Haiku", sonnet: "Sonnet", }, status: { off: "La indexación está DESACTIVADA", haiku: "Indexando con Haiku", sonnet: "Indexando con Sonnet", }, loadError: "Error al cargar los ajustes", saveError: "Error al guardar", }, journalTab: { description: "Diario automático — resume las sesiones de chat recientes en journal/*.md y extrae notas de memoria duradera. Sale desactivado. Las sesiones de automatización (scheduler / trabajadores del sistema) se excluyen siempre, independientemente de esta configuración.", modeLabel: "Modelo del diario", helperText: "Haiku es más económico; Sonnet produce resúmenes diarios y por tema más ricos. El pase horario solo se ejecuta cuando esto está activado.", mode: { off: "Desactivado", haiku: "Haiku", sonnet: "Sonnet", }, status: { off: "El diario está DESACTIVADO", haiku: "Diario en ejecución con Haiku", sonnet: "Diario en ejecución con Sonnet", }, loadError: "Error al cargar los ajustes", saveError: "Error al guardar", }, // Slots `` — los nombres `envKey` / `envFile` se renderizan // como `` en línea en SettingsModal.vue, por lo que los // literales de variable y nombre de archivo se mantienen sin // traducir. geminiRequired: "La generación de imágenes requiere {envKey}. Añádelo a {envFile} y reinicia la app.", geminiAskButton: "Preguntar a Claude", geminiAskMessage: "¿Cuál es el rol de la clave API de Gemini en esta app?", toolNamesLabel: "Nombres de herramientas", invalidToolNamesPrefix: "Estas parecen no estándar (prefijo esperado", invalidToolNamesSuffix: "):", mcpToolsError: "⚠ No se pudo obtener el estado de las herramientas MCP: {error}. Mostrando todas las herramientas sin importar si están habilitadas.", changesHint: "Los cambios se aplican en el siguiente mensaje. No hace falta reiniciar.", cannotSaveTooltip: "No se puede guardar hasta que los ajustes se carguen correctamente", saving: "Guardando…", loadingLabel: "Cargando…", unsavedMarker: "●", unsavedToolsConfirm: "Allowed Tools tiene cambios sin guardar. ¿Cerrar de todos modos?", unsavedMcpDraftConfirm: "Hay un borrador de servidor MCP abierto. ¿Cerrar de todos modos?", mcpSaveFailed: "No se pudieron guardar los cambios de los servidores MCP.", }, canvasViewToggle: { stackViewTooltip: "Vista apilada · haz clic para cambiar a vista única", singleViewTooltip: "Vista única · haz clic para cambiar a vista apilada", switchToSingle: "Cambiar a vista única", switchToStack: "Cambiar a vista apilada", }, sessionHistoryToggle: { showTooltip: "Mostrar el panel de historial de sesiones a la izquierda", hideTooltip: "Ocultar el panel de historial de sesiones", show: "Mostrar historial de sesiones", hide: "Ocultar historial de sesiones", }, sessionHistoryExpand: { expandTooltip: "Expandir el panel de historial de sesiones a ancho completo", collapseTooltip: "Contraer el panel de historial de sesiones", expand: "Expandir historial de sesiones", collapse: "Contraer historial de sesiones", }, settingsWorkspaceDirs: { explanation: "Directorios personalizados para organizar archivos dentro de {dataDir} y {artifactsDir}. Claude los utiliza para decidir dónde guardar los archivos.", noEntries: "No hay directorios personalizados configurados.", addDirTitle: "Añadir directorio", pathPlaceholder: "data/clientes o artifacts/informes", descPlaceholder: "Descripción (qué va en esta carpeta)", errPathRequired: "La ruta es obligatoria", errMustStartWith: "Debe comenzar con data/ o artifacts/", errAlreadyExists: "Ya existe", }, settingsReferenceDirs: { explanation: "Directorios externos que Claude puede leer pero no modificar. En modo Docker se montan como solo lectura. Útiles para consultar bóvedas de Obsidian, código de proyectos o carpetas de documentos.", noEntries: "No hay directorios de referencia configurados.", addDirTitle: "Añadir directorio de referencia", pathPlaceholder: "/Users/yo/ObsidianVault o ~/Documentos/notas", labelPlaceholder: "Etiqueta (opcional — por defecto el nombre de la carpeta)", readOnlyBadge: "solo lectura", errPathRequired: "La ruta es obligatoria", errMustBeAbsolute: "Debe ser una ruta absoluta o comenzar con ~/", errAlreadyExists: "Ya existe", errLabelConflict: 'La etiqueta "{label}" ya existe', }, dashboard: { empty: "Aún no hay colecciones favoritas.", emptyHint: "Fija una colección (★) para verla aquí.", viewTable: "Tabla", viewCalendar: "Calendario", viewKanban: "Kanban", viewPickerLabel: "Elegir vista", openFull: "Doble clic para abrir la vista completa", dragHint: "Arrastra para reordenar", resizeHint: "Arrastra para cambiar el tamaño", }, pluginLauncher: { chat: { label: "Chat" }, dashboard: { label: "Panel" }, automations: { label: "Acciones" }, wiki: { label: "Wiki" }, collections: { label: "Colecciones" }, feeds: { label: "Feeds" }, accounting: { label: "Contabilidad" }, files: { label: "Archivos" }, }, shortcuts: { pin: "Fijar en el lanzador", unpin: "Quitar del lanzador", zoneAriaLabel: "Accesos directos fijados", reorder: { open: "Reordenar accesos directos", title: "Reordenar", moveUp: "Subir", moveDown: "Bajar", }, }, fileContentHeader: { showRendered: "Mostrar Markdown renderizado", showRaw: "Mostrar código fuente", rendered: "Renderizado", raw: "Fuente", closeFile: "Cerrar archivo", revealInOs: "Mostrar en la carpeta", revealInOsFailed: "No se pudo mostrar en la carpeta", }, fileContentRenderer: { download: "ZIP", downloadZip: "Descargar como zip autónomo (recursos incluidos)", downloadError: "Error al descargar", selectFile: "Selecciona un archivo", htmlPreview: "Vista previa HTML", pdfPreview: "Vista previa PDF", parseError: "error al analizar", editJson: "Editar JSON", jsonEditorLabel: "Editor JSON", invalidJson: "JSON no válido", undo: "Deshacer", redo: "Rehacer", editMarp: "Editar fuente de la diapositiva", marpEditorLabel: "Fuente de diapositivas Marp", openInOs: "Abrir en el SO", openingInOs: "Abriendo…", openInOsFailed: "No se pudo abrir en el SO", }, filesView: { chatPlaceholder: "Pregunta sobre este archivo…", }, systemFiles: { schemaLabel: "Esquema", showDetails: "Mostrar detalles", hideDetails: "Ocultar detalles", editPolicy: { "agent-managed-but-hand-editable": "Gestionado por el agente (edición manual permitida)", "user-editable": "Editable por el usuario", "agent-managed": "Gestionado por el agente", "fragile-format": "Formato frágil", ephemeral: "Efímero", }, mcp: { title: "Servidores MCP", summary: "Servidores externos del Model Context Protocol conectados al agente. Añade servidores HTTP o stdio para ampliar las herramientas disponibles.", }, settings: { title: "Ajustes de la app", summary: "Preferencias de comportamiento editables — clave API de Gemini, herramientas permitidas, configuración del sandbox, etc.", }, schedulerTasks: { title: "Tareas del programador", summary: "Automatizaciones recurrentes del agente que se disparan en un horario. Se gestionan desde la UI Automations; este archivo es la fuente en disco.", }, schedulerOverrides: { title: "Sobrescrituras del programador", summary: "Sobrescrituras de hora / intervalo por tarea aplicadas sobre el horario del sistema. El agente las edita cuando pides cambiar la franja de una tarea recurrente.", }, schedulerItems: { title: "Cola de elementos del programador", summary: "Invocaciones programadas listas para dispararse. Gestionado por el agente; no edites a mano salvo que entiendas cada campo.", }, wikiIndex: { title: "Índice de la wiki", summary: "Índice autogenerado de cada página de la wiki. Se refresca con cada edición — no edites a mano (tus cambios serán sobrescritos).", }, wikiLog: { title: "Registro de edición de la wiki", summary: "Registro de actividades de creación y edición de páginas. Gestionado por el agente y solo añade — útil como feed de cambios recientes.", }, wikiSummary: { title: "Resumen de la wiki", summary: "Visión general autogenerada de la wiki — clústeres temáticos, recuento de páginas, actividad reciente. Refrescado por el agente.", }, wikiSchema: { title: "Esquema de la wiki", summary: "Especificación de formato que el agente lee para mantener las páginas consistentes. Frágil — espera una estructura concreta; prefiere ediciones del agente.", }, memory: { title: "Memoria", summary: "Hechos destilados sobre ti, cargados siempre como contexto en conversaciones nuevas. El extractor del journal añade automáticamente; también puedes editar a mano.", }, summariesIndex: { title: "Índice de resúmenes", summary: "Índice navegable que enlaza los resúmenes diarios y por tema generados por el journal. Gestionado por el agente; refrescado en cada pasada.", }, rolesJson: { title: "Definición del rol (JSON)", summary: "Configuración del rol — elección de modelo, servidores MCP, plugins permitidos, sugerencias de consulta. Editable, sin reinicio.", }, rolesMd: { title: "Descripción del rol (Markdown)", summary: "Persona y system prompt del rol, cargado como contexto cuando este rol está activo. Editable; los cambios aplican en el siguiente mensaje.", }, journalDaily: { title: "Resumen diario del journal", summary: "Recapitulación autogenerada de tu actividad para un día natural, destilada por el journal a partir de las sesiones de chat.", }, journalTopic: { title: "Journal por tema", summary: "Notas a largo plazo sobre un tema concreto, acumuladas y revisadas conforme sigues hablando de él. Gestionado por el agente.", }, }, settingsMcpTab: { explanation: "Añade servidores MCP externos. Los servidores HTTP funcionan en todos los modos. Los servidores Stdio usan el {npx} / {node} / {tsx} de la imagen del sandbox; cuando Docker está activo las rutas deben estar dentro del área de trabajo.", localhostRewrite: "En modo Docker {localhost} se reescribe como {hostDockerInternal}.", noServers: "Aún no hay servidores MCP configurados.", enabled: "activado", urlLabel: "URL:", commandLabel: "Comando:", dockerStdioUnsupported: "⚠ No se ejecutará mientras el sandbox de Docker esté activado.", dockerStdioHostExecActive: "⚠ Se ejecuta en el host: este servidor sale del sandbox de Docker.", dockerStdioHostExecOptIn: "Ejecutar en el host de todos modos (avanzado). Este servidor se ejecuta fuera del sandbox de Docker mediante una pasarela HTTP local y puede acceder a tu equipo.", learnMore: "Más información", addServerButton: "+ Añadir servidor MCP", nameLabel: "Nombre", namePlaceholder: "mi-servidor", typeHttp: "HTTP", typeStdio: "Stdio (comando)", urlFieldLabel: "URL", urlPlaceholder: "https://example.com/mcp", commandFieldLabel: "Comando", argsLabel: "Argumentos (uno por línea)", // Forma de función de mensaje — evita el compilador de mensajes de // vue-i18n para que `@` literal no se interprete como referencia a // un mensaje enlazado. argsPlaceholder: () => "-y\n@modelcontextprotocol/server-filesystem\n/workspace/path", errNoName: "Indica un Nombre, o introduce una URL / argumentos de los que podamos deducirlo.", errBadName: "El nombre debe comenzar con una letra minúscula y contener solo [a-z0-9_-].", errIdExists: 'El id de servidor "{id}" ya existe.', errBadHttpUrl: "La URL HTTP debe comenzar con http:// o https://", pendingEntryWarning: "Finaliza o cancela primero la entrada de servidor MCP pendiente.", customHeading: "Servidores personalizados", catalog: { heading: "Servidores MCP preconfigurados", audience: { general: "🟢 General", developer: "🔵 Desarrollador" }, risk: { low: "bajo", medium: "medio", high: "alto" }, upstream: "📦 Fuente", setupGuide: "📚 Configuración", entry: { memory: { displayName: "Memoria", description: "Permite que Claude recuerde el contexto entre sesiones.", }, sequentialThinking: { displayName: "Pensamiento secuencial", description: "Ayuda a Claude a abordar problemas complejos paso a paso.", }, context7: { displayName: "Context7 (documentación de librerías)", description: "Documentación al día de librerías populares — supera el corte de entrenamiento del modelo.", }, deepwiki: { displayName: "DeepWiki (wiki de repos de GitHub)", description: "Pregunta sobre cualquier repositorio de GitHub y obtén una respuesta estructurada estilo wiki.", }, notion: { displayName: "Notion", description: "Lee y escribe en tu workspace de Notion — páginas, bases de datos y búsqueda.", field: { apiKey: { label: "Token de integración de Notion", help: "Crea una integración en Notion y copia el Internal Integration Secret. Pulsa 🔑 para abrir la página de integraciones.", }, }, }, slack: { displayName: "Slack", description: "Lista canales, envía mensajes y busca en el historial de tu workspace de Slack.", field: { botToken: { label: "Token de bot", help: "App de Slack → OAuth & Permissions → Bot User OAuth Token. Empieza por xoxb-.", }, teamId: { label: "ID del equipo / workspace", help: "Ejecuta team.info o consulta la URL del workspace — algo como T01ABC23DEF.", }, }, }, googleMaps: { displayName: "Google Maps", description: "Buscar lugares, obtener rutas y consultar detalles de ubicación.", field: { apiKey: { label: "Clave API de Google Maps", help: "Google Cloud Console → APIs & Services → Credentials → Crear clave API. Habilita Places + Directions.", }, }, }, appleNative: { displayName: "Apps nativas de Apple (macOS)", description: "Lee y escribe Recordatorios, Calendario, Notas, Mail y Mapas vía AppleScript. Solo macOS — sin credenciales.", }, gmail: { displayName: "Gmail", description: "Lee, envía y etiqueta tu Gmail. Usa un cliente OAuth de Google que tú mismo creas en tu propio proyecto de Google Cloud (sin verificación de app).", field: { credentials: { label: "Ruta a credentials.json", help: "Google Cloud Console → APIs & Services → Credentials → ID de cliente OAuth (Desktop app). Descarga credentials.json y pega su ruta absoluta.", }, }, }, googleCalendar: { displayName: "Google Calendar", description: "Lee y crea eventos en Google Calendar. Mismo patrón BYO credentials.json que Gmail.", field: { credentials: { label: "Ruta a credentials.json", help: "Reutiliza el mismo cliente OAuth de Google Cloud que Gmail, o crea uno separado para Calendar.", }, }, }, googleDrive: { displayName: "Google Drive", description: "Busca y lee archivos de Google Drive. BYO credenciales OAuth de Google — el token se guarda localmente junto al archivo.", field: { credentials: { label: "Ruta a credentials.json", help: "Google Cloud Console → APIs & Services → Credentials → ID de cliente OAuth (Desktop app). Habilita la API de Google Drive en el mismo proyecto.", }, }, }, github: { displayName: "GitHub", description: "Lee repos, issues, PRs y ejecuta búsquedas con un Personal Access Token. Limita el alcance del token — los permisos de escritura (`repo`) dejan al agente hacer push a cualquier repo accesible.", field: { token: { label: "Personal Access Token", help: "GitHub → Settings → Developer settings → Personal access tokens. Se recomienda usar fine-grained tokens limitados a los repos donde quieras que el agente actúe.", }, }, }, linear: { displayName: "Linear", description: "Lee y actualiza issues, proyectos y ciclos de Linear con una clave API personal.", field: { apiKey: { label: "Clave API de Linear", help: "Linear → Settings → API → Personal API keys. Pulsa 🔑 para abrir la página y haz clic en Create key.", }, }, }, weatherOpenMeteo: { displayName: "Clima (Open-Meteo)", description: "Pronósticos meteorológicos gratuitos y condiciones actuales en todo el mundo — sin clave API.", }, spotify: { displayName: "Spotify", description: "Busca canciones, gestiona playlists y controla la reproducción. BYO app de desarrollador de Spotify — solo Client ID (flujo PKCE, sin client secret).", field: { clientId: { label: "Client ID", help: "Spotify Developer Dashboard → Create app, configura la Redirect URI como http://127.0.0.1:8888/callback, copia el Client ID. Luego ejecuta una vez en la terminal `SPOTIFY_CLIENT_ID= npx spotify-mcp@latest auth` para iniciar sesión (el refresh token se guarda en ~/.spotify-mcp/tokens.json).", }, }, }, youtubeTranscript: { displayName: "Transcripción de YouTube", description: "Obtén los subtítulos de cualquier vídeo público de YouTube por URL. Sin credenciales.", }, }, config: { howToGet: "Cómo obtenerlo", install: "Instalar", errMissingRequired: "Faltan campos obligatorios: {fields}", requiredMarker: "*", requiredAria: "obligatorio", }, }, }, pluginScheduler: { previewIcon: "📅", previewAutomations: "{count} automatización | {count} automatizaciones", previewMore: "+ {count} más…", }, pluginSchedulerTasks: { recommendedFrequencies: "Frecuencias recomendadas", tableTaskType: "Tipo de tarea", tableSuggestedSchedule: "Programación sugerida", noTasks: "No hay tareas programadas", runNow: "Ejecutar ahora", enable: "Activar", disable: "Desactivar", delete: "Eliminar", nextRun: "Próxima: {time}", originSystem: "Sistema", originUser: "Usuario", originSkill: "Skill", runFailed: "Error al ejecutar: {error}", toggleFailed: "Error al alternar: {error}", deleteFailed: "Error al eliminar: {error}", detailsToggle: "Mostrar detalles", promptLabel: "Prompt", roleLabel: "Rol", confirmDelete: "¿Eliminar la tarea « {name} »? Esta acción no se puede deshacer.", hintNewsRss: "Obtención de noticias / RSS", hintJournal: "Pase diario del diario", hintWiki: "Mantenimiento del wiki", hintMemory: "Extracción de memoria", hintCalendar: "Sincronización de calendario / contactos", }, pluginCanvas: { undo: "Deshacer", redo: "Rehacer", clear: "Limpiar", styleLabel: "Estilo:", stylePromptWithPath: "Convierte la imagen en `{path}` en una imagen de estilo {style}.", stylePromptNoPath: "Convierte mi dibujo en el lienzo en una imagen de estilo {style}.", saveFailed: "Sin guardar", }, pluginWiki: { backToIndex: "Volver al índice", pdf: "PDF", pdfFailed: "⚠ Error de PDF", tabIndex: "Índice", tabLog: "Registro", tabLint: "Lint", tabGraph: "Grafo", graphEmpty: "Aún no hay enlaces para graficar.", linkedReferences: "Referencias entrantes", empty: "El wiki está vacío. Pide al Wiki Manager que ingiera una fuente.", previewMore: "+ {count} más…", chatPlaceholder: "Pregunta sobre esta página…", emptyPage: 'La página "{title}" aún no existe.', emptyContent: 'La página "{title}" existe pero no tiene contenido.', createPage: "Solicitar la creación de esta página wiki", updatePage: "Solicitar la actualización de esta página wiki", tagFilterAll: "Todas", noMatches: "No hay páginas con la etiqueta #{tag}", lintChat: "Revisar mi wiki", taskCountMismatch: "La fuente del wiki y la salida renderizada difieren en el número de tareas. Se rechazó el cambio para evitar dañar el archivo.", metadataCreated: "Creado", metadataUpdated: "Actualizado", metadataEditor: "Editor", pageEditHeader: "Edición de wiki", snapshotExpired: "Instantánea expirada — mostrando la página actual", snapshotLoadError: "No se pudo cargar la instantánea — es posible que la página aún exista. Actualiza la página.", pageDeleted: "Página eliminada", history: { tabContent: "Contenido", tabHistory: "Historial", empty: "Aún no hay historial — edita esta página y la primera versión aparecerá aquí.", loading: "Cargando historial…", backToList: "Volver al historial", restoreButton: "Restaurar esta versión", restoreConfirmTitle: "¿Restaurar esta versión?", restoreConfirmBody: "Restaurar la página a la versión del {ts} por {editor}. La página actual será reemplazada. El historial existente se conserva.", restoreConfirmAction: "Restaurar", restoreConfirmCancel: "Cancelar", restoreSuccessToast: "Página restaurada.", restoreFailureBanner: "Error al restaurar: {error}", compareCurrent: "Comparar con la página actual", comparePrevious: "Comparar con la versión anterior", diffNoPrevious: "No hay versión anterior con la que comparar.", diffNoChanges: "No hay diferencias de contenido entre esta versión y el objeto de comparación.", editorBadgeUser: "Usuario", editorBadgeLLM: "LLM", editorBadgeSystem: "Sistema", hiddenLines: "{count} líneas sin cambios ocultas", expandHidden: "Mostrar", }, }, pluginPresentForm: { fallbackTitle: "Formulario", fieldCount: "{count} campo | {count} campos", submitted: "Enviado", errorSummary: "Por favor, corrija los siguientes errores", requiredMarker: "*", selectOption: "Seleccione una opción", charactersCount: "{current} / {max} caracteres", charactersCountNoMax: "{current} caracteres", submit: "Enviar", progress: "{filled} de {total} campos obligatorios completados", }, pluginPresentSvg: { saveAsPng: "Descargar como PNG", png: "PNG", saveAsPdf: "Guardar como PDF (abre el diálogo de impresión)", pdf: "PDF", untitled: "Dibujo SVG", editSource: "Editar código SVG", cancel: "Cancelar", applyChanges: "Aplicar cambios", saving: "Guardando...", saveError: "⚠ Error al guardar: {error}", exportError: "⚠ Error al exportar: {error}", loadingSource: "Cargando código…", sourceError: "Error al cargar el código: {error}", }, photoLocations: { title: "Ubicaciones de fotos", summary: "{total} capturadas · {withGps} con GPS", mapHint: 'Pide a Claude "muéstralas en el mapa" para trazarlas con el plugin de Google Map.', loading: "Cargando…", empty: "Aún no se ha capturado ninguna ubicación. Envía una foto con etiqueta GPS por el chat o un bridge conectado para empezar.", noGps: "Sin datos GPS", }, pluginManageSkills: { deleteProjectSkill: "Eliminar esta skill de proyecto", unstarPresetSkill: "Quitar la estrella de este preset — vuelve al catálogo", heading: "Skills", previewCount: "{count} skill | {count} skills", previewMore: "+{count} más", // La forma función evita el compilador de mensajes de vue-i18n, // que de lo contrario marca el literal `` como fragmento // HTML y avisa "Detected HTML in '…' message" en cada montaje. subheading: ({ named }: { named: (key: string) => unknown }) => `${named("count")} disponibles · haz clic para ver · "Run" la invoca como /`, emptyWithPath: "No se encontraron skills. Añade carpetas de skills en {path}.", emptySkillPath: "~/.claude/skills/", selectHint: "Selecciona una skill a la izquierda para ver su SKILL.md.", loading: "Cargando…", fieldDescription: "Descripción", fieldBody: "Cuerpo (Markdown)", emptyBody: "(cuerpo vacío)", btnEdit: "Editar", btnDelete: "Eliminar", btnUnstar: "Quitar estrella", errListFailed: "Error al cargar las skills: {error}", errDetailFailed: "Error al cargar la skill: {error}", errSaveFailed: "Error al guardar: {error}", errDeleteFailed: "Error al eliminar", confirmDelete: '¿Eliminar la skill "{name}"? Esto borrará ~/mulmoclaude/.claude/skills/{name}/SKILL.md.', confirmUnstar: '¿Devolver "{name}" al catálogo? Dejará de cargarse en el prompt, pero la copia del catálogo permanece — puedes volver a destacarla cuando quieras.', sectionActive: "Activas", sectionCatalog: "Catálogo", sectionLegendActive: "Skills que Claude puede usar ahora mismo. Claude las usa automáticamente en el flujo de la conversación, o puedes invocar una escribiendo su nombre. {system} Sistema (mc- incluida) / {project} Proyecto (editable, solo en este workspace) / {user} Usuario (skills en ~/.claude/skills/).", sectionLegendCatalog: "Catálogo: skills que pasan a Activas al marcarlas con {star}. Quitar {star} desde Activas devuelve la skill al Catálogo — Claude deja de usarla (la skill no se elimina).", catalogEmpty: "No hay skills de preajuste disponibles.", catalogPresetHeading: "Preajustes", catalogStar: "Destacar", catalogStarred: "Destacada", sourceUserTitle: "Skill de usuario (~/.claude/skills/, disponible en todos los espacios)", sourceSystemTitle: "Skill de sistema (incluida, prefijo mc- — solo lectura, sobrescrita por el launcher)", sourceProjectTitle: "Skill del proyecto (.claude/skills/ del workspace, solo este espacio)", sourcePresetTitle: "Catálogo de preajustes — pulsa Destacar para activarla en este espacio", errCatalogListFailed: "Error al cargar el catálogo: {error}", errCatalogStarFailed: "Error al destacar la skill: {error}", errCatalogPreviewFailed: "Error al cargar la vista previa de la skill: {error}", catalogAddRepo: "Añadir repositorio de skills", catalogAddRepoTitle: "Añadir un repositorio de skills", catalogRepoUrlLabel: "URL de GitHub", catalogRepoUrlPlaceholder: "https://github.com/owner/repo", catalogRepoSubpathLabel: "Subruta (opcional)", catalogRepoSubpathPlaceholder: "skills", catalogAddRepoSubmit: "Instalar", catalogAddRepoSuggestions: "Repositorios sugeridos", catalogUninstallRepo: "Desinstalar repositorio", catalogUpdateRepo: "Actualizar repositorio (volver a obtener lo último)", catalogRepoOpenLink: "Abrir el repositorio en GitHub (nueva pestaña)", catalogUninstallConfirm: "¿Desinstalar este repositorio? Las skills que ya marcaste con estrella permanecen en tu lista activa.", catalogRepoInstalling: "Instalando…", catalogRepoEmpty: "No se encontraron skills en este repositorio.", sourceExternalTitle: "Skill externa (instalada desde un repositorio de GitHub — pulsa la estrella para activar)", errCatalogRepoListFailed: "No se pudieron cargar los repositorios instalados: {error}", errCatalogRepoInstallFailed: "No se pudo instalar el repositorio: {error}", errCatalogRepoUninstallFailed: "No se pudo desinstalar el repositorio: {error}", errCatalogRepoInvalidUrl: "Introduce una URL de repositorio de GitHub.", }, pluginManageRoles: { heading: "Roles personalizados", roleCount: "{count} rol | {count} roles", addButton: "+ Añadir", createPanel: "Crear nuevo rol", fieldId: "ID", fieldName: "Nombre", fieldIcon: "Icono", fieldPrompt: "Prompt", fieldPlugins: "Plugins", fieldStarterQueries: "Preguntas iniciales", onePerLine: "(una por línea)", helpLink: "?", idFormatted: "({id})", missingEnv: "(falta {env})", requiresEnv: "Requiere {env} en .env", collapse: "Contraer", expand: "Expandir", idPlaceholder: "id-unico", creating: "Creando…", create: "Crear", updating: "Actualizando…", update: "Actualizar", cancel: "Cancelar", delete: "Eliminar", emptyHint: 'Aún no hay roles personalizados. Haz clic en "+ Añadir" o pide a Claude que cree uno.', errIdRequired: "El ID es obligatorio.", errIdInvalid: "El ID solo puede contener letras, números, '-' y '_'.", errNameRequired: "El nombre es obligatorio.", errIdDuplicate: "Ya existe un rol con el ID '{id}'.", errCreateFailed: "Error al crear", errSaveFailed: "Error al guardar", errDeleteFailed: "Error al eliminar", errNetworkError: "Error de red", errServerError: "Error del servidor: {status}", errRefreshFailed: "Guardado, pero no se pudo actualizar la lista.", confirmDelete: "¿Eliminar el rol « {name} »? Esta acción no se puede deshacer.", }, pluginUiImage: { promptLabel: "{label}:", }, markdownMermaid: { loadFailed: "⚠ Error al cargar Mermaid: {error}", renderFailed: "⚠ Error al renderizar Mermaid: {error}", }, pluginTextResponse: { pdf: "PDF", pdfFailed: "⚠ Error de PDF", editContent: "Editar contenido de texto", applyChanges: "Aplicar cambios", copyLabel: "Copiar", speakerSystem: "Sistema", speakerUser: "Tú", speakerAssistant: "Asistente", copiedLabel: "¡Copiado!", cancel: "Cancelar", seededByPlugin: "desde {pkg}", seededByPluginTooltip: "Este mensaje fue generado por el plugin {pkg}, no enviado por ti.", truncatedForRender: "Este mensaje es inusualmente largo ({total} caracteres en total). Solo se muestra la primera parte — {omitted} caracteres ocultos para mantener la pestaña receptiva. Usa Copiar para obtener el texto completo.", }, pluginSkill: { noDescription: "(sin descripción)", }, pluginSpreadsheet: { previewUntitled: "Hoja de cálculo", previewSheets: "{count} hoja | {count} hojas", untitled: "Hoja de cálculo", excel: "Excel", valuePlaceholder: "Valor", valueOrFormulaPlaceholder: "Valor o Fórmula (ej. 100 o SUM(B2:B11))", formatPlaceholder: "Formato (ej. $#,##0.00)", loading: "Cargando la hoja de cálculo...", noData: "No hay datos de hoja de cálculo disponibles", editData: "Editar datos de la hoja de cálculo", applyChanges: "Aplicar cambios", dataMustBeArray: "Los datos deben ser una matriz de hojas", loadFailed: "Error al cargar la hoja de cálculo: {error}", invalidJsonAlert: "Formato JSON no válido: {error}", unknownError: "Error desconocido", update: "Actualizar", stringType: "Texto", formulaType: "Fórmula", }, app: { startConversation: "Inicia una conversación", thinking: "Pensando…", }, suggestionsPanel: { suggestions: "Sugerencias", skills: "Habilidades", tooltip: "Sugerencias y habilidades", emptySuggestions: "No hay sugerencias.", emptySkills: "No hay habilidades instaladas.", skillsError: "Error al cargar las habilidades: {error}", sendEditHint: "clic para enviar · shift+clic para editar", }, settingsToolsTab: { // Renderizado vía con slots nombrados `allowedTools` y // `claudeMcp` para que las etiquetas `` en línea conserven // su estilo mientras el texto se traduce. explanation: "Nombres adicionales de herramientas que pasar a Claude mediante {allowedTools}. Uno por línea. Útil para servidores MCP integrados en Claude Code como Gmail / Google Calendar tras autenticarte mediante {claudeMcp}.", connectorsSectionTitle: "Conectores conectados", connectorsEmpty: "No se encontraron conectores.", connectorConnected: "Conectado", connectorDisconnected: "No conectado", connectorsGuide: "Los conectores como Slack y Gmail permiten a Claude acceder a tus cuentas. Agrega o elimina conectores desde Claude Desktop o configúralos {configLink}. (Abre claude.ai)", connectorsConfigLinkText: "aquí", }, confirmModal: { defaultTitle: "Confirmar", defaultConfirm: "Aceptar", defaultCancel: "Cancelar", }, }; export default esMessages;