1 điểm bởi GN⁺ 4 giờ trước | 1 bình luận | Chia sẻ qua WhatsApp
  • Typography web tiếng Ả Rập là một vấn đề hạ tầng render nơi việc nối chữ, văn bản hai chiều, xử lý chữ số·dấu câu và căn dòng đan xen với nhau, nên rất khó coi đây chỉ là lỗi CSS đơn thuần
  • Dàn chữ Ả Rập cổ điển căn đều hai lề không bằng cách nới khoảng trắng giữa các từ mà bằng kashida — kéo dài nét bên trong chữ — nhưng text-align: justify của trình duyệt hiện đại chủ yếu lại tăng khoảng cách giữa các từ
  • Một code point được lưu của chữ Ả Rập sẽ đổi thành dạng đứng riêng, đầu, giữa hoặc cuối tùy ngữ cảnh; nếu không có tính năng OpenType và shaping engine thì chữ sẽ được render thành các ký tự rời rạc
  • Arabic Presentation Forms của Unicode, các hệ chữ số, thuật toán hai chiều UAX #9 và các ký tự điều khiển vô hình dẫn tới những vấn đề thực tế trong sản phẩm như tìm kiếm thất bại, số điện thoại bị đảo thứ tự và con trỏ di chuyển khó hiểu
  • Các nền tảng cốt lõi như HarfBuzz, Amiri, W3C Arabic Layout Requirements đã có, nhưng việc căn chữ Ả Rập trong trình duyệt và khai thác jstf vẫn còn là khoảng trống trong triển khai

Điểm khởi đầu: vấn đề dàn chữ Ả Rập trông như một “lỗi CSS”

  • Đoạn văn tiếng Ả Rập có nội dung pha trộn trên dashboard khách hàng không được render căn đều hai lề như bản thiết kế, mà mép trái hiện lên lởm chởm
  • Bản dùng ký tự Latin trong cùng khối trông vẫn “ổn”, nhưng với tiếng Ả Rập, dòng bắt đầu từ bên phải nên mép lởm chởm xuất hiện ở bên trái
  • Dù áp dụng text-align: justify, kết quả vẫn không thể lấp đầy dòng bằng cách kéo dài nét bên trong từ như hình thức mà đội thiết kế đã duyệt
  • Trong cùng sản phẩm, trước đó cũng từng lặp lại các vấn đề xử lý tiếng Ả Rập như chữ trong tên bị tách rời trong PDF, lập chỉ mục tìm kiếm thất bại, hay rắc rối với code point Unicode cũ
  • Bản chất của vấn đề không nằm ở một stylesheet cụ thể mà ở tình trạng typography tiếng Ả Rập trên web

Vấn đề mà truyền thống chép tay từng giải quyết

  • Truyền thống chép tay tiếng Ả Rập cổ điển căn đều hai lề bằng cách kéo dài nét nối bên trong hình chữ thay vì tăng khoảng trắng giữa các từ
  • Cách này được gọi là taṭwīl, hay theo thuật ngữ kỹ thuật hiện đại là kashida, tức kéo dài nét nối giữa một số cặp chữ nhất định
  • Một trang Naskh thế kỷ 17 được dàn tốt có hai lề khớp nhau mà không làm tăng khoảng cách từ, tạo nên kết cấu dày và đều
  • al-khaṭṭ al-mansūb do Ibn Muqla hệ thống hóa đã quy chuẩn hình chữ qua các yếu tố như chấm hình thoi của ngòi bút sậy, chiều cao alif và tỷ lệ cung tròn
  • Trong truyền thống này, căn dòng không phải bài toán phân phối khoảng trắng mà là bài toán shaping: chọn hình chữ và glyph thay thế phù hợp

Một chữ cái, bốn hình dạng

  • Chữ Ả Rập luôn là hệ chữ nối nét như chữ viết tay, không có sự phân tách kiểu block letter giữa chữ in và chữ viết tay
  • Mỗi chữ thay đổi thành dạng đứng riêng, đầu, giữa hoặc cuối tùy theo chữ bên cạnh, và có sáu chữ không nối về phía trước nên làm đứt mạch bên trong từ
  • Unicode lưu ký tự trừu tượng, font cung cấp glyph theo vị trí, còn shaping engine áp dụng các tính năng OpenType như isol, init, medi, fina, rlig, mark, mkmk
  • Một từ như محمد được lưu thành bốn code point, nhưng khi render sẽ qua nhiều bước chọn glyph và tra cứu OpenType để hiện thành một nét nối liền mạch
  • Nếu không có shaping engine như HarfBuzz, hoặc bộ tạo PDF không đi qua bước này, cùng các code point đó sẽ bị render thành các chữ đứng riêng tách rời nhau

Hóa thạch của Unicode: Arabic Presentation Forms

  • Các code page 8-bit thời DOS và Windows đời đầu mã hóa chính các hình dạng như dạng đầu hay dạng giữa, thay vì mã hóa ký tự trừu tượng
  • Unicode đã chấp nhận chúng để tương thích khứ hồi, và chúng vẫn còn nằm trong khối Arabic Presentation Forms từ U+FB50 đến U+FEFF
  • Các tài liệu mới không nên chứa các code point này, nhưng công cụ trích xuất văn bản từ PDF đến nay vẫn có thể xuất chúng ra
  • Nếu cùng một tên được lưu một lần bằng Unicode hiện đại và một lần bằng Presentation Forms, chúng trông giống hệt nhau trên màn hình nhưng lại bị xử lý khác nhau khi so chuỗi và tìm kiếm
  • Áp dụng chuẩn hóa NFKC có thể gộp Presentation Forms về ký tự trừu tượng, giúp giảm hiện tượng bỏ sót khi tìm kiếm

Phần mềm bỏ qua shaping và xử lý hai chiều

  • Phần mềm bỏ qua shaping engine và thuật toán hai chiều sẽ vẽ từng chữ ở dạng đứng riêng và xếp dòng từ trái sang phải
  • Đây là kiểu đầu ra mà người đọc tiếng Ả Rập thực sự vẫn gặp trên biển hiệu cửa hàng, thẻ lên máy bay, watermark hay chữ Ả Rập trong các phim cũ
  • Photoshop cũ, matplotlib với cấu hình mặc định, nhiều bộ tạo PDF của npm và máy in hóa đơn đều có thể sinh ra vấn đề này
  • Cách lách phổ biến trong Python là arabic_reshaperpython-bidi, vốn dùng khối Presentation Forms để nướng sẵn hình dạng đã được shaping vào trong chuỗi
  • Cách lách này thực chất là đẩy việc của renderer vào ngay trong chuỗi, và qua đó phơi bày khiếm khuyết nền tảng của text stack

Ba loại chữ số và rắc rối dấu câu

  • Các số 0–9 mà thế giới gọi là “Arabic numerals” thực ra không phải hình dạng chữ số mà đa số người đọc tiếng Ả Rập dùng hằng ngày
  • Ai Cập, Sudan, Levant, Iraq và khu vực Vùng Vịnh dùng ARABIC-INDIC DIGITS của Unicode: ٠١٢٣٤٥٦٧٨٩
  • Khu vực Maghreb dùng glyph số Latin, còn Iran·Afghanistan·Pakistan dùng EXTENDED ARABIC-INDIC DIGITS: ۰۱۲۳۴۵۶۷۸۹
  • Trong UAX #9, chữ số không được xử lý như ký tự mạnh mà là ký tự yếu, và sẽ bị phân loại lại thành số châu Âu hoặc số Ả Rập theo hướng tính của ký tự mạnh đứng trước
  • Một số điện thoại như 010-1234-5678 đứng sau từ tiếng Ả Rập có thể hiện thành 5678-1234-010 trên màn hình vì dấu gạch nối bị xử lý như ký tự trung tính
  • Cách giải quyết mà nền tảng cung cấp là bọc số bằng hoặc <bdi> để tách hướng tính
  • Dấu thập phân và phân tách hàng nghìn trong thế giới Ả Rập là U+066B ٫ và U+066C ٬; ASCII ., trông gần giống nhưng khác code point và thuộc tính hai chiều

Những cách lách và đơn giản hóa kéo dài từ in ấn tới web

  • Kitāb Ṣalāt al-Sawāʿī in tại Fano năm 1514 là sách chữ Ả Rập bằng con chữ rời đầu tiên, và cho thấy các lỗi như đứt nối chữ và sai vị trí dấu chấm
  • Bản Qurʾān Paganini in tại Venice năm 1537 thất bại thương mại vì vừa lỗi dàn chữ vừa lỗi văn bản; một bản được tìm thấy vào năm 1987 tại thư viện tu viện ở Venice
  • Câu chuyện về lệnh cấm in ấn Ottoman có vấn đề ở chỗ không còn nguyên văn sắc lệnh của Bayezid II và Selim I, nên phải dựa vào ghi chép của du khách châu Âu
  • Bulaq Press ở Cairo do Muhammad Ali lập năm 1820 đã nâng chất lượng chữ kim loại Ả Rập nhờ hàng trăm mảnh chữ và rất nhiều kiên nhẫn
  • Cairo Qurʾān năm 1924 được sản xuất bằng chữ kim loại tại Amiria Press và góp phần chuẩn hóa văn bản lẫn typography trong thế kỷ 20
  • Cuối thập niên 1950, Kamel Mrowa và Linotype tạo ra Simplified Arabic để phù hợp với máy dàn tạp chí 90 kênh, bằng cách gộp dạng đầu vào dạng giữa, dạng cuối vào dạng đứng riêng và giảm số ligature
  • Simplified Arabic giúp việc sản xuất báo chí rẻ và nhanh hơn, và trong chưa đầy một thế hệ đã chiếm lĩnh newsroom tiếng Ả Rập

Kashida mà web vẫn chưa vẽ được

  • Trong bản thảo đầu của CSS Text Module Level 3 từng có giá trị kashida cho text-justify, và Internet Explorer 5.5 đã triển khai nó từ năm 2000
  • IE 5.5 cũng cung cấp thuộc tính text-kashida-space, nhưng khi các trình duyệt khác không triển khai thì giá trị đó bị loại khỏi đặc tả
  • Trên Chrome, Firefox và Safari hiện đại, text-align: justify trong tiếng Ả Rập hoạt động bằng cách tăng khoảng trắng giữa các từ
  • Vấn đề căn chữ Ả Rập trong CSS Working Group đã được mở ít nhất từ năm 2015, và công việc W3C Arabic Layout Requirements cũng bắt đầu cùng năm đó
  • Căn dòng bằng kashida đòi hỏi shaping và layout phải thương lượng lặp đi lặp lại theo từng dòng, vì glyph bị kéo dài làm đổi bề rộng, mà bề rộng đổi lại làm thay đổi chỗ xuống dòng và lượng kéo giãn cần thiết
  • OpenType đã có bảng jstf từ thập niên 1990 để font báo ưu tiên căn dòng, nhưng shaping engine hầu như không đọc và nhà làm font cũng hiếm khi cung cấp
  • Microsoft Word và InDesign Middle East Edition có hỗ trợ căn dòng bằng kashida, nhưng bộ renderer trình duyệt — nơi mọi người đọc nhiều nhất — vẫn không kéo dài được nét chữ

Các mẹo Tatweel và vấn đề ligature·dấu nguyên âm

  • Cách lách phổ biến trên web là chèn trực tiếp ký tự U+0640 TATWEEL vào văn bản để nó trông như một nét kéo dài
  • Tatweel làm thay đổi chính nội dung nên gây vấn đề cho khớp tìm kiếm, copy-paste, screen reader và reflow cột
  • Nét mà Tatweel vẽ không phải kashida được đặt theo quy tắc của font và ký tự, mà là một thanh kiểu máy đánh chữ do người viết phỏng đoán rồi chèn vào
  • Ligature trong OpenType được chia thành rlig, liga, dlig; nếu ligature bắt buộc như lām-alif bị vỡ, văn bản không chỉ xấu mà còn trở nên sai
  • Chèn U+200C ZERO WIDTH NON-JOINER giữa các chữ sẽ giữ nguyên ký tự được lưu nhưng buộc renderer hiển thị mỗi chữ ở dạng đứng riêng
  • Safari bỏ qua "rlig" 0, "liga" 0, nên demo vô hiệu hóa ligature bắt buộc không có tác dụng trên đó
  • Amiri là font Naskh do Khaled Hosny phát hành năm 2011 theo SIL Open Font License; sau bản viết lại 1.0 năm 2022, nó hỗ trợ kashida cong và xếp chồng dấu nguyên âm tinh vi
  • Thành phần card kết hợp line-height: 1 với overflow: hidden có thể cắt mất các dấu nguyên âm phía trên của tiếng Ả Rập được ghi đầy đủ nguyên âm

Thuật toán hai chiều và con trỏ biết nói dối

  • Số phiên bản, identifier tiếng Anh, URL hay từ tiếng Pháp bên trong một đoạn tiếng Ả Rập đều kích hoạt UAX #9, tức Unicode Bidirectional Algorithm
  • Chữ Ả Rập là ký tự mạnh phải-sang-trái, chữ Latin là ký tự mạnh trái-sang-phải, chữ số là ký tự yếu theo ngữ cảnh, còn khoảng trắng và dấu câu là ký tự trung tính
  • Thuật toán sẽ gán lớp hướng cho từng ký tự, diễn giải dần các ký tự yếu và trung tính, sau đó gán embedding level và đảo các run cùng cấp
  • Vì thứ tự hiển thị trên màn hình khác với thứ tự logic trong bộ nhớ, việc di chuyển con trỏ, nhấp chuột và chọn văn bản phải liên tục dịch qua lại giữa hai thứ tự này
  • Ở ranh giới giữa các run có hai vị trí con trỏ hợp lệ — vị trí logic và vị trí hiển thị — và Chrome, Firefox, Qt, Outlook có thể xử lý chúng khác nhau
  • Việc soạn văn bản pha trộn Ả Rập–Anh đến cả năm 2026 vẫn là trải nghiệm mặc định có chi phí nhận thức cao trên các trình soạn thảo chính, ứng dụng email và ứng dụng chat
  • Một khoảng như الصفحات 10-20 có thể trông như “từ 20 đến 10” do quy tắc W2 và cách xử lý dấu gạch nối trung tính; có thể sửa bằng cách chèn U+200E LEFT-TO-RIGHT MARK ở phía trước

Nền tảng đã hoạt động và khoảng trống còn lại

  • Khaled Hosny đã tạo ra Amiri, viết công cụ dòng lệnh HarfBuzz hb-shape, và cũng là đồng bảo trì HarfBuzz
  • Behdad Esfahbod đã viết phần lớn HarfBuzz trước thời Hosny, và đóng góp cho shaping engine hiện giúp trình duyệt vẽ đúng chữ Ả Rập
  • Brill đã đặt John Hudson thiết kế typeface Brill để bao gồm toàn bộ ký tự phiên âm cần cho catalog Semitics, rồi phát hành miễn phí cho mục đích phi thương mại năm 2011
  • Sakhr AX-170 là máy tính MSX Saudi-Kuwaiti khoảng năm 1984 hiển thị tiếng Ả Rập từ ROM, và hỗ trợ identifier Arabic BASIC viết từ phải sang trái
  • HarfBuzz, Amiri, Scheherazade, hỗ trợ Presentation Forms trong GNU Unifont, Noto Arabic và tài liệu W3C Arabic Layout đều phụ thuộc rất nhiều vào nỗ lực của một số ít cá nhân, tổ chức và tình nguyện viên
  • Các nhà cung cấp trình duyệt đã tiếp nhận HarfBuzz sau khi nó được hoàn thiện miễn phí, nhưng hầu như không đóng góp cho vòng lặp layout cần để hiện thực hóa cách căn dòng của truyền thống chép tay trên màn hình
  • Khoảng cách còn lại là một thuật toán đã được hiểu khá rõ, chỉ cần được triển khai trong một vài layout engine; và mép trái lởm chởm trên dashboard khách hàng chính là cách sự thiếu đầu tư này hiện ra trước mắt người dùng

1 bình luận

 
Ý kiến trên Lobste.rs
  • Bài này thực sự quá xuất sắc
    Và tôi thích việc ký tự này là chỉ một code point. Thử sao chép sẽ thấy khá kỳ diệu
    Ý nghĩa của nó là “Nhân danh Allah, Đấng Rất mực Khoan dung, Đấng Rất mực Từ bi”

    • Câu này trong bài về rất giàu chất thơ
      “Một tượng đài từ thời mà không ai tin nổi engine render, nên việc render được nướng thẳng vào encoding. Như một con ruồi đang tụng niệm bị bảo tồn vĩnh viễn trong khối hổ phách”
    • Ký tự Unicode đó cũng từng được bàn ở đây: https://lobste.rs/s/7s4sjp
      Tôi đã thấy khá thú vị khi lần theo các tài liệu tham khảo từ liên kết Wikipedia được dẫn ở đây: https://lobste.rs/c/dq2ucz
      Tóm lại, ký tự này được đưa vào Unicode vì nó đã có trong code page của Pakistan, và nó có mặt ở đó vì có yêu cầu pháp lý rằng cụm từ này phải được đưa vào các văn bản pháp luật. Urdu là một ngôn ngữ thuộc ngữ hệ Ấn-Âu, nên với công nghệ thời đó hẳn sẽ khó mà “gọi ra ngoài” sang code page Arabic để viết Basmallah
      Đáng tiếc là không phải mọi bình luận đều thể hiện được những điểm mạnh của cộng đồng đó
  • Meta: đây lại là một ví dụ tuyệt vời cho thấy các bài như thế này cần có thẻ typography

    • Tôi không hiểu vì sao lại cần. Chủ đề này có vẻ khá được ưa thích mà, có thật là có nhu cầu mạnh đến mức muốn ẩn kiểu nội dung này không?
      Trên lobste.rs, thẻ chủ yếu dùng để lọc
  • Thuộc tính text-justify của IE cơ à, thời đó đúng là có nhiều thứ thú vị. Còn có cả text-justify: newspaper, mà vài chục năm sau một số người giải thích nó như Knuth-Plass hay gì đó tương tự, nhưng tôi không tin là thực sự như vậy
    https://mediumwell.com/wp-content/uploads/… cho thấy hành vi khi đó được cho là text-justify: newspaper trùng khớp với text-justify: inter-character trong đặc tả ngày nay
    IE thực sự đã có khá nhiều tính năng hay từ rất sớm, còn các trình duyệt khác thì để chúng trong chiếc giỏ “quá khó”. Cuối cùng có những thứ chẳng bao giờ quay lại, hoặc phải 15 năm hay 30 năm sau mới trở lại. Firefox có text-justify: inter-character vào năm 2017, Chromium chỉ mới triển khai phần đó vài tháng trước, còn Safari thì vẫn chưa có

  • Một bài viết cực kỳ xuất sắc và bổ ích. Tôi đặc biệt thích bối cảnh lịch sử vì nó mang lại một khung nền rộng cho toàn bộ câu chuyện
    Với tư cách là người vừa có bằng cấp liên quan đến lịch sử vừa có sự nghiệp trong ngành IT, bài này chạm trúng hoàn hảo cả hai mối quan tâm của tôi

  • Có chỗ nào đó trong bài làm máy dò LLM của tôi kêu lên, khá đáng tiếc. Nhất là vì bài có chiều sâu và nói về những phần ít được tài liệu hóa trong stack công nghệ hiện đại
    Một ví dụ về đoạn nghe rất giống LLM là thế này, và cảm giác đó xuất hiện xuyên suốt bài:
    “Lý do không có trình duyệt nào cung cấp điều này là có tính cấu trúc, và với tư cách một trở ngại thì cấu trúc đó khá thanh nhã. Căn lề Latin xem văn bản đã được shaping là thứ cố định, đo các từ, đổ phần không gian còn lại vào khoảng cách, rồi xong. Shaping và layout nằm yên trong các chiếc hộp riêng của chúng, và mọi text stack đang hoạt động đều được thiết kế xoay quanh sự tách biệt đó. Căn lề Kashida thì mở toang chiếc hộp ấy ra.”
    Tôi muốn hỏi @lr0 liệu phần thân bài này có được LLM tạo ra, biên tập, hay dịch hay không. Nếu có, có lẽ nên điều chỉnh mức độ kiểm soát mà LLM có đối với đầu ra cuối cùng. Những bài blog trước đây, chẳng hạn https://lr0.org/blog/p/gpt/https://lr0.org/blog/p/linux_new_users/ , cho cảm giác con người hơn nhiều