fix(audit): translate event types and A2A task states (Phase 2)

- 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.
This commit is contained in:
diegosouzapw
2026-05-30 12:38:23 -03:00
parent a59a90e6a1
commit a03d7b40d7
5 changed files with 107 additions and 4 deletions

View File

@@ -182,7 +182,7 @@ export default function A2aAuditTab() {
<span
className={`rounded-full border px-2 py-1 text-xs font-medium ${STATE_STYLES[task.state]}`}
>
{task.state}
{t(`a2aState${task.state.charAt(0).toUpperCase()}${task.state.slice(1)}`)}
</span>
</td>
<td className="px-4 py-3 text-text-muted">{taskDuration(task)}</td>

View File

@@ -330,7 +330,7 @@ export default function ComplianceTab() {
</td>
<td className="px-4 py-3">
<span className="rounded-md border border-border bg-surface px-2 py-1 font-mono text-xs text-text-main">
{entry.action}
{t.has(`eventTypes.${entry.action}`) ? t(`eventTypes.${entry.action}`) : entry.action}
</span>
</td>
<td className="px-4 py-3">

View File

@@ -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",

View File

@@ -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",

View File

@@ -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");
});