Hugo Multi-Lang: Interne Permalinks
Inhalt
Wer in Hugo wie ich einen mehrsprachigen Blog baut, könnte folgendes Problem kennen:
Interne Links in Artikeln verschiedener Sprachen verweisen nur auf Seiten in der Standard-Sprache.
Konfiguration#
Grundlage für das Problem ist die folgende Einstellung:
# hugo.yml
defaultContentLanguage: "de"
defaultContentLanguageInSubdir: false
disableDefaultLanguageRedirect: true
Meine Standardsprache ist Deutsch und zugehörige Artikel werden in das Hauptverzeichnis des Blogs gebaut. Eine Weiterleitung in das Sprachverzeichnis /de findet nicht statt.
Die Verzeichnisstruktur lautet:
├── content
│ ├── de
│ │ ├── about.md
│ │ ├── announcements
│ │ ├── legal.md
│ │ ├── posts
│ │ ├── privacy.md
│ │ └── projects
│ └── en
│ ├── about.md
│ ├── legal.md
│ ├── posts
│ ├── privacy.md
│ └── projects
Das Problem mit der Standardsprache#
Ich halte Artikel durch Nutzung von slug in meiner Standardsprache unter Adressen erreichbar wie example.com/my-post, example.com/my-2nd-post/.
In der Zweitsprache hingegen erscheinen sie als example.com/en/my-post, example.com/en/my-2nd-post/.
Wenn ich zwei Artikel nun intern verlinken möchte, dann verweist der Link stets auf den Artikel meiner Standardsprache.
# English version of my post
# example.com/en/my-post
---
slug: my-post
---
Post's English content
[2nd](/my-2nd-post)
Ich erreiche damit statt des gewünschten /en/my-2nd-post nur die deutsche Version /my-2nd-post. Das finde ich blöd, denn so müsste ich jeden Link in allen Artikeln manuell anpassen. Leider scheint Hugo dafür keine eingebaute Lösung mitzubringen. Also erstelle ich mir eine Vorschrift, wie Links gerendert werden sollen:
{{- /* /layouts/_markup/render-link.html */ -}}
Lösung: Sprachabhängige Link-Prefixes#
Dort soll folgendes passieren: Wenn beim Bauen ein relativer Link innerhalb der Seite gefunden wird, so ist genau dann ein Sprachpräfix wie /en vor diesen Link zu setzen, wenn die Sprache der zu rendernden Seite nicht der Standardsprache entspricht (denn für letztere benötige ich wegen defaultContentLanguageInSubdir: false keinen Sprachpräfix).
Standardsprache auslesen#
Nur wie komme ich an die Standardsprache? Die Parameter defaultContentLanguage sind in Templates nicht zugänglich. Nach längerer Recherche finde ich eine passende Lösung für das Problem in der Hugo community: Die Standardsprache ist meist die erste Sprache des gewichtsbasierten Index (welcher die Sprachen nach Reihenfolge erscheinen lässt): index .Page.Site.Home.AllTranslations 0 Wenn ich diesen nun auslese, ist die “erste” meine Standardsprache.
Code für render-link.html#
{{- /* /layouts/_markup/render-link.html */ -}}
{{- $url := .Destination | safeURL -}}
{{- $currentLang := .Page.Language.Locale -}}
{{- $is_external := or (strings.HasPrefix $url "http://") (strings.HasPrefix $url "https://") -}}
{{- $is_anchor := strings.HasPrefix $url "#" -}}
{{- $is_asset := strings.HasPrefix $url "/assets/" -}}
{{- $is_lang_prefixed := strings.HasPrefix $url (printf "/%s/" $currentLang) -}}
{{- if $is_anchor -}}
{{- $url = printf "%s%s" .Page.RelPermalink $url -}}
{{- else -}}
{{- if not $is_external -}}
{{- if and (not $is_lang_prefixed) (not $is_asset) (not .Page.Language.IsDefault) -}}
{{- $url = printf "/%s%s" $currentLang $url -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- /* Add / to internal paths that have no file extension */ -}}
{{- if and (not $is_external) (not $is_anchor) (not $is_asset) -}}
{{- $parsed := urls.Parse $url -}}
{{- if and $parsed (not (strings.HasSuffix $parsed.Path "/")) (eq (path.Ext $parsed.Path) "") -}}
{{- $url = printf "%s/" $url -}}
{{- end -}}
{{- end -}}
<a href="{{ $url | relURL }}"{{ if $is_external }} target="_blank" rel="noopener noreferrer" class="external-link"{{ end }}>
{{- .Text | safeHTML -}}
{{- if $is_external -}}
<span class="external-link-icon" aria-hidden="true">↗</span>
{{- end -}}
</a>
Solange die Standardsprache und der Gewichtsindex übereinstimmen, d.h. die Standardsprache ganz oben auftaucht, funktioniert dies prima.
Zusatzfunktionen#
Was der Code sonst noch macht:
- Prüfung, ob Links nach “extern”, also von meiner Site weg gehen. Jene bekommen dann ein eigenes Fenster und den Indikator
↗. - Prüfung, ob der Link auf eine Überschrift “anchor” führt. Details dazu siehe unten.
- Prüfung, ob der Link zu einer (Medien-)Datei “asset” führt. Asset-Links werden nicht modifiziert.
Handelt es sich um einen Überschriftslink, so wird dieser gegebenenfalls auf die vollständige URL ergänzt. Das geschieht nach folgendem Schema: #heading --> /<opt_lang>/<opt_category>/<title>/#heading
Richtig auflösende Permalinks auf Überschriften#
Auch für hochspezielle Permalinks innerhalb einer Seite habe ich eine Lösung gefunden. Nehmen wir an, ich habe verschiedensprachige Überschriften :
---
# German
# content/de/posts/yyyy-mm-dd-hugo-create-language-specific-permalinks.md
description: Mit _markup interne Links richtig routen
title: "Hugo Multi-Lang: Interne Permalinks"
slug: hugo-create-language-specific-permalinks
---
## Richtig auflösende Permalinks auf Überschriften {#permalinks}
Lorem Ipsum...
---
# English
# /content/en/posts/yyyy-mm-dd-hugo-create-language-specific-permalinks.md
description: Routing internal links with _markup
title: "Hugo Multi-Lang: Internal Permalinks"
slug: hugo-create-language-specific-permalinks
---
## Correct Permalinks to headings {#permalinks}
Lorem Ipsum...
Dann sollte ein Klick auf diesen Link zurück zur Überschrift #permalinks sprachunabhängig wieder zurück zur Überschrift führen. Schlüssel für diese Lösung ist das Einführen einer Überschriftsdefinition (Heading-ID) in Markdown, in diesem Fall {#permalinks}.
Funktioniert so noch nicht.#
Weil #permalinks auf <blog's baseurl>/#permalinks verweist und somit der Link auf die aktuelle Seite fehlt. Doch auch das bekommen wir hin: Ich erweitere den Link-Renderer um ein paar Zeilen Code und gebe die aktuelle Seiten-URL als Präfix hinzu.
Code für render-link.html mit Unterstützung für Überschriften#
{{- $is_anchor := strings.HasPrefix $url "#" -}}
{{- if $is_anchor -}}
{{- $url = printf "%s%s" .Page.RelPermalink $url -}}
{{- end -}}
Nun wird (#permalinks)erweitert zu /hugo-create-language-specific-permalinks/#permalinks, sodass der Link endlich zum Ziel führt.