In this post I share the backlink functionality I have implemented for my blog. I have seen some other solutions out there, but none of them were complete, functional, and covered so many conditionals as this one I am presenting here. See, for example
- Implementing backlinks in a Hugo website, by Gabrielle Earnshaw.
- Parsing Backlinks in Hugo, by Scripter.
- Loseq-Schrodinger, by Aryan Sawhney.
- True Backlink Support in Hugo, by Wouter Groeneveld.
The biggest limitations I found were:
- None of them take into account multilingual sites.
- None of them take into account the use of Leaf Bundles as pages. In my case, if I have a post with images or any other content, I create a Leaf Bundle to have all the content related to a post in the same folder its content is. However, this means that the name of the file where the content is written is
index.es.md
, and therefore taking the name of this file means nothing to a backlinking routine.
Please, if you find some other condition under which this routine stops working, let me know.
Backlinks definition
Let’s assume we have a blog with four pages: page 1
, page 2
, page 3
, and page 4
. Furthermore, in pages 2 and 3 there are references (links) to page 1
. If we are standing in page 1
, there are two backlinks, one for page 2
and other for page 3
because they both have links to page 1
, they have forward-links. Therefore, a backlink to the current page is any other page which mentions it.
The code
This is the complete code I have made to solve this problem, to have backlinks in my posts. In the forthcoming section can be found the explanation of its parts.
|
|
Getting the name of the current post
In my code, I included something I have never seen before in others’, and it was the reason why no other solution worked for me. The problem arises when we have a stricture like the following in our blog:
content/
|--now/
| |--_index.en.md
| |--_index.es.md
|--legal/
| |--_index.en.md
| |--_index.es.md
|--posts/
| |--some-post-here.en.md
| |--another-post-there.es.md
| |--leaf-bundle-post/
| | |--index.en.md
| | |--index.es.md
| | |--picture.png
| | |--video.mp4
| |--_index.en.md
| |--_index.es.md
|--_index.en.md
|--_index.es.md
In this case you need to take into account two things:
- There are two languages.
- There are Leaf Bundles.
If during the code yo don’t use variables and properties which take into account this characteristics, you will never have a complete solution. It is possible that there are other conditions I have not taken into account here neither… only time will tell. If you find one, please write me back.
- If you have two or more languages, it is likely that your posts will be named with the language code:
post-1.en.md
,post-1.es.md
, andpost-1.fr.md
. However, when you make a link in one post to another, you don’t include the language code, as it is Hugo who takes charge of the correct language linking. For this reason we need to make use of the variable.File.TranslationBaseName
(which returnspost-1
etc.), and not.File.BaseFileName
which would returnpost-1.en
etc. The namepost-1.en
would never be found in the body of any other post. - If we are in a Leaf Bundle the
index
file means nothing for backlinking, therefore we need the.File.ContentBaseName
(see https://gohugo.io/variables/files/#examples), which would be in this case the real name of the post. In the example tree above, the.File.ContentBaseName
variable of theleaf-bundle-post
is notindex
, butleaf-bundle-post
. This is what we are looking for.
Solution
The first thing to do is to get the name of the current post, so we can later test against the content of all other posts and see if it appears there. If there is any post in which the present post is mentioned (with {{ ref "current-post-name" }}
in its body), then it means we need to add a backlink in this one (current-post-name
) making reference to the mention made in the other post.
First we make a variable $re
, and afterwards make use of the correct .File
variables:
- If
.File.TranslationBaseName
, that is, the name of the posts without taking into account neither the extension nor the language code, is equal toindex
, it means we are in a Leaf Bundle. In this case what we need to take as the string to search for in other posts is notindex
, but the name of the Leaf Bundle. To do that, it is necessary to use.File.ContentBaseName
. I have tested it, and it returns, indeed, the name of the last leaf or branch bundle. It is perfect for this use. - If
.File.TranslationBaseName
is not equal toindex
, there are two possibilities:- Either we are dealing with a post which has a name, it is regular content. In this case we have just to collect the name of the post and assign it to the variable
$re
using.File.TranslationBaseName
. - Or we are dealing with a Branch Bundle. This should not happen, as the branch bundles are not for content themselves (at least in my use-case). For this reason I just included another
if
to set the variable$re = "BranchBundle"
and skip later every time I see it set to this value.
- Either we are dealing with a post which has a name, it is regular content. In this case we have just to collect the name of the post and assign it to the variable
These conditions can be meet by a code like the following (I think 😉):
|
|
Conditions to be meet by a page to have backlinks
The way this functionality is being implemented, because it is embed into each page template, every page of the blog will have this code written in it. For this reason, taxonomy pages, the home page, the search page, etc. will all have a backlink section, and that is not desirable. What we want is to have only backlinks in the posts
.
Therefore, we only want to enter the cycle to get the backlinks if we are sure this page we are in must have a backlinks section. It makes no sense to enter the cycle for taxonomy pages, for the home page, etc., and then print nothing: that would consume time for no reason. To achieve this, before entering the cycle, and after having initialized $backlinks
to an empty slice
, it is necessary to write a conditional selecting for which pages are we interested in having a backlink section. That conditional could be something like this:
|
|
This means that $backlinks
will remain an empty slice
if:
- The current page is a Branch Bundle.
- The current page is not in
Section
posts
. - The current page is a
taxonomy
page, that is, tags and categories. - The current page is a
term
page, that is, a list of taxonomies (I think).
Notice that the last two conditionals are redundant, as those pages will never have a posts
Section
type. It is safe to remove that if you wish, and test 😉.
Cycle to get all posts
content and search for backlinks
The code first gets the name of the current page, then creates an empty variable (of type slice
) called $backlinks
, and finally checks if it would be desirable to have a backlinks section in the current page. At this point we are ready to search for the backlinks to this page.
The cycle ranges all pages which are not Branch Bundles (as it makes no sense to me to have a Branch Bundle with content I would like to backlink to) and who’s Section
type is posts
. In this cycle the content of each posts
page is searched for the name of the current page stored in the $re
variable . If the name of the current page appears in another one, then it means that we have found a backlink, that there is some other page mentioning this one, and it will be appended to the $backlinks
variable.
- If at the end of the cycle the variable
$backlinks
is still empty, it means that either:- There were not backlinks found,
- The current page is a Branch Bundle, or
- The current page is not a post, and therefore we don’t want to write a backlinks section on it.
- If at the end of the cycle the variable
$backlinks
is not empty, then it means we have found some backlinks. In this case you can range over the backlinks and print them the way you want.
Thanks for reading the post! Do not hesitate to write me an email, and share your point of view 😉: contact@poview.org
Backlinks
Backlinks redirects you to other posts mentioning this one you are in. Just like in Obsidian, Logseq, etc.