1

首先,我为标题道歉,但我不知道描述我的问题的好方法。因此,代码示例将使事情变得更加清晰。

假设我有以下 xml 树:

<root>
<node>
<value xml:lang="en">Some English Content</value>
<value xml:lang="fr">Some French Content</value>
<value xml:lang="de">Some German Content</value>
</node>
<node>
<value xml:lang="en">Some English Content</value>
<value xml:lang="de">Some German Content</value>
</node>
<node>
<value xml:lang="en">Some Other English Content</value>
<value xml:lang="fr">Some Other French Content</value>
<value xml:lang="de">Some Other German Content</value>
</node>
<node>
<value xml:lang="en">Some English Content</value>
<value xml:lang="fr">Some French Content</value>
<value xml:lang="de">Some German Content</value>
</node>
<node>
<value xml:lang="fr">Some French Content</value>
<value xml:lang="de">Some German Content</value>
</node>
</root>

所以基本上有各种节点集,有许多本地化的字符串,我想根据内容对这些集进行分组。节点 1、2、4 和 5 是关于同一主题的,但并非所有字符串都可能在所有语言环境中都可用,所以我不能真正使用参考字符串(比如英语,因为它在节点 5 中不可用)。节点 3 包含不同的内容,因此它应该属于不同的组。

听起来可能很复杂,但这是我想要得到的结果(使用 xslt 2):

<values>
<group>
    <value xml:lang="en">Some English Content</value>
    <value xml:lang="fr">Some French Content</value>
    <value xml:lang="de">Some German Content</value>
</group>
<group>
    <value xml:lang="en">Some Other English Content</value>
    <value xml:lang="fr">Some Other French Content</value>
    <value xml:lang="de">Some Other German Content</value>
</group>
</values>

关于如何最好地处理这个问题的任何想法?请注意,一个节点中最多可以有 40 种不同的语言,并且一个文件中可以有数百个节点,因此资源也可能成为问题。

4

1 回答 1

0

我不确定我是否完全理解您的要求,但我编写了一些代码,首先尝试nodevalues 填充缺失语言的元素,然后对填充的元素进行分组:

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="2.0"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:mf="http://example.com/mf"
  exclude-result-prefixes="xs mf">

<xsl:param name="sep" as="xs:string" select="'|'"/>

<xsl:output indent="yes"/>

<xsl:variable name="main-doc" select="/"/>

<xsl:variable name="languages" as="xs:string*">
  <xsl:perform-sort select="distinct-values(root/node/value/@xml:lang)">
    <xsl:sort select="."/>
  </xsl:perform-sort>
</xsl:variable>

<xsl:key name="k1" match="node/value" use="concat(@xml:lang, $sep, .)"/>

<xsl:template match="root">
  <values>
    <xsl:variable name="filled" as="element(node)*">
      <xsl:apply-templates select="node" mode="fill"/>
    </xsl:variable>
    <xsl:for-each-group select="$filled" group-by="string-join(value, $sep)">
      <group>
        <xsl:copy-of select="value"/>
      </group>
    </xsl:for-each-group>
  </values>
</xsl:template>

<xsl:template match="node" mode="fill">
  <xsl:copy>
    <xsl:variable name="this" as="element(node)" select="."/>
    <xsl:for-each select="$languages">

      <value xml:lang="{.}">
        <xsl:value-of
          select="if ($this/value[lang(current())]) 
                  then $this/value[lang(current())]
                  else (key('k1', 
                            concat($this/value[1]/@xml:lang, $sep, $this/value[1]),
                            $main-doc)/../value[lang(current())])[1]"/>
      </value>
    </xsl:for-each>
  </xsl:copy>
</xsl:template>

</xsl:stylesheet> 

如您所见,我需要一些订单来填充元素,因此我对 distinct 进行排序@xml:lang,我不确定您是否想要。使用这种方法,您使用 Saxon 9.5 发布的输入的输出是

<values>
   <group>
      <value xml:lang="de">Some German Content</value>
      <value xml:lang="en">Some English Content</value>
      <value xml:lang="fr">Some French Content</value>
   </group>
   <group>
      <value xml:lang="de">Some Other German Content</value>
      <value xml:lang="en">Some Other English Content</value>
      <value xml:lang="fr">Some Other French Content</value>
   </group>
</values>

我也不确定哪种策略是填充元素的预期策略(另请参阅我发布的评论)。最后,我决定将node/value元素的连接@xml:lang及其内容作为关键元素,然后在填充缺少语言的元素时,我只需匹配第一value个子元素 a node。所以这基本上意味着如果有些node人有第一个value xml:lang="foo"内容bar,那么匹配只是在那个语言foo和内容上bar,我们复制value缺少的语言的内容。

如果您不想要排序并且您可以接受初始属性序列的顺序,那么您可以省略排序,那么样式表就是

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="2.0"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:mf="http://example.com/mf"
  exclude-result-prefixes="xs mf">

<xsl:param name="sep" as="xs:string" select="'|'"/>

<xsl:output indent="yes"/>

<xsl:variable name="main-doc" select="/"/>

<xsl:variable name="languages" as="xs:string*" select="distinct-values(root/node/value/@xml:lang)"/>

<xsl:key name="k1" match="node/value" use="concat(@xml:lang, $sep, .)"/>

<xsl:template match="root">
  <values>
    <xsl:variable name="filled" as="element(node)*">
      <xsl:apply-templates select="node" mode="fill"/>
    </xsl:variable>
    <xsl:for-each-group select="$filled" group-by="string-join(value, $sep)">
      <group>
        <xsl:copy-of select="value"/>
      </group>
    </xsl:for-each-group>
  </values>
</xsl:template>

<xsl:template match="node" mode="fill">
  <xsl:copy>
    <xsl:variable name="this" as="element(node)" select="."/>
    <xsl:for-each select="$languages">

      <value xml:lang="{.}">
        <xsl:value-of
          select="if ($this/value[lang(current())]) 
                  then $this/value[lang(current())]
                  else (key('k1', 
                            concat($this/value[1]/@xml:lang, $sep, $this/value[1]),
                            $main-doc)/../value[lang(current())])[1]"/>
      </value>
    </xsl:for-each>
  </xsl:copy>
</xsl:template>

</xsl:stylesheet> 

这样输出就是你要求的:

<values>
   <group>
      <value xml:lang="en">Some English Content</value>
      <value xml:lang="fr">Some French Content</value>
      <value xml:lang="de">Some German Content</value>
   </group>
   <group>
      <value xml:lang="en">Some Other English Content</value>
      <value xml:lang="fr">Some Other French Content</value>
      <value xml:lang="de">Some Other German Content</value>
   </group>
</values>
于 2013-07-29T15:02:14.047 回答