阿拉伯语RTL网页开发指南 是做什么的?
一个用于确保阿拉伯语页面自然阅读、在375px宽度下布局正确、且不会打乱数字、URL或英语单词的清单。请应用所有与任务相关的部分,然后执行最终检查清单。
1. 方向和语言
- 根元素:在HTML中(而非通过JavaScript添加)使用
<html lang="ar" dir="rtl">,以确保首次渲染即为RTL。 - 不要用CSS的
direction: rtl替代dir属性,该属性还驱动双向文本、表单控件和屏幕阅读器。 - 阿拉伯语页面中的英语块:使用
<p lang="en" dir="ltr">。用户输入的内容(姓名、评论、聊天):使用dir="auto"。
2. 使用逻辑属性的布局
物理的左侧/右侧永远不会自动翻转。使用逻辑属性可使一个样式表在两种方向上都能正常工作。
| 避免使用的属性 | 应使用的属性 |
|---|---|
margin-left, padding-right | margin-inline-start, padding-inline-end |
left: 0, right: 0 | inset-inline-start: 0, inset-inline-end: 0 |
text-align: left | text-align: start |
border-left | border-inline-start |
border-top-left-radius | border-start-start-radius |
float: left | float: inline-start |
- Flexbox和Grid已经遵循
dir属性:在RTL中,flex-direction: row从右侧开始。切勿为了“修复”RTL而添加row-reverse。 transform: translateX()、background-position和 box-shadow 偏移是物理属性。在需要时,使用:dir(rtl)或[dir="rtl"]进行翻转。- Tailwind CSS:使用
ms-*、me-*、ps-*、pe-*、start-*、end-*、text-start类,以及rtl:/ltr:变体。
3. 阿拉伯语网页字体
- 选择专为阿拉伯语设计的字体家族(IBM Plex Sans Arabic, Noto Naskh Arabic, Noto Sans Arabic, Tajawal, Cairo, Almarai, Readex Pro)。将装饰性字体(如Aref Ruqaa)仅保留给标题使用。
- 自托管WOFF2文件:无需第三方请求,可离线工作,不会向字体CDN泄露信息。请检查许可证(OFL允许自托管)。
- 使用
font-display: swap并通过unicode-range分隔阿拉伯语和拉丁语字体文件,以便每个页面只下载其使用的部分:
@font-face { font-family: 'Plex'; font-weight: 400; font-display: swap;
src: url(plex-400-ar.woff2) format('woff2');
unicode-range: U+0600-06FF, U+0750-077F, U+08A0-08FF, U+200C-200E, U+FB50-FDFF, U+FE70-FEFC; }
@font-face { font-family: 'Plex'; font-weight: 400; font-display: swap;
src: url(plex-400-lat.woff2) format('woff2');
unicode-range: U+0000-00FF, U+2000-206F; }
body { font: 400 17px/1.75 'Plex', 'Segoe UI', Tahoma, sans-serif; }
- 阿拉伯语需要更高的行高(1.6到1.8)以容纳变音符号和下伸部分,并且字号通常比拉丁文本稍大。
- 切勿在阿拉伯语上使用
letter-spacing(它会破坏字母连接)。不要使用text-transform,不要使用假斜体。避免在窄列上使用text-align: justify。 - 不要使用塔特威尔(Tatweel,U+0640)拉伸单词,它会破坏搜索和复制粘贴。
4. 双向文本:数字、URL和阿拉伯语中的英语
Unicode双向算法会重新排序数字和拉丁文本旁边的中性字符。在RTL段落中有两个经典错误:
- 电话号码
+966 55 123 4567会以相反顺序显示其数字组:4567 123 55 +966。 - 时间范围
10:00 - 12:00会显示为12:00 - 10:00。
修复方法:
- 对未知方向的内插值使用
<bdi>:مرحبًا <bdi>${userName}</bdi>، أهلًا بك。 - 对始终为LTR的值使用
dir="ltr":<span dir="ltr">+966 55 123 4567</span>、URL、电子邮件、文件路径、IBAN、<code dir="ltr">、<pre dir="ltr">。 - 对重复出现的模式使用CSS:
.num, .code { unicode-bidi: isolate; }。注意,<bdi>默认即具有隔离效果。 - 针对电子邮件、URL和电话的输入框:
<input type="email" dir="ltr">。 - 在无法使用标记的地方(文档标题、通知、纯文本字符串),使用Unicode隔离字符包裹:FSI U+2068 ... PDI U+2069,或对于已知为LTR的文本使用 LRI U+2066 ... PDI。
5. 数字:每个页面统一使用一种数字系统
- 选择拉丁数字
0-9或阿拉伯-印度数字٠-٩(U+0660 至 U+0669),并在页面的所有位置(包括表格、图表和日期)统一使用。 - 混用数字会让读者困惑(同时包含两种数字的表格看起来像是错误),会破坏搜索功能(“15”无法匹配“١٥”)、排序以及复制粘贴到电子表格中的操作。
- 波斯语数字
۰-۹(U+06F0 至 U+06F9)是不同的码点,请勿混入。 Intl的默认值因语言环境和引擎版本而异:在Node 24中,ar-SA使用阿拉伯-印度数字格式化,而纯ar则给出拉丁数字。请显式设置数字系统:new Intl.NumberFormat('ar-SA-u-nu-latn')或'ar-SA-u-nu-arab'。- 使用阿拉伯-印度数字时,小数点分隔符是
٫(U+066B),千位分隔符是٬(U+066C)。 - 实际陷阱:在阿拉伯-印度数字旁用作分隔符的中间点(U+00B7)看起来像阿拉伯-印度零(U+0660),它也是一个小点。“12, separator, 3” 会被读作“1203”。请改为使用阿拉伯逗号«،»、斜杠或额外间距(CSS
gap)进行分隔。
6. 阿拉伯语标点符号
- 使用
،(U+060C)、؛(U+061B)和؟(U+061F),而不是,;?。句号和!是共享的。 - 标点符号前无空格,后有一个空格:«كيف حالك؟» 而不是 «كيف حالك ؟»。
- 每个页面统一选择一种引号风格(«...» 或 "...")。
7. 图标与镜像
镜像显示方向或顺序的图标:后退和前进箭头、列表和面包屑导航中的尖括号、步骤指示器和进度方向、“发送”图标、文本缩进和列表图标。
不要镜像的图标:Logo和品牌标记、媒体控制(播放、倒带、快进)、时钟和计时器、复选标记、照片,以及包含拉丁字母或数字的图标。
:dir(rtl) .icon-dir { transform: scaleX(-1); } /* 选择加入的类,切勿全局应用 */
8. 来自阿拉伯语键盘的数字输入
- 优先使用
<input inputmode="decimal" autocomplete="off">而不是type="number"。数字输入的值仅对ASCII数字有效,因此阿拉伯-印度数字可能返回空字符串;它还会添加微调按钮(spinners)。 - 用户输入
٠-٩、阿拉伯小数点分隔符٫,有时还有波斯语数字。在调用Number()之前进行规范化处理:
function parseArabicNumber(raw) {
const s = String(raw ?? '').trim()
.replace(/[٠-٩]/g, (d) => d.charCodeAt(0) - 0x0660)
.replace(/[۰-۹]/g, (d) => d.charCodeAt(0) - 0x06F0)
.replace(/٫/g, '.')
.replace(/٬/g, '');
if (s === '') return null; // Number('') 是 0,所以先检查是否为空
const n = Number(s);
return Number.isFinite(n) ? n : NaN;
}
parseArabicNumber('٩٥٫٥'); // 95.5
- 决定如何对待输入的
,(作为十进制标记还是千位分隔符)并记录在案。 - 使用阿拉伯语显示验证消息,设置
aria-invalid,并使用aria-describedby链接消息。 - 使用
new Intl.Collator('ar').compare对阿拉伯语名称进行排序。对于搜索,对查询词和数据同时规范化阿里夫(Alef)形式(أ إ آ 变为 ا),移除塔什基尔(Tashkeel)和塔特威尔(Tatweel)。
9. 测试
- 在375px宽(如果可能,320px)的环境下测试。确认
document.documentElement.scrollWidth <= innerWidth。 - 使用带有连写字母的阿拉伯语长单词(如 «والمستشفيات» 或 «باستخداماتها»)测试标题和按钮。在标题和卡片上使用
overflow-wrap: anywhere,并在flex和grid子项上使用min-width: 0。 - 使用包含混合英语和数字的真实阿拉伯语内容,而不是Lorem Ipsum。
- 在iOS上的Safari和Android上的Chrome上进行测试,并检查200%缩放和键盘焦点顺序(Tab键应从右向左移动)。
10. [hidden] 隐藏属性的陷阱
hidden 属性仅起到用户代理 display: none 的作用。任何作者规则(例如 .card { display: flex })都会覆盖它,因此 el.hidden = true 会静默失效。请将以下代码添加到基础样式表中:
[hidden] { display: none !important; }
最终检查清单
-
<html lang="ar" dir="rtl">存在于源代码中 - 新CSS中未遗留物理的左侧/右侧属性
- 已自托管阿拉伯语字体,使用了
font-display: swap和unicode-range分隔 - 电话号码、URL、代码和用户名已被隔离
- 统一使用一种数字系统;阿拉伯-印度数字旁没有中间点
- 使用了阿拉伯语标点符号,且其前无空格
- 仅镜像了方向性图标
- 数字输入可接受
٠-٩和٫ - 在375px宽度下无水平滚动条;长单词可换行
- 已包含
[hidden] { display: none !important; }