Files
Chen Yi 16b476167e 专名本土化(信达雅):冻结译名表 + 渲染层,并修掉校验器的正则盲点
- 新增 dfannals/glossary.py(译名表)与 dfannals/localize.py(渲染层)
  人名=音译+绰号意译(Ral Fastenhatchets→拉尔·缚斧、朗古德·商熊「剖胎之寒」),
  地名整体意译(Fordedwinds→涉风渡、The Volcano Of Ravens→渡鸦火山)
- 生成链路本土化:素材渲染、人物表、扩写白名单全部走译名;已有产物做确定性替换
- 修复 factcheck 的 \b 盲点:Unicode 下汉字也算词字符,紧贴汉字的英文名不被检查
  (曾因此漏掉编造专名)。修好后抓出并定点修掉长版第 1 集的 2 处编造
- 修正 title_form 大小写(YETI→Yeti),此前 Yeti/Goblin 一直漏译
- 新增 tests/test_localize.py(13 项回归)
2026-10-05 22:39:53 +08:00

209 lines
6.4 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""专名译名表(冻结)。
为什么要有这张表:正文里满屏 `Ral Fastenhatchets`、`Fordedwinds` 对中文读者很难读。
但如果交给模型每次现译,同一个角色会在第 3 集变成另一个名字,所以译名必须冻结在这里。
翻译策略(信达雅):
1. **人名 = 名字音译 + 姓氏/绰号意译**,用「·」连接。
这与《魔戒》把 Thorin Oakenshield 译作「索林·橡木盾」是同一套惯例,
读起来像中国人名,又保住了原名的意思。
2. **地名整体意译**。DF 地名同样是词表拼接(Fordedwinds = forded + winds),
直译成「涉风渡」比音译成「福德温兹」更像地名。
3. **称号(the X of Y)单独译**,DF 给传奇人物的长称号自带画面感,直译最好。
DF 的名字由词表拼接而成,所以同一批词根会在不同世界反复出现:
这张表按「整词」而非「词根」收词,避免把 Fateddie 硬拆成"命定+死"这类生硬堆叠。
遇到表里没有的名字,localize 会原样保留并登记进待译清单,而不是硬造一个。
"""
# ------------------------------------------------------------------ 名字词干(音译)
STEMS: dict[str, str] = {
"alath": "阿拉斯",
"amxu": "阿姆苏",
"ana": "安娜",
"artuk": "阿图克",
"asmel": "阿斯梅尔",
"astri": "阿斯特里",
"domas": "多马斯",
"dostngosp": "多斯特恩戈斯普",
"ducim": "杜西姆",
"espu": "埃斯普",
"evala": "埃瓦拉",
"guspu": "古斯普",
"id": "伊德",
"ineth": "伊内斯",
"ingish": "英吉什",
"langgud": "朗古德",
"lolor": "洛洛尔",
"meng": "蒙",
"moldath": "莫尔达斯",
"momuz": "莫穆兹",
"mosus": "莫苏斯",
"mudi": "穆迪",
"ngokang": "恩戈康",
"ngom": "恩戈姆",
"niri": "尼里",
"nithe": "尼瑟",
"ongu": "翁古",
"onul": "奥努尔",
"pari": "帕里",
"perom": "佩罗姆",
"ral": "拉尔",
"rigoth": "里戈斯",
"sathrel": "萨瑟雷尔",
"sayrsnis": "赛尔斯尼斯",
"shosa": "绍萨",
"sina": "西娜",
"someg": "索梅格",
"song": "松",
"stinthad": "斯廷哈德",
"tekkud": "泰库德",
"thrun": "斯伦",
"tirist": "提里斯特",
"tulon": "图隆",
"uknol": "乌克诺尔",
"usel": "乌塞尔",
"utes": "乌特斯",
"zuntir": "尊提尔",
"zutthan": "祖坦",
}
# ------------------------------------------------------------------ 姓氏/绰号(意译)
# 键是姓名后半段的拼接结果(小写、无空格)
SURNAMES: dict[str, str] = {
"baldedattic": "秃阁",
"basementrampart": "地垒",
"blazedbrushes": "焰刷",
"blottedmine": "污矿",
"bonestands": "骨立",
"bronzescorched": "铜焦",
"churchbrass": "堂铜",
"clappedbrass": "拍铜",
"crazeowned": "狂据",
"decentpage": "端页",
"dyesong": "染歌",
"fastenhatchets": "缚斧",
"fateddie": "命终",
"fencefinders": "篱寻",
"fisherfair": "渔市",
"flayerline": "剥血",
"frillyknots": "褶结",
"glimmeredhelps": "光援",
"glowroofs": "光顶",
"gleamcircled": "光环",
"growllie": "吼卧",
"hammershake": "锤摇",
"huggedfastened": "抱固",
"incenselyrics": "香诗",
"jackalbrims": "豺帽",
"matchedmenaces": "匹凶",
"menacedslinks": "危潜",
"mirroredfloors": "镜层",
"oilshove": "油推",
"palmsaved": "掌救",
"playfills": "戏填",
"ruinedhorrors": "毁怖",
"searcheddated": "寻期",
"squirtlocks": "喷锁",
"steelgrouped": "钢聚",
"stilledearthen": "静土",
"stolendipped": "窃浸",
"strikepaints": "击彩",
"talonhollows": "爪穴",
"theatercrested": "剧冠",
"thornticks": "棘蜱",
"tongswall": "钳墙",
"tradebears": "商熊",
"walkclasped": "行扣",
"watchedleads": "守铅",
"wheeleddrinks": "轮饮",
"wisdomskins": "智皮",
"yellplank": "啸板",
}
# ------------------------------------------------------------------ 地名(整体意译)
PLACES: dict[str, str] = {
"clampokes": "钳戳",
"fordedwinds": "涉风渡",
"fuchsiarumor": "紫谣",
"gloryholes": "荣耀穴",
"lashheroes": "鞭雄",
"leapdrinks": "跃饮",
"liecats": "卧猫",
"mortalpears": "殇梨",
"paperhushed": "纸寂",
"rainseduced": "雨诱",
"tomessoldiers": "典兵",
"toolfainted": "器昏",
"walkedbirds": "行鸟",
}
# ------------------------------------------------------------------ 称号(the X of Y)
EPITHETS: dict[str, str] = {
"the eviscerated chill of pregnancies": "剖胎之寒",
"the trickery of mornings": "晨之诡计",
}
# ------------------------------------------------------------------ 势力/组织
GROUPS: dict[str, str] = {
"the volcano of ravens": "渡鸦火山",
}
# ------------------------------------------------------------------ 种族
RACES: dict[str, str] = {
"AARDVARK_MAN": "土豚人",
"ALLIGATOR": "鳄鱼",
"ALLIGATOR_MAN": "鳄人",
"BAT_GIANT": "巨蝠",
"BEAR_BLACK": "黑熊",
"BEAR_GRIZZLY": "灰熊",
"BEAR_GRIZZLY_MAN": "灰熊人",
"BEAR_POLAR": "白熊",
"BIRD_OWL_GREAT_HORNED": "大雕鸮",
"BIRD_ROC": "大鹏",
"BIRD_WREN": "鹩鹪",
"BLIND_CAVE_BEAR": "盲穴熊",
"BLIND_CAVE_OGRE": "盲穴食人魔",
"BOBCAT": "山猫",
"CAMEL_1_HUMP_MAN": "单峰驼人",
"CARDINAL_MAN": "红衣主教雀人",
"CAVE_DRAGON": "穴龙",
"CHEETAH": "猎豹",
"CHINCHILLA_MAN": "绒鼠人",
"COLOSSUS_BRONZE": "青铜巨像",
"COUGAR": "美洲狮",
"CYCLOPS": "独眼巨人",
"DOG": "犬",
"DWARF": "矮人",
"EAGLE_MAN": "鹰人",
"ELEPHANT": "象",
"ELF": "精灵",
"FALCON": "隼族",
"GOAT_MOUNTAIN_MAN": "山羊山人",
"GOBLIN": "哥布林",
"HUMAN": "人类",
"HYDRA": "九头蛇",
"JAGUAR": "美洲豹",
"KOBOLD": "狗头人",
"LEOPARD": "豹",
"LION": "狮",
"TIGER": "虎",
"TROLL": "巨魔",
"WOLF": "狼",
"YETI": "雪人",
}
# ------------------------------------------------------------------ 地点类型
SITE_TYPES: dict[str, str] = {
"cave": "洞穴",
"dark fortress": "黑堡",
"dark pits": "幽坑",
"forest retreat": "林居",
"fortress": "要塞",
"hamlet": "村落",
"hillocks": "丘屯",
"tomb": "陵墓",
"town": "城镇",
}