XSLT / XPath / translate()
snow_white
- xsl
Hallo,
ich möchte gern mit der XPath-Funktion translate() Umlaute wie ö, ä, ü und ß in oe, ae, ue und ss umwandeln.
Allerdings funktioniert das nicht so wie ich es gern hätte.
Ich möchte ja nur einen Buchstaben in zwei umwandeln.
Die Funktion translate() wandelt aber nur einen Buchstaben in einen Buchstaben um.
Daher wird mein ö in ein o umgewandelt und nicht wie ich es gern hätte in ein oe.
Eine andere XPath-Funktion, die für mich geeignet wäre, habe ich nicht gefunden.
Kann ich hier irgendwie tricksen?
XSLT:
...
<xsl:if test="contains(@TEXT, 'Ö')">
<xsl:value-of select="translate(@TEXT, 'Ö', 'OE')"/>
</xsl:if>
<xsl:if test="contains(@TEXT, 'ß')">
<xsl:value-of select="translate(@TEXT, 'ß', 'ss' )"/>
</xsl:if>
...
Vielen Dank im voraus.
Viele Grüße
snow_white
Ich würde zu concat neigen:
<xsl:value-of select="translate(@TEXT, 'Ö', 'OE')"/>
<xsl:value-of select="concat(translate(@TEXT, 'Ö', 'O'), 'E')"/>
(ungetestet)
Siechfred
Noch was:
<xsl:value-of select="concat(translate(@TEXT, 'Ö', 'O'), 'E')"/>
Wenn es funktioniert, ließe es sich für die Umlaute auch vereinfachen:
<xsl:value-of select="concat(translate(@TEXT, 'ÄÖÜ', 'AOU'), 'E')"/>
Ansonsten habe ich noch das hier gefunden:
http://aspn.activestate.com/ASPN/Cookbook/XSLT/Recipe/67667
Übrigens, in XSLT 2.0 kannst du wohl auch reguläre Ausdrücke verwenden:
http://www.w3.org/TR/2004/WD-xpath-functions-20041029/#func-replace
Siechfred
Hallo Siechfred,
vielen Dank!
Es hat funktioniert. :o)
Viele Grüße
snow_white
Hallo Siechfred,
danke noch einmal. Leider funktioniert das doch nicht so richtig.
<xsl:value-of select="concat(translate(@TEXT, 'Ö', 'O'), 'E')"/>
Es wird zwar Ö durch O im String ersetzt, allerdings wird das E einfach hinten an das Wort drangehängt und nicht hinter das O direkt.
Also:
Öl wird zu Ole.
Aber vielleicht bringt mich die Funktion replace() weiter.
Viele Grüße
snow_white
<xsl:value-of select="concat(translate(@TEXT, 'Ö', 'O'), 'E')"/>
Es wird zwar Ö durch O im String ersetzt, allerdings wird das E einfach hinten an das Wort drangehängt und nicht hinter das O direkt.
Urgs. Ich weiß nicht, inwieweit du concat im Ersetzungsteil von translate unterbringen kannst, das wäre noch eine Möglichkeit. Ansonsten müsstest du irgendwas mit den Stringfunktionen basteln (so, wie im XSL-Cookbook, das ich verlinkt hatte).
Aber vielleicht bringt mich die Funktion replace() weiter.
Ja, wenn dir XSLT 2.0 zur Verfügung steht.
Siechfred
Hallo,
danke noch einmal. Leider funktioniert das doch nicht so richtig.
<xsl:value-of select="concat(translate(@TEXT, 'Ö', 'O'), 'E')"/>
Es wird zwar Ö durch O im String ersetzt, allerdings wird das E einfach hinten an das Wort drangehängt und nicht hinter das O direkt.
Also:
Öl wird zu Ole.Aber vielleicht bringt mich die Funktion replace() weiter.
Also für XSLT 1.0 ist es echt ein Problem, da kann man so etwas nur mit einer zweifachen Rekursion lösen:
-------------------------
<xsl:template name="replace">
<xsl:param name="text"/>
<xsl:param name="searchstring"/>
<xsl:param name="replacestring"/>
<xsl:variable name="textbefore" select="substring-before($text, $searchstring)"/>
<xsl:variable name="textafter" select="substring-after($text, $searchstring)"/>
xsl:choose
<xsl:when test="contains($text, $searchstring)">
<xsl:call-template name="replace">
<xsl:with-param name="searchstring" select="$searchstring"/>
<xsl:with-param name="replacestring" select="$replacestring"/>
<xsl:with-param name="text" select="concat($textbefore, $replacestring, $textafter)"/>
</xsl:call-template>
</xsl:when>
xsl:otherwise
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
-------------------------
Dies würde schon mal alle _gleichen_ Zeichen ersetzen
searchstring = ö
replacestring = oe
Jetzt müsstest du aber dieses Template wieder für jedes andere Zeichen aufrufen: d.h. du müsstest nochmal ein Template schreiben, dass mit
wehen test="constains($text, 'Ö') or constains($text, 'ä') ...." sich selbst aufruft und bei jedem Aufruf das "replace"-Template aufruft.
Mit XSLT 2.0 ist es auch nicht so einfach. Falls du mit replace() nicht klar kommst. (bin ich bei den Umlauten auch nicht und es war schneller was anderes zu schreiben als ewig zu Experimentieren)
Das " | " ist hier der Trenner zwischen den zu ersetzenden Zeichen.
Die Zahl der Zeichen im zeichen1 kann höher sein als im zeichen2, dann werden die Zeichen für die es keine Übereinstimmung gibt duch "nichts" ersetzt (sprich gelöscht).
---------------------
<xsl:template name="sonderzeichen">
<xsl:param name="text"/>
<xsl:param name="counter" select="0" />
<xsl:variable name="zeichen1">ä|Ä|ö|Ö|ü|Ü|ß|é</xsl:variable>
<xsl:variable name="zeichen2">ae|Ae|oe|Oe|ue|Ue|ss|e</xsl:variable>
<xsl:variable name="t1" select="tokenize($zeichen1, '|')"/>
<xsl:variable name="t2" select="tokenize($zeichen2, '|')"/>
xsl:choose
<xsl:when test="$counter < count($t1)">
xsl:choose
<xsl:when test="contains($text, $t1[$counter+1])">
<xsl:variable name="replacetext">
<xsl:call-template name="replace">
<xsl:with-param name="searchstring" select="$t1[$counter+1]" />
<xsl:with-param name="replacestring" select="$t2[$counter+1]" />
<xsl:with-param name="text" select="$text" />
</xsl:call-template>
</xsl:variable>
<xsl:call-template name="sonderzeichen">
<xsl:with-param name="text" select="$replacetext" />
<xsl:with-param name="counter" select="$counter+1"></xsl:with-param>
</xsl:call-template>
</xsl:when>
xsl:otherwise
<xsl:call-template name="sonderzeichen">
<xsl:with-param name="text" select="$text" />
<xsl:with-param name="counter" select="$counter+1"></xsl:with-param>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
xsl:otherwise
<xsl:sequence select="$text" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="replace">
<xsl:param name="text"/>
<xsl:param name="searchstring" />
<xsl:param name="replacestring" />
<xsl:variable name="textbefore" select="substring-before($text, $searchstring)" />
<xsl:variable name="textafter" select="substring-after($text, $searchstring)" />
xsl:choose
<xsl:when test="contains($text, $searchstring)">
<xsl:call-template name="replace">
<xsl:with-param name="searchstring" select="$searchstring" />
<xsl:with-param name="replacestring" select="$replacestring" />
<xsl:with-param name="text" select="concat($textbefore, $replacestring, $textafter)" />
</xsl:call-template>
</xsl:when>
xsl:otherwise
<xsl:sequence select="$text" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
---------------------------
Aufruf:
<xsl:call-template name="sonderzeichen">
<xsl:with-param name="text" select=" Knoten " />
</xsl:call-template>
Grüße
Thomas
Hallo zusammen,
Mit XSLT 2.0 ist es auch nicht so einfach. Falls du mit replace() nicht klar kommst.
Ich versuch's mal. Disclaimer: Ich hab keine Ahnung von XSLT 2 und probiert einfach nur mal meine Lösungsvorstellung mit bestehenden Elementen zu realisieren:
<?xml version="1.0"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="tag:tepasse.org,2007-06-19:xslt/root"
xmlns:my="tag:tepasse.org,2007-06-19:xslt/my"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all">
<xsl:output encoding="UTF-8"
indent="yes"
undeclare-prefixes="yes"
method="xml" />
<xsl:param name="text">
Diés ist ein Töxt mit Ümläuten und scharfem Eß. Öder ätwa nücht?
</xsl:param>
<xsl:function name="my:translate">
<xsl:param name="text" as="xs:string"/>
<xsl:param name="mappings" as="xs:string"/>
<xsl:param name="alternates" as="xs:string"/>
<xsl:variable name="prestrings"
select="tokenize($mappings, '\s+')"/>
<xsl:variable name="poststrings"
select="tokenize($alternates, '\s+')"/>
<xsl:variable name="length"
select="min((count($prestrings),
count($poststrings)))"/>
<xsl:variable name="newtext"
select="
if ($length > 1)
then
my:translate($text,
string-join($prestrings[position() = (2 to $length)], ' '),
string-join($poststrings[position() = (2 to $length)], ' '))
else
$text"/>
<xsl:sequence select="replace($newtext,
$prestrings[1],
$poststrings[1])"/>
</xsl:function>
<xsl:template match="/">
<test>
<xsl:value-of select="my:translate($text,
'ä ö ü Ä Ö Ü ß é',
'ae oe ue Ae Oe Ue ss e')"/>
</test>
</xsl:template>
</xsl:stylesheet>
Funktioniert in Saxon. Etwas umständlich, ja, insbesondere das Tokenisieren und Zusammenfriemeln, aber mir macht es mehr Spaß als Deine Lösung, Thomas. ;)
(Und ausserdem war ich vorhin nur hier gelandet um kundzutun, dass man sicherlicher im Notfall eine sehr quicke und sehr dirrrrty Lösung mit xsl:character-map/ machen könnte, vorausgesetzt das Ziel-Markup käme einem nicht in die Quere. Aber nicht sehr empfehlenswert.)
Tim
Hallo Tim,
Mit XSLT 2.0 ist es auch nicht so einfach. Falls du mit replace() nicht klar kommst.
Ich versuch's mal. Disclaimer: Ich hab keine Ahnung von XSLT 2 und probiert einfach nur mal meine Lösungsvorstellung mit bestehenden Elementen zu realisieren:
Funktioniert in Saxon. Etwas umständlich, ja, insbesondere das Tokenisieren und Zusammenfriemeln, aber mir macht es mehr Spaß als Deine Lösung, Thomas. ;)
Ja, sie ist eine schöne Lösung!
Ich habe ja von meiner auch nicht gesagt, dass sie schön sei (das war damals wirklich etwa '1 min quick&dirty hack' aus schon fertigen Conde-Teilen und ich hatte sie hier noch herumliegen) ;-)
(Und ausserdem war ich vorhin nur hier gelandet um kundzutun, dass man sicherlicher im Notfall eine sehr quicke und sehr dirrrrty Lösung mit xsl:character-map/ machen könnte, vorausgesetzt das Ziel-Markup käme einem nicht in die Quere. Aber nicht sehr empfehlenswert.)
So qick and ditry wäre das überhaupt nicht:
------
<?xml version="1.0"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="UTF-8" indent="yes" method="xml" use-character-maps="sonderzeichen"/>
<xsl:param name="text"> Diés ist ein Töxt mit Ümläuten und scharfem Eß. Öder ätwa nücht? </xsl:param>
<xsl:template match="/">
<test>
<xsl:value-of select="$text"></xsl:value-of>
</test>
</xsl:template>
<xsl:character-map name="sonderzeichen">
<xsl:output-character character="ö" string="oe"/>
<xsl:output-character character="Ö" string="Oe"/>
<xsl:output-character character="ä" string="ae"/>
<xsl:output-character character="Ä" string="Ae"/>
<xsl:output-character character="ü" string="ue"/>
<xsl:output-character character="Ü" string="Ue"/>
<xsl:output-character character="ß" string="ss"/>
<xsl:output-character character="é" string="e"/>
</xsl:character-map>
</xsl:stylesheet>
aber, hätte den Nachteil, dass so die Ersetzung überall im Dokument stattfindet, was ja nicht unbedingt gewünscht ist (z.b. man braucht die Ersetzung nur bei der generierung von Dateinamen, nicht aber dann in der generierten Datei selbst.)
Grüße
Thomas