Agentic CLI translator for .ass subtitle files (English → Italian) with a
configurable terminology glossary, built on the Claude API.
The first use case is anime fansub terminology (e.g. One Piece), but the tool is format-agnostic and language-pair-agnostic: glossary, language pair, and domain are data, not hardcoded logic.
Project status: Phase 1 of 5 complete. Parser + scaffolding + docs land first; deterministic translator, CLI, and self-correcting agent loop follow in subsequent phases. See
CLAUDE.mdfor the phase tracker.
Most subtitle translators do one of two things badly:
- They translate dialogue but mangle override tags (
{\an8},{\i1},{\fad(...)}) — the layout falls apart on the next render. - They translate proper nouns and franchise terminology inconsistently between episodes — "Devil Fruit" becomes "Frutto del Diavolo" in episode 12 and "Frutta del Demonio" in episode 13.
This project addresses both:
- Tag-stable I/O. Override tags, timing, styles, speaker names, and
comments are byte-stable through the pipeline — only dialogue text is
rewritten. (See
docs/SUBTITLE_FORMAT.md.) - Glossary as data. A YAML file maps source terms → target terms (or marks them as do-not-translate). The same glossary applies across an entire series, so terminology stays consistent.
- Agentic self-correction (Phase 5). Beyond a deterministic first pass,
an opt-in agent loop uses tool calls (
get_context,lookup_glossary,get_speaker,flag_for_review, …) to re-translate low-confidence lines with extra context.
| Feature | Status |
|---|---|
.ass parser preserving override tags, comments, styles, timing |
✅ Phase 1 |
| Synthetic-fixture test suite | ✅ Phase 1 |
| YAML glossary loader + relevant-term extraction | ⏳ Phase 2 |
| Deterministic batched translator with 1:1 line-count enforcement, retry/backoff | ⏳ Phase 2 |
ass-translate CLI with --dry-run, --verbose, --batch-size |
⏳ Phase 3 |
| Worked One Piece glossary example + sample subtitles | ⏳ Phase 4 |
--agentic self-correcting agent loop with tool use |
⏳ Phase 5 |
.srt, .vtt formats |
🗒️ TODO |
| Multi-provider (DeepSeek, …) | 🗒️ TODO |
| Tavily web-search tool for agent | 🗒️ TODO |
Requires Python 3.11+ and uv.
git clone <repo-url> ass-translator-it
cd ass-translator-it
uv sync --extra devOn Windows / OneDrive paths, hardlinks may fail. Use:
UV_LINK_MODE=copy uv sync --extra dev
Copy the env template and fill in your keys:
cp .env.example .env
# edit .env — at minimum set ANTHROPIC_API_KEY| Variable | Purpose | Required? |
|---|---|---|
ANTHROPIC_API_KEY |
Primary provider; needed for Phase 2 onward, and required for Phase 5 (tool use). | yes (Phase 2+) |
DEEPSEEK_API_KEY |
Alternative provider, kept for cost/benchmark experiments. Multi-provider abstraction is on the TODO. | no |
TAVILY_API_KEY |
Web-search tool for the agent loop (Phase 5+). | no |
.env is gitignored. Never commit it.
# Deterministic translate
uv run ass-translate input.ass output.ass \
--glossary examples/glossary_one_piece.yml
# Print first batch's prompt without calling the API
uv run ass-translate input.ass output.ass \
--glossary examples/glossary_one_piece.yml --dry-run
# Self-correcting agent (Phase 5, opt-in)
uv run ass-translate input.ass output.ass \
--glossary examples/glossary_one_piece.yml --agenticuv run pytestTests use synthetic fixtures only — never real subtitle files. This is a hard project rule (see "Legal").
YAML, one entry per line: Source Term: "Target Term". Use null (or an
explicit marker convention defined in docs/GLOSSARY_DESIGN.md)
to mark a term as do-not-translate (proper nouns like ship names).
# examples/glossary_one_piece.yml (added in Phase 4)
Straw Hat Pirates: "Pirati di Cappello di Paglia"
Devil Fruit: "Frutto del Diavolo"
Haki: "Ambizione"
Yonko: "Imperatore"
Marines: "Marina"
Going Merry: null # proper noun: do not translate
Thousand Sunny: null # proper noun: do not translate- Parse the
.assfile withpysubs2, extract Dialogue events into a typedDialogueLinelist (Comments and section metadata round-trip untouched). - Batch lines (default 25) and identify which glossary terms appear in each batch.
- Translate with a system prompt that enforces: 1:1 line correspondence,
42 char/row max (use
\Nfor soft wrap), preserve honorifics (-san,-kun,-chan,-sama), apply glossary verbatim, do not translate marked proper nouns or attack names. - Validate line count exactly. Mismatch → one stricter retry → on second
failure, raise
TranslationCountMismatchand preserve the English original for those lines. - (Phase 5) Self-review. Agent rates confidence per line; for low-
confidence lines it calls tools (
get_context,get_speaker,lookup_glossary, …) and re-translates. - Write the output
.asswith only Dialogue text replaced — every other byte preserved.
Deeper design docs:
docs/SUBTITLE_FORMAT.md—.assquirks, whypysubs2, what we preserve byte-stable.docs/GLOSSARY_DESIGN.md— Phase 2.docs/AGENT_DESIGN.md— Phase 5 (loop diagram, tool specs, cost model).
ass-translator-it/
├── pyproject.toml
├── README.md, CLAUDE.md, TODO.md
├── .env.example
├── src/ass_translator/
│ ├── parser.py ✅ Phase 1
│ ├── glossary.py ⏳ Phase 2
│ ├── translator.py ⏳ Phase 2
│ ├── cli.py ⏳ Phase 3
│ └── agent.py ⏳ Phase 5
├── tests/
│ ├── fixtures/synthetic.ass
│ └── test_*.py
├── docs/
│ ├── SUBTITLE_FORMAT.md ✅
│ ├── GLOSSARY_DESIGN.md ⏳
│ └── AGENT_DESIGN.md ⏳
└── examples/ ⏳ Phase 4
├── glossary_one_piece.yml
├── sample_input.ass
└── sample_output.ass
- Subtitle format:
.assonly..srt/.vttare tracked inTODO.md. - Language pair: the prompt currently targets ENG → IT. Bidirectional
/ arbitrary pairs are tracked in
TODO.md. - No multimodal context: the translator sees text only. Video frame / audio context is on the roadmap but explicitly out of scope for v1.
- Cost: the agent loop (Phase 5) calls extra tools per low-confidence
line; budget per 1000 lines documented in
docs/AGENT_DESIGN.mdonce Phase 5 lands.
This is a general-purpose .ass translator. The user supplies the input
file. No copyrighted subtitle content ships with this repository: tests and
examples use synthetic fixtures only. Real-world fan-translated content (e.g.
One Pace) must not be added to tests/, examples/, or any committed
artefact — this is enforced by the project's CI rule and by TODO.md's
contributor guidance.
The "One Piece" terminology in examples/glossary_one_piece.yml (Phase 4) is
a list of public franchise terms used as a translation reference, not
copyrighted text.
Read CLAUDE.md first — it's the canonical onboarding doc and
includes coding conventions, the phase plan, and the module map. Then
docs/ for the architectural decisions. New ideas go in
TODO.md.
MIT. See LICENSE (added with Phase 4 release prep).