Zum Inhalt springen
OneKitly

Akzente entfernen zerstört die Suche — bis du es auf beiden Seiten tust

Veröffentlicht am 9.7.2026 · 14 Min. Lesezeit · Text- & Sprach-Tools

Daniel Okonkwo

Daniel OkonkwoFront-end-Entwickler und Tech-Redakteur bei OneKitly

Web-Performance · Dateiformate

Anhand von 5 Quellen geprüft

Profil ansehen
Kurz gesagt

Diakritika zu entfernen ist ein Normalisierungsschritt, kein Suchfeature: Es hilft nur, wenn dieselbe Funktion über den Index und über die Suchanfrage läuft. Mach das Experiment - sechs Anfragen gegen sechs Namen. Ohne Normalisierung findet „cafe“ Cafe Central und „café“ findet Café Amélie: zwei Treffer, jeder halb richtig. Falte nur die Anfrage, dann findet „café“ plötzlich Cafe Central und verfehlt Café Amélie, also genau den exakten Treffer, den es vorher hatte, während „müller“ von einem Treffer auf null fällt. Falte nur den Index, liefert „café“ überhaupt nichts mehr. Falte beide Seiten, liefern alle Schreibweisen beide Cafés. Einseitige Normalisierung macht das Matching nicht weicher; sie verschiebt den Fehlschlag nur woandershin. Die Faltung selbst ist NFD gefolgt vom Löschen der kombinierenden Zeichen: é ist der einzelne Codepunkt U+00E9, wird nach der Zerlegung zu U+0065 U+0301, und das Löschen von U+0301 lässt e übrig. Das funktioniert für é, ö, å und ñ und tut überhaupt nichts für ø, ł, ß, œ und ı, die einzelne Codepunkte ohne kombinierendes Zeichen sind und NFD unverändert überstehen. Für sie braucht es eine sprachbewusste Tabelle - deutsches ö zu oe, ß zu ss - oder einen Intl.Collator mit Sensitivität base, der Ørsted und Orsted zusammenbringt, ohne etwas zu löschen.

Diakritika zu falten ist ein Normalisierungsschritt, und Normalisierung wirkt nur, wenn dieselbe Funktion über Index und Suchanfrage läuft. NFC gegen NFD mit offengelegten Codepunkten und die Buchstaben — ø, ł, ß, œ, ı —, die den Faltvorgang unverändert überstehen.

Normalisierung ist eine Funktion, und sie muss auf beiden Seiten laufen

Hier ist das ganze Argument in einem Experiment. Nimm einen Index aus sechs Namen - Café Amélie, Cafe Central, Zoë Müller, Zoe Miller, Łukasz Nowak, Ørsted Energi - und sechs Anfragen: cafe, café, muller, müller, lukasz, orsted. Ganz ohne Normalisierung findet cafe einen Eintrag und café einen anderen, muller findet nichts und müller findet Zoë Müller. Sechs Anfragen, zwei Treffer. Das ist das Problem, das man beheben will.

Wende die Faltung nun nur auf die Anfrage an - die Änderung, die man zuerst macht, weil die Anfrage das ist, was man kontrolliert. Das Ergebnis ist schlechter, nicht besser. Die Anfrage café faltet sich zu cafe und trifft Cafe Central, während Café Amélie - der Eintrag, den sie einen Moment zuvor perfekt getroffen hat - überhaupt nicht mehr trifft, denn der Index enthält weiterhin die akzentuierte Form. Die Anfrage müller verliert ihren einzigen Treffer aus demselben Grund. Aus zwei Treffern werden zwei Treffer, aber es sind nicht dieselben zwei, und einer der Verluste war ein exakter Treffer.

Falte nur den Index, und es passiert das Spiegelbild: cafe findet jetzt beide Cafés, aber café findet nichts, weil die akzentuierte Anfrage den gefalteten Index nicht mehr treffen kann. Falte beide Seiten, und die Tabelle verhält sich endlich: Alle vier Schreibweisen der café-Anfrage liefern beide Einträge, und beide Schreibweisen von Müller liefern den einen Eintrag. Die daraus folgende Regel lautet nicht „Akzente entfernen“. Sie lautet: Welche Funktion du auch anwendest, wende dieselbe an, beim Indizieren und beim Abfragen, aus demselben Codepfad. Eine einseitig angewandte Normalisierung ist keine schwächere Fassung des Richtigen - sie ist eine andere und meist schlechtere Matching-Regel.

NFC, NFD und was die Faltung wirklich ist

Unicode erlaubt zwei Schreibweisen für einen akzentuierten Buchstaben. In zusammengesetzter Form, NFC, ist der Buchstabe é ein einzelner Codepunkt, U+00E9. In zerlegter Form, NFD, sind es zwei: U+0065, das schlichte e, gefolgt von U+0301, dem kombinierenden Akut. Beide werden auf dem Bildschirm identisch dargestellt. Beide sind korrekt. Sie sind nicht dieselbe Zeichenkette: In JavaScript hat das zusammengesetzte é die Länge 1 und das zerlegte é die Länge 2, und die strikte Gleichheit der beiden ist falsch. Ein Wort wie café aus einer NFC-Quelle, das in einem NFD-Dokument gesucht wird, taucht schlicht nicht auf, und die Entwicklerin sieht eine Suche, die an einem Text scheitert, den sie auf der Seite lesen kann.

Die Faltung nutzt genau das aus. Man normalisiert nach NFD, was jeden kanonisch zerlegbaren Buchstaben in einen Grundbuchstaben plus seine Zeichen auseinandernimmt, und löscht dann alles aus der allgemeinen Unicode-Kategorie M - die kombinierenden Zeichen. é wird zu U+0065 U+0301 wird zu e. ñ wird zu U+006E U+0303 wird zu n. å wird zu U+0061 U+030A wird zu a. Das ist der gesamte Mechanismus, und deshalb heißt die Operation korrekt Falten und nicht Entfernen: Dem Alphabet wird nichts genommen, es wird eine Unterscheidung verworfen.

Eine praktische Folge: Bevor du überhaupt faltest, normalisiere alles auf eine Form. Wenn dein Index NFC ist und der eingehende Text NFD, überdeckt das Falten beider auf dieselben Grundbuchstaben den Unterschied zufällig - aber jeder Vergleich vor der Faltung, oder auf einem Feld, das du nicht falten wolltest, bleibt falsch. Normalisiere beim Einlesen auf NFC, als Speicherregel, und behandelst du die Faltung als eigenen, darauf aufbauenden Index.

Die Buchstaben, die gar kein Zeichen tragen

Der Mechanismus hat einen offensichtlichen Fehlermodus: Hat ein Buchstabe keine kanonische Zerlegung, lässt NFD ihn in Ruhe, und es gibt kein kombinierendes Zeichen zu löschen - die Faltung tut also nichts. Führe es aus, und du siehst genau, welche Buchstaben das sind. ß bleibt ß. ø bleibt ø. ł bleibt ł. œ bleibt œ. æ bleibt æ. ı bleibt ı. đ bleibt đ. In jedem dieser Fälle ist das Diakritikum kein Zeichen, das auf einen Grundbuchstaben gesetzt wird; der Querstrich im l, der Schrägstrich im o, die Verbindung zwischen o und e gehören zur Buchstabenform, und Unicode kodiert jede davon als eigenständiges Zeichen.

Deshalb liefert das Sechs-Namen-Experiment für lukasz und orsted auch nach beidseitiger Faltung nichts. Łukasz Nowak faltet sich zu Łukasz Nowak, unverändert; Ørsted Energi faltet sich zu Ørsted Energi, unverändert. Genau die beiden Einträge, die eine Nutzerin am wenigsten wahrscheinlich richtig tippt, sind die beiden, bei denen die Faltung nicht hilft. Ein Wort wie Łódź ist der klarste Fall: Falte es, und du erhältst Łodz, weil ó und ź zerlegbar sind und ł nicht - das Ergebnis ist also weder das Original noch die reine ASCII-Schreibweise, nach der jemand suchen würde.

Die Lösung ist eine kleine explizite Tabelle, angewandt vor dem NFD-Durchgang: ł zu l, ø zu oe oder o, œ zu oe, æ zu ae, đ zu d, ß zu ss. Die Kompatibilitätszerlegung NFKD erledigt die Ligaturen œ und æ, nicht aber die durchgestrichenen Buchstaben - also auch keine allgemeine Antwort. Es führt kein Weg daran vorbei zu wissen, welche Sprache man indiziert, und genau darum geht es in den nächsten drei Abschnitten.

Deutsch: oe statt o - und ss statt s

Deutsche Umlaute lassen sich zerlegen, die naive Faltung liefert also etwas. Sie liefert das Falsche. Größe faltet sich zu Große - der Umlaut verschwindet, das scharfe s bleibt, und das Ergebnis ist ein echtes deutsches Wort mit anderer Bedeutung. Müller faltet sich zu Muller, Öl zu Ol. Die Konvention, die deutsche Leserinnen und Leser tatsächlich erwarten, festgehalten in DIN 5007 Variante 2 und in Telefonverzeichnissen verwendet, ist die zweibuchstabige Auflösung: ä zu ae, ö zu oe, ü zu ue, ß zu ss. Aus Größe wird Groesse, aus Müller Mueller, aus Straße Strasse.

Das scharfe s verdient einen eigenen Absatz, weil es sich anders verhält als alles andere auf dieser Liste. Es hat keine kanonische Zerlegung, NFD lässt es also stehen; NFKC ebenfalls, sodass Straße Straße bleibt. Seine Großschreibung in JavaScript ergibt jedoch die zweizeichige Kette SS, das heißt: Eine naive Pipeline aus Großschreiben und Vergleichen faltet es bereits korrekt, eine naive Akzentfaltung nicht. Die Großform U+1E9E existiert und wird kleingeschrieben wieder zu ß. Wenn dein deutscher Index durch Großschreiben und deine Anfrage durch Akzententfernung entsteht, passt ß in die eine Richtung und nicht in die andere - wieder das einseitige Problem, nur in anderer Verkleidung.

Es gibt einen Weg, das ganz ohne eigene Tabelle richtig zu machen. Ein Intl.Collator für Deutsch mit Sensitivität base behandelt Grosse und Größe bereits als gleich - das ist die deutsche Standardsortierung, in der ß einen sekundären Unterschied zu ss hat. Verlange die Telefonbuchvariante, das Gebietsschema de-u-co-phonebk, und auch Groesse und Größe vergleichen sich als gleich, denn genau diese Sortierung behandelt ö als oe. CLDR pflegt diese Tabellen; Du musst es nicht.

Skandinavisch und Türkisch: Buchstaben, keine Verzierungen

Im Dänischen, Norwegischen und Schwedischen sind æ, ø und å Buchstaben des Alphabets, und sie kommen nach dem z. Ein Collator führt es vor: Sortiere A, Aa, Å, Ø und Z mit einem dänischen Collator, erhältst du A, Z, Ø, Å, Aa; sortiere dieselben fünf mit einem englischen Collator, erhältst du A, Å, Aa, Ø, Z. Im Dänischen ist å nicht still neben das a einsortiert worden - es hat seinen eigenen Platz am Ende, und der Digraph Aa sortiert mit ihm. å zu a zu falten entfernt also keinen Akzent, es verschmilzt zwei verschiedene Buchstaben, und ø zu o zu falten verschmilzt zwei weitere.

Das Türkische liefert den schärfsten Fall, und er betrifft die Groß- und Kleinschreibung, nicht die Akzente. Türkisch unterscheidet ein punktloses ı, U+0131, von einem punktierten i und entsprechend ein punktiertes großes İ, U+0130, vom schlichten I. Die Kleinform von I ist ı und die Kleinform von İ ist i - aber nur im türkischen Gebietsschema. Führe es in JavaScript aus, und die Falle wird sichtbar: Die Zeichenkette İSTANBUL, mit den Standardregeln kleingeschrieben, ist neun Zeichen lang, weil İ auf i gefolgt vom kombinierenden Punkt darüber U+0307 abgebildet wird. Mit toLocaleLowerCase("tr") kleingeschrieben, ist sie acht Zeichen lang - das schlichte istanbul, das eine Nutzerin tippen würde. Ein Suchindex mit der Standard-Kleinschreibung trifft diese Anfrage nie, und die beiden Zeichenketten sehen auf dem Bildschirm identisch aus.

Beide Fälle weisen in dieselbe Richtung. Die Buchstaben, die eine naive Faltung zerbrechen, sind genau jene, die eine Sprache als vollwertige Mitglieder ihres Alphabets führt, und die Umwandlung, die eine Sprache erwartet, ist eine Eigenschaft der Sprache, nicht des Zeichens. Genau dafür gibt es ein Locale-Argument, und es zu übergeben kostet nichts.

Die Wörter, die deine eigene Sprache nicht falten lässt

Faltung ist auf eine Weise verlustbehaftet, die sich in jeder Sprache mit Diakritika leicht zeigen lässt. Im Deutschen faltet sich schön zu schon: Aus einem Adjektiv und einem Adverb wird derselbe Token, und beide gehören zum Kernwortschatz. Größe faltet sich zu Große, was nicht einmal ein Fehlerwort ist, sondern ein anderes echtes deutsches Wort. Genau solche Kollisionen lassen eine Trefferliste für die Leserin kaputt aussehen.

Das ist kein Argument gegen die Faltung. Es ist ein Argument dafür, die ungefaltete Form ebenfalls zu behalten. Das Muster, das funktioniert, sind zwei Felder: Speichere den Originaltext genau so, wie er geschrieben wurde, nach NFC normalisiert und sonst nichts, und bau daneben ein zweites, gefaltetes Feld zum Abgleichen. Werte exakte Treffer auf dem Original höher als gefaltete Treffer, dann bekommt die Leserin, die den Umlaut getippt hat, zuerst den gemeinten Eintrag, während die andere trotzdem etwas findet. Faltung als zusätzlicher Index ist ein Feature; Faltung als zerstörender Ersatz deiner Daten ist ein Fehler, von dem du erst später erfährst.

Was du statt einer selbstgeschriebenen Faltung verwendest

Für Vergleich und Sortierung nimmst du einen Collator statt einer Umwandlung. Ein Intl.Collator mit Sensitivität base meldet cafe und café als gleich, Muller und Müller als gleich, Orsted und Ørsted als gleich sowie Lukasz und Łukasz als gleich - einschließlich der vier Buchstaben, die die NFD-Faltung nicht anfassen kann, denn die Sortiertabellen wissen, was diese Buchstaben sind. Und er tut es, ohne eine verstümmelte Zwischenkette zu erzeugen, die dann irgendwo gespeichert werden muss.

Für URL-Slugs, wo du tatsächlich eine begrenzte ASCII-Ausgabe brauchst, behalte die Faltung - steuere sie aber zuerst über eine explizite Sprachtabelle und erst danach über NFD, und prüfe das Ergebnis. Ein Slug-Generator ist einer der wenigen Orte, an denen eine zerstörende, unumkehrbare Faltung richtig ist, denn ein Slug ist keine Datenaussage: Er ist ein Etikett, das man neu erzeugen kann, und er darf Unterscheidungen verlieren, die der Originaltext trug. Achte nur darauf, die Tabelle vor NFD anzuwenden, sonst rutschen ł und ø geradewegs durch den Slug und wieder heraus, als unbrauchbare Zeichen.

Und wofür du sich auch entscheidest: Schreibe es einmal. Die häufigste Ursache des Fehlers aus dem Titel ist keine schlechte Faltung - es ist eine gute Faltung, zweimal implementiert, einmal im Indexer und einmal im Suchfeld, von zwei Personen, im Abstand von sechs Monaten. Eine Funktion, exportiert aus einem Modul, von beiden Seiten aufgerufen.

Neun Buchstaben durch NFD und eine naive Löschung kombinierender Zeichen, ausgeführt in Node 22. Die fünf, deren NFD-Spalte ein einzelner Codepunkt ist, bleiben unverändert: Es gibt kein Zeichen zu löschen.
BuchstabeNFC-CodepunkteNFD-CodepunkteNaive Faltung ergibtWas die Sprache braucht
éU+00E9U+0065 U+0301ee ist für die Suche richtig, verschmilzt aber Wörter, die die Sprache trennt
öU+00F6U+006F U+0308ooe im Deutschen; o ist im Schwedischen und Finnischen vertretbar
ßU+00DFU+00DF (keine Zerlegung)ß, unverändertss
øU+00F8U+00F8 (keine Zerlegung)ø, unverändertoe; ein eigener Buchstabe, kein verziertes o
åU+00E5U+0061 U+030Aaaa im Dänischen und Norwegischen; ein eigener Buchstabe, einsortiert nach z
ıU+0131U+0131 (keine Zerlegung)ı, unveränderti für einen lateinschriftlichen Index, aber innerhalb des Türkischen nie mit i gleichsetzen
İU+0130U+0049 U+0307Ii, aber nur toLocaleLowerCase("tr") erzeugt es als einen Codepunkt
łU+0142U+0142 (keine Zerlegung)ł, unverändertl
œU+0153U+0153 (keine Zerlegung)œ, unverändertoe; NFKD würde es liefern, NFD nicht
Akzente entfernenEntferne Akzente und diakritische Zeichen: é wird zu e, ü zu u.Tool ausprobieren

Häufige Fragen

Soll ich den gefalteten Text speichern oder zur Laufzeit falten?
Speichere ihn, als zusätzliches Feld, und nie als Ersatz. Zur Laufzeit zu falten heißt, bei jeder Anfrage den gesamten Index zu falten - das ist langsam und macht es viel zu leicht, dass ein Codepfad faltet und ein anderer es vergisst. Ein gespeichertes gefaltetes Feld ist billig, wird einmal von derselben Funktion erzeugt, die das Suchfeld aufruft, und lässt das Original für exakte Treffer und für die Anzeige unangetastet. Was du keinesfalls tun darfst, ist an Ort und Stelle zu falten: Ist die akzentuierte Form aus deiner Datenbank verschwunden, kannst du den Namen nicht mehr korrekt darstellen, und keine spätere Findigkeit holt sie zurück.
Ist NFKD hier besser als NFD, weil es auch Ligaturen zerlegt?
Es löst ein Problem und schafft mehrere. NFKD macht aus œ tatsächlich oe und aus fi ein fi, was in einem Suchindex genau das Gewünschte ist. Doch die Kompatibilitätszerlegung schreibt auch Hochstellungen in gewöhnliche Ziffern um, dickengleiche lateinische Buchstaben in ASCII, das Ohm-Zeichen in ein Omega und diverse Abstandszeichen in einfache Leerzeichen. In einem Anzeigefeld ist das auf eine Weise zerstörend, die du nicht bestellt hast. In einem Vergleichsfeld ist es meist vertretbar und oft hilfreich. Also: NFC zum Speichern, NFKD als eine Eingabe für ein gefaltetes Vergleichsfeld, wenn du das Ligaturverhalten willst, und eine explizite Tabelle für ø, ł und ß, die keine der beiden Formen repariert.
Erledigt eine Datenbank das für mich, wenn ich die richtige Sortierung wähle?
Weitgehend ja, und das ist meist die bessere Antwort als eine Faltung im Anwendungscode. Eine akzentunempfindliche Sortierung setzt dieselbe Idee um wie der Collator, und zwar auf der Ebene, auf der der Vergleich tatsächlich stattfindet - Index und Anfrage werden also konstruktionsbedingt nach einer Regel verglichen. Zwei Vorbehalte. Erstens gilt Sortierung pro Spalte oder pro Vergleich: Eine Abfrage, die eine sortierte Spalte mit einem selbst gefalteten Ausdruck vergleicht, ist wieder beim einseitigen Problem. Zweitens sind akzentunempfindliche Sortierungen sprachabhängig, genau wie oben beschrieben - wähl also die, die zu deinen Inhalten passt, statt einer generischen Voreinstellung.
Warum scheitern zwei identisch aussehende Zeichenketten am Gleichheitstest?
Weil die eine zusammengesetzt und die andere zerlegt ist. Strikte Gleichheit vergleicht Code-Einheiten, und ein zusammengesetztes é ist eine Einheit, ein zerlegtes é sind zwei - der Test scheitert also, obwohl die Darstellung pixelgleich ist. Das ist die häufigste Unicode-Überraschung in einer Suchfunktion und zugleich die am leichtesten zu behebende: Normalisiere beide Operanden vor dem Vergleich auf NFC. Beachte, dass ein gebietsschemabewusster Vergleich die beiden bereits als gleichwertig meldet - ein nützlicher Befund: Sagt localeCompare null und die strikte Gleichheit falsch, hast du einen Normalisierungskonflikt gefunden und keinen Datenfehler.
Ist es je richtig, den Namen einer Person zu falten?
Zum Abgleichen ja. Zur Anzeige nein. Jemand mit dem Namen Zoë Müller hat Anspruch darauf, den Namen korrekt geschrieben auf dem Bildschirm, auf der Rechnung und in der E-Mail zu sehen, und ein System, das nur die gefaltete Form speichert, kann das später mit nichts mehr einlösen. Falte in einen Suchschlüssel neben dem Datensatz, nie darüber, und stell sicher, dass jeder Ausgabepfad das Originalfeld liest. Das ist auch der praktische Grund, warum das Zwei-Felder-Muster gewinnt: Es macht Anzeigepfad und Abgleichpfad strukturell verschieden, sodass niemand versehentlich den Suchschlüssel ausgeben kann.

Artikel, die dich interessieren könnten

Alle Ratgeber
ErklärungDie Sprache eines Textes erkennen — und warum kurze Texte scheiternGemessen, nicht behauptet: 90 kurze echte Wendungen in sechs Sprachen, keine abgelehnt und 68 richtig — 76 %, unter sechzehn Buchstaben nur noch 64 %. Vier der falschen Antworten kamen mit 100 % Sicherheit zurück.ErklärungSatz- und Titelschreibweise: Die Regeln unterscheiden sich je nach SpracheDie englische Titelschreibweise hat je nach Stilhandbuch drei verschiedene Grenzen. Französisch, Spanisch, Portugiesisch und Italienisch haben gar keine. Deutsch schreibt jedes Substantiv groß. Das Werkzeug weiß von all dem nichts — hier steht genau, was es tut.ErklärungEmoji sind schwerer, als sie aussehen: warum es für „einfach die Emoji entfernen“ keine Einzeiler-Antwort gibtEin sichtbares Emoji kann ein Codepunkt sein oder vierzehn UTF-16-Einheiten. Wir haben drei verbreitete reguläre Ausdrücke auf einen echten Satz losgelassen, und jeder ist anders gescheitert — einer löschte die Ziffern. Hier steht, warum, welche Unicode-Eigenschaft welche Frage beantwortet, und welche Graphemcluster-Regel wirklich funktioniert.ErklärungWas ein Palindrom-Prüfer entscheiden muss, bevor er antworten kannGroß- und Kleinschreibung, Leerraum, Satzzeichen und diakritische Zeichen: vier Regeln, sechs echte Sätze, und mit jeder ändert sich die Antwort. Dann die schwerere Hälfte - eine Zeichenkette umzukehren ist selbst nicht definiert, und die Umkehrung nach Codeeinheiten zerstört Emoji und löst Akzente ab, in Node vorgeführt.ErklärungHashtags sind ein Suchindex, kein MegafonEin Hashtag macht einen Beitrag in einer Suche auffindbar, was eine andere Aufgabe ist, als ihn zu verbreiten. Modelliere die Sichtbarkeit, die ein Tag tatsächlich kauft, und die Antwort fällt heraus: Der Wert eines Tags ist seine Zahl an Betrachtern je veröffentlichtem Beitrag, nicht sein Volumen — ein sehr beliebtes Tag bringt also fast nichts und ein spezifisches fünfundzwanzigmal mehr.RatgeberTitle-Tags, Meta-Descriptions und was Suchmaschinen damit machenWas Googles eigene Dokumentation über das Umschreiben von Titeln und über die Meta-Description sagt — statt dessen, was die SEO-Folklore sagt. Dann der messbare Teil: Titel werden nach Pixelbreite gekürzt, weshalb zwei Titel mit exakt sechzig Zeichen 204,55 Pixel auseinanderliegen können und nur einer überlebt.

Ähnliche Tools

Quellen

Hast du einen Fehler in diesem Artikel entdeckt?