# 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)，因此不同播放器和平台支持的内容各不相同。翻译后的文件最好与源文件一样保持简洁，这样最为稳妥。

## 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): 翻译正文、图片 alt 文本、frontmatter 中的标题和描述，以及用于显示文本的组件属性。代码块、内联代码、导入语句、JSX 组件名称、frontmatter 键和链接 URL 应保持不变，除非区域设置映射会改写它们。为翻译后的 YAML 值加上引号，然后运行站点构建。
- [如何翻译 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.
