Retour à l'accueil

Traduction OpenClaw en Russe Sans Code Source

L'article décrit l'implémentation de la localisation à l'exécution d'OpenClaw en russe via un serveur proxy transparent. Le service Node.js injecte un script de traduction dans le HTML, MutationObserver gère le DOM dynamique de l'application SPA. La solution ne nécessite pas de forker les sources et fonctionne avec les mises à jour quotidiennes.

Interface OpenClaw en Russe : Proxy Sans Fork
Advertisement 728x90

Localiser OpenClaw en russe via un proxy sans modifier le code

Les développeurs d'OpenClaw n’ont pas intégré d’infrastructure i18n : les chaînes sont codées en dur dans les composants JSX, sans react-intl ni i18next. Forker avec des traductions manuelles n’est pas viable à cause des mises à jour quotidiennes. La solution ? Un proxy transparent entre nginx et l’application, qui injecte un script de traduction dans les réponses HTML.

Flux du trafic :

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

Le proxy modifie uniquement le HTML, préservant la logique de l’application. Les connexions WebSocket pour le chat passent sans modification.

Google AdInline article slot

Un serveur Node.js (~150 lignes) utilise http-proxy pour intercepter les réponses. Astuce clé : surcharger res.write et res.end pour buffer le corps de la réponse.

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

  // intercepter la réponse
  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>`
      );
    }
    // supprimer content-length, sinon le navigateur tronque la réponse
    // à cause de la longueur modifiée
    res.removeHeader("content-length");
    _write(body);
    _end();
  };

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

La gestion des WebSocket nécessite d’écouter l’événement upgrade :

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

Script côté client avec MutationObserver

OpenClaw est une SPA — le DOM se reconstruit dynamiquement lors des navigations. Une simple traversée des nœuds texte au chargement ne fonctionne pas. MutationObserver suit les nouveaux nœuds et les traduit instantanément.

Google AdInline article slot

Dictionnaire de traduction en JSON (~200 lignes) :

const dict = {
  "Settings": "Paramètres",
  "New conversation": "Nouvelle conversation",
  "Send a message": "Envoyer un message",
  "Skills": "Compétences",
  "Memory": "Mémoire",
  "Schedule": "Planning",
  "Delete": "Supprimer",
  "Cancel": "Annuler",
  "Save": "Enregistrer",
  // ... autres 200 entrées
};

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

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

// surveiller les nouveaux éléments
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 });

Composants clés du script :

  • TreeWalker pour parcourir les nœuds texte.
  • MutationObserver avec options { childList: true, subtree: true }.
  • Correspondance exacte des clés grâce à trim() pour minimiser les collisions.

Limites de cette approche

  • Attributs de placeholder : non capturés par TreeWalker — nécessitent un observateur d’attribut séparé pour les champs input/select.
  • Homonymes contextuels : « Run » dans un bouton vs. du texte exige des traductions différentes — le dictionnaire ne peut pas distinguer le contexte.
  • Nouvelles chaînes : les éléments non traduits restent en anglais jusqu’à la mise à jour du dictionnaire après une nouvelle version d’OpenClaw.

Performance : délai de traduction ~10–50 ms — imperceptible pour les utilisateurs.

Google AdInline article slot

Déploiement

Installation via script :

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

Configure automatiquement le service systemd et nginx. Annulation : sudo bash scripts/uninstall.sh.

Version Docker :

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

Avantages principaux :

  • Aucun changement nécessaire dans OpenClaw — fonctionne avec toute version actuelle.
  • MutationObserver garantit une traduction en temps réel du contenu dynamique de la SPA.
  • Mise à jour du dictionnaire requiert seulement l’ajout d’entrées JSON — pas de redémarrage serveur.
  • Les WebSocket sont correctement transférés pour préserver la fonctionnalité du chat.
  • Faible surcharge : ~150 lignes de code, 5–6 mises à jour du dictionnaire en deux mois.

Alternatives et évolutivité

Soumettre une PR au dépôt principal n’est pas réaliste — cela demanderait une refonte complète (extraire les chaînes vers JSON, envelopper les composants avec des bibliothèques i18n). La solution actuelle se déploie en quelques minutes.

Pour d’autres SPAs, améliorations possibles :

  • Shadow DOM : ajout d’un walker supplémentaire pour explorer les racines shadow.
  • Diffusion du Virtual DOM : accrochage à l’API React DevTools (expérimental).
  • Service Worker : interception des requêtes fetch au niveau du navigateur.

— Editorial Team

Advertisement 728x90

Lire ensuite