diff --git a/Docs/HuoShan/HuoshanStage6LocalizationPlan.md b/Docs/HuoShan/HuoshanStage6LocalizationPlan.md new file mode 100644 index 000000000..ba0098b19 --- /dev/null +++ b/Docs/HuoShan/HuoshanStage6LocalizationPlan.md @@ -0,0 +1,412 @@ +# 火山 Stage6 本地化完整实施计划 + +## 1. 目标与边界 + +Stage6 继续使用项目现有的两套本地化入口: + +- 固定 UI:Unity 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. 从 HuoshanStage6 String Table 解析 +普通字符串 按原始字符串使用,保持旧内容兼容 +``` + +示例: + +```yarn +<> + +<> +<> +<> +``` + +其中: + +- 前两个 `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 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 completed); + + public IEnumerator Resolve( + string textReference, + Locale locale, + Action completed); + + public IEnumerator LoadRound( + string roundId, + Locale locale, + Action 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 本地化工作,不被误报为已完成。