使游戏国际化
前言
虽然独立游戏和小众游戏通常不需要本地化,但瞄准更大市场的游戏通常需要本地化。Godot 提供了许多工具来使这个过程更加简单,因此本教程更像一个妙招和技巧的合集。
本地化通常是通过雇佣特定的工作室来完成的,尽管有大量的软件和文件格式可供使用,但迄今为止进行本地化最常见的方式仍然是使用电子表格。创建电子表格并导入电子表格的过程已经在《导入翻译》教程中介绍过了。如果你之前尚未阅读导入翻译页面,我们建议你在阅读本页面之前先阅读该页面。
备注
我们将使用官方演示作为示例,你可以从资产库下载它。
配置导入的译文
翻译可以在更改时得到更新并重新导入,但仍必须将其添加到项目中。可通过项目 → 项目设置 → 本地化来完成:

上述对话框用于添加或移除用于整个项目范围的翻译。
资源的本地化
还可以指示 Godot 根据当前语言使用资产(资源)的替代版本。这可用于本地化诸如游戏内广告牌之类的图像或者语音。
重定向选项卡便可用于此:

选择需要重定向的资源,然后为每个区域设置添加一些替代项。
备注
DynamicFonts 不支持资源重定向系统。要根据语言文字使用不同的字体,请改用 DynamicFont 后备系统,它允许你根据需要定义任意数量的后备字体。
DynamicFont 后备系统的优点是它可以忽略当前语言而工作,这使得它非常适合多人聊天即文本语言可能与客户端语言不匹配的情况。
Automatically setting a language
It is recommended to default to the user's preferred language which can be obtained via OS.get_locale_language().
If your game is not available in that language, it will fall back to the Fallback
in Project Settings > Internationalization > Locale, or to en
if empty.
Nevertheless letting players change the language in game is recommended for various reasons (e.g. translation quality or player preference).
var language = "automatic"
# Load here language from the user settings file
if language == "automatic":
var preferred_language = OS.get_locale_language()
TranslationServer.set_locale(preferred_language)
else:
TranslationServer.set_locale(language)
Locale vs. language
A locale is commonly a combination of a language with a region or country, but can also contain information like a script or a variant.
示例:
en
: English languageen_GB
: English in Great Britain / British Englishen_US
: English in the USA / American Englishen_DE
: English in Germany
Indie games generally only need to care about language, but read on for more information.
Why locales exist can be illustrated through the USA and Great Britain. Both speak the same language (English), yet differ in many aspects: - Spelling: E.g. gray (USA), grey (GB) - Use of words: E.g. eggplant (USA), aubergine (GB) - Units or currencies: E.g. feet/inches (USA), metres/cm (GB)
It can get more complex however. Imagine you offer different content in Europe and in China (e.g. in an MMO). You will need to translate each of those content variations into many languages and store and load them accordingly.
将键转换为文本
对于 Button、Label 等部分控件,如果它们的文本与某个翻译键名相匹配,则将自动获取翻译内容。例如,如果标签的文本为“MAIN_SCREEN_GREETING1”,并且该键存在于当前翻译中,则该文本将被自动翻译。
这种自动翻译行为在某些情况下可能是不可取的。例如,当使用 Label 来显示玩家的名字时,如果玩家的名字与翻译键相匹配,你很可能不希望进行翻译。要禁用某个节点的自动翻译,请在检查器中禁用Localization > Auto Translate(本地化 > 自动翻译)。
在代码中,可以使用 Object.tr() 函数。这将只在翻译中查找文本,并在找到后进行转换:
level.text = tr("LEVEL_5_NAME")
status.text = tr("GAME_STATUS_%d" % status_index)
level.Text = Tr("LEVEL_5_NAME");
status.Text = Tr($"GAME_STATUS_{statusIndex}");
备注
如果更改语言后不显示任何文字,请尝试换一个字体。默认项目字体仅支持 Latin-1 字符集的子集,无法用于显示俄语、汉语等文字。
Noto Fonts 是一系列不错的多语言字体资源。如果你使用的是不太常见的语言,请确保下载正确的变体。
下载字体后,将 TTF 文件加载到 DynamicFont 资源中,并将其用作 Control 节点的自定义字体。为了获得更好的可重用性,请将新的主题资源关联到根 Control 节点,并将 DynamicFont 定义为主题中的默认字体。
占位符
若要在翻译的字符串中使用占位符,请使用 GDScript 格式字符串 或 C# 中的等效功能。这使得翻译者可以自由移动字符串中占位符的位置,使得翻译听起来更自然。为了允许翻译人员决定占位符出现的顺序,应尽可能使用搭配使用带命名的占位符和 String.format()
的函数:
# The placeholder's locations can be changed, but not their order.
# This will probably not suffice for some target languages.
message.text = tr("%s picked up the %s") % ["Ogre", "Sword"]
# The placeholder's locations and order can be changed.
# Additionally, this form gives more context for translators to work with.
message.text = tr("{character} picked up the {weapon}").format({character = "Ogre", weapon = "Sword"})
翻译上下文
如果你使用普通的英文作为源字符串(而不是类似于 LIKE_THIS
的消息代码),那么就有可能会遇到歧义的情况,同一个英文字符串可能需要在某些目标语言中翻译为不同的字符串。你可以通过指定可选的翻译上下文来消除歧义,即便源字符串是相同的,也能够让目标语言能够使用不同的字符串:
# "Close", as in an action (to close something).
button.set_text(tr("Close", "Actions"))
# "Close", as in a distance (opposite of "far").
distance_label.set_text(tr("Close", "Distance"))
// "Close", as in an action (to close something).
GetNode<Button>("Button").Text = Tr("Close", "Actions");
// "Close", as in a distance (opposite of "far").
GetNode<Label>("Distance").Text = Tr("Close", "Distance");
复数
很多语言会根据对象的单复数使用不同的字符串。但是把“是否为复数”的条件硬编码为“对象数量是否大于 1 ”并不是对所有语言都有效。
有些语言有两种以上的复数形式,不同的复数需要的对象数量也各不相同。Godot 提供了对复数的支持,目标地区可以自动进行处理。
复数应该只用于正整数(或零)的情况。负数和浮点数所代表的物理实体数量是单数还是复数一般无法明确区分。
var num_apples = 5
label.text = tr_n("There is %d apple", "There are %d apples", num_apples) % num_apples
int numApples = 5;
GetNode<Label>("Label").Text = string.Format(TrN("There is {0} apple", "There are {0} apples", numApples), numApples);
如果有需要也可以和上下文组合使用:
var num_jobs = 1
label.text = tr_n("%d job", "%d jobs", num_jobs, "Task Manager") % num_jobs
int numJobs = 1;
GetNode<Label>("Label").Text = string.Format(TrN("{0} job", "{0} jobs", numJobs, "Task Manager"), numJobs);
备注
提供复数翻译仅支持使用 gettext 进行本地化,不支持 CSV。
使控件的大小可调
不同语言的相同文本的长度差异很大。为此,请务必阅读教程《大小和锚点》,因为动态调整控件大小可能有所帮助。Container 可能很有用,Label 的文本换行选项应该也能帮上忙。
若要检查你的 UI 是否可以容纳比原始字串更长的翻译,你可以在高级项目设置中启用伪本地化。这会将所有可本地化的字符串替换为更长的字符串,同时还会将原始字符串中的某些字符替换为带重音符号的字符串(同时仍可读)。占位符保持原样,以便在启用伪本地化时它们继续工作。
例如,当启用伪本地化时,字符串 Hello world, this is %s!
会变为 [Ĥéłłô ŵôŕłd́, ŧh̀íš íš %s!]
。
虽然第一眼看上去很奇怪,但是伪本地化的好处有很多:
它可以让你快速发现不可本地化的字串,这样你就可以检查它们并使它们可本地化(如果这样做有意义的话)。
它可以让你检查无法容纳长字符串的 UI 元素。许多语言的翻译会比源文本长得多,因此确保你的 UI 能够容纳比平常更长的字符串非常重要。
它可以让你检查你的字体是否包含支持各种语言所需的所有字符。然而,由于伪本地化的目标是保持原始字符串的可读性,因此它并不是检查字体是否支持 CJK(中文、日文、韩文) 或从右到左语言的有效测试。
项目设置允许你调整伪本地化行为,以便你可以根据需要禁用它的某些部分。
TranslationServer
Godot has a server handling low-level translation management called the TranslationServer. Translations can be added or removed during runtime; the current language can also be changed at runtime.
双向文本和 UI 镜像
阿拉伯语和希伯来语是从右到左书写的(除了混合的数字和拉丁单字),这些语言的用户界面也应该是镜像的。在某些语言中,字形的形状会根据周围的字符而变化。
对双向书写系统和 UI 镜像的支持是透明的,你通常不需要更改任何内容或了解特定的书写系统。
对于 RTL(从右至左)语言,Godot 会自动对 UI 进行以下调整:
镜像左右锚点和边距。
交换文本的左对齐和右对齐。
镜像容器中子控件的水平顺序以及 Tree/ItemList 控件中项目的水平顺序。
控件内部元素使用镜像顺序(例如 OptionButton 的下拉按钮、勾选框的位置、列表框列的顺序、Tree 项目的图标和连接线的位置等),某些镜像的控件会使用单独的主题样式。
坐标系不会镜像,非 UI 节点(精灵等)不受影响。
可以使用下列控件属性来覆盖文字和控件布局方向:
text_direction
设定基本文字方向。当设定为“auto”时,方向取决于根据 Unicode 双向算法的文字中的第一个强方向字符,language
覆盖目前项目区域设定。structured_text_bidi_override
属性和_structured_text_parser
回调,可以对结构化文本进行特殊处理。layout_direction
覆盖控件的镜像。

参见
你可以使用 BiDI 和字体功能演示项目来查看从右到左排版的实际工作方式。
在导出后的项目中添加分词迭代器数据
有些语言在书写时没有空格,单词和行的断行需要的不仅仅是字符序列规则。Godot 包含 ICU 规则和基于字典的分词迭代器数据,但默认情况下这些数据不包含在导出的项目中。要包含它,请转到项目 → 项目设置 → 本地化 → 文本服务器数据并单击安装支持数据...。分词迭代器数据大约有 4 MB 大。

结构化文本 BiDi 覆盖
Unicode BiDi 算法设计用于处理自然文本,它无法处理具有更高级别顺序的文本,例如文件名、URI、电子邮件地址、正则表达式或源代码。

例如,所显示的目录结构的路径将显示不正确(顶部“LineEdit”控件)。“文件”类型结构化文本覆盖将文本拆分为段,然后将 BiDi 算法分别应用于每个段,以正确显示任何语言的目录名称并保留文件夹的正确顺序(底部“LineEdit”控件)。
自定义回调提供了一种为其他类型的结构化文本覆盖 BiDi 的方法。
数字的本地化
专为数位输入或输出设计的控件(例如 ProgressBar、SpinBox)将自动使用本地化编号系统,对于其他控件,TextServer.format_number(string, language) 可用于将西方阿拉伯数字(0..9)转换为本地化的数字系统,而 TextServer.parse_number(string, language) 可以将其转换回来。
图标和图像的本地化
当带有左右指向箭头的图标指示移动或方向(例如后退/前进按钮)时,它们可能需要在阿拉伯语和希伯来语语言环境中翻转过来。否则,它们可以保持不变。
测试翻译
你可能会想要在发布前测试项目的翻译。Godot 为此提供了两种方法。
首先,在项目设置中的国际化 > 区域设置(启用高级设置)下,有一个测试属性。将这个属性设置为你想测试的语言的区域设置代码。Godot 将在项目运行(无论是从编辑器还是从导出时)时,使用该区域设置运行该项目。

请记住,因为这是一个项目设置,设为非空时它会在版本控制中显示。因此,将修改提交到版本控制之前,应该将其设回空值。
还可以在从命令行运行 Godot 时测试翻译。例如,要使用法语测试游戏,可以提供以下参数:
godot --language fr
翻译项目名称
项目名称将在导出到不同的操作系统和平台时成为应用名称。要以多种语言指定项目名称,请转到项目 > 项目设置 > 应用 > 配置。从这里点击可本地化字符串(大小 0)
按钮。现在下面应该有一个标有添加翻译
按钮。点击该按钮,它将带你到一个页面,可以在其中为项目名称翻译选择语言(如果需要,还可以选择地区)。完成后,你现在可以输入本地化的名称。

如果你不确定要使用的语言代码,请参阅区域代码列表。