关于 localization
面向 Godot 4.3+ 开发者的本地化指南,说明翻译文件、运行时语言切换及多语言界面处理,并提供 GDScript 和 C# 示例。
主要功能
- 01使用 tr() 标记可翻译文本
- 02支持 CSV 与 PO 翻译文件
- 03运行时切换语言环境
- 04RTL 布局与本地化格式处理
- 05Godot 4.6+ CSV 复数和上下文
适用场景
- 01为游戏界面添加多语言文本
- 02在运行时切换游戏语言
- 03检查 RTL 语言布局与翻译溢出
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jame581/ godotprompter --skill localization安装后在 Claude Code 中输入/localization确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/csv-plural-context.mdeditor-preview.mdlocale-formatting.mdrtl-support.md详细介绍
Godot 4.3+ 中的本地化
所有示例均面向 Godot 4.3+,不使用已弃用的 API;先介绍 GDScript,再介绍 C#。
相关技能: godot-ui 用于管理 Control 节点和主题,save-load 用于保存语言设置,responsive-ui 用于根据区域设置调整布局。
1. 核心概念
Godot 本地化的工作方式
- 使用
tr()(Godot 的翻译函数)包装所有面向用户的字符串 - 创建翻译文件(CSV 或 PO),将键映射到译文
- 将翻译文件导入为
Translation资源 - 通过
TranslationServer.set_locale()在运行时切换区域设置
所有具有 text、tooltip_text 或 placeholder_text 属性的 Control 节点,如果其值与翻译键匹配,都会自动翻译。
翻译键策略
| 策略 | 示例键 | 优点 | 缺点 |
|---|---|---|---|
| 语义键 | MENU_START_GAME | 意图明确,易于查找 | 需要默认语言回退 |
| 以英语为键 | Start Game | 代码易读,英语无需映射文件 | 英文文本更改时会失效 |
建议: 生产环境使用语义键(
MENU_START_GAME);以英语为键仅适用于原型或个人项目。
2. 翻译文件
CSV 格式
最简单的格式。第一列为键,后续列为区域设置代码。
keys,en,cs,de,ja
MENU_START,Start Game,Začít hru,Spiel starten,ゲームスタート
MENU_OPTIONS,Options,Nastavení,Optionen,オプション
MENU_QUIT,Quit,Ukončit,Beenden,終了
PLAYER_HEALTH,Health: %d,Zdraví: %d,Gesundheit: %d,体力: %d
ITEM_COLLECTED,%s collected!,%s sebráno!,%s gesammelt!,%sを入手!
在项目中将文件保存为 translations.csv。Godot 会在导入时自动检测格式。
导入设置(Import 面板):
- Delimiter:逗号(默认)、分号或制表符
- Compress:默认开启——存储为
OptimizedTranslation;对于使用?context/?plural的文件,则会跳过压缩(§10)
PO 格式(Gettext)
行业标准格式,翻译团队以及 Poedit、Weblate、Crowdin 等工具都优先使用此格式。
创建 POT 模板(messages.pot):
msgid "MENU_START"
msgstr ""
msgid "MENU_OPTIONS"
msgstr ""
msgid "MENU_QUIT"
msgstr ""
msgid "PLAYER_HEALTH"
msgstr ""
创建区域设置文件(例如,捷克语文件 cs.po):
msgid "MENU_START"
msgstr "Začít hru"
msgid "MENU_OPTIONS"
msgstr "Nastavení"
msgid "MENU_QUIT"
msgstr "Ukončit"
msgid "PLAYER_HEALTH"
msgstr "Zdraví: %d"
注册翻译
项目设置 → 本地化 → 翻译 → 添加... → 选择 .csv 或 .po 文件。
也可以在运行时注册:
var translation := load("res://translations/cs.po") as Translation
TranslationServer.add_translation(translation)
var translation = GD.Load<Translation>("res://translations/cs.po");
TranslationServer.AddTranslation(translation);
⚠️ Godot 4.7 中的更改:
OptimizedTranslation.generate()现在返回bool(之前为void)。GDScript 和 C# 源代码兼容,但二进制不兼容——请重新编译调用该方法的预编译 C# 插件。请参阅 4.7 迁移指南。
POT 生成钩子(Godot 4.7+)
自定义 EditorTranslationParserPlugin 可以重写 _customize_strings()——该方法会在 POT 生成期间解析完所有文件后调用一次,用于添加或移除最终提取的字符串列表中的条目:
@tool
extends EditorTranslationParserPlugin
func _customize_strings(strings: Array[PackedStringArray]) -> Array[PackedStringArray]:
strings.append(PackedStringArray(["Test 1", "context", "test 1 plurals", "test 1 comment"]))
# Drop internal strings that begin with "$".
return strings.filter(func(s): return not s[0].begins_with("$"))
#if TOOLS
using System.Linq;
using Godot;
public partial class CommentAwareParser : EditorTranslationParserPlugin
{
public override Godot.Collections.Array<string[]> _CustomizeStrings(Godot.Collections.Array<string[]> strings)
{
strings.Add(new[] { "Test 1", "context", "test 1 plurals", "test 1 comment" });
// Drop internal strings that begin with "$".
return new Godot.Collections.Array<string[]>(strings.Where(s => !s[0].StartsWith("$")));
}
}
#endif
Godot 4.7+: POT 生成器也会提取
Control.accessibility_name和accessibility_description,让无障碍字符串无需手动列出即可翻译。(GH-117134)
3. 在代码中使用 tr()
GDScript
# Basic translation
var label_text: String = tr("MENU_START") # "Start Game" or translated equivalent
# With format arguments
var health_text: String = tr("PLAYER_HEALTH") % current_health
# "Health: 85" or "Zdraví: 85"
# With string arguments
var collected_text: String = tr("ITEM_COLLECTED") % item_name
# "Sword collected!" or "Meč sebráno!"
# Pluralization (Godot 4.x)
var count := 5
var msg: String = tr_n("ONE_ENEMY", "MANY_ENEMIES", count)
# Needs plural forms in the translation: PO, or CSV `?plural` rows on Godot 4.6+ (§10)
C#
string labelText = Tr("MENU_START");
// string.Format ignores GDScript-style %d — substitute it yourself.
string healthText = Tr("PLAYER_HEALTH").Replace("%d", currentHealth.ToString());
// Pluralization
string msg = TrN("ONE_ENEMY", "MANY_ENEMIES", count);
Control 自动翻译
如果 Label、Button、RichTextLabel 和其他 Control 节点的 text 属性与翻译键匹配,就会自动翻译。将文本设为对应的键:
Button.text = "MENU_START" → displays "Start Game" (en) or "Začít hru" (cs)
提示: 若要关闭特定 Control 的自动翻译,请将
auto_translate_mode设为DISABLED。
Godot 4.7+:
Control.translation_context: StringName为每个 Control 设置翻译上下文,用于翻译显示文本并生成翻译模板——相当于tr()的上下文参数所对应的属性(C#:TranslationContext)。(GH-115340)
4. 在运行时切换区域设置
GDScript
# Switch language
func set_language(locale_code: String) -> void:
TranslationServer.set_locale(locale_code)
# All Control nodes with translation keys update automatically
# Get current locale
var current: String = TranslationServer.get_locale() # e.g. "en", "cs", "de"
# Get available locales
var locales: PackedStringArray = TranslationServer.get_loaded_locales()
C#
public void SetLanguage(string localeCode)
{
TranslationServer.SetLocale(localeCode);
}
string current = TranslationServer.GetLocale();
语言选择菜单
extends Control
@onready var language_button: OptionButton = %LanguageButton
var _locales: Array[Dictionary] = [
{"code": "en", "name": "English"},
{"code": "cs", "name": "Čeština"},
{"code": "de", "name": "Deutsch"},
{"code": "ja", "name": "日本語"},
]
func _ready() -> void:
for locale in _locales:
language_button.add_item(locale["name"])
# Set current selection
var current_locale: String = TranslationServer.get_locale()
for i in _locales.size():
if _locales[i]["code"] == current_locale:
language_button.selected = i
break
language_button.item_selected.connect(_on_language_selected)
func _on_language_selected(index: int) -> void:
TranslationServer.set_locale(_locales[index]["code"])
# Save preference — SettingsManager is a user-created autoload (see save-load skill)
SettingsManager.set_setting("general", "locale", _locales[index]["code"])
5. 从右向左(RTL)支持
阿拉伯语、希伯来语和波斯语需要为 Control 设置 layout_direction(LOCALE 会自动跟随当前区域设置),设置 structured_text_type 以避免 URL 和路径完全反转,还需要使用支持相应文字的字体——Godot 的默认字体不支持。每次区域设置更改时,都要重新应用布局方向。TranslationServer 没有信号——请重写 _notification,并监听 Node.NOTIFICATION_TRANSLATION_CHANGED(MainLoop 也有一个同名常量)。由于从未订阅,因此无需断开任何处理程序。
完整配置步骤、各控件属性表、混合方向文本的 BBCode 以及 C# LocaleAwarePanel:references/rtl-support.md
6. 区域设置感知格式化
GDScript 不支持区域设置感知的数字分组或日期格式化——"%d" % 1234567 始终会得到 1234567,因此需要手动分组数字(TextServer.format_number() 只会将数字转换为区域设置对应的数字系统)。C# 则支持:从 TranslationServer.GetLocale() 获取 CultureInfo(将 _ 替换为 -),然后使用 ToString("N"/"C"/"d", culture)。
两种辅助方法的完整说明:references/locale-formatting.md
7. 项目组织
推荐的文件结构
res://
├── translations/
│ ├── game.csv # Main game translations
│ ├── ui.csv # UI-specific translations
│ └── items.csv # Item names and descriptions
├── fonts/
│ ├── default_font.ttf # Latin, Cyrillic
│ └── cjk_font.ttf # Chinese, Japanese, Korean
└── themes/
└── default_theme.tres # Font assignments per locale
翻译键约定
# Category_Context_Description
MENU_MAIN_START # Main menu, start button
MENU_MAIN_QUIT # Main menu, quit button
HUD_HEALTH_LABEL # In-game HUD, health label
DIALOGUE_NPC_GREETING # NPC dialogue, greeting line
ITEM_SWORD_NAME # Inventory item name
ITEM_SWORD_DESC # Inventory item description
8. 常见问题
| 现象 | 原因 | 解决方法 |
|---|---|---|
| 显示的是翻译键,而不是文本 | 翻译文件未在 Project Settings 中注册 | 添加到 Project Settings → Localization → Translations |
| 切换区域设置后文本没有更新 | 使用了字符串字面量,而不是 tr() | 使用 tr() 包装所有面向用户的字符串 |
| 场景切换后 Label 显示翻译键 | 代码在运行时添加的翻译尚未在此场景中生效 | 在 Project Settings 中注册翻译(不要在运行时注册) |
| RTL 文本按 LTR 方向渲染 | 未设置 layout_direction | 在根 Control 上设置为 RTL 或 LOCALE |
| 字体无法显示字符 | 字体缺少对应的 Unicode 字符范围 | 导入支持目标文字的字体(推荐 Noto Sans) |
| CSV 无法实现复数形式 | Godot 4.6 之前的 CSV 不支持复数形式;4.6 及以上版本缺少 ?plural 列或续行 | 4.6 及以上版本添加 ?plural 列(§10);4.6 之前使用 PO |
翻译中的 %s 显示为字面量 %s | 将 tr() 的结果用作键,而不是对其进行格式化 | 使用 tr("KEY") % value,而不是 tr("KEY" % value) |
9. 编辑器区域设置预览(Godot 4.5+)
Godot 4.5 在 2D 编辑器的 View 菜单中新增了 Preview Translation 子菜单:编辑器视口会以任意已加载的区域设置重新渲染内容,让你无需进入运行模式,就能发现较长译文导致的溢出并检查 RTL 布局。此功能仅在编辑器中生效,不会影响导出的构建版本。
操作步骤和 QA 优势:references/editor-preview.md
10. CSV 复数和上下文支持(Godot 4.6+)
Godot 4.6 为 CSV 翻译新增了两个可选表头列——?context 和 ?plural——以及一个可选的 ?pluralrule 行,使 CSV 也支持上下文消歧和复数形式(此前仅 PO 支持)。额外的复数形式写在键为空的续行中,因此也能支持有 3 种及以上形式的语言(俄语、波兰语、阿拉伯语)。Control 的自动翻译会跳过这些行——请自行调用 tr() / tr_n()。
列参考、CSV 示例以及 tr() / tr_n() 用法(GDScript + C#):references/csv-plural-context.md。
11. 实施检查清单
- 所有面向用户的字符串都使用
tr()(或将其设为 Control 节点上的翻译键) - 翻译文件(CSV 或 PO)已在“项目设置 → 本地化 → 翻译”中注册
- 可以在运行时通过
TranslationServer.set_locale()切换语言 - 语言偏好设置会在游戏启动时保存并恢复
- 字体涵盖所有目标语言的字符集(拉丁文、CJK、阿拉伯文等)
- RTL 语言的根 UI 容器已将
layout_direction设为RTL或LOCALE - 格式字符串(
%s、%d)在tr()之后应用,而非之前 - 翻译键遵循一致的命名规范
- UI 布局能适应不同语言中较长或较短的文本(不使用硬编码宽度)
- 为每种目标语言提供复数形式 — 使用 PO,或在 Godot 4.6+ 中使用 CSV 的
?plural行 - 使用编辑器区域设置预览(2D 编辑器“视图 → 预览翻译”)进行翻译质量检查,而不是运行测试(Godot 4.5+)
- 当同一键在不同 UI 上下文中含义不同时,使用 CSV 的
?context列(Godot 4.6+) - CSV 复数形式使用
?plural列,每种额外的复数形式各用一行续行(仅在需要覆盖区域设置规则时使用?pluralrule行)(Godot 4.6+)