بناؤك بالاتجاه من اليمين إلى اليسار يجتاز كل اختباراتك، وهو معطوب رغم ذلك

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

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

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

لماذا تعجز مقارنة اللقطات عن رؤية شعار معكوس

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

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

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

كتابات

تريد هذا على موقعك؟

احجز مكالمة

لماذا يمرّ رقم أُعيد ترتيبه في المراجعة

ضع السلسلة +180% داخل فقرة عربية وستعمل خوارزمية Unicode ثنائية الاتجاه. الأرقام يسارية الاتجاه بقوة، فيحتفظ 180 بترتيبه. أما علامتا الزائد والنسبة فمحرفان محايدان، ما يعني أن الخوارزمية تحسم موضعهما من اتجاه الفقرة المحيطة لا من ترتيب كتابتك. فينتقل كلاهما إلى الجهة الخطأ. وتحصل على %180+.

كل محرف في ذلك الناتج صحيح. هي أربعة محارف، وهي الأربعة التي طلبتها، ومرسومة بنظافة بالحجم الصحيح والخط الصحيح. مقارنة البكسلات ترى أربعة محارف صحيحة في ترتيب معقول. والتحقق من DOM يرى textContent مساوياً لـ +180%، لأن DOM يحمل السلسلة المنطقية وإعادة الترتيب تحدث وقت الطلاء. وكلاهما يمرّ.

النصف الذي تملكه الأتمتة فعلاً

الآلات بارعة في جزء واحد من هذا، وهو جدير بالاهتمام لأنه رخيص ويعمل عند كل التزام. الخصائص الفيزيائية في CSS لا تنعكس أبداً. والمنطقية تنعكس دائماً. لذا افحص ورقة الأنماط بحثاً عن المجموعة الفيزيائية واجعل البناء يخبرك عنها.

# Every physical property that will not flip under direction: rtl.
# Run it in CI and read the output as a list of candidates, not verdicts:
# a physical value is sometimes exactly what you meant.

rg -n --type css \
  -e '\b(margin|padding|border|inset)-(left|right)\b' \
  -e '^\s*(left|right)\s*:' \
  -e '\b(text-align|float|clear)\s*:\s*(left|right)\b' \
  -e '\bborder-(top|bottom)-(left|right)-radius\b' \
  src/
البدائل المنطقية هي margin-inline-start و padding-inline-end و inset-inline-start و text-align: start و border-start-start-radius. وكل منها ينعكس تلقائياً عند تغير الاتجاه.

كن صريحاً بشأن ما لا يبلغه هذا. لن يجد إطاراً مفتاحياً يزيح بنسبة سالبة، ولا صورة مقصوصة بـ object-position، ولا أيقونة مرسومة تشير يميناً داخل ملف SVG الخاص بها، ولا قاعدة عكس محددة بنطاق أوسع من اللازم. فتلك لا تُكتب left أو right في أي موضع من الملف. الفحص أرضية لا سقف.

عزل الرقم، ولماذا لا يشكّل النمط المحسوب دليلاً

علاج الرقم أن تخبر الخوارزمية أن القيمة جزيرة صغيرة قائمة بذاتها، لها اتجاهها الخاص الذي لا تطاله الفقرة المحيطة.

.stat__value {
  direction: ltr;
  unicode-bidi: isolate;
  /* pair it with tabular figures when the value counts up, or the
     digits jitter as their widths change during the animation */
  font-variant-numeric: tabular-nums;
}

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

قراءة موضع طلاء كل محرف فعلياً

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

/**
 * The visual order of a text node, read from the rendered rects rather
 * than from the string. Paste into DevTools on the real page.
 */
function visualOrder(node, paragraphDir = "ltr") {
  const text = node.textContent;
  const range = document.createRange();
  const chars = [];

  for (let i = 0; i < text.length; i++) {
    range.setStart(node, i);
    range.setEnd(node, i + 1);
    const rect = range.getBoundingClientRect();
    if (rect.width === 0) continue; // collapsed whitespace

    chars.push({ char: text[i], logical: i, x: rect.left });
  }

  // Read the way the paragraph reads.
  chars.sort((a, b) =>
    paragraphDir === "rtl" ? b.x - a.x : a.x - b.x,
  );

  return chars.map((c) => c.char).join("");
}

const el = document.querySelector(".stat__value");
visualOrder(el.firstChild, "rtl");
// "%180+"  before the isolate  -> the bug, visible as a string
// "+180%"  after               -> proof, not inference
الحيلة كلها في الترتيب بحسب موضع x المطلي. فهو يحوّل عطلاً تخطيطياً غير مرئي إلى سلسلة يمكنك التحقق منها في اختبار.

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

الجزء الذي يجب أن يكون إنساناً

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

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

لنبنِ الموقع الذي يستحقه عملك.

أرسل ما لديك الآن وإلى أين تريد الوصول. ستحصل على إجابة صريحة حول النطاق والجدول الزمني والتكلفة، عادة خلال ساعة.

نعمل عبر أمريكا الشمالية وأوروبا والشرق الأوسط.