docs(huoshan): 补充 Stage6 本地化实施计划

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-26 23:45:02 +08:00
co-authored by Cursor
parent 95a1a86b71
commit 45d71a2d40
@@ -0,0 +1,412 @@
# 火山 Stage6 本地化完整实施计划
## 1. 目标与边界
Stage6 继续使用项目现有的两套本地化入口:
- 固定 UIUnity Localization 的现有 `UIText` String Table Collection。
- Stage6 玩法及演出内容:新增独立的 `HuoshanStage6` String Table Collection。
- Yarn 普通对白:继续沿用项目 Yarn Spinner 本地化体系,但本轮不启用 `FP_Huoshan1` 的整章对白翻译。
- `params.csv` 继续只用于 `l10n.*` 通用短语替换,不承载 Stage6 玩法文本。
本轮纳入:
- 表达小游戏目标、输出、整合率、干扰数量等固定 UI。
- 三轮语言粒子的目标句和干扰 token。
- LOG3 释放演出中的攻击词、稳定词、失稳词等动态文字。
- 相关程序接口、Yarn 命令协议和使用文档。
- 中、英、日三种语言数据;英日文首版标记为待母语审校。
本轮明确暂缓:
- 普通角色对白。
- Task 面板中的“释放表达模块的阻塞 log”和 LOG1/2/3 任务提示。
- “左键排斥、右键吸引”等 Yarn 操作教学。
- Yarn line tag 和 Yarn 本地化 CSV。
- 调试日志、Inspector Tooltip、对象名、节点名等开发文本。
因此,本轮完成后核心小游戏及释放演出可以多语言运行,但任务面板与操作教学仍会显示中文,等待之后随对白本地化统一处理。
## 2. 本地化数据结构
### 2.1 固定 UI`UIText`
继续使用现有键,不新建重复条目:
| Key | 用途 | 中文示例 | 英文草稿 | 日文草稿 |
|---|---|---|---|---|
| `huoshan_expression_objective` | 小游戏目标 | 目标:分离红色干扰,连接蓝色真心 | OBJECTIVE: ISOLATE RED INTERFERENCE. CONNECT THE BLUE TRUTH. | 目標:赤い干渉を分離し、青い本音をつなげる |
| `huoshan_expression_output` | 输出按钮 | 输出 | OUTPUT | 出力 |
| `huoshan_expression_integration` | 整合率 | 语言整合率:{0}% | LANGUAGE INTEGRATION: {0}% | 言語統合率:{0}% |
| `huoshan_expression_interference` | 干扰数量 | 干扰思绪:{0} | INTERFERENCE SIGNALS: {0} | 干渉思考:{0} |
要求:
- 保留 `{0}` 参数,程序继续通过格式化参数填入数值。
- 英文和日文目前的中文占位必须替换。
- 固定 UI 继续使用现有 `LocalizeStringEvent``StringDatabase` 绑定。
- 切换 Locale 时立即刷新这些固定 UI。
### 2.2 玩法内容:`HuoshanStage6`
新增 String Table Collection
```text
HuoshanStage6
├─ zh-Hans
├─ en
└─ ja-JP
```
键名只表达语义,不包含语言、行号或 Yarn 节点名。
#### 语言粒子数据
```text
round.log1.target
round.log1.interference_tokens
round.log2.target
round.log2.interference_tokens
round.log3.target
round.log3.interference_tokens
```
中文源数据:
| Key | 中文值 |
|---|---|
| `round.log1.target` | 我就是个笑话 |
| `round.log1.interference_tokens` | 哈哈\|嘿嘿\|呵呵 |
| `round.log2.target` | 不要离开我 |
| `round.log2.interference_tokens` | 哈哈哈\|嘿嘿嘿\|呵呵呵\|笑死了\|好好笑 |
| `round.log3.target` | 不要那样看我 |
| `round.log3.interference_tokens` | 哈哈哈哈\|笑死我了\|太好笑了\|绷不住了\|笑不活了\|真的太搞笑 |
`interference_tokens` 的表值使用 `|` 分隔。解析规则:
-`|` 拆分。
- 对每项执行 Trim。
- 丢弃空项。
- 至少保留一个有效 token,否则视为数据错误。
- token 由本地化人员针对语言重新创作,不要求与中文逐字对应。
- 英文示例:`HA|HAHA|LOL|JK`
- 日文示例:`ハハ|笑|冗談|ウケる`
#### LOG3 释放演出数据
按语义建立以下键;重复演出复用同一个键:
```text
release.attack.why
release.attack.dont_believe_me
release.attack.i_know_im_last
release.attack.bad_weather
release.attack.cannot_sell
release.attack.try_to_explain
release.attack.refuse_to_believe
release.attack.a_little_trust
release.attack.had_enough
release.attack.damn
release.attack.useless
release.attack.prove_myself
release.attack.cannot_do_anything
release.attack.catch_up
release.attack.help_yingli
release.attack.no_courage
release.attack.say_it
release.attack.trash
release.attack.hopeless
release.attack.why_loop
release.settle.want_to_win_once
release.destabilize.dont_look_at_me
```
映射规则:
| 中文文本 | Key |
|---|---|
| 为什么 | `release.attack.why` |
| 不相信我 | `release.attack.dont_believe_me` |
| 我明白我垫底 | `release.attack.i_know_im_last` |
| 天气真的不行 | `release.attack.bad_weather` |
| 卖不出去 | `release.attack.cannot_sell` |
| 努力解释 | `release.attack.try_to_explain` |
| 不愿意相信我 | `release.attack.refuse_to_believe` |
| 一点信任 | `release.attack.a_little_trust` |
| 受够了 | `release.attack.had_enough` |
| 真该死 | `release.attack.damn` |
| 真没用 | `release.attack.useless` |
| 证明自己 | `release.attack.prove_myself` |
| 什么都做不到 | `release.attack.cannot_do_anything` |
| 追上你 | `release.attack.catch_up` |
| 帮到英理 | `release.attack.help_yingli` |
| 没有勇气 | `release.attack.no_courage` |
| 说出来 | `release.attack.say_it` |
| 废物 | `release.attack.trash` |
| 烂泥扶不上墙 | `release.attack.hopeless` |
| 为什么为什么 | `release.attack.why_loop` |
| 我想赢一次 | `release.settle.want_to_win_once` |
| 不要那样看我 | `release.destabilize.dont_look_at_me` |
Yarn 中重复出现的“受够了”“废物”“为什么为什么”继续保持原来的重复次数和演出参数,只复用同一条本地化键,不能因去重而改变演出节奏。
未来如果 Stage6 使用 `expression_system_message` 或其他会显示字符串的释放命令,也必须新增对应的 `release.*` 键,不能直接写玩家可见文本。
### 2.3 表格元数据
英日文第一版可以直接投入开发测试,但每个非中文条目增加统一备注:
```text
DRAFT / NEEDS NATIVE REVIEW
Context: HuoShan Stage6 expression minigame
```
释放演出条目应额外注明:
- 显示形式,例如瞬间闪现、连续攻击或最终稳定文字。
- 建议长度。
- 是否允许创译。
- 重复次数由 Yarn 控制,本地化值本身不要手工重复。
## 3. 程序接口和数据流
### 3.1 本地化引用协议
Yarn 命令参数支持两种输入:
```text
@h6.<entry-key> 从 HuoshanStage6 String Table 解析
普通字符串 按原始字符串使用,保持旧内容兼容
```
示例:
```yarn
<<start_expression
"@h6.round.log1.interference_tokens"
"@h6.round.log1.target"
"LOG1梳理完成"
16>>
<<expression_truth_attack "@h6.release.attack.why" 0.30 0.32>>
<<expression_truth_settle "@h6.release.settle.want_to_win_once" 0.55>>
<<expression_truth_destabilize
"@h6.release.destabilize.dont_look_at_me"
1.6
0.05
22
"FF304D">>
```
其中:
- 前两个 `start_expression` 参数是本地化内容。
- 完成节点 `LOG1梳理完成` 是 Yarn 流程标识,不做本地化。
- 粒子数量、持续时间、颜色、Timeline、memory preset 等均为结构参数,不做本地化。
### 3.2 建议公共数据结构
```csharp
public readonly struct Stage6TextReference
{
public string RawValue { get; }
public bool IsLocalized { get; }
public string EntryKey { get; }
}
public sealed class Stage6RoundText
{
public string TargetText { get; init; }
public IReadOnlyList<string> InterferenceTokens { get; init; }
}
public sealed class Stage6LocalizationContext
{
public const string TableName = "HuoshanStage6";
public const string ReferencePrefix = "@h6.";
public Locale RoundLocale { get; private set; }
public IEnumerator BeginRound(Locale locale);
public IEnumerator Resolve(
string textReference,
Action<string> completed);
public IEnumerator Resolve(
string textReference,
Locale locale,
Action<string> completed);
public IEnumerator LoadRound(
string roundId,
Locale locale,
Action<Stage6RoundText> completed);
}
```
职责划分:
- `Stage6TextReference`:判断参数是本地化引用还是兼容用原始字符串。
- `Stage6LocalizationContext`:统一访问 `HuoshanStage6`,避免每个 Yarn 命令各写一套解析逻辑。
- `Stage6RoundText`:向粒子系统提供已经解析完成的数据;粒子系统不直接访问 String Table。
- `LanguageYarnCommand`:在启动小游戏前等待本地化解析完成,再把纯文本数据交给 `LanguageParticleManager`
- Log 释放相关 Yarn Command:显示前通过同一 resolver 解析字符串,再交给 `LogReleasePresentationController`
- `LanguageParticleManager``LogReleasePresentationController` 最终只接收已解析的文本,不关心键、表名或 Locale。
### 3.3 解析和错误处理
解析规则必须集中实现:
1. 参数不以 `@h6.` 开头时,直接返回原字符串。
2. 参数以 `@h6.` 开头时,取后半部分作为 Entry Key。
3. Entry Key 为空时记录错误,并显示可识别的错误占位。
4. 优先获取指定 Locale 的条目。
5. 当前语言缺失时回退到 `zh-Hans` 的同名条目。
6. 中文源条目也缺失时:
- 输出明确错误日志,包含 Table、Key、Locale 和调用命令。
- 玩家画面显示 `⟦缺失的键名⟧`
- 不得直接显示 `@h6.xxx`,也不能静默返回空字符串。
7. `interference_tokens` 解析后为空时终止该次 `start_expression`,输出数据错误,避免粒子生成阶段产生难以定位的异常。
### 3.4 Locale 切换行为
固定 UI
- `objective``output``integration``interference` 监听 `SelectedLocaleChanged`
- 切换语言后立即刷新。
- 当前数值必须保留,只替换格式模板。
语言粒子:
- 每次 `start_expression` 开始时捕获当前 Locale。
- 同一轮的目标句、目标粒子和干扰 token 全部使用该快照。
- 轮次进行中切换 Locale,不重建、不替换现有粒子。
- 下一次 `start_expression` 使用新的 Locale。
- 异步解析结束前不得开始生成粒子。
LOG 释放命令:
- 每条命令执行时解析其文字参数。
- 如果该命令属于仍在进行的表达轮次,沿用该轮的 Locale 快照。
- 没有活动轮次时使用命令执行时的当前 Locale。
- 单次命令解析完成后再启动对应演出,避免先显示 key、后替换文字。
### 3.5 Unicode 与粒子拆分
目标句不能按 UTF-16 `char``string.Length` 拆分,必须使用 Unicode 文本元素/字素簇。
规则:
- 中文汉字、日文假名、拉丁字母、组合音标均按完整文本元素处理。
- 英文空格不生成粒子,但在成功连接后的排版中形成单词间距。
- 标点是否生成粒子沿用当前玩法规则,但不能被拆成代理项。
- 有效目标粒子数量、完成判断和候选粒子数量都基于同一套文本元素结果。
- 干扰 token 是本地化短词;生成粒子时再按同样的文本元素规则取字符。
- 不使用英文 `string.Length` 直接决定粒子数量。
## 4. 具体改动安排
### 4.1 Unity Localization 资源
- 补齐 `UIText` 中四个现有火山 UI 键的英日文。
- 新建 `HuoshanStage6` Collection 及中、英、日三张表。
- 将 Collection 按项目现有 Localization Addressables 方式纳入构建。
- 配置英日文字体或 TMP fallback,覆盖拉丁字符、日文假名、常用标点。
### 4.2 语言粒子系统
- `LanguageYarnCommand` 接入统一的 `@h6.` resolver。
- `start_expression` 在调用 `LanguageParticleManager` 前解析目标句和 token。
- `LanguageParticleManager` 接收 `Stage6RoundText` 或等价的纯数据参数。
- 保留现有原始字符串调用方式,避免其他测试场景和旧 Yarn 立即失效。
- 将固定 UI 的中文常量仅作为开发兜底,不作为正式语言数据来源。
### 4.3 LOG 释放演出
- 所有接受玩家可见字符串的释放命令接入相同 resolver。
- 将 Stage6 中的攻击、settle、destabilize 参数改为 `@h6.` 引用。
- 不修改数值演出参数、调用顺序和重复次数。
- `LogReleasePresentationController` 保持表现层职责,只接收最终显示文本。
### 4.4 Yarn 内容
- 仅替换本地化范围内的 Yarn 命令参数。
- 不添加或修改 `#line:` 标签。
- 不配置 `FP_Huoshan1.yarnproject` 的整章 localization。
- 不生成 Stage6 Yarn 翻译 CSV。
- 被注释的示例命令不需要建立正式本地化条目;若未来启用,必须先补键。
- Task 行、鼠标教学和普通对白保持原样。
### 4.5 Prefab、图片与文档
- `ExpressManager.prefab` 当前没有确认的运行时玩家文本,也不是 Stage6 正式文本数据源,本轮不向其中写多语言文案。
- `ExpressionScreen_NoText.png` 已确认无烘焙文字,不制作多语言 Sprite。
- 场景或 Prefab 中诸如“输出”的序列化 TMP 文本只作为编辑器预览;运行时必须由 `UIText` 覆盖。
- 更新 `LogReleaseYarnCommands.md`
- 说明 `@h6.` 引用格式。
- 列出哪些参数可本地化、哪些是结构参数。
- 提供 attack、settle、destabilize 示例。
- 记录回退与错误显示规则。
- 同步更新语言系统说明文档中的 `start_expression` 示例,展示本地化键和旧原始字符串两种写法。
## 5. 测试与验收
### 5.1 数据完整性测试
- `HuoshanStage6` 的中、英、日表拥有相同键集合。
- 中文源条目不得为空。
- 英日文不得残留中文占位;允许通过备注标记待审校。
- 所有 `interference_tokens` 至少包含一个有效 token。
- 所有 Stage6 中实际执行的本地化命令参数都能解析。
- 结构参数没有被误当成本地化键。
- 扫描 Stage6 后,剩余硬编码玩家文本必须全部属于已声明的暂缓范围。
### 5.2 编辑模式测试
- `@h6.key` 能解析指定 Locale。
- 普通字符串保持原值。
- 缺少目标语言时回退中文。
- 三种语言都缺失时显示 `⟦key⟧` 并记录错误。
- token 能正确 Trim 和过滤空项。
- Unicode 文本元素拆分能处理中文、英文、日文和组合字符。
- 英文空格不生成粒子,但结果排版保留词间距。
- 重复攻击词引用同一 Key 时仍能按 Yarn 调用次数重复播放。
### 5.3 PlayMode/场景验收
分别使用 `zh-Hans``en``ja-JP` 运行 LOG1、LOG2、LOG3
- 固定 UI 显示正确,格式参数正确。
- 每轮目标句和干扰 token 使用对应语言。
- 粒子连接、排斥、吸引和完成判断不受文本长度影响。
- LOG3 attack、settle、destabilize 全部显示对应语言。
- 演出调用顺序、节奏、颜色和持续时间与改造前一致。
- 中途切换语言时,固定 UI 立即更新,当前轮粒子不变,下一轮使用新语言。
- 字体无缺字方框。
- 英文长文本和日文文本没有溢出、异常换行或关键画面遮挡。
### 5.4 回归验收
- 使用旧的原始字符串调用 `start_expression` 仍然可以运行。
- `languageTest` 等测试内容不因新增 resolver 失效。
- Yarn 编译无错误。
- Stage6 的完成节点跳转保持不变。
- Task 完成逻辑和 Yarn line ID 不受影响。
- Addressables/Localization 构建包含新增表格。
- 在 Unity Editor 中完成最终视觉检查;当前无法通过 Unity MCP 自动进行场景级检查。
## 6. 交付判定
满足以下条件后视为本轮完成:
- Stage6 固定小游戏 UI 已具备中英日数据。
- 三轮粒子内容和全部正式 LOG 释放文本均从 `HuoshanStage6` 获取。
- Yarn 中相关硬编码文本已替换为 `@h6.` 引用。
- 原始字符串调用保持兼容。
- Locale 快照、回退、缺键和 Unicode 拆分规则均有测试覆盖。
- 文档足以让后续策划直接新增本地化键并在 Yarn 命令中使用。
- 任务提示、操作教学和普通对白仍明确列为后续 Yarn 本地化工作,不被误报为已完成。