skills: move translation skills to ./skills/, symlink from ~/.hermes/skills/

pptx-translate, chinese-text-normalize, dharma-translation: canonical
location now ./skills/ with symlinks in ~/.hermes/skills/.

translation-review: merged CSV/XLSX review + .dj comparison workflows
into single SKILL.md. Added buddhist-terminology.md and
terms-db-alignment.md references from Hermes version.
This commit is contained in:
iacore
2026-06-09 19:46:23 +08:00
parent 10c449f4d9
commit 7e8ba4b291
9 changed files with 693 additions and 51 deletions
+68
View File
@@ -0,0 +1,68 @@
import sys, yaml
from pptx import Presentation
from pptx.util import Pt
from pptx.enum.text import MSO_AUTO_SIZE
FONT_SCALE = 0.82 # shrink ~18%
yaml_path, src_path, out_path = sys.argv[1], sys.argv[2], sys.argv[3]
with open(yaml_path) as f:
entries = yaml.safe_load(f)
index = {}
for e in entries:
index[(e["slide"], e["shape"], e["run"])] = e["en"]
def shrink_font_tf(tf):
for para in tf.paragraphs:
for run in para.runs:
if run.font.size:
run.font.size = Pt(int(run.font.size.pt * FONT_SCALE))
try:
tf.auto_size = MSO_AUTO_SIZE.TEXT_TO_FIT_SHAPE
except Exception:
pass
prs = Presentation(src_path)
for slide_num, slide in enumerate(prs.slides, 1):
for shape_idx, shape in enumerate(slide.shapes):
if shape.has_text_frame:
has_translation = False
for para_idx, para in enumerate(shape.text_frame.paragraphs):
key = (slide_num, shape_idx, para_idx)
if key in index:
has_translation = True
en = index[key]
for r in para.runs:
r.text = ""
if para.runs:
para.runs[0].text = en
else:
para.add_run().text = en
if has_translation:
shrink_font_tf(shape.text_frame)
elif shape.has_table:
num_rows = len(shape.table.rows)
for r in range(num_rows * len(shape.table.columns)):
key = (slide_num, shape_idx, r)
if key in index:
row = r % num_rows
col = r // num_rows
shape.table.cell(row, col).text = index[key]
for row in shape.table.rows:
for cell in row.cells:
shrink_font_tf(cell.text_frame)
key = (slide_num, -1, 0)
if key in index and slide.has_notes_slide:
ns = slide.notes_slide
ns.notes_text_frame.clear()
ns.notes_text_frame.paragraphs[0].add_run().text = index[key]
prs.save(out_path)
print(f"Saved {out_path}")
+68
View File
@@ -0,0 +1,68 @@
import sys, yaml
from pptx import Presentation
PLACEHOLDER_KINDS = {
1: "title", 2: "body", 3: "center_title",
4: "subtitle", 5: "body", 6: "body", 7: "body",
}
def shape_kind(shape):
if shape.has_table:
return "table"
try:
ph = shape.placeholder_format
if ph is not None and ph.type is not None:
return PLACEHOLDER_KINDS.get(ph.type, "body")
except ValueError:
pass
return "body"
def extract(pptx_path):
prs = Presentation(pptx_path)
entries = []
for slide_num, slide in enumerate(prs.slides, 1):
for shape_idx, shape in enumerate(slide.shapes):
kind = shape_kind(shape)
if shape.has_text_frame:
for para_idx, para in enumerate(shape.text_frame.paragraphs):
full = para.text.strip()
if not full:
continue
entries.append({
"slide": slide_num, "shape": shape_idx,
"run": para_idx, "kind": kind,
"zh": full, "en": "",
})
elif shape.has_table:
for row_idx, row in enumerate(shape.table.rows):
for col_idx, cell in enumerate(row.cells):
text = cell.text.strip()
if not text:
continue
entries.append({
"slide": slide_num, "shape": shape_idx,
"run": len(shape.table.rows) * col_idx + row_idx,
"kind": "table", "zh": text, "en": "",
})
if slide.has_notes_slide:
notes = slide.notes_slide.notes_text_frame.text.strip()
if notes:
entries.append({
"slide": slide_num, "shape": -1, "run": 0,
"kind": "notes", "zh": notes, "en": "",
})
return entries
if __name__ == "__main__":
entries = extract(sys.argv[1])
with open(sys.argv[2], "w") as f:
yaml.dump(entries, f, allow_unicode=True, default_flow_style=False, sort_keys=False)
print(f"Extracted {len(entries)} entries to {sys.argv[2]}")