某次版本上线前我例行检查后台的崩溃日志意外看到一个德国用户在差评里放了一张截图一个按钮上写着半截单词后面跟着省略号旁边还挤着三个换行的文字。乍一看是翻译没做完点进去才知道是文案溢出。多语言环境下的文本溢出问题绝对比大多数团队想象得普遍。很多团队会安排专人做多语言测试但效率低、覆盖差、反馈还慢等翻译公司回稿往往已经过去一两周了。我自己在几个跨语言应用里做过反复试验后得出一个结论与其等真实翻译不如先用AI按约束自动生成多语言文本结合国际化测试的基础流程把溢出风险提前炸出来。这篇文章要分享的就是一套我自己在项目中跑通的做法——怎么用AI生成不同语言的高长度测试短语怎么把生成的字符串注入到界面里怎么判断元素是否真的溢出以及这套方案到底能省多少事、踩过哪些坑。适合客户端开发、测试工程师、以及管国际化产品的同学参考就算你的项目还没有完整多语言体系看完也能直接落地一部分。1. 语言膨胀率一句保存在不同语言里差出多少个字符先理解问题根源。中文界面文案往往很精炼两三个字就能表达完一个动作。可一旦切到德语、芬兰语这类语言同样的意思可能需要两倍以上的字符。这就是所谓语言膨胀率在相同语义下不同语言所需字符数量的差异。1.1 同一句话在不同语言里的真实差别我做过一个小实验找出之前项目里几个常见的UI短语分别查了它们在几种语言里的表达长度。英文原文中文德语芬兰语日语Save保存SpeichernTallenna保存Delete item删除项目Element löschenPoista kohde項目を削除Terms of service服务条款NutzungsbedingungenPalvelun käyttöehdot利用規約注意服务条款这一类德语直接拼成一个大长词从11个字符干到24个字符芬兰语的复合词更夸张经常会整个句子只由一个词串构成。日语看着不长但假名和汉字混排时渲染宽度并不小尤其是遇到片假名外来语会突然冒出很长的音译词。这种长度差异带来的结果非常直观你按中文或英文设计的固定宽度按钮在德语环境下一旦文案超过容器宽度轻则截断、重则换行破坏布局。1.2 字符数只是起点渲染宽度才是终端这里要特别提醒一个误区不要只数字符数前端最终看的是渲染宽度。同样是10个字符WWWWWWWWWW和iiiiiiiiii宽度差出一倍因为每个字形的advance width不同。我在某个跨平台App里见过一个活生生例子一个日期选择器的按钮英文Next只有4个字符德语Fortsetzen是10个字符表面看只是多了6个字母但激活系统字体后德语的宽度是英文的1.9倍。按钮的padding一固定文字直接顶到底部边框。所以在做国际化测试时字符数膨胀率只能作为初步估算更可靠的是直接测量渲染宽度这点后面会详细讲。1.3 为什么人工测试总是漏这些如果你的团队没有以目标语言为母语的成员人工测试基本只能靠想象这句德语大概多长。多数测试人员自己拼出来的短语都是常见短词根本覆盖不到复合长词和真实口语里的长表达。还有一个很现实的问题人脑生成的测试句天然偏向合理表达而UI文本恰恰需要的是口语化但语义准确的混合体一个人很难同时产出十几个语言的靠谱测试句子。AI在这里的价值就体现出来了它不负责翻译准确性而是按约束条件快速采样出大量符合语言形态、长度分布够宽的短语把过去需要翻译公司几周才能拿到的材料压缩到几小时内。2. 传统的多语言测试为什么总是翻译完才知道布局出事在没有AI辅助之前团队做多语言文本溢出测试通常有三条路找翻译、人工造词、伪本地化。每条路都有它的局限。2.1 找翻译公司的路子周期长、反馈靠后把全部文案发给翻译公司等翻译稿回来再手动替换进工程跑一轮截图对比。一套流程走下来两个星期算快的。更难受的是翻译公司交付的往往是正确翻译而不是压力测试文本。你拿到手的德语文案刚好是6个字符但真实用户界面里可能偶尔冒出24个字符的复合词这个Case完全测不到。2.2 人工造长词覆盖率和真实性不可兼得有些同学会手动拼一些超长词比如在原有单词后面加一堆重复形容词硬凑出一个超长字符串。这种做法能测出布局会不会崩但有两个致命问题拼出来的词不符合目标语言的基本规则术语叫非自然文本。德语里合法的长词往往是由词干词缀按规则复合而成硬拼的数字串在字体渲染上不一定触发相同行为。只能覆盖长度这一个维度测不到特定语言的换行习惯、断词规则、标点悬挂等问题。2.3 伪本地化能解决一半但只有一半伪本地化pseudo-localization是很多国际团队的前置测试手段。原理很简单写一个脚本把英文文本里的ASCII字符替换为带变音符的拉丁字符比如A变成À同时额外扩展长度模拟德法西等拉丁语系的文本长度。我自己的经验里伪本地化在提前暴露宽度不足上非常有效尤其在开发早期每天都能跑。它的局限也很明显处理不了非拉丁语系。比如日语没有空格分词换行逻辑和英文完全不同芬兰语有大量不同尾缀伪本地化替换出来的字符串根本不对应真实形态泰语这类语言甚至没有明确的词边界断行规则依赖字典和音节判断。你伪本地化跑得再欢也测不到真实语言的断行行为。覆盖维度人工造词伪本地化AI生成长字符覆盖可以可以可以真实语言结构差差较好非拉丁语系断词基本覆盖不到覆盖不了可以定制批量生成速度极慢快快语义合理性一般无意义较好所以我的结论是AI生成不是用来推翻伪本地化的而是用来补齐它够不到的那一半。尤其是那些需要真实语言结构、真实断词场景的测试目标AI按约束生成是目前投入产出比最高的一条路。3. 让AI按约束造文本核心思路与具体Prompt说了这么多问题终于进入正题。AI生成多语言文本的整套逻辑并不复杂关键在按约束采样而不是让AI自由发挥。3.1 先定目标长度、语言特征、UI场景一次合格的文本溢出测试至少需要同时满足三个条件长度要够字符串的字符数或渲染宽度要高于当前容器能承受的上限通常我会要求生成英文原文长度的140%-180%。语言特征要对德语要有长复合词芬兰语要有长尾缀日语要包含适当比例的汉字和假名混排法语要能产生连读导致的排版变化。UI场景要明确按钮、标题、通知、错误提示、列表项不同场景的文案风格完全不一样。我建议在开始生成之前先把待测页面里所有需要检测的UI元素列一张表标注元素类型、容器宽度、当前文案语言、字号。这样不仅方便后续测溢出也能让AI生成时更有针对性。3.2 Prompt模板与生成代码我的做法是构造一个带JSON输出的系统级Prompt每轮请求一个语言和一个UI场景避免一次请求塞太多语言导致模型混用语言。import json import openai # 这里只是示例任何LLM SDK都可以 def build_prompt(lang, scene, max_chars): prompt f 你是擅长移动端UI文案的专家。请为{scene}生成10条测试短语。 要求 1. 语言必须是{lang}的母语者自然表达不要翻译腔。 2. 每条短语的字符数控制在{max_chars}左右允许上下浮动15%。 3. 语义覆盖操作指令、状态提示、错误反馈、成功反馈、超长名称。 4. 尽量使用{lang}中长度偏长的自然表达包括复合词、语境补充语或口语长句。 5. 不要加注释不要写多余文字直接输出JSON数组。 示例 [..., ...] return prompt def generate_test_strings(lang, scene, max_chars, count10): prompt build_prompt(lang, scene, max_chars) resp openai.chat.completions.create( modelgpt-4o-mini, messages[{role: user, content: prompt}], temperature0.7, ) texts json.loads(resp.choices[0].message.content) return deduplicate_and_clean(texts)输出示例德语、按钮场景可能是这样[ Speichern, Weitere Einstellungen öffnen, Zahlungsinformationen aktualisieren, Bitte warten Sie einen Moment ]这里的关键参数是temperature。我实测下来生成测试文本需要一定随机性temperature设在0.6到0.8之间比较合适太低容易每轮生成高度相似的短语太高则可能出现语言混乱。3.3 为什么按字符数约束而不是按Token数这是最容易犯的错。大模型的对话单位是Token但UI渲染看的是字符数和字形宽度二者并不等价。一个很现实的例子同一段德语文本可能占20个Token但渲染宽度只有80个像素一段中文文本只有8个Token却因为汉字是全角字形宽度占了120像素。如果你在Prompt里指定的是生成50个Token的句子最终出来的字符串长度很可能不符合预期。我在工具里直接写入字符数在XX到XX之间并附带一个后处理脚本遍历生成的字符串用 len() 过滤掉不满足长度范围的条目再补生成缺失的数量。def deduplicate_and_clean(texts): cleaned, seen [], set() for t in texts: t t.strip() if t and t not in seen: seen.add(t) cleaned.append(t) return cleaned3.4 本地规则 LLM的组合拳有些时候单靠LLM生成的短语语义丰富但缺少系统性的边界覆盖。我的做法是在LLM输出之外再叠一层规则模板。例如对德语我会在词库里准备一组高活性词干然后用规则把它们拼接成更长的复合词强制覆盖长词上下文的场景。用这套本地规则LLM组合可以同时得到两个方向的数据LLM负责自然但偏长规则负责极端但合法。prefixes [Zahlungsinformations, Benachrichtigungseinstellungs, Kontoverwaltungs] suffixes [system, prozess, formular, seite]两个列表做笛卡尔积再和LLM生成结果合并等于是把随机性和可控的边界压力结合在了一起。这一步让最终测试集的覆盖效果好了很多尤其是那些页面里几乎不会出现、但一旦出现就会把容器挤爆的长词。4. 把测试文本灌进真实界面三端注入与溢出判定方法有了测试字符串下一步是把它真正放到界面里跑。很多人做到这一步就卡住了——不知道怎么批量替换多语言文案。这里分享三种平台的注入方式。4.1 Web端检索替换 JSON 文件 运行时断言Web端做国际化一般会有一个语言资源文件比如 i18n/zh-CN.json。解法很简单写一个脚本读取英文原始文案用刚才生成的对应语言字符串替换掉目标语言文件里的值然后启动本地开发服务器。替代完成之后还需要用一组JS断言来检测溢出。function isOverflowing(el, tolerance 2) { return el.scrollWidth el.clientWidth tolerance; } document.querySelectorAll([data-i18n]).forEach(el { if (isOverflowing(el)) { console.warn([i18n-overflow] ${el.dataset.i18n}, el); } });这里我经验是用2像素的容差因为现代浏览器的亚像素渲染偶尔会产生微小的四舍五入误差。设成0的话会有一堆假正报设太大又会漏掉真实问题。4.2 Androidvalues-xx 批量替换Android的多语言资源文件都在 res/values-xx/strings.xml 里。我的做法是写一个Python脚本用ElementTree直接改XML节点值把测试字符串写进对应语言同时保留原英文文案作为key。string namebtn_saveSpeichern/string string namebtn_saveTallenna/string替换到目标语言文件夹后用模拟器或者真机切换系统语言就能在真实渲染流程里看到效果。Android上特别要注意一个点TextView默认有ellipsize属性如果不设置成end或marquee溢出时它可能会自动截断你根本察觉不到问题。所以检测溢出时最好先临时把ellipsize改成none强制让文字撑出容器边界才能测出真实宽度。4.3 iOS模拟器语言切换 boundingRect 计算iOS端更直接把Localizable.strings里的value替换成目标语言然后切模拟器语言重启就能跑出真实Layout。另外我不建议完全依赖模拟器截图肉眼看。更好的方式是基于CoreText的 boundingRect 方法做离线预判let text Zahlungsinformationen aktualisieren let font UIFont.systemFont(ofSize: 16) let width text.size(withAttributes: [.font: font]).width if width containerWidth { print(⚠️ overflow: \(text), width\(width)) }这套预判脚本不需要启动App就能批量检测几百条文案是早期最廉价的问题发现手段。但要注意离线计算用的是系统字体默认度量实际渲染时如果用了自定义字重或字间距数值会有偏差最终仍要落到真机截图确认一版。4.4 溢出判定别只靠肉眼截图坦白说依赖QA肉眼一张图一张图对效率太低且容易疲劳。我在落地这套流程时给团队配了三条判定通道判定方式适用阶段说明离线宽度计算生成阶段快速过滤超长文本运行时尺寸断言渲染阶段scrollWidth/clientWidth比对截图视觉回归最终验收比对基线快照像素级diff视觉回归可以引入常见的快照测试工具在替换测试字符串之后对关键页面截图把截图与预设的正常状态做像素比对超出阈值就报警。阈值我通常设在1%到2%之间的像素比例这样既能探测到明显的溢出又能忽略抗锯齿带来的亚像素噪声。5. 实战结果与坑一场覆盖12种语言的溢出排查最后分享一下我在某个跨平台客户端里的实际测试数据以及那些文档里不会写的坑。5.1 一次真实迭代的测试效果当时我们用AI生成器跑了12种语言、230条测试短语覆盖登录、支付、设置、列表、推送通知五个典型页面。全套流程大概花了一天时间其中真正耗时的反而是把字符串灌进项目环境。问题检出结果如下表问题类型数量典型案例按钮宽度不足7德语Fortsetzen在窄按钮上被截断列表行高不够3芬兰语长复合词导致行高撑破换行破坏布局2法语长词在卡片中间换行固定宽度容器溢出1日文片假名外来语在tab上溢出13个问题放在过去流程里基本要等到翻译公司回稿后的第二轮测试才能发现而且大概率只能发现其中一半。5.2 LLM在生成时的几个魔幻行为AI不是完美的用的时候有几个坑我实际踩过提醒一下。第一它偶尔会用中文回复你。明明要求生成德语前几条还是德语第三条突然冒出一句中文短语。这多半是模型认为自己需要解释而不是执行。我的对策是在Prompt最后强制加一句只返回JSON数组不允许任何解释文本并在后处理脚本里过滤掉含有常见中文标点的条目。第二生成重复内容的概率不低。尤其当每轮请求数量超过5条时前两条和后一条经常高度重复。所以我做了全局去重同时引入随机种子让每轮的内容有更大差异。第三最坑的是一本正经的错误。比如让它生成波兰语时它可能会生成西班牙语式的句子。这可能和训练语料中波兰语占比偏低有关。解决办法不要跨语言一次性生成太多种语言每种语言单独请求并在Prompt里附带一个该语言的短示例。示例带来的收益非常明显。第四绝对不要拿LLM生成的测试文本去冒充翻译直接上生产环境。它的定位是压测数据不是交付文本。这个边界一定要守住否则审校环节会出大问题。5.3 把测试字符串沉淀成资产库最后分享一个长期价值点我建议把每次AI生成出来、清洗过的字符串集按语言、场景、长度区间分类存成模型文件。这样每个迭代都能复用还能在UI改动后立刻跑一遍回归。我自己的库已经积累了三轮迭代每次新增页面或者调整组件宽度直接把这些字符串灌进去跑断言十分钟就能出报告。这个资产积累起来以后测试投入越来越小反而比做一次一次性大检查更划算。另外一个小建议AI生成跑完后依然建议找母语者做一轮终检。AI能帮你把布局问题提前筛掉但真实的语感、专业术语、俚语边界仍然需要人来把最后一道关。AI和人工配合着用这套国际化测试流程才算真正闭环。