From a03d7b40d70b5cb031d4817da8d9ab86cf66eff0 Mon Sep 17 00:00:00 2001 From: diegosouzapw Date: Sat, 30 May 2026 12:38:23 -0300 Subject: [PATCH] fix(audit): translate event types and A2A task states (Phase 2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add compliance.eventTypes (36 labels) to en.json + pt-BR.json. - ComplianceTab: render translated label via t.has/t fallback instead of raw entry.action. - A2aAuditTab: render translated task state via a2aState* keys instead of raw task.state. - Memory type/strategy dropdowns needed no i18n change — keys already exist; the Phase 1 Select fix makes them render. --- .../dashboard/audit/A2aAuditTab.tsx | 2 +- .../dashboard/audit/ComplianceTab.tsx | 2 +- src/i18n/messages/en.json | 40 ++++++++++++++++++- src/i18n/messages/pt-BR.json | 40 ++++++++++++++++++- tests/unit/audit-eventtype-i18n.test.ts | 27 +++++++++++++ 5 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 tests/unit/audit-eventtype-i18n.test.ts diff --git a/src/app/(dashboard)/dashboard/audit/A2aAuditTab.tsx b/src/app/(dashboard)/dashboard/audit/A2aAuditTab.tsx index 1cd2f7c2dc..2c390c68cd 100644 --- a/src/app/(dashboard)/dashboard/audit/A2aAuditTab.tsx +++ b/src/app/(dashboard)/dashboard/audit/A2aAuditTab.tsx @@ -182,7 +182,7 @@ export default function A2aAuditTab() { - {task.state} + {t(`a2aState${task.state.charAt(0).toUpperCase()}${task.state.slice(1)}`)} {taskDuration(task)} diff --git a/src/app/(dashboard)/dashboard/audit/ComplianceTab.tsx b/src/app/(dashboard)/dashboard/audit/ComplianceTab.tsx index f2a0dc4329..a8f86190cc 100644 --- a/src/app/(dashboard)/dashboard/audit/ComplianceTab.tsx +++ b/src/app/(dashboard)/dashboard/audit/ComplianceTab.tsx @@ -330,7 +330,7 @@ export default function ComplianceTab() { - {entry.action} + {t.has(`eventTypes.${entry.action}`) ? t(`eventTypes.${entry.action}`) : entry.action} diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json index 17156a6e8d..836b719b41 100644 --- a/src/i18n/messages/en.json +++ b/src/i18n/messages/en.json @@ -1283,7 +1283,45 @@ "a2aNoTasks": "No A2A tasks recorded.", "a2aLoadingTasks": "Loading A2A tasks...", "actor": "Actor", - "actorPlaceholder": "Filter by actor" + "actorPlaceholder": "Filter by actor", + "eventTypes": { + "apiKey.activate": "API Key Activated", + "apiKey.ban": "API Key Banned", + "apiKey.deactivate": "API Key Deactivated", + "apiKey.regenerate": "API Key Regenerated", + "apiKey.scopes.grant": "API Key Scopes Granted", + "apiKey.scopes.revoke": "API Key Scopes Revoked", + "apiKey.scopes.update": "API Key Scopes Updated", + "apiKey.unban": "API Key Unbanned", + "auth.login.error": "Login Error", + "auth.login.failed": "Login Failed", + "auth.login.locked": "Login Locked", + "auth.login.misconfigured": "Login Misconfigured", + "auth.login.setup_required": "Login Setup Required", + "auth.login.success": "Login Success", + "auth.logout.success": "Logout Success", + "compliance.cleanup": "Compliance Cleanup", + "provider.credentials.applied": "Provider Credentials Applied", + "provider.credentials.batch_revoked": "Provider Credentials Batch Revoked", + "provider.credentials.bulk_created": "Provider Credentials Bulk Created", + "provider.credentials.bulk_imported": "Provider Credentials Bulk Imported", + "provider.credentials.created": "Provider Credentials Created", + "provider.credentials.imported": "Provider Credentials Imported", + "provider.credentials.revoked": "Provider Credentials Revoked", + "provider.credentials.updated": "Provider Credentials Updated", + "provider.validation.ssrf_blocked": "Provider SSRF Blocked", + "quota.plan.updated": "Quota Plan Updated", + "quota.pool.created": "Quota Pool Created", + "quota.pool.deleted": "Quota Pool Deleted", + "quota.pool.updated": "Quota Pool Updated", + "quota.store.driver_changed": "Quota Store Driver Changed", + "server.start": "Server Start", + "service.reveal_api_key": "Service API Key Revealed", + "settings.update": "Settings Updated", + "settings.update_failed": "Settings Update Failed", + "sync.token.created": "Sync Token Created", + "sync.token.revoked": "Sync Token Revoked" + } }, "themesPage": { "title": "Themes", diff --git a/src/i18n/messages/pt-BR.json b/src/i18n/messages/pt-BR.json index 589a6400ef..464724d88e 100644 --- a/src/i18n/messages/pt-BR.json +++ b/src/i18n/messages/pt-BR.json @@ -2465,7 +2465,45 @@ "a2aNoTasks": "Nenhuma tarefa A2A registrada.", "a2aLoadingTasks": "Carregando tarefas A2A...", "actor": "Autor", - "actorPlaceholder": "Filtrar por autor" + "actorPlaceholder": "Filtrar por autor", + "eventTypes": { + "apiKey.activate": "API Key Ativada", + "apiKey.ban": "API Key Banida", + "apiKey.deactivate": "API Key Desativada", + "apiKey.regenerate": "API Key Regenerada", + "apiKey.scopes.grant": "Escopos de API Key Concedidos", + "apiKey.scopes.revoke": "Escopos de API Key Revogados", + "apiKey.scopes.update": "Escopos de API Key Atualizados", + "apiKey.unban": "API Key Desbanida", + "auth.login.error": "Erro de Login", + "auth.login.failed": "Login Falhou", + "auth.login.locked": "Login Bloqueado", + "auth.login.misconfigured": "Login Mal Configurado", + "auth.login.setup_required": "Configuração de Login Necessária", + "auth.login.success": "Login com Sucesso", + "auth.logout.success": "Logout com Sucesso", + "compliance.cleanup": "Limpeza de Conformidade", + "provider.credentials.applied": "Credenciais do Provedor Aplicadas", + "provider.credentials.batch_revoked": "Credenciais do Provedor Revogadas em Lote", + "provider.credentials.bulk_created": "Credenciais do Provedor Criadas em Massa", + "provider.credentials.bulk_imported": "Credenciais do Provedor Importadas em Massa", + "provider.credentials.created": "Credenciais do Provedor Criadas", + "provider.credentials.imported": "Credenciais do Provedor Importadas", + "provider.credentials.revoked": "Credenciais do Provedor Revogadas", + "provider.credentials.updated": "Credenciais do Provedor Atualizadas", + "provider.validation.ssrf_blocked": "SSRF do Provedor Bloqueado", + "quota.plan.updated": "Plano de Cota Atualizado", + "quota.pool.created": "Pool de Cota Criado", + "quota.pool.deleted": "Pool de Cota Excluído", + "quota.pool.updated": "Pool de Cota Atualizado", + "quota.store.driver_changed": "Driver do Store de Cota Alterado", + "server.start": "Servidor Iniciado", + "service.reveal_api_key": "API Key do Serviço Revelada", + "settings.update": "Configurações Atualizadas", + "settings.update_failed": "Falha ao Atualizar Configurações", + "sync.token.created": "Token de Sincronização Criado", + "sync.token.revoked": "Token de Sincronização Revogado" + } }, "contextCaveman": { "title": "Motor Caveman", diff --git a/tests/unit/audit-eventtype-i18n.test.ts b/tests/unit/audit-eventtype-i18n.test.ts new file mode 100644 index 0000000000..6b1d4ca302 --- /dev/null +++ b/tests/unit/audit-eventtype-i18n.test.ts @@ -0,0 +1,27 @@ +import test from "node:test"; +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import { join } from "node:path"; + +const root = join(import.meta.dirname, "../.."); +const read = (p: string) => readFileSync(join(root, p), "utf8"); +const en = JSON.parse(read("src/i18n/messages/en.json")); +const pt = JSON.parse(read("src/i18n/messages/pt-BR.json")); + +test("audit: compliance.eventTypes exists with en/pt-BR parity and key coverage", () => { + const enKeys = Object.keys(en.compliance?.eventTypes ?? {}); + const ptKeys = Object.keys(pt.compliance?.eventTypes ?? {}); + assert.ok(enKeys.length >= 30, `expected >=30 event-type labels, got ${enKeys.length}`); + assert.deepEqual(enKeys.sort(), ptKeys.sort(), "en/pt-BR eventTypes keys must match"); + for (const k of ["provider.credentials.created", "auth.login.success", "quota.pool.created", "sync.token.revoked"]) { + assert.ok(en.compliance.eventTypes[k], `en missing eventTypes.${k}`); + assert.ok(pt.compliance.eventTypes[k], `pt-BR missing eventTypes.${k}`); + } +}); + +test("audit: ComplianceTab translates action and A2aAuditTab translates task state", () => { + const ct = read("src/app/(dashboard)/dashboard/audit/ComplianceTab.tsx"); + const a2a = read("src/app/(dashboard)/dashboard/audit/A2aAuditTab.tsx"); + assert.ok(ct.includes("eventTypes.${entry.action}"), "ComplianceTab uses eventTypes i18n lookup"); + assert.ok(a2a.includes("a2aState${task.state"), "A2aAuditTab uses a2aState i18n lookup"); +});