@orbit-labs-ai/translator

Isolated Pi and Claude Code translation hooks with history reuse, independent controls, and display-only Chinese output

Packages

Package details

extension

Install @orbit-labs-ai/translator from npm and Pi will load the resources declared by the package manifest.

$ pi install npm:@orbit-labs-ai/translator
Package
@orbit-labs-ai/translator
Version
0.6.10
Published
Oct 12, 2026
Downloads
1,116/mo · 1,116/wk
Author
fhxgs
License
MIT
Types
extension
Size
616.5 KB
Dependencies
0 dependencies · 0 peers
Pi manifest JSON
{
  "extensions": [
    "./pi/index.ts"
  ]
}

Security note

Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.

README

Translator

English | 简体中文

License: MIT npm Node.js Integrations

Write prompts in Chinese, send English to the model, and read the answers in Chinese if you like.

Translator is a translation layer for the Pi coding agent and Claude Code. When you submit a prompt that contains Chinese prose, it translates the prompt into English in a separate, isolated request and submits the English instead. With answer translation turned on, it also translates each final answer into Chinese and shows the translation after the original. The model receives the translated prompt; original prompts and Chinese answer translations remain display-only records.

It is part of Orbit Tools.

Contents

Features

For example, when you type the first line below (the Chinese asks the agent to check a null-pointer problem in src/app.ts without touching the tests), the model receives something like the second line:

You type:          请检查 `src/app.ts` 中的空指针问题,不要修改测试。
The model gets:    Please check the null-pointer issue in `src/app.ts`; do not modify the tests.

Translation

  • Chinese in, English out (Input). A prompt with Chinese prose is translated by a separate request that sees only that prompt, and the translation is sent instead. Preflight sets aside certain literals and names; if no Chinese prose remains, including literal-only input, the prompt passes through without a translation call or record. Quoted Chinese prose remains translatable.
  • Answers in Chinese (Output). Optionally, each final answer is also translated into Chinese for you to read. Preflight skips answers with no English prose to translate, including literal-only answers; plain OK still translates. The model never sees that translation.
  • A language instruction (Instruction). Optionally, translated prompts ask the model to reason in English and answer in Simplified Chinese.
  • Faithful, minimal edits. The translator fixes only obvious typos and grammar slips; it does not rephrase, answer, summarize or expand.
  • Reuse. Submitting an identical prompt again reuses its earlier translation instead of calling a model.

Precision

  • Shared preflight masking. In both directions, one scanner identifies certain code, math, URLs, emails, filenames (including bare Chinese filenames), paths, literal-like identifiers/settings/mentions and Markdown link targets before translation. These spans are masked and restored byte for byte; ambiguous prose stays visible to the translator.
  • Structural safety, not a language audit. A response must be completed and nonempty, and every placeholder must return exactly once with protected input blocks in order. Each fresh input translation or output part needing translation gets one completion, with no AI audit or repair call. Translation results are not checked again for language or semantic quality.
  • Small requests. Short placeholders, and code that never reaches the translator, keep translation requests cheap.

Safety

  • Fails safe. A prompt that cannot be translated is not sent: neither the original nor a partial translation. A failed answer translation leaves the answer as it is.
  • Separate contexts. Your original prompts and answer translations are display-only copies. The model's context holds the submitted prompt and original answer, not those display records.
  • Cancel at any time. Pi: Ctrl+Alt+C. Claude Code: key 4 while a translation runs. Cancelling sends nothing and changes no setting.

Settings

  • One command. /translator opens a native settings panel with Input, Instruction, Output, Model and Thinking; subcommands such as /translator output on set each one directly.
  • Translator-only model and thinking. Translations follow your session's model by default, at their own thinking level (medium), and you can pin another model. Neither setting touches the main conversation.
  • Per session, with defaults. Settings apply to the current Pi branch or Claude Code session. The translator command sets the defaults for new sessions.
  • One set of names. Both agents use the same names: Input, Instruction and Output.

Display and records

  • Both texts on screen, each once. Your prompt shows the English that was sent and the answer stays in English; Translator adds only the Chinese: your original with each translated prompt and the translation with each answer. Pi shows them in cards rendered as Markdown. Claude Code draws them under the rows, in the terminal and in its desktop app, including prompts you send while it is working, and draws them again when you resume the session.
  • Quiet status. Claude Code shows a short status at the end of the hint line under the prompt; Pi shows a toggle bar under the editor.
  • Local records. Originals, translations and the text actually sent are kept on your machine in plain text, including diagnostics for failed input. Memory-only modes keep nothing on disk.

Hosts

  • Pi and Claude Code. Both share one translation core, literal scanner, input cache and set of names, and each uses its own model. Claude Code does not need Pi.
  • Updates while agents run. Claude Code reloads Translator by itself when its files change and keeps translating, even when the reload lands in the middle of a turn. A running Pi keeps the version it loaded until /reload.
  • Other hosts. A standalone command-line tool translates text before you hand it to any agent.

See the changelog for what changed in each version.

Requirements

  • Node.js 22.19 or newer, available as node on your PATH. The tool has no npm dependencies.
  • At least one host: Pi 1.0.4 or newer, or Claude Code 2.1.292 or newer; the installer refuses older versions. Version 0.6.10 was tested on Pi 1.1.0 and Claude Code 2.1.296. Earlier desktop-surface records also cover Claude Code 2.1.293 and 2.1.294; those older builds were not retested for this patch. See the verification record. The Claude Code integration uses the Mods API.
  • macOS or Linux. It has been exercised on macOS; Windows is not supported, and the installer refuses to run there. No sudo is needed.
  • A working model login in your agent. Translations run on your agent's own model, so they use your usual account; the installer itself never calls a model or touches credentials.

Quick start

  1. Install the package from npm and run its installer:

    npm install -g @orbit-labs-ai/translator
    translator install

    Or clone the repository and run the installer from the clone:

    git clone https://github.com/Orbit-Labs-AI/orbit-tools.git
    cd orbit-tools/Hooks/translator
    ./install.sh

    The installer finds Pi and Claude Code on your machine. If only one is installed, it sets that one up; if both are, it asks which to use. translator takes the same options as ./install.sh, which you can also run by its full path from any directory; Installation in detail explains them. Keep the installed package or the cloned directory: the agents load the tool from it.

  2. Restart your agent (in Pi, /reload also works), then run /translator status to confirm that the tool loaded. In either agent's terminal UI, /translator without arguments opens its native settings panel.

  3. Type a prompt in Chinese. Input translation is on by default.

  4. Optionally, turn on the other two features for the current session:

    /translator output on         # show a Chinese translation of each final answer
    /translator instruction on    # ask the model to reason in English and answer in Chinese

To change the defaults for new sessions, use translator. It is installed in ~/.local/bin, which must be on your PATH; the installer does not edit your shell startup files.

Usage

Commands and shortcuts

/translator is the tool's only slash command. With no arguments, it opens a native settings panel with five options: Input, Instruction, Output, Model and Thinking. Changing settings makes no model call and never changes the main conversation's model or thinking level.

Action Command (Pi and Claude Code) Pi shortcut Claude Code button key
Open settings /translator — —
Show all settings / help /translator status / /translator help — —
Input: translate prompts to English /translator input [on|off|toggle|status] Ctrl+Alt+T 1
Instruction: append "reason in English, answer in Chinese" /translator instruction [on|off|toggle|status] Ctrl+Alt+L 2
Output: show answers in Chinese /translator output [on|off|toggle|status] Ctrl+Alt+Y 3
Model /translator model [follow|MODEL] — —
Thinking /translator thinking [low|medium|high|xhigh|max] — —
  • Both agents use the same names, defined once in shared/features.mjs: the toggles read Input: ON, Instruction: OFF and Output: OFF, full status lines read Translator: Input ON · Instruction OFF · Output OFF, and notices start with Translator · Input or Translator · Output. Claude Code's prompt hint shows the short form, such as Translator · Input + Output.
  • input, instruction and output without a value toggle that switch; their status action is read-only. model and thinking without a value only report settings. Invalid arguments leave settings unchanged.
  • For Pi, MODEL is a registry provider/model ID; the compact panel includes ON/OFF badges, inline model/thinking feedback and a searchable model picker. Press ? in the settings list for contextual help; press it again to return to the controls-only view. For native Claude Code, use an alias such as sonnet or a full model ID; the panel offers common aliases and an ID input. With Claude's optional Pi backend, use provider/model instead. Availability and allowlists for Claude's backend are checked on completion, not by a test model call when saving settings.
  • model follow explicitly overrides any model pin in the config file. It follows Pi's current session model or native Claude's current session model; for Claude's Pi backend it follows Pi's configured default, not the Claude model. Thinking remains independent, defaulting to medium; follow does not adopt the main conversation's thinking level. Pi shows both requested and effective thinking, including clamping or no reasoning support.
  • All five settings apply to the current scope: Pi saves overrides per conversation branch, Claude per session ID. They override file defaults without editing the shared config. New sessions use file defaults; persisted settings restore on resume, and Pi tree navigation restores the active branch's values. No-persistence modes keep them in memory. Changes affect subsequent translations, not the settings snapshot of an in-flight request.
  • The existing toggle rows stay synchronized with the panel, commands and shortcuts. In Pi fullscreen mode, click Input, Instruction or Output below the editor. The bar wraps without changing your draft or focus; regular mode keeps keyboard controls. RPC/JSON/print create no terminal widget or panel; the no-argument command reports status/help instead. Global defaults refresh at input/agent boundaries or reload; TRANSLATOR_DISABLE=1 visibly locks input off.
  • In Claude Code, keys 1/2/3 belong to the button row above the prompt. Press a digit while the composer is empty and pause briefly, or press Ctrl+X, then Tab to focus the row first; the mouse works too. No global Ctrl shortcut or existing binding is overridden. If the row or settings pane is unavailable, use the direct subcommands. Claude serializes overlapping panel, button and command updates per session, including memory-only mode.

Cancel pending translations

Cancellation uses a shortcut/button, not a slash command or a cancel subcommand. It does not turn any setting off: cancelled input submits neither the original nor the translation, and cancelled output leaves the completed original answer intact. Subsequent translations remain enabled according to your settings.

  • Pi: Ctrl+Alt+C cancels pending input and output translations, including when both are active or the settings panel has focus. Host operation signals are used when available, but ordinary SDK/RPC abort() during idle input preprocessing is not a reliable cancellation path. Tree navigation, reload and session replacement invalidate pending results. Bare SDK dispose() blocks late writes but may leave a request running until completion or timeout.
  • Claude Code: a Cancel translation button with key 4 appears only while a translation is pending, in the above-prompt row and the open settings pane. Use 4 with an empty composer, or Ctrl+X, then Tab, then 4 to focus and activate the row; you can also click the button. Cancellation reaches native completions and the worker, including its optional Pi subprocess. Once the completed result begins publication, the button disappears: cancellation does not undo completed translations or irreversible host record/display writes. The mod also observes host dispatch signals and turn.abort without taking over key bindings. Real-host checks on 2.1.294 verify native turn.abort for pending output in both backends; they do not establish physical Escape-key behavior. See the Claude adapter for the evidence boundary.

What you see

  • Your prompt. The English prompt and your Chinese original, labelled Translator · Input · original · display only, appear together. Pi appends the original's card only after the translated user row is delivered, not when translation finishes or enters a queue. Claude Code keeps its existing synchronized original under the English row, plus its display-only transcript notice (Ctrl+O).
  • Prompts sent while the agent works. The original stays hidden while translated input waits in a steer/follow-up queue, so intervening tools cannot bury an early card. In Claude Code this covers Ctrl+X Ctrl+S (or Ctrl+Enter) into the running turn and Enter for the next turn; in Pi, both steering and follow-up. Journals and enabled input caches are still written before submission, not deferred with the display.
  • Answer translations. The Chinese appears right under the English answer, labelled Translator · Output · display only: drawn under the answer row in Claude Code, in the terminal and in its desktop app, and in a card after the answer in Pi. In both, the model never reads it.
  • Markdown in Pi's cards. Links, paths, inline code, code blocks and lists in a card are drawn with Pi's own Markdown renderer and theme. Press Ctrl+O to expand cards with the translator model and trace.
  • After a restart. Claude Code draws the originals and Chinese answers again when you resume a session, from links kept in its local journal. Pi keeps both in its own session file.
  • Failures. A failed input translation blocks the prompt and immediately shows why and the original; it does not wait for a user row that will never arrive. A display failure after successful delivery cannot undo submission; Pi reports Prompt sent; original display unavailable instead. A failed answer translation shows a short warning and leaves the answer as it is.

Recalling original prompts

In Claude Code's terminal, Up/Down recall the original Chinese for known translated prompts, while the sent transcript row stays English. Translator restores a matching history draft through the public composer API; it does not rewrite Claude's history file or take over the arrow keys. Existing records can be recovered from recent shared input history and the resumed session journal. Ordinary typing/pasting and unrelated or ambiguous history entries are left alone.

Sending that restored original unchanged replays its saved translation, with the current language-instruction setting, without a new translation call. This explicit replay also works with automatic caching off and with an older cache contract, provided the saved result still preserves the source's literals. Editing the recalled source returns to the normal translation path. Turning Input off still sends the source as typed. For a fresh model comparison, turn automatic caching off and type/paste the source instead of selecting its saved replay.

Claude 2.1.296 provides no history-navigation event, so restoration uses a 150 ms local composer check only while the terminal holds a draft and known originals. It neither calls a model nor runs in headless/desktop mode. Only recent, unambiguous matches can be restored; missing original records cannot be reconstructed. Memory-only mode keeps recall pairs in session state across a plugin reload, not across process exit.

Default settings for new sessions

The installer adds a translator command that changes the defaults in config.json:

translator                           # show the current defaults
translator input on
translator instruction on
translator output off
translator cache off
# translator <input|instruction|output|cache> [on|off|toggle|status]

It changes only the field you name. Concurrent invocations serialize read/modify/write on the canonical config path and preserve symlinks, permissions and unknown fields. Branch/session settings take precedence. It does not translate text or change other agent settings; external writers must also respect the lock to prevent lost updates.

How translation works

Your prompt
  |-- input off --> sent unchanged
  `-- shared preflight scanner: identify certain literals and inspect remaining prose
       |-- no Chinese prose / literal-only --> unchanged, no translation call or record
       `-- Chinese prose
            |-- exact source + current scanner/prompt contract cached --> literal-integrity reuse
            `-- mask identified literals --> one isolated completion
                   --> completed/nonempty response + placeholder count/block-order safety
                         |-- failure --> prompt blocked (no repair call)
                         `-- restore literals --> record + optional language instruction
                                                   |
                                     Translation sent to the model; your original
                                     is shown beside it, display only

The model's final answer ----------------------------------> left unchanged
  |-- output off --> no translation
  `-- shared preflight scanner
       |-- no English prose / literal-only --> no translation call or record
       `-- English prose --> mask literals --> one completion per part + response/literal safety
                                                --> restored translation shown/stored only

A failed input translation blocks the prompt: the tool never falls back to sending the Chinese original. To send the original on purpose, run /translator input off first. A failed answer translation keeps the original answer: nothing is rewritten and the model is not asked again.

Local language detection

Language decisions happen only before translation, locally and before a translation call or record. Both directions use the same preflight scanner to identify certain literal spans and inspect the remaining prose. If translation is needed, those same spans are masked before the request; filenames and literal-like names are not merely ignored by detection and then exposed to the model.

  • Input is sent as typed when no Chinese prose remains. Read 报告.md costs no translation call, and 报告.md alone now skips too. Quoted Chinese prose is still translatable: Change the title to “你好” takes the translation path, and the model may keep an exact quoted value. Quotes alone do not make arbitrary prose a protected literal.
  • Output is skipped when no English prose needs translating. Acronyms, camelCase names, flags, versions, dotfiles, mentions and ASCII links or paths do not make an otherwise Chinese answer English, so 已合并 PR #3,见 docs/config.md stays as it is, while 已完成 API checks is translated. The exact, case-sensitive technical names and labels Markdown, Mermaid, Downloads, Token and Tokens are also set aside in Chinese answers: 已保存 Markdown 文档和 Mermaid 图源 and 已保存到 iCloud Downloads;Token 用量未增加 make no translation request. This small output-classification list does not mask those plain words or exempt arbitrary capitalized words, and does not use the proportion of Chinese characters. Other plain English words, including unrecognized names such as Orbit, still count as prose. Adding Please review the diagram still triggers translation. A plain answer such as OK or Mermaid still translates; it is not a scanner-recognized literal-only answer.
  • Literal-only text skips in both directions, even without any prose in the other language. Examples include 报告.md, ./文档/报告.md, 用户表_v2, LABEL=中文 and @张三. Skipping makes no translation call or translation log and adds no language instruction.

Ambiguous prose remains visible to the model rather than being treated as one protected name: Chinese glued straight to Latin letters (请查看data.csv, …/docs然后) or tied to a name by : or 、 (问题:src/app.ts, 配置、README.md), a link or path that ends in Chinese without a file extension (https://example.com/文档里的第三节, D:\备份里面), a mention that runs on into a sentence (@李四麻烦今天合并一下), an address with a Chinese local part, and a join by slashes or # alone (English/中文, src/组件/按钮, 问题#3). A doubtful case costs a translation rather than a missed one. Exact strings outside the recognized syntax can be wrapped in backticks for protection.

请检查 报告.md is translated with the filename masked and then restored. Neither the draft nor the restored text is passed through a language gate afterward; literal content cannot trigger a language rejection.

Which model translates

Without a configured or session pin, translations use the model currently selected in your session, read again for every request, so switching the main model also switches the translator. They run at the translator's thinkingLevel (default medium) rather than your session's own level: a session at max still translates at requested medium unless you change the translation setting.

  • Pi clamps the requested level to what the translation model supports; a model without reasoning gets no reasoning option. The panel shows the selected/resolved model inline and requested → effective thinking only when they differ; select Thinking and press ? for the reason. /translator status retains the full details. Pin with /translator model provider/model, or set provider and model as file defaults. An unavailable pin does not silently fall back to the main model.
  • Claude Code uses its own isolated completion API by default. Pin with /translator model sonnet (or a full ID), or set the claudeModel file default. Requested thinking is sent as effort; the host determines model support and omits it for models that take none. Alternatively, claudeBackend: "pi" uses an installed Pi and accepts a provider/model pin.
  • /translator thinking high changes thinking for both translation directions only. /translator model follow clears the effective translation pin for the current branch/session even if the file has a pin; it leaves thinking unchanged. Neither command changes the main model, its thinking level or the shared config file.

Small fixes, no rewriting

The input translator may make minimal corrections to obvious spelling, grammar or typing mistakes in prose. It completes a fragment only when the missing words are unambiguous from the original; otherwise the fragment or ambiguity stays as it is.

It must not rewrite, polish, expand or guess. Meaning, tone, detail, negation, constraints, lists and Markdown are kept. These rules apply only to translated input: English prompts still pass through untouched, and answer translation is unaffected. They are instructions to the model, not a guarantee about any particular model's quality.

What is protected locally

The shared preflight scanner masks only syntax and names identified as certain literals, for both input and output. Its boundaries are conservative so that prose is not hidden inside a guessed name. Everything uncertain goes to the translator, which keeps it or translates it; this is not a complete parser for arbitrary source text.

Masked locally Examples and boundaries
Code Backtick and tilde fences (also blockquoted and unclosed ones), inline code with an exact backtick run, <pre>, <code>, <script> and <style> blocks. Chinese inside code stays as written
Math $…$ (one line, not $5 to $10), $$…$$, \(…\), \[…\]
URLs and emails ASCII URLs (https://…, www.…, mailto:) and ASCII email addresses. 请阅读https://…,然后 masks the URL alone; a URL that runs straight into Chinese, as in 看看https://a.com/docs然后, is left whole to the translator
Filenames and paths Certain bare filenames and relative paths (README.md, 报告.md, docs/报告.md), explicit paths (/, ~/, ./, ../, C:\, \\server, @src/…), and quoted paths or filenames with spaces or Chinese. Ambiguous trailing Chinese remains prose; a file extension or closing path separator provides a clear boundary. A / right after Chinese starts an ASCII path only when another / follows, so 项目/Orbit stays prose
Literal-like identifiers, settings and mentions Certain names such as 用户表_v2, LABEL=中文 and short mentions such as @张三; not arbitrary mixed prose, slash-separated phrases or a mention followed by a whole sentence
Markdown links Link and image destinations, reference definitions and identifiers, footnote identifiers and wikilinks; link labels and titles are prose

Not protected as a whole: ambiguous names and paths, JSON, ordinary HTML tags and attributes, arbitrary command-line values, quoted prose, indented text and formulas without delimiters. Recognized literals inside them may still be masked. The translator is instructed to keep exact values and translate prose, but those instructions are not a byte-integrity guarantee for unmasked text.

After the single completion, only response and restoration safety is checked locally:

  • Completed, nonempty response. Incomplete, empty or failed responses are rejected. Native Claude also requires its unique terminal marker before accepting a response.
  • Every placeholder comes back exactly once. Placeholders are short (__PT_ and 8 random hex digits, never present in your text) to keep token use low. Input word order may move an inline literal; protected fenced code blocks keep their relative order. A display-only answer translation may move any literal. Exact source bytes are restored only after these checks.
  • No AI audit or repair call. Each fresh input translation or output part needing translation gets one completion. A response or placeholder failure blocks input or leaves the original answer unchanged, without a second model call.

There is no post-translation language or semantic check: input is not rejected for remaining Chinese, and output is not required to contain Han characters. This also applies to restored literals and joined long-answer parts. Literal content cannot cause language rejection. A structurally accepted result may still be untranslated, inaccurate or otherwise poor; Translator makes no semantic quality guarantee.

For example:

请阅读https://claude.dev/blog/getting-started-with-claude-code-mods/,然后告诉我mods的工作原理

The URL is masked; the translator sees 请阅读__PT_…_0__,然后告诉我mods的工作原理 and returns Please read __PT_…_0__, then tell me how mods work. Images go to the main model unchanged and are never sent for translation; there is no OCR or translation of attachments.

Pi and the shared worker use shared/literals.mjs. Claude's sandbox uses a generated, byte-identical mirror in claude-code/hooks/literals.js; it cannot import outside the plugin. Run npm run sync:shared after scanner changes, and npm run check:shared to check without writing. Do not maintain two algorithms.

Long answers

A translation request takes longer the more it has to write, and timeoutMs bounds each request. In Pi, and with Claude Code's Pi backend, an answer longer than 2,000 characters once literals are masked is therefore translated in parts of about equal length, up to four at a time: a 7,000-character answer takes about as long as a 2,000-character one. Pi ends the turn only after its turn_end handlers, this translation included, so this also keeps that wait short.

  • Parts are cut between paragraphs, or between lines inside a block too long for one part, never inside masked code. A single line longer than 2,000 characters stays whole. The Chinese is joined back in order, with the original spacing and indentation.
  • Each part needing translation gets exactly one completion with its own deadline, a completed/nonempty response requirement and exact placeholder restoration checks. Preflight keeps parts with no English prose, including literal-only parts, without a request. Neither a part nor the joined answer is rechecked for language, and there is no audit or repair call.
  • If a part fails, the others stop, a warning names the part, for example Translation timed out (part 2 of 4), and the answer stays as it is. Cancelling stops every part.
  • Each part is translated without the others, so a term may be worded differently in two parts.
  • Claude Code's native backend translates an answer in one request, so a very long answer can still time out there; raise timeoutMs or use the Pi backend.

Translation reuse

  • A cached translation is reused only for exactly the same original text under the same scanner/prompt contract. Whitespace, line breaks and punctuation count; there is no fuzzy matching.
  • Only results accepted by response and literal-restoration checks are cached, never incomplete, cancelled or failed ones. The 0.6.10 contract bump isolates older versioned entries without deleting their history. A cache entry is not a semantic quality endorsement.
  • Pi and Claude Code share the cache stored next to the configuration file. Changing the main model, translator model, translator thinking or backend does not clear or bypass it: the same valid cached input can still be reused. To compare models with fresh translations, turn the cache off.
  • The language instruction is added after a cache hit, never twice. Reused records are marked reused and do not count usage again.
  • Pi does not change editor contents or prompt history, and reuses records from the active branch only, not abandoned branches. Claude's terminal restores known history drafts to their originals as described in Recalling original prompts, without rewriting its history file.
  • Older, unversioned Pi records can still be imported or considered from the active branch. Every nonempty candidate must retain the exact bytes and occurrence counts of source literals identified by the scanner; this is source-literal integrity only, not an order, language or semantic recheck of the cached text. A literal-changing candidate triggers a fresh translation, whose accepted result can replace it without erasing old journal records. To force a fresh translation, turn the cache off.

Configuration

Settings live in config.json in the tool directory, or in ~/.agents/translator/ for an npm install. The installer creates it from config.example.json, and Git ignores it. Without the file, these built-in defaults apply:

{
  "enabled": true,
  "cacheEnabled": true,
  "appendLanguageInstruction": false,
  "outputEnabled": false,
  "provider": null,
  "model": null,
  "claudeBackend": "native",
  "claudeModel": null,
  "thinkingLevel": "medium",
  "timeoutMs": 30000
}
Field Type and default Effect
enabled boolean, true Input translation. When off, the original is sent unchanged, without the language instruction
cacheEnabled boolean, true Automatic input-translation reuse. Off does not delete history or prevent explicit replay of an unchanged recalled Claude prompt
appendLanguageInstruction boolean, false Add the language instruction to translated or reused input
outputEnabled boolean, false Translate final answers into Chinese for display
provider, model non-empty string or null, both null Model override for the Pi backend; set both, or leave both null
claudeBackend "native" or "pi", "native" Claude Code translates with its own completion API, or with an installed Pi
claudeModel non-empty string or null, null Claude Code translation model. null follows the session's current model, including /model switches; an alias or full ID pins that model, subject to Claude's model allowlist
thinkingLevel "low", "medium", "high", "xhigh" or "max", "medium" Thinking level for translations, independent of the session's own: effort in Claude Code, reasoning level in Pi
timeoutMs integer from 1 to 120000, 30000 Deadline for each translation request, including start-up time for the Pi subprocess backend. Each part of a long answer is a request of its own

Settings are applied in this order, later ones winning: built-in defaults, the configuration file, branch/session overrides for the three switches plus translator model and thinking, and finally TRANSLATOR_DISABLE=1, which turns input translation off. Unset session fields retain file defaults; explicit model follow clears the file's model pin for that scope. The file is read for every request, so edits take effect without a restart. Code updates reach Claude Code by themselves and Pi at /reload (see Moving or updating your clone); registration changes need a restart.

The Pi provider and model override changes only the model used for Pi translations. The standalone command-line tool and Claude Code's Pi backend have no Pi session model, so they use Pi's global default model or the file override; Claude's Pi backend can also use a session provider/model pin. They pass thinkingLevel as --thinking. Claude Code's native backend ignores Pi model defaults, uses its effective claudeModel (including a session pin or explicit follow), and never reads Pi's configuration or credentials. With the Pi backend, Pi manages its own login. The installer does not check model logins, so confirm that your agent itself works after installing.

Claude Code's native translations run without tools or history, at thinkingLevel effort, with a 32,000-token output limit that thinking shares. Because the API does not report why a reply stopped, each request asks for a unique end marker, and a reply without it is rejected, so a truncated translation never reaches the model. The marker is removed before anything is shown or cached. A very long prompt may fail to translate; split it or use the Pi backend. A partial translation is never submitted.

The language instruction

The instruction is fixed:

Use English for intermediate reasoning. Write your final response in Simplified Chinese.

It is added only to translated or reused input, never to prompts skipped by preflight (no Chinese prose, including literal-only text) or input with translation turned off. In the records, translated holds the translation alone and submitted holds what was actually sent, including the instruction. The instruction does not change the thinking level or ask the model to reveal hidden reasoning.

Environment variables and plugin options

Variable or option Purpose
TRANSLATOR_CONFIG Absolute path to the configuration file. It also moves the input cache and the Claude Code session records, which are kept next to it
TRANSLATOR_DISABLE=1 Turn input translation off regardless of session switches; answer translation is unaffected
TRANSLATOR_PI_BYPASS=1 Pi only, 0.6.8+: skip the entire Pi extension for this process, before configuration or hook registration; intended for gateway/automated runs
TRANSLATOR_NO_PERSIST=1 Claude Code: write none of this tool's history, session records or settings store, and keep all five session settings in memory. Use it together with Claude Code's --no-session-persistence
TRANSLATOR_PI Path to the Pi executable, used only by the command-line tool and Claude Code's Pi backend
PI_CODING_AGENT_DIR Pi's configuration directory; the installer honors it
CLAUDE_CONFIG_DIR Claude Code's configuration directory; the installer honors it
HOME Base for the installed links, the translator command, the default agent directories and an npm install's data directory
Claude Code plugin option bridgePath Empty by default, which means the worker in the plugin's own directory, claude-code/bridge.mjs, so each copy of the mod runs its own worker. Set an absolute path to the worker for a custom installation

To set bridgePath (the plugin ID depends on how the plugin was installed):

printf '%s\n' '{"bridgePath":"/absolute/path/to/translator/claude-code/bridge.mjs"}' |
  claude plugin configure translator@skills-dir --values-stdin

Automated Pi runs

A gateway or another unattended launcher can set TRANSLATOR_PI_BYPASS=1 only in its Pi child's environment. Translator then registers no input/output hooks, controls or renderers: prompts and answers pass through without translation, a language suffix, translation model calls or new translation records. Invalid translation config, unavailable translation models and saved branch switches cannot block or delay that run. Other Pi extensions, tools, authentication and project trust remain unchanged.

TRANSLATOR_PI_BYPASS=1 pi --mode json "Run the saved task"

This is an explicit, process-wide opt-out, not a fallback after translation fails. Do not export it globally if you want translation in normal Pi chat. Without the exact value 1, existing behavior remains, including fail-closed input in JSON/print/RPC mode; prompt wording never opts out. Claude Code and the standalone translation CLI are unaffected. Unlike this bypass, TRANSLATOR_DISABLE=1 only disables input translation. A launcher must set the new variable and use a Translator version that supports it; updating only one side is insufficient. No shared settings file or existing session record is changed.

Data and privacy

Isolated does not mean offline. Each translation request is sent to your configured model provider, and it contains the text being translated, with code and other protected literals masked. It never includes your conversation history, tool results, hidden reasoning or images. Your original answer always stays in the main conversation; its Chinese translation is only shown and stored.

The tool keeps plain-text records on your machine:

Location Contents
history/input.jsonl, next to the configuration file Successful translations (original, English, rule version and source), shared by Pi and Claude Code
claude-sessions/<session-id>.jsonl, next to the configuration file Claude Code write-ahead records for input and answers: translations, submitted text, the translating model and thinking level, usage, request IDs, and which transcript row shows each translation
Pi's own session file Entries the model does not read: bilingual records and branch settings (switches, translator model and thinking)
prompt-translations/ and output-translations/, next to Pi's session files This tool's own write-ahead journals and results; Pi's session file is never written to directly
Claude Code's own transcript Bilingual notices, which the model never reads (they are not system prompts)
Claude Code's private plugin store The three switches, translator model and thinking overrides, kept per session ID

New log directories are created with mode 0700 and files with 0600, and every append is flushed to disk. The default locations are ignored by Git; if you point the configuration or logs into another repository, add your own ignore rules.

To keep nothing on disk:

  • Pi: --no-session keeps records and the cache in memory only.
  • Claude Code: use --no-session-persistence and TRANSLATOR_NO_PERSIST=1; a mod cannot see the former command-line flag. Claude Code's translation requests travel between the plugin and its worker only in memory and through standard input, never as extra files.

A started record with no result means the translation was interrupted. translated marks a result accepted by response and literal-restoration checks, not proof of its language or semantic quality or of the main model's whole turn succeeding. Deleting a session in your agent does not delete these records or the cache, and neither upgrading nor uninstalling removes them; delete them yourself using the table above.

Keeping translations out of the model's context does not sandbox the files: if you ask an agent to read the local logs, their contents enter that request like any other file.

To import records from older Pi sessions:

HOOK="$HOME/.agents/hooks/translator"
node "$HOOK/import-history.mjs"                    # defaults to ~/.pi/agent/sessions
node "$HOOK/import-history.mjs" "/path/to/sessions"

Importing is idempotent and never modifies the original sessions. Deleting the shared history/input.jsonl does not remove older records from Pi's active branch; to force fresh translations, run translator cache off.

Installation in detail

Which agents are set up

Agents found What the installer does
Pi only Sets up Pi, without touching any Claude Code directory
Claude Code only Sets up Claude Code; its default backend does not need Pi
Both Asks: 1 Pi, 2 Claude Code, 3 both, 0 cancel
Neither Exits with code 1 and creates no configuration, links, backups or directories

The installer looks for executables on your PATH, plus Pi at ~/.pi/agent/bin/pi and Claude Code at ~/.local/bin/claude or ~/.claude/local/claude. It checks each selected agent's version with --version; a configuration directory alone does not count as an installation. Older or unrecognized versions are refused, and an agent you did not select is neither installed nor upgraded.

Options

./install.sh --agent pi                 # Pi only
./install.sh --agent claude             # Claude Code only
./install.sh --agent both               # both, which must both be installed
./install.sh --yes                      # both if available, otherwise the one found
./install.sh --agent both --dry-run     # print the plan without writing anything
./install.sh --help

# Another Claude Code account, keeping the default account unchanged
./install.sh --agent claude --claude-dir "$HOME/.claude-work"

# A non-default Pi configuration directory
./install.sh --agent pi --pi-dir "/absolute/path/to/pi-agent"

Without a terminal to ask in, and with both agents installed, you must pass --agent or --yes; the installer does not guess. --yes does not skip conflict or version checks.

Configuration directories are chosen in this order: the --pi-dir and --claude-dir options, then PI_CODING_AGENT_DIR and CLAUDE_CONFIG_DIR, then the defaults. To use a configuration file elsewhere, set TRANSLATOR_CONFIG to an absolute path, and set it for every later agent process too.

What the installer changes

  1. Before writing anything, it checks Node, the agent versions, that the source is complete, and every destination. Any conflict stops the whole installation, so one agent is never left half set up.

  2. It creates a stable link to the source and the translator command:

    ~/.agents/hooks/translator  ->  the translator directory in your clone
    ~/.local/bin/translator      ->  ~/.agents/hooks/translator/control.mjs
  3. It creates config.json from config.example.json, with mode 0600, only if no configuration exists. Existing settings, history and logs are never overwritten.

  4. Pi: it adds <stable link>/pi/index.ts to the extensions list in Pi's settings.json, keeping all other fields and extensions. Known registrations of the old pi.ts entry are migrated and de-duplicated.

  5. Claude Code: it creates the link <claude-dir>/skills/translator -> <stable link>/claude-code. It does not change your settings hooks or replace an existing plugin with the same name.

  6. Before changing Pi's settings, it saves a byte-for-byte backup named settings.json.translator.bak.<timestamp>.<random>. File permissions and existing symlinks are preserved.

  7. Reinstallation is idempotent. File updates lock canonical targets and Pi's logical settings path, then recheck the baseline immediately before replacement. Detected changes abort installation. A failed rollback retains the original backup and reports its recovery path rather than claiming full restoration.

Locks coordinate cooperating writers, not arbitrary editors; the final comparison and rename are not an OS compare-and-swap. Avoid concurrent host-setting changes, especially first-time creation or access through unknown symlink aliases. A lock wait times out after five seconds. After a crash, inspect the reported .lock and confirm no writer is active before removing it; the installer never steals unknown locks.

Moving or updating your clone

Agents load the tool through these links, so do not delete the clone after installing. To move it, point ~/.agents/hooks/translator at the new location, update any registration or environment variable that names the old absolute path, then reload Pi or restart Claude Code. Move your config.json, history and logs along with the clone; data stored at custom external paths can stay where it is. The installer refuses to replace an existing real directory at the link's location, or a link to another clone: back up your configuration and data and resolve that by hand first.

Updates reach running agents. Agents load the tool straight from the clone, so whatever the clone holds is live:

  • Claude Code reloads the mod by itself when its files change: when the current turn ends, or sooner if you run /translator. Translation continues across the reload. A turn in progress still gets its answer translated once and drawn, and settings and pending originals are kept. If Claude Code prints reload failed, the previous version stays loaded, the new files have an error: the previous version keeps translating, and the next change to the files is tried again.
  • Pi keeps the version it loaded until you run /reload or restart it. Pi lets only commands reload extensions, so it cannot pick up an update by itself; it never mixes two versions.
  • A checkout that switches branches is not a stable install. Agents load whatever the linked clone holds, so checking out another branch, such as another tool's branch of this repository, changes every running agent with it. For everyday use, install the released npm package, which keeps its own copy outside Git (see Installing from npm), and load a development checkout only for testing, with claude --plugin-dir or pi -e as in Development and testing.

If Claude Code reports The loaded translation mod is out of date, a mod from before 0.4.0 is still loaded: restart Claude Code.

Installing from npm

npm install -g @orbit-labs-ai/translator places the tool in npm's global package directory and adds the translator command. translator install runs the same installer as ./install.sh and takes the same options; every other argument controls the defaults, as described above. The ~/.agents/hooks/translator link then points into npm's global directory.

npm replaces the package directory on every update, so an npm install keeps config.json, the translation cache and Claude Code's session records in ~/.agents/translator/ instead of beside the source.

  • Updating: run npm install -g @orbit-labs-ai/translator@latest, then run /reload in Pi. Claude Code reloads the mod by itself; if a session still runs the old version, run /reload-plugins there. The links stay valid, so there is no need to run the installer again.
  • Switching Node.js installations, for example with a version manager, moves npm's global directory. Remove the ~/.agents/hooks/translator link, install the package for the new Node.js, and run translator again.
  • Do not use npx: it runs packages from a temporary cache, so the installer refuses to run there.

Upgrading from earlier versions

From 0.6.0 (prompt-translate) to 0.6.1 (translator). Everything was renamed, and the installer does not remove the old entries. Do this once, then restart Claude Code and reload Pi:

  1. Remove the old entries: the ~/.agents/hooks/prompt-translate and ~/.local/bin/agent-translate links, the skills/local-translation link in each Claude Code account, and the …/prompt-translate/pi/index.ts entry in Pi's settings.json.
  2. Get the new version. Clone: git pull, then move config.json, history/ and claude-sessions/ from Hooks/prompt-translate/ to Hooks/translator/; Git does not move ignored files. npm: npm uninstall -g @orbit-labs-ai/prompt-translate, npm install -g @orbit-labs-ai/translator, then move the contents of ~/.agents/prompt-translate/ to ~/.agents/translator/.
  3. Run the installer again: ./install.sh in the clone, or translator install.
  4. Rename any environment variables you set: AGENT_TRANSLATE_CONFIG, AGENT_TRANSLATE_DISABLE, AGENT_TRANSLATE_NO_PERSIST and AGENT_TRANSLATE_PI become TRANSLATOR_CONFIG, TRANSLATOR_DISABLE, TRANSLATOR_NO_PERSIST and TRANSLATOR_PI. The old names are no longer read.

Commands changed too: /translator replaces /translate, /translate-language, /translate-output, /translation-status and /translate-cancel, and the translator command replaces agent-translate (agent-translate language is now translator instruction). Pi's saved branch settings still apply. Claude Code keeps session overrides per plugin name, so those saved under local-translation are not carried over, and resumed older sessions start from your file defaults. The changelog lists every change.

Older versions:

  • Pi's entry point moved from the old top-level pi.ts to pi/index.ts, and all Pi code now lives in pi/; no compatibility shim is left at the top level. The installer migrates old paths in the Pi settings it manages; update other manual or project-level registrations and external callers yourself.
  • Claude Code's worker moved from the top level to claude-code/bridge.mjs. The default plugin settings need no change, but update any custom, non-empty bridgePath. The cli.mjs, control.mjs and import-history.mjs paths are unchanged.
  • Claude Code now defaults to claudeBackend: "native". To keep translating through Pi, add "claudeBackend": "pi" to your configuration; the installer never overwrites an existing choice.

Command-line tool and other hosts

A standalone command translates text before you hand it to any agent. It needs Pi: Claude Code's native backend is only available inside its plugin. The Chinese sample means "Please check the code":

HOOK="$HOME/.agents/hooks/translator"
printf '%s' '请检查代码。' | node "$HOOK/cli.mjs"
printf '%s' '请检查代码。' | node "$HOOK/cli.mjs" --json
node "$HOOK/cli.mjs" --help

In plain mode, the text to submit goes to standard output and a bilingual preview to standard error. In JSON mode, standard output holds only {status,text?,error?,usage?}. Success, including a local pass-through, exits with 0; failure or cancellation exits non-zero with no text to submit. Callers must check both the status and the exit code instead of piping the output straight into an agent. The command adds no language instruction, keeps no session history and does not translate answers.

To support another host, call translatePrompt and translateOutput from shared/core.mjs and supply your own isolated completion and storage. An input failure must block submission, and answer translations must never be written into the model's context. The architecture guide documents the Claude Code worker protocol and the dependency rules.

Troubleshooting

Symptom What to do
The installer cannot find an agent Check your PATH, file permissions, and pi --version / claude --version. A configuration directory alone is not an installation
The installer reports a conflict Inspect the existing file, directory or link instead of overwriting it. Back it up, then decide what to keep or migrate
Pi still refers to the old pi.ts Run the installer again with the --pi-dir you actually use, and check project-level or command-line registrations
Claude Code cannot find the worker The worker is now claude-code/bridge.mjs; check for an old, non-empty bridgePath
Old /translate… commands, a local-translation plugin or a Pi error about prompt-translate after upgrading Remove the 0.6.0 entries as described in Upgrading from earlier versions, then restart Claude Code and reload Pi
A translation fails and the prompt is blocked Check the backend's login, model, timeout and network. Use /translator input off only if you mean to send the original
The native Claude Code model is unavailable Pin an allowed claudeModel, or set claudeBackend: "pi" and configure Pi
Prompt NOT sent: The loaded translation mod is out of date A mod from before 0.4.0 is still loaded. Restart Claude Code, including any other account that uses the same link
Claude Code prints reload failed, the previous version stays loaded The updated files have an error, or the update was caught halfway. The previous version keeps translating; once the files are complete, Claude Code reloads the mod on their next change, or run /reload-plugins
Translation stopped working after the clone was switched to another branch Agents load Translator straight from the linked clone, so the branch's version, or its absence, is live. Switch it back, and run agents from the released npm package rather than a checkout that switches branches; see Moving or updating your clone
No original under a prompt sent while Claude was working Update to 0.6.2 or later; earlier versions drew the original only under prompts sent while Claude was idle
In the Claude Code desktop app, a prompt shows the Chinese twice, or an answer or a prompt sent while Claude was working has no Chinese under it Update to 0.6.6 or later, then run /reload-plugins in the session or start a new one: the app's sessions do not reload mods by themselves. The app draws rows from its own copy and names them its own way; earlier versions drew only for the terminal's rows. Rows already in a session are drawn again when the app next shows them
Translations are slow or time out Check /translator status, raise the file's timeoutMs, use /translator thinking low, or pin a faster translator with /translator model MODEL. A warning that names a part (part 2 of 4) comes from one part of a long answer. In Pi, long answers time out before 0.6.7, which sent each answer in one request: update, then /reload
Claude Code reports an incomplete or missing completion marker The reply may have been cut off or badly formatted. Nothing was submitted; split the prompt or switch backends
The default settings seem to have no effect Branch/session settings take precedence; check /translator status. Use /translator model follow to override a file model pin
Shortcuts do nothing Pi depends on your terminal reporting the key combination; Claude Code needs an empty composer or a focused button row. Use /translator for settings; cancellation uses the controls below, not a slash command
A mostly Chinese answer is still translated Update to 0.6.9 and reload if only Markdown, Mermaid, Downloads or Token/Tokens caused it. Other English prose or an unrecognized plain-word name still triggers translation; the gate does not skip merely because most characters are Chinese. Wrap exact literal names in backticks when appropriate
An English answer containing Chinese names or phrases is translated Expected: Chinese in an answer does not stop the translation while English prose remains, and the translator keeps names and paths
An English prompt quoting Chinese is translated Expected: quoted Chinese counts as prose. The translator keeps a quoted literal value; wrap it in backticks to keep the prompt out of translation
A translation leaves prose untranslated or uses an unexpected language There is no post-translation language audit or repair call. Review the result, try another translator model, and disable the input cache to force a fresh translation; wrap exact literal strings in backticks when appropriate
Need to cancel a translation Pi: Ctrl+Alt+C. Claude: pending-only Cancel translation, key 4 with an empty composer or Ctrl+X, Tab, 4. Both cancel input/output without changing settings; do not rely on ordinary Pi abort during idle input preprocessing
File lock timeout / incomplete rollback Stop concurrent settings changes and inspect the reported lock and retained backup; never remove active locks or overwrite newer settings blindly
Claude Code lists "Hooks (0)" for the plugin That count covers classic settings hooks only. Run claude plugin validate to see the mod's events

Limitations

  • Preflight Chinese detection looks for Han characters, so Japanese kanji also count. It runs only before translation. Completed/nonempty responses and literal integrity do not guarantee meaning, completeness of the translation or the requested language; preserved literal content never triggers language rejection.
  • Hidden reasoning, tool calls and unfinished answers are never translated. Text that other templates or plugins add after this tool runs is outside its guarantees.
  • Successful originals require delivery evidence. Pi's public API has no input-to-queue ID: exact text and image matching is conservative, and different originals with indistinguishable pending translations, conflicting bypassed inputs, or host-normalized images/text can cause the card to be omitted. Persistent journals and enabled caches remain available. Queue discard, navigation, reload or session replacement never reconstruct pending cards from history; previously delivered cards remain.
  • A classic UserPromptSubmit hook cannot safely replace the original prompt (its additionalContext only adds text), so this tool offers no such fallback and no Codex hook.
  • Claude Code's safe and bare modes, disabled hooks, an untrusted workspace or an organization policy can stop the plugin from loading. A new installation should check /translator status before relying on it.
  • In Claude Code, the originals and Chinese answers drawn in the transcript come back after a restart only from the session journal, so memory-only mode (TRANSLATOR_NO_PERSIST=1) shows them only until Claude Code exits. Their notices remain in the transcript view.
  • A running Pi picks up a code update only at /reload or a restart: Pi lets only commands reload extensions.
  • Claude Code's native backend translates each answer in one request, so a very long answer can exceed timeoutMs there; Pi and the Pi backend translate it in parts.
  • A few rare Claude Code turns finish without a regular model step. Their answers are translated through a de-duplicated fallback, and whether its transcript notice is saved depends on the host; see the Claude Code adapter guide.

Uninstalling

  1. Pi: remove this tool's pi/index.ts entry from extensions in the settings.json of the Pi directory you use, then reload or restart Pi.
  2. Claude Code: remove the skills/translator symlink for each account you set up, then restart Claude Code.
  3. Remove the ~/.local/bin/translator link if you no longer need it.
  4. Remove the ~/.agents/hooks/translator link, or the source itself, only after checking that no other agent still uses it.
  5. If you installed from npm, remove the package with npm uninstall -g @orbit-labs-ai/translator.

Do not delete a whole agent configuration directory, and do not restore an old settings backup over a file that has changed since. These steps leave your configuration, translation history and logs in place; delete them separately using Data and privacy.

Development and testing

To load the tool for a single session without installing it, set HOOK to the translator directory and run pi -e "$HOOK/pi/index.ts" or claude --plugin-dir "$HOOK/claude-code". Do not load the same integration through two entry points at once.

npm run check:shared   # verify Claude's generated scanner matches the shared source
npm test                 # Node tests, plus real Pi and Claude Code against local HTTP fixtures
npm run test:claude      # validate the Claude Code plugin and run its mod tests
npm run test:all         # both of the above; full verification needs both agents installed
npm run test:coverage    # coverage for the Node modules; the scope is documented separately
bash -n install.sh

Every model response in the tests comes from a local fixture, so the tests never make paid calls. Installer tests run against temporary home and clone directories and cover all four combinations of installed agents, interactive choices, re-runs, paths with spaces, migration, conflicts, malformed settings, backups and rollback. The real-agent tests load the entries that the installer creates.

When the Pi SDK or Claude Code is missing, the matching integration tests are skipped and say so; a skipped test is not a passed test. Two variables help with debugging:

  • PI_SDK_PATH: path to the Pi SDK's dist/index.js, if Pi is not installed in its usual managed layout.
  • KEEP_TRANSLATION_TEST=1: keep the temporary directories of the Claude Code integration tests.

test/claude-tui.test.mjs drives the real Claude Code terminal through tmux and is skipped, saying so, when tmux is not installed.

The testing guide records the 1,013 passing Node tests, 48 passing Claude plugin tests, test matrix and remaining evidence boundaries for 0.6.10.

Further reading

  • Claude Code adapter: how the mod works inside Claude Code.
  • Pi adapter: how the extension works inside Pi.
  • Architecture: source layout, dependency rules and the worker protocol.
  • Testing: test matrix, recorded results and limits.
  • Changelog: what changed in each version, and how to upgrade.
  • Contributing: how to propose changes; the English and Chinese READMEs are maintained together.

License

MIT © 2026 Orbit Labs contributors.