Files
2026-07-20 10:56:52 +08:00

198 lines
4.7 KiB
Markdown
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.
# 04 本地化工作流 (Localization Workflow)
> 项目遵循 **ADR-A1**:所有玩家可见字符串必须通过 `tr("KEY")` 包裹,禁止业务代码中出现裸字符串。
---
## 1. 文件位置
```
translations/
zh_CN.po ← 简体中文(权威主档)
zh_TW.po ← 繁体中文
en.po ← 英文
ja.po ← 日文
```
---
## 2. `.po` 文件格式
每条翻译的格式为:
```po
msgid "UI_WAVE"
msgstr "第 %d 波"
```
- `msgid`:代码中 `tr("KEY")` 使用的键
- `msgstr`:该语言显示的文字
- `%d``%s``%.1f` 等格式占位符必须与代码中 `tr("KEY") % value` 完全对应
---
## 3. 添加新翻译字符串(标准流程)
### Step 1 — 在代码中使用 tr() 键
```gdscript
# ❌ 错误:裸字符串
label.text = "商店已关闭"
# ✅ 正确:通过键引用
label.text = tr("SHOP_CLOSED_MSG")
# 带参数
label.text = tr("UI_WAVE") % current_wave # → "第 3 波"
label.text = tr("UI_KILLS") % [kills, alive] # → "击杀: 7 存活: 3"
```
### Step 2 — 在全部四个 `.po` 文件中添加对应条目
**zh_CN.po**(权威档,写游戏实际中文):
```po
msgid "SHOP_CLOSED_MSG"
msgstr "商店已关闭"
```
**zh_TW.po**(繁体,改繁体字):
```po
msgid "SHOP_CLOSED_MSG"
msgstr "商店已關閉"
```
**en.po**(英文):
```po
msgid "SHOP_CLOSED_MSG"
msgstr "Shop Closed"
```
**ja.po**(日文):
```po
msgid "SHOP_CLOSED_MSG"
msgstr "ショップが閉店しました"
```
### Step 3 — 验证(运行时检查)
在游戏内执行脚本验证无缺键:
```gdscript
var keys = ["SHOP_CLOSED_MSG"] # 你新加的键
for loc in ["zh_CN","zh_TW","en","ja"]:
Locale.set_locale(loc)
for k in keys:
if tr(k) == k:
print("缺键!语言: %s 键: %s" % [loc, k])
```
---
## 4. 键名规范
**格式**`<模块>_<实体>_<含义>`,全大写 + 下划线
| 前缀 | 用途 | 示例 |
|:---|:---|:---|
| `UI_` | 通用 HUD 标签 | `UI_WAVE`, `UI_HP` |
| `SHOP_` | 商店界面 | `SHOP_TITLE`, `SHOP_REROLL` |
| `INV_` | 背包界面 | `INV_TITLE`, `INV_BENCH` |
| `SETTLE_` | 结算屏 | `SETTLE_TITLE`, `SETTLE_STATS` |
| `SETTINGS_` | 设置面板 | `SETTINGS_DIFFICULTY` |
| `STATUS_` | 状态消息 | `STATUS_PURCHASED` |
| `DIFF_` | 难度名称 | `DIFF_BEGINNER`, `DIFF_CHALLENGE` |
| `SPELL_` | 法术名/描述 | `SPELL_SPARK_BOLT_NAME`, `SPELL_SPARK_BOLT_DESC` |
| `CORE_` | Core 名/描述 | `CORE_WAND_BASIC_NAME` |
| `STATUS_EFFECT_` | 状态效果名 | `STATUS_EFFECT_BURN_NAME` |
| `BOSS_` | Boss 名称 | `BOSS_FINAL_NAME` |
---
## 5. 法术 / Core 名称本地化(待完成)
当前法术 `display_name` 字段存储的是裸英/中文字符串(如 `"Spark Bolt"`)。正式发布前需键化。
> **注意**:修改后须同步更新商店按钮渲染(`combat_s2.gd → _refresh_shop_ui`),将 `spell.display_name` 改为 `tr(spell.display_name)`。
**Step 1 — 修改 `wand_preset.gd`**
```gdscript
# 当前(裸字符串)
n.display_name = "Spark Bolt"
n.description = "发射一颗期限 4s 的电光飞弹,造成 3 伤害。"
# 修改为(键)
n.display_name = "SPELL_SPARK_BOLT_NAME"
n.description = "SPELL_SPARK_BOLT_DESC"
```
**Step 2 — 在四语 `.po` 中添加**
```po
# zh_CN.po
msgid "SPELL_SPARK_BOLT_NAME"
msgstr "电花弹"
msgid "SPELL_SPARK_BOLT_DESC"
msgstr "发射一颗快速电花飞弹,造成 3 点闪电伤害。"
```
**Step 3 — 显示时包裹 tr()**
```gdscript
# UI 显示法术名时
btn.text = tr(spell.display_name)
# 法术描述
desc_label.text = tr(spell.description)
```
---
## 6. 添加新语言
**Step 1 — 创建新 `.po` 文件**(以韩语为例)
```
translations/ko.po
```
内容:复制 `en.po`,将 `Language: en` 改为 `Language: ko`,翻译所有 `msgstr`
**Step 2 — 在 `locale_manager.gd` 中注册**
```gdscript
const LOCALES: Array = ["zh_CN", "zh_TW", "en", "ja", "ko"] # 追加 "ko"
```
新语言将自动在商店语言切换按钮中循环出现。
---
## 7. 在游戏内切换语言(测试用)
```gdscript
# 切换到英文
Locale.set_locale("en")
# 循环切换(等同商店 🌐 按钮)
Locale.cycle_locale()
# 查看当前语言
print(Locale.get_locale())
```
---
## 8. 提交规范
每次提交涉及玩家可见字符串的改动时,须**同时更新四个 `.po` 文件**。允许以英文或空字符串占位(`msgstr ""`),但 PR 审查时须标注"待翻译"。
---
## 9. 工具推荐
| 工具 | 用途 |
|:---|:---|
| [Poedit](https://poedit.net) | `.po` 文件 GUI 编辑器,支持模糊匹配和自动翻译接口 |
| VSCode + gettext 插件 | 轻量化文本编辑 |
| Godot 内置本地化 | `Project → Project Settings → Localization → Translations` 可预览各语言 |