النص مختلط الاتجاه: التعامل مع العربية والإنجليزية داخل عنصر واجهة واحد
غوص عميق في مستويات التضمين (embedding levels) الخاصة بخوارزمية اليونيكود ثنائية الاتجاه، والفروقات الحقيقية بين dir="auto" وعنصري <bdi> و<bdo> ومحارف اليونيكود الضابطة للاتجاه، وكيفية تطبيقها على مربعات البحث وفقاعات المحادثة وجداول البيانات وتسميات النماذج.
اسم مستخدم بحروف لاتينية داخل جملة عربية. سعر بأرقام غربية في نهاية وصف منتج بـ RTL. اسم علامة تجارية إنجليزية كُتب داخل مربع بحث عربي. لا شيء من هذا حالة استثنائية في منتج موجّه للشرق الأوسط وشمال أفريقيا — بل هو معظم المحتوى، لأن الاستخدام الحقيقي يمزج اللغات باستمرار، ويتوجّب على المتصفح أن يقرر، حرفًا حرفًا، أي اتجاه يسير فيه كل جزء. هذا القرار ليس تفصيلًا عرضيًا في العرض؛ إنه ناتج خوارزمية محددة وموثّقة، وفهم ما تفعله هذه الخوارزمية فعليًا هو ما يفرّق بين "غلّفتها بـ <bdi> ونجح الأمر" وبين معرفة لماذا نجح، ومتى لن ينجح، وأي أداة من أدوات HTML المتعددة المتداخلة هي الأنسب لنص بعينه.
مقالنا حول بناء نظام تصميم يتعامل مع LTR وRTL يغطي بالفعل لماذا ينبغي أن يكون <bdi> جزءًا من مكوّن نصي أساسي لا موجودًا في شيفرة كل فريق ميزة، ودليلنا حول اختبار أخطاء RTL يغطي كيفية اصطياد الأخطاء التي تظهر عند غياب العزل. هذا المقال لا يكرر أيًا منهما — بل يذهب طبقة أعمق: آليات خوارزمية اليونيكود ثنائية الاتجاه التي تجعل النص مختلط الاتجاه يُعاد ترتيبه على هذا النحو، والفروقات الحقيقية بين dir="auto" و<bdi> و<bdo> وخاصية CSS المسماة unicode-bidi، ومحارف اليونيكود الضابطة (LRM وRLM وLRE وRLE وPDF وLRI وRLI وFSI وPDI) وأيها لم تعد المواصفة توصي به، وشيفرة عملية لأنماط الواجهة التي تحتاج فعلًا إلى هذه المعالجة: نتائج البحث، ورسائل المحادثة، وجداول البيانات، وتسميات النماذج ثنائية اللغة.
أبرز النقاط
- أخطاء الاتجاه المختلط ليست عشوائية. إنها نتيجة قيام خوارزمية اليونيكود ثنائية الاتجاه (UAX #9) بحل حالة المحارف "المحايدة" — الأرقام وعلامات الترقيم والمسافات — بحسب النص القوي الاتجاه المحيط بها، تمامًا كما صُممت الخوارزمية لتفعل.
- يعزل
<bdi>مقطعًا من النص بحيث لا يؤثر في جيرانه ولا يتأثر بهم؛ أما<bdo>فيفعل العكس تمامًا — يفرض ترتيب عرض المحارف بداخله قسرًا. الأداتان تحلّان مشكلتين مختلفتين تمامًا وليستا قابلتين للتبادل.- يقوم
dir="auto"ومحرف اليونيكودFSI(العزل بحسب أول محرف قوي) بالكشف نفسه المعتمد على أول محرف قوي الاتجاه — أحدهما كخاصية HTML، والآخر كمحرف غير مرئي لسياقات لا يمكن فيها إضافة وسم.- توصي مواصفة اليونيكود صراحةً بتجنّب محارف التضمين LRE وRLE وPDF في المستندات الجديدة لصالح محارف العزل LRI وRLI وFSI وPDI، لأن التضمين يسمح للنص الداخلي "بتسريب" تأثيره على الاتجاه إلى ما يليه، بينما لا تفعل محارف العزل ذلك.
- خاصية
unicode-bidiفي CSS هي الآلية الأدنى مستوى التي يُبنى عليها كل من<bdi>وdir، لكن توثيق MDN نفسه يحذّر من أنها "مُعدّة لمصممي تعريفات أنواع المستندات (DTD)" وأن مطوري الويب عمومًا لا ينبغي أن يلجؤوا إليها مباشرة — استخدم أدوات HTML أولًا.- لا تلجأ إلى علامة يونيكود غير مرئية أو محرف عزل إلا حين لا يوجد وسم يمكن إرفاقه أصلًا — في حمولات الإشعارات النصية، والرسائل القصيرة، وملفات CSV، وسجلات النظام. أما داخل HTML، فـإرشاد W3C هو استخدام الوسم لتحقيق الأثر نفسه كلما كان الوسم متاحًا.
ما الذي تحسمه خوارزمية اليونيكود ثنائية الاتجاه فعليًا
يحمل كل محرف في اليونيكود نوعًا اتجاهيًا: قوي (من اليسار إلى اليمين قطعًا، كالحروف اللاتينية، أو من اليمين إلى اليسار قطعًا، كالحروف العربية والعبرية)، أو ضعيف (الأرقام ورموز العملة)، أو محايد (المسافات ومعظم علامات الترقيم). خوارزمية اليونيكود ثنائية الاتجاه، المحددة في UAX #9، توجد لأن سطر النص لم يعد اتجاهًا واحدًا موثوقًا فور مزج هذه الأنواع، ولولا خوارزمية موحّدة لرتّبت المحركات المختلفة النص نفسه مختلط اللغات بطرق متباينة.
الآلية التي تستخدمها هي مستوى التضمين (embedding level): رقم يتتبّع مدى تداخل المقطع النصي الحالي، حيث تُحل المستويات الزوجية من اليسار إلى اليمين والمستويات الفردية من اليمين إلى اليسار. تبدأ الفقرة عند المستوى صفر (LTR) أو واحد (RTL) بحسب اتجاهها الأساسي، وتدفع تغييرات الاتجاه الصريحة — عبر الوسم أو محارف اليونيكود الضابطة — المستوى صعودًا أو هبوطًا من هناك، مع سقف تضعه المواصفة عند 125 مستوى حتى لا يُنتج أي مستند تعقيدًا يفلت من سيطرة المحرك.
الجزء الذي يُنتج الأخطاء المرئية فعلًا هو كيفية تعيين مستوى للمحارف المحايدة والضعيفة. يضع شرح W3C لأساسيات الخوارزمية القاعدة الجوهرية بوضوح: "المحرف المحايد الواقع بين محرفين قويين من النوع الاتجاهي نفسه سيتخذ ذلك الاتجاه أيضًا"، وحين يختلف المحرفان المحيطان، يعود المحرف المحايد إلى الاتجاه الأساسي للفقرة. هذه القاعدة وحدها تفسّر كيف يمكن لاسم بـ RTL يليه " - المركز الأول" أن يُعرض بعلامات الترقيم والرقم في غير مكانهما دون عزل: الشرطة والمسافة والرقم محايدة أو ضعيفة، فتحلّها الخوارزمية بحسب أقرب اتجاه قوي، فتسحب معها حروف الاسم. لا شيء هنا معطّل — الخوارزمية تفعل تمامًا ما تنص عليه UAX #9. الخطأ هو أن أحدًا لم يُخبرها أين يبدأ المقطع من نوع RTL وأين ينتهي فعليًا.
أربع أدوات، أربع مهام مختلفة
تمنحك HTML وCSS أربع طرق للتدخل في هذا الحل، وهي غير قابلة للتبادل — واستخدام الأداة الخاطئة إما لا يفعل شيئًا أو يكسر النص الذي تحاول إصلاحه.
dir="auto" يجعل المتصفح يختار اتجاهًا أساسيًا للعنصر بفحص نصّه بحثًا عن أول محرف قوي الاتجاه، متجاوزًا <bdi> و<script> و<style> و<textarea> وأي عنصر متداخل له dir صالحة خاصة به. توضح MDN صراحةً أن هذا استدلال (heuristic) لا كشف لغة: "تعليق يبدأ باسم إنجليزي ثم يستمر بالعربية سيحصل على اتجاه أساسي من اليسار إلى اليمين." وهذا مناسب لمحتوى لا يُعرف اتجاهه فعليًا مسبقًا — مربع بحث، حقل تعليق، إدخال محادثة — وغير مناسب لمحتوى تعرف اتجاهه سلفًا، حيث تكون dir="rtl" أو dir="ltr" الصريحة أرخص حسابيًا وأكثر موثوقية.
<bdi> يعزل مقطعًا نصيًا عن محيطه في الاتجاهين معًا: بحسب توثيق MDN، لا يؤثر النص داخل <bdi> في اتجاه ما حوله، ولا يتأثر باتجاهه أيضًا. داخليًا يتصرف مثل dir="auto" مقصورة على ذلك المقطع فقط — وهذا هو حل مشكلة "المركز الأول" أعلاه، لأنه يمنح الاسم مستوى تضمين خاصًا به لا يمكن للمحايدات المحيطة أن تسحبه خارجه.
<bdo> يفعل ما يقارب العكس. فهو لا يعزل شيئًا — بل يفرض على المحارف بداخله أن تُعرض بترتيب محدد بصرف النظر عن نوعها الاتجاهي الخاص، بحسب وصف MDN: خاصية dir إلزامية، و"لا يتأثر اتجاه المحارف الفردية نفسها... فلا تُرسم بالمقلوب مثلًا." نادرًا ما يظهر <bdo> في واجهات المنتجات — استخداماته المشروعة أشياء مثل عرض مقطع عمدًا بعكس ترتيبه الطبيعي، لا محتوى ثنائي اللغة اعتيادي. إن وجدت نفسك تلجأ إلى <bdo> لإصلاح اسم مستخدم أو سعر مشوّه، فأنت على الأرجح تحتاج <bdi> بدلًا منه.
خاصية unicode-bidi في CSS هي الخاصية التي تُعد كلتا الأداتين أعلاه اختصارًا لها فعليًا على مستوى المتصفح، بقيم منها isolate (الآلية وراء <bdi>)، وbidi-override (الآلية وراء <bdo>)، وplaintext (حلّ الاتجاه اعتمادًا على خوارزمية اليونيكود وحدها، متجاهلًا اتجاه العنصر الأصل تمامًا). توثيق MDN واضح بشكل غير معتاد بشأن عدم استخدامها بشكل عارض: فالخاصية "مُعدّة لمصممي تعريفات أنواع المستندات (DTD)"، وعلى المطورين العاديين "ألا يتجاوزوها." عمليًا هذا يعني: استخدم <bdi> و<bdo> وdir أولًا، وعامل unicode-bidi: isolate كحل احتياطي لمكوّن أساسي في نظام تصميم لا عنصر طبيعي يُرفق به — عنصر <span> يمثّل قيمة بلا غلاف دلالي — لا شيئًا يُنثر عبر شيفرة الميزات.
محارف اليونيكود الضابطة، ولماذا لم يعد نصفها موصى به
أحيانًا لا يوجد عنصر يمكن إرفاق dir أو <bdi> به أصلًا — داخل رسالة نصية قصيرة، أو حمولة إشعار فوري، أو ملف CSV، أو سطر سجل. يعرّف اليونيكود محارف غير مرئية بعرض صفري لهذه الحالة بالذات، وقد تغيّر إرشاد المواصفة بشأن أيها يُستخدم فعليًا مع الوقت.
LRM وRLM — علامة اليسار-إلى-اليمين (Left-to-Right Mark، U+200E) وعلامة اليمين-إلى-اليسار (Right-to-Left Mark، U+200F) — محرفان غير مرئيين يتصرفان كحرف قوي عادي من اتجاههما دون عرض أي شيء. لا يفتحان أو يغلقان نطاقًا؛ بل يمنحان الخوارزمية محرفًا قويًا إضافيًا تحلّ عليه المحايدات المجاورة — مفيدان لدفع محرف محايد شارد واحد، كنقطتين رأسيتين أو قوس ينتهي في الجهة الخاطئة من رقم أو حد بين لغتين، دون تغليف أي شيء في حاوية. يعرّف اليونيكود أيضًا علامة الحرف العربي (ALM، U+061C)، التي تصفها UAX #9 بأنها تمنح الأثر الاتجاهي نفسه الذي تمنحه RLM لكن خصيصًا في سياقات الخط العربي — استخدم RLM للعبرية أو غيرها من نصوص RTL، واستخدم ALM حين يكون النص القوي المحيط من نوع RTL عربيًا تحديدًا.
LRE وRLE وPDF — تضمين اليسار-إلى-اليمين (Left-to-Right Embedding)، وتضمين اليمين-إلى-اليسار (Right-to-Left Embedding)، وإنهاء التنسيق الاتجاهي (Pop Directional Formatting) — هي الآلية الأقدم لفتح وإغلاق نطاق اتجاهي صريح في النص العادي: يرفع كل من LRE وRLE مستوى التضمين في اتجاهه، ويعيد PDF المستوى إلى ما كان عليه قبل الفتح. يثني اليونيكود صراحةً الآن عن استخدامها في المستندات الجديدة، لأن التضمين لا يزال يسمح لمحتواه بالتفاعل مع إعادة ترتيب النص المحيط — ولغة المواصفة نفسها تقول إن التضمينات "عادة ما يكون لها أثر قوي جدًا على محيطها، وهو ما يجعل استخدامها صعبًا دون داعٍ." وهذه هي النسخة النصية العادية من الخطأ نفسه الذي يمنعه العزل: يمكن لمقطع مُضمَّن أن يلوّث ما يليه بمجرد أن تعيد الخوارزمية ترتيب المحايدات عبر الحد الفاصل.
LRI وRLI وFSI وPDI — عزل اليسار-إلى-اليمين (Left-to-Right Isolate)، وعزل اليمين-إلى-اليسار (Right-to-Left Isolate)، والعزل بحسب أول محرف قوي (First Strong Isolate)، وإنهاء العزل الاتجاهي (Pop Directional Isolate) — هي ما يوصي به اليونيكود بدلًا منها. يفتح LRI وRLI مقطعًا معزولًا مفروض الاتجاه؛ ويفتح FSI مقطعًا معزولًا يُكشف اتجاهه تلقائيًا من أول محرف قوي فيه، فيكون المعادل النصي العادي لـ dir="auto"؛ ويُغلق PDI أيًا من الثلاثة المفتوح مؤخرًا. وخلافًا لـ LRE/RLE، لا يسمح أيٌّ من هذه الثلاثة لمحتواه بالتأثير في الترتيب خارجه — وهذا بالضبط ما يجعل إرشاد W3C بشأن محارف اليونيكود الضابطة يوصي باتباع "مواصفة اليونيكود في استخدام RLI وLRI، وتجنّب RLE وLRE." والمصدر نفسه واضح في أن الوسم، داخل HTML، ينبغي أن يقوم بهذه المهمة عمومًا بدلًا من ذلك.
نمط: مربع بحث يمزج نتائج عربية وإنجليزية
يجب أن يستخدم حقل البحث نفسه dir="auto" بحيث تتبع محاذاة الحقل ما يكتبه المستخدم فعليًا، لا اتجاه الصفحة الموروث:
<input type="search" dir="auto" lang="ar" placeholder="ابحث عن شركة أو منتج..." />
قائمة النتائج تمزج اللغات في كل صف — اسم شركة قد يكون لاتينيًا أو عربيًا، وتصنيف بلغة الواجهة، وسعر بأرقام لاتينية. اعزل الأجزاء التي لا تتحكم في لغتها، واترك الأجزاء التي تتحكم فيها كما هي:
<li class="result-row">
<bdi class="result-name">Careem</bdi>
<span class="result-category">النقل والتوصيل</span>
<bdi class="result-price">129.50 د.إ</bdi>
</li>
تغليف result-name وresult-price بـ <bdi> يعني أن اسم علامة تجارية لاتينيًا لا يمكنه أن يسحب علامات ترقيم تصنيف عربي مجاور خارج مكانها، وأن اسم شركة عربية لا يمكنه فعل الشيء نفسه بسعر لاتيني مجاور — أيًا كانت التركيبة التي تظهر في صف بعينه.
نمط: فقاعات محادثة برسائل مختلطة الاتجاه
المحادثة هي الحالة الأوضح لاستخدام dir="auto" على مستوى كل رسالة، لأنك لا تستطيع معرفة لغة الرسالة قبل كتابتها، ولأن المحادثة الواحدة تتنقل بين اللغتين من جملة إلى أخرى بشكل مشروع تمامًا:
<div class="message-bubble" dir="auto">
<p class="message-text">Wait, هل يمكنك إرسال الملف الآن؟</p>
<span class="message-time">14:32</span>
</div>
هناك تفصيلان مهمان هنا. أولًا، يجب أن توضع dir="auto" على النص نفسه، لا على حاوية الفقاعة الخارجية إن كانت هذه الحاوية هي ما يحدد موضع الصورة الرمزية ومحاذاة الرسائل المرسَلة/المستلَمة — وإلا فإن رسالة تبدأ بكلمة عربية قد تقلب الجانب الذي تقف فيه الفقاعة كلها على الشاشة، وهو قرار تخطيطي ينبغي أن يبقى مرتبطًا بمن أرسل الرسالة، لا بما كتبه. ثانيًا، الطابع الزمني حالة تناسب <bdi> أكثر من dir="auto": فهو نص قصير معروف الصيغة لا ينبغي أن يرث قرار اتجاه اتُّخذ لنص الرسالة المجاور له.
.message-bubble {
/* جانب التخطيط خاصية تتعلق بالمُرسِل، لا باتجاه النص */
margin-inline-start: var(--sent-by-me, auto);
}
.message-text {
unicode-bidi: isolate; /* يُستخدم مع dir="auto" أعلاه */
}
نمط: جداول بيانات بخلايا مختلطة اللغة
تكشف الجداول عن خلل مختلف: قد يكون العمود متسقًا داخليًا (كل الأسماء، كل الأسعار) بينما يمزج الجدول ككل اللغات عبر الصفوف، فتكون dir مضبوطة مرة واحدة على <table> بأكملها خاطئة لأي عمود لا يطابقها.
<table dir="rtl" lang="ar">
<tr>
<th>الشركة</th>
<th>الحالة</th>
<th>الإيراد الشهري</th>
</tr>
<tr>
<td><bdi>Talabat</bdi></td>
<td>نشط</td>
<td dir="ltr">$42,300</td>
</tr>
</table>
خلية اسم الشركة تحصل على <bdi> لأن محتواها غير متوقع في كل صف. أما خلية الإيراد فتحصل على dir="ltr" صريحة بدلًا من <bdi> أو dir="auto"، لأن أرقام العملة تُعرض دائمًا من اليسار إلى اليمين بصرف النظر عمّا يجاورها — فأنت تعرف الاتجاه سلفًا، فتكون dir الصريحة أرخص وأكثر قابلية للتنبؤ من الكشف التلقائي. أما مؤشرات الفرز ورؤوس الأعمدة فينبغي أن تتبع اتجاه الجدول نفسه، لا محتوى الخلية، لأنها عناصر واجهة لا بيانات.
نمط: تسميات نماذج بمحتوى مختلط
التسميات ثنائية اللغة — مثل "الاسم الكامل (Full name)"، أو تسمية تليها وحدة قياس مثل "المدة (بالأيام)" — تتعطل كثيرًا لأن التسمية عادة ما تكون سلسلة مترجمة واحدة يُدرَج فيها مقطع عبر قالب، لا مكتوبة يدويًا في كل مرة:
<label for="duration" dir="auto">
مدة الحملة <bdi>(days)</bdi>
</label>
<input id="duration" type="number" dir="ltr" />
يحصل المقطع بين قوسين على <bdi> للسبب نفسه الذي يحصل عليه اسم مستخدم: محتواه — مقطع قد يُستبدل أو يُترجم بمعزل عن التسمية المحيطة — لا ينبغي أن يسحب القوس أو المسافة قبله خارج مكانه. أما حقل الإدخال نفسه فيُفرض عليه dir="ltr" مباشرة، اتباعًا للمنطق نفسه المطبَّق على حقول البريد الإلكتروني والهاتف في دليلنا لاختبار RTL: الإدخال الرقمي بيانات بحروف لاتينية حتى داخل نموذج عربي، فلا يستفيد أبدًا من الكشف التلقائي.
أخطاء شائعة
استخدام <bdo> في موضع كان يحتاج <bdi>. يفرض <bdo> ترتيب عرض؛ ولا يحمي مقطعًا من جيرانه. اللجوء إليه لإصلاح اسم مستخدم مشوّه يزيد التشويه سوءًا، لأنه يتجاوز محارف كانت صحيحة أصلًا.
ضبط dir="auto" على محتوى تعرف اتجاهه سلفًا. الكشف التلقائي مخصص للمدخلات غير المعروفة فعليًا. حقل سعر أو تسمية واجهة ذات ترجمة ثابتة ينبغي أن يحصل على dir صريحة، لموثوقية أعلى ولأن الكشف التلقائي ليس مجانيًا.
اللجوء إلى LRE/RLE/PDF في شيفرة جديدة. لا تزال تعمل، لكن المواصفة نفسها توجّه المستندات الجديدة نحو LRI/RLI/FSI/PDI تحديدًا لأن التضمينات يمكن أن تسرّب اتجاهها إلى نص مجاور بطريقة لا تفعلها محارف العزل.
نثر unicode-bidi عبر شيفرة CSS الخاصة بالمكوّنات. إنها الآلية الكامنة وراء dir و<bdi>، لا بديلًا عنهما — وتصف MDN نفسها هذه الخاصية بأنها أداة لمصممي لغات الوسم، لا لشيفرة الميزات اليومية.
عزل كل شيء، بما في ذلك سلاسل تعرف مسبقًا أنها أحادية اللغة. يكلّف كل من <bdi> وdir="auto" فحص اتجاه؛ وتطبيقهما بشكل انعكاسي على كل مقطع، بما فيها ما يضمن القالب أن لغته ثابتة، يعالج مشكلة لا تعاني منها تلك السلسلة أصلًا.
اضبط الآليات في هذا المقال بشكل صحيح، وستصبح قائمة اصطياد الأخطاء في دليلنا لاختبار RTL تأكيدًا في معظمها لا اكتشافًا — فأنت عزلت مسبقًا القيم التي احتاجت ذلك، وفرضت الاتجاه على ما لم يحتجه، وتركت محارف اليونيكود الضابطة للسياقات النصية العادية التي تتطلبها فعلًا.