Siirry sisältöön
AI

Ensimmäinen blogini

Ludo Fourrage Ludo Fourrage 8. huhtikuuta 2026 7 min lukuaika
Ensimmäinen blogini

Ongelman kuvaus:

Alustalta puuttuu sisältömarkkinoinnin kanava. Blogi mahdollistaa jokaiselle tenantille SEO-optimoitujen, monikielisten artikkelien julkaisemisen Strapi CMS:ssä ja renderöinnin SvelteKit-sovelluksessa — kasvattaen orgaanista liikennettä, rakentaen asiantuntijuutta ja tukeutuen olemassa olevaan 13-kieliseen i18n-putkeen.

Onnistumiskriteerit

  • Strapiin kirjoitetut blogiartikkelit renderöityvät frontendissä vuokralaisen (tenantin) brändäyksen mukaisesti
  • Artikkelit käännetään automaattisesti kaikille 13 lokaalille olemassa olevan webhook-putken kautta (ei muutoksia käännösinfraan)
  • Blogisivut sijoittuvat hakukoneissa (structured data, sitemap, hreflang, og:article)
  • Vuokralaiset, joilla ei ole blog -feature flagia, eivät näe blogireittejä (404)

Käyttäjätarinat

  • Sisällöntuottajana haluan kirjoittaa blogikirjoituksia Strapiin käyttäen rikasta markdownia, kuvia ja video upotuksia, jotta voin julkaista sisältöä koskematta koodiin
  • Sivuston vierailijana haluan selata blogikirjoituksia kategorian mukaan suodatettuna, jotta löydän itselleni relevanttia sisältöä
  • Sivuston vierailijana haluan sisällysluettelon pitkiin kirjoituksiin, jotta voin liikkua tekstissä nopeasti
  • Sivuston vierailijana haluan lukea kirjoituksia omalla kielelläni, automaattisesti englannista käännettynä
  • Sivuston vierailijana haluan jakaa kirjoituksia sosiaalisessa mediassa
  • Sivuston vierailijana haluan löytää liittyviä kirjoituksia ja saada tietoa kirjoittajasta
  • SEO-vastaavana haluan, että jokaisella kirjoituksella on asianmukaiset metatunnisteet, jäsennelty data ja merkinnät sivustokartassa

Toiminnalliset vaatimukset

Pakolliset (MVP)

  1. Strapi-sisältötyypit: blog-posts, blog-authors, blog-categories — kaikissa tenant-, locale- ja inherit-kentät
  2. Blogin listaus­sivu (/blog) — sivutettu artikkeliruudukko, kategoriansuodatus query-parametrilla
  3. Artikkelin yksityiskohtainen sivu (/blog/[slug]) — täysi markdown-renderöinti sisällysluettelolla (ToC), kirjoittajakortti, liittyvät artikkelit, sosiaalisen median jako
  4. Kategoriasivu (/blog/category/[slug]) — suodatettu listaus, joka käyttää samoja listauskomponentteja
  5. Kirjoittajasivu (/blog/author/[slug]) — kirjoittajan esittely + hänen artikkelinsa
  6. Sisällysluettelo — generoidaan automaattisesti h2/h3-otsikoista, sticky-sivupalkki työpöydällä, mobiilissa kokoon­menevä, aktiivisen otsikon korostus Intersection Observerilla
  7. YouTube/Vimeo-upotukset — pelkät URL-osoitteet markdownissa muunnetaan automaattisesti responsiivisiksi iframeiksi (youtube-nocookie.com yksityisyyttä varten)
  8. Lukuaika — lasketaan palvelinpuolella sanamäärästä (Math.ceil(words / 200))
  9. Sosiaalisen median jakopainikkeet — X, Facebook, LinkedIn, sähköposti, kopioi-linkki (Web Share API, jos saatavilla)
  10. Liittyvät artikkelit — sama kategoria, nykyinen artikkeli pois rajauksesta, enintään 3, järjestetty publishedAt laskeva
  11. RSS-syöte (/blog/rss.xml) — RSS 2.0, locale query-parametrilla
  12. SEO — SeoComponent per artikkeli, og:article-tagit, BlogPosting JSON-LD -skeema, kanoniset URL-osoitteet
  13. Sivukartta (sitemap) — dynaamiset blogi-/kategoria-/kirjoittaja-URL:t lisätään olemassa olevaan sivukarttaan hreflang-merkinnöillä
  14. Feature flagblog: boolean TenantConfig.features-kentässä, layout-vartija palauttaa 404:n, kun ominaisuus on pois käytöstä
  15. i18n — Paraglide-viestiavaimet kaikille käyttöliittymäteksteille (navigaatio, labelit, sivutus), Strapi-sisältö käännetään automaattisesti olemassa olevan webhookin kautta
  16. Sivutus — sivupohjainen käyttäen Strapin omaa sivutusta (9 artikkelia per sivu)
  17. Nostettu artikkeli — boolean-tyyppinen featured-lippu blog-posts-tyypissä; nostettu artikkeli näytetään hero-korttina /blog-listaus­sivun yläosassa (isompi kortti, täysi leveys, ruudukon yläpuolella)

Mukava olla (Post-MVP)

  1. Koko tekstin haku blogiartikkeleista
  2. Artikkelisarjat / moniosaiset artikkelit
  3. Lukemisen etenemispalkki
  4. Kommentit (todennäköisesti kolmannen osapuolen upotuksena)
  5. Uutiskirjeen tilaus -kutsu toimintaan artikkelien sisällä
  6. Näyttökertojen seuranta / analytiikka-integraatio

Teknisiä vaatimuksia

Tietomalli (Strapi Content Types)

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

Kenttä Tyyppi Lokalisoitu Huomautukset
title Teksti (pakollinen) Kyllä Sovitettu käännöskonfiguraation mukaan
slug UID (title-kentästä) Ei Poissuljettu käännöksestä
excerpt Teksti Kyllä Kortteja + meta-kuvausta varten
body Rich Text (Markdown) Kyllä Renderöidään marked kautta
featuredImage Media (yksi) Ei URL + alternativeText
category Relaatio (manyToOne -> blog-category) Ei
author Relaatio (manyToOne -> blog-author) Ei
featured Boolean (oletus false) Ei Nostoartikkeli, näytetään hero-korttina listauksessa
seo Komponentti (shared.seo) Kyllä Käytetään olemassa olevaa SeoComponent-komponenttia
tenant Teksti Ei Vakio tenant-suodatin
inherit Boolean (oletus true) Ei Automaattisen käännöksen lippu

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

Kenttä Tyyppi Lokalisoitu Huomautukset
name Teksti (pakollinen) Kyllä
slug UID (name-kentästä) Ei
bio Rich Text (Markdown) Kyllä
avatar Media (yksi) Ei
socialLinks JSON Ei { twitter?, linkedin?, website? }
tenant Teksti Ei
inherit Boolean (oletus true) Ei

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

Kenttä Tyyppi Lokalisoitu Huomautukset
name Teksti (pakollinen) Kyllä
slug UID (name-kentästä) Ei
description Teksti Kyllä
color Teksti Ei Hex-väri UI-tägejä varten
tenant Teksti Ei
inherit Boolean (oletus true) Ei

TypeScript-rajapinnat

Katso src/lib/services/strapi-types.ts: BlogPost, BlogAuthor, BlogCategory.

Reitit

Kaikki polun src/routes/(marketing)/blog/ alla:

Reitti Server Load Tarkoitus
blog/+layout.server.ts Feature guard + kategorioiden lataus Portinvartija + navigaatiodataa
blog/+page.server.ts Sivutetut postaukset, valinnainen ?category= -suodatin Lista
blog/[slug]/+page.server.ts Postaus slugilla + siihen liittyvät postaukset + lukuaika Yksityiskohtasivu
blog/category/[slug]/+page.server.ts Postaukset suodatettuna kategorian mukaan Kategorian listaus
blog/author/[slug]/+page.server.ts Kirjoittaja + hänen postauksensa Kirjoittajasivu
blog/rss.xml/+server.ts RSS 2.0 XML, ?locale= -parametri Syöte

UI-komponentit

Kaikki hakemistossa src/lib/components/blog/:

Komponentti Props Huomautukset
BlogPostCard.svelte post: BlogPost Kortti, jossa kuva, kategoriamerkki, otsikko, ingressi, kirjoittaja, päivämäärä, lukuaika
BlogPostContent.svelte body: string Markdown → HTML marked-kirjaston kautta, videoupotuslaajennus + otsikko-ID:t
TableOfContents.svelte entries: TocEntry[] Sticky-sivupalkki, Intersection Observer aktiivisen tilan tunnistukseen, mobiilissa kokoon taittuva
AuthorCard.svelte author: BlogAuthor Avatar, nimi, bio, some-linkit
SocialShare.svelte url: string, title: string Jakonapit Web Share API -varajärjestelmällä
RelatedPosts.svelte posts: BlogPost[] BlogPostCard-ruudukko
BlogPagination.svelte pagination: StrapiPagination, basePath: string Edellinen/seuraava + sivunumerot

Turvallisuus & autentikointi

  • Blogi on julkinen (ei vaadi kirjautumista) — markkinointisisältöä
  • Strapi API -lukuoikeus nykyisellä STRAPI_JWT-tunnuksella
  • Video upotukset käyttävät youtube-nocookie.com:ää GDPR-vaatimusten täyttämiseksi

Reunatapaukset ja virheenkäsittely

  • Julkaisua ei löydy: 404 SvelteKitin kautta error()
  • Ei julkaisuja kategoriassa: Tyhjä tila, jossa blog_no_posts-viesti
  • Puuttuva esittelykuva: Kortti renderöidään ilman kuvaa
  • Käännöstä ei ole saatavilla: loadContentWithFallback() palaa oletuksena englantiin
  • RTL-kielet: Ulkoasu noudattaa dir="rtl"-määritystä (ar, he, ur)

E2E-testisuunnitelma

Testitiedostot sijaitsevat hakemistossa tests/blog/. Seuraa olemassa olevia malleja: parametrisoidut eri näkymäkokojen mukaan, Axe a11y-testeihin, networkidle odottaa Strapi-sisältöä.

Kriittisen polun testit

  1. Blogilistaus latautuu/blog renderöi ruudukkona artikkelikortteja, joissa on otsikko, ingressi, kategoriatunnus, kirjoittaja, päivämäärä ja lukuaika
  2. Sivutus toimii/blog?page=2 näyttää toisen sivun tulokset; edellinen/seuraava-painikkeet navigoivat oikein; sivulla 1 ei ole "Previous"-painiketta
  3. Kategoriasuodatin — kategoriasirun klikkaaminen suodattaa artikkelit; URL päivittyy muotoon ?category=<slug>; "All"-siru poistaa suodatuksen
  4. Artikkelisivu renderöityy/blog/[slug] näyttää otsikon, esiinnostetun kuvan, muotoillun leipätekstin, kirjoittajakortin, lukuaika-arvion ja julkaisupäivän
  5. Sisällysluettelo — artikkelisivu generoi sisällysluettelon h2/h3-otsikoista; sisällysluettelon kohdan klikkaaminen scrollaa vastaavaan otsikkoon
  6. Liittyvät artikkelit — artikkelisivu näyttää enintään 3 samaan kategoriaan kuuluvaa muuta artikkelia (pois lukien nykyinen)
  7. Kategoriakohtainen sivu/blog/category/[slug] näyttää kategorian nimen, kuvauksen ja vain kyseisen kategorian artikkelit
  8. Kirjoittajasivu/blog/author/[slug] näyttää kirjoittajan esittelyn, avatar-kuvan, sosiaalisen median linkit ja heidän artikkelinsa
  9. Somejako-painikkeet — jakopainikkeet näkyvät artikkelisivulla; "Copy link" kopioi URL-osoitteen ja näyttää vahvistustekstin
  10. RSS-syöteGET /blog/rss.xml palauttaa validia XML:ää, jossa on Content-Type: application/rss+xml ja artikkelitietueita
  11. Navigaatio — blogilinkki sivuston valikossa vie osoitteeseen /blog; murupolku näyttää oikean hierarkian
  12. Nostettu artikkeli/blog näyttää nostetun artikkelin isompana hero-korttina varsinaisen ruudukon yläpuolella

Reuna- ja virhetilanteiden testit

  1. Artikkelia ei löydy/blog/nonexistent-slug palauttaa 404-sivun
  2. Tyhjä kategoria — kategoriassa, jossa ei ole artikkeleita, näytetään blog_no_posts-tyhjätilaviesti
  3. Puuttuva esiinnostettu kuva — artikkelikortti renderöityy siististi ilman kuvaa (ei rikkinäistä <img>:ää)
  4. Puuttuva kirjoittaja/kategoria — artikkeli ilman kirjoittajaa tai kategoriaa renderöityy ilman näitä elementtejä (ei kaatumista)
  5. Feature flag pois päältä — kun blog: false, /blog palauttaa 404:n

Poikkileikkaavat testit

  1. i18n: kielenvaihto — navigoi osoitteeseen /fr/blog, varmista että käyttöliittymän tekstit (sivun otsikko, "Read more", sivutuslabelit) ovat ranskaksi; artikkelin sisältö fallbackaa englanniksi, jos käännöstä ei ole
  2. i18n: RTL-asettelu — navigoi osoitteisiin /ar/blog ja /ar/blog/[slug], varmista että dir="rtl" on asetettu ja layout renderöityy oikein (sisällysluettelo oikealla puolella)
  3. SEO: metatiedot — artikkelisivulla on og:type=article, og:title, og:description, article:published_time ja kanoninen linkki
  4. SEO: JSON-LD — artikkelisivu sisältää <script type="application/ld+json">-objektin, jossa on @type: BlogPosting, otsikko (headline), kirjoittaja ja datePublished
  5. SEO: sivukartta/sitemap.xml sisältää blogiartikkelien URL-osoitteet hreflang-vastineineen
  6. Saavutettavuus — aja Axe-auditit osoitteissa /blog, /blog/[slug], /blog/author/[slug] sekä mobiili- että desktop-näkymissä (seuraa olemassa olevaa a11y-testimallia)
  7. Näppäimistönavigointi — sisällysluettelon kohdat ja sivutuspainikkeet ovat fokusoitavissa ja aktivoitavissa näppäimistöllä; fokusindikaattorit ovat näkyvissä

Rajauksen ulkopuolella

  • Blogikommentit
  • Täystekstihaku
  • Uutiskirjeintegraatio julkaisujen sisällä
  • Analytiikka / näyttökerrat
  • Vuokralaiskohtaiset (tenant-kohtaiset) räätälöidyt julkaisupohjat
Ludo Fourrage
Ludo Fourrage

Ludo on teknologia-alan kouluttaja ja yrittäjä, jota ohjaa yksinkertainen ajatus: laadukkaan koulutuksen pitäisi olla kaikkien saatavilla.