Localisation
Seven locales ship with the layer, and every configured string takes three forms.
The theme's own strings — navigation labels, search, the version banner, the 404 —
ship translated in en-GB, en-US, de-DE, es-ES, fr-FR, pt-PT and
pt-BR. A site picks which of them it serves with locales, and
i18n.defaultLocale decides which one is served without a URL prefix.
locales is read at build time: locales decide routes and hreflang, not just
what a component draws, so changing the list needs a rebuild.
i18n's own locales array merges across layers instead of replacing, so
declaring ['de-DE'] there adds nothing and removes nothing — the other six stay
routed, indexed and announced in hreflang. duxt.locales is the key that
actually narrows it.
Every configured string takes three forms
Anything you write into the config — a label, a title, a description — may be a literal, an i18n key, or a record of one string per locale:
label: 'Repository' // literal
label: 'app.nav.repository' // your own key
label: { 'en-GB': 'Repository', 'de-DE': 'Repository' } // per locale
Which one it is, is decided by whether the string is a registered key. So a
single-language site never meets the other two forms, and a site with a handful
of strings in five languages needs no locale files at all — the record form
resolves through the base language, so pt-PT covers pt-BR and en-GB covers
en-US.
Do not reach into the layer's keys
duxt.defaults.* translates the defaults the layer itself ships. Those keys are
internal: renaming one is not a breaking change of the layer, and i18n answers a
missing key by printing the key — so a consumer depending on one would find out
from a reader, not from a build. Use a literal, your own key, or the record form.
Paths and links
Only paths are localised. useDuxtPath() gives the path the documentation is
at, with the locale segment stripped; useDuxtLink() writes one back out as a
link, adding the segment again. An absolute URL, a mail link or a bare hash passes
through untouched. Anything computing a documentation path should go through the
pair rather than reading the route directly.
Translated pages
The interface and the pages are two separate decisions. A site that says nothing serves one tree in every locale — the chrome changes language, the Markdown does not — and that is the right shape for most documentation.
locales on a source is what adds the second half:
sources: [{ path: 'docs', locales: ['en-GB', 'de-DE'] }]
The default locale is the tree in path itself, with no folder of its own, so
adding this key never moves a page that is already published. Every other
language is a folder inside it — docs/de-DE/ — or somewhere else entirely; see
Several languages.
/de-DE/guides/deploying and /guides/deploying are the same content path in
two collections. The segment belongs to i18n's routing, never to a collection —
which is why switching language keeps the reader on the same page, and why a
missing translation is answered by one more query rather than by a redirect.
When a page has no translation
The reader gets the nearest language that has it, with a banner saying so, and
the page is left out of the index under noindex with a canonical pointing at
the language that carries it. The chain is the one the layer already applies to
strings:
- the locale itself —
de-DEreadsde-DE - its base language —
de-DEreads adetree, so onept/folder can serve bothpt-PTandpt-BR - a sibling of the same language —
pt-BRreadspt-PTbefore falling out of Portuguese fallbackLocalefrom vue-i18n, the same value a missing string resolves through- the untranslated original
A 404 is deliberately not among them: a gap in a translation is the writer's, and answering it by hiding the page punishes the reader for it.