---
phase: 08-content-cocon-semantique
plan: 01
subsystem: content/ui
tags: [nuxt-content, i18n, hytale, cocon-semantique, blog]
requirements: [SEO-14]
requires:
- BlogCard.vue (variant compact)
- queryCollection('blog_fr'|'blog_en') collections Phase 5
- i18n locales fr/en
provides:
- HytaleRecentArticles.vue (section auto-importee, masquee si 0 article hytale)
- i18n hytale.recentArticles.{title,subtitle,viewAll}
- injection en fin de /hytale
affects:
- app/pages/hytale.vue
tech-stack:
added: []
patterns:
- queryCollection bilingual literal branches (Phase 5 Pitfall D-03)
- JS post-filter tags.includes('hytale') + slice(0,2) (D-11)
- v-if="articles.length" hide-if-empty (D-12)
key-files:
created:
- app/components/HytaleRecentArticles.vue
modified:
- app/pages/hytale.vue
- i18n/locales/fr.json
- i18n/locales/en.json
decisions:
- Filtre JS tags.includes('hytale') choisi sur LIKE SQL (D-11 — LIKE unreliable sur JSON array SQLite)
- Style accentue pour cles i18n recentArticles (aligne blog.* 2026, ecart avec hytale.* legacy ASCII accepte)
- Injection apres TestimonialsSection, dernier enfant du root div de /hytale
- BlogCard variant=compact sans prop direction (default 'next' accepte, pas de semantique prev/next dans ce contexte)
metrics:
duration: ~5 min
completed: 2026-04-22
tasks: 2
files: 4
---
# Phase 8 Plan 01: Scaffold HytaleRecentArticles — Summary
Scaffolding du cocon semantique cote /hytale : composant `HytaleRecentArticles.vue` (queryCollection bilingue + filtre JS tag `hytale` + limit 2), injection dans `/hytale`, cles i18n FR/EN. Section degrade gracieusement (v-if) tant que 0 article publie, permettant de shipper Wave 1 sans rompre /hytale.
## Changes
### Task 1 — HytaleRecentArticles.vue (commit `ddfc685`)
Nouveau composant `app/components/HytaleRecentArticles.vue` (72 lignes, auto-importe) :
- **Script** : branches litterales `queryCollection('blog_fr')` / `queryCollection('blog_en')` sur ternaire `isFr.value` (Phase 5 Pitfall D-03), key `hytale-recent-${locale.value}` + `watch: [locale]`, chaine `.where('draft','=',false).order('date','DESC').all()`.
- **Filtre JS** : `articles = computed(() => all.filter(a => Array.isArray(a.tags) && a.tags.includes('hytale')).slice(0, 2))` — guard Array.isArray contre TypeError (T-08-01), filtre JS car LIKE SQLite unreliable sur tags[] (D-11).
- **Template** : `` (masque total si vide, D-12), header `// recent-articles` + h2 `text-3xl sm:text-4xl` + subtitle, grille `grid-cols-1 md:grid-cols-2 gap-5 lg:gap-6`, `` v-for, footer NuxtLink `localePath('/blog')` avec icon arrow-right.
### Task 2 — Injection + i18n (commit `bf2ec86`)
- `app/pages/hytale.vue` : `` insere apres le wrapper TestimonialsSection, avant fermeture root div. Aucun script change (auto-import Nuxt).
- `i18n/locales/fr.json` : nouveau bloc `hytale.recentArticles` sibling de `hero/services/pricing` avec title "Articles récents", subtitle "Les dernières publications sur le développement de plugins Hytale", viewAll "Voir tous les articles" (style accentue aligne `blog.*` 2026).
- `i18n/locales/en.json` : miroir EN "Recent articles" / "Latest writing on Hytale plugin development" / "View all articles".
## Verification
- `pnpm typecheck` exit 0 (deux passes, apres chaque tache)
- `node -e "JSON.parse(...)"` FR + EN OK (pas de trailing comma ni syntax error)
- `grep "queryCollection\\('blog_(fr|en)'\\)"` → 2 branches litterales presentes
- `grep "v-if=\"articles.length\""` → present
- `grep "variant=\"compact\""` → present
- `grep "tags.*includes.*'hytale'"` → filtre JS present
- `grep "