From a227fc9d0014ab7fee8ffbaae19a6b7ab28ce0a3 Mon Sep 17 00:00:00 2001 From: Thomas Fransolet Date: Wed, 16 Sep 2026 15:18:40 +0200 Subject: [PATCH] =?UTF-8?q?VR=20:=20quatre=20=C3=A9carts=20entre=20l'ongle?= =?UTF-8?q?t=20du=20manager=20et=20le=20casque?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Relevé dans le code le 2026-09-16. Même cause pour les quatre : XR-2 a réutilisé AppConfigurationLinkScreen tel quel, et cet écran sert quatre canaux qui n'ont pas les mêmes réglages. Le sélecteur bento ne pilote rien en VR (les spans sont portés par le lien de configuration, pas par les sections), la notification annonce « application mobile », la popup d'édition d'un casque est celle du kiosk avec la moitié des champs morte, et un bento VR suppose des spans par section jusque dans Unity. Plan dans v2/vr-menu-bento-plan.md, carte kanban 340. Co-Authored-By: Claude Opus 5 (1M context) --- STATUS.md | 1 + kanban.html | 23 +- ...u-vr-n-est-pas-ce-que-le-manager-laisse.md | 21 ++ v2/vr-menu-bento-plan.md | 233 ++++++++++++++++++ 4 files changed, 276 insertions(+), 2 deletions(-) create mode 100644 kanban/cards/5-planifie/340-le-menu-vr-n-est-pas-ce-que-le-manager-laisse.md create mode 100644 v2/vr-menu-bento-plan.md diff --git a/STATUS.md b/STATUS.md index 8d4eb58..2dc16a4 100644 --- a/STATUS.md +++ b/STATUS.md @@ -178,6 +178,7 @@ Dix-neuf chantiers de canaux et d'IA, chacun avec son plan détaillé. État au - ✅ **`XR-3` est clos le 2026-09-12** : le JSON d'export est un **contrat versionné** (`exportVersion: 1`, `generatedAt`, règles de compatibilité écrites sur le DTO), et la question multilingue est tranchée par le code lui-même. - ✅ **`XR-5` est clos aussi** : **pousser une configuration vers le casque sans MQTT** — la réponse au battement porte la configuration assignée, le casque compare et recharge en revenant au menu, sans redémarrage. Coût : trois minutes de latence au pire. Ce que ça évite : une connexion permanente sur une borne et une bibliothèque MQTT dans le build IL2CPP. Le publish serveur existe déjà si un jour trois minutes sont trop. Plus l'**alerte « casque muet »** après une heure sans battement — elle corrige un mensonge, la pastille « connecté » venant du dernier battement reçu. - ➡️ **Prochain travail sur le canal XR** : **jouer le §25 du [plan de test](test-plan.md)** — 11 blocs, **135 cas**. Le §25.7 (déclarer une 360 dans le manager) et le §25.9.7 (appairer une nouvelle tablette) se jouent **sans casque** ; le reste demande le Quest. **Le POC est écrit en entier** : rien de E2 à E10 n'a jamais tourné, et c'est la seule chose qui manque. +- 🔴 **Quatre écarts entre l'onglet VR du manager et le casque, relevés dans le code le 2026-09-16** — [v2/vr-menu-bento-plan.md](v2/vr-menu-bento-plan.md), carte kanban 340. Ils ont tous la même cause : `XR-2` a réutilisé `AppConfigurationLinkScreen` tel quel (« zéro code neuf »), et cet écran sert quatre canaux qui n'ont pas les mêmes réglages. **Un** : le **sélecteur de forme bento ne pilote rien en VR** — les spans sont portés par le *lien de configuration* (`AppConfigurationLink.cs:35`, « Specific Mobile & Web ») et n'alimentent que l'écran « choisis ta configuration », qui **n'existe pas dans le casque** : un casque est appairé à une seule configuration, et le menu affiche les *sections*. `ConfigurationExport` n'a aucun champ span. **Deux** : la notification annonce « application mobile » — le paramètre `appUpdatedLabel` existe et n'est pas passé, ~15 min. **Trois** : la popup d'édition d'un casque est celle du kiosk, et **la moitié de ses champs est morte en VR** (couleurs, loader, arrondis, place des sections, heure, date — `grep` sur `Assets/Scripts` : zéro occurrence de chacun). **Quatre**, qui découle du premier : pour qu'un bento ait un sens en VR il faut des **spans par section** (backend + manager), puis un placement dense mappé sur l'arc côté Unity — l'arc est conservé, seul le calcul des positions change. Ordre : notification → popup → spans → Unity ; mini-prompts en fin de plan. - **Abandonné le 2026-09-02** — talking head : son lipsync reposait sur `enable_time_pointing` de Google Cloud TTS, or le code livré tourne sur Gemini TTS. Le plan était sans fondation, pas « à faire ». → **Détail complet, la table des 19 chantiers : [status/roadmap-canaux.md](status/roadmap-canaux.md)** diff --git a/kanban.html b/kanban.html index 6eac356..57af1b4 100644 --- a/kanban.html +++ b/kanban.html @@ -462,7 +462,7 @@
1Migration v3
2Bugs ouverts
12À tester
-
40Planifié
+
41Planifié
9Bascule prod
66Fait récemment
@@ -706,7 +706,7 @@
-

Planifié

40
+

Planifié

41
@@ -1192,6 +1192,25 @@ myinfomate-landing/audit-seo-geo.md action 19 · DOCS/v2/vr-quest-unity-plan.md · DOCS/rayban-meta-integration.md
+
+
XRmanager-appmanager-serviceV2 · 4 écarts entre l'onglet VR et le casque
+

Le menu VR n'est pas ce que le manager laisse configurer

+

L'onglet Main / VR réutilise AppConfigurationLinkScreen, l'écran de configuration partagé avec le mobile, le web et le kiosk (choix assumé de XR-2 : « zéro code neuf »). Il en hérite quatre comportements qui ne correspondent à rien en VR, relevés dans le code le 2026-09-16.

+ +

🔴 Le sélecteur de forme bento ne pilote rien dans le casque. Les spans sont portés par le lien de configuration (AppConfigurationLink.cs:35, commentaire d'origine : « Specific Mobile & Web ») et ne servent qu'à l'écran « choisis ta configuration » du mobile et du web. Or un casque est appairé à une seule configuration : cet écran n'existe pas en VR. Le menu affiche les sections racines, toutes à la même taille — grep -r "Span" Assets/Scripts ne rend rien, ConfigurationExport n'a aucun champ span.

+ +

➡️ Décision : le bento VR doit porter sur les sections, pas sur les configurations. Sans ça les boutons resteront décoratifs quoi qu'on fasse. Ça demande GridColSpan/GridRowSpan sur Section (entité, DTO, migration, client API édité à la main) — l'export les transporte ensuite gratuitement — puis un placement dense par spans côté Unity. L'arc est conservé : ce n'est pas un défaut de rendu, un mur plat se regarde de biais sur ses bords. La grille est calculée en cellules, puis chaque cellule est mappée sur un angle et une hauteur. Cible 6 colonnes × 2 rangées — le confort visuel en VR c'est ±60°, ce qui donne naturellement le format paysage proche du rendu web.

+ +

🟠 La notification annonce la mauvaise app. Modifier un contenu VR affiche « application mobile mise à jour ». Le paramètre existe déjà (appUpdatedLabel, app_configuration_link_screen.dart:144) et n'est simplement pas passé par vr_screen.dart. Une clé i18n et quatre lignes — ~15 minutes.

+ +

🟠 La popup d'édition d'un casque est celle du kiosk. Largeur figée à 580, une colonne scrollée, et deux boutons dont les libellés « Annuler »/« Changer » sont en dur. Mais le vrai problème n'est pas la mise en page : la moitié des champs est morte en VR — couleur principale, couleur de fond, loader, pourcentage d'arrondis, place des sections, fond d'image, heure, date. grep -r sur Assets/Scripts : zéro occurrence de chacun. Un casque n'a ni écran de chargement 2D ni horloge d'accueil. À remplacer par une popup VR dédiée, paysage deux colonnes.

+ +

⚠️ Ce que ce chantier dit du reste : les quatre écarts ont la même cause — un écran partagé par quatre canaux qui n'ont pas les mêmes réglages. Le plan recommande d'en sortir pour la VR plutôt que d'y ajouter un quatrième cas particulier.

+ +

Ordre : la notification (indépendante, 15 min) → la popup (indépendante) → les spans backend + manager → le rendu Unity (rien de visible avant). Quatre mini-prompts de lancement sont en fin de plan.

+ v2/vr-menu-bento-plan.md · relevé dans le code le 2026-09-16 +
+
diff --git a/kanban/cards/5-planifie/340-le-menu-vr-n-est-pas-ce-que-le-manager-laisse.md b/kanban/cards/5-planifie/340-le-menu-vr-n-est-pas-ce-que-le-manager-laisse.md new file mode 100644 index 0000000..45d025c --- /dev/null +++ b/kanban/cards/5-planifie/340-le-menu-vr-n-est-pas-ce-que-le-manager-laisse.md @@ -0,0 +1,21 @@ +--- +title: Le menu VR n'est pas ce que le manager laisse configurer +area: doc +horizon: v2 +tags: XR, manager-app, manager-service +flag: warn | V2 · 4 écarts entre l'onglet VR et le casque +src: v2/vr-menu-bento-plan.md · relevé dans le code le 2026-09-16 +--- +

L'onglet Main / VR réutilise AppConfigurationLinkScreen, l'écran de configuration partagé avec le mobile, le web et le kiosk (choix assumé de XR-2 : « zéro code neuf »). Il en hérite quatre comportements qui ne correspondent à rien en VR, relevés dans le code le 2026-09-16.

+ +

🔴 Le sélecteur de forme bento ne pilote rien dans le casque. Les spans sont portés par le lien de configuration (AppConfigurationLink.cs:35, commentaire d'origine : « Specific Mobile & Web ») et ne servent qu'à l'écran « choisis ta configuration » du mobile et du web. Or un casque est appairé à une seule configuration : cet écran n'existe pas en VR. Le menu affiche les sections racines, toutes à la même taille — grep -r "Span" Assets/Scripts ne rend rien, ConfigurationExport n'a aucun champ span.

+ +

➡️ Décision : le bento VR doit porter sur les sections, pas sur les configurations. Sans ça les boutons resteront décoratifs quoi qu'on fasse. Ça demande GridColSpan/GridRowSpan sur Section (entité, DTO, migration, client API édité à la main) — l'export les transporte ensuite gratuitement — puis un placement dense par spans côté Unity. L'arc est conservé : ce n'est pas un défaut de rendu, un mur plat se regarde de biais sur ses bords. La grille est calculée en cellules, puis chaque cellule est mappée sur un angle et une hauteur. Cible 6 colonnes × 2 rangées — le confort visuel en VR c'est ±60°, ce qui donne naturellement le format paysage proche du rendu web.

+ +

🟠 La notification annonce la mauvaise app. Modifier un contenu VR affiche « application mobile mise à jour ». Le paramètre existe déjà (appUpdatedLabel, app_configuration_link_screen.dart:144) et n'est simplement pas passé par vr_screen.dart. Une clé i18n et quatre lignes — ~15 minutes.

+ +

🟠 La popup d'édition d'un casque est celle du kiosk. Largeur figée à 580, une colonne scrollée, et deux boutons dont les libellés « Annuler »/« Changer » sont en dur. Mais le vrai problème n'est pas la mise en page : la moitié des champs est morte en VR — couleur principale, couleur de fond, loader, pourcentage d'arrondis, place des sections, fond d'image, heure, date. grep -r sur Assets/Scripts : zéro occurrence de chacun. Un casque n'a ni écran de chargement 2D ni horloge d'accueil. À remplacer par une popup VR dédiée, paysage deux colonnes.

+ +

⚠️ Ce que ce chantier dit du reste : les quatre écarts ont la même cause — un écran partagé par quatre canaux qui n'ont pas les mêmes réglages. Le plan recommande d'en sortir pour la VR plutôt que d'y ajouter un quatrième cas particulier.

+ +

Ordre : la notification (indépendante, 15 min) → la popup (indépendante) → les spans backend + manager → le rendu Unity (rien de visible avant). Quatre mini-prompts de lancement sont en fin de plan.

diff --git a/v2/vr-menu-bento-plan.md b/v2/vr-menu-bento-plan.md new file mode 100644 index 0000000..9837ae6 --- /dev/null +++ b/v2/vr-menu-bento-plan.md @@ -0,0 +1,233 @@ +# Menu VR — bento, et le reste de l'onglet VR + +> Chantier **V2**, analysé dans le code le **2026-09-16**. +> Suite de `vr-quest-unity-plan.md` (lot XR-4, item E5 — le menu flottant existe et tourne). +> Ce document ne rouvre pas les décisions du plan VR : il traite **quatre écarts** entre ce +> que le manager laisse configurer et ce que le casque affiche réellement. + +--- + +## Le constat + +L'onglet **Main / VR** du manager ([`vr_screen.dart`](../../manager-app/lib/Screens/Vr_devices/vr_screen.dart)) +réutilise `AppConfigurationLinkScreen`, l'écran de configuration partagé avec le mobile, le web +et le kiosk. Il hérite donc de tout son outillage — dont un **sélecteur de forme bento** +(les quatre petits boutons carré / large / haut / grand) qui, côté casque, ne pilote rien. + +Vérifié : + +| Fait | Preuve | +|---|---| +| Les spans sont portés par le **lien de configuration**, pas par les sections | `AppConfigurationLink.cs:35` — commentaire d'origine : *« Specific Mobile & Web »* | +| Ils ne pilotent que l'écran « choisis ta configuration » | `mymuseum-visitapp/lib/Screens/Home/home_3.0.dart:1179`, `visitapp-web/src/components/ConfigurationGrid.tsx:38` | +| Le casque ne les lit jamais | `ConfigurationExport.cs` n'a aucun champ span ; `grep -r "Span" Assets/Scripts` → rien | +| Un casque est appairé à **une seule** configuration | `MenuBootstrap.cs:80` — `pairing.ConfigurationId`. Il n'y a pas d'écran de choix de configuration en VR | +| Le menu VR affiche les **sections racines**, toutes à la même taille | `FloatingMenu.cs:106-125` — arc de 5 panneaux par rangée, `0,50 × 0,34 m` fixes | + +Autrement dit : en VR, le bento du manager porte sur un objet (la configuration) qui n'a pas +d'écran de sélection, alors que l'objet qui *a* un écran (la section) n'a pas de spans. + +Deux autres écarts, plus petits, de la même famille « écran partagé, canal différent » : +la notification et la popup d'édition d'un casque. + +--- + +## Décisions actées + +| Décision | Raison | +|---|---| +| **Le bento VR porte sur les sections**, pas sur les configurations | C'est le seul écran de choix qui existe dans le casque. Sans ça, les boutons resteront décoratifs quoi qu'on fasse | +| **L'arc est conservé** | Ce n'est pas un défaut de rendu : un mur plat oblige à regarder ses bords de biais. La grille bento est calculée en cellules, puis chaque cellule est *mappée* sur un angle + une hauteur | +| **Grille cible : 6 colonnes × 2 rangées max** | Le confort visuel en VR c'est ±60° horizontaux ; au-delà on encercle le visiteur. Format naturellement paysage, proche du rendu web | +| **Une popup casque distincte de celle du kiosk** | La moitié des champs de la popup partagée est morte en VR (voir lot 4) — les masquer un par un dans un widget commun coûte plus cher que d'en écrire un | +| Les spans de section **ne sont pas exposés au mobile/web en V1** | Ils ont déjà leur bento, au niveau configuration. Ouvrir un second niveau de grille sur ces fronts est un chantier produit à part | + +--- + +## Lot 1 — La notification annonce la mauvaise app + +**~15 minutes, indépendant des autres.** + +Modifier un contenu VR affiche « application mobile mise à jour ». Le paramètre prévu pour ça +existe déjà et n'est simplement pas renseigné : + +``` +app_configuration_link_screen.dart:144 + final appUpdatedMsg = widget.appUpdatedLabel ?? AppLocalizations.of(context)!.appUpdatedSuccess; + ↑ jamais passé par vr_screen.dart +``` + +À faire : + +1. Clé `vrAppUpdatedSuccess` dans `app_fr.arb` (template), puis `app_en.arb` et `app_nl.arb`. +2. `vr_screen.dart:38` — passer `appUpdatedLabel: l.vrAppUpdatedSuccess` à `AppConfigurationLinkScreen`. +3. `vr_screen.dart:78` (`_backgroundCard`) — même remplacement, c'est un `showNotification` séparé. +4. `vr_devices_tab.dart:280` (`_edit`) — idem. + +⚠️ Le template i18n est `app_fr.arb` ; les trois langues sont obligatoires (cf. `manager-app/CLAUDE.md`). + +--- + +## Lot 2 — Des spans par section (backend + manager) + +**Le socle du lot 3. Rien de visible dans le casque avant le lot 3.** + +### Backend + +``` +Data/Section.cs + int? GridColSpan, int? GridRowSpan +DTOs/SectionDTO.cs + gridColSpan, gridRowSpan (après `order`, même famille) + → mapping dans ToDTO / FromDTO des champs communs +Migrations/ AddSectionGridSpans +``` + +L'export est **gratuit** côté transport : `/api/configuration/{id}/export` +(`ConfigurationController.cs:437`) sérialise déjà les `SectionDTO` complets via +`SectionFactory.ToDTO`. Les nouveaux champs suivent. + +⚠️ `Section.ToDTO()` n'est pas virtuelle — vérifier que la copie des champs communs se fait +bien au même endroit que `order`, sinon les spans sortiront nuls sur les sous-types. +⚠️ `dotnet ef` ignore `appsettings.Development.json` : passer par `MIGRATIONS_CONNECTION`. + +### Client API + +`manager-app/manager_api_new/` — **éditer les fichiers générés à la main**, ne jamais relancer +la génération OpenAPI (règle projet). + +### Manager + +L'onglet « Contenu de l'application VR » ne doit plus lister des *configurations* avec des +spans, mais les *sections racines* de la configuration, avec leurs spans et un aperçu. + +Deux options, à trancher en début de conversation : + +- **a)** Un écran VR dédié, qui reprend le sélecteur de forme et l'aperçu live de + `AppConfigurationLinkScreen` mais s'alimente en sections. Plus propre, plus de code. +- **b)** Paramétrer `AppConfigurationLinkScreen` pour qu'il accepte une source « sections ». + Moins de code, mais l'écran sert déjà quatre canaux — il est le point de contact de tous + ces écarts, l'élargir encore le rend plus difficile à corriger. + +Recommandation : **(a)**, c'est précisément l'accumulation de cas particuliers dans cet écran +partagé qui produit les trois autres lots de ce document. + +À réutiliser tel quel : le debounce de 400 ms sur l'enregistrement des spans +(`app_configuration_link_screen.dart:89-98`) — un slider de forme qui écrit à chaque frame +sature l'API. + +--- + +## Lot 3 — Le rendu bento dans le casque (Unity) + +### Ce qui change + +`FloatingMenu.Place(panel, index, total)` positionne aujourd'hui par index brut : +`row = index / 5`, angle = pas fixe. Il faut passer par une grille. + +``` +1. Placement dense par spans → chaque section obtient (col, row, colSpan, rowSpan) +2. Mapping cellule → arc → angle = (col + colSpan/2 - cols/2) * StepDegrees + hauteur = HeightMeters - row * (cellH + gap) +3. Taille du quad → width = colSpan * cellW + (colSpan-1) * gap + height = rowSpan * cellH + (rowSpan-1) * gap +``` + +L'algorithme de placement dense existe en Dart dans `manager-app/myinfomate_layout/` +(le même que le mobile). Deux voies : + +- **Le porter en C#** (~150 lignes) — le casque reste autonome, cohérent avec le cache-d'abord. +- **Le faire calculer par le backend** dans l'export — une seule implémentation, mais le + casque devient dépendant d'un champ calculé, et le cache d'un vieux contenu porte un vieux + placement. + +Recommandation : **le porter en C#**, l'export reste une description, pas une mise en page. + +### Fichiers + +``` +Assets/Scripts/Net/ConfigurationExport.cs + GridColSpan / GridRowSpan sur SectionSummary +Assets/Scripts/Menu/FloatingMenu.cs Place() → grille ; MaxPerRow 5 → 6 +Assets/Scripts/Menu/MenuItemPanel.cs Create() prend une taille au lieu des const +Assets/Scripts/Menu/BentoLayout.cs (neuf) le placement dense +``` + +⚠️ `MenuItemPanel.WidthMeters` / `HeightMeters` sont des `const` **publiques**, lues ailleurs +(`FloatingMenu.Place`, et à vérifier dans `PagedView` / `SectionPages`). Les rendre variables +d'instance sans casser ces usages — garder les const comme valeurs de cellule par défaut est +le chemin le plus court. + +⚠️ La cascade d'apparition (`PlayAppear(i * 0.04f)`) doit suivre l'ordre de **lecture** de la +grille (rangée par rangée, gauche à droite), pas l'index de la liste : avec des spans, les +deux divergent. + +⚠️ Un panneau `colSpan = 2` couvre ~32° d'arc. Un quad plat reste acceptable à cette ouverture ; +au-delà (un hypothétique span 3) il faudrait le subdiviser. Ne pas le faire avant d'en avoir besoin. + +### Vérification + +Sans casque, le viewer web (`vr-app/viewer/`) est le chemin court pour valider un placement. +Avec casque : `adb push` d'un `scene.json`, l'app le préfère à celui embarqué. + +--- + +## Lot 4 — La popup d'édition d'un casque + +**Indépendant. ~une demi-journée.** + +`showChangeInfo` ([`Kiosk_devices/change_device_info_modal.dart:16`](../../manager-app/lib/Screens/Kiosk_devices/change_device_info_modal.dart)) +est partagée avec le kiosk : largeur figée à 580, une colonne scrollée, et deux boutons +`RoundedButton` dont les libellés **« Annuler » / « Changer » sont en dur** (violation i18n). + +Le vrai problème n'est pas la mise en page : **la moitié des champs ne sert à rien en VR.** +Aucun script Unity ne lit ces réglages — `grep -r` sur `Assets/Scripts` : zéro occurrence. + +| Champ | En VR | +|---|---| +| `name`, `configurationId` | **gardés** | +| `primaryColor`, `secondaryColor` | morts — le menu a sa propre palette de panneaux | +| `loaderImageId` | mort — un casque n'a pas d'écran de chargement 2D | +| `screenPercentageSectionsMainPage`, `roundedValue` | morts — ce sont des réglages d'écran plat | +| `isSectionImageBackground`, `isHour`, `isDate` | morts | + +À faire : `Vr_devices/change_headset_modal.dart`, paysage deux colonnes (~940 px), avec +identité + configuration à gauche, fond immersif à droite (il vit aujourd'hui dans le panneau +latéral de l'onglet contenu — à décider : déplacé ou dupliqué). Boutons repris du langage +visuel de la Médiathèque / du Studio IA plutôt que des `RoundedButton` historiques. + +Tout le texte passe par `AppLocalizations`, y compris les deux boutons. + +--- + +## Ordre + +``` +Lot 1 ────────────────────────────── indépendant, à faire en premier (15 min) +Lot 4 ────────────────────────────── indépendant +Lot 2 ───→ Lot 3 le 3 n'affiche rien sans le 2 +``` + +--- + +## Mini-prompts de lancement + +Un lot = une conversation. Chaque prompt suppose que ce fichier est lisible. + +**Lot 1** +> Lis `DOCS/v2/vr-menu-bento-plan.md` § Lot 1 et applique-le : la notification de l'onglet VR +> du manager annonce « application mobile ». Ajoute la clé i18n dans les trois langues et passe +> `appUpdatedLabel` aux quatre endroits listés. + +**Lot 2** +> Lis `DOCS/v2/vr-menu-bento-plan.md` § Lot 2. Ajoute `gridColSpan` / `gridRowSpan` sur +> `Section` côté `manager-service` (entité, DTO, migration, client API édité à la main), puis +> fais que l'onglet « Contenu de l'application VR » du manager édite les spans **des sections** +> et non des configurations. Commence par me dire si tu pars sur l'option (a) ou (b) du plan. + +**Lot 3** +> Lis `DOCS/v2/vr-menu-bento-plan.md` § Lot 3. Dans `vr-app/unity`, fais que le menu flottant +> place ses panneaux sur une grille bento par spans mappée sur l'arc, au lieu de l'index brut +> actuel. Le lot 2 doit être fait — vérifie que l'export porte bien les spans avant de commencer. + +**Lot 4** +> Lis `DOCS/v2/vr-menu-bento-plan.md` § Lot 4. Écris une popup d'édition de casque dédiée en +> paysage deux colonnes, sans les champs morts en VR, avec les boutons du langage visuel de la +> Médiathèque, et tout le texte en i18n FR/EN/NL.