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 { MessageResponse } from "@/components/ai-elements/message";
|
||||
import { cn } from "@/lib/utils";
|
||||
@@ -56,6 +58,7 @@ export interface PracticeThinkingData {
|
||||
}
|
||||
|
||||
export function ProblemCard({ part }: ArtifactPartProps) {
|
||||
const { t } = useI18n();
|
||||
// Always open, no chevron (owner round, 2026-10-05): the record IS the
|
||||
// statement — same container + eyebrow discipline as the answered card
|
||||
// and the dock (which mirrors this in Slice D). The commitment never
|
||||
@@ -64,7 +67,7 @@ export function ProblemCard({ part }: ArtifactPartProps) {
|
||||
const statement = typeof input.statement === "string" ? input.statement : "";
|
||||
return (
|
||||
<div data-testid="problem-card" className="lh-card-enter my-1 rounded-xl border bg-card p-4">
|
||||
<div className="mb-1 text-xs font-medium uppercase tracking-wide text-muted-foreground">Problem</div>
|
||||
<div className="mb-1 text-xs font-medium uppercase tracking-wide text-muted-foreground">{t("Problem")}</div>
|
||||
<div className="lh-prose text-[15px]">
|
||||
<MessageResponse>{repairMathForDisplay(statement)}</MessageResponse>
|
||||
</div>
|
||||
@@ -94,6 +97,7 @@ const OUTCOME_CHIP: Record<string, string> = {
|
||||
};
|
||||
|
||||
export function ConcludeCard({ part }: ArtifactPartProps) {
|
||||
const { t } = useI18n();
|
||||
// The conclusion redesigned (owner round, 2026-10-05): the SOLUTION is
|
||||
// the hero — the modelSolution committed at pose time (part.reveal, the
|
||||
// view-seam join), shown verbatim, never re-authored. The verdict sits
|
||||
@@ -111,8 +115,7 @@ export function ConcludeCard({ part }: ArtifactPartProps) {
|
||||
if (outcome === "dismissed") {
|
||||
return (
|
||||
<div data-testid="conclude-card" className="my-1 px-1 text-sm text-muted-foreground/70">
|
||||
Problem dismissed.
|
||||
</div>
|
||||
{t("Problem dismissed.")}</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -122,10 +125,9 @@ export function ConcludeCard({ part }: ArtifactPartProps) {
|
||||
eyebrow + the outcome in the quiz cards' EXACT chip register —
|
||||
no more two same-weight headings stacked. */}
|
||||
<div className="mb-1 flex items-center gap-2 text-xs font-medium uppercase tracking-wide text-muted-foreground">
|
||||
Concluded
|
||||
{(OUTCOME_LABEL[outcome] ?? "") !== "" && outcome !== "dismissed" && (
|
||||
{t("Concluded")}{(OUTCOME_LABEL[outcome] ?? "") !== "" && outcome !== "dismissed" && (
|
||||
<span className={cn("rounded px-1.5 py-0.5", OUTCOME_CHIP[outcome] ?? OUTCOME_CHIP.movedOn)}>
|
||||
{OUTCOME_LABEL[outcome]}
|
||||
{t(OUTCOME_LABEL[outcome] ?? outcome)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
@@ -142,7 +144,7 @@ export function ConcludeCard({ part }: ArtifactPartProps) {
|
||||
<>
|
||||
<div className="my-3 border-t" />
|
||||
<div>
|
||||
<div className="mb-0.5 text-xs font-medium uppercase tracking-wide text-muted-foreground">Model solution</div>
|
||||
<div className="mb-0.5 text-xs font-medium uppercase tracking-wide text-muted-foreground">{t("Model solution")}</div>
|
||||
<div className="lh-prose text-[15px]">
|
||||
<MessageResponse>{repairMathForDisplay(reveal)}</MessageResponse>
|
||||
</div>
|
||||
@@ -177,6 +179,7 @@ function MetadataPopover({
|
||||
concludeInput: { outcome?: string; rung?: unknown; missClass?: unknown; retest?: unknown };
|
||||
problemInput: { mode?: unknown; target?: unknown };
|
||||
}) {
|
||||
const { t } = useI18n();
|
||||
const [open, setOpen] = useState(false);
|
||||
const triggerRef = useRef<HTMLButtonElement | null>(null);
|
||||
const rootRef = useRef<HTMLDivElement | null>(null);
|
||||
@@ -239,8 +242,7 @@ function MetadataPopover({
|
||||
aria-expanded={open}
|
||||
className="flex items-center gap-1 text-xs font-medium uppercase tracking-wide text-muted-foreground transition-colors hover:text-foreground"
|
||||
>
|
||||
Metadata
|
||||
</button>
|
||||
{t("Metadata")}</button>
|
||||
{open &&
|
||||
anchor &&
|
||||
createPortal(
|
||||
@@ -256,12 +258,12 @@ function MetadataPopover({
|
||||
className="z-50 w-80 max-w-[calc(100vw-1rem)] rounded-xl border bg-card p-4 text-left shadow-lg"
|
||||
>
|
||||
<dl className="grid grid-cols-[auto_1fr] gap-x-4 gap-y-1 text-xs">
|
||||
<dt className="text-muted-foreground">Mode</dt>
|
||||
<dd className="font-mono">{mode || "—"}</dd>
|
||||
<dt className="text-muted-foreground">Targets</dt>
|
||||
<dt className="text-muted-foreground">{t("Mode")}</dt>
|
||||
<dd className="font-mono">{t(mode || "—")}</dd>
|
||||
<dt className="text-muted-foreground">{t("Targets")}</dt>
|
||||
<dd className="font-mono">
|
||||
{targets.length === 0 ? (
|
||||
"untagged"
|
||||
t("untagged")
|
||||
) : (
|
||||
<span>
|
||||
{targets.map((t, i) => (
|
||||
@@ -273,14 +275,14 @@ function MetadataPopover({
|
||||
</span>
|
||||
)}
|
||||
</dd>
|
||||
<dt className="text-muted-foreground">Outcome</dt>
|
||||
<dd className="font-mono">{outcome || "—"}</dd>
|
||||
<dt className="text-muted-foreground">Hint rung</dt>
|
||||
<dd className="font-mono">{rung || "none"}</dd>
|
||||
<dt className="text-muted-foreground">Miss class</dt>
|
||||
<dd className="font-mono">{miss || "none"}</dd>
|
||||
<dt className="text-muted-foreground">Retest</dt>
|
||||
<dd className="font-mono">{retest === undefined ? "—" : retest ? "yes" : "no"}</dd>
|
||||
<dt className="text-muted-foreground">{t("Outcome")}</dt>
|
||||
<dd className="font-mono">{t(outcome || "—")}</dd>
|
||||
<dt className="text-muted-foreground">{t("Hint rung")}</dt>
|
||||
<dd className="font-mono">{t(rung || "none")}</dd>
|
||||
<dt className="text-muted-foreground">{t("Miss class")}</dt>
|
||||
<dd className="font-mono">{t(miss || "none")}</dd>
|
||||
<dt className="text-muted-foreground">{t("Retest")}</dt>
|
||||
<dd className="font-mono">{retest === undefined ? "—" : retest ? t("yes") : t("no")}</dd>
|
||||
</dl>
|
||||
</div>,
|
||||
document.body,
|
||||
@@ -290,9 +292,10 @@ function MetadataPopover({
|
||||
}
|
||||
|
||||
export function ThinkingCard({ data }: { data: PracticeThinkingData }) {
|
||||
const { t } = useI18n();
|
||||
// The unified row (owner rounds, 2026-10-05): one style, one label —
|
||||
// 50%-italic quote block on expand. Legacy records' commit flag is
|
||||
// deliberately unread (one lane now). A TRANSCRIPT row (owner round,
|
||||
// 2026-10-07): full-width band + rail, arriving expanded.
|
||||
return <ThinkingBlock testId="thinking-card" label="YOUR THINKING" text={data.text} defaultOpen />;
|
||||
return <ThinkingBlock testId="thinking-card" label={t("YOUR THINKING")} text={data.text} defaultOpen />;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user