Correction : tablet-app a bien un i18n, et V2 gagne l'orientation

Correction d'une affirmation fausse écrite plus tôt dans la journée et
reprise dans la maquette, le plan et une décision validée : « tablet-app
n'a aucun i18n ». Il en a un — lib/Helpers/translations.dart, table de 10
langues (FR EN NL DE IT ES PL CN AR UK), ~150 entrées, lue par
TranslationHelper.getFromLocale à 19 endroits, avec déjà une clé back.
Ce n'est pas de l'ARB : chercher l10n/ ou AppLocalizations ne le trouve
pas, alors que le CLAUDE.md du repo le décrit.

Ce que ça change : le motif donné pour « hors dates, aucune prose » — un
bandeau serait français pour un néerlandophone — était faux. Un libellé
traduit coûte une clé × 10 langues. La règle reste défendable (rien à
traduire vaut mieux que quelque chose à traduire) mais devient un choix,
pas une contrainte. À reconfirmer.

Corollaire d'implémentation : le bouton Retour des deux écrans passe par
la clé back existante, il ne s'écrit pas en dur.

Ajouté en V2 — portrait et paysage sur toute la borne. tablet-app n'a
aucune stratégie d'orientation : chaque écran est écrit pour la forme
qu'on lui a vue, et la maquette du 12/08 ne tranche que le paysage, pour
deux types. Une borne se monte pourtant aussi bien en totem qu'en
comptoir, et le choix appartient au lieu. Le chantier n'est pas
« ajouter le portrait » mais rendre les treize types indifférents à
l'orientation — repasse UI transversale, même famille que la repasse
globale de manager-app. La maquette est compatible (le rail qui tombe
sous le texte est le comportement portrait) mais ne le spécifie pas.

Kanban : carte V2 ajoutée, Planifié 21 -> 22 (colonne et bandeau). Le
motif « et tablet-app ne compile même plus » de la carte Kiosk web est
corrigé — il est tombé avec K1.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Thomas Fransolet 2026-08-12 14:49:52 +02:00
parent c2bda75885
commit 4a4523e3a2
4 changed files with 31 additions and 13 deletions

View File

@ -120,7 +120,8 @@ Tableau de chantiers global : https://claude.ai/code/artifact/4c317904-2e23-45d3
| **SectionForm** — formulaires personnalisés (feedback, satisfaction, inscription atelier) | Nouveau type de section + table de réponses : purement additif, aucun impact sur le schéma existant |
| **Ressource 360°** — images panoramiques (`ResourceType.Panorama360` + package `panorama`) | Une valeur d'enum et un branchement dans l'affichage des ressources ; se greffe sans toucher aux sections |
| **AR image tracking (Mind AR)** — modèle `ArAnchor`, microservice Node de compilation `.mind`, WebView scanner unifiée QR + image | Le plus gros des trois : nouveau service Docker, nouveau modèle, refonte du scanner visiteur. Fort effet démo, zéro urgence |
| **Kiosk web** — version Flutter Web de `tablet-app` | Aucun client ne le demande, et `tablet-app` ne compile pas aujourd'hui : il faudrait d'abord réparer le build natif pour en dériver une cible web. ⚠️ **Ne pas confondre avec le visiteur web** (`visitapp-web`), lui déjà écrit et attendu en V1 |
| **Kiosk web** — version Flutter Web de `tablet-app` | Aucun client ne le demande. ⚠️ **Ne pas confondre avec le visiteur web** (`visitapp-web`), lui déjà écrit et attendu en V1. *Mise à jour du 2026-08-12 : l'argument « et tablet-app ne compile pas » est tombé, l'APK est produit depuis K1.* |
| **Portrait et paysage sur toute la borne** — ajouté le 2026-08-12 | `tablet-app` n'a **aucune stratégie d'orientation** : les écrans sont écrits pour la forme qu'on leur a vue, et la maquette K3/K7 tranche le paysage pour deux types seulement. Or une borne se monte aussi bien en portrait (totem d'accueil) qu'en paysage (comptoir), et le choix appartient au lieu, pas à l'app. Le chantier n'est donc pas « ajouter le portrait » mais **rendre les treize types indifférents à l'orientation** — un jeu de points de rupture et une règle unique du type « la colonne de texte reste plafonnée, le rail passe dessous quand la largeur manque ». C'est une repasse UI transversale, pas un correctif : hors V1 pour la même raison que la repasse UI globale de `manager-app` (DB1). ⚠️ **La maquette du 12/08 est compatible** — le rail qui tombe sous le texte est exactement le comportement portrait — mais elle ne l'a pas spécifié |
**Prochaine chose à faire si tu as 1h** : le **§21 du plan de test** — vérifier sur un device l'état réel de la visite hors ligne. C'est peut-être déjà remonté comme « l'appli marche mal dans le fort » sans que la cause ait été identifiée, et ça conditionne l'urgence des points 10-13.

View File

@ -951,15 +951,23 @@
</div>
<div class="ruleitem">
<h3>Et c'est l'i18n qui tranche</h3>
<h3>L'i18n existe — corrigé le 12/08</h3>
<p>
Ton réflexe sur les traductions était le bon, et il est plus contraignant que prévu :
<strong><code>tablet-app</code> n'a aucun système d'i18n</strong> — pas de <code>l10n/</code>,
pas d'<code>AppLocalizations</code>, les libellés sont en français en dur
(<code>"Ce type n'est pas supporté"</code>). Un bandeau « Édition passée » serait donc
<strong>français pour un visiteur néerlandophone</strong>. Le vrai libellé viendrait soit
d'un champ backend traduit — qui n'existe pas — soit d'un i18n à créer, ce qui n'est pas un
chantier de K3. D'où la règle ci-dessus : on affiche des dates, pas des phrases.
<strong>Une affirmation de cette page était fausse et a été retirée</strong> :
« <code>tablet-app</code> n'a aucun i18n ». Il en a un —
<code>lib/Helpers/translations.dart</code>, une table de <strong>10 langues</strong>
(FR, EN, NL, DE, IT, ES, PL, CN, AR, UK) lue par
<code>TranslationHelper.getFromLocale</code> à <strong>19 endroits</strong>, avec déjà une
clé <code>back</code>. Ce n'est pas de l'ARB, c'est une table à la main — le même pattern que
mymuseum. Chercher <code>l10n/</code> et <code>AppLocalizations</code> ne le trouve pas.
</p>
<p style="margin-top: var(--s2);">
Ce que ça change : un libellé traduit coûte <strong>une clé × 10 langues</strong>, pas un
chantier. La règle « aucune prose » ci-dessus reste défendable — les dates suffisent, et
rien à traduire vaut mieux que quelque chose à traduire — mais elle devient un
<strong>choix</strong> et non une contrainte. <strong>À confirmer.</strong> Et le bouton
« Retour » des maquettes doit passer par la clé <code>back</code> existante, pas être écrit
en dur.
</p>
</div>

View File

@ -458,7 +458,7 @@
<div class="stat"><span class="n n-info">2</span><span class="k">Migration v3</span></div>
<div class="stat"><span class="n n-warn">5</span><span class="k">Bugs ouverts</span></div>
<div class="stat"><span class="n">6</span><span class="k">À tester</span></div>
<div class="stat"><span class="n">21</span><span class="k">Planifié</span></div>
<div class="stat"><span class="n">22</span><span class="k">Planifié</span></div>
<div class="stat"><span class="n n-gate">8</span><span class="k">Bascule prod</span></div>
<div class="stat"><span class="n n-good">40</span><span class="k">Fait récemment</span></div>
</section>
@ -626,7 +626,7 @@
<!-- PLANIFIÉ -->
<section class="col" style="--stripe: var(--ink-3)">
<div class="col-head"><h2>Planifié</h2><span class="count">21</span></div>
<div class="col-head"><h2>Planifié</h2><span class="count">22</span></div>
<article class="card" data-area="manager" data-horizon="v1">
<div class="card-meta"><span class="tag">tablet-app</span><span class="flag f-warn">Lot K · K8 — nouveau 12/08</span></div>
@ -732,10 +732,19 @@
<article class="card" data-area="visitapp manager" data-horizon="v2">
<div class="card-meta"><span class="tag">tablet-app</span><span class="flag f-warn">V2</span></div>
<h3>Kiosk web (Flutter Web)</h3>
<p>Extension web de <code>tablet-app</code> pour les clients ne voulant pas d'installation native. <strong>Reporté en V2 le 07/08</strong> : aucun client demandeur, et la cible native ne compile même plus — il faudrait réparer ce build avant d'en dériver une version web.</p>
<p>Extension web de <code>tablet-app</code> pour les clients ne voulant pas d'installation native. <strong>Reporté en V2 le 07/08</strong> : aucun client demandeur. ⚠️ <em>Le second motif est tombé le 12/08</em> — « la cible native ne compile même plus » n'est plus vrai, l'APK est produit depuis K1.</p>
<span class="src">architecture-web-saas.md — Kiosk web</span>
</article>
<article class="card" data-area="visitapp" data-horizon="v2">
<div class="card-meta"><span class="tag">tablet-app</span><span class="flag f-warn">V2 — nouveau 12/08</span></div>
<h3>Portrait et paysage sur toute la borne</h3>
<p><code>tablet-app</code> n'a <strong>aucune stratégie d'orientation</strong> : chaque écran est écrit pour la forme qu'on lui a vue. Or une borne se monte aussi bien en <strong>portrait</strong> (totem d'accueil) qu'en <strong>paysage</strong> (comptoir), et le choix appartient au lieu, pas à l'app.</p>
<p>Le chantier n'est donc pas « ajouter le portrait » mais <strong>rendre les treize types indifférents à l'orientation</strong> — des points de rupture et une règle unique du type « la colonne de texte reste plafonnée, le rail passe dessous quand la largeur manque ». Repasse UI transversale, hors V1 pour la même raison que la repasse globale de <code>manager-app</code> (DB1).</p>
<p>⚠️ <strong>La maquette K3/K7 du 12/08 est compatible</strong> — le rail qui tombe sous le texte <em>est</em> le comportement portrait — mais elle ne l'a pas spécifié : elle tranche le paysage, pour deux types.</p>
<span class="src">STATUS.md §1ter V2 · maquette kiosk-paysage</span>
</article>
<article class="card" data-area="backend manager visitapp" data-horizon="v2">
<div class="card-meta"><span class="tag">produit</span><span class="flag f-warn">V2</span></div>
<h3>SectionForm — formulaires personnalisés</h3>

View File

@ -284,7 +284,7 @@ Ordre du §2 de STATUS.md, corrigé par **L12**.
| ~~**K4**~~**2026-08-12** | **Écran Game repris de mymuseum.** Les 6 fichiers de `Screens/Sections/Game/` sont dans `tablet-app/lib/Screens/Game/`, contexte adapté ; `lib/Screens/Puzzle/` supprimé ; `main_view` bascule sur `SectionType.Game` / `GameDTO` / `GamePage`. **Gains** : le puzzle glissant (mélange par mouvements valides, donc toujours soluble), le bouton d'indice, et un dimensionnement qui respecte le ratio de l'image — 508 lignes contre 237 à l'ancien `puzzle_view`.<br>⚠️ **Une décision de rendu à valider à l'œil** : mymuseum code ses couleurs **en dur par flavor client** (`kMainColor0/1/2` — rouge MDLF, bleu Fort). `tablet-app` n'a pas de flavor : un seul APK sert tous les clients et la charte vient de la configuration choisie au pincode. Recopier ces constantes aurait affiché du bleu chez MDLF. Le dégradé et le bouton d'indice sont donc **dérivés de `configuration.primaryColor`** (même parsing que `audio_player`/`loading_common`, repli `kTestSecondColor`, 3 nuances par `Color.lerp`). Plus juste sur le fond, mais **le rendu diffère de la version mobile** : si le dégradé dessiné à la main est préféré, figer les trois couleurs |
| ~~K4 — énoncé d'origine~~ | **`SectionPuzzle``SectionGame` + le type glissant.** Le serveur a renommé et porte `GameTypes { Puzzle, SlidingPuzzle }` ; `tablet-app` a encore un écran `Puzzle/` qui ne connaît que l'ancien. **Reprendre le code de `mymuseum-visitapp/lib/Screens/Sections/Game/`**`game_page.dart` et `sliding_puzzle_piece.dart` existent déjà là-bas, et cette version est **moins buguée** que celle de tablet-app | Récupération, pas réécriture |
| ~~**K5**~~**2026-08-12** | **Les trois flavors de `mymuseum-visitapp` construisent** (`dev`, `mdlf`, `fortsaintheribert`), exit 0, APK à l'appui. **D0 est débloqué.**<br>**L'énoncé était faux : il n'y avait pas de « même rattrapage » à faire.** Sa config Android était **déjà à niveau** — Gradle 8.11.1, AGP 8.9.0, `-Xmx4096M`, `enableJetifier=false`, aucun forçage d'`androidx.lifecycle`. Les dix crans de K1 ont amené **`tablet-app` jusqu'à `mymuseum`** ; le lot supposait la symétrie du problème, elle n'existait pas. Le réflexe du diff annoncé en K1 a donné la réponse en deux `cat`.<br>**Ce qui restait, et qui est fait** : les deux avertissements du build. NDK **27.0.12077973 → 28.2.13676358** (`speech_to_text` le réclame nommément) et Kotlin **2.1.0 → 2.3.10** (seuil 2.2.20 annoncé comme rupture ; c'est la version de `tablet-app`, donc un alignement). Six builds — trois avant, trois après. APK de **382 à 349 Mo**.<br>⚠️ **Un cran de plus existe, délibérément non pris** : Kotlin réglé, Flutter réclame Gradle 8.14.0 et AGP 8.11.1. C'est du **terrain neuf pour les deux repos** (`tablet-app` est à 8.11.1/8.9.1, mêmes avertissements latents) — le faire ici seul **désaligne** les deux apps. À faire d'un bloc sur les deux, ou pas du tout : c'est la dette d'outillage déjà parquée en V2 | Son code était déjà porté sur le nouveau contrat — **c'est ce qui aurait dû mettre la puce à l'oreille** : le repo qui a servi de modèle au portage K2 n'avait pas de raison d'être en retard d'outillage sur celui qui le copiait |
| **K7** | **`SectionArticle` sur le kiosk — ajouté le 2026-08-12**, en recomptant les types couverts. Son `case` est commenté « TODO » dans `main_view.getContent` et `Screens/Article/article_view.dart` fait 1 Ko : un article rend « Ce type n'est pas supporté ». **Contrairement à `SectionEvent` et `SectionParcours`, ce type existait dans Mongo** — c'est donc un vrai retard, pas un type né avec Postgres v3, et il sortira de la migration avec du contenu réel.<br>**Le code est une reprise de `mymuseum-visitapp/lib/Screens/Sections/Article/`**, pas une conception. **Le travail n'est pas là** : il est dans le **passage en paysage**. Les écrans de mymuseum sont pensés pour un téléphone tenu debout ; une borne est large, et un article rendu en colonne unique sur 1280 px de large est illisible. C'est le seul endroit du lot K où il y a une décision de mise en page à prendre, pas une copie à faire.<br>**Maquette faite et validée le 2026-08-12**`DOCS/claude design/kiosk-paysage-article-event.html`, une seule passe pour K3 et K7. Six règles communes (deux colonnes ancrage/flux, texte à 65 caractères, héros à 26 % au lieu de 52 %, plus de `showModalBottomSheet`, carte sans « Agrandir », accents dérivés de `configuration.primaryColor` selon le précédent K4) et quatre décisions tranchées : **fond clair** aux valeurs réelles de l'app (`kBackgroundColor` / `kBackgroundLight`), **pas de sélecteur de langue** (choisie en amont), **hors dates : aucune prose**, et le **retour automatique** sorti du périmètre (ligne suivante).<br>⚠️ **Deux vérifications faites dans le code plutôt que supposées.** Les **statistiques sont déjà acquises** : `section_page_detail.dart:60/72` émet `sectionView` et `sectionLeave` avec durée, et il enveloppe les treize types — l'article sera compté dès que son `case` entre dans le `switch`, sans une ligne à écrire. Et **`tablet-app` n'a aucun i18n** : pas de `l10n/`, pas d'`AppLocalizations`, libellés français en dur. C'est ce qui interdit un bandeau « Édition passée » — il serait français pour un visiteur néerlandophone — et c'est pourquoi l'état hors dates s'exprime par les **dates**, numériques donc sans traduction | **À traiter avec K3** : `SectionEvent` pose exactement la même question de paysage (héros, programme, carte), et la trancher deux fois donnerait deux mises en page différentes sur la même borne. Une seule passe de design pour les deux, même raisonnement d'économie que **L5** et **L15**.<br>**Trois états incomplets couverts par la maquette** : sans audio (le dock tombe, l'image reprend la hauteur), sans photo (le rail tombe, l'audio passe en tête de la colonne de lecture), texte seul (une colonne centrée, toujours plafonnée). ⚠️ **`audioIds` est une `List<TranslationDTO>`** : « sans audio » est un état **du visiteur**, pas de l'article. Règle actée le 2026-08-12 — **pas d'entrée pour la langue choisie, pas de lecteur du tout** : ni lecteur grisé, ni repli sur une autre langue. Et **`isContentTop` existe déjà** ; en paysage il n'y a plus de « dessus », donc **le drapeau devient le côté** : `isContentTop == true` → texte à gauche et rail média à droite, sinon média à gauche. Le rail change de bord, pas de contenu. À câbler sous peine de rendre sans effet un réglage déjà offert dans `manager-app` |
| **K7** | **`SectionArticle` sur le kiosk — ajouté le 2026-08-12**, en recomptant les types couverts. Son `case` est commenté « TODO » dans `main_view.getContent` et `Screens/Article/article_view.dart` fait 1 Ko : un article rend « Ce type n'est pas supporté ». **Contrairement à `SectionEvent` et `SectionParcours`, ce type existait dans Mongo** — c'est donc un vrai retard, pas un type né avec Postgres v3, et il sortira de la migration avec du contenu réel.<br>**Le code est une reprise de `mymuseum-visitapp/lib/Screens/Sections/Article/`**, pas une conception. **Le travail n'est pas là** : il est dans le **passage en paysage**. Les écrans de mymuseum sont pensés pour un téléphone tenu debout ; une borne est large, et un article rendu en colonne unique sur 1280 px de large est illisible. C'est le seul endroit du lot K où il y a une décision de mise en page à prendre, pas une copie à faire.<br>**Maquette faite et validée le 2026-08-12**`DOCS/claude design/kiosk-paysage-article-event.html`, une seule passe pour K3 et K7. Six règles communes (deux colonnes ancrage/flux, texte à 65 caractères, héros à 26 % au lieu de 52 %, plus de `showModalBottomSheet`, carte sans « Agrandir », accents dérivés de `configuration.primaryColor` selon le précédent K4) et quatre décisions tranchées : **fond clair** aux valeurs réelles de l'app (`kBackgroundColor` / `kBackgroundLight`), **pas de sélecteur de langue** (choisie en amont), **hors dates : aucune prose**, et le **retour automatique** sorti du périmètre (ligne suivante).<br>⚠️ **Deux vérifications faites dans le code plutôt que supposées.** Les **statistiques sont déjà acquises** : `section_page_detail.dart:60/72` émet `sectionView` et `sectionLeave` avec durée, et il enveloppe les treize types — l'article sera compté dès que son `case` entre dans le `switch`, sans une ligne à écrire. **Et une erreur à ne pas rouvrir : j'avais écrit que `tablet-app` n'a aucun i18n. C'est faux.** `lib/Helpers/translations.dart` porte une table de **10 langues** (FR, EN, NL, DE, IT, ES, PL, CN, AR, UK), ~150 entrées, lue par `TranslationHelper.getFromLocale` à **19 endroits**, avec déjà une clé `back`. Ce n'est pas de l'ARB : chercher `l10n/` ou `AppLocalizations` ne le trouve pas — c'est une table à la main, le même pattern que mymuseum, décrit dans le `CLAUDE.md` du repo. **Conséquence** : un libellé traduit coûte une clé × 10 langues, pas un chantier. La règle « hors dates, aucune prose » reste défendable — rien à traduire vaut mieux que quelque chose à traduire — mais c'est désormais un **choix, pas une contrainte**, et le motif d'origine était faux. ⚠️ Corollaire pour l'implémentation : le bouton « Retour » passe par la clé `back`, il ne s'écrit pas en dur | **À traiter avec K3** : `SectionEvent` pose exactement la même question de paysage (héros, programme, carte), et la trancher deux fois donnerait deux mises en page différentes sur la même borne. Une seule passe de design pour les deux, même raisonnement d'économie que **L5** et **L15**.<br>**Trois états incomplets couverts par la maquette** : sans audio (le dock tombe, l'image reprend la hauteur), sans photo (le rail tombe, l'audio passe en tête de la colonne de lecture), texte seul (une colonne centrée, toujours plafonnée). ⚠️ **`audioIds` est une `List<TranslationDTO>`** : « sans audio » est un état **du visiteur**, pas de l'article. Règle actée le 2026-08-12 — **pas d'entrée pour la langue choisie, pas de lecteur du tout** : ni lecteur grisé, ni repli sur une autre langue. Et **`isContentTop` existe déjà** ; en paysage il n'y a plus de « dessus », donc **le drapeau devient le côté** : `isContentTop == true` → texte à gauche et rail média à droite, sinon média à gauche. Le rail change de bord, pas de contenu. À câbler sous peine de rendre sans effet un réglage déjà offert dans `manager-app` |
| **K8** | **Retour automatique à l'accueil après inactivité — ajouté le 2026-08-12**, sorti de la maquette K3/K7. Un visiteur part sans fermer l'écran ; le suivant trouve l'article du précédent. Délai proposé : **5 minutes**.<br>**À écrire une seule fois pour les treize types**, dans `section_page_detail` — il enveloppe déjà toutes les sections et porte déjà les crochets `initState`/`dispose`. Bénéfice de bord : le retour passant par le même `dispose`, il émet un `sectionLeave` **avec sa durée réelle** au lieu de laisser une consultation ouverte jusqu'au prochain visiteur. **La statistique devient juste, en plus de l'écran** | Ce n'est ni K3 ni K7 : c'est un comportement de borne qui vaut pour tous les types. À ne pas glisser dans le port des deux écrans, sinon il sera écrit deux fois puis une troisième |
| **K6** | **Publier les APK à jour**, puis vérifier le renouvellement du parc | **L19**. Les tablettes kiosk sont des appareils que tu gères — mise à jour maîtrisée. **Les téléphones des visiteurs, non** : aucune manœuvre technique ne force une mise à jour, d'où la stratégie de coexistence du lot I |