Normalisation de la recherche en arabe : absence de prise en charge des variantes de Hamza, des formes de Ya/Kaf et de l'équivalence orthographique

Bonjour l’équipe Discourse,

Nous gérons un forum multilingue avec un contenu arabe et persan important, et nous avons rencontré une limitation critique dans la fonctionnalité de recherche liée à la normalisation orthographique arabe.

:magnifying_glass_tilted_left: Description du problème

L’écriture arabe comprend plusieurs représentations Unicode pour des caractères sémantiquement identiques. Malheureusement, le moteur de recherche actuel de Discourse semble traiter ces variantes comme distinctes, ce qui entraîne des résultats de recherche incomplets ou trompeurs.

Exemples :

  • Rechercher إطلاق مقامي ne renvoie que des correspondances exactes, tandis que les messages contenant اطلاق مقامي, أطلاق مقامي, ou إطلاق‌مقامي sont exclus.
  • De même, la recherche de ي (U+064A) ne correspond pas à ی (U+06CC), et ك (U+0643) ne correspond pas à ک (U+06A9), malgré leur équivalence fonctionnelle dans les contextes arabe/persan.

Cela affecte non seulement les variantes de hamza (أ, إ, ء, ؤ, ئ), mais aussi les substitutions courantes telles que :

Caractère Unicode Normalisation suggérée
أ, إ, ء, آ U+0623, U+0625, U+0621, U+0622 Normaliser en ا
ؤ U+0624 Normaliser en و
ئ U+0626 Normaliser en ي
ى U+0649 Normaliser en ي
ة U+0629 Normaliser en ه
ي vs ی U+064A vs U+06CC Normaliser en ی
ك vs ک U+0643 vs U+06A9 Normaliser en ک

Ce problème est aggravé lorsque les utilisateurs omettent les diacritiques ou utilisent différentes dispositions de clavier, ce qui entraîne un comportement de recherche fragmenté.


:gear: Solution proposée

Nous recommandons la mise en œuvre d’une couche de normalisation consciente de Unicode lors de l’indexation et de l’analyse des requêtes. Cela peut être réalisé en :

  1. Prétraitant le contenu indexé et les requêtes des utilisateurs pour unifier les variantes de caractères.
  2. Appliquant des règles de normalisation similaires à celles utilisées dans les bibliothèques de NLP arabe ou les moteurs de recherche (par exemple, Farasa, Hazm, ou des mappeurs personnalisés basés sur des expressions régulières).
  3. Optionnellement, en prenant en charge la correspondance floue ou la distance de Levenshtein pour des correspondances quasi exactes.

Voici un exemple simplifié d’une fonction de normalisation (style Java) :

public static String normalizeArabic(String text) {
  return text.replace("أ", "ا")
             .replace("إ", "ا")
             .replace("آ", "ا")
             .replace("ؤ", "و")
             .replace("ئ", "ي")
             .replace("ى", "ي")
             .replace("ة", "ه")
             .replace("ي", "ی")
             .replace("ك", "ک");
}

:folded_hands: Demande

Cette normalisation pourrait-elle être envisagée pour inclusion dans le moteur de recherche principal ou en tant que plugin ? Cela améliorerait considérablement la convivialité pour les communautés arabophones et persanophones utilisant Discourse.

S’il existe une solution de contournement ou un plugin existant qui résout ce problème, nous apprécierions tout conseil.

Merci de votre temps et de la création d’une plateforme aussi puissante.

Cordialement

1 « J'aime »

Le paramètre du site search_ignore_accents a-t-il un effet sur ce problème ?

1 « J'aime »

Merci beaucoup d’être intervenu et d’avoir contribué à la discussion

Pour répondre à votre question : oui, le paramètre search_ignore_accents est activé sur notre forum.
Malheureusement, cela ne résout pas le problème que nous rencontrons. Les résultats de recherche ne correspondent toujours pas aux caractères arabes et persans orthographiquement équivalents, le problème persiste donc malgré ce paramètre.

1 « J'aime »

Je pense que c’est une demande raisonnable car elle améliorerait considérablement l’expérience de recherche pour les sites arabes et persans. Nous serions ravis d’examiner une PR qui met en œuvre cette fonctionnalité, je vais donc y ajouter un pr-welcome.

Pour quiconque décide de travailler sur cette fonctionnalité : toute la logique de normalisation doit être conditionnée par un paramètre de site qui l’active par défaut pour les sites arabes et persans (voir locale_default dans site_settings.yml) et tous les autres paramètres régionaux doivent avoir ce paramètre désactivé par défaut. Core possède déjà une logique de normalisation similaire pour les caractères accentués (voir lib/search.rb), ce qui serait une référence utile lors de la mise en œuvre de cette fonctionnalité.

4 « J'aime »

Merci beaucoup, Oussama ! Je suis vraiment heureux de voir que cette suggestion a été bien accueillie.

2 « J'aime »

Pour cette partie du problème, parlons-nous d’une normalisation Unicode standard NFKC (pour en choisir une) ?

(Je ne suis même pas sûr de ce que nous faisons… Je suppose que nous normalisons le texte des publications dans le pipeline de cuisson ?)

1 « J'aime »

Je ne suis pas un expert technique, mais j’ai recherché ce problème car je veux m’assurer qu’aucune requête de recherche ne soit manquée sur un forum bilingue persan-arabe Discourse. Comme Discourse utilise PostgreSQL, la normalisation devient essentielle : un utilisateur pourrait rechercher en utilisant des caractères persans, tandis que le même mot est stocké en utilisant des caractères arabes, ou vice versa. Sans normalisation appropriée, la recherche échouera.
D’après ce que j’ai appris, l’utilisation de la normalisation Unicode NFKC est un bon point de départ : elle gère de nombreux cas de compatibilité tels que les ligatures, les formes de présentation et les chiffres arabes/persans.

Cependant, pour le texte persan et arabe, NFKC seul n’est pas suffisant. Il ne normalise pas plusieurs variantes de caractères critiques qui sont visuellement et sémantiquement équivalentes mais diffèrent au niveau binaire.

Ci-dessous, je décris les procédures et les idées auxquelles je suis parvenu grâce à mes recherches et explorations.


:wrench: Stratégie de conception globale

  1. Appliquer d’abord la normalisation Unicode NFKC pour gérer les ligatures, les formes de présentation et l’unification des chiffres.
  2. Appliquer ensuite des mappages de caractères personnalisés dans un ordre défini (par exemple, normaliser les variantes de Hamza avant le Ya arabe).
  3. Séparer les politiques de normalisation pour le stockage et la recherche :
    • Utiliser un profil Conservateur pour le stockage canonique (préserver ZWNJ, éviter les changements sémantiques).
    • Utiliser un profil Permissif pour la recherche (ignorer ZWNJ, unifier les variantes de Hamza, normaliser les chiffres).
  4. Tous les mappages doivent être configurables via une table de mappage centralisée dans la base de données ou un hash Ruby dans l’application.

:one: Profils de normalisation

:green_circle: Conservateur (pour le stockage)

  • Transformation minimale
  • Appliquer NFKC
  • Normaliser le Kaf/Ya arabe en équivalents persans
  • Supprimer les diacritiques
  • Préserver ZWNJ
  • Stocker sous forme de texte_original + normalisé_conservateur

:blue_circle: Permissif (pour la recherche)

  • Correspondance agressive
  • Appliquer toutes les règles Conservatrices
  • Supprimer/ignorer ZWNJ
  • Normaliser les variantes de Hamza en lettres de base
  • Convertir tous les chiffres en ASCII
  • Optionnellement, unifier Taa Marbuta → Heh
  • Utilisé pour le prétraitement des requêtes

:two: Table de mappage complète

Source Cible Unicode Notes
ك ک U+0643 → U+06A9 Kaf arabe → Kaf persan
ي ی U+064A → U+06CC Ya arabe → Ya persan
ى ی U+0649 → U+06CC Variante Ya finale
أ, إ, ٱ ا Divers → U+0627 Formes de Hamza → Alef
ؤ و U+0624 → U+0648 Hamza Waw
ئ ی U+0626 → U+06CC Hamza Ya
ء U+0621 Supprimer ou préserver (configurable)
ة ه U+0629 → U+0647 Taa Marbuta → Heh (optionnel)
ۀ هٔ U+06C0 ↔ U+0647+U+0654 Normaliser la forme composée
ڭ گ U+06AD → U+06AF Variantes régionales
U+200C ZWNJ : préserver en Conservateur, supprimer en Permissif
٤, ۴ 4 U+0664, U+06F4 → ASCII Normaliser les chiffres
Diacritiques U+064B–U+0652 Supprimer tous les harakat
ZWJ U+200D Supprimer les joncteurs invisibles
Espaces multiples Espace simple Normaliser l’espacement

:three: Extrait de mappage rapide (pour SQL ou Ruby)

ك → ک
ي → ی
ى → ی
أ → ا
إ → ا
ؤ → و
ئ → ی
ة → ه
ۀ → هٔ
ٱ → ا
٤, ۴ → 4
ZWNJ (U+200C) → (supprimé en permissif)
Harakat (U+064B..U+0652) → supprimé
ZWJ (U+200D) → supprimé

:four: Implémentation dans PostgreSQL

  • Créer une table text_normalization_map
  • Utiliser regexp_replace ou des chaînes TRANSLATE pour la performance
  • Optionnellement, implémenter en PL/Python ou PL/v8 pour le support Unicode
  • Normaliser le contenu stocké et les requêtes entrantes en utilisant la même logique

Stratégie d’indexation

  • Stocker normalized_conservative pour l’indexation canonique
  • Normaliser les requêtes avec normalize_persian_arabic(query, 'permissive')
  • Si vous utilisez la recherche permissive, l’index doit correspondre au même profil
  • Optionnellement, stocker les deux versions pour une comparaison croisée

:five: Exemple de Hash Ruby (pour Discourse)

NORMALIZATION_MAP = {
  "ك" => "ک",
  "ي" => "ی",
  "ى" => "ی",
  "أ" => "ا",
  "إ" => "ا",
  "ٱ" => "ا",
  "ؤ" => "و",
  "ئ" => "ی",
  "ة" => "ه",
  "ۀ" => "هٔ",
  "۴" => "4",
  "٤" => "4",
  "\u200C" => "", # ZWNJ
  "\u200D" => "", # ZWJ
}

:six: Notes de performance et pratiques

  1. Appliquer NFKC dans la couche applicative (par exemple, Ruby unicode_normalize(:nfkc))
  2. Utiliser des index séparés pour les profils conservateur et permissif
  3. Éviter les mappages forcés de caractères sémantiquement sensibles (par exemple, Hamza, Taa Marbuta) sauf s’ils sont explicitement configurés
  4. Exécuter des tests A/B sur des données réelles du forum pour mesurer le taux de correspondance et les faux positifs
  5. Documenter chaque mappage avec sa justification et des exemples
  6. Définir des tests unitaires en Ruby et en SQL pour chaque mappage

:seven: Recommandation finale

  • Utiliser Unicode NFKC comme base
  • L’étendre avec une couche de mappage personnalisée
  • Maintenir des profils doubles pour le stockage et la recherche
  • Implémenter la normalisation dans les couches applicative et de base de données
  • Documenter et tester chaque mappage
  • Créer des index appropriés (GIN + to_tsvector) sur les colonnes normalisées