建字幕翻譯工作流:從SRT解析到批量翻譯)
字幕翻譯是個很典型的場景人工翻譯整集速度太慢直接丟給通用翻譯工具又容易翻譯腔。這次我們以《惡魔君 1989》第28集為例講一條可以直接落地的英轉(zhuǎn)中字幕流水線核心工具是 DeepSeek 的 API。這個需求的本質(zhì)不復(fù)雜把英文字幕文件解析出來喂給大模型翻譯成中文再按原時間軸寫回 SRT。真正的難點在于批量處理穩(wěn)不穩(wěn)定、角色名是否一致、分多少條字幕一起翻譯、失敗之后怎么重試?!稅耗Ь?1989》是 1989 年的老動畫如果靠人工補字幕或翻譯工作量不低用 DeepSeek 做初譯再人工校對效率會明顯好于從零開始。這篇文章不是單集教程而是把一條可以復(fù)用的字幕翻譯工作流拆開講??赐昴阒辽倌茏鋈碌谝挥?DeepSeek 官方 API 翻譯單條字幕文本第二用一個 Python 腳本批量處理整集 SRT第三把腳本擴展到多集批量任務(wù)并做基本的質(zhì)量校驗。整個流程不需要本地 GPU不需要部署模型一臺普通電腦加一個 API Key 就能跑。1. 核心能力速覽能力項說明項目類型字幕翻譯工作流英文 - 中文AI 服務(wù)DeepSeek API輸入格式SRT 英文字幕輸出格式SRT 中文字幕是否需要 GPU不需要純 API 調(diào)用是否支持批量支持可循環(huán)處理多集字幕術(shù)語一致性通過術(shù)語表 分塊上下文控制啟動方式Python 腳本運行適合場景老番補全、字幕組初譯、雙語字幕制作從這張表可以看出來這不是一個需要高配置硬件部署的項目而是一個“調(diào)接口 處理文件”的工程腳本。你想用 DeepSeek 做字幕翻譯最關(guān)心的不是顯存而是 API 調(diào)用是否穩(wěn)定、Prompt 是否能把字幕格式約束住、批量任務(wù)能不能斷點續(xù)跑。2. 適用場景與使用邊界2.1 適合誰用這套工作流最先適合三類人個人譯者和追番用戶手里有英文字幕想快速得到中文字幕做參考。字幕組成員用 DeepSeek 做初譯人工在校對階段統(tǒng)一術(shù)語和潤色可以省不少時間。老番補全整理者像《惡魔君 1989》這類老動畫如果字幕資源只有英文批量跑一遍中文字幕是性價比最高的方式。2.2 不適合什么場景我不建議把 DeepSeek 機翻字幕直接當成成品發(fā)布。字幕翻譯涉及口語、語氣、文化梗和角色設(shè)定機器結(jié)果只能算初稿。如果對譯文質(zhì)量要求很高或者素材涉及商業(yè)版權(quán)必須走完整人工校對流程。2.3 版權(quán)與合規(guī)邊界這里要特別提醒字幕文件本身有版權(quán)視頻畫面和官方字幕也不能隨意傳播。你可以把《惡魔君 1989》第28集當作個人學(xué)習(xí)和技術(shù)演示的輸入但公開發(fā)布、二次分發(fā)或商用前需要確認片源和字幕的授權(quán)情況。涉及人物肖像、聲音、角色素材的場景同樣要先取得授權(quán)。3. 環(huán)境準備與前置條件這套方案不需要本地顯卡也不需要安裝 CUDA、PyTorch 之類的東西。環(huán)境要求非常低。3.1 軟件依賴Python 3.9 以上requests 庫發(fā)送 HTTP 請求一個可以正常訪問 DeepSeek API 的網(wǎng)絡(luò)環(huán)境DeepSeek API Key安裝依賴只需要一條命令pip install requests如果你習(xí)慣用 OpenAI 風(fēng)格的 Python SDK也可以安裝 openai 庫然后用 base_url 指向 DeepSeek 的接口。不過為了減少依賴這篇文章使用 requests 直接調(diào)用 HTTP 接口。3.2 獲取 API Key登錄 DeepSeek 開放平臺在控制臺創(chuàng)建 API Key。創(chuàng)建之后把 Key 存到環(huán)境變量里不要直接硬編碼在腳本中避免提交到 Git 倉庫后泄露。在 Linux / macOS 下可以臨時導(dǎo)出export DEEPSEEK_API_KEYsk-xxxxxxxxWindows PowerShell 下可以寫$env:DEEPSEEK_API_KEY sk-xxxxxxxx腳本內(nèi)部通過os.environ.get(DEEPSEEK_API_KEY)讀取沒有 Key 時直接報錯。3.3 目錄結(jié)構(gòu)建議按下面的目錄結(jié)構(gòu)組織文件subtitle_translator/ ├── en_srt/ # 英文原版 SRT ├── zh_srt/ # 生成的中文 SRT ├── output/ # 日志和中間結(jié)果 ├── translate.py # 主腳本 └── glossary.txt # 術(shù)語表原始英文字幕只保留在en_srt/生成結(jié)果單獨輸出到zh_srt/避免覆蓋原始文件。4. 字幕翻譯工作流設(shè)計4.1 SRT 文件格式SRT 是最常見的字幕格式結(jié)構(gòu)固定1 00:00:01,000 -- 00:00:04,000 Hello, world! 2 00:00:04,500 -- 00:00:07,000 This is a subtitle.每個字幕塊包含序號、時間軸和文本。翻譯腳本要做的事情就是保留序號和時間軸不變只把文本替換成中文。4.2 翻譯流程整體流程分五步讀取 SRT 文件并按塊解析。把字幕文本按批次組合成一個翻譯請求。調(diào)用 DeepSeek API 獲取中文結(jié)果。將結(jié)果按編號映射回原字幕塊。重新生成 SRT 文件并輸出到zh_srt/。4.3 分塊策略分塊大小直接影響翻譯質(zhì)量和穩(wěn)定性。建議每批 10 到 20 條字幕一起發(fā)送而不是把整集字幕一次性塞給模型。原因有兩個模型輸入長度有限整集字幕量可能超過上下文窗口。分塊翻譯可以保留上下文字幕之間如果有對話承接批量翻譯能減少前后不連貫的問題。分塊太小也有問題比如每次只翻譯 1 到 2 條模型容易丟失前面提到的角色名和設(shè)定而且請求次數(shù)會大幅增加成本變高限流風(fēng)險也變大。5. DeepSeek API 調(diào)用與字幕腳本實現(xiàn)這一章直接給代碼。示例中的接口地址和模型名請以 DeepSeek 官方文檔為準我這里使用的是 OpenAI 兼容格式的通用調(diào)用方式。5.1 基礎(chǔ) Chat 接口調(diào)用先寫一個最基礎(chǔ)的 Chat 接口封裝import os import requests DEEPSEEK_API_URL https://api.deepseek.com/chat/completions API_KEY os.environ.get(DEEPSEEK_API_KEY, ) if not API_KEY: raise RuntimeError(DEEPSEEK_API_KEY not set) def chat_completion(messages, temperature0.3, timeout120): resp requests.post( DEEPSEEK_API_URL, headers{ Authorization: fBearer {API_KEY}, Content-Type: application/json, }, json{ model: deepseek-chat, messages: messages, temperature: temperature, }, timeouttimeout, ) resp.raise_for_status() data resp.json() return data[choices][0][message][content]temperature 設(shè)置到 0.3 左右可以讓翻譯更穩(wěn)定減少模型發(fā)揮過頭的概率。字幕翻譯不是創(chuàng)作型任務(wù)隨機性越低越好。5.2 翻譯 Prompt 設(shè)計字幕翻譯的 Prompt 要強調(diào)三件事只輸出翻譯結(jié)果、保持編號格式、按術(shù)語表處理專有名詞。下面是一個推薦寫法GLOSSARY \ Akuma-kun - 惡魔君 Mephisto - 梅菲斯托 def build_translate_prompt(numbered_text, glossaryGLOSSARY): return f你是一名專業(yè)的字幕翻譯。請把下面的英文字幕翻譯成簡體中文。 要求 1. 保持口語化符合中文表達習(xí)慣 2. 專有名詞按術(shù)語表翻譯 3. 保留原文語氣和情感 4. 只輸出翻譯后的文本格式保持 [編號] 譯文 5. 不要添加任何解釋 術(shù)語表 {glossary} 待翻譯內(nèi)容 {numbered_text} 這里有一個關(guān)鍵點給模型輸入時先給每條字幕加編號比如[1] Hello、[2] World要求模型按同樣格式返回。這樣腳本解析結(jié)果時能精確還原到原字幕塊不會因為翻譯順序錯亂導(dǎo)致字幕和臺詞對不上。術(shù)語表的作用是統(tǒng)一角色名。老動畫中的角色名很容易出現(xiàn)“前面翻成惡魔君、后面翻成小惡魔”的問題提前定義術(shù)語表可以明顯減少這種錯誤。具體角色名需要根據(jù)《惡魔君 1989》的實際片源補充腳本里先用示例占位。5.3 批量翻譯與解析翻譯一批字幕時先把多條字幕拼成一個請求再解析模型返回值import re import time def translate_batch(texts, glossaryGLOSSARY, max_retries3): numbered_text \n.join(f[{i}] {t} for i, t in enumerate(texts)) prompt build_translate_prompt(numbered_text, glossary) messages [ {role: system, content: 你是一名專業(yè)的字幕翻譯助手擅長英文到中文的影視字幕翻譯。}, {role: user, content: prompt}, ] last_error None for attempt in range(max_retries): try: output chat_completion(messages) return parse_numbered_output(output) except Exception as e: last_error e print(ftranslate_batch retry {attempt 1}: {e}) time.sleep(2) raise RuntimeError(ftranslate failed after {max_retries} retries: {last_error}) def parse_numbered_output(output: str): result {} for line in output.strip().split(\n): line line.strip() m re.match(r^\[(\d)\]\s*(.*)$, line) if m: result[int(m.group(1))] m.group(2).strip() return result解析時使用正則^\[(\d)\]\s*(.*)$提取編號和譯文。如果模型沒有嚴格按格式輸出這部分解析可能丟內(nèi)容所以parse_numbered_output只返回能解析到的條目后續(xù)腳本再結(jié)合原字幕塊做填充。5.4 完整字幕翻譯腳本把解析、翻譯、寫回整合在一起就是一個可以直接跑整集字幕的腳本。下面給出完整示例import os import re import time import glob import requests DEEPSEEK_API_URL https://api.deepseek.com/chat/completions API_KEY os.environ.get(DEEPSEEK_API_KEY, ) if not API_KEY: raise RuntimeError(DEEPSEEK_API_KEY not set) GLOSSARY \ Akuma-kun - 惡魔君 Mephisto - 梅菲斯托 def parse_srt(content: str): blocks [] parts content.strip().split(\n\n) for part in parts: lines part.strip().split(\n) if len(lines) 2: continue blocks.append({ index: lines[0].strip(), time: lines[1].strip(), text: \n.join(lines[2:]).strip(), }) return blocks def build_srt(blocks): return \n\n.join( f{b[index]}\n{b[time]}\n{b[text]} for b in blocks ) \n def chat_completion(messages, temperature0.3, timeout120): resp requests.post( DEEPSEEK_API_URL, headers{ Authorization: fBearer {API_KEY}, Content-Type: application/json, }, json{ model: deepseek-chat, messages: messages, temperature: temperature, }, timeouttimeout, ) resp.raise_for_status() data resp.json() return data[choices][0][message][content] def build_translate_prompt(numbered_text, glossaryGLOSSARY): return f你是一名專業(yè)的字幕翻譯。請把下面的英文字幕翻譯成簡體中文。 要求 1. 保持口語化符合中文表達習(xí)慣 2. 專有名詞按術(shù)語表翻譯 3. 保留原文語氣和情感 4. 只輸出翻譯后的文本格式保持 [編號] 譯文 5. 不要添加任何解釋 術(shù)語表 {glossary} 待翻譯內(nèi)容 {numbered_text} def parse_numbered_output(output: str): result {} for line in output.strip().split(\n): line line.strip() m re.match(r^\[(\d)\]\s*(.*)$, line) if m: result[int(m.group(1))] m.group(2).strip() return result def translate_batch(texts, glossaryGLOSSARY, max_retries3): numbered_text \n.join(f[{i}] {t} for i, t in enumerate(texts)) prompt build_translate_prompt(numbered_text, glossary) messages [ {role: system, content: 你是一名專業(yè)的字幕翻譯助手擅長英文到中文的影視字幕翻譯。}, {role: user, content: prompt}, ] last_error None for attempt in range(max_retries): try: output chat_completion(messages) return parse_numbered_output(output) except Exception as e: last_error e print(ftranslate_batch retry {attempt 1}: {e}) time.sleep(2) raise RuntimeError(ftranslate failed after {max_retries} retries: {last_error}) def translate_srt_file(srt_path, output_dir, batch_size10): with open(srt_path, r, encodingutf-8) as f: content f.read() blocks parse_srt(content) total len(blocks) translated_count 0 for i in range(0, total, batch_size): batch blocks[i:i batch_size] texts [b[text] for b in batch] result translate_batch(texts) for j, block in enumerate(batch): if j in result: block[text] result[j] else: print(fwarning: missing translation for block {block[index]}) translated_count len(batch) print(fprogress: {translated_count}/{total}) output_srt build_srt(blocks) os.makedirs(output_dir, exist_okTrue) name os.path.splitext(os.path.basename(srt_path))[0] out_path os.path.join(output_dir, f{name}.zh.srt) with open(out_path, w, encodingutf-8) as f: f.write(output_srt) return out_path if __name__ __main__: for srt_file in glob.glob(./en_srt/*.srt): print(fprocessing {srt_file}) output translate_srt_file(srt_file, ./zh_srt) print(fsaved to {output})腳本的工作過程是掃描en_srt/下所有 SRT 文件逐集解析、逐批翻譯最終在zh_srt/下生成同名.zh.srt文件。運行方式很簡單python translate.py第一次運行建議先放一個 SRT 文件到en_srt/不要直接丟幾十集進去。先確認單集能跑通再擴大批量。6. 批量任務(wù)與穩(wěn)定運行6.1 多集批量處理上面的腳本已經(jīng)能掃描目錄下所有 SRT 文件。如果要處理《惡魔君 1989》全集直接把多集英文字幕放進en_srt/即可。腳本會逐個文件執(zhí)行互不干擾。批量處理時建議在腳本外層加一個簡單的計數(shù)輸出方便觀察進度。更完整的方式是寫一個任務(wù)清單文件記錄每集狀態(tài)[ {episode: ep01, status: done, output: zh_srt/ep01.zh.srt}, {episode: ep02, status: pending, output: } ]腳本每次啟動前先讀狀態(tài)文件已經(jīng)處理成功的跳過只處理 pending 和 failed 的任務(wù)。這樣可以避免中途斷網(wǎng)導(dǎo)致全部重跑。6.2 斷點續(xù)跑翻譯幾十集字幕時最常見的失敗原因是網(wǎng)絡(luò)超時或 API 限流。在translate_batch中已經(jīng)加了重試但整集跑一半的時候進程崩掉仍然需要斷點續(xù)跑。簡單做法是在translate_srt_file里翻譯完一批就把當前結(jié)果寫入臨時緩存文件比如output/ep01.partial.json。重新啟動時先檢查緩存已經(jīng)完成的批次不重新翻譯。6.3 API 限流與成本控制DeepSeek API 是計費服務(wù)批量任務(wù)需要控制請求頻率和成本。分塊大小是成本的關(guān)鍵變量每批 10 條字幕比每批 5 條省一半請求數(shù)但單次請求 token 更長。建議第一批測試時用小樣本估算成本再決定每批條數(shù)。如果同時跑多集可以在每批之間加一個短延時避免觸發(fā)限流time.sleep(0.5)單集翻譯幾十批字幕耗時通常取決于 API 響應(yīng)速度。具體耗時和費用以實際控制臺數(shù)據(jù)為準不要在生產(chǎn)流程里假設(shè)固定值。7. 效果驗證與質(zhì)量控制機器翻譯做完不能直接拿去用。至少要做三輪檢查。7.1 時間軸一致性檢查腳本本身不修改時間軸但需要確認生成文件里每條字幕的序號和時間軸仍在。最簡單的方法是打開生成的中文 SRT隨機挑幾個時間點把中文和英文放在同一個播放器里對比。也可以用腳本做格式化檢查解析生成后的 SRT確認塊數(shù)與原文件一致并且每個塊的時間軸格式正確。7.2 術(shù)語一致性檢查檢查角色名、地名、專有名詞是否統(tǒng)一。用術(shù)語表做一次自動掃描def check_glossary(srt_text, glossary_pairs): issues [] for en_term, zh_term in glossary_pairs.items(): if en_term in srt_text: issues.append(f{en_term} 未被替換為 {zh_term}) return issues如果術(shù)語表中出現(xiàn)英文原文殘留說明術(shù)語表沒有完全生效需要在 Prompt 中加強約束或調(diào)整術(shù)語表寫法。7.3 漏譯與錯譯排查漏譯是最容易發(fā)現(xiàn)的問題。腳本在translate_srt_file中已經(jīng)打印了missing translation警告。如果警告較多說明模型返回格式不穩(wěn)定或分塊過大導(dǎo)致部分內(nèi)容被截斷。錯譯排查需要人工抽查。建議按下面維度抽檢對話是否通順是否存在翻譯腔。語氣是否符合角色性格。長句是否被截斷或丟失信息。文化梗是否被直譯成無法理解的內(nèi)容。抽查比例不用太高一集字幕抽 20 到 30 條就足以判斷整體質(zhì)量。8. 常見問題與排查方法問題現(xiàn)象可能原因排查方式解決方案API 返回 401API Key 錯誤檢查環(huán)境變量是否設(shè)置重新創(chuàng)建 Key 并導(dǎo)出API 返回 402 或余額不足賬戶余額不足查看 DeepSeek 控制臺充值或更換 KeyAPI 返回 429請求頻率過高或限流查看返回 header 和日志增加 sleep 時間降低并發(fā)翻譯后字幕為空模型沒有按 [編號] 格式返回打印原始返回內(nèi)容調(diào)整 Prompt或增加重試時間軸錯位SRT 解析失敗打開原始文件檢查格式確保使用標準 SRT 格式留意空行批量任務(wù)中途中斷網(wǎng)絡(luò)超時或進程崩潰查看日志和緩存文件加斷點續(xù)跑緩存已完成批次角色名前后不一致術(shù)語表未生效掃描輸出中的英文殘留補充術(shù)語表并在 Prompt 中強調(diào)翻譯結(jié)果口語化不足temperature 過高檢查參數(shù)降低 temperature 到 0.3 以下如果翻譯批次總是失敗可以把max_retries提高到 5并在重試之間加指數(shù)退避sleep_time 2 ** attempt time.sleep(sleep_time)太多次重試仍然失敗時記錄當前批次到日志文件不要無限阻塞主流程。9. 最佳實踐與總結(jié)這套 DeepSeek 字幕翻譯工作流最值得復(fù)用的地方不是某一段 Prompt而是“保持時間軸不變 編號批量翻譯 術(shù)語表約束 斷點續(xù)跑”的組合方式。用同樣的思路不只是《惡魔君 1989》任何有英文字幕的視頻都能納入處理。第一次跑的時候建議只拿第28集的一小段字幕測試比如前 50 條。確認能跑通、返回格式穩(wěn)定再放整集。整集通過后再擴展到多集批量處理。這樣能把成本浪費控制在最低。最容易踩的坑有三個一是模型返回格式不固定導(dǎo)致解析失敗二是分塊太大導(dǎo)致部分內(nèi)容缺失三是忽略術(shù)語表導(dǎo)致角色名前后不一致。這三個問題都可以通過腳本里的打印日志盡早發(fā)現(xiàn)。接下來可以做的擴展方向包括接入術(shù)語庫文件讓非技術(shù)人員也能維護角色名增加雙語對照字幕輸出把腳本封裝成簡單 Web 服務(wù)上傳 SRT 就能返回中文 SRT。如果你平時做字幕相關(guān)工作建議把這套腳本保存下來作為本地字幕初譯的標準工具。