feat(seo): séparer les sitemaps et consolider les intentions

This commit is contained in:
Gautier Stefanini 2026-08-15 16:46:13 +00:00
parent 816c414bef
commit b3bee7485b
59 changed files with 581 additions and 317 deletions

View file

@ -23,7 +23,7 @@ G4/G6 sont alignés ; les contrats ciblés et la recette globale sont décrits d
`docs/DESIGN_SYSTEM.md`. G3 reste `TODO` et indépendant de la refonte.
**Surfaces publiques (VIS-08) :** la homepage (`Landing.tsx`), le template des six
vitrines (`ServiceVitrine.tsx`) et celui des 38 URL SEO (`SeoLanding.tsx`) utilisent
vitrines (`ServiceVitrine.tsx`) et celui des 40 URL SEO (`SeoLanding.tsx`) utilisent
les tokens `max-w-public`/`px-gutter`, `max-w-editorial`, `surface-*`, `ink-*`,
`line-*`, rayons et élévations du système. Ne pas réintroduire une largeur, une
gouttière ou un gris Tailwind historique dans ces trois sources ; le contrat
@ -222,7 +222,12 @@ kankwa/
- **Validation SEO obligatoire** : `npm run check:prerender`, exécuté par `npm run build`, relit les 60 HTML générés et contrôle `h1`, `lang`, canonical, hreflang et Open Graph. Pour les landings il vérifie en plus le graphe JSON-LD complet, la bannière générée, la présence dun CTA de création dans le HTML statique, le développement des pages éditoriales, le tableau et le lien `nofollow` des comparatifs, et lunicité des `og:title` par langue. Toute nouvelle page publique doit satisfaire ce contrôle.
- **Bannières de partage** : `/og/<landing-key>.<lang>.png` 1200×630 générées au build, une par landing et par langue.
- **Pages privées** : toute page accessible par lien de partage doit passer `noindex: true` à `usePageMeta`, + header `X-Robots-Tag` côté Caddy (cf. `Caddyfile.example`) pour les crawlers sans JS.
- Le sitemap est servi par l'**API** (`/sitemap.xml`), pas en statique — Caddy route ce chemin vers `kankwa-api:8000`.
- Le sitemap est servi par l'**API** (`/sitemap.xml`), pas en statique — Caddy
route ce chemin vers `kankwa-api:8000`. Depuis SEO-LP-18, la réponse dépend du
`Host` transmis : `.fr` ne publie que ses `<loc>` FR et `.com` uniquement ses
`<loc>` EN ; les `hreflang` réciproques restent cross-domain. Le serveur
frontend applique la même sélection à `robots.txt`, afin que chaque domaine
annonce son propre `/sitemap.xml`.
## Performance frontend

View file

@ -1,8 +1,9 @@
import json
import logging
from pathlib import Path
from urllib.parse import urlsplit
from fastapi import APIRouter
from fastapi import APIRouter, Request
from fastapi.responses import Response
from api.config import settings
@ -20,6 +21,10 @@ LANDINGS_FILE = CONFIG_DIR / "landings.json"
# Domaine bilingue : kankwa.fr (FR) / kankwa.com (EN) — cf. shared/email/templates.py
BASE_FR = settings.frontend_url.rstrip("/")
BASE_EN = settings.frontend_url_en.rstrip("/")
HOST_LOCALES = {
urlsplit(BASE_FR).hostname: "fr",
urlsplit(BASE_EN).hostname: "en",
}
def _load(path: Path) -> list[dict]:
@ -30,13 +35,13 @@ def _load(path: Path) -> list[dict]:
return []
def _static_urls() -> list[dict]:
"""Pages statiques (seo-routes.json) : même chemin relatif sur kankwa.fr et kankwa.com."""
def _static_urls(locale: str) -> list[dict]:
"""Pages statiques du domaine demandé, avec alternates FR/EN réciproques."""
urls = []
for p in _load(ROUTES_FILE):
alternates = {"fr": f"{BASE_FR}{p['loc']}", "en": f"{BASE_EN}{p['loc']}"}
urls.append({
"loc": alternates["fr"],
"loc": alternates[locale],
"priority": p["priority"],
"changefreq": p["changefreq"],
"lastmod": p.get("lastmod"),
@ -45,8 +50,8 @@ def _static_urls() -> list[dict]:
return urls
def _landing_urls() -> list[dict]:
"""Landing pages d'acquisition (G6) : chemin propre à chaque langue, contrairement aux pages statiques."""
def _landing_urls(locale: str) -> list[dict]:
"""Landings du domaine demandé, dont les chemins diffèrent selon la langue."""
urls = []
for landing in _load(LANDINGS_FILE):
path = landing.get("path") or {}
@ -55,14 +60,13 @@ def _landing_urls() -> list[dict]:
logger.warning("Sitemap : landing %s sans chemin fr/en complet, ignorée", landing.get("key"))
continue
alternates = {"fr": f"{BASE_FR}{fr_path}", "en": f"{BASE_EN}{en_path}"}
for loc in alternates.values():
urls.append({
"loc": loc,
"priority": landing.get("priority", "0.8"),
"changefreq": landing.get("changefreq", "monthly"),
"lastmod": landing.get("updatedAt"),
"alternates": alternates,
})
urls.append({
"loc": alternates[locale],
"priority": landing.get("priority", "0.8"),
"changefreq": landing.get("changefreq", "monthly"),
"lastmod": landing.get("updatedAt"),
"alternates": alternates,
})
return urls
@ -84,8 +88,14 @@ def _render_url(u: dict) -> str:
@router.get("/sitemap.xml", include_in_schema=False)
async def sitemap():
urls = "\n".join(_render_url(u) for u in _static_urls() + _landing_urls())
async def sitemap(request: Request):
# Caddy transmet le Host d'origine. Chaque propriété Search Console reçoit
# ainsi uniquement ses URL canoniques, tout en conservant les hreflang
# cross-domain nécessaires à la relation entre traductions.
locale = HOST_LOCALES.get(request.url.hostname, "fr")
urls = "\n".join(
_render_url(u) for u in _static_urls(locale) + _landing_urls(locale)
)
xml = f"""<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">

View file

@ -183,6 +183,8 @@ dans la source unique du pré-rendu/sitemap. Premier lot recommandé :
| SEO-LP-15 | Rendre les marques services lisibles dans les previews | Make service marks legible in previews | `DONE` |
| SEO-LP-16 | Séparer icônes de navigation SEO et previews produit | Separate SEO navigation icons from product previews | `DONE` |
| SEO-LP-17 | Fiabiliser couleurs de fenêtre et cache des previews | Make window colors and preview caching reliable | `DONE` |
| SEO-LP-18 | Séparer les sitemaps par domaine et affiner les intentions événementielles | Split sitemaps by domain and refine event intents | `DONE` |
| SEO-LP-19 | Mutualiser voyage et vacances entre amis | Merge group trip and friends holiday intent | `DONE` |
#### SEO-LP-13 — previews contextualisées par intention
@ -288,6 +290,64 @@ dans la source unique du pré-rendu/sitemap. Premier lot recommandé :
contrôlés ; build et SEO pré-rendu `60 pages` verts ; déploiement Docker
vérifié (11 août 2026).
#### SEO-LP-18 — sitemaps par domaine et intentions événementielles
- Statut : `DONE`
- Agent : Codex
- Début : 15 août 2026
- Travail : faire de `/sitemap.xml` un sitemap propre au domaine appelant, ajouter
Noël comme cas dusage distinct du réveillon du Nouvel An, puis réaligner les
modules et le contenu de voyage, pot de départ et cadeau commun sur leur intention.
- Acceptation : `kankwa.fr/sitemap.xml` ne publie que des URL canoniques FR et
`kankwa.com/sitemap.xml` que des URL canoniques EN, avec alternates réciproques ;
Noël dispose dune landing bilingue, dun template Hub, dun maillage et de
previews propres ; voyage présente Kwiz en amont ; réveillon naffiche plus Kal
parmi ses modules principaux et désambiguïse le Nouvel An ; pot de départ suit
la persona qui quitte léquipe et renvoie le cadeau vers cadeau commun ; cadeau
commun annonce immédiatement labsence dencaissement et reste centré sur Kdo ;
tests sitemap/frontend, i18n, assets et build SSG verts.
- Hors périmètre : ne pas créer « vacances entre amis » sans données de requêtes
Search Console permettant décarter une cannibalisation avec « voyage ».
- Résultat : le catalogue passe à 20 landings bilingues (40 URL), dont Noël en
famille distinct du réveillon du Nouvel An. Voyage devient un récit Hub
Kwiz→Kal→Kontrib→Kount ; pot de départ devient mono-persona avec Kal en héros ;
cadeau commun reste mono-Kdo et explicite labsence dencaissement dès
lintroduction. Chaque `/sitemap.xml` ne contient plus que les `<loc>` de son
domaine et chaque `robots.txt` annonce le sitemap correspondant.
- Preuves : `api/routers/sitemap.py`, `tests/test_sitemap.py`,
`frontend/scripts/static-server.mjs`, `robots.en.txt`, `landings.json`,
catalogues FR/EN et audit SEO mis à jour ; 40 previews SEO régénérées et revue
visuelle des cinq scénarios FR plus Noël EN ; contrôle « 46 WebP configurés
présents, dimensionnés et optimisés », empreinte `59215afb78ea` ; Pytest
sitemap `3 passed` ; Vitest `193 passed` ; build Docker
`kankwa-frontend:seo-lp-18` avec i18n et TypeScript verts, « 62 pages
pré-rendues (31 FR + 31 EN) » puis « SEO pré-rendu validé : 62 pages » ;
recette HTTP des deux `robots.txt` conforme (15 août 2026).
#### SEO-LP-19 — mutualisation voyage et vacances entre amis
- Statut : `DONE`
- Agent : Codex
- Début : 15 août 2026
- Travail : faire de `/organiser/voyage` et `/plan/group-trip` les pages
canoniques communes aux voyages de groupe et aux vacances entre amis, sans
créer une landing concurrente portant les mêmes modules.
- Acceptation : titres, introduction, problèmes, parcours, modules, FAQ et
preview couvrent explicitement les vacances entre amis ; la distinction avec
le week-end court reste lisible ; aucune clé ni route `vacances-entre-amis`
nest ajoutée ; parité FR/EN, tests, assets et build SSG verts.
- Résultat : `/organiser/voyage` cible désormais « voyage ou vacances entre
amis » et développe logement, voitures, courses, activités, réservations et
dépenses quotidiennes avec Kwiz→Kal→Kontrib→Kount. La FAQ oriente les
escapades courtes vers la page week-end ; aucune nouvelle URL nest créée.
- Preuves : catalogues `landing.json` FR/EN, garde-fou dans
`landings.test.ts`, audit SEO et previews `voyage.fr.webp` /
`voyage.en.webp` revues visuellement ; génération « 6 vues Hub et 40 previews
SEO », contrôle « 46 WebP configurés présents », empreinte
`6d2045eab5fa` ; Vitest `194 passed` ; build Docker
`kankwa-frontend:seo-lp-19`, « 62 pages pré-rendues » puis
« SEO pré-rendu validé : 62 pages » (15 août 2026).
Critères communs : intention distincte, contenu non dupliqué, démonstration produit,
CTA contextualisé, maillage, FR/EN, SSG, sitemap, canonical et données structurées.

View file

@ -131,3 +131,39 @@ Après publication, l'ordre de développement du prochain lot doit venir des
impressions non-brand, du CTR et des créations de Hub par landing, indicateurs
déjà prévus dans la roadmap. Sans ces données, ajouter davantage de pages
augmenterait le risque de contenu faible plus vite que la couverture utile.
## Réévaluation SEO-LP-18 — 15 août 2026
Une revue éditoriale complémentaire porte le catalogue à 20 landings bilingues,
soit 40 URL et 14 intentions dans le cluster `organiser`. Le build pré-rend
désormais 62 pages (31 FR + 31 EN) et contrôle 46 WebP marketing.
- Noël en famille devient une intention propre : cadeaux avec Kdo, repas avec
Kontrib, grosses courses avec Kount et décisions facultatives avec Kwiz. La
FAQ précise que Kwiz ne réalise pas le tirage dun Secret Santa.
- Réveillon désigne explicitement le Nouvel An et le 31 décembre ; Kal sort des
modules principaux. Noël et Nouvel An se maillent sans partager la même
intention.
- Voyage suit le parcours « où ? » avec Kwiz, « quand ? » avec Kal, préparation
avec Kontrib, puis dépenses avec Kount. La page devient une intention Hub.
- Pot de départ adopte une seule persona : la personne qui quitte léquipe.
Kal devient le service héros, Kontrib et Kwiz restent secondaires, et le
cadeau dun collègue est traité par le maillage vers `cadeau-commun`.
- Cadeau commun reste centré sur Kdo. Labsence dencaissement apparaît dès
lintroduction, les contributions sont qualifiées de déclarées, et les
comparaisons tarifaires variables sont remplacées par une invitation à
vérifier les conditions du prestataire au moment du choix.
La proposition « vacances entre amis » ne devient pas une landing séparée. Son
intention et ses besoins propres — logement, voitures, courses, activités et
dépenses quotidiennes — sont absorbés par `/organiser/voyage`, qui porte déjà
les mêmes modules Kwiz, Kal, Kontrib et Kount. La page week-end reste distincte :
elle cible une escapade courte avec un cadrage plus léger et sans choix de
destination central.
Le sitemap est désormais segmenté par propriété : `kankwa.fr/sitemap.xml`
contient uniquement des URL canoniques FR dans `<loc>` et
`kankwa.com/sitemap.xml` uniquement des URL canoniques EN. Chaque entrée garde
ses alternates `hreflang` réciproques. Les `robots.txt` servis par domaine
annoncent le sitemap correspondant ; aucun sitemap index cross-domain nest
nécessaire pour ce catalogue.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View file

@ -0,0 +1,17 @@
User-agent: *
Allow: /
# Block private and utility pages
Disallow: /login
Disallow: /register
Disallow: /magic
Disallow: /verify-email
Disallow: /set-password
Disallow: /cancel/
Disallow: /join
Disallow: /compte
Disallow: /admin/
Disallow: /app.html
Disallow: /style-tile
Sitemap: https://kankwa.com/sitemap.xml

View file

@ -93,10 +93,16 @@ export function createStaticServer({ root, fallback = '/app.html', locales = LOC
// <route>/index.<code>.html. Une variante absente retombe sur la langue par défaut.
const resolveFile = (urlPath, host) => {
const clean = posix.normalize(urlPath).replace(/\/+$/, '') || '/'
const locale = localeForHost(host)
// Le même build sert les deux domaines. kankwa.com doit annoncer son propre
// sitemap dans robots.txt, pas celui de la propriété Search Console FR.
if (clean === '/robots.txt' && locale.code !== defaultLocale.code) {
const localizedRobots = files.get(`/robots.${locale.code}.txt`)
if (localizedRobots) return localizedRobots
}
const exact = files.get(clean)
if (exact) return exact
const locale = localeForHost(host)
if (locale.code !== defaultLocale.code) {
const localizedHit = files.get(posix.join(clean, `index.${locale.code}.html`))
if (localizedHit) return localizedHit

View file

@ -199,45 +199,54 @@
"final_desc": "Create the meal list, share it and let everyone pick what they bring."
},
"voyage": {
"meta_title": "Plan a group trip and share the costs",
"meta_description": "Dates, bookings and expenses for a group trip in one place. Everyone follows the accounts from a link, in the group's currency, with no commission.",
"short_title": "Plan a group trip",
"h1": "Plan a group trip without ending up as the treasurer",
"intro": "With several people, a trip runs on two tracks: finding dates that suit everyone, then tracking expenses fronted by different people, sometimes in another currency. Kankwa keeps both in one place, from the first poll to the last refund.",
"meta_title": "Plan a group trip or vacation with friends",
"meta_description": "Destination, lodging, dates, cars, groceries and costs: plan a group trip or vacation with friends from one shared link.",
"short_title": "Group trip or friends vacation",
"h1": "Plan a group trip or vacation with friends from the first choice to the final balance",
"intro": "A group trip and a vacation with friends begin with the same decisions: where to stay, when to go, how to travel and how to share costs. A multi-day stay also means dividing cars, groceries, gear and activities. Kankwa keeps that whole journey in one Hub.",
"cta": "Create the trip page",
"preview": {
"label": "Preview of the trip expenses",
"title": "Trip to Lisbon",
"subtitle": "Trip accounts",
"primary": "Trip total",
"secondary": "Apartment",
"tertiary": "Transport",
"status": "4 repayments"
"label": "Participant preview of the group trip or vacation Hub",
"title": "Friends vacation in Lisbon",
"subtitle": "May 1217 · 6 travellers",
"primary": "Choose the lodging",
"secondary": "Confirm the dates",
"tertiary": "Divide up preparation",
"status": "4 stages together"
},
"problem_title": "Nobody knows who paid for what any more.",
"problem_desc": "One person books the tickets, another the accommodation, and everyone covers taxis and groceries. Notes get lost, rough conversions pile up and the accounts drag on for weeks after the return.",
"solution_title": "The accounts are kept during the trip, not after it.",
"solution_desc": "Every expense is recorded when it happens, with who paid and who it concerns. Each person's balance is visible at all times, and the final settle-up takes as few transfers as possible.",
"problem_title": "The stay is not booked yet, but its planning is already scattered.",
"problem_desc": "Destinations and lodging blur together in the chat, availability lands elsewhere, then cars, groceries, activities and daily advances pile up with no shared overview.",
"solution_title": "One path for trips and vacations: where, when, what to prepare, then who pays.",
"solution_desc": "Kwiz settles destination, lodging or activities, Kal finds the dates, Kontrib divides cars, groceries and gear, then Kount tracks bookings and daily costs. Every stage stays available from the same link.",
"steps": [
{
"title": "Find the group's dates",
"desc": "Offer several possible periods and let everyone answer before you book anything."
"title": "Choose where to go",
"desc": "Compare destinations, lodging, activities or transport in a Kwiz the whole group can read before anything is booked."
},
{
"title": "Create the expense group",
"desc": "Name the trip, add the travellers and choose the currency you will use there. Shares can be unequal where needed."
"title": "Find when to go",
"desc": "Offer the periods still in play and let everyone share their availability before anything is booked."
},
{
"title": "Settle up on the way home",
"desc": "The calculation is already done: everyone sees what they owe and to whom, without going back through the receipts."
"title": "Divide up the preparation",
"desc": "Assign cars, groceries, gear or paperwork so daily life on the trip does not fall on one person."
},
{
"title": "Track and settle the costs",
"desc": "Record bookings and daily costs with their payer; on the way home, everyone sees what they owe and to whom."
}
],
"modules": {
"kwiz": "The poll that compares destinations, lodging, activities or transport before the group books its trip or vacation.",
"kal": "The date poll that finds the shared window, with an \"if need be\" answer that avoids pointless deadlocks.",
"kount": "Trip expense tracking: payer, participants, group currency, custom shares and the refund calculation.",
"kontrib": "The logistics of leaving: who drives, who brings the gear, the supplies or the group first-aid kit."
"kontrib": "The logistics of the stay: who drives, buys groceries, brings the gear or carries the group first-aid kit.",
"kount": "Booking and daily-cost tracking: payer, participants, group currency, custom shares and repayments."
},
"faq": [
{
"q": "Can this page be used to plan a vacation with friends?",
"a": "Yes. It covers a multi-day stay: destination, lodging, dates, cars, groceries, activities and daily costs. For a short break, the weekend-with-friends page offers a lighter scope."
},
{
"q": "Does Kankwa take a cut of the group's money?",
"a": "No, and the difference is fundamental: Kankwa never receives or transfers any money. The tool works out the refunds, and the transfers happen directly between you, however you prefer."
@ -255,8 +264,8 @@
"a": "No. They check the expenses from the shared link, with no sign-up. Only the person who creates and runs the group needs an account."
}
],
"final_title": "Travel together. Come home square.",
"final_desc": "Create the trip page, share the link and let the dates and the expenses look after themselves."
"final_title": "A trip or vacation decided together, with clear accounts on the way home.",
"final_desc": "Create the stay and share one link to move every group decision forward."
},
"fete-ecole": {
"meta_title": "Organise a school party: buffet, supplies and parent volunteers",
@ -361,11 +370,11 @@
"final_desc": "Create the item list, share it with the families and keep supplies, dates and the gift in the same place."
},
"cadeau-commun": {
"meta_title": "Group gift: chip in together with no commission",
"meta_description": "Run a group gift and the expense sharing in one place: a pool everyone can follow, no commission on the amount and no account for participants.",
"meta_title": "Organise a group gift without a hosted money pool",
"meta_description": "Coordinate ideas and contributions for a group gift. Kankwa does not collect money: participants pay each other directly.",
"short_title": "Organise a group gift",
"h1": "Chip in for a group gift without losing a slice of the amount",
"intro": "A milestone birthday usually means two projects at once: the gift several people chip in on, and the costs of the occasion that everyone fronted separately. Both can live on the same page.",
"h1": "Organise a group gift and know exactly who is contributing",
"intro": "Kankwa does not collect money and is not an online money pool. The page coordinates ideas, the target and stated contributions; everyone then pays directly using the method chosen by the group.",
"cta": "Create the pool",
"preview": {
"variant": "pool",
@ -373,14 +382,14 @@
"title": "Hugo's leaving gift",
"subtitle": "Ideas from the group",
"primary": "Instant camera",
"secondary": "€680 collected",
"secondary": "€680 pledged",
"tertiary": "€950 goal",
"status": "8 contributions"
"status": "8 stated contributions"
},
"problem_title": "The amount you collect never arrives whole.",
"problem_desc": "The usual reflex is to open an online money pool for the gift and an expense app for the costs. Two tools, two links, two ways to chase people — and a cut taken from the pool when you withdraw the money.",
"solution_title": "The money moves between you, never through us.",
"solution_desc": "The pool is there to coordinate: it shows the target, who is contributing and how much. Kankwa never collects the funds, so it takes nothing; transfers happen directly between you, the way you already do it.",
"problem_title": "Gift ideas and promises to contribute get scattered.",
"problem_desc": "One person suggests the gift, others reply privately and the organiser tracks amounts in personal notes. The group no longer knows what is reserved or how much remains to cover.",
"solution_title": "Visible coordination, with no intermediary holding funds.",
"solution_desc": "Kdo gathers ideas, protects the surprise and shows progress from stated contributions. Kankwa receives no funds: transfers or payments happen directly between the people involved.",
"steps": [
{
"title": "Create the list and the pool",
@ -391,74 +400,56 @@
"desc": "Everyone reserves a gift or states their contribution to the pool. The progress bar shows what is left to cover without spoiling the surprise."
},
{
"title": "Settle the costs in the same place",
"desc": "The expenses fronted for the occasion are logged alongside, and the reimbursements are calculated at the end."
"title": "Pay each other directly",
"desc": "The page coordinates pledged amounts; it provides no wallet, payment processing or pool withdrawal."
}
],
"modules": {
"kdo": "The gift list with discreet reservation and a pool several people can contribute to on the same item, progress bar included.",
"kount": "The group's expenses, each person's share and the shortest list of reimbursements to settle up."
"kdo": "The gift list with discreet reservation and tracking of stated contributions to one item, progress bar included."
},
"sections": [
{
"title": "The pool: several people, one gift",
"title": "Several people, one gift",
"body": [
"A gift list isn't only there to collect ideas. For the big gift, it offers a pool: an item several people chip in on, with a progress bar showing how the funding is going.",
"Everyone states their contribution and the bar fills up towards the target. The group can see at any time what is left to cover, with no separate spreadsheet to maintain and nobody to chase one by one."
"A gift list is not only there to gather ideas. For the main gift, it can set a target and track what each person plans to contribute.",
"The bar fills from stated contributions. The group sees what remains to cover without a separate spreadsheet, while discreet reservations prevent duplicates and protect the surprise."
]
},
{
"title": "Why \"no commission\" actually matters",
"title": "What Kankwa does — and does not do",
"body": [
"Online money pools usually take a percentage when you withdraw the funds, sometimes with extra fees if the pool stays open too long. On a gift of one to two hundred euros, that is several euros going into fees instead of into the gift.",
"The pool works differently: it is a coordination tool, not an organisation that collects money on your behalf. You see who contributes and how much, and the amount moves directly between you — bank transfer, cash, or the organiser fronting it and being paid back. The entire amount therefore goes into the gift.",
"Worth noting: a hosted money pool also handles the collection of funds, which stays useful when participants don't know each other. The pool targets a different need — a group who trust each other and simply want to coordinate without fees. It is up to you to decide what fits your situation."
"Kankwa is a coordination tool, not a payment service. It does not hold the money, provide a balance to withdraw or guarantee that a stated contribution has been paid.",
"Participants settle directly by bank transfer, cash or any method the group chooses. This works for people who trust each other; a hosted money pool serves a different need."
]
},
{
"title": "The costs of the occasion, settled straight after",
"title": "Compare money pools when you choose",
"body": [
"A group gift often goes with an occasion that cost money: a rental, fuel, a restaurant, groceries. Take a weekend where four people fronted six hundred and sixty euros in total:"
],
"list": [
"The rental, fronted by whoever made the booking.",
"The fuel for the round trip, fronted by the driver.",
"Saturday's restaurant, paid by a third person.",
"The weekend's groceries, paid by a fourth."
]
},
{
"title": "Three transfers instead of twelve",
"body": [
"Rather than asking everyone to pay everyone else, the expenses are added up, each person's share is calculated, and the shortest list of reimbursements is suggested. In the example above, three transfers are enough to settle the accounts.",
"The real gain is elsewhere: the pool and the accounts belong to the same project, shared through a single link. The group finds the gift list and the expenses in one place, and you can add the date or the items to bring if the occasion calls for it."
"Money-pool terms change over time: possible fees, withdrawal methods, identity checks and delays may all vary. Always check the providers published terms when you create the pool.",
"If centralised collection is your priority, choose a service built for it. If your group mainly wants to share ideas and know who is contributing, Kankwa keeps that coordination simple and explicit."
]
}
],
"faq": [
{
"q": "How do you chip in for a group gift with no fees?",
"a": "Add the gift to a list, turn it into a pool and set the target amount. Each participant states their contribution; Kankwa takes no commission on the amount, since the money moves directly between you."
"q": "Does Kankwa collect the group gift money?",
"a": "No. Kankwa only tracks stated contributions. Payments happen directly between participants outside the tool."
},
{
"q": "Do online money pools charge a commission?",
"a": "Usually yes, when the funds are withdrawn, and sometimes extra fees if the pool sits inactive. Always check the terms of the tool you are using: they change regularly."
"q": "How do we track the gift target?",
"a": "Add the gift, set the target amount and let each participant state their contribution. The progress bar shows what remains to cover."
},
{
"q": "Can you manage the gift and the expenses in the same place?",
"a": "Yes. The pool and the expense sharing belong to the same project, shared through a single link: the group finds both without switching tool or account."
},
{
"q": "How is it decided who reimburses whom?",
"a": "The expenses are added up, each person's share is deducted from what they already fronted, then the shortest list of transfers is suggested to balance the accounts."
"q": "Does Kankwa verify that a contribution was paid?",
"a": "No. A Kankwa contribution is a statement of intent, not a bank transaction or proof of payment."
},
{
"q": "Do participants need an account?",
"a": "No. Participants open the list and the accounts from the shared link, with no sign-up. Only the person organising creates an account."
"a": "No. Participants open the list from the shared link and state their contribution without signing up. Only the organiser creates an account."
}
],
"final_title": "The gift and the accounts, on the same page.",
"final_desc": "Create the list, open the pool and share the link: the amount stays whole and the costs are settled in the same place."
"final_title": "The ideas and contributions, on one list.",
"final_desc": "Create the group gift, make direct payment clear and share the link with the group."
},
"sondage-date": {
"meta_title": "Free date poll, no account and no ads",
@ -917,43 +908,43 @@
"final_desc": "Create the housewarming page and give every friend one reliable source of information."
},
"pot-de-depart": {
"meta_title": "Plan a leaving party and group gift together",
"meta_description": "Choose the time, gift and contributions for a leaving party on one shared page, with no guest account required.",
"meta_title": "Plan your own leaving party with colleagues",
"meta_description": "Find the time that works for the most colleagues, see who will attend and divide up food and drinks from one link.",
"short_title": "Plan a leaving party",
"h1": "A leaving party decided without chasing everyone",
"intro": "Availability, a group present and party supplies quickly become three separate threads. One page keeps every choice clear until the send-off.",
"h1": "Is your last day approaching? Plan your leaving party without chasing everyone",
"intro": "You are leaving the team and want as many colleagues as possible to join you. One page finds the right time, shows who will be there and optionally divides up food and drinks.",
"cta": "Create the leaving party",
"preview": {
"label": "Participant preview of the leaving party Hub",
"title": "Maliks leaving party",
"subtitle": "Thursday, September 24 · Manchester",
"primary": "Choose the time",
"secondary": "Prepare the group gift",
"tertiary": "Vote on the final idea",
"primary": "Thu 24 · 6 pm",
"secondary": "Fri 25 · 12:30 pm",
"tertiary": "Fri 25 · 6 pm",
"status": "Shared with 18 colleagues"
},
"problem_title": "The last day is close, but decisions are stuck.",
"problem_desc": "Availability replies disappear, gift ideas arrive privately and two volunteers carry the refreshments because there is no shared list.",
"solution_title": "One meeting point for the group.",
"solution_desc": "The Hub connects a date poll, gift ideas, a choice poll and contributions. Everyone sees the current plan without rereading the chat.",
"problem_desc": "Availability replies disappear, the guest list stays vague and two volunteers carry the refreshments because there is no shared plan.",
"solution_title": "Your invitation and its planning in one place.",
"solution_desc": "Kal reveals the time that brings the most people together. Kontrib divides up the refreshments where needed, while Kwiz remains optional for choosing a venue or format.",
"steps": [
{ "title": "Suggest the times", "desc": "Compare lunch breaks or end-of-day slots that could work for the group." },
{ "title": "Choose the gift", "desc": "Collect wishes or put a shortlist to a vote without creating duplicates." },
{ "title": "Divide up the party", "desc": "List drinks, cake and supplies, then send one link to every participant." }
{ "title": "Find the best time with Kal", "desc": "Compare lunch breaks or end-of-day slots and pick the moment that works for the most colleagues." },
{ "title": "See who will be there", "desc": "Share the page and follow responses without chasing each person separately." },
{ "title": "Optionally divide the refreshments", "desc": "List drinks, cake or supplies in Kontrib when everyone is bringing something." },
{ "title": "Share one link with the team", "desc": "The time, replies and contributions stay current at the same address until your last day." }
],
"modules": {
"kal": "Collect availability with yes, no or if-needed answers.",
"kdo": "Keep gift ideas together and let someone reserve each one discreetly before buying.",
"kwiz": "Let the group vote between gifts, activities or venues when the choice is still open.",
"kontrib": "Divide up drinks, food, decorations and equipment in a shared list."
"kontrib": "Divide up drinks, food and supplies in a shared list when the party is participatory.",
"kwiz": "Optionally choose the venue, format or an activity when several options remain."
},
"faq": [
{ "q": "Can Kankwa be used for a workplace leaving party?", "a": "Yes, when the group can use a shared link. Kankwa is not an HR tool or a corporate invitation system." },
{ "q": "Does Kankwa collect money for the gift?", "a": "No. You can track costs or shares, but the group transfers money using its own chosen method." },
{ "q": "Can people vote on the gift without an account?", "a": "Yes. Participants can use the public Kwiz module from the group link." }
{ "q": "Can I use Kankwa for my own leaving party?", "a": "Yes. This page is designed for the person leaving the team and planning the moment with colleagues; Kankwa is not an HR tool." },
{ "q": "Do colleagues need an account to reply?", "a": "No. They open the shared link to state their availability and use the public modules." },
{ "q": "What if the team is organising a leaving gift instead?", "a": "The group gift guide covers that separate intent: it helps the team coordinate ideas and contributions without mixing the two personas." }
],
"final_title": "The gift, time and refreshments finally agree.",
"final_desc": "Create the leaving party page and let the group decide from one link."
"final_title": "Your leaving party, at a time the team can actually attend.",
"final_desc": "Create the page, suggest the times and share one link with your colleagues."
},
"cousinade": {
"meta_title": "Plan a family reunion on one shared page",
@ -1128,7 +1119,7 @@
"problem_title": "The menu is decided late and the same people advance every cost.",
"problem_desc": "Contributions appear one message at a time, quantities stay vague and shared shopping is impossible to untangle after midnight.",
"solution_title": "Give every contribution a name and quantity.",
"solution_desc": "Kontrib makes the menu and logistics visible. Kal, Kwiz and Kount join the page only if the group needs to schedule, choose or balance costs.",
"solution_desc": "Kontrib makes the menu and logistics visible. Kwiz and Kount join the page only if the group needs to choose a theme or balance costs.",
"steps": [
{ "title": "List the menu and supplies", "desc": "Break down starters, mains, drinks, beds or music with the expected quantities." },
{ "title": "Let everyone claim an item", "desc": "Participants choose what they will bring through the shared link." },
@ -1136,7 +1127,6 @@
],
"modules": {
"kontrib": "Assign dishes, drinks, decorations and beds with explicit quantities.",
"kal": "Compare December 31 with a nearby date when group availability requires it.",
"kwiz": "Let everyone choose the theme, main course or entertainment.",
"kount": "Divide the main shopping, hire or transport between the people involved."
},
@ -1147,6 +1137,47 @@
],
"final_title": "Start New Years Eve without the planning chaos.",
"final_desc": "Create the shared list, divide the menu and keep the evenings decisions together."
},
"noel-en-famille": {
"meta_title": "Plan a family Christmas: gifts, food and shared costs",
"meta_description": "Bring family gifts, food, shopping and decisions onto one page every relative can open without installing an app.",
"short_title": "Plan a family Christmas",
"h1": "Plan a family Christmas without multiplying lists and messages",
"intro": "Gift ideas arrive in one place, the menu in another and the main shopping still needs dividing up. A Christmas Hub keeps the whole family on the same page without asking relatives to install an app.",
"cta": "Create the family Christmas",
"preview": {
"label": "Family preview of the Christmas Hub",
"title": "Christmas at Grandmas",
"subtitle": "December 2425 · 14 relatives",
"primary": "Share gift ideas",
"secondary": "Divide up the meal",
"tertiary": "Track the main shopping",
"status": "4 preparations together"
},
"problem_title": "At Christmas, every branch of the family keeps a different list.",
"problem_desc": "Gifts are duplicated, the meal falls on the same people and advances for shopping disappear into private messages. As the family replies, nobody has the full picture.",
"solution_title": "One page for gifts, food and family decisions.",
"solution_desc": "Kdo organises wishes, Kontrib divides the menu, Kount tracks shared costs and Kwiz settles optional choices. Every relative finds the current information from the same link.",
"steps": [
{ "title": "Gather the gift ideas", "desc": "Create the lists, prevent duplicates with discreet reservations and coordinate a group gift where needed." },
{ "title": "Divide up the meal", "desc": "List dishes, drinks, equipment and beds with clear quantities, then let everyone choose a contribution." },
{ "title": "Track the main shopping", "desc": "Record shared advances and the people involved so repayments stay clear after Christmas." },
{ "title": "Settle the remaining choices", "desc": "Use a Kwiz for the menu, activities or gift-exchange format whenever the family needs to decide." }
],
"modules": {
"kdo": "Bring gift lists, discreet reservations and stated contributions to a group gift together.",
"kontrib": "Divide starters, mains, desserts, drinks, decorations or beds with clear quantities.",
"kount": "Track the main shopping and other advances, then calculate repayments without collecting money.",
"kwiz": "Choose the menu, activities or gift-exchange rules whenever several options remain open."
},
"faq": [
{ "q": "Do family members need an account?", "a": "No. They open the link in a browser to view the page, reserve a gift or claim an item without signing up." },
{ "q": "Can we organise a group gift for Christmas?", "a": "Yes. Kdo tracks the target and stated contributions, but Kankwa does not collect money: relatives pay each other directly." },
{ "q": "How is this different from the New Years Eve page?", "a": "This page covers family Christmas, its gifts and its meal. The New Years Eve page is dedicated to December 31 and that separate intent." },
{ "q": "Does Kwiz draw Secret Santa names?", "a": "No. Kwiz can poll the format, budget or rules, but it does not perform a secret draw between participants." }
],
"final_title": "The whole family Christmas, clear from one link.",
"final_desc": "Create the page, add the preparations you need and share it with your relatives."
}
}
}

View file

@ -199,45 +199,54 @@
"final_desc": "Créez la liste du repas, partagez-la et laissez chacun choisir ce quil apporte."
},
"voyage": {
"meta_title": "Organiser un voyage à plusieurs et partager les frais",
"meta_description": "Dates, réservations et dépenses dun voyage de groupe au même endroit. Chacun suit les comptes depuis un lien, dans la devise du groupe, sans commission.",
"short_title": "Organiser un voyage à plusieurs",
"h1": "Organiser un voyage à plusieurs sans finir trésorier",
"intro": "À plusieurs, un voyage se joue sur deux plans : caler des dates qui conviennent à tout le monde, puis suivre des dépenses avancées par des personnes différentes, parfois dans une autre devise. Kankwa garde les deux au même endroit, du premier sondage au dernier remboursement.",
"cta": "Créer le voyage",
"meta_title": "Organiser un voyage ou des vacances entre amis",
"meta_description": "Destination, logement, dates, voitures, courses et dépenses : organisez votre voyage de groupe ou vos vacances entre amis depuis un seul lien.",
"short_title": "Voyage ou vacances entre amis",
"h1": "Organiser un voyage ou des vacances entre amis, du premier choix aux derniers comptes",
"intro": "Un voyage de groupe comme des vacances entre amis commence par les mêmes décisions : où dormir, quand partir, comment se déplacer et comment partager les frais. Pour un séjour de plusieurs jours, il faut aussi répartir voitures, courses, matériel et activités. Kankwa garde toute cette progression dans le même Hub.",
"cta": "Créer le séjour",
"preview": {
"label": "Aperçu des dépenses du voyage",
"title": "Voyage à Lisbonne",
"subtitle": "Comptes du séjour",
"primary": "Total du séjour",
"secondary": "Appartement",
"tertiary": "Transports",
"status": "4 remboursements"
"label": "Aperçu du Hub du voyage ou des vacances côté participants",
"title": "Vacances à Lisbonne",
"subtitle": "1217 mai · 6 voyageurs",
"primary": "Choisir le logement",
"secondary": "Confirmer les dates",
"tertiary": "Répartir la préparation",
"status": "4 étapes réunies"
},
"problem_title": "Personne ne sait plus qui a payé quoi.",
"problem_desc": "Les billets sont réservés par lun, lhébergement par lautre, les taxis et les courses par tout le monde. Les notes se perdent, les conversions approximatives saccumulent et les comptes traînent des semaines après le retour.",
"solution_title": "Les comptes se tiennent pendant le voyage, pas après.",
"solution_desc": "Chaque dépense est enregistrée quand elle a lieu, avec son payeur et les personnes concernées. Le solde de chacun est visible en permanence, et le remboursement final se fait en un minimum de virements.",
"problem_title": "Le séjour nest pas encore réservé, mais lorganisation se disperse déjà.",
"problem_desc": "Destinations et logements se mélangent dans la conversation, les disponibilités arrivent ailleurs, puis les voitures, courses, activités et avances quotidiennes sajoutent sans vue densemble.",
"solution_title": "Un parcours commun au voyage et aux vacances : où, quand, quoi prévoir, puis qui paie.",
"solution_desc": "Kwiz tranche destination, logement ou activités, Kal trouve la période, Kontrib répartit voitures, courses et matériel, puis Kount suit réservations et dépenses quotidiennes. Chaque étape reste accessible depuis le même lien.",
"steps": [
{
"title": "Trouvez les dates du groupe",
"desc": "Proposez plusieurs périodes possibles et laissez chacun se positionner avant de réserver quoi que ce soit."
"title": "Choisissez où partir",
"desc": "Comparez destinations, logements, activités ou modes de transport dans un Kwiz lisible par tout le groupe, avant toute réservation."
},
{
"title": "Créez le groupe de dépenses",
"desc": "Nommez le voyage, ajoutez les participants et choisissez la devise utilisée sur place. Les parts peuvent être inégales si nécessaire."
"title": "Trouvez quand partir",
"desc": "Proposez les périodes encore possibles et laissez chacun indiquer ses disponibilités avant la réservation."
},
{
"title": "Soldez au retour",
"desc": "Le calcul est déjà fait : chacun voit ce quil doit et à qui, sans reprendre les tickets un par un."
"title": "Répartissez la préparation",
"desc": "Attribuez voitures, courses, matériel, provisions ou démarches pour que la vie du séjour ne repose pas sur une seule personne."
},
{
"title": "Suivez puis soldez les dépenses",
"desc": "Enregistrez réservations et dépenses quotidiennes avec leur payeur ; au retour, chacun voit ce quil doit et à qui."
}
],
"modules": {
"kwiz": "Le vote qui compare destinations, logements, activités ou transports avant que le groupe ne réserve son voyage ou ses vacances.",
"kal": "Le sondage de dates pour trouver la période commune, avec une réponse « si nécessaire » qui évite les blocages inutiles.",
"kount": "Le suivi des dépenses du voyage : payeur, participants, devise du groupe, parts personnalisables et calcul des remboursements.",
"kontrib": "La logistique du départ : qui prend la voiture, le matériel, les provisions ou la pharmacie du groupe."
"kontrib": "La logistique du séjour : qui prend la voiture, fait les courses, apporte le matériel ou la pharmacie du groupe.",
"kount": "Le suivi des réservations et dépenses quotidiennes : payeur, participants, devise, parts personnalisables et remboursements."
},
"faq": [
{
"q": "Cette page convient-elle pour organiser des vacances entre amis ?",
"a": "Oui. Elle couvre précisément un séjour de plusieurs jours : destination, logement, dates, voitures, courses, activités et dépenses quotidiennes. Pour une escapade courte, la page week-end entre amis propose un cadrage plus léger."
},
{
"q": "Kankwa prend-il une commission sur largent du groupe ?",
"a": "Non, et cest une différence de nature : Kankwa ne reçoit ni ne transfère aucun argent. Loutil calcule les remboursements, les virements se font directement entre vous, par le moyen que vous préférez."
@ -255,8 +264,8 @@
"a": "Non. Ils consultent létat des dépenses depuis le lien partagé, sans inscription. Seule la personne qui crée et administre le groupe a besoin dun compte."
}
],
"final_title": "Partez à plusieurs. Revenez sans arriérés.",
"final_desc": "Créez le voyage, partagez le lien et laissez les dates et les dépenses se tenir toutes seules."
"final_title": "Un voyage ou des vacances décidés ensemble, des comptes lisibles au retour.",
"final_desc": "Créez le séjour et partagez un seul lien pour faire avancer chaque décision du groupe."
},
"fete-ecole": {
"meta_title": "Organiser une fête d'école : buffet, matériel et parents volontaires",
@ -361,11 +370,11 @@
"final_desc": "Créez la liste des apports, partagez-la aux familles et gardez le matériel, la date et le cadeau au même endroit."
},
"cadeau-commun": {
"meta_title": "Cadeau commun : se cotiser à plusieurs sans commission",
"meta_description": "Organisez un cadeau commun et le partage des frais au même endroit : une collecte visible par tous, aucune commission sur la somme et aucun compte pour les participants.",
"meta_title": "Organiser un cadeau commun sans cagnotte encaissée",
"meta_description": "Coordonnez les idées et les contributions à un cadeau commun. Kankwa nencaisse pas largent : les paiements restent directs entre les participants.",
"short_title": "Organiser un cadeau commun",
"h1": "Se cotiser pour un cadeau commun sans perdre une part de la somme",
"intro": "Un anniversaire marquant, c'est souvent deux chantiers en même temps : le cadeau sur lequel plusieurs personnes se cotisent, et les frais du moment que chacun a avancés de son côté. Les deux peuvent tenir dans la même page.",
"h1": "Organiser un cadeau commun en sachant exactement qui participe",
"intro": "Kankwa ne collecte pas largent et nest pas une cagnotte en ligne. La page coordonne les idées, lobjectif et les contributions déclarées ; chacun paie ensuite directement, par le moyen choisi par le groupe.",
"cta": "Créer la collecte",
"preview": {
"variant": "pool",
@ -373,14 +382,14 @@
"title": "Cadeau de départ de Hugo",
"subtitle": "Idées du groupe",
"primary": "Appareil photo instantané",
"secondary": "680 € réunis",
"secondary": "680 € annoncés",
"tertiary": "Objectif : 950 €",
"status": "8 participations"
"status": "8 participations déclarées"
},
"problem_title": "La somme récoltée n'arrive jamais entière.",
"problem_desc": "Le réflexe est d'ouvrir une cagnotte en ligne pour le cadeau et une application de comptes pour les dépenses. Deux outils, deux liens, deux façons de relancer — et un prélèvement sur la cagnotte au moment de récupérer l'argent.",
"solution_title": "L'argent circule entre vous, jamais par nous.",
"solution_desc": "La collecte sert à coordonner : elle montre l'objectif, qui participe et à quelle hauteur. Kankwa n'encaisse rien et ne prélève donc rien ; les virements se font directement entre vous, comme vous en avez l'habitude.",
"problem_title": "Les idées et les promesses de participation se dispersent.",
"problem_desc": "Une personne propose le cadeau, dautres répondent en privé et lorganisateur tient les montants dans ses notes. Le groupe ne sait plus ce qui est réservé ni ce quil reste à couvrir.",
"solution_title": "Une coordination visible, sans encaissement intermédiaire.",
"solution_desc": "Kdo rassemble les idées, protège la surprise et affiche lavancement des contributions déclarées. Kankwa ne reçoit aucun fonds : les virements ou paiements se font directement entre les personnes concernées.",
"steps": [
{
"title": "Créez la liste et la collecte",
@ -391,74 +400,56 @@
"desc": "Chacun réserve un cadeau ou indique sa participation à la collecte. La barre d'avancement montre ce qu'il reste à couvrir, sans dévoiler la surprise."
},
{
"title": "Réglez les frais au même endroit",
"desc": "Les dépenses avancées pour l'occasion sont enregistrées à côté, et les remboursements calculés en fin de moment."
"title": "Payez directement entre vous",
"desc": "La page coordonne les montants annoncés ; elle ne fournit ni portefeuille, ni paiement, ni retrait de cagnotte."
}
],
"modules": {
"kdo": "La liste de cadeaux avec réservation discrète et collecte à plusieurs sur un même objet, barre d'avancement comprise.",
"kount": "Les dépenses du groupe, la part de chacun et la liste la plus courte de remboursements pour solder les comptes."
"kdo": "La liste de cadeaux avec réservation discrète et suivi des contributions déclarées sur un même objet, barre davancement comprise."
},
"sections": [
{
"title": "La collecte : plusieurs personnes, un seul cadeau",
"title": "Plusieurs personnes, un seul cadeau",
"body": [
"Une liste de cadeaux ne sert pas qu'à énumérer des idées. Pour le gros cadeau, elle propose une collecte : un objet sur lequel plusieurs personnes se cotisent, avec une barre de progression qui montre où en est le financement.",
"Chacun indique sa participation et la barre se remplit jusqu'à l'objectif. Le groupe voit en permanence ce qu'il reste à couvrir, sans qu'il soit nécessaire de tenir un tableau à part ni de relancer un par un."
"Une liste de cadeaux ne sert pas qu’à énumérer des idées. Pour le cadeau principal, elle permet de fixer un objectif et de suivre ce que chaque personne prévoit de verser.",
"La barre se remplit à partir des contributions déclarées. Le groupe voit ce quil reste à couvrir sans tableau séparé, tandis que la réservation discrète évite les doublons et préserve la surprise."
]
},
{
"title": "Pourquoi « sans commission » change quelque chose",
"title": "Ce que Kankwa fait — et ne fait pas",
"body": [
"Les cagnottes en ligne prélèvent le plus souvent un pourcentage au moment de récupérer l'argent, parfois complété par des frais si la cagnotte reste ouverte trop longtemps. Sur un cadeau de cent à deux cents euros, cela représente plusieurs euros qui partent en frais au lieu d'aller dans le cadeau.",
"La collecte fonctionne autrement : c'est un outil de coordination, pas un organisme qui encaisse à votre place. Vous voyez qui participe et à quelle hauteur, et la somme circule directement entre vous — virement, espèces, ou l'organisateur qui avance puis se fait rembourser. L'intégralité du montant va donc dans le cadeau.",
"À noter : une cagnotte hébergée gère aussi l'encaissement, ce qui reste utile quand les participants ne se connaissent pas. La collecte vise un autre besoin, celui d'un groupe qui se fait confiance et veut simplement se coordonner sans frais. À vous de voir ce qui correspond à votre situation."
"Kankwa est un outil de coordination, pas un service de paiement. Il nhéberge pas la somme, ne fournit pas de solde à retirer et ne garantit pas quune contribution annoncée a été payée.",
"Les participants règlent directement par virement, espèces ou tout autre moyen choisi par le groupe. Cette approche convient à des proches qui se font confiance ; une cagnotte encaissée répond à un autre besoin."
]
},
{
"title": "Les frais de l'occasion, réglés dans la foulée",
"title": "Comparer les cagnottes au moment de choisir",
"body": [
"Un cadeau commun accompagne souvent un moment qui a coûté de l'argent : une location, de l'essence, un restaurant, des courses. Prenons un week-end où quatre personnes ont avancé six cent soixante euros au total :"
],
"list": [
"La location du logement, avancée par la personne qui a réservé.",
"L'essence de l'aller-retour, avancée par le conducteur.",
"Le restaurant du samedi soir, réglé par une troisième personne.",
"Les courses du week-end, réglées par une quatrième."
]
},
{
"title": "Trois virements plutôt que douze",
"body": [
"Plutôt que de demander à chacun de rembourser tout le monde, les dépenses sont additionnées, la part de chaque personne est calculée, et la liste la plus courte de remboursements est proposée. Sur l'exemple ci-dessus, trois virements suffisent à solder les comptes.",
"Le vrai gain est ailleurs : la collecte et les comptes appartiennent au même projet, partagé par un seul lien. Le groupe retrouve la liste de cadeaux et les dépenses au même endroit, et vous pouvez y ajouter la date ou les apports si le moment le demande."
"Les conditions des services de cagnotte évoluent : frais éventuels, moyens de retrait, vérification didentité et délais peuvent changer. Vérifiez toujours les conditions publiées par le prestataire au moment où vous créez la cagnotte.",
"Si votre priorité est lencaissement centralisé, choisissez un service prévu pour cela. Si votre groupe veut surtout partager les idées et savoir qui contribue, Kankwa garde cette coordination simple et explicite."
]
}
],
"faq": [
{
"q": "Comment se cotiser pour un cadeau commun sans frais ?",
"a": "Ajoutez le cadeau à une liste, passez-le en collecte et indiquez le montant visé. Chaque participant déclare sa contribution ; Kankwa ne prend aucune commission sur la somme, puisque l'argent circule directement entre vous."
"q": "Kankwa encaisse-t-il largent du cadeau commun ?",
"a": "Non. Kankwa suit uniquement les contributions déclarées. Les paiements se font directement entre les participants, en dehors de loutil."
},
{
"q": "Les cagnottes en ligne prélèvent-elles une commission ?",
"a": "Le plus souvent oui, au moment du retrait des fonds, et parfois des frais supplémentaires si la cagnotte reste inactive. Vérifiez toujours les conditions de l'outil que vous utilisez : elles changent régulièrement."
"q": "Comment suivre lobjectif du cadeau ?",
"a": "Ajoutez le cadeau, indiquez le montant visé puis laissez chaque participant déclarer sa contribution. La barre davancement montre ce quil reste à couvrir."
},
{
"q": "Peut-on gérer le cadeau et les dépenses au même endroit ?",
"a": "Oui. La collecte et le partage des dépenses appartiennent au même projet, partagé par un seul lien : le groupe retrouve les deux sans changer d'outil ni de compte."
},
{
"q": "Comment est calculé qui doit rembourser qui ?",
"a": "Les dépenses sont additionnées, la part de chaque personne est déduite de ce qu'elle a déjà avancé, puis la liste la plus courte de virements est proposée pour équilibrer les comptes."
"q": "Kankwa vérifie-t-il quune contribution a été payée ?",
"a": "Non. Une contribution dans Kankwa est une déclaration de participation, pas une transaction bancaire ni une preuve de paiement."
},
{
"q": "Faut-il un compte pour participer ?",
"a": "Non. Les participants ouvrent la liste et les comptes depuis le lien partagé, sans inscription. Seule la personne qui organise crée un compte."
"a": "Non. Les participants ouvrent la liste depuis le lien partagé et déclarent leur contribution sans inscription. Seule la personne qui organise crée un compte."
}
],
"final_title": "Le cadeau et les comptes, dans la même page.",
"final_desc": "Créez la liste, ouvrez la collecte et partagez le lien : la somme reste entière et les frais se règlent au même endroit."
"final_title": "Les idées et les contributions, sur une seule liste.",
"final_desc": "Créez le cadeau commun, annoncez clairement le paiement direct et partagez le lien avec le groupe."
},
"sondage-date": {
"meta_title": "Sondage de dates gratuit, sans compte ni publicité",
@ -917,43 +908,43 @@
"final_desc": "Créez la page de la crémaillère et partagez une seule source dinformation avec tous vos proches."
},
"pot-de-depart": {
"meta_title": "Organiser un pot de départ et son cadeau commun",
"meta_description": "Choisissez le créneau, le cadeau et les apports du pot de départ sur une page partagée, sans compte obligatoire pour les participants.",
"meta_title": "Organiser son pot de départ avec ses collègues",
"meta_description": "Trouvez le créneau qui réunit le plus de collègues, voyez qui sera là et répartissez boissons et nourriture depuis un seul lien.",
"short_title": "Organiser un pot de départ",
"h1": "Un pot de départ décidé sans relancer tout le monde",
"intro": "Entre les disponibilités, le cadeau commun et ce quil faut apporter, le départ finit vite en trois fils de discussion. Une page garde les choix lisibles jusquau jour J.",
"h1": "Votre dernier jour approche ? Organisez votre pot de départ sans courir après tout le monde",
"intro": "Vous quittez léquipe et voulez réunir un maximum de collègues. Une page suffit pour trouver le bon créneau, voir qui sera là et répartir éventuellement boissons et nourriture.",
"cta": "Créer le pot de départ",
"preview": {
"label": "Aperçu du Hub du pot de départ côté participants",
"title": "Pot de départ de Malik",
"subtitle": "Jeudi 24 septembre · Lille",
"primary": "Choisir le créneau",
"secondary": "Préparer le cadeau commun",
"tertiary": "Voter pour lidée finale",
"primary": "Jeu. 24 · 18 h",
"secondary": "Ven. 25 · 12 h 30",
"tertiary": "Ven. 25 · 18 h",
"status": "Page partagée avec 18 collègues"
},
"problem_title": "Le départ approche, les décisions navancent pas.",
"problem_desc": "Les réponses de disponibilité se perdent, les idées de cadeau arrivent en privé et le buffet repose sur deux volontaires faute de liste commune.",
"solution_title": "Un point de rendez-vous pour le groupe.",
"solution_desc": "Le Hub réunit le sondage de dates, les idées de cadeau, le vote et les apports. Chacun retrouve létat réel sans relire la conversation.",
"problem_desc": "Les réponses de disponibilité se perdent, la liste des présents reste floue et le buffet repose sur deux volontaires faute de répartition commune.",
"solution_title": "Votre invitation et son organisation au même endroit.",
"solution_desc": "Kal fait émerger le créneau qui rassemble le plus de monde. Kontrib répartit le buffet si nécessaire, et Kwiz reste disponible pour choisir un lieu ou un format.",
"steps": [
{ "title": "Proposez les créneaux", "desc": "Comparez les pauses déjeuner ou fins de journée qui conviennent au groupe." },
{ "title": "Faites émerger le cadeau", "desc": "Rassemblez les envies ou soumettez plusieurs idées au vote, sans doublon." },
{ "title": "Répartissez le pot", "desc": "Listez boissons, gâteau et matériel, puis envoyez le lien unique aux participants." }
{ "title": "Trouvez le bon créneau avec Kal", "desc": "Comparez les pauses déjeuner ou fins de journée et retenez le moment qui convient au maximum de collègues." },
{ "title": "Voyez qui sera là", "desc": "Partagez la page et suivez les réponses sans relancer chaque personne séparément." },
{ "title": "Répartissez éventuellement le buffet", "desc": "Listez boissons, gâteau ou matériel dans Kontrib si chacun apporte quelque chose." },
{ "title": "Partagez un seul lien avec léquipe", "desc": "Le créneau, les réponses et les apports restent à jour à la même adresse jusquau dernier jour." }
],
"modules": {
"kal": "Recueillez les disponibilités avec des réponses oui, non ou si nécessaire.",
"kdo": "Centralisez les idées de cadeau et permettez leur réservation discrète avant lachat.",
"kwiz": "Faites voter le groupe entre plusieurs cadeaux, activités ou lieux lorsque le choix reste ouvert.",
"kontrib": "Répartissez boissons, nourriture, décoration et matériel avec une liste partagée."
"kontrib": "Répartissez boissons, nourriture et matériel avec une liste partagée lorsque le pot est participatif.",
"kwiz": "Choisissez facultativement le lieu, le format ou une activité lorsque plusieurs options restent ouvertes."
},
"faq": [
{ "q": "Peut-on utiliser Kankwa pour un pot de départ professionnel ?", "a": "Oui, tant que le groupe partage le lien. Kankwa nest toutefois pas un outil RH ni un système dinvitation dentreprise." },
{ "q": "Kankwa collecte-t-il largent du cadeau ?", "a": "Non. Vous pouvez suivre les dépenses ou les parts, mais largent est échangé directement par les moyens choisis par le groupe." },
{ "q": "Peut-on voter pour le cadeau sans créer de compte ?", "a": "Oui. Le module Kwiz est accessible aux participants depuis le lien public du groupe." }
{ "q": "Peut-on utiliser Kankwa pour son propre pot de départ ?", "a": "Oui. Cette page est pensée pour la personne qui quitte léquipe et organise le moment avec ses collègues ; Kankwa nest toutefois pas un outil RH." },
{ "q": "Les collègues doivent-ils créer un compte pour répondre ?", "a": "Non. Ils ouvrent le lien partagé pour indiquer leurs disponibilités et participer aux modules publics." },
{ "q": "Et si léquipe organise plutôt un cadeau de départ ?", "a": "Le guide cadeau commun couvre cette intention séparément : il aide le groupe à coordonner les idées et les contributions sans mélanger les deux personas." }
],
"final_title": "Le cadeau, le créneau et le buffet enfin alignés.",
"final_desc": "Créez la page du pot de départ et laissez le groupe décider depuis un seul lien."
"final_title": "Votre pot de départ, au moment où léquipe peut vraiment venir.",
"final_desc": "Créez la page, proposez les créneaux et partagez un seul lien avec vos collègues."
},
"cousinade": {
"meta_title": "Organiser une cousinade ou réunion de famille",
@ -1110,15 +1101,15 @@
"final_desc": "Lancez le vote entre amis et faites avancer chaque décision depuis la même page."
},
"reveillon": {
"meta_title": "Organiser un réveillon et répartir le repas",
"meta_description": "Répartissez le menu, choisissez la date ou le thème et partagez les dépenses du réveillon sur une page accessible à tous.",
"short_title": "Organiser un réveillon",
"h1": "Un réveillon où le menu ne repose pas sur une seule personne",
"meta_title": "Organiser un réveillon du Nouvel An et répartir le repas",
"meta_description": "Répartissez le menu, choisissez le thème et partagez les dépenses du réveillon du Nouvel An sur une page accessible à tous.",
"short_title": "Organiser le réveillon du Nouvel An",
"h1": "Un réveillon du Nouvel An où le menu ne repose pas sur une seule personne",
"intro": "Entrées, boissons, couchages, thème et courses : la soirée devient plus légère quand chaque contribution est visible avant le 31 décembre.",
"cta": "Créer le réveillon",
"preview": {
"label": "Aperçu de la liste Kontrib du réveillon",
"title": "Réveillon chez Samira",
"label": "Aperçu de la liste Kontrib du réveillon du Nouvel An",
"title": "Nouvel An chez Samira",
"subtitle": "31 décembre · Montpellier",
"primary": "Entrées pour 10",
"secondary": "Boissons sans alcool",
@ -1128,7 +1119,7 @@
"problem_title": "Le menu se décide tard et les mêmes personnes avancent tout.",
"problem_desc": "Les apports sont annoncés au fil des messages, les quantités restent floues et les courses communes ne sont plus attribuables après minuit.",
"solution_title": "Chaque contribution a un nom et une quantité.",
"solution_desc": "Kontrib rend le menu et la logistique visibles. Kal, Kwiz et Kount complètent la page uniquement si le groupe doit choisir, dater ou équilibrer les dépenses.",
"solution_desc": "Kontrib rend le menu et la logistique visibles. Kwiz et Kount complètent la page uniquement si le groupe doit choisir un thème ou équilibrer les dépenses.",
"steps": [
{ "title": "Listez le menu et le matériel", "desc": "Décomposez entrées, plats, boissons, couchages ou musique avec les quantités attendues." },
{ "title": "Laissez chacun se positionner", "desc": "Les participants choisissent ce quils apportent depuis le lien partagé." },
@ -1136,7 +1127,6 @@
],
"modules": {
"kontrib": "Attribuez plats, boissons, décoration et couchages avec des quantités explicites.",
"kal": "Comparez le 31 décembre avec une date voisine si les disponibilités du groupe lexigent.",
"kwiz": "Faites choisir le thème, le menu principal ou lanimation de la soirée.",
"kount": "Répartissez les grosses courses, la location ou le transport entre les personnes concernées."
},
@ -1147,6 +1137,48 @@
],
"final_title": "Le réveillon commence avant minuit, sans le chaos.",
"final_desc": "Créez la liste partagée, répartissez le menu et gardez les décisions de la soirée ensemble."
},
"noel-en-famille": {
"meta_title": "Organiser Noël en famille : cadeaux, repas et dépenses",
"meta_description": "Rassemblez cadeaux, repas, courses et décisions du Noël familial sur une page que tous vos proches peuvent ouvrir sans application.",
"short_title": "Organiser Noël en famille",
"h1": "Organiser Noël en famille sans multiplier les listes et les messages",
"intro": "Les idées cadeaux arrivent dun côté, le menu dun autre et les grosses courses restent à répartir. Un Hub de Noël garde toute la famille sur la même page, sans demander aux proches dinstaller une application.",
"cta": "Créer le Noël en famille",
"preview": {
"label": "Aperçu du Hub de Noël côté famille",
"title": "Noël chez Mamie",
"subtitle": "2425 décembre · 14 proches",
"primary": "Partager les idées cadeaux",
"secondary": "Répartir le repas",
"tertiary": "Suivre les grosses courses",
"status": "4 préparatifs réunis"
},
"problem_title": "À Noël, chaque branche de la famille tient sa propre liste.",
"problem_desc": "Les cadeaux se doublonnent, le repas repose sur les mêmes personnes et les avances pour les courses se retrouvent dans des messages privés. À mesure que la famille répond, personne na plus la vue densemble.",
"solution_title": "Une seule page pour les cadeaux, le repas et les décisions familiales.",
"solution_desc": "Kdo organise les envies, Kontrib répartit le menu, Kount suit les dépenses communes et Kwiz tranche les choix facultatifs. Chaque proche retrouve linformation actuelle depuis le même lien.",
"steps": [
{ "title": "Rassemblez les idées cadeaux", "desc": "Créez les listes, évitez les doublons grâce aux réservations discrètes et coordonnez un cadeau commun si nécessaire." },
{ "title": "Répartissez le repas", "desc": "Listez plats, boissons, matériel et couchages avec les quantités attendues, puis laissez chacun choisir sa contribution." },
{ "title": "Suivez les grosses courses", "desc": "Enregistrez les avances communes et les personnes concernées pour que les remboursements restent lisibles après les fêtes." },
{ "title": "Tranchez les choix restants", "desc": "Utilisez un Kwiz pour le menu, les activités ou le format de léchange de cadeaux lorsque la famille doit décider." }
],
"modules": {
"kdo": "Centralisez les listes de cadeaux, les réservations discrètes et les contributions déclarées à un cadeau commun.",
"kontrib": "Répartissez entrée, plat, dessert, boissons, décoration ou couchages avec des quantités claires.",
"kount": "Suivez les grosses courses et autres avances, puis calculez les remboursements sans encaisser largent.",
"kwiz": "Faites choisir le menu, les activités ou les règles dun échange de cadeaux quand plusieurs options restent ouvertes."
},
"faq": [
{ "q": "Les membres de la famille doivent-ils créer un compte ?", "a": "Non. Ils ouvrent le lien dans leur navigateur pour consulter la page, réserver un cadeau ou choisir un apport sans inscription." },
{ "q": "Peut-on organiser un cadeau commun pour Noël ?", "a": "Oui. Kdo suit lobjectif et les contributions déclarées, mais Kankwa nencaisse pas largent : les paiements restent directs entre proches." },
{ "q": "Quelle différence avec la page Réveillon ?", "a": "Cette page couvre le Noël familial, ses cadeaux et son repas. La page Réveillon est dédiée à la soirée du 31 décembre et à lintention Nouvel An." },
{ "q": "Kwiz effectue-t-il un tirage Secret Santa ?", "a": "Non. Kwiz sert à voter sur le format, le budget ou les règles. Il ne réalise pas de tirage secret entre participants."
}
],
"final_title": "Le Noël de toute la famille, lisible depuis un seul lien.",
"final_desc": "Créez la page, ajoutez les préparatifs utiles et partagez-la avec vos proches."
}
}
}

View file

@ -1,191 +1,201 @@
{
"anniversaire": {
"fr": {
"width": 804,
"width": 793,
"height": 511
},
"en": {
"width": 804,
"width": 793,
"height": 511
}
},
"week-end": {
"fr": {
"width": 804,
"width": 793,
"height": 511
},
"en": {
"width": 804,
"width": 793,
"height": 511
}
},
"repas": {
"fr": {
"width": 804,
"width": 793,
"height": 511
},
"en": {
"width": 804,
"width": 793,
"height": 511
}
},
"voyage": {
"fr": {
"width": 804,
"width": 793,
"height": 513
},
"en": {
"width": 804,
"width": 793,
"height": 511
}
},
"fete-ecole": {
"fr": {
"width": 804,
"width": 793,
"height": 511
},
"en": {
"width": 804,
"width": 793,
"height": 511
}
},
"cadeau-commun": {
"fr": {
"width": 804,
"width": 793,
"height": 511
},
"en": {
"width": 804,
"height": 513
"width": 793,
"height": 511
}
},
"sondage-date": {
"fr": {
"width": 804,
"height": 521
"width": 793,
"height": 523
},
"en": {
"width": 804,
"height": 521
"width": 793,
"height": 523
}
},
"liste-cadeaux": {
"fr": {
"width": 804,
"width": 793,
"height": 551
},
"en": {
"width": 804,
"width": 793,
"height": 551
}
},
"qui-apporte-quoi": {
"fr": {
"width": 804,
"height": 513
"width": 793,
"height": 511
},
"en": {
"width": 804,
"height": 513
"width": 793,
"height": 511
}
},
"partage-depenses": {
"fr": {
"width": 804,
"width": 793,
"height": 511
},
"en": {
"width": 804,
"width": 793,
"height": 511
}
},
"comparatif-doodle": {
"fr": {
"width": 804,
"width": 793,
"height": 523
},
"en": {
"width": 804,
"height": 521
"width": 793,
"height": 523
}
},
"comparatif-splitwise-tricount": {
"fr": {
"width": 804,
"width": 793,
"height": 511
},
"en": {
"width": 804,
"width": 793,
"height": 511
}
},
"cremaillere": {
"fr": {
"width": 804,
"height": 513
"width": 793,
"height": 511
},
"en": {
"width": 804,
"width": 793,
"height": 511
}
},
"pot-de-depart": {
"fr": {
"width": 804,
"height": 513
"width": 793,
"height": 543
},
"en": {
"width": 804,
"height": 513
"width": 793,
"height": 543
}
},
"cousinade": {
"fr": {
"width": 804,
"height": 513
"width": 793,
"height": 511
},
"en": {
"width": 804,
"height": 513
"width": 793,
"height": 511
}
},
"baby-shower": {
"fr": {
"width": 804,
"height": 513
"width": 793,
"height": 511
},
"en": {
"width": 804,
"height": 513
"width": 793,
"height": 511
}
},
"soiree-jeux": {
"fr": {
"width": 804,
"height": 586
"width": 793,
"height": 585
},
"en": {
"width": 804,
"height": 586
"width": 793,
"height": 585
}
},
"sortie-entre-amis": {
"fr": {
"width": 804,
"height": 586
"width": 793,
"height": 585
},
"en": {
"width": 804,
"height": 586
"width": 793,
"height": 585
}
},
"reveillon": {
"fr": {
"width": 804,
"height": 513
"width": 793,
"height": 511
},
"en": {
"width": 804,
"width": 793,
"height": 511
}
},
"noel-en-famille": {
"fr": {
"width": 793,
"height": 511
},
"en": {
"width": 793,
"height": 511
}
}

View file

@ -39,13 +39,13 @@
"key": "voyage",
"cluster": "organiser",
"path": { "fr": "/organiser/voyage", "en": "/plan/group-trip" },
"primaryService": "kount",
"modules": ["kal", "kount", "kontrib"],
"primaryService": "hub",
"modules": ["kwiz", "kal", "kontrib", "kount"],
"hubTemplate": { "eventType": "voyage" },
"related": ["week-end", "partage-depenses"],
"related": ["week-end", "sortie-entre-amis", "partage-depenses"],
"priority": "0.8",
"changefreq": "monthly",
"updatedAt": "2026-08-11"
"updatedAt": "2026-08-15"
},
{
"key": "fete-ecole",
@ -66,14 +66,14 @@
"cluster": "organiser",
"path": { "fr": "/organiser/cadeau-commun", "en": "/plan/group-gift" },
"primaryService": "kdo",
"modules": ["kdo", "kount"],
"modules": ["kdo"],
"hubTemplate": { "eventType": "autre" },
"related": ["liste-cadeaux", "partage-depenses", "anniversaire", "pot-de-depart"],
"related": ["liste-cadeaux", "anniversaire", "pot-de-depart", "noel-en-famille"],
"editorial": true,
"priority": "0.9",
"changefreq": "monthly",
"publishedAt": "2026-06-03",
"updatedAt": "2026-08-11"
"updatedAt": "2026-08-15"
},
{
"key": "sondage-date",
@ -159,13 +159,13 @@
"key": "pot-de-depart",
"cluster": "organiser",
"path": { "fr": "/organiser/pot-de-depart", "en": "/plan/leaving-party" },
"primaryService": "hub",
"modules": ["kal", "kdo", "kwiz", "kontrib"],
"primaryService": "kal",
"modules": ["kal", "kontrib", "kwiz"],
"hubTemplate": { "eventType": "soiree" },
"related": ["cadeau-commun", "sondage-date", "qui-apporte-quoi"],
"priority": "0.8",
"changefreq": "monthly",
"updatedAt": "2026-08-11"
"updatedAt": "2026-08-15"
},
{
"key": "cousinade",
@ -220,11 +220,23 @@
"cluster": "organiser",
"path": { "fr": "/organiser/reveillon", "en": "/plan/new-years-eve-party" },
"primaryService": "kontrib",
"modules": ["kontrib", "kal", "kwiz", "kount"],
"modules": ["kontrib", "kwiz", "kount"],
"hubTemplate": { "eventType": "soiree" },
"related": ["repas", "qui-apporte-quoi", "partage-depenses"],
"related": ["noel-en-famille", "repas", "qui-apporte-quoi", "partage-depenses"],
"priority": "0.8",
"changefreq": "monthly",
"updatedAt": "2026-08-11"
"updatedAt": "2026-08-15"
},
{
"key": "noel-en-famille",
"cluster": "organiser",
"path": { "fr": "/organiser/noel-en-famille", "en": "/plan/family-christmas" },
"primaryService": "hub",
"modules": ["kdo", "kontrib", "kount", "kwiz"],
"hubTemplate": { "eventType": "autre" },
"related": ["cadeau-commun", "liste-cadeaux", "repas", "reveillon"],
"priority": "0.9",
"changefreq": "monthly",
"updatedAt": "2026-08-15"
}
]

View file

@ -59,6 +59,34 @@ describe('landings.json', () => {
}
expect(getHubUseCase('inconnu')).toBeUndefined()
})
it('verrouille les intentions et modules affinés par SEO-LP-18', () => {
expect(getLandingByKey('voyage')).toMatchObject({
primaryService: 'hub',
modules: ['kwiz', 'kal', 'kontrib', 'kount'],
})
expect(getLandingByKey('pot-de-depart')).toMatchObject({
primaryService: 'kal',
modules: ['kal', 'kontrib', 'kwiz'],
})
expect(getLandingByKey('cadeau-commun')?.modules).toEqual(['kdo'])
expect(getLandingByKey('reveillon')?.modules).toEqual(['kontrib', 'kwiz', 'kount'])
expect(getLandingByKey('noel-en-famille')).toMatchObject({
path: { fr: '/organiser/noel-en-famille', en: '/plan/family-christmas' },
modules: ['kdo', 'kontrib', 'kount', 'kwiz'],
hubTemplate: { eventType: 'autre' },
})
})
it('mutualise voyage et vacances entre amis sans landing concurrente', () => {
const voyage = getLandingByKey('voyage')
expect(voyage?.modules).toEqual(['kwiz', 'kal', 'kontrib', 'kount'])
expect(frLanding.pages.voyage.meta_title).toContain('vacances entre amis')
expect(enLanding.pages.voyage.meta_title).toContain('vacation with friends')
expect(getLandingByKey('vacances-entre-amis')).toBeUndefined()
expect(getLandingByPath('/organiser/vacances-entre-amis')).toBeUndefined()
expect(getLandingByPath('/plan/vacation-with-friends')).toBeUndefined()
})
})
describe('contenu des landings', () => {
@ -113,6 +141,11 @@ describe('contenu des landings', () => {
expect(new Set(previewTitles).size, `${lang}: preview.title dupliqué`).toBe(previewTitles.length)
}
})
it("annonce l'absence d'encaissement dès l'introduction du cadeau commun", () => {
expect(frLanding.pages['cadeau-commun'].intro).toMatch(/ne collecte pas largent/)
expect(enLanding.pages['cadeau-commun'].intro).toMatch(/does not collect money/)
})
})
describe('landingPreviewImage', () => {
@ -122,7 +155,7 @@ describe('landingPreviewImage', () => {
})
it('fournit les dimensions intrinsèques générées pour éviter le CLS', () => {
expect(landingPreviewDimensions('anniversaire', 'fr')).toEqual({ width: 804, height: 511 })
expect(landingPreviewDimensions('anniversaire', 'fr')).toEqual({ width: 793, height: 511 })
expect(landingPreviewDimensions('inconnue', 'fr')).toEqual({ width: 720, height: 440 })
})
})

View file

@ -1,3 +1,3 @@
{
"version": "eb6e7a5914b2"
"version": "6d2045eab5fa"
}

View file

@ -1,7 +1,7 @@
"""Le sitemap est la seule surface backend qui lit les sources SEO du frontend.
Ces tests vérifient qu'une page publique déclarée dans `seo-routes.json` ou
`landings.json` sort bien du sitemap avec ses deux URL de langue et leurs
`landings.json` sort bien dans le sitemap propre à chaque domaine, avec ses
alternates hreflang réciproques.
"""
import json
@ -16,10 +16,10 @@ from api.routers.sitemap import router as sitemap_router
NS = {"sm": "http://www.sitemaps.org/schemas/sitemap/0.9", "xhtml": "http://www.w3.org/1999/xhtml"}
def _sitemap_root():
def _sitemap_root(base_url=BASE_FR):
app = FastAPI()
app.include_router(sitemap_router)
response = TestClient(app).get("/sitemap.xml")
response = TestClient(app, base_url=base_url).get("/sitemap.xml")
assert response.status_code == 200
assert response.headers["content-type"].startswith("application/xml")
return ET.fromstring(response.text)
@ -39,32 +39,44 @@ def _load(path):
return json.loads(path.read_text(encoding="utf-8"))
def test_sitemap_lists_every_static_route_once_per_default_domain():
entries = _entries(_sitemap_root())
for route in _load(ROUTES_FILE):
loc = f"{BASE_FR}{route['loc']}"
assert loc in entries
assert entries[loc] == {"fr": loc, "en": f"{BASE_EN}{route['loc']}"}
def test_each_domain_sitemap_lists_its_static_routes_only():
for locale, base_url, other_base in (
("fr", BASE_FR, BASE_EN),
("en", BASE_EN, BASE_FR),
):
entries = _entries(_sitemap_root(base_url))
for route in _load(ROUTES_FILE):
loc = f"{base_url}{route['loc']}"
assert loc in entries
assert entries[loc] == {
"fr": f"{BASE_FR}{route['loc']}",
"en": f"{BASE_EN}{route['loc']}",
}
assert all(loc.startswith(base_url) for loc in entries), locale
assert all(not loc.startswith(other_base) for loc in entries), locale
def test_sitemap_lists_both_language_urls_of_every_landing():
def test_each_domain_sitemap_lists_its_landing_urls_only():
landings = _load(LANDINGS_FILE)
assert landings, "landings.json ne doit pas être vide"
entries = _entries(_sitemap_root())
for landing in landings:
fr_url = f"{BASE_FR}{landing['path']['fr']}"
en_url = f"{BASE_EN}{landing['path']['en']}"
assert fr_url in entries, f"landing {landing['key']} absente du sitemap en fr"
assert en_url in entries, f"landing {landing['key']} absente du sitemap en en"
# Chaque langue déclare la même paire d'alternates : hreflang réciproque.
assert entries[fr_url] == entries[en_url] == {"fr": fr_url, "en": en_url}
for locale, base_url in (("fr", BASE_FR), ("en", BASE_EN)):
entries = _entries(_sitemap_root(base_url))
for landing in landings:
fr_url = f"{BASE_FR}{landing['path']['fr']}"
en_url = f"{BASE_EN}{landing['path']['en']}"
loc = fr_url if locale == "fr" else en_url
other = en_url if locale == "fr" else fr_url
assert loc in entries, f"landing {landing['key']} absente du sitemap {locale}"
assert other not in entries, f"landing {landing['key']} publiée sur le mauvais domaine"
assert entries[loc] == {"fr": fr_url, "en": en_url}
def test_sitemap_landing_urls_do_not_collide_with_routes():
entries = _entries(_sitemap_root())
locs = [url.find("sm:loc", NS).text for url in _sitemap_root().findall("sm:url", NS)]
assert len(locs) == len(set(locs)), "le sitemap contient des URL en double"
expected = len(_load(ROUTES_FILE)) + 2 * len(_load(LANDINGS_FILE))
assert len(entries) == expected
expected = len(_load(ROUTES_FILE)) + len(_load(LANDINGS_FILE))
for base_url in (BASE_FR, BASE_EN):
root = _sitemap_root(base_url)
entries = _entries(root)
locs = [url.find("sm:loc", NS).text for url in root.findall("sm:url", NS)]
assert len(locs) == len(set(locs)), "le sitemap contient des URL en double"
assert len(entries) == expected