تخطَّ إلى المقالة

من اليمين إلى اليسار نُشر زمن القراءة 9 د

GSAP SplitText يكسر العربية، والمشكلة ليست في الكتابة من اليمين إلى اليسار

تقسيم النص إلى حروف يكتب العربية خطأً على الشاشة، والعلاج الذي يصفه الجميع — إعلان اتجاه — يعالج عيبًا آخر تمامًا. السبب تعارض متبادل بين مواصفتين، والطبقة الوحيدة التي يمكن حلّه فيها هي الطبقة التي فوق CSS.

باختصار

لا يمكن تقسيم العربية إلى حروف، لأن الصندوق الذي يحيط بحرف واحد هو مقطع تشكيل (shaping) من حرف واحد، والحرف العربي لا صورة واحدة له — إذ تُختار صورته من أربع صور سياقية بحسب جيرانه. يقطّع SplitText النص عبر Intl.Segmenter بدقّة العنقود الكتابي (grapheme)، ثم تشترط الحركة أن يحمل كل جزء display inline-block، لأن CSS Transforms Level 1 لا تطبّق transform على صندوق سطري غير مستبدَل. والصندوق السطري الذرّي حدٌّ لا يستطيع محرّك التشكيل الوصل عبره، فتظهر كلمة من خمسة حروف كخمس صور منفردة، وتُكتب خطأً. وهذه ليست مشكلة اتجاه: العبرية تُكتب من اليمين إلى اليسار ولا تتّصل حروفها. العلاج هو تغيير وحدة التقسيم — وهذا الموقع يحذف الحروف في الكتابات المتّصلة ويحرّك الكلمات، بقياس 2026-08-24: صفر عقدة حرف في الصفحة الرئيسية العربية مقابل 187 في الإنجليزية.

ما تخرج به

  1. الاتجاه والوصل خاصّتان مختلفتان في الكتابة، وربط الفحص باللغة بدل الاتجاه هو ما يبقي العبرية خارج علاج لا تحتاجه.
  2. صناديق inline-block لكل عنقود كتابي وتشكيل العربية متعارضان بحكم المواصفة لا بحكم التنفيذ، فلا تغيير في الأنماط يجمع بينهما.
  3. الخط المنشور بريء: فيه 161 استبدالًا موضعيًّا و36 رابطة إلزامية، كلها موجودة وكلها معطَّلة في مقطع من حرف واحد.
  4. العلاج مكانه طبقة فوق ورقة الأنماط — عشرة أسطر تحذف الحروف في الكتابات المتّصلة وتجعل الكلمات حدًّا أدنى للنتيجة.
  5. أبلغ Chrome عن مصفوفة transform على حروف لم تتحرّك بكسلًا واحدًا، ولهذا يجب أن يقرأ القياس صندوق العنصر لا النمط المحسوب.
  6. منظومة اختبار بلا محور للّغة عمياء عن لغة كاملة، وهذا العيب اكتشفه صاحب الموقع وهو يقرأ صفحاته بنفسه.
جرّب بنفسك

تواصل معنا

1 صناديق

الكلمتان نفسهما، مقسّمتَين بالطريقتَين المتاحتَين لأيّ حركة. صندوق لكل كلمة يُبقي كل وصلة سليمة. وصندوق لكل حرف هو ما تفعله SplitText افتراضياً، فيعود كل حرف إلى شكله المنفصل — وتصبح العبارة مكتوبة خطأً، ولا توجد خاصية CSS واحدة تصل إلى المُشكِّل لتصحيحها.

docs/PHASE10_HANDOFF.md:24-46

ما الذي ينكسر فعلًا حين تقسّم العربية إلى حروف؟

أُطلقت الصفحة الرئيسية العربية وكل كلمة عربية فيها مكتوبة خطأً. كل كلمة يقسّمها التقسيم إلى حروف كانت تظهر صفًّا من حروف منفردة بينها فجوات، وهذا في كتابة متّصلة ليس خللًا في الأنماط بل خطأ إملائيًّا — فكلمة تواصل نفسها، وهي من خمسة حروف، خرجت خمس صور منفصلة. بقي ذلك منشورًا أسابيع، وأبلغ عنه صاحب الموقع وهو يقرأ صفحاته، لا بوابة فحص التقطته.

22 من أصل 47 مصدر تقسيم في هذا الترميز تطلب الحروف، وهي أول ما يلقاه الزائر: روابط القائمة الأربعة كلها، وكل رابط في التذييل، وخمسة من أقسام الصفحة الرئيسية. سجّل تسليم المرحلة العاشرة 23 مصدرًا يطلب الحروف و201 عقدة حرف في الصفحة الرئيسية الإنجليزية يوم كُتب؛ ويقيس الترميز اليوم 22 مصدرًا، وتقيس جولة golden في 2026-08-24 عدد 187 عقدة. كل زوج صحيح في تاريخه، ولهذا يحمل كلٌّ منهما تاريخه.

عطلان مختلفان يسافران معًا تحت استعلام بحث واحد، وفصلهما هو معظم العمل. الأول ترتيب — كلمة لاتينية داخل المستند العربي تظهر معكوسة، ولها علاج في CSS من ثلاثة أسطر. والثاني تشكيل — كلمة عربية تفقد وصلاتها، ولا علاج لها. وتكاد كل إجابة على الإنترنت تعاملهما مشكلةً واحدة وتصف علاج الأول.

ArabicShaping-17.0.0.txt · 834 entries

لماذا هذه مشكلة وصل لا مشكلة اتجاه؟

للحرف العربي نقطة ترميز واحدة وأربع صور على الأكثر. يقولها Unicode 17 صراحةً: كل حرف يأخذ قيمة محرف واحدة في كتلة العربية الأساسية مهما بلغ عدد مظاهره السياقية، وعلى عملية العرض أن تختار الرسم (glyph) بحسب سياق الوصل المباشر للحرف. والسياق هو الجيران. انزع الجيران فلا يبقى سياق يُختار منه، فيسقط محرّك التشكيل إلى الصورة المنفردة في كل مرة.

أيّ الحروف يتّصل، ومن أيّ جهة، بيانات منشورة لا فلكلور. إصدار 17.0.0 من ArabicShaping.txt، المؤرَّخ 2025-08-14، يحمل 834 مدخلة، منها 615 ثنائية الوصل و153 يمينية الوصل؛ والباقي يساري الوصل أو مسبِّب للوصل أو غير موصول أو شفّاف. وتحليل الملف محليًّا في 2026-08-26 يعطي جوابًا لكل حرف في أي نص تنشره، والجدول أدناه يعطيه للكلمة التي بدأ منها كل هذا.

العبرية هي الشاهد الذي يحسم الجدل. لا تظهر العبرية في ArabicShaping.txt أصلًا، وينصّ الملف على أن كل نقطة ترميز لا يذكرها غير موصولة افتراضًا — فصورة الحرف العبري لا تعتمد على جيرانه، والصندوق الذي يحيط بحرف واحد لا ينزع منه شيئًا. العبرية تُكتب من اليمين إلى اليسار. وهي لا تنكسر. فالاتجاه ليس الخاصية المعنيّة.

كل حرف من كلمة تواصل التي يعرضها هذا الموقع، مقروءًا من ArabicShaping-17.0.0.txt في 2026-08-26، مع صفّ شاهد بالعبرية. الحرف ثنائي الوصل يأخذ أربع صور، واليميني الوصل يأخذ صورتين.
الحرف نقطة الترميز نوع الوصل عدد الصور
ت U+062A D — Dual_Joining 4
و U+0648 R — Right_Joining 2
ا U+0627 R — Right_Joining 2
ص U+0635 D — Dual_Joining 4
ل U+0644 D — Dual_Joining 4
ש U+05E9 U — غائب عن الملف 1

التمييز هنا قابل للتنفيذ لا بلاغيّ. فالدالّة التي تقرّر هل يجوز تقسيم مستند إلى حروف تقرأ اللغة لا الاتجاه، ويؤكّد اختبار الوحدة أن مستندًا عبريًّا معلَنًا من اليمين إلى اليسار يعيد false، بينما تعيد ar وar-SY وAR كلها true. والربط بالاتجاه كان سيحذف الحروف من العبرية لسبب لا يصحّ عليها، وكان سيفوّت أي كتابة متّصلة مضبوطة من اليسار إلى اليمين.

CSS Transforms L1 · CR 2019-02-14

لماذا لا تستطيع CSS إصلاحها؟

على التأثير أن يحرّك كل جزء، والطريقة الرخيصة الوحيدة لتحريك الحبر هي transform. وتعرّف CSS Transforms Level 1 العنصر القابل للتحويل بأنه كل ما يحكم تخطيطَه نموذج الصندوق في CSS، إلا الصناديق السطرية غير المستبدَلة وصناديق أعمدة الجداول ومجموعاتها. والحرف الملفوف في span سطري عادي صندوق سطري غير مستبدَل، فالخاصية لا تُطبَّق أصلًا — تُهمَل إهمالًا، ولا تُحسَب صفرًا.

في هذا المستودع القياس، لأن المتصفّح يكذب بشأنه. يبلّغ Chrome عن المصفوفة من getComputedStyle سواء احتُرمت أم لم تُحترم، وطوال ثلاث مراحل تطوير كان كل حرف في الصفحة يحمل matrix(1, 0, 0, 1, 0, -70) ولم يتحرّك واحد منها بكسلًا واحدًا. والأداة الأمينة الوحيدة كانت قراءة المستطيل المحيط بالحرف قبل التمرير فوقه وبعده، عند عرض نافذة ثابت.

العيب P6-02: أعلى الحرف المتدحرج بالبكسل، مقيسًا عند 1440 بقراءة المستطيل المحيط قبل التمرير فوقه وبعده. العمود الأوسط هو البناء الجديد قبل وجود قاعدتَي نموذج الصندوق.
المُضيف المتدحرج الموقع القديم البناء الجديد مع القاعدتين
نصّ الزر -24.00 0.00 -20.00
رابط القائمة -70.00 0.00 -70.00
رابط النصّ -15.00 0.00 -15.00

إذن inline-block إلزامي للتأثير، وinline-block صندوق سطري ذرّي — سياق تنسيق قائم بذاته، وبالتالي حدّ. وترفض CSS Text Level 3 أن تعد بأي شيء عبره: عرض وحدة المحرف الطباعية التي يقسمها حدُّ عنصرٍ غير معرَّف، وتحذّر المواصفة المؤلّفين من أن تقسيم العناقيد الكتابية أو الروابط بحدود العناصر قد يعطي نتائج غير متّسقة أو غير مرغوبة. والمتطلَّبان متعارضان بحكم المواصفة لا بحكم التنفيذ.

والأمر أسوأ من اختيار مباشر بينهما، وهذا الجزء يُغفَل عادةً. فتحليل الثغرات في الكتابة العربية من W3C، المحدَّث في 2026-03-30 والمستند إلى بلاغ فُتح في فبراير 2020، يسجّل أن WebKit يكسر الوصل بمجرّد أن يحيط أي ترميز بالحرف — قبل تطبيق أي أنماط إطلاقًا. أما Gecko وBlink فيحفظان الوصل مع الأنماط التي لا تغيّر رسم الحرف، وتفشل الثلاثة جميعًا حين يختلف font-weight أو font-style أو font-size عبر الحدّ. فالترميز المجرّد لكل حرف لا يضمن شيئًا، وinline-block يضمن الكسر.

fontTools 4.60.2 · zain/*.woff2

ما الذي يخسره محرّك التشكيل تحديدًا في مقطع من حرف واحد؟

الخسارة قابلة للعدّ، فتستحق أن تُعدّ. قطع Zain الثلاث المنشورة تحمل جداول تشكيل متطابقة — 610 رسوم و590 مدخلة cmap في كل قطعة، و161,096 بايتًا للمجموعة — وجدول GSUB فيها يحمل 39 استبدالًا للصورة الأولية و39 للوسطية و83 للنهائية و36 رابطة إلزامية. وكل واحد من هذه الاستبدالات الموضعية الـ161 سياقيّ: لا يعمل إلا حين يقع الحرف في مقطع مع جيرانه.

ومقطع من حرف واحد لا يحقّق أيًّا منها، وخاصية الروابط الإلزامية لا تجد ما تربطه. هذه هي الآلية كلها، وهي تفسّر لماذا لا يفيد تبديل الخط العربي أبدًا — فالجداول موجودة وصحيحة في كل خط عربي كفء، والصندوق الذي يحيط بحرف واحد يعطّلها جميعًا. والتجزيء يزيد الأمر سوءًا لا تحسينًا: النقاط والعلامات رسوم منفصلة تُركَّب وقت العرض، والمُجزّئ الذي يحذف ما يظنّه غير قابل للوصول يحذفها، ولهذا تُنشر هذه الملفات كاملة بلا تجزيء عن قصد.

  • 161 استبدالًا موضعيًّا 39 أولية و39 وسطية و83 نهائية — كلها سياقية
  • 36 رابطة إلزامية صور لام-ألف، ولم يبقَ لها ما تربطه
  • 610 رسمًا في كل قطعة متطابقة في العادي والعريض وفائق العرض
  • 0 منها يمكن أن يعمل في مقطع من حرف الخط بريء، والعيب في الصندوق
مقيس بأداة fontTools 4.60.2 على قطع Zain الثلاث المنشورة في 2026-08-26. الجداول الثلاثة متطابقة بايتًا ببايت في خصائص التشكيل.

لا شيء هنا خاصّ بالخطوط التي ينشرها هذا الموقع. أجرِ القياس نفسه على خطوطك وسيبقى شكل الجواب: عدد ثلاثي الخانات من عمليات البحث السياقية، لا يمكن لأيٍّ منها أن يعمل في مقطع طوله حرف واحد، وخاصية روابط إلزامية قطعها التقسيم نصفين سلفًا.

أما بقية ما وجب قياسه في هذه القطع الثلاث — السِّنّة والألف وقيمة size-adjust التي تقرنها بالخط اللاتيني — ففي قياس خط عربي حين لا يعني ارتفاع x شيئًا.

SplitText 3.15.0 · checked 2026-08-26

هل أصلحت GSAP هذا؟ وهل من إصلاح قادم؟

لا، والجواب قابل للفحص لا مسألة اعتقاد. موقف GreenSock مسجَّل في ردّ على المنتدى من Rodrigo، أحد مديريه، بتاريخ 11 أغسطس 2023: SplitText لم يُصمَّم للغات التي تُكتب من اليمين إلى اليسار، ووُصف ذلك هناك بأنه قيد، مع دعوة مفتوحة إلى اقتراح تحسينات. هذا الردّ هو الإقرار. أما التوثيق فلا: ما زال يقول فقط إن SplitText يبلي بلاءً ممتازًا مع المحارف الأجنبية أيضًا، ولا يحمل أي تحفّظ عن الكتابات المتّصلة في أي موضع.

وأضافت إعادة الكتابة في 3.13، المنشورة في 29 أبريل 2025 وعلى npm في اليوم التالي، معالجة aria وautoSplit وonSplit وdeepSlice وmask والتقطيع المبني على Intl.Segmenter، ولم تذكر الكتابة من اليمين إلى اليسار في أي موضع. والبحث في المصدر المنشور للإصدار 3.15.0 — الأحدث على npm منذ 2026-04-13 — في 2026-08-26 عن rtl أو direction أو arabic أو cursive أو shap أو bidi أو joining لا يعيد شيئًا، وتقسيم الحروف ما زال الاستدعاء نفسه بالدقّة الافتراضية كما كان في 3.13.0.

مقيس في 2026-08-26 على Node 22.23.1، مقابل الاستدعاء نفسه الذي تجريه الإضافة في السطر 73 من مصدرها.
// SplitText، دون تغيير من 3.13.0 حتى 3.15.0:
//   _charSegmenter = new Intl.Segmenter()
 
const seg = new Intl.Segmenter()
seg.resolvedOptions().granularity
// 'grapheme' — الافتراضي، وهو المشكلة كلها
 
function pieces(word) {
  return [...seg.segment(word)].length
}
 
pieces('تواصل')   // 5  — five isolated forms, one misspelt word
pieces('العربية') // 7
pieces('لا')      // 2  — an obligatory ligature, cut in half
pieces('عبّود')    // 4  — the shadda stays put: marks survive
 
// المنصّة تقدّم الجواب سلفًا، ولا يطلبه أي مُقسِّم
const byWord = new Intl.Segmenter('ar', { granularity: 'word' })

من الإجحاف أن نسمّي هذا عيبًا في GSAP. فأي مكتبة تلفّ كل عنقود كتابي في صندوق تصطدم بالجدار نفسه، وكذلك تقسيم مكتوب باليد على حروف نصّ: الجدار هو CSS Transforms Level 1 مع CSS Text Level 3، لا تنفيذٌ بعينه. والإصلاح الوحيد المتاح على مستوى المكتبة هو التقطيع بدقّة الكلمة في الكتابات المتّصلة — وهو ما تقدّمه المنصّة سلفًا، كما يُظهر آخر سطر في القائمة — ولا يفعله أي مُقسِّم شائع من تلقاء نفسه.

وIntl.Segmenter تحسين حقيقي على التقسيم بتعبير نمطي، ويستحق الأمر دقّة في وصف ما حسّنه. فهو يبقي العلامات المركّبة ملتصقة بحرفها، فتبقى كلمة تحمل شدّة أربعة مقاطع لا خمسة. لكنه لا يستطيع أن يعين على الوصل، لأن الوصل ليس مسألة تقطيع — إنه مسألة تشكيل، تُحسم بعد التقطيع، بالجيران الذين وضعهم التقطيع للتوّ في صناديق أخرى.

split-registry.js:131-169 · 10 lines

العلاج وحدة أخرى، تُختار مرة واحدة، في مكان واحد

متى قُبل أن القرار لا يمكن أن يعيش في CSS، صار العلاج صغيرًا. دالّة خالصة تأخذ أنواع التقسيم المكتوبة وكتابة المستند، وتحذف الحروف حين تكون الكتابة متّصلة، وتجعل الكلمات حدًّا أدنى للنتيجة — لأن مصدرًا لم يطلب إلا الحروف كان سينقسم إلى لا شيء البتّة، والتأثير الذي لا يجد أجزاءً لا يتحرّك.

وحقيقة المتصفّح تُمرَّر إلى الدالّة بدل أن تُقرأ داخلها، وهذا ما يجعلها موضوعًا لاختبار وحدة لا موضوعًا لشجرة DOM. والسلسلة أدناه هي الحجّة التي تشفّرها، وكل حلقة فيها استشهاد من قسم من الأقسام السابقة.

العلاج كله، مع تسمية دالّتَي الاستدعاء بدل كتابتهما بسهمين حتى لا تحتاج هذه القائمة إلى أقواس زاوية. يعتمد على اللغة، وهو عشرة أسطر.
export const JOINED_SCRIPT_LANGS = Object.freeze(['ar'])
 
export function isJoinedScript({ lang = '' } = {}) {
  const tag = String(lang).slice(0, 2).toLowerCase()
  return JOINED_SCRIPT_LANGS.includes(tag)
}
 
function trimmed(type) { return type.trim() }
function notChars(type) { return type && type !== 'chars' }
 
export function splitTypesFor(raw, script = {}) {
  const types = splitTypesFrom(raw)
  if (!isJoinedScript(script)) return types
  const kept = types.split(',').map(trimmed).filter(notChars)
  // المصدر الذي يطلب الحروف وحدها كان سينقسم إلى لا شيء البتّة،
  // والتأثير الذي لا يجد أجزاءً لا يتحرّك.
  return (kept.length ? kept : ['words']).join(', ')
}
  1. على التأثير أن يحرّك كل حرف transform هي الطريقة الرخيصة الوحيدة لتحريك الحبر على المُركِّب، فيجب أن يكون كل جزء مولَّد صندوقًا قابلًا للتحويل.
  2. transform يتخطّى الصناديق السطرية تستثني CSS Transforms Level 1 الصناديق السطرية غير المستبدَلة، فيجب أن يكون كل جزء بـ display inline-block. والمقيس هنا: ناقص 70.00 بكسل صارت 0.00 بغير تلك القاعدة.
  3. الصندوق الذرّي حدّ الصندوق بـ inline-block ينشئ سياق تنسيق قائمًا بذاته، فيصير كل حرف مقطع تشكيل طوله حرف واحد بالضبط.
  4. عمليات البحث تتعطّل 161 استبدالًا موضعيًّا و36 رابطة إلزامية في الخط المنشور، لا يمكن لأيٍّ منها أن يعمل في مقطع طوله حرف واحد.
  5. الكلمة تُكتب خطأً كل حرف يسقط إلى صورته المنفردة، فيرى القارئ صفًّا من الصور حيث ينبغي أن تكون كلمة.
  6. فينتقل القرار طبقةً إلى أعلى نقض الخطوة الثانية يصلح الخطوة الخامسة ويهدم الخطوة الأولى. لا علاج في الأنماط، فالتقسيم نفسه يكفّ عن طلب الحروف في الكتابات المتّصلة.
من المتطلَّب إلى العلاج، في ست حلقات. عكس أي حلقة منها يكسر شيئًا آخر.

ثلاثة تأثيرات تستهلك الأجزاء، وكلٌّ منها يتدهور على نحو مختلف. فتدحرج الحروف ومسارات البطل المشدودة إلى التمرير تتراجع عبر ترتيب موثَّق — حروف، ثم كلمات، ثم أسطر، ثم لا شيء — فتتدحرج على المستند العربي كلمات كاملة وتنساب كذلك. أما مسح الشكل البيضوي فيحترس بدل أن يتراجع، فيبقى البيضوي يفتح والأسطر تسافر، ولا يغيب إلا التتابع لكل حرف. وورقة الأنماط توافق في محدِّد واحد لا في قاعدتين قد تنحرف إحداهما عن الأخرى، فتُعلي أيّ وحدة وُجدت.

والتكلفة الصادقة هي حجم الحبيبة (grain). تحمل الصفحة الرئيسية الإنجليزية 187 حرفًا موزَّعة على 28 كلمة، أي 6.68 جزءًا للكلمة مشتقًّا من مجاميع الموقع كلّه لا مقيسًا رابطًا رابطًا، ويتتابع التدحرج 0.02 ثانية لكل جزء — فنصّ لاتيني من ثمانية حروف يتتابع على 0.14 ثانية، بينما نصّ عربي من كلمة واحدة له جزء واحد ولا تتابع فيه أصلًا. التأثير ينجو، وتخشُن حبيبته بنحو سبعة أضعاف. والكلمة هي الوحدة التي يدركها القارئ العربي على أي حال.

ثم كشف تغيير الوحدة عيبًا ثانيًا لم يبلّغ عنه أحد. كان التدحرج يسافر ناقص مئة في المئة من صندوق الحرف، بينما كانت الورقة العربية قد وسّعت صندوق سطر النصّ إلى 1.55 وأنزلت التوأم الذي يقصده التدحرج إلى 2em — فكانت الكلمة الداخلة تحطّ 0.45em تحت الخارجة، في كل رابط عربي في الموقع. وصار التدحرج الآن يقرأ إزاحة التوأم من قيمة text-shadow المحسوبة بدل أن يفترض صندوق حرف، ويحتفظ بالقيم المسجَّلة حدًّا أدنى فقط للعناصر التي لا ترسم توأمًا.

وقيمة 1.55 ليست تفضيلًا بل حدٌّ أدنى محسوب، وحسابه في الحدّ الأدنى المقيس لارتفاع السطر العربي.

split-registry.js:181-189 · one read

أين تتسرّب بوابة تعمل على مستوى المستند؟

تقرأ البوابة كتابة المستند مرة واحدة، من العنصر الجذر، ولا تعود إليها. وإلا لكانت 55 مصدرًا في الصفحة الرئيسية 55 قراءة لحقيقة واحدة كتبها البناء على المستند ولا تتغيّر بعده — و55 فرصة لقراءتها على نحو مختلف. وهذا صحيح من جهة الكلفة وصحيح من جهة الاتّساق، ويجعل الفحص سؤالًا عن المستند لا عن التشغيلة.

وهذا قيد حقيقي له عواقب في الاتجاهين، وذِكره خير من إخفائه.

قراءة واحدة، لا 55
اللغة والاتجاه يكتبهما البناء على المستند ولا يتغيّران، فقراءتهما لكل عنصر تعني 55 قراءة لحقيقة واحدة.
أوسع من اللازم، وآمن
على المستند العربي تُحذف الحروف من المصادر الـ22 كلها التي تطلبها، بما فيها المقاطع اللاتينية بينها. وتلك المقاطع تخسر تتابعًا أنعم ولا تخسر شيئًا غيره.
أضيق من اللازم، وثغرة حقيقية
على المستندين الإنجليزي والألماني لا تفعل البوابة شيئًا — فالمقطع العربي الوحيد الذي يحويانه، وهو نصّ مبدّل اللغة نفسه، سيبقى مقسَّمًا إلى حروف.
مسدودة في الترميز
ذلك النصّ مكتوب ليُقسَّم إلى أسطر يدويًّا، بينما ما زال شقيقاه اللاتينيان يطلبان الكلمات والحروف. الترميز بلغ النتيجة أولًا، للنصّ الوحيد الذي انتبه له أحد.
الحجّة لصالح الشيفرة
نتيجة تُبلَغ يدويًّا، نصًّا بعد نصّ، تصمد حتى النصّ التالي. وعشرة أسطر من دالّة خالصة تصمد لكل النصوص دفعة واحدة.
ما تشتريه قراءة واحدة، وما تكلّفه على كل جانب من الحدّ.

والفحص لكل مقطع ممكن، وهذا الموقع لا يحتاجه. سيعني قراءة اللغة من أقرب سلف يعلنها، لكل مصدر، وسيشتري سلوكًا صحيحًا واحدًا بالضبط تشتريه سمة واحدة مكتوبة باليد سلفًا. وحين تُنشر لغة ثانية بكتابة متّصلة، أو حين تخلط صفحة الكتابات بطول، تنزل القراءة إلى الأسفل؛ أما اليوم فستكون كلفة بلا عيب تشير إليه.

وذلك المقطع العربي الوحيد داخل مستند لاتيني هو أيضًا ما يقرّر كيف يُحمَّل الخط العربي — التقاط مقطع عربي واحد بـ unicode-range.

arabic.css:328-341 · the legacy roster

النصف الآخر من السؤال: لماذا ظهرت الكلمة اللاتينية معكوسة؟

العطل الثاني حقيقي، وليس ذنب العربية، وله علاج. تقول CSS Writing Modes Level 4 إن كل الصناديق السطرية الذرّية عدا العناصر السطرية المستبدَلة تُعامَل معاملة المحارف المحايدة دائمًا. وتقسيم الحروف يحوّل كل حرف إلى صندوق سطري ذرّي، فتصير الكلمة اللاتينية داخل فقرة من اليمين إلى اليسار مقطعًا من محايدات — والمقطع المحايد يأخذ اتجاه الفقرة. ولا تستطيع خوارزمية الاتجاهين إنقاذه، لأن كل حرف صار مقطعًا بذاته ولم يبقَ للخوارزمية ما تُبقيه معًا.

وقد قاس الموقع القديم سجلًّا كاملًا من هذا الشكل، كلّه في صفحاته العربية، وكلّه مكتوب بالحرف اللاتيني. أما المستندان الإنجليزي والألماني فكانا يعرضان كل واحد من هذه النصوص في اتجاهه الصحيح، وهذا ما جعل قراءة السبب خطأً على أنه مشكلة عربية أمرًا سهلًا.

سجلّ الانعكاس، مسجَّلًا في تحقيق الموقع القديم على الصفحات العربية للنسخة المرجعية. كل واحد من هذه مقطع لاتيني وُضع كل حرف فيه في صندوق خاص به.
كما كُتب كما ظهر
LoadAbbod dobbAdaoL
01 / 04 04 / 01
+49 155 67042737 6704273715549+
#C4552D C4552D#
@alaaabbod alaaabbod@
6 KB JS KB JS 6

والعلاج ثلاثة أسطر، وهو علاج اتجاه محصور في الصناديق التي تحمل نصًّا لاتينيًّا داخل المستند من اليمين إلى اليسار. يعمل، وهو صحيح، ولا يفعل شيئًا البتّة للتشكيل. وورقة أنماط التنقّل تقول ذلك في جملة واحدة: التشكيل خاصية للخط ونقاط الترميز ولا يتأثر بأيٍّ من الإعلانين، فالوصلات مسألة سجل التقسيم لا مسألة القاعدة.

والعبرية تتأثر بهذا العطل ولا تتأثر بالآخر، وهذه أنظف طريقة للفصل بينهما. فكلمة عبرية مقسَّمة إلى حروف داخل حاوية من اليسار إلى اليمين تظهر معكوسة، ويصلحها إعلان اتجاه مع عزل. أما الكلمة العربية المقسَّمة إلى حروف فمكتوبة خطأً في أي اتجاه شئت.

وستة تصحيحات أخرى من هذا الشكل بالضبط، وسبب عجز الخصائص المنطقية عن التقاطها، في عيوب الاتجاه التي تفوّتها الخصائص المنطقية.

golden:rtl · 37/37 · 2026-08-24

كيف تُبقيه مُصلَحًا حين لا ينظر أحد؟

سبب نشر هذا العيب أبلد من العيب نفسه وأنفع منه. لم يكن للمنظومة البصرية محور للّغة: ملف المقاسات لا يعلن أي لغة، ومكتبة السيناريوهات لا منطق فيها للّغة، وخطوة الالتقاط تقرأ لغة السيناريو حين لا يضبطها أي سيناريو. وقبل البوابة الموصوفة هنا، كانت أداة واحدة بالضبط قد حمّلت صفحة عربية، ولتصوير بطاقة واحدة فقط. كل عيب في هذه المقالة كان خفيًّا عن مجموعة اختبارات ناجحة وواضحًا لأي شخص يفتح الصفحة.

والبوابة التي تمسكه اليوم مقصورة على المرشَّح عن قصد، وهذا ما يستحق الدفاع عنه لا الاعتذار عنه. فكل بوابة أخرى في ذلك المجلد تقارن البناء الجديد بالنسخة المرجعية؛ وهذه لا تستطيع، لأن الصفحات العربية في المرجع تنشر الخطّين اللذين استُبدلا عمدًا. والمقارنة التي أُبطل أساسها عن قصد ليست تغطية — بل جولة حمراء خلف صفّ استثناء دائم.

مسوحات المسار والمقاس، كلها عند 0 حرف 80
فحصًا في السجل، كلها ناجحة 37
فحص خصائص، ثمانية عند كل عرض من أربعة 32
مسارًا عربيًّا مسحتها تلك الجولة 20
فحصان يراقبان محرّك التشكيل تحديدًا 2
بوابة rtl في جولتها بتاريخ 2026-08-24T18:57:34.451Z، 37 فحصًا من 37 ناجحة.الأشرطة مرسومة بمقياس نسبته إلى 80 مسحًا لمسار ومقاس.

والافتراض الحامل سطر واحد: المستند العربي فيه صفر عقدة حرف، والإنجليزي فيه أكثر من صفر. والفحص المرافق يتأكد أن المستند العربي ما زال فيه ما يُحرَّك، ويبلّغ عن 26 كلمة و43 سطرًا. والمسارات تأتي من بيان المسارات المنشور لا من قائمة مكتوبة في الشيفرة، فالمسار الذي يُضاف إلى الموقع مسار تغطّيه البوابة دون أن يتذكّر أحد إضافته — الجولة المقتبسة هنا مسحت 20 مسارًا عربيًّا، والبيان يعلن 19 اليوم لأن صفحة مشروع حُذفت منذ ذلك الحين.

وكل رقم أعلاه قياس لبناء واحد في تاريخ واحد على خادم محلي، وهو بالضبط ما يقول عقد القياس في هذا الموقع إن مثل هذا الرقم يساويه. والذي يبقى بعد التاريخ هو شكله: الأعداد مؤكَّدة لا محفوظة، والدالّة مختبَرة بوحدة على مستند عبري معلَن من اليمين إلى اليسار، والانتكاسة صارت جولة حمراء بدل رسالة من الشخص الذي كان اسمه يُكتب خطأً.

ونشر ثلاث لغات من مصدر واحد أحد الأمور الأربعة التي بُني عليها هذا الموقع — التخصّصات الأربعة.

أسئلة

هل يؤثّر هذا في العبرية أيضًا؟

ليس عطل التشكيل. حروف العبرية لا تتّصل، والعبرية لا تظهر في ملف ArabicShaping.txt من يونيكود، وكل نقطة ترميز غائبة عن ذلك الملف غير موصولة افتراضًا — فالصندوق الذي يحيط بحرف واحد لا ينزع شيئًا من صورة الحرف العبري. لكن العبرية تتأثر بالعطل الآخر في هذه المقالة: صناديق inline-block لكل حرف تعاملها خوارزمية الاتجاهين معاملة المحارف المحايدة، فتظهر كلمة عبرية مقسَّمة إلى حروف داخل حاوية من اليسار إلى اليمين معكوسةً. ويُصلَح ذلك بضبط الاتجاه والعزل على الصندوق، ولن ينفع أي قدر من ضبط الاتجاه مع العربية.

وماذا عن الفارسية والأردية والبشتو والكردية والأويغورية والسندية؟

كلها تُكتب بالحرف العربي، وكلها تتّصل، وكلها تنكسر بالطريقة نفسها تمامًا. والقائمة في هذا المستودع تحمل لغة واحدة لأن الموقع ينشر لغة واحدة من هذا النوع اليوم؛ وموقع ينشر fa أو ur أو ps أو ckb أو ug أو sd يضيفها إلى المصفوفة نفسها ولا يغيّر شيئًا آخر. والقائمة ليست محصورة بالحرف العربي أيضًا — فملف ArabicShaping.txt يحمل بيانات وصل للسريانية والنكو والمندائية والمانوية والمنغولية والفاجس-با والبهلوية المزمورية والسغدية والأويغورية القديمة والخوارزمية والروهينغية الحنفية والأدلم.

هل أستطيع ضبط الحروف على display inline بدلًا من ذلك؟

هذا يقايض الحركة بالوصل، وقد لا يشتري الوصل أصلًا. تستثني CSS Transforms Level 1 الصناديق السطرية غير المستبدَلة من تعريف العنصر القابل للتحويل، فلا يُطبَّق transform إطلاقًا — وقد قاس هذا المستودع ثلاثة أهداف تمرير تنتقل من ناقص 24 وناقص 70 وناقص 15 بكسلًا إلى 0.00 بينما ظلّ Chrome يبلّغ عن مصفوفة transform على كل واحد منها. وبمعزل عن ذلك، يسجّل تحليل الثغرات العربية من W3C أن WebKit يكسر الوصل بمجرّد أن يحيط ترميز بالحرف، بأنماط أو بغير أنماط. فالترميز المجرّد قد يكسر الوصل، وinline-block يضمن كسره.

هل مكتبة تقسيم أخرى هي الجواب؟

لا، لأن الجدار مواصفة لا تنفيذ. فمكتبات SplitType وSplitting.js وأي غلاف مكتوب باليد لكل حرف تنتج جميعها الصناديق نفسها لكل عنقود كتابي، وتصطدم بـ CSS Transforms Level 1 وCSS Text Level 3 في الموضع نفسه. والإصلاح الوحيد على مستوى المكتبة هو التقطيع بدقّة الكلمة في الكتابات المتّصلة، وهو ما تنشره المنصّة منذ سنوات في Intl.Segmenter، ولا يفعله أي مُقسِّم شائع تلقائيًّا.

لماذا يُربط الفحص باللغة لا بالاتجاه؟

لأنهما يجيبان عن سؤالين مختلفين. فالاتجاه يخبرك في أي جهة يسير النص ولا يخبرك شيئًا عن اعتماد صورة الحرف على جيرانه. والربط بالاتجاه كان سيحذف الحروف من العبرية لسبب لا يصحّ عليها، وكان سيفوّت أي كتابة متّصلة مضبوطة من اليسار إلى اليمين. واختبار الوحدة يجعل التمييز قابلًا للتنفيذ: مستند عبري معلَن من اليمين إلى اليسار يعيد false، بينما تعيد ar وar-SY وAR كلها true.

المصادر

مقيس في هذا المستودع

  • src/runtime/animation/split-registry.js قائمة الكتابات المتّصلة، والدالّة المرتبطة باللغة، وsplitTypesFor، والقراءة الوحيدة لكتابة المستند.
  • src/styles/components/text-reveal.css قاعدتا نموذج الصندوق، وقياسات التمرير P6-02 التي كُتبتا عنها، والمحدِّد الواحد الذي يُعلي أيًّا من الوحدتين.
  • tools/golden/rtl.mjs البوابة: لماذا وجب اختراعها، ومِسبار العدّ، والفحصان اللذان يراقبان محرّك التشكيل.
  • tests/unit/motion-primitives.test.mjs اختبارات الوحدة، ومنها التأكيد أن مستندًا عبريًّا معلَنًا من اليمين إلى اليسار ليس كتابة متّصلة.
  • src/styles/locale/arabic.css قاعدة الاتجاه للنصوص اللاتينية داخل المستند العربي، والتباعد المصفَّر الذي يبقي الوصلات سليمة.

مُراجَع مقابل

Alaa Abbod

بقلم

علاء عبّود

مطوّر إبداعي — هيرنه، ألمانيا

مصمّم ومطوّر يبني مواقع ويب سهلة الوصول وتطبيقات جوال ومتاجر إلكترونية وهويات بصرية كعملٍ واحد، وبيديه. هذا الموقع منشور بالإنجليزية والألمانية والعربية من مصدر واحد، ومن هناك جاء معظم هذه الأسئلة.

Alaa Abbod

أتنشر كتابة متّصلة داخل بناء لاتيني؟

العربية ليست الإنجليزية بأسهم معكوسة، والمواضع التي تنكسر فيها نادرًا ما تكون المواضع التي تنظر إليها ورقة الأنماط. وإن كنت تخطّط لبناء عليه أن يحمل كتابة متّصلة، فقرارات الحركة والطباعة أرخص كثيرًا حين تُتّخذ قبل اكتمال التخطيط لا بعد النشر.

من فضلك أدر جهازك،
هذا الموقع مبني عموديًا.