做游戏本地化的人基本都懂一句话:把一款游戏翻译成多语言,最耗时间的往往不是“翻译”本身,而是“把翻译好的文本放回游戏里”。这句话听着有点反直觉,但如果你在 Unity 或 Unreal 引擎里逐场景找过 UI 文字、对话、道具名,再对照本地化表一条一条回填,你一定会点头。
传统流程下,策划先整理文本,外包翻译,再导回引擎,技术再挨个排查漏翻、错位、UI 溢出。一个几十万词的 RPG 项目,本地化周期奔着几个月去。现在 AI 翻译服务的质量已经过了“能看”的线,团队反而容易掉进另一个坑:模型翻译很快,可文本提取和写回仍然靠手工,翻译完导回引擎后要么 Key 对不上,要么长文本把按钮撑爆。
于是“整个游戏场景也能一键翻译?导回引擎直接用”就成了一个很值得认真讨论的问题。它要处理的不是“加一个翻译按钮”,而是一条完整的本地化管线:场景内文本提取、翻译服务接入、译文写回、引擎内即时预览。这篇文章不绑定任何现成工具,而是把这条管线的原理、落地步骤和常见的坑都讲清楚,让你在自己的 Unity / Unreal 项目里也能搭出一套,而不是被“一键”两个字误导。
1. 场景本地化的真正瓶颈:不在“翻译”,在“搬运”
很多人第一次听到“场景一键翻译”,第一反应是“用 AI 翻译不就行了吗”。如果只做到这一步,那你十有八九会在导回引擎后崩溃。原因很简单:游戏场景里的文本不是集中存放在某个文件里的,而是散落在各种组件、资源、事件、数据表中的。
以一个 Unity 场景为例。屏幕上显示的文本,可能来自场景里的 UI Text、TextMeshPro 文本,也可能来自动画事件、Timeline 字幕、挂载的 ScriptableObject 数据、Tile 名称,甚至敌人的血条名字是运行时拼接的字符串。你只翻译其中一部分,玩家打开游戏就会看到中英混杂的界面。
Unreal 里的情况也类似。虽然 Unreal 有官方的 Localization Dashboard 和 FText 体系,理论上所有 UI 文本都应该走 FText 并进入收集流程,但实际项目里总有绕过这套体系写死的字符串。更麻烦的是,FText 的收集、翻译、导入是基于编辑器工具的,你要先把所有场景文本“收集”出来,翻译完再“导回”,中间任何一步出错,都会造成漏翻或错位。
所以我把本地化的核心矛盾总结成一句话:翻译是“内容替换”,而本地化是“信息搬运”。
这两件事的复杂度完全不在一个量级。AI 可以把“你好”翻译成“Hello”,但它不知道这行文本在哪个场景、哪个按钮上、原文里有没有占位符、译文会不会长到把 UI 顶破。要解决这些问题,不能只靠模型,要靠工程。
这也是我在这篇文章里反复强调的判断:游戏场景一键翻译,真正的技术含量不在翻译引擎,而在文本提取与写回管线的稳定性。
2. 一句话理解“场景一键翻译”的原理
不管底层是用 Unity、Unreal 还是自研引擎,场景一键翻译的原理都可以拆成三层:
- 场景层:所有可见文本所在的位置,如 UI 组件、动画事件、数据资产、脚本字段。
- 映射层:一个稳定的 Key 对应一条原文。这个 Key 在场景、翻译文件、引擎资源之间保持唯一。
- 翻译层:把原文批量发送给翻译服务或人工译者,得到译文后再按 Key 写回。
如果你去看市面上各种本地化插件,本质上都是在解决这三层之间的数据流转。区别只在于:
- 提取能力强不强,能不能把所有可能藏文本的角落都扫到;
- Key 稳定不稳定,场景随便改个名字后译文还能不能对齐;
- 写回方式安不安全,是直接改场景文件,还是通过运行时本地化表替换。
这里特别要展开的是 Key 的设计。很多初学本地化的人会直接用组件 InstanceID 当 Key,这是最省事的做法,但也是最不稳的。因为 InstanceID 是 Unity 运行时给对象的临时编号,场景一旦重新打开就变了。翻译文件里存着旧 ID,重新导回时全部错位。
| Key 生成方式 | 稳定性 | 对开发流程的要求 | 适用场景 |
|---|---|---|---|
| GameObject 名称 | 一般,重名会冲突 | 要求场景内命名规范 | 小项目、原型验证 |
| 对象路径 + 组件类型 | 较高,改名/移动会失效 | 要求保持场景结构稳定 | 中大型项目兜底 |
| 人工维护语义 Key | 最高,结构变化不受影响 | 需要在开发阶段给文本配置 ID | 需要长期维护的正式项目 |
| InstanceID / GetInstanceID | 低,编辑会话结束就变 | 无,自动生成 | 只适合一次性工具脚本 |
从工程角度看,语义 Key 最可靠,比如Level1_Town.Forgemaster_Intro.001。但语义 Key 需要开发时维护,不能自动生成。比较实用的组合是:优先使用项目里已有的本地化组件 Key,没有 Key 时用“场景名 + 对象路径 + 组件类型”兜底。这样自动化程度和稳定性都能兼顾。
3. 完整管线拆解:从“场景里拿出所有文本”到“引擎内直接用”
把一键翻译理解成八个步骤,你就不会把它当成一个黑盒。
- 收集:遍历当前场景和引用的资源,找到所有需要翻译的文本。
- 去噪:排除空字符串、纯数字、调试输出、技术标识符等不需要翻译的内容。
- 生成 Key:给每一条文本分配稳定 ID,并记录它所在的场景路径和组件位置。
- 导出:输出为 JSON、CSV 等中间格式,便于翻译服务或人工译者处理。
- 翻译:批量调用 AI 或翻译 API,拿到译文。
- 校验:检查是否漏翻、占位符丢失、译文长度是否异常、编码是否损坏。
- 写回:把译文映射到引擎内对应的文本组件或本地化表。
- 预览:在编辑器里切换语言,逐场景查看效果,修复溢出和字体问题。
这八个步骤里,第 1 步和第 7 步最容易被低估。因为绝大多数文本困难和翻译事故,都不是翻译模型造成的,而是“该拿到的文本没拿到”“该写回的位置没写回”。
我见过一个项目,第一版翻译工具只处理了 UI Text,没有处理 TextMeshPro。结果导回引擎后,主界面 80% 的文本都切换过来了,但所有伤害数字、弹幕文字、排行榜条目还是中文。玩家截图吐槽,团队排查了半天才发现是组件类型漏了。
所以,你在设计提取器时,第一原则是“宁可多抓,不可漏抓”。多抓的文本可以在翻译校验时过滤掉,漏抓的文本只能发版后由玩家告诉你。
4. Unity 实战:提取场景文本的编辑器脚本
先讲 Unity 的落地方式。Unity 的 UI 系统有两种主流文本组件:UnityEngine.UI.Text 和 TextMeshPro 的 TMP_Text。Text 比较老,TMP 在新项目里更常用。下面这个编辑器脚本只提取 UGUI 的 Text,核心逻辑是通用的,你可以照着扩展 TMP。
在 Editor 文件夹下新建文件SceneTextExtractor.cs:
using System.Collections.Generic; using System.IO; using UnityEditor; using UnityEngine; using UnityEngine.UI; public static class SceneTextExtractor { private const string ExportDir = "Localization/Exports"; [MenuItem("Tools/Localization/Extract Current Scene")] public static void ExtractCurrentScene() { string scenePath = UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene().path; string sceneName = Path.GetFileNameWithoutExtension(scenePath); var entries = new List<TextEntry>(); int index = 0; // 注意:Unity 2023 之后推荐使用 FindObjectsByType<Text>(FindObjectsSortMode.None) // 老版本用 FindObjectsOfType<Text>(true) 即可,这里为了兼容给出最保守写法。 var texts = Object.FindObjectsOfType<Text>(true); foreach (var text in texts) { if (string.IsNullOrEmpty(text.text)) continue; string path = GetTransformPath(text.transform); string key = $"{sceneName}|{path}|{text.GetType().FullName}"; entries.Add(new TextEntry { key = key, scenePath = scenePath, source = text.text }); index++; } if (!Directory.Exists(ExportDir)) { Directory.CreateDirectory(ExportDir); } string outPath = Path.Combine(ExportDir, $"{sceneName}_texts.json"); var wrapper = new TextEntryWrapper { entries = entries }; string json = JsonUtility.ToJson(wrapper, true); File.WriteAllText(outPath, json, new System.Text.UTF8Encoding(true)); Debug.Log($"[SceneTextExtractor] 提取 {entries.Count} 条文本 -> {outPath}"); } private static string GetTransformPath(Transform current) { if (current.parent == null) { return current.name; } return GetTransformPath(current.parent) + "/" + current.name; } [System.Serializable] public class TextEntry { public string key; public string scenePath; public string source; } [System.Serializable] public class TextEntryWrapper { public List<TextEntry> entries = new List<TextEntry>(); } }这段代码做的事情很直观:打开场景,点击菜单Tools/Localization/Extract Current Scene,它会遍历当前场景里所有UnityEngine.UI.Text组件,把非空文本收集起来,生成一个 JSON 文件。
这里真正容易踩坑的地方是 Key 的生成。我用了“场景名 + 对象路径 + 组件类型”的组合,比 InstanceID 稳定很多。但它也有局限:如果你在场景里移动了对象,Key 会变化,译文就会对不上。所以在正式项目里,我建议引入一个LocalizationKey组件,允许策划在界面上手动指定一个稳定 Key,提取时优先读人工 Key,读不到再用路径兜底。
TextMeshPro 的提取逻辑几乎一样,只要把遍历类型换成TMP_Text,再把组件的 FullName 区分开即可。TMP 的文本在代码里是text属性,和 UGUI 一致,处理起来并不复杂。如果你项目里装了 TMP,可以复制一套逻辑,不要只处理 UI Text。
5. 翻译服务接入:批量调用与格式约定
文本提取出来之后,会得到一个 JSON 文件。接下来需要把其中的source字段批量翻译成目标语言。
这里我以 Python 脚本为例,因为它最适合做离线批处理。你可以把这一步接到任意翻译服务上,无论是商业翻译 API 还是自部署的大模型接口,只要支持 HTTP POST JSON 就行。代码里的 endpoint、鉴权字段都是占位符,以你实际使用的服务文档为准。
import json import os import requests import time EXPORT_FILE = "Localization/Exports/Level1_Town_texts.json" OUTPUT_FILE = "Localization/Imports/Level1_Town_en.json" TARGET_LANG = "en" # 从环境变量读取密钥,避免把敏感信息提交到 Git API_ENDPOINT = os.environ.get("TRANSLATE_ENDPOINT", "https://your-translation-service/translate") API_KEY = os.environ.get("TRANSLATE_API_KEY", "") def load_entries(path): with open(path, "r", encoding="utf-8-sig") as f: data = json.load(f) return data["entries"] def call_translate(texts): headers = { "Content-Type": "application/json", "Authorization": f"Bearer {API_KEY}" } payload = { "texts": texts, "target_lang": TARGET_LANG } resp = requests.post(API_ENDPOINT, json=payload, headers=headers, timeout=60) resp.raise_for_status() return resp.json()["translations"] def main(): entries = load_entries(EXPORT_FILE) texts = [e["source"] for e in entries] print(f"待翻译文本: {len(texts)} 条") # 分片调用,避免单次请求体过大 batch_size = 50 translations = [] for i in range(0, len(texts), batch_size): batch = texts[i:i + batch_size] result = call_translate(batch) translations.extend(result) time.sleep(0.2) # 温和限流,避免触发服务方限制 if len(translations) != len(entries): raise RuntimeError(f"翻译结果数量 {len(translations)} 与输入数量 {len(entries)} 不一致") output_entries = [] for entry, translated in zip(entries, translations): output_entries.append({ "key": entry["key"], "scenePath": entry["scenePath"], "source": entry["source"], "translation": translated }) os.makedirs(os.path.dirname(OUTPUT_FILE), exist_ok=True) with open(OUTPUT_FILE, "w", encoding="utf-8") as f: json.dump({"entries": output_entries}, f, ensure_ascii=False, indent=2) print(f"翻译完成,已输出: {OUTPUT_FILE}") if __name__ == "__main__": main()这个脚本之所以要和引擎流程分开,是因为翻译服务调用往往涉及网络请求、重试、限流、频率控制,放在 C# Editor 脚本里也能写,但不如 Python 方便调试。尤其是团队里有策划参与本地化时,一个独立的 Python 脚本比 “在 Unity 里配置 API Key” 更直觉。
翻译结果的格式约定也很重要。我建议每个字段都要保留key和scenePath,这是写回时定位组件的依据。如果你只导出“原文→译文”的映射,忽略 Key 和场景路径,那这个文件基本只能给人类看,没法安全地写回引擎。
另一个要强调的是编码问题。翻译文件必须统一使用 UTF-8。Windows 环境下,Excel 打开 CSV 再保存很容易改成带 BOM 的 UTF-8 或 GBK,如果你用 Python 读取 JSON,最好在open时指定encoding="utf-8-sig",这样能兼容带 BOM 和不带 BOM 两种文件。
6. 导回引擎:译文写回与本地化资源组织
翻译完成后,最关键的是安全地写回。在 Unity 里,写回有两种主流思路。
第一种是编辑器直接改场景里每个 Text 组件。这种方式最直接,但风险也最高,尤其不适合频繁重新导出的开发流程。因为你每次改动都需要重新切换语言、重新导回,会把本地化流程拖得很慢。
第二种是把译文做成运行时字符串表,通过一个本地化组件在运行时替换文本。这种方式不污染场景源文件,回滚容易,也符合 Unity Localization、Unreal Localization 这类系统的设计思路。
为了让你看得更清楚,这里给出第一种方式的编辑器写回脚本。它的逻辑很朴素:读取翻译文件,按照 Key 在当前场景里找到对应的 Text 组件,替换文本。
在 Editor 文件夹下新建SceneTextWriter.cs:
using System.Collections.Generic; using System.IO; using UnityEditor; using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.UI; public static class SceneTextWriter { private const string ImportDir = "Localization/Imports"; [MenuItem("Tools/Localization/Apply Translations To Current Scene")] public static void ApplyTranslations() { string scenePath = EditorSceneManager.GetActiveScene().path; string sceneName = Path.GetFileNameWithoutExtension(scenePath); string filePath = Path.Combine(ImportDir, $"{sceneName}_en.json"); if (!File.Exists(filePath)) { Debug.LogError($"[SceneTextWriter] 未找到译文文件: {filePath}"); return; } string json = File.ReadAllText(filePath, new System.Text.UTF8Encoding(true)); var wrapper = JsonUtility.FromJson<TranslationWrapper>(json); if (wrapper == null || wrapper.entries == null || wrapper.entries.Count == 0) { Debug.LogError("[SceneTextWriter] 译文文件格式不正确或内容为空"); return; } // 构建 key -> translation 映射 var map = new Dictionary<string, string>(); foreach (var entry in wrapper.entries) { map[entry.key] = entry.translation; } var texts = Object.FindObjectsOfType<Text>(true); int appliedCount = 0; foreach (var text in texts) { string path = GetTransformPath(text.transform); string key = $"{sceneName}|{path}|{text.GetType().FullName}"; if (map.TryGetValue(key, out string translated)) { text.text = translated; appliedCount++; } } EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene()); Debug.Log($"[SceneTextWriter] 已应用 {appliedCount} 条译文,请检查场景并保存"); } private static string GetTransformPath(Transform current) { if (current.parent == null) { return current.name; } return GetTransformPath(current.parent) + "/" + current.name; } [System.Serializable] public class TranslationEntry { public string key; public string scenePath; public string source; public string translation; } [System.Serializable] public class TranslationWrapper { public List<TranslationEntry> entries = new List<TranslationEntry>(); } }如果你用的是 TextMeshPro,需要把Object.FindObjectsOfType<Text>(true)换成Object.FindObjectsOfType<TMP_Text>(true)。但注意,如果项目没有引入 TMP 包,这段代码会编译报错。所以我建议在实际项目中用#if预处理指令把两种组件的处理逻辑分开,或者把 TMP 相关的代码单独放到带宏的脚本里。
Unreal 的“导回”思路与 Unity 不同。Unreal 官方推荐的是 Localization Dashboard,它的核心步骤是 Gather、Translate、Export/Import。
- Gather:让编辑器扫描工程和场景中所有 FText,生成翻译源文件。
- Translate:在 Dashboard 里逐条翻译,或导出 Portable Object(.po)文件给外部翻译。
- Import:把翻译后的文件导回 Dashboard。
- Preview:在编辑器里切换 Preview Language,查看场景中的语言效果。
因此在 Unreal 项目里做“场景一键翻译”,更稳妥的路径不是自己写一个遍历场景、直接修改 FText 的插件,而是利用 Localization Dashboard 的收集能力导出文件,再用 AI 批量翻译后导入。理论上你也可以通过反射遍历场景里的 FText 属性来自动提取,但官方管线已经解决了收集和写回的问题,开发者把精力花在翻译质量和流程校验上更划算。
Localization Dashboard 推荐工作流: 1. 配置要收集的目标场景/关卡和插件目录 2. 点击 Gather Text 收集所有 FText 3. 导出 .po 文件或 JSON 4. 用翻译脚本批量翻译 5. 导入翻译结果 6. Preview Language 实时检查场景这不是偷懒,而是“别重复造引擎已经造好的轮子”。Unreal 的 FText 和 Localization Dashboard 本来就是为这个问题设计的,你只需要把翻译环节替换成 AI 批量翻译。
7. 运行验证:如何确认场景翻译没漏、没崩
写完工具后,最难的不是“跑通”,而是“确认真的翻译完整了”。我推荐从四个维度做自动化校验。
第一是数量校验。提取阶段如果有 1200 条文本,翻译返回时也必须返回 1200 条。任何数量不一致,都说明提取过程或翻译过程有遗漏。不要只看日志里“写回成功 1200 条”,要对比提取时的原始数量。
第二是空白文本校验。有些翻译服务会把包含占位符或特殊符号的文本当成“无需翻译”返回空字符串。如果你的脚本没有校验,空字符串会直接覆盖原文。
第三是长度校验。英文译成中文、中文译成英文,长度比例经常超过 200%。如果一个按钮的原文是“确定”,只有两个字,译文变成“Are you sure you want to continue”,UI 必然会溢出。这里可以用一个简单的 Python 脚本扫出可疑内容:
import json import sys def check_length(input_path, max_ratio=1.5): with open(input_path, "r", encoding="utf-8-sig") as f: data = json.load(f) for e in data["entries"]: source = e.get("source", "") translation = e.get("translation", "") if len(source) == 0: continue ratio = len(translation) / len(source) if ratio > max_ratio: print(f"[LENGTH_WARN] {e['key']} 原文长度为 {len(source)},译文长度为 {len(translation)},比值 {ratio:.2f}") check_length("Localization/Imports/Level1_Town_en.json")第四是占位符校验。游戏文本里经常包含{0}、{playerName}、%s这类占位符。AI 翻译有时会改写成{零}或直接删掉,运行时轻则显示混乱,重则直接崩溃。校验脚本里可以加一段正则提取占位符,对比原文和译文中包含的占位符集合是否一致。
引擎内的预览验证同样重要。Unity 项目里,如果使用运行时字符串表的方式,可以加一个类似 “Language = English” 的启动参数或配置项,快速切语言。Unreal 打开 Localization Dashboard 的 Preview Language 后,能直接在关卡视口中切换语言预览,这是最直观的确认方式。真正发版前,还应该做一次完整流程走查,把每个主要场景打开,快速扫一遍界面和对话,重点看三样东西:有没有漏翻、有没有 UI 溢出、有没有字体缺字。
8. 常见问题与排查清单
本地化管线的问题往往不是单个点引起的。下面这张表总结了实际项目里出现频率最高的几个状况。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 部分文本没有切换语言 | 提取器只处理了 UGUI Text,没有处理 TMP 或动画事件文本 | 检查导出 JSON 中是否包含对应文本 | 扩展提取器,覆盖 TMP、Timeline、ScriptableObject 等文本来源 |
| 翻译后 Key 对不上,文本串位 | 用了 InstanceID 或场景对象名作为 Key,场景结构调整后 Key 变化 | 导出两份 JSON,对比 Key 差异 | 改用对象路径 + 组件类型,正式项目引入人工语义 Key |
| 中文字体正常,英文或日文变成方块 | 项目缺少目标语言字体或字体动态子集 | 在编辑器切语言查看字体渲染 | 接入动态字体或为多语言配置字体 fallback |
| 译文长度导致按钮溢出 | 翻译没做长度校验,UI 自适应关闭 | 用长度校验脚本扫描异常比值 | 开启 Overflow 处理、根据语言调整字号,或对超长文本走多行副本 |
| CSV/JSON 导入后出现乱码 | 文件编码不是 UTF-8,或 Excel 二次保存后变成 GBK | 用编辑器打开文件看编码 | 统一使用 UTF-8 with BOM,避免 Excel 直接编辑工程文件 |
| 写回后原文本被空字符串覆盖 | 翻译服务对特殊文本返回空白 | 做空白校验,拒绝写入空译文 | 在写回脚本里增加空字符串检查和日志告警 |
| Unreal 收集不到场景文本 | 文本写在硬编码字符串中,没有使用 FText | 检查本地化收集日志 | 把硬编码字符串改为 FText,并把相关模块加入收集范围 |
| 脚本调用翻译接口超时 | 一次性提交大量文本,或网络代理问题 | 查看翻译服务端日志和响应时长 | 分片提交,增加超时时间和重试机制 |
这里特别强调一下“硬编码字符串”的问题。很多团队喜欢把临时文案直接写在代码里,觉得省事。这些文本不进提取器,不参与翻译,也不会出现在本地化 Dashboard 中。对于小项目,这可能是效率选择;对于要做多语言发行的项目,这是后期最大的技术债。你可以在工程里加几条规范:所有给玩家看的字符串必须走本地化组件或资源,代码里只保留日志和技术文案。
9. 工程化建议:让“一键翻译”在真实项目里可用
把演示脚本跑通只是第一步。想要在正式项目里每天用、每周用,还需要做工程化改造。我把最重要的几条建议列出来。
9.1 文本尽量集中,不要散落
能放到配置表、DataTable、ScriptableObject 里的文本,就不要散落在场景组件上。集中的文本天然好提取、好翻译、好写回。场景里只保留 UI 布局,文本内容通过引用键获取,这是长期一劳永逸的做法。
9.2 Key 是本地化的生命线
Key 的命名建议用“模块 + 关卡 + 简短语义”的方式,比如Level1_BossIntro.001。Key 一旦生成,尽量不要改动。如果必须改,要通过工具批量重写翻译文件,而不是手动在 CSV 里改。把 Key 的变动当成代码重构来对待,每次变动都要 diff 和 review。
9.3 翻译过程要支持“上下文注入”
纯文本翻译很容易出现“同一个英文单词,在不同的 UI 位置该翻成不同中文”的情况。AAI 和普通翻译 API 只给你一句话,不会告诉你这句话是按钮、弹窗还是 NPC 台词。好的做法是在导出给翻译服务的 JSON 里增加context字段,比如context: "UI_Button"或context: "NPC_Dialog",让翻译模型能区分语境。
9.4 自动化要接入 CI,而不是依赖人工点按钮
每次场景改动后,让 CI 自动跑一次文本提取,对比上次导出结果,把新增文本和变更文本单独生成翻译任务。这样团队不需要每周手动导一次,漏翻概率也会明显下降。翻译结果合入后,CI 再跑一轮校验,阻止数量不一致或空译文进入主线。
9.5 安全与合规要提前设计
调用在线翻译服务,等于会把游戏文案发到外部接口。所以大规模接入前要确认内容安全性,避免把玩家输入的内容、隐私信息、未发布的剧情敏感内容直接发给第三方。API Key 不要硬编码进工程,优先放环境变量或服务器端代理。如果游戏面向未成年人,还要考虑文本输出和译文的合规审查。
9.6 留好回滚路径,写回前先备份
无论是 Unity 编辑器直接改场景,还是 Unreal 导入翻译文件,都应该提前确认改动可回滚。Unity 的.unity文件本身是文本格式,可以被版本控制,但如果你在未提交的状态下直接大范围写回,出问题时恢复会很痛苦。推荐做法是:写回前先确认工作区干净,或者让工具自动生成一个备份文件。
10. 结尾:别把“一键”当成魔法按钮
回到标题的问题:整个游戏场景也能一键翻译,导回引擎直接用吗?
从能力上看,能。只要把提取、翻译、写回这条管线打通,再配合引擎的本地化预览,一次处理几千条场景文本已经不是问题。但从工程上看,真正决定这套流程好不好用的,不是那一下点击,而是你花了多少精力处理文本来源、Key 稳定性和校验。
这套方案的性价比非常清晰。对于独立开发者和中小团队,几天就能搭出最小可用版本:一个提取脚本,一个翻译脚本,一个写回脚本,再加一个长度校验工具,就能把原本几周的本地化周期压缩到几小时。对于大型项目,这套思路仍然适用,只是需要更强的人工 LQA 流程和上下文管理。
如果你正在准备做多语言版本,我的建议是不要直接去找“哪个翻译工具最好”,而是先盘点你的场景里到底有多少种文本来源,再决定用引擎原生本地化方案还是自研管线。想清楚这几件事,“一键翻译”才不会变成“一键翻车”。建议收藏这篇文章,等真正搭管线的时候对照着踩坑清单来做。