Zpět na domů

Překlad OpenClaw do ruštiny bez zdrojových kódů

Článek popisuje implementaci runtime-lokalizace OpenClaw do ruského jazyka přes průhledný proxy server. Node.js služba injektuje skript překladu do HTML, MutationObserver zpracovává dynamický DOM SPA aplikace. Řešení nevyžaduje fork zdrojových kódů a funguje s denními aktualizacemi.

Ruský rozhraní OpenClaw: proxy bez fork
Advertisement 728x90

Lokalizace OpenClaw do ruštiny prostřednictvím proxy bez změny zdrojového kódu

Vývojáři OpenClaw nepředviděli infrastrukturu pro i18n: řetězce jsou pevně zakódované v JSX komponentách bez použití react-intl nebo i18next. Fork s ručním překladem je neefektivní kvůli denním aktualizacím. Řešením je transparentní proxy mezi nginx a aplikací, která vkládá skript překladu do HTML odpovědí.

Schéma provozu:

  • nginx (port 443) → proxy (port 18790) → OpenClaw (port 18789).

Proxy upravuje pouze HTML, zachovává logiku aplikace. WebSocket spojení pro chat se přesměruje beze změn.

Google AdInline article slot

Server na Node.js (~150 řádků) používá http-proxy pro zachycení odpovědí. Klíčovým momentem je nahrazení res.write a res.end pro vyrovnávací paměť těla odpovědi.

import http from "node:http";
import httpProxy from "http-proxy";

const TARGET = process.env.TARGET_ORIGIN || "http://127.0.0.1:18789";
const proxy = httpProxy.createProxyServer({ target: TARGET, ws: true });

const server = http.createServer((req, res) => {
  if (req.url === "/ru-overlay.js") {
    // poskytneme skript překladu
    res.writeHead(200, { "Content-Type": "application/javascript" });
    res.end(overlayScript);
    return;
  }

  // zachytáme odpověď
  const _write = res.write.bind(res);
  const _end = res.end.bind(res);
  let chunks = [];

  res.write = (chunk) => { chunks.push(chunk); };
  res.end = (chunk) => {
    if (chunk) chunks.push(chunk);
    let body = Buffer.concat(chunks).toString("utf-8");

    if (res.getHeader("content-type")?.includes("text/html")) {
      body = body.replace(
        "</body>",
        `<script src="/ru-overlay.js"></script></body>`
      );
    }
    // nutné odstranit content-length, jinak prohlížeč
    // zkrátí odpověď – délka se změnila
    res.removeHeader("content-length");
    _write(body);
    _end();
  };

  proxy.web(req, res);
});

Zpracování WebSocket vyžaduje sledování události upgrade:

server.on("upgrade", (req, socket, head) => {
  proxy.ws(req, socket, head);
});

Klientský skript s MutationObserver

OpenClaw je SPA, DOM se dynamicky přestavuje při navigaci. Jednoduchý průchod textovými uzly při načtení nefunguje. MutationObserver sleduje přidávání uzlů a okamžitě je překládá.

Google AdInline article slot

Slovník překladů ve formátu JSON (~200 řádků):

const dict = {
  "Settings": "Nastavení",
  "New conversation": "Nový rozhovor",
  "Send a message": "Odeslat zprávu",
  "Skills": "Dovednosti",
  "Memory": "Paměť",
  "Schedule": "Rozvrh",
  "Delete": "Smazat",
  "Cancel": "Zrušit",
  "Save": "Uložit",
  // ... dalších 200 řádků
};

function translateNode(node) {
  if (node.nodeType !== Node.TEXT_NODE) return;
  const key = node.textContent.trim();
  if (dict[key]) {
    node.textContent = node.textContent.replace(key, dict[key]);
  }
}

// primární průchod
const walker = document.createTreeWalker(
  document.body, NodeFilter.SHOW_TEXT
);
while (walker.nextNode()) translateNode(walker.currentNode);

// sledujeme nové prvky
new MutationObserver((mutations) => {
  for (const m of mutations) {
    for (const node of m.addedNodes) {
      if (node.nodeType === Node.ELEMENT_NODE) {
        const w = document.createTreeWalker(
          node, NodeFilter.SHOW_TEXT
        );
        while (w.nextNode()) translateNode(w.currentNode);
      } else {
        translateNode(node);
      }
    }
  }
}).observe(document.body, { childList: true, subtree: true });

Hlavní komponenty skriptu:

  • TreeWalker pro procházení textových uzlů.
  • MutationObserver s možnostmi { childList: true, subtree: true }.
  • Přesné shodné klíče po trim() pro minimalizaci kolizí.

Omezení přístupu

  • Atributy placeholder: Nejsou zachyceny TreeWalkerem, vyžadují samostatného pozorovatele atributů input/select.
  • Kontextové homonyma: "Run" v tlačítku a textu – různé překlady, slovník je nerozlišuje.
  • Nové řádky: Při aktualizacích OpenClaw zůstávají nepřeložené prvky v angličtině, dokud se neaktualizuje slovník.

Výkon: prodleva překladu ~10–50 ms, uživatel ji nezaznamená.

Google AdInline article slot

Nasazení

Instalace skriptem:

  • git clone https://github.com/perfectinn/openclaw-ru-layer
  • cd openclaw-ru-layer
  • sudo bash scripts/install.sh --patch-nginx

Automaticky nastaví systemd službu a nginx. Odinstalace: sudo bash scripts/uninstall.sh.

Docker varianta:

docker build -t openclaw-ru-layer .
docker run --rm -p 18790:18790 \
  -e TARGET_ORIGIN=http://host.docker.internal:18789 \
  openclaw-ru-layer

Co je důležité:

  • Proxy nepotřebuje změny v OpenClaw, funguje s aktuální verzí.
  • MutationObserver zajišťuje překlad dynamického SPA obsahu.
  • Aktualizace slovníku – přidání řádků do JSON bez restartu serveru.
  • WebSocket se správně přesměruje pro zachování chatu.
  • Minimální overhead: ~150 řádků kódu, 5–6 aktualizací slovníku za 2 měsíce.

Alternativy a škálování

PR do hlavního repozitáře není vhodný: vyžaduje refaktoring (vyňatí řetězců do JSON, obalení komponent i18n knihovnou). Současné řešení lze nasadit za minuty.

Pro jiné SPA jsou možné vylepšení:

  • Shadow DOM: Další walker pro shadow roots.
  • Virtual DOM diff: Hook na React DevTools API (experimentálně).
  • Service Worker: Zachycení fetch požadavků na úrovni prohlížeče.

— Editorial Team

Advertisement 728x90

Číst dál