From 6248699ce586cfa553aa5536946d98cb4dbe3154 Mon Sep 17 00:00:00 2001 From: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> Date: Wed, 20 May 2026 02:05:50 -0300 Subject: [PATCH 1/4] =?UTF-8?q?Release/v3.8.0=20=E2=80=94=20full=20changel?= =?UTF-8?q?og=20with=20660+=20commits=20(#2419)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(cli-tools): guard modelId type before calling indexOf E2E shakedown v3.8.0: cli-tools quebrava com TypeError quando dynamicModels continha entradas sem .id (objeto retornado diretamente em vez de string). * fix(offline): avoid SSR/CSR hydration mismatch on navigator.onLine Replace useState+lazy-initializer with useSyncExternalStore so the server snapshot (() => false) and client snapshot (() => navigator.onLine) are declared separately. React hydrates with the server value and switches to the real online status client-side without a mismatch. * chore(i18n): add missing en.json keys for translator, cli-tools, memory, onboarding Adds 58 missing keys identified by the new dashboard audit script: - cliTools: 18 custom CLI builder keys (CustomCliCard) - translator: 24 keys covering stream transformer, live monitor, test bench - memory: 12 health/pagination/dialog keys - onboarding.tier: 8 keys for the tier tour walkthrough Also adds scripts/i18n/audit-dashboard-pages.mjs which scans all dashboard pages, reports t() calls referencing missing en.json keys, and flags candidate hardcoded JSX/attribute strings. * chore(i18n): replace hardcoded UI text with t() calls across dashboard (round 1) Subagents refactored 8 high-impact dashboard pages, replacing 81 of the 407 hardcoded English/PT strings flagged by the audit with proper useTranslations() lookups. Added 73 corresponding keys to en.json across the home, apiManager, providers, settings, and usage namespaces. Pages affected: - BudgetTab (27 → 0) - HomePageClient (2 → 0) - RoutingTab (25 → 7) - ResilienceTab (38 → 18) - SystemStorageTab (42 → 21) - providers/[id] (17 → 15) - ApiManagerPageClient (14 → 13) - OneproxyTab (13 → 10) Also adds two helper scripts: - scripts/i18n/extract-keys-from-diff.mjs — extracts new keys from git diff - scripts/i18n/merge-keys.mjs — merges a pending-keys JSON into en.json Remaining hardcoded strings will be addressed in follow-up rounds. * chore(i18n): replace hardcoded UI text with t() calls across dashboard (round 2) Continues round 1 (commit 8d34f4c65). Round-2 subagents refactored additional dashboard pages, replacing 77 more hardcoded strings with useTranslations() lookups. Added 79 corresponding keys to en.json across the a2aDashboard, agents, analytics, apiManager, cliTools, common, and settings namespaces. Pages affected: - a2a/page (new useTranslations + 6 keys) - agent-skills/page (new useTranslations + 9 keys) - AutoRoutingAnalyticsTab (new useTranslations + 6 keys) - AppearanceTab (8 → 6 remaining) - OneproxyTab (10 → 0) - ResilienceTab (18 → 0 missing key) - RoutingTab (7 → 0 missing key) - VisionBridgeSettingsTab (new useTranslations + 6 keys) - CopilotToolCard (7 → 0 missing key) - ApiManagerPageClient (13 → 0 missing key) - gamification/admin (new useTranslations + 7 keys) Hardcoded total: 326 → 249. Real missing keys: 0 (the 6 still flagged are false positives in exampleTemplates.tsx where t is passed as a parameter — keys exist at translator.templatePayloads.*). * chore(i18n): replace hardcoded UI text with t() calls across dashboard (round 3) Round-3 subagents and manual edits refactored 9 more dashboard pages (plus 2 small extras), replacing ~80 hardcoded strings with useTranslations() lookups. Added 79 corresponding keys to en.json across analytics, cloudAgents, combos, common, health, settings, and usage namespaces. Pages affected: - analytics/ComboHealthTab (new useTranslations + 15 keys) - analytics/CompressionAnalyticsTab (new useTranslations + 11 keys) - settings/SystemStorageTab (21 → 0 missing key) - tokens/page (new useTranslations + 13 keys) - usage/BudgetTab (9 missing fixed) - health/page (manual: 6 keys) - cloud-agents/page (manual: 3 keys) - combos/page (manual: 1 key) Hardcoded total: 249 → 164. Real missing keys: 0 (6 remaining are exampleTemplates.tsx false positives). Also adds scripts/i18n/build-pending-from-missing.mjs which reads _audit.json and locates English values from HEAD to rebuild _pending-keys.json after race-condition resets between subagent edits. * chore(i18n): localize remaining dashboard settings labels Replace hardcoded labels in compression and resilience settings with translation lookups to continue the dashboard i18n cleanup. Add the v3.8.0 dashboard shakedown runbook to document the manual smoke-test process and known dev environment pitfalls. * chore(i18n): replace hardcoded UI text with t() calls across dashboard (round 4) Round-4 subagent + manual key-resolution refactored remaining strings in 3 high-traffic settings/API tabs, plus extracted English values for keys that were already added as t() calls but lost during the previous en.json race-condition resets. Pages affected: - api-manager/ApiManagerPageClient (7 → 0 missing key) - settings/CompressionSettingsTab (8 → 0 missing key) - settings/MemorySkillsTab (8 → 0 missing key) - settings/ResilienceTab (4 more keys recovered) Hardcoded total: 164 → 140. Real missing keys: 0 (6 remaining are the exampleTemplates.tsx false positives — t passed as parameter). * chore(i18n): replace hardcoded UI text with t() calls across dashboard (round 5) Round-5 agent began processing the remaining smaller dashboard files. Added 5 more keys to en.json for providers/[id]/page.tsx OAuth flow labels and the cross-OS auto-detection hint. Pages affected: - providers/[id]/page.tsx (5 keys) Hardcoded total: 140 → 136. Real missing keys: 0. * chore(i18n): resolve last 2 missing providers/[id] keys Adds providerDetailMyClaudeAccountPlaceholder and providerDetailPathAutoDetected — the final user-visible labels in the providers/[id] page that the round-5 subagent rewrote to t() calls without yet adding to en.json. Real missing keys: 0 (6 remaining are exampleTemplates.tsx false positives — t is passed as a parameter so the audit cannot resolve the namespace; keys do exist at translator.templatePayloads.*). * chore(i18n): replace hardcoded UI text with t() calls across dashboard (round 6 — 10 parallel agents) Round-6 dispatched 10 parallel subagents covering all 57 remaining dashboard files. Each agent worked on a disjoint file set to avoid en.json race conditions. Added ~60 new i18n keys across 9 namespaces covering small UI labels, table headers, search placeholders, and empty-state messages. Major changes: - analytics: SearchAnalyticsTab, ProviderUtilizationTab, DiversityScoreCard, CompressionAnalyticsTab (new useTranslations + keys) - batch: BatchDetailModal, BatchListTab, FileDetailModal, FilesListTab (new useTranslations + keys) - settings: CliproxyapiSettingsTab, PayloadRulesTab, ModelCooldownsCard, AppearanceTab, PricingTab (mostly new useTranslations) - endpoint: TokenSaverCard, ApiEndpointsTab, EndpointPageClient - cache: CachePerformance, IdempotencyLayer, ReasoningCacheTab, MediaPageClient, page - combos: IntelligentComboPanel, page - playground: ChatPlayground, SearchPlayground - providers: ProviderCard - onboarding: TierFlowDiagram - changelog: ChangelogViewer - home: ProviderTopology, TierCoverageWidget, BootstrapBanner, BadgeToast - usage: BudgetTab, BudgetTelemetryCards, QuotaTable - quotaShare: QuotaSharePageClient - profile: page - leaderboard: page - skills: page Hardcoded total: 131 → 60. Real missing keys: 0 plus 1 false-positive for combos.modePack (lookup via prop-passed t). * chore(i18n): finalize round-6 keys for batch/cache/endpoint/usage Adds the remaining keys produced by parallel agents A4, A6, A8, A9: - common: batch-related labels (BatchDetailModal, BatchListTab, FileDetailModal, FilesListTab, page) + profile/leaderboard - cache: hit rate, latency, retry, avg chars - endpoint: token saver, API endpoints, copy URL, cloud/local labels - usage: noSpend, activeSessions, quotaAlerts, budget timing - skills: install/marketplace/filter - proxyRegistry/quotaShare/mcpDashboard: misc labels Hardcoded total: 60 → 48. Real missing keys: 0 (modePack remaining is a false positive — combos.modePack exists but the audit can't resolve it since IntelligentComboPanel receives t as a prop). * fix(playground): dedupe filteredModels to avoid duplicate React key warning The /v1/models endpoint can return the same model id twice (e.g., when a model is listed by both an alias and its canonical provider), which made the sec @@ -1454,7 +1456,7 @@ const PermissionsModal = memo(function PermissionsModal({ type="text" value={scheduleTz} onChange={(e) => setScheduleTz(e.target.value)} - placeholder="America/Sao_Paulo" + placeholder={t("apiManagerTimezonePlaceholder")} className="w-full px-2 py-1.5 text-sm border border-border rounded-md bg-background text-text-main font-mono" />

{t("scheduleTimezoneHint")}

@@ -1466,7 +1468,7 @@ const PermissionsModal = memo(function PermissionsModal({ {/* Privacy Toggle */}
-

No-Log Payload Privacy

+

{t("noLogPayloadPrivacy")}

Disable request/response payload persistence for this API key.

@@ -1516,7 +1518,7 @@ const PermissionsModal = memo(function PermissionsModal({ {/* Ban Toggle (SECURITY) */}
-

Banned Status

+

{t("bannedStatus")}

Immediately revoke all access. Used for suspected abuse or compromised keys.

@@ -1540,7 +1542,7 @@ const PermissionsModal = memo(function PermissionsModal({ {/* Management API Access Toggle */}
-

Management API Access

+

{t("managementApiAccess")}

Allow this key to call management routes (providers, combos, settings) via{" "} Authorization: Bearer. Use for LLM agents only. @@ -1565,7 +1567,7 @@ const PermissionsModal = memo(function PermissionsModal({ {/* Expiration Date */}

-

Expiration Date

+

{t("expirationDate")}

Key will automatically stop working after this date.

@@ -1583,7 +1585,7 @@ const PermissionsModal = memo(function PermissionsModal({ {/* Management Access */}
-

Management Access

+

{t("managementAccess")}

Allow this API key to manage OmniRoute configuration.

@@ -1772,7 +1774,7 @@ const PermissionsModal = memo(function PermissionsModal({ {allConnections.length > 0 && (
-

Allowed Connections

+

{t("allowedConnections")}

@@ -191,7 +193,7 @@ export default function BatchDetailModal({ batch, files, onClose }: BatchDetailM
- - {batch.model && } - + + {batch.model && } + {relativeTime(batch.createdAt)}} />
diff --git a/src/app/(dashboard)/dashboard/batch/BatchListTab.tsx b/src/app/(dashboard)/dashboard/batch/BatchListTab.tsx index eca2569383..a5444f1f52 100644 --- a/src/app/(dashboard)/dashboard/batch/BatchListTab.tsx +++ b/src/app/(dashboard)/dashboard/batch/BatchListTab.tsx @@ -1,6 +1,7 @@ "use client"; import { useState } from "react"; +import { useTranslations } from "next-intl"; import BatchDetailModal from "./BatchDetailModal"; function relativeTime(ts: number): string { @@ -131,6 +132,7 @@ export default function BatchListTab({ loading, onRefresh, }: Readonly) { + const t = useTranslations("common"); const [selectedBatch, setSelectedBatch] = useState(null); const [statusFilter, setStatusFilter] = useState("all"); const [searchQuery, setSearchQuery] = useState(""); @@ -199,7 +201,7 @@ export default function BatchListTab({
setSearchQuery(e.target.value)} className="flex-1 min-w-[200px] px-3 py-2 rounded-lg text-sm bg-[var(--color-bg)] border border-[var(--color-border)] text-[var(--color-text-main)] placeholder:text-[var(--color-text-muted)] focus:outline-2 focus:outline-[var(--color-accent)]" @@ -219,7 +221,7 @@ export default function BatchListTab({ onClick={handleRemoveCompleted} disabled={removingCompleted} className="flex items-center gap-1.5 px-3 py-2 text-sm rounded-lg bg-red-500/10 border border-red-500/25 text-red-400 hover:text-red-300 transition-colors disabled:opacity-50 disabled:cursor-not-allowed whitespace-nowrap" - title="Delete all completed batches" + title={t("batchListDeleteAllCompletedTitle")} > {removingCompleted ? "hourglass_empty" : "delete_sweep"} @@ -230,7 +232,7 @@ export default function BatchListTab({ {/* Table */}
- +
@@ -338,7 +340,7 @@ export default function BatchListTab({ onClick={(e) => handleDeleteBatch(e, batch)} disabled={deletingId === batch.id} className="flex items-center gap-1 px-2 py-1 text-xs rounded bg-red-500/10 border border-red-500/25 text-red-400 hover:text-red-300 transition-colors whitespace-nowrap disabled:opacity-50" - title="Delete batch and its files" + title={t("batchListDeleteBatchTitle")} > {deletingId === batch.id ? "hourglass_empty" : "delete"} diff --git a/src/app/(dashboard)/dashboard/batch/FileDetailModal.tsx b/src/app/(dashboard)/dashboard/batch/FileDetailModal.tsx index 4281178a7d..0bde086453 100644 --- a/src/app/(dashboard)/dashboard/batch/FileDetailModal.tsx +++ b/src/app/(dashboard)/dashboard/batch/FileDetailModal.tsx @@ -1,6 +1,7 @@ "use client"; import { useEffect, useState } from "react"; +import { useTranslations } from "next-intl"; import { Button } from "@/shared/components"; function relativeTime(ts: number): string { @@ -69,6 +70,7 @@ export default function FileDetailModal({ batches, onClose, }: Readonly) { + const t = useTranslations("common"); const [copied, setCopied] = useState(false); const relatedBatches = (batches ?? []).filter( @@ -140,7 +142,7 @@ export default function FileDetailModal({ navigator.clipboard.writeText(file.id); }} className="text-[var(--color-text-muted)] hover:text-[var(--color-text-main)] transition-colors" - title="Copy ID" + title={t("batchFileDetailCopyId")} > content_copy @@ -149,7 +151,7 @@ export default function FileDetailModal({