Skip to content

Commit

Permalink
fix: rename locale iso property to language (#3055)
Browse files Browse the repository at this point in the history
* fix: rename locale property `iso` to `language`

* refactor: rename parameters

* fix: locale `language` merging

* docs: expand `iso` rename explanation
  • Loading branch information
BobbieGoede committed Aug 14, 2024
1 parent c906a8d commit df93837
Show file tree
Hide file tree
Showing 10 changed files with 562 additions and 36 deletions.
6 changes: 5 additions & 1 deletion docs/content/docs/5.v9/2.guide/18.breaking-changes-in-v9.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,8 @@ nuxt.config.ts

Reasons for change
1. Context - i18n files are used both server-side and client-side, using a dedicated `i18n/` folder in the root directory outside `app/` and `server/` makes more sense.
2. Clean - less clutter/fragmentation of i18n files, and should make resolving and loading files easier for us.
2. Clean - less clutter/fragmentation of i18n files, and should make resolving and loading files easier for us.

## Locale `iso` renamed to `language`

The `iso` property on a locale object has been renamed to `language` to be consistent with the usage of Language Tags on the web (e.g. `navigator.language` and `Accept-Language`). The original `iso` property name referred to ISO standards which describe valid Language Tags, see the [related issue](https://github.com/nuxt-modules/i18n/issues/2449) for more details.
85 changes: 85 additions & 0 deletions docs/content/docs/5.v9/2.guide/5.browser-language-detection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
---
title: Browser language detection
description: Detect user browser's language.
---

By default, Nuxt i18n module attempts to redirect users to their preferred language by detecting their browser's language. This is controlled by the `detectBrowserLanguage` option.

```ts [nuxt.config.ts]
export default defineNuxtConfig({
i18n: {
detectBrowserLanguage: {
useCookie: true,
cookieKey: 'i18n_redirected',
redirectOn: 'root' // recommended
}
}
})
```

::callout{icon="i-heroicons-light-bulb"}
For better SEO, it's recommended to set `redirectOn` to `root` (which is the default value). When set, the language detection is only attempted when the user visits the root path (`/`) of the site. This allows crawlers to access the requested page rather than being redirected away based on detected locale. It also allows linking to pages in specific locales.
::

Browser language is detected either from `navigator` when running on client-side, or from the `accept-language` HTTP header. Configured `locales` (or locales `language` and/or `code` when locales are specified in object form) are matched against locales reported by the browser (for example `en-US,en;q=0.9,no;q=0.8`). If there is no exact match for the full locale, the language code (letters before `-`) are matched against configured locales.

To prevent redirecting users every time they visit the app, **Nuxt i18n module** sets a cookie using the detected locale. You can change the cookie's name by setting `detectBrowserLanguage.cookieKey` option to whatever you'd like, the default is _i18n_redirected_.

```ts [nuxt.config.ts]
i18n: {
detectBrowserLanguage: {
useCookie: true,
cookieKey: 'my_custom_cookie_name'
}
}
```

If you'd rather have users be redirected to their browser's language every time they visit the app, disable the cookie by setting `detectBrowserLanguage.useCookie` to `false`.

```ts [nuxt.config.ts]
export default defineNuxtConfig({
i18n: {
detectBrowserLanguage: {
useCookie: false
}
}
})
```

To completely disable the browser's language detection feature, set `detectBrowserLanguage` to `false`.

```ts [nuxt.config.ts]
export default defineNuxtConfig({
i18n: {
// ...
detectBrowserLanguage: false
}
})
```

To redirect the user every time they visit the app and keep their selected choice, enable `alwaysRedirect`:

```ts [nuxt.config.ts]
export default defineNuxtConfig({
i18n: {
// ...
detectBrowserLanguage: {
useCookie: true,
alwaysRedirect: true
}
}
})
```

To use the cookie within a cross-origin environment (e.g. in an iFrame), you can set `cookieCrossOrigin: true`. This will change the cookie settings from `SameSite=Lax` to `SameSite=None; Secure`.

```ts [nuxt.config.ts]
export default defineNuxtConfig({
i18n: {
detectBrowserLanguage: {
useCookie: true,
cookieCrossOrigin: true
}
}
})
```
262 changes: 262 additions & 0 deletions docs/content/docs/5.v9/2.guide/6.seo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
---
title: SEO
description: When the `useLocaleHead` is called, @nuxtjs/i18n attempts to add some metadata to improve your pages SEO. Here's what it does.
---

**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>` tag
- `hreflang` alternate link generation
- OpenGraph locale tag generation
- canonical link generation

[Read more about those features below](#feature-details)

## Requirements

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:

```ts [nuxt.config.ts]
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:

```ts [nuxt.config.ts]
export default defineNuxtConfig({
i18n: {
baseUrl: 'https://my-nuxt-app.com'
}
})
```

(Note that `baseUrl` can also be set to a function. Check [`baseUrl` documentation](/docs/options/routing#baseurl).)

## Setup

The `useLocaleHead` is a composable function, Calling that composable function returns a function that returns metadata that is handled by [Head management](https://nuxt.com/docs/getting-started/seo-meta) that is integrated within Nuxt. That metadata can be specified the `setup` function in various places within Nuxt:

- [`app.vue`](https://nuxt.com/docs/guide/directory-structure/app)
- Vue components of [`pages`](https://nuxt.com/docs/guide/directory-structure/pages) directory
- Vue components of [`layouts`](https://nuxt.com/docs/guide/directory-structure/layouts) directory

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](https://nuxt.com/docs/getting-started/seo-meta#components) in [layout components](https://nuxt.com/docs/guide/directory-structure/layouts) and override some values per-page Vue component like [`definePageMeta`](https://nuxt.com/docs/guide/directory-structure/pages#page-metadata), if necessary.

::code-group

```vue [app.vue]
<template>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</template>
```

```vue [layouts/default.vue]
<script setup>
const route = useRoute()
const { t } = useI18n()
const head = useLocaleHead({
addDirAttribute: true,
identifierAttribute: 'id',
addSeoAttributes: true
})
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.id">
<Link :id="link.id" :rel="link.rel" :href="link.href" :hreflang="link.hreflang" />
</template>
<template v-for="meta in head.meta" :key="meta.id">
<Meta :id="meta.id" :property="meta.property" :content="meta.content" />
</template>
</Head>
<Body>
<slot />
</Body>
</Html>
</div>
</template>
```

```vue [pages/index.vue]
<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 [API documentation](/docs/api#uselocalehead)

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.

```vue [pages/about/index.vue]
<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>
```

## Feature details

- `lang` attribute for the `<html>` tag

Sets the correct `lang` attribute, equivalent to the current locale's `language` value, in the `<html>` tag.

- `hreflang` alternate link

Generates `<link rel="alternate" hreflang="x">` tags for every configured locale. The locales' `language` value are used as `hreflang` values.

A "catchall" locale hreflang link is provided for each locale group (e.g. `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 hreflang](https://support.google.com/webmasters/answer/189077)

An example without selected "catchall" locale:

```ts {}[nuxt.config.ts]
export default defineNuxtConfig({
i18n: {
locales: [
{
code: 'en',
language: 'en-US' // Will be used as "catchall" locale by default
},
{
code: 'gb',
language: 'en-GB'
}
]
}
})
```

Here is how you'd use `isCatchallLocale` to selected another locale:

```ts [nuxt.config.ts]
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
}
]
}
})
```

In case you already have an `en` locale `language` set, it'll be used as the "catchall" without doing anything

```ts [nuxt.config.ts]
export default defineNuxtConfig({
i18n: {
locales: [
{
code: 'gb',
language: 'en-GB'
},
{
code: 'en',
language: 'en' // will be used as "catchall" locale
}
]
}
})
```

- OpenGraph Locale tag generation

Generates `og:locale` and `og:locale:alternate` meta tags as defined in the [Open Graph protocol](http://ogp.me/#optional).

- Canonical link

Generates `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:

- When using the `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.
- When the page contains query parameters, the canonical link will **not include** the query params by default. This is typically the right thing to do as various query params can be inserted by trackers and should not be part of the canonical link. This can be overridden by using the `canonicalQueries` option. For example:

```vue
<script setup>
const i18nHead = useLocaleHead({
addSeoAttributes: {
canonicalQueries: ['foo']
}
})
useHead({
htmlAttrs: {
lang: i18nHead.value.htmlAttrs!.lang
},
link: [...(i18nHead.value.link || [])],
meta: [...(i18nHead.value.meta || [])]
})
</script>
```

[More on canonical](https://support.google.com/webmasters/answer/182192#dup-content)
Loading

0 comments on commit df93837

Please sign in to comment.