في كل مشروع مفتوح المصدر أشارك فيه، أخرج بدرس مختلف. هذه المرة كانت المساهمة في RATQ — منصة المطوّرين في مجتمع «إتقان» لاكتشاف موارد البيانات القرآنية والتحقق منها ودمجها (مكتبات، SDKs، Datasets، APIs، ومصادر تفسير). المشروع مبني بـ Next.js / TypeScript، وما زال قبل الإطلاق، لكن فرع main هو مصدر الحقيقة للكود.
المساهمة لم تكن سطرًا برمجيًا يُرسَل في Pull Request، بل مرّت بمراحل: قراءة الـ Issue بعناية، طلب الإسناد، فهم كيف يتعامل المشروع مع اللغة والاتجاه، كتابة الحل، إضافة اختبار، ثم مراجعة الكود مع المشرفين.
ما المشكلة؟
في المشروع مكوّن ToastContainer مسؤول عن إشعارات الـ Toast (رسائل النجاح والخطأ والمعلومات). المشكلة كانت في src/shared/ui/Toast.tsx: قيمتان مكتوبتان يدويًا (hardcoded):
<div className="fixed bottom-4 left-4 z-50 ..." dir="ltr">
النتيجة: الإشعارات تظهر دائمًا أسفل اليسار وباتجاه LTR، حتى عندما يكون التطبيق في الوضع العربي (RTL). وهذا يكسر تجربة المستخدم في لغة الواجهة الأساسية للمشروع.
المطلوب بسيط في ظاهره: اجعل الموضع والاتجاه ديناميكيَّين حسب لغة التطبيق. لكن «البسيط» هنا كان له شرط: ألا يبقى أي شيء مكتوبًا يدويًا، وأن يمر الحل عبر النظام الموجود بالفعل، لا أن يخترع طريقًا جانبيًا.
الحل: لا تجعل المكوّن يعرف الاتجاه، اجعله يسأل عنه
المشروع يدير اللغة والاتجاه عبر i18n context في src/shared/ui/i18n، ويوفّر hook باسم useLanguage() يُرجِع — من ضمن ما يُرجِع — قيمة direction تساوي 'rtl' للعربية و'ltr' للإنجليزية.
فبدلًا من أن يحمل ToastContainer قيمًا ثابتة، صار يقرأ الاتجاه من المصدر الوحيد للحقيقة:
const { direction } = useLanguage();
return (
<div
className={fixed bottom-4 ${direction === "rtl" ? "right-4" : "left-4"} z-50 flex flex-col gap-2}
dir={direction}
تأكدت أيضًا أن ToastProvider مُغلَّف داخل LanguageProvider في src/app/layout.tsx، وإلا لكان استدعاء الـ hook غير آمن.
النتيجة:
في وضع RTL: الإشعارات أسفل اليمين مع dir="rtl".
في وضع LTR: الإشعارات أسفل اليسار مع dir="ltr".
لا قيمة موضع أو اتجاه مكتوبة يدويًا.
التغيير صغير — سطران تقريبًا — لكن الفكرة وراءه أهم من حجمه: المكوّن لا ينبغي أن يعرف تفاصيل اللغة؛ عليه أن يطلبها من الطبقة المسؤولة عنها.
لماذا كان الاختبار مهمًا؟
في CONTRIBUTING الخاص بالمشروع قاعدة واضحة: إصلاح أي خطأ منطقي يحتاج اختبار انحدار (regression test) يفشل على الكود القديم وينجح على الجديد. فأضفت src/tests/Toast.test.tsx.
النقطة التي انتبهت لها: الاختبار يجب أن يفحص السلوك، لا مجرد أن المكوّن «يُرسَم» بلا خطأ. فالاختبار يتحقق فعليًا من:
في العربية: السمة dir="rtl" موجودة، والكلاس يحتوي right-4 ولا يحتوي left-4.
في الإنجليزية: العكس تمامًا.
اختبار يتأكد فقط أن render() لم يرمِ استثناءً كان سيمرّ على الكود المكسور نفسه.
قبل فتح الـ PR: npm run test:run وnpm run lint وtsc --noEmit — كلها نظيفة على الملفات التي لمستها.
مراجعة الكود: أوسع من «يعمل عندي»
المشرف (abubakr-itqan) راجع التغيير من الأول للآخر: رجع إلى السطر الأصلي، وأكّد أن direction يُعرَض من useLanguage() بنفس الطريقة التي يستخدمها بها كود موجود في المشروع (src/app/page.tsx)، وشغّل ملف الاختبار بنفسه، وتأكد أنه يفحص الكلاس والسمة لا مجرد الرسم، وأن التغيير مطابق لمعايير القبول في الـ Issue بلا أي توسّع خارج نطاقها (no scope creep).
CodeRabbit (المراجعة الآلية) لم يسجّل أي ملاحظة تتطلب تعديلًا. ثم دُمج الـ PR في main، ثم دُمج main في فرع beta.
الدرس الذي يتكرر معي في كل مشروع: المراجعة ليست مرحلة «إثبات أن كودي صحيح»، بل جزء من التصميم — المشرف يسأل أسئلة لا يسألها كاتب الكود عادةً: هل يتبع اصطلاحات المشروع؟ هل يؤثر على مستخدمين حاليين؟ هل يمكن لمطوّر آخر أن يفهمه دون شرح؟
درس صغير: اصطلاحات المشروع ليست تفاصيل جانبية
سمّيت الفرع fix/toast-rtl-direction. اتضح أن فحص CI لأسماء الفروع في هذا المشروع يسمح فقط بالبادئات: feature/, feat/, hotfix/, community/, chore/, release/, dependabot/. البادئة fix/ ليست من ضمنها — وهي مذكورة في CONTRIBUTING لكن يسهل تفويتها.
المشرف تجاوز الفحص يدويًا (admin-merge) لأن التغيير نفسه سليم، ونبّهني للمرة القادمة. لا ضرر حصل، لكنها تذكِرة بأن كل مشروع له اصطلاحاته الخاصة، وأن الخبرة في مشروع سابق لا تعفيك من قراءة CONTRIBUTING كاملًا في المشروع الجديد.
أين ساعدني الذكاء الاصطناعي؟
استخدمت أداة AI coding agent في أجزاء من العمل:
استكشاف بنية المشروع وتحديد مكان i18n context وطريقة استخدامه في كود موجود.
كتابة اختبار الانحدار وتشغيله.
التحقق من نظافة الـ lint والـ typecheck على الملفات المتغيّرة.
صياغة تعليق الـ Issue وتوضيح التغيير.
لكن القرار والمراجعة النهائية بقيا بيدي. CONTRIBUTING نفسه يقول ذلك صراحة: استخدام أدوات AI مرحّب به، لكن الـ PR مسؤوليتك — أن تفهم كل سطر، وأن تشرحه في المراجعة، وأن يطابق اصطلاحات المشروع لا مخرجات AI العامة. الأداة كانت مساعدًا هندسيًا، لا صاحب القرار.
ماذا تعلمت من هذه المساهمة؟
المكوّن لا يجب أن يعرف تفاصيل اللغة أو الاتجاه؛ يطلبها من الطبقة المسؤولة (i18n context).
اختبر السلوك (السمة، الكلاس)، لا مجرد أن المكوّن يُرسَم.
كل مشروع له اصطلاحاته: تسمية الفروع، متى الاختبار إلزامي، كيف يُفتح الـ PR — اقرأ CONTRIBUTING كاملًا حتى لو كنت مساهِمًا متمرّسًا.
اطلب الإسناد على الـ Issue قبل أن تكتب سطرًا.
المراجعة جزء من التطوير، لا امتحان بعده.
استخدم AI للتحليل والتحقق، واحتفظ بالمسؤولية لنفسك.
الخاتمة
مساهمة صغيرة في حجمها — إصلاح اتجاه إشعارات — لكنها كانت تذكيرًا بأن العمل داخل مشروع مفتوح المصدر جديد يبدأ دائمًا من نقطة واحدة: فهم اصطلاحاته ومجتمعه قبل كتابة الكود. والأجمل أن المشروع يخدم أدوات تتعامل مع بيانات القرآن الكريم، ما يجعل الدقة واحترام بنية المشروع أكثر أهمية.
🔗 المساهمة على GitHub: https://github.com/Itqan-community/RATQ/pull/277