Recherche: Multilinguale sprechende URL

ZodiacXP

Erfahrenes Mitglied
Heute beginnt meine Recherche, wie man eine Multilinguale sprechende URL am besten umsetzt. Da beginnt es schon bei der Grundlage: Datenbank oder XML?

Datenbank mit NestedSet halte ich für schneller (auslesen, verschieben) als XML. Trifft das zu?

Wie löst man das Multilinguale dahinter? Es ist klar, dass es noch eine zweite Quelle mit Übersetzungen gibt und die zugehörigen Relationen, jedoch ist mir die Laufzeit enorm wichtig.

Hier kommt noch erschwerend hinzu: Verschiebt man in einer Sprache bei XML einen Knoten, so muss er in den anderen Dokumenten / Sprachen ebenfalls verschoeben werden, was mit einer Datenbank und Relationstabelle für die Übersetzung nicht der Fall wäre.

EDIT: Im Anhang ist eine Datenbankstruktur dafür. Das finde ich ziemlich komplex und aufwendig, jedes mal über drei Tabellen abzufragen und sie zu verbinden. Vor allem, da es bei jedem Klick und Besuch der Seite geschehen wird.

Wie macht man es richtig?
 

Anhänge

  • ER.gif
    ER.gif
    8,9 KB · Aufrufe: 65
Zuletzt bearbeitet:
Moin!

Vor einer Antwort noch eine Frage: willst du die kompletten URLs "sprechen" lassen oder einen Teil als Identifier nutzen? Also z.B.:
Code:
http://www.example.com/a/simple/uri.html
http://www.example.com/eine/einfache/uri.html
oder:
Code:
http://www.example.com/a/simple/uri/en-12354.html
http://www.example.com/eine/einfache/uri/de-12354.html

Gruß
Enum
 
Danke für die Antworten.

Moin!

Vor einer Antwort noch eine Frage: willst du die kompletten URLs "sprechen" lassen oder einen Teil als Identifier nutzen?

Eher ersteres. Mal als Beispiel:
Code:
example.org/Haus
example.org/Building >> beide zu einem Skript, dass den Inhalt sprachbasiert ausgibt

example.org/Haus/Zimmer/Lichtschalter
example.org/Building/Room/Switch >> ebenfalls zu einem Skript, das sprachbasiert arbeitet

(edit: Passende IDs sind auch am Enden teilweise vorhanden, wobei sie erst durch vorangegangenes eine Aussage bekommen)
example.org/Unterhaltung/Film/123-District-9
example.org/Unterhaltung/Musik/123-Say-anything

Die Sprache wird per Header-Infos vom Browser und / oder per Eingabe der URL ermittelt.

Hallo,

sofern nicht nur die Sprachen, sondern auch der Rest in XML konzipiert sind, kann das durchaus Sinn machen, dann kann man das Dokument einfach transformieren, hier ein Tutorial dazu:
http://www.webholics.de/2007/10/01/xslt-als-template-engine-in-php-teil-7/

Genau daran dachte ich. Frage mich jedoch was schneller ist bei mittelgroßen Seiten.
 
Zuletzt bearbeitet:
Unsere Firma stand des öfteren vor dieser Frage, inzwischen haben wir eine gute Lösung für uns gefunden - vielleicht kannst Du einige Anregungen hier finden:

Wir arbeiten nicht mit XML-Datenbanken oder sog. "plain files". Meist mit PostgreSQL, das kann netter weise ein Query auch als XML beantworten...
Basis der gesamten Struktur ist eine Tabelle in der die grundlegende Sitemap abgelegt ist - in etwa so:
Code:
 id | parent | order |   name   |   template   | ...
----+--------+-------+----------+--------------+-----
  1 |   NULL |     0 |   "home" |         NULL | ...
  2 |   NULL |     1 |   "news" |       "blog" | ...
  3 |      2 |     0 | "offers" |  "offerlist" | ...
Natürlich wird nicht jede einzelne Seite hier registriert - die Spalte "template" z.B. regelt nicht nur das Layout, sondern auch die Art und Weise tieferer URIs, deren http-query-strings, rewriting usw.
Für Ordung sorgen Constraints und Trigger.

Aufgrund der strikten parent/child-Verknüpfung lassen sich im Quellcode die Seiten ähnlich XPath ansprechen:
Code:
mk_link("/news/offers/...")
Das gleiche geht auch in Templates:
Code:
<html:p>
 Blabla, bla <tpl:link path="/news/offers/..."/>, bla blupp...
</html:p>
Aber auch XSL-T kann man ziemlich gut einsetzten - z.B. um sitemap.xml Dateien zu generieren uvm.

Für Mehrsprachigkeit der URL gibt es jetzt mehrere Möglichkeiten:
  • In der Basistabelle selbst die diversen URI-Teile ablegen, vor allem bei kleinen bis mittleren Seiten mit wenigen Sprachen vorteilhaft. Außerdem sind die entsprechenden Constraints für die Eindeutigkeit recht simpel:
Code:
 id | parent | order |   name   |   template   |  uri_en  |    uri_de    | ...
----+--------+-------+----------+--------------+----------+--------------+-----
  1 |   NULL |     0 |   "home" |         NULL |   "Home" | "Startseite" | ...
  2 |   NULL |     1 |   "news" |       "blog" |   "News" |    "Aktuell" | ...
  3 |      2 |     0 | "offers" |  "offerlist" | "Offers" |   "Angebote" | ...
  • Eine weitere Tabelle anlegen, in der die URIs den Sprachen zugeordnet werden, vor allem bei kleinen bis mittleren Seiten vorteilhaft, die Anzahl der Sprachen ist weniger wichtig. Allerdings sind die Verknüpfungen und Constraints etwas komplizierter:
Code:
 site_id | language |      uri 
---------+----------+---------------
       1 |     "en" | "Home"
       1 |     "de" | "Startseite"
       2 |     "en" | "News"
       2 |     "de" | "Aktuell"
  • Oder für jede Sprache eine eigene Tabelle - wovon ich aber strikt abraten würde.
Das Ergebnis in allen Fällen ist eine Struktur, die zu URIs wie
Code:
/Aktuell/Angebote
oder
Code:
/News/Offers
führt, aber z.B. für Permalinks immer noch die Möglichkeit einer direkten Adressierung offen hält:
Code:
/site?id=3&lang=en
.
Außerdem ist es mit leichten Abwandlungen möglich, die URIs nahezu komplett zu vernachlässigen bzw. beliebig zu vergeben:
Code:
/en-3/all/you/need.html
/alles/was/das/herz/begehrt-de3.html
... und darüber freut sich jeder SEOtologe ;-)

Das wirkt alles recht kompliziert und vor allem rechen-aufwendig, ist aber im wesentlichen ziemlich simpel und mit dem entsprechenden Query-caching sogar sehr flott - immerhin ändert sich die Sitemap nicht alle 5 Sekunden...
Übrigens: Anstelle eines Aufwendigen URL-Rewritings in der Response-Phase des Servers oder umständlich zu aktualisierenden ModRewrite-Regeln empfiehlt sich hier z.B. eine eigene Rewrite-Engine oder, im Falle des Apache-Webservers, ein eigener RewriteHandler in (Mod)Perl oder C.

Gruß
Enum
 
Das ist toll! Überlege zwar noch welche Trigger alles notwendig werden aber die Constrains waren mir bisher noch unbekannt und das man mit Postgre einfach drauf zugreifen kann ist auch gut. Wenn es sich bei euch für ca. 100 Aufrufe pro Sekunde bewährt hat, ist es genau das was ich suche ;)

Bin heute endlich zur Umsetzung gekommen und habe bisher eine Tabelle angelegt:
Code:
CREATE TABLE sitemap (
  siteid smallint,
  parent smallint,
  name text,
  template text,
  PRIMARY KEY (siteID),
  INDEX par_ind (parent),
  FOREIGN KEY (parent) REFERENCES site (siteID)
)

Abgesehen von der Übersetzung, frage ich mich noch ob es in Postgre einfach ist einen Pfad zu erstellen, mit Bedingungen.

Zur Zeit arbeite ich umständlich mit mehreren Self-Joins, was sicher nicht performant ist.

Edit: Nach viel hin und her habe ich mir die folgende Sicht angelegt:
Code:
 WITH RECURSIVE sitemap_names(siteid, path, list) AS (
                 SELECT sitemap.siteid, ARRAY[sitemap.name] AS "array", ARRAY[sitemap.template] AS "array"
                   FROM sitemap
                  WHERE sitemap.parent IS NULL
        UNION ALL 
                 SELECT s.siteid, sitemap_names.path || ARRAY[s.name], sitemap_names.list || ARRAY[s.template]
                   FROM sitemap s
              JOIN sitemap_names ON s.parent = sitemap_names.siteid
             WHERE s.siteid <> sitemap_names.siteid
        )
 SELECT sitemap_names.siteid, sitemap_names.path, sitemap_names.list
   FROM sitemap_names
  ORDER BY sitemap_names.path;

Wird das nun jedes mal komplett rekursiv durchlaufen? Das frisst dann enorm viel.
 
Zuletzt bearbeitet:
Hi!

Wenn es sich bei euch für ca. 100 Aufrufe pro Sekunde bewährt hat, ist es genau das was ich suche ;)
Damit kommt man locker hin.

Code:
CREATE TABLE sitemap (
  siteid smallint,
  parent smallint,
  name text,
  template text,
  PRIMARY KEY (siteID),
  INDEX par_ind (parent),
  FOREIGN KEY (parent) REFERENCES site (siteID)
)
An dieser Stelle empfehle ich dringend die siteid mit in den Index aufzunehmen... ;)

Zur Zeit arbeite ich umständlich mit mehreren Self-Joins, was sicher nicht performant ist. ... Wird das nun jedes mal komplett rekursiv durchlaufen? Das frisst dann enorm viel.
Klar, das ist ein ziemlich hungriges Query. Wir haben für diesen Teil eine Klasse (C++, geht aber auch mit anderen Sprachen/Techniken) die einzelne Sites repräsentiert und somit die Rekursion in den Quellcode auslagert. Aber dank effizientem Caching juckt das den Server wenig.
Es gibt auch einen Konstruktor, der über die komplette Pfadangabe (siehe vorheriger Post) instanziert und vor allem im Rewriting und dem generieren von Links Anwendung findet. Die Identifikation der Seite erledigt eine PL/Perl Funktion intern. Doch auch hier kann man sicher noch verbessern und/oder abwandeln.

Gruß
Enum
 
Ok. Danke für die Antwort.
Das ganze entspricht voll und ganz dem, was ich gesucht habe. Vielen Dank!

Werde jetzt vorerst einmal mehrere Subquerys nutzen. Heute hole ich mir das Buch aus der Bibo (bin schon oft genug dran vorbei gelaufen und dachte mir das brauch ich nie) und demnächst lass einen solchen Query per C-Klasse als Apache Modul laufen (sofern ich das bis hier richtig verstanden habe).

Schon schwer, aber bin froh wenn es vom langsamen PHP weg geht. Und es ist toll mal wieder was neues zu lernen. Postgre scheint einiges mehr zu bieten als mySQL, vor allem freue ich mich über MVCC und die Constrains, was bei mySQL nur im ganz langsamen InnoDB gegeben war.
 

Neue Beiträge

Zurück