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.

::callout{type="warning" title="Restricting the list needs this key, not i18n's"}
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:

```ts
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 interface 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:

```ts [app/app.config.ts]
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](/guides/several-languages).

::callout{type="tip" title="The locale is not part of the content path"}
`/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:

1. the locale itself — `de-DE` reads `de-DE`
2. its base language — `de-DE` reads a `de` tree, so one `pt/` folder can serve
   both `pt-PT` and `pt-BR`
3. a sibling of the same language — `pt-BR` reads `pt-PT` before falling out of
   Portuguese
4. `fallbackLocale` from vue-i18n, the same value a missing string resolves
   through
5. 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.

## Writing direction

The active locale also supplies the document direction. Components use logical
start and end positions, so navigation, sheets, arrows and page furniture mirror
when a consumer adds an RTL locale. Technical material does not blindly mirror:
code, commands, paths, API names and other left-to-right syntax retain the
direction in which they can be read.

duxt ships no RTL locale of its own today. Direction-aware means a consumer can
add one without forking the theme; it is not a claim that one of the seven
built-in locales is RTL.