Table of Contents
multilang
INTERNAL_KEY: '_multilang'
What it does
The multilang module wires Eleventy's built-in I18nPlugin (the locale-aware URL plugin), normalises your language config, attaches a per-page locale object (with translationKey, lang, isDefaultLang), builds two translations collections, and registers three i18n filters for cross-language lookups in templates. The translation map gets written into a runtime store; the head module reads it once the cascade closes to build hreflang alternates.
A translation key is the identifier you set on a page (translationKey: 'about' in front matter) so Baseline knows the English /about/ and the French /fr/a-propos/ are the same page in different languages. Hreflang is the HTML link relation that tells search engines about those alternates.
Active when
All three of these must be set, otherwise the module exits early without registering anything:
options.multilingual: truesettings.defaultLanguage(a non-empty string)settings.languages(a non-empty object or array)
There is no inference. Setting only defaultLanguage and languages will not activate the module without the explicit multilingual: true.
Lifecycle
- Build-time. Adds
I18nPlugin, registers the i18n filters, registers a computedpage.locale, normalises the language config throughnormalizeLanguages. - Cascade-time. The
translationsandtranslationsMapcollection builders walk every page with atranslationKey, build the per-key map, and write it to the translation-map store (the runtime store other modules read).
How it works
- Normalise the language config.
normalizeLanguagesaccepts an object map or an array of strings, lowercases the keys, drops invalid entries (logged whenverbose: true), and returns the normalised object. - Activate
I18nPlugin. Adds Eleventy's built-in plugin with the resolveddefaultLanguageanderrorMode: 'allow-fallback'. - Compute
page.locale. Registered aseleventyComputed.page.locale. Resolveslangagainstdata.lang,data.language, ordefaultLanguage, then setsisDefaultLangaccordingly. - Build collections. Both collections walk the same loop, build the
translationsMaponce, and write it to the translation-map store. Pages without atranslationKeyare skipped silently. Pages whoselangis outside the allowed set are logged. - Register filters.
i18nTranslationsFor,i18nTranslationIn,i18nDefaultTranslation.
Defaults
errorModeforI18nPlugin:'allow-fallback'. Pages without a translation in the requested language fall back to the default-language version rather than 404.- Language resolution per page (in order):
data.lang, thendata.language, thensettings.defaultLanguage. - Allowed-languages set. Built from
settings.languageskeys. Pages whoselangis not in this set are logged and skipped during collection building.
defaultLanguage does not fall back to 'en' automatically. If absent, the module stays inactive (see Active when).
Settings
The multilang module reads two parts of the settings argument. Full shape on Site settings.
| Key | Type | Used for |
|---|---|---|
settings.defaultLanguage |
string |
Default-language code. Pages in this language render at unprefixed URLs; others live under /<lang>/. |
settings.languages |
object | string[] |
Map of language codes to per-language overrides, or a flat array of codes. Arrays are normalised to objects with empty entries. |
Options
| Option | Type | Default | Meaning |
|---|---|---|---|
multilingual |
boolean |
false |
Activate the module. Required, alongside settings. |
Computed page.locale
Every page receives a computed locale object on page:
export default {
page: {
translationKey: 'about', // from data.translationKey, or undefined
lang: 'en', // resolved + lowercased
isDefaultLang: true // lang === defaultLanguage
}
};
The head module reads this for hreflang. The sitemap module reads lang for per-language partitioning.
Collections
Two collections, both keyed by translationKey:
translationsis a flat list. Each entry is a safe copy of a page withlocaleattached.translationsMapis a nested map:translationsMap[translationKey][lang]. Each leaf carries{ title, url, lang, isDefaultLang, data }.
The map is also written to the translation-map store so transform-time consumers (the head module) can read it without going through collections.
Filters
Three filters for cross-language lookups in templates. Full reference on Filters.
i18nTranslationsFor(page, collections.translations): every translation sibling of the current page.i18nTranslationIn(page, collections.translations, lang): the specific-language variant, ornull.i18nDefaultTranslation(page, collections.translations): the default-language variant, ornull.
Tips
- Every localised page needs both
translationKeyandlangin its front matter. WithouttranslationKey, the page does not appear in translation collections; withoutlang(or a default), the resolver falls back and may misclassify the page. - Keep the default-language page present for every translation key. It powers the
x-defaultalternate and the head module's fallback resolution. - With multilang active, the sitemap module automatically emits per-language sitemaps plus an index. See sitemap.
- The "Unknown lang ..." log line is your signal that a page declared a language not in
settings.languages. Add it to the languages map or fix the page's front matter.
Rendering alternate links
The head module emits hreflang automatically when this module is active and the page has a translationKey. If you want to render alternates yourself (or override the default markup), translationsMap is the source:
{% set t = collections.translationsMap[translationKey] %}
{% if t %}
{% for lang, entry in t %}
<link rel="alternate" hreflang="{{ entry.lang }}" href="{{ entry.url }}">
{% if entry.isDefaultLang %}
<link rel="alternate" hreflang="x-default" href="{{ entry.url }}">
{% endif %}
{% endfor %}
{% endif %}
Peer deps
None. Uses Eleventy's built-in I18nPlugin.
See also
- Site settings for
defaultLanguageandlanguages. - Page context for where
localesurfaces on the per-page object. - Filters for the i18n filter signatures.
- head module for the hreflang consumer.
- sitemap module for per-language sitemaps.
- Tutorial: multilingual site
- How-to: multilingual index