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 { useEffect, useState } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
@@ -45,17 +47,18 @@ export function DeleteNodeBody({
|
||||
onClose: () => void;
|
||||
onRun: () => void;
|
||||
}) {
|
||||
const { t } = useI18n();
|
||||
return (
|
||||
<div
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-label="Delete node"
|
||||
aria-label={t("Delete node")}
|
||||
data-testid="delete-node-dialog"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
className="w-[32rem] max-w-[calc(100vw-2rem)] rounded-xl border bg-card p-5 shadow-xl"
|
||||
>
|
||||
<h2 className="text-sm font-semibold text-destructive">
|
||||
{nodes.length === 1 ? "Delete node?" : `Delete ${nodes.length} nodes?`}
|
||||
{nodes.length === 1 ? t("Delete node?") : t("Delete {v0} nodes?", { v0: nodes.length })}
|
||||
</h2>
|
||||
{/* The node name(s) below the title in JetBrains Mono (owner,
|
||||
2026-09-24): the name is the payload — mono suits it, and
|
||||
@@ -74,14 +77,14 @@ export function DeleteNodeBody({
|
||||
) : (
|
||||
<code data-testid="node-names-code" className="font-mono text-sm text-foreground">
|
||||
{nodes.slice(0, 8).map((n) => n.name).join(", ")}
|
||||
{nodes.length > 8 ? `, and ${nodes.length - 8} more` : ""}
|
||||
{nodes.length > 8 ? t(", and {v0} more", { v0: nodes.length - 8 }) : ""}
|
||||
</code>
|
||||
)}
|
||||
</div>
|
||||
<p className="mt-2 text-sm text-muted-foreground" data-testid="node-permanence-line">
|
||||
{nodes.length === 1
|
||||
? "This permanently deletes the node — this cannot be undone."
|
||||
: `This permanently deletes all ${nodes.length} nodes — this cannot be undone.`}
|
||||
? t("This permanently deletes the node — this cannot be undone.")
|
||||
: t("This permanently deletes all {v0} nodes — this cannot be undone.", { v0: nodes.length })}
|
||||
</p>
|
||||
{failure && (
|
||||
<div
|
||||
@@ -90,7 +93,7 @@ export function DeleteNodeBody({
|
||||
className="mt-3 rounded-sm border border-border/60 px-3 py-2 text-xs"
|
||||
>
|
||||
<span className="text-foreground">
|
||||
{failure.code === "extraction_running" ? EXTRACTION_RETRY_LINE : failure.error}
|
||||
{t(failure.code === "extraction_running" ? EXTRACTION_RETRY_LINE : failure.error)}
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
@@ -99,8 +102,7 @@ export function DeleteNodeBody({
|
||||
disabled={busy}
|
||||
className="ml-3 text-xs text-foreground underline-offset-2 hover:underline disabled:opacity-40"
|
||||
>
|
||||
Try again
|
||||
</button>
|
||||
{t("Try again")}</button>
|
||||
</div>
|
||||
)}
|
||||
<div className="mt-4 flex justify-end gap-2">
|
||||
@@ -111,8 +113,7 @@ export function DeleteNodeBody({
|
||||
disabled={busy}
|
||||
className="rounded-sm px-3 py-1.5 text-xs text-muted-foreground transition-colors hover:text-foreground disabled:opacity-40"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
{t("Cancel")}</button>
|
||||
<button
|
||||
type="button"
|
||||
data-testid="node-delete-confirm"
|
||||
@@ -123,7 +124,7 @@ export function DeleteNodeBody({
|
||||
// refusal banner): border + text, fill on hover.
|
||||
className="rounded-sm border border-destructive/50 bg-destructive/10 px-3 py-1.5 text-xs text-destructive transition-colors hover:bg-destructive/20 disabled:opacity-40"
|
||||
>
|
||||
{busy ? "Deleting…" : "Delete"}
|
||||
{busy ? t("Deleting…") : t("Delete")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -146,6 +147,7 @@ export function DeleteNodeDialog({
|
||||
* selection-side callers can prune. */
|
||||
onDeleted: (nodeIds: string[], graphRevision: number) => void;
|
||||
}) {
|
||||
const { t } = useI18n();
|
||||
const [busy, setBusy] = useState(false);
|
||||
const backdropDismiss = useBackdropDismiss(() => {
|
||||
if (!busy) onClose();
|
||||
|
||||
Reference in New Issue
Block a user