Godot 伪本地化 QA:在翻译回来之前先发现 UI 会不会撑爆

设计 Godot 伪本地化测试流程,提前发现文本溢出、缺字、硬编码、方向和排版问题。

真正的翻译通常来得很晚,但 UI 排版问题不会等翻译回来才存在。按钮能不能容纳德语,列表项能不能显示长道具名,字体有没有越南语重音,阿拉伯语方向是否需要特殊布局,硬编码中文是否散在脚本里,这些都可以通过伪本地化提前暴露。

Godot 支持 TranslationServer,但很多项目只在接入正式翻译时才开始测试多语言。那时 UI 已经定型,返工成本很高。伪本地化 QA 的目标,是在中文或英文源文本阶段就模拟翻译后的长度、字符和排版压力,让问题尽早进入日常验收。

项目里的真实问题

一个设置页在中文下非常整齐,英文翻译回来后,按钮“恢复默认设置”变成很长的句子,挤出面板;德语版本里下拉选项遮住右侧数值;越南语字体缺字显示方框。更糟的是,部分脚本直接写了中文 Toast,TranslationServer 根本找不到。

这些问题不是翻译质量问题,而是 UI 没有为多语言留空间,也没有硬编码检查。伪本地化可以把“开始游戏”变成带重音、加长、带括号的字符串,例如 [Šţářţ Ĝåmę !!!],立刻暴露按钮是否会撑爆。

设计目标

  • 提前发现:正式翻译前发现文本溢出、缺字和硬编码。
  • 覆盖自动化:关键页面能批量切换伪语言并截图。
  • 压力真实:模拟文本膨胀、特殊字符、占位符和方向差异。
  • 接入轻量:开发、QA、UI 同学都能一键切换伪本地化。

这些目标不是为了把系统做重,而是为了让 Godot 客户端在真实设备、真实网络和真实内容量下仍然可控。很多功能原型只需要一个脚本,但进入发布流程后,必须回答状态从哪里来、失败怎么恢复、UI 如何同步、日志能否说明问题。下面的设计会围绕这些问题展开。

推荐架构

flowchart TD
    A["输入事件/业务意图"] --> B["PseudoLocalizationService"]
    B --> C["文本膨胀"]
    B --> D["字符替换"]
    B --> E["硬编码扫描"]
    B --> F["截图验收"]
    C --> H["状态快照"]
    D --> H
    E --> H
    F --> H
    H --> I["UI反馈和日志"]

图里的每个模块都可以按项目规模合并或拆分。小团队可以用一个 Autoload 承担管理器职责,大项目可以拆成服务、Resource 配置和 UI ViewModel。关键是调用方向要稳定:业务层提交意图,管理器判断状态,执行层接触 Godot 节点、资源或网络,最后统一反馈给 UI 和日志。

关键实现细节

伪本地化不是简单重复字符。要保留占位符和富文本标签,替换可见字母,增加长度,并加入常见重音字符。比如 {count}[color]%s 不能被破坏,否则测试会制造假问题。
文本膨胀比例可以按语言族设置。英文到德语可能 1.3 到 1.6,短按钮文本甚至更长;中文到英文也会显著变长。伪语言可以提供 130%、160%、200% 三档,用来测试普通和极限情况。
硬编码扫描同样重要。脚本里直接写的中文、英文 UI 文案不会进入翻译表。可以用正则扫描 .gd.tscn.tres 中的 CJK 字符或常见英文句子,并允许白名单。扫描结果进入 PR 报告。
截图验收要覆盖关键页面:主菜单、设置、背包、任务、商城、弹窗、Toast、战斗 HUD。伪语言下截图能直观看到溢出和遮挡,比单纯跑字符串检查更有说服力。

失败处理和恢复路径

伪本地化生成失败时,不能影响正常语言。工具应只在开发和 QA 模式启用,正式包不依赖它。
若字体缺字,页面可能显示方框。QA 报告要区分文本溢出和字体缺字,修复路径不同。
富文本标签被伪本地化破坏时,要修生成器,而不是让 UI 适配错误文本。

GDScript 接口草图

class_name PseudoLocalizationService
extends Node

signal state_changed(snapshot: Dictionary)
signal operation_failed(code: String, detail: Dictionary)

var _version := 0
var _snapshot := {}

func submit(intent: Dictionary) -> void:
    _version += 1
    var token := _version
    _snapshot = {"phase": "pending", "intent": intent}
    emit_signal("state_changed", _snapshot)
    _execute(intent, func(result: Dictionary):
        if token != _version:
            return
        if result.get("ok", false):
            _snapshot = result
            emit_signal("state_changed", _snapshot)
        else:
            emit_signal("operation_failed", result.get("code", "unknown"), result)
    )

func current_snapshot() -> Dictionary:
    return _snapshot.duplicate(true)

这段代码只表达接口边界。真实项目里,intent 可以替换成 typed Resource 或明确的 Dictionary schema,_execute 里也要接入超时、取消和错误码。保留 _version 的原因,是客户端经常出现旧异步结果晚于新操作返回的情况。没有版本保护,UI 快速切换、网络重试和资源加载都会把状态改回旧值。

数据契约和协作接口

所有 UI 文案通过 message_id 获取,不允许业务脚本直接拼最终文本。动态参数通过 params 传入,伪本地化生成器保留参数。
PseudoLocalizationService 提供 enable(profile),profile 包含膨胀比例、字符替换表、是否包裹括号、是否模拟 RTL。
截图脚本保存页面名、语言 profile、分辨率和设备比例,方便比对。

分阶段落地

第一阶段接入伪语言生成和手动切换。
第二阶段加入硬编码扫描和关键页面截图。
第三阶段把伪本地化截图纳入发布前 UI 验收。

自动化验证和人工验收

切换 160% 伪语言,检查主菜单、设置、背包、任务和弹窗。
扫描脚本和场景中的硬编码文本,确认白名单外都被发现。
包含占位符和富文本的句子伪本地化后仍能正确渲染。
字体缺字和文本溢出能在报告里区分。

观测指标

  • 硬编码文本数量和趋势。
  • 伪语言截图中溢出问题数量。
  • 缺字字符集合和涉及字体。
  • 正式翻译回来后新增 UI 排版问题数量。

指标不必一开始就全部上报。开发包可以展示完整调试面板,内测包采样关键字段,正式包只保留错误码和聚合计数。重要的是每个异常都能留下足够证据,团队能判断它是内容问题、网络问题、平台问题还是客户端状态机问题。

上线前检查清单

  • UI 文案通过 message_id 获取。
  • 伪本地化保留占位符和富文本标签。
  • 关键页面有伪语言截图验收。
  • 硬编码扫描有白名单和 PR 报告。
  • 字体缺字和文本溢出分开记录。

清单最好能逐步脚本化。不能自动检查的内容,也要明确由谁在什么阶段确认。Godot 项目里的客户端系统经常横跨程序、策划、美术、运营和 QA,如果验收口径只停留在口头,下一次类似问题还会以不同名字回来。

现场演练

现场演练可以把设置页切到 200% 伪语言,再依次打开下拉框、滑杆、确认弹窗和 Toast。所有文字应在容器内换行或缩略,不应遮挡下一项。随后运行硬编码扫描,确认新加的测试中文不会漏进脚本。

案例复盘

伪本地化最有价值的案例,是翻译回来之前就发现按钮撑爆。某个“领取全部附件”按钮在中文下很短,伪语言 180% 后直接盖住货币图标。UI 团队把按钮改成自适应宽度并允许换行,正式德语上线时没有再返工。相比等翻译完成后修,伪本地化提前两周暴露问题,成本低很多。

上线后的维护策略

伪本地化上线后,维护重点是截图基线。关键页面布局变化后,应重新生成伪语言截图。不要让截图工具只在本地化冲刺时使用,越日常越能提前发现问题。

灰度阶段要有回退开关。回退不是把功能粗暴关闭,而是退回更简单但完整的玩家路径:离线队列可以暂停新入队但继续处理已有队列,改键系统可以回到默认档案,地图标记可以关闭聚合但保留任务目标,邮件可以禁用批量领取但保留单封领取。每个系统上线前都应该写清楚“降级后玩家还能做什么”。

责任边界也要明确。谁维护配置,谁看指标,谁处理内容接入,谁判断是否回滚,都要写在系统说明里。Godot 客户端功能经常横跨多个岗位,如果只有实现者知道细节,后续每次活动、版本或平台接入都会重新踩坑。文档不需要很长,但必须包含接入示例、常见错误和验收步骤。

灰度验收脚本

灰度验收可以把伪语言作为 QA 固定语言之一。每天冒烟时至少打开主菜单、设置、背包、任务、商城和战斗 HUD。截图命名包含页面、分辨率和伪语言档位。发现溢出后,不要只修当前字符串,要看容器是否缺少自适应规则。

验收脚本要同时面向人和机器。机器负责断言状态、错误码、数量和耗时;人负责判断文案是否能理解、视觉反馈是否打扰、操作路径是否顺手。很多客户端系统的失败不是“没有执行”,而是“执行了但玩家不知道发生了什么”。因此每个验收步骤都应该包含预期 UI、预期日志和预期状态快照三部分。

灰度结束后要做一次小复盘。指标是否符合预期,玩家是否使用了降级路径,QA 是否发现难以描述的问题,配置是否需要收紧。复盘结论要回写到检查清单里。这样下一批内容或下一次平台接入时,团队不需要重新摸索同一类边界。

边界补充

伪本地化还应覆盖动态文本。道具名、玩家名、数量、倒计时、富文本链接都可能撑爆 UI。只测试静态翻译表不够,QA 应准备极长玩家名、长道具名和多位数数量,组合进背包、邮件、任务和 Toast 页面。

交付补充

交付时可以把伪本地化开关放进内部调试菜单,并记录当前 profile 到截图角落。这样 QA 发回截图时,开发能知道是 130%、160% 还是 RTL 模拟。没有 profile 信息的截图,很难判断问题严重程度。

最后补充

另外,伪本地化报告要能区分阻断和建议。遮挡关键按钮是阻断,普通列表项略长可能是建议。分级清楚,UI 修复才有优先级。

安全区补充

伪本地化截图还要覆盖移动端安全区。刘海屏、窄屏和横屏平板会放大文本溢出问题,尤其是顶部货币栏、底部按钮和弹窗标题。只在桌面分辨率下通过,不能代表移动端通过。

小团队接入版本

小团队可以先做一个伪语言 Translation 文件,不必马上自动截图。只要开发和 QA 能一键切换,就能提前发现大量按钮和弹窗问题。等页面稳定后,再把截图脚本补上。

交付边界

交付标准是正式翻译回来前,关键页面已经通过至少一档文本膨胀测试,硬编码文本可控,字体缺字有清单。伪本地化不能替代母语审校,但能把工程问题提前暴露。

结语

本地化 QA 不应该等翻译结束才开始。Godot 项目用伪本地化提前拉伸文本、替换字符、扫描硬编码,就能在 UI 还容易调整时发现问题。多语言体验越早进入日常流程,后期返工越少。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页