Cookbook: Hugo Multilingual Site
Set up Hugo's multilingual system with i18n-rosetta handling both JSON string files and Markdown content translation. This covers the complete workflow from project setup to production deployment.
What you'll build: A Hugo site with English, French, and Japanese — string translations via locale files, content translations via Markdown processing.
Project Structure
Hugo expects this layout for multilingual content:
my-hugo-site/
├── content/
│ ├── en/
│ │ ├── _index.md
│ │ ├── about.md
│ │ └── blog/
│ │ └── first-post.md
│ ├── fr/ ← rosetta generates these
│ └── ja/ ← rosetta generates these
├── i18n/
│ ├── en.json ← your source strings
│ ├── fr.json ← rosetta generates these
│ └── ja.json ← rosetta generates these
└── hugo.toml
Step 1: Configure Hugo
defaultContentLanguage = 'en'
[languages]
[languages.en]
languageName = 'English'
weight = 1
[languages.fr]
languageName = 'Français'
weight = 2
[languages.ja]
languageName = '日本語'
weight = 3
Step 2: Configure Rosetta
Rosetta needs two things configured: the locale file path (for JSON strings) and the content directory (for Markdown).
{
"version": 3,
"inputLocale": "en",
"localesDir": "./i18n",
"contentDir": "./content",
"model": "openai/gpt-4o-mini",
"pairs": {
"en:fr": { "method": "llm" },
"en:ja": { "method": "llm", "model": "openai/gpt-4o" }
},
"languages": {
"fr": { "name": "French", "register": "Formal (vous-form)" },
"ja": { "name": "Japanese", "register": "Polite/formal" }
}
}
Step 3: Create Source Content
String Translations (i18n/)
{
"nav": {
"home": "Home",
"about": "About",
"blog": "Blog",
"contact": "Contact"
},
"footer": {
"copyright": "© 2026 My Company. All rights reserved.",
"privacy": "Privacy Policy"
}
}
Markdown Content (content/en/)
---
title: "About Us"
description: "Learn more about our team and mission"
date: 2026-01-15
---
We build software that helps businesses communicate across languages.
Our platform supports **real-time translation** for over 30 languages,
with specialized support for low-resource languages.
## Our Mission
Language should never be a barrier to understanding.
## The Team
{{< team-grid >}}
Step 4: Run the Sync
npx i18n-rosetta sync
Rosetta processes both types:
- String files (
i18n/en.json→i18n/fr.json,i18n/ja.json) - Content files (
content/en/*.md→content/fr/*.md,content/ja/*.md)
Content Translation Details
When translating Markdown, rosetta automatically:
- Shields code blocks, shortcodes (
{{< ... >}}), inline code, and HTML - Translates front matter fields (
title,description,summary) - Preserves all other front matter fields (
date,draft,weight,tags) - Restores shielded blocks after translation
The Hugo shortcode {{< team-grid >}} passes through untranslated.
Step 5: Verify
# Preview the site
hugo server
# Check translation status
npx i18n-rosetta status
Navigate to localhost:1313/fr/ and localhost:1313/ja/ to review the translated content.
Step 6: Hugo Language Switcher
Add a language switcher to your Hugo layout:
<nav class="language-switcher">
{{ range $.Site.Home.AllTranslations }}
<a href="{{ .Permalink }}"
{{ if eq .Lang $.Site.Language.Lang }}class="active"{{ end }}>
{{ .Language.LanguageName }}
</a>
{{ end }}
</nav>
Keeping Content in Sync
When you update English content, run sync again. Rosetta only retranslates files that have changed:
# Edit content/en/about.md, then:
npx i18n-rosetta sync
The lock file tracks content hashes per file, so stable pages aren't retranslated.
Next Steps
- Content Translation Guide — Deep dive into shielding, front matter, and edge cases
- Framework Integration — Next.js and React setups
- CI/CD Guide — Automate syncs on push to
content/en/