English
EN
← Research

Where to Break a Subtitle Is Geometry, Not Language

Subtitle line breaks look like a language problem. In production they are mostly geometry—fitting glyphs into a frame across Latin, CJK, Arabic, Indic, and spaceless scripts.

The earlier posts in this series focused on alignment—how the dubbing audio matches the visuals—and on meaning—how a translation is shaped beyond its generic default. This one is about space. When you add the subtitles, where should the line breaks go?

First, a distinction that is easy to blur: A translation turns the source into a dubbing text — a line the voice actor will read. A subtitle is not that line pasted onto a frame. It is a different object: timed, width-capped, often shortened, sometimes rewritten so it can be glanced at while the picture moves. Arabic Modern Standard makes the gap concrete. The dubbing text we produce is fully vocalized, so an AI reader can pronounce it correctly:

ثُمَّ هُنَاكَ خُطَّةٌ لِأَخْذِ الْأَطْفَالِ إِلَى هُنَا. أَوَّلاً لِلْمُشَاهَدَةِ، وَثَانِياً، لَقَدْ أَجْرَيْنَا قَبْلَ قَلِيلٍ فِي الْمَعْرِضِ، أَيْ قَبْلَ الْبَدْءِ بِالرَّسْمِ وَالنَّسْخِ، نِقَاشاً مُثْمِراً.

The subtitle that lands on screen is already a different string — one clause, stripped of the diacritics a voice needs but a viewer does not:

لقد أجرينا قبل قليل في المعرض، أي قبل البدء بالرسم والنسخ،

Same meaning, same language, not the same object. Translation owns the dubbing text; this post is about the subtitle — specifically, where its line breaks land once the words exist.

In 2026, the reflexive answer is "call a model." There are good models for exactly this: open-source sentence segmenters such as [wtpsplit](https://github.com/segment-any-text/wtpsplit) whose SaT ("Segment any Text") models place sentence boundaries in 85 languages without needing punctuation. We compared our own segmentation approach against this kind of neural splitter on real captions. Production subtitle segmentation may use a model internally, but only where it adds value. In many cases, the final decision is still driven by width and layout constraints rather than pure linguistic boundaries.

It would be easy to tell that story as "our rules beat their model." They don't. We tested both on real captions, and on the thing the model is actually built for — deciding where one thought ends and the next begins — it's a wash: sometimes the deterministic path reads better, sometimes SaT does — and even the case we most expected the model to win, the spaceless scripts like Thai, came out a wash once the lines had to fit a frame. So this post isn't a victory lap for handwritten rules. It's about why, when the headline comparison comes out even, we still shipped the rules — and what that says about what a subtitle actually is.

Two problems wearing one sentence

Ask where to break a subtitle, and you're really asking two unrelated questions:

1. Where does a semantic unit end? Sentence and clause boundaries. This is linguistic, and, on messy input — an ASR transcript with no punctuation — it's genuinely hard.

2. Does this line fit the frame? A line has a rendered width; the frame has a width; text that overflows gets clipped or auto-shrunk. This is geometry.

A neural segmenter answers question 1. It has nothing to say about question 2 — it will happily hand you a beautiful sentence boundary that is three times too wide for a phone held upright. And the two questions are wildly unequal in how much they matter to a shipped product: a break in a slightly awkward place is a cosmetic nit, but a line that runs off the screen is a defect a customer screenshots. The half the model solves is the half that matters less.

On boundaries alone, it's genuinely a wash

Let's give the model its due first, because it's real.

For a language with clear sentence punctuation, the deterministic path has an easy job: prefer to cut on punctuation, and there isn't much a model can add. Hindi ends sentences with a danda (`।`, U+0964) rather than a period — miss that in your break set, and Devanagari falls straight through to hard-wrapping mid-phrase — but once the danda is in the set, "cut on sentence marks" is most of the way there, and SaT's learned boundaries land in roughly the same places.

Where you'd expect the model to pull ahead is where punctuation runs out. Thai, Khmer and Burmese don't put spaces between words or reliably mark sentence ends; the deterministic path handles them by tokenizing into words (a dictionary segmenter for Thai) and packing those tokens to the width budget, with no notion of where a clause ends. A model trained on them does have a learned sense of boundary. So we ran it — the real neural path (SaT for sentence boundaries, then the identical width-fit) against production, on real Thai captions pulled from the pipeline.

It didn't pull ahead (a real run, not a mock-up)

A Thai caption arrives as a solid block:

วันนี้ผมจะไปตลาดซื้อผักและผลไม้เพื่อทำอาหารเย็น

(“Today I’m going to the market to buy vegetables and fruit to make dinner.”)

At a 40-character portrait budget, SaT returns the whole thing as one unit — it finds no sentence boundary to add — so the neural path falls straight through to the same pythainlp width-fit production already uses. Byte for byte, both produce:

วันนี้ผมจะไปตลาดซื้อผักและผลไม้เพื่อทำ / อาหารเย็น

(Line 1: “Today I’m going to the market to buy vegetables and fruit to make” / Line 2: “dinner.”)

Note that we do have a logic to average-split the sentence if the second part is too short, but since the Thai word for "dinner." seems indeed not that short enough.

Broken on a word boundary, because a dictionary segmenter cannot cut mid-word. And that agreement was the rule, not the exception: across eight real Thai cues, the two paths were identical five times. Where they diverged, the model did not win.

The longest run-on shows the trade most clearly:

ก็จะมีแบบว่าเกรงใจนิดนึงเพราะว่าเขาเป็นหัวหน้า แล้วเราต้องคอนเซิร์นเกี่ยวกับการใช้จ่ายอะไรอย่างเงี้ยค่ะ

(“It’s just that I feel a bit awkward because he’s the boss, and we have to worry about expenses and things like that.”)

Ours (3 lines):

ก็จะมีแบบว่าเกรงใจนิดนึงเพราะว่าเขาเป็น /

หัวหน้า แล้วเราต้องคอนเซิร์นเกี่ยวกับการ /

ใช้จ่ายอะไรอย่างเงี้ยค่ะ

(1: “It’s just that I feel a bit awkward because he’s” / 2: “the boss, and we have to worry about” / 3: “expenses and things like that.”)

SaT path (4 lines):

ก็จะมีแบบว่าเกรงใจนิดนึงเพราะว่าเขาเป็น /

หัวหน้า /

แล้วเราต้องคอนเซิร์นเกี่ยวกับการใช้จ่าย /

อะไรอย่างเงี้ยค่ะ

(1: “It’s just that I feel a bit awkward because he’s” / 2: “the boss,” / 3: “and we have to worry about expenses” / 4: “and things like that.”)

SaT respected a clause boundary our width-fit skips — but left หัวหน้า (“the boss”) stranded on its own line. That is the kind of extra break that reads fine on paper but costs a full subtitle line in practice.

The one time SaT’s boundary read cleaner, it bought that with three half-empty lines against our two:

เจอกันครั้งแรกเขาเป็น อ่า ฝ่ายบัญชีใช่ไหมคะ ซึ่งเราต้องทำงานกับเขา

(“The first time we met, he was, uh, from the accounting department, right? And we have to work with him.”)

Ours (2 lines):

เจอกันครั้งแรกเขาเป็น อ่า ฝ่ายบัญชี /

ใช่ไหมคะ ซึ่งเราต้องทำงานกับเขา

(1: “The first time we met, he was, uh, from the accounting department,” / 2: “right? And we have to work with him.”)

SaT path (3 lines):

เจอกันครั้งแรกเขาเป็น /

อ่า ฝ่ายบัญชีใช่ไหมคะ /

ซึ่งเราต้องทำงานกับเขา

(1: “The first time we met, he was,” / 2: “uh, from the accounting department, right?” / 3: “And we have to work with him.”)

SaT’s breaks track the spoken rhythm more closely — but at the cost of an extra line, which subtitles are designed to avoid.

So we’re not claiming better segmentation. We’re claiming that segmentation quality is roughly a tie, and a tie is decided by everything else on the table. Here’s everything else.

The half the model doesn't touch: fitting the frame

The frame-fitting problem is entirely ours to solve, model or no model, and it's most of the code. It has three parts.

A budget, in characters, derived from pixels. Roughly: about twenty characters fit comfortably along the frame's narrow edge; landscape gets more, portrait gets less, and the number also moves with font size. There are actually two budgets — a Latin character is about half the width of a CJK one, so Latin lines are allowed nearly twice the count before they're as wide on screen. Which forces the question the whole thing hinges on: what is a "character," for width?

A width proxy, because a codepoint is not a unit of width. Counting codepoints is wrong in three different directions, one per script family that the usual Latin-and-CJK mental model skips over:

- Whitespace isn't uniform.

In Latin it's a real unit that participates in wrapping; in CJK it's mostly noise. The proxy drops it for CJK and normalizes it elsewhere.

- Indic combining marks don't all take space.

`मैसेज` is five codepoints but not five things on screen — the two vowel signs (`ै`, `े`) sit above the consonants and take no advance width. Unicode calls those nonspacing (`Mn`) or enclosing (`Me`); they are safe to drop from a width count. Spacing vowel signs (`Mc`) are the opposite: Hindi `का` is not one cell wide; it is `क` plus a wide `ा`; Tamil `கா` does the same with `ா`. How often a script leans on those spacing marks is the single biggest width difference between Brahmic families. On real subtitle text, the order is clear — Tamil packs the densest spacing marks, then Devanagari, then Kannada close behind — which is why the same letter count of Tamil routinely outruns Hindi on screen. Strip the spacing marks, and you erase exactly that gap, and then you are stuck inventing a hand-tuned constant per script, forever. Keep them, and one weight, calibrated once on Devanagari, covers every Indic script at once.

- A glyph's width depends on the script.

Arabic runs wider than Latin; Indic aksharas wider still. Each gets weight. These aren't linguistic facts; they're typographic ones, and no arithmetic settles what they — fonts do. The weights stay exposed as calibration knobs and get checked against a rendered frame, not a spec.

None of that is something a sentence-boundary model knows or could learn from text; it's about how glyphs paint, not what they mean.

How much traffic does it need? A recent sample of 4,563 subtitle jobs spanned 98 target locales and 60 base languages:

Script familyShare of jobs
Latin & other63%
CJK (wide)22%
Arabic-script7%
Indic (Brahmic)4%
Spaceless SEA (Thai/Khmer/Burmese/Lao)3%

More than a third of jobs are in scripts where "codepoint = width" is simply false — and that third is invisible to a segmenter, which sees where the clauses are and nothing about how wide they render.

Solving the whole problem

Everything above is still only part of the story.

A sentence segmenter solves one problem well: where a thought ends. The production subtitle system in VMEG has to solve the whole problem. It has to fit text into a frame, align bilingual subtitles, support dozens of scripts, and run efficiently in production. Those constraints exist whether or not a neural model is used, so the deterministic pipeline remains the backbone of the system, with the model serving as an optional helper where it genuinely adds value.

The one-line version

A codepoint is a unit of storage, and a sentence boundary is a unit of meaning. A subtitle has to read well—but first, it has to fit.