import { useState, type ReactNode } from "react"; import { api } from "../api"; import { Icon } from "../ui/Icon"; import { useUi } from "../ui/Ui"; import { STATE_LABEL, type GlossaryTerm, type LangStats, type LineState } from "./translation-types"; export function ProgressBar({ stats }: { stats: LangStats }) { const total = stats.lines || 1; const seg = (state: LineState, cls: string) => stats.states[state] ?
: null; return (
{seg("approved", "seg-approved")} {seg("translated", "seg-ok")} {seg("fuzzy", "seg-warn")} {seg("machine", "seg-warn")} {seg("outdated", "seg-error")}
{stats.translated} из {stats.lines} реплик ({Math.round(stats.progress * 100)}%) {stats.states.outdated ? ` · устарело ${stats.states.outdated}` : ""} {" · "}строк {stats.stringsTranslated}/{stats.strings}
); } export function highlight(text: string, terms: GlossaryTerm[]): ReactNode { if (!terms.length) return text; const escaped = terms.map((t) => t.src.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).sort((a, b) => b.length - a.length); const re = new RegExp(`(${escaped.join("|")})`, "gi"); return text.split(re).map((part, i) => { const term = i % 2 ? terms.find((t) => t.src.toLowerCase() === part.toLowerCase()) : undefined; return term ? {part} : part; }); } export function GlossaryEditor({ versionId, lang, editable, terms, onChange }: { versionId: string; lang: string; editable: boolean; terms: GlossaryTerm[]; onChange: (t: GlossaryTerm[]) => void; }) { const ui = useUi(); const [rows, setRows] = useState(terms.length ? terms : [{ src: "", t: "", note: "" }]); const save = async () => { try { await api.put(`/api/versions/${versionId}/i18n/${lang}`, { glossary: rows }); onChange(rows.filter((r) => r.src.trim())); ui.toast("Глоссарий сохранён", "ok"); } catch (e) { ui.toast((e as Error).message, "error"); } }; const set = (i: number, key: keyof GlossaryTerm, value: string) => setRows((rs) => rs.map((r, n) => n === i ? { ...r, [key]: value } : r)); return (

Имена и термины, которые нужно переводить единообразно. В репликах они подсвечиваются, при наведении виден принятый перевод.

{rows.map((r, i) => ( ))}
ОригиналПереводЗаметка
set(i, "src", e.target.value)} /> set(i, "t", e.target.value)} lang={lang} /> set(i, "note", e.target.value)} /> {editable && ( )}
{editable && (
)}
); }