Skip to content

Commit

Permalink
fix: crash on using $nuxtI18nHead from nuxt.config's head (#1273)
Browse files Browse the repository at this point in the history
Fixes #1266
  • Loading branch information
rchl authored Aug 31, 2021
1 parent cd44eb5 commit b4aae9d
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 66 deletions.
124 changes: 68 additions & 56 deletions docs/content/en/seo.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,26 +52,38 @@ You must also set the `baseUrl` option to your production domain in order to mak

## Setup

The `$nuxtI18nHead` function returns metadata expected by the [Vue Meta](https://github.com/nuxt/vue-meta) plugin which in Nuxt is supported through a `head()` function within your **page** components or the Nuxt layout file.
The `$nuxtI18nHead` function returns metadata that is handled by the [Vue Meta](https://github.com/nuxt/vue-meta) plugin that is integrated within Nuxt. That metadata can be specified in various places within Nuxt:
- the `head()` option within the Nuxt configuration file (`nuxt.config.js`)
- the `head()` component option in your **page** components or layout files

To enable SEO metadata, declare a [`head`](https://nuxtjs.org/guides/features/meta-tags-seo) function in your app's layout file or **page** component and return the result of a `$nuxtI18nHead` function call from it. It's recommended to add that code within your layout file rather than in individual **page** components since this way a lot of duplication can be avoided. If you have more layouts, don't forget to add the same code in all of them.
To enable SEO metadata, declare a [`head`](https://nuxtjs.org/guides/features/meta-tags-seo) function in one of the places specified above and make it return the result of a `$nuxtI18nHead` function call.

```js {}[layouts/default.vue]
To avoid duplicating the code, it's recommended to set that option globally in your `nuxt.config.js` file and potentially override some values per-layout or per-page component, if necessary.

```js {}[nuxt.config.js]
export default {
// ...other Nuxt options...
head () {
return this.$nuxtI18nHead({ addSeoAttributes: true })
}
}
```

<alert type="warning">

Unfortunately such `head()` definition can crash during static generation (`nuxt generate`) due to Nuxt executing that function in a non-Vue Component context during generation of the `fallback` file. If this issue affects you then null-check `this.$nuxtI18nHead` before calling it.

</alert>

Check out the options you can pass to the `$nuxtI18nHead` in the [API documentation](/api#nuxti18nhead).

That's it!

If you also want to add your own metadata, you have to merge your data with the object returned by `$nuxtI18nHead`. Either as in the example below or using some library like `deepmerge` to perform a deep merge of two objects.

```js {}[layouts/default.vue]
```js {}[nuxt.config.js]
export default {
// ...other Nuxt options...
head () {
const i18nHead = this.$nuxtI18nHead({ addSeoAttributes: true })
return {
Expand Down Expand Up @@ -103,76 +115,76 @@ export default {

## Feature details

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

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

### `hreflang` alternate link
- `hreflang` alternate link

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

Since version [v6.6.0](https://github.com/nuxt-community/i18n-module/releases/tag/v6.6.0), a "catchall" locale hreflang link is provided for each locale group (e.g. `en-*`) as well. 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 **@nuxtjs/i18n** configuration. [More on hreflang](https://support.google.com/webmasters/answer/189077)

An example without selected "catchall" locale:
An example without selected "catchall" locale:

```js {}[nuxt.config.js]
['@nuxtjs/i18n', {
locales: [
{
code: 'en',
iso: 'en-US' // Will be used as "catchall" locale by default
},
{
code: 'gb',
iso: 'en-GB'
}
]
}]
```

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

```js {}[nuxt.config.js]
['@nuxtjs/i18n', {
locales: [
{
code: 'en',
iso: 'en-US'
},
{
code: 'gb',
iso: 'en-GB',
isCatchallLocale: true // This one will be used as catchall locale
}
]
}]
```
```js {}[nuxt.config.js]
['@nuxtjs/i18n', {
locales: [
{
code: 'en',
iso: 'en-US' // Will be used as "catchall" locale by default
},
{
code: 'gb',
iso: 'en-GB'
}
]
}]
```

Here is how you'd use `isCatchallLocale` to selected another locale:
```js {}[nuxt.config.js]
['@nuxtjs/i18n', {
locales: [
{
code: 'en',
iso: 'en-US'
},
{
code: 'gb',
iso: 'en-GB',
isCatchallLocale: true // This one will be used as catchall locale
}
]
}]
```
In case you already have an `en` locale `iso` set, it'll be used as the "catchall" without doing anything

```js {}[nuxt.config.js]
['@nuxtjs/i18n', {
locales: [
{
code: 'gb',
iso: 'en-GB'
},
{
code: 'en',
iso: 'en' // will be used as "catchall" locale
}
]
}]
```
```js {}[nuxt.config.js]
['@nuxtjs/i18n', {
locales: [
{
code: 'gb',
iso: 'en-GB'
},
{
code: 'en',
iso: 'en' // will be used as "catchall" locale
}
]
}]
```

### OpenGraph Locale tag generation
- 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
- 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 the query parameters, the canonical link will **not include** query params. 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. Note that there is currently no way to override that in case that including a specific query param would be desired.

[More on canonical](https://support.google.com/webmasters/answer/182192#dup-content)
[More on canonical](https://support.google.com/webmasters/answer/182192#dup-content)
3 changes: 2 additions & 1 deletion src/templates/plugin.routing.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,8 @@ const VueInstanceProxy = function (targetFunction) {
localePath: this.localePath,
localeRoute: this.localeRoute,
localeLocation: this.localeLocation,
req: process.server ? this.$ssrContext.req : null,
// @ts-ignore
req: process.server ? this.$root.context?.req || this.$ssrContext?.req : null,

This comment has been minimized.

Copy link
@kedrzu

kedrzu Sep 9, 2021

This change breaks compilation when used with nuxt babel preset.

This comment has been minimized.

Copy link
@kedrzu

kedrzu Sep 9, 2021

I mean - using null coalescence operator does.

This comment has been minimized.

Copy link
@rchl

rchl Sep 9, 2021

Author Collaborator

Can you create an issue for it with reproduction? I'm using those JS features in many of my projects and have no problems so it sounds like either you are using some very old Nuxt version or some non-standard settings.
BTW. nuxt babel preset is used by default

route: this.$route,
router: this.$router,
store: this.$store
Expand Down
8 changes: 0 additions & 8 deletions test/fixture/basic/layouts/default.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,3 @@
<template>
<Nuxt />
</template>

<script>
export default {
head () {
return this.$nuxtI18nHead({ addDirAttribute: false, addSeoAttributes: true })
}
}
</script>
11 changes: 10 additions & 1 deletion test/fixture/basic/nuxt.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,16 @@ import BaseConfig from '../base.config'
const config = {
...BaseConfig,
buildDir: resolve(__dirname, '.nuxt'),
srcDir: __dirname
srcDir: __dirname,
// Verifies that there is no SSR crash when used that way.
head () {
// SPARenderer calls this function without having `this` as the root Vue Component
// so null-check before calling.
if (this.$nuxtI18nHead) {
return this.$nuxtI18nHead({ addSeoAttributes: true })
}
return {}
}
}

module.exports = config

0 comments on commit b4aae9d

Please sign in to comment.