name: chm-translate description: "Translate CHM (Compiled HTML Help) documentation files into another language. Covers the complete workflow: decompile CHM with 7-Zip, translate HTML/HHC/HHK content using DeepSeek API with concurrent chunking, rebuild HHC/HHK with GBK encoding for correct Chinese TOC display, generate a correct HHP project file with all images included, recompile with hhc.exe, and binary-patch #WINDOWS to restore navigation panels and toolbar buttons. This skill should be used when the user wants to translate a .chm help file, localize CHM documentation, or convert CHM content to another language. Triggers: translate chm, 翻譯chm, localize chm, CHM翻譯, 幫助文件翻譯, chm to chinese." agent_created: true
Translate Microsoft CHM (Compiled HTML Help) documentation files into another language (typically Chinese). This skill handles the complete pipeline: decompile → translate → fix encoding → recompile, with critical attention to the encoding incompatibilities of the legacy hhc.exe compiler.
.chm help file to another languageThis skill depends on two other skills — load both before starting:
These pitfalls were discovered through hard experience. Ignoring them will cause compilation failures or broken CHM navigation:
Previous approach (HTML entities) is WRONG. Earlier documentation recommended converting Chinese to HTML numeric entities (简) and saving as windows-1252. Testing revealed this causes garbled text in the TOC panel:
&#xxxx; entities简 text or garbled characters instead of ChineseCorrect approach: GBK encoding. On Chinese Windows (code page 936), hhc.exe can compile GBK-encoded HHC/HHK files without errors. The GBK bytes are stored as-is and correctly rendered by hh.exe's TOC panel.
UTF-8 still does NOT work — it causes HHC5003 errors because hhc.exe's parser interprets multi-byte UTF-8 sequences as windows-1252, breaking HTML tags.
Solution: Save HHC/HHK files with GBK encoding (raw Chinese characters, not entities):
# Read translated HHC (UTF-8)
with open('translated.hhc', 'r', encoding='utf-8') as f:
text = f.read()
# Write as GBK
with open('output.hhc', 'wb') as f:
f.write(text.encode('gbk'))
If some characters can't be encoded in GBK (rare), use errors='replace' as fallback.
Any non-ASCII character in the HHP file (including the Title field) causes silent compilation failure.
Solution: Use English-only text in the HHP file. For the Title, use ASCII text like POV-Ray 3.7 Documentation (Chinese).
The [WINDOWS] line is a comma-separated field list. Between the default topic (field 5) and the toolbar flags (field 10), there must be exactly 6 commas (5 empty fields: home, jump1url, jump1text, jump2url, jump2text). If there are only 5 commas, the toolbar flags value is parsed as jump2text instead, leaving the actual toolbar flags empty — this causes the CHM to open with NO navigation panel and NO toolbar buttons.
Correct format (count the commas carefully):
main="Title","toc.hhc","index.hhk","default.html",,,,,,0x77F3E,220,0x10384e,[0,0,1024,768],0,0
Fields: caption, toc, index, default, home, jump1url, jump1text, jump2url, jump2text, toolbar_flags, nav_width, win_props, win_rect, show_state, valid_info
Use 0x77F3E to enable all standard navigation panels and toolbar buttons:
| Bit | Name | Function |
|---|---|---|
| 0x2 | BACK | Back button |
| 0x4 | FORWARD | Forward button |
| 0x8 | STOP | Stop button |
| 0x10 | REFRESH | Refresh button |
| 0x20 | HOME | Home button |
| 0x200 | CONTENTS | Table of Contents panel |
| 0x400 | SYNC | Sync button |
| 0x800 | OPTIONS | Options button |
| 0x1000 | Print button | |
| 0x2000 | INDEX | Index panel |
| 0x4000 | SEARCH | Search panel |
| 0x10000 | FAVORITES | Favorites panel |
| 0x20000 | JUMP1 | Jump 1 button |
| 0x40000 | JUMP2 | Jump 2 button |
| 0x100 | NOTES | Notes (optional) |
DeepSeek API sometimes wraps translated HTML content in markdown code blocks (html ...). These must be stripped from all translated files before compilation.
Files over ~20KB should be split by heading tags (<h2>-<h4>). Files without headings (e.g., table-of-contents pages) should be split by ` — stylesheets and scripts
-images/` — image resources
Count files and estimate total text size:
cd extracted_dir
echo "HTML files:" && find . -name "*.html" -o -name "*.htm" | wc -l
echo "HHC/HHK:" && find . -name "*.hhc" -o -name "*.hhk"
echo "Images:" && find . \( -name "*.png" -o -name "*.gif" -o -name "*.jpg" \) | wc -l
echo "Total text:" && find . -name "*.html" -exec cat {} \; | wc -c
If total text exceeds ~50KB, use the API translation script. If smaller, consider manual translation.
Use scripts/translate_chm.py for the main translation. This script:
- Reads all HTML/HHC/HHK files with automatic encoding detection
- Splits large files by heading tags into chunks (~20KB each)
- Translates all chunks concurrently using DeepSeek API
- Reassembles translated chunks into complete files
- Fixes charset and lang attributes
- Writes output as UTF-8
Configuration (edit the script before running):
DEEPSEEK_API_KEY = "sk-..." # API key
MODEL = "deepseek-v4-flash" # Model name
INPUT_DIR = Path(r"path/to/extracted") # Extracted CHM directory
OUTPUT_DIR = Path(r"path/to/translated") # Output directory
CHUNK_THRESHOLD = 30000 # Files larger than this get chunked
TARGET_CHUNK_SIZE = 20000 # Target chunk size in characters
CONCURRENCY = 15 # Concurrent API calls
Translation system prompt (designed for technical documentation):
You are a professional translator. Translate the following text from English to Chinese (Simplified).
Translation rules:
1. Preserve ALL formatting: HTML tags, attributes, structure, lists, tables, etc.
2. Do NOT translate code, variable names, function names, CLI commands, or file paths.
3. Do NOT translate the content of HTML attributes like href, src, name, class, id.
4. Only translate visible text content between HTML tags.
5. Maintain the original document structure and line breaks.
6. Use industry-standard terminology for technical terms.
7. If unsure about a term, keep the original and add translation in parentheses.
8. Keep section numbers (like 3.4.1.1) as-is.
9. Output ONLY the translated text — no explanations, no notes, no preamble.
Running the script:
PYTHONUNBUFFERED=1 python -u translate_chm.py
oreets and scripts -images/— image resources
Count files and estimate total text size:
cd extracted_dir
echo "HTML files:" && find . -name "*.html" -o -name "*.htm" | wc -l
echo "HHC/HHK:" && find . -name "*.hhc" -o -name "*.hhk"
echo "Images:" && find . \( -name "*.png" -o -name "*.gif" -o -name "*.jpg" \) | wc -l
echo "Total text:" && find . -name "*.html" -exec cat {} \; | wc -c
If total text exceeds ~50KB, use the API translation script. If smaller, consider manual translation.
Use scripts/translate_chm.py for the main translation. This script:
- Reads all HTML/HHC/HHK files with automatic encoding detection
- Splits large files by heading tags into chunks (~20KB each)
- Translates all chunks concurrently using DeepSeek API
- Reassembles translated chunks into complete files
- Fixes charset and lang attributes
- Writes output as UTF-8
Configuration (edit the script before running):
DEEPSEEK_API_KEY = "sk-..." # API key
MODEL = "deepseek-v4-flash" # Model name
INPUT_DIR = Path(r"path/to/extracted") # Extracted CHM directory
OUTPUT_DIR = Path(r"path/to/translated") # Output directory
CHUNK_THRESHOLD = 30000 # Files larger than this get chunked
TARGET_CHUNK_SIZE = 20000 # Target chunk size in characters
CONCURRENCY = 15 # Concurrent API calls
Translation system prompt (designed for technical documentation):
You are a professional translator. Translate the following text from English to Chinese (Simplified).
Translation rules:
1. Preserve ALL formatting: HTML tags, attributes, structure, lists, tables, etc.
2. Do NOT translate code, variable names, function names, CLI commands, or file paths.
3. Do NOT translate the content of HTML attributes like href, src, name, class, id.
4. Only translate visible text content between HTML tags.
5. Maintain the original document structure and line breaks.
6. Use industry-standard terminology for technical terms.
7. If unsure about a term, keep the original and add translation in parentheses.
8. Keep section numbers (like 3.4.1.1) as-is.
9. Output ONLY the translated text — no explanations, no notes, no preamble.
Running the script:
PYTHONUNBUFFERED=1 python -u translate_chm.py
` tags. If a chunk returns empty from the API, retry with a smaller chunk size (5KB).
The API may return empty content for files that are primarily link tables with little translatable text. Always check if the translated output is empty and retry with smaller chunks. If still empty after retry, use the original content as fallback.
This is the most critical pitfall. Even with a perfectly correct HHP file, hhc.exe always writes fsWinProperties=0x6E into the compiled #WINDOWS binary, completely ignoring the windowstyles value in the HHP [WINDOWS] line.
The value 0x6E contains two fatal flag bits:
- 0x20 (NO_TOOLBAR) — hides the entire toolbar (隱藏/查詢/上一步/下一步/前進/停止/重新整理/主頁/字型/列印/選項 buttons)
- 0x08 (NODEF_STYLES) — prevents default window styles, hiding the navigation panel (目錄/索引/搜尋/收藏夾 tabs)
The correct value (from the original CHM) is 0x516, which does NOT contain these bits.
Minimal patch (fsWinProperties only) is NOT sufficient. Testing showed that patching only fsWinProperties from 0x6E to 0x516 restores the left navigation panels (目錄/索引/搜尋/收藏夾) but does NOT restore the toolbar buttons. The full fix requires copying ALL non-string-offset fields from the original CHM's #WINDOWS into the compiled CHM's #WINDOWS.
Solution: Binary-patch the compiled CHM's #WINDOWS after compilation. See Step 9 and references/windows_binary_patch.md for the complete procedure.
hhc.exe only includes files explicitly listed in the HHP [FILES] section. If images are not listed, hhc.exe may auto-include some images referenced by <img> tags, but it flattens their paths (e.g., images/3/34/DocImgPovlogotext.jpg becomes DocImgPovlogotext.jpg), causing path mismatches with HTML references.
Solution: The gen_hhp.py script must collect ALL files (HTML + images + CSS + JS) with their full relative paths and include them in the [FILES] section. See the updated scripts/gen_hhp.py.
Use 7-Zip to extract the CHM file:
"/c/Program Files/7-Zip/7z.exe" x "input.chm" -o"extracted_dir" -y
Verify the extracted contents:
- *.html — content pages
- *.hhc — table of contents
- *.hhk — index file
- *.css, *.js — stylesheets and scripts
- images/ — image resources
Count files and estimate total text size:
cd extracted_dir
echo "HTML files:" && find . -name "*.html" -o -name "*.htm" | wc -l
echo "HHC/HHK:" && find . -name "*.hhc" -o -name "*.hhk"
echo "Images:" && find . \( -name "*.png" -o -name "*.gif" -o -name "*.jpg" \) | wc -l
echo "Total text:" && find . -name "*.html" -exec cat {} \; | wc -c
If total text exceeds ~50KB, use the API translation script. If smaller, consider manual translation.
Use scripts/translate_chm.py for the main translation. This script:
- Reads all HTML/HHC/HHK files with automatic encoding detection
- Splits large files by heading tags into chunks (~20KB each)
- Translates all chunks concurrently using DeepSeek API
- Reassembles translated chunks into complete files
- Fixes charset and lang attributes
- Writes output as UTF-8
Configuration (edit the script before running):
DEEPSEEK_API_KEY = "sk-..." # API key
MODEL = "deepseek-v4-flash" # Model name
INPUT_DIR = Path(r"path/to/extracted") # Extracted CHM directory
OUTPUT_DIR = Path(r"path/to/translated") # Output directory
CHUNK_THRESHOLD = 30000 # Files larger than this get chunked
TARGET_CHUNK_SIZE = 20000 # Target chunk size in characters
CONCURRENCY = 15 # Concurrent API calls
Translation system prompt (designed for technical documentation):
You are a professional translator. Translate the following text from English to Chinese (Simplified).
Translation rules:
1. Preserve ALL formatting: HTML tags, attributes, structure, lists, tables, etc.
2. Do NOT translate code, variable names, function names, CLI commands, or file paths.
3. Do NOT translate the content of HTML attributes like href, src, name, class, id.
4. Only translate visible text content between HTML tags.
5. Maintain the original document structure and line breaks.
6. Use industry-standard terminology for technical terms.
7. If unsure about a term, keep the original and add translation in parentheses.
8. Keep section numbers (like 3.4.1.1) as-is.
9. Output ONLY the translated text — no explanations, no notes, no preamble.
Running the script:
PYTHONUNBUFFERED=1 python -u translate_chm.py
Use
PYTHONUNBUFFERED=1and-uflag to avoid Python output buffering issues when running in background.
After translation, check for empty output files:
cd translated_dir
for f in *.html *.hhc *.hhk; do
size=$(wc -c < "$f" 2>/dev/null)
if [ "$size" -lt 10 ]; then
echo "EMPTY: $f ($size bytes)"
fi
done
For empty files, retry translation with smaller chunk size (5KB) and split by scripts/gen_hhp.py or create manually:
[OPTIONS]
Compatibility=1.1 or later
Compiled file=output_zh.chm
Contents file=toc.hhc
Index file=index.hhk
Default Topic=index.html
Display compile progress=Yes
Language=0x804
Title=Document Title (English Only)
[WINDOWS]
main="Title","toc.hhc","index.hhk","index.html",,,,,,0x77F3E,220,0x10384e,[0,0,1024,768],0,0
[FILES]
index.html
page1.html
page2.html
[INFOTYPES]
Critical requirements:
- Language=0x804 for Simplified Chinese
- Title must be ASCII only (no Chinese characters)
- [WINDOWS] line must have exactly 6 commas between default topic and toolbar flags
- Toolbar flags = 0x77F3E for full navigation panel + toolbar
- [FILES] section must list ALL HTML files
Auto-generating [FILES] section (must include ALL files — HTML, images, CSS, JS — with full relative paths):
from pathlib import Path
FILE_EXTENSIONS = {'.html', '.htm', '.css', '.js',
'.jpg', '.jpeg', '.png', '.gif', '.bmp', '.ico',
'.svg', '.webp'}
output_dir = Path("translated_dir")
all_files = []
for f in sorted(output_dir.rglob("*")):
if f.is_file() and f.suffix.lower() in FILE_EXTENSIONS:
rel_path = f.relative_to(output_dir).as_posix() # use forward slashes
all_files.append(rel_path)
files_section = "\n".join(all_files)
or line boundaries instead of heading tags. Usescripts/retry_empty.py` as a template.
Run scripts/fix_translated.py to clean up common issues:
html and markers that the API may have addedcharset=windows-1252 to charset=utf-8lang="en" to lang="zh-CN"This is a critical step. Do NOT skip it.
The translation script outputs HHC/HHK as UTF-8, but hhc.exe cannot compile UTF-8 HHC files (HHC5003 error). Use scripts/rebuild_hhc_hhk.py to:
Local URL values as keys)Name values in the original file with translated valuesThis approach preserves the original file structure perfectly while injecting translated text.
Running the script:
7w4.net小蔥技能。
python rebuild_hhc_hhk.py --input-dir extracted_dir --output-dir translated_dir
The script automatically detects HHC and HHK files by extension.
Why GBK, not entities: Testing showed that HTML entities (简) are stored as literal text by hhc.exe and displayed garbled in the CHM viewer's TOC panel. GBK-encoded Chinese characters are stored and rendered correctly on Chinese Windows systems.
Create a pure ASCII HHP file. Use scripts/gen_hhp.py or create manually:
[OPTIONS]
Compatibility=1.1 or later
Compiled file=output_zh.chm
Contents file=toc.hhc
Index file=index.hhk
Default Topic=index.html
Display compile progress=Yes
Language=0x804
Title=Document Title (English Only)
[WINDOWS]
main="Title","toc.hhc","index.hhk","index.html",,,,,,0x77F3E,220,0x10384e,[0,0,1024,768],0,0
[FILES]
index.html
page1.html
page2.html
[INFOTYPES]
Critical requirements:
- Language=0x804 for Simplified Chinese
- Title must be ASCII only (no Chinese characters)
- [WINDOWS] line must have exactly 6 commas between default topic and toolbar flags
- Toolbar flags = 0x77F3E for full navigation panel + toolbar
- [FILES] section must list ALL HTML files
Auto-generating [FILES] section (must include ALL files — HTML, images, CSS, JS — with full relative paths):
from pathlib import Path
FILE_EXTENSIONS = {'.html', '.htm', '.css', '.js',
'.jpg', '.jpeg', '.png', '.gif', '.bmp', '.ico',
'.svg', '.webp'}
output_dir = Path("translated_dir")
all_files = []
for f in sorted(output_dir.rglob("*")):
if f.is_file() and f.suffix.lower() in FILE_EXTENSIONS:
rel_path = f.relative_to(output_dir).as_posix() # use forward slashes
all_files.append(rel_path)
files_section = "\n".join(all_files)
Critical: If images are not listed in [FILES], hhc.exe flattens their paths (e.g.,
images/3/34/xxx.jpg→xxx.jpg), causing broken images. See Pitfall 9.
cd translated_dir && "/path/to/hhc.exe" project.hhp
Verify compilation success:
- Exit code 0 = success
- "未編譯檔案" (uncompiled files) list should be empty
- Check that the output .chm file exists and has reasonable size
Common compilation errors:
| Error | Cause | Fix |
|---|---|---|
| HHC5003 | HHC/HHK file is UTF-8 encoded | Rebuild with GBK encoding (Step 6) |
| HHC3000 | HHP file contains non-ASCII characters | Rewrite HHP as pure ASCII (Step 7) |
| No navigation panel/toolbar | hhc.exe writes wrong fsWinProperties (always 0x6E) | Binary-patch #WINDOWS (Step 9) — this is mandatory for every CHM |
| HHC6003 (itircl.dll) | Full-text search DLL not registered | Non-fatal warning; CHM still works without search |
This step is mandatory. Without it, the compiled CHM will have no navigation panel and no toolbar buttons (see Pitfall 8).
The challenge: hhc.exe stores #WINDOWS inside the LZX-compressed section (section 1) of the CHM, so it cannot be directly modified in the binary file. The solution is to migrate #WINDOWS from the compressed section to the uncompressed section (section 0), then patch it there.
Use scripts/patch_windows.py to perform the patch automatically. The script:
/#WINDOWS entry1→0, offset → new locationPrerequisites: You need the original CHM file to extract the correct #WINDOWS field values.
python patch_windows.py \
--compiled-chm translated.chm \
--original-chm original.chm \
--output patched.chm
Key fields patched (offsets within the 204-byte #WINDOWS structure):
| Offset | Size | Field | Why |
|---|---|---|---|
| 0x14 | 4 | fsWinProperties | 0x6E→original (e.g. 0x516); removes NO_TOOLBAR and NODEF_STYLES flags |
| 0x20 | 4 | dwStyles | Window styles (WS_OVERLAPPEDWINDOW etc.); needed for toolbar creation |
| 0x24 | 4 | dwExStyles | Extended window styles |
| 0x28 | 4 | unknown | Unknown but affects window behavior |
| 0x78 | 4 | unknown | Unknown but affects toolbar display |
| 0xA4 | 4 | pszTocTitle | String table offset for TOC title (set to 0 if no "Start" string) |
String-offset fields NOT patched (offsets 0x1C, 0x68, 0x6C, 0x70, 0x74): These point into the compiled CHM's #STRINGS table, which differs from the original. They must keep the compiled values.
Verification:
# 7-zip should open without warnings
"/c/Program Files/7-Zip/7z.exe" l patched.chm
# Extract and verify #WINDOWS
"/c/Program Files/7-Zip/7z.exe" x -o/tmp/verify patched.chm "#WINDOWS"
python -c "import struct; d=open('/tmp/verify/#WINDOWS','rb').read(); print(f'fsWinProperties: 0x{struct.unpack_from(\"<I\",d,0x14)[0]:X}')"
See references/windows_binary_patch.md for the full technical details.
Main translation script. Reads all HTML/HHC/HHK files, chunks large files by heading tags, translates concurrently via DeepSeek API, writes UTF-8 output. Configure API key, model, input/output directories, chunk size, and concurrency at the top of the script.
Retry translation for files that returned empty output. Uses smaller chunk size (5KB) and splits by </tr> tags or line boundaries instead of heading tags. Includes fallback to original content if API still returns empty.
Post-translation cleanup. Removes markdown code block wrappers (html /), fixes charset meta tags (windows-1252 → utf-8), fixes lang attribute (en → zh-CN), and cleans excess blank lines.
Rebuilds HHC/HHK files with GBK encoding. Reads original HHC/HHK to preserve structure, extracts translated Name values from the translated version, replaces Name values in the original, and writes as GBK encoding (NOT HTML entities — entities cause garbled TOC text, see Pitfall 1). This is the critical step that makes hhc.exe able to compile Chinese HHC/HHK files.
Generates a pure-ASCII HHP project file with correct [WINDOWS] line (6 commas, full toolbar flags) and auto-generated [FILES] section. Now includes ALL files (HTML + images + CSS + JS) with their full relative paths, not just top-level HTML files.
Binary-patches the compiled CHM's #WINDOWS to fix navigation panel and toolbar display. Migrates #WINDOWS from the LZX-compressed section (section 1) to the uncompressed section (section 0), copies all non-string-offset fields from the original CHM's #WINDOWS, and updates the PMGL directory and HST table. This is the mandatory post-compilation step (see Step 9).
Detailed explanation of all encoding-related pitfalls discovered during CHM translation, including why hhc.exe fails with UTF-8, why HTML entity encoding does NOT work (causes garbled TOC), how GBK encoding is the correct approach, and the exact HHP [WINDOWS] field layout with correct comma positions.
Complete technical reference for the #WINDOWS binary patching procedure, including: CHM file structure (ITSF/ITSP/PMGL), #WINDOWS 204-byte layout, why hhc.exe produces wrong values, the section-migration technique, ENCINT encoding, and field-by-field patching guide.
CHM file
↓ 7-Zip extract
Extracted files (HTML + HHC + HHK + images)
↓ translate_chm.py (DeepSeek API, concurrent chunking)
Translated files (UTF-8)
↓ fix_translated.py (strip ```html, fix charset/lang)
Cleaned translated files
↓ rebuild_hhc_hhk.py (GBK encoding for Chinese TOC text)
HHC/HHK ready for compilation
↓ gen_hhp.py (pure ASCII, correct [WINDOWS], ALL files in [FILES])
HHP project file
↓ hhc.exe compile
Compiled CHM (navigation panel + toolbar MISSING)
↓ patch_windows.py (binary patch #WINDOWS from original CHM)
Patched CHM (navigation panel + toolbar WORKING)
這個 Skill 質量很好,文件寫得很詳細清楚,遇到的坑和解決方案都講得很明白,程式碼邏輯清晰、容易理解。優點是功能完整、教程詳盡、技術專業;不足是腳本里有硬編碼的 API 金鑰存在安全風險,建議開發者改進。總體推薦使用。