Jako że nie lubię redundancji kodu, chciałem stworzyć sobie konwencję do współdzielenia fragmentów kodu między wielojęzycznymi notkami (bo kod zawsze piszemy po angielsku 😉). Jak się okazuje, nie jest to takie oczywiste w Hugo.

Rozeznanie w temacie

Wydawać by się mogło, że sprawa jest banalnie prosta: tworzymy sobie jakiś katalog, odwołujemy się do umieszczonych tam plików z każdej wersji językowej i wyświetlamy w każdej to samo, a ewentualne zmiany robimy w jednym miejscu, widząc efekt w każdym. No nie bardzo 😉

Próbowałem różne podejścia, od headless bundles, przez leaf bundles, po shortcode‘y z użyciem .Site.GetPage (podawane przez różne artykuły). Zawsze coś nie działało i podejrzewam, że ma to związek z wybraną konwencją podziału treści na katalogi.

Gdy już zacząłem rozważać zmianę konwencji i wrzucenie wszystkiego w jeden katalog (i stosowanie *.<lang>.md dla wsparcia wielojęzyczności), w oko wpadła mi jeszcze jedna funkcja oferowana przez Hugo: readFile. Tutaj w końcu można było odwołać się do plików spoza bieżącego contentDir.

Implementacja skrawków

Określiłem sobie następującą strukturę plików:

hugo/
├─ content/
│  ├─ _snippets/
│  │  ├─ 2022/
│  │  │  ├─ 04/
│  │  │  │  ├─ some-post/
│  │  │  │  │  ├─ snippet1.md
│  │  │  │  │  ├─ snippet2.md
│  ├─ en/
│  │  ├─ 2022/
│  │  │  ├─ 04/
│  │  │  │  ├─ some-post.md
│  ├─ pl/
│  │  ├─ 2022/
│  │  │  ├─ 04/
│  │  │  │  ├─ jakis-post.md

Następnie stworzyłem shortcode do renderowania skrawków:

<!-- layouts/shortcodes/snippet.html -->

{{ $path := (printf "/content/_snippets/%s" (.Get 0))}}

{{ if eq (path.Ext $path) "" }}
    {{ $path = (printf "%s.md" $path)}}
{{ end }}

{{ if os.FileExists (path.Clean $path) }}
    {{ $page := os.ReadFile (path.Clean $path) }}

    {{ $page | markdownify }}
{{ else }}
    <div class="banner banner-danger">{{ i18n "invalid_snippet_reference" }}</div>
{{ end }}

Dzięki temu w postach mogę użyć:

{{</* snippet "2022/04/some-post/snippet1" */>}}

{{</* snippet "2022/04/some-post/snippet2.md" */>}}

Zaletą tego podejścia jest również fakt, że w przyszłych postach będę mógł odwoływać się do kodu użytego wcześniej poprzez zagnieżdżenie wybranego skrawka, bez konieczności powielania fragmentu kodu w kolejnym pliku. Czy są minusy? Okaże się w praktyce 😅