最近在做视频精读内容时,一直在跟字幕较劲:普通 SRT 字幕只能整句显示、整句消失,想做到“单词跟着发音逐个高亮”,要么手动打关键帧到崩溃,要么找遍全网都没有顺手的工具。直到接触到“词级双语字幕”这个方向,才发现原来字幕可以拆到每个单词、每个读音,配合中英对照,整个做字幕的过程像水果忍者切水果一样顺畅干净。
这篇文章不绑定某一个具体产品,而是把这类“词级双语字幕制作软件”背后的能力拆开讲清楚:它解决什么问题、底层用到了哪些技术、字幕格式如何承载词级时间轴,以及如何用 Python 完整实现一条从视频到词级双语 ASS 字幕的生产链路。适合正在做语言学习视频、外刊精读、双语影视剪辑的创作者,也适合想了解语音识别与字幕文件格式的开发者。
1. 词级双语字幕是什么,解决什么问题
1.1 从传统字幕到词级双语字幕
传统字幕以“句子”为最小单位。一条 SRT 记录里有开始时间、结束时间、一行文本,播放器到点就把整行字幕显示出来,到点再整行消失。这种模式对“看懂剧情”完全够用,但对语言学习、发音模仿、词汇记忆来说,颗粒度太粗了。
词级双语字幕把最小单位从“句子”下探到“单词”。每个英文单词都拥有独立的开始时间和结束时间,视频播放时,当前正在发音的单词会被高亮,然后顺势过渡到下一个单词。整体效果类似卡拉 OK 的逐字变色,只不过对象从歌词变成了口语对白。
如果在这个基础上再叠加中文翻译,就形成了“英文原句 + 词级高亮 + 中文释义”三层信息结构。读者既能看整句意思,也能精确知道当前读到的是哪个词、这个词的发音起点和落点在哪里。这种格式最早在语言学习播放器里出现,最近几年开始被独立的字幕制作软件做成标准功能。
1.2 词级双语字幕的典型应用场景
词级双语字幕最核心的使用场景是语言学习,尤其是“精听”和“影子跟读”。精听要求学习者逐词听辨,普通字幕一眼扫过去,很容易被整句翻译带跑,难以真正逼自己听清单词边界;而词级高亮相当于给每个词画了一条时间线,发音到哪,高亮就到哪,耳朵和眼睛可以严格对齐。
第二个场景是外刊精读、英语影视解说类视频制作。UP 主在剪辑原声片段时,如果字幕能逐词高亮,观众的注意力会自然落在正在发音的单词上,配合中文注释,完播率和互动率通常比静态双语字幕更好。
第三个场景是口译训练和配音练习。练习者需要盯住原声的语速、重音和停顿,词级时间轴可以直接展示说话人的停顿位置,哪些词连读了、哪些词被弱读了,在高亮节奏里一目了然。这个能力对做发音教学的老师同样有价值。
1.3 和普通双语字幕的本质区别
普通双语字幕与词级双语字幕的区别,并不仅仅是“多了高亮效果”,而是底层数据模型发生了变化。
普通双语字幕保存的是“时间段 + 文本”,本质上是一张时间表;词级双语字幕保存的是“句子时间轴 + 单词时间轴 + 双语对照文本”,本质上是一棵层级结构,句子是根节点,单词是叶子节点,每个叶子节点都带有自己的时间属性。
这个差异直接影响了制作流程。普通字幕只需要对时间轴、抄写文本、翻译三件事;词级字幕在此基础上还多出了“单词切分”和“单词对齐”两个环节。更关键的是,普通字幕可以用任何视频剪辑软件手工拖拽完成,词级字幕则必须依赖语音识别加对齐算法,手工逐词打点几乎不现实。这也是为什么“词级双语字幕”会被作为独立软件品类的核心卖点,而不是剪辑软件的附属功能。
2. 制作词级双语字幕需要哪些准备
2.1 工具链整体思路
要制作词级双语字幕,不能只靠一个编辑器,通常需要一条工具链:
- 语音识别(ASR):把视频里的英文对白转成带时间戳的文本,最好能输出到单词级别;
- 字幕解析:读取已有的 SRT 字幕,把句子级时间轴提取出来;
- 双语对齐:把机器翻译或人工译文按照句子索引回填到字幕结构里;
- 字幕渲染:生成带卡拉 OK 标签的 ASS 字幕,让播放器支持逐词高亮;
- 校对与微调:对识别错误、断句错误、单词粘连进行人工修正。
这条链路的前半段可能由“词级双语字幕制作软件”一体化完成,后半段则非常适合用脚本批量处理。下面几个小节会逐个说明每一环需要准备什么。
2.2 本文演示环境与版本说明
这篇文章的实战示例使用 Python 编写,核心只依赖标准库,不涉及复杂框架。建议环境如下:
- 操作系统:Windows 10/11、macOS 或主流 Linux 发行版均可;
- Python:3.9 及以上版本;
- ffmpeg:用于从视频中抽取音频,建议使用 4.x 及以上版本;
- 语音识别引擎:以 OpenAI Whisper 为例演示,具体版本请按项目实际环境调整;
- 播放器:建议使用支持 ASS 卡拉 OK 标签的播放器,例如 potplayer、VLC 等。
版本需要注意:Whisper 的word_timestamps参数在不同版本中支持程度不同,老版本可能只返回句子级时间戳,新版本才返回单词级结果。文章中的代码会标注“以你安装的版本为准”,不要照搬后不做验证。
2.3 数据准备:视频、音频与文本
动手之前需要确认三份素材:原始视频、可提取的音频、版权允许处理的文本内容。制作字幕涉及复制原声和转写原文,务必只处理自己有授权或属于合理使用范围的素材,不要拿商业影视资源做公开传播。
先把音频从视频里抽出来。使用 ffmpeg 的通用命令如下:
ffmpeg -i input.mp4 -vn -acodec pcm_s16le -ar 16000 -ac 1 audio.wav这条命令的作用是把input.mp4的视频流丢弃(-vn),音频转成 16kHz 单声道 PCM 格式,输出为audio.wav。16kHz 单声道是大多数语音识别模型的标准输入设置,能有效减少背景音乐对识别结果的影响。
如果手里已经有一份 SRT 字幕,可以直接进入解析流程;如果没有,可以让语音识别引擎先输出整句文本,再人工校对后生成 SRT。无论走哪条路,最终都需要一份“句子级 + 带时间轴”的字幕文件作为后续词级处理的基础。
3. 核心原理与格式拆解
3.1 字幕文件格式基础:SRT 与 ASS
先看 SRT,它是目前兼容性最好的字幕格式。一个典型的 SRT 文件长这样:
1 00:00:12,340 --> 00:00:15,670 Hello world, this is a sample subtitle. 2 00:00:16,000 --> 00:00:18,000 Let's make word-level bilingual subtitles.每条记录由序号、时间行、文本行组成。时间格式是时:分:秒,毫秒,毫秒部分用逗号或点分隔都可以。SRT 格式简单,但缺点也很明显:它没有样式系统,不支持逐词高亮,无法表达“单词 12.34 秒开始、13.20 秒结束”这种细粒度信息。
ASS 是 Advanced SubStation Alpha 的缩写,比 SRT 强大得多。ASS 文件分为多个 Section,常见的有[Script Info]、[V4+ Styles]、[Events]。核心的逐词高亮能力来自[Events]区块里的卡拉 OK 标签:
Dialogue: 0,0:00:12.34,0:00:15.67,Default,,0,0,0,,{\kf86}Hello {\kf187}world{\kf86}的含义是:从当前位置开始,用 86 厘秒(0.86 秒)完成“Hello”这个词的高亮填充。{\kf187}则表示“world”这个词的高亮时长为 187 厘秒。播放器渲染时,会按照这些标签做逐词推进,形成卡拉 OK 效果。
ASS 里常见的卡拉 OK 标签有两种:\k和\kf。\k是完全填充式高亮,\kf会带一点淡入渐变,视觉上更柔和。制作词级字幕时,我通常选择\kf,因为单词切换的过渡更自然。
3.2 词级时间轴:从 ASR 到对齐
词级时间轴从哪里来?理论上可以人工逐词打点,但 5 分钟的视频就有几百个单词,人工打点效率太低。实际生产中基本都靠语音识别引擎自动生成。
以 Whisper 为例,加载模型后调用转写接口,部分版本支持返回单词级时间戳。示例代码如下:
import whisper model = whisper.load_model("medium") result = model.transcribe("audio.wav", word_timestamps=True, language="en") for segment in result["segments"]: print(segment["start"], segment["end"], segment["text"]) for word in segment.get("words", []): print(" ", round(word["start"], 2), round(word["end"], 2), word["word"])输出结果大致是:
12.34 15.67 Hello world, this is a sample subtitle. 12.34 13.20 Hello 13.80 15.67 world注意不同版本的 Whisper 对word_timestamps的支持情况不同,words字段也可能不存在。如果发现识别结果里没有单词级时间戳,建议先升级版本,或者改用支持词级对齐的第三方工具。
这里的“对齐”是指把识别出来的单词映射到准确的时间点上。识别引擎给出的时间并不是绝对精确,尤其在嘈杂背景、连读、弱读场景下,单词边界经常偏出几十毫秒甚至一两百毫秒。后续人工校对时,优先检查整句时间轴是否准确,再抽查单词边界有没有明显错位。
3.3 双语对齐与字幕分层设计
有了词级时间轴之后,接下来要把中文翻译填进去。这里有一个容易被忽略的要点:翻译的单位应该是“句子”,而不是“单词”。
为什么?因为语言不是逐词对应的。英文的 “How are you doing”,逐词直译成中文是“怎么是你做”,完全不通顺。把整句交给翻译模型或人工翻译,得到通顺的中文后,再以句子为单位与英文原句对齐,是更合理的方式。词级高亮只作用于英文原文,中文译文保持整句显示即可,这样既保留了逐词精读能力,又不会让中文被切得支离破碎。
这种数据关系可以用一个 JSON 结构来描述,方便脚本处理和后续扩展:
[ { "index": 1, "start": 12340, "end": 15670, "text": "Hello world, this is a sample subtitle.", "target": "你好世界,这是一个示例字幕。", "words": [ { "text": "Hello", "start": 12340, "end": 13200 }, { "text": "world", "start": 13800, "end": 15670 } ] } ]这个结构里,index对应 SRT 序号,start、end是句子级时间轴,words是单词级时间轴,target是整句中文翻译。渲染成 ASS 时,英文单词逐词使用\kf标签,中文译文使用\N换行放在下一行。这就是整套词级双语字幕的分层设计。
4. 完整实战:从视频到词级双语字幕
4.1 创建项目结构与准备文件
先建立一个干净的项目目录,方便脚本和素材管理。目录结构如下:
word-level-subtitle/ ├── input.mp4 ├── audio.wav ├── sample.srt ├── word_timings.json ├── translations.json └── make_word_level_ass.py其中input.mp4是原始视频,audio.wav是从视频里抽出的音频,sample.srt是句子级字幕,word_timings.json是单词级时间轴,translations.json是中文翻译表,最后的 Python 脚本负责把所有数据合并成 ASS 字幕。
假设你已经用 ffmpeg 抽出了音频,并且有一条标准的 SRT 字幕,接下来从解析 SRT 开始。
4.2 解析 SRT 并生成词级时间轴
编写parse_srt.py,把 SRT 文件解析成结构化数据。完整代码如下:
# -*- coding: utf-8 -*- """ parse_srt.py 将标准 SRT 字幕解析为结构化字典列表 """ import re from pathlib import Path def parse_srt(file_path): text = Path(file_path).read_text(encoding="utf-8-sig") blocks = re.split(r"\n\s*\n", text.strip()) items = [] for block in blocks: lines = block.strip().split("\n") if len(lines) < 2: continue try: index = int(lines[0]) except ValueError: continue time_line = lines[1] content = "\n".join(lines[2:]) m = re.match( r"(\d{2}):(\d{2}):(\d{2})[,.](\d{3})\s*-->\s*" r"(\d{2}):(\d{2}):(\d{2})[,.](\d{3})", time_line, ) if not m: continue h1, mi1, s1, ms1 = map(int, m.groups()[:4]) h2, mi2, s2, ms2 = map(int, m.groups()[4:]) start = (h1 * 3600 + mi1 * 60 + s1) * 1000 + ms1 end = (h2 * 3600 + mi2 * 60 + s2) * 1000 + ms2 items.append({ "index": index, "start": start, "end": end, "text": content.strip(), }) return items if __name__ == "__main__": subs = parse_srt("sample.srt") for item in subs: print(item["index"], item["start"], item["end"], item["text"])代码核心是正则表达式匹配时间行,支持逗号和点两种毫秒分隔符。解析结果统一转成毫秒整数,后续计算时不会出现浮点精度问题。运行后应该能看到类似下面的输出:
1 12340 15670 Hello world, this is a sample subtitle. 2 16000 18000 Let's make word-level bilingual subtitles.得到句子级时间轴后,再用语音识别引擎输出单词级时间戳,保存到word_timings.json。这时脚本里的words字段就可以和 SRT 序号对应上。
4.3 构建双语词级数据
接着准备中文翻译文件。为了保持“整句翻译”的原则,翻译表只按 SRT 序号存储中文,不切分单词。translations.json内容如下:
{ "1": "你好世界,这是一个示例字幕。", "2": "让我们制作词级双语字幕。" }同时把识别得到的单词时间戳整理成word_timings.json:
[ { "index": 1, "words": [ { "text": "Hello", "start": 12340, "end": 13200 }, { "text": "world", "start": 13800, "end": 15670 } ] }, { "index": 2, "words": [ { "text": "Let's", "start": 16000, "end": 16450 }, { "text": "make", "start": 16500, "end": 17000 }, { "text": "word-level", "start": 17050, "end": 17800 }, { "text": "bilingual", "start": 17850, "end": 18500 }, { "text": "subtitles", "start": 18550, "end": 19500 } ] } ]这两份数据完全可以由脚本自动合并。如果某个序号在word_timings.json里缺失,脚本会退化成“整句作为一个单词时间块”,保证输出文件不会因为个别识别失败而中断。
4.4 生成 ASS 字幕文件
下面编写核心脚本make_word_level_ass.py,完成“SRT 句子级时间轴 + 单词级时间轴 + 中文翻译”的合并,并生成 ASS 文件。
# -*- coding: utf-8 -*- """ make_word_level_ass.py 将普通 SRT + 词级时间轴 + 中文翻译合并为词级双语 ASS 字幕 """ import json import re from pathlib import Path def parse_srt(file_path): text = Path(file_path).read_text(encoding="utf-8-sig") blocks = re.split(r"\n\s*\n", text.strip()) items = [] for block in blocks: lines = block.strip().split("\n") if len(lines) < 2: continue try: index = int(lines[0]) except ValueError: continue time_line = lines[1] content = "\n".join(lines[2:]) m = re.match( r"(\d{2}):(\d{2}):(\d{2})[,.](\d{3})\s*-->\s*" r"(\d{2}):(\d{2}):(\d{2})[,.](\d{3})", time_line, ) if not m: continue h1, mi1, s1, ms1 = map(int, m.groups()[:4]) h2, mi2, s2, ms2 = map(int, m.groups()[4:]) start = (h1 * 3600 + mi1 * 60 + s1) * 1000 + ms1 end = (h2 * 3600 + mi2 * 60 + s2) * 1000 + ms2 items.append({ "index": index, "start": start, "end": end, "text": content.strip(), }) return items def format_ass_time(ms): h = ms // 3600000 m = (ms % 3600000) // 60000 s = (ms % 60000) // 1000 cs = (ms % 1000) // 10 return f"{h}:{m:02d}:{s:02d}.{cs:02d}" def build_karaoke(words): parts = [] for w in words: duration_cs = max(1, round((w["end"] - w["start"]) / 10)) parts.append(r"{\kf%d}%s" % (duration_cs, w["text"])) return " ".join(parts) def build_ass(items, output_path): header = """[Script Info] ScriptType: v4.00+ PlayResX: 1920 PlayResY: 1080 WrapStyle: 0 ScaledBorderAndShadow: yes [V4+ Styles] Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding Style: Default,Noto Sans CJK SC,72,&H00FFFFFF,&H000000FF,&H00000000,&H96000000,-1,0,0,0,100,100,0,0,1,3,1,2,80,80,40,1 [Events] Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text """ lines = [header] for item in items: start = format_ass_time(item["start"]) end = format_ass_time(item["end"]) source = build_karaoke(item["words"]) target = item.get("target", "") text = source if target: text += r"\N" + target lines.append( f"Dialogue: 0,{start},{end},Default,,0,0,0,,{text}" ) Path(output_path).write_text("\n".join(lines), encoding="utf-8") print("已生成:", output_path) def main(): srt_items = parse_srt("sample.srt") timings = json.loads(Path("word_timings.json").read_text(encoding="utf-8")) translations = json.loads(Path("translations.json").read_text(encoding="utf-8")) merged = [] for item in srt_items: idx = item["index"] timing = next((t for t in timings if t["index"] == idx), None) words = timing["words"] if timing else [ {"text": item["text"], "start": item["start"], "end": item["end"]} ] merged.append({ "index": idx, "start": item["start"], "end": item["end"], "words": words, "target": translations.get(str(idx), ""), }) build_ass(merged, "output.ass") if __name__ == "__main__": main()脚本主要做了三件事:把中文翻译按序号挂到每条字幕上;把单词级时间轴转换成 ASS 卡拉 OK 标签;最终拼出完整的 ASS 文件。build_karaoke函数是核心,每个单词的持续时间由end - start计算得到,单位从毫秒换算成厘秒后写入{\kf}标签。
4.5 运行脚本与验证结果
在项目目录下依次运行:
python parse_srt.py python make_word_level_ass.py第二个脚本执行成功后,会输出:
已生成: output.ass打开output.ass,应该能看到类似下面的内容:
[Script Info] ScriptType: v4.00+ PlayResX: 1920 PlayResY: 1080 WrapStyle: 0 ScaledBorderAndShadow: yes [V4+ Styles] Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding Style: Default,Noto Sans CJK SC,72,&H00FFFFFF,&H000000FF,&H00000000,&H96000000,-1,0,0,0,100,100,0,0,1,3,1,2,80,80,40,1 [Events] Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text Dialogue: 0,0:00:12.34,0:00:15.67,Default,,0,0,0,,{\kf86}Hello {\kf187}world\N你好世界,这是一个示例字幕。放到支持 ASS 标签的播放器里播放,英文单词会随着读音逐个高亮,中文译文稳定显示在第二行。如果发现中文没有换行,检查字幕样式中的WrapStyle和\N是否被编辑器转义。
5. 高频问题与排查清单
词级双语字幕制作过程中,最容易遇到下面几类问题。我把现象、原因和解决思路整理成表格,方便快速定位。
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| 单词高亮和发音明显错位 | 语音识别单词边界不准 | 检查整句时间轴,手动修正关键单词的 start/end |
| 中文译文显示在英文同一行 | ASS 换行符被转义 | 使用\N而不是\n,字符串不要被二次转义 |
| 字幕完全不显示高亮效果 | 播放器不支持 ASS 卡拉 OK 标签 | 换用 potplayer、VLC 等支持 ASS 完整特性的播放器 |
| 中文字体显示为方块 | 系统缺少对应字体 | 安装 Noto Sans CJK 或思源黑体,并在样式里指定字体名 |
| 句子识别成乱码 | 音频采样率或声道不符合识别引擎要求 | 统一用 16kHz 单声道 WAV,重新抽取音频 |
| 单词时间戳缺失 | Whisper 版本不支持词级输出 | 升级版本或换用支持词级对齐的工具 |
几个高频问题的详细说明:
第一个是单词高亮错位。语音识别引擎在连读、弱读、背景音乐干扰下,单词边界经常偏差几十毫秒到几百毫秒。处理方法是先保证整句时间轴正确,再用“听一句、对一句”的方式抽查关键单词。不要试图用脚本一次性解决所有对齐误差,人工校对是词级字幕质量的重要保障。
第二个是播放器兼容问题。不是所有播放器都完整支持 ASS 的卡拉 OK 标签,部分播放器会把{\kf}当作无效标签忽略,导致字幕整体变成普通静态文本。验证高亮效果时,优先使用对 ASS 支持完善的桌面播放器,不要在预览阶段依赖网页播放器。
第三个是文本编码问题。JSON 和 ASS 文件建议统一使用 UTF-8 编码,否则中文会乱码。Windows 环境下尤其注意,不要在记事本里另存为带 BOM 的 UTF-8 后又被 Python 以utf-8读取,建议读取时统一使用utf-8-sig容错。
6. 最佳实践与工程建议
6.1 时间轴精度与断句策略
词级字幕的体验上限,很大程度上由时间轴精度决定。这里有一个工程判断:句子级时间轴必须准,单词级时间轴允许少量误差。为什么?因为人类听觉对单词边界有较大的容忍度,但对整句的出现时机非常敏感。如果整句早出或晚出几十毫秒,观众会立刻觉得“字幕跟声音对不上”。
断句策略也值得注意。语音识别引擎默认按静音切句,有时会把一个完整句子的从句切开,有时又把两个短句合并。生成词级字幕前,最好先人工调整断句,让一条字幕对应一个完整意群。这样中文翻译更好组织,观众阅读也更省力。单词之间的空隙不用刻意填满,只要保证高亮顺序和读音顺序一致即可。
6.2 翻译质量与术语一致性
翻译在词级双语字幕里的权重非常高,因为它决定了学习内容的正确性。建议采用“整句翻译、术语统一”的策略:先整体翻译句子,再通过术语表保证同一部视频、同一系列视频中的人名、地名、专业名词翻译一致。
对于系列视频,还可以建立一份 glossary 文件,用 JSON 或纯文本保存术语对照。脚本合并字幕时,可以根据术语表做翻译后处理,比如把已确定的专有名词统一替换。这比每次手工修改几十条字幕要可靠得多。翻译模型给出的结果只能作为初稿,最终发布前必须人工过一遍,尤其是口语化表达、双关语和文化梗。
6.3 批量处理与性能优化
如果处理的视频不止一个,可以让脚本支持批量模式。常见做法是把项目目录分成raw、intermediate、output三个子目录,分别存放原始视频、中间 JSON、最终 ASS。脚本遍历raw下所有视频,自动抽取音频、调用识别接口、解析 SRT、生成 ASS,并把日志写到文件里。
批量处理时,语音识别是最耗时的环节。建议根据机器配置选择模型大小,显存充足时用 large 模型,普通 CPU 环境下用 small 或 medium 模型更现实。识别是一个计算密集任务,跑批前先确认磁盘空间和 CPU/GPU 占用,避免一次启动过多任务把机器卡死。
6.4 协作、版本管理与合规
词级双语字幕是结构化数据,非常适合纳入版本管理。项目目录里除了代码,还应该把sample.srt、word_timings.json、translations.json都提交到 Git 仓库。这样每一次翻译修改、时间轴修正都能追踪到差异,多人协作时也不会互相覆盖。
合规方面需要特别留意:字幕文本、原声片段、翻译内容都可能涉及版权。只处理自己有权使用的素材,避免在公开平台传播未经授权的影视资源字幕。如果字幕用于商业课程或付费内容,务必确认原声与文本的授权边界。技术能力可以让制作效率大幅提升,但内容合规的底线不能因为工具变方便就放松。
7. 总结与下一步学习方向
到这里,一条完整的“词级双语字幕制作链路”已经跑通了:先用 ffmpeg 抽取音频,再用语音识别生成句子级和单词级时间轴,接着准备整句中文翻译,最后用 Python 脚本把数据合并成带卡拉 OK 高亮的 ASS 字幕。核心不是某个特定软件,而是“句子时间轴 + 单词时间轴 + 双语文本”这套数据结构,以及 ASS 格式里的{\kf}标签用法。
如果接下来想继续深入,可以从三个方向入手:一是优化单词对齐精度,比如引入强制对齐工具,对 Whisper 原始输出做二次修正;二是给字幕脚本加上图形界面或 Web 页面,让不懂命令行的创作者也能操作;三是接入翻译记忆库和术语库,把单文件制作升级为可持续积累的双语语料库项目。做字幕这件事,工具只会越来越顺手,但真正决定成品的仍然是内容质量和校对耐心。建议先拿一小段自己录制的视频练手,走完一遍流程后,再决定要不要把它做成日常生产力工具。