Personal tools
You are here: Home Membres jordinas's Home Plone i18n zpt2pot.xsl
Document Actions

zpt2pot.xsl

Version 1.5 of my XSLT transformation for extract translatable strings in Zope Page Templates.

Click here to get the file

Size 22.8 kB - File type text/xml

File contents

<?xml version="1.0" encoding="iso-8859-1"?>
<!-- $Id: zpt2pot.xsl,v 1.5 2005/07/14 17:19:50 joan Exp $ -->

<!--====================================================================
 !	zpt2pot.xsl - A gettext extractor for (one) Zope Page Templates
 !
 !	Requeriments:
 !		The input template must be well-formed XML, and declare all the
 !		used namespaces. An example of a correct root element is this:
 !			<html	xmlns="http://www.w3.org/1999/xhtml"
 !					xmlns:tal="http://xml.zope.org/namespaces/tal"
 !					xmlns:metal="http://xml.zope.org/namespaces/metal"
 !					xmlns:i18n="http://xml.zope.org/namespaces/i18n"
 !					i18n:domain="plone"
 !					xml:lang="en"
 !					lang="en"
 !			>
 !
 !		The attribute 'i18n:domain', if present, must be in the root
 !		element, and is ignored in other elements.
 !
 !		The attributes 'i18n:data', 'i18n:source' and 'i18n:target' are
 !		not yet supported.
 !
 !		The 'split' function extension in the 'http://exslt.org/strings'
 !		module is used.
 !
 ! 	Author:
 !		Joan Ordinas <jordinas at gmail dot com>
 !
 !	License:
 !
 !	The source of this software is public domain and provided here "as is". I
 !	don't feel like commenting it more but if you have problems then feel free to
 !	ask me. If you derive your own software from the source or put a part of the
 !	source into your own software, please, give me a credit and send a copy to me.
 !
 !	The author accepts no liability for any damage or data loss caused by this
 !	software.
 !===================================================================-->

<!--
 !	New line character(s):
 !		Redefine as "&#13;&#10;" (MSDOS) or "&#13;" (Mac) if you want.
 !-->
<!DOCTYPE xsl:transform [
	<!ENTITY NL "&#10;">
]>

<!--
 !	Extra namespaces used:
 !		'i18n' is for catch i18n attributes in the input template.
 !		'data' is for local data located at the end of the file.
 !		'str' is for the string module extension.
 !-->
<xsl:transform
	version="1.0"
	xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
	xmlns:i18n="http://xml.zope.org/namespaces/i18n"
	xmlns:str="http://exslt.org/strings"
	xmlns:data="uri:xsl:zpt:pot:zpt2pot.xsl"
>

<!--
 !	Character encoding:
 !		If you change the original 'iso-8859-1' you must change also
 !		the 'Content-Type' definition in the template 'pot-headers'.
 !-->
<xsl:output
	method="text"
	encoding="iso-8859-1"
/>

<!--====================================================================
 !	Input parameters and global variables
 !===================================================================-->

<!--
 !	To be passed from environment to the processor
 !-->
<xsl:param name="creation-date" select="'YYYY-MM-DD HH:MM:SS+0000'"/>
<xsl:param name="input-filename" select="'stdin'"/>
<xsl:param name="on-errors-terminate" select="'yes'"/>
<xsl:param name="emit-warnings" select="'yes'"/>
<xsl:param name="user-domain" select="''"/>

<!--
 !	Local data
 !-->
<xsl:variable name="DATA" select="document('')/*/data:data"/>

<!--
 !	Constants extracted from input template
 !-->
<xsl:variable name="domain">
	<xsl:choose>
		<xsl:when test="$user-domain != ''"><xsl:value-of select="$user-domain"/></xsl:when>
		<xsl:when test="/*/@i18n:domain"><xsl:value-of select="/*/@i18n:domain"/></xsl:when>
		<!-- default to 'plone' -->
		<xsl:otherwise>plone</xsl:otherwise>
	</xsl:choose>
</xsl:variable>

<xsl:variable name="lang">
	<xsl:choose>
		<xsl:when test="/*/@lang"><xsl:value-of select="/*/@lang"/></xsl:when>
		<xsl:when test="/*/@xml:lang"><xsl:value-of select="/*/@xml:lang"/></xsl:when>
		<!-- default to 'en' -->
		<xsl:otherwise>en</xsl:otherwise>
	</xsl:choose>
</xsl:variable>

<!--====================================================================
 !	Utilities
 !===================================================================-->

<!-- Print a warning -->
<xsl:template name="warn">
	<xsl:param name="sMSG"/>

	<xsl:if test="$emit-warnings = 'yes'">
		<xsl:message terminate="no">
			<xsl:value-of select="concat('warning(', $input-filename, '): ')"/>
			<xsl:value-of select="normalize-space($sMSG)"/>
		</xsl:message>
	</xsl:if>
</xsl:template>

<!-- Print an error message and exit -->
<xsl:template name="error">
	<xsl:param name="sMSG" select="'Unknown error'"/>

	<xsl:choose>
		<xsl:when test="$on-errors-terminate = 'no'">
			<xsl:message terminate="no">
				<xsl:value-of select="concat('error(', $input-filename, '): ')"/>
				<xsl:value-of select="normalize-space($sMSG)"/>
			</xsl:message>
		</xsl:when>
		<xsl:when test="$on-errors-terminate = 'yes'">
			<xsl:message terminate="yes">
				<xsl:value-of select="concat('error(', $input-filename, '): ')"/>
				<xsl:value-of select="normalize-space($sMSG)"/>
			</xsl:message>
		</xsl:when>
		<xsl:otherwise>
			<xsl:message terminate="yes">
				<xsl:text>error: unexpected 'on-errors-terminate' value:</xsl:text>
				<xsl:value-of select="$on-errors-terminate"/>
			</xsl:message>
		</xsl:otherwise>
	</xsl:choose>
</xsl:template>

<!-- Output an entry -->
<xsl:template name="print-entry">
	<xsl:param name="sMsgid"/>
	<xsl:param name="sMsgstr"/>

	<!-- Generate entry (if necessary) -->
	<xsl:if test="$sMsgid != '' and $sMsgid != '&#160;' ">
		<xsl:text>msgid "</xsl:text>
		<xsl:value-of select="$sMsgid"/>
		<xsl:text>"&NL;msgstr "</xsl:text>
		<xsl:value-of select="$sMsgstr"/>
		<xsl:text>"&NL;&NL;</xsl:text>
	</xsl:if>
</xsl:template>

<!--====================================================================
 !	Main template
 !===================================================================-->

<xsl:template match="/">
	<!-- Detect unsupported attributes -->
	<xsl:if test="count(//@i18n:data | //@i18n:source | //@i18n:target) != 0">
		<xsl:call-template name="warn"><xsl:with-param name="sMSG">
			Unsupported i18n attributes detected
		</xsl:with-param></xsl:call-template>
	</xsl:if>
	<!-- Detect multiple domains -->
	<xsl:if test="count(//@i18n:domain)  > 1">
		<xsl:call-template name="warn"><xsl:with-param name="sMSG">
			Detected more than one i18n:domain attribute
		</xsl:with-param></xsl:call-template>
	</xsl:if>

	<!-- Generate headers for POT file -->
	<xsl:call-template name="pot-headers"/>

	<!-- Generate filename special comment -->
	<xsl:text>#: </xsl:text>
	<xsl:value-of select="$input-filename"/>
	<xsl:text>&NL;</xsl:text>

	<!-- Process input recursing over elements -->
	<xsl:apply-templates select="*"/>

	<!-- End of POT file -->
	<xsl:text># vim:syntax=po&NL;</xsl:text>
</xsl:template>

<xsl:template match="*">
	<!-- Follow the tree (only elements) -->
	<xsl:apply-templates select="*"/>
</xsl:template>

<!--====================================================================
 !	Enter and follow the 'translating' mode
 !===================================================================-->
 
<xsl:template match="*[@i18n:translate]" name="translate">
	<!-- Obtain and normalize text to translate -->
	<xsl:variable name="text">
		<!-- Follow the tree in 'translating' mode (only elements and text) -->
		<xsl:apply-templates select="*|text()" mode="translating"/>
	</xsl:variable>
	<xsl:variable name="msgstr" select="normalize-space($text)"/>

	<!-- Obtain msgid -->
	<xsl:variable name="msgid">
		<xsl:choose>
			<xsl:when test="@i18n:translate = ''">
				<xsl:value-of select="$msgstr"/>
			</xsl:when>
			<xsl:otherwise>
				<xsl:value-of select="@i18n:translate"/>
			</xsl:otherwise>
		</xsl:choose>
	</xsl:variable>

	<!-- Generate entry -->
	<xsl:call-template name="print-entry">
		<xsl:with-param name="sMsgid" select="$msgid"/>
		<xsl:with-param name="sMsgstr" select="$msgstr"/>
	</xsl:call-template>

	<!-- Follow other elements in normal mode -->
	<xsl:apply-templates select="*"/>
</xsl:template>

<xsl:template match="*" mode="translating">
	<!-- Render element -->
	<xsl:text>&lt;</xsl:text>
	<xsl:value-of select="name(.)"/>
	<xsl:for-each select="@*">
		<xsl:text> </xsl:text>
		<xsl:value-of select="name(.)"/>
		<xsl:text>='</xsl:text>
		<xsl:value-of disable-output-scaping="no" select="."/>
		<xsl:text>'</xsl:text>
	</xsl:for-each>
	<xsl:text>&gt;</xsl:text>
		<!-- Follow the tree in 'translating' mode (only elements and text) -->
		<xsl:apply-templates select="*|text()" mode="translating"/>
	<xsl:text>&lt;/</xsl:text>
	<xsl:value-of select="name(.)"/>
	<xsl:text>&gt;</xsl:text>
</xsl:template>

<xsl:template match="*[@i18n:name]" mode="translating">
	<!-- Generate ${name} macro -->
	<xsl:text>${</xsl:text>
	<xsl:value-of select="@i18n:name"/>
	<xsl:text>}</xsl:text>
</xsl:template>

<xsl:template match="text()" mode="translating">
	<!-- Quote '"' in text -->
	<xsl:for-each select="str:split(., '&quot;')">
		<xsl:value-of select="."/>
		<xsl:if test="position() &lt; last()">
			<xsl:text>&amp;quot;</xsl:text>
		</xsl:if>
	</xsl:for-each>
</xsl:template>

<!--
 !	Catch anomalous situations
 !-->

<xsl:template match="*[@i18n:translate and @i18n:name]" priority="1.0">
	<xsl:call-template name="warn"><xsl:with-param name="sMSG">
		Detected combination of 'i18n:translate' and 'i18n:name' attributes
	</xsl:with-param></xsl:call-template>
	<!-- Anyway do the work... -->
	<xsl:call-template name="translate"/>
</xsl:template>

<xsl:template match="*[@i18n:translate and not(@i18n:name)]" mode="translating">
	<xsl:call-template name="error"><xsl:with-param name="sMSG">
		Attribute 'i18n:translate' cannot nest as a child
	</xsl:with-param></xsl:call-template>
</xsl:template>

<xsl:template match="*[@i18n:name and not(ancestor::*/@i18n:translate)]" priority="1.0">
	<xsl:call-template name="error"><xsl:with-param name="sMSG">
		Element with attribute 'i18n:name' must be child of element
		with 'i18n:translate' attribute
	</xsl:with-param></xsl:call-template>
</xsl:template>

<xsl:template match="*[@i18n:attributes]" priority="1.0" mode="translating">
	<xsl:call-template name="error"><xsl:with-param name="sMSG">
		Element with attribute 'i18n:attributes' cannot be a child
		of element with 'i18n:translate' attribute
	</xsl:with-param></xsl:call-template>
</xsl:template>

<xsl:template match="*[@i18n:attributes and @i18n:attributes = '']" priority="1.0">
	<xsl:call-template name="error"><xsl:with-param name="sMSG">
		Expected attribute name(s)
	</xsl:with-param></xsl:call-template>
</xsl:template>

<!--
 !	Follow elements with the 'i18n:attributes' attribute
 !-->

<xsl:template match="*[@i18n:attributes]" name="attributes">
	<!-- Each case deserves a template... -->
	<xsl:choose>
		<!-- case 'a x; b y ...' -->
		<xsl:when test="contains(@i18n:attributes, ';')">
			<xsl:call-template name="attributes-with-msgid">
				<xsl:with-param name="nsPairs" select="str:split(@i18n:attributes, ';')"/>
				<xsl:with-param name="nsAttrset" select="@*"/>
			</xsl:call-template>
		</xsl:when>
		<!-- case 'a b c ...' -->
		<!-- case 'a b' -->
		<!-- case 'a x' ??? -->
		<xsl:when test="contains(@i18n:attributes, ' ')">
			<xsl:variable name="nsTokens" select="str:split(@i18n:attributes, ' ')"/>
			<xsl:variable name="nTokens" select="count($nsTokens)"/>
			<xsl:choose>
				<!-- 3 or more attribute names -->
				<xsl:when test="$nTokens &gt; 2">
					<xsl:call-template name="attribute-list">
						<xsl:with-param name="nsAttrnames" select="$nsTokens"/>
						<xsl:with-param name="nsAttrset" select="@*"/>
					</xsl:call-template>
				</xsl:when>
				<!-- 2 tokens and nsTokens[2] is not an attribute (is a msgid) -->
				<xsl:when test="not(@*[name()=$nsTokens[2]])">
					<xsl:call-template name="attribute-single">
						<xsl:with-param name="sAttrname" select="$nsTokens[1]"/>
						<xsl:with-param name="sId" select="$nsTokens[2]"/>
						<xsl:with-param name="nsAttrset" select="@*"/>
					</xsl:call-template>
				</xsl:when>
				<!-- 2 attribute names -->
				<xsl:otherwise>
					<xsl:call-template name="attribute-list">
						<xsl:with-param name="nsAttrnames" select="$nsTokens"/>
						<xsl:with-param name="nsAttrset" select="@*"/>
					</xsl:call-template>
				</xsl:otherwise>
			</xsl:choose>
		</xsl:when>
		<!-- case 'a' -->
		<xsl:otherwise>
			<!-- 1 attribute name -->
			<xsl:call-template name="attribute-single">
				<xsl:with-param name="sAttrname" select="@i18n:attributes"/>
				<xsl:with-param name="nsAttrset" select="@*"/>
			</xsl:call-template>
		</xsl:otherwise>
	</xsl:choose>

	<!-- Follow other elements -->
	<xsl:apply-templates select="*"/>
</xsl:template>

<!-- Iterate pairs attribute-name/msgid -->
<xsl:template name="attributes-with-msgid">
	<xsl:param name="nsPairs"/>
	<xsl:param name="nsAttrset"/>

	<xsl:for-each select="$nsPairs">
		<xsl:variable name="nsPair" select="str:split(., ' ')"/>
		<xsl:call-template name="attribute-single">
			<xsl:with-param name="sAttrname" select="$nsPair[1]"/>
			<xsl:with-param name="sId">
				<xsl:choose>
					<xsl:when test="count($nsPair) = 1">
						<xsl:value-of select="''"/>
					</xsl:when>
					<xsl:otherwise>
						<xsl:value-of select="$nsPair[2]"/>
					</xsl:otherwise>
				</xsl:choose>
			</xsl:with-param>
			<xsl:with-param name="nsAttrset" select="$nsAttrset"/>
		</xsl:call-template>
	</xsl:for-each>
</xsl:template>

<!-- Iterate attribute names -->
<xsl:template name="attribute-list">
	<xsl:param name="nsAttrnames"/>
	<xsl:param name="nsAttrset"/>

	<xsl:for-each select="$nsAttrnames">
		<xsl:call-template name="attribute-single">
			<xsl:with-param name="sAttrname" select="."/>
			<xsl:with-param name="nsAttrset" select="$nsAttrset"/>
		</xsl:call-template>
	</xsl:for-each>
</xsl:template>

<!-- Generate an entry -->
<xsl:template name="attribute-single">
	<xsl:param name="sAttrname"/>
	<xsl:param name="nsAttrset"/>
	<xsl:param name="sId" select="''"/>

	<xsl:variable name="text" select="string($nsAttrset[name()=$sAttrname])"/>

	<!-- Check for attribute presence -->
	<xsl:if test="not($text)">
		<xsl:call-template name="warn"><xsl:with-param name="sMSG">
			Attribute '<xsl:value-of select="$sAttrname"/>' without text to translate
		</xsl:with-param></xsl:call-template>
	</xsl:if>

	<!-- Generate the entry -->
	<xsl:call-template name="print-entry">
		<xsl:with-param name="sMsgid">
			<xsl:choose>
				<xsl:when test="$sId = ''"><xsl:value-of select="$text"/></xsl:when>
				<xsl:otherwise><xsl:value-of select="$sId"/></xsl:otherwise>
			</xsl:choose>
		</xsl:with-param>
		<xsl:with-param name="sMsgstr" select="$text"/>
	</xsl:call-template>
</xsl:template>

<!--
 !	Catch elements with the 'i18n:attributes' an 'i18n:translate' attribute
 !-->

 <xsl:template match="*[@i18n:attributes and @i18n:translate]" priority="1.0">
	<xsl:call-template name="translate"/>
	<xsl:call-template name="attributes"/>
</xsl:template>

<!--====================================================================
 !	Generate POT headers
 !===================================================================-->

<xsl:template name="pot-headers">

	<xsl:text># Gettext Message File for </xsl:text>
	<xsl:value-of select="$domain"/>
	<xsl:text>&NL;</xsl:text>

	<xsl:text>msgid ""&NL;</xsl:text>
	<xsl:text>msgstr ""&NL;</xsl:text>

	<xsl:text>"Project-Id-Version: </xsl:text>
	<xsl:value-of select="$domain"/>
	<xsl:text>\n"&NL;</xsl:text>

	<xsl:text>"POT-Creation-Date: </xsl:text>
	<xsl:value-of select="$creation-date"/>
	<xsl:text>\n"&NL;</xsl:text>

	<xsl:text>"PO-Revision-Date: \n"&NL;</xsl:text>
	<xsl:text>"Last-Translator:  &lt;@&gt;\n"&NL;</xsl:text>
	<xsl:text>"Language-Team: &lt;@&gt;\n"&NL;</xsl:text>
	<xsl:text>"MIME-Version: 1.0\n"&NL;</xsl:text>
	<xsl:text>"Content-Type: text/plain; charset=iso-8859-1\n"&NL;</xsl:text>
	<xsl:text>"Content-Transfer-Encoding: 8bit\n"&NL;</xsl:text>
	<xsl:text>"Plural-Forms: nplurals=1; plural=0;\n"&NL;</xsl:text>

	<xsl:text>"Language-code: </xsl:text>
	<xsl:value-of select="$lang"/>
	<xsl:text>\n"&NL;</xsl:text>

	<xsl:text>"Language-name: </xsl:text>
	<xsl:value-of select="$DATA/data:code[@lang=$lang]"/>
	<xsl:text>\n"&NL;</xsl:text>
	
	<xsl:text>"Preferred-encodings: latin1 utf-8\n"&NL;</xsl:text>

	<xsl:text>"Domain: </xsl:text>
	<xsl:value-of select="$domain"/>
	<xsl:text>\n"&NL;</xsl:text>

	<xsl:text>"X-Is-Fallback-For: </xsl:text>
	<xsl:value-of select="$DATA/data:fallback[@lang=$lang]"/>
	<xsl:text>\n"&NL;</xsl:text>

	<xsl:text>&NL;</xsl:text>

</xsl:template>

<!--====================================================================
 !	Local data
 !===================================================================-->

<data:data>
	<!-- Language codes -->
	<data:code lang="aa">Afar</data:code>
	<data:code lang="ab">Abkhazian</data:code>
	<data:code lang="af">Afrikaans</data:code>
	<data:code lang="am">Amharic</data:code>
	<data:code lang="ar">Arabic</data:code>
	<data:code lang="as">Assamese</data:code>
	<data:code lang="ay">Aymara</data:code>
	<data:code lang="az">Azerbaijani</data:code>
	<data:code lang="ba">Bashkir</data:code>
	<data:code lang="be">Belarusian</data:code>
	<data:code lang="bg">Bulgarian</data:code>
	<data:code lang="bh">Bihari</data:code>
	<data:code lang="bi">Bislama</data:code>
	<data:code lang="bn">Bengali</data:code>
	<data:code lang="bo">Tibetan</data:code>
	<data:code lang="br">Breton</data:code>
	<data:code lang="ca">Catalan</data:code>
	<data:code lang="co">Corsican</data:code>
	<data:code lang="cs">Czech</data:code>
	<data:code lang="cy">Welsh</data:code>
	<data:code lang="da">Dansk</data:code>
	<data:code lang="de">German</data:code>
	<data:code lang="dz">Bhutani</data:code>
	<data:code lang="el">Greek</data:code>
	<data:code lang="en">English</data:code>
	<data:code lang="eo">Esperanto</data:code>
	<data:code lang="es">Spanish</data:code>
	<data:code lang="et">Estonian</data:code>
	<data:code lang="eu">Basque</data:code>
	<data:code lang="fa">Farsi</data:code>
	<data:code lang="fi">Finnish</data:code>
	<data:code lang="fj">Fiji</data:code>
	<data:code lang="fo">Faeroese</data:code>
	<data:code lang="fr">French</data:code>
	<data:code lang="fy">Frisian</data:code>
	<data:code lang="ga">Irish</data:code>
	<data:code lang="gd">Gaelic</data:code>
	<data:code lang="gl">Galician</data:code>
	<data:code lang="gn">Guarani</data:code>
	<data:code lang="gu">Gujarati</data:code>
	<data:code lang="ha">Hausa</data:code>
	<data:code lang="he">Hebrew</data:code>
	<data:code lang="hi">Hindi</data:code>
	<data:code lang="hr">Croatian</data:code>
	<data:code lang="hu">Hungarian</data:code>
	<data:code lang="hy">Armenian</data:code>
	<data:code lang="ia">Interlingua</data:code>
	<data:code lang="id">Indonesian</data:code>
	<data:code lang="ie">Interlingue</data:code>
	<data:code lang="ik">Inupiak</data:code>
	<data:code lang="is">Icelandic</data:code>
	<data:code lang="it">Italian</data:code>
	<data:code lang="iu">Inuktitut</data:code>
	<data:code lang="ja">Japanese</data:code>
	<data:code lang="jw">Javanese</data:code>
	<data:code lang="ka">Georgian</data:code>
	<data:code lang="kk">Kazakh</data:code>
	<data:code lang="kl">Greenlandic</data:code>
	<data:code lang="km">Cambodian</data:code>
	<data:code lang="kn">Kannada</data:code>
	<data:code lang="ko">Korean</data:code>
	<data:code lang="ks">Kashmiri</data:code>
	<data:code lang="ku">Kurdish</data:code>
	<data:code lang="ky">Kirghiz</data:code>
	<data:code lang="la">Latin</data:code>
	<data:code lang="ln">Lingala</data:code>
	<data:code lang="lo">Laothian</data:code>
	<data:code lang="lt">Lituanian</data:code>
	<data:code lang="lv">Latvian</data:code>
	<data:code lang="mg">Malagasy</data:code>
	<data:code lang="mi">Maori</data:code>
	<data:code lang="mk">Macedonian</data:code>
	<data:code lang="ml">Malayalam</data:code>
	<data:code lang="mn">Mongolian</data:code>
	<data:code lang="mo">Moldavian</data:code>
	<data:code lang="mr">Marathi</data:code>
	<data:code lang="ms">Malay</data:code>
	<data:code lang="mt">Maltese</data:code>
	<data:code lang="my">Burmese</data:code>
	<data:code lang="na">Nauru</data:code>
	<data:code lang="ne">Nepali</data:code>
	<data:code lang="nl">Dutch</data:code>
	<data:code lang="no">Norwegian</data:code>
	<data:code lang="oc">Occitan</data:code>
	<data:code lang="om">Oromo</data:code>
	<data:code lang="or">Oriya</data:code>
	<data:code lang="pa">Punjabi</data:code>
	<data:code lang="pl">Polish</data:code>
	<data:code lang="ps">Pashto</data:code>
	<data:code lang="pt">Portuguese</data:code>
	<data:code lang="qu">Quetchua</data:code>
	<data:code lang="rm">Rhaeto-Romanic</data:code>
	<data:code lang="rn">Kirundi</data:code>
	<data:code lang="ro">Romanian</data:code>
	<data:code lang="ru">Russian</data:code>
	<data:code lang="rw">Kinyarwanda</data:code>
	<data:code lang="sa">Sanskrit</data:code>
	<data:code lang="sc">Sardinian</data:code>
	<data:code lang="sd">Sindhi</data:code>
	<data:code lang="sg">Sangho</data:code>
	<data:code lang="sh">Serbo-Croatian</data:code>
	<data:code lang="si">Singhalese</data:code>
	<data:code lang="sk">Slovak</data:code>
	<data:code lang="sl">Slovenian</data:code>
	<data:code lang="sm">Samoan</data:code>
	<data:code lang="sn">Shona</data:code>
	<data:code lang="so">Somali</data:code>
	<data:code lang="sq">Albanian</data:code>
	<data:code lang="sr">Serbian</data:code>
	<data:code lang="ss">Siswati</data:code>
	<data:code lang="st">Sesotho</data:code>
	<data:code lang="su">Sundanese</data:code>
	<data:code lang="sv">Swedish</data:code>
	<data:code lang="sw">Swahili</data:code>
	<data:code lang="ta">Tamil</data:code>
	<data:code lang="te">Telugu</data:code>
	<data:code lang="tg">Tajik</data:code>
	<data:code lang="th">Thai</data:code>
	<data:code lang="ti">Tigrinya</data:code>
	<data:code lang="tk">Turkmen</data:code>
	<data:code lang="tl">Tagalog</data:code>
	<data:code lang="tn">Setswana</data:code>
	<data:code lang="to">Tonga</data:code>
	<data:code lang="tr">Turkish</data:code>
	<data:code lang="ts">Tsonga</data:code>
	<data:code lang="tt">Tatar</data:code>
	<data:code lang="tw">Twi</data:code>
	<data:code lang="ug">Uyghur</data:code>
	<data:code lang="uk">Ukrainian</data:code>
	<data:code lang="ur">Urdu</data:code>
	<data:code lang="uz">Uzbek</data:code>
	<data:code lang="vi">Vietnamese</data:code>
	<data:code lang="vo">Volapuk</data:code>
	<data:code lang="wo">Wolof</data:code>
	<data:code lang="xh">Xhosa</data:code>
	<data:code lang="yi">Yiddish</data:code>
	<data:code lang="yo">Yoruba</data:code>
	<data:code lang="za">Zhuang</data:code>
	<data:code lang="zh">Chinese</data:code>
	<data:code lang="zu">Zulu</data:code>
	<!-- Fallbacks -->
	<data:fallback lang="ca">ca-es ca-fr ca-it ca-ad</data:fallback>
	<data:fallback lang="de">de-at de-li de-lu de-ch de-de</data:fallback>
	<data:fallback lang="en">en-au en-bz en-ca en-ie en-jm en-nz en-ph en-za en-tt en-gb en-us en-zw</data:fallback>
	<data:fallback lang="es">es-bo es-cl es-co es-cr es-do es-ec es-sv es-gt es-hn es-mx es-ni es-pa es-py es-pe es-pr es-us es-uy es-ve</data:fallback>
	<data:fallback lang="eu">eu-es</data:fallback>
	<data:fallback lang="fr">fr-be fr-ca fr-lu fr-mc fr-ch fr-fr</data:fallback>
	<data:fallback lang="it">it-ch</data:fallback>
	<data:fallback lang="ja">ja-jp</data:fallback>
	<data:fallback lang="ko">ko-kr</data:fallback>
	<data:fallback lang="nl">nl-be</data:fallback>
	<data:fallback lang="nn">nn-no no-nyn</data:fallback>
	<data:fallback lang="no">nb nb-no no-bok</data:fallback>
	<data:fallback lang="sv">sv-fi</data:fallback>
	<data:fallback lang="tr">tr-tr</data:fallback>
	<data:fallback lang="zh">zh-cn zh-hk zh-mo zh-sg</data:fallback>
</data:data>

</xsl:transform>

<!--
 ! vim:syntax=xslt:nowrap:ts=4:sw=4:ai:nowrap
 !-->