Auto-generate glossaries With Hugo
Last updated: Mar 24, 2024
Store your definitions in a data file, and use Hugo shortcodes to create glossaries and tooltips from a single source of truth.
Hugo can use data files, like JSON and YAML, to make templates. This feature is often useful for technical writing:
- The data file can serve as a source of truth, and writers can use its properties wherever they please.
- For long repetitive structures, the format needs to be written only once in the shortcode, rather than once for every item.
This topic shows some shortcodes that use a YAML file as a single source of terminology truth in a Hugo site. These templates save some formatting tedium and ensure that terminology definitions stay consistent across different pages and presentations.
Steps to generate a glossary
The procedure is as follows:
- Write the glossary to something like
data/termbase.yaml
. - Write a template that reads the YAML file and makes a formatted list.
- Extend its usefulness by adding however many properties you want to the data file.
Write the YAML file
First create the file to be your source of truth:
- In the
data
directory, create a YAML file called something likedata/termbase.yaml
. - Create an array of term objects. For a glossary, you need at least
term
anddefinition
properties (choose whatever key names you want). - Optionally, add other properties to extend the templating possibilities (subsequent sections give examples).
Alphabetizing the list is probably a good idea, but the sort
function can do that for you.
Write the glossary template
Next, write a template to generate a glossary page. This is a good time to use an HTML description list.
{{- $arg := (.Get 0) -}}
{{- range sort .Site.Data.termbase "term" -}}
<dl>
<dt>{{- .term }}{{ with .abbr }} ({{ . -}}){{ end -}}
</dt>
<dd>{{- .definition -}}</dd>
</dl>
{{- end -}}
In this particular template, if the term has an abbr
property, it prints an abbreviation in parenthesis next to the definition.
But it works fine without abbreviations, too. Feel free to copy and paste.
Use the shortcode in a page
This shortcode renders the preceding YAML file as follows:
More uses of the termbase
With a single source of terminology truth, you can add more shortcodes to manage terminology across an entire site. Here are some ways.
Reuse the definitions in tooltips
Each entry in the preceding YAML file has properties for term
and definition
—that’s all the data you need to create a tooltip that expands a definition on hover.
I started using this quite often in my work on docs.rhize.com.
This shortcode takes one argument: either the term
or, if you have one, the abbreviation
.
It creates an HTML abbr
( or “tooltip”) on the page.
<!--- variables based on shortcode argument -->
{{- $entry := (.Get 0) -}}
<!-- range over data file -->
{{- range $.Site.Data.termbase -}}
<!-- Find matches -->
{{- if eq ( .abbr | lower) ( $entry | lower ) -}}
<abbr title="{{- .term -}}. {{ .definition | plainify -}}"> {{- .abbr -}}</abbr>
{{- else if eq ( .term | lower) ( $entry | lower ) -}}
<abbr title="{{ .definition | plainify -}}"> {{- $entry -}}</abbr>
{{- end -}}
{{- end -}}
When called, it prints the term along with a tooltip that expands its definition. For example:
Some knowledge of {{< abbr "tcp" >}}
is useful to anyone who works with networks.
Renders as:
Some knowledge of TCP is useful to anyone who works with networks.
To get even fancier, you could make an abbreviation that expands dynamically.
Make mini-glossaries
If you notice, most words in the termbase have a category
property.
You could use this to create mini glossaries.
This requires only two more evaluations in the preceding glossary creator:
- Accept one argument in the call
- Print the term and definition only if its
category
property matches the argument.
{{- $entry := (.Get 0) -}}
{{- range sort .Site.Data.termbase "term" -}}
{{- if (eq .category $entry) -}}
- {{- .term }} {{ with .abbreviation }} ({{ . -}}) {{- end -}}
- {{- .definition -}}
{{- end -}}
{{- end -}}
Now I’ll make some mini-glosses:
#### Nature gloss:
{{< mini-gloss "nature" >}}
#### Protocol gloss:
{{< mini-gloss "protocols" >}}
#### Docs gloss:
{{< mini-gloss "docs" >}}
These render as follows:
Add supportive links to the glossary
Perhaps you want to link some glossary entries to their most relevant documentation topic.
For this, you can just add an array of links in a ReadMore
property, then modify the glossary shortcode to print them at the end of the html <dd>
.
Ways to improve
These shortcodes are unsophisticated. I wouldn’t be surprised to learn of ways to write the logic more efficiently or separate concerns more elegantly within the Hugo architecture. Still, I have used or am actively using all these strategies in my professional work, and they’ve worked well enough.
But I’d be happy to learn of better ways, and I’ll update this doc to acknowledge any help. Some ideas to iterate:
- In the mini-gloss, make the
categories
property an array so one term could display in multiple mini-glossaries. I think this involves managing the state of an outer context from an inner one. - Give the abbreviation smarter capitalization.
At some point, a huge YAML file would be a pain to maintain: I wouldn’t want to write a whole dictionary like this. Some ideas to improve the UX:
- Add validation of the data file.
- Combine multiple files into one gloss (the opposite of the mini-gloss example)
- Add some interface to manage entries without directly editing the data file
Hugo also has an open issue to add glossaries, so maybe this doc will be outdated someday. Either way, I like the flexibility of using a data file to manage terms.