feat: add Russian localization and independent STT languages
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
"use client";
|
||||
import { useI18n } from "@/components/i18n";
|
||||
|
||||
|
||||
import { useContext } from "react";
|
||||
import { CheckIcon, SettingsIcon, XIcon } from "lucide-react";
|
||||
@@ -27,12 +29,13 @@ const KIND_LABEL: Record<JobKind, string> = {
|
||||
};
|
||||
|
||||
export function DispatchCard({ toolCallId, kind }: { toolCallId: string; kind: JobKind }) {
|
||||
const { t } = useI18n();
|
||||
const jobs = useContext(JobsContext);
|
||||
const job = jobs.find((j) => j.toolCallId === toolCallId) ?? null;
|
||||
const label = KIND_LABEL[kind];
|
||||
|
||||
if (!job || job.status === "running") {
|
||||
return <DispatchActivityCard job={job} label={label} />;
|
||||
return <DispatchActivityCard job={job} label={t(label)} />;
|
||||
}
|
||||
// BYOK (owner, 2026-09-22): a job refused for missing models is INFO,
|
||||
// not scary-error — gray glyph + "needs model setup" + the deep-link
|
||||
@@ -41,10 +44,10 @@ export function DispatchCard({ toolCallId, kind }: { toolCallId: string; kind: J
|
||||
// whose key is gone reads as the key being needed — the deep-link
|
||||
// lands on the SetupDialog's first-key stage on its own.
|
||||
if (isModelsRefusal(job.error)) {
|
||||
return <DispatchRefusedCard label={label} line="needs model setup" button="Set up models" />;
|
||||
return <DispatchRefusedCard label={t(label)} line={t("needs model setup")} button={t("Set up models")} />;
|
||||
}
|
||||
if (isProviderKeyRefusal(job.error)) {
|
||||
return <DispatchRefusedCard label={label} line="needs a provider key" button="Add a key" />;
|
||||
return <DispatchRefusedCard label={t(label)} line={t("needs a provider key")} button={t("Add a key")} />;
|
||||
}
|
||||
const failed = job.status === "error";
|
||||
return (
|
||||
@@ -53,14 +56,15 @@ export function DispatchCard({ toolCallId, kind }: { toolCallId: string; kind: J
|
||||
data-testid="dispatch-settled"
|
||||
>
|
||||
{failed ? <XIcon className="size-3.5 text-red-500" /> : <CheckIcon className="size-3.5" />}
|
||||
<span className="font-medium uppercase tracking-wide">{label}</span>
|
||||
<span>{failed ? "failed" : "delivered"}</span>
|
||||
<span className="font-medium uppercase tracking-wide">{t(label)}</span>
|
||||
<span>{failed ? t("failed") : t("delivered")}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/** The refused card: quiet info tone — nothing red, nothing "failed". */
|
||||
function DispatchRefusedCard({ label, line, button }: { label: string; line: string; button: string }) {
|
||||
const { t } = useI18n();
|
||||
const ctx = useContext(ChatActionsContext);
|
||||
return (
|
||||
<div
|
||||
@@ -68,7 +72,7 @@ function DispatchRefusedCard({ label, line, button }: { label: string; line: str
|
||||
data-testid="dispatch-refused"
|
||||
>
|
||||
<SettingsIcon className="size-3.5" />
|
||||
<span className="font-medium uppercase tracking-wide">{label}</span>
|
||||
<span className="font-medium uppercase tracking-wide">{t(label)}</span>
|
||||
<span>{line}</span>
|
||||
{ctx && (
|
||||
<button
|
||||
@@ -85,6 +89,7 @@ function DispatchRefusedCard({ label, line, button }: { label: string; line: str
|
||||
}
|
||||
|
||||
function DispatchActivityCard({ job, label }: { job: JobRecord | null; label: string }) {
|
||||
const { t } = useI18n();
|
||||
// Elapsed from createdAt — jobs appear here only via the jobs feed (post-mount),
|
||||
// so no SSR/hydration mismatch.
|
||||
const elapsed =
|
||||
@@ -99,19 +104,18 @@ function DispatchActivityCard({ job, label }: { job: JobRecord | null; label: st
|
||||
the Braille spinner replaces the pulsing dot — one indicator
|
||||
everywhere. This is the ONLY dispatch-card change; the card's
|
||||
shape is untouched. */}
|
||||
<BrailleSpinner label={`${label} running`} />
|
||||
{label}
|
||||
{elapsed !== null && <span className="font-normal">· {elapsed}s</span>}
|
||||
<BrailleSpinner label={t("{v0} running", { v0: label })} />
|
||||
{t(label)}
|
||||
{elapsed !== null && <span className="font-normal">· {t("{v0}s", { v0: elapsed })}</span>}
|
||||
</div>
|
||||
{job?.activity && (
|
||||
<div className="mt-1 truncate text-sm text-muted-foreground" data-testid="dispatch-activity-line">
|
||||
{job.activity}
|
||||
{t(job.activity)}
|
||||
</div>
|
||||
)}
|
||||
{!job && (
|
||||
<div className="mt-1 text-sm text-muted-foreground" data-testid="dispatch-pending">
|
||||
Dispatched…
|
||||
</div>
|
||||
{t("Dispatched…")}</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user