كيف تختبر تطبيقك بحثًا عن أخطاء RTL قبل أن يكتشفها المستخدمون
دليل عملي لفريق الجودة لاكتشاف أخطاء الاتجاه من اليمين إلى اليسار قبل الإطلاق: حيل أدوات المطوّرين في المتصفح، أنماط Playwright وCypress، اختبار الانحدار البصري، وفحوصات قارئ الشاشة مع NVDA وVoiceOver.
معظم أخطاء RTL التي تصل إلى بيئة الإنتاج لم يُنظر إليها فعليًا قبل إطلاقها — ليس لأن أحدًا لم يهتم، بل لأن جولة اختبار الجودة التي كان يفترض أن تكتشفها نفّذت نفس المسار الإنجليزي المعتاد، على نسخة تدعم أيضًا dir="rtl"، ولم يبدّل أحد المفتاح. الخطأ غير مرئي في أي اختبار لا يبحث عنه تحديدًا.
هذا دليل للبحث عنه فعليًا. وهو يفترض أن تصميم واجهتك مبني أصلًا على الخصائص المنطقية (logical properties) وأن أيقوناتك مرتّبة مسبقًا — إن لم يكن الأمر كذلك، فتلك مشكلة معمارية تناولها مقالنا المرافق عن بناء نظام تصميم يدعم LTR وRTL. أما هذا المقال فهو طبقة اختبار الجودة فوق ذلك: كيف تفرض عرض RTL دون لمس لغة نظام التشغيل، وما الذي يجب فحصه فعليًا بمجرد أن تبدأ النظر، وكيف تُدرج هذا الفحص ضمن خط CI، وكيف تتحقق من أن قارئ الشاشة يتعامل مع المحتوى العربي بنفس الطريقة التي تراها عين مختبِر مبصر.
أبرز النقاط
- النص الإنجليزي التجريبي (placeholder) لا يمكنه كشف أخطاء RTL، لأن خوارزمية اليونيكود للنص ثنائي الاتجاه لا تُنتج ترتيبًا ملتبسًا إلا حين يمزج السطر فعليًا بين أنظمة كتابة أو أرقام أو علامات ترقيم — فاختبار RTL على نص "لوريم إيبسوم" داخل حاوية RTL لا يختبر شيئًا حقيقيًا.
- يملك متصفح Chromium علامة تطوير حقيقية لهذا الغرض:
chrome://flags/#force-ui-direction(أو--force-ui-direction=rtlمن سطر الأوامر) والتي تحوّل المتصفح بالكامل إلى وضع RTL دون لمس نظام التشغيل أو إعدادات اللغة — وهذا أسرع من الاحتفاظ بجهاز ثانٍ بلغة نظام مختلفة لكل فحص.- لا يملك كل من Playwright وCypress وضعًا مدمجًا لـ"الاختبار بـ RTL". خيارات المحاكاة في Playwright تغطي اللغة (locale) والمنطقة الزمنية، وليس اتجاه النص — والتحقق من
dirأمر يجب أن يقوم به كود الاختبار صراحةً.- تملك أداة Percy ميزة موثّقة ومخصّصة لـ RTL: إضافة Storybook الخاصة بها تدعم
additionalSnapshotsالتي تلتقط لقطة إضافية موسومة بـ RTL لكل قصة (story). أما نظام Modes في Chromatic فلا يملك وضع RTL مدمجًا مكافئًا — يتوجّب عليك تركيب واحد بنفسك من عناصره العامة الخاصة باللغة والمُزخرفات (decorators).dir="rtl"يتحكم في التخطيط لا في النطق. قارئات الشاشة تبدّل الأصوات بناءً على سمةlang، ووفق إرشادات W3C حول لغة الأجزاء، فإن أي تغيير لغوي غير موسوم يُقرأ وفق قواعد النطق الافتراضية لقارئ الشاشة — أي أن نصًا عربيًا بلاlang="ar"قد يُنطق بشكل خاطئ حتى لو ظهر بشكل صحيح على الشاشة.- الأدوات الآلية تُضيّق الفجوة لكنها لا تُغلقها: وجد تقرير Deque الخاص بتغطية الاختبار الآلي لإمكانية الوصول أن الاختبار الآلي يكتشف 57.38% من المشكلات من حيث الحجم — وهو رقم يشير التقرير نفسه إلى أنه مبالغ فيه بسبب فحوصات تباين الألوان، التي يكتشفها الاختبار الآلي بنسبة تتجاوز 83% من الوقت.
ابدأ بمحتوى قادر فعلًا على كشف الأخطاء
السبب الأكثر شيوعًا لعدم اكتشاف أي شيء في جولة اختبار RTL هو أنها نُفّذت على محتوى غير قادر أصلًا على كشف خطأ. نص "لوريم إيبسوم"، أو جملة بلا أرقام، أو نموذج بلا حقل بريد إلكتروني ظاهر — لا شيء من ذلك يُفعّل الجزء المعطوب من مسار العرض. توجد خوارزمية اليونيكود للنص ثنائي الاتجاه أساسًا لأنه، كما تنص المواصفة، "دون تحديد واضح، يمكن أن تنشأ حالات التباس عند تحديد ترتيب الأحرف المعروضة حين لا يكون الاتجاه الأفقي للنص موحّدًا" — وهذا الالتباس لا يظهر إلا حين يمزج السطر فعليًا بين اتجاهين.
قبل فتح أدوات المطوّرين، جهّز محتوى اختبار مضمون التفعيل لهذه الحالة:
- جملة عربية تتضمن اسم علامة تجارية أو منتج بالإنجليزية.
- سعر أو رقم طلب بأرقام لاتينية داخل نص عربي (
الطلب رقم ORD-4471 بقيمة 129.50 د.إ). - بريد إلكتروني أو رابط مضمّن داخل نص عربي.
- كلمة عربية طويلة غير منقطعة، لفحص سلوك القطع (
text-overflow). - نص من إنتاج المستخدم يبدأ بالإنجليزية ويستمر بالعربية — فالمتصفح يختار اتجاه الأساس من أول حرف ذي اتجاه قوي، لا من لغة الصفحة نفسها.
كل فحص في بقية هذا المقال يصبح أسهل تنفيذًا، وكل إصلاح يحصل على اختبار انحدار حقيقي، بمجرد أن تختبر على محتوى بهذا الشكل بدلًا من فقرة من "lorem ipsum dolor sit amet".
فرض اتجاه RTL دون لمس نظام التشغيل
لست بحاجة إلى جهاز ثانٍ بلغة نظام عربية لفحص تخطيط ما. يملك Chromium علامة مخصصة لهذا: chrome://flags/#force-ui-direction، والتي يمكن استخدامها أيضًا من سطر الأوامر بصيغة --force-ui-direction=rtl، وهي تحوّل المتصفح إلى RTL دون المساس بنظام التشغيل أو إعدادات اللغة الأساسية. وهي أمر مختلف فعليًا عن ضبط dir="rtl" على مستندك الخاص — فهي تقلب أيضًا واجهة كروم نفسها وعناصر النماذج الأصلية، وهو أمر مفيد لاكتشاف أخطاء لا تلمسها ملفات CSS الخاصة بك إطلاقًا. ويشير النقاش نفسه إلى محدودية صادقة: يبدو عرض النص غير طبيعي قليلًا تحت تأثير هذه العلامة (قد تقع علامات الترقيم في نهاية الجملة في مكان غريب)، لذا استخدمها للحكم على التخطيط، لا لمراجعة النص العربي إملائيًا.
للفحص على مستوى الصفحة فقط، افتح أدوات المطوّرين، اختر عنصر <html> في لوحة العناصر، وعدّل سمة dir مباشرة — تُعاد الصفحة إلى الرسم فورًا، ما يجعل التبديل ذهابًا وإيابًا أثناء فحص مكوّن واحد أمرًا سريعًا. تعمل الحيلة نفسها في أداة الفحص الخاصة بفَيَرفُكس، لأنها مجرد تعديل سمة DOM وليست ميزة خاصة:
// يقلب اتجاه المستند نفسه فقط، لا واجهة المتصفح
document.documentElement.setAttribute('dir', 'rtl');
document.documentElement.setAttribute('lang', 'ar');
احتفظ بالحيلتين معًا: علامة Chromium تكتشف أخطاء في ما يعرضه المتصفح نفسه (أشرطة التمرير، قوائم <select> الأصلية، فقاعات التحقق من صحة النماذج)؛ بينما تعديل السمة أسرع للتكرار على مكوّناتك الخاصة.
البحث عن الأخطاء: ما الذي يتعطّل فعليًا
بعد تحميل محتوى ثنائي الاتجاه حقيقي وقلب الصفحة، راجع هذه النقاط تقريبًا بترتيب تكرار حدوثها.
الأيقونات وعناصر التحكم الاتجاهية. تناولناها بالتفصيل في مقال نظام التصميم — أما نسخة اختبار الجودة فبسيطة: هل يشير كل زر رجوع/تقدّم، وترقيم صفحات، ومؤشر تقدّم إلى الاتجاه الذي يسير فيه ترتيب القراءة فعليًا؟ وهل بقيت كل أيقونة لا يجب أن تنعكس (التشغيل، علامة الصح، الساعة، الشعار) دون تغيير؟
حقول النماذج التي يجب أن تبقى LTR. البريد الإلكتروني والهاتف والرابط وكلمة المرور بيانات بأحرف لاتينية حتى داخل صفحة عربية، والحقل الذي يرث dir="rtl" سيُحاذى إلى اليمين، وقد يجعل — وفق إرشادات أهل الخبرة في تدقيق تخطيطات RTL — مؤشر الكتابة يقفز إلى الطرف الخاطئ من النص أثناء الكتابة. أعِد هذه الحقول قسرًا إلى LTR بغض النظر عن السياق المحيط:
input[type="email"],
input[type="tel"],
input[type="url"],
input[type="password"] {
direction: ltr;
text-align: start;
}
نظام الأرقام الخاطئ. يجب أن تعرض معظم واجهات المنتجات في منطقة الشرق الأوسط وشمال أفريقيا الأرقام بصيغة لاتينية (123 وليس ١٢٣) حتى داخل النص العربي، مع الاحتفاظ بالأرقام الهندية-العربية للسياقات الثقافية أو الأدبية فقط — تحقق من ألا تكون مكتبة تنسيق اللغة قد بدّلت الأسعار أو العدّادات أو الطوابع الزمنية دون قصد.
نصوص ثنائية الاتجاه غير معزولة. أينما يُدرج متغيّر داخل نص مترجم — اسم مستخدم، رقم طلب — تحقق مما يحدث حين يختلف نظام كتابة ذلك المتغيّر عن الجملة المحيطة به. وفق توثيق MDN لسمة dir، صُمم dir="auto" لهذه الحالة بالذات: فهو "يتيح للمتصفح تحديد اتجاه الأساس من النص، عادةً باستخدام أول حرف ذي اتجاهية قوية"، وهو مخصص "للبيانات ذات الاتجاهية غير المعروفة، مثل البيانات القادمة من إدخال المستخدم". إن لم تُعزل القيمة بعنصر <bdi> أو بـ dir="auto"، انتبه لوقوع علامات الترقيم أو الأقواس في الجانب الخاطئ من رقم أو اسم.
فيض أفقي عند الحافة الخاطئة. التخطيط الذي فُحص فقط بوضع LTR قد يفيض عبر الحافة المنطقية النهائية دون ظهور شريط تمرير حيث يُتوقع، أو قد يظهر شريط تمرير حيث لا ينبغي. غيّر عرض الإطار إلى أضيق من المعتاد وتحقق من الحافتين معًا.
عناصر المتصفح الأصلية. منتقيات التاريخ، قوائم <select>، وفقاعات التحقق من صحة النماذج يعرضها المتصفح نفسه وليس CSS، لذا افحصها تحديدًا تحت علامة force-ui-direction — فتعديل السمة على مستوى الصفحة لا يقلبها دائمًا.
أتمتة الفحص: Playwright وCypress
لا تُعامل أي من الأداتين اتجاه RTL كمفهوم أساسي. تغطي وثائق المحاكاة في Playwright خياري locale وtimezoneId كخيارات سياق —
const context = await browser.newContext({ locale: 'ar-AE', timezoneId: 'Asia/Dubai' });
— لكن محاكاة اللغة تغيّر navigator.language وترويسة Accept-Language، لا اتجاه النص. إن كان تطبيقك يستنتج dir من اللغة على مستوى الخادم، فضبط locale كافٍ لتفعيله من طرف إلى طرف، وهذه هي النسخة الأجدر بالاختبار لأنها ما يصادفه المستخدمون الحقيقيون فعليًا:
test('RTL checkout renders logical alignment', async ({ page }) => {
await page.goto('/ar/checkout');
await expect(page.locator('html')).toHaveAttribute('dir', 'rtl');
const total = page.getByTestId('order-total');
await expect(total).toHaveCSS('text-align', 'start'); // نمط محسوب فعليًا، لا مجرد لقطة شاشة
});
لا تملك Cypress أي خيار لمحاكاة اللغة إطلاقًا، لذا اضبط السمة مباشرة وتحقق مقابل محتوى الاختبار ثنائي الاتجاه الموصوف أعلاه:
cy.visit('/ar/dashboard');
cy.get('html').should('have.attr', 'dir', 'rtl');
cy.get('[data-testid="user-balance"]').should('contain.text', 'ORD-4471');
النمط الجدير بإدراجه في CI هو نفسه في الحالتين: تحقق صراحةً من dir/lang بدلًا من افتراض أن المسار عُرض بشكل صحيح، ونفّذ ذلك مقابل محتوى ثنائي الاتجاه — لا نصوص نظيفة أحادية نظام الكتابة لا يمكنها كشف الخطأ حتى لو كان موجودًا.
اختبار الانحدار البصري: تحقق من دعم RTL الفعلي للأداة
تناسب مقارنة لقطات الشاشة اختبار RTL جيدًا لأن أخطاء الاتجاه مكانية بطبيعتها — هامش في الجانب الخاطئ أو أيقونة لم تنعكس يظهران كفرق في البكسل. لكن "دعم الاختبار البصري" و"دعم اختبار RTL" ليسا الادعاء نفسه.
تملك Percy ميزة موثّقة لهذا الغرض: إضافة Storybook الخاصة بها تقبل additionalSnapshots، والتي تلتقط لقطة إضافية بلاحقة مميزة ومعامل استعلام direction: 'rtl':
// .storybook/percy.js
module.exports = {
additionalSnapshots: [
{ suffix: ' [RTL]', queryParams: { direction: 'rtl' }, include: ['^FormElement: .*'] },
],
};
وهذا لا يعمل إلا إذا كانت معاينة Storybook الخاصة بك تقرأ فعليًا معامل الاستعلام هذا وتقلب dir على جذر المعاينة — فالإضافة تلتقط اللقطة الإضافية، لكن المُزخرف (decorator) الخاص بك هو من يُحدث تغيير الاتجاه فعليًا.
أما Chromatic فلا يملك وضع RTL مدمجًا مكافئًا. توثيق Modes الخاص به يصف نظامًا عامًا لتنويع الحالة العامة (globals) لـ Storybook — حجم الإطار، السمة، اللغة — عبر مُزخرفات "تقرأ القيمة العامة وتوفر السياق اللازم لعرض وضع معيّن". اللغة عنصر أساسي هناك؛ أما الاتجاه فلا، لذا عليك تعريف وضع RTL خاص بك بنفس الطريقة التي تعرّف بها وضع سمة مخصص. لا خطأ في أي من النهجين — الفرق هو هل بنت الأداة توصيل RTL نيابة عنك أم تتوقع منك تركيبه بنفسك. تحقق من أيهما تعتمد عليه قبل افتراض أن أيًا منهما "يدعم RTL" جاهزًا، وازدوج فحص الفرق البصري لأي أداة بالتحقق الصريح من السمات المذكور أعلاه، لأن حد تشابه متساهلًا قد يُجيز لقطة شاشة خاطئة بشكل متسق وموحّد.
قارئات الشاشة: RTL تخطيط، لا نطق
من السهل افتراض أن صفحة تُعرض بشكل صحيح بالعربية ستُسمع بشكل صحيح أيضًا. dir وlang إشارتان منفصلتان، وواحدة فقط منهما تؤثر في الكلام. إرشادات W3C حول لغة الأجزاء واضحة: عند الوسم الصحيح، "ستتمكن أجهزة تحويل النص إلى كلام التي تدعم لغات متعددة من نطق النص باللكنة المناسبة والنطق الصحيح"؛ وعند عدم الوسم، "سيحاول الجهاز جاهدًا نطق الكلمات باللغة الافتراضية التي يعمل بها"، فينطقها بشكل خاطئ. تخطيط مثالي البكسل في RTL لكنه يفتقد lang="ar" سيفشل مع ذلك في جولة اختبار قارئ الشاشة.
يجعل قارئا شاشة شائعان هذا الأمر قابلًا للاختبار فعليًا:
- NVDA يوفّر إعدادًا، وفق دليل المستخدم الخاص به، لـ"تبديل لغات محرك تحويل النص إلى كلام تلقائيًا إذا حدّد النص المقروء لغته" — وعلى الويب، هذا التحديد هو سمة
lang. حمّل صفحتك العربية باستخدام NVDA ومحرك نطق يدعم العربية، وتحقق من أنه يبدّل الأصوات فعليًا عند كل حدود لغوية. - VoiceOver يعرض الفكرة نفسها عبر عجلته (rotor): وفق توثيق دعم آبل، فإن تدوير العجلة إلى "اللغة" وتمريرها لأعلى أو لأسفل يبدّل بين اللغات المضافة في الإعدادات، ما يتيح لك التحقق من أن المحتوى العربي واللاتيني يُقرآن كل بصوته الصحيح.
الأدوات الآلية تُضيّق الفجوة لكنها لا تُغلقها. يتحقق كل من axe-core وLighthouse من وجود سمة lang صالحة على المستند — وتوثّق Deque هذا صراحةً باسمي القاعدتين html-has-lang وvalid-lang — وهو ما يكتشف حالة نسيان lang تمامًا، لكن لا يمكن لأي منهما التحقق من أن مقطعًا عربيًا مُدرَجًا بعينه حصل على lang="ar" الخاص به، أو أن محرك النطق يملك أصلًا صوتًا عربيًا مثبّتًا. وجد تقرير Deque الخاص بتغطية الاختبار الآلي لإمكانية الوصول أن الأدوات الآلية تكتشف 57.38% من مشكلات إمكانية الوصول من حيث الحجم عبر صفحات مدقَّقة فعليًا — وهو رقم يشير التقرير نفسه إلى أنه منحاز بسبب فحوصات تباين الألوان، التي يكتشفها الاختبار الآلي بنسبة تتجاوز 83% من الوقت وتُشكّل ما يقارب ثلث كل المشكلات المسجّلة. أخطاء اللغة والنطق ليست ضمن هذه الفئة السهلة؛ خصّص وقتًا لجولة فعلية بـ NVDA مع Firefox أو VoiceOver مع Safari، ولا تدع تقرير axe-core الأخضر يحل محلها.
قائمة تحقق عملية لاختبار RTL
نفّذ هذه القائمة بعد أي تغيير يمس التخطيط أو النماذج أو النصوص المترجمة — لا قبل الإطلاق فقط.
- [ ] حمّلت الصفحة بمحتوى اختبار حقيقي ثنائي الاتجاه — لا نص "لوريم إيبسوم".
- [ ] تحققت من ضبط كل من
dir="rtl"وlang="ar"معًا، لا واحدة منهما فقط. - [ ] فحصت عناصر المتصفح الأصلية (منتقيات التاريخ،
<select>، فقاعات التحقق) تحت--force-ui-direction=rtl. - [ ] تأكدت أن أيقونات التسلسل (رجوع، تقدّم، ترقيم الصفحات، التقدّم) تشير بالاتجاه الصحيح، وأن الأيقونات غير الاتجاهية لم تنعكس.
- [ ] تأكدت أن حقول البريد الإلكتروني والهاتف والرابط وكلمة المرور تبقى LTR داخل نموذج RTL.
- [ ] تأكدت أن الأسعار والعدّادات والمعرّفات تستخدم نظام الأرقام المقصود.
- [ ] فحصت الفيض الأفقي عند حافتي الإطار معًا بعرض ضيق.
- [ ] كتبت اختبار Playwright أو Cypress يتحقق من
dir/langومن نمط محاذاة منطقي محسوب، لا مجرد لقطة شاشة. - [ ] التقطت لقطة انحدار بصري لـ RTL باستخدام الآلية الموثّقة الفعلية للأداة، وتأكدت أن هيكل التطبيق يستجيب للإشارة التي ترسلها.
- [ ] نفّذت جولة فعلية بـ NVDA أو VoiceOver وتأكدت من تبديل الصوت عند كل حدود لغوية.
لا يتطلب أي مما سبق أدوات نادرة — علامة متصفح، تعديل سمة، بضعة تحققات اختبار، وعشرون دقيقة مع قارئ شاشة تملكه معظم الفرق أصلًا. ما يتطلبه هو تنفيذ ذلك عن قصد، على محتوى مصمّم ليفشل إن كان هناك خطأ، بدلًا من افتراض أن نسخة تُبنى وتُعرض بشكل صحيح بالإنجليزية صحيحة أيضًا في الاتجاه الذي لا يقرأ به أحد في الفريق بشكل افتراضي.