Das 1.000.000$-Bug
Ein E-Commerce-Unternehmen verlor eine Million Dollar weil Kundenaddressen als "Sch????nhauser Allee" statt "Schönhauser Allee" angezeigt wurden. Lieferungen gingen an falsche Adressen. Kunden beschwerten sich. Rückerstattungen stapelten sich.
Die Ursache? Ein Zeichenkodierungs-Mismatch zwischen ihrer Datenbank und dem Frontend.
Was ist Zeichenkodierung?
Zeichenkodierung ist die Zuordnung von Zeichen zu Bytes. Computer verstehen nur Bits (0 und 1). Um Text zu speichern, brauchen wir ein System das Zeichen in Bytes übersetzt.
Die Evolution
| Jahr | Standard | Zeichenanzahl |
|---|---|---|
| 1963 | ASCII | 128 |
| 1987 | ISO-8859-1 | 256 |
| 1991 | Unicode | 1.114.112 |
| 1993 | UTF-8 | Alle Unicode |
ASCII: Der Anfang
ASCII kodiert 128 Zeichen in 7 Bits:
- A-Z (65-90)
- a-z (97-122)
- 0-9 (48-57)
- Sonderzeichen
JavaScript'A'.charCodeAt(0); // 65 String.fromCharCode(65); // 'A'
Das Problem: Nur englische Zeichen. Keine Umlaute, kein é, kein ñ.
Unicode: Die Lösung
Unicode weist jedem Zeichen einen eindeutigen "Code Point" zu:
A = U+0041
ä = U+00E4
é = U+00E9
😀 = U+1F600
Aber: Unicode definiert nur die Zuordnung, nicht wie Bytes gespeichert werden.
Dafür brauchen wir...
UTF-8: Der Goldstandard
UTF-8 kodiert Unicode-Zeichen in 1-4 Bytes:
| Bereich | Bytes | Beispiel |
|---|---|---|
| U+0000 - U+007F | 1 | A, z, 9 |
| U+0080 - U+07FF | 2 | ä, é, ñ |
| U+0800 - U+FFFF | 3 | 中, 日, € |
| U+10000 - U+10FFFF | 4 | 😀, 🎉 |
JavaScriptconst text = 'Ägäis'; console.log(Buffer.from(text, 'utf8').length); // 7 Bytes console.log(text.length); // 5 Zeichen
Mojibake: Wenn alles schief geht
Mojibake (文字化け) bedeutet "Zeichenverstümmelung" auf Japanisch.
Es passiert wenn Text mit einer Kodierung geschrieben und mit einer anderen gelesen wird.
Häufige Mojibake-Muster
| Originale Zeichen | Bei Fehlkodierung |
|---|---|
| ü | ü |
| ö | ö |
| ä | ä |
| € | € |
| — | â€" |
Ursache diagnostizieren
JavaScript1// Text wurde als UTF-8 gespeichert, aber als Latin-1 gelesen 2const broken = 'ü'; // sollte "ü" sein 3 4// Test: Als Latin-1 encodieren, als UTF-8 dekodieren 5const buffer = Buffer.from(broken, 'latin1'); 6const fixed = buffer.toString('utf8'); 7console.log(fixed); // 'ü' ✅
Debugging-Checkliste
1. Datenbank prüfen
SQL1-- MySQL: Datenbank-Kodierung prüfen 2SHOW CREATE DATABASE mydb; 3 4-- Tabellen-Kodierung prüfen 5SHOW CREATE TABLE users; 6 7-- Auf UTF-8 setzen 8ALTER DATABASE mydb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; 9ALTER TABLE users CONVERT TO CHARACTER SET utf8mb4;
2. Connection-Kodierung setzen
JavaScript1// MySQL 2const connection = mysql.createConnection({ 3 charset: 'utf8mb4' // ✅ Nicht nur 'utf8' 4}); 5 6// PostgreSQL 7const pool = new Pool({ 8 client_encoding: 'UTF8' 9});
3. HTTP-Headers prüfen
Content-Type: text/html; charset=utf-8
4. HTML-Meta-Tag
HTML<meta charset="UTF-8">
Sprach-spezifische Lösungen
JavaScript
JavaScript1// Datei lesen 2const fs = require('fs'); 3const text = fs.readFileSync('file.txt', 'utf8'); // ✅ 4 5// Strings korrekt vergleichen 6'ä'.normalize() === 'ä'.normalize(); // true
Python
Python1# Python 3 ist standardmäßig UTF-8 ✅ 2text = "Größe" 3 4# Datei mit Kodierung öffnen 5with open('file.txt', encoding='utf-8') as f: 6 content = f.read()
Java
JAVA1// Immer Charset angeben! 2BufferedReader reader = new BufferedReader( 3 new InputStreamReader(new FileInputStream("file.txt"), 4 StandardCharsets.UTF_8) 5);
Best Practices
Regel 1: UTF-8 überall
| Komponente | Einstellung |
|---|---|
| Datenbank | utf8mb4 |
| Connection | charset=utf8mb4 |
| HTTP Headers | charset=utf-8 |
| HTML | <meta charset="UTF-8"> |
| Dateien | Mit UTF-8 speichern |
Regel 2: Kodierung explizit angeben
JavaScript1// ❌ Schlecht: Implizite Kodierung 2fs.readFileSync('file.txt'); 3 4// ✅ Gut: Explizite Kodierung 5fs.readFileSync('file.txt', 'utf8');
Regel 3: String-Länge vs Byte-Länge
JavaScript1const emoji = '😀'; 2console.log(emoji.length); // 2 (JavaScript Surrogate Pairs) 3console.log(Buffer.from(emoji).length); // 4 Bytes 4 5// Für echte Zeichenzahl: 6console.log([...emoji].length); // 1
Zusammenfassung
| Problem | Lösung |
|---|---|
| Mojibake (ü → ü) | UTF-8 konsistent nutzen |
| Datenbank-Fehler | utf8mb4, nicht utf8 |
| Datei-Lese-Fehler | Kodierung explizit angeben |
| Emoji-Probleme | utf8mb4, nicht utf8 |
Der wichtigste Rat: UTF-8 überall. Keine Ausnahmen. Immer explizit.
IntlPull handhabt Zeichenkodierung automatisch. Ihre Übersetzungen sind immer UTF-8-sicher, egal in welcher Sprache.
