ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

用DeepSeek API批量翻译SRT字幕:老动画英转中完整方案

用DeepSeek API批量翻译SRT字幕:老动画英转中完整方案 说起老番补档这件事很多动漫爱好者都深有体会上世纪 90 年代的 OVA 动画画风复古、剧情扎实可惜大部分作品没有官方中文字幕。尤其是 1995 年推出的 OVA《偶像万人迷》在英文圈子里还有一些英文字幕资源中文观众想啃生肉却往往卡在语言关上。以前遇到这种情况只能等字幕组用爱发电或者自己逐句查词典硬翻效率极低。最近我在整理这部老动画的字幕时尝试用 DeepSeek API 把英文字幕批量转成中文字幕效果出乎意料地好。整个流程可以完全脚本化自动读取 SRT 字幕文件、分批调用大模型翻译、再回写到新的字幕文件几十集动画的字幕也能在几分钟内处理完。这篇文章就把这套完整方案拆开讲清楚包括环境准备、完整 Python 代码、常见报错排查和工程化建议新手可以照着做有经验的开发者也能直接改造成自己的字幕批处理工具。1. 项目背景老动画缺中文字幕为什么选 DeepSeek1.1 《偶像万人迷》OVA 与字幕现状《偶像万人迷》是 90 年代日系动画中的一部偶像题材作品1995 年以 OVA 形式推出。这类老番在中文互联网上的译名并不统一有人按日文原名翻译成《偶像传说英里子》也有人直接叫《偶像万人迷》但不管片名怎么变字幕资源匮乏的问题是共通的。由于年代较早这些动画的 DVD 或家用录像带大多只带了日语音轨和少量英文字幕官方中文字幕几乎没有。过去字幕组做老番字幕需要先找日文原稿再请日语翻译一句句听写、翻译、打轴人力成本很高所以很多冷门老番迟迟等不到中文字幕。而英文字幕资源相对多一些如果能高效地把英文翻译成中文就能大大降低制作中文字幕的门槛。1.2 传统字幕翻译的痛点传统字幕翻译有几个明显的痛点纯人工翻译太慢。一部 OVA 通常有八九百条字幕人工校对可能需要几个晚上。机翻质量参差不齐。早期机器翻译对口语化对白、语气词的翻译非常生硬经常出现字面意思对但读起来不像人话的问题。字幕格式处理麻烦。SRT、ASS 等字幕文件包含时间轴、序号和样式标签翻译时如果破坏了时间轴字幕就无法正确显示。大模型出现之后字幕翻译这件事有了新的解法。模型不仅能理解上下文还能根据提示词控制翻译风格口语化对白、角色语气都能处理得比较自然而且 SRT 这种纯文本格式很容易用脚本批量处理。1.3 DeepSeek 在字幕翻译中的定位DeepSeek 是目前国内使用成本比较低、中文理解能力强的开源大模型之一。它的 API 兼容 OpenAI 接口协议用 OpenAI 的 Python SDK 就能直接调用对开发者非常友好。在字幕翻译场景中DeepSeek 的价值主要体现在三个方面中文翻译质量高尤其适合动漫、影视这类需要口语化表达的文本。API 调用方式简单一条请求就能翻译一批字幕适合批量处理。支持自定义 system prompt可以通过提示词控制术语、人名的翻译方式。因此我选用 DeepSeek API 作为翻译引擎配合 Python 脚本完成整条字幕翻译流水线。2. 技术选型与核心概念2.1 字幕文件格式基础SRT 结构SRT 是最常见的字幕格式之一纯文本结构非常简单。一个标准的 SRT 文件由多个字幕块组成每个字幕块之间用空行分隔基本结构如下1 00:00:01,000 -- 00:00:04,000 Hello everyone! 2 00:00:05,000 -- 00:00:08,000 Welcome to the show.其中第一行是字幕序号从 1 开始递增。第二行是时间轴格式为时:分:秒,毫秒箭头两边分别是开始时间和结束时间。第三行及之后是字幕文本可以有多行。我们要做的翻译任务本质上就是保留序号和时间轴不变只把字幕文本从英文替换成中文。所以脚本的核心工作是解析 SRT - 提取文本 - 调用 DeepSeek 翻译 - 把译文写回原来的位置。ASS 格式比 SRT 复杂一些包含样式定义、事件行和特效标签翻译时需要剥离{\an8}这类标签只翻译内部文本。本文主讲 SRTASS 的处理思路会在后面扩展部分简单说明。2.2 DeepSeek API 调用方式DeepSeek 提供了兼容 OpenAI 格式的 HTTP API因此我们可以直接使用openai这个 Python 库来调用。关键配置只有三项api_key在 DeepSeek 开放平台创建 API Key。base_url设为 DeepSeek 的 API 基础地址。model以deepseek-chat为例具体模型名以官方文档为准。最基本的调用代码如下from openai import OpenAI client OpenAI( api_key你的 DeepSeek API Key, base_urlhttps://api.deepseek.com ) response client.chat.completions.create( modeldeepseek-chat, messages[ {role: system, content: 你是一个字幕翻译助手。}, {role: user, content: 请把下面的英文对话翻译成中文\nHello!}, ], temperature0.3, streamFalse ) print(response.choices[0].message.content)这里messages是聊天消息列表system用来设定角色和翻译规范user是实际要翻译的内容。temperature控制随机性字幕翻译场景建议调低让输出更稳定。2.3 翻译流程与批量策略字幕通常有几百上千条如果一条一条请求 API速度慢、费用高而且每条字幕之间缺少上下文翻译质量也会打折。更合理的方式是批量翻译每批 15 到 20 条字幕一起发给模型让模型在上下文连贯的前提下翻译。批量翻译的关键问题是对齐。模型返回结果时可能漏掉某一条也可能顺序错乱。解决方案是在输入时给每条字幕加上序号例如1. Hello everyone! 2. Welcome to the show.然后要求模型输出时也保留序号1. 大家好 2. 欢迎收看本期节目。脚本拿到结果后按序号把译文映射回原字幕块这样即使模型漏译也能定位到具体哪一条。3. 环境准备3.1 安装 Python 与依赖本教程示例使用 Python 3.9 及以上版本。建议先创建一个虚拟环境避免依赖冲突。python -m venv venv source venv/bin/activate # Windows 下使用 venv\Scripts\activate然后安装 openai 库pip install openai如果你的网络环境比较特殊也可以使用国内镜像源安装pip install openai -i https://pypi.tuna.tsinghua.edu.cn/simple版本方面openai 库 1.x 版本都支持上面的调用方式本文示例以 1.x 为参考。3.2 获取 DeepSeek API Key要调用 DeepSeek API需要先到 DeepSeek 开放平台注册账号然后在控制台中创建一个 API Key。创建后把 Key 保存好不要泄露到公开代码仓库。为了方便脚本读取推荐把 API Key 放到环境变量中。Linux 和 macOS 下执行export DEEPSEEK_API_KEY你的 API KeyWindows PowerShell 下执行$env:DEEPSEEK_API_KEY你的 API Key脚本中默认会读取DEEPSEEK_API_KEY这个环境变量。3.3 准备字幕文件开始翻译之前需要准备一份英文字幕文件编码必须是 UTF-8。如果你手上的 SRT 是其他编码比如 ANSI 或 UTF-16建议先用文本编辑器转换成 UTF-8否则脚本解析时会出现乱码。本文示例使用的输入文件路径为input/input.srt读者可以按自己的实际目录结构调整。4. 实战编写 DeepSeek 字幕翻译脚本4.1 项目结构建议创建如下项目结构deepseek-subtitle-translator/ ├── input/ │ └── input.srt # 输入英文字幕 ├── output/ # 输出目录 ├── subtitle_translator.py # 翻译脚本 └── requirements.txt # 依赖文件依赖文件requirements.txt内容如下openai1.0.04.2 完整代码下面给出完整的翻译脚本。代码包含 SRT 解析、批量翻译、译文对齐、结果回写四个核心步骤并加入了异常重试机制。 DeepSeek SRT 字幕翻译脚本 用法 python subtitle_translator.py --input input/input.srt --output output/output.srt 依赖 pip install openai import argparse import os import re import sys import time from pathlib import Path from openai import OpenAI MODEL_NAME deepseek-chat BATCH_SIZE 15 SYSTEM_PROMPT 你是一名资深的日本动画字幕翻译。请把用户提供的英文字幕翻译成简体中文。 翻译要求 1. 译文要口语化、自然符合中文动画对白习惯。 2. 必须保留原有编号格式每行翻译结果写成“编号. 译文”。 3. 不要添加原文没有的信息。 4. 人名、地名、专有名词如果不好翻译使用常见中文音译。 5. 翻译后不要输出任何解释或前后缀。 def parse_srt(content: str) - list[dict]: 解析 SRT 字幕内容返回字幕区块列表。 blocks [] pattern re.compile( r(\d)\n r(\d{2}:\d{2}:\d{2},\d{3}) -- (\d{2}:\d{2}:\d{2},\d{3})\n r(.*?)(?\n\n|\Z), re.DOTALL, ) for match in pattern.finditer(content.strip()): index int(match.group(1)) start match.group(2) end match.group(3) text match.group(4).strip().replace(\n, ) blocks.append({ index: index, start: start, end: end, text: text, }) return blocks def format_batch_source(blocks: list[dict]) - str: 把一批字幕格式化为带编号的文本。 lines [] for item in blocks: lines.append(f{item[index]}. {item[text]}) return \n.join(lines) def translate_batch_with_retry( client: OpenAI, blocks: list[dict], model: str, max_retries: int 3, ) - str: 调用 DeepSeek API 翻译一批字幕失败时指数退避重试。 source format_batch_source(blocks) user_content f请将下面的英文字幕翻译成简体中文\n\n{source} for attempt in range(max_retries): try: response client.chat.completions.create( modelmodel, messages[ {role: system, content: SYSTEM_PROMPT}, {role: user, content: user_content}, ], temperature0.3, streamFalse, ) return response.choices[0].message.content.strip() except Exception as exc: print(f[WARN] 第 {attempt 1} 次请求失败{exc}) if attempt max_retries - 1: raise time.sleep(2 ** attempt) return def parse_translated_text(text: str) - dict[int, str]: 解析模型返回的译文提取“编号. 译文”映射关系。 result {} for line in text.splitlines(): line line.strip() if not line: continue match re.match(r^(\d)[.、:]\s*(.*)$, line) if match: idx int(match.group(1)) result[idx] match.group(2).strip() return result def write_srt(output_path: str, blocks: list[dict]) - None: 把字幕区块写回 SRT 文件。 with open(output_path, w, encodingutf-8) as f: for block in blocks: f.write(f{block[index]}\n) f.write(f{block[start]} -- {block[end]}\n) f.write(f{block.get(translated_text, block[text])}\n\n) def main() - int: parser argparse.ArgumentParser(descriptionDeepSeek SRT 字幕翻译工具) parser.add_argument(--input, -i, requiredTrue, help输入英文字幕文件路径) parser.add_argument(--output, -o, requiredTrue, help输出中文字幕文件路径) parser.add_argument( --api-key, -k, defaultos.getenv(DEEPSEEK_API_KEY), helpDeepSeek API Key默认为环境变量 DEEPSEEK_API_KEY, ) parser.add_argument( --model, -m, defaultMODEL_NAME, helpDeepSeek 模型名称, ) parser.add_argument( --batch-size, -b, typeint, defaultBATCH_SIZE, help每批翻译的字幕条数建议 10-20, ) args parser.parse_args() if not args.api_key: print(错误请通过 --api-key 参数或环境变量 DEEPSEEK_API_KEY 提供 API Key) return 1 if not Path(args.input).exists(): print(f错误输入文件不存在{args.input}) return 1 output_dir os.path.dirname(args.output) if output_dir and not os.path.exists(output_dir): os.makedirs(output_dir, exist_okTrue) client OpenAI( api_keyargs.api_key, base_urlhttps://api.deepseek.com, ) with open(args.input, r, encodingutf-8-sig) as f: content f.read() blocks parse_srt(content) print(f[INFO] 解析到 {len(blocks)} 条字幕) total_batches (len(blocks) args.batch_size - 1) // args.batch_size for batch_idx, start in enumerate( range(0, len(blocks), args.batch_size), start1, ): batch blocks[start:start args.batch_size] print( f[INFO] 处理第 {batch_idx}/{total_batches} 批 f共 {len(batch)} 条字幕 ) try: translated_raw translate_batch_with_retry( client, batch, args.model ) except Exception as exc: print(f[ERROR] 第 {batch_idx} 批翻译失败{exc}) return 1 translated_map parse_translated_text(translated_raw) missing [ b[index] for b in batch if b[index] not in translated_map ] if missing: print(f[WARN] 以下字幕未匹配到译文将保留原文{missing}) for block in batch: translated translated_map.get(block[index], ) block[translated_text] translated or block[text] write_srt(args.output, blocks) print(f[INFO] 翻译完成输出文件{args.output}) return 0 if __name__ __main__: sys.exit(main())4.3 模块拆解SRT 解析脚本中的parse_srt函数负责把 SRT 文件内容解析成结构化的字幕区块列表。正则表达式(\d)\n(...) -- (...)\n(.*?)(?\n\n|\Z)用到了非贪婪匹配可以在 DOTALL 模式下把每个字幕块完整提取出来。(\d)匹配字幕序号。两个(\d{2}:\d{2}:\d{2},\d{3})匹配开始和结束时间。(.*?)非贪婪匹配字幕文本。(?\n\n|\Z)向前查找空行或文件末尾作为字幕块的结束边界。读取文件时使用utf-8-sig而不是utf-8是为了自动去掉 UTF-8 BOM。很多字幕文件是从 Windows 软件导出的带有 BOM用普通 UTF-8 读取时会把\ufeff带进第一行导致序号解析出错。4.4 模块拆解批量翻译与重试translate_batch_with_retry是调用 DeepSeek API 的核心函数。它把一批字幕文本拼成带编号的字符串放进user消息中发送给模型。网络请求可能会出现超时、限流或临时服务异常所以函数里加了最多 3 次重试。每次重试使用指数退避策略第一次失败后等 2 秒第二次失败后等 4 秒第三次失败后抛出异常。这样做的好处是避免在接口临时抖动时频繁请求给服务端恢复的时间。temperature设为 0.3是为了让翻译结果更稳定、更忠实原文。字幕翻译和创意写作不同不需要高随机性温度太高反而容易产生原文没有的内容。4.5 模块拆解译文对齐与回写模型返回的译文是纯文本需要解析成{序号: 译文}的映射关系。parse_translated_text用正则^(\d)[.、:]\s*(.*)$匹配每一行兼容英文句点、中文句号、顿号和冒号等多种分隔符。在合并译文时如果某个序号没有匹配到译文脚本会保留原文并打印警告。这个兜底策略很重要因为大模型偶尔会漏译一行直接跳过会导致字幕错位。回写 SRT 时脚本保留原始序号和时间轴只把translated_text字段写入输出文件。由于写入时使用encodingutf-8生成的字幕文件在主流播放器中都能正常显示中文。4.6 运行命令在项目根目录执行以下命令运行脚本python subtitle_translator.py \ --input input/input.srt \ --output output/output.srt如果 API Key 没有放到环境变量可以手动指定python subtitle_translator.py \ --input input/input.srt \ --output output/output.srt \ --api-key 你的 API Key运行过程中会输出类似下面的日志[INFO] 解析到 960 条字幕 [INFO] 处理第 1/64 批共 15 条字幕 [INFO] 处理第 2/64 批共 15 条字幕 ... [INFO] 翻译完成输出文件output/output.srt5. 运行结果与质量检查5.1 预期输出翻译完成后打开输出的 SRT 文件每一块字幕应该是这样的1 00:00:01,000 -- 00:00:04,000 大家好 2 00:00:05,000 -- 00:00:08,000 欢迎收看本期节目。序号和时间轴与原文完全一致只是文本内容变成了中文。5.2 对照检查字幕翻译完成之后建议做一次人工抽检。重点检查以下几类内容专有名词是否一致。比如同一个角色名字在 1 号字幕和 100 号字幕中是否用了相同的译法。语气是否符合角色性格。热血角色、温柔角色、反派角色说话语气应该有区别如果翻译得千篇一律需要调整提示词。是否出现过度翻译。比如原文只是Huh?译文却扩展成这是怎么回事就说明温度可能偏高或提示词不够严格。时间轴是否错位。如果发现字幕和画面对不上先检查 SRT 解析是否有问题。我实际测试时DeepSeek 对动画对白的翻译比较自然人名方面偶尔会有不统一的情况比如同一个角色有时音译成艾莉卡有时意译成英里子。这个问题可以通过在提示词中给出术语表来解决。6. 常见问题与排查思路6.1 问题排查表格问题现象常见原因解决思路401 Authentication ErrorAPI Key 错误或已失效检查环境变量和--api-key参数确认 Key 没有多余空格429 Rate Limit请求频率过高触发限流增大批量大小降低并发请求开启重试机制模型返回空内容输入格式有问题或触发内容过滤检查字幕文本是否正常尝试拆分更小的批次翻译后部分行保留英文模型漏译未返回对应编号脚本已自动保留原文重新翻译该批即可中文字幕在播放器中乱码SRT 文件编码不是 UTF-8确认输出文件使用 UTF-8 编码保存解析到 0 条字幕SRT 文件格式不标准人工查看文件开头确认序号、时间轴格式是否标准6.2 典型场景说明场景一API Key 报错如果脚本运行时报AuthenticationError先确认环境变量有没有正确设置echo $DEEPSEEK_API_KEY如果输出为空说明环境变量没有配置成功。另一个常见坑是复制 API Key 时带上了行尾的换行符可以用export DEEPSEEK_API_KEY$(echo -n 你的 API Key)清理掉。场景二翻译请求超时大批量翻译时偶尔会出现请求超时。我的经验是先把--batch-size从 15 降到 8 到 10减少单次请求的 token 量超时概率会明显下降。同时脚本中的重试机制会自动处理偶发超时不需要人工干预。场景三字幕文本包含特殊符号英文字幕里经常出现♪、...、?!等符号。翻译时这些符号要保留合理的位置如果模型把它们弄丢了也不影响阅读。但要注意SRT 格式中不能包含裸的--字符串正常翻译不会产生这种问题。7. 最佳实践与工程建议7.1 字幕翻译的提示词设计提示词直接决定翻译质量。我在项目中使用的系统提示词强调了三点口语化、保留编号、不添加原文信息。实际使用时还可以针对不同动画调整提示词。比如翻译少女偶像题材动画可以补充5. 女性角色和男性角色的说话语气要有区分。 6. 歌词部分保留在译文末尾用“♪”符号括起来。如果要翻译有大量专有名词的作品可以维护一个术语表作为额外的 user 消息发过去术语表 Eriko - 英里子 Takeshi - 武志也可以把这些术语放进 system 提示词中这样每一批翻译都会遵循相同的术语规范。7.2 批量并发与成本控制API 翻译的成本主要由 token 数量决定。中文字幕的 token 消耗通常比英文字幕略高因此翻译成本需要提前估算。建议先用 100 条字幕做一次小规模测试确认质量和成本都符合预期后再全量跑。如果字幕量很大比如几十集的整季动画不要开太多并发请求否则容易触发限流。工程上可以做成断点续传每翻译完一批就把结果写回文件程序中断后可以从上次进度继续避免重复消耗 API 额度。简化版的断点续传思路是把每批的翻译结果保存成一个 JSON 文件全部完成后统一合并。本文的脚本是整批翻译完才写文件如果中途失败会重头再来你可以在此基础上做增量保存。7.3 术语表与一致性处理大模型翻译长文本时同一个术语在上下文不同位置可能翻译成不同名称。要解决一致性问题最有效的方法是显式术语表。TERM_SYSTEM_PROMPT 术语表必须严格遵守 Eriko - 英里子 Miki - 美纪 Kouji - 浩二 除了术语表还可以在提示词里写明所有角色名必须与术语表保持一致不得自行变通。这样可以减少后期人工校对成本。7.4 版权与合规最后补充一点非常重要的提醒。字幕翻译通常用于个人学习、粉丝交流和非商业性的字幕补全场景。请不要把翻译后的字幕与受版权保护的视频资源放在一起公开传播牟利也不要在商业视频平台使用未经授权的字幕。脚本能力是工具使用工具的方式要遵守相关法律法规和版权要求。同时DeepSeek API 的调用费用和使用条款应以官方文档为准。在生产环境中使用前建议先阅读 API 服务条款确认你的用途符合平台规范。7.5 后续扩展方向这个脚本还有不少可扩展空间支持 ASS 字幕格式解析样式标签只翻译文本内容。增加并发请求用ThreadPoolExecutor控制并发同时需要注意限流。集成术语表读取外部 JSON 文件自动注入提示词。人工校对界面生成翻译后输出一个原文-译文对照表格方便人工快速审核。字幕预览检查按时间轴抽帧显示画面上下文辅助校对语气词。8. 总结与下一步这篇文章以 1995 年 OVA《偶像万人迷》的英转中字幕翻译为切入点完整介绍了用 DeepSeek API 批量翻译 SRT 字幕的流程。核心步骤包括解析 SRT 字幕结构、通过 OpenAI 兼容接口调用 DeepSeek、按编号批量翻译并自动对齐、最终回写到新字幕文件。这套方案不依赖具体动画作品任何 SRT 格式的英文字幕都可以直接套用。关键点可以归纳为四条一是批量翻译比逐条翻译更高效二是在提示词中强制保留编号能解决对齐问题三是重试机制和兜底策略能大幅提升稳定性四是要用术语表保证专有名词一致性。如果你手头也有积压多年的老番字幕不妨把脚本跑一遍先拿 100 条字幕做小规模验证再决定是否全量处理。AI 工具不会取代字幕组的细致校对但至少能把最耗时的初翻环节压缩到几分钟让有限的翻译精力花在质量打磨上。
返回列表