Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ass-translator-it

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.md for the phase tracker.


Why this exists

Most subtitle translators do one of two things badly:

  1. They translate dialogue but mangle override tags ({\an8}, {\i1}, {\fad(...)}) — the layout falls apart on the next render.
  2. 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.

Features

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

Install

Requires Python 3.11+ and uv.

git clone <repo-url> ass-translator-it
cd ass-translator-it
uv sync --extra dev

On Windows / OneDrive paths, hardlinks may fail. Use: UV_LINK_MODE=copy uv sync --extra dev

Configure

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.

Usage (target — finalised in Phase 3)

# 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 --agentic

Run the tests

uv run pytest

Tests use synthetic fixtures only — never real subtitle files. This is a hard project rule (see "Legal").

Glossary format

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

How it works (short version)

  1. Parse the .ass file with pysubs2, extract Dialogue events into a typed DialogueLine list (Comments and section metadata round-trip untouched).
  2. Batch lines (default 25) and identify which glossary terms appear in each batch.
  3. Translate with a system prompt that enforces: 1:1 line correspondence, 42 char/row max (use \N for soft wrap), preserve honorifics (-san, -kun, -chan, -sama), apply glossary verbatim, do not translate marked proper nouns or attack names.
  4. Validate line count exactly. Mismatch → one stricter retry → on second failure, raise TranslationCountMismatch and preserve the English original for those lines.
  5. (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.
  6. Write the output .ass with only Dialogue text replaced — every other byte preserved.

Deeper design docs:

  • docs/SUBTITLE_FORMAT.md.ass quirks, why pysubs2, what we preserve byte-stable.
  • docs/GLOSSARY_DESIGN.md — Phase 2.
  • docs/AGENT_DESIGN.md — Phase 5 (loop diagram, tool specs, cost model).

Project layout

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

Limitations

  • Subtitle format: .ass only. .srt / .vtt are tracked in TODO.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.md once Phase 5 lands.

Legal

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.

Contributing

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.

License

MIT. See LICENSE (added with Phase 4 release prep).

About

Agentic CLI translator for .ass subtitle files (English -> Italian) with configurable glossary, built on the Claude API.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages