Table of Contents
Build a multilingual Baseline site
Add a second language to a Baseline site, with everything you'd expect: per-language URLs, hreflang on every page, a sitemap for each language plus an index. Multilingual is opt-in. Most projects don't need it; if yours does, this chapter is the full setup.
By the end you'll have an English and Dutch site sharing a single Eleventy build, with Baseline doing the hreflang and sitemap wiring on its own.
What you will build
- Pages in English and Dutch sharing one Eleventy build.
- Baseline configured with multilingual on, plus
defaultLanguageandlanguagesset in_data/settings.js. - Hreflang links (the
<link rel="alternate" hreflang="...">tags that point search engines at a page's translations) written automatically, and a per-language sitemap for each language.
How activation works
Multilingual mode requires three things, all present:
options.multilingual: truepassed tobaseline()ineleventy.config.js.defaultLanguageset in_data/settings.js.- A non-empty
languagesmap in_data/settings.js.
Setting
defaultLanguageandlanguagesis the site's identity. Turningmultilingualon is the runtime decision. Both halves are needed; Baseline won't infer one from the other.
Prerequisites
- Node 20.15.0 (or >=20) and npm.
package.jsonwith"type": "module"and thedev/buildscripts from the simple site tutorial:{ "name": "simple-baseline-site", "type": "module", "scripts": { "start": "rimraf dist/ && npx @11ty/eleventy --serve", "build": "rimraf dist/ && cross-env ELEVENTY_ENV=production npx @11ty/eleventy" } }
1) Install required packages
npm install @11ty/eleventy @11ty/eleventy-img # Eleventy and 11ty Image
npm install rimraf cross-env # Helper packages
npm install @apleasantview/eleventy-plugin-baseline # Finally, install Baseline
2) Configure Eleventy with multilingual on
Create eleventy.config.js:
import baseline, { config as baselineConfig } from '@apleasantview/eleventy-plugin-baseline';
import settings from './src/_data/settings.js';
/** @param {import("@11ty/eleventy").UserConfig} eleventyConfig */
export default async function (eleventyConfig) {
eleventyConfig.addPlugin(
baseline(settings, {
multilingual: true,
head: {
titleSeparator: ' | ',
showGenerator: true
}
})
);
}
export const config = baselineConfig;
Create a local .env so canonical URLs, hreflang, and sitemaps use a real origin during development. In production, set URL in your hosting environment so the same outputs reach for the live domain instead.
ELEVENTY_ENV="development"
URL="http://localhost:8080/"
Baseline's multilingual support is a thin layer over Eleventy's built-in i18n plugin. The Eleventy i18n docs cover the mechanics underneath.
3) Site data and languages
Open src/_data/settings.js from the previous tutorials and add defaultLanguage and the languages map. Everything else (title, url, head extras) carries forward; the merged file looks like this:
export default {
title: 'Multilingual Baseline Site',
tagline: 'Hello, Eleventy + Baseline (i18n)',
url: process.env.URL || 'http://localhost:8080/',
noindex: false,
defaultLanguage: 'en',
languages: {
en: {
contentDir: 'content/en/',
languageCode: 'en',
languageName: 'English',
title: 'Baseline (EN)',
tagline: 'Hello'
},
nl: {
contentDir: 'content/nl/',
languageCode: 'nl',
languageName: 'Nederlands',
title: 'Baseline (NL)',
tagline: 'Hallo'
}
},
head: {
link: [{ rel: 'stylesheet', href: '/assets/css/index.css' }],
script: [{ src: '/assets/js/index.js', defer: true }],
meta: [{ name: 'color-scheme', content: 'light dark' }]
}
};
Each language's contentDir points at its per-language source folder under src/. Baseline reads the map to know which languages exist, and uses defaultLanguage to mark the canonical one.
4) Per-language directory data
Tell Eleventy which language each content folder belongs to. Baseline reads lang on each page to group translations and emit hreflang.
src/content/en/en.11tydata.js:
export default { lang: 'en' };
src/content/nl/nl.11tydata.js:
export default { lang: 'nl' };
5) Minimal layout
Create src/_includes/layouts/base.njk. The <baseline-head> placeholder is what Baseline replaces with the rendered <head> at build time. With multilingual on and a translationKey on each page (next step), the hreflang links go in automatically. No for loop in your layout.
Refer to the minimal layout step in the Build a simple Baseline site tutorial.
6) Add localized pages
A translationKey is the string that ties translations of the same page together. Pages with the same key across languages are recognised as translations of one another, and that's what lets Baseline emit the right hreflang.
src/content/en/pages/index.md:
---
title: 'Hello Baseline (EN)'
slug: 'multilingual-site-en'
description: 'English home'
permalink: '/'
layout: 'layouts/base.njk'
translationKey: 'homepage'
---
Welcome to the English home page.
src/content/nl/pages/index.md:
---
title: 'Hallo Baseline (NL)'
slug: 'multilingual-site-nl'
description: 'Nederlandse home'
permalink: '/nl/'
layout: 'layouts/base.njk'
translationKey: 'homepage'
---
Welkom op de Nederlandse homepagina.
Delete the permalink field from src/content/pages/index.md.
7) Minimal assets (reuse from prior tutorial)
The asset side is unchanged, so reuse what you already have from the Build a simple Baseline site tutorial:
src/assets/css/index.csswith the same minimal styles as before.src/assets/js/index.js, optionally a small script (aDOMContentLoadedlog is plenty).
8) Hreflang
Hreflang is the set of <link rel="alternate" hreflang="..."> tags that point search engines at a page's translations. Baseline emits them automatically once two things are in place:
- Multilingual mode is on (
options.multilingual: trueplusdefaultLanguageandlanguagesin settings). - The page has a
translationKeyin its front matter, matched by its translations.
With both pages in place from the previous step and multilingual on, the rendered <head> for the homepage will contain:
<link rel="alternate" hreflang="en" href="http://localhost:8080/" />
<link rel="alternate" hreflang="nl" href="http://localhost:8080/nl/" />
<link rel="alternate" hreflang="x-default" href="http://localhost:8080/" />
9) Run the site locally
npm start
- Visit
/en/for English and/nl/for Dutch. - View page source on either side. You should see
<link rel="alternate" hreflang="en" href="...">,<link rel="alternate" hreflang="nl" href="...">, and thex-defaultentry. Baseline emits all three because both pages sharetranslationKey: 'homepage'and multilingual is on. - Dev writes to
dist/while it runs, so the generatedsitemap.xmlplus per-language sitemaps (dist/en/sitemap.xml,dist/nl/sitemap.xml) are inspectable while the server is up.
10) Production build and inspect output
Change URL in .env to an absolute URL. On a deployed site that's your real production URL; for this exercise, any absolute URL (e.g. https://www.example.com/) works to verify the output. Then run:
npm run build
- Check
dist/for per-language output. dist/sitemap.xmlis now the sitemap index, with the per-language sitemaps (dist/en/sitemap.xml,dist/nl/sitemap.xml) listed inside.- Reset
URLin.envto "http://localhost:8080/".
Next steps
- Add more pages under
src/content/en/andsrc/content/nl/, matchingtranslationKeyvalues so the translations stay linked. - Localise labels (nav text and similar) through data files keyed by
lang. - Set
pathPrefixif you deploy under a subpath, and keepsettings.urlpointing at the production origin so canonicals and sitemap links match. - The multilang module reference has the full activation rules and helpers; the filters reference covers the translation-aware filters.
Previous: Build a simple Baseline site
Next: Feature guides