Files
Pegas b4552df1a6 docs: EN source-of-truth workflow (en2ru + DOCSTRAT)
- en2ru.py: EN markdown -> Russian via Ollama translategemma-4b (tag: translategemma:latest)
  strips code-fence artifacts, prints jargon-review hints to stderr
- DOCSTRAT.md: rule -- EN canonical source, RU for comprehension only,
  never edit on drifted Russian (dedup/receipt mistranslation guard)
- ROADMAP.ru.md: sample RU generated from EN source
- AGENTS.md: section 8 documenting the EN-first workflow
2026-10-15 23:13:12 +05:00

99 lines
3.7 KiB
Python

#!/usr/bin/env python3
"""en2ru — English-source-of-truth -> Russian comprehension doc.
RULE (see DOCSTRAT.md): English is the CANONICAL source of truth. Russian is
only a comprehension aid; terminology may drift and MUST NEVER be used as the
editing basis. Always edit the `*.md` (EN), then re-run this to refresh
`<file>.ru.md`.
Usage:
python3 en2ru.py path/to/doc.md # writes path/to/doc.ru.md
cat doc.md | python3 en2ru.py - # stdin -> stdout + doc.ru.md
Model: translategemma:latest (Ollama, local @ 127.0.0.1:11434).
Jargon drift hints are printed to STDERR (never inside the RU output).
No external deps (urllib only) so it works offline after the model is pulled.
"""
import sys
import json
import urllib.request
OLLAMA_URL = "http://127.0.0.1:11434/v1/chat/completions"
MODEL = "translategemma:latest"
# Terms TranslateGemma-4b mishandles -> flag for human review (never auto-fix).
# Maps the EN token to the safe RU handling. JAPN is intentional: keep short.
JARGON_HINTS = [
("dedup", "дедупляция / удаление дублей"), # NOT «дублирование» (reversed!)
("receipt", "рецепт (паттерн действий)"), # NOT «результат»
("atom-level", "на уровне атомов"),
("stale", "устаревший (status=stale)"),
("gap", "пробел (gap)"),
("verify", "верификация / verify"),
("rerank", "реранг / reranking"),
("cache", "кэш"),
("inferred", "выведено / inferred"),
("web_search", "web_search (инструмент)"),
("ingest", "ингестия / загрузка / ingest"),
]
def translate(text: str) -> str:
prompt = (
"Translate the following Markdown technical text to Russian.\n"
"Rules:\n"
"- Output ONLY the Russian text. Preserve Markdown structure, code blocks,\n"
" backticks, code identifiers, URLs, command and tool names verbatim.\n"
"- Do NOT translate: code, identifiers, tool names, file extensions, URLs,\n"
" and product names (Memgraph, PostgreSQL, Qdrant, Memgraph).\n"
"- If unsure about a term, keep it as-is in Latin (do not guess).\n"
"- Words you must handle carefully: "
+ ", ".join(en for en, _ in JARGON_HINTS) + ".\n\n"
"TEXT:\n```\n" + text + "\n```"
)
payload = {
"model": MODEL,
"stream": False,
"messages": [{"role": "user", "content": prompt}],
}
req = urllib.request.Request(
OLLAMA_URL,
data=json.dumps(payload).encode("utf-8"),
headers={"Content-Type": "application/json"},
)
with urllib.request.urlopen(req, timeout=300) as resp:
return json.loads(resp.read())["choices"][0]["message"]["content"]
def main() -> None:
if len(sys.argv) < 2 or sys.argv[1] == "-":
src = sys.stdin.read()
base = "doc"
else:
with open(sys.argv[1], encoding="utf-8") as fh:
src = fh.read()
base = sys.argv[1].rsplit(".", 1)[0]
ru = translate(src)
# model echoes the prompt's ``` fences -> drop leading/trailing fence lines
lines = ru.splitlines()
if lines and lines[0].strip() == "```":
lines = lines[1:]
if lines and lines[-1].strip() == "```":
lines = lines[:-1]
ru = "\n".join(lines).strip()
out_path = base + ".ru.md"
with open(out_path, "w", encoding="utf-8") as fh:
fh.write(ru)
low = src.lower()
print("# en2ru -> " + out_path, file=sys.stderr)
for en, hint in JARGON_HINTS:
if en.lower() in low:
print(" review: '%s' -> ensure '%s'" % (en, hint), file=sys.stderr)
print("OK: " + out_path)
if __name__ == "__main__":
main()