跳至主要内容

Ollama:用本地 LLM 翻譯字幕

前情提要

SRT 字幕怎麼來,可參考「Whisper:影音轉錄字幕檔」筆記。

這篇記錄怎麼用 Ollama 跑本地模型,把一支日文 SRT 字幕翻成通順的中文。

以前我都丟到 translatesubtitles.co 套 Google 翻譯,一秒完成,但翻得不精準、也不吃上下文。

改用本地 LLM 後品質好很多,關鍵在幾件事:提示詞要寫好、要分批帶上下文、還要有夠強的重試與補齊機制。

環境準備

開始前需要兩樣東西:

  1. Ollama:在自己電腦上跑本地模型的工具,到官網下載安裝即可。
  2. 一個語言模型:裝好 Ollama 後,在終端機執行 ollama pull qwen3:8b 把模型抓下來(也可以換成別的,用 ollama list 可看已安裝的模型)。
資訊

不確定自己的電腦能跑多大的模型?可以參考「Can I Run AI locally?」網站來挑。

設計重點

1. 提示詞要寫好

字幕翻譯最大的麻煩是「對齊」,小模型常會偷偷合併、拆分或漏句,導致譯文對不回原本的時間軸。

解法是給每句一個 [編號],並在系統提示裡把規則講死:

BATCH_SYSTEM_STRICT = (
"你是專業的日中字幕譯者,把日文字幕翻成自然、口語的繁體中文(台灣用語)。\n"
"最重要:輸出必須「全部是繁體中文」,連一個日文假名(平假名、片假名)都不可以出現;"
"每一句都要翻,再短、重複、或看起來沒意義的句子也要照樣翻成中文,絕不可保留日文原文。\n"
"- 只翻譯有 [編號] 的句子,用相同編號逐句輸出,格式「[編號] 譯文」。\n"
"- 標「(參考,勿翻)」的句子只是上下文,不要輸出。\n"
"- 編號數量與順序和輸入完全一致。"
)

收到回覆後用正規表達式把 [編號] 譯文 拆回來,編號對得上才算數:

m = re.match(r"\s*\[(\d+)\]\s*(.*)", line)

2. 要分批,並帶「鄰居」上下文

一次送一句太慢、又完全沒有上下文;一次送整部又會超出小模型能穩定處理的長度。

折衷是一次送一批帶編號的句子,並在前後各附幾句「參考、勿翻」的句子補接縫上下文:

BATCH_SIZE = 20          # 一批幾句(越大越快、上下文越多,但對齊風險略升)
CONTEXT_OVERLAP = 3 # 前後各附幾句當「參考、勿翻」,補接縫上下文

附帶一提,這個上下文是取「該組句子真正的左右鄰居」,而不是固定的批次邊界——後面補漏、只送剩下幾句時,一樣拿得到貼近的語境。

3. 對不上就層層補,補到近乎全翻

就算提示寫得再嚴,小模型偶爾還是會對不上。這裡用好幾層保險,一層補不到就換下一層:

MAX_BATCH_RETRY = 3      # 單輪內對齊失敗時,把缺的句子湊成小批補翻幾次
MAX_ROUNDS = 3 # 整批最多重跑幾輪(全翻完或某輪零進展就提前停)
  1. 護欄:把「原樣照抄日文」「整句還是假名」的回覆擋掉,不當成翻好,交給後面幾層補。
  2. 補漏重試:把沒對上的那幾句湊成小批再送;剩不到幾句時乾脆一句一句單獨送(單句不可能對齊錯)。
  3. 多輪重跑:整批掃完還有漏的就再跑一輪,直到全翻完、或某一輪一句都補不到才停。
  4. 逐句保險:多輪跑完仍有極少數對不齊的,最後強制逐句補一次。
  5. 斷點續傳:每翻完一批就把進度寫進 progress.json,同時覆蓋整支 SRT。過程隨時中斷都不會白做,任何時候打開輸出檔都是完整可播的字幕(沒翻到的顯示日文原文);下次再跑自動只補沒翻到的。

補充:換成線上 LLM 呢?

如果改用 ChatGPT、Gemini 這類線上大模型來翻,整套思路其實一樣,只是因為大模型「聽話」很多,可以大幅簡化:

  • 提示詞:核心要求不變(逐句對編號、保留時間軸、口語自然),但本地那套層層防呆(「編號絕對不可合併拆分漏句」「參考、勿翻」)是用來補小模型的對齊能力差,大模型多半用不上,寫簡短一點就好。
  • 分批:大模型的上下文很長,一支影片的字幕常常一次就塞得下,批次可以放到幾百句、批數大幅變少。但別期待「一批搞定整部」,輸出行數一多,模型還是可能中途偷懶、合併或截斷,所以還是會分批,只是批次大得多。
  • 重試:大模型對齊失敗率低很多,但不是零。差別在通常是手動回一句「第幾到第幾句漏了,補一下」,而不像本地要寫自動補漏、多輪重跑與斷點續傳。

一句話:框架同形,但可以瘦身。本地這套之所以工程味這麼重,多半是在補小模型的不可靠;換成線上大模型,這些保險機制大多能拿掉。代價則是內容容易被審查、字幕也得上傳第三方。

怎麼跑

我把它做成一支互動腳本,用 IDLE 開啟後直接 F5 就能跑:

  1. 列出資料夾裡的 .jp.srt 來源檔:輸入號碼翻單一檔,或直接按 Enter 全部依序翻
  2. 選模型與思考模式(整批只選一次)。
  3. 輸出成品 input.srt(與影片同名,播放器可自動掛載),原文 input.jp.srt 不會被覆蓋。

沒翻完就中斷也沒關係,靠 progress.json 續傳,再跑一次會自動補掉剩下的句子。

完整腳本是請 Claude 寫的,上面這些設計片段才是重點;建議照自己的需求修改,或直接請 LLM 幫你重新生成一份。