You are already using the last() function, but possibly thinking it is not working. Although you have not shown the full XSLT, it is possible you are relying on XSLT's built-in templates to select the contrib elements. In this case, it is selecting nodes in the XML, not just elements, and the position() relates to the nodes just selected, including text nodes. The last contrib element is not the last node because there is a white-space node after the last element.
What you could do is use the strip-space command to remove such white-space nodes.
Try this XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*" />
<xsl:template match="contrib">
<xsl:choose>
<xsl:when test="position()=last()">
<xsl:text> and </xsl:text>
</xsl:when>
<xsl:when test="position() > 1">
<xsl:text>, </xsl:text>
</xsl:when>
</xsl:choose>
<xsl:apply-templates />
</xsl:template>
</xsl:stylesheet>
However, this may not work if there are other elements in the XML. So, to get around this, you could explicitly select the contrib elements, in which case position would pick up the last one. Try this XSLT too
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*" />
<xsl:template match="/*">
<xsl:apply-templates select="contrib" />
</xsl:template>
<xsl:template match="contrib">
<xsl:choose>
<xsl:when test="position()=last()">
<xsl:text> and </xsl:text>
</xsl:when>
<xsl:when test="position() > 1">
<xsl:text>, </xsl:text>
</xsl:when>
</xsl:choose>
<xsl:apply-templates />
</xsl:template>
</xsl:stylesheet>