diff --git a/.claude/skills/wiki-sync-translate/SKILL.md b/.claude/skills/wiki-sync-translate/SKILL.md index e775e78..b8cd905 100644 --- a/.claude/skills/wiki-sync-translate/SKILL.md +++ b/.claude/skills/wiki-sync-translate/SKILL.md @@ -20,15 +20,27 @@ description: 同步英文 MediaWiki 页面变更到中文翻译文档。当用 ## 执行步骤 -### Step 1: 运行同步脚本 +### Step 1: 更新符文之语名称参考 + +每次同步前先拉取最新的符文之语中文名称: -使用 skill 目录下的专用脚本获取变更: - ```bash cd /mnt/d/code/sync-pd2-wiki source venv/bin/activate -python .claude/skills/wiki-sync-translate/ -scripts/wiki_sync.py --title "<页面名称>" --since <上次同步时间> --run +python .claude/skills/wiki-sync-translate/scripts/fetch_runeword_names.py +``` + +此脚本从中文 Wiki 拉取 RWWeapons、RWChests、RWQuivers、RWShields、RWHelms 五个页面,提取所有符文之语的中英文名称,保存到 `references/runeword_names.json`。翻译符文之语相关页面时必须参考此文件。 + +### Step 2: 运行同步脚本 + +使用 skill 目录下的专用脚本获取变更: + +```bash +cd /mnt/d/code/sync-pd2-wiki +source venv/bin/activate +python .claude/skills/wiki-sync-translate/ +scripts/wiki_sync.py --title "<页面名称>" --since <上次同步时间> --run ``` 参数说明: @@ -36,7 +48,7 @@ scripts/wiki_sync.py --title "<页面名称>" --since <上次同步时间> --run - `--since`: 起始时间,格式如 `2026-01-02T12:07:05Z` - `--run`: 必须提供此参数才会执行 -### Step 2: 读取输出文件 +### Step 3: 读取输出文件 脚本会在 `wiki_sync_output/<时间戳>/changed_pages/` 目录下生成: @@ -50,7 +62,7 @@ scripts/wiki_sync.py --title "<页面名称>" --since <上次同步时间> --run **重要:** 只读取 `comparison.json`,不要读取整个 `*.cn.txt` 文件以节省 token。 -### Step 3: 解析 comparison.json +### Step 4: 解析 comparison.json `comparison.json` 格式: ```json @@ -88,7 +100,7 @@ scripts/wiki_sync.py --title "<页面名称>" --since <上次同步时间> --run **注意:** 如果 `old_line` 和 `new_line` 差距很大但内容相似(如行号从171变到193),这通常意味着中间有其他行被插入或删除,需要仔细检查变更是否真的相关。 -### Step 4: 更新中文文档 +### Step 5: 更新中文文档 **核心原则:行号必须完全一致,使用增量修改减少 token 消耗** @@ -117,7 +129,7 @@ cp wiki_sync_output/<时间戳>/changed_pages/*.cn.txt wiki_sync_output/<时间 // 3. 只读取该行附近内容确认,然后用 Edit 修改 ``` -### Step 5: 输出结果 +### Step 6: 输出结果 更新后的文档位于 `wiki_sync_output/<时间戳>/result_pages/<页面名>.cn.txt`,用户可直接复制到 Wiki。 @@ -167,6 +179,11 @@ cp wiki_sync_output/<时间戳>/changed_pages/*.cn.txt wiki_sync_output/<时间 翻译时参考 `references/PD2_glossary.md` 中的标准术语对照表,确保技能名称、灵气名称和通用术语的翻译一致。优先级高于模型自身的翻译选择。 -## 翻译术语表 +## 符文之语名称规则 -翻译时参考 `references/PD2_glossary.md` 中的标准术语对照表,确保所有技能名称、灵气名称和通用术语的翻译一致。 \ No newline at end of file +**`{{#lsth:}}` 模板必须使用中文+英文格式**,因为中文 Wiki 页面中每个符文之语的章节标题是 `=== 中文名 英文名 ===` 格式。例如: + +- 正确:`{{#lsth:RWWeapons|力量 Strength}}` +- 错误:`{{#lsth:RWWeapons|Strength}}` (只写英文名无法匹配中文页面的章节) + +**名称来源:** 翻译时必须查阅 `references/runeword_names.json`(由 Step 1 的脚本从中文 Wiki 实时拉取生成)。该 JSON 的 key 是英文原名,value 是 `中文名 英文名`。不得自行猜测或翻译符文之语名称。 \ No newline at end of file diff --git a/.claude/skills/wiki-sync-translate/references/runeword_names.json b/.claude/skills/wiki-sync-translate/references/runeword_names.json new file mode 100644 index 0000000..fa639a5 --- /dev/null +++ b/.claude/skills/wiki-sync-translate/references/runeword_names.json @@ -0,0 +1,103 @@ +{ + "Steel": "钢铁 Steel", + "Malice": "怨恨 Malice", + "Leaf": "叶子 Leaf", + "Pattern": "图纹 Pattern", + "Zephyr": "和风 Zephyr", + "Holy Thunder": "神圣雷击 Holy Thunder", + "Neophyte": "新教徒 Neophyte", + "Strength": "力量 Strength", + "Edge": "边缘 Edge", + "King's Grace": "王者的慈悲 King's Grace", + "Spirit": "精神 Spirit", + "Purity": "纯洁 Purity", + "Insight": "眼光 Insight", + "Honor": "荣耀 Honor", + "Rampage": "狂暴 Rampage", + "Echo": "回响 Echo", + "Black": "黑色 Black", + "White": "白色 White", + "Memory": "记忆 Memory", + "Harmony": "和谐 Harmony", + "Melody": "旋律 Melody", + "Unbending Will": "坚定意志 Unbending Will", + "Obedience": "遵从 Obedience", + "Passion": "热情 Passion", + "Voice of Reason": "思考之声 Voice of Reason", + "Lawbringer": "执法者 Lawbringer", + "Loyalty": "忠诚 Loyalty", + "Crescent Moon": "新月 Crescent Moon", + "Venom": "毒牙 Venom", + "Oath": "誓约 Oath", + "Rift": "裂缝 Rift", + "Kingslayer": "弑王者 Kingslayer", + "Heart of the Oak": "橡树之心 Heart of the Oak", + "Silence": "寂静 Silence", + "Death": "死神 Death", + "Chaos": "混沌 Chaos", + "Call to Arms": "战争召唤 Call to Arms", + "Fortitude": "刚毅 Fortitude", + "Grief": "悔恨 Grief", + "Wind": "轻风 Wind", + "Wrath": "愤怒 Wrath", + "Beast": "野兽 Beast", + "Eternity": "永恒 Eternity", + "Infinity": "无限 Infinity", + "Fury": "狂怒 Fury", + "Famine": "饥荒 Famine", + "Faith": "信心 Faith", + "Ice": "冰冻 Ice", + "Brand": "烙印 Brand", + "Phoenix": "凤凰 Phoenix", + "Destruction": "毁灭 Destruction", + "Last Wish": "最后希望 Last Wish", + "Rapture": "狂喜 Rapture", + "Plague": "瘟疫 Plague", + "Mist": "迷雾 Mist", + "Dominion": "圣域 Dominion", + "Doom": "末日 Doom", + "Hand of Justice": "正义之手 Hand of Justice", + "Pride": "骄傲 Pride", + "Asylum": "庇护 Asylum", + "Obsession": "着魔 Obsession", + "Breath of the Dying": "死亡呼吸 Breath of the Dying", + "Zenith": "天顶 Zenith", + "Stealth": "隐密 Stealth", + "Peace": "和平 Peace", + "Myth": "神话 Myth", + "Smoke": "烟雾 Smoke", + "Hustle": "催促 Hustle", + "Lionheart": "狮子心 Lionheart", + "Treachery": "背信 Treachery", + "Wealth": "财富 Wealth", + "Enlightenment": "教化 Enlightenment", + "Duress": "强制 Duress", + "Stone": "石块 Stone", + "Gloom": "幽暗 Gloom", + "Bone": "白骨 Bone", + "Prudence": "慎重 Prudence", + "Rain": "降雨 Rain", + "Principle": "原理 Principle", + "Bramble": "野蔷薇 Bramble", + "Dragon": "飞龙 Dragon", + "Chains of Honor": "荣耀之链 Chains of Honor", + "Enigma": "谜团 Enigma", + "Innocence": "纯真 Innocence", + "Ancient's Scripture": "远古圣痕 Ancient's Scripture", + "Ancient's Foresight": "远古预兆 Ancient's Foresight", + "Ancients' Pledge": "古代人的契约 Ancients' Pledge", + "Rhyme": "押韵 Rhyme", + "Splendor": "灿烂 Splendor", + "Sanctuary": "圣堂 Sanctuary", + "Exile": "流亡 Exile", + "Epiphany": "顿悟 Epiphany", + "Dream": "梦境 Dream", + "Shattered Wall": "破碎之墙 Shattered Wall", + "Nadir": "天底 Nadir", + "Radiance": "光辉 Radiance", + "Lore": "知识 Lore", + "Wisdom": "智慧 Wisdom", + "Delirium": "迪勒瑞姆 Delirium", + "Flickering Flame": "闪烁火焰 Flickering Flame", + "Ferocity": "残暴 Ferocity" +} \ No newline at end of file diff --git a/.claude/skills/wiki-sync-translate/scripts/fetch_runeword_names.py b/.claude/skills/wiki-sync-translate/scripts/fetch_runeword_names.py new file mode 100644 index 0000000..ed422a6 --- /dev/null +++ b/.claude/skills/wiki-sync-translate/scripts/fetch_runeword_names.py @@ -0,0 +1,122 @@ +# -*- coding: utf-8 -*- +""" +从中文 Wiki 拉取所有符文之语页面,提取符文之语的中英文名称并保存为 JSON。 +供 wiki-sync-translate skill 在翻译符文之语相关页面时参考使用。 +""" + +import os +import re +import json +import requests +from pathlib import Path + +# ==================== 配置区 ==================== +WIKI_API_URL_CN = os.getenv("WIKI_API_URL_CN", "https://wiki.projectdiablo2.cn/w/api.php") + +SESSION_CN = requests.Session() +SESSION_CN.headers.update({ + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" +}) +SESSION_CN.trust_env = False + +# 符文之语页面列表(按装备部位) +RUNEWORD_PAGES = [ + "RWWeapons", + "RWChests", + "RWQuivers", + "RWShields", + "RWHelms", +] + +# 输出路径 +SCRIPT_DIR = Path(__file__).resolve().parent +REFERENCES_DIR = SCRIPT_DIR.parent / "references" +OUTPUT_FILE = REFERENCES_DIR / "runeword_names.json" +# ================================================ + + +def get_page_content(title): + """从中文 Wiki 获取页面完整内容""" + params = { + "action": "query", + "prop": "revisions", + "titles": title, + "rvprop": "content", + "rvslots": "main", + "format": "json" + } + try: + r = SESSION_CN.get(WIKI_API_URL_CN, params=params) + r.raise_for_status() + data = r.json() + pages = data["query"]["pages"] + page = next(iter(pages.values())) + if "revisions" in page: + return page["revisions"][0]["slots"]["main"]["*"] + except Exception as e: + print(f" 获取页面 '{title}' 时出错: {e}") + return None + + +def extract_runeword_names(wikitext): + """从 wikitext 中提取所有三级标题作为符文之语名称""" + if not wikitext: + return [] + return re.findall(r'===\s*(.+?)\s*===', wikitext) + + +def split_cn_en(full_name): + """ + 分割 "中文名 英文名" 格式的标题。 + 英文名可能包含多个单词(如 "Flickering Flame", "King's Grace"), + 所以需要从尾部向前匹配连续的英文单词序列。 + """ + match = re.search(r'((?:[A-Za-z][A-Za-z\']*(?:\'[A-Za-z]+)*)(?:\s+[A-Za-z][A-Za-z\']*(?:\'[A-Za-z]+)*)*)\s*$', full_name) + if match: + en_name = match.group(1) + cn_part = full_name[:match.start()].strip() + if cn_part: + return cn_part, en_name + # 纯英文或无法分割 + return None, full_name + + +def build_lookup(names): + """ + 构建查找表:英文原名 -> 完整中英文名称 + 标题格式为 "中文名 英文名",英文名可含多个单词 + """ + lookup = {} + for full_name in names: + cn_name, en_name = split_cn_en(full_name) + lookup[en_name] = full_name + return lookup + + +def main(): + REFERENCES_DIR.mkdir(exist_ok=True) + + all_lookup = {} + page_stats = {} + + for page_title in RUNEWORD_PAGES: + print(f"拉取: {page_title} ...", end=" ") + wikitext = get_page_content(page_title) + names = extract_runeword_names(wikitext) + lookup = build_lookup(names) + all_lookup.update(lookup) + page_stats[page_title] = len(names) + print(f"找到 {len(names)} 个符文之语") + + # 保存 JSON + with open(OUTPUT_FILE, "w", encoding="utf-8") as f: + json.dump(all_lookup, f, ensure_ascii=False, indent=2) + + total = len(all_lookup) + print(f"\n总计 {total} 个符文之语名称已保存到 {OUTPUT_FILE}") + for page, count in page_stats.items(): + print(f" {page}: {count}") + + +if __name__ == "__main__": + main()