تخطي إلى المحتوى
الذكاء الاصطناعي

مدونتي الأولى

Ludo Fourrage Ludo Fourrage 8 أبريل 2026 10 دقيقة قراءة
مدونتي الأولى

بيان المشكلة:

تفتقر المنصّة حاليًا إلى قناة تسويق بالمحتوى. يوفّر إنشاء مدوّنة لكل مستأجر (tenant) إمكانية نشر مقالات متعددة اللغات ومهيّأة لتحسين محرّكات البحث (SEO)، تُكتَب في Strapi CMS وتُعرَض في تطبيق SvelteKit — ممّا يساهم في زيادة الزيارات العضوية، وبناء الموثوقية والريادة، ودعم خط أنابيب i18n الحالي الذي يشمل 13 إعدادًا لغويًا.

معايير النجاح

  • يتم عرض مقالات المدوّنة التي يتم تأليفها في Strapi على الواجهة الأمامية ضمن هوية العلامة التجارية الخاصة بالمستأجر (tenant)
  • يتم ترجمة المقالات تلقائيًا إلى جميع اللغات الثالثة عشرة عبر خط أنابيب الـ webhook الحالي (دون أي تغيير في بنية الترجمة)
  • تحقق صفحات المدوّنة ترتيبًا جيدًا في محرّكات البحث (بيانات منظَّمة، خريطة موقع sitemap، وسوم hreflang، ووسوم og:article)
  • المستأجرون الذين لا يمتلكون علم الميزة blog لا يرون أي مسارات للمدوّنة (تُرجع 404)

قصص المستخدم

  • بصفتي كاتب محتوى، أريد أن أكتب مقالات مدونة في Strapi باستخدام ماركداون غني، وصور، وتضمين فيديو حتى أتمكن من نشر المحتوى دون لمس الكود
  • بصفتي زائرًا للموقع، أريد تصفّح مقالات المدونة مع إمكانية التصفية حسب التصنيف حتى أتمكن من العثور على المحتوى المناسب
  • بصفتي زائرًا للموقع، أريد جدول محتويات في المقالات الطويلة حتى أتمكن من التنقل بسرعة
  • بصفتي زائرًا للموقع، أريد قراءة المقالات بلغتي، مع ترجمة تلقائية من الإنجليزية
  • بصفتي زائرًا للموقع، أريد مشاركة المقالات على وسائل التواصل الاجتماعي
  • بصفتي زائرًا للموقع، أريد اكتشاف مقالات ذات صلة والتعرّف على الكاتب
  • بصفتي مدير SEO، أريد أن تحتوي كل مقالة على وسوم ميتا صحيحة، وبيانات منظَّمة، ومدخلات في خريطة الموقع (Sitemap)

المتطلبات الوظيفية

ضروريات (النسخة الأولى MVP)

  1. أنواع محتوى Strapi: blog-posts، blog-authors، blog-categories — جميعها تحتوي حقول tenant, locale, inherit
  2. صفحة عرض المدوّنة (/blog) — شبكة منشورات مع ترقيم صفحات (pagination)، وفلتر للفئات عبر معامل في الاستعلام (query param)
  3. صفحة تفاصيل المنشور (/blog/[slug]) — عرض كامل للـ markdown مع فهرس محتوى (ToC)، بطاقة الكاتب، منشورات ذات صلة، ومشاركة على الشبكات الاجتماعية
  4. صفحة الفئة (/blog/category/[slug]) — قائمة منشورات مفلترة تعيد استخدام نفس مكوّنات صفحة العرض
  5. صفحة الكاتب (/blog/author/[slug]) — نبذة عن الكاتب + منشوراته
  6. فهرس المحتوى (Table of Contents) — يُولَّد تلقائيًا من عناوين h2/h3، شريط جانبي ثابت (sticky) على سطح المكتب، قابل للطي على الهاتف، مع تمييز العنوان النشط عبر Intersection Observer
  7. تضمين YouTube/Vimeo — عناوين URL العارية في markdown تتحوّل تلقائيًا إلى iframes متجاوبة (استخدام youtube-nocookie.com للخصوصية)
  8. وقت القراءة — يُحسب على الخادم اعتمادًا على عدد الكلمات (Math.ceil(words / 200))
  9. أزرار المشاركة الاجتماعية — X، فيسبوك، لينكدإن، البريد الإلكتروني، نسخ الرابط (استخدام Web Share API عند توفره)
  10. منشورات ذات صلة — من نفس الفئة، باستثناء المنشور الحالي، بحد أقصى 3 منشورات، مرتّبة تنازليًا حسب publishedAt
  11. خلاصة RSS (/blog/rss.xml) — وفق معيار RSS 2.0، مع تمرير اللغة (locale) عبر معامل في الاستعلام
  12. تحسين محرّكات البحث (SEO) — مكوّن SeoComponent لكل منشور، وسوم og:article، ومخطط JSON-LD من نوع BlogPosting، وروابط canonical
  13. خريطة الموقع (Sitemap) — إضافة روابط ديناميكية لمنشورات المدوّنة/الفئات/الكتّاب إلى خريطة الموقع الحالية مع hreflang
  14. علم ميزة (Feature flag)blog: boolean في TenantConfig.features، وحارس في التخطيط (layout guard) يعيد 404 عند تعطيله
  15. تعدد اللغات (i18n) — مفاتيح رسائل Paraglide لكل نصوص الواجهة (التنقّل، التصنيفات، ترقيم الصفحات)، ومحتوى Strapi يُترجَم تلقائيًا عبر الـ webhook الموجود
  16. ترقيم الصفحات (Pagination) — معتمد على الصفحات باستخدام ترقيم Strapi الأصلي (9 منشورات في كل صفحة)
  17. منشور مميَّز — حقل منطقي featured في blog-posts؛ يُعرَض المنشور المميَّز كبطاقة بطل (hero card) في أعلى صفحة عرض /blog (بطاقة أكبر، بعرض كامل، فوق شبكة المنشورات)

ميزات مستحسنة (بعد النسخة الأولى Post-MVP)

  1. بحث كامل النص عبر منشورات المدوّنة
  2. سلسلة منشورات / مقالات متعددة الأجزاء
  3. شريط يوضّح تقدّم القراءة
  4. تعليقات (على الأرجح عبر تضمين طرف ثالث)
  5. دعوة للاشتراك في النشرة البريدية داخل المنشورات
  6. عدّاد مشاهدات / تكامل مع التحليلات

المتطلبات التقنية

نموذج البيانات (أنواع المحتوى في Strapi)

blog-posts (api::blog-post.blog-post):

الحقل النوع متعدد اللغات ملاحظات
title نص (إجباري) نعم يطابق إعدادات الترجمة
slug UID (من title) لا مستثنى من الترجمة
excerpt نص نعم لبطاقات التدوينات + وصف الميتا
body نص منسّق (Markdown) نعم يُعرَض عبر marked
featuredImage وسائط (عنصر واحد) لا عنوان URL + حقل alternativeText
category علاقة (manyToOne -> blog-category) لا
author علاقة (manyToOne -> blog-author) لا
featured منطقي (الافتراضي false) لا تدوينة مميزة تُعرَض كبطاقة بطل في صفحة القائمة
seo مكوّن (shared.seo) نعم إعادة استخدام SeoComponent الموجود
tenant نص لا عامل تصفية قياسي للـ tenant
inherit منطقي (الافتراضي true) لا علم الترجمة التلقائية

blog-authors (api::blog-author.blog-author):

الحقل النوع متعدد اللغات ملاحظات
name نص (إجباري) نعم
slug UID (من name) لا
bio نص منسّق (Markdown) نعم
avatar وسائط (عنصر واحد) لا
socialLinks JSON لا { twitter?, linkedin?, website? }
tenant نص لا
inherit منطقي (الافتراضي true) لا

blog-categories (api::blog-category.blog-category):

الحقل النوع متعدد اللغات ملاحظات
name نص (إجباري) نعم
slug UID (من name) لا
description نص نعم
color نص لا لون Hex لشارات واجهة المستخدم (UI badges)
tenant نص لا
inherit منطقي (الافتراضي true) لا

واجهات TypeScript

راجِع src/lib/services/strapi-types.ts لتعريفات BlogPost و BlogAuthor و BlogCategory.

المسارات (Routes)

كلّها تحت src/routes/(marketing)/blog/:

المسار Server Load الغرض
blog/+layout.server.ts التحقق من تفعيل الميزة + تحميل الفئات بوابة الدخول + بيانات التنقل
blog/+page.server.ts تدوينات مع ترقيم الصفحات، مع عامل تصفية اختياري ?category= صفحة قائمة التدوينات
blog/[slug]/+page.server.ts تدوينة حسب الـ slug + تدوينات ذات صلة + حساب وقت القراءة صفحة التفاصيل
blog/category/[slug]/+page.server.ts تدوينات مفلترة حسب الفئة صفحة فئة محددة
blog/author/[slug]/+page.server.ts الكاتب + تدويناته صفحة الكاتب
blog/rss.xml/+server.ts RSS 2.0 XML، مع معامل ?locale= موجّه الخلاصات (Feed)

مكوّنات واجهة المستخدم (UI Components)

كلّها في src/lib/components/blog/:

المكوّن Props ملاحظات
BlogPostCard.svelte post: BlogPost بطاقة تحتوي على صورة، شارة الفئة، العنوان، المقتطف، الكاتب، التاريخ، وقت القراءة
BlogPostContent.svelte body: string تحويل Markdown إلى HTML عبر marked مع إضافة دعم تضمين الفيديو + معرفات للعناوين
TableOfContents.svelte entries: TocEntry[] شريط جانبي ثابت (Sticky)، يستخدم Intersection Observer لحالة العنصر النشط، وقابل للطي على الجوال
AuthorCard.svelte author: BlogAuthor صورة شخصية، اسم، نبذة، وروابط اجتماعية
SocialShare.svelte url: string, title: string أزرار مشاركة مع بديل باستخدام Web Share API
RelatedPosts.svelte posts: BlogPost[] شبكة من مكوّنات BlogPostCard
BlogPagination.svelte pagination: StrapiPagination, basePath: string السابق/التالي + أرقام الصفحات

الأمان والمصادقة

  • المدوّنة عامّة (لا يلزم تسجيل دخول) — محتوى تسويقي
  • الوصول للقراءة إلى واجهة برمجة تطبيقات Strapi يتم عبر رمز STRAPI_JWT الحالي
  • تضمين الفيديوهات يستخدم youtube-nocookie.com للامتثال للائحة حماية البيانات العامة (GDPR)

الحالات الحدّية ومعالجة الأخطاء

  • المنشور غير موجود: خطأ 404 عبر SvelteKit error()
  • لا منشورات في التصنيف: حالة فارغة مع رسالة blog_no_posts
  • الصورة البارزة مفقودة: تظهر البطاقة بدون صورة
  • الترجمة غير متوفرة: loadContentWithFallback() تعود افتراضيًا إلى الإنجليزية
  • اللغات من اليمين إلى اليسار: التخطيط يراعي dir="rtl" (ar, he, ur)

خطة اختبار شاملة (E2E Test Plan)

توجد ملفات الاختبار في tests/blog/. اتبع الأنماط الحالية: مُعمَّمة (parameterized) عبر أحجام الشاشات المختلفة (viewports)، استخدام Axe لاختبارات إمكانية الوصول (a11y)، و networkidle ينتظر محتوى Strapi.

اختبارات المسار الحرج (Critical Path Tests)

  1. تحميل قائمة المدوّنة/blog يعرض شبكة من بطاقات المقالات تحتوي على العنوان، المقتطف، شارة التصنيف، اسم الكاتب، التاريخ، ووقت القراءة
  2. عمل الترقيم (Pagination)/blog?page=2 تعرض الصفحة الثانية من النتائج؛ أزرار السابق/التالي تنتقل بشكل صحيح؛ الصفحة الأولى لا تحتوي على زر "السابق"
  3. فلتر التصنيف — النقر على شريحة تصنيف (category chip) يرشّح المقالات؛ يتم تحديث رابط الصفحة إلى ?category=<slug>؛ شريحة "الكل" تلغي الفلترة
  4. عرض صفحة المقال/blog/[slug] تعرض العنوان، الصورة البارزة، النص منسّقًا، بطاقة الكاتب، وقت القراءة، وتاريخ النشر
  5. جدول المحتويات (ToC) — صفحة تفاصيل المقال تُنشئ جدول محتويات من عناوين h2/h3؛ النقر على عنصر في جدول المحتويات يمرِّر الصفحة إلى العنوان المقابل
  6. مقالات ذات صلة — صفحة تفاصيل المقال تعرض حتى 3 مقالات ذات صلة من نفس التصنيف (باستثناء المقال الحالي)
  7. صفحة التصنيف/blog/category/[slug] تعرض اسم التصنيف، الوصف، وفقط المقالات التابعة لهذا التصنيف
  8. صفحة الكاتب/blog/author/[slug] تعرض نبذة الكاتب، الصورة الرمزية، روابط التواصل الاجتماعي، ومقالاته
  9. أزرار المشاركة على الشبكات الاجتماعية — أزرار المشاركة مرئية في صفحة تفاصيل المقال؛ زر "نسخ الرابط" ينسخ رابط الصفحة ويعرض رسالة تأكيد
  10. خلاصة RSSGET /blog/rss.xml تُرجِع XML صالحًا يحتوي على Content-Type: application/rss+xml ويتضمّن عناصر مقالات
  11. التنقّل (Navigation) — رابط المدوّنة في قائمة الموقع ينقل إلى /blog؛ مسارات التنقّل (breadcrumbs) تعرض التسلسل الهرمي الصحيح
  12. المقال المميز (Featured post)/blog يعرض المقال المميز كبطاقة رئيسية (hero card) أكبر فوق الشبكة العادية

اختبارات الحالات الحدّية والأخطاء (Edge Case & Error Tests)

  1. مقال غير موجود/blog/nonexistent-slug تُرجِع صفحة 404
  2. تصنيف فارغ — التصنيف الذي لا يحتوي على مقالات يعرض رسالة حالة الفراغ blog_no_posts
  3. صورة بارزة مفقودة — بطاقة المقال تُعرَض بشكل سليم بدون صورة (بدون <img> مكسورة)
  4. كاتب/تصنيف مفقود — مقال بدون كاتب أو تصنيف يُعرَض بدون هذه العناصر (بدون تعطل)
  5. تعطيل العلم المميِّز (Feature flag) — عند blog: false، /blog تُرجِع 404

اختبارات مشتركة (Cross-Cutting Tests)

  1. i18n: تبديل اللغة — الانتقال إلى /fr/blog، والتحقق من أن نصوص الواجهة (عنوان الصفحة، "اقرأ المزيد"، تسميات الترقيم) بالفرنسية؛ محتوى المقال يعود افتراضيًا إلى الإنجليزية إذا لم يكن مترجمًا
  2. i18n: تخطيط من اليمين إلى اليسار (RTL) — الانتقال إلى /ar/blog و /ar/blog/[slug]، والتحقق من أن dir="rtl" مضبوط وأن التخطيط يُعرَض بشكل صحيح (جدول المحتويات في الجهة الصحيحة)
  3. SEO: وسوم الميتا — صفحة تفاصيل المقال تحتوي على og:type=article، og:title، og:description، article:published_time، ورابط canonical
  4. SEO: JSON-LD — صفحة تفاصيل المقال تحتوي على <script type="application/ld+json"> مع @type: BlogPosting، العنوان (headline)، الكاتب، و datePublished
  5. SEO: خريطة الموقع (sitemap)/sitemap.xml تتضمّن عناوين URL لمقالات المدوّنة مع بدائل hreflang
  6. إمكانية الوصول (Accessibility) — تشغيل تدقيقات Axe على /blog، /blog/[slug]، /blog/author/[slug] على أحجام شاشات الهاتف وسطح المكتب (وفق نمط اختبارات a11y الحالي)
  7. التنقّل عبر لوحة المفاتيح — عناصر جدول المحتويات وأزرار الترقيم قابلة للتركيز والتفعيل عبر لوحة المفاتيح؛ مؤشرات التركيز (focus indicators) مرئية

خارج النطاق

  • تعليقات التدوينات
  • البحث في النص الكامل
  • دمج النشرات البريدية داخل التدوينات
  • التحليلات/عدد المشاهدات
  • قوالب التدوينات المخصصة لكل مستأجر
Ludo Fourrage
Ludo Fourrage

لودو هو مربٍّ ورائد أعمال في مجال التكنولوجيا، يؤمن بفكرة بسيطة: يجب أن تكون التعليم الجيد متاحًا للجميع.