Comments:
<xsl:template match="array">
<xsl:for-each select="dict">
<xsl:apply-templates select="key"/>
</xsl:for-each>
</xsl:template>
that's equivalent to
<xsl:template match="array">
<xsl:apply-templates select="dict/key"/>
</xsl:template>
Similarly
<xsl:for-each select="../array/dict">
<xsl:apply-templates select="key"/>
</xsl:for-each>
could be <xsl:apply-templates select="../array/dict/key"/>
Finally, Sam's solution has
<xsl:template match="key">
<xsl:element name="{translate(text(), ' ', '_')}">
<xsl:choose>
<xsl:when test="following-sibling::node()[1][self::array]">
<xsl:apply-templates select="following-sibling::node()[1]/dict"/>
</xsl:when>
<xsl:when test="following-sibling::node()[1][self::false|self::true]">
<xsl:value-of select="following-sibling::node()[1]/local-name()" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="following-sibling::node()[1]"/>
</xsl:otherwise>
</xsl:choose>
</xsl:element>
</xsl:template>
but to me the xsl:choose is crying out to be an apply-templates: <xsl:apply-templates select="following-sibling::*[1]"/> with separate rules for the different things you might find as the next sibling after a <key>.
Michael Kay
http://www.saxonica.com/
Author, XSLT Programmer's Reference and XPath 2.0 Programmer's Reference