🌐 [Feature] Support bilingue FR/EN #9

Open
opened 2026-08-03 10:20:35 -04:00 by NixCyRy · 0 comments
NixCyRy commented 2026-08-03 10:20:35 -04:00 (Migrated from github.com)

Problème

Tous les aliments ont name_fr ET name_en dans la DB, mais l'app est FR uniquement. Le Guide alimentaire canadien est bilingue par nature.

Solution

  • Toggle FR/EN dans le header ou user settings
  • Utiliser name_fr ou name_en selon la langue
  • Traduire les labels UI (boutons, titres, messages)
  • Stocker la préférence dans users table ou localStorage

Localisation

  • backend/app.py — _build_food_dict() retourne déjà name_fr or name_en
  • Tout le frontend — chaînes FR en dur

Analyse:

Issue #9 — Support bilingue FR/EN : Plan de match

Ce qui existe déjà ✅

  • DB : Tous les aliments ont name_fr ET name_en (source : Guide alimentaire canadien, naturellement bilingue)
  • Backend : _build_food_dict() retourne déjà name_fr (fallback name_en). La fonction CNF search cherche déjà dans les deux langues
  • DB CNF : Les tables food et nutrient ont aussi name_fr / name_en

Ce qui reste à faire 🔧

1. Backend — Dictionnaire de traduction (~60 chaînes)

  • ~30 messages d'erreur éparpillés dans 8 blueprints (auth.py, foods.py, tracking.py, selections.py, journal.py, etc.)
  • 3 constantes dans extensions.py (ERR_UNAUTHORIZED, ERR_FORBIDDEN, ERR_RATE_LIMIT)
  • ~10 messages informatifs ("Refresh lancé", etc.)
  • _build_food_dict — accepter un paramètre lang pour choisir name_fr vs name_en
  • Préférence langue utilisateur — ajouter un champ language dans la table users (ou utiliser un header/query param)

2. Frontend — Le gros du travail (~85 chaînes FR en dur)

  • 15 fichiers JS (~4 775 lignes total) avec des chaînes FR partout
  • index.html (830 lignes) — titre, meta, labels de modales
  • Domaines clés :
    • auth.js — Connexion, inscription, mot de passe oublié (~42 références)
    • render.js — Rendu principal, titres, labels (891 lignes, le plus gros)
    • cnf.js — Recherche d'aliments, formulaires d'ajout (561 lignes)
    • grocery.js, history.js, journal.js, deals.js, food-modal.js — chacun 10-20 chaînes
    • nutrition.js — labels de nutriments (Calories, Protéine, Fibre, Fer, etc.)

3. Mécanique de toggle

  • Objet i18n avec dictionnaire fr / en centralisé
  • t('key') helper qui lit la langue courante (localStorage)
  • Toggle UI dans le header (📐 FR | EN) ou dans le menu utilisateur
  • Appliquer la langue au rendu — pas de rechargement de page, juste re-render
  • <html lang="..."> dynamique

4. Noms d'aliments

  • Le toggle change name_fr → name_en via un paramètre ?lang=en sur l'API /api/foods
  • Le search cherche dans les deux langues (déjà fait côté CNF)

Ordre d'exécution proposé

Étape Quoi Complexité
1 Créer i18n.js — dictionnaire centralisé FR/EN de toutes les chaînes UI Moyenne
2 Helper t(key) + état global currentLang dans core.js Simple
3 Backend : lang param sur /api/foods → retourne name_en ou name_fr Simple
4 Remplacer toutes les chaînes en dur dans les 15 fichiers JS par t('key') Long
5 Remplacer les chaînes dans index.html Moyen
6 Toggle FR/EN dans le header + menu utilisateur Simple
7 Messages d'erreur backend — accepter Accept-Language header ou ?lang= Moyen
8 Tests : pytest (backend) + manuel mobile/desktop Normal
9 QA : Lighthouse, ZAP, vérifier que rien casse Normal

Risques / notes

  • Volume : ~85 chaînes en dur dans le frontend + ~40 dans le backend. C'est méticuleux mais pas complexe.
  • render.js (891 lignes) sera le plus touché — beaucoup de HTML construit en string
  • innerHTML += est déjà problématique dans ce codebase → attention de ne pas casser les event listeners
  • Le plus safe : faire le i18n.js d'abord, puis fichier par fichier, tester après chaque
  • DB users : pas besoin de migration complexe — juste ajouter colonne language TEXT DEFAULT 'fr'
## Problème Tous les aliments ont `name_fr` ET `name_en` dans la DB, mais l'app est FR uniquement. Le Guide alimentaire canadien est bilingue par nature. ## Solution - Toggle FR/EN dans le header ou user settings - Utiliser `name_fr` ou `name_en` selon la langue - Traduire les labels UI (boutons, titres, messages) - Stocker la préférence dans `users` table ou localStorage ## Localisation - `backend/app.py` — `_build_food_dict()` retourne déjà `name_fr or name_en` - Tout le frontend — chaînes FR en dur ## Analyse: <h2>Issue #9 — Support bilingue FR/EN : Plan de match</h2> <h3>Ce qui existe déjà ✅</h3> <ul> <li><strong>DB :</strong> Tous les aliments ont <code>name_fr</code> ET <code>name_en</code> (source : Guide alimentaire canadien, naturellement bilingue)</li> <li><strong>Backend :</strong> <code>_build_food_dict()</code> retourne déjà <code>name_fr</code> (fallback <code>name_en</code>). La fonction CNF search cherche déjà dans les deux langues</li> <li><strong>DB CNF :</strong> Les tables <code>food</code> et <code>nutrient</code> ont aussi <code>name_fr</code> / <code>name_en</code></li> </ul> <h3>Ce qui reste à faire 🔧</h3> <h4>1. Backend — Dictionnaire de traduction (~60 chaînes)</h4> <ul> <li><strong>~30 messages d'erreur</strong> éparpillés dans 8 blueprints (<a class="markdown-file-link" data-file-path="auth.py"><code>auth.py</code></a>, <a class="markdown-file-link" data-file-path="foods.py"><code>foods.py</code></a>, <a class="markdown-file-link" data-file-path="tracking.py"><code>tracking.py</code></a>, <a class="markdown-file-link" data-file-path="selections.py"><code>selections.py</code></a>, <a class="markdown-file-link" data-file-path="journal.py"><code>journal.py</code></a>, etc.)</li> <li><strong>3 constantes</strong> dans <a class="markdown-file-link" data-file-path="extensions.py"><code>extensions.py</code></a> (<code>ERR_UNAUTHORIZED</code>, <code>ERR_FORBIDDEN</code>, <code>ERR_RATE_LIMIT</code>)</li> <li><strong>~10 messages informatifs</strong> ("Refresh lancé", etc.)</li> <li><strong><code>_build_food_dict</code></strong> — accepter un paramètre <code>lang</code> pour choisir <code>name_fr</code> vs <code>name_en</code></li> <li><strong>Préférence langue utilisateur</strong> — ajouter un champ <code>language</code> dans la table <code>users</code> (ou utiliser un header/query param)</li> </ul> <h4>2. Frontend — Le gros du travail (~85 chaînes FR en dur)</h4> <ul> <li><strong>15 fichiers JS</strong> (~4 775 lignes total) avec des chaînes FR partout</li> <li><strong><a class="markdown-file-link" data-file-path="index.html"><code>index.html</code></a></strong> (830 lignes) — titre, meta, labels de modales</li> <li>Domaines clés : <ul> <li><a class="markdown-file-link" data-file-path="auth.js"><code>auth.js</code></a> — Connexion, inscription, mot de passe oublié (~42 références)</li> <li><a class="markdown-file-link" data-file-path="render.js"><code>render.js</code></a> — Rendu principal, titres, labels (891 lignes, le plus gros)</li> <li><a class="markdown-file-link" data-file-path="cnf.js"><code>cnf.js</code></a> — Recherche d'aliments, formulaires d'ajout (561 lignes)</li> <li><a class="markdown-file-link" data-file-path="grocery.js"><code>grocery.js</code></a>, <a class="markdown-file-link" data-file-path="history.js"><code>history.js</code></a>, <a class="markdown-file-link" data-file-path="journal.js"><code>journal.js</code></a>, <a class="markdown-file-link" data-file-path="deals.js"><code>deals.js</code></a>, <a class="markdown-file-link" data-file-path="food-modal.js"><code>food-modal.js</code></a> — chacun 10-20 chaînes</li> <li><a class="markdown-file-link" data-file-path="nutrition.js"><code>nutrition.js</code></a> — labels de nutriments (Calories, Protéine, Fibre, Fer, etc.)</li> </ul> </li> </ul> <h4>3. Mécanique de toggle</h4> <ul> <li><strong>Objet <code>i18n</code></strong> avec dictionnaire <code>fr</code> / <code>en</code> centralisé</li> <li><strong><code>t('key')</code></strong> helper qui lit la langue courante (<code>localStorage</code>)</li> <li><strong>Toggle UI</strong> dans le header (📐 <code>FR | EN</code>) ou dans le menu utilisateur</li> <li><strong>Appliquer la langue au rendu</strong> — pas de rechargement de page, juste re-render</li> <li><strong><code>&lt;html lang="..."&gt;</code></strong> dynamique</li> </ul> <h4>4. Noms d'aliments</h4> <ul> <li>Le toggle change <code>name_fr</code> → <code>name_en</code> via un paramètre <code>?lang=en</code> sur l'API <code>/api/foods</code></li> <li>Le search cherche dans les deux langues (déjà fait côté CNF)</li> </ul> <hr> <h3>Ordre d'exécution proposé</h3> Étape | Quoi | Complexité -- | -- | -- 1 | Créer i18n.js — dictionnaire centralisé FR/EN de toutes les chaînes UI | Moyenne 2 | Helper t(key) + état global currentLang dans core.js | Simple 3 | Backend : lang param sur /api/foods → retourne name_en ou name_fr | Simple 4 | Remplacer toutes les chaînes en dur dans les 15 fichiers JS par t('key') | Long 5 | Remplacer les chaînes dans index.html | Moyen 6 | Toggle FR/EN dans le header + menu utilisateur | Simple 7 | Messages d'erreur backend — accepter Accept-Language header ou ?lang= | Moyen 8 | Tests : pytest (backend) + manuel mobile/desktop | Normal 9 | QA : Lighthouse, ZAP, vérifier que rien casse | Normal <h3>Risques / notes</h3> <ul> <li><strong>Volume :</strong> ~85 chaînes en dur dans le frontend + ~40 dans le backend. C'est méticuleux mais pas complexe.</li> <li><strong><a class="markdown-file-link" data-file-path="render.js"><code>render.js</code></a> (891 lignes)</strong> sera le plus touché — beaucoup de HTML construit en string</li> <li><strong><code>innerHTML +=</code></strong> est déjà problématique dans ce codebase → attention de ne pas casser les event listeners</li> <li><strong>Le plus safe :</strong> faire le <a class="markdown-file-link" data-file-path="i18n.js"><code>i18n.js</code></a> d'abord, puis fichier par fichier, tester après chaque</li> <li><strong>DB users :</strong> pas besoin de migration complexe — juste ajouter colonne <code>language TEXT DEFAULT 'fr'</code></li> </ul>
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
SlopVibe-org/nutri-food#9
No description provided.