Translate UI strings
Every site accumulates labels that live in templates: "Read more", "Back to top", the words around a form. Reach for this when you want them in one place instead of scattered across partials, or when a second language means each one now needs two spellings.
This is not only for multilingual sites. The t filter is registered either way, and a single-language site gets the same benefit: one file to edit when the wording changes, instead of a search across templates.
Prerequisites
- Baseline installed and building.
- Nunjucks templates, which is what Baseline configures by default.
defaultLanguage(ordefaultLocale) set in settings, even on a single-language site. It is the languagetreads when a page has none of its own, and without it every lookup misses and renders its key.
Create a table for your default language
// src/_data/translations/en.js
export default {
nav: { home: 'Home', about: 'About' },
readMore: 'Read more'
};
Eleventy auto-loads _data/, and a nested directory becomes a nested key, so this file lands at translations.en. There is nothing to register.
Swap one hardcoded label
Find a label in a template and pipe the key through t:
-<a href="/about/">About</a>
+<a href="/about/">{{ "nav.about" | t }}</a>
Keys are dot-paths, so nav.about reads the nested key.
Build and check the page. If you see nav.about rendered where the label should be, the key is missing from the table, and Baseline has logged a warning naming it.
Move the rest, one at a time
There is no shortcut here worth having. Move a label, build, look at the page. The failure mode is quiet if you move twenty at once and mistype one key, and obvious if you move one.
Group keys by where they appear rather than by what they say. nav.home and footer.copyright will still make sense to you in six months; homeLink and copy1 will not.
Fill in values that change
A {name} placeholder is replaced from the params you pass:
// src/_data/translations/en.js
export default {
nav: { home: 'Home', about: 'About' },
readMore: 'Read more',
+ greeting: 'Hello, {name}'
};
{{ "greeting" | t({ name: page.author }) }}
An unmatched placeholder stays visible as {name} rather than rendering blank, so a missing param shows up rather than leaving a gap you have to notice.
Handle counts
Write the entry as an object of plural forms and pass a count:
// src/_data/translations/en.js
export default {
nav: { home: 'Home', about: 'About' },
readMore: 'Read more',
greeting: 'Hello, {name}',
+ items: { one: '{count} item', other: '{count} items' }
};
{{ "items" | t({ count: results.length }) }}
Selection goes through Intl.PluralRules, so the categories are CLDR's rather than English's. A language with three forms needs three keys, and Polish is the usual example: one for 1, few for 2, many for 5.
Add a second language
One file per language code, alongside the first:
// src/_data/translations/nl.js
export default {
nav: { home: 'Home', about: 'Over ons' },
readMore: 'Lees meer'
};
Nothing else changes. Each page reads the table matching its own page.lang, so the templates you edited when you swapped that first label already work in both.
A key present in en.js and missing from nl.js falls back to the default language rather than breaking the page, which means a half-translated table ships something readable while you finish it.
Notes
- Override the language for a single call with
{{ "nav.home" | t({ lang: 'fr' }) }}. Useful for a language switcher that names each language in its own words. - A key missing everywhere renders as the key, and logs a warning. That is deliberate: a visible
nav.abouton the page is easier to catch than a blank space. - This is the string half of translation only. Page relationships, sibling URLs and hreflang are the multilang module's job, and
page.translationsis what you loop over for a language switcher. - Nunjucks specifically. Add another template engine and
treturns the key there rather than the string.
Full behaviour, including the lookup order and the language resolution, is in the filters reference.
See also
- Filters -
tin full, lookup order included. - Multilang module - page relationships, sibling URLs and hreflang, which
tdoes not touch. - Build a multilingual site - the setup these strings sit on top of.