Different Domains
You might want to use a different domain name for each language your app supports.
Here is how to achieve this:
- Set
differentDomainsoption totrue - Configure the
localesoption as an array of objects, where each object has adomainkey whose value is the domain name you'd like to use for that locale. Optionally include a port (if non-standard) and/or a protocol. If the protocol is not provided then an attempt will be made to auto-detect it but that might not work correctly in some cases like when the pages are statically generated. - Optionally set
detectBrowserLanguagetofalse. When enabled (which it is by default), a first visit is redirected to the domain serving the locale detected from the browser, while a visitor arriving from one of your own domains (a locale switcher link, a cross-domain link) stays on the domain they chose. A crawler sending anAccept-Languageheader (Bingbot does, Googlebot mostly doesn't) counts as such a first visit and is answered with a redirect instead of content on domains that don't serve its language. Set tofalseif you want to ensure that visiting a given domain always shows the page in that domain's own locale, crawlers included. Build cross-domain switchers with<SwitchLocalePathLink>- a plain<NuxtLink>to another domain carriesrel="noreferrer", so the arrival cannot be recognized as one of your own and is redirected by detection again. A site-wideReferrer-Policythat strips the referer has the same effect - there, only acookieDomainspanning the domains keeps switches sticky. - When your domains share a suffix (e.g. subdomains of one site), set
detectBrowserLanguage.cookieDomainto that suffix so the visitor's locale choice travels between the domains. A cookie scoped to a single domain is only applied on the domain that set it, it never redirects to another domain - the same applies to acookieDomainthat doesn't cover every configured domain.
export default defineNuxtConfig({
i18n: {
locales: [
{
code: 'en',
domain: 'mydomain.com'
},
{
code: 'es',
domain: 'es.mydomain.com'
},
{
code: 'fr',
domain: 'fr.mydomain.com'
},
{
code: 'pl',
domain: 'http://pl.mydomain.com'
},
{
code: 'ua',
domain: 'https://ua.mydomain.com'
}
],
differentDomains: true
// Or enable the option in production only
// differentDomains: (process.env.NODE_ENV === 'production')
}
})
When using different domain names, your lang switcher links to another origin. Prefer <SwitchLocalePathLink>, which also writes the locale cookie so the choice survives the switch where detectBrowserLanguage.cookieDomain spans your domains. Regular <a> tags work as well - unlike <NuxtLink>, which adds rel="noreferrer" to an external link, so detection cannot recognize the arrival as one of your own and redirects it away again:
<script setup>
const { locale, locales } = useI18n()
const switchLocalePath = useSwitchLocalePath()
const availableLocales = computed(() => {
return locales.value.filter(i => i.code !== locale.value)
})
</script>
<template>
...
<a v-for="locale in availableLocales" :href="switchLocalePath(locale.code)" :key="locale.code">
{{ locale.code }}
</a>
...
</template>
Runtime environment variables
Sometimes there's a need to change domains in different environments, e.g. staging and production.
As nuxt.config.ts is used at build time it would be necessary to create different builds for different environments.
export const localeDomains = {
uk: process.env.DOMAIN_UK,
fr: process.env.DOMAIN_FR
}
import { localeDomains } from './locale-domains.config'
export default defineNuxtConfig({
modules: ['@nuxtjs/i18n'],
i18n: {
differentDomains: process.env.NODE_ENV === 'production',
locales: [
{
code: 'uk',
domain: localeDomains.uk
},
{
code: 'fr',
domain: localeDomains.fr
}
]
}
})
With the above config, a build would have to be run for staging and production with different .env files that specify DOMAIN_UK and DOMAIN_FR.
Alternatively, to avoid the need for multiple builds, the locale domains can be overridden via runtime environment variables. The variable name should follow the format NUXT_PUBLIC_I18N_DOMAIN_LOCALES_{code}_DOMAIN
For example:
NUXT_PUBLIC_I18N_DOMAIN_LOCALES_UK_DOMAIN=uk.example.test
NUXT_PUBLIC_I18N_DOMAIN_LOCALES_FR_DOMAIN=fr.example.test
NUXT_PUBLIC_I18N_DOMAIN_LOCALES_UK_DOMAIN=uk.staging.example.test
NUXT_PUBLIC_I18N_DOMAIN_LOCALES_FR_DOMAIN=fr.staging.example.test
The override changes which domain serves a locale, it can't make a locale the default for a domain it wasn't already the default for. Which locale a domain serves unprefixed is decided by the routes generated at build time, so each locale needs a domain (and domainDefault, where a domain serves several locales) in nuxt.config.ts for the override to re-point. A locale left without one - because the build ran with DOMAIN_FR unset, for example - keeps its prefix on the domain it is overridden onto, and that domain serves whichever locale the build did resolve as its default. The 'no_prefix' strategy is unaffected, no route shape depends on the domain there.
Using different domains for only some of the languages
If one or more of the domains need to host multiple languages, the default language of each domain needs to have domainDefault: true so there is a per domain fallback locale.
The option differentDomains still need to be set to true though.
export default defineNuxtConfig({
// ...
i18n: {
locales: [
{
code: 'en',
domain: 'mydomain.com',
domainDefault: true
},
{
code: 'pl',
domain: 'mydomain.com'
},
{
code: 'ua',
domain: 'mydomain.com'
},
{
code: 'es',
domain: 'es.mydomain.com',
domainDefault: true
},
{
code: 'fr',
domain: 'fr.mydomain.com',
domainDefault: true
}
],
strategy: 'prefix',
differentDomains: true
// Or enable the option in production only
// differentDomains: (process.env.NODE_ENV === 'production')
},
// ...
})
Given above configuration with the 'prefix' strategy, following requests will be:
- https://mydomain.com -> https://mydomain.com/en (en language)
- https://mydomain.com/pl -> https://mydomain.com/pl (pl language)
- https://mydomain.com/ua -> https://mydomain.com/ua (ua language)
- https://es.mydomain.com -> https://es.mydomain.com/es (es language)
- https://fr.mydomain.com -> https://fr.mydomain.com/fr (fr language)
The same requests when using the 'prefix_except_default' strategy, will be:
- https://mydomain.com -> https://mydomain.com (en language)
- https://mydomain.com/pl -> https://mydomain.com/pl (pl language)
- https://mydomain.com/ua -> https://mydomain.com/ua (ua language)
- https://es.mydomain.com -> https://es.mydomain.com (es language)
- https://fr.mydomain.com -> https://fr.mydomain.com (fr language)
Caching considerations with different domains
When using different domains, make sure to configure caching properly so that responses are correctly separated per domain.
Because the same route may be served under multiple domains (e.g. en.mydomain.com
and fr.mydomain.com), caches need to vary by the request host. Otherwise,
a response generated for one domain could be reused on another, causing the wrong language to render,
leading to hydration mismatches and visible flashes on the client.
The recommended setup is to use cache.varies: ['host'] in your route rules,
so that the host header is included in the cache key:
export default defineNuxtConfig({
routeRules: {
- '/': { swr: 60 },
+ '/': { swr: 60, cache: { varies: ['host'] } },
},
// ...
})