In Hugo, each language is an independent site In a multilingual website, each language is a site on the language dimension. This page explains how to configure it.
Configuration
Declare the languages your site uses in hugo.yaml:
defaultContentLanguage = 'en'
[languages.en]
label = 'English'
locale = 'en'
weight = 1
[languages.fr]
label = 'Français'
locale = 'fr'
weight = 2defaultContentLanguage: The default language. Content with no language tag belongs to this language.[languages.en],[languages.fr]: Used to match against a directory name or filename suffix (en,fr). Only an exact string match counts as the same language. If no matching string is found, Hugo falls back to the default language. Because Hugo always lowercases these keys before comparing them, you should always write your settings in lowercase1.weight: Determines the ordering of languages in menus and switchers.
Content Directory Structure
There are two ways to map content to a language. Pick one.
Language Suffix in the Filename
Same path, same filename, distinguished by a language code suffix:
content/
├── about.en.md
└── about.fr.mdHugo v0.161.0 also supports more flexible naming.
Separate Directories
Each language gets its own content directory, mapped through contentDir:
module:
mounts:
- source: content/en
target: content
- source: content/fr
target: contentcontent/
├── en/
│ └── about.md
└── fr/
└── about.mdContent at the same path and filename under two different language directories is treated as a translation of the same page.
Under the hood, contentDir sets up a module.mount for you. It's just syntactic sugar. The contentDir setting above is equivalent to this module configuration:
module:
mounts:
- source: content/en
target: content
- source: content/fr
target: contentWorth remembering the term module here. It's a powerful part of Hugo that we'll cover in detail in Hugo Modules.
Choosing Between the Two Structures
For a personal blog, the two approaches make no practical difference. If you want an actual comparison, here's hugo-community-docs's recommendation:
- Few languages, small amount of content: Use the filename suffix approach. It requires the least configuration.
- Many languages, or combined with other dimensions such as versioning (see Sites Matrix): Use separate directories. The structure is clearer, maps more directly to the underlying mount configuration, and is easier to extend later.
Developers who need more advanced functionality can refer to Hugo's official community discussion:
- Using the multidimensional content model to fill-in missing translations
- Do all page bundles need localized copies once you add a new language?
Page Relationships
Regardless of which structure you use, Hugo determines translation relationships by matching "same path plus same filename." If the path or filename differs, you need to manually set a matching translationKey in front matter, to force two pages to be linked as different language versions of the same page:
---
translationKey: 'about'
---Congratulations! if you've read this far, you're now a confident Hugo user. What follows leans toward development needs, so feel free to skip it.
The
localesetting is the exception, for the same reason explained in Basic Configuration. ↩︎