Skip to content

Repository files navigation

claude-code-polyglot

Use Claude Code in your native language. Polyglot is a local ANTHROPIC_BASE_URL proxy that sits between Claude Code and the API:

  • You type in your language → the proxy rewrites your messages into English before they reach the model (full replacement — the model never sees the original).
  • Claude replies in English → the proxy translates the response stream back into your language, line by line, as it arrives.

Code is sacred: fenced blocks, inline code, file paths, URLs, CLI flags, env vars, and markdown structure are never sent to the translator. A real GFM parser (mdast) decides what is prose, and every message is reassembled byte-for-byte around everything else. Tool calls, tool results, system prompts, and thinking blocks pass through untouched.

Translation goes through Google Translate, and you pick which endpoint in the config:

Mode Endpoint Key Notes
public (default) translate.googleapis.com/translate_a/single none Nothing to set up. Unofficial and undocumented; Google rate-limits per IP and answers a captcha page when it decides you've had enough.
key Cloud Translation API v2 your Google Cloud API key Official, documented, quota-backed, billed per character.
polyglot api public            # keyless endpoint
polyglot api key AIza...       # Cloud Translation API v2
polyglot api                   # pick interactively

The key is stored in polyglot's config, or kept out of it entirely with POLYGLOT_GOOGLE_API_KEY (the env var wins). Switching modes is live — the proxy re-reads the config on every request.

Install

npm install -g @migiht/claude-code-polyglot

Quick start

# your language (Claude's side defaults to English)
polyglot init ru
# then just run Claude Code through the proxy
polyglot claude

init asks which language you write in, which Google endpoint to use, and proves the choice works with a real translation. It is scriptable too:

polyglot init ru --api key --key AIza...

polyglot claude starts the proxy on an ephemeral port, points ANTHROPIC_BASE_URL at it, and hands your terminal to claude — every argument passes through (polyglot claude --continue, polyglot claude -p "...", …). When claude exits, the proxy exits with it.

Shortcuts

init also offers to install a shell alias for the language you set up, so you never type polyglot claude again:

ruclaude              # = polyglot claude, in Russian
ruclaude --continue   # arguments still pass through

Every language gets one, named after the country you'd expect: ruclaude, chclaude (zh-Hans), twclaude (zh-Hant), jpclaude, uaclaude, krclaude, declaude, frclaude, esclaude, czclaude, inclaude (hi)… — the language code is the fallback.

polyglot alias                 # pick your shortcuts interactively
polyglot alias add ja zh-Hans  # add more (each needs its own `polyglot init`)
polyglot alias remove ja
polyglot alias list

Aliases are written into a single managed block in ~/.bashrc, ~/.zshrc, and ~/.config/fish/config.fish (whichever exist), and removed again by polyglot uninstall. Each one pins its language with POLYGLOT_LANG=<code>, which overrides the configured language for that run only — so ruclaude and jpclaude work side by side in different terminals.

Commands

Command What it does
polyglot init [source] [target] Pick languages and the translation backend (polyglot init ru, target defaults to en)
polyglot claude [...args] Run Claude Code through the translation proxy
polyglot api [public|key] [apiKey] Choose the Google endpoint: keyless or Cloud Translation API v2
polyglot alias [add|remove|list] [langs...] Manage the <lang>claude shell shortcuts (ruclaude, jpclaude, …)
polyglot proxy [--port N] [--upstream URL] Standalone proxy; set ANTHROPIC_BASE_URL yourself
polyglot scopes [on|off] [names...] Choose which markdown blocks get translated
polyglot stats [--days N] [--reset] Translation metrics (calls, chars, latency) from local SQLite
polyglot enable / polyglot disable Toggle translation live (the proxy re-reads config per request)
polyglot status Languages, translation backend, proxy configuration
polyglot translate <text> Run one string through the exact proxy pipeline (--reverse for the reply direction)
polyglot uninstall Delete local data (config, API key, stats), uninstall the npm package

Compatible with other proxies and tools

Polyglot chains: if ANTHROPIC_BASE_URL is already set (a router, a corporate gateway, any other local proxy), polyglot claude uses it as the upstream instead of api.anthropic.com — the stack becomes claude → polyglot → your proxy → API. For manual stacks, polyglot proxy --upstream <url> pins it explicitly.

It also never touches ~/.claude/settings.json, hooks, or the Claude Code binary — tools like rtk (PreToolUse-hook based) keep working unchanged next to it. All requests polyglot doesn't understand (count_tokens, models, future endpoints) and all headers (OAuth/API keys, beta flags) are relayed verbatim.

What gets translated (and what never does)

поправь баг в `resolvePaths` — тесты в src/lib/paths.ts падают

reaches the model as:

fix the bug in `resolvePaths` — tests in src/lib/paths.ts fail

Always protected, in every mode:

  • fenced code blocks (``` / ~~~), including fences split across stream chunks
  • indented code blocks, inline code, HTML
  • URLs, unix paths (src/lib/config.ts:42), bare filenames (package.json)
  • CLI flags (--verbose), env vars ($HOME, ${PATH})
  • all markdown syntax: #, list markers, >, table pipes, **, link targets
  • tool calls and their JSON, tool results, system prompts, thinking blocks

Machine translation is trained on plain sentences, so structured blocks are also skipped by default — only plain paragraph text is translated. Opt structures in per block type with polyglot scopes on tables lists headings blockquotes.

Chunks already written in the target language are detected by script (Cyrillic, CJK, Arabic, … vs Latin) and skipped.

How it works

  1. polyglot claude (or polyglot proxy) starts a local HTTP relay; Claude Code talks to it via ANTHROPIC_BASE_URL.
  2. On each POST /v1/messages, user text blocks are translated into English; assistant history is restored to its byte-exact original English from a local translation memory (SQLite), so the model always sees a consistent English conversation and prompt caching keeps working.
  3. The SSE response stream is translated per completed line, with code-fence state threaded across chunks; partial lines are held until they finish. Non-text events pass through untouched.
  4. Everything is fail-open: a translation error (rate limit, dead network, expired key) forwards the original bytes; a disabled config turns the proxy into a pure relay.

Only prose chunks are sent to Google, batched per message: the keyed API takes up to 64 segments per request, the public endpoint takes one and is called with a small parallelism. Transient failures (429/5xx) are retried twice with backoff.

Languages

50+ languages:

ar az be bg bn bs ca cs da de el es et eu fa fi fr gl gu he hi hr hu id is it ja kn ko lt lv ml ms mt nb nl nn pl pt ro ru sk sl sq sr sv ta te th tr uk vi zh-Hans zh-Hant

Codes Google names differently are mapped on the way out (zh-Hanszh-CN, zh-Hantzh-TW, nb/nnno, heiw).

Usage metrics

Every translated request and reply is recorded into a local SQLite database (nothing leaves your machine):

polyglot stats

Shows calls, translated counts, characters in/out, and average/max latency per direction, plus a daily breakdown (--days N). polyglot stats --reset clears it.

Debugging

POLYGLOT_DEBUG=1 polyglot claude   # proxy errors are appended to the polyglot log dir
polyglot translate "любой текст"
polyglot translate --reverse "any text"

POLYGLOT_HOME=/some/dir sandboxes every path (config, stats, logs) — used by the test suite. POLYGLOT_GOOGLE_API_KEY=... overrides the stored key for one run.

Development

npm install
npm test          # node:test via tsx
npm run typecheck
npm run build     # tsdown → dist/

Requires Node ≥ 22.5 (built-in SQLite).

Limitations

  • Prose leaves your machine. Every translated chunk is sent to Google — code, paths, tool calls, and system prompts are not, but the sentences around them are. If that is unacceptable, keep polyglot disabled (polyglot disable) for those sessions.
  • The public mode is an unofficial endpoint with no SLA: Google throttles it per IP and will eventually answer a captcha page instead of a translation. Polyglot reports that clearly and keeps relaying untranslated; polyglot api key <your-key> is the fix.
  • The key mode is billed per character by Google Cloud — a long session translates a lot of characters. polyglot stats shows how many.
  • Machine translation is fast, not literary. Technical prose comes through fine; idioms may wobble.
  • Claude's replies are stored in your language in the session transcript (the proxy restores the English original for the model on every turn).
  • Latin↔Latin pairs (e.g. de↔en) can't be script-detected, so already-English prompts are still run through the translator.
  • Thinking blocks stay in English (they are cryptographically signed and must not be modified).

License

MIT

About

Use Claude Code in your native language: a local proxy that translates your prompts for the model and its replies for you, fully offline (Bergamot/Marian WASM + Firefox Translations models). Code, paths, and tool calls are never touched.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages