PD-227 — Plan d'implémentation
📚 Navigation User Story
| Document | | | ---------- | -- | | 📋 [Spécification](PD-227-specification.md) | | | 🛠️ **Plan d'implémentation** | *(ce document)* | | ✅ [Critères d'acceptation](PD-227-acceptability.md) | | | 📝 [Retour d'expérience](PD-227-rex.md) | | [← Retour à site-vitrine](../PD-225-epic.md) · [↑ Index User Story](index.md)
1. Découpage en composants
Structure i18n
src/
├── pages/
│ ├── index.astro # Redirect → /fr/
│ ├── fr/
│ │ ├── index.astro # Homepage FR
│ │ ├── product.astro
│ │ ├── pricing.astro
│ │ └── [...slug].astro # Pages dynamiques FR
│ └── en/
│ ├── index.astro # Homepage EN
│ ├── product.astro
│ ├── pricing.astro
│ └── [...slug].astro # Pages dynamiques EN
├── i18n/
│ ├── index.ts # Fonctions utilitaires i18n
│ ├── fr.json # Traductions FR
│ └── en.json # Traductions EN
└── components/
└── LangSwitcher.astro # Bascule FR↔EN
Composants clés
| Composant | Responsabilité |
astro.config.mjs | Configuration i18n native Astro |
LangSwitcher.astro | Lien vers l'autre langue |
i18n/index.ts | Helpers : t(), getLocale(), getAlternateUrl() |
BaseLayout.astro | Injection lang, hreflang, LangSwitcher |
2. Flux techniques
Routage linguistique
Requête: /fr/product
│
▼
Astro i18n routing
│
├── Locale détectée: fr
├── Page: src/pages/fr/product.astro
└── Traductions: i18n/fr.json
│
▼
HTML avec lang="fr" + hreflang alternates
Redirection racine
Requête: /
│
▼
src/pages/index.astro
│
└── return Astro.redirect('/fr/', 302)
Bascule de langue
Page: /fr/product
│
▼
LangSwitcher.astro
│
├── Calcule URL alternative: /en/product
└── Affiche lien "EN"
3. Mapping invariants → mécanismes
| Invariant | Mécanisme technique |
| INV-1 : 100% pages en FR et EN | Structure miroir pages/fr/ et pages/en/ |
| INV-2 : Préfixe langue dans URL | prefixDefaultLocale: true dans config |
| INV-3 : Lien bascule FR↔EN | LangSwitcher.astro dans header |
| INV-3 : Pas de page non traduite | Script CI vérifie parité des fichiers |
Configuration Astro i18n
// astro.config.mjs
export default defineConfig({
site: 'https://probatiovault.com',
i18n: {
defaultLocale: 'fr',
locales: ['fr', 'en'],
routing: {
prefixDefaultLocale: true // INV-2: /fr/ explicite
}
}
});
Composant LangSwitcher
---
// src/components/LangSwitcher.astro
const { lang } = Astro.props;
const currentPath = Astro.url.pathname;
const alternateLang = lang === 'fr' ? 'en' : 'fr';
const alternateUrl = currentPath.replace(`/${lang}/`, `/${alternateLang}/`);
---
<a href={alternateUrl} hreflang={alternateLang}>
{alternateLang.toUpperCase()}
</a>
Balises hreflang (SEO)
---
// Dans BaseLayout.astro
const lang = Astro.currentLocale;
const canonicalUrl = Astro.url.href;
const alternateUrl = canonicalUrl.replace(`/${lang}/`, `/${lang === 'fr' ? 'en' : 'fr'}/`);
---
<html lang={lang}>
<head>
<link rel="canonical" href={canonicalUrl} />
<link rel="alternate" hreflang="fr" href={canonicalUrl.replace(`/${lang}/`, '/fr/')} />
<link rel="alternate" hreflang="en" href={canonicalUrl.replace(`/${lang}/`, '/en/')} />
<link rel="alternate" hreflang="x-default" href={canonicalUrl.replace(`/${lang}/`, '/fr/')} />
</head>
4. Gestion des erreurs
| Erreur | Cause | Mitigation |
| Page non traduite | Fichier manquant en/fr | CI compare les arborescences |
| Langue inconnue | URL /de/product | Middleware redirect → /fr/ |
| Clé traduction manquante | t('missing.key') | Fallback FR + warning dev |
| hreflang incorrect | URL mal formée | Tests automatisés |
Script de validation i18n
#!/bin/bash
# scripts/validate-i18n.sh
FR_PAGES=$(find src/pages/fr -name "*.astro" | wc -l)
EN_PAGES=$(find src/pages/en -name "*.astro" | wc -l)
if [ "$FR_PAGES" != "$EN_PAGES" ]; then
echo "ERROR: Nombre de pages FR ($FR_PAGES) ≠ EN ($EN_PAGES)"
exit 1
fi
# Vérifier que chaque page FR a son équivalent EN
for fr_page in src/pages/fr/*.astro; do
en_page="${fr_page/fr/en}"
if [ ! -f "$en_page" ]; then
echo "ERROR: Page manquante: $en_page"
exit 1
fi
done
5. Impacts sécurité
| Aspect | Mesure |
| Open redirect | Validation des URLs de redirection |
| XSS | Échappement des paramètres de langue |
| SEO spam | Canonical + hreflang corrects |
6. Hypothèses techniques
| Hypothèse | Justification |
| Astro i18n natif suffisant | Pas besoin de plugin externe |
| Traductions JSON statiques | Pas de backend de traduction |
| Français = langue par défaut | x-default pointe vers /fr/ |
| Contenu existe en 2 langues | Équipe fournit les traductions |
7. Points de vigilance
| Point | Risque | Action |
| Parité pages | Page oubliée | CI obligatoire |
| Liens internes | Lien hardcodé /fr/ depuis /en/ | Utiliser helpers i18n |
| Sitemap | URLs dupliquées | Sitemap avec hreflang |
| Canonical | Mauvaise langue | Tests automatisés |
| SEO | Contenu dupliqué | hreflang correctement implémenté |
Fichiers à créer/modifier
| Fichier | Description |
astro.config.mjs | Ajouter config i18n |
src/i18n/index.ts | Helpers i18n |
src/i18n/fr.json | Traductions FR |
src/i18n/en.json | Traductions EN |
src/components/LangSwitcher.astro | Bascule langue |
src/pages/index.astro | Redirect → /fr/ |
scripts/validate-i18n.sh | Validation CI |