Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: API for handling locale change during page transitions #963

Merged
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions docs/content/en/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,22 @@ Instance of [VueI18n class](http://kazupon.github.io/vue-i18n/api/#vuei18n-class

Switches locale of the app to specified locale code. If `useCookie` option is enabled, locale cookie will be updated with new value. If prefixes are enabled (`strategy` other than `no_prefix`), will navigate to new locale's route.

#### setPendingLocale
pmrotule marked this conversation as resolved.
Show resolved Hide resolved

- **Arguments**:
- no arguments
- **Returns**: `Promise<undefined>`

Switches to the pending locale that would have been set on navigate, but was prevented by the option [`skipSettingLocaleOnNavigate`](./options-reference#skipsettinglocaleonnavigate). See more information in [Wait for page transition](./lang-switcher#wait-for-page-transition).

#### waitForPendingLocale

- **Arguments**:
- no arguments
- **Returns**: `Promise<undefined>`

Returns a promise that will be resolved once the pending locale is set.

#### getBrowserLocale

- **Arguments**:
Expand Down
43 changes: 43 additions & 0 deletions docs/content/en/lang-switcher.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,46 @@ export default {
**nuxt-i18n** won't reset parameters translations for you, this means that if you use identical parameters for different routes, navigating between those routes might result in conflicting parameters. Make sure you always set params translations in such cases.

</alert>

## Wait for page transition

By default, the locale will be changed right away when calling [`switchLocalePath(path)`](./api#switchlocalepath) which means that if you have a page transition, it will fade out the page with the text already switched to the new language and fade back in with the same content.
pmrotule marked this conversation as resolved.
Show resolved Hide resolved

To work around the issue, you can set the option [`skipSettingLocaleOnNavigate`](./options-reference#skipsettinglocaleonnavigate) to `true` and handle setting the locale yourself from a `beforeEnter` transition hook defined in a plugin.

```js{}[nuxt.config.js]
export default {
plugins: ['~/plugins/router'],
i18n: {
skipSettingLocaleOnNavigate: true,
}
}
```

```js{}[plugins/router.js]
export default ({ app }) => {
app.nuxt.defaultTransition.beforeEnter = () => {
app.i18n.setPendingLocale()
}
// Optional: wait for locale before scrolling for a smoother transition
app.router.options.scrollBehavior = async (to, from, savedPosition) => {
// Make sure the route has changed
if (to.name !== from.name) await app.i18n.waitForPendingLocale()
return savedPosition || { x: 0, y: 0 }
}
}
```

If you have a specific transition defined in a page component, you would also need to call `setPendingLocale` from there.

```js{}[pages/foo.vue]
export default {
transition: {
beforeEnter() {
this.$i18n.setPendingLocale()
}
}
}
```
7 changes: 7 additions & 0 deletions docs/content/en/options-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,13 @@ A listener called after app's locale has changed.

Internal suffix added to generated route names for default locale, if strategy is `prefix_and_default`. You shouldn't need to change this.

## `skipSettingLocaleOnNavigate`

- type: `boolean`
- default: `false`

If true, the locale will not be set when navigating to a new locale. This is useful if you want to wait for the page transition to end before setting the locale yourself using [`setPendingLocale`](./api#skipsettinglocaleonnavigate). See more information in [Wait for page transition](./lang-switcher#wait-for-page-transition).

## `routesNameSeparator`

- type: `string`
Expand Down
16 changes: 16 additions & 0 deletions docs/content/es/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,22 @@ Instance of [VueI18n class](http://kazupon.github.io/vue-i18n/api/#vuei18n-class

Switches locale of the app to specified locale code. If `useCookie` option is enabled, locale cookie will be updated with new value. If prefixes are enabled (`strategy` other than `no_prefix`), will navigate to new locale's route.

#### setPendingLocale

- **Arguments**:
- no arguments
- **Returns**: `Promise<undefined>`

Switches to the pending locale that would have been set on navigate, but was prevented by the option [`skipSettingLocaleOnNavigate`](./options-reference#skipsettinglocaleonnavigate). See more information in [Wait for page transition](./lang-switcher#wait-for-page-transition).

#### waitForPendingLocale

- **Arguments**:
- no arguments
- **Returns**: `Promise<undefined>`

Returns a promise that will be resolved once the pending locale is set.

#### getBrowserLocale

- **Parámetros**:
Expand Down
43 changes: 43 additions & 0 deletions docs/content/es/lang-switcher.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,46 @@ export default {
**nuxt-i18n** no restablecerá las traducciones de parámetros por usted, esto significa que si utiliza parámetros idénticos para diferentes rutas, navegar entre esas rutas podría generar parámetros conflictivos. Asegúrese de establecer siempre traducciones de parámetros en tales casos.

</alert>

## Wait for page transition

By default, the locale will be changed right away when calling [`switchLocalePath(path)`](./api#switchlocalepath) which means that if you have a page transition, it will fade out the page with the text already switched to the new language and fade back in with the same content.

To work around the issue, you can set the option [`skipSettingLocaleOnNavigate`](./options-reference#skipsettinglocaleonnavigate) to `true` and handle setting the locale yourself from a `beforeEnter` transition hook defined in a plugin.

```js{}[nuxt.config.js]
export default {
plugins: ['~/plugins/router'],
i18n: {
skipSettingLocaleOnNavigate: true,
}
}
```

```js{}[plugins/router.js]
export default ({ app }) => {
app.nuxt.defaultTransition.beforeEnter = () => {
app.i18n.setPendingLocale()
}
// Optional: wait for locale before scrolling for a smoother transition
app.router.options.scrollBehavior = async (to, from, savedPosition) => {
// Make sure the route has changed
if (to.name !== from.name) await app.i18n.waitForPendingLocale()
return savedPosition || { x: 0, y: 0 }
}
}
```

If you have a specific transition defined in a page component, you would also need to call `setPendingLocale` from there.

```js{}[pages/foo.vue]
export default {
transition: {
beforeEnter() {
this.$i18n.setPendingLocale()
}
}
}
```
7 changes: 7 additions & 0 deletions docs/content/es/options-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,13 @@ A listener called after app's locale has changed.

Internal suffix added to generated route names for default locale, if strategy is `prefix_and_default`. You shouldn't need to change this.

## `skipSettingLocaleOnNavigate`

- type: `boolean`
- default: `false`

If true, the locale will not be set when navigating to a new locale. This is useful if you want to wait for the page transition to end before setting the locale yourself using [`setPendingLocale`](./api#skipsettinglocaleonnavigate). See more information in [Wait for page transition](./lang-switcher#wait-for-page-transition).

## `routesNameSeparator`

- type: `string`
Expand Down
1 change: 1 addition & 0 deletions src/helpers/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ exports.DEFAULT_OPTIONS = {
},
parsePages: true,
pages: {},
skipSettingLocaleOnNavigate: false,
beforeLanguageSwitch: () => null,
onLanguageSwitched: () => null
}
Expand Down
30 changes: 29 additions & 1 deletion src/templates/plugin.main.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
onLanguageSwitched,
rootRedirect,
routesNameSeparator,
skipSettingLocaleOnNavigate,
STRATEGIES,
strategy,
vueI18n,
Expand Down Expand Up @@ -196,11 +197,33 @@ export default async (context) => {
(detectBrowserLanguage && doDetectBrowserLanguage(route)) ||
getLocaleFromRoute(route) || app.i18n.locale || app.i18n.defaultLocale || ''

await app.i18n.setLocale(finalLocale)
if (skipSettingLocaleOnNavigate) {
app.i18n.__pendingLocale = finalLocale
app.i18n.__pendingLocalePromise = new Promise(resolve => {
app.i18n.__resolvePendingLocalePromise = resolve
})
} else {
await app.i18n.setLocale(finalLocale)
}

return [null, null]
}

const setPendingLocale = async () => {
if (!app.i18n.__pendingLocale) {
return
}
await app.i18n.setLocale(app.i18n.__pendingLocale)
app.i18n.__resolvePendingLocalePromise()
app.i18n.__pendingLocale = null
}

const waitForPendingLocale = async () => {
if (app.i18n.__pendingLocale) {
await app.i18n.__pendingLocalePromise
}
}

const getBrowserLocale = () => {
if (process.client && typeof navigator !== 'undefined' && navigator.languages) {
// Get browser language either from navigator if running on client side, or from the headers
Expand Down Expand Up @@ -261,8 +284,13 @@ export default async (context) => {
i18n.setLocaleCookie = locale => setLocaleCookie(locale, res, { useCookie, cookieDomain, cookieKey, cookieSecure, cookieCrossOrigin })
i18n.getLocaleCookie = () => getLocaleCookie(req, { useCookie, cookieKey, localeCodes })
i18n.setLocale = (locale) => loadAndSetLocale(locale)
i18n.setPendingLocale = setPendingLocale
i18n.waitForPendingLocale = waitForPendingLocale
i18n.getBrowserLocale = () => getBrowserLocale()
i18n.__baseUrl = app.i18n.__baseUrl
i18n.__pendingLocale = app.i18n.__pendingLocale
i18n.__pendingLocalePromise = app.i18n.__pendingLocalePromise
i18n.__resolvePendingLocalePromise = app.i18n.__resolvePendingLocalePromise
}

// Set instance options
Expand Down