diff --git a/docs/content/en/api.md b/docs/content/en/api.md index e570280b6..c3befa55e 100644 --- a/docs/content/en/api.md +++ b/docs/content/en/api.md @@ -116,6 +116,22 @@ Instance of [VueI18n class](http://kazupon.github.io/vue-i18n/api/#vuei18n-class Returns browser locale code filtered against the ones defined in options. +#### finalizePendingLocaleChange v6.20.0+ + + - **Arguments**: + - no arguments + - **Returns**: `Promise` + + 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). + +#### waitForPendingLocaleChange v6.20.0+ + + - **Arguments**: + - no arguments + - **Returns**: `Promise` + + Returns a promise that will be resolved once the pending locale is set. + ### Properties #### defaultDirection v6.19.0+ diff --git a/docs/content/en/lang-switcher.md b/docs/content/en/lang-switcher.md index 0602357bb..057037610 100644 --- a/docs/content/en/lang-switcher.md +++ b/docs/content/en/lang-switcher.md @@ -98,3 +98,49 @@ 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. + +## Wait for page transition + +By default, the locale will be changed right away when navigating to a route with a different locale 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: { + // ... your other options + skipSettingLocaleOnNavigate: true, + } +} +``` + +```js {}[~/plugins/router.js] +export default ({ app }) => { + app.nuxt.defaultTransition.beforeEnter = () => { + app.i18n.finalizePendingLocaleChange() + } + + // 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.waitForPendingLocaleChange() + } + return savedPosition || { x: 0, y: 0 } + } +} +``` + +If you have a specific transition defined in a page component, you would also need to call `finalizePendingLocaleChange` from there. + +```js {}[~/pages/foo.vue] +export default { + transition: { + beforeEnter() { + this.$i18n.finalizePendingLocaleChange() + } + } +} +``` diff --git a/docs/content/en/options-reference.md b/docs/content/en/options-reference.md index 70457dd34..2034a82c7 100644 --- a/docs/content/en/options-reference.md +++ b/docs/content/en/options-reference.md @@ -93,7 +93,7 @@ export default { - type: `string` - default: `ltr` -The app's default direction. Will only be used when `dir` is not specified. +The app's default direction. Will only be used when `dir` is not specified. ## `defaultLocale` @@ -268,6 +268,15 @@ A listener called right before app's locale changes. A listener called after app's locale has changed. +## `skipSettingLocaleOnNavigate` v6.20.0+ + +v6.20.0+ + +- 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 [`skipSettingLocaleOnNavigate`](./api#skipsettinglocaleonnavigate). See more information in [Wait for page transition](./lang-switcher#wait-for-page-transition). + ## `defaultLocaleRouteNameSuffix` - type: `string` diff --git a/docs/content/es/api.md b/docs/content/es/api.md index bcd0acf40..2c41e7d1c 100644 --- a/docs/content/es/api.md +++ b/docs/content/es/api.md @@ -116,6 +116,22 @@ Instance of [VueI18n class](http://kazupon.github.io/vue-i18n/api/#vuei18n-class Devuelve el código del idioma que utiliza el navegador, filtrado según los códigos definidos en las opciones. +#### finalizePendingLocaleChange v6.20.0+ + + - **Arguments**: + - no arguments + - **Returns**: `Promise` + + 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). + +#### waitForPendingLocaleChange v6.20.0+ + + - **Arguments**: + - no arguments + - **Returns**: `Promise` + + Returns a promise that will be resolved once the pending locale is set. + ### Properties #### defaultDirection v6.19.0+ diff --git a/docs/content/es/lang-switcher.md b/docs/content/es/lang-switcher.md index 1f8814e0d..2e1ca4c90 100644 --- a/docs/content/es/lang-switcher.md +++ b/docs/content/es/lang-switcher.md @@ -96,3 +96,49 @@ 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. + +## Wait for page transition + +By default, the locale will be changed right away when navigating to a route with a different locale 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: { + // ... your other options + skipSettingLocaleOnNavigate: true, + } +} +``` + +```js {}[~/plugins/router.js] +export default ({ app }) => { + app.nuxt.defaultTransition.beforeEnter = () => { + app.i18n.finalizePendingLocaleChange() + } + + // 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.waitForPendingLocaleChange() + } + return savedPosition || { x: 0, y: 0 } + } +} +``` + +If you have a specific transition defined in a page component, you would also need to call `finalizePendingLocaleChange` from there. + +```js {}[~/pages/foo.vue] +export default { + transition: { + beforeEnter() { + this.$i18n.finalizePendingLocaleChange() + } + } +} +``` diff --git a/docs/content/es/options-reference.md b/docs/content/es/options-reference.md index f2bc1976e..6f5095205 100644 --- a/docs/content/es/options-reference.md +++ b/docs/content/es/options-reference.md @@ -266,6 +266,15 @@ A listener called right before app's locale changes. A listener called after app's locale has changed. +## `skipSettingLocaleOnNavigate` v6.20.0+ + +v6.20.0+ + +- 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 [`skipSettingLocaleOnNavigate`](./api#skipsettinglocaleonnavigate). See more information in [Wait for page transition](./lang-switcher#wait-for-page-transition). + ## `defaultLocaleRouteNameSuffix` - type: `string` diff --git a/src/helpers/constants.js b/src/helpers/constants.js index d5c371fe6..edf8dbab2 100644 --- a/src/helpers/constants.js +++ b/src/helpers/constants.js @@ -54,6 +54,7 @@ exports.DEFAULT_OPTIONS = { }, parsePages: true, pages: {}, + skipSettingLocaleOnNavigate: false, beforeLanguageSwitch: () => null, onLanguageSwitched: () => null } diff --git a/src/templates/plugin.main.js b/src/templates/plugin.main.js index 107145dc9..fa3636f48 100644 --- a/src/templates/plugin.main.js +++ b/src/templates/plugin.main.js @@ -18,6 +18,7 @@ import { onLanguageSwitched, rootRedirect, routesNameSeparator, + skipSettingLocaleOnNavigate, STRATEGIES, strategy, vueI18n, @@ -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 finalizePendingLocaleChange = async () => { + if (!app.i18n.__pendingLocale) { + return + } + await app.i18n.setLocale(app.i18n.__pendingLocale) + app.i18n.__resolvePendingLocalePromise() + app.i18n.__pendingLocale = null + } + + const waitForPendingLocaleChange = 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 @@ -262,7 +285,12 @@ export default async (context) => { i18n.getLocaleCookie = () => getLocaleCookie(req, { useCookie, cookieKey, localeCodes }) i18n.setLocale = (locale) => loadAndSetLocale(locale) i18n.getBrowserLocale = () => getBrowserLocale() + i18n.finalizePendingLocaleChange = finalizePendingLocaleChange + i18n.waitForPendingLocaleChange = waitForPendingLocaleChange i18n.__baseUrl = app.i18n.__baseUrl + i18n.__pendingLocale = app.i18n.__pendingLocale + i18n.__pendingLocalePromise = app.i18n.__pendingLocalePromise + i18n.__resolvePendingLocalePromise = app.i18n.__resolvePendingLocalePromise } // Set instance options diff --git a/types/nuxt-i18n.d.ts b/types/nuxt-i18n.d.ts index 86d7e8da5..bd4248f82 100644 --- a/types/nuxt-i18n.d.ts +++ b/types/nuxt-i18n.d.ts @@ -80,6 +80,7 @@ declare namespace NuxtVueI18n { rootRedirect?: string | null | RootRedirectInterface routesNameSeparator?: string seo?: boolean + skipSettingLocaleOnNavigate?: boolean, strategy?: Strategies vueI18n?: VueI18n.I18nOptions | string vueI18nLoader?: boolean diff --git a/types/vue.d.ts b/types/vue.d.ts index a1bc4f528..8ffb36977 100644 --- a/types/vue.d.ts +++ b/types/vue.d.ts @@ -12,10 +12,12 @@ declare module 'vue-i18n' { // it is necessary for the $i18n property in Vue interface: "readonly $i18n: VueI18n & IVueI18n" interface IVueI18n extends NuxtVueI18n.Options.NuxtI18nInterface { localeProperties: NuxtVueI18n.Options.LocaleObject - getLocaleCookie() : string | undefined - setLocaleCookie(locale: string) : undefined - setLocale(locale: string) : Promise - getBrowserLocale() : string | undefined + getLocaleCookie(): string | undefined + setLocaleCookie(locale: string): undefined + setLocale(locale: string): Promise + getBrowserLocale(): string | undefined + finalizePendingLocaleChange(): Promise + waitForPendingLocaleChange(): Promise } }