Nuxt i18n module provides the useLocaleHead() composable function. Calling this composable function returns a function which you can use to generate SEO metadata to optimize locale-related aspects of the app for the search engines.
Here are the specific optimizations and features that it enables:
lang attribute for the <html> taghreflang alternate link generationRead more about those features below
To leverage the SEO benefits, you must configure the locales option as an array of objects, where each object has an language option set to the locale language tags:
export default defineNuxtConfig({
i18n: {
locales: [
{
code: 'en',
language: 'en-US'
},
{
code: 'es',
language: 'es-ES'
},
{
code: 'fr',
language: 'fr-FR'
}
]
}
})
You must also set the baseUrl option to your production domain in order to make alternate URLs fully-qualified:
export default defineNuxtConfig({
i18n: {
baseUrl: 'https://my-nuxt-app.com'
}
})
(Note that baseUrl can also be set to a function. Check baseUrl documentation.)
The useLocaleHead() is a composable function, Calling that composable function returns a function that returns metadata that is handled by Head management that is integrated within Nuxt. That metadata can be specified the setup function in various places within Nuxt:
To enable SEO metadata, declare a setup function in one of the places specified above and make it return the result of a useLocaleHead() function call.
To avoid duplicating the code, it's recommended to set globally with Meta Components in layout components and override some values per-page Vue component like definePageMeta(), if necessary.
<template>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</template>
<script setup>
const route = useRoute()
const { t } = useI18n()
const head = useLocaleHead()
const title = computed(() => t(route.meta.title ?? 'TBD', t('layouts.title'))
);
</script>
<template>
<div>
<Html :lang="head.htmlAttrs.lang" :dir="head.htmlAttrs.dir">
<Head>
<Title>{{ title }}</Title>
<template v-for="link in head.link" :key="link.key">
<Link :id="link.key" :rel="link.rel" :href="link.href" :hreflang="link.hreflang" />
</template>
<template v-for="meta in head.meta" :key="meta.key">
<Meta :id="meta.key" :property="meta.property" :content="meta.content" />
</template>
</Head>
<Body>
<slot />
</Body>
</Html>
</div>
</template>
<script setup>
definePageMeta({
title: 'pages.title.top' // set resource key
})
const { locale, locales, t } = useI18n()
const switchLocalePath = useSwitchLocalePath()
const availableLocales = computed(() => {
return locales.value.filter(i => i.code !== locale.value)
})
</script>
<template>
<div>
<p>{{ t('pages.top.description') }}</p>
<p>{{ t('pages.top.languages') }}</p>
<nav>
<template v-for="(locale, index) in availableLocales" :key="locale.code">
<span v-if="index"> | </span>
<NuxtLink :to="switchLocalePath(locale.code)">
{{ locale.name ?? locale.code }}
</NuxtLink>
</template>
</nav>
</div>
</template>
Check out the options you can pass to the useLocaleHead() in the composable documentation
That's it!
If you also want to add your own metadata, you have to call useHead(). When you call useHead() with the additional metadata, useHead() will merge it global metadata that has already defined.
<script setup>
// define page meta for layouts/default.vue
definePageMeta({
title: 'pages.title.about'
})
useHead({
meta: [{ property: 'og:title', content: 'this is og title for about page' }]
})
</script>
<template>
<h2>{{ $t('pages.about.description') }}</h2>
</template>
lang attribute for the <html> taglang attribute, equivalent to the current locale's language value, in the <html> tag.hreflang alternate link<link rel="alternate" hreflang="x"> tags for every configured locale. The locales' language value are used as hreflang values.en-*). By default, it is the first locale provided, but another locale can be selected by setting isCatchallLocale to true on that specific locale object in your Nuxt i18n module configuration. More on hreflangexport default defineNuxtConfig({
i18n: {
locales: [
{
code: 'en',
language: 'en-US' // Will be used as "catchall" locale by default
},
{
code: 'gb',
language: 'en-GB'
}
]
}
})
isCatchallLocale to selected another locale:export default defineNuxtConfig({
i18n: {
locales: [
{
code: 'en',
language: 'en-US'
},
{
code: 'gb',
language: 'en-GB',
isCatchallLocale: true // This one will be used as catchall locale
}
]
}
})
en locale language set, it'll be used as the "catchall" without doing anythingexport default defineNuxtConfig({
i18n: {
locales: [
{
code: 'gb',
language: 'en-GB'
},
{
code: 'en',
language: 'en' // will be used as "catchall" locale
}
]
}
})
og:locale and og:locale:alternate meta tags as defined in the Open Graph protocol.rel="canonical" link on all pages to specify the "main" version of the page that should be indexed by search engines. This is beneficial in various situations:prefix_and_default strategy there are technically two sets of pages generated for the default locale -- one prefixed and one unprefixed. The canonical link will be set to the unprefixed version of the page to avoid duplicate indexation.canonicalQueries option. For example:<script setup>
const i18nHead = useLocaleHead({ seo: { canonicalQueries: ['foo'] } })
useHead(() => ({
htmlAttrs: {
lang: i18nHead.value.htmlAttrs.lang
},
link: [...(i18nHead.value.link || [])],
meta: [...(i18nHead.value.meta || [])]
}))
</script>