一篇发布于 2026 年 6 月的技术长文,把一个看似普通的前端工单写成了阿拉伯文字排版的技术解剖:设计稿要求阿拉伯文两端对齐,生产页面却只能把词间距拉大,边缘仍不如预期。

这件事重要,不在于某个浏览器少实现了一个 CSS 选项,而在于网页、PDF、搜索索引和字体工程长期默认“文字就是拉丁字母那一套”。阿拉伯文不是换字体就能解决的语种,它要求渲染引擎、字体、编码规范和数据清洗一起工作。

一个对齐工单暴露了网页排版的默认偏见

原文作者遇到的工单很具体:阿拉伯文从右向左排版,左边缘参差不齐;设计稿要的是两端齐。CSS 的 text-align: justify 能做的,主要是拉伸词与词之间的空白。

但阿拉伯传统排版不是这样做的。手稿和经典印刷中,行宽通常靠字母内部连笔延展来填满,这种延展叫 kashida,现代编码里也有 U+0640 TATWEEL 可用于表示延长线。原文演示里靠手工插入 tatweel 才接近设计稿效果,这不是推荐工程方案,而是承认网页平台在高级阿拉伯排版上的能力不足。

场景拉丁文字常见做法阿拉伯传统做法工程后果
两端对齐拉大词间空格字内连笔延展 kashidaCSS 默认模型不够用
字形选择字母形态相对固定按上下文变形必须依赖 shaping engine
文本存储字符接近显示形态存抽象字母字体和 OpenType 负责成形

这里不能简单归咎于“浏览器不行”。现代操作系统和字体回退机制已经能完成多数基础显示。真正难的是高级排版:行宽、连写、变体、标音符号堆叠,以及不同语言社区对同一字母体系的不同习惯。

阿拉伯文必须在渲染时“成形”

阿拉伯字母天然连写。很多字母会根据位置呈现 isolated、initial、medial、final 等不同字形,六个字母还不能向后连接,会打断词内连笔。也就是说,存储里的一个字符,到屏幕上不一定对应一个固定外观。

现代正确做法是:Unicode 存抽象字母,字体提供字形,shaping engine 在渲染时调用 OpenType 特性,例如 isolinitmedifinarligmark。HarfBuzz、Core Text、DirectWrite、Pango 这类文本栈的价值,正在这里体现。

横向看,拉丁文字排版的复杂度更多落在断行、连字、字距和语言规则;阿拉伯字母体系则把“字母变成什么样”本身交给渲染过程。PDF 生成库如果没有 shaping,名字就可能被打成一串互不连接的孤立字母。对用户来说,这不是小瑕疵,而是合同、票据、客服后台里“名字看起来不对”。

受影响范围也不止阿拉伯语。波斯语、乌尔都语、普什图语、库尔德语、维吾尔语等都使用或扩展阿拉伯字母体系。Noto Sans Arabic、Noto Nastaliq Urdu 等字体家族之所以分支复杂,正是因为同一套字母在不同语言里有不同的可读性要求。

搜索失败提醒后端:看起来一样,不等于字符串一样

原文里最有工程警示意义的案例,不在前端,而在搜索索引。一个客户数据库里,一部分姓名来自现代 Unicode 输入,另一部分来自遗留系统,使用 Arabic Presentation Forms。两者显示几乎一样,但底层码点不同,搜索自然只命中其中一类。

Arabic Presentation Forms 是 Unicode 为兼容早期编码留下的区域,大致位于 U+FB50 到 U+FEFF。它们编码的是“显示形态”,不是现代文本应存的抽象字母。新文本不该写入这些码点,但 PDF 抽取、历史导入和旧系统迁移仍会把它们带进数据库。

修复路径并不神秘:搜索、去重、索引入库前应做 Unicode 规范化,原文提到 NFKC 可以把表现形式归并回抽象字母。但难点在排查。客服看到的是“客户不在系统”,数据库看到的是“字符串不同”,没有人一开始会想到去看码点。

接下来最该观察的不是哪家浏览器先补一个选项,而是团队有没有把多语言文本当作基础设施来治理:PDF 库是否支持 shaping,搜索链路是否做规范化,字体是否覆盖目标语言社区,测试数据里有没有真实姓名和真实段落。对前端和后端工程师来说,这比临时塞 tatweel 更有价值。