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

- الدور
- تصميم وتنفيذ، من البداية للنهاية
- النطاق
- متجر ثنائي اللغة + لوحة إدارة — ٢٣ مسارًا
- التقنيات
- Next.js 14 · TypeScript · Tailwind · Prisma
- المُسلَّم
- لوحة شعار وصورة تراس — ملفان
المطلوب
مجموعة مطاعم إيطالية في القاهرة، ثلاثة فروع واثنان في الطريق. كانت لديهم هوية مكتملة — شعار مرسوم على هيئة قوس يحوي فيلا توسكانية، وصورة تراس، ولا شيء غير ذلك. لا موقع ولا طلب ولا حجز، وقائمة طعام تعيش في ملف جداول.
المطلوب كان موقعًا يعمل بالكفاءة نفسها بالعربية والإنجليزية. يبدو ذلك مهمة ترجمة وهو ليس كذلك. فالعربية تُكتب من اليمين، وقواعد جمعها ست صيغ مقابل صيغتين في الإنجليزية، والمحتوى نفسه — أسماء الأطباق والفروع والعناوين ووسوم المميزات — يجب أن يوجد باللغتين داخل قاعدة البيانات. والموقع الذي يترجم أزراره ويطبع أسماء أطباقه بالإنجليزية لم يُترجَم.
قيدان شكّلا ما عداهما. المطبخ يعمل أصلًا على Foodics، وهو نظام نقاط بيع شائع في الخليج ومصر، فكان لا بد للطلبات من وجهة. وهذا مشروع مفهومي لا نشر إنتاجي — فصار السؤال المثير: أي أجزاء تشغيل المطعم يمكن أن تعمل فعلًا بلا بوابة دفع ولا حساب نقاط بيع حقيقي خلفها.
ما تم تسليمه
- موقع عام بالعربية والإنجليزية — الرئيسية والقائمة وإتمام الطلب والحجز — بتخطيط RTL كامل، لا ملف أنماط معكوس
- قائمة طعام بتصفية حسب الفئة، وبحث ثنائي اللغة، ومخزون حيّ وحالات نفاد
- درج سلة وإتمام طلب بأكواد خصم تُتحقَّق على الخادم قبل أن يتغير الإجمالي على الشاشة
- حجوزات تقرأ مواعيد العمل الفعلية لكل فرع، بما فيها الفترات التي تعبر منتصف الليل
- مرشد طعام يقترح أطباقًا من القائمة الحيّة، مع بديل يعتمد مطابقة الكلمات حين لا يكون مفتاح الذكاء الاصطناعي مضبوطًا
- لوحة إدارة: لوحة معلومات، ومسار حالات الطلب، وإدارة القائمة، والحجوزات، وأكواد الخصم، ومزامنة Foodics، والتحليلات
- ثلاثة أدوار إدارية — ADMIN وMANAGER وVIEWER — يُعاد التحقق منها داخل كل Server Action
- إنشاء الطلب يخصم المخزون، ويزيد عدّاد استخدام كود الخصم، ويحسب الإجمالي من قاعدة البيانات، كل ذلك داخل معاملة واحدة
- مسارًا
- 23
- مكوّنًا
- 22
- سطر TypeScript
- 5,800+
- مفتاح ترجمة، بلا نقص
- 154 × 2


القرارات، ولماذا
سرد التقنيات المستخدمة لا يقول شيئًا. المفيد في دراسة الحالة هو القرارات التي كان لها بديل حقيقي — لذلك يذكر كل قرار هنا ثمنه، لا ما تم اختياره فحسب.
- ٠١
ثنائية اللغة مسألة قاعدة بيانات، لا مسألة ملف نصوص
كل نموذج محتوى يحمل عمودَي `*_ar` و`*_en`، ودالة واحدة تقرر أيّهما يُقرأ.
أسماء الأطباق وأوصافها وأسماء الفروع والعناوين ووسوم المميزات تعيش كلها في أعمدة مزدوجة، تحسمها وحدة واحدة لا ترتد إلى اللغة الأخرى إلا حين تكون المفضَّلة فارغة فعلًا. أما البديل — ملف ترجمة للواجهة ومحتوى إنجليزي فقط من قاعدة البيانات — فهو ما تشحنه أغلب المواقع ثنائية اللغة، وهو سبب أن صفحاتها العربية نصفها إنجليزي.
الثمن · عمودان لكل حقل قابل للترجمة، ولوحة الإدارة تملأ كليهما. وفي المقابل لا يوجد في الموقع العربي أي نص غير مترجم — وهذا قابل للتحقق لا مجرد ادّعاء.
- ٠٢
الـ Server Actions تعيد التحقق من الصلاحية
الـ ١١ Server Action كلها تنادي `requireRole()` كأول سطر فيها.
الـ Server Action نقطة نهاية قائمة بذاتها. والحارس المكتوب في جسم الصفحة لا يعمل قبل تنفيذ الإجراء، ومعرّفات الإجراءات بصمات ثابتة تُنتَج وقت البناء وتبقى في متصفح فتح الصفحة يومًا. ولولا ذلك لاستطاع أي مستخدم مسجَّل احتفظ بواحد منها أن يحذف أطباقًا أو يصدر أكواد خصم.
الثمن · السطر نفسه مكرر إحدى عشرة مرة. وهذا هو القدر الصحيح من التكرار لفحص صلاحية — فتجميعه في غلاف واحد هو ما يجعل نسيان أحدها ممكنًا.
- ٠٣
الأسعار لا تأتي من المتصفح أبدًا
مسار الطلب يعيد حساب السلة من `dishId` و`quantity` فقط.
العميل يرسل ما طُلب، لا ما يكلّفه. والخادم يحمّل تلك الأطباق، ويأخذ السعر من قاعدة البيانات، ويبني الإجماليات — بما في ذلك الحمولة المُمرَّرة إلى نظام نقاط البيع، وهي الجزء الذي يسهل إغفاله لأنه يبدو تمريرًا لبيانات أرسلها العميل أصلًا.
الثمن · استعلام إضافي واحد لكل عملية شراء. والبديل متجر يكون السعر فيه اقتراحًا.
- ٠٤
مُقيِّم خصم واحد، تتشاركه المعاينة والخصم الفعلي
`evaluatePromo()` هي الكود الوحيد الذي يقرر انطباق الكود، و`usedCount` يزيد داخل المعاملة نفسها التي تنشئ الطلب.
معاينة الشراء وإنشاء الطلب ينادِيان الدالة نفسها، فالخصم المعروض على الشاشة هو حسابيًا الخصم المُطبَّق. وكل قاعدة في النموذج — الفعالية والنافذة الزمنية والحد الأدنى للطلب وأقصى عدد استخدامات — تُطبَّق في موضع واحد. وتقسيم المنطق هو ما ينتهي بعميل يرى إجماليًا ويُخصم منه غيره، وبكود ذي استخدام واحد يُستهلك مرتين من طلبين متزامنين.
الثمن · رحلة إضافية واحدة حين يضغط العميل «تطبيق». وفي المقابل لا تستطيع المعاينة أن تكذب.
- ٠٥
سعة الحجز تحسب المقاعد، لا الحجوزات
`capacityPerSlot` تُقارَن بمجموع `partySize`.
عدّ الصفوف يُدخِل خمسًا وعشرين مجموعة، كلٌّ منها عشرون فردًا، إلى فترة سعتها خمسة وعشرون مقعدًا. والمجموع هو القراءة الوحيدة لكلمة «سعة» التي تصمد أمام صالة طعام حقيقية.
الثمن · لا ثمن في هذا المجال. هو ببساطة النموذج الصحيح، وذُكر هنا لأن التنفيذ البديهي هو الخاطئ.
- ٠٦
لوحة الألوان استُخرجت من الرسم، ولم تُختَر
الزيتوني #283710 والذهبي #C0944D والنبيذي #701F15 هي الأحبار التي يستخدمها الشعار فعلًا.
وصلت الهوية في ملفَي صورة. وبدلًا من تخمين لوحة ألوان بجوارهما، جرت تجزئة الرسم لونيًا وقيست عائلات الحبر الثلاث المهيمنة مباشرةً، ثم بُنيت داخل نظام الألوان. وسكريبت واحد يعيد اشتقاق كل صورة يشحنها الموقع — الشعار المقصوص، والشارة، وأيقونة الموقع، ولوحة الواجهة، والملمس — من الأصلين نفسيهما.
الثمن · استبدال لوحة مصدرية يعني إعادة تشغيل سكريبت واحد بدل تعديل ملفات يدويًا. وهذا هو المقصود.
- ٠٧
ملمس الورق معكوس في بلاطة ٢×٢
الحواف الأربع الخارجية متماثلة بحكم البناء، فتتكرر البلاطة بلا خطوط وصل.
الموقع مطبوع على رَق مأخوذ من الركن الفارغ في صورة التراس المُسلَّمة. والقصاصة الخام لا تتبلّط — حافتها اليسرى لا تطابق اليمنى أبدًا، وتكرارها رسم شبكة ظاهرة عبر كل قسم. وعكس القصاصة أفقيًا ورأسيًا في رباعية يجعل خطوط الوصل مستحيلة رياضيًا.
الثمن · الملمس متماثل. وعند التباين الذي يعمل به — انحراف معياري نحو ٩ على متوسط ٢٤٦ — فهذا غير محسوس، ويحل محل شبكة كانت محسوسة تمامًا.
جعل العربية لغة من الدرجة الأولى
الكتابة من اليمين هي الجزء الذي يعرفه الجميع، وهو الجزء السهل: خاصية `dir` واحدة وخصائص CSS منطقية بدل اليمين واليسار. أما العمل فهو في كل ما عدا ذلك.
الجموع. للعربية ست فئات جمع، وللإنجليزية اثنتان. و«فرد واحد / فردان» شرطٌ من فرعين في الإنجليزية، وخطأٌ في العربية التي تحتاج صيغًا متمايزة للواحد والاثنين والقلة والكثرة. وهذا ICU MessageFormat لا وصل نصوص، ومعناه أن قائمة عدد الأفراد تركيب مختلف فعلًا في كل لغة.
الخطوط. يحمل Cinzel العناوين اللاتينية — وهو خط نقشي روماني، أقرب قريب حي للحروف المحفورة في الشعار. وتأخذ العربية عارف رقعة، وهو من الخطية بما يكفي ليقف بالوزن نفسه بجواره، مع خط القاهرة للنص الجاري والواجهة. والنصّان مضبوطان بأحجام مختلفة قليلًا، لأن ارتفاع حروف القاهرة صغير بجوار Lora.
الأرقام. كانت الأرقام العربية الهندية أول تنفيذ وهي الخيار الأصيل، ثم تُراجع عنها: فمع `ar-EG` كانت الأسعار تظهر ١٦٥ بينما تبقى الساعات وأرقام الهواتف لاتينية، فخلطت الصفحة الواحدة نظامَي ترقيم. وتثبيت اللغة على `ar-EG-u-nu-latn` يبقي أعراف التنسيق العربية مع أرقام لاتينية، وهو ما تفعله مواقع المطاعم المصرية فعلًا.
حركة واعية بالاتجاه. درج السلة ينزلق من اليمين في الإنجليزية ومن اليسار في العربية. وصورة التراس في الواجهة معكوسة، فتبقى الفيلا عند الحافة الخارجية ويبقى الرَق المفتوح خلف النص في اتجاهَي القراءة كليهما.
التحقق. سكريبت يمشي على كل عقدة نصية في الصفحات العربية ويرفع علمًا على أي نص لاتيني الغالب وليس في قائمة أسماء العلم المسموح بها. وقد وجد تسعة نصوص متبقية في المرور الأول — وسوم مميزات الفروع خارجةً من قاعدة البيانات كبيانات إنجليزية فقط، وهو ما دفع إلى فصل `features_ar` عن `features_en` وترحيلهما. وهو الآن يبلّغ بصفر.
ما استُبعد عن قصد
ضبط النطاق جزء من العمل. كل بند هنا دُرس ثم استُبعد لسبب مذكور.
- الدفع بالبطاقة أونلاين
- يحتاج بوابة حية وحساب تاجر حقيقي. والدفع بالبطاقة يسجّل النية ويخبر العميل أنه يدفع عند الاستلام. واستبدالها بـ Paymob أو Stripe يغيّر وحدة واحدة، لا المسار.
- حسابات للعملاء
- أكبر ما يُبنى في التجارة الإلكترونية وأقله تمييزًا لمطعم. والشراء كضيف مع رقم طلب يغطي ما يفعله الناس فعلًا.
- تأكيدات بالبريد والرسائل القصيرة
- يحتاج حساب مزوّد. والطلب والحجز كلاهما يؤكَّد على الشاشة بدلًا من ذلك.
- مزامنة السلة على الخادم
- السلة تعيش في `localStorage`، فلا تتبع الضيف بين الأجهزة. ومزامنتها تحتاج الحسابات التي استُبعدت أصلًا.
المطعم وفروعه ومؤسسوه وتاريخه التجاري كلها متخيَّلة. لا بياتزا مشروع عرضي، والموقع يقول ذلك في تذييله. وأرقام هذه الصفحة قيست على المستودع في ٤ سبتمبر ٢٠٢٦.
التقنيات
- Next.js 14
- React
- TypeScript
- Tailwind CSS
- Prisma
- Vercel
الخدمات
- تصميم المنتج
- هندسة الواجهة
- هندسة الخلفية
- التوطين (عربي/إنجليزي)
- امتداد الهوية
محتاج حاجة مبنية بنفس المستوى؟
نصمم ونبني أنظمة كاملة — مسار شراء يعمل، وصلاحيات، ولوحة تحكم تدير الشغل فعليًا. والعربية عندنا ليست ترجمة تُركَّب في الآخر.
