Umlaute

April 19th, 2012

Immer wieder gibt es Probleme mit Umlauten in E-Mails. Das liegt fast immer daran, dass die verschiedenen Bestandteile einer E-Mail Umlaute in ganz unterschiedlichen Datenformaten erwarten.


Der Text-Inhalt

Hier gibt es mehrere Möglichkeiten. Am einfachsten wäre es, den UTF-8-Zeichensatz zu verwenden. Leider wird der aber oftmals nicht richtig interpretiert und es entstehen die bekannten kryptischen Zeichen wie z.B. ö für ein ö.

Besser ist es, den String in einen Quoted-Printable umzuwandeln. Wenn der Text im UTF-8-Format vorliegt, muss er zunächst in das ISO-8859-1-Format umgewandelt werde. In PHP geht das einfach mit:

$myIsoString = utf8_decode($myUtf8String);

Es ist auch möglich, bereits in einem HTML-Formular dafür zu sorgen, dass eine Eingabe als ISO-8859-1 zum Server geschickt wird:

<form  ...  accept-charset="ISO-8859-1">

Um Browserprobleme zu vermeiden, ist es dabei sinnvoll, für die ganze HTML-Datei dieselbe Kodierung zu verwenden und das im <head> entsprechen zu setzen:

<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />

Wenn der Text nun in ISO-8859-1 vorliegt, kann er in Quoted-Prinable verwandelt werden. Dazu müssen alle Umlaute und Sonderzeichen >= 7F, alle Ctrl-Zeichen <= 1F außer 09, 0A und 0C  sowie das Gleichheitszeichen 3D als Hex-Werte (in Großschrift) angegeben werden, dem ein = vorangestellt ist. Also z.B. Ä in =C4 . In PHP (ab 5) geht das einfach mit:

$myQpString = quoted_printable_encode($myIsoString);

Das reicht aber noch nicht. Im Header der E-Mail muss angegeben werden, dass man dieses Format verwendet. In PHP z.B. in der mail-Function:

mail($To, $Subject, $Text,
    "MIME-Version: 1.0\r\n".
    "Content-Type: text/plain; charset=ISO-8859-1\r\n".
    "Content-Transfer-Encoding: quoted-printable"
);

Statt text/plain kann hier auch text/html stehen, wenn die E-Mail im HTML-Format verfasst wird. Man sollte aber auch hier nicht die HTML-Entities wie z.B. &Auml; verwenden sondern eben das Quoted-Printable-Format.

Anmerkung: Die meisten E-Mail-Clients verstehen allerdings auch einen Text in ISO-8859-1 richtig und es ist je nach Anforderung eventuell nicht notwendig, Quoted-Printable zu verwenden.

Euro-Zeichen
Probleme macht immer wieder das Euro-Zeichen €. Leider ist es in ISO-8859-1 nicht enthalten. Auf Windows-E-Mail-Clients wird es trotzdem meist richtig angezeigt, wenn man ISO-8859-1 nicht explizit angibt, denn dann wird hier der Windows-Zeichensatz verwendet, der das Euro-Zeichen auf 80 (hex., 128 dez.) enthält. Beim Mac erscheint dort aber die unbeliebte ?-Raute. Als Alternative bietet sich an, den Zeichensatz ISO-8859-15 zu verwenden, der das Euro-Zeichen auf A4 (hex., 164 dez.) enthält. In HTML-Mail kann man auch &euro; verwenden, was aber nicht immer korrekt angezeigt wird. Auf der sicheren Seite ist man, wenn man es durch EUR oder Euro ersetzt.


Subject

Die Betreffzeile erfordert eine Sonderbehandlung. Wenn man hier Umlaute und Sonderzeichen darstellen will, muss das explizit durch Nennung des Zeichensatzes angegeben werden. Dazu bedient man sich des folgenden Konstrukts. Z.B. steht für „Unnötiges Geschwätz“

=?ISO-8859-1?Q?Unn=F6tiges Geschw=E4tz?=

Der Text ist wie schon gehabt in Quoted-Printable verwandelt worden. Darüber hinaus ist die Angabe des Zeichensatzes vorangestellt und das Ganze außerdem in =??= eingefasst. Zusätzlich muss mit ?Q? angegeben werden, dass Quoted-Printable verwendet wird. In PHP sähe das so aus:

$Subject = "=?ISO-8859-1?Q?" . quoted_printable_encode($Subject) . "?=";

E-Mail-Adressen

E-Mail-Adressen bestehen aus dem local-part – das ist der Teil vor dem @ – und dem domain-part dahinter. Hier gelten für Umlaute unterschiedliche Regeln.


E-Mail-Adressen – Local-Part

Im Local-Part sind eine ganze Menge Zeichen erlaubt – mehr als man glaubt –,  aber keine Umlaute. Auch von anderen Zeichen als Ziffern, Buchstaben, Punkt, Minus und Unterstrich sollte man die Finger lassen, da sie oftmals nicht richtig interpretiert werden. Normgemäß wird übrigens zwischen Groß- und Kleinschrift unterschieden. Die meisten E-Mail-Provider machen da aber wegen der Verwechslungsgefahr keinen Unterschied.


E-Mail-Adressen – Domain-Part

Der Domain-Part selbst ist wiederum in zwei verschiedene Teile gegliedert: Dem Domainnamen am Anfang und der TLD (Top Level Domain) – das ist das .de, .com usw. am Ende.

Die TLD ist von Umlauten nicht betroffen, denn es gibt keine TLDs mit Umlauten.

Aber im davor stehenden Domainnamen können sich Umlaute befinden. In diesem Fall muss der Domainname in einen sog. ACE-String umgewandelt werden. Dieser String ist der Domainname in Kleinbuchstaben, dem ein xn-- vorangestellt ist. Umlaute sind aus dem Namen entfernt und mittels Punycode kodiert an dessen Ende gesetzt. Hier ist die Information über das Umlautzeichen und dessen Originalposition kodiert. Je nach TLD sind verschiedene Umlaute und Sonderzeichen erlaubt, was durch das IDNA geregelt ist.

Besonders zu beachten ist das deutsche ß. Dieses Zeichen gilt im Domain-Part nicht als Umlaut oder Sonderzeichen, sondern muss immer in ss umgewandelt werden.

Die Punycode-Kodierung ist in PHP standardmäßig nicht vorhanden. Bei GitHub gibt es aber z.B. die Klasse punycode.class.php, die in PHP eingebunden werden kann und das mit wenigen Befehlen erledigt:

include_once 'punycode.class.php';
$myDomainPart = Punycode::encodeHostname($myUmlautDomain);


Komplettes Beispiel

Dieses Beispiel stellt nur ein Grundgerüst dar. Die syntaktische Richtigkeit der Mail-Adressen wird hier z.B. nicht überprüft. Es muss sichergestellt werden, dass das Mail-Programm nicht zu Spamming oder Schlimmerem missbraucht werden kann.

include_once 'punycode.class.php';
list($ToLocal, $ToDomain) = explode('@', $To, 2);
list($FromLocal, $FromDomain) = explode('@', $From, 2);
mail(
    /* To */
        $ToLocal .'@'. Punycode::encodeHostname($ToDomain),
    /* Subject */
        '=?ISO-8859-1?Q?' . quoted_printable_encode($Subject) . '?=',
    /* Contents */
        quoted_printable_encode($Contents),
    /* Content-Type-Headers */
        'MIME-Version: 1.0' ."\r\n".
        'Content-Type: text/plain; charset=ISO-8859-1' ."\r\n".
        'Content-Transfer-Encoding: quoted-printable' ."\r\n".
    /* From-Header */
        'From: ' . $FromLocal .'@'. Punycode::encodeHostname($FromDomain)
) or exit('Mail-Error!');


Newline bei Header

Wenn wie hier mehrere Header angegeben werden, müssen diese durch ein Newline von einander getrennt werden. Standardmäßig ist das die Zeichenkombination \r\n (CarriageReturn Linefeed). Manche Server ersetzen allerdings \n automatisch durch \r\n. In diesem Fall darf als Newline nur \n benutzt werden, sonst kommt es zu einem Header-Fehler.