fix(ui): localize chat status indicators

This commit is contained in:
Vincent Koc
2026-07-28 08:03:21 +02:00
parent 4e8c2c5248
commit 984990e28b
5 changed files with 89 additions and 23 deletions
-14
View File
@@ -519,20 +519,6 @@
"path": "ui/src/pages/chat/components/chat-composer-slash-menu.ts",
"text": "Tab"
},
{
"count": 1,
"kind": "html-text",
"name": "text",
"path": "ui/src/pages/chat/components/chat-composer-status.ts",
"text": "Compacting context..."
},
{
"count": 1,
"kind": "html-text",
"name": "text",
"path": "ui/src/pages/chat/components/chat-composer-status.ts",
"text": "Context compacted"
},
{
"count": 1,
"kind": "html-text",
+9
View File
@@ -4439,6 +4439,15 @@ export const en: TranslationMap = {
runDone: "Done",
runInterrupted: "Interrupted",
runStatus: "Run status: {status}",
compactingContext: "Compacting context...",
contextCompacted: "Context compacted",
fallbackActive: "Fallback active: {model}",
fallbackCleared: "Fallback cleared: {model}",
fallbackSelected: "Selected: {model}",
fallbackCurrent: "Active: {model}",
fallbackPrevious: "Previous fallback: {model}",
fallbackReason: "Reason: {reason}",
fallbackAttempts: "Attempts: {attempts}",
cancelReply: "Cancel reply",
compactRecommendedContext: "Compact recommended thread context",
removeAttachment: "Remove attachment",
+5
View File
@@ -858,6 +858,11 @@ describe("renderChatComposer status", () => {
expect(container.querySelector(".compaction-indicator--fallback")?.textContent?.trim()).toBe(
"Fallback active: deepinfra/moonshotai/Kimi-K2.5",
);
expect(
container.querySelector(".compaction-indicator--fallback")?.getAttribute("aria-label"),
).toBe(
"Selected: fireworks/minimax-m2p5 • Active: deepinfra/moonshotai/Kimi-K2.5 • Attempts: fireworks/minimax-m2p5: rate limit",
);
});
it("renders an expandable live plan checklist and hides it when idle", () => {
@@ -0,0 +1,58 @@
import { render } from "lit";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { i18n } from "../../../i18n/index.ts";
import { renderCompactionIndicator, renderFallbackIndicator } from "./chat-composer-status.ts";
describe("chat composer status localization", () => {
beforeEach(async () => {
i18n.registerTranslation("de", {
chat: {
composer: {
compactingContext: "Kontext wird komprimiert...",
fallbackActive: "Ausweichmodell aktiv: {model}",
fallbackSelected: "Ausgewählt: {model}",
fallbackCurrent: "Aktiv: {model}",
fallbackAttempts: "Versuche: {attempts}",
},
},
});
await i18n.setLocale("de");
vi.spyOn(Date, "now").mockReturnValue(1_000);
});
afterEach(async () => {
await i18n.setLocale("en");
vi.restoreAllMocks();
document.body.innerHTML = "";
});
it("renders translated compaction and fallback status", () => {
render(
renderCompactionIndicator({
phase: "active",
runId: "run-1",
startedAt: 1_000,
completedAt: null,
}),
document.body,
);
expect(document.querySelector(".compaction-indicator")?.textContent?.trim()).toBe(
"Kontext wird komprimiert...",
);
render(
renderFallbackIndicator({
selected: "provider/selected",
active: "provider/active",
attempts: ["provider/selected: rate limit"],
occurredAt: 900,
}),
document.body,
);
const fallback = document.querySelector(".compaction-indicator--fallback");
expect(fallback?.textContent?.trim()).toBe("Ausweichmodell aktiv: provider/active");
expect(fallback?.getAttribute("aria-label")).toBe(
"Ausgewählt: provider/selected • Aktiv: provider/active • Versuche: provider/selected: rate limit",
);
});
});
@@ -50,7 +50,7 @@ export function renderCompactionIndicator(status: CompactionStatus | null | unde
role="status"
aria-live="polite"
>
${icons.loader} Compacting context...
${icons.loader} ${t("chat.composer.compactingContext")}
</div>
`;
}
@@ -63,7 +63,7 @@ export function renderCompactionIndicator(status: CompactionStatus | null | unde
role="status"
aria-live="polite"
>
${icons.check} Context compacted
${icons.check} ${t("chat.composer.contextCompacted")}
</div>
`;
}
@@ -81,18 +81,26 @@ export function renderFallbackIndicator(status: FallbackStatus | null | undefine
return nothing;
}
const details = [
`Selected: ${status.selected}`,
phase === "cleared" ? `Active: ${status.selected}` : `Active: ${status.active}`,
phase === "cleared" && status.previous ? `Previous fallback: ${status.previous}` : null,
status.reason ? `Reason: ${status.reason}` : null,
status.attempts.length > 0 ? `Attempts: ${status.attempts.slice(0, 3).join(" | ")}` : null,
t("chat.composer.fallbackSelected", { model: status.selected }),
t("chat.composer.fallbackCurrent", {
model: phase === "cleared" ? status.selected : status.active,
}),
phase === "cleared" && status.previous
? t("chat.composer.fallbackPrevious", { model: status.previous })
: null,
status.reason ? t("chat.composer.fallbackReason", { reason: status.reason }) : null,
status.attempts.length > 0
? t("chat.composer.fallbackAttempts", {
attempts: status.attempts.slice(0, 3).join(" | "),
})
: null,
]
.filter(Boolean)
.join(" • ");
const message =
phase === "cleared"
? `Fallback cleared: ${status.selected}`
: `Fallback active: ${status.active}`;
? t("chat.composer.fallbackCleared", { model: status.selected })
: t("chat.composer.fallbackActive", { model: status.active });
const className =
phase === "cleared"
? "compaction-indicator compaction-indicator--fallback-cleared"