Ollama:用本地 LLM 翻譯字幕
SRT 字幕怎麼來,可參考「Whisper:影音轉錄字幕檔」筆記。
這篇記錄怎麼用 Ollama 跑本地模型,把一支日文 SRT 字幕翻成通順的中文。
以前我都丟到 translatesubtitles.co 套 Google 翻譯,一秒完成,但翻得不精準、也不吃上下文。
改用本地 LLM 後品質好很多,關鍵在幾件事:提示詞要寫好、要分批帶上下文、還要有夠強的重試與補齊機制。
環境準備
開始前需要兩樣東西:
- Ollama:在自己電腦上跑本地模型的工具,到官網下載安裝即可。
- 一個語言模型:裝好 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 # 整批最多重跑幾輪(全翻完或某輪零進展就提前停)
- 護欄:把「原樣照抄日文」「整句還是假名」的回覆擋掉,不當成翻好,交給後面幾層補。
- 補漏重試:把沒對上的那幾句湊成小批再送;剩不到幾句時乾脆一句一句單獨送(單句不可能對齊錯)。
- 多輪重跑:整批掃完還有漏的就再跑一輪,直到全翻完、或某一輪一句都補不到才停。
- 逐句保險:多輪跑完仍有極少數對不齊的,最後強制逐句補一次。
- 斷點續傳:每翻完一批就把進度寫進
progress.json,同時覆蓋整支 SRT。過程隨時中斷都不會白做,任何時候打開輸出檔都是完整可播的字幕(沒翻到的顯示日文原文);下次再跑自動只補沒翻到的。
補充:換成線上 LLM 呢?
如果改用 ChatGPT、Gemini 這類線上大模型來翻,整套思路其實一樣,只是因為大模型「聽話」很多,可以大幅簡化:
- 提示詞:核心要求不變(逐句對編號、保留時間軸、口語自然),但本地那套層層防呆(「編號絕對不可合併拆分漏句」「參考、勿翻」)是用來補小模型的對齊能力差,大模型多半用不上,寫簡短一點就好。
- 分批:大模型的上下文很長,一支影片的字幕常常一次就塞得下,批次可以放到幾百句、批數大幅變少。但別期待「一批搞定整部」,輸出行數一多,模型還是可能中途偷懶、合併或截斷,所以還是會分批,只是批次大得多。
- 重試:大模型對齊失敗率低很多,但不是零。差別在通常是手動回一句「第幾到第幾句漏了,補一下」,而不像本地要寫自動補漏、多輪重跑與斷點續傳。
一句話:框架同形,但可以瘦身。本地這套之所以工程味這麼重,多半是在補小模型的不可靠;換成線上大模型,這些保險機制大多能拿掉。代價則是內容容易被審查、字幕也得上傳第三方。
怎麼跑
我把它做成一支互動腳本,用 IDLE 開啟後直接 F5 就能跑:
- 列出資料夾裡的
.jp.srt來源檔:輸入號碼翻單一檔,或直接按 Enter 全部依序翻。 - 選模型與思考模式(整批只選一次)。
- 輸出成品
input.srt(與影片同名,播放器可自動掛載),原文input.jp.srt不會被覆蓋。
沒翻完就中斷也沒關係,靠 progress.json 續傳,再跑一次會自動補掉剩下的句子。
完整腳本是請 Claude 寫的,上面這些設計片段才是重點;建議照自己的需求修改,或直接請 LLM 幫你重新生成一份。