Engineeringこの記事を日本語で読む

Subtitles in localized games: timing, line limits, and language pairs

Subtitles feel like a solved problem right up until you translate them. The timing that felt comfortable while you were writing the lines is suddenly too fast, the two-line box wants a third line, and players start asking for the original voice track with subtitles in their own language.

None of that is a rendering problem. A subtitle is a piece of text with a start time, an end time, a speaker and a size limit, and every one of those four behaves differently once the text is no longer the language you wrote it in. Treating subtitles as strings that happen to appear during audio is what produces the complaints.

This covers the design decisions that are expensive to change later: what drives duration, how to limit lines, what to do when one voice line needs more than one card, and how to store the whole thing so it can be checked mechanically.

Timing follows reading speed, not the audio clip

The default implementation ties the subtitle exactly to the audio: show it when the clip starts, hide it when the clip ends. In the language you wrote the game in, that reads perfectly well, because you wrote the line to match the delivery. In translation the same rule quietly turns into a reading test, since a translation carrying the same meaning can take noticeably longer to read than the original took to say, and the player has no way to pause.

What governs comfort is reading speed, and reading speed is not universal. Professional subtitling practice works with a maximum reading speed and a minimum on-screen duration, expressed in characters per second for scripts such as Japanese and Chinese and in characters or words per minute for Latin-script languages. The exact figures differ between style guides, platforms and languages, so treat them as parameters you choose for your project and then verify by watching, not as constants you can look up once and hard-code.

Whatever numbers you settle on, a few rules hold regardless:

  • Let a subtitle persist past the end of its voice line, up to the moment the next line needs the space
  • Enforce a minimum on-screen duration so short lines do not flash
  • Avoid very short gaps between consecutive cards — bridge or merge them instead of blinking the box
  • When a card still exceeds its reading budget with the extra time, split or shorten it rather than expecting the player to read faster
  • Compute duration from the rendered text of the current language, never from a fixed per-character constant baked into code

Line limits, line breaks, and when one voice line needs two cards

The real constraint is rendered width, not character count. A Japanese subtitle with fewer characters than its English source can still occupy more horizontal space, because CJK characters are typically laid out at roughly double the advance width of Latin ones. A limit expressed in characters is therefore wrong in one direction for some languages and wrong in the other direction for others.

Two lines is the conventional maximum and it is worth keeping. A third line starts to cover the picture, and on a handheld screen it eats a meaningful part of the play area. Express the limit as rendered width times line count, at the smallest resolution you support, and validate against it per language rather than trusting that it fits because it fits in your language.

Where the line breaks matters more than people expect, because a subtitle is read in a glance rather than scanned. Break at a syntactic boundary — after a complete phrase, never in the middle of one. In Japanese that means breaking at phrase boundaries and after punctuation, never inside a word and never between a noun and the particle attached to it. Automatic wrapping knows none of this, so either let translators insert explicit break markers in the text or review the rendered result language by language and fix what reads badly.

There is one break error players consistently notice: a split where the first line reads as a complete sentence that means something different from the whole. Getting halfway through a sentence and having to revise your understanding is far more disruptive than an awkward but honest break.

Most subtitle systems start from an assumption that one audio clip maps to exactly one subtitle entry. It is convenient, and it holds in the language the script was written in. It stops holding under translation, because a long line's translation may not fit two lines within the time the clip runs.

If the data model cannot express two timed cards for one clip, the translator is left with only bad options: cut meaning, cram the text into a block nobody can read in time, or blow through the reading budget. All three arrive later as a quality complaint that looks like a translation problem but is actually a schema problem.

So allow one voice line to map to one or more timed subtitle entries, per language, with offsets relative to the clip's start. The number of cards can legitimately differ between languages — that flexibility is the entire point. The corollary is that subtitles cannot be keyed by audio filename under a one-to-one assumption; key them by a stable voice line id, and let the table hold several rows per id per language.

voice_line_id,lang,card_index,start_ms,end_ms,speaker_id,text
vo_ch2_mira_014,en,1,0,2400,mira,I told you not to come here.
vo_ch2_mira_014,ja,1,0,1600,mira,ここへは来るなと
vo_ch2_mira_014,ja,2,1600,3200,mira,言ったはずだけど。

Speaker labels, captions, and the text that is not dialogue

Speaker labels earn their space when the speaker is off screen, when several characters talk in quick succession, or when the player has the volume low. If you show them, the name is a localizable string with its own key — not text typed into the subtitle line. A name typed into hundreds of subtitle rows becomes hundreds of chances for it to drift.

It is also worth separating subtitles from closed captions in your own head, because they are different products. Subtitles assume the player can hear and only needs the words. Captions serve players who cannot hear, so they add speaker identification, relevant sound effects, and music or tone cues. If you offer a caption mode, all of that extra text is content that needs translating too, and it is routinely missed when the scope is estimated from the dialogue script alone.

The same goes for subtitle-adjacent text that is not cutscene dialogue: ambient barks with no dialogue box, radio chatter over gameplay, and text read aloud on screen. Lines delivered while the player is actually playing need a more generous reading budget than cutscene lines, because attention is split between reading and doing.

Voice language and subtitle language are two settings

A large share of players want the original voice track with subtitles in their own language. If you ship dubs and tie the two selectors together, you have removed the combination those players came for. Make them two independent settings, defaulted to a sensible pairing on first launch and presented in the same place so the combination is discoverable.

This has a content consequence that is easy to miss. A dub script and a subtitle translation are not the same text. A dub is written to fit mouth movement and delivery timing; a subtitle is written to be read quickly. If you produce both, decide explicitly which text the subtitles show when a dubbed track is playing. Subtitles that do not match the words being spoken in the same language is the single mismatch players report most often, so at minimum, when subtitle language equals voice language, the subtitle should say what the actor says.

Defaults deserve a rule too: if a dub exists in the player's detected language, offering it as the initial pairing is reasonable, but an explicit choice must always win afterwards, and switching either setting must never silently change the other.

Storing subtitle data, and the checks worth automating

There are two workable storage shapes. Keep subtitles in your normal string table, keyed by voice line id with the timing in separate columns; or keep dedicated per-language subtitle files. The string table keeps everything inside one pipeline, so subtitles get counted, checked and reviewed alongside every other string, which is usually worth more than the tidiness of separate files. Dedicated files make sense when you already have external subtitle tooling or a video pipeline that speaks a subtitle format natively.

Either way, keep timing data separate from the text so that re-translating a line does not throw away the timing work, and keep the voice line id stable, because it is the join key between the audio, the text and any translation memory you accumulate.

The checks below are cheap to automate and remove most of the manual review burden. The reading-speed check in particular is the one that saves the most time, because it converts a subjective judgement into a list of specific cards to look at:

  • Every voice line has a subtitle in every language, and no subtitle row points at a missing audio clip
  • Computed reading speed for each card, in each language, is inside the budget you set
  • Rendered width times line count fits the box at the smallest supported resolution
  • No card is shorter than the minimum duration, and no two cards for the same line overlap in time
  • Placeholder tokens and markup tags match the source exactly
  • Speaker ids are present and resolve to a translated name

The review pass automation cannot replace

Build a subtitle review mode: a debug screen that plays every voice line in sequence with its card, in the selected language, without requiring anyone to play the game. It turns a full playthrough per language into a viewing session, and it is the only realistic way to review lines that sit behind branches or optional content.

Check the result on the smallest screen you support, and for console builds check it from across a room. A subtitle that is perfectly legible on a monitor at desk distance can be unreadable on a television, and that failure has nothing to do with translation quality at all — but it is the thing a player will remember about your localized version.

Related articles