Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion client/src/i18n/__tests__/i18n.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -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 () => {
Expand Down
35 changes: 34 additions & 1 deletion client/src/i18n/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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: [
Expand Down
10 changes: 10 additions & 0 deletions client/src/i18n/locales/es/activity.json
Original file line number Diff line number Diff line change
@@ -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"
}
80 changes: 80 additions & 0 deletions client/src/i18n/locales/es/alerts.json
Original file line number Diff line number Diff line change
@@ -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."
}
}
54 changes: 54 additions & 0 deletions client/src/i18n/locales/es/analytics.json
Original file line number Diff line number Diff line change
@@ -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"
}
Loading
Loading