
你會用什麼工具來翻譯字幕檔案呢?機翻字幕很差,AI 翻譯又限制很多……
有時候我們會有翻譯字幕的需求,例如把已經做好的影片從原本的語言翻譯成另一種語言,如此一來你的影片就可以同時擁有多國字幕,增加來自不同國家的觀眾。
在以前,翻譯字幕可是個大工程,就像書籍譯者一樣,字幕譯者同樣是影片的重要貢獻者,影片大賣受到好評,字幕譯者也功不可沒。
現在的翻譯工具越來越強大,許多譯者初翻也會利用翻譯工具來作,然後在校對時才會人為介入修改翻譯內容以提升翻譯的精確度。AI 時代後翻譯的工具更是百花齊放,AI 憑藉著強大的 LLM,大多數的書籍文件都可以快速又輕易的翻譯。
文件可以翻譯,照理來說字幕翻譯應該也可以吧?只是,AI 工具如果是要針對內含時間軸的字幕檔案,那幾乎都是要收費的。而且實際算下來翻一部超過兩個小時的影片要價不低……翻出來的品質如果沒有專業用詞可能還馬馬虎虎,但是如果有專業用詞,就還需要花時間校對……
如果你用 ChatGPT 的話
ChatGPT 翻譯很強大,但是如果要用 ChatGPT 來翻譯字幕,你把字幕拷貝貼到 ChatGPT 上進行翻譯會發生什麼事呢?答案是它會故意做一些手腳(不讓你把它當成翻譯工具來用),我觀察到 ChatGPT 會故意把有些斷句、不同時間軸的用詞,用合併的方式來翻完字幕,然後你的時間軸、翻譯就會和原文字幕對不起來。

假如你還是要用 ChatGPT 來翻譯,你就必需要把它拆解成幾個流程來操作。
先把字幕文字與時間碼分離
這邊假設是使用最開放、相容性最好的 srt 字幕。
因為 ChatGPT 無法理解時間碼,所以你需要把文字和時間碼分開,時間碼的格式像下面這樣:
1
00:00:49,424 --> 00:00:51,969
"I'll miss you, Chihiro!"
2
00:00:55,013 --> 00:00:57,224
"Stay well... Risa."
3
00:01:07,067 --> 00:01:09,403
Chihiro? We're almost there.
4
00:01:09,778 --> 00:01:11,822
This is really the middle of nowhere.
必須先把文字分割成一句一句的段落,最後把文字存成 txt 格式,然後用程式迴圈透過 ChatGPT 的 API 來送出翻譯。
"I'll miss you, Chihiro!"
"Stay well... Risa."
Chihiro? We're almost there.
This is really the middle of nowhere.
不過,如果你處理文字的技術能力不夠,要分離字幕和時間碼就會是一項大挑戰。(反正不可能是一行一行拷貝貼)
把文字分段形式送給 ChatGPT
由於 ChatGPT 有 Token 限制,付費的用量會比較充裕但還是有限制,總之,就像我前面說的,你要將文字分割之後多次傳送,目前來說除非是自己寫程式或是利用 ChatGPT API,但這部分是不在 ChatGPT Plus 的方案裡面的,除非你預算充足,否則用 ChatGPT 的 API 會需要額外的預算。
官方建議方式
在 OpenAI API 的手冊中查到,可以用 Python 讀取 SRT → 依「字數/估算 token」把多個字幕行分批 → 逐批送到 OpenAI Responses API 翻譯 → 再組回 SRT。流程大致上是(含錯誤重試、速率控制、JSON 輸出防亂序)。
把 N 行字幕文字打包成一個 JSON 陣列,讓 AI 模型依照你對等的 JSON 陣列來翻譯,這樣可以避免順序亂掉。
用「估算 token」或「字元數」湊批,別超過模型情境長度與輸出上限。雖然 GPT-4.1 系列標稱長情境(最高可到 1M tokens 的版本),實際可用上限依帳號/部署而異,保守抓小一點比較穩。
參考資料:Introducing GPT-4.1 in the API
總之,這個方式除了需要另外付錢之外,你還要自己寫程式,最後再把時間碼和文字打包回去。
我比較偏好用 Google 翻譯
如果一樣要寫程式,那我會偏好改用 Google 翻譯。
「Google 翻譯」大家都用過,我們可以貼文字讓「Google 翻譯」來替我們快速的翻譯文字,但是如果要翻字幕的話,每次「Google 翻譯」是有長度和取用時間限制的,官方的說法是:建議不要超過5,000 個字元(碼位)。
英文一個字母、數字、標點:1 code unit,中文、日文、韓文每個字:2 code units;由此得知中文段落約 2,000–2,500 字,英文段落約 4,000–5,000 字,如果考慮混合中英情況取中間值,可以抓約 3,000–4,000 code units。(不過實測 Web 翻譯那個介面可能更少)
官方詳細的配額請看這邊:https://cloud.google.com/translate/quotas?hl=zh-tw
你要用 Google Web 翻譯來逐行翻譯可能會很燃燒生命。

換個思維,那如果用程式去串「Google 翻譯」呢?
如果你和我一樣,想到用分段的方式去取用免費的資源來分段完成這件事情,我認為這是一個可行的開發邏輯。程式和技術本來就是堆疊完成的,多加練習未來你可能不只是一個開發者,也可能是一個開發團隊的 PM。

分析如何取用資源的方式
你注意看「Google 翻譯」的 URL:
https://translate.google.com.tw/?source=gtx&sl=en&tl=zh-TW&text=good&op=translate
你會發現網址後面帶著一串像參數的名稱「?sl=en&tl=zh-TW&text=good&op=translate」,這種帶參數的網址叫「web api」,簡單來說就是如果你在網址列輸入你要指定的參數,執行後就會在網頁呈現你的執行結果。我用簡單的解釋讓大家知道這些 api 參數到底帶了些什麼:
- ? 是網址後面要用參數去執行的第一個字串,如果你以後在其他網站的網址上發現有個個問號,就是代表這個網頁有類似 http 請求的用法。
- & 是連接符號,基本上就用來連接多個參數用。
- sl=en 其實就是 source language = en,而這個 en 代表「英文」的意思。
- tl=zh-TW 則是 target language = zh-TW,而這個 zh-TW 代表「台灣繁體中文」的意思。
- text=good 就是 test 是參數本身,而 good 就是你的 source 文字,就是你要翻譯的目標。
- 最後的 op 是「Google 翻譯」Web API 的模式,translate 是即時翻譯、images 是翻譯圖片、docs 是文件檔案、websites 則是翻譯整個網頁。
無論是「en」或是「zh-TW」,他們都是語言代碼(language code)或區域語言代號(locale code),遵循 IETF BCP 47 標準,常用於翻譯、API、網站語系設定等場合。
你可以試著把這個網址貼到你的瀏覽器上面,它就會自動的用 Google 翻譯的 API:
https://translate.google.com/?source=gtx&sl=en&tl=zh-TW&text=Reading%20Publishub%20every%20day%20can%20broaden%20your%20horizons&op=translate
但是如果要用這個方法去和我們的程式串在一起,可能會有點不切實際,因為它是去執行 web 介面(除非你要用無頭瀏覽器)。
google translate web api for json
其實 Google 翻譯除了標準版本的「https://translate.google.com」之外,還有另一個叫「https://translate.googleapis.com」的服務位置。你可以看到他的 Domain 是「googleapis.com」,這代表是 Google 專用的 api 通道。(同樣是免費的)
下面的網址是同樣的翻譯需求,透過「googleapis.com」送出的:
https://translate.googleapis.com/translate_a/single?client=gtx&sl=en&tl=zh-TW&dt=t&q=Reading%20Publishub%20every%20day%20can%20broaden%20your%20horizons
實際執行後會發現我們會下載一個叫「json.txt」,打開之後你會發現翻譯的文字在裡面。
[[["每天閱讀Publishub可以開闊你的視野","Reading Publishub every day can broaden your horizons",null,null,11,null,null,[[]],[[["6ffafab0da7e640be86ac09d0d5e539c","en_zh_2023q1.md"],null,null,null,null,[[[9,8,0]]]]]]],null,"en",null,null,null,null,[]]
我們就是要用這個「googleapis.com」來逐字產生「json.txt」,然後把它合併到字幕檔裡面。
如何把它變成自動化程式?
由於整個程式是用多個邏輯串起來的,詳細講起來比較複雜,我直接提供整段程式,你們可以拷貝後,貼到記事本工具並儲存名為「srt_translate_free_v0.1b.sh」,如果你不知道要放哪裡,可以先放在桌面上。
另外,如果你要分享這個指令我不反對,但請不要刪掉我前面的版權註記,我會在程式裡面標暗號,但是希望大家能在取用的時候尊重一下創作者,我會非常感激~
#!/bin/bash
# srt_translate_free_v0.1b.sh
# Copyright (c) 2025 Alrin
# 使用方式:
# ./srt_translate_free.sh "/path/來源字幕.srt" ja zh-TW
# ./srt_translate_free.sh "/path/來源字幕.srt" en zh-tw
set -Ee -o pipefail
IFS=$'\n\t'
DEFAULT_SRC="en"
DEFAULT_TGT="zh-TW"
INPUT="${1:-}"
SRC_LANG="${2:-$DEFAULT_SRC}"
TGT_LANG="${3:-$DEFAULT_TGT}"
if [[ -z "$INPUT" ]]; then
echo "用法: bash $0 /path/input.srt [SRC_LANG] [TGT_LANG]"
exit 1
fi
# 語言碼標準化(Bash 3.2 相容)
_lower_tgt="$(printf '%s' "$TGT_LANG" | tr '[:upper:]' '[:lower:]')"
case "$_lower_tgt" in
zh-tw) TGT_LANG="zh-TW" ;;
zh-hk) TGT_LANG="zh-HK" ;;
zh-cn) TGT_LANG="zh-CN" ;;
*) : ;;
esac
if [[ ! -f "$INPUT" ]]; then
echo "❌ 找不到檔案:$INPUT"
exit 1
fi
# (可留可刪)環境檢查
# command -v jq >/dev/null 2>&1 || { echo "❌ 需要安裝 jq(brew install jq)"; exit 1; }
# command -v python3 >/dev/null 2>&1 || { echo "❌ 需要安裝 python3"; exit 1; }
# 讀入並去掉 CR(避免 \r 造成段落判斷異常)
CLEAN_INPUT="$(mktemp)"
tr -d '\r' < "$INPUT" > "$CLEAN_INPUT"
# ✅ 明確指定輸出到來源同一個資料夾
INPUT_DIR="$(cd "$(dirname "$INPUT")" && pwd)" # ★
INPUT_STEM="$(basename "${INPUT%.*}")" # ★
OUTPUT="${INPUT_DIR}/${INPUT_STEM}_zhTW.srt" # ★
# (可選)確認可寫
if [[ ! -w "$INPUT_DIR" ]]; then # ★
echo "❌ 目的資料夾不可寫:$INPUT_DIR" # ★
rm -f "$CLEAN_INPUT" # ★
exit 1 # ★
fi # ★
SLEEP_SEC="0.25"
echo "🈶 開始翻譯:$INPUT"
echo "(來源:${SRC_LANG:-$DEFAULT_SRC} → 目標:${TGT_LANG:-$DEFAULT_TGT})"
echo "🔨 輸出:$OUTPUT"
: > "$OUTPUT"
# 正規式
TIME_RE='^[0-9]{2}:[0-9]{2}:[0-9]{2},[0-9]{3}[[:space:]]*-->[[:space:]]*[0-9]{2}:[0-9]{2}:[0-9]{2},[0-9]{3}$'
INDEX_RE='^[0-9]+$'
# URL encode(用 argv 傳參避免引號/空白)
urlencode() {
python3 - "$1" <<'PY'
import sys, urllib.parse
print(urllib.parse.quote(sys.argv[1]))
PY
}
# 將 Google Translate 回傳的所有切片串接
gt_free_translate() {
local text="${1-}"
if [[ -z "$text" ]]; then echo ""; return; fi
local q url json out
q="$(urlencode "$text")"
url="https://translate.googleapis.com/translate_a/single?client=gtx&sl=${SRC_LANG}&tl=${TGT_LANG}&dt=t&q=${q}"
json="$(curl -sS "$url" || true)"
out="$(echo "$json" | jq -r '.[0][]?[0] // empty' 2>/dev/null | paste -sd '' - || true)"
if [[ -z "$out" ]]; then
out="$(echo "$json" | jq -r '.[0][0][0]' 2>/dev/null || true)"
fi
if [[ -z "$out" || "$out" == "null" ]]; then echo "$text"; else echo "$out"; fi
}
# 可選:中文行自動略過(避免中翻中)
is_mostly_cjk() {
local s="$1"
local total cjk
total="$(printf '%s' "$s" | awk '{print length}')"
if [[ "${total:-0}" -eq 0 ]]; then echo 1; return; fi
cjk="$(printf '%s' "$s" | perl -CSDA -ne 'use utf8; my $t=$_; $t=~s/[^\p{Han}\p{Hiragana}\p{Katakana}\p{Hangul}]//g; print length($t);')"
awk -v c="${cjk:-0}" -v t="${total:-1}" 'BEGIN{print (t>0 && c/t>0.6)?1:0}'
}
# 區塊累積
block_index=""
block_time=""
block_lines=()
flush_block() {
if [[ -n "$block_index" ]]; then
{
echo "$block_index"
echo "$block_time"
((${#block_lines[@]})) && printf "%s\n" "${block_lines[@]}"
echo
} >> "$OUTPUT"
block_index=""
block_time=""
block_lines=()
fi
}
# 逐行讀入(從已清理 CR 的臨時檔)
while IFS= read -r line || [[ -n "$line" ]]; do
if [[ "$line" =~ $INDEX_RE ]]; then
flush_block
block_index="$line"
elif [[ "$line" =~ $TIME_RE ]]; then
block_time="$line"
elif [[ -z "$line" ]]; then
flush_block
sleep "$SLEEP_SEC"
else
if [[ "$(is_mostly_cjk "$line")" -eq 1 ]]; then
trans="$line"
else
trans="$(gt_free_translate "$line")"
fi
printf "🔹 %s → %s\n" "$line" "$trans"
block_lines+=("$trans")
fi
done < "$CLEAN_INPUT"
flush_block
rm -f "$CLEAN_INPUT"
echo "✅ 翻譯完成:$OUTPUT"
如何使用這個指令?
把指令存檔之後,用 cd 導航到你存檔指令的目錄再執行,或者直接指定執行(指名指令的絕對路徑,用 ./ 去執行它):
cd /Users/alrin/Desktop/
alrin@Alrin-MBP-16 Desktop % ./srt_translate_free_v0.1b.sh [你的來源字幕.srt] [來源語言] [翻譯語言]
假設,我的來源檔案是桌面上的「matrix.srt」,我想要英翻中,於是:
./Users/alrin/Desktop/srt_translate_free_v0.1b.sh Users/alrin/Desktop/matrix.srt en zh-tw
然後你會看到它開始逐行自動翻譯(用 Google 翻譯的 API),翻譯完成會在你原本的字幕附近新增一個叫「*_zhTW.srt」

翻譯結果比較,左邊是 AI 工具(MacWhisper)直接聽譯,右邊是我的程式「srt_translate_free_v0.1b.sh」英翻中。兩邊用詞會稍微有一點不一樣。不過比較起來會發現用 Google 翻字作逐字幕翻譯,結果並沒有比較差。
兩邊都會有需要人工潤飾的地方,不存在完美翻譯。

如何修改翻譯速度?
由於我這邊是使用逐字翻譯(迴圈)去處理,所以每一句對於 Google 都是 Web 請求,所以如果每秒鐘執行一句翻譯,2000 句的字幕就是耗時約 33 分鐘。
如果你想要加快翻譯的速度,你就要調整中間的間隔時間。你可以在我的程式碼的 58 行找到「SLEEP_SEC="0.25"」,雖然你縮短這個間隔的時間秒數,它的翻譯速度就會變快,但是調間隔時間調得太短就會有被 Google 阻擋的風險,要不要冒這個險就看各位自己了~
或者,乾脆用 Whisper 直接跨過所有流程
如果你有用 Whisper 這一類的工具(我用的是 MacWhisper),那麼就是連 Code 都不用寫,也不用理會字幕檔案要如何翻譯,就是導入影片後,並且在翻譯處直接選擇你要翻譯的目的語言即可。

輸出的話,MacWhisper 還可以讓你輸出原文字幕或是翻譯字幕,因為可以同時取得,所以這很適合懶人使用。
MacWhisper 有一個大缺點,就是在打軸的時間並不精確,常常會誤差 1-2 秒,如果你已經有字幕了,原本的時間軸可能會比 MacWhisper 打出來的還精確,所以這種情況用我的自動化工具來做好像也還挺不錯的~
最後
我自己手上其實寫了蠻多開源的工具,也寫了很多 InDesign 的 JSX,如果有人有意願付費閱讀,請讓我知道,我很樂意分享我寫的小工具,有一點回饋才會有寫新程式分享的動力。XD