import { useCallback, useEffect, useMemo, useState } from "react"; import { Link } from "react-router-dom"; import { api } from "../api"; import { ErrorNote } from "../ui/Bits"; import { Icon } from "../ui/Icon"; import { statusText, useTranslationRun, type RunState } from "./runner"; import type { Session } from "./session"; import "./translator.css"; /** What the list below shows right now: the run walks it in the same order. */ export interface View { kind: "lines" | "strings" | "glossary"; module: string; q: string; } /** Machine translation of the list on screen through the translator running on this computer. */ export function MachineTranslate({ session, versionId, lang, title, view, follow, onFollow, onClose }: { session: Session; versionId: string; lang: string; /** the game and version, for the card in the floating window */ title: string; view: View; /** the list jumps to the page being translated */ follow: boolean; onFollow: (on: boolean) => void; onClose: () => void; }) { const { client, link, ready, prefs, setPref } = session; const run = useTranslationRun(); const task = run.task; const mine = !!task && task.versionId === versionId && task.lang === lang; const running = run.busy && mine; const elsewhere = run.busy && !mine; const [todo, setTodo] = useState(null); const [preparing, setPreparing] = useState(false); const [error, setError] = useState(""); useEffect(() => { if (link === "idle") void session.connect(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const listable = view.kind !== "glossary"; const states = useMemo(() => (prefs.outdated ? ["untranslated", "outdated"] : ["untranslated"]), [prefs.outdated]); const countUrl = useCallback((state: RunState) => `/api/versions/${versionId}/i18n/${lang}/entries?${new URLSearchParams({ kind: view.kind, state, module: view.module, q: view.q, offset: "0", limit: "1" })}`, [versionId, lang, view.kind, view.module, view.q]); // how much there is to translate with the current list and options useEffect(() => { if (run.busy || !listable) return; let stale = false; Promise.all(states.map((s) => api.get<{ total: number }>(countUrl(s)))) .then((r) => !stale && setTodo(r.reduce((n, x) => n + x.total, 0))).catch(() => !stale && setTodo(null)); return () => { stale = true; }; }, [states, countUrl, run.busy, listable, task?.status]); const start = async () => { if (view.kind === "glossary") return; setError(""); setPreparing(true); try { const projectId = await session.project(); const started = await run.start({ client, projectId, versionId, lang, title, kind: view.kind, module: view.module, q: view.q, states, batch: prefs.batch, }); if (!started) setError("Другой перевод уже идёт (возможно, в другой вкладке): дождитесь его или остановите."); } catch (e) { setError((e as Error).message); } finally { setPreparing(false); } }; const health = link !== "idle" && link !== "checking" && link.state === "ok" ? link.health : null; const scope = view.kind === "strings" ? "имена, меню и интерфейс" : "реплики"; const progress = mine && task ? task.progress : null; const percent = progress && progress.total ? Math.min(100, ((progress.done + progress.failed) / progress.total) * 100) : 0; const locked = running || elsewhere; return (

Машинный перевод

Перевод выполняет программа на вашем компьютере, сайт только получает готовые строки с пометкой «Машинный». Как установить и настроить. Отдельную строку можно перевести кнопкой «Перевести» рядом с ней. Перевод идёт в фоне: ход виден в окне справа внизу, можно переходить по сайту.

{(link === "idle" || link === "checking") &&

Ищу переводчик на {client.base}…

} {typeof link === "object" && link.state === "offline" && ( Переводчик не отвечает на {client.base}. Запустите его (инструкция) и нажмите «Проверить снова». )} {typeof link === "object" && link.state === "blocked" && ( Переводчик запущен, но браузер не пускает этот сайт к нему. Добавьте {location.origin} в allowed_origins в config.toml (в начале файла, выше первой секции) и перезапустите переводчик (подробнее). )} {typeof link === "object" && link.state === "old" && ( Версия переводчика слишком старая: обновите репозиторий local-translator и перезапустите его. )} {typeof link === "object" && link.state !== "ok" && (
)} {health?.startup_errors[prefs.provider] && {health.startup_errors[prefs.provider]}} {elsewhere && task && (
Сейчас идёт другой перевод: «{task.title}» ({task.lang}). Одновременно работает только один. Дождитесь его окончания или остановите.{" "}
)} {ready && ( <>