A Hugo module with partials to easily include Nostr events and associated metadata as content in your Hugo generated website.
The module sources the events and metadata from the data/nostr directory of your Hugo project,
assuming they are organized in the same way as the huno
utility does. The partials can then be called inside content adapters
to create content from that data. Multilingual capability is also possible with a certain tagging
scheme of the Nostr events (detailed below).
These programs must be installed:
For demonstration purposes, start with a new Hugo project:
hugo new site mysite
cd mysite
Follow the quickstart of Huno to initialize data in the
data/nostr directory.
Then we can add the module as a theme for simplicity and use some existing theme like ananke for example:
git clone https://github.com/straumer/hugo-nostr.git themes/hugo-nostr
git clone https://github.com/theNewDynamic/gohugo-theme-ananke.git themes/ananke
Add them in hugo.toml:
theme = ["hugo-nostr", "ananke"]
Add an about page in content/_content.gotmpl:
{{ partial "nostr/profile.html" (dict
"path" "about"
"ctx" .
) }}
Add your kind 1 (notes) and kind 30023 (long-form) events in content/posts/_content.gotmpl:
{{ partial "nostr/events.html" (dict
"urlSections" "posts"
"ctx" .
)}}
Then just run hugo server and your kind 1 (notes) and kind 30023 (long-form) should be displayed at
http://localhost:1313. Go to /posts to see all the events. Go to /about to see the kind 0 (profile)
content.
This should cover some basics on how the module can be used. Check out the layouts/_partials/nostr
directory for more partials to use or get an idea about how to make your own. It's not very
documented at the moment unfortunately.
In order for the multilingual functionality to work correctly, the Nostr events need to conform to the following scheme.
Kind 0 events will have an optional lang field in the content's JSON:
{
"about": "I am John and I am a carpenter",
"name": "John Doe"
"lang": {
"es": {
"about": "Yo soy John y yo soy carpintero"
},
"is": {
"about": "Ég er John og ég er trésmiður"
}
}
}
The languages (es, is ...) are from the ISO-639-1 namespace. When the given language is being used during site generation
matches one of those fields, the corresponding lang object is merged with the content's JSON. In case of es, the content's
JSON would become:
{
"about": "Yo soy John y yo soy carpintero",
"name": "John Doe"
}
If no language is found, the default fields will simply be used.
Other multilingual events, such as kind 1 and kind 30023 have different language versions as separate events. They must
have these tags:
["L", "ISO-639-1"]
["l", "<language>", "ISO-639-1"]
["lgid", "<unique-id>", "ISO-639-1"]
language here can be, as before, one of en, es, is and so on. lgid is a unique "label group id" in the context of
the event's kind and pubkey, and the events that are different language versions of the same content have the same lgid.
We refer to such events as an event group.
Each tag type can correspond to a taxonomy, and such tags are only picked
from the event with the l tag as the default language. The tags of a given tag type are linked to their translating tags in the
other events of a different language in the order that they appear. As an example with the tag types t and category used for
taxonomy and the languages en, es and is, with en as the default language, we would have three events linked by lgid:
event1
["L", "ISO-639-1"]
["l", "en", "ISO-639-1"]
["lgid", "some-unique-id", "ISO-639-1"]
["t", "aliens"]
["t", "predators"]
["category", "movie-review"]
event2
["L", "ISO-639-1"]
["l", "es", "ISO-639-1"]
["lgid", "some-unique-id", "ISO-639-1"]
["t", "alinigenas"]
["category", "revista-de-pelicula"]
["t", "depredadores"]
event3
["L", "ISO-639-1"]
["l", "is", "ISO-639-1"]
["lgid", "some-unique-id", "ISO-639-1"]
["category", "kvikmyndaumsögn"]
["t", "geimverur"]
["t", "rándýr"]
The resulting taxonomy, terms and translations in Hugo would be:
| taxonomy | term | es translation | is translation |
|---|---|---|---|
| tag | aliens | alinigenas | geimverur |
| tag | predators | depredadores | rándýr |
| category | movie-review | revista-de-pelicula | kvikmyndaumsögn |
So far, the partials just account for the tag and category taxonomies. Adding to the quickstart above, we could add the
translated events, tags and paths like this:
Add languages to hugo.toml:
[languages]
[languages.en]
[languages.es]
[languages.is]
Add an about page in content/_content.es.gotmpl:
{{ partial "nostr/profile.html" (dict
"path" "about"
"title" "Sobre mí"
"ctx" .
) }}
and content/_content.es.gotmpl:
{{ partial "nostr/profile.html" (dict
"path" "about"
"title" "Um mig"
"ctx" .
) }}
Add your kind 1 (notes) and kind 30023 (long-form) events in content/posts/_content.es.gotmpl:
{{ partial "nostr/events.html" (dict
"urlSections" "postes"
"ctx" .
)}}
and content/posts/_content.is.gotmpl:
{{ partial "nostr/events.html" (dict
"urlSections" "faerslur"
"ctx" .
)}}
Then you can add the tag translations to content/tags/_content.es.gotmpl:
{{ partial "nostr/tags.html" (dict
"urlSections" "etiquetas"
"ctx" .
)}}
and content/tags/_content.is.gotmpl:
{{ partial "nostr/tags.html" (dict
"urlSections" "togg"
"ctx" .
)}}
Finally the category translations go to content/categories/_content.es.gotmpl:
{{ partial "nostr/categories.html" (dict
"urlSections" "categorias"
"ctx" .
)}}
and to content/categories/_content.is.gotmpl:
{{ partial "nostr/categories.html" (dict
"urlSections" "flokkar"
"ctx" .
)}}
Also add similar to the frontmatter of these files for url and title translations:
content/posts/_index.es.md
content/posts/_index.is.md
content/tags/_index.es.md
content/tags/_index.is.md
content/categories/_index.es.md
content/categories/_index.is.md
This is a bit more involved at first, but it should be easy to keep up this scheme if one follows the tagging rules on the Nostr events.
- Download images in content and link to them internally during site generation.
- Account for content and more in user metadata tags.
- Convert nostr links to internal events as internal http links.
- Make audio file links show up as playable widgets.
- Make video file links show up as playable widgets.