Amal traite toute la complexité des diacritiques arabes : 8 marques tashkeel (fatha, damma, kasra, shadda, sukun, fathatan, dammatan, kasratan), 4 variantes d'alef (standard, madda, hamza au-dessus, hamza en-dessous, wasla), 3 variantes de hamza (isolée, sur waw, sur ya) et les ligatures Lam-Alef. La reconnaissance vocale de l'application, le rendu du texte et le scoring de similarité traitent tous l'arabe diacritisé ("كَتَبَ") différemment de l'arabe non diacritisé ("كتب") — une distinction cruciale ignorée par la plupart des applications d'apprentissage de l'arabe.
Importance des diacritiques pour l'apprentissage
Le problème de l'ambiguïté
L'arabe sans diacritiques est ambigu :
- "كتب" peut signifier :
- "kataba" (il a écrit) — passé
- "kutub" (livres) — nom pluriel
- "kutiba" (il a été écrit) — voix passive
Tout est orthographié identiquement sans diacritiques. Les diacritiques lèvent l'ambiguïté.
La progression de l'apprentissage
- Débutant : Apprendre à lire AVEC diacritiques (facile — voyelles marquées)
- Intermédiaire : S'entraîner AVEC diacritiques jusqu'à automatisation
- Avancé : Retirer peu à peu les diacritiques, lecture devient plus difficile
- Fluent : Lire sans diacritiques couramment (niveau natif)
La plupart des applications d'apprentissage de l'arabe ignorent l'étape 1 — elles n'enseignent pas les diacritiques du tout, ou les suppriment. Cela engendre de mauvaises habitudes. La progression d'Amal est scientifiquement correcte.
Notre implémentation au niveau Unicode
Les marques diacritiques (8 au total)
// lib/src/utils/arabic_extension.dart
class ArabicExtension {
static const Map tashkeelMarks = {
'FATHA': '\u064E', // َ (voyelle 'a')
'DAMMA': '\u064F', // ُ (voyelle 'u')
'KASRA': '\u0650', // ِ (voyelle 'i')
'SUKUN': '\u0652', // ْ (pas de voyelle)
'SHADDA': '\u0651', // ّ (lettre doublée)
'FATHATAN': '\u064B', // ً (tanween 'an')
'DAMMATAN': '\u064C', // ٌ (tanween 'un')
'KASRATAN': '\u064D', // ٍ (tanween 'in')
};
static const Map alefVariants = {
'ALEF_STANDARD': 'ا', // ا
'ALEF_WITH_MADDA': 'آ', // آ (allongé)
'ALEF_WITH_HAMZA_ABOVE': 'أ', // أ
'ALEF_WITH_HAMZA_BELOW': 'إ', // إ
'ALEF_WASLA': 'ٰ', // ٰ (alef de connexion)
};
static const Map hamzaVariants = {
'HAMZA_ISOLATED': 'ء', // Hamza seul
'HAMZA_ON_WAW': 'ؤ', // Hamza sur waw (و + hamza)
'HAMZA_ON_YEH': 'ئ', // Hamza sur yeh (ي + hamza)
};
}
Diacritiques coraniques et arrêts Uthmani
Pour Thurayya, nous prenons en charge des marques spécifiques au Coran :
static const Map quranicMarks = {
'STOP_FULL': 'ۖ', // Arrêt complet (‖)
'STOP_HALF': 'ۗ', // Demi-arrêt
'STOP_QUA': 'ۙ', // Arrêt Qua
'STOP_NECESSARY': 'ۚ', // Arrêt nécessaire
'TAJWEED_ELONGATION': '', // Indicateur d'allongement
};
Reconnaissance vocale sensible aux diacritiques
Biais contextuel avec diacritiques
Lorsqu'un enfant apprend "كَتَبَ" (il a écrit, temps passé), nous biaisons la reconnaissance vocale vers cette vocalisation exacte :
# src/services/stt_client.py
def recognize_with_diacritical_context(audio_bytes, expected_text):
# expected_text = "كَتَبَ" (avec diacritiques)
# Créer un indice de contexte vocal
speech_context = {
'phrases': [expected_text],
'boost': 20.0 # Fort boost pour texte attendu
}
# Envoyer à Google Cloud STT
response = google_stt_client.recognize(
audio=audio_bytes,
language_code='ar-SA',
speech_contexts=[speech_context]
)
# Résultat : Google STT est biaisé vers la prononciation "kataba"
return response
Scoring de similarité sensible aux diacritiques
Le scoring de similarité distingue le diacritisé du non-diacritisé :
def compare_pronunciations(expected, actual):
"""
expected: "كَتَبَ" (avec diacritiques)
actual: "كتب" (tentative de l'enfant, éventuellement non diacritisée)
"""
# Supprimer les diacritiques pour une comparaison grossière
expected_base = strip_diacritics(expected) # "كتب"
actual_base = strip_diacritics(actual) # "كتب"
# Similarité de base (ignorant les diacritiques)
base_similarity = string_similarity(expected_base, actual_base) # 1.0 (parfait)
# Bonus diacritique (si la tentative de l'enfant inclut des diacritiques)
diacritic_bonus = 0.0
if has_diacritics(actual):
diacritic_accuracy = diacritics_match_ratio(expected, actual)
diacritic_bonus = diacritic_accuracy * 0.15 # Jusqu'à +15% pour diacritiques corrects
# Score final
final_score = min(base_similarity + diacritic_bonus, 1.0)
return {
'base_score': base_similarity,
'diacritic_bonus': diacritic_bonus,
'final_score': final_score,
'feedback': 'Super ! La prononciation est parfaite. Ensuite, pratiquez les marques diacritiques.'
}
Cela signifie :
- L'enfant dit "كتب" (non diacritisé) → score 85-90% (base correcte, diacritiques manquants)
- L'enfant dit "كَتَبَ" (entièrement diacritisé) → score 98%+ (parfait)
- La progression est claire : maîtriser d'abord la prononciation de base, puis ajouter la subtilité diacritique
Défis d'affichage RTL
Gestion de la direction du texte
// lib/src/screens/lesson_screen.dart
Column(
children: [
Directionality(
textDirection: TextDirection.rtl, // Pour texte arabe
child: Text(
'كَتَبَ',
textAlign: TextAlign.right, // Aligné à droite pour RTL
style: TextStyle(
fontFamily: 'IBMPlexSansArabic',
fontSize: 36,
height: 1.8, // Hauteur de ligne supplémentaire pour diacritiques
),
),
),
// Instructions en anglais ci-dessous
Directionality(
textDirection: TextDirection.ltr, // Pour anglais
child: Text(
'Pronounce: "he wrote"',
textAlign: TextAlign.left, // Aligné à gauche pour LTR
),
),
],
)
Façonnage des lettres connectées
Les lettres arabes changent de forme selon la position :
- Isolée : "ك" (Kaf)
- Initiale : "كَـــ" (Kaf au début du mot)
- Médiane : "ـــكَـــ" (Kaf au milieu)
- Finale : "ـــكَ" (Kaf à la fin)
La police IBMPlexSansArabic gère la formation automatiquement, mais nous avons besoin de séquences Unicode correctes :
// Correct : Utilise caractères de jonction Unicode
String word = 'ك' + '\u0640' + 'ت' + '\u0640' + 'ب'; // Kashida (caractère d'extension)
// Incorrect : Concaténation directe
String word = 'ك' + 'ت' + 'ب'; // Peut ne pas se former correctement sur tous les appareils
Mélange de texte bidirectionnel
Lorsque l'anglais et l'arabe apparaissent ensemble :
RichText(
textDirection: TextDirection.rtl, // Globalement RTL
text: TextSpan(
children: [
TextSpan(text: 'means ', style: englishStyle), // LTR
TextSpan(text: 'كتاب', style: arabicStyle), // RTL
TextSpan(text: ' (book)', style: englishStyle), // LTR
],
),
)
Résultat : "means كتاب (book)" affiché avec flux bidirectionnel correct.
FAQ
Q : Pourquoi imposer les diacritiques aux débutants ? Cela ne rend-il pas l'apprentissage plus difficile ?
A : Initialement, oui. Mais apprendre avec diacritiques crée des associations son-lettre plus fortes. La recherche montre que l'apprentissage avec diacritiques produit une lecture fluide plus rapidement. Après la maîtrise avec diacritiques, lire sans eux devient une progression naturelle.
Q : Que faire si le clavier de mon enfant ne permet pas de taper des diacritiques ?
A : L'application ne demande jamais aux enfants de taper des diacritiques. La reconnaissance et la prononciation sont basées sur la voix. Seuls les adultes (enseignants, créateurs de contenu) doivent saisir des diacritiques, et ils utilisent des claviers spécialisés en arabe.
Q : Amal prend-il en charge les combinaisons diacritiques non standard ?
A : Nous prenons en charge toutes les combinaisons normalisées Unicode. Les combinaisons rares ou personnalisées peuvent ne pas s'afficher correctement, mais l'arabe coranique et moderne standard sont entièrement pris en charge.



