KreyolKeyb

Notes techniques — État actuel de l’application

Page générée le 09 July 2026 à 20:55, mise à jour le 10 juillet 2026 (heure locale du dépôt). Agrégation brute des rapports d’audit techniques produits pendant le développement, classés chronologiquement pour suivre l’évolution du projet et les corrections apportées au fil du temps.

⚠️ Ce sont des notes d’ingénierie interne, publiées ici sans filtrage : bugs connus avec références de code précises, limitations, dette technique et pistes de refonte. Elles s’adressent en premier lieu aux développeurs et contributeurs curieux de l’état réel du projet — pas un résumé marketing. Quand un point a été corrigé depuis la rédaction d’un rapport, une note ✅ Statut l’indique juste après son titre.

Chronologie


🗓️ Date du rapport : 06 novembre 2025

Analyse Lexicographique du Kreyòl Guadeloupéen

Métadonnées du Corpus


1. Corpus et Échantillonnage

1.1 Taille et Couverture

2. Analyse Morphologique

2.1 Distribution par Longueur

Longueur Nombre de mots Pourcentage
2 lettres 82 2.2% █
3 lettres 303 8.2% ████
4 lettres 524 14.2% ███████
5 lettres 657 17.9% ████████
6 lettres 644 17.5% ████████
7 lettres 487 13.2% ██████
8 lettres 354 9.6% ████
9 lettres 251 6.8% ███
10 lettres 156 4.2% ██
11 lettres 78 2.1% █
12 lettres 56 1.5%
13 lettres 50 1.4%
14 lettres 15 0.4%
15 lettres 11 0.3%
16 lettres 6 0.2%
17 lettres 2 0.1%
18 lettres 1 0.0%
19 lettres 1 0.0%
21 lettres 1 0.0%
22 lettres 1 0.0%

2.2 Mots Composés (avec trait d’union)

3. Analyse Phonographématique

3.1 Caractères Diacritiques

Caractère Fréquence Usage
é 1,542 Très fréquent
è 818 Très fréquent
ò 333 Très fréquent
à 6 Rare
ê 1 Rare
ô 1 Rare

3.2 Digrammes les Plus Fréquents

Digramme Fréquence
an 866
la 602
ou 581
on 420
-l 332
en 323
ma 254
ko 250
nn 242
ch 228
ra 213
é- 209
yé 196
ré 190
té 189
as 184
al 183
ka 168
nm 167
yo 165

4. Analyse Lexicale Stratifiée

4.1 Distribution de Fréquence (Loi de Zipf)

4.2 Principe de Pareto

4.3 Vocabulaire Fondamental (Top 50)

Rang Mot Fréquence % Cumul
1 ka 15,519 5.07%
2 an 10,729 8.58%
3 sé 7,177 10.92%
4 on 6,933 13.19%
5 té 6,834 15.42%
6 yo 6,063 17.40%
7 pou 5,812 19.30%
8 nou 5,712 21.17%
9 pa 5,244 22.88%
10 ki 4,569 24.38%
11 mwen 4,082 25.71%
12 ou 3,709 26.92%
13 sa 3,348 28.02%
14 fè 3,274 29.09%
15 la 2,531 29.92%
16 ni 2,313 30.67%
17 di 2,305 31.42%
18 kon 2,230 32.15%
19 adan 2,082 32.83%
20 ké 2,042 33.50%
21 lè 2,032 34.17%
22 si 2,016 34.82%
23 moun 1,836 35.42%
24 tout 1,802 36.01%
25 pé 1,756 36.59%
26 a-y 1,654 37.13%
27 kou 1,609 37.65%
28 mé 1,461 38.13%
29 vwè 1,454 38.61%
30 menm 1,433 39.07%
31 zòt 1,350 39.52%
32 pran 1,261 39.93%
33 viktò 1,235 40.33%
34 jan 1,177 40.72%
35 tini 1,147 41.09%
36 ti 1,105 41.45%
37 pè 1,096 41.81%
38 san 1,057 42.16%
39 dé 998 42.48%
40 épi 944 42.79%
41 ja 941 43.10%
42 rivé 940 43.41%
43 èvè 873 43.69%
44 fanm 849 43.97%
45 men 832 44.24%
46 ba 823 44.51%
47 anba 790 44.77%
48 pyè 767 45.02%
49 palé 763 45.27%
50 fò 744 45.51%

5. Analyse Syntaxique et Collocations

5.1 Bigrammes les Plus Fréquents

Rang Bigramme Fréquence
1 té ka 152
2 ka fè 86
3 yo ka 81
4 an mwen 75
5 sé on 59
6 pè pyè 59
7 an nou 58
8 pa té 52
9 an ka 47
10 ou ka 47
11 pa ka 45
12 an té 42
13 ki té 41
14 nou ka 40
15 ka di 39
16 pa ni 39
17 adan on 38
18 ka pran 33
19 pou nou 33
20 yo té 33
21 ka vwè 32
22 té ni 31
23 sé té 31
24 pou yo 30
25 moun ka 29
26 ou pa 29
27 ni on 27
28 sa ki 27
29 kou kou 27
30 ou té 26

5.2 Marqueurs Temps-Mode-Aspect (TMA)

Marqueur Fonction Fréquence Collocations principales
ka Aspect progressif/habituel 15,519 fè, di, pran
té Passé 6,834 ka, ni, ké
ké Futur 2,042 —
kay Futur 148 —
pa Négation 5,244 té, ka, ni
ja Déjà (accompli) 941 —

5.3 Exemples de Prédictions Contextuelles

Mot source Prédictions (probabilité)
ka fè (0.08), di (0.04), pran (0.03), vwè (0.03), palé (0.02)
nou ka (0.10), ké (0.06), pa (0.05), pé (0.04), té (0.04)
mwen ka (0.09), té (0.07), pa (0.05), an (0.04), sé (0.04)
yo ka (0.18), té (0.07), pa (0.05), pé (0.03), sé (0.03)
an mwen (0.10), nou (0.08), ka (0.07), té (0.06), pa (0.02)
la ka (0.13), pou (0.04), ki (0.04), yo (0.03), an (0.03)
té ka (0.31), ni (0.06), ké (0.05), ja (0.03), pé (0.03)
pa té (0.14), ka (0.12), ni (0.10), tini (0.02), menm (0.02)
tout sé (0.12), biten (0.06), biten-la (0.04), ti (0.02), pakèt (0.02)
pou nou (0.08), yo (0.07), kou (0.03), fè (0.03), mwen (0.03)

6. Mots Longs et Complexité Morphologique

6.1 Mots de 10 Lettres et Plus (378 mots)

Rang Mot Longueur Fréquence
1 diz-nèf-san-swasannsèt 22 lettres 5
2 sèvis-ladministrasyon 21 lettres 18
3 rèsponsabilité-lasa 19 lettres 13
4 lagyè-lendépandans 18 lettres 18
5 konkou-agrégasyon 17 lettres 18
6 propriyétè-fonsyé 17 lettres 13
7 toutmoun-bouliki 16 lettres 20
8 fanm-laplenn-lin 16 lettres 18
9 pawòl-senk-é-kat 16 lettres 18
10 konsititisyon-la 16 lettres 18
11 bazil-lanmòsibit 16 lettres 5
12 lawa-a-gran-tété 16 lettres 5
13 kréyòl-gwadloup 15 lettres 72
14 véridik-véritab 15 lettres 36
15 pipirit-chantan 15 lettres 20
16 pè-dè-bòn-famiy 15 lettres 20
17 lapèldéchanpyon 15 lettres 20
18 popilasyon-moun 15 lettres 18
19 sitiyasyon-lasa 15 lettres 18
20 konstitisyon-la 15 lettres 18
21 dèmen-spéra-dyé 15 lettres 13
22 tout-sòt-kalité 15 lettres 13
23 charlottesville 15 lettres 9
24 rèsponsabilité 14 lettres 26
25 gratèdpapyé-la 14 lettres 26
26 gratèdjouné-la 14 lettres 26
27 lagyè-sésésyon 14 lettres 18
28 konchonni-lasa 14 lettres 18
29 minis-lajistis 14 lettres 18
30 andidanbway-la 14 lettres 13

7. Évolution Diachronique du Lexique

8. Qualité et Validation Linguistique

8.1 Analyse de Qualité

9. Métriques Linguistiques Avancées

10. Recommandations Linguistiques

10.1 Forces du Corpus

10.2 Axes d’Amélioration

Annexes

A. Références Bibliographiques

B. Méthodologie

Tokenisation : Expression régulière Unicode préservant les diacritiques créoles

N-grams : Probabilités conditionnelles P(w₂ w₁) avec seuil de pertinence à 1%

Normalisation : Conversion en minuscules, préservation des traits d’union


Rapport généré automatiquement par Kreyòl Potomitan™ Pipeline v3.0 - Pipeline Unique

Pou an kreyòl ki ka viv é ka evolyé !


🗓️ Date du rapport : 03 juillet 2026

Rapport d’analyse — Clavier prédictif Klavyé Kréyòl Karukera

Date : 3 juillet 2026 Périmètre : pipeline de suggestions Android (SuggestionEngine.kt, InputProcessor.kt, LevenshteinDistance.kt, AccentTolerantMatcher.kt, FrenchDictionary.kt, KreyolInputMethodServiceRefactored.kt, BilingualSuggestion.kt) et assets JSON associés.

✅ Statut : Les deux points « A » de la feuille de route ont été traités les 06-07/08/2026. Le modèle n-grams porte désormais des clés à deux mots issues des trigrammes (v10.4.0), et le filtre de chemins mal orthographié Dictionnaries/** de build-apk.yml a été corrigé en Dictionnaires/**. Le suivi du curseur (onUpdateSelection(), absent du service actif) a également été corrigé en v10.4.0.


1. Résumé exécutif

Le moteur de suggestions est fonctionnel et bien découpé en composants, mais l’analyse révèle six points majeurs, par ordre de priorité :

  1. Les prédictions bigram/trigram sont du code mort : le modèle creole_ngrams.json ne contient que des clés à un seul mot (3 582 unigrammes, 0 bigrammes/trigrammes), alors que le code cherche des clés "mot1 mot2" et "mot1 mot2 mot3". Deux des trois stratégies contextuelles ne se déclenchent donc jamais.
  2. Problème de performance sévère sur le chemin chaud : à chaque frappe, AccentTolerantMatcher.normalize() compile ~10 objets Regex par mot du dictionnaire (3 680 mots), soit ~37 000 compilations de regex par caractère tapé. C’est la cause la plus probable des lenteurs constatées sur appareils low-end (Samsung A21s).
  3. Confidentialité : un point de journalisation interne aux builds de développement a été identifié et documenté (voir §5, section retirée de cette page publique).
  4. Biais de classement contre les mots accentués : le bonus de préfixe (+50) est calculé sans normalisation des accents, ce qui défavorise précisément les graphies correctes (fè, kréyòl) que le clavier veut promouvoir.
  5. Le mode bilingue est incohérent : désactivé dans le chemin normal mais réactivé dans le chemin de récupération A21s ; le dictionnaire français est chargé en mémoire pour rien.
  6. Aucun apprentissage utilisateur : les mots choisis ne renforcent pas les fréquences, et les mots hors dictionnaire ne sont jamais appris.

2. Architecture actuelle du pipeline

Frappe → KeyboardLayoutManager → onKeyPress()
       → InputProcessor.processKeyPress()      (maintient currentWord)
       → onWordChanged(word)                   (service IME)
       → SuggestionEngine.generateDictionarySuggestions(word)
            1. AccentTolerantMatcher (préfixe insensible aux accents)
            2. Fallback Levenshtein si zéro résultat préfixe (≥ 3 lettres)
            3. Scoring (fréquence + bonus préfixe + longueur + accents)
       → onSuggestionsReady() → displaySuggestions() (3 boutons max)

Espace/Entrée → finalizeCurrentWord() → addWordToHistory()
             → generateContextualSuggestions() (N-grams, mot suivant)

Point important : le mode bilingue étant désactivé (enableBilingualSupport() commenté, KreyolInputMethodServiceRefactored.kt:229), le chemin réellement actif est generateDictionarySuggestions() — les chemins bilingues et MIXED décrits dans le code sont inertes.


3. Bugs et incohérences

3.1 N-grams : stratégies bigram et trigram mortes — critique

getNgramSuggestions() (SuggestionEngine.kt:620-687) tente trois lookups : bigram "mot1 mot2", unigram "mot", trigram "mot1 mot2 mot3". Or le fichier creole_ngrams.json ne contient que des clés unigrammes (vérifié : 3 582 clés, aucune ne contient d’espace). Les stratégies 1 et 3, ainsi que leurs bonus de probabilité (+0.2 / +0.4), ne s’exécutent jamais. La prédiction contextuelle se réduit donc à « mot suivant le plus probable après le dernier mot », sans contexte étendu. Correctif : régénérer le modèle avec de vraies clés bigram/trigram dans Dictionnaires/KreyolComplet.py, ou supprimer le code mort.

3.2 Mode bilingue : activation incohérente — majeur

Résultat : un appareil low-end dont la première initialisation échoue se retrouve en mode bilingue, les autres non. Par ailleurs FrenchDictionary (700 mots) est systématiquement chargé (SuggestionEngine.kt:151) alors qu’il n’est jamais consulté dans le chemin actif, et onBilingualSuggestionsReady() est un no-op (KreyolInputMethodServiceRefactored.kt:389-392). Correctif : trancher (réactiver partout ou nulle part), et ne charger le dictionnaire français que si le mode est actif.

3.3 Biais de classement contre les mots accentués — majeur

calculateDictionaryScore() (SuggestionEngine.kt:735) accorde +50 si word.startsWith(input, ignoreCase = true) — comparaison brute, sans normalisation d’accents. Tous les candidats proviennent pourtant d’un match par préfixe normalisé. Conséquence : en tapant fe, un mot comme fenmé reçoit +50 mais fè non ; le malus (-50 relatif) écrase largement le bonus « mot accentué » de +5 (SuggestionEngine.kt:760). Le classement pénalise donc les graphies créoles correctes, à rebours de l’objectif pédagogique du projet. Correctif : appliquer le bonus sur les formes normalisées (AccentTolerantMatcher.normalize(word).startsWith(normalize(input))).

3.4 Bonus Levenshtein jamais appliqué — mineur

Le paramètre levenshteinDistance de calculateDictionaryScore() (SuggestionEngine.kt:730) n’est jamais transmis par aucun appelant : le bonus de correction (+30/+15) et son log sont du code mort. Les corrections orthographiques sont classées uniquement par (distance, fréquence) dans LevenshteinDistance.findClosestMatches().

3.5 Désynchronisation du mot courant — majeur

InputProcessor.currentWord est la seule source de vérité pour les suggestions, mais :

Les suggestions peuvent donc être calculées sur un préfixe fantôme, et processSuggestionSelection() (InputProcessor.kt:293) peut supprimer le mauvais nombre de caractères. Correctif : implémenter onUpdateSelection() et reconstruire currentWord depuis getTextBeforeCursor().

3.6 Race condition sur les suggestions — modéré

Chaque frappe lance une coroutine (SuggestionEngine.kt:385) sans annuler la précédente ni horodater les résultats. Une requête lente (fallback Levenshtein sur 3 680 mots) peut aboutir après la requête suivante plus rapide et afficher des suggestions périmées. Correctif : conserver le Job courant et l’annuler avant chaque nouvelle génération (pattern latestJob?.cancel()), ou utiliser un Flow + collectLatest.

3.7 wordHistory non thread-safe — modéré

wordHistory (SuggestionEngine.kt:75) est une mutableListOf mutée depuis le thread main (addWordToHistory) et lue depuis Dispatchers.Default (getNgramSuggestions). Risque faible mais réel de ConcurrentModificationException ou de lecture incohérente.

3.8 Backspace et émojis — mineur

handleBackspace() (InputProcessor.kt:121) fait deleteSurroundingText(1, 0) : sur un émoji (paire de substitution UTF-16, 2 code units), cela n’efface que la moitié du caractère et produit un caractère invalide. Utiliser la longueur du dernier grapheme cluster (ou sendKeyEvent(KEYCODE_DEL)).

3.9 Divers


4. Performance

Le chemin « une frappe → suggestions » est exécuté à chaque caractère ; c’est lui qu’il faut optimiser en priorité.

4.1 Compilation de regex massive à chaque frappe — critique

AccentTolerantMatcher.normalize() (AccentTolerantMatcher.kt:26-48) crée ~10 objets Regex à chaque appel. findAccentTolerantSuggestions() (AccentTolerantMatcher.kt:82-85) l’appelle sur chacun des 3 680 mots du dictionnaire, à chaque frappe. Ordre de grandeur : ~37 000 compilations de regex + autant de chaînes intermédiaires par caractère tapé — énorme pression GC, particulièrement sensible sur les appareils que le code tente déjà de ménager (fix A21s, monitoring mémoire). Correctifs, par ordre d’impact :

  1. Précalculer la forme normalisée de chaque mot une seule fois au chargement du dictionnaire (stocker Triple(mot, motNormalisé, fréquence)).
  2. Remplacer les regex par une table Char → Char (un when ou un IntArray indexé) ou java.text.Normalizer.normalize(s, NFD) + filtrage des diacritiques.
  3. À défaut, compiler les Regex une fois en val de l’objet.

4.2 Recherche linéaire — majeur

La recherche préfixe parcourt toute la liste (O(n) par frappe). Avec un dictionnaire trié alphabétiquement sur la forme normalisée, une recherche binaire donne la plage de préfixes en O(log n) ; un trie ferait encore mieux. Combiné au 4.1, le coût par frappe deviendrait négligeable.

4.3 Fallback Levenshtein coûteux — modéré

findClosestMatchesNormalized() (LevenshteinDistance.kt:150-187) normalise tout le dictionnaire deux fois (une fois pour le pré-filtre longueur, une fois pour la distance) avec les regex du 4.1, puis calcule une matrice DP complète par candidat. Le pré-calcul des formes normalisées (4.1) et une DP à deux lignes avec sortie anticipée (bande de Ukkonen) réduiraient fortement le pic de latence — précisément dans le cas « l’utilisateur a fait une faute », où la réactivité compte.

4.4 Cache Levenshtein non borné — mineur

LevenshteinDistance.cache (LevenshteinDistance.kt:193) croît sans limite et clearCache() n’est jamais appelé ; calculateCached() n’a d’ailleurs aucun appelant. Supprimer, ou remplacer par un LruCache.

4.5 Travaux parasites au démarrage du clavier — modéré

runAccentTolerantTests() et testCreoleSpecificCases() (KreyolInputMethodServiceRefactored.kt:979-1065) s’exécutent en production à chaque création du service. testCreoleSpecificCases remplace temporairement le listener de suggestions réel : si l’utilisateur tape pendant ces ~600 ms, ses suggestions partent dans le listener de test (UI figée) et la restauration peut écraser un listener légitime. À déplacer en tests unitaires JVM ou à gater derrière BuildConfig.DEBUG.

4.6 Divers


5. Confidentialité

Section retirée de cette page publique : elle détaillait un point de journalisation interne aux builds de développement. Le build de production (release) est vérifié comme non concerné (R8 supprime les logs concernés). Voir l’historique Git du dépôt pour le détail technique complet si besoin.


6. Qualité des prédictions — axes d’amélioration produit

6.1 Le contexte n’influence pas les suggestions pendant la frappe

Le chemin actif generateDictionarySuggestions() classe uniquement par fréquence globale ; le bonus n-gram (+50) n’existe que dans mergeAndRankSuggestions(), appelé par le mode MIXED inutilisé. Après « an ka », taper « ma » devrait favoriser « manjé » si le corpus le suggère — ce n’est pas le cas aujourd’hui. Réintégrer le signal n-gram dans le scoring de frappe est une amélioration à fort impact et faible risque.

6.2 Aucun apprentissage utilisateur

L’infrastructure existe pourtant : la gamification (CreoleDictionaryWithUsage) compte déjà l’usage réel de chaque mot et le persiste. Brancher ce compteur comme signal de personnalisation (score += k·log(usageCount)) donnerait un clavier qui s’adapte à chaque utilisateur à moindre coût.

6.3 Pas de suggestion à la première lettre

MIN_WORD_LENGTH = 2 (SuggestionEngine.kt:23) et le garde-fou de findAccentTolerantSuggestions() (AccentTolerantMatcher.kt:75) suppriment toute suggestion à 1 caractère. Or le kréyòl est riche en mots courts très fréquents (ka 15 519, an 10 729, sé 7 177 — têtes du dictionnaire). Autoriser les suggestions dès la première lettre (éventuellement limitées au top fréquence) améliorerait directement la vitesse de saisie.

6.4 Correction orthographique trop timide

Le fallback Levenshtein (SuggestionEngine.kt:561-563) ne se déclenche que si la recherche préfixe ne renvoie rien. Une faute en début de mot (« bpnjou ») est corrigée, mais « bonjoy » ne le sera jamais si « bonjo… » matche autre chose. Pistes : mélanger systématiquement 1-2 candidats de correction quand le meilleur score préfixe est faible, et proposer une auto-correction à la validation (espace) avec possibilité d’annuler — comportement standard des claviers modernes.

6.5 Régénérer le modèle n-gram

Cf. 3.1 : tant que creole_ngrams.json ne contient pas de clés multi-mots, la prédiction contextuelle reste limitée à un seul mot de contexte. Le pipeline Dictionnaires/KreyolComplet.py est l’endroit où produire de vraies entrées bigram/trigram (attention : le déclencheur CI build-apk.yml filtre sur Dictionnaries/** — chemin mal orthographié — les changements de dictionnaire ne déclenchent pas de build).

6.6 Note annexe — documentation

CLAUDE.md et le README annoncent ~1 867 mots ; le dictionnaire embarqué en contient 3 680. À mettre à jour.


7. Recommandations priorisées

Quick wins (quelques heures, risque faible)

# Action Impact Réf.
1 Précalculer les formes normalisées du dictionnaire au chargement + supprimer les regex de normalize() Latence par frappe divisée par un ordre de grandeur §4.1
2 Gater tous les logs du chemin de saisie derrière BuildConfig.DEBUG Confidentialité + perf §5
3 Corriger le bonus préfixe pour comparer les formes normalisées Classement cohérent des mots accentués §3.3
4 Annuler la coroutine de suggestion précédente à chaque frappe Fin des suggestions périmées §3.6
5 Sortir les tests runAccentTolerantTests/testCreoleSpecificCases du démarrage production Démarrage plus rapide, pas d’écrasement du listener §4.5
6 Supprimer le code mort (applyCaseToSuggestion, calculateCached, paramètre levenshteinDistance, stratégies bigram/trigram si non régénérées) Lisibilité §3.4, §3.9, §4.4
7 Autoriser les suggestions dès 1 lettre (top fréquence) Vitesse de saisie §6.3

Chantiers (impact produit fort, effort plus important)

# Action Impact Réf.
A Régénérer creole_ngrams.json avec clés bigram/trigram (+ corriger le filtre CI Dictionnaries) Prédiction contextuelle réelle §3.1, §6.5
B Réintégrer le signal n-gram dans le scoring pendant la frappe Suggestions contextuelles §6.1
C Personnalisation : brancher les compteurs CreoleDictionaryWithUsage dans le score + apprendre les mots hors dictionnaire Clavier qui s’adapte à l’utilisateur §6.2
D Implémenter onUpdateSelection() et resynchroniser currentWord Fiabilité des suggestions et du remplacement §3.5
E Trancher le sort du mode bilingue (réactiver proprement ou retirer le chargement français) Cohérence + mémoire §3.2
F Index préfixe (tri alphabétique + recherche binaire, ou trie) Scalabilité si le dictionnaire grossit §4.2
G Auto-correction à l’espace + corrections mêlées aux suggestions préfixe Rattrapage des fautes courantes §6.4

8. Addendum — quick wins appliqués (3 juillet 2026)

Les 7 quick wins ont été appliqués et vérifiés (compileDebugKotlin, assembleDebug et 51 tests unitaires verts).

# Quick win Ce qui a été fait
1 Normalisation précalculée AccentTolerantMatcher.normalize() réécrit avec une table char→char (zéro regex) ; SuggestionEngine précalcule les formes normalisées du dictionnaire au chargement (normalizedWords) et fait la recherche préfixe dessus, avec arrêt anticipé dès 10 résultats (le dictionnaire étant trié par fréquence). Bonus : normalize() gère désormais aussi les majuscules accentuées (É→e), bug de l’ancienne version.
2 Logs Le remplacement massif n’était pas nécessaire : R8 supprime déjà tous les Log.* en release (cf. mise à jour du §5). Le spam SHIFT_REAL_DEBUG a été supprimé de InputProcessor et du service.
3 Bonus préfixe normalisé calculateDictionaryScore() utilise AccentTolerantMatcher.startsWith() : taper « fe » favorise désormais fè au même titre que les mots en « fe… ».
4 Annulation coroutines suggestionJob?.cancel() avant chaque génération dans les 4 points d’entrée (generateSuggestions, generateBilingualSuggestions, generateDictionarySuggestions, generateContextualSuggestions).
5 Tests hors production runAccentTolerantTests() / testCreoleSpecificCases() supprimés du service ; AccentTolerantMatchingTest.kt (sourceset main) supprimé ; remplacés par un vrai test JVM AccentTolerantMatcherTest.kt.
6 Code mort Supprimés : applyCaseToSuggestion(), cache Levenshtein (calculateCached/clearCache), paramètre levenshteinDistance de calculateDictionaryScore, stratégies bigram/trigram de getNgramSuggestions() (le modèle n’a que des clés unigrammes), getSuggestionListener(), calculateMatchScore()/getDebugInfo()/runTests() du matcher.
7 Suggestions dès 1 lettre MIN_WORD_LENGTH passé de 2 à 1 : ka, an, sé… sont proposés dès la première frappe.

Chantier G (partie classement) appliqué également : la distance de Levenshtein est désormais propagée jusqu’au score (LevenshteinDistance renvoie (mot, fréquence, distance), calculateDictionaryScore donne un poids dominant à la distance). Vérifié sur émulateur : « mesli » propose désormais mèsi (distance 1) en première position, devant mésyé/mépri (distance 2, plus fréquents). Le second volet du chantier (auto-correction à l’espace, corrections mêlées aux résultats préfixe) reste à faire.

Découverte en passant — la suite de tests n’a jamais été verte (25 échecs préexistants sur main) pour deux raisons corrigées au passage :

À noter également : le script android_keyboard/gradlew est corrompu (il passe les arguments quotés à Gradle, eval manquant) — la CI n’est pas affectée car elle installe Gradle via gradle-version: 8.7, mais en local il faut invoquer java -classpath gradle/wrapper/gradle-wrapper.jar org.gradle.wrapper.GradleWrapperMain <tâche> ou régénérer le wrapper.


Rapport généré par analyse statique du code (branche main, commit 26e73bd) et inspection des assets JSON embarqués.


🗓️ Date du rapport : 03 juillet 2026

✅ Statut : Le point « supprimer le code mort VocabularyStatsActivity/MainActivity » de la Phase 1 a été traité le 04/07/2026 (commit 827254a), en même temps que les corrections UX ci-dessous.

Rapport d’Audit et d’Amélioration de la Gamification

Projet: KreyolKeyb (Clavier Prédictif Créole)
Date: 2026-07-03
Version analysée: 7.0.0
Auteur: Mistral Vibe


📊 Sommaire

  1. Architecture Actuelle
  2. Points Forts
  3. Problèmes Identifiés
  4. Recommandations d’Amélioration
  5. Priorisation
  6. Annexes

1. Architecture Actuelle

1.1 Composants de Gamification

┌─────────────────────────────────────────────────────────────────────┐
│                      ARCHITECTURE GAMIFICATION                         │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  ┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐ │
│  │  SettingsActivity │    │ VocabularyStats  │    │ WordSearch      │ │
│  │  (Niveaux + UI)  │◄───►│  + WordUsage    │    │ + WordScramble  │ │
│  └─────────────────┘    └─────────────────┘    └─────────────────┘ │
│           │                        │                        │         │
│           ▼                        ▼                        ▼         │
│  ┌─────────────────────────────────────────────────────────────────┐ │
│  │            CreoleDictionaryWithUsage.kt                          │ │
│  │  - Tracking mots utilisateur                                    │ │
│  │  - Persistance JSON (creole_dict_with_usage.json)               │ │
│  │  - Cache mémoire + sauvegarde batch                            │ │
│  └─────────────────────────────────────────────────────────────────┘ │
│                                                                     │
│  ┌─────────────────────────────────────────────────────────────────┐ │
│  │            Fragments (ViewPager2)                                │ │
│  │  0: OnboardingFragment → Démarrage                              │ │
│  │  1: StatsFragment → Statistiques vocabulaire                    │ │
│  │  2: WordSearchFragment → Jeu Mots Mêlés                         │ │
│  │  3: WordScrambleFragment → Jeu Mots Mélangés                    │ │
│  │  4: AboutFragment → À propos                                    │ │
│  └─────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘

1.2 Système de Niveaux

8 Niveaux Culturels Créoles (Distribution Gaussienne)

Niveau Emoji Seuil (%) Seuil (3680 mots) Pourcentage Population
Pipirit 🌍 0% 0 0.15%
Ti moun 🌱 1.5% 55 2%
Débrouya 🔥 5% 184 14%
An mitan 💎 12% 442 34%
Kompè Lapen 🐇 25% 920 34%
Kompè Zamba 🐘 45% 1656 14%
Potomitan 👑 70% 2576 2%
Benzo 🧙🏿‍♀️ 100% 3680 0.15%

Formule: calculateGaussianThresholds() dans SettingsActivity.kt (lignes 2070-2093)

1.3 Fichiers Clés

android_keyboard/app/src/main/java/com/example/kreyolkeyboard/
├── gamification/
│   ├── VocabularyStats.kt          # Data class statistiques
│   ├── WordUsageStats.kt           # Data class usage par mot
│   ├── CreoleDictionaryWithUsage.kt # Gestion dictionnaire + tracking
│   ├── WordCommitListener.kt       # Interface callback
│   └── VocabularyStatsActivity.kt   # Activity stats (obsolète?)
├── SettingsActivity.kt              # COEUR: Niveaux + UI + ViewPager2
├── MainActivity.kt                  # Navigation par onglets (obsolète?)
├── wordsearch/
│   ├── WordSearchActivity.kt       # Jeu mots mêlés
│   ├── WordSearchGenerator.kt      # Génération grille
│   └── WordSearchModels.kt          # Data classes
└── wordscramble/
    ├── WordScrambleActivity.kt      # Jeu mots mélangés
    └── WordScrambleModels.kt        # Data classes + chargement

2. Points Forts

✅ Ce qui fonctionne bien

  1. Système de niveaux culturel
    • Noms authentiques créoles (Pipirit, Benzo, Potomitan…)
    • Distribution gaussienne mathématiquement correcte
    • Adaptation automatique à la taille du dictionnaire
  2. Respect de la vie privée
    • Seuls les mots du dictionnaire créole sont trackés
    • Pas de synchronisation cloud (tout local)
    • Filtrage des mots sensibles (URLs, emails, chiffres)
    • Migration automatique des formats de données
  3. Architecture modulaire
    • Séparation claire des responsabilités
    • Data classes bien définies (VocabularyStats, WordUsageStats)
    • Utilisation de Kotlin et coroutines
  4. Persistance optimisée
    • Sauvegarde par batch (SAVE_BATCH_SIZE = 1)
    • Cache en mémoire pour performance
    • Écriture atomique (fichier temporaire + rename)
  5. Intégration avec le clavier
    • Tracking via WordCommitListener
    • Détection des mots validés (espace, ponctuation, entrée)
    • Méthode statique updateWordUsage() accessible depuis le service IME
  6. Jeux éducatifs
    • Word Search: Mots cachés dans grille 8x8
    • Word Scramble: Retrouver l’ordre des lettres
    • Intégration directe avec le dictionnaire créole

3. Problèmes Identifiés

❌ Problèmes Critiques

3.1 Duplication de Code et Redondance

PROBLÈME: Deux activités principales avec logique similaire
├── MainActivity.kt (62 lignes)
│   ├── Onglets: Clavier, Stats, Mots Mêlés, Paramètres
│   └── Navigation basique
│
└── SettingsActivity.kt (3115 lignes)
    ├── ViewPager2 avec 5 fragments
    ├── Onboarding, Stats, WordSearch, WordScramble, About
    └── TOUTE la logique de gamification

Impact:

3.2 Centralisation Excessive dans SettingsActivity

La classe SettingsActivity.kt contient:

Violations:

3.3 Problèmes de Persistance

Code dupliqué:

// Dans CreoleDictionaryWithUsage.kt
private fun saveDictionaryToFile(dict: JSONObject) {
    val file = File(context.filesDir, DICT_FILE)
    file.writeText(dict.toString(2))
}

// Dans SettingsActivity.kt (companion object)
// Sauvegarde avec écriture atomique (meilleure implémentation)
val tempFile = File(context.filesDir, "creole_dict_with_usage.json.tmp")
tempFile.bufferedWriter().use { writer ->
    writer.write(existingData.toString())
}
tempFile.renameTo(usageFile)

Problèmes:

  1. Deux implémentations différentes pour la même fonctionnalité
  2. CreoleDictionaryWithUsage n’utilise pas l’écriture atomique
  3. Pas de synchronisation entre les deux systèmes
  4. Risk de corruption de données

3.4 Gestion de l’État Inconsistante

Problème de cache:

// Dans SettingsActivity.kt
private var cachedTotalWords: Int? = null

private fun getTotalDictionaryWords(): Int {
    cachedTotalWords?.let { return it }  // Cache jamais invalidé!
    // ... chargement depuis assets
}

Problèmes:

3.5 Système de Tracking Fragile

Dans CreoleDictionaryWithUsage.kt:

// Ligne 163: Normalisation basique
val normalized = word.lowercase().trim()

// Ligne 167: Vérification dans le dictionnaire
return if (dictionary.has(normalized)) { ... }

// Problème: La normalisation ne gère pas les accents!
// Ex: "mesli" -> "mesli" (pas trouvé) mais devrait matcher "mèsi"

Impact:

3.6 Fragments vs Activities Séparées

Incohérence architecturale:

├── SettingsActivity (ViewPager2)
│   ├── StatsFragment → Affiche les stats
│   └── WordSearchFragment → Lance WordSearchActivity
│
└── Activities séparées:
    ├── VocabularyStatsActivity.kt (226 lignes)
    └── WordSearchActivity.kt (jeu complet)

Problèmes:

3.7 Code Mort et Commentaires Inutiles

Exemples:

// VocabularyStatsActivity.kt ligne 152-153:
// Note: Le système de niveaux est maintenant géré dans SettingsActivity.kt
// avec la distribution gaussienne et les noms culturels créoles

Problème: Si le système est déplacé, pourquoi garder ce fichier?

3.8 Tests Manquants


4. Recommandations d’Amélioration

4.1 Architecture Globale

🎯 Recommandation 1: Adopter MVVM

┌─────────────────────────────────────────────────────────────────────┐
│                    ARCHITECTURE PROPOSÉE (MVVM)                          │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  ┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐ │
│  │     View         │    │    ViewModel     │    │    Repository   │ │
│  │  (Activities +    │    │  (Gamification    │    │  (Data Layer)   │ │
│  │   Fragments)     │◄───►│   ViewModel)     │◄───►│                 │ │
│  └─────────────────┘    └─────────────────┘    └─────────────────┘ │
│           │                        │                        │         │
│           ▼                        ▼                        ▼         │
│  ┌─────────────────────────────────────────────────────────────────┐ │
│  │  View:                                                                 │ │
│  │  - MainActivity (navigation)                                       │ │
│  │  - SettingsActivity (config)                                       │ │
│  │  - GameActivity (jeux)                                             │ │
│  └─────────────────────────────────────────────────────────────────┘ │
│  ┌─────────────────────────────────────────────────────────────────┐ │
│  │  ViewModel:                                                                        │ │
│  │  - GamificationViewModel: niveaux, stats, progression               │ │
│  │  - WordUsageViewModel: tracking des mots                           │ │
│  │  - GameViewModel: logique des jeux                                │ │
│  └─────────────────────────────────────────────────────────────────┘ │
│  ┌─────────────────────────────────────────────────────────────────┐ │
│  │  Repository:                                                                      │ │
│  │  - DictionaryRepository: chargement, sauvegarde                    │ │
│  │  - UsageRepository: tracking, stats                               │ │
│  └─────────────────────────────────────────────────────────────────┘ │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

Fichiers à créer:

app/src/main/java/com/example/kreyolkeyboard/
├── viewmodel/
│   ├── GamificationViewModel.kt
│   ├── DictionaryViewModel.kt
│   └── WordUsageViewModel.kt
├── repository/
│   ├── DictionaryRepository.kt
│   └── UsageRepository.kt
└── gamification/
    ├── level/
    │   ├── LevelSystem.kt
    │   └── LevelThresholds.kt
    └── stats/
        ├── VocabularyStats.kt (existant)
        └── WordUsageStats.kt (existant)

🎯 Recommandation 2: Séparer SettingsActivity

Diviser SettingsActivity.kt (3115 lignes) en:

// 1. GamificationManager.kt (~300 lignes)
class GamificationManager {
    fun calculateGaussianThresholds(): IntArray
    fun getCurrentLevel(wordsDiscovered: Int): Level
    fun getNextLevelInfo(wordsDiscovered: Int): LevelInfo
}

// 2. SettingsViewModel.kt (~200 lignes)
class SettingsViewModel : ViewModel() {
    val levels: LiveData<List<Level>>
    val currentLevel: LiveData<Level>
    fun loadStats()
}

// 3. SettingsActivity.kt (~800 lignes)
class SettingsActivity : AppCompatActivity() {
    // Seulement la logique UI + ViewPager2
    private lateinit var viewModel: SettingsViewModel
    // ... gestion des fragments
}

// 4. Fragments séparés (déjà bien)
// StatsFragment.kt, OnboardingFragment.kt, etc.

4.2 Améliorations du Système de Niveaux

🎯 Recommandation 3: Externaliser la Configuration des Niveaux

Actuellement:

// Hardcodé dans SettingsActivity.kt
private fun calculateGaussianThresholds(): IntArray {
    val percentages = doubleArrayOf(0.0, 0.015, 0.05, 0.12, 0.25, 0.45, 0.70, 1.0)
    // ...
}

Proposition:

// LevelConfiguration.kt
data class LevelConfig(
    val name: String,
    val emoji: String,
    val percentage: Double,
    val description: String
)

object LevelConfiguration {
    val ALL_LEVELS = listOf(
        LevelConfig("Pipirit", "🌍", 0.0, "Les tout premiers pas"),
        LevelConfig("Ti moun", "🌱", 0.015, "Débutant"),
        LevelConfig("Débrouya", "🔥", 0.05, "Débutant avancé"),
        // ...
    )
    
    fun getThresholds(totalWords: Int): IntArray = ...
}

Avantages:

🎯 Recommandation 4: Gérer les Accents dans le Tracking

Problème actuel: CreoleDictionaryWithUsage.kt ligne 163

val normalized = word.lowercase().trim()
// Ne gère pas: mèsi, kréyòl, etc.

Solution: Utiliser AccentTolerantMatcher (déjà existant)

// Dans CreoleDictionaryWithUsage.kt
fun normalizeForTracking(word: String): String {
    return AccentTolerantMatcher.normalize(word).lowercase().trim()
}

fun incrementWordUsage(word: String): Boolean {
    val normalized = normalizeForTracking(word)
    // ... vérifier dans dictionnaire avec normalisation
}

4.3 Améliorations de la Persistance

🎯 Recommandation 5: Repository Pattern pour le Dictionnaire

// DictionaryRepository.kt
interface DictionaryRepository {
    suspend fun loadDictionary(): Map<String, WordData>
    suspend fun updateWordUsage(word: String): Boolean
    suspend fun getStats(): VocabularyStats
    suspend fun save()
}

class FileDictionaryRepository(private val context: Context) : DictionaryRepository {
    private var dictionary: Map<String, WordData> = emptyMap()
    private var pendingUpdates = mutableMapOf<String, Int>()
    
    override suspend fun loadDictionary() = withContext(Dispatchers.IO) {
        // Charger depuis assets + fichier usage
    }
    
    override suspend fun updateWordUsage(word: String): Boolean = withContext(Dispatchers.IO) {
        // Mise à jour + sauvegarde atomique
    }
}

Avantages:

🎯 Recommandation 6: Cache avec Invalidation

// CacheManager.kt
class CacheManager {
    private val cache = mutableMapOf<String, Any>()
    private val cacheExpiry = mutableMapOf<String, Long>()
    private val CACHE_TTL_MS = 300_000L // 5 minutes
    
    fun <T> getOrLoad(key: String, loader: () -> T): T {
        if (isCacheValid(key)) {
            return cache[key] as T
        }
        val value = loader()
        cache[key] = value
        cacheExpiry[key] = System.currentTimeMillis() + CACHE_TTL_MS
        return value
    }
    
    fun invalidate(key: String) {
        cache.remove(key)
        cacheExpiry.remove(key)
    }
    
    fun invalidateAll() {
        cache.clear()
        cacheExpiry.clear()
    }
    
    private fun isCacheValid(key: String): Boolean {
        val expiry = cacheExpiry[key] ?: return false
        return System.currentTimeMillis() < expiry
    }
}

4.4 Améliorations des Jeux

🎯 Recommandation 7: Intégrer les Jeux dans la Gamification

Problème actuel:

Solution:

// GameManager.kt
interface GameManager {
    fun onGameComplete(gameType: GameType, score: Int, wordsFound: List<String>)
    fun updateGlobalProgress(word: String)
}

// Implémentation:
class GamificationGameManager(
    private val usageRepository: UsageRepository
) : GameManager {
    override fun onGameComplete(gameType: GameType, score: Int, wordsFound: List<String>) {
        // Mettre à jour les stats globales
        wordsFound.forEach { word ->
            usageRepository.incrementUsage(word)
        }
        // Ajouter bonus de score
    }
}

🎯 Recommandation 8: Système de Récompenses

// RewardSystem.kt
data class Reward(
    val type: RewardType,
    val points: Int,
    val description: String
)

enum class RewardType {
    DAILY_LOGIN,
    WORD_DISCOVERED,
    WORD_MASTERED,
    GAME_COMPLETED,
    LEVEL_UP
}

class RewardManager {
    private val rewards = mutableListOf<Reward>()
    
    fun addReward(reward: Reward) {
        rewards.add(reward)
    }
    
    fun getTotalPoints(): Int = rewards.sumOf { it.points }
    
    fun getUnclaimedRewards(): List<Reward> = rewards.filter { !it.claimed }
}

4.5 Améliorations de l’UI

🎯 Recommandation 9: Composants UI Réutilisables

Problème: La création des vues est dupliquée et inline

Solution: Créer des composants custom

// LevelBadge.kt
class LevelBadge @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null
) : LinearLayout(context, attrs) {
    
    fun setLevel(level: Level) {
        findViewById<TextView>(R.id.levelEmoji).text = level.emoji
        findViewById<TextView>(R.id.levelName).text = level.name
        // Style dynamique
        setBackgroundColor(level.color)
    }
}

// ProgressCard.kt
class ProgressCard @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null
) : CardView(context, attrs) {
    
    fun setProgress(current: Int, max: Int, level: Level) {
        progressBar.max = max
        progressBar.progress = current
        levelBadge.setLevel(level)
    }
}

🎯 Recommandation 10: Animation et Feedback

// LevelUpAnimator.kt
class LevelUpAnimator(private val context: Context) {
    fun showLevelUpAnimation(oldLevel: Level, newLevel: Level) {
        val dialog = AlertDialog.Builder(context)
            .setView(R.layout.dialog_level_up)
            .setCancelable(false)
            .create()
        
        dialog.findViewById<TextView>(R.id.tvNewLevel)?.text = newLevel.name
        dialog.findViewById<TextView>(R.id.tvMessage)?.text = "Félisitasyon! Ou monte nan ${newLevel.name}"
        
        // Animation
        val animation = AnimationUtils.loadAnimation(context, R.anim.level_up)
        dialog.findViewById<View>(R.id.animationView)?.startAnimation(animation)
        
        dialog.show()
        
        // Auto-dismiss après 5 secondes
        Handler(Looper.getMainLooper()).postDelayed({
            dialog.dismiss()
        }, 5000)
    }
}

4.6 Tests et Qualité

🎯 Recommandation 11: Suite de Tests pour la Gamification

// GamificationTest.kt
class LevelSystemTest {
    @Test
    fun `test level thresholds calculation`() {
        val thresholds = LevelSystem.calculateThresholds(3680)
        assertEquals(0, thresholds[0])      // Pipirit
        assertEquals(55, thresholds[1])    // Ti moun (1.5%)
        assertEquals(184, thresholds[2])   // Débrouya (5%)
        assertEquals(3680, thresholds[7])  // Benzo (100%)
    }
    
    @Test
    fun `test level progression`() {
        val levelSystem = LevelSystem(3680)
        assertEquals(Level.PIPIRIT, levelSystem.getLevel(0))
        assertEquals(Level.TI_MOUN, levelSystem.getLevel(55))
        assertEquals(Level.BENZO, levelSystem.getLevel(3680))
    }
}

class UsageTrackingTest {
    @Test
    fun `test word usage tracking`() {
        val repo = InMemoryUsageRepository()
        repo.incrementUsage("bonjou")
        repo.incrementUsage("bonjou")
        
        assertEquals(2, repo.getUsageCount("bonjou"))
    }
    
    @Test
    fun `test accent normalization`() {
        val repo = InMemoryUsageRepository()
        repo.incrementUsage("mèsi")
        
        assertEquals(1, repo.getUsageCount("mesi")) // Normalisé
    }
}

🎯 Recommandation 12: Tests d’Intégration

// GamificationIntegrationTest.kt
@ExperimentalCoroutinesApi
@ExtendWith(InstantTaskExecutorRule::class)
class GamificationIntegrationTest {
    @get:Rule
    val coroutineTestRule = CoroutineTestRule()
    
    @Test
    fun `test full gamification flow`() = coroutineTestRule.runBlockingTest {
        val repo = FakeDictionaryRepository()
        val viewModel = GamificationViewModel(repo)
        
        // Simuler l'utilisation de mots
        repo.incrementUsage("bonjou")
        repo.incrementUsage("mèsi")
        
        // Vérifier la mise à jour des stats
        val stats = viewModel.stats.value
        assertEquals(2, stats?.wordsDiscovered)
        
        // Vérifier le niveau
        assertEquals(Level.PIPIRIT, viewModel.currentLevel.value)
    }
}

5. Priorisation

🎯 Roadmap d’Amélioration

Phase 1: Critique (À faire immédiatement)

# Tâche Complexité Impact Effort (jours)
1 Supprimer le code mort (VocabularyStatsActivity) Faible ⭐⭐⭐ 0.5
2 Fixer la normalisation des accents dans le tracking Moyenne ⭐⭐⭐⭐ 1
3 Unifier la persistance (écriture atomique partout) Moyenne ⭐⭐⭐⭐ 1
4 Clarifier la navigation (MainActivity vs SettingsActivity) Élevée ⭐⭐⭐ 2

Phase 2: Architecture (Moyen terme)

# Tâche Complexité Impact Effort (jours)
5 Extraire GamificationManager de SettingsActivity Élevée ⭐⭐⭐⭐ 3
6 Implémenter DictionaryRepository Élevée ⭐⭐⭐⭐ 2
7 Créer GamificationViewModel Moyenne ⭐⭐⭐⭐ 2
8 Externaliser LevelConfiguration Faible ⭐⭐ 0.5

Phase 3: Fonctionnalités (Long terme)

# Tâche Complexité Impact Effort (jours)
9 Intégrer les jeux dans la progression globale Élevée ⭐⭐⭐ 3
10 Implémenter le système de récompenses Moyenne ⭐⭐⭐ 2
11 Créer des composants UI réutilisables Moyenne ⭐⭐⭐ 2
12 Ajouter des animations de niveau Faible ⭐⭐ 1

Phase 4: Qualité (Continu)

# Tâche Complexité Impact Effort (jours)
13 Suite de tests unitaire gamification Moyenne ⭐⭐⭐⭐ 2
14 Tests d’intégration Élevée ⭐⭐⭐ 3
15 Documentation architecture Faible ⭐⭐ 1

6. Annexes

6.1 Extraits de Code Actuels

Système de Niveaux (SettingsActivity.kt:2016-2093)

private fun getCurrentLevel(wordsDiscovered: Int): String {
    val thresholds = calculateGaussianThresholds()
    return when {
        wordsDiscovered >= thresholds[7] -> "🧙🏿‍♀️ Benzo"
        wordsDiscovered >= thresholds[6] -> "👑 Potomitan"
        // ...
        else -> "🌍 Pipirit"
    }
}

private fun calculateGaussianThresholds(): IntArray {
    val totalWords = getTotalDictionaryWords()
    val percentages = doubleArrayOf(0.0, 0.015, 0.05, 0.12, 0.25, 0.45, 0.70, 1.0)
    return IntArray(8) { index ->
        if (index == 7) totalWords else (totalWords * percentages[index]).toInt()
    }
}

Tracking des Mots (CreoleDictionaryWithUsage.kt:158-220)

fun incrementWordUsage(word: String): Boolean {
    val normalized = word.lowercase().trim()
    if (!isValidForTracking(normalized)) return false
    
    return if (dictionary.has(normalized)) {
        val wordData = getWordDataSafe(normalized)
        val currentCount = wordData?.getInt("user_count") ?: 0
        wordData?.put("user_count", currentCount + 1)
        unsavedChanges++
        if (unsavedChanges >= SAVE_BATCH_SIZE) saveDictionary()
        true
    } else {
        false // Mot pas dans le dictionnaire
    }
}

private fun isValidForTracking(word: String): Boolean {
    if (word.length < MIN_WORD_LENGTH) return false
    if (word.any { it.isDigit() }) return false
    if (word.contains("http") || word.contains("www") || word.contains(".com")) return false
    if (word.contains("@")) return false
    return true
}

6.2 Métriques de Complexité

Fichier Lignes Complexité Cyclomatique Commentaire
SettingsActivity.kt 3115 ~50+ À refactorer urgent
CreoleDictionaryWithUsage.kt 466 ~15 Bon mais à améliorer
VocabularyStatsActivity.kt 226 ~10 Code mort?
WordSearchActivity.kt ~300 ~12 OK
WordScrambleActivity.kt ~200 ~10 OK

6.3 Outils Recommandés


📝 Conclusion

Le système de gamification de KreyolKeyb a une bonne base conceptuelle (niveaux culturels, distribution gaussienne, respect de la vie privée) mais souffre de problèmes architecturaux majeurs qui entravent sa maintenabilité et son évolutivité.

Priorité absolue:

  1. Nettoyer le code mort et la duplication
  2. Séparer la logique métier de l’UI (MVVM)
  3. Fixer les bugs de tracking (accents)

Impact potentiel:

Estimation totale: ~15-20 jours de développement pour la refactorisation complète.


Document généré par Mistral Vibe - 2026-07-03


🗓️ Date du rapport : 04 juillet 2026

✅ Statut : La plupart des « quick wins » listés en fin de rapport ont été appliqués le jour même (commit 827254a, 2026-07-04) : mojibake corrigé, version figée remplacée par BuildConfig.VERSION_NAME, contraste illisible corrigé, et le code mort confirmé (MainActivity, VocabularyStatsActivity, Activities standalone des jeux) supprimé.

Rapport d’audit UX — Écrans de l’application Klavyé Kréyòl Karukera

Ce rapport complète rapport.md (audit du moteur de suggestions) en couvrant cette fois les écrans de l’application companion : Paramètres (5 onglets), tableau de bord de progression, et les deux jeux de vocabulaire. Méthode : inspection du code (Activities, Fragments, layouts XML) croisée avec des captures d’écran live sur émulateur (Pixel 5, Android 14).

1. Résumé exécutif

  1. Un écran entier de gamification est mort-né : VocabularyStatsActivity (dashboard complet, thème sombre) n’est reliée à aucun bouton de l’UI shippée — remplacée en doublon par un onglet Stats qui réimplémente la même chose en thème clair.
  2. Chaque jeu existe deux fois (Activity standalone + Fragment embarqué dans les Paramètres), avec deux bases de code distinctes qui ont déjà divergé visuellement (thème clair/sombre) et fonctionnellement (boutons Indice/Thèmes présents ou absents selon la version).
  3. Un bug de contraste texte, dupliqué deux fois (Fragment Kotlin + Activity XML) : trois libellés clés du jeu Mots Mélangés sont quasi invisibles (pas de textColor défini).
  4. La version affichée dans « À Propos » est figée en dur (« 6.5.1 ») et ne suit pas le build réel (7.0.0 actuellement) — désynchronisée à chaque release.
  5. 107 couleurs hexadécimales codées en dur dans SettingsActivity.kt malgré une palette complète déjà définie dans colors.xml, sans aucun themes.xml/styles.xml — dette de cohérence visuelle qui grandit à chaque écran ajouté.
  6. Boutons non-fonctionnels qui ressemblent à des boutons actifs : « Indice », « Thèmes », partage de fin de partie sont tous des TODO no-op dans le jeu Mots Mêlés standalone — l’utilisateur clique et rien ne se passe.
  7. Zéro accessibilité : aucun contentDescription dans toute l’application.

2. Inventaire des écrans et navigation

Écran Fichier Accessible depuis l’UI ?
SettingsActivity (launcher, 5 onglets) SettingsActivity.kt (~3200 lignes, construit entièrement en code, sans layout XML) Oui — écran d’accueil
VocabularyStatsActivity (dashboard autonome, thème sombre) gamification/VocabularyStatsActivity.kt + activity_vocabulary_stats.xml Non — orpheline, seule MainActivity (elle-même absente du manifest) y référence un lien
WordSearchActivity (Mots Mêlés standalone, thème sombre) wordsearch/WordSearchActivity.kt + activity_word_search.xml Oui, via VocabularyStatsActivity.launchWordSearchGame() — mais cette dernière est elle-même inatteignable ; dupliqué par WordSearchFragment (thème clair) dans les Paramètres
WordScrambleActivity (Mots Mélangés standalone, thème clair) wordscramble/WordScrambleActivity.kt + activity_word_scramble.xml Dupliqué par WordScrambleFragment dans les Paramètres, pas de lien direct depuis l’UI shippée
MainActivity + activity_main.xml / activity_main_tabs.xml MainActivity.kt Code mort — absente du manifest, ne peut pas être lancée

En pratique, un utilisateur normal ne voit que SettingsActivity et ses 5 onglets. Les trois autres Activities ne sont accessibles que via adb shell am start direct (comme fait pour cet audit) — ce qui signifie que tout le travail de style/fonctionnalité qui y a été investi (thèmes sombres, boutons Indice/Thèmes, animations de score) n’est jamais vu par un utilisateur réel.

3. Bugs d’affichage et incohérences

3.1 Caractère corrompu (mojibake) visible en production

SettingsActivity.kt:2460 — text = "� Chargement..." : le caractère d’encodage cassé s’affiche brièvement dans l’en-tête de l’onglet Mots Mêlés pendant le chargement du thème.

3.2 Version figée en dur, désynchronisée du build

SettingsActivity.kt:1245 — text = "Version : 6.5.1\n" + ... : chaîne littérale jamais mise à jour, alors que build.gradle déclare versionName "7.0.0". L’onglet « À Propos » ment sur la version installée à chaque release depuis au moins deux versions. Corriger avec BuildConfig.VERSION_NAME.

3.3 Grille Mots Mêlés tronquée à l’écran (standalone)

activity_word_search.xml:57 déclare android:numColumns="10", mais WordSearchActivity.kt:85 génère toujours une grille 8×8 et écrase la valeur à l’exécution (gridView.numColumns = puzzle.gridSize, ligne 112). Conséquence visible : sur la capture live, la dernière ligne de lettres est coupée par le bas de la grille, le conteneur ayant été dimensionné pour 10 colonnes de large mais la grille réelle génère un ratio différent — rendu clairement moins propre que la version embarquée (WordSearchFragment), qui n’a pas ce problème.

3.4 Boutons d’apparence fonctionnelle qui ne font rien (jeu Mots Mêlés standalone)

Visibles et stylés sur la capture live (« 💡 INDICE », « 🎨 THÈMES »), mais tous des TODO no-op côté code :

Un utilisateur qui appuie sur « Indice » ou « Thèmes » ne reçoit aucun retour visuel — pas même un message d’erreur.

3.5 Grand espace vide dans l’onglet Statistiques

Capture live de l’onglet « Kréyòl an mwen » : un vide vertical d’environ 300px sépare la carte « Mots à Découvrir » de la section « Mots les plus utilisés », sans élément visible pour l’expliquer — probablement une marge/hauteur de conteneur mal calculée.

4. Accessibilité

Aucun contentDescription n’existe dans l’ensemble de res/layout/*.xml (grilles de jeu, boutons icône, barres de progression). Un utilisateur avec lecteur d’écran ne peut identifier aucun élément interactif de l’application par son nom.

4.1 Contraste texte quasi nul — bug dupliqué deux fois

Trois libellés du jeu Mots Mélangés n’ont aucune couleur de texte définie et héritent d’une couleur par défaut illisible sur le fond clair (#F5F5F5) :

Le fait que le même bug existe indépendamment dans les deux implémentations illustre concrètement le risque de la duplication de code (constat §6). Comparer avec les éléments voisins qui, eux, définissent bien une couleur explicite (title l.2778 → #1976D2, tvScore → #4CAF50) : l’omission n’est pas un choix de design, c’est un oubli.

5. Cohérence visuelle / dette de style

6. Code mort et duplication

7. Autres constats visuels (issus des captures live)

8. Recommandations priorisées

Priorité Action Effort
Quick win Corriger le mojibake SettingsActivity.kt:2460 Trivial
Quick win Remplacer "Version : 6.5.1" par BuildConfig.VERSION_NAME (SettingsActivity.kt:1245) Trivial
Quick win Ajouter setTextColor(...) aux 3 libellés illisibles, dans les 2 fichiers concernés Trivial
Quick win Fixer android:numColumns dans activity_word_search.xml pour refléter la grille 8×8 réelle Trivial
Quick win Supprimer MainActivity.kt + activity_main.xml + activity_main_tabs.xml (code mort confirmé) Trivial
Chantier Décider du sort de VocabularyStatsActivity : soit la relier réellement (bouton dans l’onglet Stats), soit la supprimer au profit de StatsFragment Moyen
Chantier Dédupliquer les jeux : garder une seule implémentation par jeu (Fragment réutilisé dans l’Activity standalone, ou inversement) pour arrêter la divergence déjà observée Important
Chantier Unifier le thème clair/sombre entre tous les écrans de jeu/dashboard Moyen
Chantier Créer un themes.xml/styles.xml et migrer progressivement les Color.parseColor vers @color/... Important
Chantier Implémenter ou retirer les boutons no-op (Indice, Thèmes, partage) du jeu Mots Mêlés standalone Moyen
Chantier Ajouter les contentDescription sur les éléments interactifs (accessibilité) Important
Chantier Externaliser les libellés UI vers strings.xml (prérequis à toute traduction future) Important

🗓️ Date du rapport : 09 juillet 2026

✅ Statut : Aucun lag perceptible mesuré (calcul des suggestions en dessous du plancher de mesure ADB, ~100 ms de bruit). Un bug de casse mineur identifié (majuscules intempestives sur la 1ʳᵉ suggestion d’un message). Les 3 trous de dictionnaire (blòké, apeti/lapeti, nwit/lannwit) restent ouverts — voir le rapport du 10 juillet, qui confirme qu’ils persistent même après un enrichissement du dataset source.

Rapport de test — Suggestions du clavier Kréyòl Karukera en conditions réelles

Date : 09 juillet 2026 Heure : 15:47 – 16:08 (CEST) Testeur : Claude Code (agent), à la demande de l’utilisateur Version testée : 7.0.1 (versionCode 70001), compilée depuis main à jour

Environnement de test

Élément Détail
Émulateur AVD kreyol_test — Pixel 5, Android 14 (API 34), image google_apis x86_64
Affichage Fenêtre visible via WSLg (X11/Wayland)
APK Potomitan_Kreyol_Keyboard_v7.0.1_debug_2026-07-09.apk, recompilé pour l’occasion
IME com.potomitan.kreyolkeyboard/com.example.kreyolkeyboard.KreyolInputMethodServiceRefactored — activé et sélectionné par défaut
Application cible Google Messages (com.google.android.apps.messaging), nouvelle conversation SMS vers un contact fictif 0690654321

L’émulateur n’a ni SIM ni réseau : les messages ne partent pas réellement, mais chaque envoi vide le champ de saisie et crée une bulle dans le fil, ce qui simule un véritable échange de messages continu (contexte n-gramme conservé d’un message à l’autre, comme dans une vraie conversation).

Méthodologie

Résultats — 50 phrases kréyòl du quotidien

# Phrase tapée Texte final dans le champ Suggestions affichées (dernier mot)
1 bèl bonjou Bèl bonjou 1 : bonjou
2 sa ou fè Sa ou fè 5 : fè, fèt, fèmé, fè-nou, fè-y
3 ka ou fè jòdi a Ka ou fè jòdi a 5 : an, adan, a-y, anba, asi
4 mwen la wi Mwen la wi 1 : wi
5 ou byen Ou byen 3 : byen, byendéfwa, byenmèsi
6 mèsi anpil Mèsi anpil 1 : anpil
7 an ka vini An ka vini 2 : vini, vini-zòt
8 ki lè i yé Ki lè i yé 5 : yé, yenki, yè, yépa, yen
9 ou vlé manjé Ou vlé manjé 2 : manjé, manjé-la
10 mwen fen Mwen fen 2 : fen, fenfon
11 an bwè an tigout dlo An bwè an tigout dlo 2 : dlo, dlo-la
12 koté ou yé Koté ou yé 5 : yé, yenki, yè, yépa, yen
13 an kaz mwen An kaz mwen 4 : mwen, mwenmenm, mwens, mwen-la
14 ou ka travay jòdi a Ou ka travay jòdi a 5 : an, adan, a-y, anba, asi
15 wi mwen ka travay Wi mwen ka travay 5 : travayè, travay, travayè-la, travay-la, travay-li
16 an ka alé lékòl An ka alé lékòl 2 : lékòl, lékòl-la
17 timoun yo la Timoun yo la 5 : la, lang, lavi, laplaj, larichès
18 gran moun ka palé Gran moun ka palé 4 : palé, palé-w, palé-la, palé-mwen
19 fanmi mwen la Fanmi mwen la 5 : la, lang, lavi, laplaj, larichès
20 an renmen’w An renmen’w 5 : wouvè, wayayay, woy, wi, woulé
21 sa ka fè plézi Sa ka fè plézi 1 : plézi
22 bon apeti Bon apeti 5 : avèti, apési, pati, péyi, pété
23 bon nwit Bon nwit 5 : nuit, ni, dwèt, wi, pit
24 dòmi byen Dòmi byen 3 : byen, byendéfwa, byenmèsi
25 rété la Rété la 5 : la, lang, lavi, laplaj, larichès
26 vini isi Vini isi 2 : isidan, isi
27 an ka vann fwi An ka vann fwi 2 : fwi, fwitaj
28 jaden an bel Jaden an bel 4 : bèl, bèl-la, bèlté, bel
29 soley ka bril jòdi a Soley ka bril jòdi a 5 : an, adan, a-y, anba, asi
30 lapli ka tonbé Lapli ka tonbé 1 : tonbé
31 lanmè bel toubannman Lanmè bel toubannman 1 : toupannan
32 an ka péché pwason An ka péché pwason 1 : pwason
33 sé bon manjé Sé bon manjé 2 : manjé, manjé-la
34 dokan an ouvè Dokan an ouvè 1 : ouvè
35 bous mwen vid Bous mwen vid 1 : vid
36 lajan ka manké Lajan ka manké 1 : manké
37 travay la red Travay la red 5 : rèd, rédé, rédé-yo, rédé-y, red
38 an fatigé anpil An fatigé anpil 1 : anpil
39 an bizwen répozé An bizwen répozé 5 : dépozé, réponn, pozé, opozé, répondè
40 telefòn mwen kasé Telefòn mwen kasé 1 : kasé
41 voiti an pann Voiti an pann 5 : pannan, pann, pannansitan, pannyé, pannyé-la
42 wout la blòké Wout la blòké 5 : baké, blo, blé, blagé, lòdè
43 lékòl fini Lékòl fini 1 : fini
44 lévé bonnè Lévé bonnè 1 : bonnè
45 kouché ta Kouché ta 5 : tan, tann, tanbou, ta, tata
46 fè cho jòdi a Fè cho jòdi a 5 : an, adan, a-y, anba, asi
47 van ka soufflé Van ka soufflé 3 : souflé, soufè, souplé
48 mizik la bon Mizik la bon 5 : bon, bonjou, bonmaten-la, bondyé, bonswa
49 dansé kréyòl Dansé kréyòl 4 : kréyòl, kréyòl-gwadloup, kréyol, kréyol-la
50 viv kréyòl gwadloup Viv kréyòl gwadloup 2 : gwadloup, gwadloupéyen

Observations

Conclusion (1ʳᵉ passe)

Le pipeline de suggestions fonctionne de façon fiable sur l’ensemble des 50 phrases testées, avec une bonne couverture du vocabulaire courant et une gestion correcte des accents. Piste d’amélioration concrète identifiée : enrichir creole_dict.json avec blòké, apeti/lapeti et nwit/lannwit, absents du dictionnaire actuel et donc mal servis par le fallback Levenshtein.


Addendum — Rapidité et pertinence détaillée (09 juillet 2026, 20:53 CEST)

Second passage sur le même APK 7.0.1, cette fois avec l’émulateur affiché en fenêtre (WSLg) et deux angles d’analyse supplémentaires demandés : la rapidité des suggestions et la progression caractère par caractère de la pertinence sur la première phrase. Même méthodologie de frappe (taps réels sur le clavier custom), nouveau fil de conversation avec un contact fictif différent (0690999888) pour repartir sur un historique n-gramme propre.

Rapidité — méthode et limite de mesure

Pour chaque phrase, le script clique sur la dernière touche du dernier mot en notant l’horodatage juste avant le tap (horloge hôte Python), puis sonde le logcat de l’émulateur (adb logcat -v epoch) jusqu’à l’apparition de la ligne displaySuggestions appelé avec ... correspondante, et calcule le delta.

Limite importante : cette mesure inclut l’aller-retour ADB (hôte WSL → émulateur) en plus du calcul réel côté clavier, et dépend de la synchronisation d’horloge hôte/émulateur. Sur les 50 phrases mesurées :

Une partie des valeurs est négative, ce qui est physiquement impossible pour un vrai délai de calcul — cela révèle simplement que le bruit de mesure (gigue d’horloge + aller-retour ADB, de l’ordre de ±100 ms) est supérieur au temps de calcul réel des suggestions. Autrement dit : le calcul lui-même est trop rapide pour être isolé par cette méthode de mesure côté hôte. C’est en soi une conclusion positive et cohérente avec les correctifs de performance déjà appliqués le 3 juillet 2026 (normalisation précalculée du dictionnaire, arrêt anticipé de la recherche préfixe — voir le rapport d’audit du moteur de suggestions ci-dessus, §4.1 et §8) : aucun ralentissement perceptible n’a été observé sur les 3 680 mots du dictionnaire, y compris sur les phrases déclenchant un fallback Levenshtein (ex. phrase 31, toubannman, delta mesuré : +136 ms — dans le même ordre de grandeur bruité que le reste).

Seul un point dépasse nettement le lot : la frappe de l’espace après « Bèl » (voir tableau de progression ci-dessous) a montré +319 ms — à surveiller si le phénomène se reproduit sur d’autres mesures, mais un seul échantillon ne permet pas de conclure à une régression.

Verdict rapidité : aucun lag perceptible constaté sur l’émulateur (Pixel 5 / Android 14) — les suggestions sont prêtes essentiellement au moment où la frappe est traitée, sans décalage visible à l’écran ni dans les captures.

Détail des 50 latences mesurées | # | Phrase | Latence mesurée (dernier mot) | |---|---|---| | 1 | bèl bonjou | -39 ms | | 2 | sa ou fè | +7 ms | | 3 | ka ou fè jòdi a | -37 ms | | 4 | mwen la wi | -81 ms | | 5 | ou byen | +165 ms | | 6 | mèsi anpil | -6 ms | | 7 | an ka vini | -23 ms | | 8 | ki lè i yé | -12 ms | | 9 | ou vlé manjé | -74 ms | | 10 | mwen fen | -73 ms | | 11 | an bwè an tigout dlo | -52 ms | | 12 | koté ou yé | -73 ms | | 13 | an kaz mwen | -58 ms | | 14 | ou ka travay jòdi a | -78 ms | | 15 | wi mwen ka travay | -85 ms | | 16 | an ka alé lékòl | -79 ms | | 17 | timoun yo la | -52 ms | | 18 | gran moun ka palé | -60 ms | | 19 | fanmi mwen la | -49 ms | | 20 | an renmen'w | -56 ms | | 21 | sa ka fè plézi | -91 ms | | 22 | bon apeti | +27 ms | | 23 | bon nwit | +14 ms | | 24 | dòmi byen | -67 ms | | 25 | rété la | -77 ms | | 26 | vini isi | -64 ms | | 27 | an ka vann fwi | -41 ms | | 28 | jaden an bel | -65 ms | | 29 | soley ka bril jòdi a | -87 ms | | 30 | lapli ka tonbé | -68 ms | | 31 | lanmè bel toubannman | +136 ms | | 32 | an ka péché pwason | -66 ms | | 33 | sé bon manjé | -68 ms | | 34 | dokan an ouvè | -75 ms | | 35 | bous mwen vid | -63 ms | | 36 | lajan ka manké | -18 ms | | 37 | travay la red | -63 ms | | 38 | an fatigé anpil | -44 ms | | 39 | an bizwen répozé | +35 ms | | 40 | telefòn mwen kasé | -76 ms | | 41 | voiti an pann | -74 ms | | 42 | wout la blòké | -51 ms | | 43 | lékòl fini | -84 ms | | 44 | lévé bonnè | -44 ms | | 45 | kouché ta | -13 ms | | 46 | fè cho jòdi a | -75 ms | | 47 | van ka soufflé | +7 ms | | 48 | mizik la bon | -79 ms | | 49 | dansé kréyòl | -83 ms | | 50 | viv kréyòl gwadloup | +96 ms |

Pertinence — progression caractère par caractère sur « Bèl bonjou »

Contrairement au reste du test (qui ne capture que l’état final des suggestions par mot), la première phrase a été tapée avec une capture après chaque caractère individuel, pour observer concrètement comment les propositions évoluent pendant la frappe :

Caractère tapé Champ Latence Suggestions affichées
b B +18 ms BA, BYEN, BÈL, BITEN, BON
è Bè -20 ms Bèl, Bèf, Bésé, Bèf-la, Bennyé
l Bèl +3 ms Bèl, Bèl-la, Bèlté, Bel
` ` Bèl +319 ms  
b Bèl b -25 ms ba, byen, bèl, biten, bon
o Bèl bo +14 ms bon, bout, bouch, boug, bouko
n Bèl bon +36 ms bon, bonjou, bonmaten-la, bondyé, bonswa
j Bèl bonj +45 ms bonjou
o Bèl bonjo +17 ms bonjou
u Bèl bonjou -39 ms bonjou

Observations :

Captures d’écran de la progression : progression_05.png (« Bèl b »), progression_07.png (« Bèl bon »), progression_10.png (« Bèl bonjou »).

Conclusion mise à jour

Les deux nouveaux axes confirment et complètent la première passe :


🗓️ Date du rapport : 10 juillet 2026

✅ Statut : Deux cycles d’enrichissement le 10/07 (703 puis 2383 textes), tous deux confirmés sans régression (+363 puis +868 mots). Les 3 trous de vocabulaire déjà connus (blòké, apeti/lapeti, nwit/lannwit) persistent aux deux cycles — même un enrichissement ciblé (phrases de sécurité) n’a pas suffi car ces mots précis n’étaient dans aucun texte ajouté. Pollution par noms propres (wilyàm, ana, kévin) détectée au second cycle, à corriger.

Rapport de test — Impact de l’enrichissement du dataset POTOMITAN/PawolKreyol-gfc

Date : 10 juillet 2026 Heure : 11:41 – 12:06 (CEST) Testeur : Claude Code (agent), à la demande de l’utilisateur Contexte : l’utilisateur a enrichi le dataset Hugging Face source de vérité et l’a rendu public (gated: false), puis a demandé de mesurer concrètement l’impact de cet enrichissement sur le dictionnaire embarqué et sur la qualité des suggestions.

Résumé

  Avant (dataset initial) Après (dataset enrichi) Delta
Textes du corpus HF 427 703 +276 (+64,6%)
Mots du dictionnaire 3 680 4 043 +363 (+9,9%)
Mots supprimés — — 0
Prédictions n-grammes 3 582 3 902 +320 (+8,9%)

Malgré une croissance du corpus de +64,6%, le dictionnaire ne croît que de +9,9% : effet attendu de la loi de Heaps — les mots les plus fréquents du kréyòl étaient déjà couverts par le corpus initial, et l’enrichissement apporte surtout du vocabulaire plus rare (voir échantillon ci-dessous).

Méthodologie

  1. Régénération du dictionnaire/n-grams via Dictionnaires/KreyolComplet.py (pipeline existant, non modifié) contre le dataset HF fraîchement enrichi.
  2. Reconstruction de l’APK debug avec les nouveaux assets (android_keyboard/app/src/main/assets/creole_dict.json et creole_ngrams.json).
  3. Réinstallation sur le même AVD kreyol_test (Pixel 5, Android 14), émulateur affiché à l’écran (WSLg).
  4. Rejeu à l’identique du protocole du 09/07/2026 : mêmes 50 phrases kréyòl du quotidien, même table de coordonnées clavier, même méthode de frappe touche par touche et de mesure de latence (adb logcat -v epoch), dans une nouvelle conversation Google Messages (0690777000).
  5. Comparaison programmatique des suggestions finales et des latences entre results2.json (09/07, avant enrichissement) et results3.json (10/07, après enrichissement).

Les 3 trous de dictionnaire connus persistent

Le rapport du 09/07 avait identifié 3 mots/orthographes absents du dictionnaire : blòké, apeti/lapeti, nwit/lannwit. Vérification directe dans le nouveau creole_dict.json :

Mot recherché Présent après enrichissement ?
blòké ❌ Absent
apeti ❌ Absent
lapeti ❌ Absent
nwit ❌ Absent
lannwit ❌ Absent
nuit (orthographe française) ✅ Présent (déjà avant)

Confirmé également en conditions réelles sur l’émulateur (captures d’écran) :

Conclusion : les 276 textes ajoutés au corpus ne contiennent pas ces mots/graphies précis. L’enrichissement quantitatif du corpus ne garantit pas la couverture de vocabulaire spécifique déjà identifié comme manquant — il faudrait soit cibler l’ajout de textes contenant ces mots, soit les ajouter manuellement au dictionnaire.

Comparaison des 50 phrases — avant / après

43 phrases sur 50 ont des suggestions finales strictement identiques. 7 phrases montrent une évolution (marquées 🔄), toutes dues à l’arrivée de nouveaux mots dans le dictionnaire qui viennent concurrencer les suggestions existantes :

# Phrase Suggestions avant (09/07) Suggestions après (10/07)
10 mwen fen fen, fenfon fen, fenfon, fenyan
11 an bwè an tigout dlo dlo, dlo-la dlo, dlo-la, dlo-a
17 timoun yo la la, lang, lavi, laplaj, larichès la, lang, lavi, lapenn, larichès
19 fanmi mwen la la, lang, lavi, laplaj, larichès la, lang, lavi, lapenn, larichès
20 an renmen’w wouvè, wayayay, woy, wi, woulé (voir note ci-dessous)
25 rété la la, lang, lavi, laplaj, larichès la, lang, lavi, lapenn, larichès
28 jaden an bel bèl, bèl-la, bèlté, bel bèl, bèl-la, bèlté, bel, belmè

Note sur la phrase 20 : le script de mesure a capturé 0 suggestion juste après le dernier caractère (w), ce qui ressemblait à une régression. Un re-test manuel immédiat a montré que les 5 suggestions (wouvè, wayayay, woy, wi, woulé) réapparaissent bien, identiques à avant — le logcat montre une brève fenêtre à 0 suggestion juste après la finalisation du mot précédent (apostrophe), que le script a capturée par malchance avant l’arrivée de la vraie suggestion. Artefact de mesure, pas une régression du moteur.

Aucune suggestion existante n’a disparu ou changé d’ordre parmi les 4 premiers rangs sur l’ensemble des 50 phrases — l’enrichissement n’a eu qu’un effet marginal et additif (nouveaux mots insérés en fin de liste de suggestions), sans perturber les suggestions déjà correctes.

Détail complet des 50 phrases (suggestions + latence, avant/après) | # | Phrase | Suggestions avant (09/07) | Suggestions après (10/07) | Latence avant | Latence après | |---|---|---|---|---|---| | 1 | bèl bonjou | bonjou | bonjou | -38.8ms | -14.4ms | | 2 | sa ou fè | fè, fèt, fèmé, fè-nou, fè-y | fè, fèt, fèmé, fè-nou, fè-y | 7.3ms | 407.8ms | | 3 | ka ou fè jòdi a | an, adan, a-y, anba, asi | an, adan, a-y, anba, asi | -37.1ms | -23.4ms | | 4 | mwen la wi | wi | wi | -81.2ms | -39.6ms | | 5 | ou byen | byen, byendéfwa, byenmèsi | byen, byendéfwa, byenmèsi | 164.9ms | -140.2ms | | 6 | mèsi anpil | anpil | anpil | -6.1ms | -55.7ms | | 7 | an ka vini | vini, vini-zòt | vini, vini-zòt | -22.7ms | 175.5ms | | 8 | ki lè i yé | yé, yenki, yè, yépa, yen | yé, yenki, yè, yépa, yen | -12.1ms | -79.1ms | | 9 | ou vlé manjé | manjé, manjé-la | manjé, manjé-la | -73.8ms | -38.5ms | | 10 | mwen fen 🔄 | fen, fenfon | fen, fenfon, fenyan | -72.6ms | -32.1ms | | 11 | an bwè an tigout dlo 🔄 | dlo, dlo-la | dlo, dlo-la, dlo-a | -52.4ms | -112.9ms | | 12 | koté ou yé | yé, yenki, yè, yépa, yen | yé, yenki, yè, yépa, yen | -72.8ms | -119.0ms | | 13 | an kaz mwen | mwen, mwenmenm, mwens, mwen-la | mwen, mwenmenm, mwens, mwen-la | -58.0ms | -115.5ms | | 14 | ou ka travay jòdi a | an, adan, a-y, anba, asi | an, adan, a-y, anba, asi | -78.5ms | -146.4ms | | 15 | wi mwen ka travay | travayè, travay, travayè-la, travay-la, travay-li | travayè, travay, travayè-la, travay-la, travay-li | -84.8ms | -43.4ms | | 16 | an ka alé lékòl | lékòl, lékòl-la | lékòl, lékòl-la | -79.3ms | -178.8ms | | 17 | timoun yo la 🔄 | la, lang, lavi, laplaj, larichès | la, lang, lavi, lapenn, larichès | -52.3ms | -119.5ms | | 18 | gran moun ka palé | palé, palé-w, palé-la, palé-mwen | palé, palé-w, palé-la, palé-mwen | -60.1ms | -79.0ms | | 19 | fanmi mwen la 🔄 | la, lang, lavi, laplaj, larichès | la, lang, lavi, lapenn, larichès | -49.0ms | -132.6ms | | 20 | an renmen'w 🔄 (artefact) | wouvè, wayayay, woy, wi, woulé | (voir note) | -55.6ms | -196.8ms | | 21 | sa ka fè plézi | plézi | plézi | -91.1ms | -174.7ms | | 22 | bon apeti | avèti, apési, pati, péyi, pété | avèti, apési, pati, péyi, pété | 26.6ms | -45.2ms | | 23 | bon nwit | nuit, ni, dwèt, wi, pit | nuit, ni, dwèt, wi, pit | 13.7ms | -83.4ms | | 24 | dòmi byen | byen, byendéfwa, byenmèsi | byen, byendéfwa, byenmèsi | -67.1ms | -129.8ms | | 25 | rété la 🔄 | la, lang, lavi, laplaj, larichès | la, lang, lavi, lapenn, larichès | -77.1ms | -185.3ms | | 26 | vini isi | isidan, isi | isidan, isi | -64.3ms | -130.6ms | | 27 | an ka vann fwi | fwi, fwitaj | fwi, fwitaj | -41.3ms | -127.2ms | | 28 | jaden an bel 🔄 | bèl, bèl-la, bèlté, bel | bèl, bèl-la, bèlté, bel, belmè | -64.9ms | -123.6ms | | 29 | soley ka bril jòdi a | an, adan, a-y, anba, asi | an, adan, a-y, anba, asi | -86.6ms | -195.2ms | | 30 | lapli ka tonbé | tonbé | tonbé | -67.9ms | -129.1ms | | 31 | lanmè bel toubannman | toupannan | toupannan | 136.2ms | -75.4ms | | 32 | an ka péché pwason | pwason | pwason | -66.0ms | -156.9ms | | 33 | sé bon manjé | manjé, manjé-la | manjé, manjé-la | -68.1ms | -108.1ms | | 34 | dokan an ouvè | ouvè | ouvè | -74.6ms | -174.7ms | | 35 | bous mwen vid | vid | vid | -62.6ms | -166.0ms | | 36 | lajan ka manké | manké | manké | -17.7ms | -62.6ms | | 37 | travay la red | rèd, rédé, rédé-yo, rédé-y, red | rèd, rédé, rédé-yo, rédé-y, red | -62.6ms | -135.0ms | | 38 | an fatigé anpil | anpil | anpil | -44.5ms | 8.5ms | | 39 | an bizwen répozé | dépozé, réponn, pozé, opozé, répondè | dépozé, réponn, pozé, opozé, répondè | 34.9ms | -50.2ms | | 40 | telefòn mwen kasé | kasé | kasé | -75.7ms | -62.3ms | | 41 | voiti an pann | pannan, pann, pannansitan, pannyé, pannyé-la | pannan, pann, pannansitan, pannyé, pannyé-la | -74.3ms | 12.4ms | | 42 | wout la blòké | baké, blo, blé, blagé, lòdè | baké, blo, blé, blagé, lòdè | -51.4ms | -83.4ms | | 43 | lékòl fini | fini | fini | -84.2ms | -168.2ms | | 44 | lévé bonnè | bonnè | bonnè | -43.6ms | -162.7ms | | 45 | kouché ta | tan, tann, tanbou, ta, tata | tan, tann, tanbou, ta, tata | -12.8ms | -168.0ms | | 46 | fè cho jòdi a | an, adan, a-y, anba, asi | an, adan, a-y, anba, asi | -74.6ms | -179.9ms | | 47 | van ka soufflé | souflé, soufè, souplé | souflé, soufè, souplé | 6.6ms | 46.9ms | | 48 | mizik la bon | bon, bonjou, bonmaten-la, bondyé, bonswa | bon, bonjou, bonmaten-la, bondyé, bonswa | -78.9ms | -150.1ms | | 49 | dansé kréyòl | kréyòl, kréyòl-gwadloup, kréyol, kréyol-la | kréyòl, kréyòl-gwadloup, kréyol, kréyol-la | -83.0ms | -86.4ms | | 50 | viv kréyòl gwadloup | gwadloup, gwadloupéyen | gwadloup, gwadloupéyen | 95.5ms | 139.0ms |

Rapidité

  Avant (09/07) Après (10/07)
Latence moyenne -40.7ms -83.8ms
Latence médiane -61.4ms -114.2ms
Écart-type 54.4ms 105.8ms

Les deux séries restent négatives en moyenne, ce qui — comme documenté le 09/07 — signifie que le bruit de mesure (aller-retour ADB + désynchronisation d’horloge hôte/émulateur, de l’ordre de ±100 à 200ms) domine largement le temps de calcul réel des suggestions : celui-ci reste trop rapide pour être isolé par cette méthode, avant comme après l’enrichissement. L’écart-type plus élevé après enrichissement (105.8ms vs 54.4ms) est cohérent avec un bruit de mesure plus variable ce jour-là (l’émulateur a par ailleurs affiché deux fois le dialogue « System UI isn’t responding » au démarrage), pas avec un ralentissement réel du moteur — le dictionnaire (+363 mots) et les n-grams (+320 prédictions) restent des structures en mémoire minuscules pour un appareil mobile moderne.

Aucun ralentissement perceptible attribuable à l’enrichissement du dictionnaire.

Captures d’écran

Captures conservées pour les phrases 1, 2, 3, 10, 20, 22, 23, 30, 40, 42, 50, ainsi que la progression caractère par caractère de « Bèl bonjou » (phrases 5 à 10) — dossier rapport_test_dataset_enrichi_2026-07-10_screenshots/. Les phrases 22, 23 et 42 (les 3 mots-trous) ont été spécifiquement capturées pour ce test.

Le bug de casse déjà documenté le 09/07 (première suggestion en MAJUSCULES sur le tout premier caractère d’un message, ex. VWÈ, VIKTÒ, VLÉ pour « van ka soufflé ») a été observé à nouveau à l’identique — non lié à l’enrichissement, toujours ouvert dans applyCasingPattern().

Conclusion (1ʳᵉ passe, 703 textes)

L’enrichissement du dataset (427 → 703 textes, +64,6%) a produit une croissance mesurée mais réelle du dictionnaire embarqué (+363 mots, +9,9%) et des n-grams (+320 prédictions, +8,9%), sans aucune régression : 43 des 50 phrases de test ont des suggestions identiques à avant, les 7 phrases modifiées ne font qu’ajouter de nouvelles options sans supplanter les suggestions correctes déjà en place, et aucun ralentissement n’est mesurable.

En revanche, les 3 trous de vocabulaire identifiés le 09/07 persistent à l’identique (blòké, apeti/lapeti, nwit/lannwit) : l’enrichissement générique du corpus n’a pas ciblé ces mots précis. Pour les combler, il faudrait soit ajouter spécifiquement des textes contenant ce vocabulaire courant au dataset source, soit les insérer manuellement dans creole_dict.json.

Suite donnée : dictionnaire ciblé des trous de vocabulaire

Plutôt que de continuer à enrichir le dataset de façon générique, une liste de 48 mots courants confirmés absents du dictionnaire (vérifiés programmatiquement, organisés par thème : verbes, famille, nourriture, météo, temps, émotions, adjectifs, objets du foyer, santé, école, animaux) a été établie pour orienter un enrichissement ciblé — voir Dictionnaire des trous de vocabulaire — kréyòl Gwadloupéyen. Cette liste part de connaissances linguistiques générales (pas d’un corpus) et doit être validée par des locuteurs natifs avant toute intégration ; elle inclut la marche à suivre pour transformer ces mots en textes puis en enrichissement effectif du dataset POTOMITAN/PawolKreyol-gfc.

Addendum — Second cycle d’enrichissement ciblé (2383 textes, 10 juillet 2026, 16:33 CEST)

Un lot de 1 680 phrases (curées séparément à partir d’un dataset de traduction privé, dont 53 phrases de sécurité/premiers secours) a été ajouté au dataset PawolKreyol-gfc le jour même (703 → 2383 textes). Le pipeline a été rejoué, l’APK reconstruit, et le même protocole de 50 phrases rejoué une troisième fois pour mesurer l’impact de ce second cycle.

Résumé : dictionnaire 4 043 → 4 911 mots (+868, +21,5%), n-grams 3 902 → 4 232 prédictions (+330, +8,5%), 0 mot supprimé. Un enrichissement ciblé fait donc croître le dictionnaire proportionnellement bien plus qu’un enrichissement générique (+21,5% de mots pour ce cycle contre +9,9% lors du premier).

Les 3 trous historiques persistent une troisième fois (blòké, apeti/lapeti, nwit/lannwit) : les 1 680 phrases ajoutées ne contenaient tout simplement pas ces graphies précises. Enseignement confirmé : cibler un thème (sécurité, quotidien) ne suffit pas à combler un trou de vocabulaire précis — il faut que le mot exact apparaisse dans un texte ajouté.

En revanche, une partie du vocabulaire des phrases de sécurité est bien détectable : blesé, doktè, rimèd, vitman, évakwasyon sont désormais dans le dictionnaire — preuve que l’approche fonctionne quand le mot cible est effectivement présent dans le texte source.

⚠️ Nouveau problème détecté : des noms de personnages fictifs issus de dialogues pédagogiques (wilyàm, ana, kévin) sont désormais dans le dictionnaire et apparaissent en concurrence avec du vocabulaire réel dans les suggestions (ex. « mwen la wi » propose désormais wi, wilyàm, wilyam, wifi). À filtrer avant un prochain cycle d’enrichissement à partir de contenu dialogué.

Rapport complet avec tableau des 11 phrases modifiées et détails méthodologiques : rapport_test_dataset_enrichi_2026-07-10.md.


🗓️ Date du rapport : 10 juillet 2026

✅ Statut : Release publiée, build et signature réussis, 4 artefacts disponibles (APK/AAB × debug/release).

Version 7.0.2 — Dictionnaire enrichi

Suite aux deux cycles d’enrichissement documentés ci-dessus, la version 7.0.2 (versionCode 70002) a été publiée le 10 juillet 2026 : voir la release GitHub.

Contenu de la release

Artefacts publiés

Fichier Usage
KreyolKeyboard-Release-v7.0.2.aab À uploader sur Google Play Console
KreyolKeyboard-Release-v7.0.2.apk Installation directe, signé production
KreyolKeyboard-Debug-v7.0.2.aab / .apk Build de développement

Les notes de version ont été générées automatiquement depuis android_keyboard/CHANGELOG.md par le workflow build-apk.yml (job create-release, déclenché par le tag v7.0.2), et sont directement réutilisables pour la fiche Google Play Store.

Limites connues, non résolues dans cette version

Documentées en détail dans les rapports ci-dessus, non corrigées dans le code à ce stade :


🗓️ Date du rapport : 10 juillet 2026

✅ Statut : 76,9 % des messages envoyés parfaitement corrects malgré 8,7 % de mots fautés délibérément ; 98,8 % d’exactitude caractère par caractère. Aucun ralentissement mesurable. Deux artefacts isolés de l’automatisation identifiés et documentés (pas des défauts du clavier).

Rapport de simulation — Frappe humaine réaliste sur un dialogue créole de 982 mots

Date : 10 juillet 2026 Heure : 21:38 – 22:10 (CEST) Testeur : Claude Code (agent), à la demande de l’utilisateur Version testée : 7.0.2 (versionCode 70002)

⚠️ Méthodologie et limites du contenu : le dialogue ci-dessous est un texte imaginaire écrit à partir de connaissances générales du kréyòl guadeloupéen (pas un corpus authentique ni relu par un locuteur natif). Il sert uniquement de matériau de test pour mesurer le comportement du clavier — pas une référence linguistique.

Pourquoi cette simulation ?

Tous les tests précédents (50 phrases, 09-10 juillet) simulaient une frappe parfaite : chaque caractère de la phrase cible tapé sans erreur, sans jamais utiliser les suggestions ni corriger quoi que ce soit. Cette simulation va plus loin : elle imite un comportement humain réaliste — fautes de frappe sur des touches physiquement voisines, corrections au backspace, et sélection de mots dans la barre de suggestions (autocomplétion et récupération après faute) — pour mesurer des statistiques plus représentatives d’un usage réel.

Limite technique : un seul appareil est disponible (pas de second téléphone pour l’autre ami). Le dialogue est donc tapé et envoyé intégralement depuis le même champ de composition, dans l’ordre, comme une suite de messages sortants — les deux locuteurs sont distingués ci-dessous (« Ami A » / « Ami B ») mais apparaissent tous comme des messages envoyés sur l’écran du téléphone.

Méthodologie

Environnement de test : émulateur Android, AVD kreyol_test.

Modèle de faute de frappe

Ce qui n’a pas été testé

Le dialogue (982 mots, 134 messages)

Les messages marqués ⚠️ ont été envoyés avec au moins un caractère différent du texte visé (faute non corrigée) — voir le détail dans la section Résultats.

Voir les 134 messages - **Ami A** : Bèl bonjou zanmi. - **Ami B** : Bonjou doudou, ka ou fè. - **Ami A** : Mwen la wi, on tibren fatigé. - **Ami B** : Poukisa ou fatigé kon sa. - **Ami A** : An travay tar yè oswè, an pa dòmi anpil. ⚠️ (1 car.) - **Ami B** : Sa two red, fo ou repozé on ti kal. ⚠️ (1 car.) - **Ami A** : Wi wi, an ka pran on kafé pou rivé. ⚠️ (3 car.) - **Ami B** : Bon, é lékòl, ka i ka fè. - **Ami A** : Lékòl ka fatigé mwen menm plis ki travay. - **Ami B** : An konprann ou, mwen menm menm bagay. ⚠️ (3 car.) - **Ami A** : Ou manjé jòdi maten. - **Ami B** : Wi, an fè on ti dijenné rapid, pen é bè. - **Ami A** : Mwen menm an pa ni tan manjé anyen. ⚠️ (2 car.) - **Ami B** : Sa pa bon, fo ou manjé, ou ké tonbé malad. - **Ami A** : Ou ni rézon, an ké achté on sandwich midi. - **Ami B** : Bon lidé, pran osi on ji fré. - **Ami A** : Nou tou lé dé fatigé kon chyen. - **Ami B** : Sa vré, mé simenn ka fini byento. ⚠️ (2 car.) - **Ami A** : Wi dié mèsi, an bizwen repozé. - **Ami B** : Ka ou ka fè jòdi aprémidi. - **Ami A** : Mwen ka rété kaz, an ka gadé télé. - **Ami B** : Ban mwen non, di mwen, ka ka pasé èvè Jòj. - **Ami A** : Ay, pa palé mwen di sa, i pa réponn mwen dépi twa jou. - **Ami B** : Sérié, sa two red pou ou. ⚠️ (2 car.) - **Ami A** : An kwè i ka wè on lot fi. - **Ami B** : Non, pa kwè sa dirèk, mandé i sa ki ka pasé. ⚠️ (1 car.) - **Ami A** : An pè mandé i, an pè répons la. - **Ami B** : Kouraj doudou, ou merité myé ki sa. - **Ami A** : Mèsi anpil, ou toujou la pou mwen. ⚠️ (1 car.) - **Ami B** : Sa nòwmal, nou sé bon zanmi. - **Ami A** : É ou menm, ou ni kikanmarad. - **Ami B** : An ni on ti kwi asi kè mwen, mé an pa sèten. - **Ami A** : Kimoun, di mwen non. - **Ami B** : Sé kouzen a Vytò, i ka rété Bastè. - **Ami A** : Ay sa bèl, i bèl. - **Ami B** : Wi anpil, mé i timid, i pa ka palé mwen souvan. - **Ami A** : Alé palé i, pa rété la san fè anyen. ⚠️ (1 car.) - **Ami B** : An pè i ka refizé mwen. - **Ami A** : Sa pa on rézon pou pa éséyé. - **Ami B** : An sav, mé mwen two timid pou palé i dirèk. - **Ami A** : Ékri i on ti mo, sa pli fasil pou ou. - **Ami B** : Bon idé, an ka réfléchi asi sa. ⚠️ (1 car.) - **Ami A** : Fè sa vitman avan on lot moun pran i. - **Ami B** : Ay pa fè mwen pè kon sa. - **Ami A** : An ka blagé, mé sérié, alé pou sa. ⚠️ (2 car.) - **Ami B** : Ou ni rézon, an ka éséyé simenn pwochen. - **Ami A** : Anfen, on bon nouvèl. - **Ami B** : Bon, sa asé palé lanmou, ka nou ka fè aswè. ⚠️ (1 car.) - **Ami A** : An té ka pansé sòti, ni on fèt bò kaz Marilèn. ⚠️ (3 car.) - **Ami B** : A ki lè fèt la ka kòmansé. ⚠️ (1 car.) - **Ami A** : Vè uitè, mé nou pé rivé pli ta. - **Ami B** : Ki moun ki ké la. - **Ami A** : Tout bann zanmi nou, é Marilèn ni on nouvo bwason espésyal. - **Ami B** : Miam, an ja anvi alé. - **Ami A** : Nou ka rankontré koté. ⚠️ (2 car.) - **Ami B** : Bò lékòl, tibren avan nèfè. - **Ami A** : Dakò, pòté on ti kado pou Marilèn. - **Ami B** : Wi, an ka achté on gato. - **Ami A** : Ka ou ka mété pou fèt la. - **Ami B** : An pa sav ankò, pétèt wòb blé mwen. - **Ami A** : Bèl chwa, i ka fè byen si figi ou. - **Ami B** : Mèsi, é ou menm, ka ou ka mété. - **Ami A** : On pantalon é chimiz blan, senp mé bon. - **Ami B** : Nou ké bèl tou lé dé, sa asé. ⚠️ (2 car.) - **Ami A** : Bon lidé, i ka renmen gato chokola. - **Ami B** : Pafèt, an sav sa i renmen. - **Ami A** : Ou tann pou konsè la simenn pwochen. ⚠️ (1 car.) - **Ami B** : Ki konsè. - **Ami A** : Sa a gwo chantè kréyòl la, i ka vini Pwentapit. - **Ami B** : Sérié, an té ja anvi wè i an sèn. - **Ami A** : Mwen ja achté dé tiké, on pou mwen on pou ou. - **Ami B** : Ou an anj, mèsi milyon fwa. - **Ami A** : An sav ou renmen mizik li anpil. - **Ami B** : Ki chanson ou pito. - **Ami A** : Sa i fè lanné pasé, sa touché mwen anpil. - **Ami B** : Sa on bon chwa, mwen menm an renmen sa nouvo. - **Ami A** : Nou ké tann tou lé dé aswè la. - **Ami B** : Sa ké on bèl mélanj, ansyen é nouvo. ⚠️ (2 car.) - **Ami A** : Chak chanson i ka touché kè mwen. ⚠️ (4 car.) - **Ami A** : Nou ké chanté é dansé tout la nuit. - **Ami B** : An ka mété rad bèl mwen pou sa. - **Ami A** : An osi, fo nou bèl pou konsè tala. ⚠️ (2 car.) - **Ami B** : Ès nou ka rété douvan sèn oben pli lwen. - **Ami A** : Douvan, pou wè byen figi chantè a. - **Ami B** : Ay, sa ké on bèl aswè. - **Ami A** : An ka konté jou la ja. - **Ami B** : Mwen osi, an pa ka tann pasyaman. - **Ami A** : Démen, ki tan ké fè. - **Ami B** : Soley ké kléré tout jounen, sa ka anonsé. ⚠️ (1 car.) - **Ami A** : Nou ka alé lanmè, ka ou ka di. - **Ami B** : Ay wi, sa ké fè byen apré simenn tala. - **Ami A** : Ki plaj nou ka chwazi. - **Ami B** : Sen Fwanswa, dlo a toujou klè la. - **Ami A** : Nou pòté manjé oben nou ka achté asou plas. - **Ami B** : Nou ka pòté manjé, sa mwens chè. - **Ami A** : An ka fè bannann é diri. - **Ami B** : Miam, an ka pòté ji zoranj. - **Ami A** : Ou konnèt najé byen. - **Ami B** : Wi on tibren, mé an pi renmen rété bò bò. ⚠️ (1 car.) - **Ami A** : Mwen ka najé lwen, an renmen gran dlo. ⚠️ (2 car.) - **Ami B** : Fè atansyon, pa alé two lwen tousèl. - **Ami A** : Pa enkyèté, an toujou pridan. - **Ami B** : Bon, an konfyans nan ou. - **Ami A** : Ki lè nou ka pati. - **Ami B** : Bonnè, avan soley two fò. - **Ami A** : Nou ka bengné oben nou ka rété soley kouché. ⚠️ (3 car.) - **Ami B** : Nou ka fè tou lé dé, jounen ka la pou sa. - **Ami A** : An anvi jwé volé plaj osi. - **Ami B** : Dakò, an ka pòté balon an. - **Ami A** : Ay sa ké on bon jounen, mèsi ou pansé sa. - **Ami B** : Sé nòwmal, nou zanmi pou lavi. - **Ami A** : Bon, sonjé pòté kwem solè, soley red la ba. - **Ami B** : Wi manman, an ja ni sa an sak mwen. ⚠️ (1 car.) - **Ami A** : É sèvyèt, ou ni ase. - **Ami B** : Wi dé, on pou ou si ou bliyé tan a ou. - **Ami A** : Ou toujou pansé tout bagay, mèsi anpil. - **Ami B** : Sa nòwmal, nou zanmi dépi lékòl primè. ⚠️ (1 car.) - **Ami A** : Vré, sa fè lontan konsa. - **Ami B** : Anpil bèl souvni, é ni pli ankò ka vini. ⚠️ (1 car.) - **Ami A** : Sa vré, kon konsè démen é plaj apré. - **Ami B** : Simenn la ké bon anpil pou nou. - **Ami A** : Bon, an ka rantré, an ni lésòn démen bonnè. - **Ami B** : Dakò, dòmi byen, fè bon rèv. - **Ami A** : Mèsi, ou osi. - **Ami B** : An kontan nou palé jòdi a. ⚠️ (1 car.) - **Ami A** : Mwen osi, sa fè byen palé èvè ou. - **Ami B** : A démen, pou konsè a. - **Ami A** : A démen, byen bonnè. - **Ami B** : Mèsi pou tout, ou on bon zanmi. ⚠️ (1 car.) - **Ami A** : Ou osi, an chans ni ou an lavi mwen. - **Ami B** : Nou ké viv anpil bon moman ankò. ⚠️ (2 car.) - **Ami A** : Wi sèten, kréyòl gwadloup ka viv an nou. - **Ami B** : Bon nwit doudou, fè dous rèv. - **Ami A** : Bon nwit ou osi, a talè.

Résultats

Vue d’ensemble

Métrique Valeur
Messages envoyés 134
Mots tapés 982
Mots avec faute injectée 85 (8,7 %)
Messages envoyés identiques au texte visé 103 / 134 (76,9 %)
Exactitude au niveau caractère (Levenshtein) 98,79 %
Distance de Levenshtein moyenne par message 0,39 caractère
Pire cas 4 caractères d’écart (sur 134 messages) — message « Chak chanson i ka touché kè mwen. »

Malgré des fautes de frappe délibérément injectées sur 8,7 % des mots, plus de 3 messages sur 4 arrivent parfaitement corrects grâce aux corrections manuelles et aux suggestions — un signal positif sur l’utilité réelle du correcteur.

Répartition des actions par mot

Action Nombre %
Tapé intégralement (sans incident) 794 80,9 %
Autocomplétion tapée avec succès 51 5,2 %
Autocomplétion tentée mais mot absent des suggestions 52 5,3 %
Faute corrigée au backspace 36 3,7 %
Faute laissée telle quelle (délibéré) 18 1,8 %
Faute « récupérée » via une suggestion 16 1,6 %
Récupération par suggestion tentée mais échouée 15 1,5 %

Autocomplétion (mots tapés correctement, ≥4 lettres)

Sur les 103 mots où une tentative d’autocomplétion a eu lieu (suggestion vérifiée à mi-frappe), le mot visé était disponible dans la liste et a été tapé 49,5 % du temps. Dans l’autre moitié des cas, le mot n’était pas encore proposé au point de vérification et la frappe manuelle a été terminée normalement.

Fautes de frappe : que deviennent-elles ?

Sur les 85 mots avec une faute injectée (distribution observée sur cet échantillon : substitution par touche voisine géométrique 36 %, omission 39 %, inversion 25 % — à comparer à la cible du modèle 50 % / 30 % / 20 %, l’écart restant dans la variance attendue pour n=85) :

Devenir Nombre %
Corrigée au backspace (l’utilisateur se corrige lui-même) 36 42,4 %
Récupérée via une suggestion (le clavier « rattrape » la faute) 16 18,8 %
Suggestion tentée mais le mot visé n’apparaissait pas → envoyée telle quelle 15 17,6 %
Envoyée telle quelle (l’utilisateur ne corrige pas, délibéré) 18 21,2 %
Total envoyé avec la faute encore présente 33 38,8 %

Sur les 31 tentatives de récupération via suggestion (36,5 % des 85 fautes), le mot visé apparaissait effectivement dans la liste 51,6 % du temps — le correcteur flou (distance de Levenshtein) rattrape environ une faute sur deux qu’on lui présente, le reste nécessitant une correction manuelle.

Latence

  Valeur
Mesures (une par mot minimum) 1068
Moyenne -127,0 ms
Médiane -180,6 ms
Écart-type 180,4 ms

Comme lors des tests précédents, les valeurs négatives confirment que le calcul des suggestions reste en dessous du plancher de mesure (bruit ADB/horloge hôte-émulateur) — aucun ralentissement perceptible, y compris avec la charge supplémentaire des fautes/corrections/taps de suggestion.

Anomalies observées (artefacts du harnais de test)

Deux cas isolés parmi les 134 messages montrent une perte ou un déplacement de caractère (espace manquant, lettre dupliquée) sans qu’aucune faute n’ait été injectée à ce mot d’après le journal d’actions — par exemple « an ka » envoyé comme « a nka », ou « konprann ou » envoyé comme « konpranou ». Ces deux cas coïncident avec des séquences de frappe rapides sur ~40 minutes d’exécution automatisée ; l’hypothèse la plus probable est un événement tactile émis par le harnais légèrement plus vite que l’IME ne pouvait le committer de façon fiable — un artefact connu de l’automatisation par adb input tap sur des sessions longues, pas un défaut du moteur de suggestions. Ces deux cas sont inclus tels quels dans les statistiques d’exactitude ci-dessus (ils n’ont pas été retirés).

Captures d’écran

Une capture a été prise tous les ~15-20 messages, plus une capture montrant la barre de suggestions active en cours de frappe (suggestions_actives.png) — dossier rapport_simulation_frappe_humaine_2026-07-10_screenshots/.

Démonstration : un tap de suggestion, avant/après

L’usage des suggestions par le simulateur (colonne « Autocomplétion tapée avec succès », 51 mots sur 982) est peu visible à l’œil nu pendant un test de 40 minutes qui défile vite — il ne concerne qu’environ 1 mot sur 7-8, et l’action est instantanée. Voici une démonstration ciblée, rejouée manuellement sur l’émulateur juste après le test, reproduisant exactement un des 51 cas réels du journal (message 5, mot « dòmi », an bizwen repozé → suivi immédiatement du mot « dòmi » dans le message suivant) :

Avant le tap Après le tap
Avant Après
Champ : Dòm (3 lettres tapées). Barre de suggestions : Dòmi, Domino, Dominasyon. Un seul tap sur la 1ʳᵉ suggestion → champ : Dòmi (mot complété + espace inséré automatiquement), nouvelle barre de suggestions déjà prête pour le mot suivant.

Conclusion

Cette simulation de frappe humaine — la première de cette série de tests à inclure fautes, corrections et usage réel des suggestions plutôt qu’une frappe parfaite — montre un clavier globalement robuste face à des erreurs réalistes : 76,9 % des messages arrivent parfaitement corrects malgré 8,7 % de mots fautés, et l’exactitude caractère par caractère atteint 98,8 %. La correction floue (Levenshtein) rattrape environ une faute sur deux quand on la sollicite, et l’autocomplétion est adoptée dans un cas sur deux quand le mot visé est disponible. Aucun ralentissement mesurable n’a été observé malgré la charge de test nettement supérieure aux passages précédents (982 mots avec mesure systématique, contre 50 phrases mesurées ponctuellement).

Deux artefacts isolés de l’automatisation elle-même (perte/déplacement de caractère sans faute injectée) ont été identifiés et documentés plutôt que masqués — un signal de fiabilité de la méthode de test autant que du clavier.

Rapport de simulation — Mode « suggestions uniquement » sur le dialogue créole de 982 mots

Date : 11 juillet 2026 Heure : 12:08 – 12:46 (CEST) Testeur : Claude Code (agent, modèle Fable 5), à la demande de l’utilisateur Version testée : 7.0.2 (versionCode 70002) Environnement : émulateur Android, AVD kreyol_test, fenêtre affichée à l’écran (WSLg) pour observation en direct ; conversation SMS dédiée dans Google Messages.

⚠️ Méthodologie et limites du contenu : le dialogue est le même texte imaginaire que celui du rapport du 10 juillet (écrit à partir de connaissances générales du kréyòl guadeloupéen, non relu par un locuteur natif). Il sert uniquement de matériau de test — pas une référence linguistique.

Pourquoi ce mode ?

En observant la simulation « frappe humaine réaliste » à l’écran, l’utilisateur a constaté que les taps de suggestion étaient quasiment invisibles : dans ce mode, seul ~1 mot sur 7-8 est committé via la barre de suggestions, et le tap est instantané. Ce nouveau passage inverse le principe : l’utilisateur simulé ne termine jamais un mot à la main. Pour chaque mot, il tape les lettres une à une et, dès que le mot visé apparaît parmi les 3 suggestions affichées, il tape dessus. La frappe intégrale n’arrive que si le clavier ne propose jamais le mot — ce cas est compté à part et constitue une mesure directe de la couverture du dictionnaire.

C’est aussi le protocole le plus exigeant pour le moteur de suggestions : il est interrogé à chaque lettre (2 123 lectures sur ce passage, contre 1 068 sur le passage réaliste du 10 juillet).

Note de traçabilité : une réplique du passage « réaliste » du 10 juillet (même dialogue, nouvelle graine) avait été lancée en début de séance, puis interrompue à la demande de l’utilisateur après 43 messages au profit du présent mode ; aucune statistique n’en a été conservée.

Méthodologie

Résultats

Vue d’ensemble : l’utilisateur « strictement suggestions »

Métrique Valeur
Messages envoyés 134
Mots traités 982
Mots committés par tap de suggestion 806 (82,1 %)
… dont avant la fin du mot (vraie économie) 667 (67,9 % des mots)
… dont seulement à la dernière lettre 139
Mots jamais proposés (frappe intégrale forcée) 176 (17,9 %)
Lettres tapées 2 142 / 3 196 (67,0 %)
Lettres économisées 1 054 (33,0 %) — plus les espaces, offerts par le commit

Un utilisateur qui s’appuie exclusivement sur les suggestions économise donc un tiers des frappes de lettres, et la quasi-totalité des espaces.

Rapidité d’apparition du mot visé

Sur les 667 mots attrapés avant la fin de frappe :

Lettres tapées avant apparition 1 2 3 4 5 6
Mots 362 171 88 38 7 1

Médiane : 1 lettre. Plus de la moitié des mots attrapés le sont dès la première lettre — l’effet combiné des fréquences du dictionnaire et du modèle n-gram contextuel.

Position du chip choisi

1ᵉʳ chip 2ᵉ chip 3ᵉ chip
468 (58,1 %) 244 (30,3 %) 94 (11,7 %)

Le classement est bon : quand le mot est disponible, il est en tête plus d’une fois sur deux.

Disponibilité selon la longueur du mot

Longueur Proposé / total Taux
1 lettre 0 / 44 0 % (jamais suggérés — voir ci-dessous)
2 lettres 365 / 389 93,8 %
3 lettres 153 / 173 88,4 %
4 lettres 157 / 174 90,2 %
5 lettres 69 / 97 71,1 %
6 lettres 48 / 78 61,5 %
7-8 lettres 14 / 24 58,3 %
9-10 lettres 0 / 3 0 %

Les mots d’une lettre (i, a, é — 44 occurrences) ne sont jamais proposés : ils sont absents du dictionnaire et, une fois la lettre tapée, il n’y a de toute façon plus rien à compléter. Pour un utilisateur « strictement suggestions », ce sont les seuls mots où l’espace doit être tapé à la main systématiquement.

Exactitude des messages envoyés

Métrique Brute Insensible à la casse
Messages identiques au texte visé 47 / 134 (35,1 %) 101 / 134 (75,4 %)
Exactitude caractère (Levenshtein) 95,08 % 97,44 %

L’écart spectaculaire entre les deux colonnes vient d’un seul phénomène, détaillé ci-dessous : 54 messages (40,3 %) ne diffèrent du texte visé que par la casse du premier mot.

Deux découvertes sur le moteur (actionnables)

1. Tap de suggestion sous majuscule automatique → mot entier en MAJUSCULES

Quand le premier mot d’un message est committé par tap alors que le shift automatique est encore actif (une seule lettre tapée, en majuscule), applyCasingPattern() généralise la casse du préfixe « 100 % majuscule » à toute la suggestion : Bèl → BÈL, An → AN, Nou → NOU, Mwen → MWEN… Le phénomène a touché 54 messages sur 134. Un préfixe d’une seule lettre majuscule devrait être traité comme une casse de type titre (première lettre seulement), pas comme du tout-majuscules.

2. La correspondance exacte peut être battue par les variantes accentuées

176 mots n’ont jamais été proposés. La plupart sont réellement absents du dictionnaire, mais 5 mots présents dans creole_dict.json ne sont jamais apparus dans le top 3, éjectés par des concurrents plus fréquents via la correspondance tolérante aux accents :

Mot tapé (fréq. dict) Top 3 affiché après frappe complète
fo (60) fò, fòs, fon
bò (72) bon, bout, bouch
red (15) rèd, rédé, rédé-yo (red classé 5ᵉ en interne)
jòdi (3), soley (2) variantes plus fréquentes

Le cas bò est le plus parlant : l’utilisateur a explicitement tapé le ò accentué, mais les mots en bo-/bon- non accentués, plus fréquents, remplissent les 3 chips. Une priorité de rang à la correspondance exacte du préfixe (accents compris) corrigerait les cinq cas d’un coup.

Mots réellement absents du dictionnaire (candidats à l’enrichissement)

renmen (6 occ.), konsè (5), démen (5), fatigé (4), sérié (3), nòwmal (3), aswè (3), ainsi que les mots-outils d’une lettre i, é, a. Rappel : le dictionnaire étant régénéré depuis le dataset Hugging Face par la CI, l’enrichissement durable passe par le dataset (les ajouts manuels dans le JSON seraient écrasés).

Latence

  Valeur
Mesures (une par lettre tapée) 2 123
Moyenne 27,6 ms
Médiane -38,0 ms
Écart-type 223,8 ms

Toujours sous le plancher de mesure (bruit ADB/horloge hôte-émulateur), alors que ce mode sollicite le moteur à chaque lettre — soit le double de lectures du passage du 10 juillet. Aucun ralentissement observé sur les ~40 minutes.

Anomalies observées (essentiellement des races du harnais)

33 messages diffèrent du texte visé au-delà de la casse. Trois familles, tirées de la comparaison texte envoyé / texte visé :

  1. Mot entièrement perdu (~14 cas — ka, mwen ×3, ni, gwadloup…) : le tap sur le chip est parti pendant un rafraîchissement de la barre et n’a rien committé, le harnais croyant le mot inséré.
  2. Mauvais mot committé (~13 cas — pou ou → pou ouvriyé, té → tout, lwen → lè…) : la liste s’est rafraîchie entre la lecture du logcat et le tap, un autre mot occupait la position visée.
  3. Espace fusionné (~11 cas — an pi → anpi, ka alé → kalé…) : espace automatique ou manuel perdu dans les mêmes conditions de vitesse.

Ces races sont un artefact de l’automatisation (taps à ~100 ms du rafraîchissement, 806 taps de chips contre 67 au passage réaliste, d’où une exposition ~12× supérieure — 2 anomalies le 10 juillet, ~38 ici, ordres de grandeur cohérents). À noter tout de même : un humain rapide peut rencontrer la variante « mauvais mot committé » dans la vraie vie (tap au moment où la barre se rafraîchit) — un délai minimal de stabilité de la barre avant prise en compte du tap pourrait la neutraliser. Tous ces cas sont inclus dans les statistiques ci-dessus, non retirés.

Captures d’écran

Une capture tous les ~15-20 messages (12 au total, human3_msg_*.png) — dossier rapport_simulation_suggestions_uniquement_2026-07-11_screenshots/. Contrairement au passage réaliste, chaque mot de ce passage est visible à l’écran en train d’être committé depuis la barre de suggestions.

Conclusion

En usage « strictement suggestions », le Klavyé Kréyòl committe 82 % des mots par tap et fait économiser un tiers des frappes de lettres (plus tous les espaces), avec un mot visé disponible dès la première lettre dans plus de la moitié des cas attrapés en cours de frappe et classé en 1ᵉʳ chip 58 % du temps. La latence reste indétectable malgré une sollicitation du moteur à chaque lettre.

Le protocole a surtout révélé deux défauts actionnables du moteur — la casse tout-majuscules appliquée aux suggestions committées sous shift automatique (40 % des messages touchés dans ce mode), et l’absence de priorité de la correspondance exacte face aux variantes accentuées plus fréquentes — ainsi qu’une liste concrète de mots à ajouter au dataset (renmen, konsè, démen, fatigé, sérié, nòwmal, aswè…). C’est exactement le type de retour que le passage « frappe parfaite » et le passage « réaliste » ne pouvaient pas produire.

Rapport de simulation — Suggestions uniquement, avec hésitations, sur v7.0.3

Date : 11 juillet 2026 Heure : 13:20 – 14:05 (CEST) Testeur : Claude Code (agent, modèle Fable 5), à la demande de l’utilisateur Version testée : 7.0.3 (versionCode 70003) — inclut le correctif de casse sous majuscule automatique Environnement : émulateur Android, AVD kreyol_test, fenêtre affichée à l’écran ; conversation SMS dédiée dans Google Messages.

⚠️ Méthodologie et limites du contenu : même dialogue imaginaire de 982 mots que les rapports précédents (non relu par un locuteur natif) — pas une référence linguistique.

Pourquoi ce passage ?

Deux demandes ont motivé ce troisième passage sur le même dialogue :

  1. Revalider le correctif de casse livré en 7.0.3 (rapport du 11/07 « suggestions uniquement » sur 7.0.2 : 54 messages sur 134 mis en tout-majuscules par un tap de suggestion sous shift automatique).
  2. Ajouter un comportement d’hésitation au mode « suggestions uniquement », qui jusqu’ici tapait chaque lettre sans jamais se tromper : sur ~20 % des mots de 3 lettres ou plus, l’utilisateur simulé tape 1-2 lettres correctes, tape une lettre fautive (touche physiquement voisine), s’en rend compte, l’efface au backspace, puis reprend la frappe normalement jusqu’à taper la suggestion.

Méthodologie

Résultats

Vue d’ensemble

Métrique Valeur Rappel run précédent (7.0.2, sans hésitation)
Messages envoyés 134 134
Mots traités 982 982
Messages identiques au texte visé (sensible casse) 93 / 134 (69,4 %) 47 / 134 (35,1 %)
Exactitude caractère (Levenshtein) 97,62 % 95,08 % (97,44 % insensible casse)
Mots committés par tap de suggestion 807 (82,2 %) 806 (82,1 %)
Mots jamais proposés 175 (17,8 %) 176 (17,9 %)

Le bug de casse est corrigé : sur 134 messages, seuls 4 diffèrent encore par la casse, et ce ne sont pas des majuscules parasites mais l’inverse — des noms propres en milieu de phrase (Jòj, Vytò, Pwentapit, Fwanswa) tapés en minuscules, conforme à la méthodologie documentée depuis le premier rapport (seul le début du champ bénéficie de l’auto-capitalisation native). Rien à corriger côté clavier ici.

Effet de l’hésitation

Métrique Valeur
Mots ayant déclenché une hésitation 98 / 982 (10,0 %)
… committés par suggestion malgré l’hésitation 85 / 98 (86,7 %)
… jamais proposés après l’hésitation 13 / 98
Taux de disponibilité — mots hésités 86,7 %
Taux de disponibilité — mots non hésités 81,7 %
Frappes additionnelles dues à l’hésitation (préfixe + faute + backspace) 320

L’hésitation ne dégrade pas la disponibilité des suggestions — au contraire, légèrement au-dessus de la moyenne (86,7 % vs 81,7 %), ce qui est attendu : le préfixe fautif est corrigé avant toute vérification finale, le moteur ne voit jamais la faute une fois le backspace effectué. La différence n’est pas significative sur cet échantillon (bruit d’échantillonnage), mais confirme au minimum que l’hésitation n’introduit pas de pénalité.

Rapidité d’apparition et position du chip

Lettres tapées avant apparition 1 2 3 4 5 6
Mots 343 182 101 37 8 1

Médiane inchangée : 1 lettre. Position du chip : 1ᵉʳ 474 (58,7 %), 2ᵉ 240 (29,7 %), 3ᵉ 93 (11,5 %) — quasi identique au run précédent.

Disponibilité par longueur de mot et mots jamais proposés

Longueur Disponible / total
2 lettres 362/389
3 lettres 156/173
4 lettres 158/174
5 lettres 69/97
6 lettres 48/78
7-8 lettres 14/24

Même liste qu’avant : renmen, konsè, démen, fatigé, sérié, nòwmal, bò, soley, red, fo, jòdi — confirmée stable, ces mots restent candidats à l’enrichissement du dataset (rappel : tout ajout doit passer par le dataset Hugging Face source, pas par une édition directe du JSON, qui serait écrasée au prochain run du pipeline).

Latence

  Valeur
Mesures (une par lettre + une par lettre fautive d’hésitation) 2 140
Moyenne 21,3 ms
Médiane -30,2 ms
Écart-type 197,4 ms

Toujours sous le plancher de mesure, y compris avec la charge supplémentaire des lectures pendant les hésitations.

Anomalies observées (races du harnais, hors casse)

37 messages sur 134 diffèrent du texte visé au-delà des 4 cas de noms propres non capitalisés — mot perdu ou tronqué (ex. sav → sa, on disparu), mauvais mot committé pendant un rafraîchissement de la barre (ex. mété → mét pou sa fusionné en mété sa), du même ordre de grandeur et de même nature que dans les deux rapports précédents : le tap arrive juste au moment où la liste de suggestions se rafraîchit. Comme précédemment, ces cas sont inclus dans les statistiques et non retirés. Le nombre plus élevé qu’au run du 10/07 (2 cas) reflète la même explication déjà documentée : ce mode sollicite un tap de suggestion sur 82 % des mots (contre 8 % au run réaliste), donc une exposition largement supérieure au même phénomène.

Captures d’écran

Une capture tous les ~15-20 messages (12 au total) — dossier rapport_simulation_suggestions_hesitation_2026-07-11_screenshots/.

Conclusion

Ce troisième passage confirme deux choses. D’abord, le correctif 7.0.3 fonctionne : le taux de messages parfaitement conformes double (35,1 % → 69,4 %) et les 4 écarts de casse restants sont un phénomène différent, déjà documenté et non lié au bug corrigé. Ensuite, l’ajout d’hésitations réalistes (frappe, faute, backspace) au mode « suggestions uniquement » ne change quasiment rien aux statistiques de fond — couverture du dictionnaire (82,2 % vs 82,1 %), vitesse d’apparition (médiane 1 lettre), position du chip, latence : tous stables par rapport au run sans hésitation. C’est un résultat rassurant en soi : le moteur de suggestions ignore proprement les faux départs de l’utilisateur, sans effet de bord une fois le backspace effectué.

Rapport UX — Parcours d’installation et d’activation du clavier Klavyé Kréyòl

Date : 11 juillet 2026 Testeur : Claude Code (agent), à la demande de l’utilisateur Version testée : 7.0.3 Méthode : inspection du code (SettingsActivity.kt, AndroidManifest.xml, docs/, KreyolKeybPlayStore/) croisée avec un parcours live sur émulateur (AVD kreyol_test, Pixel 5, Android 14) après désinstallation + désactivation forcée de l’IME côté système, pour reproduire fidèlement l’état d’un appareil qui n’a jamais eu l’app — ce que l’émulateur de test réutilisé sur cette session n’était plus, ses réglages système ayant persisté d’un test précédent malgré une simple désinstallation.

Ce rapport complète rapport_ux.md (audit général des écrans) en se concentrant spécifiquement sur le tunnel d’activation : du premier lancement post-install Play Store jusqu’au premier mot tapé avec le clavier actif ailleurs que dans l’app.

1. Résumé exécutif

  1. L’utilisateur affronte deux avertissements système consécutifs, pas un seul — et l’app ne prépare à aucun des deux. Le premier (“ce clavier peut collecter tout ce que vous tapez, y compris mots de passe et numéros de carte”) est le plus dissuasif de tout le parcours ; le second, moins connu, avertit que l’app sera indisponible tant que le téléphone n’est pas déverrouillé après un redémarrage.
  2. La politique de confidentialité — complète, rassurante, “zéro collecte” — n’est reliée nulle part : ni dans l’app, ni dans la fiche Play Store, ni au moment précis où l’avertissement système fait le plus peur.
  3. Le libellé du clavier est tronqué dans les écrans système (“Klavyé Kréyòl Karukera Potomita…”) faute de place — visible aussi bien dans la liste des claviers que dans le sélecteur.
  4. Deux fonctions de statut plus visibles ont été codées puis jamais branchées (createActivationBanner(), createStatusBar()) : le signal qu’une meilleure mise en avant de l’état d’activation avait été envisagée, sans être finalisée.
  5. La confirmation de succès existe et est bien faite (“Tout est prêt ! Vous pouvez taper en Kréyòl partout !”) — mais elle n’apparaît que si l’utilisateur revient dans l’app ; rien ne le signale s’il quitte directement vers Messages ou une autre app après la sélection.
  6. Aucun tip contextuel au premier usage réel : l’astuce accents longue-pression n’existe que dans la carte de succès de l’app, jamais montrée quand le clavier apparaît pour la première fois dans un vrai champ de saisie ailleurs.
  7. La checklist d’activation elle-même est bien conçue : vérification d’état en temps réel (pas de simple “je te fais confiance”), déblocage progressif des étapes, boutons qui déclenchent exactement les bons écrans système. C’est la meilleure partie du parcours actuel.

2. Cartographie du parcours réel, étape par étape

2.1 Premier lancement

Pas de MainActivity : SettingsActivity (seule Activity LAUNCHER du manifest) s’ouvre directement sur l’onglet « 🚀 Démarrage », Étape 1 sur 3, avec un bouton unique OUVRIR LES PARAMÈTRES.

Premier lancement

2.2 Écran système « Clavier virtuel »

openKeyboardSettings() (SettingsActivity.kt:1493) lance Intent(Settings.ACTION_INPUT_METHOD_SETTINGS). L’utilisateur atterrit sur la liste des claviers du téléphone, avec le nôtre éteint, à côté de Gboard et Google Voice Typing.

Écran système clavier virtuel

2.3 Premier avertissement système (le plus critique du parcours)

Dès que l’utilisateur bascule l’interrupteur, Android affiche un dialogue générique et non personnalisable par l’app :

Attention — This input method may be able to collect all the text you type, including personal data like passwords and credit card numbers. It comes from the app Klavyé Kréyòl. Use this input method?

Avertissement collecte de données

Rien dans le parcours ne prépare à ce moment. C’est le point de friction le plus élevé de tout le tunnel — l’endroit où un utilisateur prudent (le public visé, personnes soucieuses de confidentialité) abandonne, alors que le projet a précisément une politique « zéro collecte » à faire valoir ici.

2.4 Second avertissement système, inattendu

Après avoir validé le premier, un second dialogue apparaît immédiatement, sans lien apparent avec le premier :

Note: After a reboot, this app can’t start until you unlock your phone.

Avertissement direct boot

Cause technique : AndroidManifest.xml ne déclare android:directBootAware nulle part (ni sur <application>, ni sur le service IME), donc l’app est traitée par défaut comme incompatible avec le stockage chiffré avant déverrouillage — comportement standard Android pour toute app dans ce cas, pas un bug introduit par le code du projet, mais un second choc inattendu dans une séquence déjà tendue. La plupart des utilisateurs n’ont jamais vu ce message et ne savent pas à quoi il correspond.

L’IME apparaît enfin activé dans la liste :

IME activé

2.5 Retour dans l’app — détection automatique

L’utilisateur revient (bouton retour du téléphone) : l’app a déjà détecté le changement d’état par elle-même — carte « Vous y êtes presque ! », étape 1 cochée ✓ ACTIVÉ, étape 2 débloquée. Ce rafraîchissement automatique (isKeyboardEnabled()/isKeyboardSelected() revérifiés à onResume() et par un polling toutes les 2s tant que l’onglet est visible, startPeriodicRefresh, l.2229) fonctionne bien et évite à l’utilisateur de devoir cliquer sur un bouton « J’ai fini ».

2.6 Sélection du clavier — libellé tronqué

openInputMethodPicker() (l.1516) appelle imm.showInputMethodPicker(). Le sélecteur système liste Gboard et notre clavier — mais le nom est coupé :

Klavyé Kréyòl Karukera Potomita…

Sélecteur tronqué

Le nom affiché dans les réglages système est en réalité « Klavyé Kréyòl Karukera Potomitan™ » (label de l’IME dans le manifest), plus long que celui utilisé dans les textes d’onboarding de l’app (« Klavyé Kréyòl Karukera », sans « Potomitan™ »). Un utilisateur qui scanne rapidement la liste ne voit ni le nom exact annoncé par l’app, ni le nom complet.

2.7 Confirmation de succès — bien faite, mais seulement si l’utilisateur revient

Une fois le clavier sélectionné, l’utilisateur qui revient dans l’app voit :

Tout est prêt ! Vous pouvez taper en Kréyòl partout ! — Configuration terminée ✓

Configuration terminée

C’est un bon écran, clair, positif. Mais rien ne le montre à l’utilisateur qui ne revient pas dans l’app — par exemple s’il change de clavier directement depuis la barre système (appui long sur la barre d’espace) puis va droit dans Messages : aucun Toast, aucune notification, aucun signal ne confirme jamais explicitement que tout est en ordre en dehors de ce retour volontaire dans l’app.

2.8 Premier usage réel du clavier

Le champ de test intégré (« Tester le clavier », hint Ékri an Kréyòl la…) fait apparaître le clavier pour la première fois :

Premier affichage du clavier

Rien à l’écran n’indique la fonctionnalité d’accents par appui long (AccentHandler) — elle n’est mentionnée que dans le texte de la carte « Tout est prêt ! » de l’onglet Démarrage, jamais affichée au niveau du clavier lui-même, ni ici ni la première fois que l’utilisateur tape réellement dans une autre app (Messages, Notes…).

3. Constats détaillés

3.1 Aucune préparation à l’avertissement système Android — le point le plus critique

Recherche exhaustive dans le code de l’app (android_keyboard/app/src/main/) : zéro occurrence de « privacy »/« confidentialité ». Rien n’anticipe le dialogue « collecte de données » avant qu’il n’apparaisse, alors que c’est prévisible et systématique (Android l’affiche pour tout IME tiers, sans exception).

3.2 Politique de confidentialité complète mais orpheline

docs/privacy/PRIVACY_POLICY_FR.md (856 lignes) et privacy-policy.html existent, sont à jour (v2.0), détaillent précisément le stockage 100% local, le zéro-collecte, les permissions (BIND_INPUT_METHOD, WRITE_USER_DICTIONARY), la conformité RGPD. Ce document n’est lié nulle part : ni dans SettingsActivity.kt, ni dans KreyolKeybPlayStore/texts/description.md. Le contenu qui répondrait le mieux à la peur suscitée par le dialogue système (§3.1) existe déjà, il est juste invisible au bon moment.

3.3 Deux fonctions de statut mortes, signe d’une UX inachevée

Deux implémentations alternatives d’un statut plus visible ont donc été écrites puis abandonnées au profit de la checklist actuelle — ce n’est pas gênant en soi, mais laisser ce code mort dans le fichier source (déjà signalé comme pattern récurrent par rapport_ux.md) complique la maintenance future de cet écran précis.

3.4 Nom de l’IME trop long pour les listes système

Le label déclaré (android:label de l’IME, probablement dans strings.xml/manifest) est « Klavyé Kréyòl Karukera Potomitan™ » — 33 caractères, systématiquement tronqué dans les listes système Android (§2.6, capture à l’appui). Les textes internes de l’app utilisent une version plus courte (« Klavyé Kréyòl Karukera »), créant une discordance entre ce que l’app annonce et ce que le système affiche réellement.

3.5 Textes d’onboarding non externalisés

Aucun texte de l’onglet Démarrage n’est dans strings.xml (qui ne contient que 9 entrées, toutes des noms d’app/service) — tout est en dur dans SettingsActivity.kt. Cela ferme la porte à une traduction (anglais, pour la diaspora ?) sans toucher au code, et complique les tests unitaires de contenu.

3.6 Fiche Play Store silencieuse sur le parcours réel

KreyolKeybPlayStore/texts/description.md promet une « installation simple en 2 minutes » et met en avant le zéro-collecte, mais ne prépare à aucune des étapes réelles (Paramètres > Langues et saisie, ni les deux dialogues système). Un futur utilisateur qui lit la fiche avant d’installer n’a aucune idée de ce qui l’attend.

3.7 Aucun onboarding contextuel dans le clavier lui-même

Aucune détection de « première frappe réelle » nulle part dans le code (recherche de patterns first_run/isFirstTime infructueuse). Le seul contenu pédagogique (accents longue-pression) est enfermé dans l’onglet Démarrage de l’app, jamais poussé au moment où il serait le plus utile : la première fois que l’utilisateur tape réellement quelque part.

4. Ce qui fonctionne déjà bien

5. Propositions d’amélioration, priorisées

# Proposition Constat lié Effort estimé
1 Ajouter une carte explicative juste avant le bouton « Ouvrir les paramètres » : une phrase du type « Android va vous montrer un avertissement standard affiché pour tous les claviers tiers — nous ne collectons rien, tout reste sur votre téléphone » + un lien cliquable vers la politique de confidentialité déjà écrite. §3.1, §3.2 Faible — texte + Intent(ACTION_VIEW) vers l’URL déjà publiée
2 Lier la politique de confidentialité depuis l’onglet « À Propos » et depuis la fiche Play Store (description.md), au minimum. §3.2, §3.6 Très faible
3 Raccourcir ou clarifier le label système de l’IME (retirer « Potomitan™ » du label affiché dans les listes système, le garder pour le branding interne) pour qu’il ne soit plus tronqué et corresponde au nom utilisé dans les textes d’onboarding. §3.4 Faible — modification du label déclaré, à valider avec l’identité de marque
4 Ajouter une confirmation de succès qui ne dépend pas du retour dans l’app : un Toast ou une notification légère déclenchée dès que isKeyboardEnabled() && isKeyboardSelected() devient vrai (détectable par un ContentObserver sur Settings.Secure.DEFAULT_INPUT_METHOD plutôt que seulement du polling en foreground). §3.5, §2.7 Moyen
5 Supprimer ou finaliser createActivationBanner()/createStatusBar() — code mort qui alourdit SettingsActivity.kt sans bénéfice utilisateur. §3.3 Faible
6 Tip contextuel au premier usage réel : une notification ou un overlay léger déclenché une seule fois (flag SharedPreferences) lors de la première frappe détectée hors de l’app, rappelant l’astuce accents longue-pression. §3.7 Moyen — nécessite une détection de premier usage dans l’IME lui-même
7 (Optionnel, faible priorité) Envisager android:directBootAware="true" sur le service IME pour supprimer le second dialogue — complexité et implications sécurité à évaluer avant de s’engager (accès au stockage chiffré par les identifiants uniquement) ; à documenter au moins dans la carte d’explication du point 1 si non traité. §2.4 Élevé / à évaluer

Conclusion

Le tunnel d’activation du Klavyé Kréyòl repose sur une base technique solide — détection d’état en temps réel, déblocage progressif, bons Intents système — mais laisse l’utilisateur seul face aux deux moments les plus anxiogènes du parcours : les deux avertissements système consécutifs, dont le premier est exactement celui qu’une politique de confidentialité « zéro collecte » déjà rédigée pourrait neutraliser, si elle était montrée au bon endroit. Les corrections les plus rentables (liens vers la politique de confidentialité, phrase d’explication avant l’avertissement, nettoyage du code mort) sont à faible effort et s’attaquent directement au point où un utilisateur prudent — précisément le public que ce projet cherche à rassurer — est le plus susceptible d’abandonner.

Améliorations du tunnel d’activation — 11 juillet 2026

Suite au rapport rapport_onboarding_activation_2026-07-11.md, 5 des 7 recommandations ont été implémentées et vérifiées sur émulateur (kreyol_test, premier lancement reproduit à l’identique : désinstallation + désactivation forcée de l’IME côté système). La recommandation #7 (directBootAware) reste volontairement hors scope (implications sécurité à évaluer séparément).

1. Carte explicative avant l’avertissement système Android

Une carte apparaît désormais dans l’onglet Démarrage, tant que le clavier n’est pas activé, expliquant que l’avertissement système est standard pour tout clavier tiers et pointant vers la politique de confidentialité.

Carte explicative

Le lien ouvre bien la politique de confidentialité dans le navigateur :

Lien politique de confidentialité

2. Lien vers la politique de confidentialité dans « À Propos »

Nouvelle carte « 🔒 Confidentialité » ajoutée à l’onglet À Propos, avec le même lien.

À Propos Carte confidentialité

3. Nom du clavier non tronqué dans les listes système

keyboard_name/subtype_kreyol raccourcis de « Klavyé Kréyòl Karukera Potomitan™ » à « Klavyé Kréyòl Karukera » — le nom s’affiche désormais en entier dans les paramètres système.

Nom non tronqué

4. Confirmation de succès + tip contextuel au premier usage réel

Un Toast unique (« Kréyòl a klavyé a ! Apiyé lontan asi on lèt pou wè aksan yo… ») se déclenche la première fois que le clavier est utilisé réellement en dehors de l’app elle-même (détecté via EditorInfo.packageName, flag SharedPreferences posé une seule fois). Vérifié fonctionnellement : le flag first_real_use_tip_shown se pose bien lors de la première frappe dans Google Messages, jamais dans le champ de test intégré à l’app.

5. Suppression du code mort

createActivationBanner() et createStatusBar() — deux fonctions de statut jamais appelées — retirées de SettingsActivity.kt.

Fiche Play Store

KreyolKeybPlayStore/texts/description.md complétée avec le lien vers la politique de confidentialité et le rappel explicite des 3 étapes d’activation (Paramètres > Langues et saisie > Claviers virtuels).

6. Nouvel onglet « Guide »

Un 6ᵉ onglet a été ajouté (GuideFragment), un guide détaillé de l’utilisateur : écriture en kréyòl, accents par appui long, suggestions/autocomplétion, correction orthographique système, chiffres/symboles, les 2 jeux de vocabulaire, les 8 niveaux de progression (Pipirit → Benzo), et une FAQ courte (clavier invisible, changer de clavier, confidentialité).

Barre d'onglets à 6 éléments Contenu du guide FAQ du guide

Vérification

Build + suite de tests unitaires au vert après chaque changement. Parcours complet rejoué manuellement sur l’émulateur kreyol_test (état vierge reproduit). Navigation cyclique entre les 6 onglets vérifiée (tab bar et contenu toujours synchronisés).