From 129b05741cfecf0d6b3c4deffba0f2c8aec072b2 Mon Sep 17 00:00:00 2001 From: Ronaldo Davi Date: Thu, 21 May 2026 23:14:42 -0300 Subject: [PATCH] feat(i18n): Complete and fix Brazilian Portuguese (pt-BR) translation (#2543) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit feat(i18n): Complete pt-BR translation — integrated into release/v3.8.2 --- .../settings/components/RoutingTab.tsx | 288 +- src/i18n/messages/en.json | 75 +- src/i18n/messages/pt-BR.json | 5511 +++++++++-------- 3 files changed, 2994 insertions(+), 2880 deletions(-) diff --git a/src/app/(dashboard)/dashboard/settings/components/RoutingTab.tsx b/src/app/(dashboard)/dashboard/settings/components/RoutingTab.tsx index 1563957099..f5fe1bfe3c 100644 --- a/src/app/(dashboard)/dashboard/settings/components/RoutingTab.tsx +++ b/src/app/(dashboard)/dashboard/settings/components/RoutingTab.tsx @@ -192,74 +192,50 @@ type TransformOpKind = | "obfuscate_words"; const OP_KIND_LABELS: Record = { - drop_paragraph_if_contains: "Drop paragraph (contains)", - drop_paragraph_if_starts_with: "Drop paragraph (starts with)", - replace_text: "Replace text", - replace_regex: "Replace regex", - drop_block_if_contains: "Drop block (contains)", - prepend_system_block: "Prepend system block", - append_system_block: "Append system block", - inject_billing_header: "Inject billing header", - obfuscate_words: "Obfuscate words (ZWJ)", + drop_paragraph_if_contains: "routingOpDropParagraphContainsLabel", + drop_paragraph_if_starts_with: "routingOpDropParagraphStartsWithLabel", + replace_text: "routingOpReplaceTextLabel", + replace_regex: "routingOpReplaceRegexLabel", + drop_block_if_contains: "routingOpDropBlockContainsLabel", + prepend_system_block: "routingOpPrependSystemBlockLabel", + append_system_block: "routingOpAppendSystemBlockLabel", + inject_billing_header: "routingOpInjectBillingHeaderLabel", + obfuscate_words: "routingOpObfuscateWordsLabel", }; // Human-readable description shown above each op's editor. Explains in one // sentence what the op DOES (transformation effect) and one sentence WHEN // to use it (the typical fingerprint-sanitization use-case). const OP_KIND_DESCRIPTIONS: Record = { - drop_paragraph_if_contains: - "Removes any paragraph (text block split on blank lines) inside the system prompt whose text contains ANY of the listed substrings. Use to strip third-party client fingerprints like 'github.com/anomalyco/opencode' or 'docs.openwebui.com' that Anthropic's classifier flags.", - drop_paragraph_if_starts_with: - "Removes any paragraph that STARTS WITH one of the listed prefixes. Use for identity lines like 'You are OpenCode' or 'You are Open WebUI' that announce the calling client.", - replace_text: - "Replaces a literal substring with another literal substring. Use for known trigger phrases — e.g. rewrite 'Here is some useful information about the environment you are running in:' to 'Environment context you are running in:' (an empirically-validated trigger phrase).", - replace_regex: - "Replaces text matching a regular expression. Use when you need patterns (character classes, optional whitespace, anchors) instead of literal substrings. A malformed pattern is caught at runtime when the op runs.", - drop_block_if_contains: - "Removes ENTIRE system blocks (not just paragraphs) whose text contains any of the listed substrings. Use when a whole block is fingerprint-bearing and you want it gone — e.g. an injected MCP-server description.", - prepend_system_block: - "Inserts a new text block at the FRONT of the system array. Use to add the SDK identity 'You are a Claude agent, built on Anthropic's Claude Agent SDK.' that Anthropic's classifier expects.", - append_system_block: - "Inserts a new text block at the END of the system array. Use for cosmetic additions that don't need to be at position [0].", - inject_billing_header: - "Prepends the special 'x-anthropic-billing-header: cc_version=...; cc_entrypoint=...; cch=...;' text block that Anthropic's classifier validates. Required for CC bridge relay endpoints; for the native claude provider OmniRoute already injects its own billing line so this op is usually redundant there.", - obfuscate_words: - "Inserts a Zero-Width-Joiner character after the first letter of each listed word, so 'opencode' becomes 'o\u200dpencode'. Reads identical to humans but bypasses classifier word matches. Targets system blocks, user/assistant messages, and tool descriptions.", + drop_paragraph_if_contains: "routingOpDropParagraphContainsDesc", + drop_paragraph_if_starts_with: "routingOpDropParagraphStartsWithDesc", + replace_text: "routingOpReplaceTextDesc", + replace_regex: "routingOpReplaceRegexDesc", + drop_block_if_contains: "routingOpDropBlockContainsDesc", + prepend_system_block: "routingOpPrependSystemBlockDesc", + append_system_block: "routingOpAppendSystemBlockDesc", + inject_billing_header: "routingOpInjectBillingHeaderDesc", + obfuscate_words: "routingOpObfuscateWordsDesc", }; // Per-field hints rendered under each Input/Select/Toggle inside the // editor. Short, plain-English. Keep under ~120 chars each. const FIELD_HINTS = { - needles: - "List of substrings. A paragraph matches if it contains ANY one of them. Add one per line via 'Add entry'.", - prefixes: - "List of strings. A paragraph matches if it starts with any one of them (leading whitespace is trimmed before matching).", - caseSensitive: - "When ON, 'OpenCode' and 'opencode' are different strings. When OFF (default), the comparison ignores case.", - matchLiteral: - "Exact literal substring to find. No regex syntax — special chars like . * ? are treated as themselves.", - replacementText: - "Replacement string. Leave blank to delete the match. The output preserves surrounding text.", - allOccurrences: - "When ON (default), every instance is replaced. When OFF, only the first match is replaced.", - pattern: - "JavaScript regex source. Don't wrap in slashes — just 'foo(.*)bar'. Server rejects patterns that fail to compile.", - regexFlags: - "JavaScript regex flags (g = all matches, i = case-insensitive, s = dot matches newline, m = multiline). Default 'g'.", - blockText: - "Full text of the new system block. Use a literal string; the system block stores text only.", - idempotencyKey: - "Optional. If set, the op skips when a block whose text starts with this key is already present. Prevents double-prepend on retries.", - billingEntrypoint: - "Value injected as 'cc_entrypoint='. Anthropic accepts 'sdk-cli' (Agent SDK), 'cli' (Claude Code CLI), or other documented values.", - billingVersionFormat: - "How the 3-char build hash after cc_version= is computed. 'ex-machina' = sha256 of CCH_SALT+chars-from-first-user-msg+version (per-message). 'omniroute-daystamp' = sha256 of YYYY-MM-DD+version (stable per-day).", - billingCchAlgo: - "How the 5-char cch= token is computed. 'sha256-first-user' = sha256 of first user message text. 'xxhash64-body' = body-level signing fills it later. 'static-zero' = literal '00000' placeholder.", - obfuscateWords: - "Lowercase words to obfuscate. ZWJ insertion is applied case-insensitively, so 'opencode' also matches 'OpenCode' and 'OPENCODE'.", - obfuscateTargets: - "Which body regions to scan for the words: system blocks, user/assistant messages, and/or tool descriptions.", + needles: "routingNeedlesHint", + prefixes: "routingPrefixesHint", + caseSensitive: "routingCaseSensitiveHint", + matchLiteral: "routingMatchLiteralHint", + replacementText: "routingReplacementTextHint", + allOccurrences: "routingAllOccurrencesHint", + pattern: "routingPatternHint", + regexFlags: "routingRegexFlagsHint", + blockText: "routingBlockTextHint", + idempotencyKey: "routingIdempotencyKeyHint", + billingEntrypoint: "routingBillingEntrypointHint", + billingVersionFormat: "routingBillingVersionFormatHint", + billingCchAlgo: "routingBillingCchAlgoHint", + obfuscateWords: "routingObfuscateWordsHint", + obfuscateTargets: "routingObfuscateTargetsHint", }; function makeDefaultOp(kind: TransformOpKind): any { @@ -342,7 +318,7 @@ function StringListEditor({ onClick={() => onChange([...items, ""])} className="self-start" > - Add entry + {t("common.add") || "Add entry"} ); @@ -360,7 +336,7 @@ function OpEditor({ const t = useTranslations("settings"); const updateField = (field: string, value: any) => onChange({ ...op, [field]: value }); const kind = op?.kind as TransformOpKind | undefined; - const opDescription = kind ? OP_KIND_DESCRIPTIONS[kind] : null; + const opDescription = kind ? t(OP_KIND_DESCRIPTIONS[kind]) : null; const wrap = (body: React.ReactNode) => (
@@ -379,14 +355,14 @@ function OpEditor({
updateField("needles", next)} disabled={disabled} /> updateField("caseSensitive", c)} size="sm" @@ -399,14 +375,14 @@ function OpEditor({
updateField("prefixes", next)} disabled={disabled} /> updateField("caseSensitive", c)} size="sm" @@ -419,21 +395,21 @@ function OpEditor({
updateField("match", e.target.value)} /> updateField("replacement", e.target.value)} /> updateField("allOccurrences", c)} size="sm" @@ -446,21 +422,21 @@ function OpEditor({
updateField("pattern", e.target.value)} /> updateField("flags", e.target.value)} /> updateField("replacement", e.target.value)} @@ -471,7 +447,7 @@ function OpEditor({ return wrap( updateField("needles", next)} disabled={disabled} @@ -490,11 +466,11 @@ function OpEditor({ onChange={(e) => updateField("text", e.target.value)} className="w-full rounded-md border border-black/10 dark:border-white/10 bg-white dark:bg-white/5 px-3 py-2 text-sm text-text-main font-mono focus:ring-1 focus:ring-primary/30 focus:border-primary/50 focus:outline-none transition-all shadow-inner disabled:opacity-50 disabled:cursor-not-allowed" /> -

{FIELD_HINTS.blockText}

+

{t(FIELD_HINTS.blockText)}

updateField("idempotencyKey", e.target.value)} @@ -506,14 +482,14 @@ function OpEditor({
updateField("entrypoint", e.target.value)} /> updateField("cchAlgo", e.target.value)} @@ -541,14 +517,16 @@ function OpEditor({
updateField("words", next)} disabled={disabled} />
- Targets -

{FIELD_HINTS.obfuscateTargets}

+ + {t("routingObfuscateTargetsLabel")} + +

{t(FIELD_HINTS.obfuscateTargets)}

{(["system", "messages", "tools"] as const).map((target) => { const targets: string[] = op.targets || ["system", "messages", "tools"]; @@ -576,26 +554,53 @@ function OpEditor({ } } -function summarizeTransformOp(op: any): string { +function summarizeTransformOp(op: any, t: any): string { switch (op?.kind) { case "drop_paragraph_if_contains": - return `drop paragraphs containing: ${(op.needles || []).slice(0, 3).join(", ")}${(op.needles || []).length > 3 ? "…" : ""}`; + return t("routingSummarizeDropParagraphContains", { + items: + (op.needles || []).slice(0, 3).join(", ") + ((op.needles || []).length > 3 ? "…" : ""), + }); case "drop_paragraph_if_starts_with": - return `drop paragraphs starting with: ${(op.prefixes || []).slice(0, 3).join(", ")}${(op.prefixes || []).length > 3 ? "…" : ""}`; + return t("routingSummarizeDropParagraphStartsWith", { + items: + (op.prefixes || []).slice(0, 3).join(", ") + ((op.prefixes || []).length > 3 ? "…" : ""), + }); case "replace_text": - return `replace "${(op.match || "").slice(0, 40)}${(op.match || "").length > 40 ? "…" : ""}" → "${(op.replacement || "").slice(0, 40)}${(op.replacement || "").length > 40 ? "…" : ""}"`; + return t("routingSummarizeReplaceText", { + match: (op.match || "").slice(0, 40) + ((op.match || "").length > 40 ? "…" : ""), + replacement: + (op.replacement || "").slice(0, 40) + ((op.replacement || "").length > 40 ? "…" : ""), + }); case "replace_regex": - return `regex /${op.pattern}/${op.flags || ""} → "${(op.replacement || "").slice(0, 40)}"`; + return t("routingSummarizeReplaceRegex", { + pattern: op.pattern, + flags: op.flags || "", + replacement: (op.replacement || "").slice(0, 40), + }); case "drop_block_if_contains": - return `drop blocks containing: ${(op.needles || []).slice(0, 3).join(", ")}`; + return t("routingSummarizeDropBlockContains", { + items: (op.needles || []).slice(0, 3).join(", "), + }); case "prepend_system_block": - return `prepend block: "${(op.text || "").slice(0, 60)}${(op.text || "").length > 60 ? "…" : ""}"`; + return t("routingSummarizePrependSystemBlock", { + text: (op.text || "").slice(0, 60) + ((op.text || "").length > 60 ? "…" : ""), + }); case "append_system_block": - return `append block: "${(op.text || "").slice(0, 60)}${(op.text || "").length > 60 ? "…" : ""}"`; + return t("routingSummarizeAppendSystemBlock", { + text: (op.text || "").slice(0, 60) + ((op.text || "").length > 60 ? "…" : ""), + }); case "inject_billing_header": - return `inject billing header (entrypoint=${op.entrypoint}, version=${op.versionFormat}, cch=${op.cchAlgo})`; + return t("routingSummarizeInjectBillingHeader", { + entrypoint: op.entrypoint, + versionFormat: op.versionFormat, + cchAlgo: op.cchAlgo, + }); case "obfuscate_words": - return `obfuscate ${(op.words || []).length} word(s) via ZWJ in ${(op.targets || ["system", "messages", "tools"]).join("+")}`; + return t("routingSummarizeObfuscateWords", { + count: (op.words || []).length, + targets: (op.targets || ["system", "messages", "tools"]).join("+"), + }); default: return JSON.stringify(op); } @@ -997,10 +1002,7 @@ export default function RoutingTab() {

{t("routingAntigravitySignatureTitle")}

-

- Control whether OmniRoute reuses only stored Gemini thought signatures or accepts - validated client-provided signatures in Antigravity-compatible tool-call flows. -

+

{t("routingAntigravitySignatureDesc")}

@@ -1008,18 +1010,18 @@ export default function RoutingTab() { {[ { value: "enabled", - label: "Enabled", - desc: "Current behavior. Ignore client-provided signatures and keep using the stored OmniRoute flow.", + label: t("routingAntigravitySignatureEnabledLabel"), + desc: t("routingAntigravitySignatureEnabledDesc"), }, { value: "bypass", - label: "Bypass", - desc: "Accept client-provided signatures after lightweight validation and fall back to the stored signature when invalid.", + label: t("routingAntigravitySignatureBypassLabel"), + desc: t("routingAntigravitySignatureBypassDesc"), }, { value: "bypass-strict", - label: "Bypass Strict", - desc: "Require full protobuf validation before accepting a client-provided signature.", + label: t("routingAntigravitySignatureBypassStrictLabel"), + desc: t("routingAntigravitySignatureBypassStrictDesc"), }, ].map((option) => (
} - subtitle={`${opCount} op${opCount === 1 ? "" : "s"} · ${enabled ? "enabled" : "disabled"}`} + subtitle={ + t("routingOpSummaryCount", { count: opCount }) + + t("routingOpStatusSeparator") + + (enabled ? t("routingOpEnabled") : t("routingOpDisabled")) + } trailing={ <> toggleProviderEnabled(providerId, checked)} disabled={loading} - ariaLabel={`Enable ${display.name} transforms`} + ariaLabel={ + t("common.enable") + " " + display.name + " " + t("systemTransforms") + } /> {!isBuiltin && (
)} @@ -1252,9 +1257,12 @@ export default function RoutingTab() { {index + 1} - {op?.kind} + + {t(OP_KIND_LABELS[op?.kind as TransformOpKind] || op?.kind)} +
} + subtitle={summarizeTransformOp(op, t)} trailing={ <>
@@ -1336,12 +1344,14 @@ export default function RoutingTab() { } className="text-[11px] text-primary hover:underline" > - {isJsonOpen ? "▾ Hide JSON editor" : "▸ Import / export JSON"} + {isJsonOpen + ? "▾ " + t("common.hide") + " JSON editor" + : "▸ Import / export JSON"} {isJsonOpen && (