Mxhlix

我运营着一大批“在线预览、导出为 PNG”的网页工具——姓名标签、证书生成器、价签、徽章——支持五种语言:日语、英语、西班牙语、法语、葡萄牙语。

只要只用日语和英语测试,Canvas 2D 文本渲染看起来就没问题。

一旦把 es/fr/pt 跑一遍,就会出问题。

踩过这些坑之后,问题归纳为四种模式。

前提:拉丁语系文本长度是日语的 1.4–2 倍

先看设计数据。同一标签在五种语言下的长度对比:

示例 ja en es fr pt
工具名称 22 字符 35 62 48 50
“标准”按钮 4 8 10 20 12

经验法则:fr/pt 比 ja 长 1.4–1.7 倍;es 甚至可膨胀到近 3 倍。针对日语调好的字号与 maxWidth,对拉丁语系完全不适用。四种失效模式都由此产生。

模式 1:手写换行 text.split(/\s+/) 在 CJK 上失效

经典代码片段——按空格拆分、逐词换行——对日语或中文完全无效,因为这些语言没有空格分词。整句会变成一个无法拆分的 token,在画布边缘被裁切。

要用真实日语输入测试,确保最后一行渲染到最后一个字符。“大部分显示出来了”不算通过。

模式 2:仅 ASCII 的分词器会在带重音字符处拆分单词

用字符类分词器(如 [A-Za-z0-9'\-_])修复模式 1 后,换来另一种回归:重音字符 ç é ã ó ñ 不在该集合中,导致 produção 被拆成 produ / ç / ão

英语测试永远不会发现这个问题。

生成真实的 fr/es/pt 样本 PNG,肉眼检查重音字符附近。我还没找到其他检测方法——字符串比对测试看不到渲染级别的拆分。

模式 3:尾部的重要单词消失在“……”中

由于 fr/pt 比布局最初针对的 ja 长 1.4–1.7 倍,文本溢出两行,被省略号截断。最残忍的是:被截掉的往往是短语的尾部——通常是语义关键的词,如 production / produção

解决方案是将字段分为两类。允许截断的字段(描述)使用换行+省略;绝不能截断的字段(标题、姓名)使用自动适配:在离屏 canvas 用 measureText 测量,搜索能适配的字号,再以此尺寸渲染。标题出现省略号即为交付物损坏,因此两种策略绝不能混用。

模式 4:预览与下载的 PNG 在不同位置换行

最隐蔽的问题。只在 canvas 端实现模式 3 的自动字号,而 DOM 预览保持固定字号,导致屏幕上看到的换行与 PNG 中的换行不一致。从用户角度看,工具的核心承诺——“下载所见即所得”——被打破。

解决方案:将自动字号决策集中在一处(离屏 canvas 测量),并把结果字号同时应用到 canvas 渲染和预览 DOM 的 style.fontSize。验收标准:所有五种语言中,预览(DOM)与下载的 PNG 行数一致。

发布前检查清单

针对每一个新的图像输出工具:

  • [ ] ja:CJK 渲染到最后一行最后一个字符,无边缘裁切
  • [ ] fr / es / pt:生成真实 PNG;ç é ã ó ñ 附近无单词拆分
  • [ ] fr / pt:无省略号吞掉尾部重要单词
  • [ ] 全部五种语言:预览(DOM)与下载的 PNG 在换行位置与行数上一致

只检查 en 和 ja 就宣布“多语言支持完成”,是最大的陷阱。

限制与注意事项

字符数比例基于我自己的工具集(实用工具名称、描述、UI 标签)测量。不同内容类型比例会有差异。

自动化限制:模式 2 和 4 完全无法通过静态分析检测。生成 PNG 并肉眼检查是唯一有效的办法。五种语言×生成检查是一笔真实成本——任何遗漏都会在生产中暴露。


验证时间:2026 年 4–5 月。环境:Canvas 2D API / 原生 JS / 生产中的 ja·en·es·fr·pt 语言环境。