diff --git a/skills/chinese-text-normalize/SKILL.md b/skills/chinese-text-normalize/SKILL.md new file mode 100644 index 0000000..8fa848b --- /dev/null +++ b/skills/chinese-text-normalize/SKILL.md @@ -0,0 +1,42 @@ +--- +name: chinese-text-normalize +description: Normalize Chinese markdown files — remove extraneous mid-sentence line breaks from fixed-width exports while preserving TOC structures, section headers, and intentional paragraph breaks. +--- + +When Chinese text has hard line breaks at a fixed width (~20-25 chars) — common in PDF exports, OCR output, or poorly-converted documents — use this skill to join them into flowing paragraphs. + +## Triggers + +- User asks to "fix line breaks" or "remove extraneous breaks" in Chinese text +- Chinese markdown files with lines that break mid-sentence at a consistent short width +- Files with vertical TOC (single-char-per-line 【】 sections) that need preservation + +## Approach + +Run `scripts/normalize_breaks.py ` — it processes all .md files in the directory. + +The script handles three file patterns: + +1. **Vertical TOC + fixed-width body** — Preserves the decorative single-char TOC section, joins body paragraphs, strips inline page numbers (standalone digits like "3", "4") +2. **Outline TOC with stray breaks** — Preserves numbered outline items (一、...、1、...、...... separators), joins body paragraphs +3. **Already in paragraph format** — No change (safe to run idempotently) + +### What it preserves + +- Vertical TOC: single CJK/punctuation lines with 【】 brackets +- Section headers: 【...】、## ...、# ...、一、二、三、...、1、2、3、... +- Outline TOC entries: short numbered lines, lines with ...... separators +- Blank lines as paragraph separators + +### What it removes + +- Mid-sentence hard line breaks (joins consecutive CJK body lines) +- Inline page numbers (standalone 1-2 digit lines) +- Trailing blank lines + +## Pitfalls + +- **TOC detection boundaries**: The vertical TOC end is detected by finding the first line with 3+ CJK characters. If a page number like "2" sits between TOC and body, it lands in the TOC section — harmless but visible. +- **Section headers without markers**: Plain-text section titles (e.g., "生命可以被设计的依据") without 【】 or number prefixes won't be detected as headers. They'll form standalone paragraphs separated by blank lines, which is fine as long as blank lines exist around them. +- **Wiki-link TOC files**: Files like a course index with [[wiki links]] are NOT prose and should be excluded. The script has no special handling — skip those files manually or restore from git. +- **Not for mixed CJK/English prose**: The script treats any line with CJK characters as body text. Mixed-language documents may need manual review. diff --git a/skills/chinese-text-normalize/scripts/normalize_breaks.py b/skills/chinese-text-normalize/scripts/normalize_breaks.py new file mode 100644 index 0000000..8c5f3c4 --- /dev/null +++ b/skills/chinese-text-normalize/scripts/normalize_breaks.py @@ -0,0 +1,168 @@ +""" +Fix extraneous line breaks in Chinese markdown files. + +Three file patterns: +1. Fixed-width body text (20-25 chars/line) + vertical TOC -> join lines, remove page nums +2. Mostly-paragraph with stray breaks + outline TOC -> join broken lines, preserve list items +3. Already fine -> skip (idempotent) + +Usage: python3 normalize_breaks.py +""" +import re +import sys +from pathlib import Path + +CJK = re.compile(r'[\u4e00-\u9fff\u3400-\u4dbf\uf900-\ufaff]') +CN_PUNCT = ',。!?;:、""''()《》【】…—~·' +NUM_MARKER = re.compile(r'^[一二三四五六七八九十]+[、,,]') +DIGIT_MARKER = re.compile(r'^\d+[、.,]') +TOC_SEP = re.compile(r'\.{3,}') # "......" separators in outline TOCs + + +def has_cjk(s): + return bool(CJK.search(s)) + + +def is_page_num(line): + s = line.strip() + return s and s.isdigit() and len(s) <= 2 + + +def is_toc_line(line): + """Vertical TOC: single char, or 【, 】, ·, or solo digit""" + s = line.strip() + if not s: + return False + if len(s) == 1 and (has_cjk(s) or s in CN_PUNCT or s in '【】·' or s.isdigit()): + return True + return False + + +def is_section_header(line): + """Section headers: 【...】, ## ..., # ..., 一、..., 1、..., or standalone title lines""" + s = line.strip() + if not s: + return False + if s.startswith('【') and s.endswith('】'): + return True + if s.startswith('#'): + return True + if NUM_MARKER.match(s): + return True + if DIGIT_MARKER.match(s): + return True + return False + + +def is_outline_toc_line(line): + """Outline/list TOC: entries separated by ...... or short numbered items""" + s = line.strip() + if TOC_SEP.search(s): + return True + m = re.match(r'^(\d+[.、,]|[一二三四五六七八九十]+[、,])\s*\S', s) + if m and len(s) < 30: + return True + return False + + +def find_toc_end(lines): + """Find where the vertical TOC section ends and body text begins.""" + for i, line in enumerate(lines): + s = line.strip() + if has_cjk(s) and len([c for c in s if has_cjk(c)]) >= 3: + j = i + while j > 0 and not lines[j - 1].strip(): + j -= 1 + return j + return 0 + + +def process_body(lines): + """Join body text lines into paragraphs, preserving section headers and outline items.""" + result = [] + buf = [] + + def flush(): + nonlocal buf + if buf: + joined = ''.join(buf) + result.append(joined) + buf = [] + + for line in lines: + s = line.strip() + + if not s: + flush() + result.append('') + continue + + if is_section_header(s): + flush() + result.append(s) + continue + + if is_outline_toc_line(s): + flush() + result.append(s) + continue + + if is_page_num(s): + continue + + if has_cjk(s) or (buf and s): + buf.append(s) + else: + flush() + result.append(s) + + flush() + return result + + +def process_file(filepath): + content = filepath.read_text(encoding='utf-8') + lines = content.split('\n') + + toc_end = find_toc_end(lines) + + if toc_end > 10: + toc_part = lines[:toc_end] + body_part = lines[toc_end:] + body_processed = process_body(body_part) + new_lines = toc_part + body_processed + else: + new_lines = process_body(lines) + + cleaned = [] + prev_blank = False + for line in new_lines: + is_blank = line.strip() == '' + if is_blank and prev_blank: + continue + cleaned.append(line) + prev_blank = is_blank + + while cleaned and cleaned[-1] == '': + cleaned.pop() + + new_content = '\n'.join(cleaned) + '\n' + + if new_content != content: + filepath.write_text(new_content, encoding='utf-8') + return True + return False + + +def main(): + workdir = Path(sys.argv[1]) + files = sorted(workdir.glob('*.md')) + + for f in files: + changed = process_file(f) + status = 'FIXED' if changed else 'OK' + print(f'{status}: {f.name}') + + +if __name__ == '__main__': + main() diff --git a/skills/dharma-translation/SKILL.md b/skills/dharma-translation/SKILL.md new file mode 100644 index 0000000..a4b7975 --- /dev/null +++ b/skills/dharma-translation/SKILL.md @@ -0,0 +1,75 @@ +--- +name: dharma-translation +description: Translate Chinese Buddhist Dharma talks into English djot with annotations. Use when the user asks to translate a Dharma talk, 开示, Buddhist lecture, or similar material from Chinese to English. +--- + +# Dharma Talk Translation + +## Trigger + +User asks to translate a Chinese Buddhist Dharma talk (开示, 讲座, 法义) to English, or convert a PDF of such material into a translated djot file. + +## Workflow + +### 1. Extract source text from PDF + +Prefer `pdftotext -layout` — it's the most reliable fallback and almost always available: + +```bash +pdftotext -layout input.pdf /tmp/extracted.txt +``` + +pypdf and pdfplumber may not be installed; pdftotext (poppler-utils) is the safe default. + +### 2. Convert to structured djot + +Clean the extracted text into a djot file (`original.dj`) with this structure: + +- `# Title` — h1, the talk title +- Subtitle line — date and venue, prefixed with `——` +- Opening remarks — body paragraphs before the first section +- `## 一、Section Name` — h2 for each numbered section (一、二、三、etc.) +- Closing line — revision date in parentheses + +Strip page numbers (standalone digits separated by form feeds `\f`). Join broken lines within paragraphs — Chinese text joins cleanly with `''.join()` since there are no inter-word spaces. + +### 3. Translate + +Translate directly — no external translation APIs. The model IS the translator. + +Key approach: +- Preserve the djot structure exactly (headings, paragraphs) +- Translate section headings preserving the Chinese numbering (一、→ 1., 二、→ 2., etc.) +- Keep the original Chinese in `%` comment lines alongside the translation (see annotations below) + +### 4. Annotate in djot comments + +Use `%` comment lines before relevant paragraphs to annotate: + +- **Buddhist technical terms**: Sanskrit equivalents (e.g., 五蕴 → five aggregates / skandhas), doctrinal context +- **Cultural references**: Historical figures, place names, sutra names with explanation +- **Structural notes**: Why the speaker frames something a certain way, organizational philosophy +- **Recurring metaphors**: The McDonald's analogy throughout, the lamp/light metaphor + +Format: +``` +% Chinese term (pinyin) = English gloss, Sanskrit if applicable. Brief context. +Paragraph text here... +``` + +For sutra quotes, annotate the sutra name with both Chinese and Sanskrit. +For organizational terms (传帮带, 分灯, 三种精神), explain their meaning within the community's framework. + +### 5. Output files + +- `original.dj` — cleaned Chinese djot +- `translated.dj` — English translation with `%` annotations + +## Pitfalls + +- Do NOT call external translation APIs. Translate directly. +- Do NOT delete comparison/对照 files — they are intentional work artifacts. +- pdftotext may produce hard line breaks at column boundaries; join within paragraphs using `''.join()` for Chinese. +- Page numbers appear as isolated digits between form feeds (`\f`); strip them with regex. +- Djot `%` comments work line-by-line; place them on their own lines before the relevant paragraph. +- Keep annotations concise — one or two lines, not an essay. diff --git a/skills/pptx-translate/SKILL.md b/skills/pptx-translate/SKILL.md new file mode 100644 index 0000000..89abda4 --- /dev/null +++ b/skills/pptx-translate/SKILL.md @@ -0,0 +1,84 @@ +--- +name: pptx-translate +description: Translate PowerPoint files between Chinese and English — extract strings to YAML, translate, quality review, and write back with font-shrink + auto-fit for layout. +category: productivity +--- + +# PPTX Translation + +Translate `.pptx` files between Chinese and English. Covers the full pipeline: extraction → translation → review → write-back. + +## Workflow + +### 1. Extract strings to YAML + +Run `scripts/extract.py original.pptx strings.yaml`. Produces YAML with entries: + +```yaml +- slide: 1 + shape: 0 + run: 0 + kind: title + zh: 开启生命的富足 + en: "" +``` + +- `slide` — 1-based slide number +- `shape` — 0-based shape index within slide +- `run` — 0-based paragraph index within text frame (or computed index for tables) +- `kind` — `title` | `subtitle` | `center_title` | `body` | `table` | `notes` +- `zh` — source text +- `en` — translation target (initially empty) + +Table `run` index formula: `num_rows * col + row`. Reverse with `row = run % num_rows`, `col = run // num_rows`. + +Speaker notes use `shape: -1`. + +### 2. Translate + +**Do NOT call external translation APIs.** Translate directly — the agent IS the model. The user corrects this: "Why do you call external models to do it? You can do it yourself!" + +Fill in the `en` field for every entry. Batch if needed, but translate in your response, not via API calls. + +Terminology guidance for Buddhist/gratitude content: +- 感恩=gratitude, 缘起=dependent origination, 众生=sentient beings +- 因缘=causes and conditions, 三宝=Three Jewels, 福报=merit/blessings +- 座上=formal practice, 座下=daily life practice, 共修=group practice +- 上报四重恩=repaying the four great kindnesses + +### 3. Quality review + +Scan for: +- Terminology consistency (same zh term → same en term throughout) +- Ellipsis convention — English uses 3 dots `...`, zh may use 6 +- Buddhist term accuracy +- Missing translations +- Overly literal renderings + +### 4. Write back with layout fixes + +Run `scripts/build.py strings.yaml original.pptx translated.pptx`. + +The script: +- Replaces text in matching paragraphs (clears all runs, sets first run) +- Replaces table cell text (using row/col from computed index) +- Reduces font size by 18% (`FONT_SCALE = 0.82`) on all translated shapes and tables +- Sets `auto_size = TEXT_TO_FIT_SHAPE` on text frames to handle overflow +- English text is ~1.3–1.5× longer than Chinese — font shrink + auto-fit handles most cases + +## Alternate scripts + +The absorbed `pptx-translation` skill had alternate script names: `extract_pptx.py` and `build_pptx.py`. These are functionally equivalent to `extract.py` and `build.py` with minor formatting differences (docstrings, variable naming). If the primary scripts fail, the alternates are available in the archive at `~/.hermes/skills/.archive/pptx-translation/scripts/`. + +## Pitfalls + +- "run" in the YAML is actually the **paragraph index** within a text frame, not the OOXML text-run index. python-pptx iterates paragraphs, not runs. +- Font shrink only applies to runs that have an explicit `font.size` — inherited sizes from paragraph/layout defaults are skipped. +- After write-back, verify with `python -m markitdown translated.pptx` to check text landed correctly. +- Tables: font shrink is applied per-cell text frame. Each cell is its own text frame. +- markitdown may fail with `ModuleNotFoundError: dotenv` — run `pip install python-dotenv` first. + +## Scripts + +- `scripts/extract.py` — extract strings from PPTX to YAML +- `scripts/build.py` — write translations back with font shrink + auto-fit diff --git a/skills/pptx-translate/scripts/build.py b/skills/pptx-translate/scripts/build.py new file mode 100644 index 0000000..f757cc8 --- /dev/null +++ b/skills/pptx-translate/scripts/build.py @@ -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}") diff --git a/skills/pptx-translate/scripts/extract.py b/skills/pptx-translate/scripts/extract.py new file mode 100644 index 0000000..90d9063 --- /dev/null +++ b/skills/pptx-translate/scripts/extract.py @@ -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]}") diff --git a/skills/translation-review/SKILL.md b/skills/translation-review/SKILL.md index c44f5e3..5b54171 100644 --- a/skills/translation-review/SKILL.md +++ b/skills/translation-review/SKILL.md @@ -1,93 +1,122 @@ --- name: translation-review -description: Review Chinese↔English translation pairs for quality issues — terminology errors, grammar, consistency, formatting. Works with CSV/XLSX files and writes edit suggestions in .dj format. +description: Review Chinese↔English translations for quality issues — terminology, grammar, consistency, formatting. Two workflows: CSV/XLSX batch review (write .dj suggestions) and .dj comparison line-by-line review (surgical patching). --- # Translation Review -## When to use +Two workflows, used depending on input format. -- User has a CSV or XLSX file with `Chinese`/`English` (or similar) columns -- User asks you to "find problems," "check translations," or "review localization" -- User mentions `.dj` edit-suggestions files +## Workflow A: CSV/XLSX batch review -## Workflow +Use when input is a CSV/XLSX with `Chinese`/`English` columns. Produces an `edit-suggestions.dj` file. ### 1. Get the data into CSV -If the file is XLSX, have the user export to CSV (or use `openpyxl` if installed). CSV is easier and faster to process. The user may also provide XHTML — CSV is preferred. +If XLSX, export to CSV (or use `openpyxl`). CSV is faster. ### 2. Read the full file -Use `read_file` with offsets to get the complete CSV into context. Don't sample — issues repeat across rows and you need full coverage. +Use `read_file` with offsets for complete coverage. Don't sample. ### 3. Write a systematic analysis script -Write a Python script to `/tmp/` and run it with `terminal: python3 /tmp/script.py`. Do NOT use heredocs (`<<'PYEOF'`) or `-c` — the terminal tool may block these. Always write to a temp file. +Write to `/tmp/script.py`, run with `python3 /tmp/script.py`. No heredocs or `-c`. The script should: -- Parse the CSV with `csv.DictReader` -- Apply detection rules (regex-based) for each known issue category -- Collect issues with: CSV row number, page context, CN text, EN text, problem description, suggested fix -- Group/deduplicate identical issues across rows +- Parse CSV with `csv.DictReader` +- Apply detection rules per category +- Collect issues: row number, CN text, EN text, problem, suggested fix +- Group/deduplicate identical issues -Common detection categories for Chinese→English: -- **Buddhist terminology**: 正念→mindfulness (not "righteous thoughts"), 布施→generosity (not "alms"), 胜解→resolute conviction, etc. -- **Identity terms**: 学士/修士/胜士/智士 are practice stages, not "bachelor/monk/winner/wise man" -- **Literal machine translations**: "Is we"→"if we", "hard drive" for 硬盘 (endurance), "Walk without letting go" for 行舍不放逸 -- **四摄法 terms**: 同事→"acting in harmony" (not "colleagues"), 爱语→"kind speech" (not "love words") -- **Grammar**: subject-verb agreement, "have it been"→"has it been", unbalanced quotes -- **Typos/formatting**: "AndroidAndroid", "IOS"→"iOS", unbalanced HTML tags, Chinese punctuation in English -- **UI terminology**: "Suspended"→"Paused" for media, product name consistency -- **Inconsistency**: same CN term translated differently across rows (e.g., "Bodhi Navigator" vs "Bodhi Navigation") +Common detection categories: +- **Buddhist terminology**: 正念→mindfulness (not "righteous thoughts"), 布施→generosity (not "alms") +- **Identity terms**: 学士/修士/胜士/智士 are practice stages, not titles +- **Literal machine translations**: "hard drive" for 硬盘 (endurance) +- **四摄法 terms**: 同事→"acting in harmony", 爱语→"kind speech" +- **Grammar**: subject-verb agreement, unbalanced quotes +- **Typos/formatting**: Chinese punctuation in English, "IOS"→"iOS" +- **Inconsistency**: same CN term translated differently across rows -### 4. Deduplicate into unique issue categories - -The same error pattern often repeats across many rows (e.g., "subversion" for 覆 appears in 6+ rows). Group these into single entries in the .dj file — one entry per unique problem, with a list of affected rows. - -### 5. Write edit-suggestions.dj +### 4. Write edit-suggestions.dj Format: - ``` # 1 original: translated: - + # 2 ... ``` -Each entry gets a `# N` header, `original:` and `translated:` fields, then a free-text explanation. End with suggested replacement text. Mention affected row numbers. For globally-wrong terms, note "Change globally." +One entry per problem category, not per row. Mention affected row numbers. -Do NOT write one entry per CSV row — group by problem type. +## Workflow B: .dj comparison file review -### 6. Sanity check +Use when input is a `.dj` comparison file (Chinese/English alternating line pairs). Produces `translation-findings.dj` and applies patches. -Run a quick second pass to catch: empty English fields, Chinese characters leaking into English column, untranslated rows (CN == EN), trailing whitespace. +### 1. Read the full file + +Use `terminal: cat` — `read_file` deduplicates within a session. + +### 2. Scan for problems (ordered by severity) + +**Terms database drift** (systematic): +- Cross-reference glossary terms against the MPI terms database +- HTTP API: `http://localhost:8910/search?q=...` (start: `python3 /home/user/documents/mpi/terms-search/server.py &`) +- Prefer DoT定稿 > 内部特色词 > 佛教术语 > 经论名 +- Fix both glossary comments AND body text +- See `references/terms-db-alignment.md` for batch-lookup patterns + +**Real errors** (affect meaning): +- Mistranslation of key terms +- Garbled/malformed source text +- Wrong proper names or technical terms + +**Inconsistency** (confusing but not wrong): +- Terminology drift across file +- Numbering style chaos +- Grammatical voice/person shifts + +**Cleanup needed**: +- Processing artifacts (HTML comments, markers) +- Stray spacing in Chinese text +- Awkward line splits +- Odd word choices + +**Missing content**: bare headings with no body — flag, don't invent. + +### 3. Dump findings to `translation-findings.dj` + +``` +Finding N — Title (line numbers) + Chinese: ... + English: ... + Issue: description +``` + +### 4. Apply fixes with `patch` + +Surgical string replacement. Verify every patch with `cat` — never rely on `read_file` (session dedup). + +## Buddhist terminology reference + +See `references/buddhist-terminology.md` for Chinese-English term mappings and common pitfalls. ## Pitfalls -- **Don't use heredocs or `-c` for multi-line Python** — write to `/tmp/script.py` first, then `python3 /tmp/script.py`. The terminal tool may block heredocs as long-lived processes. -- **Deduplicate aggressively** — 80+ raw issues may collapse to 20-25 unique categories. Writing one .dj entry per CSV row is useless noise. -- **Buddhist terminology is technical** — don't guess. 正念 is mindfulness (sati), not "righteous thoughts." 唯识 is Yogācāra/Consciousness-Only, not "knowledge and view alone." When uncertain, flag for human review rather than confidently suggesting wrong fixes. -- **Don't delete the comparison/对照 file** — translation projects keep these as intentional work artifacts. +- **Don't use heredocs or `-c`** — write to `/tmp/script.py` first +- **Deduplicate aggressively** — group by problem type, not per-row +- **Buddhist terminology is technical** — don't guess. When uncertain, flag for review +- **Never delete .dj comparison files** — intentional work artifacts +- **Verify patches with `cat`** — `read_file` dedup makes it unreliable +- **Re-read before fixing** — user may have made interim edits -## .dj file format reference +## References -``` -# N - -original: -translated: - - - -# N+1 -... -``` - -Entries may end with `{% TK %}` to mark "to check" items. The file lives alongside the source CSV/XLSX in the same directory. +- `references/buddhist-terminology.md` — Chinese-English Buddhist term mappings and pitfalls +- `references/terms-db-alignment.md` — Batch-aligning glossary terms against the MPI terms database diff --git a/skills/translation-review/references/buddhist-terminology.md b/skills/translation-review/references/buddhist-terminology.md new file mode 100644 index 0000000..45f4dd5 --- /dev/null +++ b/skills/translation-review/references/buddhist-terminology.md @@ -0,0 +1,47 @@ +# Buddhist Text Translation — Terminology + +Terms encountered in Chinese-English translation of Dharma study materials. These may vary by translator/context; document actual usage per-project. + +## Section headers (common triad) + +| Chinese | English options seen | Notes | +|---------|---------------------|-------| +| 法义 | Understanding, Dharma Teachings | | +| 思考 | Contemplation, Reflection | Consistency within a document matters more than which word | +| 练习 | Practice, Application, Exercises | "Application" seen as section header; "Practice"/"Exercise" in running text | + +## Key Buddhist terms + +| Chinese | English | Pitfalls | +|---------|---------|----------| +| 慈经 | Metta Sutta (Karaniya Metta Sutta) | NOT "Mettavihari Sutta" | +| 回向 | Dedication (of merit) | | +| 因缘之网 | Web of Causes and Conditions | Also "Network of Causes and Conditions" | +| 感恩 | Gratitude | | +| 众生 | sentient beings | Consistent throughout | +| 使人内心调柔 | makes one's heart gentle | 使人 = makes ONE(self), never "makes others" | +| 利益思维 | benefit-oriented thinking | NOT "mindset of benefiting others" — it's about considering benefits TO oneself | +| 恩田 | field of gratitude / gratitude as a field of merit | | +| 观照 | attend to the mind / mindful observation | Contemplative practice, not intellectual study. NOT "observe" (passive) or "study" (analytical). | +| 闻思修 | hearing, contemplating, cultivating | 修 = broad cultivation/practice, not specifically 禅 (meditation). Distinct from 禅修 (meditative cultivation). | +| 八步三禅 | Eight Steps and Three Meditations | Community-specific structured contemplative method | +| 传帮带 | transmit, help, guide (three-part mentoring) | Core community methodology | +| 分灯 | lamp-dividing (decentralization) | Deliberate decentralization of authority across nodes | +| 自觉 | self-awareness, voluntary commitment | First of the "Three Spirits" | +| 法治 | rule-based governance | Second of the "Three Spirits" | +| 无我利他 | selfless service to others | Third of the "Three Spirits" | +| 凡夫心 | ordinary mind | Mind governed by afflictions, contrasted with awakened mind | +| 贪嗔痴 | greed, anger, ignorance (三毒) | The three root poisons: rāga, dveṣa, moha | +| 愿心 | mind of vows, bodhicitta aspiration | Plural "vows" in English | +| 重要感、优越感、主宰欲 | sense of importance, superiority, desire to control | Three ego-driven motivations | + +## Structural patterns + +- Section numbering: Chinese uses 一、二、三... English should pick one style (Part One/Two, First/Second, I/II) and stick with it. +- Poetry/prayer blocks: Chinese uses parallel structures (感恩... 感恩...; 愿... 愿...). English must match the parallelism. +- 愿 (yuàn) at sentence start = optative "May..." — not "we hope that", not "we should". + +## Formatting artifacts + +- Stray spaces between Chinese characters (eg. `感 恩 研 究`) are justified-text paste artifacts from the source document — remove them. +- `` is a processing marker — remove from final file. diff --git a/skills/translation-review/references/terms-db-alignment.md b/skills/translation-review/references/terms-db-alignment.md new file mode 100644 index 0000000..defa836 --- /dev/null +++ b/skills/translation-review/references/terms-db-alignment.md @@ -0,0 +1,61 @@ +# Terms Database Alignment + +Batch-align translation glossary entries and body text against the MPI terms database. + +## Setup + +Start the HTTP API server if not running: +``` +python3 /home/user/documents/mpi/terms-search/server.py & +``` +Server listens on port 8910. + +## Batch lookup pattern + +Use Python via execute_code to query the API for multiple terms: + +```python +import urllib.request, json, urllib.parse + +terms = ["三无漏学", "八步三禅", "闻思修", ...] + +for term in terms: + q = urllib.parse.quote(term) + resp = urllib.request.urlopen(f"http://localhost:8910/search?q={q}&limit=5", timeout=10) + data = json.loads(resp.read()) + # Filter to authoritative sources + author_sources = ["DoT定稿", "内部特色词", "佛教术语", "经论名"] + relevant = [r for r in data["results"] if r["source"] in author_sources] + # Compare against current translation, report mismatches +``` + +Or with curl: +``` +curl -s "http://localhost:8910/search?q=三级修学&limit=5" | python3 -c "import sys,json; ..." +``` + +## Priority ranking + +When the same term has entries in multiple source tables, prefer: +1. DoT定稿 (highest authority — final translation decisions) +2. 内部特色词 (MPI internal terminology) +3. 佛教术语 (general Buddhist terminology) +4. 经论名 (sutra/shastra titles) + +## Alignment workflow + +1. Extract all Chinese glossary terms from `{% "TERM" ... %}` blocks in the .dj file +2. Extract body-text domain terms that may not have glossary entries +3. Batch-query each term against the HTTP API +4. Filter results to authoritative source tables +5. Compare DB canonical translation against current file translation +6. Flag mismatches where DB entry differs materially from current +7. Apply fixes with `patch` tool — fix both glossary comments AND body text occurrences +8. Verify with `grep` that no old terms remain + +## Pitfalls + +- `replace_all` can create doubled words when the surrounding context already contains the replacement string (e.g., "The Eight Steps" → "The The Eight Steps"). Prefer targeted single-replacement patches. +- The `search.py` CLI does not support `src:` or `loc:` filters — use the HTTP API. +- Start patches from the bottom of the file upward to preserve line numbers. +- Some DB entries are contextual phrases (e.g., "珍惜法缘" → a full sentence), not standalone term translations. Use standalone term entries where available.