2012-04-05 10 views
5

Dado el siguiente código XML:XSLT: ¿cómo generar solo datos localizados?

<?xml version="1.0" encoding="UTF-8" ?> 
<?xml-stylesheet type="text/xsl" href="form.xsl"?> 
<Document> 
    <Translations> 
    <Translation name="Resource">Invariant Resource</Translation> 
    <Translation name="Resource" lang="en">English Resource</Translation> 
    <Translation name="Resource" lang="en-CA">Canadian English Resource</Translation> 
    <Translation name="Resource" lang="en-GB">British English Resource</Translation> 
    <Translation name="Message">Invariant Message</Translation> 
    <Translation name="Message" lang="en">English Message</Translation> 
    <Translation name="Message" lang="en-CA">Canadian English Message</Translation> 
    <Translation name="Message" lang="en-AU">Australian English Message</Translation> 
    </Translations> 
</Document> 

necesito seleccionar un conjunto de elementos de traducciones de tal manera que el conjunto contiene valores únicos para el atributo "nombre", y el "mejor partido" para una localidad dada ('en -US ',' es-MX ',' fr ', etc.). Cuando digo mejor coincidencia, me gustaría buscar primero un elemento con la configuración regional completa, luego buscar una coincidencia basada solo en los dos primeros caracteres, luego buscar un elemento sin especificar lang.

Por ejemplo, si paso en un escenario de 'en-CA' al transformar los datos anteriores, me gustaría obtener los dos elementos siguientes:

<Translation name="Resource" lang="en-CA">Canadian English Resource</Translation> 
<Translation name="Message" lang="en-CA">Canadian English Message</Translation> 

Pero si paso en 'en- GB', me gustaría llegar:

<Translation name="Resource" lang="en-GB">British English Resource</Translation> 
<Translation name="Message" lang="en">English Message</Translation> 

Y, finalmente, si paso en un valor tal como 'es' o 'ES-MX', que se puede esperar para obtener:

<Translation name="Resource">Invariant Resource</Translation> 
<Translation name="Message">Invariant Message</Translation> 

Soy muy nuevo en XSLT, pero creo que tengo algo que funciona. Sólo necesito saber si hay una mejor manera de hacerlo (más simple, más elegante, con más prestaciones, etc.)

Aquí es mi primera puñalada en ella:

<?xml version="1.0" encoding="UTF-8" ?> 
<xsl:stylesheet version="1.0" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output encoding="utf-8" indent="yes" method="xml" omit-xml-declaration="yes"/> 

    <xsl:key match="Translation" name="TranslationName" use="concat(@name,':',@lang)"/> 

    <xsl:template match="/"> 
    <!-- locale parameter for translation --> 
    <xsl:param name="locale"/> 

    <xsl:for-each select="Document/Translations/Translation[@lang=$locale or @lang=substring($locale,1,2) or not(@lang)]"> 
     <xsl:choose> 
     <xsl:when test="@lang=$locale and count(key('TranslationName', concat(@name,':',$locale)))=1"> 
      <xsl:element name="p"> 
      <xsl:value-of select="."/> 
      </xsl:element> 
     </xsl:when> 
     <xsl:when test="@lang=substring($locale,1,2) and count(key('TranslationName', concat(@name,':',$locale)))=0"> 
      <xsl:element name="p"> 
      <xsl:value-of select="."/> 
      </xsl:element> 
     </xsl:when> 
     <xsl:when test="not(@lang) and count(key('TranslationName', concat(@name,':',$locale))|key('TranslationName', concat(@name,':',substring($locale,1,2))))=0"> 
      <xsl:element name="p"> 
      <xsl:value-of select="."/> 
      </xsl:element> 
     </xsl:when> 
     </xsl:choose> 
    </xsl:for-each> 
    </xsl:template> 
</xsl:stylesheet> 

Esta es la primera vez que publique una pregunta, por favor avíseme si necesito agregar/editar/eliminar algo.

Gracias!

+0

+1 para una buena primera pregunta. –

+0

tenga en cuenta que su xsl para 'en-AU' establecido en un archivo con solo' en-CA', 'en-GB' y no' en' no generará salida. Mi respuesta, con suerte corrige esto también. Después de todo, se pueden entender si realmente lo intentan, ¿no? –

+0

Aunque estoy de acuerdo, el debería ser capaz de entenderse entre sí, mis requisitos son muy claros acerca de cómo quieren que el lenguaje alternativo funcione. Pero gracias por atrapar eso. – Nick

Respuesta

0

si se pudiera utilizar msxsl: conjunto de nodos o similar, es posible hacerlo como:

<xsl:template match="/"> 
    <xsl:param name="locale" select="'en-AU'"/> 
<!-- locale parameter for translation --> 
    <xsl:variable name="sorted"> 
     <xsl:for-each select="Document/Translations/Translation"> 
      <xsl:sort select="@name"/> 
      <xsl:sort select="not(@lang=$locale)"/> 
      <xsl:sort select="not(starts-with(@lang, substring($locale,1,2)))"/> 
      <xsl:sort select="@lang"/> 
      <xsl:copy-of select="."/> 
     </xsl:for-each> 
    </xsl:variable> 
    <xsl:for-each select="msxsl:node-set($sorted)/*"> 
     <xsl:if test="position() = 1 or @name!=preceding-sibling::*[1]/@name"> 
      <xsl:copy-of select="."/> 
     </xsl:if> 
    </xsl:for-each> 
</xsl:template> 

P. S. Éste podría funcionar en el estándar 1.0

<xsl:template match="/"> 
    <xsl:param name="locale" select="'en-AU'"/> 
<!-- locale parameter for translation --> 
    <xsl:variable name="path" select="Document/Translations/Translation"/> 
    <xsl:for-each select="$path"> 
     <xsl:variable name="curName" select="$path[@name=current()/@name]"/> 
     <xsl:if test="count($curName[1] | .)=1"> 
      <xsl:for-each select="$curName"> 
      <xsl:sort select="not(@lang=$locale)"/> 
      <xsl:sort select="not(starts-with(@lang, substring($locale,1,2)))"/> 
      <xsl:sort select="@lang"/> 
      <xsl:if test="position()=1"> 
       <xsl:copy-of select="."/> 
      </xsl:if> 
      </xsl:for-each> 
     </xsl:if> 
    </xsl:for-each> 
</xsl:template> 

P.P.S. Si no desea ordenar, puede hacer el filtrado (conserva el orden de los documentos).También, diferente mecanismo de agrupación:

<xsl:template match="/"> 
    <xsl:param name="locale" select="'en'"/> 
    <xsl:variable name="locale-lang" select="substring($locale,1,2)"/> 
<!-- locale parameter for translation --> 
    <xsl:variable name="path" select="Document/Translations/Translation"/> 
    <xsl:for-each select="$path[not(preceding-sibling::Translation/@[email protected])]"> 
     <xsl:variable name="curName" select="$path[@name=current()/@name]"/> 
     <xsl:variable name="test1" select="$curName[@lang=$locale]"/> 
     <xsl:variable name="test2" select="$curName[@lang=$locale-lang]"/> 
     <xsl:variable name="test3" select="$curName[starts-with(@lang, $locale-lang)]"/> 
     <xsl:variable name="test4" select="$curName[not(@lang)]"/> 
     <xsl:choose> 
      <xsl:when test="$test1"> 
       <xsl:copy-of select="$test1[1]"/> 
      </xsl:when> 
      <xsl:when test="$test2"> 
       <xsl:copy-of select="$test2[1]"/> 
      </xsl:when> 
      <xsl:when test="$test3"> 
       <xsl:copy-of select="$test3[1]"/> 
      </xsl:when> 
      <xsl:when test="$test4"> 
       <xsl:copy-of select="$test4[1]"/> 
      </xsl:when> 
     </xsl:choose> 
    </xsl:for-each> 
</xsl:template> 
+0

¡Todos son geniales! En este momento, creo que puedo usar un enfoque híbrido con piezas de cada uno. ¡Gracias! – Nick

0

este corto y simple transformación (sin variables, xsl:choose, xsl:when, xsl:otherwise, xsl:if, xsl: sort, xsl:element):

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 

<xsl:param name="pCode" select="'en-GB'"/> 

<xsl:key name="kTransName" match="@name" use="."/> 

<xsl:key name="Resource" match="Translation[@name='Resource']" 
      use="@lang"/> 
<xsl:key name="Message" match="Translation[@name='Message']" 
      use="@lang"/> 
<xsl:key name="Resource" match="Translation[@name='Resource']" 
          use="boolean(@lang)"/> 
<xsl:key name="Message" match="Translation[@name='Message']" 
          use="boolean(@lang)"/> 

<xsl:template match="/"> 

    <xsl:for-each select= 
    "/*/*/*/@name[generate-id()=generate-id(key('kTransName', .)[1])]"> 
     <xsl:copy-of select= 
     "key(., $pCode) 
     | 
     key(., substring($pCode, 1, 2)) 
      [not(key(current(), $pCode))] 
     | 
     key(., 'false') 
      [not(key(current(), $pCode) 
       | 
       key(current(), substring($pCode, 1, 2)) 
      ) 
      ] 
     "/> 
    </xsl:for-each> 
</xsl:template> 
</xsl:stylesheet> 

cuando se aplica en el documento XML provisto:

<Document> 
    <Translations> 
     <Translation name="Resource">Invariant Resource</Translation> 
     <Translation name="Resource" lang="en">English Resource</Translation> 
     <Translation name="Resource" lang="en-CA">Canadian English Resource</Translation> 
     <Translation name="Resource" lang="en-GB">British English Resource</Translation> 
     <Translation name="Message">Invariant Message</Translation> 
     <Translation name="Message" lang="en">English Message</Translation> 
     <Translation name="Message" lang="en-CA">Canadian English Message</Translation> 
     <Translation name="Message" lang="en-AU">Australian English Message</Translation> 
    </Translations> 
</Document> 

produce el, resultado correcto deseada:

<Translation name="Resource" lang="en-GB">British English Resource</Translation> 
<Translation name="Message" lang="en">English Message</Translation> 

Si cambiamos el parámetro global/externa a:

<xsl:param name="pCode" select="'en-CA'"/> 

de nuevo el resultado correcto se produce:

<Translation name="Resource" lang="en-CA">Canadian English Resource</Translation> 
<Translation name="Message" lang="en-CA">Canadian English Message</Translation> 

si cambiamos el parámetro global/externa a:

<xsl:param name="pCode" select="'es-MX'"/> 

de nuevo el resultado deseado se produce:

<Translation name="Resource">Invariant Resource</Translation> 
<Translation name="Message">Invariant Message</Translation> 
+0

Gracias por la respuesta, pero desafortunadamente habrá muchos más valores posibles para @name que no sean solo "Recurso" y "Mensaje". Creo que debería haber dejado eso un poco más claro en la pregunta. – Nick

Cuestiones relacionadas