diff --git a/client/src/i18n/__tests__/i18n.test.ts b/client/src/i18n/__tests__/i18n.test.ts index dc7b456c..e4e16b8d 100644 --- a/client/src/i18n/__tests__/i18n.test.ts +++ b/client/src/i18n/__tests__/i18n.test.ts @@ -16,7 +16,7 @@ describe("i18n resources", () => { expect(i18n.t("nav:languageShort.vi")).toBe("VI"); }); - it("should keep Agent terminology untranslated in zh, vi, and ko locales", async () => { + it("should keep Agent terminology untranslated in zh, vi, ko, and es locales", async () => { await i18n.changeLanguage("zh"); expect(i18n.t("common:agent")).toBe("Agent"); expect(i18n.t("common:subagent")).toBe("Subagent"); @@ -28,6 +28,25 @@ describe("i18n resources", () => { await i18n.changeLanguage("ko"); expect(i18n.t("common:agent")).toBe("Agent"); expect(i18n.t("common:subagent")).toBe("Subagent"); + + await i18n.changeLanguage("es"); + expect(i18n.t("common:agent")).toBe("agente"); + expect(i18n.t("common:subagent")).toBe("subagente"); + }); + + it("should provide Spanish translations for navigation keys", async () => { + await i18n.changeLanguage("es"); + + expect(i18n.t("nav:dashboard")).toBe("Panel"); + expect(i18n.t("nav:agentBoard")).toBe("Tablero Kanban"); + expect(i18n.t("nav:languageShort.es")).toBe("ES"); + }); + + it("should support non-explicit Spanish locale tags", async () => { + await i18n.changeLanguage("es-ES"); + + expect(i18n.resolvedLanguage?.startsWith("es")).toBe(true); + expect(i18n.t("nav:dashboard")).toBe("Panel"); }); it("should support non-explicit Vietnamese locale tags", async () => { diff --git a/client/src/i18n/index.ts b/client/src/i18n/index.ts index e31b1c6b..a4b30d8e 100644 --- a/client/src/i18n/index.ts +++ b/client/src/i18n/index.ts @@ -133,6 +133,22 @@ import run_ko from "./locales/ko/run.json"; import alerts_ko from "./locales/ko/alerts.json"; import splash_ko from "./locales/ko/splash.json"; +import common_es from "./locales/es/common.json"; +import nav_es from "./locales/es/nav.json"; +import dashboard_es from "./locales/es/dashboard.json"; +import sessions_es from "./locales/es/sessions.json"; +import activity_es from "./locales/es/activity.json"; +import analytics_es from "./locales/es/analytics.json"; +import workflows_es from "./locales/es/workflows.json"; +import settings_es from "./locales/es/settings.json"; +import kanban_es from "./locales/es/kanban.json"; +import errors_es from "./locales/es/errors.json"; +import updates_es from "./locales/es/updates.json"; +import ccConfig_es from "./locales/es/ccConfig.json"; +import run_es from "./locales/es/run.json"; +import alerts_es from "./locales/es/alerts.json"; +import splash_es from "./locales/es/splash.json"; + i18n .use(LanguageDetector) .use(initReactI18next) @@ -206,8 +222,25 @@ i18n alerts: alerts_ko, splash: splash_ko, }, + es: { + common: common_es, + nav: nav_es, + dashboard: dashboard_es, + sessions: sessions_es, + activity: activity_es, + analytics: analytics_es, + workflows: workflows_es, + settings: settings_es, + kanban: kanban_es, + errors: errors_es, + updates: updates_es, + ccConfig: ccConfig_es, + run: run_es, + alerts: alerts_es, + splash: splash_es, + }, }, - supportedLngs: ["en", "zh", "vi", "ko"], + supportedLngs: ["en", "zh", "vi", "ko", "es"], nonExplicitSupportedLngs: true, fallbackLng: "en", ns: [ diff --git a/client/src/i18n/locales/es/activity.json b/client/src/i18n/locales/es/activity.json new file mode 100644 index 00000000..0100b782 --- /dev/null +++ b/client/src/i18n/locales/es/activity.json @@ -0,0 +1,10 @@ +{ + "title": "Feed de Actividad", + "subtitle": "Flujo en tiempo real de todos los eventos de los agentes", + "paused": "(pausado - {{count}} en búfer)", + "resume": "Reanudar", + "pause": "Pausar", + "noActivity": "Sin actividad aún", + "noActivityDesc": "Los eventos se transmitirán aquí en tiempo real mientras los agentes de Claude Code trabajan.", + "viewSession": "Sesión" +} diff --git a/client/src/i18n/locales/es/alerts.json b/client/src/i18n/locales/es/alerts.json new file mode 100644 index 00000000..3201ce64 --- /dev/null +++ b/client/src/i18n/locales/es/alerts.json @@ -0,0 +1,80 @@ +{ + "title": "Alertas", + "subtitle": "Alertas basadas en reglas para sesiones, agentes, eventos y uso de tokens", + "refresh": "Actualizar", + "ackAll": "Reconocer todas ({{count}})", + "ruleTypes": { + "event_pattern": "Patrón de evento", + "inactivity": "Inactividad", + "status_duration": "Agente bloqueado", + "token_threshold": "Umbral de tokens" + }, + "ruleTypeHints": { + "event_pattern": "Se activa cuando un evento ingestado coincide con el patrón. Establece un conteo superior a 1 para requerir N eventos coincidentes dentro de una ventana de tiempo (p. ej., más de 5 errores en 2 minutos).", + "inactivity": "Se activa cuando una sesión activa no ha recibido eventos durante el número de minutos configurado.", + "status_duration": "Se activa cuando un agente ha permanecido en el estado seleccionado sin actividad durante el número de minutos configurado.", + "token_threshold": "Se activa cuando el uso total de tokens de una sesión (entrada + salida + caché) supera el umbral." + }, + "ruleDesc": { + "eventPattern": "Coincide con {{pattern}}", + "eventPatternCount": "≥{{count}} × {{pattern}} en {{window}} min", + "inactivity": "Sin actividad de sesión durante {{minutes}} min", + "statusDuration": "Agente bloqueado en {{status}} durante {{minutes}} min", + "tokenThreshold": "Tokens de sesión superan {{tokens}}" + }, + "rules": { + "title": "Reglas de alerta", + "add": "Añadir regla", + "cancel": "Cancelar", + "create": "Crear regla", + "saving": "Guardando…", + "empty": "Sin reglas de alerta aún. Añade una para comenzar a monitorear agentes bloqueados, ráfagas de errores, sesiones inactivas o picos de tokens.", + "enabled": "Habilitada", + "disabled": "Deshabilitada", + "enable": "Habilitar regla", + "disable": "Deshabilitar regla", + "delete": "Eliminar regla", + "confirmDelete": "¿Eliminar la regla \"{{name}}\" y su historial de alertas?", + "cooldown": "{{seconds}}s de tiempo de espera", + "form": { + "name": "Nombre de la regla", + "namePlaceholder": "p. ej., Ráfaga de errores", + "type": "Tipo de regla", + "eventType": "Tipo de evento (exacto)", + "toolName": "Nombre de herramienta (exacto)", + "summaryContains": "El resumen contiene", + "count": "Conteo de activación", + "windowMinutes": "Ventana (minutos)", + "agentStatus": "Estado del agente", + "minutes": "Minutos", + "totalTokens": "Total de tokens", + "cooldown": "Tiempo de espera (segundos)" + }, + "deleteTitle": "¿Eliminar regla de alerta?", + "help": { + "name": "Una etiqueta para esta regla. Aparece en el feed de alertas y se incluye en los payloads de webhook (rule_name). p. ej., 'Errores Bash repetidos'.", + "type": "Qué observa esta regla. event_pattern coincide con el flujo de eventos en vivo; inactivity = una sesión activa queda en silencio; status_duration = un agente está bloqueado en un estado; token_threshold = el uso de tokens de una sesión cruza un límite.", + "eventType": "El evento hook de Claude Code a coincidir. Dejar en blanco para coincidir con cualquier tipo de evento. Se compara de forma exacta (sensible a mayúsculas).", + "toolName": "La herramienta que invocó el agente (en eventos PreToolUse / PostToolUse). Dejar en blanco para coincidir con cualquier herramienta. Se compara de forma exacta.", + "summaryContains": "Coincidencia de subcadena insensible a mayúsculas con el texto de resumen del evento. Útil para detectar errores o frases específicas. Dejar en blanco para omitir.", + "count": "Cuántos eventos coincidentes deben ocurrir antes de que se dispare la alerta. 1 = disparar en la primera coincidencia. Establecer por encima de 1 para detectar ráfagas (p. ej., errores repetidos).", + "window": "La ventana de retroceso continua para el conteo. p. ej., conteo 5 + ventana 2 significa '5 eventos coincidentes dentro de cualquier intervalo de 2 minutos'.", + "status": "Qué estado del agente observar. 'working' detecta agentes bloqueados a mitad de tarea; 'waiting' detecta agentes bloqueados en entrada/permiso.", + "minutesInactivity": "Disparar cuando una sesión activa no ha tenido eventos durante este número de minutos (una sesión bloqueada u olvidada).", + "minutesStatus": "Disparar cuando un agente permanece en el estado seleccionado sin actividad durante este número de minutos (un agente bloqueado).", + "totalTokens": "Disparar cuando los tokens acumulados de una sesión (entrada + salida + lectura de caché + escritura de caché) superan este número. p. ej., 1000000 = 1M tokens.", + "cooldown": "Segundos mínimos entre alertas repetidas para la misma regla + sesión + agente. Evita duplicados ruidosos. Por defecto 300 (5 min); 0 = sin tiempo de espera." + } + }, + "feed": { + "title": "Feed de alertas", + "unackedCount": "{{count}} sin reconocer", + "unackedOnly": "Solo sin reconocer", + "ack": "Reconocer", + "viewSession": "Ver sesión", + "loadMore": "Cargar más ({{shown}} de {{total}})", + "emptyTitle": "Sin alertas", + "emptyDescription": "Las alertas disparadas por tus reglas aparecerán aquí en tiempo real.", + "emptyUnacked": "Todo está reconocido. Las nuevas alertas aparecerán aquí cuando se disparen." + } +} diff --git a/client/src/i18n/locales/es/analytics.json b/client/src/i18n/locales/es/analytics.json new file mode 100644 index 00000000..0b7dc4d8 --- /dev/null +++ b/client/src/i18n/locales/es/analytics.json @@ -0,0 +1,54 @@ +{ + "title": "Analíticas", + "subtitle": "Monitoreo y analíticas en tiempo real para sesiones de Claude Code", + "export": "Exportar", + "totalSessions": "Total de Sesiones", + "totalAgents": "Total de Agentes", + "totalTokens": "Total de Tokens", + "totalCost": "Costo Total", + "totalEvents": "Total de Eventos", + "cacheHitRate": "% de tasa de aciertos de caché", + "perSession": " por sesión", + "noDataCost": "Sin datos", + "eventCountLabel_one": "{{count}} evento", + "eventCountLabel_other": "{{count}} eventos", + "tabs": { + "tokenAnalytics": "Analíticas de Tokens", + "costAnalytics": "Analíticas de Costo", + "workflowIntelligence": "Inteligencia de Flujos", + "productivityAnalytics": "Analíticas de Productividad" + }, + "eventActivity": "Actividad de Eventos - Últimas 52 Semanas", + "last30Days": "Últimos 30 Días", + "dailyEventCount": "Conteo diario de eventos", + "peakDay": "Día pico", + "total30d": "Total (30d)", + "tokenDistribution": "Distribución de Tokens", + "tokenMix": "Mezcla de Tokens", + "cacheEfficiency": "Eficiencia de caché", + "tokenBreakdown": "Desglose de Tokens", + "tokenInfo": "Datos de tokens capturados de los eventos Stop de Claude Code.", + "costByModel": "Costo por Modelo", + "dailyCostTrends": "Tendencias de Costo Diario", + "costByWeekday": "Costo por Día de la Semana", + "costPerDay": "Costo por día", + "peakCostDay": "Día de mayor costo", + "totalCost30d": "Costo total (30d)", + "noDailyCostData": "Sin datos de costo diario aún.", + "noCostData": "Sin datos de costo aún.", + "subagentTypes": "Tipos de Subagentes", + "noSubagentData": "Sin datos de subagentes aún.", + "agentStatus": "Estado del Agente", + "totalAgentsLabel": "Total de agentes", + "eventTypes": "Tipos de Eventos", + "noEventData": "Sin datos de eventos aún.", + "toolUsage": "Uso de Herramientas", + "noToolData": "Sin datos de uso de herramientas aún.", + "sessionOutcomes": "Resultados de Sesiones", + "totalSessionsLabel": "Total de sesiones", + "dailySessionTrends": "Tendencias de Sesiones Diarias", + "noSessionTrendData": "Sin datos de tendencias de sesiones aún.", + "last7Days": "Últimos 7 días", + "less": "Menos", + "more": "Más" +} diff --git a/client/src/i18n/locales/es/ccConfig.json b/client/src/i18n/locales/es/ccConfig.json new file mode 100644 index 00000000..60e1cb54 --- /dev/null +++ b/client/src/i18n/locales/es/ccConfig.json @@ -0,0 +1,264 @@ +{ + "title": "Explorador de Config de Claude", + "subtitle": "Tu configuración de Claude Code de un vistazo - plugins, skills, subagentes, servidores MCP, hooks y configuración.", + "refresh": "Actualizar", + "refreshing": "Actualizando…", + "lastUpdated": "Última actualización {{time}}", + "loadError": "Error al cargar: {{message}}", + "scope": { + "all": "Todos los ámbitos", + "user": "Usuario", + "project": "Proyecto", + "projectLocal": "Proyecto (local)", + "home": "Inicio" + }, + "tabs": { + "overview": "Resumen", + "skills": "Skills", + "agents": "Subagentes", + "commands": "Comandos de barra", + "outputStyles": "Estilos de salida", + "plugins": "Plugins", + "marketplaces": "Marketplaces", + "mcp": "Servidores MCP", + "hooks": "Hooks", + "keybindings": "Atajos de teclado", + "settings": "Configuración", + "memory": "Memoria" + }, + "overview": { + "rootsTitle": "Raíces", + "claudeHome": "Inicio de Claude", + "projectClaudeDir": "Proyecto .claude", + "projectRoot": "Raíz del proyecto", + "claudeJson": "claude.json", + "summary": "Resumen rápido", + "user": "usuario", + "project": "proyecto", + "items": "elementos" + }, + "common": { + "search": "Buscar por nombre…", + "clearSearch": "Limpiar búsqueda", + "empty": "Nada aquí aún.", + "viewSource": "Ver fuente", + "close": "Cerrar", + "copyPath": "Copiar ruta", + "copied": "Copiado", + "truncated": "(truncado para vista previa)", + "size": "Tamaño", + "modified": "Modificado", + "scopeLabel": "Ámbito", + "noFrontmatter": "Sin frontmatter", + "redactedNotice": "Los valores que coincidan con /token|secret|password|key|auth/i son ocultados en el servidor." + }, + "skills": { + "name": "Nombre", + "description": "Descripción" + }, + "agents": { + "name": "Nombre", + "model": "Modelo", + "tools": "Herramientas" + }, + "plugins": { + "manifest": "Manifiesto", + "marketplace": "Marketplace", + "version": "Versión", + "installedAt": "Instalado", + "lastUpdated": "Actualizado", + "installPath": "Ruta de instalación", + "missing": "Ruta de instalación faltante", + "manifestMissing": "No se encontró installed_plugins.json en {{path}}", + "enabled": "Habilitado", + "disabled": "Deshabilitado", + "contributes": "Contribuye", + "skills_one": "{{count}} skill", + "skills_other": "{{count}} skills", + "agents_one": "{{count}} agente", + "agents_other": "{{count}} agentes", + "commands_one": "{{count}} comando", + "commands_other": "{{count}} comandos", + "outputStyles_one": "{{count}} estilo de salida", + "outputStyles_other": "{{count}} estilos de salida", + "hooks_one": "{{count}} script de hook", + "hooks_other": "{{count}} scripts de hook", + "author": "Autor", + "homepage": "Página de inicio", + "license": "Licencia", + "description": "Descripción" + }, + "marketplaces": { + "manifest": "Manifiesto de marketplaces conocidos", + "noMarketplaces": "No hay marketplaces registrados.", + "source": "Fuente", + "lastUpdated": "Última sincronización", + "pluginCount": "Plugins", + "owner": "Propietario", + "installLocation": "Ruta de instalación", + "addCmd": "claude plugin marketplace add " + }, + "keybindings": { + "file": "Archivo fuente", + "missing": "No se encontró keybindings.json en {{path}}", + "context": "Contexto", + "key": "Tecla", + "action": "Acción", + "docsLink": "Ver documentación de atajos", + "contextPlaceholder": "Contexto (p. ej., Global, Chat)", + "addContext": "Añadir contexto", + "removeContext": "Eliminar contexto", + "addBinding": "Añadir atajo", + "removeBinding": "Eliminar atajo", + "errContext": "Cada contexto necesita un nombre.", + "errDupContext": "Contexto duplicado: {{context}}", + "errEmpty": "Cada atajo en \"{{context}}\" necesita tanto una tecla como una acción.", + "errDupKey": "Tecla duplicada \"{{key}}\" en el contexto \"{{context}}\"." + }, + "statusline": { + "title": "Barra de estado", + "configured": "Comando de barra de estado configurado", + "scripts": "Scripts de barra de estado encontrados en CLAUDE_HOME", + "noStatusline": "No hay barra de estado configurada." + }, + "hookScripts": { + "title": "Scripts ~/.claude/hooks/", + "subtitle": "Scripts de ayuda referenciados por tus comandos hook. Se ejecutan cuando settings.json `hooks..hooks[].command` los invoca.", + "empty": "No hay scripts en ~/.claude/hooks/." + }, + "mcp": { + "userScope": "Servidores de ámbito de usuario", + "projectScope": "Servidores de ámbito de proyecto", + "command": "Comando", + "args": "Args", + "env": "Nombres de variables de entorno", + "url": "URL", + "headers": "Nombres de cabeceras", + "kind": "Tipo", + "source": "Fuente", + "noServers": "No se encontraron servidores MCP." + }, + "hooks": { + "noHooks": "No hay hooks configurados.", + "matcher": "Matcher", + "command": "Comando", + "type": "Tipo", + "timeout": "Tiempo de espera", + "fileMissing": "(no hay archivo de configuración en este ámbito)" + }, + "settings": { + "fileMissing": "(no hay archivo de configuración en este ámbito)" + }, + "memory": { + "userScope": "Memoria de usuario", + "projectScope": "Memoria de proyecto", + "missing": "No se encontró CLAUDE.md.", + "autoTitle": "Almacén de memoria del proyecto", + "autoSubtitle": "Memoria basada en archivos por proyecto en ~/.claude/projects//memory/ — un índice MEMORY.md más un archivo por hecho recordado. Solo lectura aquí.", + "noMatches": "Ningún archivo de memoria coincide con tu búsqueda.", + "indexFiles": "Índice", + "jumpTo": "Ir al archivo", + "fileCount_one": "{{count}} archivo", + "fileCount_other": "{{count}} archivos", + "factFiles_one": "{{count}} hecho", + "factFiles_other": "{{count}} hechos", + "newFile": "Nuevo archivo", + "newFileTitle": "Nuevo archivo de memoria en {{project}}", + "namePlaceholder": "p. ej., mi_hecho.md", + "nameHelp": "Nombre de archivo terminado en .md (.md se añade si lo omites).", + "projectLabel": "Proyecto", + "projectHelp": "Creado en el directorio de memoria de este proyecto." + }, + "edit": { + "newButton": "Nuevo", + "editButton": "Editar", + "deleteButton": "Eliminar", + "save": "Guardar", + "cancel": "Cancelar", + "saving": "Guardando…", + "deleting": "Eliminando…", + "newTitle": "Crear nuevo {{type}}", + "editTitle": "Editar {{name}}", + "namePlaceholder": "nombre (kebab-case, sin espacios)", + "nameLabel": "Nombre", + "nameHelp": "Letras, dígitos, punto, guión, guión bajo. Máx 64 caracteres.", + "contentLabel": "Contenido", + "scopePicker": "Ámbito", + "writeError": "Error al guardar: {{message}}", + "deleteError": "Error al eliminar: {{message}}", + "saveSuccess": "Guardado · copia de seguridad en {{path}}", + "saveSuccessNew": "Creado.", + "deleteSuccess": "Eliminado · copia de seguridad en {{path}}", + "confirmDelete": "¿Eliminar este elemento?", + "confirmDeleteBody": "Esto moverá el archivo (o carpeta de skill, incluidos los activos empaquetados) a una copia de seguridad con marca de tiempo antes de eliminarlo. La copia de seguridad vive fuera de los directorios que Claude Code escanea, por lo que la entrada desaparecerá de tu CLI.", + "confirmDeletePath": "Ruta: {{path}}", + "confirmDeleteAction": "Sí, hacer copia de seguridad y eliminar", + "templates": { + "skills": "---\nname: my-skill\ndescription: Descripción de una línea (mostrada cuando Claude elige el skill)\n---\n\nCuerpo - qué hace el skill, cuándo usarlo, ejemplos.\n", + "agents": "---\nname: my-agent\ndescription: Cuándo invocar este subagente\ntools: Read, Grep\nmodel: sonnet\n---\n\nPrompt del sistema para el subagente.\n", + "commands": "---\ndescription: Descripción de una línea mostrada en el menú de barra\n---\n\nQué hace /mi-comando.\n", + "output-styles": "---\nname: my-style\ndescription: Descripción del estilo de salida\n---\n\nInstrucciones de estilo.\n", + "memory": "# Memoria del proyecto\n\nClaude lee esto al inicio de la sesión.\n", + "auto-memory": "---\nname: my-fact\ndescription: Resumen de una línea del hecho recordado\nmetadata:\n type: feedback\n---\n\nEl hecho a recordar.\n" + } + }, + "backups": { + "title": "Copias de seguridad", + "empty": "Sin copias de seguridad aún - cada edición o eliminación crea una copia con marca de tiempo que aparece aquí.", + "viewLink": "Mostrar historial de copias de seguridad", + "openButton": "Copias de seguridad", + "subtitle": "Cada guardado / eliminación en el editor escribe una copia aquí primero. Las carpetas de copia de seguridad viven fuera de los directorios que Claude Code escanea, por lo que no reaparecen como fantasmas en tu CLI.", + "restoreHint": "Restaura renombrando la copia de seguridad de vuelta a su lugar:", + "size": "Tamaño", + "created": "Creado", + "type": "Tipo", + "scopeLabel": "Ámbito", + "filterAll": "Todos los tipos" + }, + "explain": { + "hooks": { + "title": "Los hooks son de solo lectura aquí a propósito", + "body": "Los hooks viven en el bloque `hooks` de los archivos `settings.json` que Claude Code escribe constantemente. Editarlos desde el panel competiría con la CLI en vivo y podría romper silenciosamente la ingesta propia del panel (la entrada `hook-handler.js` abajo).", + "howTo": "Edítalos tú mismo con uno de:", + "cmd1": "claude config /hooks", + "cmd1Note": "Editor interactivo de hooks dentro de Claude Code", + "cmd2": "$EDITOR ~/.claude/settings.json", + "cmd2Note": "Editar a mano el bloque de hooks de nivel de usuario", + "cmd3": "$EDITOR .claude/settings.json", + "cmd3Note": "Editar a mano el bloque de hooks de nivel de proyecto" + }, + "settings": { + "title": "La configuración es de solo lectura aquí a propósito", + "body": "Estos archivos JSON son reescritos por la CLI `claude` en vivo en cada cambio de modelo, cambio de tema, habilitación de plugin, etc. Una escritura del panel competiría con la CLI y perdería estado.", + "howTo": "Edita la configuración a través del propio Claude Code:", + "cmd1": "claude config set ", + "cmd1Note": "Establecer una clave individual de forma segura", + "cmd2": "/config", + "cmd2Note": "Abrir la UI de configuración interactiva dentro de la CLI", + "cmd3": "$EDITOR ~/.claude/settings.json", + "cmd3Note": "Editar a mano (solo cuando no haya ningún proceso `claude` en ejecución)" + }, + "plugins": { + "title": "Los plugins son gestionados por la CLI", + "body": "Los plugins son paquetes con su propio registro - `installed_plugins.json` más rutas de instalación bajo `plugins/cache/`. El panel los lee pero delega la instalación/desinstalación a `claude plugin …` para que el registro se mantenga consistente.", + "install": "Instalar un plugin:", + "installCmd": "claude plugin install /", + "uninstall": "Desinstalar este plugin:", + "uninstallButton": "Copiar comando de desinstalación" + }, + "mcp": { + "title": "Los servidores MCP son gestionados por la CLI", + "body": "La configuración del servidor MCP vive dentro de `~/.claude.json`, el mismo archivo que la CLI en vivo escribe constantemente. Las escrituras concurrentes desde el panel corromperían el estado del proyecto, por lo que el flujo de añadir/eliminar pasa por el propio Claude Code.", + "list": "Listar servidores configurados:", + "listCmd": "claude mcp list", + "add": "Añadir un servidor:", + "addCmd": "claude mcp add [args…]", + "remove": "Eliminar este servidor:", + "removeButton": "Copiar comando de eliminación" + } + }, + "snippet": { + "copy": "Copiar", + "copied": "Copiado" + } +} diff --git a/client/src/i18n/locales/es/common.json b/client/src/i18n/locales/es/common.json new file mode 100644 index 00000000..0e1d84bf --- /dev/null +++ b/client/src/i18n/locales/es/common.json @@ -0,0 +1,192 @@ +{ + "refresh": "Actualizar", + "retry": "Reintentar", + "cancel": "Cancelar", + "export": "Exportar", + "loading": "Cargando...", + "noData": "Sin datos", + "total": "Total", + "active": "activo", + "total_lower": "total", + "subagent": "subagente", + "subagents": "subagentes", + "subagent_label_one": "{{count}} subagente", + "subagent_label_other": "{{count}} subagentes", + "events": "eventos", + "event": "evento", + "sessions": "sesiones", + "session": "sesión", + "agents": "agentes", + "agent": "agente", + "live": "En vivo", + "offline": "Sin conexión", + "running": "ejecutando", + "dashboardRun": "Ejecutar", + "days": "días", + "hours": "horas", + "updated": "Actualizado ", + "lastUpdated": "Última actualización: ", + "showMore": "Mostrar más ({{count}} restantes)", + "plusMore": "+{{count}} más", + "ofSessions": "% de las sesiones", + "save": "Guardar", + "edit": "Editar", + "delete": "Eliminar", + "actions": "Acciones", + "close": "Cerrar", + "unexpectedError": "Error inesperado", + "depth": "Profundidad", + "type": "Tipo", + "statusLabel": "Estado", + "status": { + "working": "Trabajando", + "waiting": "En espera", + "completed": "Completado", + "error": "Error", + "active": "Activo", + "abandoned": "Abandonado" + }, + "awaitingReason": { + "notification": { + "label": "Necesita entrada", + "desc": "Bloqueado por un aviso de permiso o una solicitud de entrada — Claude no puede continuar hasta que respondas." + }, + "stop": { + "label": "Turno terminado", + "desc": "Claude terminó su respuesta; la sesión está inactiva hasta que envíes el siguiente prompt." + }, + "session_start": { + "label": "En prompt", + "desc": "La sesión acaba de comenzar o reanudarse y está en un prompt vacío — aún no se ha solicitado nada." + }, + "interrupted": { + "label": "Interrumpido", + "desc": "El último turno fue interrumpido (Esc o un hook recuperado) — Claude está esperando dirección." + } + }, + "time": { + "justNow": "ahora mismo", + "mAgo": "hace {{count}}m", + "hAgo": "hace {{count}}h", + "dAgo": "hace {{count}}d" + }, + "pagination": { + "previous": "Anterior", + "next": "Siguiente", + "showing": "Mostrando {{from}}-{{to}} de {{total}}" + }, + "token": { + "input": "Entrada", + "output": "Salida", + "cacheRead": "Lectura de Caché", + "cacheWrite": "Escritura de Caché", + "totalTokens": "Total de tokens" + }, + "cost": { + "model": "Modelo", + "cost": "Costo", + "totalCost": "Costo Total", + "totalEstimatedCost": "Costo Total Estimado" + }, + "duration": { + "h_m": "{{h}}h {{m}}m", + "m_s": "{{m}}m {{s}}s", + "s": "{{s}}s" + }, + "eventDetail": { + "summary": "Resumen", + "seeDetailsBelow": "Consulta más abajo los detalles completos en {{fields}}", + "eventId": "ID de Evento", + "recordedAt": "Registrado en", + "session": "Sesión", + "sessionId": "ID de Sesión", + "agentId": "ID de Agente", + "agent": "Agente", + "cwd": "Directorio de trabajo", + "permissionMode": "Modo de permiso", + "toolUseId": "ID de Uso de Herramienta", + "toolName": "Herramienta", + "toolInput": "Entrada de Herramienta", + "toolResponse": "Respuesta de Herramienta", + "isError": "Error", + "transcriptPath": "Ruta de Transcripción", + "agentTranscriptPath": "Ruta de Transcripción del Subagente", + "hookEventName": "Nombre del Evento Hook", + "stopReason": "Razón de Parada", + "stopHookActive": "Hook de Parada Activo", + "subagentType": "Tipo de Subagente", + "agentType": "Tipo de Agente", + "notificationType": "Tipo de Notificación", + "message": "Mensaje", + "prompt": "Prompt", + "model": "Modelo", + "reason": "Razón", + "source": "Fuente", + "imported": "Importado", + "type": "Tipo", + "timestamp": "Marca de tiempo", + "uuid": "UUID", + "durationMs": "Duración (ms)", + "compactionNumber": "Número de Compresión", + "totalCompactions": "Total de Compresiones", + "lastAssistantMessage": "Último Mensaje del Asistente", + "rawPayload": "Payload Raw", + "copy": "Copiar", + "copied": "Copiado", + "noPayload": "No se capturó payload para este evento.", + "viewDetails": "Ver detalles", + "expand": "Expandir evento", + "collapse": "Colapsar evento" + }, + "eventFilters": { + "searchPlaceholder": "Buscar resumen, herramienta, payload...", + "status": "Estado", + "eventType": "Tipo de evento", + "toolName": "Herramienta", + "agentId": "Agente", + "sessionId": "Sesión", + "from": "Desde", + "to": "Hasta", + "clearAll": "Limpiar filtros", + "noOptions": "Sin opciones", + "showing": "Mostrando {{shown}} de {{total}}", + "loadMore": "Cargar más", + "loading": "Cargando...", + "noResults": "Ningún evento coincide con los filtros actuales", + "grouped": "Agrupar por llamada de herramienta", + "flat": "Lista plana", + "groupEventCount_one": "{{count}} evento", + "groupEventCount_other": "{{count}} eventos", + "help": { + "toggle": "Ayuda", + "title": "Acerca de eventos, filtros y agrupación", + "subtitle": "Desbloquea información más profunda: insignias, ciclos de vida y mecánicas de filtros explicados.", + "valuesTitle": "Qué acepta cada filtro", + "valueStatusDesc": "Atajo preestablecido que se expande a valores de event_type (Working → PreToolUse, Completed → Stop/SubagentStop/Compaction, ...).", + "valueEventTypeDesc": "Cadenas de event_type crudas de la BD. Obtenidas de /api/events/facets.", + "valueToolNameDesc": "Valores de tool_name actualmente en la BD (Bash, Edit, Read, Grep, herramientas mcp__...). Obtenidos de /api/events/facets.", + "valueAgentIdDesc": "UUIDs completos de agentes (principal + subagentes). Selecciona uno o más para acotar a agentes específicos.", + "valueSessionIdDesc": "UUIDs de sesión. Normalmente navegarías a /sessions/:id en su lugar, pero esto te permite intersectar múltiples sesiones.", + "valueSearchDesc": "Coincidencia de subcadena en resumen, tool_name y el payload JSON raw. Rebotado 300ms.", + "valueDateRangeDesc": "Límites inferior/superior de fecha y hora ISO en el created_at del evento.", + "statusesTitle": "Insignias de estado", + "statusesIntro": "El estado se deriva de event_type. Es lo que ves en cada fila.", + "statusWorkingDesc": "El agente inició una llamada de herramienta (PreToolUse). La herramienta está a punto de ejecutarse.", + "statusConnectedDesc": "La llamada de herramienta se completó y devolvió un resultado (PostToolUse).", + "statusCompletedDesc": "Límite de turno o ciclo de vida (Stop, SubagentStop, Compaction).", + "statusErrorDesc": "Se registró un error (error, APIError).", + "statusIdleDesc": "Ciclo de vida o metadatos (Notification, TurnDuration, SessionStart, SessionEnd).", + "lifecycleTitle": "Ciclo de vida del evento", + "lifecycleDesc": "Una llamada de herramienta típica emite dos eventos compartiendo el mismo tool_use_id:", + "lifecycleFlow": "PreToolUse (Working) → PostToolUse (Connected) → ... → Stop (Completed)", + "filtersTitle": "Cómo funcionan los filtros", + "filterTip1": "Los filtros se componen con AND entre categorías. Dentro de una categoría, la selección múltiple es OR.", + "filterTip2": "Status es un atajo que se expande a valores de event_type (Working → PreToolUse, Completed → Stop/SubagentStop/Compaction, ...). Status + Event type se componen como OR en event_type.", + "filterTipGrouping": "Consejo de agrupación: una fila agrupada necesita ambos eventos Pre y Post. Si filtras por Status, elige Working + Connected juntos para mantener los pares intactos; de lo contrario, un grupo solo mostrará la mitad de la llamada.", + "filterTip3": "La búsqueda de texto coincide con el resumen, tool_name y el payload raw (columna data).", + "filterTip4": "El rango de fechas se aplica al created_at de cada evento." + } + }, + "examples": "Ejemplos", + "help": "Ayuda" +} diff --git a/client/src/i18n/locales/es/dashboard.json b/client/src/i18n/locales/es/dashboard.json new file mode 100644 index 00000000..f0fd0ee7 --- /dev/null +++ b/client/src/i18n/locales/es/dashboard.json @@ -0,0 +1,22 @@ +{ + "title": "Panel", + "subtitle": "Resumen en tiempo real de la actividad de los agentes de Claude Code", + "totalSessions": "Total de Sesiones", + "activeAgents": "Agentes Activos", + "activeSubagents": "Subagentes Activos", + "eventsToday": "Eventos Hoy", + "totalEvents": "Total de Eventos", + "totalCost": "Costo Total", + "viewBoard": "Ver Tablero", + "viewAll": "Ver Todo", + "activeAgentsSection": "Agentes Activos", + "recentActivity": "Actividad Reciente", + "noAgents": "Sin agentes activos", + "noAgentsDesc": "Los agentes aparecerán aquí cuando haya una sesión de Claude Code en ejecución.", + "noActivity": "Sin actividad aún", + "noActivityDesc": "Los eventos de las sesiones de Claude Code se transmitirán aquí en tiempo real.", + "failedConnect": "No se pudo conectar al servidor", + "failedLoad": "No se pudieron cargar los datos", + "activeTrend": " activo", + "totalTrend": " en sesiones activas" +} diff --git a/client/src/i18n/locales/es/errors.json b/client/src/i18n/locales/es/errors.json new file mode 100644 index 00000000..afff9792 --- /dev/null +++ b/client/src/i18n/locales/es/errors.json @@ -0,0 +1,31 @@ +{ + "notFound": { + "code": "404", + "title": "Página no encontrada", + "description": "La ruta solicitada no existe. Usa la navegación para volver a una página válida.", + "goDashboard": "Ir al Panel", + "goBack": "Volver" + }, + "notifications": { + "newSession": "Nueva Sesión", + "sessionDefault": "Sesión ", + "sessionError": "Error de Sesión", + "subagentSpawned": "Subagente Iniciado", + "finishedResponding": "Claude Terminó de Responder", + "readyForInput": "Listo para entrada", + "sessionCompleted": "Sesión Completada", + "sessionClosed": "Sesión cerrada", + "defaultTitle": "Claude Code", + "defaultBody": "Notificación" + }, + "toolLegend": { + "read": "Leer", + "write": "Escribir", + "edit": "Editar", + "bash": "Bash", + "grep": "Grep", + "glob": "Glob", + "agent": "Agent", + "other": "Otro" + } +} diff --git a/client/src/i18n/locales/es/kanban.json b/client/src/i18n/locales/es/kanban.json new file mode 100644 index 00000000..39f2c4dd --- /dev/null +++ b/client/src/i18n/locales/es/kanban.json @@ -0,0 +1,45 @@ +{ + "title": "Tablero Kanban", + "subtitle": "Vista Kanban de todos los agentes por estado", + "subtitleSessions": "Vista Kanban de todas las sesiones por estado", + "viewToggle": { + "agents": "Agentes", + "sessions": "Sesiones" + }, + "noAgents": "Sin agentes rastreados aún", + "noAgentsDesc": "Inicia una sesión de Claude Code con los hooks instalados para ver los agentes aquí.", + "agentCount": "{{count}} agente rastreado", + "agentCount_plural": "{{count}} agentes rastreados", + "noAgentsInColumn": "Sin agentes", + "noSessions": "Sin sesiones rastreadas aún", + "noSessionsDesc": "Inicia una sesión de Claude Code con los hooks instalados para ver las sesiones aquí.", + "sessionCount": "{{count}} sesión rastreada", + "sessionCount_plural": "{{count}} sesiones rastreadas", + "noSessionsInColumn": "Sin sesiones", + "ran": "ejecutó ", + "running": "ejecutando ", + "session": { + "anonymous": "Sesión sin título", + "agentSummary_one": "{{count}} agente", + "agentSummary_other": "{{count}} agentes", + "subagentSummary_one": "{{count}} subagente", + "subagentSummary_other": "{{count}} subagentes", + "turnSummary_one": "{{count}} turno", + "turnSummary_other": "{{count}} turnos" + }, + "tooltip": { + "agent": { + "working": "El agente está ejecutando activamente una llamada de herramienta ahora mismo.\n\nEntra en PreToolUse o UserPromptSubmit, sale en Stop (turno finalizado).", + "waiting": "El agente terminó su turno y está bloqueado por el humano — o bien Claude está esperando el siguiente mensaje del usuario o ha solicitado permiso para usar una herramienta.\n\nSe limpia en el siguiente PreToolUse / PostToolUse, o cuando la sesión termina.", + "completed": "El agente ha finalizado completamente para esta sesión — normalmente porque se disparó SessionEnd o, para subagentes, se procesó SubagentStop.\n\nNo se registrará más actividad para este agente.", + "error": "El agente registró un error y no continuará. Para agentes principales, esto proviene de un Stop con stop_reason=error; para subagentes, de una ejecución fallida." + }, + "session": { + "active": "La sesión está a mitad de turno — Claude está ejecutándose actualmente y el usuario no bloquea nada. Breve por naturaleza: una sesión pasa por Activa durante las llamadas de herramientas y vuelve a Espera al final del turno.", + "waiting": "La sesión está esperando al usuario. O bien Claude terminó su turno (más común) o ha presentado un aviso de permiso y está bloqueado hasta que el humano responda.\n\nBusca aquí las sesiones a las que debes responder.", + "completed": "La sesión se ha cerrado completamente — se disparó SessionEnd (CLI salió limpiamente) o el usuario la cerró explícitamente.\n\nTodos los agentes en esta sesión también están completados.", + "error": "La sesión terminó con un error — Claude devolvió stop_reason=error, ocurrió un fallo irrecuperable, o un error de API upstream terminó la ejecución.", + "abandoned": "La sesión quedó sin actividad sin un SessionEnd limpio. Marcada como abandonada después de 5+ minutos sin actividad (huérfana de /resume, caída de red, o la CLI fue terminada). Distinta de Completada: nunca vimos una señal de salida real." + } + } +} diff --git a/client/src/i18n/locales/es/nav.json b/client/src/i18n/locales/es/nav.json new file mode 100644 index 00000000..a4f2c6bd --- /dev/null +++ b/client/src/i18n/locales/es/nav.json @@ -0,0 +1,71 @@ +{ + "brand": "Agent Dashboard", + "brandSub": "Claude Code Monitor", + "dashboard": "Panel", + "agentBoard": "Tablero Kanban", + "sessions": "Sesiones", + "activityFeed": "Feed de Actividad", + "analytics": "Analíticas", + "workflows": "Flujos", + "alerts": "Alertas", + "ccConfig": "Config de Claude", + "run": "Ejecutar Claude", + "settings": "Configuración", + "language": "Idioma", + "github": "GitHub", + "website": "Sitio web", + "notFound": "No encontrado", + "skipToContent": "Saltar al contenido", + "expand": "Expandir barra lateral", + "collapse": "Colapsar barra lateral", + "collapseShort": "Colapsar", + "scrollUp": "Desplazar navegación hacia arriba", + "scrollDown": "Desplazar navegación hacia abajo", + "live": "En vivo", + "disconnected": "Desconectado", + "connectionDetails": "Detalles de conexión", + "connection": "Conexión", + "wsEndpoint": "WebSocket", + "connectionUptime": "Conectado", + "notConnected": "Sin conexión", + "eventsReceived": "Eventos recibidos", + "eventsTotal": "Total", + "eventsLastMin": "Últimos 60s", + "peakRate": "Pico", + "unitEvents": "eventos", + "unitPerSec": "eventos/s", + "throughput60s": "Rendimiento · últimos 60s", + "avg": "prom", + "topEventTypes": "Principales tipos de eventos", + "recentActivity": "Actividad reciente", + "statsPersisted": "Las estadísticas persisten entre recargas", + "resetStats": "Restablecer", + "lastEvent": "Último evento", + "noEventsYet": "Sin eventos aún", + "justNow": "ahora mismo", + "secondsAgo": "hace {{count}}s", + "minutesAgo": "hace {{count}}m", + "hoursAgo": "hace {{count}}h", + "daysAgo": "hace {{count}}d", + "close": "Cerrar", + "checkForUpdates": "Buscar actualizaciones", + "checkingForUpdates": "Buscando actualizaciones...", + "updateAvailable": "Actualización disponible", + "upToDate": "Al día", + "checkFailed": "Error al buscar actualizaciones", + "switchLanguage": "Cambiar a {{language}}", + "languageNames": { + "en": "Inglés", + "zh": "Chino", + "vi": "Vietnamita", + "ko": "Coreano", + "es": "Español" + }, + "languageShort": { + "en": "EN", + "zh": "中文", + "vi": "VI", + "ko": "한국어", + "es": "ES" + } +} diff --git a/client/src/i18n/locales/es/run.json b/client/src/i18n/locales/es/run.json new file mode 100644 index 00000000..2c632420 --- /dev/null +++ b/client/src/i18n/locales/es/run.json @@ -0,0 +1,161 @@ +{ + "title": "Ejecutar Claude", + "subtitle": "Inicia una sesión de Claude Code directamente desde el panel. Salida en streaming en vivo, conversación en múltiples turnos y las mismas analíticas impulsadas por hooks que tus sesiones de terminal.", + "mode": { + "label": "Modo", + "conversation": "Conversación", + "conversationHint": "Múltiples turnos — sigue escribiendo mientras el agente trabaja.", + "headless": "Único", + "headlessHint": "Un solo prompt, una sola respuesta. La entrada estándar se cierra tras el inicio." + }, + "fields": { + "prompt": "Prompt", + "promptPlaceholder": "Pregúntale cualquier cosa a Claude…", + "cwd": "Directorio de trabajo", + "cwdHint": "Ruta absoluta. Por defecto el cwd del propio panel.", + "cwdPlaceholder": "Escribe para buscar o pega una ruta absoluta…", + "cwdGroups": { + "dashboard": "Servidor del panel", + "home": "Inicio", + "recent": "Recientes - usados por Claude Code" + }, + "cwdNoMatches": "Sin coincidencias. Escribe una ruta absoluta para usarla directamente.", + "model": "Modelo", + "modelInheritLabel": "Heredar de configuración", + "modelCustom": "Personalizado…", + "modelCustomPlaceholder": "id del modelo (p. ej., claude-opus-4-7-20260101)", + "permissionMode": "Modo de permiso", + "permissionAcceptEdits": "acceptEdits (recomendado)", + "permissionDefault": "default (prompts interactivos - las sesiones pueden bloquearse sin cabeza)", + "permissionPlan": "plan (planificación de solo lectura)", + "permissionBypass": "bypassPermissions (PELIGRO - permite todo sin preguntar)", + "effort": "Esfuerzo de razonamiento", + "effortHint": "Controla cuánto razona el modelo antes de responder. Mayor = más tokens de pensamiento, más lento, más profundo. Vacío hereda el valor predeterminado del modelo." + }, + "actions": { + "start": "Ejecutar", + "starting": "Iniciando…", + "send": "Enviar", + "sending": "Enviando…", + "stop": "Detener", + "stopping": "Deteniendo…", + "newRun": "Nueva ejecución", + "viewSession": "Ver sesión" + }, + "status": { + "idle": "Inactivo", + "spawning": "Iniciando…", + "running": "Ejecutando", + "completed": "Completado", + "error": "Error", + "killed": "Terminado", + "abandoned": "Abandonado" + }, + "binary": { + "missing": "La CLI `claude` no está en tu PATH. Instala Claude Code o configura el PATH para que el panel pueda iniciarlo.", + "found": "Usando {{path}}" + }, + "concurrency": { + "active_one": "{{count}} ejecución ya en curso", + "active_other": "{{count}} ejecuciones ya en curso", + "viewActive": "Ver ejecuciones activas", + "atCap": "Límite de concurrencia alcanzado ({{max}}). Detén una de las ejecuciones activas para iniciar una nueva." + }, + "hint": { + "shortcut": "Cmd+Enter / Ctrl+Enter para enviar", + "permissionWarning": "bypassPermissions permite TODAS las invocaciones de herramientas sin confirmación. Usar solo para prompts de confianza.", + "headlessExplain": "El modo sin cabeza es ideal para tareas programadas donde sabes exactamente qué quieres. La sesión no puede hacer preguntas de seguimiento y se bloqueará en prompts de permiso a menos que estés en acceptEdits." + }, + "empty": { + "title": "Nada aún", + "body": "Escribe un prompt y haz clic en Ejecutar. La salida se transmite aquí mientras el modelo piensa, llama herramientas y responde." + }, + "events": { + "you": "Tú", + "claude": "Claude", + "tool": "Herramienta", + "toolResult": "Resultado de herramienta", + "system": "Sistema", + "thinking": "Pensando", + "expand": "Expandir", + "collapse": "Colapsar" + }, + "footer": { + "duration": "Duración", + "cost": "Costo", + "turns": "Turnos", + "exitCode": "Código de salida", + "sessionId": "ID de Sesión" + }, + "errors": { + "startFailed": "No se pudo iniciar la ejecución: {{message}}", + "sendFailed": "No se pudo enviar: {{message}}", + "killFailed": "No se pudo detener: {{message}}", + "attachFailed": "No se pudo adjuntar a la ejecución: {{message}}" + }, + "runs": { + "switcher": "Ejecuciones activas", + "switcherEmpty": "Sin ejecuciones activas", + "attached": "Adjuntado a ejecución existente", + "started": "Iniciado {{when}}", + "viewActive": "Ver ejecución activa", + "viewActive_other": "{{count}} ejecuciones activas", + "scopeNote": "Solo muestra ejecuciones que iniciaste desde este panel.", + "allSessionsLink": "Ver todas las sesiones de Claude Code →", + "viewLabel": "Ver" + }, + "resume": { + "label": "Sesión de origen", + "freshOption": "Nueva sesión", + "freshHint": "Inicia una sesión de Claude Code nueva.", + "resumeOption": "Reanudar sesión existente", + "resumeHint": "Elige una sesión de tu historial y continúa la conversación. El cwd queda bloqueado al original.", + "pickSession": "Elige una sesión…", + "search": "Buscar por ID, cwd o estado…", + "noSessions": "No se encontraron sesiones.", + "originalCwd": "Cwd original", + "preview": "Última actividad", + "selectedBadge": "Reanudando", + "clear": "Limpiar selección" + }, + "limitations": { + "title": "Ejecutar Claude en tu navegador", + "subtitle": "mismo binario, superficie diferente", + "peek": "La mayoría de las funciones TUI se transfieren. Algunas interactivas no.", + "why": "Por qué", + "intro": "Bajo el capó iniciamos el mismo binario `claude` que usa tu terminal — pero stream-json (la única interfaz programática que Claude expone) es una manguera de un solo sentido, no el TUI interactivo. Cualquier cosa que dependa de una sesión de terminal en ejecución — prompts de permiso en el lugar, la superposición de atajos de teclado, cambios de configuración a mitad de sesión — debe configurarse en el inicio en lugar de a mitad de conversación.", + "supported": "Funciona aquí", + "limited": "Solo CLI", + "tldr": "Si una función no está aquí, abre la misma sesión en tu terminal con `claude --resume ` — ambos lados comparten la misma transcripción JSONL en disco, por lo que nunca pierdes contexto al cambiar de superficie.", + "dismiss": "Entendido", + "minimize": "Minimizar", + "restore": "Mostrar notas de ejecución en navegador", + "expand": "Leer más", + "collapse": "Colapsar" + }, + "tokens": { + "label": "Contexto", + "input": "Entrada", + "output": "Salida", + "cacheRead": "Acierto de caché", + "cost": "Costo" + }, + "autocomplete": { + "slashHint": "Escribe / para comandos de barra", + "fileHint": "Escribe @ para referenciar un archivo", + "noMatches": "Sin coincidencias", + "groupBuiltin": "Integrado (solo CLI - no se ejecutará desde aquí)", + "groupUser": "Comandos de usuario", + "groupProject": "Comandos de proyecto", + "groupFiles": "Archivos en cwd" + }, + "statusHeader": { + "model": "Modelo", + "effort": "Esfuerzo", + "permissionMode": "Modo", + "cwd": "Cwd", + "session": "Sesión", + "envelopes": "Eventos", + "duration": "Transcurrido" + } +} diff --git a/client/src/i18n/locales/es/sessions.json b/client/src/i18n/locales/es/sessions.json new file mode 100644 index 00000000..48610942 --- /dev/null +++ b/client/src/i18n/locales/es/sessions.json @@ -0,0 +1,43 @@ +{ + "title": "Sesiones", + "searchPlaceholder": "Buscar sesiones...", + "filterAll": "Todas", + "filterActive": "Activas", + "filterWaiting": "En espera", + "filterCompleted": "Completadas", + "filterError": "Error", + "filterAbandoned": "Abandonadas", + "sessionCount": "{{count}} sesión registrada", + "sessionCount_plural": "{{count}} sesiones registradas", + "noSessions": "No se encontraron sesiones", + "noSessionsDesc": "Intenta ajustar tu búsqueda o los filtros.", + "noSessionsHint": "Inicia una sesión de Claude Code con los hooks instalados para comenzar el seguimiento.", + "tableSession": "Sesión", + "tableStatus": "Estado", + "tableLastActive": "Última Actividad", + "tableDuration": "Duración", + "tableAgents": "Agentes", + "tableCost": "Costo", + "tableDirectory": "Directorio", + "defaultName": "Sesión ", + "detail": { + "waitingBanner": { + "title": "Esperando tu entrada", + "generic": "Claude Code está pausado en esta sesión hasta que respondas en la terminal." + }, + "failedLoad": "No se pudo cargar la sesión", + "loading": "Cargando sesión...", + "notFound": "Sesión no encontrada", + "backToSessions": "Volver a Sesiones", + "agents": "Agentes", + "noAgents": "No se registraron agentes.", + "unparented": "Subagentes Sin Padre", + "costBreakdown": "Desglose de Costos", + "eventTimeline": "Línea de Tiempo de Eventos", + "noEvents": "No se registraron eventos.", + "conversation": "Conversación", + "timeline": "Línea de tiempo ({{shown}}/{{total}})", + "transcriptNotFound": "No se encontró la transcripción de la conversación para este agente. El archivo de transcripción puede estar ausente o aún no vinculado." + }, + "remoteSourceBadgeTitle": "Recopilado desde una máquina remota" +} diff --git a/client/src/i18n/locales/es/settings.json b/client/src/i18n/locales/es/settings.json new file mode 100644 index 00000000..6fa94e15 --- /dev/null +++ b/client/src/i18n/locales/es/settings.json @@ -0,0 +1,435 @@ +{ + "title": "Configuración", + "subtitle": "Gestiona precios, notificaciones, datos y hooks", + "exportData": "Exportar Datos", + "acrossSessions": "En todas las sesiones rastreadas", + "basedOnUsage": "Basado en el uso de tokens por modelo", + "tabby": { + "title": "Compañero Tabby", + "description": "Un gato flotante que reacciona a tus sesiones en vivo.", + "enable": "Mostrar Tabby", + "enableDesc": "Mostrar el compañero de esquina en todo el panel (⌘B para abrir)" + }, + "pricing": { + "title": "Precios de Modelos", + "description": "Tarifas por millón de tokens (USD). Usa % como comodín en los patrones. Algunos modelos tienen una tarifa introductoria por tiempo limitado (mostrada en violeta); el uso en o antes de su fecha de corte se factura a esa tarifa, y el uso posterior a la tarifa estándar.", + "introNote": "Intro ${{input}}/${{output}} hasta {{until}}", + "introRatesTitle": "Tarifas introductorias", + "introRatesHint": "Deja la fecha vacía para sin promoción", + "introUntil": "Hasta (fecha)", + "introUntilInvalid": "El corte introductorio debe ser una fecha YYYY-MM-DD", + "resetConfirm": "Confirmar Restablecimiento", + "resetDefaults": "Restablecer Valores Predeterminados", + "resetResult": "Restablecido a {{count}} reglas predeterminadas", + "addModel": "Añadir Modelo", + "pattern": "Patrón", + "patternPlaceholder": "claude-opus-4%", + "namePlaceholder": "Claude Opus 4", + "validationRequired": "El patrón y el nombre para mostrar son obligatorios", + "lastUpdated": "Última actualización: ", + "tooltip": { + "title": "Cómo funcionan los precios de los modelos", + "howItWorks": "Cómo funciona", + "howItWorksBody": "Cuando una sesión informa el uso, el panel encuentra la primera regla de precios cuyo patrón coincide con el id del modelo y multiplica (tokens de entrada / 1M) y (tokens de salida / 1M) por las tarifas a continuación para calcular el costo. Las reglas se evalúan de arriba a abajo - la primera coincidencia gana.", + "patternsTitle": "Sintaxis de patrones", + "patternsBody": "Los patrones son globs de estilo SQL. % es un comodín que coincide con cualquier carácter. Por ejemplo: claude-opus-4-7% coincide con cada id de modelo que comienza con claude-opus-4-7 (p. ej., claude-opus-4-7, claude-opus-4-7-20260101). claude-%-haiku coincide con cualquier variante haiku. Un id exacto como claude-sonnet-4-6 solo coincide consigo mismo.", + "manualUpdates": "Debes actualizar los precios manualmente", + "manualUpdatesBody": "Anthropic publica cambios de precios en su sitio web - no hay un feed en vivo. Cuando se lanza un nuevo modelo o cambian las tarifas, edita la regla coincidente (o añade una nueva encima del catch-all) para que las nuevas sesiones registradas se calculen correctamente. Las sesiones ya almacenadas conservan el precio que se aplicó en el momento de la ingesta.", + "apiPricing": "Solo de referencia - tarifas de API", + "apiPricingBody": "Estas son tarifas de API de pago por uso. Si usas Claude Code con un plan de suscripción (Pro, Max, Team o Enterprise), tu costo real es una tarifa mensual fija, no por token - por lo que estas cifras generalmente serán más altas que lo que realmente pagas. Trata los costos aquí como una estimación de uso de referencia, no una factura." + }, + "cacheWrite5m": "Escritura de Caché (5m)", + "cacheWrite1h": "Escritura de Caché (1h)", + "fastInput": "Entrada Rápida", + "fastOutput": "Salida Rápida" + }, + "claudeHome": { + "title": "CLAUDE_HOME", + "description": "Directorio raíz para los datos de sesión de Claude Code. Cambiar esto actualiza la ruta de escaneo para transcripciones e importaciones de forma inmediata.", + "save": "Guardar", + "saving": "Guardando...", + "current": "Actual:", + "placeholder": "/ruta/a/claude-home", + "saveFailed": "No se pudo actualizar CLAUDE_HOME" + }, + "hooks": { + "title": "Configuración de Hooks", + "description": "Los hooks de Claude Code reenvían eventos al panel en tiempo real.", + "allInstalled": "Todos los hooks instalados", + "incomplete": "Hooks incompletos", + "reinstall": "Reinstalar Hooks", + "success": "Hooks instalados correctamente", + "failed": "Error al instalar los hooks" + }, + "notifications": { + "title": "Notificaciones", + "description": "Notificaciones del navegador para eventos importantes. Requiere permiso.", + "enable": "Habilitar Notificaciones del Navegador", + "granted": "Permiso concedido", + "blocked": "Permiso bloqueado", + "required": "Permiso requerido", + "notifyWhen": "Notificarme cuando...", + "newSession": "Inicia una nueva sesión", + "sessionComplete": "Se completa una sesión", + "sessionError": "Errores de sesión", + "subagentSpawned": "Se inicia un subagente", + "testTitle": "Agent Monitor", + "testBody": "¡Las notificaciones funcionan!", + "sendTest": "Enviar Notificación de Prueba", + "disabledInfo": "Las notificaciones están deshabilitadas - habilita para recibir alertas de eventos de sesión" + }, + "import": { + "title": "Importar Historial", + "description": "Trae sesiones de Claude Code existentes al panel. Los conteos de tokens, el costo por modelo, las compresiones, los subagentes, los usos de herramientas y las duraciones de turno se reconstruyen a partir de las transcripciones JSONL originales - idéntico a cómo se capturan las sesiones en vivo.", + "accuracyNote": "Volver a ejecutar es seguro. Las sesiones se deduplican por ID de sesión, y las líneas de base de compresión garantizan que los totales de tokens nunca cuenten doble.", + "supportedFiles": "Entradas admitidas: .jsonl, .meta.json, .zip, .tar, .tar.gz, .tgz, .gz", + "instructions": "Cómo recopilar tu historial", + "stepLocate": "1. Localiza tu historial de Claude Code", + "stepLocateBody": "Cada sesión de Claude Code se almacena como una transcripción JSONL bajo la carpeta de proyectos. Cada subcarpeta lleva el nombre del directorio de trabajo donde comenzó la sesión (barras reemplazadas por guiones).", + "stepArchive": "2. Empaquétalo para transferencia (opcional)", + "stepArchiveBody": "Para mover el historial desde otra máquina, archiva primero toda la carpeta, luego cópiala (AirDrop, scp, USB, almacenamiento en la nube):", + "stepChoose": "3. Elige un modo de importación abajo", + "stepChooseBody": "Vuelve a escanear la ubicación predeterminada, apunta a cualquier carpeta donde hayas extraído el historial, o sube archivos/archivos directamente.", + "stepVerify": "4. Verifica tokens y costo", + "stepVerifyBody": "Después de importar, abre Analíticas → Costo para confirmar el desglose. Volver a importar la misma sesión siempre es seguro.", + "defaultLocation": "Ubicación predeterminada en esta máquina", + "locationFound": "Carpeta encontrada", + "locationMissing": "Carpeta no encontrada", + "projectsLabel": "proyectos", + "jsonlLabel": "Archivos JSONL", + "modeRescan": "Volver a escanear carpeta predeterminada", + "modeRescanDesc": "Volver a leer ~/.claude/projects e importar cualquier cosa nueva.", + "modeFolder": "Escanear una carpeta", + "modeFolderDesc": "Escanear cualquier directorio en esta máquina en busca de transcripciones JSONL (recorre subcarpetas).", + "modeUpload": "Subir archivos", + "modeUploadDesc": "Suelta archivos JSONL o un archivo comprimido (.zip / .tar.gz) directamente en el navegador.", + "modeBackup": "Restaurar copia de seguridad", + "modeBackupDesc": "Importar una exportación del panel (.json) desde Exportar datos — combinar el historial de otra máquina.", + "backupHint": "Suelta un .json de exportación aquí o haz clic para seleccionar", + "backupSub": "El archivo producido por el botón Exportar datos (parte superior de Configuración)", + "runRestore": "Restaurar", + "backupResult": { + "title": "Restauración completa", + "sessionsImported": "Sesiones añadidas: {{count}}", + "sessionsSkipped": "Ya presentes: {{count}}", + "events": "Eventos: {{count}}", + "pricing": "Reglas de precios: {{count}}", + "detail": "{{agents}} agentes · {{workflows}} flujos · {{runs}} ejecuciones · {{rules}} reglas de alerta" + }, + "folderPlaceholder": "/ruta/absoluta/a/historial-claude", + "folderHelper": "Ruta absoluta. La tilde (~) se expande. Las subcarpetas se escanean recursivamente.", + "runRescan": "Volver a Escanear Ahora", + "runScan": "Escanear Carpeta", + "runUpload": "Subir e Importar", + "dropzoneHint": "Arrastra archivos aquí o haz clic para seleccionar", + "dropzoneSub": "Acepta .jsonl, .meta.json, .zip, .tar, .tar.gz, .tgz, .gz", + "filesSelected": "{{count}} archivo(s) seleccionado(s)", + "clearSelection": "Limpiar", + "copy": "Copiar", + "copied": "Copiado", + "progress": { + "scan": "Escaneando directorio...", + "extract": "Extrayendo archivos comprimidos ({{processed}}/{{total}})...", + "parse": "Analizando transcripciones ({{processed}}/{{total}})...", + "complete": "Importación completa", + "error": "Error en la importación" + }, + "result": { + "imported": "Importado: {{count}}", + "backfilled": "Enriquecidos existentes: {{count}}", + "skipped": "Ya actualizados: {{count}}", + "errors": "Errores: {{count}}", + "filesScanned": "Archivos escaneados: {{count}}", + "title": "Último resultado de importación" + }, + "errors": { + "noFiles": "No se seleccionaron archivos", + "noJsonl": "No se encontraron archivos .jsonl en el contenido subido", + "pathRequired": "La ruta es obligatoria", + "pathInvalid": "La ruta debe ser absoluta", + "pathNotFound": "La ruta no existe", + "pathNotDir": "La ruta no es un directorio", + "generic": "Error en la importación: {{message}}" + } + }, + "data": { + "title": "Gestión de Datos", + "description": "Información de la base de datos, importación/exportación y limpieza.", + "dbOverview": "Resumen de la Base de Datos", + "dbSize": "Tamaño de BD", + "loadingDb": "Cargando información de la base de datos...", + "sessionCleanup": "Limpieza de Sesiones", + "cleanupDesc": "Abandonar sesiones obsoletas y purgar datos antiguos", + "abandonAfter": "Abandonar sesiones activas obsoletas de más de", + "purgeAfter": "Purgar sesiones completadas/con error de más de", + "confirmCleanup": "Confirmar Limpieza", + "runCleanup": "Ejecutar Limpieza", + "nothingToClean": "Nada que limpiar", + "abandonedResult": " sesiones abandonadas", + "purgedResult": " sesiones purgadas ({{events}} eventos, {{agents}} agentes)" + }, + "danger": { + "title": "Zona de Peligro", + "description": "Acciones irreversibles que eliminan datos permanentemente", + "warning": "Esto eliminará todas las sesiones, agentes, eventos y datos de tokens. ¿Estás seguro?", + "yesClearAll": "Sí, Eliminar Todo", + "clearAllData": "Eliminar Todos los Datos", + "clearedResult": "Se eliminaron {{count}} filas" + }, + "about": { + "title": "Acerca de", + "description": "Información del runtime del servidor.", + "release": "Versión", + "uiBuild": "UI build v{{version}} (reconstruye el cliente para actualizar)", + "uptime": "Tiempo activo", + "nodejs": "Node.js", + "platform": "Plataforma", + "wsClients": "Clientes WS", + "loadingInfo": "Cargando información del servidor..." + }, + "tables": { + "sessions": "sesiones", + "agents": "agentes", + "events": "eventos", + "sessionsWithCost": "sesiones con costo", + "pricingRules": "reglas de precios" + }, + "messages": { + "failedLoad": "Error al cargar", + "failedSave": "Error al guardar", + "failedDelete": "Error al eliminar", + "unknownError": "Desconocido", + "actionFailed": "Error: {{message}}" + }, + "webhooks": { + "title": "Notificaciones por Webhook", + "description": "Reenvía alertas disparadas a Slack, Discord, Microsoft Teams, o cualquier endpoint HTTP. La entrega es automática, se reintenta en caso de fallo, y nunca bloquea el monitoreo.", + "count_one": "{{count}} destino", + "count_other": "{{count}} destinos", + "add": "Añadir Webhook", + "loading": "Cargando webhooks…", + "empty": "Sin webhooks configurados. Añade uno para reenviar alertas a Slack, Discord, Teams o un endpoint personalizado.", + "scopedTo_one": "{{count}} regla", + "scopedTo_other": "{{count}} reglas", + "enabled": "Habilitado", + "test": "Probar", + "edit": "Editar", + "delete": "Eliminar", + "confirmDelete": "Confirmar", + "testOk": "Entregado (HTTP {{status}})", + "testFail": "Falló: {{error}}", + "addTitle": "Nuevo Webhook", + "editTitle": "Editar Webhook", + "fieldName": "Nombre", + "fieldNamePlaceholder": "p. ej., Slack del equipo #alertas", + "fieldType": "Tipo", + "type": { + "slack": "Slack", + "discord": "Discord", + "teams": "Microsoft Teams", + "generic": "Genérico (JSON personalizado)" + }, + "fieldUrl": "URL del Webhook", + "urlKeepHint": "dejar en blanco para mantener el actual", + "urlKeepPlaceholder": "•••••••• (sin cambios)", + "urlPlaceholder": { + "slack": "https://hooks.slack.com/services/…", + "discord": "https://discord.com/api/webhooks/…", + "teams": "https://outlook.office.com/webhook/…", + "generic": "https://example.com/webhook" + }, + "fieldSecret": "Secreto de firma (opcional)", + "secretPlaceholder": "Secreto HMAC-SHA256", + "secretKeepPlaceholder": "•••••••• (dejar en blanco para mantener)", + "secretHint": "Cuando se establece, las solicitudes se firman con una cabecera X-Webhook-Signature.", + "fieldHeaders": "Cabeceras personalizadas (opcional)", + "replaceHeaders": "Reemplazar cabeceras", + "headerKey": "Cabecera", + "headerValue": "Valor", + "addHeader": "Añadir cabecera", + "scopeAll": "Recibir alertas de todas las reglas", + "enabledOnSave": "Habilitado", + "save": "Guardar", + "create": "Crear", + "cancel": "Cancelar", + "urlOptional": "anulación opcional", + "urlAuto": "La URL se establece automáticamente a partir de las credenciales a continuación.", + "deleteTitle": "¿Eliminar webhook?", + "deleteMessage": "¿Eliminar \"{{name}}\"? Las alertas disparadas dejarán de entregarse a este destino. Esto no se puede deshacer.", + "guideToggle": "Cómo configurar {{provider}}", + "guideDocs": "Documentos oficiales de {{provider}}", + "guideStaleNote": "Las pantallas de configuración del proveedor cambian con frecuencia, por lo que estos pasos pueden estar desactualizados - siempre confirma con la documentación oficial." + }, + "alertsHub": { + "title": "Alertas", + "description": "Define reglas de alerta, enrútalas a canales de webhook y revisa lo que se ha disparado - todo en un solo lugar.", + "tabRules": "Reglas", + "tabChannels": "Canales", + "tabActivity": "Actividad", + "rulesHint": "Condiciones que desencadenan una alerta. Evaluadas en el servidor contra el flujo de eventos en vivo.", + "rulesEmptyHint": "Añade una regla para comenzar a alertar sobre errores, inactividad, agentes bloqueados o umbrales de tokens." + }, + "jumpTo": "Ir a", + "webhookGuides": { + "slack": { + "steps": [ + "Crea o abre una app de Slack en api.slack.com/apps.", + "Activa 'Incoming Webhooks', luego haz clic en 'Add New Webhook to Workspace'.", + "Elige el canal donde publicar y autoriza.", + "Copia la URL generada (https://hooks.slack.com/services/…) y pégala arriba." + ] + }, + "discord": { + "steps": [ + "Abre tu servidor → Configuración del servidor → Integraciones → Webhooks.", + "Haz clic en 'New Webhook', elige un canal, opcionalmente renómbralo.", + "Haz clic en 'Copy Webhook URL' y pégala arriba." + ] + }, + "teams": { + "steps": [ + "En el canal de Teams de destino, haz clic en ⋯ → 'Workflows'.", + "Elige la plantilla 'Post to a channel when a webhook request is received'.", + "Termina el flujo y copia la URL HTTP POST generada.", + "Pégala arriba. (Los conectores clásicos de Office 365 fueron retirados en 2025 - usa Workflows.)" + ] + }, + "google_chat": { + "steps": [ + "Abre el espacio de Google Chat → título del espacio → 'Apps & integrations'.", + "Haz clic en 'Webhooks' → 'Add webhook' y dale un nombre.", + "Copia la URL del webhook y pégala arriba." + ] + }, + "mattermost": { + "steps": [ + "Menú principal → Integraciones → Webhooks entrantes (un administrador debe habilitar los webhooks primero).", + "Haz clic en 'Add Incoming Webhook' y elige un canal.", + "Guarda, luego copia la URL del webhook y pégala arriba." + ] + }, + "rocketchat": { + "steps": [ + "Administración → Integraciones → Nuevo → Entrante.", + "Habilítalo, elige un canal y guarda.", + "Copia la 'Webhook URL' y pégala arriba." + ] + }, + "telegram": { + "steps": [ + "Envía un mensaje a @BotFather, ejecuta /newbot y copia el token del bot.", + "Añade el bot a tu grupo/canal (o simplemente envíale un DM).", + "Encuentra el ID del chat: envía un mensaje, abre https://api.telegram.org/bot/getUpdates, lee result[].message.chat.id.", + "Ingresa el token del Bot y el ID del Chat arriba - no se necesita URL." + ] + }, + "pagerduty": { + "steps": [ + "Abre el Servicio de destino → Integraciones → 'Add integration'.", + "Elige 'Events API v2'.", + "Copia la Integration Key (la clave de enrutamiento).", + "Ingrésala como Routing key arriba - la URL de la Events API está precompletada." + ] + }, + "opsgenie": { + "steps": [ + "Configuración → Integraciones → añade una integración 'API'.", + "Copia la clave API generada.", + "Anota la región de tu cuenta (US o EU).", + "Ingresa la clave API y la región arriba - la URL de alertas se deriva de la región." + ] + }, + "splunk_oncall": { + "steps": [ + "Integraciones → REST Endpoint, y habilítalo.", + "Copia la URL REST - ya contiene tu clave API.", + "Reemplaza el '$routing_key' al final por una de tus claves de enrutamiento.", + "Pega la URL completa arriba." + ] + }, + "zapier": { + "steps": [ + "Crea un Zap con el disparador 'Webhooks by Zapier' → 'Catch Hook'.", + "Copia la URL del webhook personalizado que te proporciona.", + "Pégala arriba. La alerta llega como JSON: { event, alert }." + ] + }, + "make": { + "steps": [ + "Añade un módulo 'Webhooks' → 'Custom webhook' como primer paso de un escenario.", + "Haz clic en 'Add', crea el hook y copia su URL.", + "Pégala arriba, luego ejecuta una vez para que Make aprenda la forma del payload." + ] + }, + "n8n": { + "steps": [ + "Añade un nodo 'Webhook' a tu flujo de trabajo; establece el método en POST.", + "Copia la URL de producción.", + "Activa el flujo de trabajo y pega la URL arriba (http está permitido para auto-alojado)." + ] + }, + "pipedream": { + "steps": [ + "Crea un flujo de trabajo con un disparador 'HTTP / Webhook'.", + "Copia la URL de endpoint única.", + "Pégala arriba; inspecciona las entregas en el inspector de eventos de Pipedream." + ] + }, + "generic": { + "steps": [ + "Apunta esto a cualquier endpoint que acepte un POST JSON (https, o http para pruebas locales).", + "Forma del cuerpo: { event: 'alert.triggered', source, sent_at, alert: { … } }.", + "Opcional: establece un secreto de firma (HMAC-SHA256 sobre el cuerpo → cabecera X-Webhook-Signature) y cabeceras personalizadas.", + "Consejo: usa un inspector gratuito como webhook.site para ver el payload exacto." + ] + } + }, + "remoteSources": { + "title": "Fuentes de Datos Remotas", + "description": "Recopila el uso de Claude Code de otras máquinas por SSH - p. ej., una caja de desarrollo o VM en la nube que controlas por SSH mientras ejecutas este panel localmente. La autenticación usa tu propia configuración SSH (~/.ssh/config, claves, agente); no se almacenan contraseñas aquí.", + "scopeTitle": "Alcance de datos", + "scopeDesc": "Elige qué datos de máquinas muestra todo el panel. Los cambios se aplican inmediatamente en todas las páginas — sesiones, analíticas y costo.", + "scopeAll": "Todas las fuentes", + "scopeAllDesc": "Esta máquina más cada fuente remota configurada, combinadas.", + "scopeLocal": "Solo esta máquina", + "scopeLocalDesc": "Solo sesiones recopiladas localmente — oculta todos los datos de fuentes remotas.", + "scopeSelected": "Fuentes seleccionadas", + "scopeSelectedDesc": "Elige exactamente qué máquinas incluir, abajo.", + "scopeSelectedCount": "{{n}} de {{total}} seleccionadas", + "scopePickMachines": "Máquinas a incluir", + "thisMachine": "Esta máquina", + "listTitle": "Fuentes configuradas", + "add": "Añadir fuente", + "addTitle": "Añadir una fuente remota", + "editTitle": "Editar fuente", + "fieldLabel": "Etiqueta", + "fieldHost": "Host SSH", + "fieldPort": "Puerto (opcional)", + "fieldIdentity": "Archivo de identidad (opcional)", + "fieldRemoteHome": "Inicio de Claude remoto (opcional)", + "fieldRemoteHomeHint": "Linux/macOS: por defecto ~/.claude (o una ruta absoluta como /home/tú/.claude). Windows SSH + Claude en WSL: dejar en blanco (autodetectar) o usar wsl:~/.claude. Windows nativo: C:/Users/tú/.claude.", + "fieldEnabled": "Sincronizar automáticamente en segundo plano", + "empty": "Sin fuentes remotas aún.", + "emptyHint": "Añade una máquina a la que accedas por SSH para importar su uso de Claude Code.", + "paused": "Sincronización automática desactivada", + "sessionCount": "{{n}} sesiones", + "sessionCountLinked": "{{n}} vinculadas", + "sessionCountHint": "Sesiones vinculadas a esta fuente (no todas las sesiones visibles cuando el alcance de datos es Todo)", + "lastSync": "Última sincronización: {{when}}", + "neverSynced": "Nunca sincronizado", + "imported": "{{n}} importadas", + "syncNew": "{{n}} nuevas", + "syncOnRemote": "{{n}} en remoto", + "test": "Probar", + "syncNow": "Sincronizar", + "syncAll": "Sincronizar todo", + "enable": "Habilitar", + "disable": "Deshabilitar", + "confirmDelete": "¿Eliminar esta fuente?", + "purgeData": "También eliminar las sesiones importadas de esta fuente (no se puede deshacer)", + "deleteAndPurge": "Eliminar + borrar datos", + "removeKeep": "Eliminar (conservar datos)", + "remoteSourceBadgeTitle": "Recopilado desde una máquina remota" + }, + "cursorPathsNote": "Informativo: Las sesiones de Cursor también se cuentan aquí — Cursor usa las mismas rutas ~/.claude que Claude Code (localmente y en remotos sincronizados)." +} diff --git a/client/src/i18n/locales/es/splash.json b/client/src/i18n/locales/es/splash.json new file mode 100644 index 00000000..b8e2dd38 --- /dev/null +++ b/client/src/i18n/locales/es/splash.json @@ -0,0 +1,85 @@ +{ + "greeting": { + "morning": "Buenos días", + "afternoon": "Buenas tardes", + "evening": "Buenas noches", + "night": "Trabajando tarde" + }, + "tagline": "Nada de lo que hacen tus agentes queda en la oscuridad.", + "sub1": "Observabilidad en tiempo real para cada sesión de Claude Code.", + "sub2": "Tokens, herramientas, costos y flujos, todo en vivo.", + "brand": "Claude Code Monitor", + "taglines": [ + "Nada de lo que hacen tus agentes queda en la oscuridad.", + "Ve cada movimiento que hacen tus agentes.", + "Tus agentes, completamente observables.", + "Cada token, herramienta y turno — a plena vista.", + "Observa cómo piensa tu flota.", + "Donde las sesiones de Claude Code cobran vida.", + "Ojos en tiempo real sobre cada agente.", + "Del prompt al resultado, todo registrado.", + "La sala de control para tus agentes de IA.", + "Cada subagente contabilizado.", + "Sabe exactamente lo que cuestan tus agentes.", + "Tus flujos agénticos, hechos legibles.", + "Observa. Mide. Optimiza.", + "Control de misión para Claude Code.", + "Convierte el caos de agentes en una imagen clara.", + "Los recibos de cada ejecución." + ], + "subs": [ + [ + "Observabilidad en tiempo real para cada sesión de Claude Code.", + "Tokens, herramientas, costos y flujos, todo en vivo." + ], + [ + "Sesiones, agentes y eventos en vivo a medida que ocurren.", + "Desgloses de tokens y costos por modelo, sin suposiciones." + ], + [ + "Rastrea cada llamada de herramienta a través de tu árbol de agentes.", + "Detecta el turno lento, el modelo costoso, el fallo silencioso." + ], + [ + "Los hooks se transmiten directamente a un panel local.", + "Tus datos nunca salen de tu máquina." + ], + [ + "Observa cómo los orquestadores delegan a subagentes.", + "DAGs, concurrencia y propagación de errores, visualizados." + ], + [ + "Seguimiento de costos hasta el token de caché.", + "Precios por modelo con líneas de base conscientes de compresión." + ], + [ + "Cada sesión, agente y evento en un solo lugar.", + "Busca, reproduce e inspecciona sin salir de la sala." + ], + [ + "Desde SessionStart hasta SessionEnd, capturado.", + "Compresiones, errores de API y duraciones de turno incluidos." + ], + [ + "Sabe qué modelo hizo qué y cuánto costó.", + "Subagentes con precio a su propia tarifa, no la del orquestador." + ], + [ + "Flotas de flujos y agentes internos, a la vista.", + "Incluso las ejecuciones que nunca disparan un hook." + ], + ["Una ventana local hacia tus agentes.", "Sin nube, sin cuenta, sin telemetría."], + [ + "Mide la productividad, no las sensaciones.", + "Velocidad, eficiencia de caché y efectividad de herramientas, puntuados." + ], + [ + "Detecta la anomalía antes de que lo haga la factura.", + "Valores atípicos de costo y picos de tokens, detectados temprano." + ], + [ + "Tu configuración de Claude Code, completamente mapeada.", + "Skills, agentes, hooks, MCP y memoria de un vistazo." + ] + ] +} diff --git a/client/src/i18n/locales/es/updates.json b/client/src/i18n/locales/es/updates.json new file mode 100644 index 00000000..471b8cd6 --- /dev/null +++ b/client/src/i18n/locales/es/updates.json @@ -0,0 +1,15 @@ +{ + "title": "Actualización disponible", + "lead": "Hay una nueva versión del panel disponible. Para actualizar, ejecuta el siguiente comando en tu terminal:", + "commitsBehind": "{{count}} commit(s) detrás de {{ref}}", + "copy": "Copiar comando", + "commandLabel": "Comando de terminal para actualizar el panel", + "copied": "Copiado", + "dismiss": "Descartar", + "fetchError": "No se pudo alcanzar el repositorio remoto. Inténtalo de nuevo cuando estés en línea.", + "notGit": "Esta instalación no es un clon de git; usa una ruta de actualización manual.", + "restartNote": "Cuando termine el comando, cierra y reinicia el panel de la misma forma en que lo iniciaste: si usaste npm start, vuelve a ejecutar npm start.", + "checkNow": "Buscar ahora", + "checking": "Buscando...", + "checkError": "No se pudieron buscar actualizaciones" +} diff --git a/client/src/i18n/locales/es/workflows.json b/client/src/i18n/locales/es/workflows.json new file mode 100644 index 00000000..fd0305b1 --- /dev/null +++ b/client/src/i18n/locales/es/workflows.json @@ -0,0 +1,468 @@ +{ + "title": "Flujos", + "subtitle": "Inteligencia de orquestación de agentes", + "failedLoad": "No se pudieron cargar los datos de flujos", + "filteredBy": "Filtrado por:", + "clearFilter": "Limpiar filtro", + "refreshData": "Actualizar datos", + "exportJson": "Exportar como JSON", + "allSessions": "Todas las Sesiones", + "activeOnly": "Solo Activas", + "completed": "Completadas", + "runs": { + "title": "Flujos Dinámicos", + "subtitle": "Flotas iniciadas por la herramienta Workflow, reconstruidas a partir de los diarios de ejecución en disco", + "sessionTitle": "Flujos Dinámicos", + "loading": "Cargando flujos dinámicos…", + "empty": "Sin flujos dinámicos aún. Las ejecuciones de la herramienta Workflow aparecen aquí una vez que se escribe su diario (al completarse).", + "noAgents": "No se registraron agentes internos para esta ejecución.", + "openSession": "Abrir sesión padre", + "filterPhase": "Filtrar por fase", + "clearFilter": "Limpiar", + "resultsLabel": "Resultados", + "promptLabel": "Prompt", + "resultLabel": "Resultado", + "loadingFull": "Cargando texto completo…", + "tokens": "tokens", + "agents_one": "{{count}} agente", + "agents_other": "{{count}} agentes", + "tools_one": "{{count}} llamada de herramienta", + "tools_other": "{{count}} llamadas de herramienta", + "status": { + "running": "Ejecutando", + "working": "Ejecutando", + "queued": "En cola", + "completed": "Completado", + "done": "Hecho", + "success": "Hecho", + "error": "Error", + "failed": "Falló" + }, + "col": { + "agent": "Agente", + "phase": "Fase", + "state": "Estado", + "tokens": "Tokens", + "tools": "Herramientas", + "duration": "Duración" + } + }, + "chartInfo": { + "labels": { + "what": "Qué muestra esto", + "howToRead": "Cómo leerlo", + "why": "Por qué importa" + }, + "orchestration": { + "what": "Un DAG por capas de cómo las sesiones de Claude Code se expanden: desde las sesiones de origen, hacia el agente principal, hacia cada tipo de subagente, a través de cualquier compresión de contexto, y finalmente hacia los resultados terminales (completado / error / abandonado).", + "howToRead": "Cada columna es una capa. El ancho de la caja es constante; la pequeña insignia es el conteo. Los bordes fluyen de izquierda a derecha; los bordes más gruesos = transiciones más frecuentes. Pasa el cursor sobre cualquier nodo para ver estadísticas; haz clic para filtrar el resto de la página por él.", + "why": "Úsalo para responder '¿a dónde va el trabajo en mis flujos?' - qué tipos de subagentes dominan, dónde entra la compresión, y qué caminos terminan en errores vs completaciones limpias." + }, + "toolFlow": { + "what": "Un gráfico estilo Sankey de transiciones consecutivas de llamadas de herramientas en cada sesión en el alcance. Cada banda es un par (origen → destino) contado en todas las sesiones.", + "howToRead": "Las herramientas aparecen como barras de colores a la izquierda y a la derecha. Las bandas las conectan: el grosor = número de veces que el origen se ejecutó inmediatamente antes que el destino. Pasa el cursor sobre una banda para ver el porcentaje de origen y destino; pasa el cursor sobre un nodo para ver su uso total y su participación en todas las llamadas de herramientas.", + "why": "Muestra cadenas de herramientas habituales (p. ej., Read → Edit → Bash). Útil para detectar bucles, saltos desperdiciados y falta de variedad de herramientas en el comportamiento del agente." + }, + "pipeline": { + "what": "Un grafo dirigido de co-ocurrencia de tipos de agentes: qué tipos de agentes tienden a ejecutarse secuencialmente después de cuáles, y con qué frecuencia.", + "howToRead": "Los círculos son tipos de agentes; el tamaño codifica el total de inicios. Las flechas van de A → B cuando se observó que B se ejecutaba poco después de A en la misma sesión, con la etiqueta = conteo. Pasa el cursor sobre una flecha para ver la participación del origen/destino; pasa el cursor sobre un círculo para ver inicios, sesiones y tasa de éxito. Arrastra los nodos para reorganizarlos.", + "why": "Muestra pipelines de delegación reales que quizás no diseñaste intencionalmente - p. ej., un agente de revisión de código que casi siempre sigue a un agente de compilación - para que puedas codificarlos o simplificarlos." + }, + "effectiveness": { + "what": "Una tarjeta por tipo de subagente con un anillo de tasa de éxito, sesiones totales, duración promedio y un sparkline de actividad de 7 días.", + "howToRead": "El relleno del anillo es la tasa de éxito; el porcentaje en el centro es el mismo. Los dos cuadros dan conteos y duración promedio en tiempo real. El sparkline son sesiones por día durante la última semana - pasa el cursor sobre una barra para el conteo diario.", + "why": "Verificación rápida de salud por agente: cuáles son confiables, cuáles son lentos, y cuáles están tendiendo al alza o a la baja en uso." + }, + "patterns": { + "what": "Una lista clasificada de secuencias recurrentes de orquestación de agentes detectadas a partir de trazas de sesiones, más un aviso para sesiones en solitario que se ejecutaron sin subagentes.", + "howToRead": "Cada fila es una secuencia de pasos (principal → sub → sub …). El lado derecho muestra cuántas sesiones coincidieron y qué porcentaje de todas las sesiones representa. Haz clic en un patrón para resaltarlo.", + "why": "Revela las 'formas' estándar del trabajo en tu proyecto. Los patrones frecuentes son buenos candidatos para formalizarse como flujos de trabajo o skills con nombre." + }, + "modelDelegation": { + "what": "Un flujo de dos columnas que muestra qué modelos principales delegan a qué modelos de subagentes, más conteos de agentes por nodo y tokens totales.", + "howToRead": "Columna izquierda = modelos principales para sesiones; columna derecha = modelos usados dentro de subagentes. Las líneas indican emparejamientos principal → subagente observados. Pasa el cursor sobre un nodo para ver la participación de todos los agentes, sesiones, tokens totales y una descripción de la familia del modelo (Opus / Sonnet / Haiku).", + "why": "Te ayuda a confirmar la jerarquía de costo/latencia - p. ej., principal en Sonnet, subagentes en Haiku - y detectar escalaciones no deseadas a Opus." + }, + "errorPropagation": { + "what": "Dónde se agrupan los errores en la jerarquía de agentes: por profundidad (sesión/principal vs subagente directo vs anidado), por tipo de agente y por categoría de error de API/sesión.", + "howToRead": "El banner superior da la tasa de error a nivel de sesión. Las barras de profundidad muestran cuántos errores ocurrieron en cada nivel de anidamiento. La lista de tipos propensos a errores clasifica los tipos de subagentes por conteo de errores. El bloque inferior muestra resúmenes de errores de API/sesión.", + "why": "Identifica si los fallos viven en la columna vertebral de orquestación (agente principal), en subagentes específicos, o en cadenas anidadas profundas - guiando dónde invertir en reintentos, validación o reescrituras." + }, + "concurrency": { + "what": "Temporización agregada de cuándo cada tipo de agente está activo en relación con la línea de tiempo de la sesión, más cuántas sesiones lo presentan.", + "howToRead": "Cada fila es un tipo de agente. La longitud de la barra es proporcional al conteo de sesiones. Los porcentajes de la derecha muestran la posición promedio de inicio y fin dentro de una sesión (0% = inicio de sesión, 100% = fin de sesión). Pasa el cursor para ver una descripción del patrón de temporización (carga frontal, a mitad de sesión, carga trasera, etc.).", + "why": "Revela el ritmo de tus sesiones - p. ej., lint al frente, revisión de código al final - y muestra agentes que monopolizan toda la sesión." + }, + "complexity": { + "what": "Un diagrama de dispersión de cada sesión: x = duración en tiempo real, y = total de agentes involucrados, tamaño de burbuja = tokens totales, color = estado final.", + "howToRead": "Las burbujas en la parte superior derecha son sesiones largas y con muchos agentes. Las burbujas grandes quemaron la mayor cantidad de tokens. Los rojos/amarillos = resultados de error/abandonado. Pasa el cursor sobre cualquier burbuja para estadísticas completas de la sesión; haz clic para profundizar en ella abajo.", + "why": "Da una vista única de la distribución de costo/complejidad. Los valores atípicos son las sesiones que vale la pena investigar por comportamiento desbocado o picos de costo." + }, + "compaction": { + "what": "Con qué frecuencia Claude activó la compresión de contexto en las sesiones: eventos totales, tokens recuperados y una distribución por sesión.", + "howToRead": "Los dos cuadros de estadísticas muestran los eventos de compresión totales y los tokens acumulados recuperados. El gráfico de barras lista las sesiones con al menos una compresión, ordenadas de forma descendente por conteo.", + "why": "Las compresiones indican presión de contexto. Una larga cola de sesiones con alta compresión generalmente significa flujos de trabajo que deberían dividirse, resumirse o migrarse a un modelo de contexto más largo." + }, + "drillIn": { + "what": "Vista de detalle por sesión: árbol completo de agentes, línea de tiempo de llamadas de herramientas y secuencia de eventos sin procesar para la sesión que selecciones.", + "howToRead": "Elige una sesión del menú desplegable de arriba o haz clic en una burbuja en el diagrama de dispersión. Las pestañas te permiten cambiar entre vistas jerárquica (Árbol de Agentes), temporal (Línea de Tiempo de Herramientas) y cronológica (Secuencia de Eventos) de la misma sesión.", + "why": "Cuando un gráfico agregado señala una sesión sospechosa, aquí es donde vas a leer exactamente qué sucedió, en orden." + } + }, + "stats": { + "avgAgentDepth": "Profundidad Promedio de Agentes", + "avgSubagentsPerSession": "Promedio de Subagentes / Sesión", + "agentSuccessRate": "Tasa de Éxito de Agentes", + "mostCommonFlow": "Flujo Más Común", + "avgCompactions": "Promedio de Compresiones", + "avgDuration": "Duración Promedio", + "tooltip": { + "howCalc": "Cómo se calcula", + "whatItMeans": "Qué significa este número", + "moreInfo": "Explicar esta métrica", + "valueMeansFmt": "{{value}} {{phrase}} significa {{interpretation}}", + "phrase": { + "depth": "profundidad promedio de agentes", + "subagents": "subagentes por sesión", + "success": "tasa de éxito de agentes", + "topFlow": "flujo más común", + "compactions": "compresiones por sesión", + "duration": "duración promedio" + }, + "calc": { + "depth": "Para cada sesión registramos el nivel más profundo de anidamiento de agentes (principal = 0, subagente directo = 1, sub-subagente = 2, …) y promediamos esos máximos en todas las sesiones del filtro actual.", + "subagents": "Número total de invocaciones de subagentes (cada par Task / SubagentStop) dividido por el número de sesiones en el alcance. Cuenta cada ejecución de subagente, incluidas las paralelas y las reiniciadas.", + "success": "En todas las ejecuciones de agentes principales + subagentes en el alcance: ejecuciones completadas ÷ (completadas + con error + abandonadas) × 100. Una ejecución cuenta como completada cuando se observa un Stop / SubagentStop sin un evento de error en medio.", + "topFlow": "Se cuenta cada par consecutivo de llamadas de herramientas dentro de una sesión como una transición. El par que aparece con mayor frecuencia en todas las sesiones se muestra aquí como 'origen → destino'.", + "compactions": "Número total de eventos de compresión de contexto registrados por los hooks PreCompact / SessionStart(compact), dividido por el número de sesiones en el alcance.", + "duration": "Tiempo promedio en tiempo real desde el primer evento de cada sesión (SessionStart) hasta su último evento (Stop / SessionEnd / último hook), promediado en todas las sesiones en el alcance." + }, + "depth": { + "zero": "no se han observado agentes anidados. Cada sesión se ejecutó solo con el agente principal - no ocurrió ninguna delegación de subagentes.", + "rare": "el anidamiento es raro. La mayoría de las sesiones se mantienen en profundidad 0 (solo agente principal); solo una pequeña fracción alcanza subagentes.", + "single": "las sesiones típicamente delegan a una sola capa de subagentes (principal → subagente), sin mayor anidamiento.", + "multi": "la orquestación de múltiples capas es común - los subagentes frecuentemente inician más subagentes (principal → sub → sub).", + "deep": "la orquestación profunda y recursiva es la norma. Las sesiones encadenan consistentemente tres o más capas de agentes." + }, + "subagents": { + "zero": "no se han activado subagentes. Las sesiones se completaron completamente en el agente principal.", + "lowFreq": "prácticamente: aproximadamente un subagente cada ~{{count}} sesiones. La mayoría de las sesiones no inician ningún subagente.", + "moderate": "las sesiones típicamente delegan a 1-3 subagentes - uso moderado de ayudantes especializados.", + "heavy": "delegación pesada - las sesiones rutinariamente se expanden a varios subagentes.", + "veryHeavy": "muy intensivo en orquestación: las sesiones promedian 6+ ejecuciones de subagentes cada una." + }, + "success": { + "perfect": "casi perfecto. Casi cada ejecución de agente termina con un evento Stop limpio - sin errores ni abandono.", + "healthy": "saludable. La gran mayoría de las ejecuciones de agentes se completan con éxito; solo fallos esporádicos.", + "acceptable": "aceptable, pero vigila la tendencia de errores - aproximadamente 1 de cada 5-20 ejecuciones falla o se abandona.", + "concerning": "preocupante - una parte significativa de las ejecuciones de agentes está fallando. Inspecciona la página de Errores para los principales culpables.", + "critical": "crítico: la mayoría de las ejecuciones de agentes están fallando o siendo abandonadas. Revisa ErrorPropagation y los deploys recientes." + }, + "topFlow": { + "none": "aún no se han registrado suficientes llamadas de herramientas consecutivas para identificar un par dominante.", + "selfLoop": "{{tool}} se sigue a sí mismo con mayor frecuencia - típico de lecturas/ediciones repetidas o comandos shell encadenados.", + "pair": "{{source}} se ejecuta con mayor frecuencia inmediatamente antes de {{target}}. Léelo como un hábito: los agentes tienden a {{sourceLower}} y luego {{targetLower}} como su siguiente paso." + }, + "compactions": { + "zero": "no se ha observado compresión de contexto. Las sesiones se mantuvieron cómodamente dentro de la ventana de contexto del modelo.", + "lowFreq": "prácticamente: ~1 compresión cada {{count}} sesiones. Las sesiones de larga duración son la excepción.", + "moderate": "las sesiones típicamente alcanzan la compresión una o dos veces - la presión de contexto existe pero es manejable.", + "high": "alta tasa de compresión. Las sesiones rutinariamente se ejecutan lo suficientemente largo como para requerir múltiples compresiones de contexto; considera dividir el trabajo." + }, + "duration": { + "zero": "sin sesiones completadas aún - la duración es 0.", + "veryShort": "sesiones muy cortas (menos de un minuto) - probablemente llamadas de herramientas rápidas o prompts de un solo disparo.", + "short": "sesiones cortas, del orden de unos pocos minutos. Típico para trabajo enfocado en una sola función.", + "medium": "sesiones de duración media (5-30 min). Cadencia de desarrollo estándar.", + "long": "sesiones largas (30-60 min). Probablemente flujos de trabajo de múltiples pasos o ciclos de revisión más largos.", + "veryLong": "sesiones muy largas (1-3 h). Vigila la compresión y la presión de contexto.", + "marathon": "sesiones maratón (3+ horas). Considera si el trabajo podría dividirse en múltiples sesiones más cortas." + } + } + }, + "orchestration": { + "title": "Grafo de Orquestación de Agentes", + "subtitle": "Patrones de inicio agregados en todas las sesiones - Haz clic en un nodo para filtrar", + "sessions": "Sesiones", + "mainAgent": "Agente Principal", + "compactions": "Compresiones", + "noData": "Sin datos de orquestación", + "noDataDesc": "Los datos de inicio de agentes aparecerán aquí una vez que se registren sesiones con subagentes.", + "legend": "Leyenda", + "edgeWeight": "Peso del borde = frecuencia", + "count": "Conteo", + "otherTypes": "{{count}} otros tipos", + "ariaLabel": "Grafo acíclico dirigido de orquestación de agentes", + "layers": { + "origin": "Origen", + "mainAgent": "Agente Principal", + "subagentTypes": "Tipos de Subagentes", + "compactions": "Compresiones", + "outcomes": "Resultados" + }, + "tooltip": { + "tipClickToFilter": "Consejo: haz clic para filtrar el resto de la página por este nodo.", + "layer": { + "session": "Capa 0 - Origen", + "main": "Capa 1 - Agente principal", + "subagent": "Capa 2 - Tipo de subagente", + "subagentOverflow": "Capa 2 - Tipos de subagentes", + "nested": "Capa 3 - Compresiones", + "outcome": "Capa 4 - Resultado" + }, + "desc": { + "session": "Cada unidad aquí es una sesión de Claude Code que coincide con el filtro de estado actual. Los bordes salientes se expanden hacia los agentes principales que estas sesiones lanzaron.", + "main": "La ejecución Claude de nivel superior para cada sesión. Cada sesión tiene exactamente un agente principal; desde aquí el grafo se ramifica hacia los tipos de subagentes a los que delegó.", + "subagent": "Un subagente especializado al que el agente principal delegó (p. ej., a través de la herramienta Task).", + "subagentClean": " Cada ejecución de este subagente se ha completado limpiamente.", + "subagentAllError": " Cada ejecución de este subagente ha tenido error - investiga la definición del agente.", + "subagentSomeErrors": " {{count}} ejecución(es) con error - consulta la Propagación de Errores a continuación para más detalles.", + "subagentOverflow": "Todos los tipos de subagentes restantes colapsados en un nodo para que el grafo siga siendo legible. Pasa el cursor sobre cualquier otro nodo de subagente para ver estadísticas individuales.", + "nested": "Eventos de compresión de ventana de contexto. Claude activa una compresión cuando el contexto en ejecución se acerca al límite del modelo; el conteo refleja con qué frecuencia ocurrió eso en las sesiones en el alcance.", + "outcomeCompleted": "Sesiones cuyo hook final fue un evento Stop limpio. No se emitieron eventos de error antes de la terminación.", + "outcomeError": "Sesiones que emitieron al menos un evento de error (error de herramienta, error de subagente o error de sesión) antes de terminar.", + "outcomeAbandoned": "Sesiones que se detuvieron sin un evento Stop limpio y sin errores explícitos - generalmente cerradas a mitad de vuelo.", + "outcomeFallback": "El estado terminal de las sesiones en el alcance, calculado a partir del último evento hook observado para cada sesión." + } + } + }, + "toolFlow": { + "title": "Flujo de Ejecución de Herramientas", + "subtitle": "Cómo se encadenan las herramientas - Flujo dirigido estilo Sankey", + "noData": "Sin datos de flujo de herramientas", + "transitionCount_one": "{{count}} transición", + "transitionCount_other": "{{count}} transiciones", + "tooltip": { + "node": "Nodo de herramienta", + "link": "Transición de herramienta", + "totalCalls": "Total de llamadas", + "shareOfAll": "Participación de todo el uso de herramientas", + "transitionsObserved": "Transiciones observadas", + "shareOfSourceFmt": "Participación de {{source}} → siguiente", + "shareOfTargetFmt": "Participación de anterior → {{target}}", + "nodeDescFmt": "La altura de la barra muestra con qué frecuencia aparece {{name}} en todas las sesiones en el alcance. Las bandas que fluyen desde la izquierda muestran qué se ejecutó antes de {{name}}; las bandas que fluyen hacia la derecha muestran qué se ejecutó después.", + "linkDescFmt": "En cada sesión, esto cuenta con qué frecuencia {{source}} se ejecutó inmediatamente antes de {{target}}. Bandas más gruesas = transiciones más comunes; los porcentajes muestran qué tan dominante es este salto entre el flujo de salida/entrada de cada herramienta." + } + }, + "pipeline": { + "title": "Grafo de Pipeline de Agentes", + "subtitle": "Flujo de trabajo dirigido - qué tipos de agentes se ejecutan después de cuáles, con etiquetas de frecuencia", + "noData": "Sin datos de pipeline aún", + "noDataDesc": "Los bordes del flujo de trabajo dirigido aparecen una vez que los tipos de agentes se ejecutan secuencialmente en múltiples sesiones.", + "legend": "Leyenda", + "legendDesc": "A se ejecuta antes que B (conteo etiquetado)", + "frequency": "Frecuencia", + "meaning": "Significado", + "times": " veces", + "ranAfter": " se ejecutó después de ", + "spawns": " inicios", + "spawned": "Iniciado", + "inSessions": "En sesiones", + "ariaLabel": "Grafo de pipeline de agentes", + "tooltip": { + "agentType": "Tipo de agente", + "edge": "Borde secuencial", + "shareOfAllSpawns": "Participación de todos los inicios", + "sequentialPairs": "Pares secuenciales", + "totalSpawnsFmt": "{{id}} inicios totales", + "shareOfSrcFmt": "Participación de {{source}} → siguiente", + "shareOfTgtFmt": "Participación de anterior → {{target}}", + "edgeDescFmt": "{{target}} se ejecutó inmediatamente después de {{source}} {{count}} vez/veces en todas las sesiones. Cuanto más gruesa la flecha, más frecuente fue esta transferencia. Úsalo para detectar cadenas de delegación habituales en tus flujos de trabajo.", + "nodeDescFmt": "{{id}} se ejecutó {{total}} vez/veces en {{sessions}} sesión(es); su tasa de éxito es {{rate}}% ({{health}}). El tamaño del círculo codifica el total de inicios; las flechas muestran qué tipos de agentes tienden a ejecutarse antes o después.", + "health": { + "perfect": "casi perfecto", + "healthy": "saludable", + "shaky": "inestable", + "failing": "fallando con frecuencia" + } + } + }, + "effectiveness": { + "title": "Efectividad de Subagentes", + "subtitle": "Métricas de rendimiento por tipo de agente", + "success": "Éxito", + "sessions": "Sesiones", + "avgDuration": "Duración Promedio", + "weeklyActivity": "Actividad semanal", + "noData": "Sin datos de subagentes disponibles", + "successRateAria": "Tasa de éxito: {{rate}}%", + "weeklyActivityAria": "Sparkline de actividad semanal", + "sessionCount_one": "{{count}} sesión", + "sessionCount_other": "{{count}} sesiones" + }, + "patterns": { + "title": "Patrones de Flujo de Trabajo Detectados", + "subtitle": "Secuencias comunes de orquestación de agentes", + "noData": "Sin patrones detectados aún", + "noDataDesc": "Los patrones aparecen una vez que múltiples sesiones comparten la misma secuencia de agentes.", + "solo": "solitario (sin subagentes)", + "label": "Patrones de Flujo", + "detail": { + "clickHint": "Haz clic para más detalles", + "stepsHeading": "Secuencia completa de pasos", + "statsHeading": "Estadísticas", + "stepsCount_one": "{{count}} paso", + "stepsCount_other": "{{count}} pasos", + "uniqueAgents": "Tipos de agentes únicos", + "occurrences": "Sesiones que coinciden con este patrón", + "shareOfSessions": "Participación de todas las sesiones", + "narrativeHeading": "Qué significa esto", + "suggestionHeading": "Sugerencia", + "narrative": { + "soloFmt": "Sesiones que se ejecutaron solo con {{first}} - sin delegación a otros tipos de agentes.", + "twoStepFmt": "Una transferencia de dos pasos: {{first}} delega directamente a {{last}}, sin más ramificaciones.", + "shortFmt": "Una cadena de {{count}} pasos que comienza en {{first}} y termina en {{last}}.", + "longFmt": "Una larga cadena de {{count}} pasos - comienza en {{first}}, atraviesa varios agentes y aterriza en {{last}}.", + "loopHintFmt": " {{agent}} aparece más de una vez en esta secuencia - típico de un bucle de verificación o revisión.", + "dominant": " Este es el flujo de trabajo dominante en tu proyecto - más de la mitad de las sesiones lo siguen.", + "common": " Este es un flujo de trabajo común, que representa aproximadamente un cuarto de las sesiones.", + "regular": " Un flujo de trabajo reconocido, pero no el patrón mayoritario.", + "niche": " Un patrón de nicho que solo aparece ocasionalmente." + }, + "suggestion": { + "solo": "Ejecución solitaria - ¿un subagente de revisión de código o ejecutor de pruebas elevaría la calidad de estas sesiones?", + "shortChain": "Cadena corta. Si sigue recurriendo, nómbrala como un comando de barra, skill o flujo de trabajo con nombre para que sea fácil de reproducir.", + "mediumChain": "Suficientemente frecuente para formalizar. Un flujo de trabajo o plantilla con nombre haría este patrón reutilizable.", + "longChain": "Cadena larga - revisa si cada salto es necesario, o si algunos agentes podrían fusionarse u omitirse.", + "loop": "Bucle detectado. Confirma que el paso repetido es una verificación intencional en lugar de un reintento silencioso de una llamada fallida." + } + } + }, + "modelDelegation": { + "title": "Flujo de Delegación de Modelos", + "subtitle": "Cómo los modelos se enrutan a través de las jerarquías de agentes", + "mainModels": "MODELOS PRINCIPALES", + "subagentModels": "MODELOS DE SUBAGENTES", + "noData": "Sin datos de delegación de modelos disponibles", + "agentsCount_one": "{{count}} agente", + "agentsCount_other": "{{count}} agentes", + "tokensCount": "{{tokens}} tokens", + "sessionsCount_one": "{{count}} sesión", + "sessionsCount_other": "{{count}} sesiones", + "ariaLabel": "Diagrama de flujo de delegación de modelos", + "tooltip": { + "mainModel": "Modelo principal", + "subagentModel": "Modelo de subagente", + "agentRuns": "Ejecuciones de agentes", + "shareOfAll": "Participación de todos los agentes", + "sessionsOnModel": "Sesiones en este modelo", + "totalTokens": "Total de tokens", + "family": { + "opus": "Opus - nivel de mayor capacidad; mejor para razonamiento complejo, contexto más largo, más costoso por token.", + "sonnet": "Sonnet - nivel equilibrado; buen valor predeterminado para la mayoría de los flujos de trabajo de codificación y uso de herramientas.", + "haiku": "Haiku - nivel más rápido y barato; ideal para subagentes cortos y sensibles a la latencia.", + "other": "Modelo no Anthropic o no reconocido - queda fuera de las familias estándar Opus/Sonnet/Haiku." + }, + "lines": { + "main": "Las líneas de la derecha muestran los modelos de subagentes a los que este modelo principal delegó trabajo.", + "sub": "Las líneas de la izquierda muestran los modelos principales que delegaron trabajo a este modelo de subagente." + } + } + }, + "errorPropagation": { + "title": "Mapa de Propagación de Errores", + "subtitle": "Dónde se agrupan los errores en la profundidad de la jerarquía de agentes", + "noErrors": "No se registraron errores", + "allSuccess": "Todas las sesiones se completaron con éxito", + "sessionsHadErrors": " sesiones tuvieron errores", + "sessionsErrorSummary": "{{errorSessions}} de {{totalSessions}} sesiones tuvieron errores", + "agentErrors": " error(es) a nivel de agente en toda la jerarquía", + "sessionErrorsOnly": "Solo errores a nivel de sesión", + "errorsByDepth": "Errores por profundidad de jerarquía", + "errorProneTypes": "Tipos de agentes propensos a errores", + "apiSessionErrors": "Errores de API y sesión", + "depthLabels": { + "sessionMain": "Sesión / Principal", + "directSubagent": "Subagente Directo", + "nested": "Anidado (profundidad 2)", + "deep": "Profundo (profundidad 3+)" + } + }, + "concurrency": { + "title": "Línea de Tiempo de Concurrencia de Agentes", + "subtitle": "Patrones de ejecución paralela de agentes - Muestra cómo se superponen los agentes en el tiempo", + "noData": "Sin datos de concurrencia", + "noDataDesc": "Los datos de concurrencia aparecen una vez que se registren sesiones con subagentes.", + "sessions": "Sesiones", + "timing": "Temporización", + "max": " máx", + "sessionInfo_one": "{{count}} sesión - activo {{from}}%-{{to}}% de la sesión", + "sessionInfo_other": "{{count}} sesiones - activo {{from}}%-{{to}}% de la sesión", + "tooltip": { + "lane": "Carril de concurrencia", + "sessionsWith": "Sesiones con este agente", + "avgStart": "Inicio promedio en sesión", + "avgEnd": "Fin promedio en sesión", + "barHint": "La longitud de la barra es proporcional a cuántas sesiones presentan este tipo de agente. Los porcentajes de temporización son promedios de esas sesiones.", + "timing": { + "wholeSession": "Activo durante casi toda la sesión - se ejecuta desde el inicio hasta el final.", + "frontLoadedFmt": "Carga frontal: típicamente activo desde el inicio de la sesión hasta ~{{end}}% del camino.", + "backLoadedFmt": "Carga trasera: típicamente se activa alrededor del {{start}}% de la sesión y permanece hasta el final.", + "tightFmt": "Alcance estrecho: se ejecuta en una banda estrecha entre {{start}}% y {{end}}% de la línea de tiempo de la sesión.", + "midSessionFmt": "Trabajo a mitad de sesión: se ejecuta aproximadamente del {{start}}% al {{end}}% de la línea de tiempo de la sesión." + } + } + }, + "complexity": { + "title": "Dispersión de Complejidad de Sesiones", + "subtitle": "Duración vs conteo de agentes vs tokens - Tamaño de burbuja = uso de tokens", + "noData": "Sin datos de complejidad de sesiones", + "noDataDesc": "Las sesiones aparecerán aquí una vez que se completen con actividad de agentes.", + "duration": "Duración", + "agentCount": "Conteo de Agentes", + "ariaLabel": "Gráfico de dispersión de complejidad de sesiones", + "tooltip": { + "duration": "Duración:", + "agents": "Agentes:", + "subagents": "Subagentes:", + "tokens": "Tokens:", + "model": "Modelo:" + } + }, + "compaction": { + "title": "Análisis de Impacto de Compresión", + "subtitle": "Cómo se distribuye la compresión de contexto en tus sesiones", + "help": "Cuando la ventana de contexto de una sesión se llena, Claude Code comprime el historial más antiguo en un resumen. Cada compresión es uno de esos eventos.", + "noData": "Sin compresiones registradas", + "totalCompactions": "Total de compresiones", + "sessionsAffected": "Sesiones afectadas", + "ofTotal": "de {{total}}", + "avgPerSession": "Promedio / sesión afectada", + "peakSession": "Máximo en una sesión", + "tokensRecovered": "Tokens Recuperados", + "distribution": "Sesiones por conteo de compresiones", + "xAxis": "Compresiones por sesión", + "yAxis": "Sesiones", + "summary": "{{affected}} de {{total}} sesiones ({{pct}}%) usaron compresión de contexto.", + "tokensFreed": "La compresión liberó ~{{tokens}} tokens de contexto.", + "ariaLabel": "Número de sesiones por conteo de compresiones", + "tipTitle": "Comprimido {{count}}x", + "tipDetail": "{{sessions}} sesiones · {{pct}}% de las afectadas" + }, + "drillIn": { + "title": "Profundizar en Sesión", + "subtitle": "Selecciona una sesión del gráfico de dispersión o usa el menú desplegable para explorar", + "searchPlaceholder": "Buscar sesiones...", + "noSessionSelected": "Sin sesión seleccionada", + "noSessionDesc": "Busca y selecciona una sesión arriba, o haz clic en una burbuja en el gráfico de dispersión, para profundizar en su árbol de agentes, línea de tiempo de herramientas y secuencia de eventos.", + "failedLoad": "No se pudo cargar la sesión", + "loading": "Cargando...", + "loadMore": "Cargar más", + "noMatch": "Ninguna sesión coincide con tu búsqueda.", + "notFound": "No se encontraron sesiones.", + "unknownModel": "modelo desconocido", + "closePanel": "Cerrar panel de profundización", + "close": "Cerrar", + "tabs": { + "agentTree": "Árbol de Agentes", + "toolTimeline": "Línea de Tiempo de Herramientas", + "eventSequence": "Secuencia de Eventos" + }, + "noAgentTree": "Sin datos de árbol de agentes disponibles.", + "noToolEvents": "No se registraron eventos de herramientas.", + "noEvents": "No se registraron eventos para esta sesión.", + "showingOf": "Mostrando 100 de {{total}} eventos", + "sessionInfo": " sesión(es) - activo {{from}}%-{{to}}% de la sesión", + "sortedByCompaction": " sesión(es) (ordenadas por conteo de compresiones)" + } +}