Arabische Suchnormalisierung: Fehlende Unterstützung für Hamza-Varianten, Ya/Kaf-Formen und orthografische Äquivalenz

Hallo Discourse-Team,

wir betreiben ein mehrsprachiges Forum mit erheblichen arabischen und persischen Inhalten und sind auf eine kritische Einschränkung der Suchfunktionalität im Zusammenhang mit der arabischen orthografischen Normalisierung gestoßen.

:magnifying_glass_tilted_left: Problembeschreibung

Das arabische Skript enthält mehrere Unicode-Darstellungen für semantisch identische Zeichen. Leider scheint die aktuelle Suchmaschine von Discourse diese Varianten als unterschiedlich zu behandeln, was zu unvollständigen oder irreführenden Suchergebnissen führt.

Beispiele:

  • Eine Suche nach إطلاق مقامي liefert nur exakte Treffer, während Beiträge mit اطلاق مقامي, أطلاق مقامي oder إطلاق‌مقامي ausgeschlossen werden.
  • Ähnlich stimmt die Suche nach ي (U+064A) nicht mit ی (U+06CC) überein, und ك (U+0643) stimmt nicht mit ک (U+06A9) überein, obwohl sie im arabischen/persischen Kontext funktional äquivalent sind.

Dies betrifft nicht nur Hamza-Varianten (أ, إ, ء, ؤ, ئ), sondern auch gängige Ersetzungen wie:

Zeichen Unicode Vorgeschlagene Normalisierung
أ, إ, ء, آ U+0623, U+0625, U+0621, U+0622 Normalisierung zu ا
ؤ U+0624 Normalisierung zu و
ئ U+0626 Normalisierung zu ي
ى U+0649 Normalisierung zu ي
ة U+0629 Normalisierung zu ه
ي vs ی U+064A vs U+06CC Normalisierung zu ی
ك vs ک U+0643 vs U+06A9 Normalisierung zu ک

Dieses Problem wird durch das Weglassen von Diakritika durch Benutzer oder die Verwendung unterschiedlicher Tastaturlayouts noch verschärft, was zu einem fragmentierten Suchverhalten führt.


:gear: Vorgeschlagene Lösung

Wir empfehlen die Implementierung einer Unicode-fähigen Normalisierungsschicht sowohl bei der Indizierung als auch bei der Abfrageverarbeitung. Dies kann erreicht werden durch:

  1. Vorverarbeitung sowohl des indizierten Inhalts als auch der Benutzerabfragen, um Zeichenvarianten zu vereinheitlichen.
  2. Anwendung von Normalisierungsregeln, ähnlich denen, die in arabischen NLP-Bibliotheken oder Suchmaschinen verwendet werden (z. B. Farasa, Hazm oder benutzerdefinierte Regex-basierte Mapper).
  3. Optional: Unterstützung von Fuzzy-Matching oder Levenshtein-Distanz für nahezu exakte Treffer.

Hier ist ein vereinfachtes Beispiel für eine Normalisierungsfunktion (im Java-Stil):

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

:folded_hands: Anfrage

Könnte diese Normalisierung für die Aufnahme in die Kernsuchmaschine oder als Plugin in Betracht gezogen werden? Sie würde die Benutzerfreundlichkeit für arabische und persische Gemeinschaften, die Discourse nutzen, erheblich verbessern.

Wenn es eine bestehende Problemumgehung oder ein Plugin gibt, das dieses Problem löst, würden wir uns über jede Anleitung freuen.

Vielen Dank für Ihre Zeit und dafür, dass Sie eine so leistungsstarke Plattform aufbauen.

Mit freundlichen Grüßen

1 „Gefällt mir“

Hat die Website-Einstellung search_ignore_accents eine Auswirkung auf dieses Problem?

1 „Gefällt mir“

Vielen Dank, dass Sie sich an der Diskussion beteiligt haben.

Um Ihre Frage zu beantworten: Ja, die Einstellung search_ignore_accents ist in unserem Forum aktiviert.
Leider löst dies das Problem, mit dem wir konfrontiert sind, nicht. Die Suchergebnisse stimmen immer noch nicht mit orthografisch äquivalenten arabischen und persischen Zeichen überein, sodass das Problem trotz dieser Einstellung weiterhin besteht.

1 „Gefällt mir“

Ich denke, das ist eine vernünftige Anfrage, da sie die Sucherfahrung für arabische und persische Websites erheblich verbessern würde. Wir würden uns freuen, einen PR zu prüfen, der diese Funktion implementiert, daher werde ich ihr ein pr-welcome geben.

Für alle, die sich entscheiden, an dieser Funktion zu arbeiten: Die gesamte Normalisierungslogik sollte hinter einer Website-Einstellung stehen, die diese standardmäßig für arabische und persische Websites aktiviert (siehe locale_default in site_settings.yml), und für alle anderen Lokalisierungen sollte diese Einstellung standardmäßig deaktiviert sein. Core hat bereits eine ähnliche Normalisierungslogik für akzentuierte Zeichen (siehe lib/search.rb), daher wäre dies eine nützliche Referenz bei der Implementierung dieser Funktion.

4 „Gefällt mir“

Vielen Dank, Osama! Ich freue mich sehr, dass dieser Vorschlag gut aufgenommen wurde.

2 „Gefällt mir“

Wenn wir über diesen Teil des Problems sprechen, meinen wir eine Unicode-Standard-NFKC-Normalisierung (um eine auszuwählen)?

(Ich bin mir nicht einmal sicher, was wir tun … Ich nehme an, wir normalisieren den Post-Text in der Cooking-Pipeline?)

1 „Gefällt mir“

Ich bin kein technischer Experte, aber ich habe mich mit diesem Thema beschäftigt, da ich sicherstellen möchte, dass keine Suchanfragen auf einem persisch-arabischen zweisprachigen Discourse-Forum übersehen werden. Da Discourse PostgreSQL verwendet, wird die Normalisierung unerlässlich: Ein Benutzer könnte mit persischen Zeichen suchen, während dasselbe Wort in arabischen Zeichen gespeichert ist – oder umgekehrt. Ohne ordnungsgemäße Normalisierung schlägt die Suche fehl.
Basierend auf dem, was ich gelernt habe, ist die Unicode NFKC-Normalisierung ein solider Ausgangspunkt – sie behandelt viele Kompatibilitätsfälle wie Ligaturen, Darstellungsformen und arabische/persische Ziffern.

NFKC allein reicht jedoch für persischen und arabischen Text nicht aus. Es normalisiert mehrere kritische Zeichenvarianten nicht, die visuell und semantisch äquivalent sind, sich aber auf binärer Ebene unterscheiden.

Unten skizziere ich die Verfahren und Erkenntnisse, zu denen ich durch meine Forschung und Erkundung gelangt bin.


:wrench: Gesamtentwurfsstrategie

  1. Zuerst Unicode NFKC-Normalisierung anwenden, um Ligaturen, Darstellungsformen und Ziffernvereinheitlichung zu behandeln.
  2. Dann benutzerdefinierte Zeichenzuordnungen anwenden in einer definierten Reihenfolge (z. B. Hamza-Varianten vor arabischem Ya normalisieren).
  3. Separate Normalisierungsrichtlinien für Speicherung vs. Suche:
    • Verwenden Sie ein konservatives Profil für die kanonische Speicherung (ZWNJ beibehalten, semantische Verschiebungen vermeiden).
    • Verwenden Sie ein permissives Profil für die Suche (ZWNJ ignorieren, Hamza-Varianten vereinheitlichen, Ziffern normalisieren).
  4. Alle Zuordnungen sollten über eine zentralisierte Zuordnungstabelle in der Datenbank oder einen Ruby-Hash in der Anwendung konfigurierbar sein.

:one: Normalisierungsprofile

:green_circle: Konservativ (für Speicherung)

  • Minimale Transformation
  • NFKC anwenden
  • Arabisches Kaf/Ya in persische Entsprechungen normalisieren
  • Diakritika entfernen
  • ZWNJ beibehalten
  • Speichern als original_text + normalized_conservative

:blue_circle: Permissiv (für Suche)

  • Aggressives Matching
  • Alle konservativen Regeln anwenden
  • ZWNJ entfernen/ignorieren
  • Hamza-Varianten in Basisbuchstaben normalisieren
  • Alle Ziffern in ASCII konvertieren
  • Optional Taa Marbuta → Heh vereinheitlichen
  • Wird für die Vorverarbeitung von Abfragen verwendet

:two: Umfassende Zuordnungstabelle

Quelle Ziel Unicode Hinweise
ك ک U+0643 → U+06A9 Arabisches Kaf → Persisches Kaf
ي ی U+064A → U+06CC Arabisches Ya → Persisches Ya
ى ی U+0649 → U+06CC Finale Ya-Variante
أ, إ, ٱ ا Verschiedene → U+0627 Hamza-Formen → Alef
ؤ و U+0624 → U+0648 Hamza Waw
ئ ی U+0626 → U+06CC Hamza Ya
ء U+0621 Entfernen oder beibehalten (konfigurierbar)
ة ه U+0629 → U+0647 Taa Marbuta → Heh (optional)
ۀ هٔ U+06C0 ↔ U+0647+U+0654 Zusammengesetzte Form normalisieren
ڭ گ U+06AD → U+06AF Regionale Varianten
U+200C ZWNJ: in Konservativ beibehalten, in Permissiv entfernen
٤, ۴ 4 U+0664, U+06F4 → ASCII Ziffern normalisieren
Diakritika U+064B–U+0652 Alle Harakat entfernen
ZWJ U+200D Unsichtbare Verbinder entfernen
Mehrere Leerzeichen Einzelnes Leerzeichen Leerzeichen normalisieren

:three: Schneller Zuordnungs-Snippet (für SQL oder Ruby)

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

:four: Implementierung in PostgreSQL

  • Erstellen Sie eine Tabelle text_normalization_map.
  • Verwenden Sie regexp_replace oder TRANSLATE-Ketten für die Leistung.
  • Implementieren Sie optional in PL/Python oder PL/v8 für Unicode-Unterstützung.
  • Normalisieren Sie sowohl gespeicherte Inhalte als auch eingehende Abfragen mit derselben Logik.

Indexierungsstrategie

  • Speichern Sie normalized_conservative für die kanonische Indexierung.
  • Normalisieren Sie Abfragen mit normalize_persian_arabic(query, 'permissive').
  • Wenn Sie permissive Suche verwenden, muss der Index demselben Profil entsprechen.
  • Speichern Sie optional beide Versionen für den Quervergleich.

:five: Ruby Hash Beispiel (für Discourse)

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

:six: Leistungs- und praktische Hinweise

  1. Wenden Sie NFKC in der Anwendungsschicht an (z. B. Ruby unicode_normalize(:nfkc)).
  2. Verwenden Sie separate Indizes für konservative vs. permissive Profile.
  3. Vermeiden Sie erzwungene Zuordnungen von semantisch sensiblen Zeichen (z. B. Hamza, Taa Marbuta), es sei denn, sie sind explizit konfiguriert.
  4. Führen Sie A/B-Tests mit echten Forendaten durch, um Trefferquote und Fehlalarme zu messen.
  5. Dokumentieren Sie jede Zuordnung mit Begründung und Beispielen.
  6. Definieren Sie Unit-Tests sowohl in Ruby als auch in SQL für jede Zuordnung.

:seven: Abschließende Empfehlung

  • Verwenden Sie Unicode NFKC als Basis.
  • Erweitern Sie es mit einer benutzerdefinierten Zuordnungsschicht.
  • Pflegen Sie duale Profile für Speicherung und Suche.
  • Implementieren Sie die Normalisierung sowohl in der Anwendungs- als auch in der Datenbankebene.
  • Dokumentieren und testen Sie jede Zuordnung.
  • Erstellen Sie geeignete Indizes (GIN + to_tsvector) für normalisierte Spalten.