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:

  1. 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.
  2. Cue numbers. When cues are merged or lost, the numbering drifts.
  3. Tags and symbols. <i>, <b>, music symbols, markup in curly braces.
  4. 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

  1. Parse the file with a program into cues: number, time, text.
  2. Send the model only the texts, under service keys (0, 1, 2, ...) rather than SRT numbers.
  3. Ask it to return JSON with the same keys.
  4. 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.

bash
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_srt splits the file into cues and handles files with a BOM (utf-8-sig) and Windows line endings;
  • ask sends a block as JSON, and translate_chunk checks that exactly the same keys came back and that no values are empty; otherwise the block is resent (up to three attempts);
  • fit_lines wraps 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:

  1. The cue count in the source and translated files matches.
  2. The file opens in a player or subtitle editor (VLC, Aegisub and the like) without errors.
  3. <i> tags in the translation are paired: the number of opening tags equals the number of closing ones.
  4. No Latin text where there should be none: leftover English phrases often mean a skipped cue.
  5. 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):

ModelInput / outputIndex
deepseek-v4.1-flash0.013 / 0.03839
glm-5.3-flash0.04 / 0.0442
gpt-6.1-sol0.27 / 1.3252
claude-sonnet-5-50.4 / 248

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.

Get API keyDocumentation

api.zukko.pro · fetch_*

Neural network for subtitle translation: SRT · Zukko