Static translate
Last time in the series on Hugo, we looked at using the ‘Bootstrap’ framework, in this post we’ll continue with our site and look at translation. In our example, I’ve chosen Welsh purely down to a part of my ancestry.
Disclaimers
Names / logos can be trademarks of their respective owners. Please review their website for details. I am independent from the organisations mentioned and am in no way writing for or endorsed by them. The information presented in this article is written according to my own understanding, there could be inaccuracies, so please do undertake your own research. The featured image is my copyright, please don’t use without my permission.
Why
Being able to provide information in different languages allows you to engage with a greater audience that you want to reach out to.
Even though I’ve used ‘Google translate’ (translate.google.com), and ‘translated back’ to see if the meaning works both ways, you have control over the words as against the content being translated or interpreted by the reader.
How
Looking at ‘Multilingual mode’ (gohugo.io/content-management/multilingual) we need to make some changes to our site and theme. There are two approaches, file and directory. I’ve gone for the former so that I can reuse the same images and easily spot if content hasn’t been translated because there is not a version of the same file in the same folder.
To understand the changes I needed to make I also read:
- www.regisphilibert.com/blog/2018/08/hugo-multilingual-part-1-managing-content-translation/
- staticmania.com/blog/how-to-set-up-a-multilingual-website-in-hugo
- dev.to/aowendev/adding-languages-to-a-hugo-site-2gfj
- stackoverflow.com/questions/71387017/hugo-how-to-set-date-language
There are two aspects to the translation, the ‘md’ files that you translate within themselves and the ‘Templates’. With the templates, there is the concept of an ‘identifier’, the ‘key’ that is used as a ‘lookup’ to find the translation to be used in that place. I’ve used ‘i18n’, which is an alias of ‘lang.Translate’, see: gohugo.io/functions/lang/translate. ‘i18n’ is itself an abbreviated form of ‘internationalisation and localisation’, en.wikipedia.org/wiki/Internationalization_and_localization.
The process itself was one of trial and error, so here I’ll describe the changes since the last post, which can be seen on github.com/gjb2048/hugo-elw/compare/Feb_2024…Feb_2025. Note that there is an unrelated change with ‘resources.ToCSS’ to ‘css.Sass’ due to improvements in Hugo itself, and a few other ‘tidy’ up changes. Everything else I’ll describe.
Looking at ‘hugo.toml’:
As there is more than one language, then “languageCode = ‘en-gb’” changes to “defaultContentLanguage = ‘en-gb’”, and we remove “title = ‘eLearningWorld Example Hugo Site’” from the top level.
Next we tell Hugo about the languages:
[languages] [languages.en-gb] disabled = false languageCode = 'en-gb' languageDirection = 'ltr' languageName = 'English' title = 'eLearningWorld Example Hugo Site' weight = 1 [languages.cy] disabled = false languageCode = 'cy' languageDirection = 'ltr' languageName = 'Cymraeg' title = 'Enghraifft eDdysguByd Safle Hugo' weight = 2
This is where the site title can now be defined. Next we change the ‘name’s to ‘identifier’s where needed in the menu definitions so that they show the correct value for each language, such as “name = ‘Home’” to “identifier = ‘home’”. I’ve gone for the ‘Use translation tables’ (gohugo.io/content-management/multilingual/#use-translation-tables) method as I don’t like duplication. The identifier values need to then be defined in the ‘i18n’ folder (gohugo.io/getting-started/directory-structure/#i18n), with ‘en-gb.toml’ being:
about = "About" elearningworld = "eLearningWorld" home = "Home" posts = "Posts" terms = "Terms" and ‘cy.toml’ being: about = "Ynghylch" elearningworld = "eDdysguByd" home = "Cartref" posts = "Pyst" terms = "Termau"
The theme also has its ‘en-gb.toml’ being:
generated = "Generated" rssfeed = "RSS Feed" translations = "Translations"
and ‘cy.toml’ being:
generated = "Cynhyrchwyd" rssfeed = "Porthiant RSS" translations = "Cyfieithiadau"
which are used in the layouts, such as “i18n “rssfeed”” in the ‘navbar.html’ partial.
Next we change the layouts to use ‘or (i18n .Identifier) .Name’ instead of just ‘.Name’ so that the identifier is used instead of the ‘Name’ if it exists, such as in the ‘navbar.html’ partial.
As the language specific URL’s have the language code, then we need to get them correct by ‘piping’ (gohugo.io/templates/introduction/#pipes) the output of ‘.URL’ (gohugo.io/methods/pager/url) which gives the name and path of the page into ‘absLangURL’ (gohugo.io/functions/urls/abslangurl) or ‘RelLangURL
’ (gohugo.io/functions/urls/rellangurl) as needed. For example ‘href=”{{ .URL | absLangURL }}”’. This can also be seen in the ‘navbar.html’ partial.
In the ‘list-item.html’ partial, even though Wales has the same date format and timezone with the rest of the United Kingdom, we change:
{{ .Date.Format (.Site.Params.dateFormat | default "Monday, 2 January 2006") | .Scratch.Set "subheader" }}
to:
{{ .Date | time.Format ":date_full" | .Scratch.Set "subheader" }}
References: gohugo.io/content-management/multilingual/#dates and gohugo.io/functions/time/format/#localization.
On the navigation bar, there needed to be a way to switch between the languages used. I also wanted to use an icon, as with the rest of the icons on that line, so with the images: www.iconfinder.com/icons/2634461/ensign_flag_nation_wales_icon and www.iconfinder.com/icons/2634450/ensign_flag_kingdom_nation_icon – both are ‘CC BY 3.0 Deed Attribution 3.0 Unported, https://creativecommons.org/licenses/by/3.0/ – Hopnguyen Mr, www.iconfinder.com/Mr-hopnguyen’ in the ‘static’ folder:

with the code to show the links:
{{ range $.Site.Home.AllTranslations }} <li> <a class="nav-link" href="{{ .Permalink }}"> <span class="icon" title="{{ .Language.LanguageName }}" aria-hidden="true"> <img src="{{ print .Language.Lang "_nation_icon.png" | absURL }}"> </span> <span class="sr-only">{{ .Language.LanguageName }}</span> </a> </li> {{ end }}
Technically the Union Jack represents all nations within the United Kingdom, including Wales. But as English is spoken in all, then the Saint George’s Cross would be restrictive and less familiar.
In the ‘footer.html’ example, there is an additional line of:
{{ partial "i18nlist.html" . }}
This new partial is there to reference a translation of a page if it exists, instead of switching language and finding it. This is also useful to know if a page has, as the server only lists the number of pages:

There is one specific English and one specific Welsh page, so I’m not sure what is going on but does seem to be okay, though could be something to do with gohugo.io/content-management/multilingual/#bypassing-default-linking. The code I’ve used for this, is:
{{ if .IsTranslated }} <h4>{{ i18n "translations" }}</h4> <ul class="navbar-nav flex-row"> {{ range .Translations }} <li> <a href="{{ .RelPermalink }}"> <span class="icon" title="{{ .Language.LanguageName }}" aria-hidden="true"> <img src="{{ print .Language.Lang "_nation_icon.png" | absURL }}"> </span> {{ .LinkTitle }} </a> </li> {{ end }} </ul> {{ end }}
Which I adapted from gohugo.io/content-management/multilingual/#reference-translated-content and produces:

In the partials ‘tags.html’ and ‘terms.html’ the URL’s need to be language specific. What I have not yet got my head around completely is the translation of the tags / terms. In that I do define specific content pages for the ‘index’ in ‘content/tags’ but have not created specific pages that explain a given tag. Looking at stackoverflow.com/questions/47984745/how-to-translate-tags-taxonomies-in-hugo which then links to gohugo.io/content-management/multilingual/#bypassing-default-linking then you’d have tag names all in one language, but then have tag content in each language with the tag named in the ‘one language’ with a ‘translationKey’ in the other to link them together. Perhaps something for another time, and to build upon this.
And that’s it… so far!
Upcoming
In the next few posts about Hugo, I intend to look at render hooks and publishing your site.
Conclusion
I hope that you’ve been able to understand how to translate your Hugo site.
You can get all of the code for this post from: github.com/gjb2048/hugo-elw/tree/Feb_2025 – please do have a go and tell me what you think. Pragmatically it has taken me a while to write this post and get my head around it. I’ve struggled in places and I’m not sure if everything is as good as it can be, so peer feedback / input would be beneficial.
- Network fonts – 16th March 2025
- Static translate – 16th February 2025
- Subsections – 16th January 2025