You can write a function that maps each id attribute value to its replacement, in XSLT 3 (supported since 2017 by Saxon 9.8 and later and Altova 2017 and later) you can use a map as a function:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="3.0">
<xsl:param name="replacement-map" as="map(xs:integer, xs:integer)" select="map { 11 : 24, 12 : 75, 13 : 145 }"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="cats">
<cat-id>
<xsl:value-of select="cat/@id/$replacement-map(xs:integer(.))" separator="," />
</cat-id>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/jyH9rNs
In XSLT 2 you could use an XML structure to represent your replacement table/map and use a key to find the replacement for an attribute value:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="3.0">
<xsl:param name="replacement-map">
<value key="11">24</value>
<value key="12">75</value>
<value key="13">145</value>
</xsl:param>
<xsl:key name="rep" match="value" use="@key"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="cats">
<cat-id>
<xsl:value-of select="cat/@id/key('rep', ., $replacement-map)" separator="," />
</cat-id>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/jyH9rNs/1
Finally in XSLT 1, as the key function doesn't have a third parameter to change the context doc, you could use
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
xmlns:msxml="urn:schemas-microsoft-com:xslt"
exclude-result-prefixes="exsl msxml"
version="1.0">
<xsl:param name="replacement-map-rtf">
<value key="11">24</value>
<value key="12">75</value>
<value key="13">145</value>
</xsl:param>
<xsl:param name="replacement-map" select="exsl:node-set($replacement-map-rtf)"/>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:key name="rep" match="value" use="@key"/>
<xsl:template match="cats">
<cat-id>
<xsl:apply-templates select="cat/@id"/>
</cat-id>
</xsl:template>
<xsl:template match="cat/@id">
<xsl:if test="position() > 1">,</xsl:if>
<xsl:variable name="this" select="."/>
<xsl:for-each select="$replacement-map">
<xsl:value-of select="key('rep', $this)"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/jyH9rNs/2
string-join()requires XSLT 2.0. If the above works for you, then you must be using an XSLT 2.0 processor. -- Also, where do you want to keep the replacement table? Can it be hard-coded in the XSLT stylesheet?