Unity游戏本地化自动化:基于Google翻译API与Localization包的实战方案
2026/7/27 3:47:18 网站建设 项目流程

1. 项目概述:为什么Unity本地化需要“自动”方案?

做Unity游戏开发的朋友,尤其是面向全球市场的独立开发者或小团队,应该都对本地化(Localization)这件事又爱又恨。爱的是,它能帮你打开新市场,让游戏触及全球玩家;恨的是,这个过程往往繁琐、耗时,且容易出错。传统的本地化流程是什么样?策划或文案把文本整理成Excel表格,发给翻译公司或社区志愿者,等翻译文件回来,再手动导入到Unity项目中,为每个语种配置对应的文本资源。这中间任何一个环节出问题——比如表格格式变了、ID对不上、翻译有歧义——都会导致游戏里出现“Missing Translation”或者更糟的,直接报错。

所以,当看到“自动翻译与本地化解决方案”时,很多开发者的眼睛会亮起来。这不仅仅是“省事”,更是对开发流程的一次革命性优化。它意味着,你可以将游戏内的文本资源(UI、对话、物品描述等)与一个智能的翻译管道连接起来,实现近乎实时的翻译、导入和测试。这对于需要频繁更新内容、进行A/B测试,或者希望快速将游戏推向多语言市场的团队来说,价值巨大。本指南要解决的,就是如何用一套清晰、可靠的步骤,在Unity项目中搭建这样一个自动化流程,让你从繁琐的重复劳动中解放出来,把精力集中在游戏本身。

2. 核心思路与方案选型:构建翻译“流水线”

在动手配置之前,我们必须先想清楚整个自动化流程的架构。一个完整的自动本地化解决方案,本质上是一条从“源文本”到“多语言游戏包”的流水线。这条流水线需要几个核心组件协同工作:

  1. 文本收集与管理系统:如何高效地管理游戏中的所有待翻译字符串?是继续用Excel,还是用更专业的工具?
  2. 翻译引擎:谁来执行翻译?是免费的机器翻译API,还是付费的专业翻译服务,或者是两者结合(先机翻,后人工润色)?
  3. Unity集成层:翻译好的文本如何无缝、自动地进入Unity项目,并关联到正确的UI组件或游戏对象?
  4. 测试与验证流程:如何快速检查翻译结果在游戏中的实际显示效果,避免文本溢出、字体缺失或格式错误?

基于这些考量,目前社区和商业实践中,主要有两种主流思路:

思路一:基于专业本地化管理平台(如Localizatron, Lokalise, Crowdin)这类平台提供了端到端的解决方案。你只需在Unity中安装其插件,将需要本地化的文本标记出来,插件会自动将文本上传到平台的云端工作区。在平台上,你可以邀请翻译者协作,使用集成的机器翻译(如Google Translate, DeepL)进行初翻,然后进行人工审核。审核完成后,平台会自动将翻译好的资源包(如AssetBundle或直接生成LocalizationTable)推送回Unity项目,甚至可以直接触发构建。这种方案省心、功能强大,尤其适合团队协作,但通常需要付费订阅。

思路二:基于API的自建流水线(更灵活、可控)这也是本指南将重点阐述的方案。其核心是:利用Unity的本地化组件(如Localization包)管理文本资源,然后编写编辑器脚本,调用第三方翻译API(如Google Cloud Translation API, DeepL API,甚至是开源的离线翻译模型)进行批量翻译,并自动填充到本地化表格中。这种方案的优势是完全自主可控,成本灵活(按API调用量计费),可以深度定制流程,例如与你的版本控制系统(Git)或持续集成/持续部署(CI/CD)管道结合。

对于大多数中小型项目或追求极致控制的开发者,我推荐从思路二入手。它不仅能让你透彻理解整个流程的每一个环节,还能根据项目需求进行最灵活的调整。接下来,我们将以Unity官方推荐的Localization包为核心,搭配Google Cloud Translation API,来搭建这条自动化流水线。

注意:选择Google Cloud Translation API是因为其准确性、语言覆盖广且文档完善。你也可以替换为DeepL API(对欧洲语言质量极高)或微软Azure Translator。如果对数据隐私有极高要求,可以考虑在本地部署开源大模型(如基于transformers库的翻译模型),但这会带来额外的部署和性能成本。

3. 环境准备与核心工具安装

工欲善其事,必先利其器。在开始自动化配置前,我们需要确保Unity项目和开发环境已经装备齐全。

3.1 Unity项目与Localization包配置

首先,你需要一个Unity项目(建议使用2020 LTS或更新版本)。我们将使用Unity官方提供的Localization包,这是目前功能最全面、集成度最高的本地化解决方案。

  1. 打开Package Manager:在Unity编辑器中,点击Window->Package Manager
  2. 添加官方注册表:点击左上角的“+”号,选择“Add package from git URL...”。如果看不到官方包,请确保在Package Manager窗口左上角的下拉菜单中选择了“Unity Registry”。
  3. 安装Localization包:在搜索框中输入“localization”,找到名为“Localization”的包(由Unity Technologies发布),点击“Install”。这个包提供了管理字符串、资产(如图片、音频)本地化的全套工具。
  4. 初始化本地化设置:安装完成后,Unity可能会提示你初始化设置。如果没有,你可以通过Window->Asset Management->Localization Tables打开本地化表格编辑器。首次打开时,系统会引导你创建本地化设置资产(LocalizationSettings),并让你添加支持的语言(例如,英语en、简体中文zh-CN、日语ja)。

3.2 翻译API服务申请与配置

我们将使用Google Cloud Translation API。你需要一个Google Cloud Platform(GCP)账号。

  1. 创建GCP项目:访问 Google Cloud Console ,创建一个新项目(例如MyGame-Localization)。
  2. 启用Translation API:在控制台侧边栏找到“API和服务” -> “库”,搜索“Cloud Translation API”并启用它。
  3. 创建服务账号密钥:这是安全调用API的关键。进入“API和服务” -> “凭据”,点击“创建凭据” -> “服务账号”。创建一个服务账号(如unity-translator),并赋予它“Cloud Translation API User”角色。创建完成后,在该服务账号的“密钥”选项卡中,选择“添加密钥” -> “创建新密钥”,格式选择JSON。下载生成的JSON密钥文件,并妥善保管,不要提交到版本库。
  4. 记下项目ID:在GCP控制台首页,找到你的项目ID(一串唯一的名称或数字),稍后会用到。

3.3 开发环境与脚本准备

为了在Unity编辑器内调用API,我们需要编写C#编辑器脚本。这要求你的开发机器能够访问外网(以调用Google API)。同时,我们需要安装Google的.NET客户端库。

  1. 在Unity中管理NuGet包(可选但推荐):Unity本身不直接支持NuGet。我们可以使用一个名为“NuGetForUnity”的包来安装所需的库。在Package Manager中,同样通过Git URL添加:https://github.com/GlitchEnzo/NuGetForUnity.git。安装后,你会在Assets->NuGet->Manage NuGet Packages中找到包管理器。
  2. 安装Google.Cloud.Translation.V2客户端库:打开NuGetForUnity,搜索“Google.Cloud.Translation.V2”,选择并安装。这个库会处理与Google API的所有通信和认证。
  3. 放置API密钥:将之前下载的JSON密钥文件放到Unity项目目录下一个安全且方便引用的位置,例如Assets/Editor/Resources/(注意,Resources文件夹内的文件在构建时会被打包,所以务必确保这个路径在构建时被排除,或者使用.gitignore忽略该文件)。在我们的脚本中,将通过读取这个文件来初始化认证。

4. 核心自动化脚本设计与实现

这是整个方案的大脑。我们将创建一个编辑器窗口,用于选择要翻译的本地化表格,并批量调用翻译API。

4.1 创建翻译管理器编辑器窗口

Assets/Editor/文件夹下,创建一个新的C#脚本,命名为AutoTranslationWindow.cs

using UnityEngine; using UnityEditor; using UnityEditor.Localization; using System.Collections.Generic; using Google.Cloud.Translation.V2; using System.IO; using Newtonsoft.Json; public class AutoTranslationWindow : EditorWindow { // 用于存储选择的本地化表格集合 private LocalizationTableCollection selectedCollection; // 源语言代码,例如 "en" private string sourceLanguage = "en"; // 目标语言代码列表,例如 ["zh-CN", "ja", "ko"] private List<string> targetLanguages = new List<string>(); // 输入的单个目标语言,用于UI添加 private string singleTargetLanguage = ""; // API客户端实例 private TranslationClient translationClient; // 是否正在翻译 private bool isTranslating = false; [MenuItem("Tools/Localization/Auto Translator")] public static void ShowWindow() { GetWindow<AutoTranslationWindow>("Auto Translator"); } private void OnGUI() { GUILayout.Label("Automatic Translation Setup", EditorStyles.boldLabel); EditorGUILayout.Space(); // 1. 选择本地化表格 selectedCollection = EditorGUILayout.ObjectField("Localization Table Collection", selectedCollection, typeof(LocalizationTableCollection), false) as LocalizationTableCollection; // 2. 设置源语言 sourceLanguage = EditorGUILayout.TextField("Source Language Code", sourceLanguage); // 3. 管理目标语言列表 GUILayout.Label("Target Languages:", EditorStyles.boldLabel); EditorGUILayout.BeginHorizontal(); singleTargetLanguage = EditorGUILayout.TextField("Add Language Code", singleTargetLanguage); if (GUILayout.Button("Add") && !string.IsNullOrEmpty(singleTargetLanguage)) { if (!targetLanguages.Contains(singleTargetLanguage)) targetLanguages.Add(singleTargetLanguage); singleTargetLanguage = ""; } EditorGUILayout.EndHorizontal(); EditorGUILayout.LabelField("Languages to translate:"); for (int i = 0; i < targetLanguages.Count; i++) { EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField(targetLanguages[i]); if (GUILayout.Button("Remove", GUILayout.Width(60))) { targetLanguages.RemoveAt(i); break; } EditorGUILayout.EndHorizontal(); } EditorGUILayout.Space(); // 4. 初始化翻译客户端(懒加载) if (translationClient == null) { if (GUILayout.Button("Initialize Translation Client")) { InitializeClient(); } } else { EditorGUILayout.HelpBox("Translation client is ready.", MessageType.Info); } EditorGUILayout.Space(); // 5. 执行翻译按钮 GUI.enabled = (selectedCollection != null && targetLanguages.Count > 0 && translationClient != null && !isTranslating); if (GUILayout.Button("Start Automatic Translation", GUILayout.Height(40))) { StartTranslation(); } GUI.enabled = true; if (isTranslating) { EditorGUILayout.HelpBox("Translation in progress... Please wait.", MessageType.Warning); } } private void InitializeClient() { // 从Resources文件夹加载服务账号JSON密钥 // 注意:实际项目中,应使用更安全的方式管理密钥,如环境变量。 TextAsset keyFile = Resources.Load<TextAsset>("gcp_service_account_key"); if (keyFile == null) { EditorUtility.DisplayDialog("Error", "API key file not found in Assets/Resources/. Please place your JSON key file there and name it 'gcp_service_account_key.bytes'.", "OK"); return; } // 因为Unity的TextAsset读取JSON文件可能需要特殊处理,这里假设你已将.json文件后缀改为.bytes // 更优做法:将JSON文件放在Editor目录下,使用System.IO直接读取。 string jsonPath = "Assets/Editor/Resources/gcp_service_account_key.json"; // 假设实际路径 if (!File.Exists(jsonPath)) { EditorUtility.DisplayDialog("Error", $"Key file not found at {jsonPath}", "OK"); return; } string jsonContent = File.ReadAllText(jsonPath); var credential = Google.Apis.Auth.OAuth2.GoogleCredential.FromJson(jsonContent).CreateScoped(TranslationClient.Scope.CloudPlatform); translationClient = TranslationClient.Create(credential); Debug.Log("Translation client initialized successfully."); } private async void StartTranslation() { if (selectedCollection == null || translationClient == null) return; isTranslating = true; try { // 获取源语言表格 var sourceTable = selectedCollection.GetTable(sourceLanguage) as StringTable; if (sourceTable == null) { EditorUtility.DisplayDialog("Error", $"Source table for language '{sourceLanguage}' not found.", "OK"); return; } // 遍历所有目标语言 foreach (var targetLang in targetLanguages) { // 获取或创建目标语言表格 var targetTable = selectedCollection.GetTable(targetLang) as StringTable; if (targetTable == null) { // 如果表格不存在,Localization包可能会自动创建,这里我们显式处理 Debug.LogWarning($"Table for {targetLang} not found. Ensure it's added in Localization Tables window."); continue; } // 收集需要翻译的条目 var entriesToTranslate = new List<StringTableEntry>(); var sourceTexts = new List<string>(); foreach (var entry in sourceTable.Values) { // 检查目标表中是否已有翻译,若为空或需要覆盖,则加入翻译列表 var targetEntry = targetTable.GetEntry(entry.Key); if (targetEntry == null || string.IsNullOrEmpty(targetEntry.Value)) { entriesToTranslate.Add(entry); sourceTexts.Add(entry.Value); } } if (sourceTexts.Count == 0) { Debug.Log($"No new texts to translate for {targetLang}."); continue; } Debug.Log($"Translating {sourceTexts.Count} entries to {targetLang}..."); // 调用Google Translation API进行批量翻译 // 注意:API有每次请求的文本长度和数量限制,生产环境需要分批次处理 var response = await translationClient.TranslateTextAsync(sourceTexts, targetLang, sourceLanguage); // 将翻译结果写回目标表格 for (int i = 0; i < response.Count; i++) { var translatedText = response[i].TranslatedText; var entryKey = entriesToTranslate[i].Key; // 获取或创建目标条目 var targetEntry = targetTable.GetEntry(entryKey); if (targetEntry == null) { targetEntry = targetTable.AddEntry(entryKey, translatedText); } else { targetEntry.Value = translatedText; } EditorUtility.SetDirty(targetTable); // 标记为已修改,以便保存 } Debug.Log($"Successfully translated {response.Count} entries to {targetLang}."); } // 保存所有修改的资源 AssetDatabase.SaveAssets(); EditorUtility.DisplayDialog("Success", "Automatic translation completed!", "OK"); } catch (System.Exception ex) { Debug.LogError($"Translation failed: {ex.Message}"); EditorUtility.DisplayDialog("Error", $"Translation failed: {ex.Message}", "OK"); } finally { isTranslating = false; } } }

这个脚本创建了一个简单的编辑器窗口,允许你选择本地化表格、设置语言,并一键触发批量翻译。它包含了基本的错误处理和状态提示。

4.2 配置本地化表格与UI绑定

脚本准备好后,我们需要在Unity中设置本地化资源。

  1. 创建本地化字符串表格集合:在Localization Tables窗口中,创建一个新的String Table Collection,命名为UI_Texts
  2. 添加表格条目:在UI_Texts集合下,为源语言(如英语en)添加条目。例如,添加一个Key为”greeting”,Value为”Hello, Player!”的条目。
  3. 添加目标语言表格:在集合中添加目标语言,如中文zh-CN。此时,中文表格里”greeting”对应的Value是空的。
  4. 在游戏UI中使用本地化文本:在场景中创建一个UI Text或TextMeshPro - Text对象。为其添加Localize String Event组件。在组件的String Reference中,选择Table CollectionUI_TextsTable Entry”greeting”。这样,文本就会根据当前游戏语言自动切换。

现在,运行游戏,在Localization Settings中切换语言,你应该能看到UI文本随之变化(前提是目标语言表格已填充内容)。

5. 自动化流程的优化与进阶配置

基础的批量翻译已经实现,但要投入生产环境,我们还需要考虑更多细节和优化点。

5.1 处理API限制与批量请求优化

Google Translation API对单次请求有大小限制(每请求最多128个文本,总字符数有限制)。我们的脚本需要增加分批次处理逻辑。

StartTranslation方法中,替换调用API的部分:

// ... 之前收集 sourceTexts 和 entriesToTranslate 的代码 ... const int maxBatchSize = 100; // 每批次最大文本数 for (int batchStart = 0; batchStart < sourceTexts.Count; batchStart += maxBatchSize) { int batchCount = Mathf.Min(maxBatchSize, sourceTexts.Count - batchStart); var batchTexts = sourceTexts.GetRange(batchStart, batchCount); var batchEntries = entriesToTranslate.GetRange(batchStart, batchCount); try { var response = await translationClient.TranslateTextAsync(batchTexts, targetLang, sourceLanguage); for (int i = 0; i < response.Count; i++) { // ... 更新目标表格 ... } // 每完成一个批次,可以稍微延迟一下,避免触发API速率限制 await System.Threading.Tasks.Task.Delay(200); } catch (Google.GoogleApiException gex) { Debug.LogError($"API Error in batch: {gex.Message}. Retrying or skipping..."); // 这里可以加入重试逻辑 } }

5.2 术语表与翻译记忆库集成

机器翻译对于游戏专有名词(如角色名、技能名、虚构地名)往往处理不好。我们可以利用Google Cloud Translation API的“术语表”(Glossary)功能。

  1. 在GCP创建术语表:在Cloud Console中,进入Translation API的“术语表”页面,创建一个新的术语表。上传一个CSV文件,格式为en,zh-CN\n”Hero”, “英雄”\n”Mana”, “法力值”
  2. 在脚本中指定术语表:修改API调用,传入术语表ID。
    var request = new TranslateTextRequest { Contents = { batchTexts }, TargetLanguageCode = targetLang, SourceLanguageCode = sourceLanguage, Parent = new ProjectName(projectId).ToString(), GlossaryConfig = new TranslateTextGlossaryConfig { Glossary = new GlossaryName(projectId, "us-central1", glossaryId).ToString() } }; var response = await translationClient.TranslateTextAsync(request);

5.3 与CI/CD管道集成(全自动本地化)

对于追求极致自动化的团队,可以将此流程集成到CI/CD中。例如,使用GitHub Actions或Jenkins。

  1. 触发条件:每当master分支有新的提交,或者当特定的本地化源文件(如主英语表格)发生变化时,触发工作流。
  2. 工作流步骤
    • 检出代码:获取最新的Unity项目。
    • 设置Unity环境:使用Docker镜像或安装Unity命令行工具(Unity CLI)。
    • 执行翻译脚本:以非交互模式(-batchmode)运行一个我们预先写好的、加强版的编辑器脚本。这个脚本会读取最新的源文本,调用API翻译所有目标语言,并保存结果。
    • 提交更改:将翻译后生成的多语言资源文件自动提交回版本库的特定分支(如i18n-updates)。
    • 触发构建:可选地,随后触发多语言版本的自动构建。

这实现了“开发人员更新英文文本 -> 自动翻译成所有语言 -> 自动生成多语言包”的完整闭环,无需人工干预。

6. 常见问题、调试与避坑指南

在实际操作中,你肯定会遇到各种问题。以下是我在多个项目中总结的常见坑点和解决方案。

6.1 翻译API调用失败

  • 错误:权限不足 (Permission Denied)
    • 原因:服务账号密钥文件无效、密钥文件路径错误,或服务账号未被授予正确的角色。
    • 排查
      1. 检查JSON密钥文件内容是否完整。
      2. 在GCP控制台确认该服务账号已启用,且拥有“Cloud Translation API User”角色。
      3. 确保脚本中读取密钥文件的路径绝对正确。建议在脚本开头用Debug.Log(Application.dataPath)打印路径进行核对。
  • 错误:超出配额 (Quota Exceeded)
    • 原因:免费 tier 有每月字符数限制,或请求频率过高。
    • 解决
      1. 在GCP控制台的“配额”页面,查看Translation API的配额使用情况。
      2. 优化脚本,增加批次间的延迟(如上述的Task.Delay)。
      3. 对于大型项目,考虑升级到付费套餐。

6.2 Unity本地化显示异常

  • 问题:游戏运行时文本不切换,或显示为Key(如##greeting##
    • 原因1LocalizationSettings中未设置或未正确设置Startup Locale Selector
    • 解决:检查LocalizationSettings资产,确保Startup Locale Selectors列表中有有效的选择器(如SpecificLocaleSelectorSystemLocaleSelector)。
    • 原因2:UI组件上的Localize String Event组件引用的Table EntryKey 在表格中不存在。
    • 解决:检查组件配置,确保Key的拼写完全一致(区分大小写)。
  • 问题:翻译后的文本在UI中布局错乱、溢出或换行异常
    • 原因:不同语言文本长度差异巨大。德语、芬兰语通常比英语长很多,而中文、日文可能较短但字符宽度不同。
    • 解决
      1. UI设计预留空间:在设计UI时,为文本容器预留足够的扩展空间,避免使用固定宽度。
      2. 使用TextMeshPro:TextMeshPro比传统UI Text有更好的文本渲染和布局控制能力。
      3. 字体回退(Fallback):确保为每种语言指定了正确的字体资产,特别是对于中文、日文、韩文等,否则会显示为方块。在Localization SettingsLocale Specific Font Settings中配置。
      4. 人工干预与标签:对于机器翻译后明显过长或容易引起歧义的句子,必须在翻译管理平台或表格中进行人工修正。可以在源文本中加入特殊的“翻译标签”,提示翻译人员注意,但机器翻译会忽略这些标签。

6.3 流程与协作问题

  • 问题:源文本更新后,如何同步到已翻译的语言?
    • 解决:这是本地化流程管理的核心。我们的脚本逻辑是“目标语言为空时才翻译”,这只适用于初次填充。更完善的方案需要“差异对比”:
      1. 记录每个条目的版本号或哈希值(如MD5)。
      2. 当源文本更新时,其哈希值改变。
      3. 在自动化流程中,对比源条目和目标条目的哈希值。如果源已更新而目标未更新,则将该条目标记为“需要重新翻译”或“需要人工审核”。
    • 工具推荐:这正是专业本地化平台(如Lokalise)的核心功能。如果自建流程,你需要开发更复杂的版本管理逻辑。
  • 问题:如何管理上下文(Context)以提高翻译质量?
    • 机器翻译的局限:单独的句子如“Attack”可能被译为“攻击”(动词)或“攻击力”(名词),取决于上下文。
    • 解决
      1. 在Key和注释中提供上下文:使用有意义的Key,如”battle_button_attack””stat_attack_power”。在本地化表格的“注释”栏中,为翻译者(或用于提示AI)写明上下文,例如“用于战斗按钮的动词”。
      2. 使用术语表:如上文所述,建立项目术语表是保证专有名词一致性的最佳实践。
      3. 后期人工审核:全自动流程适用于初版或频繁更新的文本,但对于核心剧情、物品描述等关键内容,必须安排母语者进行人工审核和润色。

7. 扩展思路:从文本到全资源本地化

真正的游戏本地化远不止文本。我们的自动化思路可以扩展到其他资源类型。

  1. 图片与Sprite本地化Unity Localization包支持Asset Table。你可以为不同语言创建不同的图片资源(如包含文字的UI按钮图、文化特定的图标)。自动化流程可以扩展为:当设计师上传了英文版图片后,脚本自动通知外部团队或通过图像处理API生成/替换其他语言版本的图片(但这通常涉及设计,自动化程度有限)。
  2. 音频本地化:对于角色配音,自动化主要是流程管理。可以搭建一个系统,将需要配音的台词文本(及其上下文)自动导出,发送给配音管理平台,待录制完成后,再根据返回的音频文件链接,自动下载并导入到Unity项目中对应的Asset Table里。
  3. 配置数据本地化:游戏平衡数据、活动配置中可能也包含需要本地化的描述字段。这些数据通常存储在ScriptableObject或JSON文件中。可以编写脚本,在导出游戏配置数据时,自动提取其中的文本字段,送入翻译管道,然后再写回对应语言版本的数据文件。

搭建起这样一个以Unity Localization包为核心,通过自定义编辑器脚本连接云端翻译API,并辅以术语表和CI/CD集成的自动化流程后,你会发现游戏本地化从一个令人头疼的“后期工序”,变成了一个顺畅的、可监控的“日常流水线”。它并不能完全取代人工翻译对于质量和文化适配的把握,但它能极大地解放生产力,确保基础翻译的覆盖速度和一致性,让开发团队能更敏捷地应对全球市场。

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

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

立即咨询