1. 项目概述:为什么我们需要“自动汉化”?
做独立游戏开发或者接手海外项目移植的朋友,应该都遇到过本地化这个“老大难”问题。尤其是面对文本量巨大的RPG、AVG或者模拟经营类游戏,手动翻译不仅耗时耗力,而且容易出错,后期维护更是噩梦。传统的汉化流程,要么是策划在Excel里一条条改,要么是程序员硬编码,文本一变动就得重新编译,效率极低。
“自动翻译”这个概念,听起来像是黑科技,其实核心思路很简单: 将游戏内的文本资源(如UI、对话、物品描述)与翻译流程解耦,通过脚本或工具链,实现从提取、翻译到回填的自动化 。这不仅能将翻译工作从开发流程中剥离,交给更专业的本地化团队(甚至机器翻译初筛),还能实现多语言的热更新——玩家在游戏内切换语言,资源即时生效,无需重新下载安装包。
我最近在将一个Steam上的小型独立游戏进行中文社区适配时,就深度实践了这套方法。项目基于Unity 2021.3 LTS,文本分散在预制体、ScriptableObject和代码字符串中。手动处理几乎不可能,于是折腾出了一套“三步走”的自动化方案。它不依赖任何昂贵的商业插件,核心是利用Unity自身的资源管理接口和一些开源工具,构建一个轻量但高效的汉化管线。下面,我就把这套踩过不少坑才总结出来的实战经验,毫无保留地分享给你。
2. 整体设计与核心思路拆解
在动手之前,我们必须明确目标:不是做一个“万能”的汉化工具,而是针对 特定项目 ,建立一个 可靠、可重复、对开发者友好 的自动化流程。我们的设计需要围绕以下几个核心原则展开:
2.1 原则一:非侵入式设计
汉化系统不应该深度耦合游戏核心逻辑。理想状态下,游戏代码中不应该出现 if (language == “Chinese”) 这样的硬编码。我们应该通过一个 本地化键(Localization Key) 系统来解耦。游戏运行时,所有需要显示的文本都通过一个唯一的键值(如 ”UI_MainMenu_StartGame” )向本地化管理器请求,管理器根据当前语言设置返回对应的翻译文本。这样,文本内容的变更完全不影响游戏逻辑代码。
2.2 原则二:资源与逻辑分离
所有翻译文本应该作为外部资源(如JSON、CSV、ScriptableObject资产)存在,而不是写在C#脚本的字符串里。这有两个巨大好处:一是便于翻译人员使用专业工具(如Poedit、Excel)进行处理,他们甚至不需要打开Unity编辑器;二是支持热重载,在编辑器播放模式或某些运行时环境下,可以动态加载新的翻译文件,立刻看到效果。
2.3 原则三:自动化管线
这是“自动翻译”的精髓。我们的管线应该包含三个核心环节,也对应了标题中的“三步”:
- 提取(Extract) :自动扫描项目中的所有文本资源,生成一份包含所有唯一键和源文本(如英文)的清单文件。
- 翻译(Translate) :将这份清单文件交给翻译环节。这里可以是人工翻译,也可以是调用机器翻译API(如Google Cloud Translation, DeepL)进行批量初翻,再由人工校对,效率倍增。
- 注入(Inject) :将翻译好的文本文件,自动映射回游戏资源中,替换或关联对应的显示组件。
2.4 技术选型考量
为什么不直接用Asset Store里成熟的本地化插件(如I2 Localization, Lean Localization)?对于中小项目或快速原型,它们非常优秀。但当我们追求深度定制、与特定CI/CD流程集成、或需要处理非常规文本资源时,自己搭建管线更有优势。我们的方案基于:
- Unity Editor Scripting :用于编写提取和注入资源的自定义编辑器工具。
- JSON/CSV :作为中间翻译文件的格式,通用且易处理。
- C#的
JsonUtility或Newtonsoft.Json:用于序列化和反序列化翻译数据。 - (可选)机器翻译API :用于自动化初翻,节省大量时间。
3. 第一步:精准提取游戏内所有文本
这是整个流程的基石,如果提取不全或有遗漏,后续步骤都是空中楼阁。文本可能藏在各种角落:
- UI Text / TextMeshPro (TMP) :这是最明显的,
Text组件的text属性,TMP_Text组件的text属性。 - Inspector中的字符串字段 :自定义ScriptableObject或MonoBehaviour中声明为
public string或[SerializeField] private string的字段,可能用于配置物品名称、技能描述等。 - 代码中的字符串字面量 :比如
Debug.Log(“Loading…”);或者button.onClick.AddListener(() => ShowDialog(“Are you sure?”));中的字符串。严格来说,这些也应该被本地化,但提取难度较大,通常建议在架构上就避免在逻辑代码中写死显示文本。
我们的提取工具需要遍历所有相关资源。
3.1 编写资源扫描编辑器工具
我们在Unity中创建一个Editor文件夹,在里面编写一个 TextExtractorWindow 类。
using UnityEngine;
using UnityEditor;
using System.IO;
using System.Collections.Generic;
using TMPro;
public class TextExtractorWindow : EditorWindow
{
[MenuItem("Tools/本地化/提取所有文本")]
public static void ShowWindow()
{
GetWindow<TextExtractorWindow>("文本提取器");
}
private void OnGUI()
{
if (GUILayout.Button("扫描Prefabs和Scene中的UI文本"))
{
ExtractTextFromUI();
}
if (GUILayout.Button("扫描指定ScriptableObject类型"))
{
ExtractTextFromScriptableObjects();
}
// ... 更多扫描选项
}
private void ExtractTextFromUI()
{
// 1. 查找所有Prefab
string[] prefabGuids = AssetDatabase.FindAssets("t:Prefab");
List<TextAssetData> textDataList = new List<TextAssetData>();
foreach (string guid in prefabGuids)
{
string path = AssetDatabase.GUIDToAssetPath(guid);
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(path);
// 使用EditorUtility.CollectDependencies来深度遍历,但这里简单起见,直接实例化到临时场景(需小心)
// 更稳妥的方法:使用PrefabUtility.LoadPrefabContents
var prefabContents = PrefabUtility.LoadPrefabContents(path);
ExtractTextFromGameObject(prefabContents, textDataList, path);
PrefabUtility.UnloadPrefabContents(prefabCon


348

被折叠的 条评论
为什么被折叠?



