Assuming an XSLT 2.0 processor you can use
<xsl:stylesheet
version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs">
<xsl:template match="@* | node()" mode="#all">
<xsl:copy>
<xsl:apply-templates select="@* , node()" mode="#current"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[note]">
<xsl:copy>
<xsl:for-each-group select="node()" group-ending-with="note">
<xsl:choose>
<xsl:when test="current-group()[last()][self::note]">
<xsl:apply-templates select="current-group()[position() lt last() - 2]"/>
<xsl:apply-templates select="current-group()[last() - 1]" mode="wrap"/>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
<xsl:template match="text()" mode="wrap">
<xsl:analyze-string select="." regex="\w+$">
<xsl:matching-substring>
<span id="{current-group()[last()]/@n}" class="{current-group()[last()]/@type}">
<xsl:value-of select="."/>
</span>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>
</xsl:stylesheet>
It transforms the input
<p>— Cher Cléonte, me dit-il, dès que <pb xml:id="nn2_4" n="4"/> je fus entré dans sa
chambre, je vous ai envoyé chercher afin que vous prissiez part au divertissement que je
dois avoir aujourd'hui. Je dînai<note type="glossary" n="nn2_4n1">déjeunai</note> il y a quelque temps en
un lieu où il se rencontra trois de ces messieurs qui font profession de ne rien ignorer</p>
into the result
<p>— Cher Cléonte, me dit-il, dès que je fus entré dans sa
chambre, je vous ai envoyé chercher afin que vous prissiez part au divertissement que je
dois avoir aujourd'hui. Je <span id="nn2_4n1" class="glossary">dînai</span> il y a quelque temps en
un lieu où il se rencontra trois de ces messieurs qui font profession de ne rien ignorer</p>
That currently drops the note
element from the input, if you want it processed at its place change
<xsl:when test="current-group()[last()][self::note]">
<xsl:apply-templates select="current-group()[position() lt last() - 2]"/>
<xsl:apply-templates select="current-group()[last() - 1]" mode="wrap"/>
</xsl:when>
to
<xsl:when test="current-group()[last()][self::note]">
<xsl:apply-templates select="current-group()[position() lt last() - 2]"/>
<xsl:apply-templates select="current-group()[last() - 1]" mode="wrap"/>
<xsl:apply-templates select="current-group()[last()]"/>
</xsl:when>
I have assumed a note
is preceded by a plain text node as in your sample, if we had e.g. Je <b>dînai</b><note type="glossary" n="nn2_4n1">déjeunai</note>
it is more complicated.
And I have only tested with the one simple input sample, test yourself with more complex ones and report back if you encounter problems.