szmmctag

  • Wahlwerbung

    Aus gegebenem Anlass, heute etwas Wahlwerbung:

    Wähle Piratenpartei!

  • Zensurmittelchen (rezeptfrei)

    Aus aktuellem Anlass einmal ein völlig anderes Thema. Zensur wird wieder hoffähig. Nicht in Deutschland, natürlich, wir haben ja eine Verfassung, die in dieser Angelegenheit eine klare Sprache spricht (Zitat: „Eine Zensur findet nicht statt.“), aber in Staaten wie China, Iran oder Dänemark…

    Immerhin: Das Internet wurde von Anfang an so konzipiert, dass es praktisch unmöglich ist, Zugriffe zu verhindern. Dies war ursprünglich dazu gedacht, das Netzwerk auch im Falle eines Atomkrieges funktionsfähig zu halten, kommt uns heute aber zugute, wenn es darum geht, Bürgerrechte zu bewahren.

    Ich möchte deshalb hier kurz drei einfache Methoden vorstellen, wie staatliche Zensur umgangen werden kann. Es handelt sich dabei um keine „Hacker-Tricks“ oder ähnliches, sondern um simple Konfigurationsoptionen bzw. im letzten Fall um ein anerkanntes und vielfach eingesetztes System zur Umgehung von Zensur.

    1. Proxy Server

    Dies ist eigentlich bereits ein „mittelschweres Geschütz“ im Kampf gegen die Zensur. Dabei ist diese Methode auch für Anfänger recht einfach zu benutzen, deswegen soll sie hier an erster Stelle erscheinen.

    Zuerst ein paar Infos: Ein Proxy-Server ist ein Computer, der Anfragen nach (anderen) Web-Sites annimmt, und weiterleitet. Oft werden die Daten auch in einem Zwischenspeicher („Cache“) behalten, damit die nächste Anfrage auf dieselben Dateien schneller stattfinden kann.

    Der Nachteil ist, dass die Daten nun natürlich einen längeren Weg durch das Internet nehmen müssen. Wenn sie also noch nicht auf dem Proxy-Server zwischengespeichert sind, ist der Zugriff eher langsamer als wenn man direkt auf die Site zugreift. Wenn man jedoch einen günstig gelegenen Proxy auswählt, und auf Seiten surft, die schon ein anderer Nutzer besucht hat, kann die Seite aber auch schon mal schneller laden als vom eigentlichen Web-Server.

    Wichtiger ist jedoch: Als Benutzer verbindet man sich nun nicht mehr mit der eigentlichen Web-Server, sonder „nur“ noch mit dem (unverdächtigen) Proxy-Server. Dieser tut dann den Rest. Und das alles kann völlig transparent im Hintergrund geschehen, d.h. als Benutzer bemerkt man höchstens einen Unterschied in der Wartezeit, ansonsten funktioniert alles wie gehabt.

    Allerdings sind die meisten Proxy-Server nur einem beschränkten Personenkreis zugänglich. Es gibt aber auch einige, die jeder benutzen darf. Hierfür werden zahlreiche Listen geführt, aus denen man sich einen passenden Server aussuchen kann. Am einfachsten findet man diese, indem man etwa in Google nach „open proxy list“ sucht.

    Diese Server sind einer gewissen Fluktuation unterworfen, und nicht alle Listen werden täglich auf den neuesten Stand gebracht. Mitunter muss man daher ein paar Server ausprobieren, bis man einen brauchbaren findet.

    Als nächstes muss man den Browser so konfigurieren, dass er den Proxy auch benutzt. Dies ist je nach verwendetem Browser unterschiedlich, deswegen der Reihe nach:

    a) Internet Explorer

    Da dies immer noch der verbreitetste Browser ist, fange ich einmal mit diesem an.

    Zuerst muss der Dialog „Internetoptionen“ geöffnet werden. Dies geht am einfachsten über das „Extras“-Menü (der letzte Eintrag), dann den Reiter „Verbindungen“ auswählen und auf den Knopf „LAN-Einstellungen“ klicken. Der folgende Dialog öffnet sich:

    Internet Explorer 7 Proxyeinstellungen

    Hier einfach in der Zeile für „HTTP“ einen der Proxy Server angeben, und bitte auch nicht die Port-Nummer vergessen. In den meisten Fällen reicht es, einfach den Schalter „Für alle Protokolle…verwenden“ zu aktivieren. Ansonsten kann man die anderen Felder auch erst einmal leer lassen.

    Ein Hinweis noch: alle Daten laufen nun über den Proxy-Server. Im Prinzip könnte ein übel meinender Server-Administrator dort nun natürlich auch Passwörter und ähnliches mitlesen, und damit Unfug treiben. Um das zu verhindern, sollte man wichtige Adressen, wie die Bank (für’s Online-Banking) oder die Firma (falls man sich dort gelegentlich einloggt) oder ähnliches ausdrücklich vom Proxy-Server ausschließen.
    Das war’s schon.

    b) Firefox

    Die besten Konfigurationsmöglichkeiten bietet wie üblich Firefox. Auch hier findet man einen komfortablen Dialog, in dem man den Proxy-Server eintragen kann. In diesem Fall findet man ihn indem man unter Extras > Einstellungen > Erweitert > Netzwerk auf den Knopf Einstellungen klickt. So sieht er aus:

    Firefox Proxy Dialog

    In den meisten Fällen ist es das einfachste, den Schalter „Für alle Protokolle … verwenden“ zu aktivieren. Andernfalls können die anderen Felder („FTP-Proxy“, etc.) meist auch einfach frei gelassen werden.

    Auch hier empfehle ich, wichtige Domains, wie z.B. die für’s Online-Banking und für den Zugriff aufs Firmen-Netzwerk zu den Ausnahmen hinzuzufügen.

    Aber Firefox wäre nicht Firefox, wenn es nicht noch Dutzende Erweiterungen gäbe, die den Umgang mit Proxies wirklich zu einem Kinderspiel machen. Am besten gefiel mir FoxyProxy, aber das sollte niemanden davon abhalten nicht auch mal einen der anderen auszuprobieren.

    c) Proxy Auto-Config

    Für alle modernen Browser gibt es darüber hinaus die Möglichkeit, die Proxy-Einstellungen mittels eines Scriptes konfigurieren zu lassen. Dieses kann auch automatisch von einem Server geladen werden.

    So ein Script muss von jemandem, der sich auskennt, an die jeweiligen Gegebenheiten angepasst werden, deswegen ist dies eher keine Lösung für Otto-Normalsurfer. Der Vollständigkeit halber hier ein Beispiel für ein solches Script, das für alle Web-Adressen, die auf den Server 1.2.3.4 geleitet werden, den Proxy "proxy.server.org" auf Port 8080 benutzt, während zu allen anderen Sites direkt verbunden wird:

    function FindProxyForURL(url, host)
    {
       if (isInNet(host, "1.2.3.4", "255.255.255.255")) {
           return"PROXY proxy.server.org:8080";
       }
       return "DIRECT";
    }

    Für mehr Informationen zu diesen Dateien, sei hier nur auf den dazu gehörigen Wikipedia-Artikel verwiesen.

    2. Alternativer DNS-Server

    Für einfache Zensursysteme, die auf dem DNS aufsetzen kann man auch eine Ebene niedriger ansetzen: Die DNS-Server sind normalerweise dafür zuständig, aus einem Internet-Namen (wie z.B. „www.blog.de“) eine IP-Adresse (wie „62.146.3.166“) zu machen. Normalerweise laufen bei jedem Internet-Zugangsprovider mehrere solcher Server, deren Adressen dem Benutzer (bzw. seinem Computer) beim Einloggen automatisch mitgeteilt werden.

    Bei einer DNS-basierten Zensur werden die Namen bestimmter Internet-Server von diesem Dienst nicht mehr korrekt aufgelöst, sondern es wird stattdessen die Adresse einer Sperrseite oder etwas ähnlichem geliefert.

    Niemand zwingt einen jedoch, die DNS-Server des Providers zu benutzen. Man kann auch ganz einfach andere Einstellungen benutzen. Passende DNS-Server-Adressen findet man z.B. unter DNSServerlist.org oder wie üblich mittels Google.

    Der Vorteil dieser Lösung ist, dass der eigentliche Internet-Verkehr nicht verlangsamt wird. Allerdings kann die Zeit für „DNS-Lookups“, also für das Nachschlagen der Adresse länger werden, da die Server des Providers gewöhnlich „näher“ am eigenen Computer liegen als andere. Dies sollte in den meisten Fällen jedoch keine spürbare Verzögerung bewirken.

    Im Gegensatz zur Proxy-Lösung hat dies auch den Nachteil, dass es keine so komfortablen Werkzeuge zum Umschalten gibt. Und gerade in der Standardeinstellung von Windows ist der Einstellungsdialog etwas schwierig zu finden:

    Klicken Sie zunächst auf den „Start“-Knopf, dann auf „Systemsteuerung“, dann auf „Netzwerk- und Internetverbindungen“ und schließlich auf „Netzwerkverbindungen“.

    Nun sollte eine Liste der verfügbaren Netzwerkverbindungen dieses Computers. Das kann bei jedem anders aussehen und es ist manchmal etwas knifflig, die richtige zu finden. In den meisten Fällen ist es entweder eine „LAN-Verbindung“ oder eine „Drahtlose Netzwerkverbindung“ oder etwas ähnliches. Diese müssen Sie nun rechtsklicken (!), und dann den Eintrag „Eigenschaften“ (gewöhnlich ganz unten der letzte) auswählen.

    Es erscheint ein Dialog wie folgender:

    Eigenschaften von LAN-Verbindung

    Uns interessiert hier nur das „Internetprotokoll (TCP/IP)“. Aus irgendeinem Grund ist das stets der letzte Eintrag, daher muss man evtl. zuerst etwas nach unten scrollen, um ihn zu finden. Einfach den Eintrag auswählen und dann auf den Knopf „Eigenschaften“ klicken; Es erscheint folgender Dialog:

    Eigenschaften von Internetprotokoll

    Hier einfach einen oder zwei passende DNS Server eintragen. „OK“ klicken. Fertig.
    Auch hier gilt: Wenn der DNS-Server Probleme macht, einfach einen anderen ausprobieren. Es gibt ja genug davon.

    Tatsächlich braucht man den/die alternativen Server nicht die ganze Zeit eingetragen zu halten. Zum einen dürfte ja kaum das ganze Internet blockiert sein, zum anderen braucht man die DNS Server ja nur einmal, um die „richtige“ IP-Adresse zu erhalten. Wenn die Verbindung funktioniert, kann man relativ einfach sehen, wie sie lautet und dann anstelle des Namens verwenden. Oder man trägt die Adresse in der „hosts“-Datei ein, einer Textdatei, in der Zuordnungen von Namen zu IP-Adressen unabhängig vom DNS-Server hinterlegt sind. Beides sind jedoch fortgeschrittene Themen, die hier nicht weiter vertieft werden sollen. Wer sich dafür interessiert, sollte einfach mal nach „nslookup.exe“ oder „hosts“ googeln.

    3. Tor

    (z.Zt. in Arbeit)

  • JavaScript Prototypen

    Also, wie gestern schon geschrieben, habe ich mich in letzter Zeit viel mit JavaScript beschäftigt. Reichlich spät, sollte man sagen; Immerhin gibt es diese Sprache schon seit mindestens 1995 - und damals war ich schon professioneller Web-Programmierer, hätte also allen Grund gehabt, mich intensiver damit zu beschäftigen.

    Tatsächlich hatte ich mich schon damals intensiv damit beschäftigt. Allerdings nicht sehr lange; tatsächlich war JavaScript immer eine eher frustriernde Angelegenheit: es gab keinen Debugger und bestenfalls unbrauchbare Fehlermeldungen (meist gar keine - das Script machte dann einfach gar nichts!).

    Das hat sich zum Glück geändert. Zumindest unter Firefox gibt es die wirklich sehr, sehr hilfreiche Firebug-Erweiterung, ohne die ich wohl schon mehrere Male wieder aufgegeben hätte.

    Leider ist es unter Internet Explorer nicht so einfach: Im Prinzip sollte man die Express Edition von Visual Web Developer benutzen können, um JScripts (so heisst das da!) zu debuggen - tatsächlich ist mir das bisher noch nicht gelungen. Deswegen ist es ziemlich nervig, die Scripts unter IE zu testen (muss leider sein!)

    Für die anderen Browser auf meiner Kompatibilitätsliste (Opera, Safari, Konqueror) gilt übrigens eigentlich das selbe - debuggen ist nicht. Allerdings habe ich bisher die nette Erfahrung gemacht, dass die Scripts, so sie erst einmal unter Firefox und Internet Explorer laufen, auf diesen Browsern dann auch keine Probleme mehr bereiten.

    Leider gilt das nicht für die beiden großen Browser. Wenn etwas in dem einen läuft, gibt es keine Garantie dafür, dass dies auch auf dem anderen so ist. Tatsächlich ist es sogar eher unwahrscheinlich, denn gerade die etwas fortgeschrittenen Themen funktionieren grundsätzlich verschieden auf diesen beiden Plattformen.

    Auch dies ein Grund, warum ich mich immer wieder gerne um JavaScript-Programmierung gedrückt hatte. Am Ende hatte ich eine Menge Funktionen, die eigentlich besser auf dem Client ausgeführt würden (anzeigen/verstecken von Inhalten) auf dem Server ausgeführt - mit all den damit verbundenen Nachteilen (lange Wartezeiten, hohe Serverlast, etc.), aber eben mit dem Vorteil, dass ich mich nicht um die Unterschiede in der Implementierung des JavaScript-Interpreters kümmern musste. Eben, weil ich kein JavaScript benutzte.

    Zur Rettung kam Prototype; Eine Script-Bibliothek, die geradezu wie für mich gemacht zu sein schien.

    Nicht nur bietet diese Bibliothek eine Menge sehr, sehr nützliche Funktionen, sondern sie 'bügelt' auch die Unterschiede zwischen den Plattformen 'aus' (oder wenigstens einige davon). Damit ist dann noch nicht alles verschwunden, wo sich die Browser unterscheiden, aber die Unterschiede sind erträglicher geworden. Immerhin erträglich genug, um sich noch einmal mit JavaScript zu befassen.

    Wer also Client-Side-Scripting schon vor Jahren frustriert aufgegeben hat, sollte sich ruhig noch einmal Prototype ansehen. Oder eine der vielen anderen JS-Bibliotheken (es scheinen zur Zeit ja fast täglich neue aufzutauchen). Es lohnt sich.

  • Hexgrid Reloaded

    Nun kam ich also eine ganze Weile nicht zum schreiben neuer Einträge. Der Grund war, dass ich in letzter Zeit mit anderen Projekten beschäftigt war - tja, und da ging es hier auch auf meiner Seite nicht viel weiter.

    Zuletzt habe ich mir ein paar Gedanken zu einer Art Online-Game gemacht - das wahrscheinlich niemals zu Ende gebracht werden wird, aber immerhin habe ich ziemlich viel gelernt dabei, ein wenig damit herumzuspielen.

    Nun, die erste Frage war die nach der Plattform, die ich verwenden möchte. Und da ich mich eh' ein wenig mehr mit DHTML auseinander setzten wollte, habe ich natürlich für ein AJAX-basiertes Web-Interface entschieden. Das bedeutet ziemlich viel JavaScript und eine Menge CSS-Magie...

    Die zweite Frage ist die, wie so ein Spielfeld aussehen sollte. Ein einfaches Schachbrett-Schema war mir zu unflexibel (und vor allem langweilig), es musste schon ein Hexgrid sein.

    Nun gibt es im Internet zwar ein paar recht gute Einführungen in Hexgrids (z.B. hier oder hier), aber keine davon beschäftigt sich damit, wie man ein perspektivisches Hex-Grid benutzt. Nach ein wenig Herumprobieren (mit Bleistift im Notizuch) kam ich auf folgendes Schema:

    hexgrid

    Das sieht erst mal ziemlich kompliziert aus, ist aber tatsächlich eher einfacher zu handhaben als ein "normales" Hexgrid. Tatsächlich habe ich z.B. so nur eine Teilfläche, in der ich eine Diagonale berücksichtigen muss, oder anders gesagt: 80% der Fläche kann man als Quadrate (oder Rechtecke, je nach Zellengröße) betrachten. Im normalen Hexgrid gibt es mindestens 2 Diagonalen.

    Tatsächlich benutze ich so eine Art "Zonen-Modell" auch, um alle Berechnungen anzustellen, d.h. Bildschirmkoordinaten (z.B. bei Mausklicks) werden zuerst in solche Zonen-Koordinaten umgerechnet, und erst in einem zweiten Schritt zu den tatsächlichen Grid-Koordinaten umgewandelt. Und nur falls die Koordinaten in der "ersten" Zone (hier: links oben) landen, muss ich die (relativ aufwändige) Berechnung  durchführen, um zu sehen, ob sie in der oberen oder der unterem Grid-Zelle liegt.

    Übrigens ist diese Berechnung in diesem Fall auch ganz besonders einfach. Keine Vektorrechnung und all solcher Quatsch. Wenn die Summe der x- und der y-Komponente (normalisiert auf die Zelle) größer ist als die Zellengröße (ggf. das Mittel aus Höhe und Breite), ist es die untere Zelle, andernfalls die obere. Fertig.

     

    Koordinaten

    Ein größeres Problem stellen die Koordinaten dar: Offensichtlich sind die "normalen" x- und y-Koordinaten bereits bei einem normalen Hex-Grid kaum sinnvoll zu gebrauchen. Hier gibt es einen recht guten Artikel zu dem Thema (falls sich jemand dafür interessiert), aber wie gesagt, mir nutzte es gar nix.

    Zuerst dachte ich darüber nach, ein 3-dimensionales Koordinatensystem zu verwenden, mit drei Achsen in jeweils einer Richtung, in der man sich von Zelle zu Zelle bewegen kann. Das ist zuerst etwas ungewohnt, weil sich nun die z-Achse auch sozusagen auf der "Papierebene" befindet, aber im Prinzip ist das machbar.

    Im Endeffekt bedeutet das aber man ständig zwischen verschiedenen Bedeutungen der z-Achse geistig "umschalten" muss. Selbst wenn ich das hinbekomme, ist das nichts, was ich einem Designer oder gar einem Praktikanten - wer auch immer später die Levels designed - zumuten möchte.

    Der zweite Gedanke war, man könnte die y-Achse ja auch einfach um 60° gedreht annehmen, und schon bekommt man eine relativ einfache Zuordnung der Zellen. Zumindest auf den ersten Blick; auf den zweiten ist nicht viel gewonnen, denn nun hat je nachdem wie man zählt entweder viele negative Koordinaten, oder viele "kleine" Zahlen, die aber nicht benutzt werden dürfen. Und einfach nachvollziehbar ist das ganze auch nicht...

    Ich entschied mich also für ein anderes System: Zunächst einmal beginne ich mit dem Zählen von unten. Der Grund ist, dass der Fokus der dargestellten Szenen wohl in den meisten Fällen am unteren Rand sein dürfte. Zweitens bleibe ich zunächst einmal bei den anfänglichen "Zonen-Koordinaten" (also den grauen Quadraten in dem Bild rechts und betrachte die jeweils obere Zeile der Spielfeldzellen. Das ist soweit ja recht einfach zu finden.

    Schwieriger ist es mit der x-Koordinate. Ich wollte die Zellen sequenziell durchnummeriert haben (also keine Lücken), und möglichst keine negativen Zahlen, also werden die Zellen, die in dieser Zeile "anfangen" einfach durchgezählt. Relevant sind dabei die Quadrate, die ich in dem Bild oben mit einem roten Kreuz markiert habe.

    Das ist auch einfacher als es klingt. Wenn man weiß, dass sich das Raster alle 5x5 Quadrate wiederholt, kann man mit ausgiebiger Nutzung des Modulo-Operators eine schöne, kurze (sprich: schnelle) und gut handhabbare Formel zur Umrechnung finden.

    So viel für heute, wenn ich Zeit finde, etwas mehr zur JavaScript-Programmierung...

  • Das furchtbare @ Zeichen!

    Ein paar Gedanken zum Thema e-Mail-Adressen:

    Mittlerweile sollte es sich herumgesprochen haben, dass Spammer systematisch nach neuen e-Mail-Addressen suchen, indem sie das WWW nach allem durchkämmen, was irgendwie wie eine solche aussieht.

    Nun, sie haben es auch relativ leicht: da das @-Zeichen sonst so gut wie für garnix verwendet wird, brauchen sie nur nach diesem zu suchen, und dann alles was vorne oder hinten dran hängt herauszuschneiden.

    Aus diesem Grund benutze ich, wenn ich z.B. eine Kleinanzeige aufgebe, immer eine etwas erweiterte Adresse: sascha_leib@web.NOSPAM.de, wobei ich im Text dann erwähne, dass man bitte den "NOSPAM" Teil herausnehmen soll.

    Zumindest die völlig automatisierten Adressensammler trickst man damit aus. Etwas anderes ist es bei denen, die sich die Adressen noch einmal genau anschauen.

    Immerhin: den e-Mail-Account, den ich normalerweise für so etwas benutze, habe ich so (fast) Spam-frei gehalten. Nur der gelegentliche Liebesbrief aus Nigerie kommt dort herein, aber damit kann ich leben (tatsächlich finde ich diese sogar regelmäßig amüsant! :-)

    Schöner wäre es jedoch, wenn die Betreiber der Site selber an so etwas denken, und erst keine (durch die Maschine) erkennbaren e-Mail-Adressen anzeigen.

    atEine brauchbare Lösung wird z.B. auf php.net benutzt: das @-Zeichen wird einfach zu "(at)" umgewandelt - die Techies dort wissen schon was gemeint ist.

    Ich fand es dagegen besser, das Zeichen durch eine Grafik zu ersetzen. Das geht ganz einfach so:

    sascha_leib<img src="/images/at.gif" alt="(at)" />web.de

    Dabei bleibt nur noch ein Problem: Wenn der Benutzer eine größere Schriftart wählt als vorgesehen, passt die Größe des @ nicht mehr zum restlichen Text. Man muss also ein wenig tricksen:

    Zunächst mal gibt man dem Bild einen Klassennamen. Etwa so:

    …<img src="/images/at.png" class="at" alt="(at)" />…

    Dann legt man in der CSS-Datei eine entsprechende Definition an:

    img.at {
      width: 1em;
      height: 1em;
    }

    Da die Maßeinheit "em" immer relativ zur Schriftgröße gilt, skaliert das Bild nun mit. Wenn man nun noch eine Grafik nimmt, die deutlich größer ist als die Schrift (siehe das Beispiel hier), hat man zwar ein wenig mit Skalierungsartifakten zu kämpfen, dafür ist das Zeichen von der Größe her immer einigermaßen brauchbar.

    Sonderfall

    So weit so gut. Im Normalfall jedenfalls. Nun habe ich aber diesen Stilumschalter implementiert. Und da die Grafik nicht auf ein Ändern der Textfarbe reagiert, muss ich mir hier etwas anderes einfallen lassen:

    …<img src="/images/spacer.gif" class="at" alt="(at)" />…

    "spacer.gif" ist hier wieder die altbekannte leere (=transparente) GIF-Datei, von der wir eigentlich dachten, sie sei seitdem es CSS gibt, in der untersten Schublade des Webdesigners verschwunden…

    In der Datei "nightstyle.css" findet sich nun der folgende Eintrag:

    img.at {
      background: url('/images/night/at.gif') no-repeat;
    }

    und entsprechend in "daystyle.css":

    img.at {
      background: url('/images/day/at.gif') no-repeat;
    }

    Leider kann ich nun kein größeres Bild mehr verwenden, weil die Optionen für das Hintergrundbild eben etwas eingeschränkt sind. Aber immerhin stimmen die Farben. Nun, einen Tod muss man halt sterben.

  • b 1337: L10n & I18n!

    Wenn Übersetzer versuchen, zu zeigen, dass sie sich mit „Computern und so“ auskennen, werfen sie mit Begriffen wie L10n und I18n um sich. Diese Begriffe stehen für „Localization“ und „Internationalization“ und beziehen sich darauf, dass sich jeweils 10 bzw. 18 Buchstaben zwischen den ersten und dem letzten Buchstaben in diesen Wörtern befinden.

    Vermutlich wäre es einfacher, die Wörter auszuschreiben, als jedesmal allen Uneingeweihten zu erklären (sprich: eigentlich allen – selbst die meisten Übersetzer verstehen da nur Bahnhof) aber dann würde man ja seinen Status als Elite (Neusprech: „1337“) der Übersetzergilde aufgeben.

    Nun, jedenfalls geht es diesmal darum, wie ich versucht habe, meine Web-Anwendung von regionalen Besonderheiten (wie z.B. der Sprache) unabhängig zu gestalten. Eben das ist es, was man unter dem Begriff „Internationalisierung“ versteht.

    Lokalisierte Pfade

    Einen wichtigen Aspekt dieser Internationalisierung haben aufmerksame Leser schon in einem früheren Blog-Eintrag gefunden: Die Pfade, unter denen Resourcen aufgerufen werden können, sind in einer Datenbanktabelle abgelegt. Darin findet man auch die Zuordnung zu den passenden Sprachen und zu den Artikeln (aus einer anderen Tabelle).

    Artikel

    Freilich müssen die Artikel, die nun verlinkt sind, auch zu der passenden Sprache passen. Nun, im Moment gibt es kein Backend für die Site, sodass ich gezwungen bin, „von Hand“ dafür zu sorgen, dass dies zusammenpasst. Mit einem orgendlichen Backend könnte man natürlich dafür sorgen, dass diese Zuordnung automatisch abläuft.

    Statische Elemente

    Nun wird es spannend: Neben Pfaden und dem eigentlichen Artikeltext gibt es auf der Website ja noch erstaunlich viele andere Textelemente, die übersetzt werden müssen: Vor allem sind das die statischen Elemente, welche auf jeder Seite immer wieder auftauchen.Day Design

    So gibt es z.B. oben auf jeder Seite einen Link die Hilfe aufzurufen. Oder den bereits erwähnten Tag/Nacht-Umschalter. Zwar sind beide Links als Grafiken ausgeführt, aber es bleiben trotzdem noch die ALT Texte sowie das TITLE-Attribut (welches auch als MouseOver-Label fungiert). Und diese müssen auf jeden Fall übersetzt werden.

    Um die Zahl der zu übersetzenden Texte gering zu halten, habe ich diesen Grafiken übrigens einfach jeweils ein passendes Symbol aus dem reichhaltigen Unicode-Symbolschatz als ALT-Text zugewiesen, nämlich ‚☼‘, ‚☾‘ und ‚?‘. Das dürfte in jeder Sprache verständlich sein. Der Nachteil ist, dass ein Screenreader wahrscheinlich mit Sonne und Mond nicht viel anfangen kann – aber dafür gibt es ja noch den TITLE-Text, und für Sehbehinderte dürfte die Stilumschaltung ohnehin eher von geringerem Interesse sein.

    Zum Glück bietet das Zend Framework bereits sehr ausgefeilte Funktionen an, mit denen man so etwas realisieren kann. Mehr noch: die Lokalisierungs-Komponenten sind sehr ausgefeilt, und gehören meiner Meinung nach zu den absoluten Highlights dieses Frameworks.

    Im Prinzip muss man nichts weiter tun, als alle Strings, die via PHP ausgegeben werden, wie folgt zu umschließen:

    echo $translator->_('Hello World!');

    Die dazugehörigen Übersetzungen können in allen möglichen Formaten abgelegt werden, darunter CSV, QT, MO und PHP-Arrays. Als Ex-Lokalisierungsmanager habe ich erst mal TMX gewählt. Das hat den Nachteil relativ langsam (sprich: Prozessor-Intenviv) zu sein, dafür können solche Dateien von praktisch allen Übersetzern mit ihren jeweiligen Lieblings-Werkzeugen geöffnet (und samt Übersetzungen wieder geschrieben) werden.

    Und was fast noch wichtiger ist: Für mich, der keine Lust hat, einen Batzen Geld hinzulegen, um so ein Tool installieren zu dürfen, ist es kein Problem, diese Dateien einfach in einem Texteditor zu erstellen - es handelt sich nämlich um eine relativ einfach gestrickte XML-Datei.

    Später, wenn alle Übersetzungen erstellt, und keine allzu großen Änderungen mehr zu erwarten sind, ist es aber ein leichtes, diese Daten in das GetText-Format (MO) umzuwandeln, um den Server zu entlasten. Die kann man dann zwar nicht mehr einfach im Texteditor bearbeiten, aber es gibt recht brauchbare Freeware-Programme (wie z.B. POEdit), mit denen man leicht Änderungen machen kann.

    Sprachkennzeichnung

    Der Rest ist ziemlich einfach: Im HTML-Code sollte natürlich klar spezifiziert sein, in welcher Sprache das Dokument vorliegt. Dies kann man durch einen Header wie den folgenden erreichen:

    <meta name="content-language" content="de" />

    Je nach HTML-Variante können es aber auch noch andere Stellen sein, an denen man die Sprache einfügen muss. Zum Beispiel:

    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de" lang="de">

    Kniffliger wird es, wenn die Sprachen gemischt werden. Hier zum Beispiel ein Sprachwahl-Menü:

    <label for="language">Sprache:</label>
    <select name="language">
      <option value="en" lang="en">English</option>
      <option value="de" lang="de">Deutsch</option>
      <option value="ar" lang="ar">العربية</option>
      <option value="ja" lang="ja">日本語</option>
    </select>

    Das Ergebnis sieht dann so aus:

    Es ist wichtig, dabei im Hinterkopf zu haben, dass dieses Menü ganz wunderbar auch ohne die korrekten lang-Angaben funktioniert hätte - so lange man nur Lateinische Schriftsysteme dabei verwendet. Sobald aber die erste Nicht-Westliche Sprache dazu gekommen wäre, gäbe es früher oder später Problem.

    Tipp: Die Sprache, die am meisten Probleme macht ist gewöhnlich Arabisch. Der Grund ist, dass im Arabischen gleich mehrere Komplikationen zusammen kommen: es ist ein Mehr-Byte Zeichensatz, die Schriftrichtung ist von Rechts nach Links - und es finden verschiedene Substituierungen im Schriftbild statt. Daher benutze ich gerne Text in dieser Sprache (aus Wikipedia kopiert - verstehen tue ich es schließlich auch nicht) zum Testen der Site. Wenn es in Arabisch funktioniert, sind die anderen Sprachen gewöhnlich kein Problem mehr.

  • Dude, wo ist mein Passwort?

    Eine Web-Anwendung macht nur so richtig Spass, wenn man eine Möglichkeit hat, sich am Server anzumelden und seinen Namen auf der Website zu sehen. Nun ja, manche Leute haben kein Problem damit, als anonymous735 mit anonymous384 zu chatten, aber viele andere wünschen sich zumindest ein wenig persönliche Ansprache...

    Nun gut, es muss also eine Anmeldung her. Was macht man in so einem Fall? Ich für meinen Teil, lasse mir eine Badewanne vollaufen, und meditiere im kochend heißen Wasser darüber, wie man das am Besten implementiert.

    Hier meine Erkenntnisse:

    1. Login-Namen sind Sch..e.

    Ich für meinen Teil vergesse immer, mit welchem Login-Namen ich auf welchem Dienst angemeldet bin. Zwar habe ich normalerweise immer den gleichen, aber der eine Server mag keinen Punkt im Namen, der nächste erlaubt nur 8 Zeichen, der dritte hat wieder etwas anderes auszusetzen... Am Ende muss ich immer raten.

    Immerhin: mein Web-Browser merkt sich, mit welchem Namen (und Passwort) ich mich angemeldet habe, sodass ich mich nur erinnern muss, wenn ich an einen anderen Rechner gehe.

    Trotzdem ist da etwas grundlegend falsch mit dem Konzept der Login-Namen: Normalerweise gibt es eine 1:1-Beziehung zwischen Login-Namen und e-Mail-Adresse. Warum also nicht gleich die e-Mail-Adresse benutzen?

    Das hat auch den Vorteil, dass diese wohlformuliert ist (also mittels RegEx-Pattern überprüft werden kann) und dass der Benutzer sie auch nicht so leicht vergisst.

    Der Nachteil ist, dass es relativ aufwändig wird, falls mal jemand seine e-Mail-Adresse ändern möchte...

    2. Mein Passwort geht niemanden etwas an

    Im Prinzip gilt für Passwörter das gleiche wie für die Login-Namen: Niemand merkt sich die hunderte von Passwörter, die man zur Anmeldung an verschiedenen Diensten benutzt hat. Im Prinzip benutzen wir alle das gleiche Passwort für alle Dienste. Bestenfalls zwei oder drei verschiedene, um zwischen "unkritischen" Diensten (wie Diskussionsforen) und wichtigen (wie dem Admin-Zugang zum Firmenserver) zu unterscheiden.

    Das bedeutet nun auch, dass jemand, der auf irgendeine Weise an das Passwort zu einem Dienst gelangt, meist auch gleich das für alle anderen Dienste gefunden hat (wenn er auch meist für den Login-Namen ein wenig raten muss). Deswegen sind die Passwörter unbedingt als "streng geheim" anzusehen, auch wenn der Dienst, den man implementiert eher unwichtig ist.

    Mehr noch: viele (wahrscheinlich die meisten) Benutzer sind auch extrem denkfaul, wenn es um ihre Passwörter geht. Es ist kaum zu glauben, wie viele User Passwörter wie "12345" oder "qwertz" benutzen; Oder den Namen der Freundin oder des Hundes, die Telefonnummer für's Büro, oder ähnliche einfach zu erratenden Begriffe.

    Zeit also, einmal ein wenig am Konzept zu feilen:

    Zunächst mal die Anmeldung: Um sich einzuschreiben braucht der Benutzer eigentlich nur seine e-Mail-Adresse einzugeben. Ich frage noch nach Vor- und Nachnamen, aber das ist eigentlich optional.

    An diese e-Mail-Adresse schickt der Server dann eine e-Mail mit dem Passwort. Das hat den Vorteil, dass auf diese Weise auch die e-Mail-Adresse verifiziert wird, sodass man mit Sicherheit sagen kann, dass die Adresse gültig ist.

    Wichtiger ist natürlich das Passwort: Ich wollte verhindern, dass die Benutzer ein einfach zu erratendes Passwort setzen, welches auch sonst überall verwendet wird. Der einfachste Weg dahin schien mir, ihnen ein Passwort vorzuschlagen, welches sowohl einigermaßen sicher, als auch einfach zu merken ist.

    Hier ist das Script, welches diese Passwörter erzeugt:

    static public function RandomPassword($syllables = 3)
    {
      $consonants = explode(',', 'b,c,d,f,g,h,j,k,m,n,p,q,r,s,t,v,w,x,y,z');
      $vowels = explode(',', 'a,e,i,o,u');
      $pass = '';
    
      // create the syllables:
      for ($i=1; $i<=$syllables; $i++) {
        $pass.= $consonants[rand(0, count($consonants)-1)]
              . $vowels[rand(0, count($vowels)-1)];
      }
    
      // uppercase it for ledgibility:
      $pass = ucfirst($pass);
    
      // add a number for security:
      $pass.= rand(10, 99);
    
      // finished. That should be a reasonably secure password:
      return $pass;
    }
    

    Hier mal ein paar Passwörter, die auf diese Weise entstanden sind:

      Lidaxi34
      Gomara76
      Tokami10
      Yojono58
    

    Das ist wahrscheinlich nicht ganz so sicher wie die High-Security Passwörter, die mache Sites in so einem Fall erzeugen, dafür dürfte die Chance größer sein, dass der Benutzer dieses Passwort beibehält - auch wenn er natürlich die Möglichkeit hat, ein neues Passwort zu setzen.

    Man könnte das noch verfeinern, indem man bestimmte 3-Buchstaben-Silben erlaubt, oder andere linguistische Finessen einbaut. Wichtig ist nur, dass "Wörter" entstehen, die in keinem Wörterbuch zu finden sind, die aber trotzdem wie "richtige" Wörter klingen.

    Wer besonders international sein möchte, könnte sogar je nach Muttersprache des Benutzers andere Buchstaben oder Regeln verwenden. Hier ein Beispiel:

      // for Finnish users - no c,q,w,x,z, and ä,ö,y are vowels:
      $consonants = explode(',', 'b,d,f,g,h,j,k,l,m,n,p,r,s,t,v');
      $vowels = explode(',', 'a,e,i,o,u,ä,ö,y');
    

    Trau, schau wem

    Nun ist es nicht damit getan, ein sicheres Passwort zu haben, es sollte auch niemandem in die Hände fallen. Mir kamen spontan drei Möglichkeiten in den Sinn, wo man Passwörter auslesen könnte: Entweder direkt in der Eingabemaske, bei der Übertragung durch's Internet, oder direkt in der Datenbank auf dem Server.

    Deswegen meine ich, es ist sicherer, die Passwörter erst gar nicht zu speichern. Nirgends. Gar nicht.

    Aber der Reihe nach: Das Login-Formular sieht (vereinfacht) etwa so aus:

    <form action="#" method="post" onsubmit="return login_submit();">
    	<input type="text" name="email" value="" />
    	<input type="password" name="password" value="" />
    	<input type="hidden" name="pw_hash" value="" />
    	<input type="submit" />
    </form>

    Der Trick ist natürlich die JavaScript-Funktion "login_submit", die dazu dient, die Daten ein wenig umzuorganisieren: Und zwar wird ein MD5-Hash des Passwortes erstellt und in das (versteckte) Feld "pw_hash" geschrieben. Dann wird das Feld "password" gelöscht, und erst dann das OK gegeben, die Daten zu übertragen.

    Eine wichtige Frage in solchen Fällen: Was passiert eigentlich wenn der Benutzer JavaScript ausgeschaltet hat? Das ist nicht so abwegig, wie manche vielleicht denken: es tauchen immer mal wieder Sicherheitslücken in den verschiedenen JavaScript-Implementierungen auf, weswegen es nicht wenige Benutzer gibt, die es nur bei Bedarf aktivieren.

    Nun, in diesem Fall würde nun also einfach das unverschlüsselte Passwort übertragen - das ist unschön, aber kein Beinbruch (v.a. wenn man bedenkt, dass sich die meisten anderen Websites überhaupt nicht um so was kümmern) - und der Server kann dann eine Fehlerseite anzeigen, auf der der Benutzer aufgefordert wird, JavaScript zu aktivieren. Alternativ könnte das Passwort auch einfach auf dem Server in eine MD5-Hash umgewandelt werden, und alles funktioniert weiter als sei nichts passiert, aber da meine Site ohnehin JavaScript für verschiedene Ajax-Controls benötigt, ist eine Fehlermeldung wohl angebrachter.

    Nun bekommt der Server also einen Login-Namen (in diesem Fall eine e-Mail-Adresse) und einen MD5-Hash also Passwort-Ersatz. So weit so gut.

    In der Datenbank lagern nun natürlich die Benutzerdaten (mit der e-Mail-Addresse als Primary Key), aber keine Passwörter (!) sondern nur - man ahnt es schon: MD5-Hashes der Passwörter.

    Der Grund ist, dass man auch der Datenbank (bzw. dem Datenbank-Admin) keine so vertraulichen Daten anvertrauen sollte (wenn es nicht unbedingt sein muss). Wenn - siehe oben - der Benutzer überall das gleiche Passwort benutzt, und die Datenbank dieses Dienstes irgendwann einmal kompromitiert werden sollte, hat der Angreifer möglicherweise auch gleich die Passwörter für dutzende andere Dienste (einschließlich e-Mail und Skype-Account) 'gezogen'. Hat er dagegen 'nur' den MD5-Hash, ist er noch nicht viel weiter gekommen.

    Klar gibt es Möglichkeiten, an Klartext-Phrasen für MD5-Hashes zu kommen. Vor allem, wenn man sich in aller Ruhe zuhause hinsetzen kann, und keine Timeouts oder IP-Blockaden zu fürchten hat. Aber es kostet Zeit, die der Admin nutzen kann, die User zu warnen, und/oder das Login-Script zu ändern.

    Man könnte noch weitere Sicherheitsmaßnahmen einbauen, z.B. um gegen brute force Angriffe vorzugehen könnte man nach 3 fehlgeschlagenen Anmeldeversuchen den Account für eine gewisse Zeitspanne sperren, aber für die Durchschnittswebsite reicht es wahrscheinlich, von Zeit zu Zeit einen Blick auf die Server-Logs zu werfen.

    Der Nachteil dieses Systems ist, dass es, da das Passwort nirgends abgespeichert ist, unmöglich ist, es dem Benutzer zuzuschicken, falls er es mal vergessen haben sollte (kommt ziemlich häufig vor). In diesem Fall muss der Server ein neues Passwort generieren. Das hat aber ein paar unangenehme Nebeneffekte:

    So schnell kannst Du dich gar nicht einloggen...

    Nun wäre es also möglich, einen Benutzer effektiv vom Server auszuschließen, indem man einfach in seinem Namen anfrägt, bitte ein neues Passwort zugeschickt zu bekommen. Nun, nicht, wenn man das nur einmal macht, aber im Prinzip spricht nichts dagegen, dies alle 30 Sekunden zu tun. Durch ein Script, versteht sich. Das darf natürlich nicht passieren.

    Aus diesem Grund habe ich mir folgendes ausgedacht: Wenn ein Benutzer ein neues Passwort anfragt, wird dies neben dem alten in der Datenbank gespeichert. Es gibt also gleich zwei Felder für die Passwort-Hashes. Meldet der Benutzer sich nun wieder mit seinem alten Passwort an, wird der neue Hash gelöscht. Benutzt er dagegen das neue, wird das alte gelöscht (tatsächlich wird das neue in das andere Feld verschoben, etc.. zu kompliziert für hier...)

    Wer sagt mir, dass Du ein Mensch bist?

    Es fehlt noch eine Sicherheitsmaßnahme: Um zu verhindern, dass verschiedene Dinge von Scripts ausgeführt werden können, sollten an strategisch wichtigen Stellen sog. Captchas eingesetzt werden. Das habe ich noch nicht implementiert - vor allem, da ich bisher kein sinnvolles Script gefunden habe, welches solche Captchas erzeugt. Wenn jemand etwas brauchbares kennt, lasst es mich wissen. Andernfalls werde ich wohl irgendwann selber etwas zusammenpfriemeln müssen...

  • Licht und Schatten, Tag und Nacht...

    Man sollte denken, es sei eigentlich ein wenig zu früh, um an Web-Design zu denken, aber ich bin anderer Meinung.

    Zunächst mal muss das Design, das während der Entwicklung verwendet wird, nicht unbedingt das gleiche zu sein, wie das, welches später noch zu sehen ist (zumindest nicht, wenn man es sauber implementiert – sprich: mit CSS!).

    Es lohnt sich aber vor allem, sich frühzeitig Gedanken zu machen, wie die Anwendung aussehen könnte, einfach, weil man sich so immer mal wieder Gedanken darüber macht, und so vielleicht irgendwann eine bessere Idee bekommt.

    Day DesignNun, mein erster Gedanke war, ein einfaches, solides Design zu bauen. So im Stil etwa wie Wikipedia oder ähnliche Community-Sites. Auf jeden Fall grau und schlicht.

    Hier ist ein Detail aus diesem Design. Man beachte die Spiegelung der Icons und den Verlauf unter dem Titelbalken.

    Dann kam es wie so häufig: zwei Ideen kommen zusammen, und daraus wird eine neue:

    Zuerst las ich irgendwo, dass (CRT-)Monitore mehr Energie verbrauchen, wenn sie ein helles Bild darstellen, als wenn sie ein dunkles Bild anzeigen, und dass, würde Google die Startseite mit hellem Text auf dunklem Hintergrund gestalten, weltweit der Energieverbrauch für ca. ein Dutzend Einfamilienhäuser eingespart werden könnte.

    Ob das übertrieben ist oder nicht - egal. Auf jeden Fall ist es ein schöner Gedanke.

    Dann stellte ich fest, dass mein Navigationsgerät im Auto (Marke verrate ich nicht!) bei Sonnenuntergang automatisch in einen "Nachtmodus" umschaltet, in dem die Farben dunkler dargestellt werden.

    Hm, das Display so ist tatsächlich besser abzulesen, dachte ich noch.

    So war schnell der Entschluss gefasst: Es muss auch ein Nachtmodus her, und man muss zwischen den beiden Modi leicht und schnell umschalten können.

    Night DesignBis das ganze System so funktionabel war, dauerte es noch eine Weile, aber jetzt funktioniert es schon ganz ordentlich. So sieht der Nachtmodus aus:

    Wichtig für's schnelle Umschalten: es wird nicht etwa ein neues Design geladen, sondern tatsächlich ist das zweite Stylesheet bereits geladen, etwa so:

    <link rel="alternate stylesheet" type="text/css" href="nightstyle.css" media="screen" title="nightstyle" />

    und wird dann nur per JavaScript aktiviert. Es gibt dafür zahllose Beispiele im Netz, aber hier habe ich, glaube ich wenigstens, zum ersten Mal eine Anwendung dafür gefunden, wo das wirklich Sinn macht.

    Umgeschaltet wird übrigens durch Klick auf die Sonne, bzw. den Mond. Wie sonst?

  • Der richtige Pfad zur richtigen Sprache

    So, nun also zu den wirklichen konzeptuellen Gedanken.

    Zu den Vorgaben, die ich mir selber gestellt hatte, gehörten unter anderem:

    • Lokalisierbarkeit (und auch wenigstens gleich 2 Sprachversionen haben!)
    • Standardkonformität (in allen möglichen Aspekten)
    • Suchmaschinen-freundlich (klar, ich will ja auch gefunden werden )
    • Usability (Neusprech für "Benutzerfreundlichkeit")

    Wenn ich nun aber meine Pfade suchmaschinenfreundlich gestalten möchte, aber mehrere Sprachversionen auf dem Server habe, kann ich keinen Pfad im Stil von

    /user/sascha.leib/data/edit.php?lang=de

    brauchen. Denn das widerspäche der Lokalisierbarkeit.Und benutzerfreundlich ist es auch nicht.

    Besser wäre es, ich könnte die Sprachversionen schon an der Sprache des Request-Pfades unterscheiden:

    /user/sascha.leib/data/
    /benutzer/sascha.leib/daten/

    Für uns ist es relativ einfach zu unterscheiden: der erste Pfad soll wohl Englisch sein, der zweite Deutsch. Wieso soll ein Computer das nicht können?

    Jedenfalls komme ich hier mit den existierende Frameworks nicht weit. Es muss etwas selbstgestricktes sein.

    Kein Problem. Ich lege also erst einmal in der Datenbank eine Tabelle an, in der ich entsprechende Daten hinterlege. Diese sieht in etwa wie folgt aus (die tatsächliche Tabelle enthält noch ein paar mehr Daten, aber zur Erklärung reichen uns erst mal diese:

    uri lang article parameters
    /benutzer de 1014 short_name=string/action=string
    /user en 1013 short_name=string/action=string

    Und bevor jemand fragt: ja, 'uri' ist der falsche Begriff. Tatsächlich sollte das Feld "Pfad-Fragment" genannt werden. Ist mir jetzt aber auch egal…

    So, "Benutzer" und "User" ist klar unterscheidbar. Was aber, wenn der Pfad keine eindeutige Zuordnung zulässt? Nun, das ist zum Beispiel bei einem besonders wichtigen Pfad der Fall: beim '/' Root-Pfad! Daher noch ein Eintrag:

    uri lang article parameters
    / en,de en=1000;de=1001 p1=string/p2=string

    Ah, und schon wird es etwas komplizierter. Aber keine Sorge, es wird alles erklärt.

    Noch ein Hinweis an alle Informatiker: Ja, das bedeutet, dass die Datenbank nicht mehr in Normalform ist. Aber Effektivität ist hier wichtiger als noch so nützliche und glorreiche Theorien.

    Letztlich muss ich sagen, dass – zumindest bis zu dem Punkt, an dem ich jetzt bin – das Root-Verzeichnis der einzige Punkt war, wo ich mehrere Sprachen auf einen Pfad mappen musste. Das wäre sicher anders, wenn ich näher verwandte Sprachen (z.B. Dänisch und Norwegisch) integrieren würde, aber zumindest bei Deutsch und Englisch hatte ich bisher keine Probleme.

    Trotzdem: es bleibt das Problem mit dem Root-Pfad, und wie damit umgegangen werden soll.

    Nun wollte ich keinen Splash-Screen mit Sprachwahl, wie ihn manche Sites haben, also musste ich eine andere Lösung finden. Nun, zum Glück gibt es die Möglichkeit, aus den HTTP-Header, die der Browser sendet auszulesen, welche Sprachen der Benutzer bevorzugt.

    Klar, die meisten Benutzer haben diese Einstellung noch nie in ihrem Leben angeschaut. Aber da der Browser-Hersteller dies gewöhnlich auf die Interface-Sprache des Betriebssystems voreingestellt hat, ist es zumindest eine gute Vorauswahl.

    Zweitens nutze ich die Möglichkeit, ein Cookie zu setzen. Allerdings nur, wenn der Benutzer explizit eine Seite besucht, die einer anderen Sprache zugeordnet ist als das Script aus den HTTP-Headers normalerweise empfohlen hätte.

    Kommt der Benutzer nun also auf die Startseite (oder irgendeine andere, der mehrere Sprachen zugeordnet wurden – siehe im Feld 'lang', hier also 'en,de'), dann wird zuerst geschaut, ob so ein Cookie existiert, und falls die Cookie-Sprache also eine der für diese Seite erlaubten ist, haben wir die Sprache schon gefunden.

    Falls nicht, wird die Liste aus dem HTTP-Header hergenommen, und mit der Liste der Sprachen für diesen Pfad vergleichen. Falls es eine Überschneidung gibt, nehmen wir die mit der höheren Gewichtung laut Header (gewöhnlich die erste)

    Und falls das alles nichts hilft, nehmen wir einfach die erste Sprache aus der Liste. Hier also Englisch.

    Der Rest ist relativ einfach zu verstehen. Wenn nur eine Sprache in Frage kommt, steht im Feld "Article" eben nur eine Nummer; Wenn es mehrere sind, eine Liste, in der jeder Sprache eine Artikelnummer zugewiesen wird.

    Der aufmerksame Beobachter entdeckt sicher die eher inkonsistente Verwendung der Listentrennzeichen in den verschiedenen Spalten. Keine Sorge, ich hatte mir etwas dabei gedacht, als ich das so angelegt hatte. Wenn ich nur noch wüsste, was…

    Das nächste Problem sind die Parameter, die dem Script später übergeben werden sollen. Ich habe mich für eine explizite Lösung entschieden, d.h. die Parameter werden inklusive Typ in der Datenbank spezifiziert, und alles, was nicht in das Schema passt, wird verworfen.

    Ein Beispiel: Für eine Anfrage nach /benutzer/sascha.leib/edit/ möchte ich letztlich eine Datenstruktur haben, die in etwa so aussieht:

    Request Object
    (
        [uri] => /benutzer
        [lang] => de
        [article] => 1014
        [parameters] => Array
            (
                [short_name] => sascha.leib
                [action] => edit
            )
        u.s.w. …
    )

    Im Moment "kennt" das System nur Parameter vom Typ "String" oder "Int". Im Prinzip könnte man auch Typen wie "Username" definieren, die dann einem existierenden Benutzernamen entsprechen müssen, u.s.w.

    Dieses Vorgehen hat natürlich vor allem Sicherheitsgründe: Die genannten Parameter werden genau darauf untersucht, ob sie auch wirklich passen, und alles andere wird einfach ignoriert. Aber es bietet sich auch an, um komplexere Setups durchzuführen.

    Eine wichtige Frage: wie bekommt das Script eigentlich die Anfragen zugewiesen, wenn der Pfad, der angefragt wird überhapt nichts mit dem des Scriptes zu tun hat?

    Zum Glück gibt es einen sehr mächtigen Mechanismus namens mod_rewrite. Das ist ein Modul, welches für den Apache webserver geladen werden kann (meist ist es schon automatisch aktiv), und das gerade für solche Sachen sehr gut eingesetzt werden kann.

    In der einfachsten Form legt man einfach eine Datei mit dem Namen .htaccess (der Punkt ist wichtig!) in das Web-Root-Verzeichnis, und schreibt das folgende hinein:

    RewriteRule ^.*$ dispatch.php [L]

    In aller Kürze: Dieser Befehl sorgt dafür, dass immer das Script "dispatch.php" aufgerufen wird, egal wie der angefragte Pfad wirklich heisst.

    Im Endeffekt wird man noch ein paar Ausnahmen definieren müssen, um sicher zu gehen, dass statische Inhalten, wie z.B. Grafiken oder CSS-Dateien direkt geladen werden können, aber im Prinzip ist das da oben schon der ganze Trick.

  • Ge- oder Ver-plant?

    Da saß ich also mit meinem frisch heruntergeladenen Zend-Framework und versuchte, mal dahinter zu steigen, wie das denn funktionieren soll...

    Ach, noch mal einen Schritt zurück: Normalerweise fängt man so ein Projekt erst mal damit an, sich darüber klar zu werden, was man eigentlich für Features implementieren möchte, und wie das alles zusammen zu spielen hat.

    Hier nicht. Ich hatte eigentlich überhaupt keinen Plan, was die Site so alles können soll - meine einzige Vorgabe war, dass ich im Laufe dieses Projektes möglichst viel lernen sollte. Klar, die üblichen Hypes sollten dabei irgendwie verwurschtelt werden, aber wie und wozu... keine Ahnung.

    Aus dieser Perspektive hat sich mein erster Ansatz: eine Web-Applikation Bottom-Up zu bauen auch bald als ziemliche Idiotie erwiesen. Ähm, um genau zu sein: leider eben nicht allzu bald. Erst habe ich ein paar Wochen (bei einer Quote von ca. 1h pro Tag) damit verbracht, eine Klassenbibliothek zu bauen, von der ich später fast gar nix brauchen konnte.

    Na ja, immerhin habe ich so das Framework besser kennen gelernt. Auch gut.

    Aber die Entscheidung für das Zend-Framework habe ich nicht bereut. Während die meisten anderen Frameworks die ich gesehen habe mir vorschreiben, wie ich die Site zu strukturieren habe, blieb mir das angenehmerweise hier selbst überlassen. Daher habe ich mich auch für ein eher unkonventionelles Ordner-System entschieden, das sonst nicht möglich gewesen wäre. Aber dazu später mehr.

Footer:

Die auf diesen Webseiten sichtbaren Daten und Inhalte stammen vom Blog-Inhaber, blog.de ist für die Inhalte dieser Webseiten nicht verantwortlich.