图片里的中文短句翻成英文或德文后,文字挤出了原来的按钮。把字号调小以后虽然放进去了,手机上却很难读。这类问题经常发生在商品图、海报和说明图的本地化过程中。
根因不一定是翻译太长,而是排版程序把"字符数"当成了"占用宽度"。同样十几个字符,不同字体、字号和字母组合,画出来的宽度可以不同。
下面用 Pillow 做一个单行排版检查:在固定文本框内测量译文,寻找允许范围内的字号;放不下就返回待复核,不一直缩小。这个例子只研究文字排版,不包含 OCR、翻译模型或背景擦除。
1. 先划出真正能用的空间
假设图片里的文本框宽 360 px、高 72 px,四周各留 12 px 内边距,可用空间就是:
text
可用宽度 = 360 - 12 × 2 = 336 px
可用高度 = 72 - 12 × 2 = 48 px
这两个尺寸应该来自设计要求或已确认的文字区域。OCR 检测框通常只是原文字的包围区域,不一定等于设计允许使用的最大文本框;旁边可能还有图标、商品轮廓和不能遮挡的内容。

图中尺寸是本文构造实验的参数,不是图片翻译行业的固定标准。判断是否放得下,应同时检查宽和高,并使用最终渲染时相同的字体、字号与绘制参数。
2. 用 textbbox 检查,不用 len 估算
len(text) 统计字符串长度,不测量字形。这里用 ImageDraw.textbbox() 获取文字包围框,再比较 right-left 与 bottom-top。
别把 textlength() 与包围框宽度混为一谈:前者用于获得排版前进距离;斜体等字形可能伸出该距离。要判断视觉区域是否越界,包围框更直接。如果绘制时加了描边,也要在测量时传入相同的 stroke_width。
下面代码只处理一行非空文字,字号允许 24--48 px。为了让逻辑容易检查,直接从大到小遍历;几十个候选字号无需先设计复杂搜索算法。
bash
python -m pip install Pillow==12.3.0
python fit_text.py /path/to/your/font.ttf
字体路径必须指向你有权使用、且覆盖目标字符的字体。本次实际运行使用 macOS 的 PingFang 字体;换字体或平台后需要重新测量。
python
import sys
from PIL import Image, ImageDraw, ImageFont
FONT_PATH = sys.argv[1] # Use a font that covers all characters.
BOX = (360, 72)
PADDING = 12
MIN_SIZE, MAX_SIZE = 24, 48
probe = ImageDraw.Draw(Image.new('RGB', (1, 1)))
def fit_one_line(text):
if not text or '\n' in text:
raise ValueError('one non-empty line required')
for size in range(MAX_SIZE, MIN_SIZE - 1, -1):
font = ImageFont.truetype(FONT_PATH, size)
left, top, right, bottom = probe.textbbox((0, 0), text, font=font)
if (right-left <= BOX[0]-2*PADDING
and bottom-top <= BOX[1]-2*PADDING):
return size, (left, top, right, bottom)
return None
for text in ['Free shipping', 'Kostenloser Versand', '免费配送',
'Kostenloser Versand bei allen Bestellungen']:
result = fit_one_line(text)
print(text, len(text), result)
# A failure must go to layout review; never silently shrink forever.
注意:函数里的最小字号只是本实验的约束,不是可读性标准。最终图片显示在手机上时还会缩放,应按投放尺寸、观看距离和内容层级确定阈值。
3. 同一个框,四段文字得到不同结果
本次使用 Pillow 12.3.0,文本框 360 × 72 px、内边距 12 px,测得:
| 文字 | 字符数 | 选择的字号 | 文字包围框宽 × 高 |
|---|---|---|---|
| Free shipping | 13 | 48 px | 302 × 46 px |
| Kostenloser Versand | 19 | 34 px | 325 × 26 px |
| 免费配送 | 4 | 48 px | 192 × 46 px |
| Kostenloser Versand bei allen Bestellungen | 42 | 无可用字号 | 返回 None |
这些短句为手工准备的排版样本。最后一句额外包含"所有订单"的范围限定,仅作为更长字符串测试,不应被看成前三句的等义翻译,也不是某款翻译产品的输出。

图中三行均由同一字体文件、上述字号与测量规则绘制。框能容纳文字只说明几何检查通过,不说明译文准确、风格合适或视觉层级理想。
如果需要把测量结果画到框内,别忽略包围框的左上偏移。例如从 (x, y) 开始放置一个文本框,可以这样对齐可见字形:
python
size, (left, top, right, bottom) = result
font = ImageFont.truetype(FONT_PATH, size)
draw.text(
(x + PADDING - left, y + PADDING - top),
text, font=font, fill="black"
)
这里要求 result 不是 None,draw 是目标图片的 ImageDraw 对象。减去 left、top 是为了让可见字形从内边距起点开始;需要基线对齐的一组标签,则应另设统一基线,不能逐行随意顶对齐。
4. 放不下时,需要决策,不只是再缩一点
当函数返回 None,可以按内容性质选择下一步:
- 允许换行: 扩展成多行排版,同时测量总高度与行距。英文等语言通常按词换行;中日韩文字还要考虑行首行尾禁则,不能机械按固定字符数截断。
- 允许改框: 增加文本框宽高或调整局部布局,但先检查是否遮挡商品、尺寸线与其他内容。
- 允许改文案: 由懂目标语言的人调整表达,保留价格、数量、适用范围和条件;不要让排版程序自动删除"仅限""不包含"等限定词。
- 不允许改动: 标记待人工处理。比起把关键说明压成不可读的小字,明确失败更利于交付。

多行、竖排、阿拉伯语等复杂场景不能直接套用本文单行函数。字体回退、双向文字和字形塑形需要合适的排版引擎支持;出现缺字方框时,即使包围框通过也不能算成功。
5. 图片翻译交付时,分开检查文字与画面
如果使用现成的图片翻译工作台,比如 LingFlow,可以通过其图片翻译流程生成结果,再预览和下载。本文的 Pillow 检查适用于拿到文字区域与待绘制文本后的独立排版步骤;它不是对产品内部实现的说明,也不能从已经合成的结果图自动还原可编辑文字层。
本次只核实了产品的公开介绍与物料,没有把这些手工排版样本上传作为产品效果测试。图片翻译结果的复核可以分成两遍:先核对数字、单位、否定词和修饰范围,再检查文字是否遮挡主体、是否越框以及在实际展示尺寸下能否读清。
还要区分三种不同能力:可以下载图片、可以检查文字位置、可以直接编辑图片里的文字。拿到一张 PNG,并不意味着它包含可编辑的文字图层。需要精修时,保留源设计文件、字体和译文清单,会比只保存最终图片更容易返工。