2012-04-05 5 views
5

Учитывая следующий XML:XSLT: Как выводить только локализованные данные?

<?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> 

Мне нужно выбрать набор элементов перевода таким образом, что набор содержит уникальные значения для «имя» атрибута, и «лучший матч» для данной местности ('ан -US ',' es-MX ',' fr 'и т. Д.). Когда я говорю наилучшее совпадение, я хотел бы сначала найти элемент с полным совпадающим языком, а затем искать совпадение, основанное только на первых двух символах, а затем искать элемент без lang.

Например, если я прохожу в локали «ан-CA» при преобразовании вышеуказанных данных, я хотел бы получить следующие два элемента:

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

Но если я перехожу в «ен- GB», я хотел бы получить:

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

и, наконец, если я прохожу в значение, например,„эс“или„эс-МХ“, я бы ожидать, чтобы получить:

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

Я чрезвычайно новичок в XSLT, но думаю, что у меня есть что-то, что работает. Мне просто нужно знать, если есть лучший способ сделать это (проще, более элегантный, более производительный и т.д.)

Вот мой первый удар в нем:

<?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> 

Это мой первый раз отправьте вопрос, поэтому, пожалуйста, дайте мне знать, нужно ли мне добавлять/редактировать/удалять что-либо.

Спасибо!

+0

+1 за хороший первый вопрос. –

+0

обратите внимание, что ваш xsl для 'en-AU', установленный в файле только с' en-CA', 'en-GB' и no' en', не будет выводить результат. Мой ответ, надеюсь, исправляет это также. В конце концов, они могут понять друг друга, если они действительно попытаются, нет? –

+0

В то время как я согласен, они должны понимать друг друга, мои требования очень ясны в отношении того, как они хотят, чтобы языковой резерв работал. Но спасибо, что поймали это. – Nick

ответ

0

, если вы могли бы использовать msxsl: набор узлов, или тому подобное, вы можете сделать это нравится:

<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. Это может работать на стандарте 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. Если вы не хотите сортировать, вы можете просто выполнить фильтрацию (сохраняет порядок документов).Кроме того, другой механизм группировки:

<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

Все это здорово! Прямо сейчас, я думаю, что я могу использовать гибридный подход с кусками из каждого. Благодаря! – Nick

0

Это короткое и простое преобразование (нет переменных, xsl:choose, xsl:when, xsl:otherwise, xsl:if, XSL: сортировка, 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> 

при нанесении на предоставленный XML-документ:

<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> 

производит разыскиваемого, правильный результат:

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

Если изменить глобальный/внешний параметр:

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

снова правильный результат получается:

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

Если изменить глобальный/внешний параметр для:

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

снова разыскиваемый результат получается:

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

Спасибо за ответ, но, к сожалению, будет еще много возможных значений для @name, кроме как только «Ресурс» и «Сообщение». Наверное, я должен был сделать это более ясно в вопросе. – Nick

Смежные вопросы