Uluslararasılaştırma
Sağdan sola ve karma yönlü metin, Arapça şekillendirme ve odak alanının çevirisi.
Text.js, İbranice, Arapça, Farsça ve diğer sağdan sola yazıları DevTools’ta, terminallerde ve DOM aynalarında hücre ızgarasına hizalı, doğru sırada render eder. Unicode Çift Yönlü Metin Algoritmasını (UAX #9, Unicode 18.0) uygular ve resmî uyumluluk testlerinin tamamını geçer: 770.241 BidiTest.txt vakasının tümü (her paragraf yönünde her girdi) ve 91.707 BidiCharacterTest.txt vakasının tümü.
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> );}Yön
Her paragraf (<Text> içindeki \n ile ayrılmış her satır), HTML’deki dir="auto" gibi temel yönünü ilk güçlü karakterinden belirler. Sağdan sola paragraflar varsayılan olarak sağa hizalanır.
<Text>שלום</Text> {/* auto: RTL, right-aligned */}<Text dir="rtl">hello!</Text> {/* "!hello", right-aligned */}<Text dir="ltr">שלום!</Text> {/* LTR paragraph: "םולש!" reads correctly left of the "!" */}<Text align="left">שלום</Text> {/* RTL order, pinned to the left edge */}align | Soldan sağa paragraf | Sağdan sola paragraf |
|---|---|---|
"start" (varsayılan) | sol | sağ |
"end" | sağ | sol |
"left" / "center" / "right" | yazıldığı gibi | yazıldığı gibi |
İç içe bir <Text> üzerinde dir, span öğesini HTML’deki <bdi> gibi yalıtır: içeriği kendi başına yerleştirilir ve çevresindeki metnin sırasını değiştiremez. Kullanıcıların sağladığı adlar, başlıklar ve yönü bilinmeyen diğer metinler için bunu kullanın.
<Text>user <Text dir="auto">{name}</Text> replied 3 times</Text>// name = "שלום!" → user !םולש replied 3 times (isolated: "!" ends the name)// without dir → user םולש! replied 3 times ("!" resolves with the Latin text around it)Render işleyişi
- Mantıksal sırada satırlara bölme. Satırlar, soldan sağa metinde olduğu gibi hücre genişliğine göre bölünür.
- Her satırı yeniden sıralama. Algoritma tüm paragrafın gömme düzeylerini çözümler (açık gömmeler ve yalıtımlar, zayıf ve nötr türler, eşleşen parantezler), ardından bölünmüş her satır kendi başına yeniden sıralanır (L1–L2 kuralları). Parantezler ve diğer aynalanan karakterler sağdan sola düzeylerde ters çevrilir (L4), böylece İbranice içindeki
(abc),(abc)olarak kalır. - Arapçayı biçimlendirme. Harfler bağlamsal biçimlerini (yalıtılmış, başta, ortada, sonda) Arabic Presentation Forms bloklarından alır; bunlar satırın her düzey dizisi içinde mantıksal sırada hesaplanır (ZWJ ve ZWNJ bulundukları yerde hesaba katılır). Bir harf bir hücredir, birleşik biçimler her türlü span sınırında (imleç, renk değişimi) korunur ve konsolun bunları biçimlendirmesi gerekmez.
- Sırayı sabitleme. Hücreler artık glifleri görüntüleme sırasında tutar. Tarayıcılar ve birçok terminal bunlara ikinci kez bidi uygulayacağından sürücüler sırayı kilitler:
| Çıktı | Sıranın sabitlenmesi |
|---|---|
DevTools (CssConsoleDriver) | Sağdan sola karakterler içeren span öğeleri LRO … PDF (U+202D / U+202C) içine alınır. DevTools’un %c temizleyicisi direction ve unicode-bidi değerlerini kaldırır. |
Terminaller (AnsiDriver, text run) | Satır başına bidi uygulayan terminaller için sağdan sola metin içeren satırlar LRO … PDF içine alınır. renderToTerminal, girişte bir kez ECMA-48 BDSM açık moduna (CSI 8 l, VTE tabanlı terminallerin uyduğu mod), çıkışta ise örtük moda (CSI 8 h) geçer; sağdan sola metin içeren tek seferlik renderToAnsi kareleri CSI 8 l ile başlar. Hücreleri olduğu gibi üretmek için bidi: "none" geçirin. |
DOM aynaları (<ConsoleMirror>) | Ayna direction: ltr kullanır (böylece RTL yönündeki bir ana sayfa onu ters çeviremez) ve sağdan sola metin içeren span öğelerine unicode-bidi: bidi-override uygulanır. |
<Box title>, <Box bottomTitle> ve <Divider title> aynı adımlardan geçer (drawLogicalText(buffer, x, y, text, …) herhangi bir tek satırı bu şekilde çizer), böylece etiketleri sıralı ve birleşik görüntülenir.
Sağdan sola karakter içermeyen metin tüm bu işlemleri atlar. Tamamen soldan sağa bir metin modeli, grafemleri bölümlenirken algılanır (grafem başına bir karşılaştırma) ve değişmeyen yoldan çizilir; konsol kareleri bayt düzeyinde aynıdır.
Hücre genişlikleri
Dağıtılan yazı tipi yığını "SF Mono", Menlo, "Cascadia Mono", Consolas, monospace şeklindedir. Bu yazı tiplerinin hiçbirinde İbranice veya Arapça glifler yoktur, bu nedenle tarayıcılar her karakter için yedek yazı tipine geçer. macOS’te bu, 0,6 em sabit genişlikli Courier New yazı tipidir: 12 px boyutta glif başına 7,2012 px, Menlo’da ise 7,2246 px. %0,3 daha dardır; bu, tam bir İbranice satırını yaklaşık yarım hücre kaydırmaya yeter.
CssConsoleDriver, sağdan sola her span öğesini konsol yazı tipindeki bir canvas ile ölçer (DevTools’un kullandığı aynı Blink biçimlendirmesi ve yedek yazı tipi seçimi), ardından tam LayoutUnit (1/64 px) farkı kadar dolgu ekler. Span, kapladığı ızgara hücreleri kadar geniş olur (emoji gibi geniş bir grafem iki sayılır). Bir span en fazla yarım hücre daraltılır; böylece orantılı bir yedek yazı tipi (Windows’ta Cascadia Mono yanında Times New Roman) sonraki span öğesinin glifleriyle örtüşmesine izin vermek yerine taşar. Gerçek DevTools ön yüzünde (Chromium 151), İbranice, Arapça, karma ve İbranice içinde emoji içeren satırlar ile RTL kutu başlıklarından oluşan 64×21 kare üzerinde ölçülmüştür (bun bench/devtools/bidi-shot.ts):
| Kare | Satırlar arasında sağ kenar kayması |
|---|---|
| Sabitlemeden günlüğe yazılan hücreler | 14,5 px (DevTools yeniden sıralar ve biçimlendirir) |
| Sabitlenmiş, genişlik uyarlaması yok | 2,22 px |
| Dağıtılan sürücü | 0,156 px |
Kontrol: aynı hücreler, her RTL glifi x ile değiştirilmiş | 1,67 px |
Dağıtılan kare, emoji satırı dışındaki her satırda tamamen Latin karakterli kontrol karesiyle eşleşir; emoji satırında ise daha iyi sonuç verir: emojiler, çevrelerindeki sağdan sola span ile birlikte uyarlanır (0,03 px), kontrol karesinin soldan sağa emoji satırı ise uyarlanmaz (1,66 px).
DevTools her %c span öğesini contain: paint ile çizer ve her birini ayrı ayrı cihaz piksellerine hizalar; bu nedenle iki arka planın kesirli bir konumda buluştuğu yerde aradaki piksel hiçbirine ait olmayabilir ve 1 px ince çizgi bırakabilir. Chrome ve Edge’de arka planı olan bir span, arka planını sağındaki opak komşunun altına 1 px uzatır ve uyarlanmış bir span bu pikseli dolgusuna ekler. Yukarıdaki dört karenin hiçbirinde ince çizgi görünmez.
Uyarlamayı kapatmak için measureText: false geçirin veya kendi (text, bold, italic) => px ölçüm işlevinizi sağlayın.
Girdi ve düzenleme
<Input> ve <Textarea> mantıksal bir imleç tutar. Ok tuşları metinde depolama sırasına göre ilerler, bu nedenle sağdan sola metinde ← görsel olarak sağa hareket eder. Blok imleç, yeniden sıralama onu nereye koyarsa koysun, kapsadığı mantıksal karakterin üzerinde durur. Sağdan sola bir dizide bu, önceki karakterin solundaki hücredir. Seçim de mantıksal bir aralıktır ve bu nedenle vurgusu karma yönlü satırlarda bitişik olmayan hücreleri kapsayabilir. Tek satırlı bir <Input> bileşeninin yatay kaydırması mantıksal sütunu izler; bu nedenle uzun bir sağdan sola değer, imleci görünüm dışına kaydırabilir. Boşluk üzerindeki veya metnin sonundaki imleç, yalıtılmış bölünemez boşluk olarak çizilir; böylece nötr kalır ve iki yanındaki sayılarla asla birleşmez.
Sınırlamalar
- Paragraf ayırıcıları. Satırlar
\nnoktasında bölünür. U+2029 ve diğer paragraf ayırıcıları bir bidi paragrafını bitirir ancak yeni satır başlatmaz. - Sekmeler. Sekmeler bidi çalışmadan önce boşluklara dönüştürülür; bu nedenle bölüm ayırıcısı (S) yerine boşluk (WS) olarak davranırlar.
- Biçimlendirme. Yalnızca sunum biçimleri olan harfler biçimlendirilir: Arapça, Farsça, Urduca ve U+FB50–U+FDFF ile U+FE70–U+FEFF aralıklarındaki diğer harfler. Lam-elif, tek glifli bir ligatür yerine iki hücrede birleşmiş iki harf olarak kalır. Süryanice, N'Ko ve diğer birleşen yazılar nominal biçimlerini korur.
- Kopyalama. Konsoldan kopyalanan metin mantıksal sırada değil, görüntüleme sırasındadır (
LRO/PDFişaretleriyle birlikte). - Terminaller. Hem BDSM hem de
LROişaretlerini yok sayıp kendi bidi işlemlerini uygulayan terminaller (örneğin iTerm2’de bidi etkinleştirildiğinde), sağdan sola satırları yeniden sıralayabilir. - DOM aynaları.
<ConsoleMirror>genişlikleri uyarlamaz; bu nedenle buradaki sağdan sola diziler, genişliklerinin %0,3’üne kadar kayabilir.
Odak alanını çevirme
@textjs/react/dom paketindeki <FocusPad> (<TextCanvas> da bunu render eder), ziyaretçilere konsolu açmalarını, sayfaya tıklamalarını ve bir tuşa basmalarını söyler. Metni varsayılan olarak İngilizcedir. Çevirmek için değiştirmek istediğiniz dizelerle messages geçirin. Belirtmediğiniz anahtarlar, defaultFocusPadMessages içinde listelenen İngilizce metinlerini korur.
import { FocusPad, type FocusPadMessages } from "@textjs/react/dom";const ja: Partial<FocusPadMessages> = { openConsole: "コンソールを開く", step: (n, total) => `${total}ステップ中${n}ステップ目`, then: (action) => `次にここをクリックして、${action}。`, shortcutOpens: (shortcut) => <>{shortcut}でコンソールが開きます。</>,};<FocusPad root={root} hints={[{ keys: "↑ ↓", label: "件数" }]} action="↑ ↓を押します" messages={ja} />;Değer içeren bir dize, cümlenin tamamını döndüren bir işlevdir; böylece çeviri, değeri cümlenin herhangi bir yerine koyabilir. step, pressShortcut, then, defaultAction ve shortcutOpens birer işlevdir. step, adım numarasını ve adım sayısını alır. then, action prop değerini veya action yoksa defaultAction metnini alır. defaultAction, ilk ipucunun tuşlarını veya ipucu yoksa null alır. pressShortcut, ekran okuyucular için kısayolun tuşlarını, shortcutOpens ise kısayolu <kbd> öğeleri olarak alır.
messages, ekran okuyucuların seslendirdiği etiketleri ve konsol boyutu denetimini (sizeControl) de kapsar. <TextCanvas> aynı messages prop değerini alır. <ViewportControl> da bunu alır ve boyut denetiminin anahtarlarını kullanır. ⌘⌥J, Ctrl+Shift+J ve Enter gibi tuş etiketleri çevrilmez.
API
import { reorderLine, paragraphDirection, resolveLevels, bidiClass, shapeArabicCodePoints } from "@textjs/core";reorderLine("שלום (abc) 123!"); // "!123 (abc) םולש": one line in display orderreorderLine("مرحبا", "auto", { shape: true }); // presentation forms, display orderparagraphDirection("123 שלום"); // "rtl" (rules P2–P3)resolveLevels(types, codePoints, direction), applyLineRules ve reorderIndices, algoritmanın kendisini sunar: P1–I2, L1 ve L2 kuralları.