# General Translation Knowledge Base: 如何在不破坏时间轴的情况下翻译 SRT 字幕？
URL: https://generaltranslation.com/zh/kb/translate-srt-subtitles.mdx
Docs index: https://generaltranslation.com/llms.txt
Description: 只翻译 SRT 文件中的字幕文本；字幕序号、时间码、空行分隔符和格式标签均与源文件保持一致。然后根据目标平台的要求检查每行长度和阅读速度：例如，Netflix 的英语字幕指南规定，成人节目每行最多 42 个字符，每秒最多 20 个字符。
Category: 翻译
Last updated: 2026-10-20
Authors: Kevin Wu, Jackie Chen
Published: 2026-10-20

SRT (SubRip) 并没有正式规范。美国国会图书馆称其[“标准化程度不高”](https://www.loc.gov/preservation/digital/formats/fdd/fdd000569.shtml)，因此不同播放器和平台支持的内容各不相同。翻译后的文件最好与源文件一样保持简洁，这样最为稳妥。

## Video

The video is narrated in English, with English captions.

[How do I translate SRT subtitles without breaking timing?](https://caxecubb9c64cezv.public.blob.vercel-storage.com/films/kb-translate-srt-subtitles.mp4) (45秒)

Translate only the cue text in an SRT file; keep cue numbers, timecodes, blank-line separators, and formatting tags as in the source. Then check line length and reading speed against your destination's requirements, such as Netflix's 42 characters per line. With General Translation, add an srt entry under files in `gt.config.json` and run `npx gt translate`.

## SRT 文件包含哪些内容

SRT 文件由一系列以空行分隔的字幕条目 (cue) 组成。每个条目包含一个序号、一行格式为 `HH:MM:SS,mmm --> HH:MM:SS,mmm` 的时间轴 (毫秒前用逗号分隔) ，以及一行或多行文本：

```text title="subtitles/en/intro.srt"
1
00:00:01,000 --> 00:00:03,500
Welcome back to the channel.

2
00:00:03,600 --> 00:00:06,200
Today we're looking at
<i>how subtitles are timed</i>.

3
00:00:06,400 --> 00:00:08,000
Let's get started.
```

本指南采用固定时间轴的工作流：只翻译文本，保留现有的字幕条目结构。如果目标语言需要不同的时间轴或条目边界，请在字幕编辑器中对照视频进行调整，并单独验证修改后的结果。

* **字幕序号**：按顺序排列，每个条目一个。
* **时间轴行**：须逐字符保留。把逗号换成句点，或毫秒数稍有偏差，都可能导致播放器拒绝该条目，或在错误的时间点显示。
* **条目之间的空行。** 缺少分隔符可能导致播放器合并、误读或跳过受影响的条目。后续条目使用的是绝对时间戳，其时间轴不会自动顺延。
* **条目内的换行**：用于控制每行长度，便于阅读。译文可以重新调整换行位置，但不应增加第三行。
* **标签**：例如 `<i>`、`<b>` 和 `<font>`，以及 `{\an8}` 等位置标签，播放器会将其视为格式化信息。能否渲染取决于播放器：例如 YouTube 仅接受基础 SRT 文件，不识别任何标记，并且要求使用纯 UTF-8 编码 ([支持的字幕文件](https://support.google.com/youtube/answer/2734698)) 。
* **文本编码。** SRT 没有统一的编码标准，因此除非播放器另有要求，否则请将译文保存为 UTF-8。

## 行长与阅读速度

字幕的时间轴是固定的，但译文长度会变化，因此一条在英语中读起来很从容的字幕，换成另一种语言后可能一闪而过。Netflix 为每种语言公布了相应的限制。其 [English (USA) Timed Text Style Guide](https://partnerhelp.netflixstudios.com/hc/en-us/articles/217350977-English-USA-Timed-Text-Style-Guide) 规定每行最多 42 个字符、最多两行，成人节目的阅读速度上限为每秒 20 个字符 (儿童节目为 17 个) 。[德语指南](https://partnerhelp.netflixstudios.com/hc/en-us/articles/217351587-German-Timed-Text-Style-Guide) 同样限定每行 42 个字符，但将阅读速度降至成人每秒 17 个字符、儿童每秒 13 个字符。

可用时间不变，译文所需的字符数却可能多于源文本。请根据每条译文字幕及其时长计算每秒字符数；渲染后的文本宽度则是另一项指标。如果某条字幕超出交付规范，请在不损失原意的前提下改写，并放到视频中审校。

## 逐条字幕翻译，还是以整个文件为上下文

一句对白常常横跨两三条字幕。逐条单独翻译可以确保结构不出错，但译出来的只是零散片段：以德语为例，从句的动词位于句末，可能会落到下一条字幕中。结合前后字幕作为上下文进行翻译，译文会更通顺，前提是译文必须回填到原来的字幕条目中，并保持时间轴不变。

## 翻译 SRT 文件的几种方式

| 方式                      | 处理 SRT 结构                    | 机器翻译                                  | 阅读速度检查                       | 适用场景                 |
| ----------------------- | ---------------------------- | ------------------------------------- | ---------------------------- | -------------------- |
| Subtitle Edit (免费、开源)   | 解析并写出 SRT                    | Google Translate、DeepL、LLM API 以及本地模型 | Netflix 风格的检查，涵盖每秒字符数、行长度和行数 | 由一人手动翻译并润色文件         |
| DeepL 文档翻译              | 支持上传 `.srt`                  | DeepL                                 | 无                            | 想快速得到单个文件的初稿         |
| Google Translate        | 消费者版应用和 Cloud 文档翻译均不支持上传 SRT | 仅限文本 API；需自行重建文件                      | 无                            | 你打算编写脚本并自行处理文件       |
| General Translation CLI | 专门的 SRT 处理，保留字幕时间轴和受保护的标记    | GT 服务，共享词汇表和自定义提示词                    | 运行下方的检查，并对照视频审校              | 字幕需随产品或文档的定期发布进行版本管理 |
| 直接把文件交给 LLM             | 以纯文本读取文件；无法保证结构完好            | 任意自选模型                                | 仅在你明确要求时才做，且结果未经验证           | 一次性文件，且你会逐行检查        |

下面补充一些表格背后的细节。YouTube Studio 没有列入表中，因为它负责发布字幕，而不负责翻译：拿到翻译后的文件后，打开 **Subtitles**，选择视频，点击 **Add language**，选择语言，然后上传文件 ([添加字幕](https://support.google.com/youtube/answer/2734796)) 。也就是说，上传的前提是译文已经准备好。[Subtitle Edit](https://github.com/SubtitleEdit/subtitleedit) (MIT 许可，支持 Windows、macOS 和 Linux) 提供 **Translate &gt; Auto-translate** 对话框，可选引擎包括 Google Translate、DeepL、ChatGPT、Claude、Gemini，以及通过 Ollama 运行的本地模型；此外还内置 **Tools &gt; Check and fix Netflix errors** 检查器。DeepL 的[文档 API](https://developers.deepl.com/api-reference/document) 将 `srt` 列为支持的格式之一。Google 的[文档翻译](https://docs.cloud.google.com/translate/docs/advanced/translate-documents)支持 Office 文件和 PDF，而[消费者网站](https://support.google.com/translate/answer/2534559?co=GENIE.Platform%3DDesktop)仅支持 `.docx`、`.pdf`、`.pptx` 和 `.xlsx`。

如果由一人手动翻译并润色文件，推荐使用 Subtitle Edit，它将机器翻译与时间轴、阅读速度检查集于一身。如果只需快速得到单个文件的初稿，可使用 DeepL 的文档翻译。如果同一批字幕文件会随版本发布不断更新，并且需要与你的应用和文档共用术语，请选择 General Translation；最后对照视频的审校工作，则交给字幕编辑器完成。

## 检查翻译后的文件

此 Node.js 脚本会检查翻译后的文件与源文件的字幕条数、字幕序号和时间码是否一致，并确认没有空字幕。传入第三个参数，即可标记超出指定阅读速度的字幕，例如德语可设为每秒 17 个字符：

```js title="check-srt.mjs"
// 用法：node check-srt.mjs <source.srt> <translated.srt> [每秒最大字符数]
import { readFileSync } from 'node:fs';

const TIMING = /^(\d{2}:[0-5]\d:[0-5]\d,\d{3}) --> (\d{2}:[0-5]\d:[0-5]\d,\d{3})$/;
const cues = (file) =>
  readFileSync(file, 'utf8')
    .replace(/^﻿/, '')
    .split(/\r?\n\s*\r?\n/)
    .map((block) => block.trim().split(/\r?\n/))
    .filter((lines) => lines[0])
    .map(([number, timing = '', ...text]) => ({ number, timing, text }));

const [srcFile, outFile, maxCps] = process.argv.slice(2);
if (!srcFile || !outFile || (maxCps !== undefined && !(Number(maxCps) > 0 && Number.isFinite(Number(maxCps))))) {
  throw new Error('Provide source and translated files, and an optional positive reading-speed limit.');
}
const [source, target] = [srcFile, outFile].map(cues);
const seconds = (tc) => tc.split(/[:,]/).reduce((acc, n, i) => acc + n * [3600, 60, 1, 0.001][i], 0);
const problems = [];
if (!source.length) problems.push('source has no cues');
if (source.length !== target.length) problems.push(`cue count: ${source.length} in source, ${target.length} translated`);

source.forEach((cue, i) => {
  const t = target[i];
  if (!t) return;
  if (cue.number !== t.number) problems.push(`cue ${i + 1}: number "${cue.number}" became "${t.number}"`);
  if (!TIMING.test(t.timing)) problems.push(`cue ${cue.number}: invalid timing line "${t.timing}"`);
  else if (cue.timing !== t.timing) problems.push(`cue ${cue.number}: timing changed to "${t.timing}"`);
  if (!t.text.join('').trim()) problems.push(`cue ${cue.number}: empty text`);
  const [, start, end] = TIMING.exec(t.timing) ?? [];
  const duration = start ? seconds(end) - seconds(start) : 0;
  if (start && duration <= 0) problems.push(`cue ${cue.number}: end must be after start`);
  if (maxCps && duration > 0) {
    // 计入空格和标点，但不计入 <i> 或 {\an8} 等标记
    const chars = t.text.join(' ').replace(/<[^>]+>|\{\\an\d\}/g, '').length;
    const cps = chars / duration;
    if (cps > maxCps) problems.push(`cue ${cue.number}: ${cps.toFixed(1)} characters per second`);
  }
});

console.log(problems.length ? problems.join('\n') : `OK: ${source.length} cues, numbers and timecodes match`);
process.exitCode = problems.length ? 1 : 0;
```

下面是该示例文件的两份译文。西班牙语版保留了全部字幕条目；德语版则把三条字幕合并成了两条，还在时间码里误输入了一个句点：

```text title="subtitles/es/intro.srt"
1
00:00:01,000 --> 00:00:03,500
Bienvenidos de nuevo al canal.

2
00:00:03,600 --> 00:00:06,200
Hoy veremos
<i>cómo se sincronizan los subtítulos</i>.

3
00:00:06,400 --> 00:00:08,000
¡Empecemos!
```

```text title="subtitles/de/intro.srt"
1
00:00:01,000 --> 00:00:03,500
Willkommen zurück auf dem Kanal.
Heute sehen wir uns an,

2
00:00:03,600 --> 00:00:06.200
<i>wie Untertitel getimt werden</i>.
Los geht's!
```

使用 Node.js 24 运行后，该脚本检查通过了西班牙语文件，但德语文件未通过。字幕 1 现在需要在 2.5 秒内显示 56 个字符：

```bash
$ node check-srt.mjs subtitles/en/intro.srt subtitles/es/intro.srt 20
OK: 3 cues, numbers and timecodes match

$ node check-srt.mjs subtitles/en/intro.srt subtitles/de/intro.srt 17
cue count: 3 in source, 2 translated
cue 1: 22.4 characters per second
cue 2: invalid timing line "00:00:03,600 --> 00:00:06.200"
```

该脚本发现任何问题时都会以退出码 1 退出，因此可以在翻译完成后放在 CI 中运行。它只接受此处所示的纯时间轴行，不支持可选的位置坐标。它会检查字幕条目是否一一对应、时间轴是否有效、文本是否非空，以及是否超出可选的阅读速度上限。它不检查标签是否保留、行长度、行数或语义；请使用目标平台的字幕检查工具，并请一位懂该语言的审校人员观看最终效果。其基于 JavaScript 的字符计数仅用于初步筛查，不能替代各平台自身的计数规则。

## 如何使用 General Translation 翻译 SRT 文件

如果字幕文件存放在代码仓库中，且每次变更后都需要翻译成多种语言，可以使用 General Translation 的 CLI `gt`：自 2.22.2 版本起，它支持翻译 `.srt` 文件。安装并登录后 (参见 [CLI 快速入门](/docs/cli/quickstart)) ，在 `gt.config.json` 的 `files` 下添加一个 `srt` 条目，并在路径中加入 `[locale]` 占位符：

```json title="gt.config.json"
{
  "defaultLocale": "en",
  "locales": ["es", "de"],
  "files": {
    "srt": {
      "include": ["subtitles/[locale]/**/*.srt"]
    }
  }
}
```

然后运行 [`npx gt translate`](/docs/cli/reference/commands/translate)。CLI 会从 `subtitles/en/` 读取源字幕，并将译文写入 `subtitles/es/` 和 `subtitles/de/`。如需翻译成英语，请将 `defaultLocale` 设置为源语言，并在 `locales` 中列出 `"en"`。

只有字幕条目文本会被翻译。字幕序号、时间轴行 (包括时间戳之后的位置数据) 、空行分隔符、`<i>` 和 `<font>` 等标记，以及开头的 `{\an1}` 到 `{\an9}` 位置标签均保持不变；没有有效时间轴行的块也会原样保留。翻译后的行沿用该条目原有的换行符，下载的文件则沿用源文件的编码。未改动的条目可以复用之前的译文，项目中的词汇表和自定义提示词同样适用于字幕。 (请参阅 [SRT 格式参考](/docs/cli/reference/formats/srt-files)和[为翻译定义上下文](/docs/platform/dashboard/guides/defining-context-for-translations)。) 

由于时间轴行会与源文件保持完全一致，请对每个翻译后的文件运行上面的检查脚本，尤其是译文往往比源语言更长的语言。

## 要点总结

* 在时间轴固定的翻译中，只修改字幕文本，同时保留字幕序号、时间轴行、分隔符和格式标签。如有意调整时间轴或字幕边界，应另行对照视频审校。
* Netflix 规定每行最多 42 个字符，最多两行；成人节目的阅读速度上限为英语每秒 20 个字符、德语每秒 17 个字符。
* DeepL 的文档翻译和 Subtitle Edit 均支持 SRT 文件，Google Translate 的文档上传功能则不支持。
* 使用 `gt` CLI (2.22.2 或更高版本) 时，只需添加带有 `[locale]` 路径的 `files.srt.include` 并运行 [`npx gt translate`](/docs/cli/reference/commands/translate)，即可翻译 SRT 文件；它仅翻译字幕文本，时间轴与源文件保持一致。

## Related questions

- [如何在不破坏语法的前提下翻译 Markdown 和 MDX？](https://generaltranslation.com/zh/kb/translate-markdown-files.mdx): 翻译面向读者的文本，同时保留可执行代码、导入语句、JSX 组件名称和机器内部标识符。General Translation 可直接处理 Markdown 和 MDX，包括用于显示文本的属性和 frontmatter 标题，并在文档更新时复用匹配的译文。发布前，请审校本地化后的文件并运行站点构建。
- [如何翻译 i18n JSON 文件？](https://generaltranslation.com/zh/kb/translate-json-files.mdx): 只翻译 JSON 区域设置文件中的字符串值。非复数键、{{name}} 之类的占位符以及标签都应保持不变，但复数键会因语言而异：例如波兰语需要 inbox_few 和 inbox_many。请使用能识别文件格式的工具或 API，然后先解析并验证输出，再进行审校。
- [AI 翻译的准确度足以用于生产环境应用吗？](https://generaltranslation.com/zh/kb/is-ai-translation-accurate.mdx): 对于使用广泛的语言，通常可以，但前提是它要先通过针对你自己的语言、UI 字符串和高风险内容的评估。在 WMT25 中，Gemini 2.5 Pro 在 16 个语言对中有 10 个达到或超过了人工参考译文的水平，但文档级基准测试无法说明简短的界面字符串是否已经可以上线。
- [Google Translate 替代方案：General Translation 与 Google Translate 对比](https://generaltranslation.com/zh/kb/general-translation-vs-google-translate.mdx): Google Translate 适合快速翻译一次性文本，但要以另一种语言发布产品，还需要结合上下文翻译每个字符串、保持术语一致、正确处理变量和复数形式、进行审校，并建立发布流程。General Translation 能理解代码中每个字符串的上下文，将从代码、审校到每一次发布的整个本地化工作流全面自动化。

## Previous and next questions

- Previous question: [如何在 CSS 中支持 RTL（从右到左）语言？](https://generaltranslation.com/zh/kb/right-to-left-css.mdx)
- Next question: [如何在不破坏语法的前提下翻译 Markdown 和 MDX？](https://generaltranslation.com/zh/kb/translate-markdown-files.mdx)

## Sitemap

See the [Knowledge Base sitemap](https://generaltranslation.com/zh/kb/sitemap.md) for every article.
