更新日誌
每一版改了什麼,全部在這裡。
內容與 repo 的 CHANGELOG.md 同源;正文保留英文原文。預編譯檔在 GitHub Releases。
v1.3.0最新版本 · LATEST2026-09-01
新增 · Added
- The interactive session knows a book is half finished. The book list
marks any book with an unfinished job cache beside it ("8/12 chapters
done"); the confirm screen prices and describes only the chapters still to
run, and says plainly that the finished ones are not billed again; the
progress board opens with those chapters already ticked and tagged
cached, so they read as paid-for rather than impossibly fast; and the
completion summary reports how many segments (and notes) came back from the
cache — a number that previously only ever reached the flag-driven output.
Two things the screen now says out loud instead of leaving to be discovered:
changing the model or the depth voids the whole cache, and changing "Save
to" moves the job cache with it and leaves the finished work behind.
- A chapter counts as finished only when it is actually cached. The job's
chapter status was recorded as
done even when every call in the chapter
had failed and nothing was cached. It is now partial in that case, so a
resume can never quote a discount the next run does not honour (a whole book
that failed against an unreachable endpoint would otherwise have priced as
almost free).
改進 · Changed
- Product term (zh-Hant / zh-Hans): annotations are 註解 / 注解 in the
interactive session and
docs/READER-PROFILE.md, matching README.zh-TW.md
and the rest of the Chinese-language surface. English ("margin notes"),
Japanese (注釈) and Korean (주석) are unchanged. Internal identifiers — the
annotate subcommand, the --note-* flags, the annotation cache salt — are
untouched, so no existing cache is invalidated.
- The interactive session is fully localised again. The banner tagline and
the main menu's own key legend were hard-coded English under a translated
menu; so were two keys in the paragraph editor's legend. All now go through
the same five-language table as everything else.
修復 · Fixed
- The model picker no longer overwrites the model you saved. When the
local Ollama scan came up empty (the
ollama binary not on PATH, the
daemon slow to answer) the picker fell back to a curated list; when it
succeeded it offered the scan in its own order. Either way the saved model
was silently replaced by the first entry, and written back to
settings.json. Because the model is part of the cache signature, opening
the config screen could void every finished chapter of a half-translated
book and point the run at a model that is not installed — every unit then
fails and the output is a book of untranslated text. The picker now always
offers the configured model, and keeps the selection by name when the list
changes.
- Chinese and Japanese punctuation is normalised at the writer. Some runs
came back with half-width
, : between Han characters while others, same
book and same model, did not. Marks are widened only when the visible
neighbours on both sides are CJK, so quoted English, thousands separators,
decimals, URLs and markup attributes are untouched; Japanese takes 、;
Korean is deliberately left alone (modern horizontal Korean sets the Latin
comma and full stop). The prompt says so too, but the writer is the fuse.
Prompt wording has never entered the cache signature, so existing caches
re-render through the fix at no cost.
- Subscription runs report what they actually spent. Only
usage.input_tokens was counted, and under prompt caching almost every
input token arrives in the cache fields instead: one real annotate run
reported 7 input tokens against an actual 10,851. All three buckets are now
counted, weighted at the provider boundary into a billable figure (cache
reads and cache writes do not cost the same as fresh input), with the honest
total still reported as tokens_in. The Codex path had the mirror-image
bug: its input_tokens already includes cached tokens, so the whole packet
was being priced at full rate. claude-sonnet-5 was also priced at
$3/$15 by a contains("claude-sonnet") match; the published rate is $2/$10.
v1.2.72026-08-24
新增 · Added
translatus doctor. One command (add --json for machines) that checks
binary/PATH self-consistency (including the "install replaced while
running" state), the subscription sidecar kit and Node, the default sidecar
port, per-client MCP registration, and the settings file. Exit 1 only on a
hard failure.
--client claude|codex on mcp install / mcp uninstall. Install
records which registrations it created; a bare uninstall now removes only
those — never a registration the user or another tool made — and --client
targets one client explicitly.
--json runs open with a run_start header (version + timestamp), so
interrupted-and-resumed runs appended to one log stay separable.
改進 · Changed
estimate accepts translate-only flags and ignores them (with a stderr
note), so a translate command line can be replayed as an estimate by
swapping the verb instead of hitting a hard argument error.
- MCP tool schemas state the
base_url credential rule inline: stored
tokens are attached only when the value exactly matches the endpoint saved
in Settings — agents no longer need the guide to explain a silent 401.
- Subscription sidecar: the Claude SDK's native binary is resolved once at
startup and pinned, its path logged; a port conflict at startup names
LLM_SUB_KIT_PORT, and a mid-run "Native CLI binary not found" (install
upgraded/removed underneath a running server) now explains itself.
v1.2.62026-08-24
改進 · Changed
- Bilingual is now the CLI default layout.
translate without --mode
produces original + translation interleaved, matching the desktop default;
pass --mode replace for translation-only output. The cache signature does
not include the layout, so existing job caches re-render either way at zero
cost.
v1.2.52026-08-24
修復 · Fixed
- Vertical books get their accents on the correct side. In vertical-rl
(tategumi) and rtl-progression books the block axis runs right-to-left, so
the left-edge accent that marks translations and margin notes landed on the
far side of the block — visually attached to the following paragraph. Those
books now receive mirrored stylesheets: right-edge accent, mirrored padding
and inter-block margins. Horizontal books are unchanged, and the choice is
regression-locked.
v1.2.42026-08-24
改進 · Changed
- Tightened an internal source comment. No functional changes.
v1.2.32026-08-23
修復 · Fixed
- Margin notes are tuned for e-ink readers. Monochrome panels collapse
colour to luminance and usually implement dark mode as inversion; the
low-alpha vermilion tint could vanish or dither there. A final
@media (monochrome) layer now drops the tint and returns note text to the
device's own ink; the accent border and note prefix carry the distinction.
Every accent colour was audited to keep ≥5:1 luminance contrast against its
ground (note ink >10:1), so greyscale and desaturating colour e-ink panels
(Kaleido) stay legible in both light and dark modes.
v1.2.22026-08-23
修復 · Fixed
- Translations and margin notes are readable in dark mode. The CSS injected
into bilingual EPUBs hardcoded near-black text (
#222 on translations,
#5c2d24 on notes), which disappeared into the background on night themes.
The default rules now inherit the reader theme's text colour everywhere;
the indigo/vermilion accents are tuned per scheme behind
prefers-color-scheme guards, with a pale-vermilion note ink on dark
grounds. Regression-locked so no future default rule can hardcode a text
colour again.
v1.2.12026-08-20
修復 · Fixed
- A Homebrew install can find the sidecar it was just given.
bin/translatus
is a symlink into the Cellar and current_exe returns the link, so the
libexec lookup landed on /opt/homebrew/libexec — where the formula installs
nothing. The path is canonicalised before its prefix is derived.
v1.2.02026-08-20
新增 · Added
--provider subscription: translatus starts the sidecar itself. Reaching
a Codex/Claude plan used to be homework — npm install, npm start, copy the
token it prints, paste it into Settings, keep the terminal open — and every
step was something the program already knew how to do: the kit ships in the
release archive, the command is fixed, the token is generated by the server.
One flag now allocates a free loopback port, generates a per-run token, starts
the sidecar, waits for it, and shuts it down when the run ends.This is not only convenience. The pasted token had to live in the OS keychain,
and an ad-hoc-signed binary cannot read its own keychain items without an
authorisation dialog — which nobody can click in an agent session, so the MCP
path returned 401 even when everything was configured correctly. A sidecar
owned by the run needs no keychain at all.
subscription is a CLI-level alias, not an engine provider, so the cache
signature is unchanged: switching between a managed sidecar and a hand-started
one never re-bills a book.
- Homebrew installs the sidecar. The formula shipped the binary alone, which
left the documented first install path unable to use the documented first
model source. It now installs the kit under
libexec and depends on Node,
with dependencies resolved at install time so the first translation does not
stall on npm.
改進 · Changed
- The MCP server falls back to the managed sidecar instead of refusing. With
nothing configured it now runs
--provider subscription, so an agent handed a
book and no other information can actually translate it.
v1.1.92026-08-20
修復 · Fixed
- The MCP server no longer dry-runs a book in silence.
--provider
defaults to mock on the flag interface, which is correct there: a dry run
is something you ask for. Over MCP it was not. An agent asked to translate a
book has no reason to pass a provider — the parameter is optional and its
description called mock the default — so a real request went to the mock
provider, finished in a minute with zero model calls, wrote a 962 KB EPUB
containing 509 placeholder translations, returned status: completed, and
the agent told its user the book was translated. A wrong book reported as a
right one is worse than any error.When no provider is named, the server now uses the model source saved in the
user's settings, and refuses with an actionable message when there is none.
An explicit provider (including mock) and cache_only are untouched.
v1.1.82026-08-20
修復 · Fixed
- The confirm row agrees with the estimate above it. 1.1.5 moved
subscription runs from money to work volume, but only in the block; the row
the user actually presses still read
~$0.83. One screen carried two
framings of the same run, and the money one was the one being agreed to.
v1.1.72026-08-20
Carries 1.1.5 and 1.1.6, whose releases never built (see below).
修復 · Fixed
- CI and release runs can no longer burn the six-hour job ceiling.
apt-get
on a hosted runner stalled with no timeout anywhere in either workflow, so a
single stalled mirror ran to the 6h maximum and was cancelled: on 2026-08-19
that killed one CI run and both release runs, and the v1.1.5 and v1.1.6 tags
ended up with no published artifacts at all. Every job now has a
timeout-minutes, and the apt step bounds each attempt at 180s and retries
three times before giving up. The macOS builds in the same matrix finished in
under two minutes throughout — only the Linux job hung.
v1.1.62026-08-20
Found by translating a whole real book rather than an excerpt.
修復 · Fixed
v1.1.52026-08-19
新增 · Added
- The copyright page is never sent to a model. It is the one page in a book
that must not be rewritten: the publisher's rights statement, ISBN and
cataloguing data. Translating it produces an unauthorised-looking restatement
of the text that says the work may not be restated, costs tokens, and gives
the reader nothing. Identified from what the book itself declares — EPUB 3
epub:type="copyright-page" landmarks and the EPUB 2 <guide> — and, for
books that declare nothing, by a deliberately narrow content check: a short
document carrying two or more rights markers at once. Japanese books put the
same thing (奥付) at the back, so position is not the test. imprint and
colophon are treated the same way. Skipped pages are named in the estimate
and on the confirm screen, and the run log says skipped (publisher
apparatus, sent to no model) rather than 0 translated — which would have
been indistinguishable from a fully-cached chapter. They are written out
byte-identical and are never annotated.
改進 · Changed
- A subscription run is quoted in work, not dollars. A subscription is a
flat fee, so a dollar figure was fiction dressed as precision; what is
actually being spent is quota and wall time, and token volume is the honest
proxy for both. API-key runs still show money.
- The number is labelled a floor, not an estimate. Measured against real
runs it is always low and never high (1.55x on a Latin book at sentence
level, far worse on a CJK book at expert level), because prompts, retries and
repairs are not modelled. "At least" is what the number actually means.
v1.1.42026-08-19
改進 · Changed
- The pre-run estimate counts CJK at CJK rates and expert at expert's number
of passes. Two things it did not model. Every script was priced at 2.5
characters per token, which is roughly right for Latin and about 2.5x wrong
for Han, kana and Hangul. And
--level was not an input at all, so a run
that reads the whole book for a glossary, drafts, reflects against the source
and then re-reads everything for the consistency check was priced as a single
pass. Measured on a Japanese book at expert level with notes, the quoted
figure moves from $0.35 to $1.42.It is still an under-estimate, knowingly. The same run really cost about
$24: 154 segments of ruby-dense Japanese produced 276,535 output tokens, or
~1,800 per segment, which no character count predicts. Closing that needs
per-phase token accounting inside the engine — the numbers to calibrate
against do not exist yet, and inventing a multiplier to cover a 17x residual
would only move the error. A Latin book at sentence level is 1.55x out; that
one is within the "rough numbers" the screen already claims.
v1.1.32026-08-19
Second pass over the same end-to-end walk: the things the first pass surfaced but did not yet act on.
修復 · Fixed
- The interactive session honours
^C during a run. It held raw mode for
the whole session and polled no keys while translating, so the board's own
"^C stop" legend was inert for exactly as long as a run lasted — measured at
20 minutes before the process had to be killed from outside. Raw mode exists
to read keys; the board only writes, so it is handed back for the duration
and the terminal turns ^C into the signal the process already handles.
- The session no longer fans out against an endpoint that serves one request
at a time. It set
--concurrency 4 for every non-Ollama source, including
the subscription sidecar its own guide says to keep at 1. Three of every four
batches came back 429, which (before 1.1.2) silently cost those units.
- Foreign-name interpuncts are consistent in Traditional Chinese output.
Measured over one 534-segment book: U+30FB (the Japanese katakana middle dot)
30 times, U+FF0E (a fullwidth full stop) 17, U+00B7 (the mainland middle dot)
16, and U+2027 — the Big5 間隔號, the one Traditional Chinese wants — only 4.
Between two Han characters the first three are now normalised to U+2027;
anywhere else they are left exactly as written.
- The keychain can no longer hang a run forever. A macOS read can raise an
authorisation dialog, routinely after a rebuild changes the binary signature,
and the call blocked until somebody clicked it — indistinguishable from
working if the display is asleep. It now gives up after 20s and says what to
do.
改進 · Changed
estimate prices against the model the run will use. It defaulted to
gpt-5.4-mini regardless of configuration, so an agent that estimated and
then translated with a Claude model reported a number 3.5x under what the run
cost. Naming --model still overrides. (The remaining under-count — the
estimator models one pass and has no notion of --level — needs measurement,
not a new multiplier, and is not addressed here.)
- Expert level says what "progress is saved" means. It checkpoints at
chapter boundaries, so an interruption restarts the chapter in flight; the
stop legend and the resume line now say so instead of implying batch
granularity.
- The flag form of a run is printed where it can be copied. The confirm
screen truncates it to the width of a box, which made "graduate to the CLI"
unkeepable; the full command now also lands in scrollback when the run ends.
v1.1.22026-08-19
修復 · Fixed
- A busy endpoint is waited for, not counted as a failure. The subscription
sidecar serves one paid completion at a time and a book-length one runs for
minutes, but its 429 went through the ordinary transient path — two retries,
1s and 2s — and then failed the unit. Starting a second book while one was
running therefore put gaps in it. 429 now backs off on the scale of the thing
being waited for (15s → 2m).
v1.1.12026-08-19
Found by an end-to-end pass over the real install paths on macOS: Homebrew and the release archive, the interactive session, and an agent driving the MCP server. Every item below reproduced on a clean machine.
修復 · Fixed
- A failed segment no longer poisons the resume cache. When a provider call
failed, the unit fell back to its source text — correct for assembling the
book — but that fallback was then written to the job cache. One transient 401
or timeout was therefore permanent: every later run of the same command
reported
units_failed: 0, restored the source from cache, and handed back an
untranslated book, with deleting the .etjob as the only undocumented way
out. Failed units are now excluded from both the streaming checkpoint and the
chapter-boundary write, so re-running retries them. This is the guarantee in
the README ("re-running never re-bills a translated segment") actually holding
for the case where it mattered most.
- A prefixed OPF is read like any other.
<opf:item> / <opf:itemref> are
the same elements as <item> / <itemref> — the namespace prefix is
arbitrary, and real toolchains emit it — but the manifest and spine scanners
matched the literal strings. Such books parsed as zero chapters.
- A book with no readable chapters is an error, not a free success. The
above surfaced as
chapters: 0, est_cost_usd: 0, status: "completed", which a
caller cannot tell apart from an already-finished book.
- The run screen's estimate no longer reads "~38h56m left". Time remaining
was extrapolated from wall-clock elapsed divided by characters completed;
expert level's whole-book pre-scan landed entirely on the few front-matter
characters that finish first. It now measures throughput from the chapters'
own durations and says nothing until 5% of the text has been timed.
- The run screen names the whole-book passes. The pre-scan and the note
planning pass showed a 0% bar and nothing else, for minutes.
- A dropped inline-emphasis marker costs one extra call, not the paragraph.
When the source italicises a word the target language has no separate word
for — "there *is* one right way" — the model returns a good sentence with the
marker pair missing, validation rejects it, and the unit falls back to its
source: one English paragraph inside a translated book, reproducible on the
same paragraph every run. The engine now sends one targeted repair naming the
markers that went missing, which recovers it; re-rolling the same prompt does
not, so this is not simply more retries.
- Connection-test rows no longer collide. The label column padded by
character count, so "Access token" (12 chars in a 10-wide column) rendered as
Access tokenpresent; every CJK label was misaligned for the same reason.
改進 · Changed
- The MCP server can reach the subscription sidecar without extra wiring.
A caller-supplied
base_url still receives no ambient credential, but the
token bound to *the user's own saved endpoint* is now sent when the requested
URL matches it exactly — read from the OS keychain, never copied into another
program's config file. Previously translatus mcp install produced a
registration that could not authenticate against the sidecar it documents, so
every subscription translation an agent started came back 401 while the same
settings worked from the CLI. TRANSLATUS_MCP_ENDPOINT_URL/_TOKEN still
override.
- The subscription model picker offers the current generation. It listed
claude-sonnet-4-6 first and had no Claude 5 entry at all, so a new install
silently landed a generation behind what the same subscription serves.
claude-sonnet-5 and claude-opus-5 are offered (and priced); the older ids
stay selectable, since a job cache is keyed by model.
- The subscription sidecar's request timeout defaults to 10 minutes, up from
150s. Measured completions for one chapter batch run 48s, 86s and 121s; the
old ceiling clipped the largest, and each clip cost the full 150s before the
caller could split and retry.
v1.1.02026-08-18
新增 · Added
translatus mcp install / mcp uninstall: registers the MCP server with the
agent clients found on this machine (Claude Code, Codex) through each one's
own CLI rather than by editing its configuration file. The interactive session
offers to do it once on first run, so a normal install is one step; package
installation itself never writes into another program's config.
v1.0.02026-08-17
First public release.
安全 · Security
- The Codex and Claude subscription children run with a reduced environment,
every built-in tool disabled, and no session persistence; a contextual
trusted-book reminder is shown when users choose Codex subscription mode.
- Hardened bearer-token redaction, redirect handling, settings-file permissions,
MCP write paths, CLI path collision checks, and release-workflow permissions.
- Client disconnects cancel the provider call, and the sidecar serves one paid
request at a time.
- Final book output is an owner-only atomic replacement, target-language
filename traversal is blocked, and MCP-selected loopback endpoints never see
environment or OS-keychain credentials.
新增 · Added
- Reader-profile contract (
--note-profile, MCP note_profile): one JSON
document — purpose, cognitive anchors, presets, voice, language, density,
style — that a reader or the reader's own AI assistant fills to steer the
margin notes. Schema and the standard hand-to-your-AI prompt:
docs/READER-PROFILE.md. Explicit flags override document fields; over MCP
only inline JSON is accepted (file paths would read arbitrary server paths).
- Service menu & explanation level. The preset help angles grew into the
single "what should the notes do for you" layer — three new services join
the six existing ones:
world (connect passages to the real world: later
developments, other fields, historical parallels — verified facts only),
methods (unpack methods with their conditions, costs and limits) and
research (prioritise citable facts, sources, the book's structure, and
points of dispute). Picking is enough: with at least one service ticked the
free-text profile becomes optional, and the interactive session leads with
the picker, demoting free text to "In your own words". A new
--note-level beginner|general|insider dial sets how much the notes
assume: beginner explains in everyday language with examples; insider only
adds what an insider could not easily look up. The session also collects
anchors ("You already know") and the voice register.
- Cognitive anchors (
--note-anchors): short labels of what the reader
already knows; notes explain new concepts by bridging from that familiar
ground (with an accuracy guard: a wrong analogy is worse than none), and are
still never allowed to mention the reader.
- Note voice registers (
--note-voice study|companion): the restrained
study register stays the default; the companion register writes in a
friend-at-your-side tone with a higher share of short reaction notes. Hard
rules (neutrality, no reader-addressing, no spoilers) identical in both.
- Book-wide thread map: the planning pass now also samples mid-chapter text
and returns cross-chapter threads (concept first appears → pays off), so the
per-chapter selection pass can place notes AFTER an insight lands and reach
back to already-read chapters instead of discovering the connection too late.
Note text still never previews unread content.
- Reader-boundary output check: every accepted note (writing and review
passes) is program-scanned for reader-addressing phrasings; violations are
rejected and rewritten, making "the notes never talk about you" a machine
guarantee rather than a prompt promise.
改進 · Changed
- Annotation cache signature salt bumped (v6 → v7): existing note caches
re-annotate under the new prompts; translation caches are untouched and are
never re-billed.
新增 · Added
- Interactive session. Running
translatus with no subcommand on a
terminal opens a guided flow: it finds the books around you, asks how they
should read, quantifies the run (chapters, characters, estimated cost), waits
for an explicit confirmation before spending anything, and paints a live
progress board with per-chapter status and a remaining-time estimate
extrapolated from measured throughput. It prints the equivalent command as
you go, so the flags stay learnable. In a pipe or a script a bare
translatus prints usage exactly as before, and the --json agent contract
is unchanged — the interactive path builds the same arguments and calls the
same engine. The session speaks five languages (English, 繁體中文, 简体中文,
日本語, 한국어), following the terminal locale with a TRANSLATUS_LANG
override; it offers real model sources only (subscription sidecar, API key,
or Ollama — the Ollama model list is read live from ollama list), while
the mock provider stays available behind the --provider flag for
offline format checks. In subscription mode the Settings screen takes the
sidecar's printed access token (stored in the OS keychain like a key) and
accepts a sidecar on any loopback port, not just the default 8765.
- MCP server (
translatus mcp): a stdio Model Context Protocol server so
agents (Claude Code, or any MCP client) can call estimate_book,
translate_book, and annotate_book as first-class tools. Results reuse
the --json schema by construction (each call self-execs the CLI in
--json mode); long calls stream per-chapter notifications/progress;
re-calling a tool with the same arguments resumes from the job cache.
Registration: claude mcp add translatus -- translatus mcp. Covered by
e2e tests (handshake, tools/list, mock estimate/translate with progress)
and declared in CAPABILITIES.toml (mcp-server, CLI-only with reason).
- Human-mode CLI experience: a cost-estimate
line before every real run (
estimate logic reused; --json untouched), a
fail-fast setup card when a hosted provider has no API key (subscription
sidecar / API key / Ollama — the same three choices the interactive
launch), a mock-provider dry-run notice, per-chapter and per-note progress
lines, a completion summary (segments, notes written/edited/dropped,
tokens, cost, resume tip), and a Ctrl-C message explaining that re-running
the same command resumes without re-billing. translatus --help now ends
with a provider setup card; estimate prints a human summary without
--json. All CLI messages are English.
- docs/GUIDE.md and docs/FAQ.md: a full user guide (install, providers
including the subscription sidecar, every flag with examples, cache/resume
behavior, JSON mode for agents, troubleshooting) and an honest FAQ (costs,
subscription compliance, funding model, data flow, prior-art
differences). docs/ANNOTATION-TUNING.md is now in English.
- Preset help angles for margin notes (
--note-presets chips): six
fixed ids — terms, history, author, culture,
characters, concepts — each mapping to one guidance sentence injected
into the selection pass (angle guidance) and the writing pass, right after
the reader's free-text profile. Unknown ids are ignored with a warning. The
canonical preset set joins annotation_signature (tick a chip →
re-annotate; the translation cache is never touched).
- Per-book annotation settings support in the shared
Settings struct
(annotations.presets / annotations.density global defaults +
annotations.books per-book overrides) for per-book onboarding.
改進 · Changed
- Margin-note writing framework landed in the annotation prompts:
DEFAULT_NOTE_STYLE now encodes the ten craft rules (concrete first
sentence, one note = one thing, length varies with content, judgements must
anchor to specific words, the reader's likely misreading gets priority,
claims must be checkable), the selection pass prioritises spots a reader of
this background would misread or miss, and the review pass flags notes that
are all the same length. Derived from a cross-tradition study of real
annotators (金聖嘆/脂硯齋/毛宗崗, Gardner's *Annotated Alice*, high-voted
Genius notes) and validated by a persona blind-review loop. A second pass
added: a no-forward-spoiler rule (cross-chapter links may only look back
at what the reader has already read, never reveal later plot), a
factual-accuracy rule for real-world connections (a note may tie the text
to the wider world — later history, other fields — but only with verifiable
facts, never fabricated ones), selection guidance that climaxes and famous
scenes are not note-spots and every note must map to a concrete reader need,
and a native-register requirement (write in the target language's natural
voice, no translationese). The annotation cache salt bumped through
inkferry-anno-v5 to inkferry-anno-v6 (the prompt texts changed but do
not enter the hash on their own); translation caches are untouched.
- Public mode name: "fast" → "standard" (App: 一般/一般/Standard/標準/
표준 across the five UI languages). The default single-pass mode is now
called *standard mode* everywhere user-facing;
--level standard was
already accepted and is now the documented spelling, and 一般 joins the
accepted aliases. Nothing internal moved: the sentence level value, the
cache signature, and the historical aliases (fast, 快速…) all remain
valid, so existing caches and scripts are untouched.
- README: leads with the engine/CLI; product positioning moved to the
end and shrank to a short note. English docs now use English-reader
examples (
--to English); the zh-TW README keeps --to "繁體中文".
- Project rename: InkFerry → Translatus. The open-source engine + CLI is
now Translatus (Latin *translatus*, "carried across" — the root of
*translate*). The CLI binary is
translatus, the repository is
doka-software/translatus.
Cache/signature salts (inkferry-anno-*) keep their historical strings so
no existing cache is invalidated by the rename; older entries below use the
old name as written at the time.
- Product term rename: the zh-Hant product name for annotations is now
眉批 (was 夾註); en "Margin notes", zh-Hans 批注, ja AI注釈, ko AI 주석
(ja/ko revised 2026-07-19 after a native-corpus audit: in Japanese and
Korean ebook UIs メモ/노트 mean the user's own notes, while 注釈/주석 are
the native words for provided explanatory notes).
Prompts, CLI messages and docs follow. Internal identifiers (
annotate
module, etc-note class, CLI subcommand/flags) and the in-book 「〔註〕」
marker are unchanged. The annotation cache salt bumped to
inkferry-anno-v4 (prompt texts changed + presets joined the recipe);
translation caches are untouched.
- Repository split: non-engine code moved out. This repository is the
MIT-licensed engine (
et-core) + CLI (inkferry), free forever, and is
the single upstream any embedding host consumes as a pinned dependency.
- Hostile EPUB entry names can no longer ride into the output. Reading was
always safe (entries are held in memory, never extracted), but the writer
rebuilt the archive from the same names, so an entry called
../../../../tmp/x survived translation verbatim. The translated book was
therefore a zip-slip payload carried by a tool the reader trusts, and the
next program to unpack it took the hit. Absolute and ..-bearing names are
now dropped when the book is read; OCF forbids them anyway, so nothing
legitimate is affected.
- The input file is size-capped before it is read. Every other guard
describes decompressed bytes and so could not fire until the file was
already resident in memory. A large file with an
.epub name — not even a
valid archive — was a memory spike before the parser had an opinion.
- Provider error bodies are scrubbed of credentials.
--base-url points at
whatever OpenAI-compatible gateway you choose, and a gateway that echoes
request headers in an error would put your API key into stderr and, over
MCP, into an agent's context. Key-shaped tokens and bearer headers are
redacted before the body reaches an error message.
- A remote
base_url must use HTTPS. The rule existed and was tested but
nothing called it; --base-url http://… would have sent your key in clear
text. It is now enforced where every caller funnels through.
- The job cache is created owner-only (
0600) on Unix. It stores
translated passages and margin notes as plaintext and previously inherited
the ambient umask, which on a shared machine can mean world-readable.
- The subscription sidecar refuses to bind anywhere but loopback. It was a
default rather than a guarantee:
LLM_SUB_KIT_HOST could move it onto the
network, and requests without an Origin were trusted on the strength of
the Host header, which any client can set to localhost. A non-loopback
bind is now fatal at startup, and peer identity is read from the socket.
修復 · Fixed
translatus exits non-zero when segments fail. A run that failed every
segment still printed done: and exited 0, so a script or CI job checking
only the exit code read a total failure as success. The output file and the
resumable cache are still written, and the --json summary is unchanged.
--provider anthropic is refused instead of silently failing. It built
an OpenAI client pointed at api.anthropic.com, which speaks
POST /v1/messages with an x-api-key header rather than
/chat/completions with a bearer token, so every request failed after the
docs had promised it would work. It now fails immediately with a message
pointing at the two routes that do work: an OpenAI-compatible gateway via
--provider openai --base-url, or the subscription sidecar for Claude.
- MCP progress and results no longer echo book-controlled filenames, provider
diagnostics, subprocess output, or model-authored text into the calling
agent's context; only fixed status and numeric metadata cross that boundary.
- Cost estimates read
$0.00 for Claude Opus, Fable and Mythos models,
which were never priced in the estimator. All Claude tiers are now priced
(verified against the official pages 2026-07-21) and covered by a test that
fails if any model the UI offers falls through to zero.
開源之前的版本紀錄保留在 repo 的 CHANGELOG.md 末段。