Communitygithub.com

DrunkMan9o/agent-skills

将 Unity(Mono)游戏或任意 .NET 程序的界面文本汉化为简体中文,并在写入前使用自审计引擎检查完整性、一致性、格式与结构风险。适用于文本硬编码在 Assembly-CSharp.dll 的 #US 字符串堆、没有本地化表、且内嵌动态字体的项目。用于将 Unity/.NET 游戏本地化为中文时,从 #US 堆提取硬编码的 UI 字符串、修补 ldstr 令牌,或审计现有中文本地化中的遗漏/未翻译/不一致文本。

agent-skills 是什麼?

该技能提供了一套完整的四阶段流水线,用于将 Unity(Mono)游戏或任意 .NET 程序的界面文本汉化为简体中文。其核心并非翻译本身,而是在翻译前先通过风险判定规则识别哪些字符串不能翻译,以避免破坏游戏逻辑。它支持从托管程序集的 #US 堆中提取硬编码字符串,并根据代码形状(如是否用于构建对象、绘制界面或匹配比较)对字符串进行风险分级,防止将路径、引擎键、正则表达式等高风险字符串错误翻译。写入后,自审计引擎会执行 31 项检查(涵盖完整性、一致性、格式、风险、结构、运行时异常),并输出结构化报告和修复清单,确保汉化质量。该技能支持在 Claude Code、Cursor、Codex 等 AI 代理平台上使用,通过命令行脚本 `zhl10n.py` 驱动,依赖 `dnfile`、`dncil`、`UnityPy`、`fonttools` 等库。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/DrunkMan9o/agent-skills/tree/HEAD/skills/unity-zh-l10n

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

Unity 汉化流水线(含自审计)

把「提取 → 风险判定 → 翻译 → 写入 → 自审计」做成可复用的四阶段流水线。 核心不是翻译本身,而是先判断哪些串不能翻——这是唯一会把游戏改崩的地方。

什么时候用它

用之前先确认三条,任一条不成立就不用往下走:

  1. 文本在哪:Mono/IL2CPP 的 Unity 游戏,文本硬编码在托管程序集(*_Data/Managed/Assembly-CSharp.dll)或 resources.assets 里,没有 Localization 表、.po、.csv 之类的本地化资源。 若有现成本地化表,直接用表,别改程序集。
  2. 能拿到原始文件:改的是可执行文件,必须有一份未被修改过的原件做基线(备份目录、Steam 校验后重下的都行)。没有基线就不要做原地修改。
  3. 中文能显示:项目内嵌的是动态字体(TMP 或 Legacy Font)。静态字体/图集字体的中文需要换贴图,本流水线只处理动态字体。

IL2CPP(无 Assembly-CSharp.dll,只有 GameAssembly.dll)不在支持范围,需要 Il2CppDumper + 重编译,另说。

快速开始

# 0) 建项目骨架(生成 l10n.json)
python <skill>/scripts/zhl10n.py init -C 你的项目目录

# 1) 清点全部 ldstr 站点(约 10s / 5000 条)
python <skill>/scripts/zhl10n.py extract

# 2) 看风险分级:哪些能翻、哪些只能定向翻、哪些绝对不能翻
python <skill>/scripts/zhl10n.py risk

# 3) 填 trans_map.json / site_map.json,然后写入(不改原件,输出到 out/build/)
python <skill>/scripts/zhl10n.py apply

# 4) 自审计(默认读 out/build 产物)
python <skill>/scripts/zhl10n.py audit --fail-on P1

一条龙:zhl10n.py all(extract → apply → audit)。

装依赖:pip install dnfile dncil UnityPy fonttools。只跑 audit 不装 UnityPy 也能用。

各环节职责

阶段入口输入输出责任边界
initzhl10n.py init目标游戏路径l10n.json、空表只生成配置,不碰游戏文件
extractextract.py原始 DLL(+ 可选 assets)out/extract/sites.jsonsites.json 每项含 str/type/method/token/offset只读。必须读未打补丁的原件,否则会提取到已汉化的串
riskrisk.pysites.json控制台分级统计只判定,不修改任何表
applyapply.py原始 DLL + 翻译表 + 字体子集out/build/Assembly-CSharp.dllout/build/resources.assets写新文件,原件不动(--inplace 才原地改,会先留 .orig)。任何命中 block 的译文拒绝写入并报错
auditaudit.py产物 + 翻译表 + 术语表 + 日志out/audit/audit.{json,md,html}fixlist.csv只读。可对任意版本的文件跑,用于新旧对比

三层翻译表,职责各不相同:

  • trans_map.json —— 全局表,按字符串替换所有出现位置。只放「翻成中文不会破坏逻辑」的串。
  • site_map.json —— 定向表,键是 类型::方法,只改该处的 ldstr。给「同一个词在菜单里该翻、在输入系统里不能翻」这种情况用。
  • enum_map.json —— 枚举名表,改的是 #Strings 堆里的字段名。给 ToString() 出来的枚举显示名用。

判定顺序:定向 → 全局。定向命中就不再走全局。

自审计

自审计是这套流水线的重点。它在写入之后独立读产物,不信任翻译表自己的说法。

输出

文件内容
audit.json完整结构化结果:每条 finding 含 check/severity/count/items[]/hint
audit.md人读摘要,按 P0→P2 排序
audit.html可折叠的单文件报告
fixlist.csv需修复项清单,一行一条,可直接当待办表用

检查项(31 项,6 族)

CV 完整性——有没有漏

编号级别检查判定依据
CV1P1未翻译文本站点可显示 + 两种表都无译文 + 未被风险规则放行
CV1bP2形态像界面文本但被拦下说明是引擎约束,不是漏译,需定向翻译并实测
CV2P1空译 / 占位译译文为空、含 TODO/待译
CV3P1长句译文与原文完全一致疑似根本没翻
CV4P2短词/符号译文与原文一致图标字、按键名保留原文通常正确
CV5P2定向表死条目声明了但原版 IL 里找不到,方法改名或已被全局表覆盖

CS 一致性——术语对不对得上

编号级别检查
CS1P2同一原文在全局表与定向表译法不同
CS2P1术语表强制译法未遵守
CS3P1译文命中术语表禁用词
CS4P2重复键(仅大小写/空白不同)
CS5P2中文标点风格偏半角

FM 格式——拼接会不会坏

编号级别检查后果
FM1P1占位符不守恒String.Format 抛异常或数字消失
FM1bP2移除英文复数占位符正确做法,仅提示确认没误删真参数
FM2P2换行数量不一致排版错位
FM3P1单位/拼接片段丢失首尾空格1000coins 粘连
FM4P2其它首尾空格变化中文之间通常无所谓
FM5/FM6P2半角标点 / 连续空白观感
FM7P2译文长度膨胀过大多为把说明写进了标签

RK 风险——会不会把游戏改崩

编号级别检查
RK1P0高风险字符串被纳入翻译表(路径、引擎键、正则、输入动作名…)
RK2P2全局表内含单标识符,建议改定向
RK3P1翻译表 key 含中文,二次应用会错乱
RK4P2译文残留英文片段

IG 结构——文件本身是否还合法

编号级别检查
IG0/IG1/IG2/IG4P0目标文件缺失、PE/元数据异常、#US 无法解析、ldstr token 非法、元数据行数与原版不一致
IG3P2补丁后仍为英文的站点(预期内的白名单,用于核对)
IG5/IG6P1字体缺字 / 字体文件无法解析

RT 运行——游戏自己招了没

编号级别检查
RT1P1Player.log 里出现异常堆栈(需要 source.logs 配置)

触发方式

三种,按场景选:

# 1. 写完立刻自检(开发循环里最常用)
zhl10n.py audit

# 2. 当门禁用:CI 或发布前,不达标直接非零退出
zhl10n.py audit --fail-on P0      # 只要有阻断级就失败
zhl10n.py audit --fail-on P1      # 默认严格度,发布建议用这个
zhl10n.py audit --fail-on P2      # 连提示都要清

# 3. 回归对比:这次改完比上次多了哪些问题
zhl10n.py audit --baseline

apply 结束时也会自动跑一次 audit,把结论打印出来——写入阶段不会在不知情的情况下产出坏包。

怎么用审计结果

  1. 先看 P0。任何一条 P0 都不许发布,它等于「游戏可能起不来」。
  2. 再看 P1。CV1 是漏译清单,FM1 是必崩清单,RT1 是已经崩了的证据。
  3. P2 当核对表:CV1b 告诉你哪些是引擎硬约束而不是你漏了,CV5/CS4 是可以直接删的垃圾条目。
  4. fixlist.csv 直接对着一行行改,改完重跑,直到 P0=0 且 P1=0。

配置(l10n.json)

{
  "project": "游戏名", "version": "1.0",
  "source": {
    "dll":     "…/Managed/Assembly-CSharp.dll",   // 只用于定位,extract 会读 backup
    "backup":  "…/_Backup/Assembly-CSharp.dll",   // 必须是未修改的原件
    "assets":  ["…/resources.assets"],
    "logs":    ["…/Player.log"]                    // 可选,供 RT1
  },
  "output": { "dir": "out" },
  "text":   { "map": "trans_map.json", "site_map": "site_map.json", "enum_map": "enum_map.json" },
  "glossary": "glossary.csv",
  "font":   { "subset": "font/zh-subset.ttf", "source": "font/NotoSansSC-Bold.otf", "targets": {} },
  "audit":  { "severity_fail": "P1", "max_expansion_ratio": 2.5, "max_expansion_abs": 40 },
  "risk":   { "extra_patterns": [], "extra_allow": [] }
}

两个逃生口,遇到报错先想它们:

  • risk.extra_patterns —— 项目里已知不能翻的串(正则)。例:^Levels$
  • risk.extra_allow —— 已确认不必翻的具体串。加进去就不算漏译

术语表 glossary.csv:en,zh,forbidden 三列,forbidden 用 | 分隔同义词。CS2/CS3 依赖它,没有则跳过这两项。

判定规则:哪些串绝对不能翻

一句话版:在代码里当「键」用的串,翻了就找不到东西。

类别例子翻了会怎样
资源路径"Levels"、"UI/Fonts/X"关卡表空 → 存档恢复 NRE → 卡加载界面
输入动作/字段名Move/Up/Button键位绑定失效
存档字段legacySkillPointCredit存档读不出来
枚举名Deposit(ToString() 之外还有 Enum.Parse)解析失败
正则模式([0-9.]+)% more coins提示文本改写失效,退回英文
对象/资源名new GameObject("Display glass")通常不崩,但会污染资源名
格式串{0:N0} / {1:N0}String.Format 异常

同形不同命:Pause 在设置菜单是按钮文字(该翻),在 GameInput 里是动作名(不能翻)。 靠文本形态永远分不出来,只能靠「出现在哪个类型、哪个方法」。这是 risk.py 的 _code_shape() 在做的事:

  • 建对象的方法(.ctor/Build/Initialize/Create/Ensure…)里的字面量 → 名字,不翻
  • 画界面的方法(Draw/OnGUI/Format/get_…)里 → 文案,翻
  • 比对型方法(Is/Has/Matches/Find…)里 → 拿去匹配的键,不翻

加了新规则后必须用负向测试验证:往翻译表里故意塞一条 "Levels": "等级",确认审计报 P0。

字体的坑

内嵌动态字体只能等长原位替换。整体重写 resources.assets 会改变 Texture2D 的 m_StreamData 偏移,贴图全错位。

流程:font 阶段按「实际用到的汉字集合」子集化,转成 TTF(Unity 对 CFF/OTF 支持不稳),再填进 m_FontData。字形数不够就减字号或压字重,不能超原尺寸。

Bangers-Regular 这类只有几十 KB 的字体放不下中文——把字体路径重定向到另一个中文字体(在 l10n.json 的 font.targets 里配),比硬塞可靠。

交付前清单

[ ] audit --fail-on P1 通过
[ ] 实际启动游戏,逐屏走一遍:主菜单 / 设置 / 存档列表 / 游戏内 HUD / 弹窗 / 报错
[ ] Player.log 无新异常(RT1 为 0)
[ ] 备份原件位置写进发布说明(还原就靠它)
[ ] 记录产物 MD5,写进版本说明

改坏了的定位方法:先只换回原 DLL、保留其他,若正常则问题一定在程序集补丁;再只换 DLL、保留其他,若复现则确认。二分比读代码快。

参考文档

  • references/flow.md —— 每个阶段的详细实现与数据结构
  • references/audit-spec.md —— 31 项检查的完整定义、级别、修复指引
  • references/risk-rules.md —— 风险规则表与它背后的真实事故
  • assets/l10n.example.json / assets/glossary.example.csv —— 配置模板

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

相關技能