Volver al inicio

Traducción de OpenClaw al Ruso Sin Código Fuente

Artículo describe la implementación de localización en tiempo de ejecución de OpenClaw al ruso mediante servidor proxy transparente. Servicio Node.js inyecta script de traducción en HTML, MutationObserver maneja el DOM dinámico de la aplicación SPA. La solución no requiere bifurcar las fuentes y funciona con actualizaciones diarias.

Interfaz de OpenClaw en Ruso: Proxy Sin Fork
Advertisement 728x90

Localizar OpenClaw en ruso mediante proxy sin cambios de código

Los desarrolladores de OpenClaw no incluyeron infraestructura de i18n: las cadenas están codificadas directamente en los componentes JSX sin usar react-intl ni i18next. Hacer una bifurcación con traducciones manuales no es práctico debido a las actualizaciones diarias. La solución? Un proxy transparente entre nginx y la aplicación que inyecta un script de traducción en las respuestas HTML.

Flujo de tráfico:

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

El proxy modifica solo el HTML, preservando la lógica de la aplicación. Las conexiones WebSocket para el chat se pasan sin cambios.

Google AdInline article slot

Un servidor Node.js (~150 líneas) usa http-proxy para interceptar las respuestas. Truco clave: sobrescribir res.write y res.end para almacenar en búfer el cuerpo de la respuesta.

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") {
    // servir el script de traducción
    res.writeHead(200, { "Content-Type": "application/javascript" });
    res.end(overlayScript);
    return;
  }

  // interceptar respuesta
  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>`
      );
    }
    // eliminar content-length, sino el navegador trunca la respuesta
    // por cambio de longitud
    res.removeHeader("content-length");
    _write(body);
    _end();
  };

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

El manejo de WebSocket requiere escuchar el evento upgrade:

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

Script del lado del cliente con MutationObserver

OpenClaw es una SPA: el DOM se reconstruye dinámicamente al navegar. Una simple exploración de nodos de texto al cargar no funcionará. MutationObserver rastrea nuevos nodos y los traduce instantáneamente.

Google AdInline article slot

Diccionario de traducción en JSON (~200 líneas):

const dict = {
  "Settings": "Configuración",
  "New conversation": "Nuevo diálogo",
  "Send a message": "Enviar mensaje",
  "Skills": "Habilidades",
  "Memory": "Memoria",
  "Schedule": "Horario",
  "Delete": "Eliminar",
  "Cancel": "Cancelar",
  "Save": "Guardar",
  // ... otras 200 entradas
};

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]);
  }
}

// pasada inicial
const walker = document.createTreeWalker(
  document.body, NodeFilter.SHOW_TEXT
);
while (walker.nextNode()) translateNode(walker.currentNode);

// observar nuevos elementos
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 });

Componentes clave del script:

  • TreeWalker para recorrer nodos de texto.
  • MutationObserver con opciones { childList: true, subtree: true }.
  • Coincidencia exacta de claves usando trim() para minimizar colisiones.

Limitaciones de este enfoque

  • Atributos de marcadores de posición: No capturados por TreeWalker—requieren un observador separado para campos input/select.
  • Homónimos contextuales: "Run" en un botón vs. texto necesita traducciones diferentes—el diccionario no puede distinguir contexto.
  • Cadenas nuevas: Los elementos sin traducir permanecen en inglés hasta que se actualice el diccionario tras la liberación de OpenClaw.

Rendimiento: el retraso de traducción es de ~10–50 ms—imperceptible para los usuarios.

Google AdInline article slot

Despliegue

Instalar mediante script:

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

Configura automáticamente el servicio systemd y nginx. Deshacer: sudo bash scripts/uninstall.sh.

Versión Docker:

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

Ventajas clave:

  • No se necesitan cambios en OpenClaw—funciona con cualquier versión actual.
  • MutationObserver garantiza traducción en tiempo real del contenido dinámico de la SPA.
  • Actualizar el diccionario solo requiere añadir entradas al JSON—sin reiniciar el servidor.
  • Los WebSocket se transmiten correctamente para mantener la funcionalidad del chat.
  • Carga mínima: ~150 líneas de código, 5–6 actualizaciones del diccionario en dos meses.

Alternativas y escalabilidad

Enviar una solicitud de fusión al repositorio principal no es viable—requeriría refactorización completa (extraer cadenas a JSON, envolver componentes con librerías de i18n). La solución actual se despliega en minutos.

Para otras SPAs, posibles mejoras:

  • Shadow DOM: Caminante adicional para recorrer raíces de sombra.
  • Diferenciación del Virtual DOM: Conectar con la API de React DevTools (experimental).
  • Service Worker: Interceptar solicitudes fetch a nivel del navegador.

— Editorial Team

Advertisement 728x90

Leer después