Skip to content

Commit

Permalink
refactor!: replace onlyOnNoPrefix and onlyOnRoot with redirectOn (#1210)
Browse files Browse the repository at this point in the history
  • Loading branch information
divine authored and rchl committed Aug 3, 2021
1 parent 8891d28 commit 449a2e8
Showing 10 changed files with 48 additions and 35 deletions.
4 changes: 2 additions & 2 deletions docs/content/en/browser-language-detection.md
Original file line number Diff line number Diff line change
@@ -13,14 +13,14 @@ By default, **nuxt-i18n** attempts to redirect users to their preferred language
detectBrowserLanguage: {
useCookie: true,
cookieKey: 'i18n_redirected',
onlyOnRoot: true, // recommended
redirectOn: 'root', // recommended
}
}]
```

<alert type="info">

For better SEO, it's recommended to set `onlyOnRoot` to `true`. With it 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.
For better SEO, it's recommended to set `redirectOn` to `root`. With it 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.

</alert>

2 changes: 2 additions & 0 deletions docs/content/en/migrating.md
Original file line number Diff line number Diff line change
@@ -17,6 +17,8 @@ Follow this guide to upgrade from one major version to the other.

### `seo` option has been removed. Use `$nuxtI18nHead({ addDirAttribute: true, addSeoAttributes: true })` instead.

### `onlyOnRoot` and `onlyOnNoPrefix` has been removed. It's features are combined into `redirectOn` instead. Use `redirectOn: 'all|root|no prefix'` instead.

## Upgrading from 5.x to 6.x

### Global SEO features are now disabled by default
10 changes: 6 additions & 4 deletions docs/content/en/options-reference.md
Original file line number Diff line number Diff line change
@@ -160,23 +160,25 @@ Directory that contains translation files to load. Can be used with or without l
## `detectBrowserLanguage`

- type: `object`
- default: `{ alwaysRedirect: false, fallbackLocale: '', onlyOnRoot: false, useCookie: true, cookieCrossOrigin: false, cookieDomain: null, cookieKey: 'i18n_redirected', cookieSecure: false }`
- default: `{ alwaysRedirect: false, fallbackLocale: '', redirectOn: 'all', useCookie: true, cookieCrossOrigin: false, cookieDomain: null, cookieKey: 'i18n_redirected', cookieSecure: false }`

Enables browser language detection to automatically redirect visitors to their preferred locale as they visit your site for the first time.

See also [Browser language detection](/browser-language-detection) for a guide.

<alert type="info">

Note that for better SEO it's recommended to set `onlyOnRoot` to true.
Note that for better SEO it's recommended to set `redirectOn` to `root`.

</alert>

Supported properties:
- `alwaysRedirect` (default: `false`) - Set to always redirect to the value stored in the cookie, not just on first visit.
- `fallbackLocale` (default: `null`) - If none of the locales match the browser's locale, use this one as a fallback.
- `onlyOnRoot` (default: `false`) - Set to `true` (recommended for improved SEO) to only attempt to detect the browser locale on the root path (`/`) of the site. Only effective when using strategy other than `'no_prefix'`.
- `onlyOnNoPrefix` (default: `false`) - This is a more permissive variant of `onlyOnRoot` that will attempt to detect the browser locale on the root path (`/`) and also on paths that have no locale prefix (like `/foo`). Only effective when `onlyOnRoot` is not enabled and using strategy other than `'no_prefix'`.
- `redirectOn` (default: `all`) - Supported options:
- `all` - detect browser locale on all paths.
- `root` (recommended for improved SEO) - only detect the browser locale on the root path (`/`) of the site. Only effective when using strategy other than `'no_prefix'`.
- `no prefix` - a more permissive variant of `root` that will detect the browser locale on the root path (`/`) and also on paths that have no locale prefix (like `/foo`). Only effective when using strategy other than `'no_prefix'`.
- `useCookie` (default: `true`) - If enabled, a cookie is set once the user has been redirected to browser's preferred locale, to prevent subsequent redirections. Set to `false` to redirect every time.
- `cookieKey` (default: `'i18n_redirected'`) - Cookie name.
- `cookieDomain` (default: `null`) - Set to override the default domain of the cookie. Defaults to the **host** of the site.
12 changes: 10 additions & 2 deletions src/helpers/constants.js
Original file line number Diff line number Diff line change
@@ -17,6 +17,15 @@ export const STRATEGIES = {
NO_PREFIX: STRATEGY_NO_PREFIX
}

const REDIRECT_ON_ALL = 'all'
const REDIRECT_ON_ROOT = 'root'
const REDIRECT_ON_NO_PREFIX = 'no prefix'
export const REDIRECT_ON_OPTIONS = {
ALL: REDIRECT_ON_ALL,
ROOT: REDIRECT_ON_ROOT,
NO_PREFIX: REDIRECT_ON_NO_PREFIX
}

export const COMPONENT_OPTIONS_KEY = 'nuxtI18n'

/** @type {Options} */
@@ -40,8 +49,7 @@ export const DEFAULT_OPTIONS = {
cookieKey: 'i18n_redirected',
cookieSecure: false,
fallbackLocale: '',
onlyOnNoPrefix: false,
onlyOnRoot: false,
redirectOn: 'all',
useCookie: true
},
differentDomains: false,
5 changes: 3 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ import { readdirSync } from 'fs'
import merge from 'lodash.merge'
// @ts-ignore
import { directive as i18nExtensionsDirective } from '@intlify/vue-i18n-extensions'
import { COMPONENT_OPTIONS_KEY, DEFAULT_OPTIONS, ROOT_DIR, STRATEGIES } from './helpers/constants'
import { COMPONENT_OPTIONS_KEY, DEFAULT_OPTIONS, ROOT_DIR, STRATEGIES, REDIRECT_ON_OPTIONS } from './helpers/constants'
import { buildHook, createExtendRoutesHook } from './core/hooks'
import { formatMessage } from './templates/utils-common'

@@ -53,7 +53,8 @@ export default function (moduleOptions) {
const templatesOptions = {
Constants: {
COMPONENT_OPTIONS_KEY,
STRATEGIES
STRATEGIES,
REDIRECT_ON_OPTIONS
},
nuxtOptions: {
isUniversalMode: nuxtOptions.mode === 'universal',
3 changes: 2 additions & 1 deletion src/templates/options.d.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import Vue from 'vue'
import { ComponentOptions } from 'vue/types/options'
import { STRATEGIES } from '../helpers/constants'
import { STRATEGIES, REDIRECT_ON_OPTIONS } from '../helpers/constants'
import { LocaleFileExport, ResolvedOptions } from '../../types/internal'

interface ModuleConstants {
COMPONENT_OPTIONS_KEY: keyof Pick<ComponentOptions<Vue>, 'nuxtI18n'>
STRATEGIES: typeof STRATEGIES
REDIRECT_ON_OPTIONS: typeof REDIRECT_ON_OPTIONS
}

interface ModuleNuxtOptions {
11 changes: 5 additions & 6 deletions src/templates/plugin.main.js
Original file line number Diff line number Diff line change
@@ -55,8 +55,7 @@ export default async (context) => {
const {
alwaysRedirect,
fallbackLocale,
onlyOnNoPrefix,
onlyOnRoot,
redirectOn,
useCookie,
cookieKey,
cookieDomain,
@@ -181,9 +180,9 @@ export default async (context) => {
}

if (getLocaleFromRoute(route) === locale) {
// If "onlyOnRoot" or "onlyOnNoPrefix" is set and strategy is "prefix_and_default", prefer unprefixed route for
// If "redirectOn" is "all" and strategy is "prefix_and_default", prefer unprefixed route for
// default locale.
if (!(onlyOnRoot || onlyOnNoPrefix) || locale !== options.defaultLocale || options.strategy !== Constants.STRATEGIES.PREFIX_AND_DEFAULT) {
if (redirectOn === Constants.REDIRECT_ON_OPTIONS.ALL || locale !== options.defaultLocale || options.strategy !== Constants.STRATEGIES.PREFIX_AND_DEFAULT) {
return ''
}
}
@@ -287,11 +286,11 @@ export default async (context) => {
}

if (options.strategy !== Constants.STRATEGIES.NO_PREFIX) {
if (onlyOnRoot) {
if (redirectOn === Constants.REDIRECT_ON_OPTIONS.ROOT) {
if (route.path !== '/') {
return ''
}
} else if (onlyOnNoPrefix) {
} else if (redirectOn === Constants.REDIRECT_ON_OPTIONS.NO_PREFIX) {
if (!alwaysRedirect && route.path.match(getLocalesRegex(options.localeCodes))) {
return ''
}
28 changes: 14 additions & 14 deletions test/browser.test.js
Original file line number Diff line number Diff line change
@@ -385,7 +385,7 @@ describe(`${browserString} (generate, no subFolders, trailingSlash === false)`,
})

for (const target of ['server', 'static']) {
describe(`${browserString} (target ${target}, generate, prefix strategy, alwaysRedirect, onlyOnRoot)`, () => {
describe(`${browserString} (target ${target}, generate, prefix strategy, alwaysRedirect, redirectOn is root)`, () => {
/** @type {import('playwright-chromium').ChromiumBrowser} */
let browser
/** @type {import('playwright-chromium').Page} */
@@ -403,7 +403,7 @@ for (const target of ['server', 'static']) {
detectBrowserLanguage: {
alwaysRedirect: true,
fallbackLocale: 'en',
onlyOnRoot: true
redirectOn: 'root'
}
}
}
@@ -968,7 +968,7 @@ describe(`${browserString} (SPA with router in hash mode)`, () => {
})
})

describe(`${browserString} (onlyOnRoot + alwaysRedirect + no_prefix)`, () => {
describe(`${browserString} (redirectOn is root + alwaysRedirect + no_prefix)`, () => {
/** @type {Nuxt} */
let nuxt
/** @type {import('playwright-chromium').ChromiumBrowser} */
@@ -982,7 +982,7 @@ describe(`${browserString} (onlyOnRoot + alwaysRedirect + no_prefix)`, () => {
detectBrowserLanguage: {
useCookie: false,
alwaysRedirect: true,
onlyOnRoot: true
redirectOn: 'root'
}
}
}
@@ -998,13 +998,13 @@ describe(`${browserString} (onlyOnRoot + alwaysRedirect + no_prefix)`, () => {
await nuxt.close()
})

test('onlyOnRoot does not affect locale detection on root path', async () => {
test('redirectOn is root does not affect locale detection on root path', async () => {
const page = await browser.newPage({ locale: 'fr' })
await page.goto(url('/'))
expect(await (await page.$('body'))?.textContent()).toContain('locale: fr')
})

test('onlyOnRoot does not affect locale detection on sub-path', async () => {
test('redirectOn is root does not affect locale detection on sub-path', async () => {
const page = await browser.newPage({ locale: 'fr' })
await page.goto(url('/about'))
expect(await (await page.$('#current-page'))?.textContent()).toContain('page: À propos')
@@ -1059,7 +1059,7 @@ describe(`${browserString} (alwaysRedirect, prefix)`, () => {
})
})

describe(`${browserString} (onlyOnRoot + prefix_except_default)`, () => {
describe(`${browserString} (redirectOn is root + prefix_except_default)`, () => {
/** @type {Nuxt} */
let nuxt
/** @type {import('playwright-chromium').ChromiumBrowser} */
@@ -1071,7 +1071,7 @@ describe(`${browserString} (onlyOnRoot + prefix_except_default)`, () => {
defaultLocale: 'en',
strategy: 'prefix_except_default',
detectBrowserLanguage: {
onlyOnRoot: true
redirectOn: 'root'
}
}
}
@@ -1131,7 +1131,7 @@ describe(`${browserString} (onlyOnRoot + prefix_except_default)`, () => {
})
})

describe(`${browserString} (onlyOnRoot + alwaysRedirect + prefix_except_default)`, () => {
describe(`${browserString} (redirectOn is root + alwaysRedirect + prefix_except_default)`, () => {
/** @type {Nuxt} */
let nuxt
/** @type {import('playwright-chromium').ChromiumBrowser} */
@@ -1144,7 +1144,7 @@ describe(`${browserString} (onlyOnRoot + alwaysRedirect + prefix_except_default)
strategy: 'prefix_except_default',
detectBrowserLanguage: {
alwaysRedirect: true,
onlyOnRoot: true
redirectOn: 'root'
}
}
}
@@ -1176,7 +1176,7 @@ describe(`${browserString} (onlyOnRoot + alwaysRedirect + prefix_except_default)
})
})

describe(`${browserString} (onlyOnRoot + prefix_and_default)`, () => {
describe(`${browserString} (redirectOn is root + prefix_and_default)`, () => {
/** @type {Nuxt} */
let nuxt
/** @type {import('playwright-chromium').ChromiumBrowser} */
@@ -1188,7 +1188,7 @@ describe(`${browserString} (onlyOnRoot + prefix_and_default)`, () => {
defaultLocale: 'en',
strategy: 'prefix_and_default',
detectBrowserLanguage: {
onlyOnRoot: true
redirectOn: 'root'
}
}
}
@@ -1219,7 +1219,7 @@ describe(`${browserString} (onlyOnRoot + prefix_and_default)`, () => {
})
})

describe(`${browserString} (onlyOnRoot + prefix)`, () => {
describe(`${browserString} (redirectOn is root + prefix)`, () => {
/** @type {Nuxt} */
let nuxt
/** @type {import('playwright-chromium').ChromiumBrowser} */
@@ -1231,7 +1231,7 @@ describe(`${browserString} (onlyOnRoot + prefix)`, () => {
defaultLocale: 'en',
strategy: 'prefix',
detectBrowserLanguage: {
onlyOnRoot: true
redirectOn: 'root'
}
}
}
4 changes: 2 additions & 2 deletions test/module.test.js
Original file line number Diff line number Diff line change
@@ -1799,7 +1799,7 @@ describe('prefix + detectBrowserLanguage', () => {
})
})

describe('prefix + detectBrowserLanguage + onlyOnNoPrefix', () => {
describe('prefix + detectBrowserLanguage + redirectOn is no_prefix', () => {
/** @type {Nuxt} */
let nuxt

@@ -1810,7 +1810,7 @@ describe('prefix + detectBrowserLanguage + onlyOnNoPrefix', () => {
strategy: 'prefix',
detectBrowserLanguage: {
useCookie: true,
onlyOnNoPrefix: true
redirectOn: 'no prefix'
}
}
}
4 changes: 2 additions & 2 deletions types/index.d.ts
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ import './vue'
export { Locale }
export type Strategies = 'no_prefix' | 'prefix_except_default' | 'prefix' | 'prefix_and_default'
export type Directions = 'ltr' | 'rtl' | 'auto'
export type RedirectOnOptions = 'all' | 'root' | 'no prefix'

export interface LocaleObject extends Record<string, any> {
code: Locale
@@ -21,8 +22,7 @@ export interface DetectBrowserLanguageOptions {
cookieKey?: string
cookieSecure?: boolean
fallbackLocale?: Locale | null
onlyOnNoPrefix?: boolean
onlyOnRoot?: boolean
redirectOn?: RedirectOnOptions
useCookie?: boolean
}

0 comments on commit 449a2e8

Please sign in to comment.