UE5本地化核心配置PropertyNames.ini深度解析与实战指南
2026/8/3 2:09:20 网站建设 项目流程

1. 项目概述:为什么PropertyNames.ini值得深挖?

如果你在UE5项目里做过本地化,尤其是涉及C++代码的文本翻译,大概率遇到过这个场景:你在代码里写了一句FText::FromString(TEXT(“Hello World”)),然后信心满满地打开本地化工具,却发现工具根本找不到这个“Hello World”字符串,无法提取出来进行翻译。或者,你发现翻译后的文本在运行时,其“键”(Key)是一串难以理解的、由命名空间和标识符组成的哈希值,而不是你期望的、有意义的字符串。这些问题,十有八九都跟一个不起眼的配置文件——PropertyNames.ini——有关。

这个文件是UE5本地化系统(Localization)中一个非常核心但文档稀少的“元数据”配置文件。它不像Game.iniDefaultEngine.ini那样频繁露面,却直接决定了引擎如何扫描、识别和归类你代码中那些需要被翻译的文本属性。简单来说,它是一份“寻人启事”,告诉本地化工具:“嘿,当你去C++头文件里找需要翻译的字符串时,你应该关注哪些类型的属性,以及这些属性可能叫什么名字。”

我接手过一个从UE4迁移到UE5的大型项目,本地化工作一度陷入混乱。新写的UI文本死活提取不出来,而老的翻译键名变得面目全非,导致已有的翻译大量失效。经过一番痛苦的排查,最终发现症结就在于迁移过程中忽略了对PropertyNames.ini的适配和深入理解。自那以后,我就养成了在项目启动或升级引擎时,优先审视这个文件的习惯。今天,我就结合UE5.3的源码,带你彻底拆解这个文件,让你不仅能解决眼前的问题,更能理解其背后的设计哲学,从而在未来的开发中游刃有余。

2. 核心机制:PropertyNames.ini如何驱动本地化收集?

要理解PropertyNames.ini,我们必须先快速回顾一下UE5本地化收集(Gather)的基本流程。这个过程通常由命令行工具或编辑器功能触发,核心任务是扫描项目中的所有源代码和资源文件,找出所有标记为需要本地化的文本(FTextFString属性),并为它们生成唯一的键和对应的源文本。

2.1 收集流程中的角色定位

本地化收集器(Localization Gatherer)在扫描C++头文件(.h)时,面对的是一个充满各种宏(如UPROPERTYUFUNCTION)和复杂类型的森林。它不可能,也不应该去理解每一行代码的语义。它的工作模式更像是“模式匹配”。PropertyNames.ini在这里扮演了“模式匹配规则手册”的角色。

收集器的工作简化流程如下:

  1. 解析头文件:使用类似编译器的词法/语法分析器,将头文件解析成抽象语法树(AST)的简化版,识别出类、结构体、属性声明等。
  2. 匹配属性声明:对于每一个识别出的属性(变量),收集器会检查它的类型和元数据(Metadata)。
  3. 查阅规则手册(PropertyNames.ini):收集器将属性的类型和可能的元数据关键字,与PropertyNames.ini中定义的规则进行比对。
  4. 决策与提取:如果匹配到某条规则,并且该规则指示需要本地化(Localizable=true),收集器就会将这个属性的“默认值”或指定的元数据值,作为一个待翻译的文本单元提取出来。
  5. 生成定位信息:同时,收集器会记录这个文本出现在哪个文件、哪个类、哪个属性中,这些信息最终会体现在本地化资源的“键”或上下文信息中。

如果没有PropertyNames.ini,收集器就不知道FText类型的UPROPERTY需要被提取,更不用说那些通过元数据DisplayNameToolTip来提供UI文本的FString属性了。

2.2 文件位置与加载时机

PropertyNames.ini通常位于引擎目录下:[UE5_Install_Path]\Engine\Config\Localization\PropertyNames.ini

你也可以在项目目录的Config\Localization\文件夹下创建或覆盖同名文件,以实现项目特定的规则。引擎在启动本地化收集流程时,会按照标准的配置加载顺序(引擎默认 -> 项目覆盖)来合并这些规则。

注意:直接修改引擎目录下的文件不是好习惯,这会导致引擎升级时你的修改被覆盖。最佳实践是在你的项目Config\Localization\目录下创建自己的PropertyNames.ini文件,只写入你需要新增或修改的规则。引擎的配置系统会自动处理覆盖逻辑。

3. 源码结构深度解读

理论讲完了,我们直接上硬菜——看源码。UE5.3中,与PropertyNames.ini相关的核心代码主要在Engine\Source\Runtime\Core\Private\Internationalization\TextLocalizationResourceGenerator.cppEngine\Source\Runtime\Core\Public\Internationalization\TextProperty.h等文件中。但最直观的入口是解析这个配置文件本身的逻辑。

我们可以通过搜索PropertyNames.ini这个字符串在源码中的使用来定位。关键的处理类通常是FLocalizationConfigurationScriptFTextLocalizationResourceGenerator。不过,对于我们理解其格式和含义,直接分析一个实际的PropertyNames.ini文件内容更为高效。下面是一个基于UE5.3引擎版本的典型文件结构节选和解读:

[/Script/Engine.Engine] +PropertyNames="DisplayName" +PropertyNames="ToolTip"

这是一个最常见的规则块。我们来拆解它的每一个部分:

  • [/Script/Engine.Engine]:这是一个“节”(Section)的头。它不是指某个具体的C++类,而是一个“配置对象”的路径。这里的/Script/Engine.Engine可以理解为引擎全局配置的命名空间。更重要的是,这个节名定义了一条规则的作用范围。收集器在处理一个属性时,会看这个属性所属的类(或结构体)是否“匹配”这个节所描述的规则。匹配逻辑通常不是精确的类名匹配,而是更灵活的。 实际上,节名中的“类名”部分(如Engine)用于匹配属性的类型或其外层类的类型。这使得你可以为某一类特定的属性(如所有AActor派生类的DisplayName)定义规则。

  • +PropertyNames="DisplayName":这是一条规则项。

    • +符号表示“添加”,这是UE配置系统的语法,用于向一个数组类型的配置项添加元素。
    • PropertyNames是配置项的名称,它指定了需要关注的属性名称。注意,这里的“属性名称”指的是属性(变量)本身的名称,而是指其“元数据”(Metadata)的关键字特殊的标识符
    • "DisplayName"是属性名称的值。当收集器发现一个属性拥有名为DisplayName的元数据时,就会触发这条规则。

那么,这条规则合起来的意思是:对于任何匹配到本规则作用范围(/Script/Engine.Engine)的属性,如果该属性拥有一个DisplayName元数据,则收集器应该尝试将DisplayName元数据对应的字符串值提取出来,作为待本地化的文本。

3.1 规则项的完整语法与参数

一条完整的规则项远比上面的例子复杂。在源码中,它通常被解析成一个结构体,包含以下关键字段:

+PropertyNames=(Name="DisplayName", Category="Editor", Localizable=true, Searchable=true, IncludeParentClasses=true)

让我们逐一解读每个参数:

  1. Name(必需):规则所针对的“属性名称”。如前所述,这通常是元数据关键字(如DisplayName,ToolTip),也可以是特殊的标识符如FText(用于直接匹配FText类型的属性本身)。

  2. Category(可选):类别。这个信息会被附加到收集到的文本条目上,用于在本地化管理工具(如Localization Dashboard)中对文本进行分类筛选。例如,所有Category="Editor"的文本可能都是编辑器UI用的,而Category="Game"的是游戏内文本。这纯粹是一个组织性字段。

  3. Localizable(可选,默认true)核心参数。布尔值,指示匹配到的字符串是否应该被本地化。如果设置为false,收集器会忽略这个文本。什么情况下会设为false?例如,某些ToolTip可能是纯技术描述(如“单位:厘米”),在任何语言下都不需要改变,就可以标记为Localizable=false

  4. Searchable(可选,默认true):布尔值,指示该文本是否应被纳入可搜索的索引中。这主要影响本地化工具内部的搜索功能。通常保持默认即可。

  5. IncludeParentClasses(可选,默认值依情况而定):布尔值。这是一个非常重要的参数。它控制规则的继承性。

    • 当它为true时,这条规则不仅作用于直接匹配节名所指定类的属性,也作用于该类的所有派生类(子类)的属性。
    • 当它为false时,规则仅精确作用于指定的类。 例如,如果你为[/Script/CoreUObject.Object]定义了一条DisplayName规则且IncludeParentClasses=true,那么UE中几乎所有的UObject派生类的DisplayName元数据都会被收集,因为Object是基类。这通常是你想要的效果。但如果你只想为某个特定的、非派生的工具类(比如一个独立的工具类UMyStandaloneTool)定义规则,就可以设为false

3.2 特殊规则:直接匹配FText类型

除了匹配元数据,PropertyNames.ini还有一个至关重要的功能:直接匹配FText类型的属性。这是如何做到的呢?答案在于一个特殊的Name值:

[/Script/CoreUObject.Property] +PropertyNames=(Name="FText", Localizable=true)

这条规则非常强大:

  • [/Script/CoreUObject.Property]Property是UE属性系统(UProperty)的基类。这个节名非常宽泛,意图匹配所有类型的属性。
  • Name="FText":这不是一个元数据关键字,而是一个类型名。当收集器遇到一个属性,并且该属性的类型是FText时,就会触发这条规则。
  • Localizable=true:指示收集器提取这个FText属性本身的值(例如,在UPROPERTY初始化列表或构造函数中赋予的默认FText值)。

这是为什么你代码中的FText类型UPROPERTY能够被自动收集并本地化的根本原因!如果没有这条规则,或者你错误地修改了它,你的所有FText属性都将从本地化雷达上消失。

实操心得:在自定义项目规则时,永远不要在项目的PropertyNames.ini中覆盖或删除引擎默认的[/Script/CoreUObject.Property]节中关于FText的规则。你可以在项目配置中添加新的节和规则,但不要动这个根基。我见过有团队为了“清理”配置,误删了这条,导致整个项目的UI文本都无法本地化,排查了整整两天。

4. 实战:自定义规则解决真实问题

理解了原理和语法,我们来看几个实战案例,看看如何通过自定义PropertyNames.ini来解决实际开发中的痛点。

4.1 案例一:为自定义UI组件添加ToolTip本地化

假设你开发了一个自定义的UMyAwesomeButton组件,它有一个FString类型的ButtonToolTip属性,你希望这个属性的默认值能被本地化工具收集。

未配置前:你在头文件中这样定义:

UCLASS() class UMyAwesomeButton : public UButton { GENERATED_BODY() public: UPROPERTY(EditDefaultsOnly, Category="Appearance", meta=(ToolTip="This is the default tooltip for my awesome button.")) FString ButtonToolTip; };

运行本地化收集命令后,你会发现“This is the default tooltip...”这个字符串并没有被提取出来。因为默认的PropertyNames.ini规则只匹配通用的ToolTip元数据,且作用范围可能不包含你的自定义类。

解决方案:在你的项目Config\Localization\PropertyNames.ini文件中添加如下规则:

[/Script/MyGame.MyAwesomeButton] +PropertyNames=(Name="ToolTip", Category="GameUI", Localizable=true, IncludeParentClasses=false)
  • 节名/Script/MyGame.MyAwesomeButton精确匹配你的类。MyGame是你的模块名。
  • 规则:匹配名为ToolTip的元数据,分类到GameUI,需要本地化。
  • IncludeParentClasses=false:因为我们只希望这条规则精确作用于UMyAwesomeButton类,不影响其他可能继承自UButton的类(除非它们也明确需要此规则)。

添加此规则并重新运行收集命令后,ButtonToolTip属性的ToolTip元数据值就会被成功提取。

4.2 案例二:排除特定属性的本地化

有时,某些属性虽然有DisplayName,但其值是固定的、不应翻译的。例如,一个表示单位的属性:

UPROPERTY(EditAnywhere, Category="Physics", meta=(DisplayName="Distance Unit")) FString Unit = TEXT("meter");

这里的“meter”作为单位,在任何语言环境下都应保持为“meter”或“米”,而不应被翻译成其他语言的长度单位。

解决方案:为这个特定的类或属性类型创建一条Localizable=false的规则。但更精准的做法是利用Category和更具体的节名。不过,PropertyNames.ini的匹配粒度是“类+元数据名”,无法精确到某个具体属性变量。因此,更常见的做法是在代码层面避免给不需要本地化的文本使用FText类型或本地化相关的元数据。对于这个例子,更好的设计是使用FString且不添加DisplayName元数据,或者使用一个枚举类型。

然而,如果你确实需要为一个广泛使用的元数据(如ToolTip)在某个特定类上禁用本地化,可以这样做:

[/Script/MyGame.MyPhysicsComponent] +PropertyNames=(Name="ToolTip", Localizable=false)

这表示在MyPhysicsComponent类中,所有ToolTip元数据都不参与本地化。

4.3 案例三:处理第三方插件或复杂继承链

当你集成一个第三方插件,或者你的类继承自一个复杂的引擎类时,默认的规则可能不生效,或者生效了但产生了你不期望的副作用。

策略

  1. 首先检查插件的文档:看看插件是否提供了自己的PropertyNames.ini配置片段。
  2. 使用IncludeParentClasses进行控制:如果你希望规则只应用于你的直接类,就设为false。如果你希望影响所有子类,就设为true。理解你的类继承层次至关重要。
  3. 从宽到窄测试:可以先定义一个作用范围较宽的规则(如针对基类),观察收集结果。如果收集了太多不需要的文本,再逐步收窄范围(指定更具体的子类)。本地化收集命令通常有输出日志,可以详细查看每个文本被收集的来源(文件、行号、类、属性),这是调试的黄金信息。

5. 调试与验证:确保你的规则生效

配置了PropertyNames.ini之后,如何验证它是否按预期工作?

5.1 使用命令行工具收集

最可靠的方式是使用UE附带的命令行工具进行本地化收集,并查看详细输出。

  1. 打开命令行,导航到你的UE5引擎的Engine\Binaries\DotNET\UnrealBuildTool目录(或确保UnrealBuildTool在系统路径中)。
  2. 运行收集命令。命令格式通常如下(具体参数需参考项目设置):
    UnrealBuildTool.exe -Mode=GatherText -Config="Path/To/Your/Project.uproject"
    或者使用更现代的UnrealEditor-Cmd.exe
    UnrealEditor-Cmd.exe "Path/To/Your/Project.uproject" -run=GatherText
  3. 在命令输出中,搜索你的目标字符串或类名。成功的收集会输出类似这样的日志:
    LogTextLocalizationResourceGenerator: Display: Gathering text from source file: MyAwesomeButton.h LogTextLocalizationResourceGenerator: Display: Found localized text: "This is the default tooltip for my awesome button." (Namespace: "MyGame", Key: "[Hash...]", Source: "MyAwesomeButton.h - UMyAwesomeButton::ButtonToolTip [ToolTip]")
    如果没找到,检查日志中是否有警告或错误,并确认你的PropertyNames.ini文件被正确加载(引擎启动日志会显示加载的配置文件)。

5.2 在编辑器中检查本地化仪表板

在Unreal Editor中,打开“窗口” -> “本地化仪表板”

  1. 在“收集”标签页,确保你的目标文化(如中文)已被选中。
  2. 点击“从文本中收集”按钮。
  3. 收集完成后,切换到“翻译”标签页。
  4. 在搜索框中,输入你期望被收集的字符串片段或类名。如果能搜索到,并且其“源”信息正确指向你的类和属性,则说明规则生效。

5.3 常见排查点

如果规则不生效,请按以下顺序检查:

  1. 文件位置:确认你的自定义PropertyNames.ini文件位于YourProject/Config/Localization/目录下。
  2. 文件编码:确保文件是UTF-8编码,无BOM头。Windows记事本保存时默认可能是带BOM的UTF-8,这有时会导致解析问题。建议使用VS Code、Notepad++等编辑器保存为UTF-8无BOM。
  3. 语法错误:检查INI文件语法,括号、引号是否成对,逗号分隔是否正确。一个错误的逗号或缺失的引号可能导致整条规则甚至整个文件被忽略。
  4. 节名匹配:确认你写的节名/Script/Module.ClassName中的模块名(Module)和类名(ClassName)完全正确。类名是C++类名(如MyAwesomeButton),而不是蓝图显示名。
  5. 属性名匹配:确认Name字段的值与代码中元数据的关键字完全一致,大小写敏感。
  6. 继承与覆盖:如果你在项目配置中写了与引擎默认配置中相同的节和规则,项目配置会覆盖引擎配置。确保你的覆盖是故意的,并且没有意外地禁用了关键规则(如对FText的匹配)。
  7. 重启编辑器/重新生成项目文件:有时配置文件的更改需要重启编辑器,或者需要重新运行GenerateProjectFiles脚本(对于Visual Studio解决方案)才能被构建系统完全识别。

6. 高级话题:与源码宏和元数据的协同

PropertyNames.ini并非孤立的,它与你在C++代码中使用的宏和元数据紧密相关。理解这种协同关系,能让你更好地设计可本地化的代码。

6.1 LOCTEXT宏与命名空间

对于在代码中硬编码的FText,最佳实践是使用LOCTEXT宏:

FText MyText = LOCTEXT(“MyKey”, “Hello World”);

LOCTEXT宏会自动为字符串“Hello World”创建一个唯一的键,并将其归属到一个“命名空间”(Namespace)下。这个命名空间通常由LOCTEXT_NAMESPACE宏定义。本地化收集器会专门处理这些宏,这个过程不完全依赖于PropertyNames.iniPropertyNames.ini主要处理的是属性(UPROPERTY)上的文本。

6.2 元数据(Metadata)的灵活运用

PropertyNames.ini规则匹配的是元数据的关键字。因此,你可以通过定义自定义的元数据关键字来触发特定的本地化行为。

例如,你有一个专门用于任务描述的属性:

UPROPERTY(EditAnywhere, Category=”Quest”, meta=(QuestDescription=”Find the hidden treasure.”)) FString Description;

你可以为此在PropertyNames.ini中添加规则:

[/Script/MyGame.QuestBase] +PropertyNames=(Name=”QuestDescription”, Category=”GameQuest”, Localizable=true)

这样,你就创建了一个专用于任务描述的本地化通道,与通用的ToolTipDisplayName分离开,便于在本地化管理工具中分类管理。

6.3 性能考量

PropertyNames.ini的规则数量不宜过多,且应尽可能精确。过于宽泛的规则(如为基类Object定义大量规则且IncludeParentClasses=true)会导致收集器在扫描每一个属性时都要进行大量的规则匹配,拖慢收集速度。对于大型项目,收集文本本身就是一个耗时操作,优化规则集是提升效率的一个小技巧。

7. 总结与最佳实践清单

经过对源码和实战的剖析,我们可以将PropertyNames.ini的精髓总结为以下几点最佳实践:

  1. 敬畏默认配置:不要轻易修改引擎自带的PropertyNames.ini。所有项目特定的定制,都应放在YourProject/Config/Localization/PropertyNames.ini中。
  2. 理解核心规则:牢记引擎默认配置中[/Script/CoreUObject.Property]节下Name="FText"的规则。这是FText属性能被自动收集的生命线。
  3. 精确匹配原则:在添加自定义规则时,尽量使用精确的类名(IncludeParentClasses=false),避免规则意外应用到不相关的类上。
  4. 善用Category分类:为不同的规则设置清晰的Category,如“Editor”“GameUI”“Gameplay”等。这能极大地方便后期在本地化仪表板中对海量文本进行筛选和管理。
  5. 代码与配置协同设计:在编写C++代码时,就应考虑到本地化。思考哪些字符串需要翻译,并决定是通过FText属性、LOCTEXT宏,还是通过元数据(如DisplayName)来暴露它们。然后相应地设计或补充PropertyNames.ini规则。
  6. 调试与验证:修改配置后,务必通过命令行收集或编辑器仪表板来验证规则是否生效,文本是否被正确提取。养成查看收集日志的习惯。
  7. 文档化团队规范:如果是在团队中开发,应将项目自定义的PropertyNames.ini规则及其用途写入团队的技术文档或代码规范中。确保所有程序员都了解,添加新的需要本地化的文本属性时,是否需要更新此配置文件。

PropertyNames.ini就像UE5本地化系统中的一个精密齿轮,虽然小巧隐蔽,但一旦错位,整个文本翻译流程就可能卡壳。希望这篇近万字的深度解读,能帮你把这个齿轮调整到最佳状态,让你在应对多语言项目的复杂需求时,更加得心应手。毕竟,让全世界玩家都看懂你的游戏,第一步就是确保引擎能看懂你代码里哪些文字需要被翻译。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询