format-and-translate
将 YouTube 英文字幕转换为双语字幕(中文+英文),核心是 7 步工作流。 输入为 en.json3、info.json(可选) 文件,可通过 yt-dlp 下载;输出为字幕的 srt 和 json。
用法
/format-and-translate <en_json3_path> [info_json_path] [--steps 1-7] [--output-dir <dir>]
参数说明:
en_json3_path:YouTube json3 格式的英文字幕文件路径(必填)info_json_path:包含 chapters 信息的 JSON 文件路径(可选,无则跳过章节标题)--steps:指定执行哪些步骤,格式1-7(默认全部)。可指定范围如3-7,或单步如5--output-dir:中间文件输出目录(默认为<en_json3_dir>/Subtitle)
示例:
/format-and-translate example/video.en.json3 example/video.info.json
/format-and-translate example/video.en.json3 --steps 5-7
/format-and-translate /absolute/path/to/video.en.json3 --output-dir /tmp/output/
Script Directory
Scripts in scripts/ subdirectory. {baseDir} = this SKILL.md's directory path. Resolve ${BUN_X} runtime: if bun installed → bun; if npx available → npx -y bun; else suggest installing bun. Replace {baseDir} and ${BUN_X} with actual values.
执行前准备
- 解析
$ARGUMENTS,提取en_json3_path、info_json_path(可能为空)、--steps范围、--output-dir - 将
en_json3_path转为绝对路径;提取其所在目录en_json3_dir - 若未指定
--output-dir,则:output_dir = <en_json3_dir>/Subtitle - 验证
en_json3_path文件存在 - 若未提供
info_json_path,则在en_json3_dir下寻找对应的info.json:从en_json3_path文件名去掉.en.json3后缀,加上.info.json(例如video.en.json3->video.info.json);若文件存在则使用,否则设为空字符串"" - 确认
bun可用:bun --version - 创建 output 目录:
mkdir -p <output_dir> - 告知用户将执行哪些步骤
步骤范围解析规则:
--steps 1-7(默认):执行所有步骤--steps 3-7:从第 3 步开始--steps 5:仅执行第 5 步- 若 output 目录中已有某步骤的输出文件,且用户未明确指定该步骤,可跳过并提示
Workflow
Step 1: 整理输入的 json3 文件 (Code)
输入、输出:en.json3 -> 1.en.indexed.md + 1.en.indexed.json
执行方式(Code):
${BUN_X} {baseDir}/scripts/step1.ts <en_json3_path> <output_dir>
Step 2: 英文断句 (LLM + Code)
输入、输出:1.en.indexed.md -> 2.en.indexed.flag.md -> 2.en.indexed.flag.full.md
执行方式:
读取提示词:
{baseDir}/prompts/step2_segmentation.md使用
{baseDir}/scripts/chunk.ts对<output_dir>/1.en.indexed.md进行分块(max-words 3000),输出到<output_dir>/step2_chunks/:${BUN_X} {baseDir}/scripts/chunk.ts <output_dir>/1.en.indexed.md --max-words 3000 --output-dir <output_dir>/step2_chunks对每个 chunk,按提示词处理:根据句意确定
[end]边界位置;对每个 chunk 并行启动子 agent(subagent),每个 agent:- 读取提示词
- 读取对应 chunk 文件
- 输出到
<output_dir>/step2_chunks/chunk-NN-flagged.md - 等待全部完成后,按顺序合并为
<output_dir>/2.en.indexed.flag.md
展开完整文本并检查
[end]flag 准确性:${BUN_X} {baseDir}/scripts/runExpandFlagFull.ts <output_dir>/1.en.indexed.json <output_dir>/2.en.indexed.flag.md <output_dir>/2.en.indexed.flag.full.md输出格式为
[n:+m1+m2+-m3] sentence_text,其中:n为合并后的句子序号+mi表示来源原始条目 mi(其[end]已匹配,或无 flag)+-mi表示来源原始条目 mi,但其 compact flag 中的[end]未被匹配 输出:<output_dir>/2.en.indexed.flag.full.md(合并后句子 + 来源索引)
根据
2.en.indexed.flag.full.md评估断句质量,决定后续操作:5.1:匹配率检查(全量重跑判断)
运行统计脚本:
${BUN_X} {baseDir}/scripts/analyzeFlags.ts <output_dir>/2.en.indexed.flag.full.md <output_dir>/2.en.indexed.flag.analy.json输出(保存至
<output_dir>/2.en.indexed.flag.analy.json):{ "totalMi": X, "unmatchedMiCount": Y, "matchedRate": 0.ZZ, "unmatchedSentences": [{ "n": N, "words": W, "text": "...", "miList": [...] }], "longSentencesCount": Z, "longSentences": [{ "n": N, "words": W, "text": "...", "miList": [...] }] }(exit code 0 表示 matchedRate ≥ 0.8,1 表示 < 0.8)若
matchedRate < 0.8(读取2.en.indexed.flag.analy.json中matchedRate)(LLM 标记的[end]大量无法匹配):- 清空
<output_dir>/step2_chunks/ - 重新分块,改为
--max-words 2000:${BUN_X} {baseDir}/scripts/chunk.ts <output_dir>/1.en.indexed.md --max-words 2000 --output-dir <output_dir>/step2_chunks - 重复步骤 3 和步骤 4(LLM 处理 + 展开 flag.full.md)
- 若重跑后仍 < 80%,输出警告,继续执行5.2(局部修复),不再全量重跑。
5.2:对断句质量差的片段局部重新断句
① 判断:读取
2.en.indexed.flag.analy.json,若unmatchedSentences和longSentences均为空,跳过以下步骤。② 备份(若文件存在则备份):
cp <output_dir>/2.en.indexed.flag.md <output_dir>/2.en.indexed.flag.{timestamp}.md cp <output_dir>/2.en.indexed.flag.full.md <output_dir>/2.en.indexed.flag.full.{timestamp}.md cp <output_dir>/2.en.indexed.flag.analy.json <output_dir>/2.en.indexed.flag.analy.{timestamp}.json # 以下两个文件在第一次迭代时不存在,第二次及以后迭代时需要备份: cp <output_dir>/2.en.indexed.part.md <output_dir>/2.en.indexed.part.{timestamp}.md cp <output_dir>/2.en.indexed.part.flag.md <output_dir>/2.en.indexed.part.flag.{timestamp}.md③ 提取需修复片段(
unmatchedSentences和longSentences中各条目的miList):${BUN_X} {baseDir}/scripts/extractPartialIndexed.ts <output_dir>/2.en.indexed.flag.analy.json <output_dir>/1.en.indexed.md <output_dir>/2.en.indexed.part.md输出:
<output_dir>/2.en.indexed.part.md(需重新断句的原始行,保留原始 mi 索引)④ 重新断句:对
2.en.indexed.part.md直接运行步骤 3 的 LLM 断句(不分块,启用单个子 agent),得到:<output_dir>/2.en.indexed.part.flag.md⑤ 合并:将
2.en.indexed.part.flag.md合并回2.en.indexed.flag.md(以2.en.indexed.flag.md中已匹配的 mi 条目为基础,用part.flag.md中的条目添加或替换对应 mi):${BUN_X} {baseDir}/scripts/mergePartialFlags.ts <output_dir>/2.en.indexed.part.flag.md <output_dir>/2.en.indexed.flag.md <output_dir>/2.en.indexed.flag.analy.json⑥ 重新生成 flag.full.md:
${BUN_X} {baseDir}/scripts/runExpandFlagFull.ts <output_dir>/1.en.indexed.json <output_dir>/2.en.indexed.flag.md <output_dir>/2.en.indexed.flag.full.md⑦ 重新统计:
${BUN_X} {baseDir}/scripts/analyzeFlags.ts <output_dir>/2.en.indexed.flag.full.md <output_dir>/2.en.indexed.flag.analy.json⑧ 再次判断:若仍存在
unmatchedSentences或longSentences,从步骤②重复执行;最多再执行两次(共最多 3 次)。- 清空
Step 3: 生成格式化 JSON (Code)
输入、输出:1.en.indexed.json + 2.en.indexed.flag.md -> 3.en.formatted.json
执行方式(Code):
${BUN_X} {baseDir}/scripts/step3.ts <output_dir>/1.en.indexed.json <output_dir>/2.en.indexed.flag.md <output_dir>/3.en.formatted.json
Step 4: 生成待翻译 Markdown (Code)
输入、输出:3.en.formatted.json + info.json -> 4.en.formatted.indexed.md
执行方式(Code):
# 有 info.json:
${BUN_X} {baseDir}/scripts/step4.ts <output_dir>/3.en.formatted.json <info_json_path> <output_dir>/4.en.formatted.indexed.md
# 无 info.json:
${BUN_X} {baseDir}/scripts/step4.ts <output_dir>/3.en.formatted.json "" <output_dir>/4.en.formatted.indexed.md
Step 5: 翻译 (LLM)
输入、输出:4.en.formatted.indexed.md -> 5.en.formatted.indexed.zh.md
工具:baoyu-skills:baoyu-translate skill
执行方式:
调用 baoyu-translate skill,处理 <output_dir>/4.en.formatted.indexed.md
额外要求(必须在调用时明确指定):
- 保持序号一一对应:
[N]英文 ↔[N]中文,不合并不拆分行;使用grep -c '^\[\d\+\.\?\d\?\]' file.md验证 行数 是否一致。 - 保留所有
[数字]序号前缀 - 翻译
# 章节标题行 - 输出格式与输入完全一致
Step 6: 中文分句与对齐 (LLM + Code)
输入、输出:4.en.formatted.indexed.md + 5.en.formatted.indexed.zh.md -> 6.en.formatted.indexed.zh.segmention.full.md
提示词:{baseDir}/prompts/step6_segmentation_alignment.md
执行方式:
读取提示词:
{baseDir}/prompts/step6_segmentation_alignment.md检查 chunks。判断 Step 5 是否产生了 chunks:检查 baoyu-translate 实际输出目录(
<output_dir>/4.en.formatted.indexed-zh-CN/chunks/chunks/,或 baoyu-translate 实际输出目录下chunks/chunks/)是否存在chunk-NN.md文件。分支 A:有英文 chunks(利用现有 chunks)
运行以下脚本,将
5.en.formatted.indexed.zh.md按现有的英文 chunk 边界拆分为对应的中文 chunks:${BUN_X} {baseDir}/scripts/splitZhByChunks.ts <output_dir>/5.en.formatted.indexed.zh.md <en_chunks_dir>/chunks <output_dir>/step6_chunks其中
<en_chunks_dir>为 baoyu-translate 输出目录下的chunks/子目录(如<output_dir>/4.en.formatted.indexed-zh-CN/chunks)。输出为:<output_dir>/step6_chunks/chunk-NN-zh.md分支 B:无英文 chunks(生成 chunks)
使用
{baseDir}/scripts/chunk.ts对<output_dir>/4.en.formatted.indexed.md进行分块(max-words 2000),输出到<output_dir>/step6_chunks/:${BUN_X} {baseDir}/scripts/chunk.ts <output_dir>/4.en.formatted.indexed.md --max-words 2000 --output-dir <output_dir>/step6_chunks运行以下脚本,将
5.en.formatted.indexed.zh.md按上述生成的英文 chunk 边界拆分为对应的中文 chunks:${BUN_X} {baseDir}/scripts/splitZhByChunks.ts <output_dir>/5.en.formatted.indexed.zh.md <output_dir>/step6_chunks <output_dir>/step6_chunks处理 chunks 2.2 对每对 chunk 并行启动子 agent,每个 agent:
- 读取提示词
- 读取对应英文 chunk:
<en_chunks_dir>/chunks/chunk-NN.md或者<output_dir>/step6_chunks/chunk-NN.md - 读取对应中文 chunk:
<output_dir>/step6_chunks/chunk-NN-zh.md(最终翻译,非草稿) - 按提示词进行分句对齐,输出到
<output_dir>/step6_chunks/chunk-NN-segmented.md2.3 等待全部完成后,按顺序合并为<output_dir>/6.en.formatted.indexed.zh.segmention.md
展开完整双语对照文本和生成分析报告:
${BUN_X} {baseDir}/scripts/runExpandSegmentFull.ts <output_dir>/3.en.formatted.json <output_dir>/5.en.formatted.indexed.zh.md <output_dir>/6.en.formatted.indexed.zh.segmention.md <output_dir>/6.en.formatted.indexed.zh.segmention.full.md匹配率检查与局部重试
根据
6.en.formatted.indexed.zh.segmention.full.md评估对齐质量,决定后续操作:5.1:匹配率检查(全量重跑判断)
读取
6.en.formatted.indexed.zh.segmention.analy.json中的matchedRate。若
matchedRate < 0.8(LLM 标记的分隔符大量无法匹配):- 清空
<output_dir>/step6_chunks/ - 重新分块,改为
--max-words 1500:${BUN_X} {baseDir}/scripts/chunk.ts <output_dir>/4.en.formatted.indexed.md --max-words 1500 --output-dir <output_dir>/step6_chunks - 重新拆分中文 chunks:
${BUN_X} {baseDir}/scripts/splitZhByChunks.ts <output_dir>/5.en.formatted.indexed.zh.md <output_dir>/step6_chunks <output_dir>/step6_chunks - 重复步骤 3 和步骤 4(LLM 处理 + 展开 full.md)
- 若重跑后仍 < 80%,输出警告,继续执行 5.2(局部修复),不再全量重跑。
5.2:对质量差的片段局部重新对齐
① 判断:读取
6.en.formatted.indexed.zh.segmention.analy.json,若unmatchedSegmentsList为空,跳过以下步骤。② 备份(若文件存在则备份):
cp <output_dir>/6.en.formatted.indexed.zh.segmention.md <output_dir>/6.en.formatted.indexed.zh.segmention.{timestamp}.md cp <output_dir>/6.en.formatted.indexed.zh.segmention.full.md <output_dir>/6.en.formatted.indexed.zh.segmention.full.{timestamp}.md cp <output_dir>/6.en.formatted.indexed.zh.segmention.analy.json <output_dir>/6.en.formatted.indexed.zh.segmention.analy.{timestamp}.json # 以下文件在第一次迭代时不存在,第二次及以后迭代时需要备份: cp <output_dir>/6.en.part.md <output_dir>/6.en.part.{timestamp}.md cp <output_dir>/6.zh.part.md <output_dir>/6.zh.part.{timestamp}.md cp <output_dir>/6.en-zh.part.segmented.md <output_dir>/6.en-zh.part.segmented.{timestamp}.md③ 提取需修复片段:
${BUN_X} {baseDir}/scripts/extractPartialSegments.ts <output_dir>/6.en.formatted.indexed.zh.segmention.analy.json <output_dir>/4.en.formatted.indexed.md <output_dir>/5.en.formatted.indexed.zh.md <output_dir>/6.en.part.md <output_dir>/6.zh.part.md④ 重新对齐:对
6.en.part.md和6.zh.part.md运行步骤 3 的 LLM 对齐(不分块,启用单个子 agent),输出到:<output_dir>/6.en-zh.part.segmented.md⑤ 合并:将修复后的片段合并回主文件:
${BUN_X} {baseDir}/scripts/mergePartialSegments.ts <output_dir>/6.en-zh.part.segmented.md <output_dir>/6.en.formatted.indexed.zh.segmention.md⑥ 重新生成 full.md 和 analy.json:
${BUN_X} {baseDir}/scripts/runExpandSegmentFull.ts <output_dir>/3.en.formatted.json <output_dir>/5.en.formatted.indexed.zh.md <output_dir>/6.en.formatted.indexed.zh.segmention.md <output_dir>/6.en.formatted.indexed.zh.segmention.full.md⑦ 再次判断:若仍存在
unmatchedSegmentsList,从步骤②重复执行;最多再执行两次(共最多 3 次)。- 清空
Step 7: 生成最终 SRT (Code)
输入、输出:3.en.formatted.json + 6.en.formatted.indexed.zh.segmention.md + 5.en.formatted.indexed.zh.md -> 7.final.srt
执行方式(Code):
${BUN_X} {baseDir}/scripts/step7.ts <output_dir>/3.en.formatted.json <output_dir>/6.en.formatted.indexed.zh.segmention.md <output_dir>/5.en.formatted.indexed.zh.md <output_dir>/7.final.srt
统计汇报 (Code)
输入、输出:1.en.indexed.json + 3.en.formatted.json + 7.final.json -> statistic.json
执行方式(Code):
${BUN_X} {baseDir}/scripts/report.ts <output_dir>/1.en.indexed.json <output_dir>/3.en.formatted.json <output_dir>/7.final.json <output_dir>/statistics.json