
ChatGPT Subtitle Translator
CLITranslates SRT subtitles or plain text in batches through any OpenAI-compatible API, stripping timestamps and other overhead to save tokens, keeping output line-aligned with the source, and resuming interrupted jobs. Needs Node.js 20+, a source install and an OpenAI API key or local Ollama.
ChatGPT API SRT Subtitle Translator
Large language models (LLMs), such as ChatGPT, have demonstrated their capabilities as a robust translator, capable of handling common natural languages with contextual understanding, as well as unconventional forms of writing like word scrambling. However, it may not always produce a deterministic output or adhere to line-to-line correlation, potentially disrupting the timing of subtitles, even when instructed to follow precise instructions and with the model temperature parameter set to 0.
This utility uses the OpenAI ChatGPT API to translate text, with a specific focus on line-based translation, especially for SRT subtitles. The translator optimizes token usage by removing SRT overhead and grouping text into batches, resulting in arbitrary length translations without excessive token consumption while ensuring a one-to-one match between line input and output.
Upgrading from v2? See the v2 -> v3 Migration Guide for breaking changes.
Web Interface: https://cerlancism.github.io/chatgpt-subtitle-translator
Features
- Web User Interface (Web UI) and Command Line Interface (CLI)
- Supports Structured Output: for more concise results, enabled by default in the Web UI and CLI
- Supports Prompt Caching: by including the full context of translated data, the system instruction and translation context are packaged to work well with prompt caching, controlled with
-c, --context(CLI only) - Supports any OpenAI API compatible providers such as running Ollama locally
- Line-based batching: avoids token limit per request, reduces overhead token wastage, and maintains translation context to a certain extent
- Optional OpenAI Moderation tool check: prevents token wastage if the model is highly likely to refuse to translate, enabled with
--use-moderator(CLI only) - Streaming process output
- Request per minute (RPM) rate limits
- Progress resumption (CLI only)
Setup
Reference: https://github.com/openai/openai-quickstart-node#setup
- Node.js version
>= 20required. This README assumesbashshell environment - Clone this repository
git clone https://github.com/Cerlancism/chatgpt-subtitle-translator - Navigate into the directory
cd chatgpt-subtitle-translator - Install the requirements
npm install - Give executable permission
chmod +x cli/translator.mjs - Copy
.env.exampleto.envcp .env.example .env - Add your provider API key to the newly created
.envfile. For a non-OpenAI provider, setOPENAI_BASE_URLto its OpenAI-compatible/v1endpoint (see the commented examples in.env.example).
CLI
cli/translator.mjs --help
Usage: translator [options]
Translation tool based on ChatGPT API
Options:
-
--from <language>
Source language (default: "") -
--to <language>
Target language (default: "English") -
-s, --system-instruction <instruction>
Override the prompt system instruction templateTranslate ${from} to ${to}with this text, ignoring--fromand--tooptions -
-i, --input <file>
Input source text with the content of this file, in.srtformat or plain text -
-o, --output <file>
Output file name, defaults to be based on input file name -
-r, --structured <mode>Structured response format mode. (default:array, choices:array,object,timestamp,agent,none)arrayStructures the input and output into an array format.objectStructures the input and output as a keyed object.timestampProvides the model with entry timings alongside each entry's text (a batch start offset plus per-entry offset/length in milliseconds), allowing it to merge adjacent entries into one. A batch is only retried when the output time span boundaries don't match the input - unlike other modes which retry on any line count mismatch - significantly reducing token wastage from retries. Uses more tokens per batch due to timestamps in input and output. Output entry count may differ from input, so progress file resumption is not supported.agentAlias for theagentsubcommand with default options. Use the dedicated subcommand for full configuration. See Agent Mode.noneLegacy compatibility mode, disables structured output.
-
-c, --context <tokens>Include translation history up to a token budget to work well with prompt caching. Default:2000. Set to0to include history without a token limit check.Recommended value: set
<tokens>to ~50% of the model's max context length to leave room for the current batch and system prompts. For example, for a128Kcontext model:--context 64000. When--batch-sizesis omitted, the batch size is derived from this budget as well. -
-b, --batch-sizes <sizes>Batch sizes of increasing order for translation prompt slices in JSON ArrayThe number of lines to include in each translation prompt, provided that they are estimated to be within the token limit.
In case of mismatched output line quantities, this number will be decreased step by step according to the values in the array, ultimately reaching one.Larger batch sizes generally lead to more efficient token utilization and potentially better contextual translation.
However, mismatched output line quantities or exceeding the token limit will cause token wastage, requiring resubmission of the batch with a smaller batch size.When omitted, batch size is determined automatically per batch from the
--contexttoken budget: each batch is sized so that several batches of history fit in the portion of the context window freed by a trim, keeping the prompt cache warm in between. The history cost per input token is measured from the translated batches. The size is spread evenly across the remaining lines. On failure, the size is reduced and retried; once it reaches the minimum, translation falls back to single-entry mode. After a reduction, the size eases back up stepwise following consecutive successful batches. -
-g, --guard-repetition <threshold>Minimum number of pattern repeats before aborting a streaming response (default:10). When the model falls into a repetition loop during streaming, the response is aborted and retried with a smaller batch. Set to0to disable repetition detection. -
--initial-prompts <prompts>Initial prompts for the translation in JSON (default:"[]") -
--use-moderatorUse the OpenAI Moderation tool -
--moderation-model <model>(default:"omni-moderation-latest") https://developers.openai.com/api/docs/models -
--no-prefix-numberDon't prefix lines with numerical indices. Ignored in-r, --structuredarray|object|timestamp- prefix numbers are always disabled there. -
--no-line-matchingDon't enforce one-to-one line quantity input output matching. Ignored in-r, --structuredtimestamp- line matching is always disabled there since entries may be merged. -
-p, --plain-text <text>Input source text with this plain text argument. Not supported in-r, --structuredtimestampmode, or when using theagentsubcommand with-r timestamp. -
--no-streamDisable stream progress output to terminal (streaming is on by default) -
--log-level <level>
Log level (default:debug, choices:trace,debug,info,warn,error,silent) -
--silent
Same as--log-level silent -
--quietSame as--log-level silent
Additional Options for GPT: https://developers.openai.com/api/reference/resources/chat/subresources/completions/methods/create
-m, --model <model>(default:"gpt-4o-mini") https://developers.openai.com/api/docs/models--reasoning_effort <reasoning_effort>Constrains effort on reasoning for reasoning models. Accepted values depend on the model (e.g."low","medium","high"), follows the model's default when not set."none"disables reasoning/thinking entirely (supported by OpenAI o-series/GPT-5+ and open models via Ollama such as Qwen3).-t, --temperature <temperature>Sampling temperature to use, should set a low value such as0to be more deterministic for translation (default:0)--top_p <top_p>Nucleus sampling parameter, top_p probability mass--presence_penalty <presence_penalty>Penalty for new tokens based on their presence in the text so far--frequency_penalty <frequency_penalty>Penalty for new tokens based on their frequency in the text so far--logit_bias <logit_bias>Modify the likelihood of specified tokens appearing in the completion
Agent Mode
Subcommand for multi-pass agentic translation. Accepts all standard translation options.
cli/translator.mjs agent --help
Agent mode runs multiple passes before translating:
Overview - Samples the file to produce a content overview (file identity, duration, genre/tone, character names) and detects the source language.
Planning - Scans the file in token-bounded windows. Each window produces a batch summary (characters, locations, events, tone). Summaries are consolidated and used to generate a refined translation instruction.
Translation - Translates using the enriched instruction. After the first batch, a sample of the output is checked to confirm the target language before proceeding.
Structured mode defaults to array, pass --structured timestamp to use timestamp mode instead.
# Default (array delegate)
cli/translator.mjs agent --input subtitles.srt --from Japanese --to English
# Timestamp delegate
cli/translator.mjs agent --input subtitles.srt --structured timestamp --from Japanese --to English
--no-refine
Skip the final instruction refinement step at the end of the planning pass and use the original system instruction directly.--no-fitting
Skip LLM-based token-range fitting for planning summaries and consolidation. Summaries are used as-is regardless of token range.--context-summary <summary>
Provide a context summary directly, bypassing the planning pass entirely and proceeding straight to translation.
Examples
Plain text
cli/translator.mjs --plain-text "你好"
Standard Output
Hello.
Emojis
cli/translator.mjs --to "Emojis" --plain-text "Chuck Norris can walk with the animals, talk with the animals; grunt and squeak and squawk with the animals... and the animals, without fail, always say 'yessir Mr. Norris'."
Standard Output
👨🦰💪🚶♂️🦜🐒🐘🐅🐆🐎🐖🐄🐑🦏🐊🐢🐍🐿️🐇🐿️❗️🌳💬😲👉🤵👨🦰👊=🐕🐑🐐🦌🐘🦏🦍🦧🦓🐅🦌🦌🦌🐆🦍🐘🐘🐗🦓=👍🤵.
Scrambling
cli/translator.mjs --system-instruction "Scramble characters of words while only keeping the start and end letter" --no-prefix-number --no-line-matching --plain-text "Chuck Norris can walk with the animals, talk with the animals;"
Standard Output
Cuhck Nroris can wakl wtih the aiamnls, talk wtih the aiamnls;
Unscrambling
cli/translator.mjs --system-instruction "Unscramble characters back to English" --no-prefix-number --no-line-matching --plain-text "Cuhck Nroris can wakl wtih the aiamnls, talk wtih the aiamnls;"
Standard Output
Chuck Norris can walk with the animals, talk with the animals;
Plain text file
cli/translator.mjs --input test/data/test_cn.txt
Input file: test/data/test_cn.txt
你好。
拜拜!
Standard Output
Hello.
Goodbye!
SRT file
cli/translator.mjs --input test/data/test_ja_small.srt
Input file: test/data/test_ja_small.srt
1
00:00:00,000 --> 00:00:02,000
おはようございます。
2
00:00:02,000 --> 00:00:05,000
お元気ですか?
3
00:00:05,000 --> 00:00:07,000
はい、元気です。
4
00:00:08,000 --> 00:00:12,000
今日は天気がいいですね。
5
00:00:12,000 --> 00:00:16,000
はい、とてもいい天気です。
Output file: test/data/test_ja_small.srt.out_English.srt
1
00:00:00,000 --> 00:00:02,000
Good morning.
2
00:00:02,000 --> 00:00:05,000
How are you?
3
00:00:05,000 --> 00:00:07,000
Yes, I'm doing well.
4
00:00:08,000 --> 00:00:12,000
The weather is nice today, isn't it?
5
00:00:12,000 --> 00:00:16,000
Yes, it's very nice weather.
How it works
SRT indices and timestamps are stripped or simplified before sending to the model, reducing tokens. Lines are batched together into a single prompt - removing repeated per-entry overhead. The default system instruction is a minimal Translate to <language> (3 tokens). Structured output modes enforce a schema so the model returns only the translated text.
Five modes are available via --structured:
array (default)
Lines are sent as a JSON array. The model returns a matching array.
| Input (SRT) | Prompt (User Message) | Transform (Model Response) | Output (SRT) |
|---|---|---|---|
|
Tokens: |
Tokens: |
Tokens: |
Tokens: |
|
(compact JSON, formatted here for readability)
|
(compact JSON, formatted here for readability)
|
|
object
Source lines are used as keys in the response schema. The model maps each source key to its translation. No explicit user message is sent - the schema itself conveys the input.
| Input (SRT) | Prompt (Schema Keys) | Transform (Model Response) | Output (SRT) |
|---|---|---|---|
|
Tokens: |
Tokens: |
Tokens: |
Tokens: |
|
Source lines are encoded as response schema keys (no user message)
|
(compact JSON, formatted here for readability)
|
|
timestamp
Timestamps are preserved alongside the text. Lines are sent using the compact Toon format: each batch carries its absolute start time as a top-level offset, and each entry an offset (relative to the batch start) and length (duration) in milliseconds. This keeps the numbers small regardless of the batch's position in the file - absolute timestamps grow to 7 digits over a feature-length runtime. The model may merge subtitle entries when contextually appropriate.
| Input (SRT) | Prompt (User Message) | Transform (Model Response) | Output (SRT) |
|---|---|---|---|
|
Tokens: |
Tokens: |
Tokens: |
Tokens: |
|
(Toon format - compact, not JSON)
|
(compact JSON, formatted here for readability)
|
|
none
No structured output. Lines are sent as text and the model returns text.
| Input (SRT) | Prompt (User Message) | Transform (Model Response) | Output (SRT) |
|---|---|---|---|
|
Tokens: |
Tokens: |
Tokens: |
Tokens: |
|
|
|
|
Ressources associées

Subtitle Translator
CLIBatch-translates SRT, ASS, VTT and LRC subtitles, handling a whole season at once, with bilingual output and 120+ languages. The default free translation API needs no setup, or connect DeepL or various LLMs. The CLI runs from a cloned repo with yarn and needs Node.js 24+.
rockbenben/subtitle-translator1.2k
GEMBA
CLIMicrosoft Translator's translation quality tool: GPT scores each sentence using MQM error categories or a direct score; --list_mqm_errors shows error types and severity. Needs line-aligned source and target files, Python and an OpenAI or Azure OpenAI key (paid); a translator should still review.
MicrosoftTranslator/GEMBA156
FFsubsync
CLIAutomatically aligns subtitle timing to a video's audio track, fixing translated subtitles that drift out of sync, or syncs against an already aligned subtitle file. Runs locally with no account; install Python and FFmpeg first.
smacke/ffsubsync7.9k
Translate Books with LLMs
CLITranslates whole EPUB books, Word files, SRT subtitles and TXT with local Ollama or cloud LLMs (OpenAI, Gemini, DeepSeek), keeping layout and timings. Builds a glossary for consistent terms and can resume jobs. Mainly a desktop app; the CLI runs from source; cloud models need an API key.
hydropix/TranslateBooksWithLLMs2.5k
LLM-Subtrans
CLIUses an LLM to translate SRT, ASS and VTT subtitles into any language in batches, convert formats and transcribe audio or video. Install the CLI from source (Python 3.10+); needs an API key (e.g. OpenRouter) or a local model, and subtitles go to that provider.
machinewrapped/llm-subtrans660
Bilingual Book Maker
CLIUses LLMs to translate EPUB, TXT, Markdown, SRT, PDF and other files into side-by-side bilingual editions, via OpenAI, Anthropic, Gemini and other APIs or DeepL and Google Translate. LLM APIs need your own key or a Codex subscription. Only translate content you have rights to.
yihong0618/bilingual_book_maker9.9k