Hi Pablo,

As I said previously, here is a continuation of my experiments around lemmatization and glossaries.

As I mentioned in my previous message, there is an important distinction between building a glossary for an entire chapter or book, and supporting the kind of pedagogical work done by teachers such as Geoffrey Steadman, who present texts by short sections, with vocabulary and commentary laid out page by page. (I did not try to count the exact number of lines in each excerpt he treats, nor the size of the corresponding vocabularies.)

Constructing a glossary per page (or per section, or even per paragraph) raises a specific problem: the mechanism must be able to /forget/ what was collected for the previous unit, reset itself, and then rebuild a new vocabulary list for the next unit — while also avoiding duplicates within each unit and preserving order of appearance. In other words, the workflow becomes: collect => print => reset => repeat

This is quite different from classical indexes, which are designed for global accumulation across a whole document. In practice, this is where I found it difficult to rely solely on standard register/index mechanisms. They naturally assume long-range memory, whereas a pedagogical glossary needs short-term, local memory. My current experimental solution uses a small Lua layer (see below the MWE).

Very briefly, the Lua code does three simple things:

1. it maintains a small local “bucket” (a table) where each marked word
   is stored under a key (the lemma), together with its displayed form
   and short gloss;
2. it keeps a separate list to preserve the order of first appearance
   and to avoid duplicates within the same section;
3. when the end of a section is reached, TeX asks Lua to print the
   collected entries, and then Lua is explicitly reset so that the next
   section starts from zero.

TeX remains responsible for typography and line numbering; Lua is only used as a temporary memory that can be cleared on demand.

So, at least from these small experiments, it seems to me that section-level reading glossaries are not just a technical variation on indexes: they correspond to a different editorial logic, centered on locality and frequent resets.

This is still very much exploratory on my side, but I thought the concrete MWE might help clarify where the conceptual difficulty lies. Of course, we can still improve the presentation of the glossary (in two columns), add lemmatization, etc.

Best regards,
JP

Here is the MWE :

% steadman-demo-localgloss.tex

% ------------------------------------------------------------

% Source (Grec): Aristote, Nicomachean Ethics I.3 (1094b–1095a),

% as presented in Geoffrey Steadman’s pedagogical PDF:

% "Aristotle’s Nicomachean Ethics Book I: Greek Text with Facing Vocabulary and Commentary"

% (G. Steadman, 2025-01 PDF provided by

% https://geoffreysteadman.com/wp-content/uploads/2025/01/arist.nicobk1.beta10jan25.pdf).

%

% Objectif : texte grec numéroté + glossaire local collecté avec Lua.

% ------------------------------------------------------------

% --- Langues

\setuplanguage[fr][patterns={fr},spacing=french]


% Grec ancien : patterns "agr" (et éventuellement "gr" en secours)

\setuplanguage[agr][patterns={agr}]

% (Optionnel) si tu veux aussi pouvoir utiliser le grec moderne :

% \setuplanguage[gr][patterns={gr}]


% --- Police : Libertinus (polytonique OK) + fallback grec

\definefontfamily[mainface][serif][Libertinus Serif]

\definefallbackfamily[mainface][serif][GFS Didot][preset=range:greek,force=yes]

\setupbodyfont[mainface,11pt]


% --- Mise en page

\setuplayout[

topspace=14mm,

backspace=18mm,

width=middle,

header=10mm,

footer=12mm,

height=middle,

]


\setupalign[hz,hanging]

\setuptolerance[verytolerant]


% ------------------------------------------------------------

% Lua: local glossary bucket

% - keeps first occurrence order

% - avoids duplicates by key

% - stores list of line numbers (unique)

% ------------------------------------------------------------

\startluacode

moduledata = moduledata or {}

moduledata.localgloss = moduledata.localgloss or {}


local function newbucket()

return { map = {}, order = {} }

end


moduledata.localgloss.bucket = moduledata.localgloss.bucket or newbucket()


local function reset(bucket)

bucket.map, bucket.order = {}, {}

end


local function addline(lines, ln)

-- keep unique line numbers, preserve insertion order

for i=1,#lines do

if lines[i] == ln then return end

end

lines[#lines+1] = ln

end


local function add(bucket, key, form, gloss, lineno)

if not key or key == "" then return end

if not bucket.map[key] then

bucket.map[key] = { form=form, gloss=gloss, lines={} }

bucket.order[#bucket.order+1] = key

end

if lineno and lineno ~= "" then

addline(bucket.map[key].lines, lineno)

end

end


local function joinlines(lines)

if #lines == 0 then return "" end

return table.concat(lines, ", ")

end


local function print(bucket)

local context = context

if #bucket.order == 0 then

context("\\noindent{\\tfxx\\it (no entries)}\\par")

return

end

context("\\startitemize[packed,joinedup]\\tfxx\n")

for i=1,#bucket.order do

local k = bucket.order[i]

local v = bucket.map[k]

context("\\item {\\bf ")

context(v.form)

context("} — ")

context(v.gloss)

local ln = joinlines(v.lines)

if ln ~= "" then

context("\\hfill{\\tfxx[")

context(ln)

context("]}")

end

context("\n")

end

context("\\stopitemize\n")

end


function moduledata.localgloss.reset()

reset(moduledata.localgloss.bucket)

end


function moduledata.localgloss.add(key, form, gloss, lineno)

add(moduledata.localgloss.bucket, key, form, gloss, lineno)

end


function moduledata.localgloss.print()

print(moduledata.localgloss.bucket)

end

\stopluacode


% ------------------------------------------------------------

% Du côté de ConTeXt: numbered lines we control (stable)

% ------------------------------------------------------------

\newcount\LineNo

\unexpanded\def\ResetLines{\global\LineNo=0}


% Print one logical line with a left line number:

\unexpanded\def\Line#1{%

\global\advance\LineNo by 1

\noindent

\llap{\tfxx\the\LineNo\quad}#1\par

}


% Add a word to glossary (key, form, gloss) with current line number:

\unexpanded\def\G#1#2#3{%

#2%

\ctxlua{moduledata.localgloss.add([==[#1]==],[==[#2]==],[==[#3]==],[==[\the\LineNo]==])}%

}


\unexpanded\def\LocalGlossary{%

\blank[big]\hrule\blank[small]

{\bf Glossaire local (par ordre d'apparition)}\par

\ctxlua{moduledata.localgloss.print()}%

}


\unexpanded\def\ResetLocalGlossary{%

\ctxlua{moduledata.localgloss.reset()}%

}


% ------------------------------------------------------------

% Document

% ------------------------------------------------------------

\starttext


\chapter{Aristotle, Nicomachean Ethics I.3 (1094b): numbered text + Lua glossary}


{\tfx\it

Text excerpt: Aristotle, \emph{Nicomachean Ethics} I.3 (1094b–1095a).

Pedagogical model and vocabulary layout inspired by Geoffrey Steadman’s PDF

(\emph{Aristotle’s Nicomachean Ethics Book I}, 2025-01).

}\par


\blank[medium]


\ResetLines

\ResetLocalGlossary


% --- Greek passage, broken into stable "edition-like" lines.

% Glossary calls: \G{key}{form}{gloss}

% (Key can be normalized/lemmatized; form is what prints in the text.)


\Line{οὖν \G{μέθοδος}{μέθοδος}{inquiry, investigation, method}

τούτων \G{ἐφίημι}{ἐφίεται}{aim at (middle; takes gen.)},

πολιτικὴ τις οὖσα.}


\Line{Λέγοιτο δ᾽ ἂν \G{ἱκανῶς}{ἱκανῶς}{adequately, sufficiently},

εἰ κατὰ τὴν \G{ὑποκείμενος}{ὑποκειμένην}{underlying, assumed}

\G{ὕλη}{ὕλην}{matter, material}

\G{διασαφέω}{διασαφηθείη}{be made clear, be explained}.}


\Line{τὸ γὰρ \G{ἀκριβής}{ἀκριβὲς}{exact, precise}

οὐχ ὁμοίως ἐν ἅπασι τοῖς \G{λόγος}{λόγοις}{accounts, arguments}

\G{ἐπιζητητέος}{ἐπιζητητέον}{to be sought}.}


\Line{ὥσπερ οὐδ᾽ ἐν τοῖς \G{δημιουργέω}{δημιουργουμένοις}{things made by art, fabricated}.}


\Line{τὰ δὲ \G{καλός}{καλὰ}{noble, fine}

καὶ τὰ \G{δίκαιος}{δίκαια}{just things},

περὶ ὧν ἡ πολιτικὴ \G{σκοπέω}{σκοπεῖται}{examines, considers},

πολλὴν ἔχει \G{διαφορά}{διαφορὰν}{difference}

καὶ \G{πλάνη}{πλάνην}{wandering, fluctuation}.}


\Line{ὥστε δοκεῖν \G{νόμος}{νόμῳ}{by law, by convention}

μόνον εἶναι, \G{φύσις}{φύσει}{by nature}

δὲ μή.}


\Line{τοιαύτην δέ τινα \G{πλάνη}{πλάνην}{fluctuation}

ἔχει καὶ τἀγαθὰ διὰ τὸ πολλοῖς συμβαίνειν \G{βλάβη}{βλάβας}{harms}

ἀπ᾽ αὐτῶν.}


\Line{ἤδη γάρ τινες \G{ἀπόλλυμι}{ἀπώλοντο}{perished}

διὰ \G{πλοῦτος}{πλοῦτον}{wealth},

ἕτεροι δὲ δι᾽ \G{ἀνδρεία}{ἀνδρείαν}{courage}.}


\Line{\G{ἀγαπητός}{ἀγαπητὸν}{desired, desirable}

οὖν περὶ τοιούτων καὶ ἐκ τοιούτων λέγοντας

\G{παχυλῶς}{παχυλῶς}{roughly, coarsely}

καὶ \G{τύπος}{τύπῳ}{in outline, as a sketch}

τἀληθὲς \G{ἀληθής}{ἀληθές}{true}

\G{ἐνδείκνυμι}{ἐνδείκνυσθαι}{to point out, show}.}


\Line{καὶ περὶ τῶν ὡς ἐπὶ τὸ πολὺ καὶ ἐκ τοιούτων λέγοντας

τοιαῦτα καὶ \G{συμπεραίνω}{συμπεραίνεσθαι}{to conclude}.}


\Line{τὸν αὐτὸν δὴ τρόπον καὶ \G{ἀποδέχομαι}{ἀποδέχεσθαι}{to accept}

χρεὼν ἕκαστα τῶν λεγομένων.}


\Line{\G{πεπαιδευμένος}{πεπαιδευμένου}{educated person}

γάρ ἐστιν ἐπὶ \G{τοσοῦτος}{τοσοῦτον}{so far, to such an extent}

τἀκριβὲς \G{ἐπιζητέω}{ἐπιζητεῖν}{to seek}

καθ᾽ ἕκαστον \G{γένος}{γένος}{kind, class}.}


\Line{ἐφ᾽ ὅσον ἡ τοῦ \G{πρᾶγμα}{πράγματος}{matter, affair}

\G{φύσις}{φύσις}{nature}

\G{ἐπιδέχομαι}{ἐπιδέχεται}{admits, allows}.}


\Line{παραπλήσιον γὰρ φαίνεται \G{μαθηματικός}{μαθηματικοῦ}{mathematician}

τε \G{πιθανολογέω}{πιθανολογοῦντος}{using probable arguments}

ἀποδέχεσθαι,}


\Line{καὶ \G{ῥητορικός}{ῥητορικὸν}{rhetorician}

\G{ἀπόδειξις}{ἀποδείξεις}{proofs}

\G{ἀπαιτέω}{ἀπαιτεῖν}{to demand} (sc. from him).}


\Line{ἕκαστος δὲ \G{κρίνω}{κρίνει}{judges}

καλῶς ἃ \G{γιγνώσκω}{γινώσκει}{knows},

καὶ τούτων ἐστὶν ἀγαθὸς \G{κριτής}{κριτής}{judge}.}


% --- Glossary at end of unit:

\LocalGlossary


\stoptext




Le 31/01/2026 à 00:17, Jean-Pierre Delange via ntg-context a écrit :

Hi Pablo,

I don’t claim to have a definitive answer here — I’ve simply been experimenting over the past months, trying to understand what is structurally involved, and I thought a small MWE prototype might help clarify things.

What I currently have is very simple. In the running text, each annotated word sends its information into one of several note channels (for example: /critical/, /explanatory/, /translation/). These notes are not printed immediately as footnotes; instead they are stored (|location=text|). At the end of the textual unit, they are printed under explicit headings:

 *

    Critique

 *

    Explicatif

 *

    Traduction

So the headings themselves are just empty containers: what fills them is whatever has been collected earlier via |\critnote|, |\explnote|, |\transnote| (or small wrapper macros around them).

Conceptually, this gives something like:

 *

    the main text = reading flow

 *

    several parallel annotation streams = editorial layers

 *

    a final “apparatus” that replays each stream separately

From there, moving toward a glossary seems to be only one extra step: instead of (or in addition to) sending content into notes, the same data can also be sent into a small Lua bucket (key → lemma + gloss), reset per block, and printed as a local vocabulary list. That gives a “collect → print → reset → repeat” pattern, close to the glossary developed by Steadman.

What strikes me, while experimenting, is the specific difficulty of /short textual units/ (chapters, sections, passages). In that case the glossary is not global by nature: it is local, temporary, and tightly coupled to the immediately preceding text. This feels quite different from classical registers, which assume long-range accumulation across a whole document. So my tentative impression (very provisional) is that these local reading aids solve a different problem than global indexes:

 *

    ordered by appearance,

 *

    scoped to a block,

 *

    frequently reset,

 *

    aimed at immediate pedagogical use.

Once this distinction is made, it becomes easier to see why standard registers don’t quite fit this use case, and why a lightweight Lua layer may help — though I’m still very much in exploration mode here.

See below the MWE file I’m playing with (I will deliver another MWE later with a glossary extracted with Lua code).

Best,
JP

% notes-critiques-example-v2.tex

% Grec 1 colonne — notes multi-niveaux stockées (location=text)

% puis imprimées sous rubriques via \placenotes


\mainlanguage[agr]

\setuppapersize[A4]


\definefontfamily[mainface][serif][Libertinus Serif]

\setupbodyfont[mainface,11pt]


\setuplayout[

topspace=1.5cm,

backspace=2cm,

width=middle,

header=1cm,

footer=1.2cm,

height=middle,

]


\setupalign[hz,hanging]

\setuptolerance[verytolerant]


% --- Trois séries de notes

\definenote[critnote]

\definenote[explnote]

\definenote[transnote]


% IMPORTANT : on STOCKE les notes au fil du texte (pas en bas de page)

% pour pouvoir les imprimer ensuite en fin d'unité.

\setupnote[critnote] [location=text, number=no, style=\tfxx]

\setupnote[explnote] [location=text, number=no, style=\tfxx]

\setupnote[transnote][location=text, number=no, style=\tfxx]


% --- Macros : forme (#1), lemme (#2), glose (#3)

\unexpanded\def\Gcrit#1#2#3{#1\critnote{\emph{#1} (\emph{#2}).~#3}}

\unexpanded\def\Gexpl#1#2#3{#1\explnote{\emph{#1} (\emph{#2}).~#3}}

\unexpanded\def\Gtrans#1#2#3{#1\transnote{\emph{#1} (\emph{#2}).~#3}}


% --- Apparat final (fin d'unité) : ICI les rubriques se remplissent

\unexpanded\def\ApparatNotes{%

\blank[big]\hrule\blank[small]

{\bf Critique}\par

\placenotes[critnote]

\blank[small]

{\bf Explicatif}\par

\placenotes[explnote]

\blank[small]

{\bf Traduction}\par

\placenotes[transnote]

}


\starttext


\chapter{Exemple — Rubriques "critique", "explicatif", "traduction"}


τὰ \Gexpl{ὁμώνυμα}{ὁμώνυμος}{Même nom, mais définition (λόγος) différente.}

λέγεται, ὅταν τὸ \Gexpl{ὄνομα}{ὄνομα}{Le “nom” au sens de signifiant commun.} μόνον κοινόν ᾖ,

ὁ δὲ τῆς \Gexpl{οὐσίας}{οὐσία}{Ici : “essence / être” selon le contexte ; terme philosophiquement chargé.}

\Gexpl{λόγος}{λόγος}{Souvent : formule définitionnelle (pas “raison” au sens moderne).}

ἕτερος.


\blank[line]


τὰ δὲ \Gexpl{συνώνυμα}{συνώνυμος}{Même nom et même définition ; opposition structurante avec ὁμώνυμα.}

ὧν καὶ τὸ ὄνομα κοινόν, καὶ ὁ τῆς οὐσίας λόγος ὁ αὐτός·

τὰ δὲ \Gexpl{παρώνυμα}{παρώνυμος}{Dérivés : mots formés à partir d’un autre (nom → adjectif, etc.).}

ἀπὸ τῶν ὀνομάτων μετωνομασμένα.


% --- Critique (leçon / édition)

\Gcrit{ἕτερος}{ἕτερος}{Variante de leçon selon les éditions : à harmoniser avec l’édition de référence.}

\Gcrit{μετωνομασμένα}{μετονομάζω}{Graphie/accents parfois divergents : vérifier la normalisation polytonique adoptée.}


% --- Traduction (français)

\Gtrans{ὁμώνυμα}{ὁμώνυμος}{Souvent “homonymes”, mais la logique est celle des “équivoques” (nom commun, définition différente).}

\Gtrans{λόγος}{λόγος}{Ici : “définition” ; ailleurs : “raison”, “discours”, “compte”.}

\Gtrans{παρώνυμα}{παρώνυμος}{“Paronymes” est technique ; “dérivés” est plus lisible selon le public.}


% --- Impression en fin d'unité : rubriques remplies

\ApparatNotes


\stoptext



Le 30/01/2026 à 18:45, Pablo Rodriguez via ntg-context a écrit :
On 1/30/26 10:47, Mikael Sundqvist wrote:
Hi Pablo,

Not sure I understand what you are after with the notes. Does this do
what you want?
Hi Mikael,

I‘m afraid I only get the same as with my sample.

I would like to have invidiual notes synced, such as with these body texts:

     \definecolumnset
       [parallel]
       [n=1,
        align={lesswidows,lessclubs,lessbroken,lessorphans}]

     \definesubcolumnset[parallel][L][1]
     \definesubcolumnset[parallel][R][2]

     \startsetups balance:example
         \balancetopskip    \strutht
         \balancebottomskip \strutdp
     \stopsetups

     \starttext
     \page[even]
     \startcolumnset[parallel]
       \dorecurse{50}
       {\startsubcolumnset[L]
         \samplefile{knuth}
       \stopsubcolumnset
       \startsubcolumnset[R]
         \samplefile{zapf}
       \stopsubcolumnset
       \flushsubcolumnsets[spread]}
     \stopcolumnset
     \stoptext

I wonder wether this is possible.

Many thanks for your help,

Pablo
___________________________________________________________________________________
If your question is of interest to others as well, please add an entry to the 
Wiki!

maillist :[email protected] 
/https://mailman.ntg.nl/mailman3/lists/ntg-context.ntg.nl
webpage  :https://www.pragma-ade.nl /https://context.aanhet.net (mirror)
archive  :https://github.com/contextgarden/context
wiki     :https://wiki.contextgarden.net
___________________________________________________________________________________

___________________________________________________________________________________
If your question is of interest to others as well, please add an entry to the 
Wiki!

maillist :[email protected] 
/https://mailman.ntg.nl/mailman3/lists/ntg-context.ntg.nl
webpage  :https://www.pragma-ade.nl /https://context.aanhet.net (mirror)
archive  :https://github.com/contextgarden/context
wiki     :https://wiki.contextgarden.net
___________________________________________________________________________________
___________________________________________________________________________________
If your question is of interest to others as well, please add an entry to the 
Wiki!

maillist : [email protected] / 
https://mailman.ntg.nl/mailman3/lists/ntg-context.ntg.nl
webpage  : https://www.pragma-ade.nl / https://context.aanhet.net (mirror)
archive  : https://github.com/contextgarden/context
wiki     : https://wiki.contextgarden.net
___________________________________________________________________________________

Reply via email to