Internationalisation des énigmes pédagogiques¶
Le parcours pédagogique de PerfShop repose sur 100 énigmes réparties sur cinq niveaux (BAC1 à BAC5, 20 énigmes par niveau) et sur un pool de 25 questions logique tirées aléatoirement lors de certains parcours. Tout ce contenu est entièrement internationalisé via des fichiers JSON dédiés.
Sources
backend/src/main/resources/i18n/enigmes/bac1/ à bac5/, backend/src/main/resources/i18n/logique/
Structure des répertoires¶
backend/src/main/resources/i18n/
├── enigmes/
│ ├── bac1/
│ │ ├── enigmes_fr.json ← complet (~5,9 KB)
│ │ ├── enigmes_en.json ← complet (~5,4 KB)
│ │ ├── enigmes_de.json ← placeholder
│ │ ├── enigmes_es.json ← placeholder
│ │ ├── enigmes_it.json ← placeholder
│ │ ├── enigmes_pt.json ← placeholder
│ │ └── enigmes_zh.json ← placeholder
│ ├── bac2/
│ ├── bac3/
│ ├── bac4/
│ └── bac5/
└── logique/
├── logique_fr.json ← 25 questions
└── logique_en.json ← 25 questions
Chaque niveau BAC1-BAC5 dispose de sept fichiers — un par langue supportée. Au moment de cette rédaction, seuls français et anglais sont complets. Les cinq autres (allemand, espagnol, italien, portugais, chinois) sont présents sous forme de placeholders structurels, prêts à recevoir leurs traductions.
Le pool logique est organisé différemment : deux fichiers seulement (FR et EN), sans structure par niveau, car le tirage des questions logique est indépendant du niveau du parcours.
Format des fichiers énigmes¶
Chaque fichier enigmes_<lang>.json est un objet JSON plat dont les clés suivent la convention BAC<level>-<step>.<property> :
{
"BAC1-1.text": "📡 SIGNAL REÇU. Bienvenue, agent.\n\nLe catalogue contient 994 produits.\nTa mission : trouver le produit dont le prix correspond à la somme des chiffres de ce nombre.\n\nRègle les filtres Prix min ET Prix max sur ce montant en euros.\nEntre ce prix.",
"BAC1-1.hint": "Additionne les trois chiffres : 9 + 9 + 4",
"BAC1-1.culturalNote": "22 octobre 1991 — Linus Torvalds publie Linux 0.0.1 sur internet. Il a 21 ans et prévient que ça ne sera pas grand-chose. Aujourd'hui, 96% des serveurs web tournent sous Linux.",
"BAC1-2.text": "📡 Bien joué. Tu as trouvé le câble USB-C à 22€.\n...",
"BAC1-2.hint": "Après 5 jours : 60 − 35 = 25 (encore au-dessus). Après 6 jours : 60 − 42 = ?",
"BAC1-2.culturalNote": "Les suites arithmétiques sont partout : ..."
}
Les trois propriétés par énigme¶
| Clé | Rôle | Obligatoire |
|---|---|---|
BAC<N>-<step>.text |
Énoncé affiché à l'étudiant dans l'overlay pédagogique | ✅ oui |
BAC<N>-<step>.hint |
Indice dévoilé à la demande (si les indices sont activés) | ✅ oui |
BAC<N>-<step>.culturalNote |
Anecdote historique, scientifique ou culturelle affichée après validation réussie | ⭕ optionnelle, présente sur la majorité des énigmes |
Les réponses ne figurent pas dans ces fichiers. Elles sont codées en dur dans les classes Java PedagogiqueEnigmeBacN et sont universelles : un nombre, une chaîne fixe, un prix exact. Elles ne changent jamais d'une langue à l'autre. Cette séparation garantit qu'un étudiant suivant BAC3 en anglais et un autre en français résolvent exactement le même puzzle mathématique, seule la langue d'affichage diffère.
Placeholders et substitutions¶
Contrairement au backend Spring Boot qui utilise MessageFormat avec {0}, {1}, le format des énigmes est statique : pas de substitution à la volée. Les valeurs affichées (nombre de produits, prix exact, code agent) sont soit écrites en dur dans l'énoncé (comme 994 produits ou 22€), soit construites par le composant React qui rend l'overlay.
Cette simplicité est volontaire : le contenu pédagogique change rarement et les placeholders dynamiques compliqueraient inutilement la traduction.
Chargement par PedagogiqueEnigme¶
Le service PedagogiqueEnigme (côté Java) charge les traductions au démarrage via une méthode loadTranslations(level) qui :
- Détermine le fichier à lire :
i18n/enigmes/bac<level>/enigmes_<PERFSHOP_LANG>.json - Si le fichier n'existe pas ou est vide (cas des placeholders DE/ES/IT/PT/ZH), fallback sur
enigmes_fr.json - Parse le JSON avec un parser minimal (zéro dépendance Jackson supplémentaire)
- Stocke la map
key → valueen mémoire - Expose une méthode
t(level, key)qui résoutBAC<level>-<step>.<property>
Le fallback vers le français est la raison pour laquelle les fichiers DE/ES/IT/PT/ZH peuvent rester sous forme de placeholders vides : l'expérience utilisateur reste fonctionnelle dans toutes les langues, même celles non encore traduites. Seul l'affichage est en français au lieu de la langue demandée.
Architecture par niveau¶
Chaque niveau BAC1 à BAC5 possède sa propre classe Java dédiée :
backend/src/main/java/com/perfshop/pedagogique/
├── PedagogiqueEnigme.java ← Superclasse commune
├── PedagogiqueEnigmeBac1.java ← Logique + réponses BAC1
├── PedagogiqueEnigmeBac2.java
├── PedagogiqueEnigmeBac3.java
├── PedagogiqueEnigmeBac4.java
└── PedagogiqueEnigmeBac5.java
La superclasse PedagogiqueEnigme expose le chargement JSON et la résolution de clés. Chaque sous-classe apporte :
- La liste des étapes du parcours (20 par niveau)
- La fonction de validation pour chaque étape (calcul mathématique, comparaison de chaîne, hash attendu)
- La règle de progression et les éventuels sauts conditionnels
Pour le détail du moteur et des 20 étapes par niveau, voir Niveaux BAC1 à BAC5 et Système d'énigmes.
Volumétrie des fichiers¶
Le volume de texte varie selon la complexité des énigmes :
| Niveau | Taille FR | Taille EN | Nombre de clés |
|---|---|---|---|
| BAC1 | ~5,9 KB | ~5,4 KB | ~50 |
| BAC2 | ~6,5 KB | ~6,0 KB | ~55 |
| BAC3 | ~8,0 KB | ~7,5 KB | ~60 |
| BAC4 | ~10,0 KB | ~9,2 KB | ~65 |
| BAC5 | ~12,1 KB | ~11,2 KB | ~75 |
Le niveau BAC5 est le plus long parce qu'il combine des énoncés détaillés (cryptographie, calculs en plusieurs temps) avec des notes culturelles plus riches. Les 20 étapes × 5 niveaux × 2 langues complètes représentent environ 100 KB de contenu pédagogique soigneusement écrit.
Format du pool logique¶
Le fichier logique/logique_fr.json est un tableau JSON de 25 objets :
[
{"text": "Suite : 2, 6, 18, 54, ?", "hint": "Chaque terme est multiplié par 3"},
{"text": "Si A=1, B=2, ..., Z=26 : que vaut M + A + T + H ?", "hint": "M=13, A=1, T=20, H=8"},
{"text": "Combien de diagonales possède un hexagone (polygone à 6 côtés) ?", "hint": "Formule : n×(n−3) / 2 avec n=6"},
...
]
Deux propriétés par question :
text— énoncé de la questionhint— indice facultatif
Les réponses sont stockées dans un tableau Java parallèle au pool JSON, indexé dans le même ordre. Les indices des 25 questions sont consultables côté backend ; le tirage utilise un LCG (générateur congruentiel linéaire) seeded par le X-Student-Token de la session pour tirer 5 questions reproductibles par parcours.
Les 25 questions sont volontairement de niveau mathématique général — additions, suites, géométrie élémentaire, factorielles, nombres premiers. Elles ne dépendent pas de connaissances informatiques et sont résolubles par n'importe quel étudiant post-lycée. Le tirage aléatoire (mais déterministe par session) évite le bachotage par mémorisation.
Chargement des questions logique¶
Le service ThemeLogique charge le pool FR/EN au démarrage et expose :
L'algorithme utilise un LCG seeded par le hash du token pour tirer count indices distincts dans [0, 25), puis retourne les questions correspondantes dans la langue active. Les indices tirés sont stockés en base dans la colonne logique_question_indices de pedagogique_sessions (voir Schéma de la base de données) pour garantir la reproductibilité entre le serveur et le client.
Auparavant, l'algorithme LCG était dupliqué côté frontend — cette approche a été abandonnée car elle générait des désynchronisations en cas de changement de token. Désormais, seul le backend tire les questions, et le frontend récupère la liste finale via GET /pedagogique/logique/questions.
Symétrie FR/EN¶
Au moment de cette rédaction, les fichiers FR et EN sont en parfaite symétrie pour chaque niveau BAC1-BAC5 et pour le pool logique. Un audit « 3 amigos » a validé cette parité avant la rédaction de cette documentation.
Un script simple peut vérifier la symétrie :
jq 'keys | sort' enigmes_fr.json > /tmp/fr.keys
jq 'keys | sort' enigmes_en.json > /tmp/en.keys
diff /tmp/fr.keys /tmp/en.keys
Pour les fichiers placeholders DE/ES/IT/PT/ZH, un contenu minimal type {} suffit — le fallback FR s'applique automatiquement.
Ajouter une nouvelle langue¶
Le processus est le suivant pour chaque niveau :
- Copier
enigmes_fr.jsonversenigmes_<lang>.jsondans le dossier du niveau - Traduire uniquement les valeurs (laisser les clés
BAC<N>-<step>.textetc. telles quelles) - Refaire la même opération pour chaque niveau BAC1-BAC5
- Copier et traduire
logique/logique_fr.jsonverslogique/logique_<lang>.json - Déployer avec
PERFSHOP_LANG=<lang>
Aucune modification du code Java n'est nécessaire. Le fallback FR prendra en charge les clés éventuellement manquantes pendant la période de traduction.
Points de vigilance¶
- Les nombres dans les énoncés sont intangibles. Si une énigme dit « 994 produits » parce que le catalogue contient exactement 994 produits, cette valeur doit être préservée telle quelle dans toutes les langues. De même pour les prix (
22€,64€,255€,34,99€) qui correspondent aux produits pédagogiques créés par la migration V10. - Les noms de produits sont parfois en français. Par exemple « Câble USB-C basique 1m » apparaît dans l'énoncé BAC1-2. En anglais, la traduction peut utiliser le nom anglais (« Basic USB-C Cable 1m ») mais il faut que ce nom corresponde au nom réel du produit dans la base — ou accepter que l'énoncé anglais cite le nom français entre guillemets.
- Les credentials agents sont universels. Les mots de passe
Cable22Go,Code64Exp6,Docker8080Get,Lorem255FF42,Rsa3Xor51Pi14sont des chaînes fixes qui ne doivent jamais être traduites. - Les notes culturelles peuvent être remplacées. Une anecdote sur Linus Torvalds pour le public francophone peut être remplacée par une anecdote plus pertinente pour un public germanophone ou hispanophone. Ce n'est pas une simple traduction — c'est une adaptation culturelle.