The first locale is the default one and keeps the bare paths. Every other
language is served from its own
/<slug> prefix.
All languages live in the route table at the same time, so one build serves
all of them and switching a language is a plain navigation. This is the main
difference from the old Nuxt 2 implementation, which rebuilt the router for the
language of each incoming request.
Options
Everything sits undercrudadmin.localization:
Translated paths
Paths are keyed by route name, which stays stable no matter how the path is rewritten:/o-nas and /en/about. Child routes are translated as well, but only
the root of a page tree receives the language prefix.
Paths translated in the administration
SettranslateRoutes and the paths come from the backend gettext catalog
instead of the config, which is how the Nuxt 2 implementation worked:
<buildDir>/crudadmin.routes.js. Nothing imports that file, it exists so the
CrudAdmin scanner finds the paths and offers them for translation. Point the
backend at it:
APP_SRC_PATH is the Nuxt project root, so the same one value locates both the
sources and the generated route list. The tail of that path is the Nuxt build
directory, which is .nuxt on Nuxt 3 and node_modules/.cache/nuxt/.nuxt on
Nuxt 4.
Then the catalog of every language is read from /api/bootstrap?only=locale
(/<crudadmin.bootstrap.path>?only=locale, from bootstrap.baseURL when set)
and each route path is looked up in it. Translating /about to /o-nas in the
administration moves that page, and every link to it follows, because links
resolve by route name.
Routes are static in Nuxt, so a translated path takes effect on the next
build, not immediately. That is the one thing the per request router of the
Nuxt 2 version could do and this cannot.
The answer of the last successful build is kept in crudadmin.routes.json at
the project root and used when the backend cannot be reached, so a build never
silently drops every translated path. Commit it. Pointing cacheFile inside
the build directory defeats it, since a deploy wipes that.
Because routes may also be a function, the paths can come from somewhere else
entirely. It is awaited during the build:
How the language is picked
- the
/<slug>prefix of the url, which always wins - the domain, through
domainsor a matching top level domain - the
localecookie, from the visitor’s last switch defaultLocale
redirect is on.
They cause a 302 to the prefixed url rather than rendering another language at
the same address, so the url and the rendered language never disagree.
Links build themselves
The router is taught to resolve in the active language, so ordinary links and ordinary navigation stay in that language on their own. Nothing in an application has to know that prefixed routes exist:/en these resolve to /en/about and /en/projects/<slug>, on the default
language to /about and /projects/<slug>.
Address routes by name rather than by a written out path. A name survives a
path translation, a hardcoded /about does not. Paths still work and are
localized the same way, but they have to be written the way the default
language spells them.
Pointing at a language other than the active one is what the language
switcher does, through useSwitchLocalePath.
Composables
All auto-imported:
A language switcher:
useSetLocale reloads the page by default, because the translations and any
content the backend localizes have to be fetched again. Pass
{ reload: false } for a client side navigation instead.
Localized route parameters
A dynamic segment usually differs per language, because the record behind it is localized too. The router cannot know the other language’s value, so the page hands it over:Notes
localeshas to be listed in the config. Routes are built ahead of time while the languages themselves live in the database, so the two are kept in sync by hand. A slug missing fromlocalessimply has no routes.- The layer writes the resolved language into both the store and the cookie before the application’s own plugins run, so anything fetched on boot is already requested in the right language.