---
page: keywords
sous-page: recherche
route: /keywords/recherche
statut: draft
version: 0.1
transverse-liées: [reservoir-data-model (TBD), i18n, design-system]
date: 2026-06-03
---

# CdC — `keywords/recherche`

> Plan de travail de **triage**, pas de la découverte. On part du **Reservoir** déjà rempli (l'alimentation en nouveaux keywords passe par `export-import`). Ici on **sélectionne, cible par pays, et pousse en planning**.

## 1. Objectif

Depuis le Reservoir d'un site, **sélectionner** les keywords à travailler, leur **assigner une ou plusieurs target countries** (multi-select), visualiser la **couverture `keyword × pays`**, repérer les **trous**, et **pousser en planning** (`keywords/planning`) ceux qu'on veut produire — au **bon moment** (saisonnalité). Donne une **vue d'ensemble par localisation**.

> **Locale = langue + pays.** La langue vient du keyword (Reservoir) ; le **pays** est la cible choisie ici. `fr` peut viser `FR`, `BE`, `CA`, `CH`… avec des chiffres différents par pays.

## 2. Périmètre

- **Dans** : triage/sélection depuis le Reservoir ; **ciblage multi-pays** par keyword ; **matrice de couverture `keyword × pays`** ; métriques **par pays** ; détection de *gaps* + expansion inter-pays ; suggestions de **timing saisonnier** ; shortlist de travail ; **suivi du statut d'avancement** (candidat → planifié → en ligne) ; push vers `keywords/planning`.
- **Hors** : découverte/scraping de *nouveaux* keywords (→ alimentation par `export-import`). **Rank-tracking de positions** (hors périmètre actuel). Calendrier éditorial / pages (→ `contenu/planning`).

## 3. User stories *(persona : consultant SEO multi-sites / multi-langues)*

- Depuis le Reservoir, je **sélectionne un lot** et je lui assigne **plusieurs pays d'un coup** (multi-select).
- Je lis une **matrice `keyword × pays`** pour voir où je suis **présent / absent**.
- Je compare le **volume/KD par pays** pour décider quel pays vaut le coup (`fr-BE` ≠ `fr-FR` ≠ `fr-CA`).
- Je repère qu'un keyword est planifié en `FR` mais pas en `BE/CA` et je l'**étends** en un clic.
- On me **suggère de planifier** un keyword saisonnier **avant son pic**.
- Je **pousse ma sélection en planning** et je **suis l'avancement** (candidat → planifié → en ligne).

## 4. Fonctionnalités

- F1 — **Sélecteur de site global** (isolation par `site_id`, comme partout).
- F2 — **Source = Reservoir** : liste filtrable (réutilise les filtres légers du Reservoir : langue, thème, intent, statut, tendance).
- F3 ⭐ — **Matrice de couverture `keyword × pays`** : lignes = keywords, colonnes = pays cibles, cellule = **statut** (`— non ciblé · candidat · planifié · en ligne`). Toggle vue **liste ↔ matrice**.
- F4 — **Ciblage multi-pays (multi-select)** : assigner **un ou plusieurs pays** à un keyword **ou à une sélection** en une seule action.
- F5 — **Métriques par pays** : volume / KD / CPC du pays de la cellule (issu de `keyword_source_metric.location`).
- F6 ⭐ — **Détection de gaps + expansion inter-pays** : « planifié en FR, pas en BE/CA » → *fan-out* vers les pays manquants.
- F7 ⭐ — **Suggestion de timing saisonnier** : à partir de la série mensuelle du Reservoir, proposer de planifier **en amont du pic**.
- F8 — **Shortlist / panier de triage** : construire sa sélection avant de tout pousser d'un coup.
- F9 — **Push en planning** (bulk) : passe les cibles en statut `planifié` (visibles dans `keywords/planning`).
- F10 — **Suivi de statut d'avancement** : lecture du statut des cibles (candidat → planifié → en ligne).

## 5. UI / écrans

- **Écran principal = la matrice `keyword × pays`** (DataTable shadcn, en-têtes de ligne *et* de colonne, cellules à badge de statut). **Toggle liste ↔ matrice.**
- **Multi-select pays** (combobox multi) au-dessus de la sélection + bulk-actions bar.
- **Panneau détail** par cellule `(keyword, pays)` : métriques de ce pays + mini-graphe série.
- **Shortlist** en panneau latéral.
- États **vide / chargement / erreur** ; responsive : matrice scrollable horizontalement, cards en mobile. Tokens `design.md` (`badge-*` pour les statuts).

## 6. Données & actions

- **Affiché** : matrice de couverture, métriques par pays, statuts, suggestions saisonnières.
- **Actions** : filtrer la source Reservoir, sélectionner, **assigner pays (multi)**, étendre (fan-out), ajouter à la shortlist, **pousser en planning**, changer le statut.

## 7. Modèle de données *(ajout au socle Reservoir — détail dans `_transverse/reservoir-data-model.md`, TBD)*

```
keyword_target(id, keyword_id, country, status, priority?, planned_for?, created, updated)
   status ∈ { candidate, planned, live }      -- "non ciblé" = absence de ligne
   country = code pays (ISO)                    -- locale effective = keyword.language + country
```

- **Multi-select** = créer **plusieurs `keyword_target` d'un coup** (1 par pays).
- **Unicité** : `(keyword_id, country)`.
- Les **métriques par pays** proviennent de `keyword_source_metric.location` (déjà au modèle) — pas de duplication.
- La **langue** n'est pas stockée dans `keyword_target` : elle vient du `keyword`.
- **À trancher** : liste des pays cibles = définie par `site.locales` (cadrée) ou libre ?

## 8. API Hono

- `GET /sites/:siteId/research/matrix` (matrice `keyword × pays`, filtres + pagination)
- `POST /sites/:siteId/keywords/:id/targets` *(body: `countries[]` — **multi**)*
- `POST /sites/:siteId/research/targets/bulk` *(sélection de keywords × `countries[]`)*
- `POST /sites/:siteId/research/expand` *(fan-out d'un keyword vers les pays manquants)*
- `PATCH /sites/:siteId/targets/:id` (statut / priorité)
- `POST /sites/:siteId/research/plan` *(push sélection → status `planned`)*
- `GET /sites/:siteId/research/seasonal-suggestions`
- `GET|POST /sites/:siteId/research/shortlist`

> Routes **préfixées par `siteId`** (isolation structurelle).

## 9. Capacités requises *(fonctionnel — mapping modules TBD)*

Lecture filtrée du Reservoir · gestion de **cibles multi-pays** · calcul de **couverture/gaps** · lecture des **métriques par pays** · **suggestions saisonnières** (série mensuelle) · opérations **bulk**.

## 10. Tools / capacités MCP

- `research_matrix` (read) · `research_gaps` (read) · `research_seasonal` (read)
- `research_target_set` (write — **multi pays**) · `research_expand` (write)
- `research_plan` (write — push vers planning)

> Privé vs public-limité : **TBD**.

## 11. i18n

- **Distinguer langue (contenu) et pays (ciblage)** — c'est le cœur de la page. Réf. fiches `hreflang`, `international-url-structure`, `locale-content`, `lang-attribute` de `docs/specification/`.
- UI localisée (clés). Réf `_transverse/i18n.md` (TBD).

## 12. Qualité *(réf. fiches des 128 par slug)*

- **Perf** : matrice potentiellement large (`keywords × pays`) → virtualisation, agrégats serveur, pagination. `core-web-vitals`, `cache-control`.
- **A11y** : `data-tables` (la matrice est un **vrai tableau** avec `scope` ligne **et** colonne), `keyboard-navigation`, `touch-target-size`, `focus-indicators`.
- **Sécu** : isolation **par site** ; `cookie-attributes`, auth.
- **Indexation** : app privée → `meta-robots` (noindex).

## 13. Critères d'acceptation

- [ ] Assigner **3 pays** à un keyword en **une action** (multi-select) crée **3 `keyword_target`**.
- [ ] La matrice reflète le statut **par cellule** et se met à jour après assignation.
- [ ] Les métriques de la cellule `(keyword, BE)` correspondent bien à `Location = BE`.
- [ ] L'expansion `FR → {BE, CA}` crée **uniquement les cibles manquantes**, sans doublon.
- [ ] Pousser une sélection en planning passe les cibles en `planned` et les rend visibles dans `keywords/planning`.
- [ ] Une **suggestion saisonnière** apparaît pour un keyword dont le pic mensuel approche.
- [ ] Le **switch de site** isole tout (aucune cible d'un autre site).

## 14. Dépendances & risques · questions ouvertes

- **Dépend de** : `keywords/reservoir` (source), `_transverse/reservoir-data-model` (entité `keyword_target`), `keywords/planning` (destination), `contenu/planning` (aval).
- **Risques** : volumétrie de la matrice (`keywords × pays`) ; définition de la liste des pays cibles.
- **Questions ouvertes** :
  - Liste des pays cibles = `site.locales` (cadrée) ou libre par recherche ?
  - Le statut `live` (en ligne) est-il **renseigné en retour** par `contenu/planning`, ou posé manuellement ?
  - Priorité d'une cible : manuelle, ou dérivée du **score d'opportunité recalculé par pays** ?
