
Umlaute, ß und fremde Schriften im QR-Code kodieren
Ein Café druckt Tischaufsteller, die auf eine reine Textkarte auf Türkisch verlinken. Auf dem Rechner der Gestalterin sieht die Vorschau richtig aus. Auf dem Telefon eines Gastes macht derselbe Code daraus Kahvaltı Menüsü. Nicht das Telefon ist schuld und nicht der Drucker, sondern eine Annahme über Zeichensätze, die nie ausgesprochen wurde.
Der Druck ist sauber. Die Fehlerkorrektur hat nie eingegriffen. Die Bytes kamen genau so an, wie sie kodiert wurden, und wurden dann mit dem falschen Wörterbuch gelesen.
Ein QR-Code speichert Bytes, keine Buchstaben
Die Symbologie definiert vier Arten, Daten ins Raster zu packen, und sie sind nach Dichte gewählt, nicht nach Sprache.
- Der numerische Modus packt drei Ziffern in zehn Bit.
- Der alphanumerische Modus packt zwei Zeichen in elf Bit, mit einem eingeschränkten Satz aus Ziffern, Großbuchstaben und einer Handvoll Symbole.
- Der Byte-Modus speichert rohe Achtbitwerte, ein Byte nach dem anderen.
- Der Kanji-Modus speichert japanische Doppelbytezeichen in rund dreizehn Bit je Zeichen, gegenüber den sechzehn, die dasselbe Zeichen im Byte-Modus verbrauchen würde.
Alles außerhalb der numerischen und alphanumerischen Sätze landet im Byte-Modus, und dort landet jede nicht-lateinische Schrift. Der Byte-Modus ist ehrlich in dem, was er ist: ein Behälter für Bytes. Er trägt keine eingebaute Aussage darüber, dass diese Bytes Türkisch, Griechisch oder sonst etwas sind. Die Dichteunterschiede zwischen den Modi sind der Grund, warum die Kapazität je nach Inhaltstyp so stark schwankt, aufgeschlüsselt in wie viele Daten ein QR-Code fasst.
Die Standardannahme, die niemand erklärt hat
Die Spezifikation definiert für den Byte-Modus eine Standardinterpretation rund um einen Einbyte-Latinzeichensatz. Die meisten modernen Encoder schreiben trotzdem UTF-8, weil UTF-8 das ist, womit das übrige Web läuft, und die meisten modernen Decoder erkennen UTF-8, wenn das Bytemuster nach gültigem UTF-8 aussieht. Diese Erkennung klappt meistens, und "meistens" ist das ganze Problem.
Zeichensalat entsteht, wenn UTF-8-Mehrbytesequenzen Byte für Byte durch eine Einbyte-Tabelle gelesen werden. Ein türkisches punktloses i oder ein Umlaut wird zu zwei sichtbaren Zeichen statt zu einem. Die Paare ı und ü in der Speisekarte oben sind genau die Signatur dieser Fehlpaarung. Kyrillisch zerfällt meist in Ketten aus Ð- und Ñ-Zeichen, was derselbe Fehler in anderem Kostüm ist.
Was ECI hinzufügt
Extended Channel Interpretation ist der Mechanismus, den der Standard bereitstellt, um das Raten zu beseitigen. Eine ECI-Kennung wird dem Datenstrom vor der Nutzlast vorangestellt und trägt eine Zuweisungsnummer, die den Zeichensatz benennt, zu dem die folgenden Bytes gehören. UTF-8 hat in diesem Register eine eigene Nummer, die 26. Ein Decoder, der die Kennung versteht, hört auf zu raten und wendet den deklarierten Satz an.
Die Unterstützung schwankt. Manche Lesegeräte respektieren die Kennung, manche ignorieren sie und fallen auf ihre eigene Erkennung zurück, und auch das Verhalten der Encoder unterscheidet sich, denn nicht jeder Generator gibt überhaupt eine Kennung aus. Sie können nicht steuern, welches Lesegerät auf dem Telefon eines Fremden läuft — behandeln Sie ECI also als Verbesserung der Chancen, nicht als Garantie.
URLs umgehen das ganze Problem
Die URL-Syntax beschränkt das, was in einer Adresse vorkommen darf, auf eine Teilmenge von ASCII. Alles darüber hinaus wird prozentkodiert, das heißt, jedes Nicht-ASCII-Byte wird zu einem Prozentzeichen und zwei Hexziffern. Nicht-lateinische Domainnamen durchlaufen eine eigene ASCII-kompatible Kodierung, die sie in eine Form mit dem Präfix xn-- verwandelt.
Das Ergebnis ist eine Nutzlast, die ausschließlich aus Zeichen besteht, die jede Zeichensatzannahme eines Decoders überleben. Das ist das stärkste praktische Argument dafür, URLs statt rohem Text in gedruckte Codes zu setzen, und es gilt genauso für die Weiterleitungskniffe in mehrsprachige QR-Erlebnisse.
Eine Warnung: Kopieren Sie die kodierte Form, nicht die hübsche. Adresszeilen von Browsern zeigen oft die menschenlesbare Fassung eines Pfades an, während sie darunter die kodierte halten, und wer einfügt, was er sieht, statt dessen, was gespeichert ist, holt sich rohe Bytes zurück in die Nutzlast.
Reiner Text und Kontaktkarten sind die Bruchstellen
Zwei Nutzlasttypen machen die meisten echten Beschwerden aus.
Eine Textnutzlast besteht aus Bytes ohne Protokollhülle drumherum, es gibt also nichts, das eine Kodierung deklariert, und nichts, das eine falsche Annahme korrigiert. Was der Scanner entscheidet, ist das, was die Nutzerin liest.
Eine Kontaktkarte fügt eine zweite Risikoschicht hinzu. Der QR-Decoder interpretiert die Bytes, dann zerlegt der Kontaktimport des Telefons die vCard-Felder und übergibt sie dem Adressbuch. Der Umgang des vCard-Formats mit Zeichensätzen hat sich zwischen Versionen verschoben, sodass eine App die Bytes korrekt dekodieren und trotzdem einen verstümmelten Namen in den Kontaktdatensatz schreiben kann. Namen tragen das höchste Risiko, denn dort leben die schriftspezifischen Zeichen: türkisches punktiertes und punktloses i, aserbaidschanisches Schwa, arabischer und hebräischer Text, der zusätzlich von der empfangenden App für die Rechts-nach-links-Darstellung abhängt.
Zwei Telefone, vor der Druckauflage
Kodierungsfehler sind billig zu finden und teuer nachzudrucken — testen Sie also, statt darüber nachzudenken.
- Scannen Sie mit der nativen Kamera auf einem iPhone und auf einem Android-Gerät. Diese beiden Wege decken die meisten echten Scans ab.
- Nehmen Sie mindestens eine Scanner-App eines Drittanbieters dazu, plus jeden In-App-Scanner, den Ihr Publikum wahrscheinlich nutzt.
- Testen Sie die längste Zeichenkette, die Sie kodieren wollen, nicht eine kurze Probe. Abschneidungen und Kodierungsfehler tauchen beide bevorzugt am Ende auf.
- Lesen Sie das gesamte Ergebnis, bis zum letzten Zeichen. Teilweise Verstümmelung in der Mitte einer Zeichenkette überliest sich leicht.
Umlaute, ß und deutsche Adressen
Deutsch hat bei diesem Problem Glück: ä, ö, ü, Ä, Ö, Ü und ß stehen alle in Latin-1, also in genau jenem Zeichensatz, den ein Decoder ohne weitere Angabe annimmt. Deutscher Text kommt deshalb meistens richtig an, während polnischer, türkischer oder tschechischer Text im gleichen Aufbau zerfällt. Der Fehler dreht sich damit nur um: Er entsteht, wenn der Generator UTF-8 schreibt, ohne es per ECI anzukündigen. Dann liest der Decoder jedes Zwei-Byte-Zeichen als zwei Latin-1-Zeichen, und aus Grüße wird Grüße.
Die Stellen, an denen das in der Praxis auffällt, sind immer dieselben: die Straße in einer vCard, ein Verwendungszweck mit Umlaut, eine reine Textnotiz und vor allem der Netzname im WLAN. Ein Netz, das Gästenetz Café heißt, ist zweifach angreifbar, einmal durch die Kodierung im Code und einmal dadurch, dass ein Gast den Namen bei Problemen von Hand nachtippen muss, womöglich auf einer Tastatur ohne Umlaute. Für gedruckte Zugangskarten sind Netzname und Passwort in reinem ASCII der ruhigere Weg; die Gründe stehen ausführlicher im Beitrag zu WLAN-Codes, die nicht verbinden.
Prüfen lässt sich das mit einer einzigen Testzeichenkette. Nehmen Sie ein Wort mit ä, ö, ü und ß, dahinter ein reines ASCII-Wort, kodieren Sie das und lesen Sie es mit zwei Telefonen unterschiedlicher Hersteller. Bleibt das ASCII-Wort stehen, während die Umlaute kippen, liegt es an der Kodierung und nicht am Druck.
Der Rückfallweg, den man zum Standard machen sollte
Verstümmelt ein Decoder Ihren Text, hilft es am Ende nicht, den Decoder besser zu überreden. Nehmen Sie ihm die Aufgabe weg: Legen Sie den Text auf eine Seite und kodieren Sie nur die Adresse dieser Seite. Eine URL besteht aus ASCII, sie übersteht jede Annahme über Zeichensätze, und die Seite selbst nennt ihre Kodierung im Kopf, worauf sich jeder Browser seit zwanzig Jahren verlässt.
Die Seite deklariert ihre Kodierung in den Antwort-Headern und im Dokument selbst, der Browser ist ein weit fähigerer Textrenderer als der Ergebnisbildschirm eines Scanners, und Rechts-nach-links-Layout wird zu CSS statt zu einer Vermutung. Machen Sie den Code dynamisch, und ein Tippfehler wird zu einer Bearbeitung statt zu einem Nachdruck, und dasselbe Symbol kann je nach scannender Person eine andere Sprache bedienen.
Der Preis ist ein Netzwerk-Roundtrip und eine Abhängigkeit von Hosting, das Sie kontrollieren. Für alles, was in Auflage in einer anderen Schrift als schlichtem Latein gedruckt wird, ist das meist der richtige Preis.

