阿拉伯語 RTL 網頁 是做什麼的?
一份用於阿拉伯語頁面的檢查清單,確保頁面閱讀自然、在 375px 寬度下正確佈局,且絕不搞亂數字、網址或英文單詞。請根據任務套用相關的所有小節,最後執行最終檢查清單。
1. 方向與語言
- 根元素:
<html lang="ar" dir="rtl">,需寫在 HTML 中(而非由 JavaScript 添加),以便首次繪製時即是 RTL。 - 不要用 CSS
direction: rtl取代dir屬性。該屬性同時驅動雙向文字 (bidi)、表單控制項和螢幕閱讀器。 - 阿拉伯語頁面中的英文區塊:
<p lang="en" dir="ltr">。使用者輸入的文字(姓名、評論、聊天):使用dir="auto"。
2. 使用邏輯屬性的版面配置
物理性的 left/right 永遠不會翻轉。使用邏輯屬性,讓一份樣式表在兩種方向下都能運作。
| 避免使用 | 使用 |
|---|---|
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從右側開始。切勿添加row-reverse來「修正」RTL。 transform: translateX()、background-position和 box-shadow 偏移量是物理性的。必要時,使用:dir(rtl)或[dir="rtl"]來翻轉它們。- Tailwind:使用
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. 雙向文字 (Bidi):數字、網址和阿拉伯語中的英文
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>、網址、電子郵件、檔案路徑、IBAN、<code dir="ltr">、<pre dir="ltr">。 - 針對重複模式使用 CSS:
.num, .code { unicode-bidi: isolate; }。請注意,<bdi>預設會隔離。 - 電子郵件、網址和電話的輸入框:
<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, 分隔符, 3」可能會被解讀為「1203」。請改用阿拉伯逗號 «،»、斜線或額外間距(CSS
gap)來分隔。
6. 阿拉伯語標點符號
- 使用
،(U+060C)、؛(U+061B) 和؟(U+061F),而不是,;?。句號和!是共用的。 - 標點符號前不留空格,之後留一個空格:使用 «كيف حالك؟» 而不是 «كيف حالك ؟»。
- 每個頁面選擇一種引號樣式 («...» 或 "...")。
7. 圖示與鏡像
鏡像那些顯示方向或順序的圖示:返回和下一步箭頭、列表和麵包屑中的 V 形符號、步驟顯示器和進度方向、「傳送」、文字縮排和列表圖示。
不要鏡像:標誌和品牌標記、媒體控制項(播放、倒轉、快轉)、時鐘和計時器、勾選標記、照片,以及包含拉丁字母或數字的圖示。
:dir(rtl) .icon-dir { transform: scaleX(-1); } /* 選擇性加入的類別,切勿全域使用 */
8. 來自阿拉伯語鍵盤的數字輸入
- 偏好使用
<input inputmode="decimal" autocomplete="off">而非type="number"。數字輸入框的值僅在包含 ASCII 數字時才有效,因此阿拉伯語-印度數字可能會傳回空字串;它也會添加微調按鈕。 - 使用者輸入
٠-٩、阿拉伯語小數分隔符號٫,有時還有波斯數字。在轉換為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 形式(أ إ آ 轉為 ا),並移除查詢資料和資料中的變音符號和 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 中沒有遺留物理性的 left/right 屬性
- 自行託管阿拉伯語字型、
font-display: swap、unicode-range分割 - 電話號碼、網址、程式碼和使用者名稱已隔離
- 統一的數字系統;阿拉伯語-印度數字旁無中間點
- 阿拉伯語標點符號,前面無空格
- 僅鏡像有方向性的圖示
- 數字輸入框接受
٠-٩和٫ - 在 375px 寬度下無水平滾動;長單詞會換行
-
[hidden] { display: none !important; }已存在