LinuxFocus.org(/Nederlands) maken met XML - XSLT

ArticleCategory: [Artikel Kategorie]

Applications

AuthorImage:[Bild des Autors]

[Egon Willighagen]

TranslationInfo:[Author and translation history]

original in en Egon Willighagen

en to nl Floris Lambrechts

AboutTheAuthor:[Über den Autor]

Hij studeert dit jaar af (Master's-diploma) en begint dan aan zijn PhD over chemometrie. Hij houdt nog steeds veel van basketbal, net als van LinuxFocus en Linux in het algemeen.

Abstract:[Zusammenfassung]

Dit artikel bevat de presentatie zoals gegeven op de Libre Software Meeting in Bordeaux in juli van dit jaar. Het legt uit hoe de XML-database in elkaar zit die we gebruiken voor het maken van onze website, LinuxFocus.org(/Nederlands).

ArticleIllustration:[Titelbild des Artikels]

[Illustratie: xml]

ArticleBody:[Der eigentliche Artikel]

Introductie

Het oude systeem voor het onderhouden van de artikelen en vertalingen hier bij LinuxFocus, bestond uit verschillende tekstbestanden, onder meer resdb.txt, issuedb.txt en maindb.txt. Deze bestanden hadden een vast formaat en werden gebruikt voor het automatisch maken van webpagina's. Spijtig genoeg was het lastig om ze uit te breiden, en omdat het om meerdere bestanden ging was het moeilijk om alle info over één bepaald artikel te overzien.

Toen ik aan de database begon, werden er bij LinuxFocus nog niet veel pagina's automatisch gemaakt. Als redacteur van het Nederlandse team zag ik er wel wat in om de overzichten op de website automatisch te laten maken. Al die HTML's aanpassen, elke keer opnieuw als er een nieuwe vertaling uit was, was veel te veel werk en bovendien een bron van gebroken links. Daarom wilde ik een nieuw systeem dat gemakkelijk uit te breiden was en dat de overzichten automatisch zou kunnen maken. Ik begon eraan te werken ergens in de zomer van 2000.

De keuze voor XML was een beetje willekeurig. Er werd geopperd om een relationele database te gebruiken, maar ik had al ervaring met XML en ik voelde meer voor een tekst-gebaseerde oplossing. Al gauw bleek dat er een nieuw nummeringssysteem nodig was, zodat de database de nummers van de artikelen zou kunnen gebruiken als unieke ID. Voorheen werden er twee of zelfs drie nummeringen door elkaar gebruikt. Guido Socher deed al het werk voor die hernummering, wat niet gering was (bedankt Guido!).

Op dat punt was de Document Type Definition (DTD) van de database in ontwikkeling en hadden we ook al een beetje inhoud om te kunnen testen. Nu de nieuwe nummering klaar was, werd het tijd om de database te vullen met inhoud. Nadat ik zo'n twintig artikelen had ingevoerd, werd het duidelijk dat dit een enorm karwei zou worden. Ik had scripts kunnen schrijven die informatie haalden uit de oude bestanden, maar daar stond ook niet alles in en de informatie was veel te verspreid. Gelukkig begon Floris Lambrechts mee te werken, en ik moet hem bedanken voor het invoeren van het grootste deel van de database. Zonder zijn hulp zou het systeem nu niet zijn wat het is. (Noot van de vertaler: bedankt Egon, graag gedaan!)

Met het nieuwe formaat kregen we ook de mogelijkheid om nieuwe informatie toe te voegen. In het voorbije jaar zijn er dan ook verschillende soorten nieuwe gegevens ingevoegd in de database. Eerst maakten we een overzicht van auteurs, vertalers, redacteurs en andere mensen die meewerken aan LinuxFocus. Ook namen we de locaties van bestanden op. Deze laatste waren nodig omdat de plaats en de namen van de artikelen door de jaren heen meermaals veranderd waren. Na de hernummering bleven er nog twee systemen over. Sommige bestanden gebruiken server side includes (SSI) en hadden een .shtml-extensie, terwijl oudere artikelen op .html eindigden. Met de <file>-tag kunnen we nu een standaardwaarde overschijven. (Op dit moment is de standaard "article" + nummer van het artikel + ".shtml". Hier komt nog een eventuele ".meta" tussen in het geval dat het artikel in het door LinuxFocus gebruikte meta-formaat geschreven is.)

Nu de database gevuld werd, ging ik eindelijk de software testen die ik aan het schrijven was. De XSLT-stylesheets die we nu gebruiken waren namelijk niet de eerste implementatie. Ik had eerst Perl-code geschreven, maar naarmate de database groeide werd de snelheid daarvan onwerkbaar. Maar voordat we het over de software hebben, doe ik eerst het formaat van de database uit de doeken.

De Document Type Definition

Om te beginnen is XML een syntax-specificatie voor markup-talen. XML bepaalt hoe markup eruit hoort te zien. Het zegt dat een document één root-element (hoofd-element) moet hebben en dat dit root-element bestaat uit een begin-tag, inhoud (tekst, sub-elementen, of beide), en een eind-tag. Deze tags bestaan op hun beurt uit het teken "<" gevolgd door een naam en aan het eind het teken ">". Een eind-tag moet een "/" hebben direct voor de naam. Een begin-tag mag attributen bevatten, deze hebben ook weer hun eigen syntax. Je kan dus stellen dat de syntax bepaalt welke opeenvolging van karakters een geldig XML-document vormt. Een simpel XML-document kan er zo uitzien:

<begroeting>Hello, world!</begroeting>

Een taal bestaat niet alleen uit syntax, er is ook nog zoiets als semantiek. De semantiek van een taal beschrijft hoe de verschillende elementen met elkaar verbonden zijn. Om HTML als voorbeeld te nemen: de semantiek bepaalt dat <body> binnen het <html>-element moet staan en niet andersom. De semantiek zegt ook dat het <img>-element leeg is (het heeft wel attributen), net als <br>. Als de semantiek gegeven is in een formele notatie, dan kan je documenten parsen en valideren volgens die semantiek. Eén van die formele notaties heet Document Type Definition, of korter DTD. Alleen wanneer een document gevalideerd is, kan je het een geldig XML-document noemen.

Nu we weten wat een DTD is, gaan we de DTD voor de XML-database van LinuxFocus een keer bekijken. We geven bij de meeste elementen ook meteen een voorbeeld, waardoor je een goed idee krijgt over hoe de informatie er in de database uitziet.

<database>

Het root-element van onze XML-database, en ook van de uitbreidingen en vertalingen, is <database>.

<!ELEMENT database    (themes?, persons?, issues?, articles?)>
    

Merk allereerst op dat de "?" betekent dat het sub-element ("child element") 0 of 1 keer mag voorkomen. Onze LinuxFocus-databank kan dus informatie bevatten over thema's, personen, nummers (issues) en artikelen. Aangezien dit vrij duidelijk is, gaan we over naar het volgende element.

<themes>

De thema's staan in het element <themes>, dat een child-element is van <database>. Elk thema heeft een eigen ID, een titel, en optioneel ook een beschrijving (description) en een afbeelding (image).

<!ELEMENT themes      (theme+)>
  <!ELEMENT theme       (title*, desc?, img?)>
    <!ELEMENT title       (#PCDATA)>
    <!ELEMENT desc       (#PCDATA)>
    <!ELEMENT img         (EMPTY)>
    

Sommige van deze sub-elementen hebben verplichte attributen. Ook dit wordt aangegeven door de DTD. Tekstuele inhoud staat altijd in een element met het attribuut xml:lang. De waarde van dit attribuut moet overeenkomen met een landcode conform ISO-standaard 3166. Voorbeelden zijn "en", "fr" en "nl". Zowel het id- als het xml:lang-attribuut zijn beschreven in de oospronkelijke XML-specificatie en maken dus deel uit van de XML-syntax.

<!ATTLIST theme       id            ID            #REQUIRED>
<!ATTLIST title       xml:lang      NMTOKEN       #REQUIRED>
<!ATTLIST desc        xml:lang      NMTOKEN       #REQUIRED>
<!ATTLIST img         src           CDATA         #REQUIRED>
    

Een voorbeeld-database ziet er zo uit:

<database>
  <themes>
    <theme id="hw">
      <title xml:lang="en">Hardware</title>
      <img src="Hardware.jpg"/>
    <theme>
  <themes>
</database>
    

<issues>

De verschillende LF-nummers (issues) worden bijgehouden in het <issues>-element. Net zoals bij de themes heeft elk issue zijn eigen ID.

<!ELEMENT issues      (issue+)>
  <!ELEMENT issue       (title+, published?, file*)>
    <!ELEMENT title       (#PCDATA)>
    <!ELEMENT published   (EMPTY)>
    <!ELEMENT file        (#PCDATA)>
    

Het element <published> staat alleen bij gepubliceerde nummers. Issues zoals 'het volgende nummer' en 'onvertaalde Franse artikelen' hebben dit element dus niet. De <title> heeft ook hier het attribuut @xml:lang. Bij <file> wordt aangegeven in welke directory het element staat. Dit is geen link naar de index.html, want het wordt gebruikt om de bestandsnamen van de artikelen te genereren.

Een voorbeeld (merk op dat het attribuut @code wordt gebruikt om te kunnen sorteren):

    <issue id="ToBeWritten" code="999996">
      <title xml:lang="en">Not yet written articles</title>
    </issue>
    <issue id="September2001" code="200109">
      <title xml:lang="en">September2001</title>
      <published/>
      <current/>
    </issue>

<persons>

Hierin plaatsen we de informatie over auteurs en vertalers. Elke persoon heeft een uniek ID.

  <!ELEMENT persons     (person+)>
    <!ELEMENT person      ((name|email)*,(homepage|nickname|desc|team)*)>
      <!ELEMENT email       (#PCDATA)>
      <!ELEMENT name        (#PCDATA)>
      <!ELEMENT homepage    (#PCDATA)>
      <!ELEMENT nickname    (#PCDATA)>
      <!ELEMENT desc        (#PCDATA|%html-els;)*>
      <!ELEMENT team        EMPTY>
    

De volgende soorten informatie zijn mogelijk: een naam, een e-mailadres (of meerdere), homepagina('s) en roepnaam (nickname). Als de persoon ook deel uitmaakt van een vertaalteam, krijgt zij ook een <team>-element. Om bijvoorbeeld aan te geven dat Floris lid is van het Nederlandse vertaalteam, schrijven we het volgende in het element <person>: <team xml:lang="nl"/>. Tenslotte kan elke persoon ook een beschrijving hebben, waarin op zijn beurt ook weer hyperlinks kunnen staan.

Een voorbeeld:

    <person id="nl-ew">
      <name>Egon Willighagen</name>
      <email>egonw@linuxfocus.org</email>
      <team xml:lang="nl"/>
    </person>

<articles>

De artikelen vormen natuurlijk het hart van de databank.

  <!ELEMENT articles    (article+)>
    <!ELEMENT article     (title+, 
        (file|personref|abstract|issueref|themeref|
         nometa|nohtml|translation|proofread)*)>
      <!ELEMENT abstract    (#PCDATA)>
      <!ELEMENT nohtml      EMPTY>
      <!ELEMENT nometa      EMPTY>
      <!ELEMENT translation (personref*, (reserved|finished|proofread)*)>
        <!ELEMENT reserved    (#PCDATA)>
        <!ELEMENT finished    (#PCDATA)>
        <!ELEMENT proofread   (personref*, (reserved|finished)*)>
<!ATTLIST article     id            ID            #REQUIRED
                      xml:lang      NMTOKEN       #IMPLIED
                      type          (article|coverpage)
                                                  "article"
                      next          IDREF         #IMPLIED
                      prev          IDREF         #IMPLIED>
<!ATTLIST file        xml:lang      NMTOKEN       #REQUIRED
                      type          (target|meta) "target">
<!ATTLIST translation from          NMTOKEN       #REQUIRED
                      to            NMTOKEN       #REQUIRED>

    

Elk artikel heeft op zijn minst één titel; één voor elke taal. Het element <file> wordt gebruikt om de locatie van het bestand aan te geven, zowel voor de HTML- als voor de META-versie (zie het voorbeeld hieronder). Indien er geen META- of HTML-versie beschikbaar is, gebruiken we de optionele <nometa/> en <nohtml/>. Elk artikel kan zijn eigen abstract hebben; deze tekst verschijnt als beschrijving van het artikel in de automatisch aangemaakte pagina's.

Het element <article> kent vijf attributen: de verplichte ID, optioneel een xml:lang om aan te geven in welke taal het oorspronkelijk geschreven was en een type-attribuut voor inleidingen van nummers (die voor de handigheid ook gezien worden als artikelen). De laatste twee zijn dan next en prev, die gebruikt worden om aan te duiden welke de eventuele voorgaande en volgende artikelen in dezelfde serie zijn.

Een artikel wordt verbonden met zijn nummer en met een thema d.m.v. issue en theme, die elk een href-attribuut hebben. De waarde van dit attribuut is dan de ID van het nummer of thema in kwestie.

Een voorbeeld:

    <article id="article206" xml:lang="en">
      <title xml:lang="en">Using XML and XSLT to build 
        LinuxFocus.org(/Nederlands)</title>
      <personref href="nl-ew"/>
      <issueref href="ToBeWritten"/>
      <themeref href="appl"/>
      <abstract xml:lang="en">
This article shows you how parts of the Dutch web site of LinuxFocus are 
generated with XSLT tools from the XML database. It compares this with 
the (very) much slower DOM tools in Perl.
      </abstract>
    </article>

Een gelokaliseerd <article>-element ziet er zo uit:

    <article id="52">
      <title xml:lang="nl">Enlightenment</title>
      <file xml:lang="nl">Nederlands/July1998/article52.html</file>
      <translation from="en" to="nl">
        <personref href="nl-tu"/>
        <reserved>2000-09-06</reserved>
        <finished>2000-10-04</finished>
        <proofread>
          <personref href="nl-fl"/>
          <reserved>2000-10-04</reserved>
          <finished>2000-10-04</finished>
        </proofread>
      </translation>
      <abstract xml:lang="nl">
Enlightenment is een window manager voor Linux met uitgebreide mogelijkheden. 
Dit artikel bespreekt ze, samen met de installatie en de instelling van E. 
Dit alles is niet voor beginners daar E op het moment nog in bèta-stadium 
verkeert.
      </abstract>
    </article>

Merk op dat dit artikel gereserveerd is voor vertaling op een bepaalde datum, dat de vertaling afgewerkt is, en ook nagelezen (proofread). Telkens wordt de persoon die deze bepaalde taak gedaan heeft gelinkt met <personref>.

Voor alle elementen kun je waarschijnlijk het meeste leren in de database zelf:

Automagisch webpagina's maken

Zoals eerder gezegd, wilden we de database vooral gebruiken om er automatisch webpagina's mee te maken. Nu we het formaat van de database goed begrijpen (?) kunnen we dieper ingaan op het gebruik ervan.

Eerst een stukje geschiedenis. De eerste implementatie gebruikte Perl-modules om de data uit de database te halen. Alhoewel het een zeer elegante interface was, werkte het programma veel te traag. De informatie werd immers voorgesteld volgens het Document Object Model (DOM), en de meeste DOM-implementaties zijn nu eenmaal enorm traag. Veel trager in ieder geval dan hun alternatief, de Simple API for XML (SAX).

Maar als je enkel webpagina's gaat maken, blijkt een derde alternatief nog meer geschikt: XSLT. Dit is een transformatietaal, uitgedrukt in XML-syntax. Er bestaan al veel XSLT-processors en de meeste programmeertalen kunnen er ook mee overweg. Een tijdje terug hadden we hier in LinuxFocus een artikel over: Een introductie tot de Perl-module XML::XSLT. Sinds die tijd zijn er een paar nieuwe implementaties verschenen, waarvan ik de volgende kan aanbevelen:

Voor de rest van dit artikel gebruiken we Sablotron als voorbeeld.

Een XSLT-processor krijgt twee bestanden als invoer. Het ene is de XML-code die getransformeerd moet worden, het andere is de XSLT-stylesheet die de transformatie beschrijft. Voor het maken van de LinuxFocus-webpagina's hebben we de volgende stylesheets:

Merk op dat dit niet de laatste versies zijn van onze stylesheets! Neem contact op met mij of met één van de Nederlandse redacteurs om de laatste versies te krijgen. (Noot van de vertaler: of zoek ze via FTP op onze server.)

Om bijvoorbeeld de mainindex.html te maken, draaien wij (het /Nederlands-team):

sabcmd stylesheets/mainindex.xslt db/lfdb.nl.xml > ../mainindex.html

De stylesheets weten zelf waar de Engelse hoofd-database is, dus we geven enkel de gelokaliseerde database mee als invoer. Sommige stylesheets hebben nog een extra parameter nodig, bijvoorbeeld:

sabcmd stylesheets/theme.xslt db/lfdb.nl.xml '$theme=appl' > ../Themes/appl.html

De Nederlandse hoofdpagina, index.shtml, maken we ook uit de database, maar hier verloopt alles wat ingewikkelder. De index.html wordt eigenlijk gemaakt met Guido Socher's lfpagecomposer uit een reeks op voorhand bewerkte bestanden. Die bestanden worden gemaakt op basis van .pre-bestanden. Een paar voorbeelden? Een overzicht van alle nummers van LinuxFocus:

<H2>Vorige nummers</H2>
 
<ul>
<!-- macro xslt previssues -->
</ul>

Een lijst van de tien meest recent vertaalde artikelen:

<H2>Recent vertaalde artikelen</H2>
<!-- macro xslt recently_translated -->

Met andere woorden, de .pre-bestanden zijn gewoon stukjes HTML met een macro erin die een stylesheet toepast op de gelokaliseerde database. Het verwerken van de .pre-bestanden (en dus het uitvoeren van de macro's) gebeurt met een programma genaamd apply_stylesheets.pl. Dit programma zoekt naar <!-- macro xslt [stylesheet] -->-opdrachten en past dan die stylesheet toe op de database. Merk op dat de extensie .xslt weggelaten is. Onze Makefile bevat:

%.shtml: %.pre
        @echo "Making $*..."
        @../../xml/bin/apply_stylesheets.pl $*.pre

Het resultaat van deze bewerking is een reeks *.shtml-bestanden, waaruit lfpagecomposer dan de index.html maakt.

Lokaliseren

Om dit systeem ook voor andere LinuxFocus-talen te gebruiken, moet je twee dingen doen:

  1. lokaliseer de XML-database (naar het voorbeeld van lfdb.nl.xml)
  2. lokaliseer de stylesheets

Eigenlijk is het een beetje spijtig dat de tweede stap nodig is. In principe moet je enkel de tekst in de uitvoer veranderen, maar de stylesheets hebben daar op dit moment nog geen handige faciliteiten voor. Het is zeker mogelijk om deze alsnog in te bouwen, en dat zou ik dan ook graag zien gebeuren.

Ik raad je aan om gebruik te maken van een XML-editor die om kan met DTD's. Bijvoorbeeld: in Emacs is er de psgml major mode waarmee je documenten kunt valideren (met nsgmls). Zeer handig om fouten te voorkomen. In Emacs kan je dan ook klikken met de rechtermuisknop om een lijst te zien van alle elementen en attributen die je op dat punt kunt invoegen. (Dank gaat uit naar Jaime Villate voor zijn uitstekende presentatie op de LSM-conferentie in Bordeaux dit jaar.)

Een laatste hulpmiddel is de Nederlandse lokalisatie van de XML-database. Als je problemen tegenkomt, kun je altijd dat bestand raadplegen. Je kan erin zien hoe de verschillende elementen georganiseerd zijn. En als dat niet helpt, kun je me nog altijd e-mailen.

Het lokaliseren van de stylesheets is waarschijnlijk een beetje lastig. De tekst staat midden tussen XSLT-commando's. Die laatste moet je laten staan tenzij je goed weet wat je doet (anders krijg je problemen met de funcionaliteit). Ik ben van plan om in de toekomst een systeem te maken zodat je voor een lokalisatie slechts één bestand met vertalingen moet bewerken. Dan zouden alle stylesheets werken met deze vertalingen. Het klinkt mooi, maar het is verre van afgewerkt...

Toekomstplannen

Oké, dit zou je op weg moeten helpen. De meeste dingen kun je gewoon kopiëren uit de Nederlandse bestanden. Ze hebben allemaal FDL- en GPL-licentie. Voor het volgend jaar zijn dit mijn plannen voor het systeem: