Blazing-fast batch subtitle translation for 120+ languages — powered by AI
English · 简体中文
Paste a subtitle file into a general-purpose translator and two things go wrong: the model rewrites your timecodes, and you're doing it one file at a time. Subtitle Translator strips the timing out locally and sends only the dialogue to the engine — the timeline physically cannot be touched — then does a whole season in one drop.
Subtitle Translator is a free, browser-based batch subtitle translation tool for .srt, .ass, .vtt, and .lrc files. With chunked compression and parallel processing it hits ~1 second per episode. Batch-upload a whole season at once, connect to 8 traditional translation APIs (DeepL, Google, Azure, DeepLX, Qwen-MT, TranslateGemma, GTX, Edge) or 27 LLM providers and gateways, and translate into 120+ languages — or into several target languages in a single pass, each exported as its own file. Everything runs locally in your browser; subtitle content and API keys never touch a server. A CLI drives the same engine headlessly when you'd rather script it.
👉 Try it online: https://tools.newzone.top/en/subtitle-translator
- Real-Time Translation: Chunked compression + parallel processing → ~1 second per episode (GTX is slightly slower).
- Batch Processing: Drop hundreds of subtitle files at once (a whole season in one go); each file translates and downloads independently with its original filename, and you get an aggregated success/failure summary (e.g. "Exported (3/5)") when the run finishes.
- Multi-Language Output: Translate into multiple target languages in a single pass — each language is exported as its own file with the language code appended (e.g.
movie.zh.srt,movie.fr.srt). - Format Compatibility: Auto-detects
.srt,.ass,.vtt, and.lrc. WebVTT NOTE / STYLE / REGION non-cue blocks are correctly skipped (not translated as dialogue). One-click format conversion (SRT ↔ VTT, SRT/VTT → ASS) during translation. - Bilingual Output: Insert the translation above or below the original; alignment preserved across formats. For SRT / VTT sources you can also export ASS with separate styles for original and translation (Default 70pt white + Secondary 55pt cyan), tweakable in any subtitle editor.
- Context-Aware Translation (LLM only): Sends surrounding lines as context for more coherent dialogue and consistent character voice.
- Structural Separation: Timecodes, cue numbers, ASS headers, and VTT cue IDs are extracted locally — only dialogue text is sent to the engine, so the model can never disrupt your timeline.
- Subtitle Extraction: Strip cues / timing and export clean text (auto-copied to clipboard) for AI summarization, scripts, or content repurposing.
- Unlimited Caching (IndexedDB): All translations cached locally with no browser-storage size limit; refreshing the page doesn't lose translated files.
- 120+ Languages: Translate to/from 120+ languages, with source defaulting to Auto-detect.
- Multi-Locale UI: Powered by next-intl, with full UI translation across 18 languages.
- Command Line:
yarn cliruns the same engine, parsers, and cache from a terminal — see Command Line. - Private by Design: Fully client-side — subtitle content and API keys stay in your browser; LLM requests go directly from your browser to the API endpoint you configure.
Supports 8 traditional MT APIs and 27 LLM providers and gateways:
| API | Quality | Stability | Free Tier |
|---|---|---|---|
| DeepL | ★★★★★ | ★★★★☆ | 500K chars/month |
| Google Translate | ★★★★☆ | ★★★★★ | 500K chars/month |
| Azure Translate | ★★★★☆ | ★★★★★ | 2M chars/month (first 12 months) |
| DeepLX (Free) | ★★★★☆ | ★★★☆☆ | Self-host or free public endpoints |
| Qwen-MT | ★★★★☆ | ★★★★☆ | Alibaba DashScope quota |
| TranslateGemma | ★★★★☆ | ★★★★☆ | Self-host (LM Studio / Ollama / etc.) |
| GTX API (Free) | ★★★☆☆ | ★★★☆☆ | Free (rate-limited) |
| Edge API (Free) | ★★★★☆ | ★★★☆☆ | Free (rate-limited) |
GTX and Edge need no configuration at all — they are the zero-setup defaults, and each is the other's fallback.
DeepSeek, OpenAI, Claude, Gemini, Qwen, Moonshot (Kimi), Doubao, Xiaomi MiMo, Zhipu GLM, MiniMax, Baidu ERNIE, Tencent Hunyuan, Mistral, xAI (Grok), Perplexity, Cohere, and YandexGPT.
OpenRouter, OpenCode Zen, Groq, SiliconFlow, Atlas Cloud, GitHub Models, Nvidia NIM, Azure OpenAI, LiteLLM, plus any Custom (OpenAI-compatible) endpoint (Ollama / LM Studio / vLLM / Together AI / Fireworks AI etc.).
Providers walled off from browsers by CORS can be routed through an API relay. The built-in relay works out of the box; API Settings → Relay address points every relayed provider at your own deployment of the relay Worker instead.
LLM modes give you:
- Best for: literary works, technical talks, multilingual dialogue
- Customization: configure system / user prompts for a specific translation style
- Temperature Control: adjust AI creativity (0–1 scale)
- Thinking Mode: per-provider toggle for reasoning-capable models
- Extra Request Body: send any JSON the provider accepts — the escape hatch for per-vendor thinking switches this app doesn'''t model yet
LLM modes can send surrounding lines as context for each batch, improving dialogue coherence and character-voice consistency.
- Concurrent Lines: max lines translated in parallel (default 20). Too high triggers rate limits.
- Context Lines: lines included per batch as context (default 50). Higher = better coherence but more tokens.
| Format | Auto-detect | Bilingual | Notes |
|---|---|---|---|
| .srt | ✅ | ✅ | 1–3 digit milliseconds, 100+ hour timestamps |
| .ass | ✅ | ✅ | Line-leading position tags (e.g. \an8) auto-restored; complex inline effect tags simplified |
| .vtt | ✅ | ✅ | NOTE / STYLE / REGION blocks correctly skipped; inline <c.classname> and karaoke timestamps handled on VTT→SRT |
| .lrc | ✅ | ✅ | Karaoke lines with multiple time tags handled correctly |
- Automatic Encoding Detection: jschardet auto-detects UTF-8 / UTF-16 / GBK / Shift-JIS, avoiding garbled output (falls back to UTF-8 if detection fails).
- Filename Preservation: Exported files inherit the original name; multi-language output appends a language code suffix.
- Format Conversion: Convert SRT ↔ VTT and SRT/VTT → ASS during translation — no separate converter needed (identical source/target languages are blocked, so conversion requires a translation pass).
- Batch Mode (default): drop hundreds of files (a whole season) at once; each file translates independently and auto-downloads, with an aggregated success/failure summary.
- Single-File Mode: instant preview; uploading a new file replaces the current one.
Which formats are supported? SRT, ASS, VTT, and LRC. SRT/VTT suit YouTube and HTML5 players; ASS suits Aegisub / anime fansubs (position tags like \an8 auto-restored); LRC suits music lyrics.
Machine translation or LLM? Machine translation (Google, DeepL, Azure, Qwen-MT) is cheap or free but reads flat. LLMs bill per token but produce far more natural dialogue — DeepSeek is the value pick for whole-season batches, Claude Sonnet / GPT give the most natural dialogue, and Gemini's large context handles book-length subtitles.
How do I keep names and proper nouns consistent? Add a glossary in the System Prompt (e.g. "Keep verbatim: iPhone, OpenAI, John Smith") on any LLM engine; all episodes share the same context, so terminology stays consistent across a season.
Do I need "preserve timecodes / line numbers" prompts? No. Timecodes, cue numbers, and headers are extracted locally and re-inserted after translation — the model never sees them. Keep your prompt focused on style, glossary, and tone.
My model thinks by default and translation is slow — how do I turn thinking off? Set Thinking Mode to Off in API Settings when the provider is supported. For a model or gateway we don'''t model yet, use Extra request body (JSON) and send the vendor'''s own switch verbatim — e.g. {"enable_thinking": false}, {"chat_template_kwargs": {"thinking": false}}, or {"reasoning": {"enabled": false}} (OpenRouter). Whatever you put there is merged into the request last, so it overrides the built-in parameters. Check your provider'''s docs for the exact parameter name.
Is it private? Yes. Everything runs client-side: subtitle parsing, translation requests, and caching all happen in your browser. API keys are stored only in local browser storage, and LLM requests go directly from your browser to your configured endpoint.
See the full FAQ in the docs for more.
yarn cli translates files headlessly over the same engine as the web app — same parsers, same retry and rate-limit handling, same cache keys. Configure the service once in the browser, hit Export settings, and hand the JSON to the CLI: keys, prompts, glossary and retry settings all carry over.
-i takes a directory as well as a file, and scans it recursively. Every translation lands next to the subtitle it came from, so a whole season keeps its folder structure:
yarn install # once
# A whole season, translated in place: season/s01/e01.srt -> season/s01/e01.zh.srt
yarn cli -i ./season -t zh
# Driven by your exported web settings; two targets + bilingual in one pass.
yarn cli -i ./season -t zh -t ja --bilingual -s ~/subtitle-settings.json
# A local model — nothing leaves the machine.
yarn cli -i ./season -t zh -m llm --url http://localhost:11434/v1 --model qwen3The scan skips dot-prefixed files and directories (.git, .DS_Store), so pointing it at a project root is safe.
The whole pipeline is built to be interrupted and resumed:
- Each file is written to disk the moment it finishes.
Ctrl-Chalfway through leaves the completed episodes on disk — you never lose work you already paid for. - Re-running skips whatever is already done, via two gates:
- the output already exists (
movie.zh.srtsits next tomovie.srt) → the file is skipped, logged asskipped (already translated); - files whose names say they are translations (
movie.zh.srt,movie.zh_bilingual.ass) are never taken as input, somovie.zh.zh.srtcan't happen.
- the output already exists (
- A line-level cache (
~/.translate-cli-cache.json) covers the rest: if one file died mid-way, a re-run only pays for the missing lines.
So the normal workflow is: just run it. Stop whenever you like, run it again later.
To deliberately redo finished work, use --overwrite:
# After switching models or editing the glossary, redo one episode.
yarn cli -i ./season/s01e01.srt -t zh --overwritePoint
--overwriteat a file, not a whole folder. Against a folder, the existing*.zh.srtfiles are themselves inputs, and the "output would overwrite an input" guard refuses to clobber them (that guard is deliberate).
Output lands beside the input (or in -o <dir>) as movie.zh.srt. Bilingual runs add _bilingual, and for .srt / .vtt sources they default to ASS with separate styles per line — movie.zh_bilingual.ass. Pass --bilingual-format srt to stay in SRT.
| Option | Meaning |
|---|---|
-i, --input <file|dir> |
Input file or directory (scanned recursively). Repeatable. |
--overwrite |
Redo work that already looks done (an existing <stem>.<lang>.<ext>, or a name that says it is a translation). Off by default. |
-t, --to <lang> |
Target language. Repeatable. Default zh. |
-f, --from <lang> |
Source language. Default auto. |
-m, --method <id> |
Service id. Default gtxFreeAPI; --list-methods prints them all. |
-s, --settings <file> |
Settings JSON exported from the web UI (keys, prompts, glossary, retry…). |
-o, --out-dir <dir> |
Output directory. Default: next to each input. |
--api-key · --url · --model |
One-off overrides for the chosen service. |
--bilingual · --original-first · --bilingual-format <ass|srt> |
Bilingual output. |
--no-context |
Turn off context-aware LLM batching (on by default for subtitles). |
--no-cache · --cache-file <file> |
Cache control. Default ~/.translate-cli-cache.json. |
--relay · --no-relay |
Route through the API relay. Off by default — Node has no CORS to work around. |
--format <fmt> |
Force a format instead of inferring it from the extension. |
Subtitles are not the only input: the same command handles Markdown (.md, .markdown, .mdx — code blocks, links and LaTeX protected by default) and JSON locale files (.json — keys untouched, values translated). yarn cli --list-formats prints the mapping, yarn cli --help the full option list including the Markdown-specific flags.
Exit codes: 0 everything translated (including "there was nothing left to do") · 1 finished but some lines soft-failed (kept as source text in the output) or a file failed · 2 bad invocation · 130 cancelled.
From the project directory in PowerShell:
yarn install # once
yarn cli -i "D:\anime\Season 1" -t zh- Quote paths with spaces or non-ASCII characters (
-i "D:\My Season 1"), or PowerShell splits them into several arguments. - Export your settings from the web UI once: configure the service (key, prompts, glossary) in the browser, hit Export settings, save it as
settings.json, then pass-s D:\path\settings.jsonon every run. - Long jobs are safe to stop:
Ctrl-Cmid-season keeps what is finished, and the next identical command picks up where it left off. - No key needed to try it: omit
-mand the free GTX endpoint is the default (rate-limited); adding-sroutes through the service you configured instead.
Node.js >= 20.9.0 and Yarn (or npm / pnpm).
git clone https://github.com/Ray4AI/subtitle-translator.git
cd subtitle-translator
yarn install
yarn dev # http://localhost:3000
yarn build # production build
yarn test # unit + CLI end-to-end tests (vitest)yarn test covers the engine-adjacent logic, including a real end-to-end run of
scripts/cli.ts against a temporary subtitle tree — it asserts that translations
land beside their source files, that re-running is a no-op, and that an existing
translation is never overwritten. It talks to the free GTX endpoint once per run
and then serves everything from cache.
docker run -d -p 3000:3000 --name subtitle-translator ghcr.io/ray4ai/subtitle-translator:mainOr with the bundled docker-compose.yml (edit the image tag to pin a version):
docker compose up -d # start
docker compose pull && docker compose up -d # update to the latest build
docker compose logs -f # follow logsImages are multi-arch (linux/amd64 + linux/arm64) and published to GHCR on
every push to main (:main), on every v* tag (:<version>, :latest).
For detailed configuration, API setup, and self-hosting instructions, see the Official Documentation.
Quick Deployment: Deploy Guide
Contributions are welcome! Feel free to open issues and pull requests.
- Fork the repo and create a feature branch
- Run
yarnandyarn devlocally - Add tests / docs when applicable
- Submit a PR with a clear description
