Picking an SSG for a Multi-Language Documentation Site

A multi-language documentation site multiplies every decision by the number of locales. Routing, content organization, fallback behavior, and build time all change character once you have four languages instead of one. The four major static generators — Astro, Hugo, Eleventy, and Jekyll — handle internationalization very differently, from Hugo's deep built-in support to Eleventy's convention-driven approach. This guide compares them on the dimensions that matter for translated docs, with measured numbers from a four-language, 1,000-source-page corpus. It sits under the SSG Framework Selection Matrix, within Choosing the Right Static Site Generator for Production.

Prerequisites

  • A clear list of target locales and a fallback policy decision (what a reader sees when a page is not yet translated).
  • A sense of your translation workflow — whether translators edit Markdown directly or content comes from a TMS export — since that shapes the content layout.
  • hyperfine for build timing if you intend to compare candidates on your own corpus, plus each generator installed for a representative trial.
Internationalization capability matrix across four SSGs A grid rating Astro, Hugo, Eleventy, and Jekyll on built-in routing, content organization, fallback handling, and hreflang generation. Hugo is strong on all four; Astro is strong except for manual fallback and hreflang; Eleventy and Jekyll rely on add-ons or hand wiring, with Jekyll weakest overall. i18n capability by generator Astro Hugo Eleventy Jekyll routing content org fallback hreflang native folders manual from data native folders auto native plugin folders manual from data plugin collections manual manual built-in / strong convention / add-on manual / weak
Hugo is strongest across all four i18n dimensions; Astro is close behind with native routing and folder layout but hand-built fallback and hreflang; Eleventy and Jekyll require more manual wiring as locales grow.

Routing Across Locales

Routing is where the gap is widest. Hugo treats languages as a first-class concept: declare them in hugo.toml and it generates /, /fr/, /de/ routes automatically with no plugin.

# hugo.toml
defaultContentLanguage = "en"
[languages.en]
  weight = 1
[languages.fr]
  weight = 2
[languages.de]
  weight = 3

Astro added native i18n routing in v4 — configure i18n in astro.config.mjs and it handles locale prefixes and getRelativeLocaleUrl helpers:

// astro.config.mjs
export default defineConfig({
  i18n: {
    defaultLocale: "en",
    locales: ["en", "fr", "de"],
    routing: { prefixDefaultLocale: false },
  },
});

Eleventy has no built-in locale router; the common approach is folder-per-language plus the community eleventy-plugin-i18n for string lookups, with permalinks driven by directory data. Jekyll relies on a plugin such as jekyll-multiple-languages-plugin or manual collections, which is the most hand-wired of the four.

Content Organization

For documentation at scale, folder-per-language is the layout that stays legible:

content/
  en/getting-started/index.md
  fr/getting-started/index.md
  de/getting-started/index.md

Hugo, Astro, and Eleventy all map this cleanly to per-locale routes. Hugo also supports filename suffixes (index.fr.md), which is tidy for small sites but noisy across thousands of pages. Jekyll typically models languages as collections, which works but couples your locale structure to Jekyll's collection conventions. Whichever generator you pick, mirror the directory tree exactly across locales so translators always know where a page lives — the same discipline that keeps large single-language docs maintainable, as discussed in Choosing Between Astro and Eleventy for Large Docs.

Fallback and hreflang

Two i18n-specific concerns separate a polished multi-language site from a broken one. Fallback: when a page is not yet translated, Hugo can serve the default-language version automatically; with Astro and Eleventy you decide explicitly whether to render the default page or hide the link, so readers never hit a 404. hreflang: each translated page should declare its alternates so the right locale is matched. Hugo emits these from its translation data; in Astro and Eleventy you build them from a small data structure listing each page's available locales:

{% raw %}{% for locale in page.locales %}
<link rel="alternate" hreflang="{{ locale.lang }}" href="{{ site.url }}{{ locale.url }}">
{% endfor %}{% endraw %}

Build Cost

Each locale is a full set of pages, so build time scales roughly linearly with total rendered pages. Timed with hyperfine --warmup 1 --runs 5 on the four-language corpus (1,000 source pages × 4 locales = 4,000 rendered pages):

GeneratorSingle locale (1,000 pages)Four locales (4,000 pages)i18n setup
Hugo6 s22 snative, no add-ons
Astro19 s71 snative routing (v4)
Eleventy9 s36 sfolders + i18n plugin
Jekyll26 s104 smulti-language plugin

The four-language numbers track the single-language ratios, confirming that i18n itself adds little overhead beyond the extra pages. Hugo's lead widens in absolute terms simply because it is the fastest per page. If build time at scale is your constraint, that ranking matches the broader picture in Hugo Build Times for Large Repositories, and the caching levers in Speeding Up Hugo Builds with Render Hooks and Caching apply per locale.

Putting It Together

For a large multi-language documentation site, the ranking is clear: Hugo if you want the deepest built-in i18n and the fastest builds; Astro if you want native i18n routing with a component model and can absorb the higher per-page build cost; Eleventy if your team prefers templates-and-Markdown and you accept wiring locale routing yourself; Jekyll only if you are already committed to it, since its i18n story is the most manual. Score these against your own weights using the SSG Framework Selection Matrix rather than picking on i18n alone, and run i18n as one line in the SSG Selection Checklist for Engineering Teams so it is weighed against build scale, team skills, hosting, and maintenance rather than in isolation.

Pitfalls & Rollback

  • Inconsistent directory trees across locales: if fr/ and en/ diverge in structure, translators and fallback logic both break. Mirror the tree exactly.
  • Missing fallback policy: without an explicit decision, untranslated pages 404. Pick fallback-to-default or hide-the-link and apply it uniformly.
  • Forgetting hreflang: translated pages without alternate declarations leave locale matching to guesswork. Generate hreflang from a single source of locale data.
  • Underestimating build time: four locales is four times the pages. Benchmark the full multiplied corpus, not a single language, before committing.
  • Rollback: because all four generators consume a folder-per-language Markdown tree, a trial migration of one locale is cheap. Keep content generator-agnostic and you can build the same translated tree with another tool to compare real numbers.
Three ways to model locales Three panels: path prefix, subdomain and separate site. Path prefix keeps one build and one deploy and is the usual answer. Subdomain isolates locales at the cost of shared assets. A separate site per locale isolates everything and duplicates all the work. Three ways to model locales Path prefix /en/ /de/ /ja/ one build, one deploy shared assets and search the usual right answer Subdomain de.example.com separate cache and headers assets duplicated per locale useful for legal separation Separate site its own repository no shared components every change made N times only for divergent content Whichever you pick, decide before content exists — moving locales later is a URL migration.
Path prefixes keep the translation workflow inside one repository, which is where the cost of a multi-language site actually lives.
Build cost as locales are added Build time against locale count for two models. One build containing all locales rises from 12 seconds at one locale to 41 seconds at four. Separate builds per locale rise from 12 seconds to 96 seconds because each build repeats the shared work. One build with four locales beats four builds separate builds · 96 s one build · 41 s 1 locale 2–3 4 locales Same corpus per locale · the gap is the shared work each separate build repeats
Separate builds repeat template compilation, asset processing and search indexing once per locale — work a single build does once regardless of how many languages it emits.

The build-cost curve is the argument that usually settles the model. Shared work — compiling templates, processing images, building a search index — happens once in a single build and once per locale in separate ones, so the gap widens with every language added rather than staying proportional.

Translation Status Is Part of the Content Model

The mechanism nobody plans for is partial translation. At any moment some pages exist in every locale, some in two, and some only in the source language — and how the site handles that is a content-model decision rather than a template detail.

Three behaviours are worth deciding explicitly. What a reader sees when a page is missing in their locale: the source-language page with a notice is almost always better than a 404, because the content is usually still useful. Whether an out-of-date translation is labelled: a page translated against a version of the source that has since changed should say so, which means tracking the source revision each translation was made from. And whether the navigation lists untranslated pages at all: hiding them makes the locale look complete and makes content undiscoverable, showing them makes the gap visible.

Whatever you choose, the build should be able to answer "what percentage of the corpus exists in each locale" as a number, because that is the metric a translation effort is actually managed against.

Conclusion

Internationalization is the dimension where these four generators differ most. Hugo leads on built-in routing, fallback, hreflang, and raw build speed; Astro's v4 routing makes it a strong second; Eleventy and Jekyll handle multiple languages but ask for more manual wiring as locales grow. Decide your fallback policy, mirror the directory tree across locales, emit hreflang from one data source, and benchmark the full multiplied page count. Then weight i18n alongside your other criteria in the parent SSG Framework Selection Matrix.

FAQ

Which SSG has the strongest built-in internationalization?

Hugo has the most complete built-in i18n: native language configuration, automatic per-language routing, translation tables, and per-language sitemaps without plugins. Astro added solid native i18n routing in version 4. Eleventy and Jekyll handle multiple languages but lean more on conventions and add-ons.

How does build time scale with more languages?

Roughly linearly with total page count, since each locale is a full set of pages. In our four-language, 1,000-source-page test the 4,000 rendered pages built in 22 seconds with Hugo and 71 seconds with Astro, tracking the single-language ratio between the two.

Should each language live in its own folder or use filename suffixes?

Both work. Folder-per-language (content/en, content/fr) is the clearest for large docs and is the idiomatic layout in Hugo and Astro. Filename suffixes like page.fr.md are convenient for small sites but get noisy at scale.

How do I handle untranslated pages?

Decide on a fallback policy. Hugo can fall back to the default language for missing translations; with Astro or Eleventy you typically render the default-language page or hide the link. Define this explicitly so readers never hit a 404 for a page that exists in another language.

Do I need hreflang tags on a static multi-language site?

Yes, so each translated page declares its alternates. Hugo can emit these from its translation data; in Astro and Eleventy you generate them from a small data structure that lists each page's available locales.