Files
arnaudne 5bf6013460 feat: full feature buildout — streaming, i18n, mastery map, admin, jobs
Progressive lesson streaming via onSegment callback (fixes SSE for non-English
users — locale was shadowed in lesson-reader useEffect). Adds: BullMQ workers,
Redis stream buffer, token budget enforcement, Langfuse tracing, golden-eval
runner, Playwright e2e scaffolding, lesson depth/locale/preferences schema,
mastery map UI, admin panel (blueprints/users/reports/quality/misconceptions),
image queries, source citations, view transitions, reading animations, i18n
(next-intl), PDF export, surprise endpoint, and 402 passing unit tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-08 22:08:14 +02:00

586 lines
23 KiB
JSON
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"lesson": {
"ariaLesson": "Leçon",
"ariaProgress": "Progression de la leçon",
"reading": "Lecture",
"segments": "{count, plural, one {# segment} other {# segments}}",
"segmentLocked": "Complétez le point de contrôle précédent pour continuer.",
"checkpoint": {
"predict": "Prédire",
"explain": "Expliquer",
"solve": "Résoudre",
"placeholder": "Écrivez votre réponse ici…",
"checking": "Analyse en cours…",
"submitted": "Envoyé",
"confidenceLegend": "Avant d'envoyer — à quel point êtes-vous sûr(e) ?",
"notSure": "Pas sûr(e)",
"thinkSo": "Je pense",
"confident": "Confiant(e)",
"notSureAria": "Je ne suis pas sûr(e) de ma réponse",
"thinkSoAria": "Je pense que ma réponse est correcte",
"confidentAria": "Je suis confiant(e) dans ma réponse"
},
"print": "Exporter PDF",
"considering": "En cours d'évaluation…",
"empty": "Cette leçon n'a pas encore de contenu. Revenez dans un instant — le contenu est en cours de préparation.",
"outline": {
"intro": "Ce que vous allez explorer",
"preparing": "Génération de votre leçon en cours…",
"streaming": "Rédaction de votre leçon…",
"streamingLabel": "Contenu de la leçon en chargement"
},
"tangent": {
"placeholder": "Quelque chose vous intrigue ?",
"ariaInput": "Question de curiosité",
"ariaAsk": "Envoyer",
"considering": "Je cherche…"
},
"journeyProgress": "Leçon {current} sur {total}",
"complete": {
"ariaLabel": "Leçon terminée",
"heading": "Leçon terminée.",
"saved": "Progression sauvegardée.",
"saving": "Sauvegarde…",
"markComplete": "Marquer comme terminé",
"nextLesson": "Leçon suivante →",
"deepen": "Approfondir →",
"journeyDone": "Parcours terminé.",
"journeyDoneNote": "Vous avez terminé toutes les leçons de ce parcours."
}
},
"review": {
"title": "Révision quotidienne",
"aria": "Session de révision quotidienne",
"loading": "Chargement de votre révision…",
"empty": "Rien à réviser pour l'instant. Revenez demain.",
"progress": "{current} sur {total}",
"considering": "En cours d'analyse…",
"next": "Suivant →",
"finish": "Terminer",
"complete": {
"heading": "Session terminée",
"reviewed": "{count, plural, one {# concept révisé} other {# concepts révisés}}",
"netDelta": "Maîtrise nette : {delta}",
"nothingDue": "Vous êtes à jour.",
"backHome": "Retour à l'apprentissage"
},
"delta": {
"up": "↑ {pct}%",
"down": "↓ {pct}%",
"same": "inchangé"
},
"emptyUntil": "Tout est à jour. Votre prochaine révision est le {date}.",
"emptyNoHistory": "Pas encore de révisions. Complétez une leçon pour commencer."
},
"nav": {
"learn": "Apprendre",
"review": "Révision",
"mastery": "Maîtrise",
"map": "Carte",
"signIn": "Se connecter",
"signOut": "Se déconnecter",
"profile": "Profil",
"admin": "Admin"
},
"home": {
"eyebrow": "Maîtrise guidée par la curiosité",
"title": "Qu'est-ce que vous voulez comprendre ?",
"lede": "Dites-nous ce qui vous intrigue.",
"fieldLabel": "Votre intention d'apprentissage",
"placeholder": "ex. comment fonctionnent les vaccins",
"examplesLabel": "Ou essayez l'un de ceux-ci",
"submit": "Commencer",
"submitting": "Recherche de votre leçon…",
"error": "Une erreur s'est produite. Réessayez.",
"examples": [
"Pourquoi la glace est glissante",
"Comment un gyroscope reste droit",
"Pourquoi le ciel est bleu",
"Ce qu'est vraiment la température",
"Comment fonctionne un casque à réduction de bruit",
"Pourquoi la vitesse de la lumière est une limite",
"Comment fonctionnent les lasers",
"Pourquoi les aimants attirent",
"Ce qui cause la friction",
"Comment un micro-ondes chauffe les aliments",
"Pourquoi le verre est transparent",
"Comment fonctionnent les supraconducteurs",
"Ce qu'est l'intrication quantique",
"Pourquoi les miroirs inversent la gauche et la droite",
"Comment le son se propage",
"Ce qui crée un arc-en-ciel",
"Pourquoi l'air chaud monte",
"Comment fonctionne un réacteur nucléaire",
"Ce que pourrait être la matière noire",
"Pourquoi le temps ralentit près de la lumière",
"Comment se forment les trous noirs",
"Pourquoi la Lune cause les marées",
"Ce qu'est une étoile à neutrons",
"Comment les étoiles fabriquent les éléments",
"Pourquoi Mars est rouge",
"Comment les fusées atteignent l'orbite",
"Ce qu'était le Big Bang",
"Pourquoi Pluton n'est pas une planète",
"Comment le GPS utilise la relativité",
"Ce qui cause les éclipses solaires",
"Comment les télescopes voient le passé",
"Pourquoi les galaxies sont en spirale",
"Ce que mesure une année-lumière",
"Comment le Soleil produit son énergie",
"Pourquoi l'espace est froid mais le soleil brûle",
"Ce qui cause les saisons",
"Comment les comètes ont une queue",
"Pourquoi l'univers est en expansion",
"Ce que sont les exoplanètes",
"Comment se forment les aurores",
"Comment fonctionnent les vaccins",
"Pourquoi les oignons font pleurer",
"Comment la mémoire se forme",
"Pourquoi nous rêvons",
"Comment les muscles se développent",
"Ce qui cause le vieillissement",
"Comment les antibiotiques tuent les bactéries",
"Pourquoi nous avons de la fièvre",
"Comment le système immunitaire apprend",
"Ce que fait vraiment l'ADN",
"Comment les nerfs transmettent des signaux",
"Pourquoi nous bâillons",
"Comment le cœur garde son rythme",
"Ce que font les bactéries intestinales",
"Comment les blessures guérissent",
"Pourquoi nous avons besoin de dormir",
"Comment les yeux perçoivent la couleur",
"Ce qui cause les allergies",
"Comment les cellules se divisent",
"Pourquoi l'exercice développe l'endurance",
"Pourquoi le savon nettoie",
"Comment les batteries stockent l'énergie",
"Ce qui rend une substance acide",
"Comment on fabrique le verre",
"Pourquoi les métaux conduisent l'électricité",
"Comment les feux d'artifice ont des couleurs",
"Ce qu'est vraiment la rouille",
"Comment les catalyseurs accélèrent les réactions",
"Pourquoi l'huile et l'eau ne se mélangent pas",
"Comment on fabrique le plastique",
"Ce que mesure le pH",
"Comment fonctionne la fermentation",
"Pourquoi le diamant est dur",
"Comment se forment les bulles de savon",
"Ce qui rend les piments forts",
"Comment fonctionne la glace sèche",
"Pourquoi le pain lève",
"Comment le parfum se diffuse",
"Ce que sont les alliages",
"Comment fonctionne la datation au carbone",
"Le contrôle de congestion TCP",
"Comment le chiffrement protège les secrets",
"Ce que fait un index de base de données",
"Comment les réseaux de neurones apprennent",
"Pourquoi les mots de passe sont hachés",
"Comment Internet achemine les données",
"Ce que fait un compilateur",
"Comment le GPS trouve votre position",
"Pourquoi les ordinateurs utilisent le binaire",
"Comment un processeur exécute du code",
"Ce que résout vraiment le cache",
"Comment le Wi-Fi transporte les données",
"Ce qu'est vraiment la blockchain",
"Comment les moteurs de recherche classent les pages",
"Pourquoi la RAM est plus rapide que le disque",
"Comment fonctionne la compression d'images",
"Ce qu'est une API",
"Comment fonctionne la cryptographie à clé publique",
"Pourquoi les logiciels ont des bugs",
"Comment fonctionne la traduction automatique",
"Pourquoi les nombres premiers comptent",
"Ce que mesure le calcul différentiel",
"Comment fonctionnent les probabilités",
"Pourquoi pi est partout",
"Ce que signifie vraiment l'infini",
"Ce que révèle une dérivée",
"Pourquoi le zéro fut révolutionnaire",
"Comment les statistiques peuvent tromper",
"Ce que signifie la croissance exponentielle",
"Pourquoi certains problèmes sont insolubles",
"Comment les intérêts composés croissent",
"Ce qu'est un logarithme",
"Pourquoi la courbe en cloche apparaît",
"Comment on crée les fractales",
"Ce que fait le théorème de Bayes",
"Pourquoi on ne peut pas diviser par zéro",
"Comment le GPS triangule une position",
"Ce que représente une matrice",
"Pourquoi le hasard est difficile",
"Comment les banques fixent les taux",
"Ce qui cause l'inflation",
"Comment fonctionnent les marchés boursiers",
"Pourquoi l'argent a de la valeur",
"Ce qu'est une récession",
"Comment les impôts influencent les comportements",
"Pourquoi les prix montent et baissent",
"Ce que signifie l'offre et la demande",
"Comment l'assurance répartit le risque",
"Pourquoi le commerce profite aux deux camps",
"Ce que mesure le PIB",
"Comment fonctionnent les scores de crédit",
"Pourquoi les bulles se forment et éclatent",
"Comment évoluent les taux de change",
"Pourquoi les monopoles sont risqués",
"Ce qu'est une obligation",
"Comment fonctionne le capital-risque",
"Pourquoi la diversification compte",
"Ce qu'explique la théorie des jeux",
"Comment l'imprimerie a changé l'Europe",
"Pourquoi les empires s'effondrent",
"Comment l'écriture fut inventée",
"Ce qui a causé la révolution industrielle",
"Pourquoi l'Empire romain est tombé",
"Comment la monnaie a remplacé le troc",
"Ce qui a déclenché la Renaissance",
"Comment la démocratie a commencé",
"Pourquoi la Route de la soie comptait",
"Ce qu'ont changé les Lumières",
"Comment les épidémies ont remodelé la société",
"Pourquoi les frontières sont là où elles sont",
"Comment les familles de langues se sont répandues",
"Ce qui a causé la guerre froide",
"Comment les premières villes sont nées",
"Pourquoi les révolutions arrivent",
"Comment la navigation a ouvert le monde",
"Ce qu'était vraiment la féodalité",
"Comment le calendrier fut fixé",
"Pourquoi certaines civilisations ont disparu",
"Comment se produisent les tremblements de terre",
"Pourquoi les volcans entrent en éruption",
"Ce qui cause la météo",
"Comment se forment les montagnes",
"Pourquoi l'océan est salé",
"Comment les rivières façonnent le paysage",
"Ce qui anime les courants océaniques",
"Pourquoi les déserts se forment où ils sont",
"Comment se forment les fossiles",
"Comment les nuages forment la pluie",
"Pourquoi le climat change",
"Comment grandissent les récifs coralliens",
"Ce qu'explique la tectonique des plaques",
"Comment se forment les grottes",
"Pourquoi les feuilles changent de couleur",
"Comment se forme la foudre",
"De quoi le sol est fait",
"Comment les glaciers se déplacent",
"Pourquoi certaines régions ont des moussons",
"Pourquoi nous procrastinons",
"Comment se forment les habitudes",
"Pourquoi la musique nous émeut",
"Comment nous apprenons les langues",
"Ce qui rend les choses virales",
"Pourquoi les illusions d'optique nous trompent",
"Comment marchent les astuces de mémoire",
"Pourquoi nous ressentons le déjà-vu",
"Comment fonctionne la persuasion",
"Ce qui rend une mélodie entêtante",
"Pourquoi les foules agissent irrationnellement",
"Comment se développent les accents",
"Pourquoi nous voyons des visages partout",
"Comment fonctionne l'effet placebo",
"Pourquoi les délais stimulent la concentration",
"Comment la couleur influence l'humeur",
"Pourquoi les histoires nous marquent",
"Comment le cerveau bilingue change de langue",
"Pourquoi nous oublions les noms",
"Comment fonctionne vraiment l'attention"
],
"spark": "Surprenez-moi",
"sparking": "Recherche en cours…",
"or": "ou",
"depthLabel": "Profondeur de la leçon",
"depth": {
"quick": "Express",
"quickMeta": "~5 min",
"standard": "Standard",
"standardMeta": "~10 min",
"deep": "Approfondi",
"deepMeta": "~20 min"
},
"modeLabel": "Format",
"mode": {
"practice": "Exercices",
"read": "Lecture seule"
},
"continue": {
"heading": "Reprendre",
"lessonProgress": "Leçon {position} sur {total}",
"resume": "Reprendre →"
},
"dueReviews": "{count} à réviser →"
},
"auth": {
"login": {
"title": "Connexion",
"email": "Email",
"password": "Mot de passe",
"submit": "Se connecter",
"submitting": "Connexion…",
"error": "Email ou mot de passe incorrect.",
"forgotPassword": "Mot de passe oublié ?",
"noAccount": "Pas de compte ?",
"register": "S'inscrire",
"resetSuccess": "Mot de passe mis à jour. Connectez-vous avec votre nouveau mot de passe.",
"or": "ou",
"continueWithGoogle": "Continuer avec Google",
"continueWithGitHub": "Continuer avec GitHub",
"continueWith": "Continuer avec {provider}"
},
"register": {
"title": "Créer un compte",
"name": "Nom",
"email": "Email",
"password": "Mot de passe",
"confirmPassword": "Confirmer le mot de passe",
"passwordMismatch": "Les mots de passe ne correspondent pas.",
"submit": "Créer un compte",
"submitting": "Création en cours…",
"signInFailed": "Compte créé mais la connexion a échoué. Essayez de vous connecter manuellement.",
"hasAccount": "Déjà un compte ?",
"signIn": "Se connecter"
},
"forgotPassword": {
"title": "Réinitialiser le mot de passe",
"email": "Email",
"submit": "Envoyer le lien",
"submitting": "Envoi…",
"sentTitle": "Vérifiez votre boîte mail",
"sentBody": "Si un compte existe pour cet email, nous avons envoyé un lien de réinitialisation. Il expire dans 1 heure.",
"backToSignIn": "Retour à la connexion",
"error": "Une erreur s'est produite. Réessayez."
},
"resetPassword": {
"title": "Définir un nouveau mot de passe",
"newPassword": "Nouveau mot de passe",
"confirmPassword": "Confirmer le mot de passe",
"passwordMismatch": "Les mots de passe ne correspondent pas.",
"submit": "Enregistrer",
"submitting": "Enregistrement…",
"error": "Échec de la réinitialisation du mot de passe.",
"invalidLink": "Lien de réinitialisation invalide.",
"requestNew": "Demander un nouveau lien"
},
"acceptInvite": {
"title": "Accepter votre invitation",
"subtitle": "Configurez votre compte pour commencer.",
"email": "E-mail",
"name": "Votre nom",
"password": "Mot de passe",
"confirmPassword": "Confirmer le mot de passe",
"passwordMismatch": "Les mots de passe ne correspondent pas.",
"submit": "Créer le compte",
"submitting": "Création…",
"error": "Impossible daccepter cette invitation.",
"invalidLink": "Ce lien dinvitation est invalide, expiré ou déjà utilisé.",
"goToLogin": "Aller à la connexion"
},
"verifyEmail": {
"title": "Vérifiez votre email",
"body": "Nous avons envoyé un lien de vérification à votre adresse email. Cliquez dessus pour activer votre compte.",
"subtext": "Vous ne l'avez pas reçu ? Vérifiez vos spams."
},
"error": {
"title": "Erreur d'authentification",
"default": "Une erreur s'est produite lors de la connexion.",
"Configuration": "Erreur de configuration du serveur. Contactez le support.",
"AccessDenied": "Accès refusé.",
"Verification": "Le lien de vérification a expiré ou a déjà été utilisé.",
"backToSignIn": "Retour à la connexion"
}
},
"settings": {
"title": "Paramètres",
"profile": {
"title": "Profil",
"name": "Nom affiché",
"save": "Enregistrer",
"saving": "Enregistrement…",
"saved": "Enregistré.",
"saveFailed": "Échec de l'enregistrement."
},
"language": {
"title": "Langue",
"label": "Langue de l'interface",
"description": "Choisissez la langue utilisée dans toute l'application.",
"en": "English",
"fr": "Français"
},
"security": {
"title": "Sécurité",
"currentPassword": "Mot de passe actuel",
"newPassword": "Nouveau mot de passe",
"confirmPassword": "Confirmer le nouveau mot de passe",
"changePassword": "Changer le mot de passe",
"changing": "Modification…",
"changed": "Mot de passe modifié.",
"failed": "Échec.",
"oauthNote": "Le changement de mot de passe n'est pas disponible pour les comptes utilisant la connexion sociale.",
"linked": "Connexion via : {providers}",
"passwordMismatch": "Les nouveaux mots de passe ne correspondent pas."
},
"appearance": {
"title": "Apparence",
"theme": {
"label": "Thème",
"system": "Système",
"light": "Clair",
"dark": "Sombre"
}
},
"danger": {
"title": "Zone dangereuse",
"deleteAccount": "Supprimer le compte",
"deleteConfirm": "Supprimer mon compte",
"deleteWarning": "Cette action supprimera définitivement votre compte et toutes vos données d'apprentissage. Elle est irréversible.",
"deleting": "Suppression…"
},
"learning": {
"title": "Apprentissage",
"description": "Personnalisez la façon dont les leçons sont rédigées pour vous.",
"ageGroup": {
"label": "Pour qui est-ce ?",
"child": "Enfant (812 ans)",
"teen": "Adolescent (1317 ans)",
"adult": "Adulte"
},
"difficulty": {
"label": "Niveau de difficulté par défaut",
"1": "Débutant",
"2": "Intermédiaire",
"3": "Avancé"
},
"depth": {
"label": "Durée de leçon par défaut",
"quick": "Courte (~5 min)",
"standard": "Standard (~10 min)",
"deep": "Approfondie (~20 min)"
},
"hint": {
"label": "À propos de vous (optionnel)",
"placeholder": "p. ex. Je suis infirmière, j'aime les analogies avec la cuisine…",
"description": "Une courte note aide à adapter les explications à votre profil."
},
"save": "Enregistrer",
"saving": "Enregistrement…",
"saved": "Enregistré.",
"saveFailed": "Échec de l'enregistrement."
},
"notifications": {
"title": "Notifications",
"description": "Gérez comment Curio vous contacte.",
"emailDigest": "M'envoyer un email quotidien quand des révisions sont en attente"
}
},
"onboarding": {
"title": "Bienvenue sur Curio",
"subtitle": "Configurez votre profil d'apprentissage. Vous pourrez le modifier à tout moment.",
"ageGroup": {
"label": "Pour qui est-ce ?",
"child": "Enfant (812 ans)",
"teen": "Adolescent (1317 ans)",
"adult": "Adulte"
},
"difficulty": {
"label": "Quel est votre niveau habituel ?",
"1": "Débutant — partir de zéro",
"2": "Intermédiaire — partir des bases",
"3": "Avancé — aller en profondeur"
},
"depth": {
"label": "Durée de leçon préférée",
"quick": "Courte (~5 min)",
"standard": "Standard (~10 min)",
"deep": "Approfondie (~20 min)"
},
"hint": {
"label": "Parlez-nous de vous (optionnel)",
"placeholder": "p. ex. Je suis infirmière, j'aime les analogies avec la cuisine…",
"description": "Aide à adapter les explications à votre profil."
},
"submit": "Commencer",
"submitting": "Enregistrement…",
"skip": "Passer pour l'instant"
},
"masteryMap": {
"title": "Carte de maîtrise",
"loading": "Chargement…",
"empty": "Aucune donnée de maîtrise pour l'instant.",
"emptyLink": "Commencer à apprendre",
"due": "à revoir",
"dueZone": {
"heading": "À réviser",
"startReview": "Commencer la révision →",
"none": "Rien à revoir pour l'instant."
},
"overview": {
"mastered": "Maîtrisés",
"learning": "En cours",
"needsWork": "À revoir",
"progress": "{mastered} concept{mastered, plural, one {} other {s}} maîtrisé{mastered, plural, one {} other {s}} sur {total}"
},
"status": {
"mastered": "Maîtrisé",
"learning": "En cours",
"needsWork": "À revoir",
"notStarted": "Non commencé"
},
"filter": {
"all": "Tout",
"due": "À réviser",
"needsWork": "À consolider"
},
"remove": "Retirer",
"removeBlueprint": "Retirer {title} de la carte de maîtrise"
},
"profile": {
"title": "Votre profil",
"masteryTitle": "Aperçu de la maîtrise",
"noMastery": "Aucune donnée de maîtrise pour l'instant. Commencez à apprendre pour voir votre progression ici."
},
"grade": {
"verdict": {
"mastered": "Compris",
"partial": "Partiel",
"misconception": "Erreur de compréhension",
"uncertain": "Incertain"
},
"calibration": {
"confidentFailed": "Vous vous êtes senti confiant — cela vaut la peine de réfléchir à ce qui vous a fait trébucher.",
"unsurePassed": "Vous vous êtes dit incertain, mais votre réponse était solide. Faites confiance à votre compréhension."
},
"referenceHeading": "Réponse complète",
"ariaFeedback": "Retour du tuteur",
"ariaCalibration": "Note de calibration",
"socratic": {
"ariaLabel": "Question socratique",
"placeholder": "Allez-y…",
"ariaInput": "Réponse de suivi",
"submit": "Répondre",
"considering": "En cours d'évaluation…"
},
"reexplain": {
"loading": "Préparation d'une autre explication…",
"trigger": "Essayer une autre explication →"
},
"retry": "Réessayer →",
"report": {
"toggle": "Signaler un problème",
"ariaLabel": "Signaler un problème",
"thanks": "Merci — nous allons examiner ça.",
"reasonGradeWrong": "La note semble incorrecte",
"reasonContentError": "Erreur de contenu",
"reasonOther": "Autre",
"notesPlaceholder": "Détails optionnels…",
"ariaReason": "Raison",
"ariaDetails": "Détails",
"submit": "Envoyer",
"cancel": "Annuler"
}
}
}