ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

UE5场景文本一键翻译:编辑器自动化导入导出完整指南

UE5场景文本一键翻译:编辑器自动化导入导出完整指南 1. 这个需求是怎么来的游戏本地化不该只盯着 UI 控件做过游戏项目的人应该都有体会本地化Localization是一项看起来不难、做起来却很碎的工作。尤其是当游戏里散布着大量场景文本比如 NPC 头顶的名字、门上的标牌、墙上的涂鸦、任务板上的说明、关卡中的提示文本甚至是某个可互动物体在世界里直接显示的文字时传统本地化流程会变得非常痛苦。传统做法通常是这样策划把文本整理成 Excel翻译拿去翻然后程序把译文一条一条填回代码或数据表。如果只是 UI 界面上的按钮和弹窗这个流程还算可控但一旦文本来自游戏场景中的 Actor、关卡蓝图、UMG 控件、数据资产问题就来了——你很难知道哪些 FText 藏在哪个关卡里只能靠编辑器里逐个搜索效率极低而且容易漏翻。于是就有了一个很自然的想法能不能写一个工具把整个游戏场景里的可翻译文本一键导出交给翻译翻译完成后再一键把译文导回引擎让游戏场景直接用上翻译后的文本这篇文章就来完整拆解这个需求——从通用原理到编辑器内自动化流程设计再到一个可直接落地的 Python 引擎脚本示例。文章以Unreal Engine 5UE5作为主示例讲解Unity 项目也能套用同样的设计思路。2. 场景翻译的三种主流方案对比在写工具之前先搞清楚市面上常见的几种游戏文本翻译实现方式。它们各有适用场景了解之后才能判断“一键导出、导回引擎”这个方案到底解决了什么问题。2.1 运行时翻译方案运行时翻译是在游戏运行过程中通过插件或代码在显示文本之前进行替换。比如很多独立游戏用的“本地化组件”在BeginPlay时把文本 Key 映射到当前语言的字符串表。优点不需要修改原始资产。适合玩家社区翻译、热切换语言。对已有项目侵入性低。缺点翻译文件需要额外打包和加载。场景里有些文本如果被烘焙到某个材质或渲染资源中运行时无法替换。性能有轻微开销而且文本仍可能在代码中“写死”难以统一管理。2.2 静态资源替换方案静态资源替换是在编辑器里直接把文本改成目标语言。比如把关卡中某个 Actor 的Text属性从英文改成中文保存关卡重新打包。优点简单粗暴直接改资产。对运行性能零影响。缺点覆盖多语言时极度痛苦无法同时保留英文和中文两套文本。如果你想把一个游戏同时发布到多个地区这种方案基本不可行。2.3 基于编辑器工具的一键导入导出方案本文重点这个方案的思路是开发一个编辑器工具它负责扫描场景中的可翻译文本生成一份结构化翻译文件CSV/JSON/PO翻译完成后再通过工具把译文写回引擎的本地化系统并让场景中的 FText 自动引用对应的本地化 Key。这样做的好处非常明显翻译工作流标准化策划和翻译不需要接触引擎。一套资产可以支持多语言版本管理也干净。导回后是引擎原生本地化数据运行时按语言自动切换不需要额外插件。可以把“扫描、翻译、导回”固化成流水线减少手工遗漏。这篇文章后面的实战部分就是围绕第三种方案展开。3. 环境准备UE5 编辑器 Python在进入代码之前先按工程实践套路把环境梳理清楚。不同引擎版本界面入口有差异以下以 UE5.1 / 5.2 / 5.3 常见环境为例重点是思路。3.1 硬件与操作系统Windows 10/11 64 位。或 macOS 12。内存建议 16GB 以上UE5 编辑器对内存要求较高。3.2 引擎版本Unreal Engine 5.1 及以上版本。示例项目使用蓝图 C 混合工程。需要启用Localization Dashboard插件该插件在 UE5 中默认存在但需要确认已勾选。3.3 开发语言与外围工具引擎侧脚本推荐使用 PythonUE5 官方提供了 Python Editor Scripting 支持可以在项目设置中启用。本机还需要安装Python 3.9 或以上版本用于编写翻译文件处理脚本。一个可用的翻译 API或者本地翻译记忆库。示例中会保留接口占位实际调用方式按你使用的服务调整。3.4 项目结构参考建议把本地化工具脚本放在项目根目录下统一管理MyGame/ ├── Content/ │ ├── Maps/ │ ├── UI/ │ └── Localization/ ├── LocalizationTools/ │ ├── export_scene_text.py │ ├── translate_text.py │ ├── import_translated_text.py │ └── config.json └── MyGame.uprojectLocalizationTools目录存放工具脚本不参与打包。4. 核心原理拆解一个“一键翻译”工具的关键环节在写工具之前必须先想清楚几个核心问题。否则脚本写到一半很容易卡住。4.1 场景中的文本都在哪里所谓“整个游戏场景”通常包含以下几类文本关卡中放置的 Actor 组件属性例如TextRenderComponent的Text。Actor 的变量例如一个FText类型的自定义变量。关卡蓝图中的FText变量值。UMG 控件蓝图中的文本控件例如TextBlock。数据资产中引用的字符串。String Table中维护的文本条目。第一版工具建议先覆盖最常见、最刚需的部分带 TextRenderComponent 的 Actor、UMG 控件中的 FText、以及关卡蓝图中的 FText 变量。这三类基本覆盖了 80% 以上的场景文本需求。4.2 为什么要用 FText 而不是 FString这一点必须讲清楚否则工具设计会跑偏。FString是普通字符串不参与本地化。FText是 UE 的本地化文本类型可以包含命名空间Namespace和 Key支持运行时切换语言。工具的核心思路是把场景里所有FText提取出来转换成FText的命名空间 Key 源文本三要素然后生成翻译文件。翻译完成后再按三要素映射回对应的本地化资产。如果项目里大量使用了FString那说明历史包袱较大建议先制定规范逐步迁移到FText。4.3 翻译文件格式怎么选从实用角度推荐 CSV 或 JSON。这里以 JSON 为例结构清晰Python 处理方便也方便接翻译平台。[ { namespace: /Game/Maps/Level1, key: NPC_Guard_Name, source: Guard, translation: 守卫 }, { namespace: /Game/UI/HUD, key: Quest_Title_001, source: Find the Lost Sword, translation: 寻找失落之剑 } ]CSV 的好处是翻译人员可以直接用 Excel 编辑比较友好。两种格式可以互相转换这里先以 JSON 为例方便后续自动化。4.4 “导回引擎”具体是怎么导的这里需要理解 UE 本地化数据的存储方式。UE 的本地化数据最终会生成.locres文件其中包含指定 target culture 的翻译文本。项目在构建时会根据Localization Dashboard配置收集所有文本并生成.locres。但如果我们想“脚本导回”直接修改.locres是不建议的因为它是生成文件手动改会被覆盖。更合理的做法是把翻译结果写回String Table或者本地化 CSV/PO 源文件。通过 Localization Dashboard 重新收集并生成.locres。关卡中原本散落的 FText通过工具统一替换为对本地化 Key 的引用。也就是说工具需要完成“散落 FText 的引用收敛”这比单纯生成语言包更彻底也更符合“导回引擎直接用”的需求。5. 完整实战UE5 场景文本一键翻译工具下面进入核心内容。这一节会给出一个最小但完整可用的方案包含编辑器内导出收集关卡中的文本并生成 JSON。外部翻译调用翻译 API 或人工翻译生成译文 JSON。编辑器内导回根据译文 JSON 更新场景文本并写入本地化源文件。5.1 第一步启用 UE Python 支持在 UE5 编辑器中点击Edit - Project Settings - Plugins - Python Script Plugin勾选启用。同时建议启用Editor Scripting Utilities插件它提供了很多编辑器自动化能力比如加载关卡、保存关卡、遍历资产。启用后可以在编辑器底部打开Output Log切换到 Python 模式这样可以直接执行 Python 脚本也可以写.py文件后通过py 脚本路径执行。5.2 第二步编写场景文本导出脚本新建文件LocalizationTools/export_scene_text.py。这个脚本的核心逻辑是接收一个关卡路径参数。打开该关卡。遍历关卡中的所有 Actor。收集TextRenderComponent和 Actor 的FText变量。生成 JSON 文件到LocalizationTools/output/目录。# 文件路径LocalizationTools/export_scene_text.py import unreal import json import os unreal.uclass() class SceneTextExporter(unreal.EditorUtilityObject): pass def export_scene_text(level_path: str, output_path: str): 从指定关卡中导出所有可翻译文本到 JSON 文件。 注意运行该脚本前请确保关卡已保存。 if not unreal.EditorAssetLibrary.does_asset_exist(level_path): unreal.log_warning([SceneText] 关卡不存在: {}.format(level_path)) return False # 打开关卡 unreal.EditorLevelLibrary.load_level(level_path) # 获取当前关卡的 Actor 列表 actors unreal.EditorLevelLibrary.get_all_level_actors() text_items [] for actor in actors: if actor is None: continue # 获取 actor 的 display label actor_name actor.get_actor_label() # 1. 检查 TextRenderComponent text_component actor.get_component_by_class(unreal.TextRenderComponent) if text_component is not None: text_value text_component.get_editor_property(text) if text_value and not text_value.is_empty(): text_items.append({ namespace: /Game/Maps/{}.format(os.path.basename(level_path)), key: {}_TextRender.format(actor_name), source: str(text_value), translation: }) # 2. 遍历 actor 的属性找 FText 类型变量 # 这里只做演示实际应通过反射遍历更完整的属性列表 # 此处依赖引擎版本属性名需要根据项目调整 try: # TextRenderComponent 已经处理过的就不再重复 pass except Exception as e: unreal.log_warning([SceneText] 处理 Actor {} 时出错: {}.format(actor_name, str(e))) if not text_items: unreal.log([SceneText] 未找到可导出文本) return False os.makedirs(os.path.dirname(output_path), exist_okTrue) with open(output_path, w, encodingutf-8) as f: json.dump(text_items, f, ensure_asciiFalse, indent2) unreal.log([SceneText] 导出完成共 {} 条文本输出到 {}.format(len(text_items), output_path)) return True if __name__ __main__: # 在编辑器 Python 环境中使用 export_scene_text(/Game/Maps/MyLevel, LocalizationTools/output/scene_text.json)注意这个脚本是演示性质。实际项目中Actor 上的FText变量可能分布在各种组件和子对象中你需要根据项目的实际情况遍历更深的属性树。另外运行时执行前必须保存关卡并做好版本管理避免误操作导致场景内容丢失。5.3 第三步翻译处理翻译有两种常见方式。一种是通过翻译平台 API比如人工翻译平台的POST /translate接口或者直接用大模型/机器翻译接口。这里写一个通用的调用函数实际接口地址和鉴权参数需要按你的服务调整。# 文件路径LocalizationTools/translate_text.py import json import requests def call_translate_api(text: str, target_lang: str zh-CN) - str: 调用翻译服务的通用占位实现。 请替换为你实际使用的翻译服务地址与鉴权方式。 api_url https://your-translate-service.example.com/translate headers { Authorization: Bearer YOUR_API_KEY, Content-Type: application/json } payload { text: text, target_lang: target_lang } resp requests.post(api_url, jsonpayload, headersheaders, timeout10) resp.raise_for_status() data resp.json() # 假设接口返回 {translation: xxx} return data[translation] def translate_json_file(input_path: str, output_path: str, target_lang: str zh-CN): 读取导出 JSON逐条调用翻译接口生成译文 JSON。 with open(input_path, r, encodingutf-8) as f: items json.load(f) translated_count 0 for item in items: source item.get(source, ) if not source: continue try: # 实际生产建议批量翻译、错误重试、缓存机制 item[translation] call_translate_api(source, target_lang) translated_count 1 except Exception as e: print(f[Translate] 翻译失败: {source}, error: {e}) item[translation] source # 失败时保留原文 with open(output_path, w, encodingutf-8) as f: json.dump(items, f, ensure_asciiFalse, indent2) print(f[Translate] 完成成功翻译 {translated_count} 条输出到 {output_path}) if __name__ __main__: translate_json_file(output/scene_text.json, output/scene_text_zh.json, zh-CN)如果你使用人工翻译不需要这个脚本直接把导出的 JSON 或 CSV 发给翻译人员收回后再进行下一步。5.4 第四步编写导回引擎脚本这一步是整个工具的核心也最容易踩坑。import_translated_text.py要做几件事读取译文 JSON。重新打开目标关卡。遍历所有 Actor根据 Key 匹配场景中的文本组件。把译文写回TextRenderComponent的Text或者更新为本地化 Key 引用。保存关卡。这里演示两种导回方式方式 A直接修改组件文本为译文。这种方式适合单语言脚本化替换。方式 B写入本地化源文件CSV/PO再把组件改为引用本地化 Key。这种方式适合多语言项目。# 文件路径LocalizationTools/import_translated_text.py import unreal import json def import_text_to_level(level_path: str, translation_json_path: str, apply_localization_key: bool False): 将翻译 JSON 导回关卡。 apply_localization_key 为 True 时会把组件文本替换为本地化 Key 引用 为 False 时直接写入译文文本。 with open(translation_json_path, r, encodingutf-8) as f: items json.load(f) # 建立 key - translation 映射 trans_map {} for item in items: trans_map[item[key]] item[translation] unreal.EditorLevelLibrary.load_level(level_path) actors unreal.EditorLevelLibrary.get_all_level_actors() update_count 0 for actor in actors: if actor is None: continue actor_name actor.get_actor_label() text_component actor.get_component_by_class(unreal.TextRenderComponent) if text_component is not None: key {}_TextRender.format(actor_name) if key in trans_map: translation trans_map[key] if apply_localization_key: # 方式 B设置本地化 Key 引用 # 注意这里需要你在 Localization Dashboard 中配置好命名空间 # 并且 key 必须与本地化源文件中的 key 一致 text_component.set_editor_property(text, unreal.TextLib.create_localized_text(key, /Game/Maps/{}.format(level_path.split(/)[-1]))) unreal.log([Import] 更新 Actor {} 为本地化 Key {}.format(actor_name, key)) else: # 方式 A直接写译文 text_component.set_editor_property(text, unreal.TextLib.create_text(translation)) unreal.log([Import] 更新 Actor {} 的文本为{}.format(actor_name, translation)) update_count 1 unreal.EditorLevelLibrary.save_current_level() unreal.log([Import] 导回完成共更新 {} 条文本.format(update_count)) return update_count if __name__ __main__: import_text_to_level(/Game/Maps/MyLevel, LocalizationTools/output/scene_text_zh.json, apply_localization_keyFalse)注意TextLib只在这个伪代码中代表 UE Python 中创建文本的工具类。不同版本的 UE Python API 中创建 FText 的入口略有差异如果你发现 API 不一致可以通过编辑器 Python 环境查看可用的unreal.Text相关方法与属性。发布脚本前一定要在目标版本上验证 API。5.5 第五步运行时验证在编辑器里打开地图点击 Play你会看到直接写译文时场景里的 NPC 名字、标牌、任务提示都会变成中文。使用本地化 Key 时需要在项目设置里把目标语言切到中文或者通过Internationalization的设置预览中文。具体操作路径是Project Settings - Languages - Preview Game Language选择中文编辑器 UI 和场景 PIE 都会按中文语言重新加载本地化资源。如果你在 PIE 中看不到效果优先检查本地化是否为该语言生成了.locres文件。场景组件的Text是否被硬编码值覆盖。项目是否启用了对应语言的本地化。6. 进阶如何把“整个项目”的场景一起翻译上面演示的是单个关卡。实际项目往往有几十个关卡手工逐个跑显然不符合“一键翻译”的定位。因此我们需要把流程扩展为“批量处理整个项目”。思路如下扫描Content/Maps目录下所有.umap资产。遍历每个关卡导出文本到同一个 JSON。翻译完成后再遍历所有关卡导回。以下脚本是批量导出的核心部分# 文件路径LocalizationTools/export_all_maps.py import unreal import os from export_scene_text import export_scene_text ALL_MAPS_ASSET_PATH /Game/Maps OUTPUT_DIR LocalizationTools/output def export_all_maps(): asset_registry unreal.AssetRegistryHelpers.get_asset_registry() all_assets asset_registry.get_assets_by_path(ALL_MAPS_ASSET_PATH, recursiveTrue) all_text_items [] for asset_data in all_assets: asset_name asset_data.asset_name package_name asset_data.package_name if not asset_name.endswith(_MAP) and not asset_data.asset_class World: continue unreal.log([ExportAll] 处理关卡: {}.format(package_name)) # 重新使用单关卡导出逻辑但这里为了简单直接调用 # 你需要修改 export_scene_text 把结果追加到同一个列表 # 这里只做演示 export_scene_text(package_name, os.path.join(OUTPUT_DIR, {}_text.json.format(asset_name))) unreal.log([ExportAll] 所有关卡导出完成) if __name__ __main__: export_all_maps()这里有个工程问题不同关卡可能存在相同的 Actor 名称如果直接以 Actor 名作为 Key会冲突。所以实际生产级工具建议 Key 生成规则包含关卡路径Actor 唯一 ID 或资产路径组件路径例如/Game/Maps/Town_01::NPC_Guard::TextRenderComponent这样导回时才能精确定位到某个组件。7. 常见问题与排查思路在实际使用这个流程时我整理了下面这些高频问题建议直接收藏备用。问题现象常见原因解决思路导出后 JSON 文本为空Actor 上没有 TextRenderComponent或使用了其他文本组件扩展脚本遍历更多组件类型导回时找不到对应的 ActorActor 名称在关卡保存后被引擎重命名使用 Actor 的get_path_name()或 GUID 作为唯一标识中文导回后显示乱码JSON 编码不是 UTF-8或引擎不识别 BOMPython 写入时使用encodingutf-8必要时检查.locres生成配置多个 Actor 同名导致 Key 冲突以 Actor 名作为唯一 Key 所致改用完整路径作为 KeyPIE 运行时仍是英文没有生成对应语言的.locres或预览语言没有切换在 Localization Dashboard 中生成 target culture并在 Project Settings 中切换预览语言翻译 API 频繁超时逐条调用太快增加批量接口或加入线程池与重试修改后关卡无法打开脚本更新属性时出错引擎崩溃开发时开启每日自动保存或用版本控制工具及时回滚7.1 一个容易被忽略的坑编辑器脚本也会受加载状态影响UE Python 引擎脚本运行时关卡必须是“已加载”状态。如果你从批量列表遍历到某个未加载的关卡一定要先load_level再操作 Actor。另外操作完后保存当前关卡否则直接切换到下一个关卡前面的修改会丢失。7.2 另一个坑直接操作 FText 的坑在 UE Python 中直接给一个TextRenderComponent的text属性赋值字符串通常是不行的。需要把字符串包装成FText。在 UE Python 环境中你可能会看到text_component.set_editor_property(text, unreal.TextHelper.create_text(hello))不同的 UE 版本 API 差异比较大一定要在编辑器里执行下面这行确认可用函数print([x for x in dir(unreal) if Text in x])根据输出结果选择适合你版本的 API。8. 最佳实践与工程建议工具能跑通是一回事能在项目里长期稳定使用是另一回事。下面这些建议来自项目落地后的总结参考价值很高。8.1 建立统一的文本规范所有需要本地化的文本必须使用FText。禁止在关卡蓝图里硬编码用户可见字符串。文本尽量收口到String Table或集中数据资产中。如果项目已经存在大量硬编码字符串可以把这个问题加入技术债列表有空就逐步迁移。8.2 使用版本控制与备份一键导入导出工具虽然方便但误操作风险也高。脚本只要写错一个属性名就可能把整个关卡的文本覆盖掉。所以强烈建议每次运行导回脚本前确保关卡已经提交到版本控制。脚本中加入“预览模式”只输出日志不实际修改资产确认无误后再执行“写入模式”。或者使用 UE 的Transaction机制让脚本操作可以 Undo。8.3 翻译质量与人工校对机器翻译的速度很快但游戏文本的特殊性在于带有变量占位符例如{PlayerName}翻译后不能丢失。受字数限制UI 控件显示区域有限译文过长需要调整。上下文很重要单独一句话可能翻译错误。因此建议工具链中加入术语表和上下文截图。条件允许的话把翻译 JSON 里同时保存源文本所在场景的截图或 Actor 路径帮助翻译人员理解上下文。8.4 翻译文件的版本管理翻译文件应当纳入版本控制。推荐采用源语言 JSON / CSV 入库。翻译后 JSON 单独命名例如scene_text_zh-CN.json。每次版本发布前生成一份带版本号的翻译归档。8.5 增量导出项目迭代到后期每次全量导出几千条文本会很浪费时间。所以工具最好支持增量导出记录每个文本的LastModified时间戳。只导出新增和修改的条目。翻译完成后只更新变更部分。这能大幅提高翻译工作流的效率。9. 扩展思路不只是文本还有语音和本地化资产场景一键翻译的思路其实可以扩展到更多本地化内容过场动画中的字幕文件.srt或.vtt。音频文件名与字幕的映射。图片中的文字比如海报、路牌贴图需要配合 AI 抠图 OCR 生成多语言贴图。打包后的.pak文件校验和多语言分包。如果你已经掌握了基础的文本导出/导回能力下一步可以尝试把这些内容也整合进同一套工具链中。这样游戏本地化就能真正实现“从关卡到发布的一条龙”。在当前这个阶段建议先扎实地把文本导出、翻译、导回这条主流程跑通期间会积累很多对引擎资产结构的感性认识这些经验对后续做语音本地化、资源本地化都会有很大的帮助。编写编辑器自动化脚本时别怕踩坑。UE 的 Python API 虽然有文档但细节问题还是得靠实际的print和输出日志来排查。每解决一个坑工具链就会成熟一截后续维护起来也就越顺手。
返回列表