开发者

How do I write an xsl-stylesheet to translate parts of a document based on content of matching tags...sort of?

开发者 https://www.devze.com 2023-01-23 03:03 出处:网络
I have a xml-structure thar looks a little like this: <Page> <Id>Page1</Id> <Content>

I have a xml-structure thar looks a little like this:

<Page>
 <Id>Page1</Id>
 <Content>
  <Header>This is the Header</Header>
  <Body>This is the body</Body>
  <Nested>
   <NestedItem>
    <Id>N1</Id>
    <Content>This is a nested element</Content>
   </NestedItem>
   <NestedItem>
    <Id>N2</Id>
    <Content>This too is a nested element</Content>
   </NestedItem>
  </Nested>
 </Content>
 <Localizations>
  <Localization>
   <Locale>ES</Locale>
   <Content>
    <Header>Esta un caballo</Header>
    <Body>Esta body</Body>
    <Nested>
     <NestedItem>
      <Id>N2</Id>
      <Content>Esta una element nest开发者_如何学编程ado</Content>
     </NestedItem>
    </Nested>
   </Content>
  <Localization>
 </Localizations>
</Page>

and after xslt-transformation, into which i pass the variable "ES", in this case, i want it to look something like this:

<Page>
 <Id>Page1</Id>
 <Content>
  <Header>Esta un caballo</Header>
  <Body>Esta body</Body>
  <Nested>
   <NestedItem>
    <Id>N1</Id>
    <Content>This is a nested element</Content>
   </NestedItem>
   <NestedItem>
    <Id>N2</Id>
    <Content>Esta una element nestado</Content>
   </NestedItem>
  </Nested>
 </Content>
</Page>

ie, i want to translate the elemnts that have corresponding elements in the Localization, but keep the original text where there is orginal text. The thing is that the structure of the content-element might be unknown, so i cant use specific tag-names in the xsl-file.

so far, i've come up whith this:

<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="utf-8" doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"/>
<xsl:param name="locale"></xsl:param>





<xsl:template match="/" name="foo">

  <xsl:for-each select="./*">
   <xsl:if test="name() != 'Localizations'">
    <xsl:element name="{name()}">

     <xsl:variable name="elementName" select="name()"/>
     <xsl:variable name="elementId" select="Id"/>

     <xsl:variable name="elementContent">
      <xsl:value-of select="./text()" />
     </xsl:variable> 

     <xsl:variable name="localContent">
      <xsl:for-each select="./ancestor::Page[1]/Localizations/Localization">
       <xsl:if test="./Locale = $locale">

          <xsl:copy-of select="*[name()=$elementName]/*"/>

       </xsl:if>
      </xsl:for-each>
     </xsl:variable> 

       <xsl:copy-of select="$localContent"/>
       <xsl:call-template name="foo"/>


    </xsl:element>

   </xsl:if>
  </xsl:for-each>  


</xsl:template>


</xsl:stylesheet>

Which outputs the xml allright, but it creates duplicates of the elements in the content-tag, and only the spanish content, the other tags remain empty. Am I at all going the right way about this? Any pointers would be appriciated, guides to xslt is quite hard to find and i'm trying to teach myself here...


This transformation:

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

 <xsl:key name="kContentByElName" match="Localization/Content/*"
  use="name()"/>

 <xsl:key name="kNestedById" match="Localization/Content/Nested/NestedItem"
  use="Id"/>

 <xsl:template match="node()|@*">
     <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
     </xsl:copy>
 </xsl:template>

 <xsl:template match="/*/Content/*[not(self::Nested)]/text()">
   <xsl:variable name="vTranslation"
        select="key('kContentByElName', name(..))"/>
   <xsl:value-of select="$vTranslation | self::node()[not($vTranslation)]"/>
 </xsl:template>

 <xsl:template match=
   "NestedItem[not(ancestor::Localization)]/Content/text()">
   <xsl:variable name="vTranslation"
        select="key('kNestedById', ../../Id)/Content"/>
   <xsl:value-of select="$vTranslation | self::node()[not($vTranslation)]"/>
 </xsl:template>

 <xsl:template match="Localizations"/>
</xsl:stylesheet>

when applied on the provided XML document:

<Page>
 <Id>Page1</Id>
 <Content>
  <Header>This is the Header</Header>
  <Body>This is the body</Body>
  <Nested>
   <NestedItem>
    <Id>N1</Id>
    <Content>This is a nested element</Content>
   </NestedItem>
   <NestedItem>
    <Id>N2</Id>
    <Content>This too is a nested element</Content>
   </NestedItem>
  </Nested>
 </Content>
 <Localizations>
  <Localization>
   <Locale>ES</Locale>
   <Content>
    <Header>Esta un caballo</Header>
    <Body>Esta body</Body>
    <Nested>
     <NestedItem>
      <Id>N2</Id>
      <Content>Esta una element nestado</Content>
     </NestedItem>
    </Nested>
   </Content>
  </Localization>
 </Localizations>
</Page>

produces the wanted, correct result:

<Page>
   <Id>Page1</Id>
   <Content>
      <Header>Esta un caballo</Header>
      <Body>Esta body</Body>
      <Nested>
         <NestedItem>
            <Id>N1</Id>
            <Content>This is a nested element</Content>
         </NestedItem>
         <NestedItem>
            <Id>N2</Id>
            <Content>Esta una element nestado</Content>
         </NestedItem>
      </Nested>
   </Content>
</Page>

Do note:

  1. This is a pure XSLT 1.0 solution.

  2. Keys are used for fast searching.

  3. All nodes are copied "as-is" by using the identity rule.

  4. Templates matching the nodes that need processing override the identity rule.

  5. Whenever there is no translation found the original text is preserved.


This stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:param name="pLang" select="'ES'"/>
    <xsl:key name="kElementByLang"
             match="Localization/Content/*[not(self::Nested)]|
                    Localization/Content/Nested/NestedItem"
             use="concat(ancestor::Localization/Locale,'++',
                         name(),'++',Id)"/>
    <xsl:template match="node()|@*" name="identity">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="Localizations"/>
    <xsl:template match="Page/Content//*">
        <xsl:variable name="vMatch"
                      select="key('kElementByLang',
                                  concat($pLang,'++',
                                         name(),'++',
                                         Id))"/>
        <xsl:apply-templates select="$vMatch"/>
        <xsl:if test="not($vMatch)">
            <xsl:call-template name="identity"/>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>

Output:

<Page>
    <Id>Page1</Id>
    <Content>
        <Header>Esta un caballo</Header>
        <Body>Esta body</Body>
        <Nested>
            <NestedItem>
                <Id>N1</Id>
                <Content>This is a nested element</Content>
            </NestedItem>
            <NestedItem>
                <Id>N2</Id>
                <Content>Esta una element nestado</Content>
            </NestedItem>
        </Nested>
    </Content>
</Page>


Here is an XSLT 2.0 stylesheet (you can run with Saxon 9 or AltovaXML Tools or XQSharp)

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="2.0">

  <xsl:param name="locale" select="'ES'"/>

  <xsl:key name="k1" 
    match="Localizations/Localization[Locale = $locale]//*[Id]" 
    use="Id"/>
  <xsl:key name="k2" 
    match="Localizations/Localization[Locale = $locale]//*[not(Id) and not(*)]" 
    use="local-name()"/>

  <xsl:template match="Page/Content//*[not(Id) and *]">
    <xsl:copy>
      <xsl:apply-templates/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="Page">
    <xsl:copy>
      <xsl:copy-of select="Id"/>
      <xsl:apply-templates select="Content"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="Page//*[Id]">
    <xsl:variable name="loc" select="key('k1', Id)"/>
    <xsl:choose>
      <xsl:when test="$loc">
        <xsl:copy-of select="$loc"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:copy-of select="."/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <xsl:template match="Page//*[not(Id) and not(*)]">
    <xsl:variable name="loc" select="key('k2', local-name())"/>
    <xsl:choose>
      <xsl:when test="$loc">
        <xsl:copy-of select="$loc"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:copy>
          <xsl:apply-templates/>
        </xsl:copy>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

But I am not sure your requirements are clearly stated and implemented by that stylesheet.

0

精彩评论

暂无评论...
验证码 换一张
取 消