← Blog

Konsolda sağdan sola metin

Text.js, Unicode Çift Yönlü Algoritması’nı uygular ve BidiTest.txt dosyasındaki 770.241 testin tamamını geçer. İbranice ve Arapça, Arapça harf biçimlendirmesiyle hücre ızgarasına hizalı olarak görsel sırada render edilir.

Adam

Text.js artık İbranice, Arapça, Farsça ve diğer sağdan sola yazıları doğru sırayla, Arapça harfleri birleştirerek, diğer her şeyle aynı hücre ızgarasında render ediyor.

import { Box, Text } from "@textjs/react";export default function Page() {  return (    <Box flexDirection="column" padding={1}>      <Text>שלום עולם! זהו טקסט בעברית.</Text>      <Text>مرحبا بالعالم! هذا نص عربي.</Text>      <Text>Order #1234 (<Text color="accent">שלום</Text>) total: 25.50 ₪</Text>      <Text>user <Text dir="auto" color="success">שלום!</Text> replied 3 times</Text>    </Box>  );}

Her paragraf, HTML’de dir="auto" özelliğinin yaptığı gibi temel yönünü ilk güçlü karakterinden alır ve sağdan sola paragraflar sağa hizalanır. İç içe bir <Text> bileşeninde dir, span öğesini <bdi> gibi yalıtır; böylece yönü bilinmeyen bir kullanıcı adı, çevresindeki metnin sırasını değiştiremez.

Algoritma

Sağdan sola desteği, Unicode Çift Yönlü Algoritması’nın (UAX #9, Unicode 18.0) @textjs/core içinde sıfırdan yazılmış bir uygulamasıdır. Açık gömmeler ve yalıtımlar, zayıf ve nötr türler ve eşleşen parantezler dahil olmak üzere her paragrafın gömme düzeylerini çözümler. Ardından kaydırılmış her satır ayrı ayrı yeniden sıralanır ve aynalanan karakterler sağdan sola düzeylerde yer değiştirir; böylece İbranice içinde (abc), (abc) olarak kalır.

Unicode, algoritma için iki uygunluk dosyası yayımlar. İkisi de gzip ile sıkıştırılmış olarak depoya eklenmiştir ve test paketi her durumu çalıştırır:

  • BidiTest.txt, girdileri her birinin geçerli olduğu paragraf yönleriyle birlikte bidi sınıfı dizileri olarak listeler. Text.js, 770.241 durumun tamamını geçer.
  • BidiCharacterTest.txt, gerçek dizeleri beklenen düzeyleri ve sıralarıyla listeler. Text.js, 91.707 durumun tamamını geçer.
for (const [bit, dir] of [[1, -1], [2, 0], [4, 1]] as const) {  if ((bitset & bit) === 0) continue;  cases++;  const r = run(types, null, dir);  const gotLevels = fmtLevels(r.levels);  const gotOrder = r.order.join(" ");  if (gotLevels !== levels || gotOrder !== reorder) {    if (failures.length < 20) failures.push(`${input.trim()} [dir ${dir}]: levels ${gotLevels} (want ${levels}), order ${gotOrder} (want ${reorder})`);    else failures.push("");  }}

Mantıksal sıradan hücrelere

Sağdan sola bir metin satırı, konsola ulaşmadan önce dört adımdan geçer:

  1. Mantıksal sırayla satırlara bölme. Satırlar, hücre genişliğine göre soldan sağa metinle aynı noktalarda bölünür.
  2. Her satırı yeniden sıralama. Çözümlenen düzeyler, satırın grafemlerinin görüntülenme sırasını belirler.
  3. Arapçayı şekillendirme. Harfler, bağlamsal biçimlerini (yalın, başta, ortada, sonda) Arabic Presentation Forms bloklarından alır. Bir harf bir hücredir ve birleşik biçimler, sözcüğün ortasındaki bir imleç veya renk değişikliğinde korunur.
  4. Sırayı sabitleme. Hücreler artık glifleri görüntülenme sırasıyla tutar.

Son adım gereklidir çünkü DevTools, metin üzerinde bidi işlemini ikinci kez çalıştırıp sırayı tekrar tersine çevirir. DevTools’un temizleyicisi direction ve unicode-bidi özelliklerini kaldırdığı için bir %c stili bunu kapatamaz. Bu nedenle sürücü, sağdan sola karakterler içeren her span öğesini LRO ve PDF (U+202D ve U+202C) ile çevreler; bu da hücrelerin tuttuğu içeriğin aynen soldan sağa görüntülenmesini zorunlu kılar.

Yarım hücrelik kayma

Ne SF Mono ne de Menlo İbranice veya Arapça glifler içerir; bu nedenle tarayıcı her karakter için yedek yazı tipine başvurur. macOS’te bu yazı tipi, yine eş aralıklı olan Courier New’dur; 12 px boyutunda Menlo’nun 7,2246 px değerine karşılık glif başına 7,2012 px genişliğindedir. %0,3 daha dardır; bu fark, tam bir İbranice satırını yaklaşık yarım hücre kaydırmaya yeter.

CssConsoleDriver, sağdan sola her span öğesini konsol yazı tipini kullanan bir canvas ile ölçer; bu canvas, DevTools ile aynı Blink yedek yazı tipi mekanizmasını kullanır ve span öğesine LayoutUnits (1/64 px) cinsinden tam fark kadar dolgu ekler. Her span, kapladığı ızgara hücreleriyle aynı genişliğe ulaşır. Bir span en fazla yarım hücre daraltılır; böylece çok daha geniş bir yedek yazı tipi, sonraki span ile örtüşmek yerine taşar.

bun bench/devtools/bidi-shot.ts komutu, sonucu gerçek DevTools ön yüzünde, İbranice, Arapça, karma ve İbranice içinde emoji içeren satırlardan oluşan 64×21 boyutunda bir karede ölçer:

KareSatırlar arasındaki sağ kenar kayması
Sabitlenmeden günlüğe yazılan hücreler14,5 px
Sabitlenmiş, genişlik uyarlaması yok2,22 px
Yayındaki sürücü0,156 px
Kontrol: her RTL glifi x ile değiştirilmiş1,67 px

Kontrol, aynı karenin Latin harfli metin içeren hâlidir. Yayındaki sürücü, sağdan sola span öğelerinin içindeki emojilerin genişliği de bu öğelerle birlikte uyarlandığı için kontrolden daha iyi hizalanır.

Soldan sağa metnin değişmeden kalması

Sağdan sola karakter içermeyen metin, tüm bu işlemleri atlar. Kontrol, grafemler bölümlenirken grafem başına bir karşılaştırmayla yapılır ve tamamen soldan sağa olan metin öncekiyle aynı işlem yolundan çizilir; bu nedenle konsol kareleri bayt düzeyinde aynıdır.

Sağdan sola metin desteği Text.js 1.0 ile sunulur. Uluslararasılaştırma, girdi, düzenleme, terminaller ve API’yi kapsar.