דלג לתוכן
בינה מלאכותית

הבלוג הראשון שלי

Ludo Fourrage Ludo Fourrage 8 באפריל 2026 8 דקות קריאה
הבלוג הראשון שלי

הצגת הבעיה:

הפלטפורמה חסרה ערוץ שיווק תוכן. בלוג מאפשר לכל טננט לפרסם מאמרים מרובי-שפות, מותאמים ל‑SEO, שנכתבים ב‑Strapi CMS ומוצגים באפליקציית SvelteKit — מה שמניע תנועה אורגנית, מבסס סמכות, ותומך בצנרת ה‑i18n הקיימת ל‑13 לוקאלים.

קריטריוני הצלחה

  • פוסטים בבלוג שנכתבו ב-Strapi מוצגים בפרונטאנד בתוך המיתוג של ה-tenant
  • פוסטים מתורגמים אוטומטית לכל 13 ה-locales דרך ה-webhook pipeline הקיים (ללא שינויים בתשתית התרגום)
  • עמודי הבלוג מדורגים במנועי חיפוש (structured data, sitemap, hreflang, og:article)
  • Tenants ללא דגל הפיצ'ר blog אינם רואים נתיבי בלוג (404)

סיפורי משתמש

  • בתור כותב תוכן, אני רוצה לכתוב פוסטים לבלוג ב‑Strapi עם מרקडाउन עשיר, תמונות והטמעת וידאו, כדי שאוכל לפרסם תוכן בלי לגעת בקוד
  • בתור מבקר באתר, אני רוצה לעיין בפוסטים בבלוג שמסוננים לפי קטגוריה, כדי שאוכל למצוא תוכן רלוונטי
  • בתור מבקר באתר, אני רוצה תוכן עניינים בפוסטים ארוכים, כדי שאוכל לנווט במהירות
  • בתור מבקר באתר, אני רוצה לקרוא פוסטים בשפה שלי, עם תרגום אוטומטי מאנגלית
  • בתור מבקר באתר, אני רוצה לשתף פוסטים ברשתות חברתיות
  • בתור מבקר באתר, אני רוצה לגלות פוסטים קשורים ולהכיר את כותב הפוסט
  • בתור מנהל SEO, אני רוצה שלכל פוסט יהיו תגיות מטא מתאימות, נתונים מובנים ורישום במפת האתר

דרישות פונקציונליות

חייב להיות (MVP)

  1. Strapi content types: blog-posts, blog-authors, blog-categories — לכולם שדות tenant, locale, inherit
  2. עמוד רשימת בלוג (/blog) — גריד פוסטים מדופדף (paginated), פילטר קטגוריה דרך query param
  3. עמוד פוסט (/blog/[slug]) — רינדור מלא של markdown עם ToC, כרטיס מחבר, פוסטים קשורים, שיתוף ברשתות חברתיות
  4. עמוד קטגוריה (/blog/category/[slug]) — רשימה מסוננת שממיחה (reuses) את אותם קומפוננטים של הרשימה
  5. עמוד מחבר (/blog/author/[slug]) — ביוגרפיה של המחבר + הפוסטים שלו/שלה
  6. תוכן עניינים (Table of Contents) — נוצר אוטומטית מכותרות h2/h3, סיידבר "דביק" בדסקטופ, מתקפל במובייל, הדגשת כותרת פעילה דרך Intersection Observer
  7. הטמעות YouTube/Vimeo — כתובות URL חשופות ב-markdown מומרות אוטומטית ל‑iframes רספונסיביים (youtube-nocookie.com לשמירה על פרטיות)
  8. זמן קריאה — מחושב בצד השרת מתוך ספירת מילים (Math.ceil(words / 200))
  9. כפתורי שיתוף ברשתות חברתיות — X, Facebook, LinkedIn, אימייל, העתקת קישור (Web Share API כשזמין)
  10. פוסטים קשורים — מאותה קטגוריה, ללא הפוסט הנוכחי, מוגבל ל‑3, ממוינים לפי publishedAt בסדר יורד
  11. RSS feed (/blog/rss.xml) — פורמט RSS 2.0, locale דרך query param
  12. SEO — SeoComponent לכל פוסט, תגיות og:article, סכמת BlogPosting ב‑JSON-LD, כתובות URL קנוניות (canonical)
  13. Sitemap — הוספת כתובות URL דינמיות של פוסטים/קטגוריות/מחברים ל‑sitemap הקיים כולל hreflang
  14. Feature flagblog: boolean בתוך TenantConfig.features, ה‑layout guard מחזיר 404 כשהפיצ'ר כבוי
  15. i18n — מפתחות הודעות Paraglide לכל מחרוזות ה‑UI (ניווט, תוויות, פאג'ינציה), תוכן Strapi מתורגם אוטומטית דרך ה‑webhook הקיים
  16. פאג'ינציה — מבוססת עמודים באמצעות הפאג'ינציה המובנית של Strapi (9 פוסטים בכל עמוד)
  17. פוסט מומלץ (Featured post) — שדה בוליאני featured על blog-posts; פוסט מומלץ מוצג ככרטיס hero בחלק העליון של עמוד הרשימה /blog (כרטיס גדול, ברוחב מלא, מעל הגריד)

נחמד שיהיה (Post-MVP)

  1. חיפוש טקסט חופשי על פני כל פוסטי הבלוג
  2. סדרות פוסטים / מאמרים מרובי חלקים
  3. סרגל התקדמות קריאה
  4. תגובות (כנראה דרך הטמעת שירות צד שלישי)
  5. קריאת הרשמה לניוזלטר (CTA) בתוך הפוסטים
  6. ספירת צפיות / אינטגרציית אנליטיקס

דרישות טכניות

מודל נתונים (סוגי תוכן ב‑Strapi)

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

Field Type Localized Notes
title Text (required) Yes מותאם לפי תצורת התרגום
slug UID (from title) No לא נכלל בתרגום
excerpt Text Yes עבור כרטיסים + תיאור מטא
body Rich Text (Markdown) Yes נרנדר דרך marked
featuredImage Media (single) No URL + alternativeText
category Relation (manyToOne -> blog-category) No
author Relation (manyToOne -> blog-author) No
featured Boolean (default false) No פוסט נבחר שמוצג ככרטיס הירו ברשימה
seo Component (shared.seo) Yes שימוש חוזר ב‑SeoComponent הקיים
tenant Text No מסנן tenant סטנדרטי
inherit Boolean (default true) No דגל תרגום אוטומטי

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

Field Type Localized Notes
name Text (required) Yes
slug UID (from name) No
bio Rich Text (Markdown) Yes
avatar Media (single) No
socialLinks JSON No { twitter?, linkedin?, website? }
tenant Text No
inherit Boolean (default true) No

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

Field Type Localized Notes
name Text (required) Yes
slug UID (from name) No
description Text Yes
color Text No צבע Hex לתגיות UI
tenant Text No
inherit Boolean (default true) No

ממשקי TypeScript

ראו src/lib/services/strapi-types.ts עבור BlogPost, BlogAuthor, BlogCategory.

ראוטים

הכול תחת src/routes/(marketing)/blog/:

Route Server Load Purpose
blog/+layout.server.ts Feature guard + טעינת קטגוריות שער + נתוני ניווט
blog/+page.server.ts פוסטים בדפדוף (paginated), מסנן אופציונלי ?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= פיד

רכיבי UI

הכול ב‑src/lib/components/blog/:

Component Props Notes
BlogPostCard.svelte post: BlogPost כרטיס עם תמונה, תגית קטגוריה, כותרת, תקציר, מחבר, תאריך, זמן קריאה
BlogPostContent.svelte body: string המרת Markdown ל‑HTML דרך marked עם הרחבת הטמעת וידאו + מזהי כותרות
TableOfContents.svelte entries: TocEntry[] סרגל צד דביק, Intersection Observer למצב פעיל, מתקפל במובייל
AuthorCard.svelte author: BlogAuthor אווטאר, שם, ביוגרפיה, קישורים חברתיים
SocialShare.svelte url: string, title: string כפתורי שיתוף עם fallback ל‑Web Share API
RelatedPosts.svelte posts: BlogPost[] גריד של BlogPostCard
BlogPagination.svelte pagination: StrapiPagination, basePath: string קודם/הבא + מספרי עמודים

אבטחה ואימות

  • הבלוג הוא ציבורי (ללא צורך באימות) — תוכן שיווקי
  • גישת קריאה ל־Strapi API באמצעות טוקן STRAPI_JWT הקיים
  • הטמעת וידאו משתמשת ב־youtube-nocookie.com לצורך עמידה בדרישות GDPR

מקרים קיצוניים וטיפול בשגיאות

  • פוסט לא נמצא: 404 דרך SvelteKit error()
  • אין פוסטים בקטגוריה: מצב ריק עם הודעת blog_no_posts
  • תמונת נושא חסרה: הכרטיס מוצג ללא תמונה
  • תרגום לא זמין: loadContentWithFallback() נופל חזרה לאנגלית
  • שפות מימין לשמאל: הפריסה מכבדת dir="rtl" (ar, he, ur)

תוכנית בדיקות E2E

קבצי הבדיקות נמצאים ב-tests/blog/. עקבו אחרי התבניות הקיימות: פרמטריזציה לפי גדלי מסך, Axe לנגישות (a11y), networkidle ממתין לתוכן מ-Strapi.

בדיקות מסלול קריטי

  1. טעינת רשימת בלוג/blog מציג גריד של כרטיסי פוסטים עם כותרת, תקציר, תג קטגוריה, כותב, תאריך וזמן קריאה
  2. פאג’ינציה עובדת/blog?page=2 מציג את עמוד התוצאות השני; כפתורי הקודם/הבא מנווטים נכון; בעמוד 1 אין כפתור "Previous"
  3. פילטר קטגוריה — לחיצה על צ’יפ של קטגוריה מסננת את הפוסטים; ה-URL מתעדכן ל-?category=<slug>; צ’יפ "All" מנקה את הפילטר
  4. עמוד פוסט נטען/blog/[slug] מציג כותרת, תמונה ראשית, גוף מעוצב, כרטיס מחבר, זמן קריאה ותאריך פרסום
  5. תוכן עניינים — עמוד פוסט מייצר TOC מתוך כותרות h2/h3; לחיצה על פריט ב-TOC גוללת לכותרת המתאימה
  6. פוסטים קשורים — עמוד פוסט מציג עד 3 פוסטים קשורים מאותה קטגוריה (ללא הפוסט הנוכחי)
  7. עמוד קטגוריה/blog/category/[slug] מציג שם קטגוריה, תיאור ורק פוסטים מאותה קטגוריה
  8. עמוד מחבר/blog/author/[slug] מציג ביוגרפיית מחבר, אוואטר, לינקים חברתיים והפוסטים שלו
  9. כפתורי שיתוף — כפתורי שיתוף נראים בעמוד פוסט; "Copy link" מעתיק את ה-URL ומציג טקסט אישור
  10. פיד RSSGET /blog/rss.xml מחזיר XML תקין עם Content-Type: application/rss+xml ומכיל רשומות פוסטים
  11. ניווט — קישור הבלוג בתפריט האתר מנווט ל-/blog; פירורי לחם מציגים היררכיה נכונה
  12. פוסט מומלץ/blog מציג את הפוסט המומלץ ככרטיס הירו גדול מעל הגריד הרגיל

בדיקות קצה ושגיאות

  1. פוסט לא נמצא/blog/nonexistent-slug מחזיר עמוד 404
  2. קטגוריה ריקה — קטגוריה ללא פוסטים מציגה את הודעת מצב הריק blog_no_posts
  3. ללא תמונה ראשית — כרטיס פוסט מוצג בצורה תקינה גם בלי תמונה (אין <img> שבור)
  4. חוסר במחבר/קטגוריה — פוסט ללא מחבר או קטגוריה מוצג ללא האלמנטים האלו (ללא קריסה)
  5. דגל פיצ’ר כבוי — כאשר blog: false, ‏/blog מחזיר 404

בדיקות רוחב

  1. i18n: החלפת לוקל — נווט ל-/fr/blog, ודא שמחרוזות ה-UI (כותרת עמוד, "Read more", תוויות פאג’ינציה) בצרפתית; תוכן הפוסט נופל חזרה לאנגלית אם לא תורגם
  2. i18n: פריסת RTL — נווט ל-/ar/blog ו-/ar/blog/[slug], ודא שה-dir="rtl" מוגדר ושהפריסה מוצגת נכון (TOC בצד הנכון)
  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, author, datePublished
  5. SEO: מפת אתר/sitemap.xml כולל כתובות URL של פוסטים עם hreflang alternates
  6. נגישות — הרצת בדיקות Axe על /blog, /blog/[slug], /blog/author/[slug] גם במובייל וגם בדסקטופ (בהתאם לתבנית בדיקות a11y הקיימת)
  7. ניווט עם מקלדת — פריטי TOC וכפתורי פאג’ינציה ניתנים לפוקוס ולהפעלה באמצעות מקלדת; אינדיקטורי הפוקוס נראים לעין

מחוץ לתחום

  • תגובות לפוסטים בבלוג
  • חיפוש בטקסט מלא
  • אינטגרציית ניוזלטר בתוך פוסטים
  • אנליטיקות/ספירת צפיות
  • תבניות פוסט מותאמות אישית לכל טננט
Ludo Fourrage
Ludo Fourrage

לודו הוא מחנך ויזם בתחום הטכנולוגיה, שמונע מאמונה פשוטה: חינוך איכותי צריך להיות נגיש לכולם.