Translating subtitles with a neural network: SRT without broken timecodes
The main risk when translating SRT with a neural network is a broken file structure. A scheme where timecodes never reach the model, and a Python script with validation.
6 min · · автоматизация · development
If you give a whole SRT file to a chat and ask to "translate it", the result often looks plausible but fails inspection: cues are missing, numbers drift, neighboring phrases merge, and sometimes the timecodes themselves change. It is more reliable not to ask the model to preserve the structure but not to show it the structure at all. Below is a scheme for using a neural network for subtitle translation so that numbers and timings stay untouched, plus a Python script and a cost calculation for a film.
How SRT is built and what is fragile in it
The file consists of blocks separated by an empty line:
12
00:01:05,200 --> 00:01:08,000
<i>I told you not to come here.</i>
Why did you?
A number, a time line, and one or more lines of text. Only the text needs translating; the model must not translate anything else. There are four fragile spots:
- Timecodes. The format is
HH:MM:SS,mmm --> HH:MM:SS,mmm, with a comma before the milliseconds. The model may "fix" it to a dot or change digits. - Cue numbers. When cues are merged or lost, the numbering drifts.
- Tags and symbols.
<i>,<b>, music symbols, markup in curly braces. - Line splitting. A Russian translation, as a rule, is longer than the English original and may not fit into two lines of 42 characters.
The scheme: the model sees only text
- Parse the file with a program into cues: number, time, text.
- Send the model only the texts, under service keys (0, 1, 2, ...) rather than SRT numbers.
- Ask it to return JSON with the same keys.
- Check that the keys match, then put the translation back under the original numbers and timecodes.
This way timecodes physically cannot change: they never leave your program. What remains is checking the cue count and line length.
Blocks and context
Subtitle cues break off mid-sentence, and translating cue by cue loses meaning. But sending the entire file in one request is risky too: the answer may be cut off by a limit, and an error in one place forces you to translate everything again. A workable middle ground:
- a block of 30-50 cues per request;
- 2-3 previous cues added to the request as context marked "do not translate";
- on a wrong answer, only that block is repeated.
It helps to add the film's names and terms to the request ("keep Mr. Holmes as Мистер Холмс") so they are translated identically in all blocks. For a long video you can extend the context with a short description: genre, the plot, who is speaking.
The Python script
The script uses the OpenAI SDK and the Zukko gateway: address https://api.zukko.pro/v1, key in the ZUKKO_API_KEY variable. More on connecting is in the Python documentation.
import json
import os
import re
import textwrap
from openai import OpenAI
client = OpenAI(base_url="https://api.zukko.pro/v1", api_key=os.environ["ZUKKO_API_KEY"])
MODEL = "deepseek-v4.1-flash"
CHUNK = 40 # cues per request
CONTEXT = 3 # previous cues shown as context
TIME_RE = re.compile(r"^\d{2}:\d{2}:\d{2}[,.]\d{3} --> \d{2}:\d{2}:\d{2}[,.]\d{3}")
def parse_srt(text):What the code does:
parse_srtsplits the file into cues and handles files with a BOM (utf-8-sig) and Windows line endings;asksends a block as JSON, andtranslate_chunkchecks that exactly the same keys came back and that no values are empty; otherwise the block is resent (up to three attempts);fit_lineswraps overly long lines so that there are at most two lines of 42 characters; if that fails, the cue number goes into a list for manual editing;- the output file gets the original numbers and timecodes unchanged.
The script skips cues without text. If your file has such cues, handle them separately. Tune the parameters (block size, line width) to your subtitles.
Line length and reading speed
For subtitles, two lines of 42 characters is the most common target. It is a widespread convention, not a requirement of the SRT format: your player or platform may have different rules. Reading speed matters even more: if a cue lasts one second and the translation takes two lines, the viewer will not manage to read it. You can ask the network to shorten the translation ("compress to the meaning if the cue is shorter than two seconds"), but the cue duration must be passed to the model explicitly. For that you can add the duration in seconds, computed by you from the timecodes, to the JSON. Just do not ask the model to edit the timecodes themselves.
Checking the result
After the run, do automatic checks and a few manual ones:
- The cue count in the source and translated files matches.
- The file opens in a player or subtitle editor (VLC, Aegisub and the like) without errors.
<i>tags in the translation are paired: the number of opening tags equals the number of closing ones.- No Latin text where there should be none: leftover English phrases often mean a skipped cue.
- A manual sample: 10-20 cues from different places in the film, especially slang, jokes and cues addressing someone as informal or formal "you".
Which model and what it costs
Translation usually does not need the strongest model. Try cheap ones and compare on an excerpt of 100-200 cues. From the table as of October 18, 2026 (Zukko, $ per 1M tokens, input / output, intelligence index):
| Model | Input / output | Index |
|---|---|---|
deepseek-v4.1-flash | 0.013 / 0.038 | 39 |
glm-5.3-flash | 0.04 / 0.04 | 42 |
gpt-6.1-sol | 0.27 / 1.32 | 52 |
claude-sonnet-5-5 | 0.4 / 2 | 48 |
A calculation for a film of 1,500 cues. Assumptions (check them on your file via the usage field): 60,000 input tokens including instructions and context and 50,000 output tokens, because Russian text tokenizes longer than English.
Formula: input × input price + output × output price.
deepseek-v4.1-flash: 0.06 × 0.013 + 0.05 × 0.038 = 0.00078 + 0.0019 = $0.00268.glm-5.3-flash: 0.06 × 0.04 + 0.05 × 0.04 = 0.0024 + 0.002 = $0.0044.gpt-6.1-sol: 0.06 × 0.27 + 0.05 × 1.32 = 0.0162 + 0.066 = $0.0822.claude-sonnet-5-5: 0.06 × 0.4 + 0.05 × 2 = 0.024 + 0.1 = $0.124.
(Here 0.06 and 0.05 are millions of tokens.) A whole film costs from a fraction of a cent to about 12 cents, depending on the model. Many people look for a free neural network for translation; Zukko has no free tier and payment is per token from a balance starting at $1, but by the calculation above translating a film costs well under a dollar. You can pay via SBP (the payment page adds an 8.5% fee on top), cryptocurrency or a MasterCard card.
If the cheap model's quality does not satisfy you (humor, idioms, a lot of slang), move up the table and compare on the same excerpt.
Practical tips
- Keep the original and work on a copy.
- Keep a glossary of names and terms at the start of every request.
- For a bilingual result you can leave the English text as the first line in the file, but that is a separate task.
- Other formats (VTT, ASS) follow the same scheme, but file parsing differs. ASS has a more complex structure, and the texts are better extracted with a dedicated library.
- Do not publish translations of other people's films without rights: that is a matter for the rights holder, not for technology.
To run it, create a key in the Zukko dashboard. For large series it helps to set a daily limit on the key: details are in How to set a daily and monthly limit on an API key. If you need to translate many files, see Translating texts via API and Translating i18n files.
FAQ
Can I send the SRT to the model whole?
For short files you can, but the structure may suffer. Parsing in a program is more reliable: the model gets only the texts, and numbers and timecodes stay with you.
Why is the translation longer than the original?
Russian text is usually longer than English. That is why shortening and line wrapping are needed, and for short cues a reading speed check too.
Does the method work for translating from a language other than English?
Yes. Only the instruction in the request changes: languages, names and rules. The scheme with keys and validation stays the same.
How much does translating one film cost?
It depends on the model and volume. In the calculation above for 1,500 cues it ranges from a fraction of a cent on deepseek-v4.1-flash to about 12 cents on claude-sonnet-5-5. Prices as of October 18, 2026; take exact figures from the usage field of the responses.
zukko
Pay 25×
less
for Claude Code & ChatGPT
One fetch_* key and base URL api.zukko.pro — for Cursor, Claude Code, Codex, and any SDK.
Related articles
- Editing texts with a neural network: proofreading, style and cuttingDifferent kinds of editing need different prompts. Wording for proofreading, style and cutting, how to get edits with explanations and how not to lose the author's voice.
- A neural network for writing scripts: Bash, PowerShell and PythonA model writes scripts for routine fast, but running them blindly is risky. How to state the task, demand a safe dry-run mode and check the result before launch.
- Dify in Docker: installation, first-run setup and connecting modelsInstall Dify with Docker Compose, create the admin account and connect Claude, GPT and DeepSeek models on one key. Plus what to do when containers will not start.
- Regular expressions with AI: how to get a working regex and test itA model writes regex fast but errs confidently on edge cases. A request with examples, an auto-check loop and protection against hangs.