diff --git a/docs/content/2.guide/15.migrating.md b/docs/content/2.guide/15.migrating.md index 95c7523e7..3ef507554 100644 --- a/docs/content/2.guide/15.migrating.md +++ b/docs/content/2.guide/15.migrating.md @@ -149,6 +149,10 @@ About details, See also [Lang Switcher](/api/lang-switcher#dynamic-route-paramet This option is no longer necessary, because i18n custom block is supported by [unplugin-vue-i18n](https://github.com/intlify/bundle-tools/tree/main/packages/unplugin-vue-i18n) **as default**. +### Deprecated `onBeforeLanguageSwitch` and `onLanguageSwitched` function options + +These functions can now be triggered using Nuxt runtime hooks. Please refer to [runtime hooks](/guide/runtime-hooks) to see how to use these. + ### Change some export APIs name on Nuxt context The following API will be changed to `$`: diff --git a/docs/content/2.guide/2.callbacks.md b/docs/content/2.guide/2.runtime-hooks.md similarity index 55% rename from docs/content/2.guide/2.callbacks.md rename to docs/content/2.guide/2.runtime-hooks.md index e4815c7c2..2bff2fe73 100644 --- a/docs/content/2.guide/2.callbacks.md +++ b/docs/content/2.guide/2.runtime-hooks.md @@ -1,12 +1,12 @@ -# Callbacks +# Runtime Hooks -Nuxt i18n module exposes some callbacks that you can use to perform specific tasks that depend on the app's language. +Nuxt i18n module exposes some [runtime hooks](https://nuxt.com/docs/guide/going-further/hooks#app-hooks-runtime) as callbacks that you can use to perform specific tasks that depend on the app's language. --- **Nuxt i18n module** exposes some callbacks that you can use to perform specific tasks that depend on the app's language. -### `onBeforeLanguageSwitch` +### `i18n:beforeLocaleSwitch` Called before the app's locale is switched. Can be used to override the new locale by returning a new locale code. @@ -14,12 +14,12 @@ Parameters: - **oldLocale**: the app's locale before the switch - **newLocale**: the app's locale after the switch -- **isInitialSetup**: set to `true` if it's the initial locale switch that triggers on app load. It's a special case since the locale is not technically set yet so we're switching from no locale to locale. -- **nuxtApp**: the Nuxt app +- **initialSetup**: set to `true` if it's the initial locale switch that triggers on app load. It's a special case since the locale is not technically set yet so we're switching from no locale to locale. +- **context**: the Nuxt app Returns: `string` or nothing -### `onLanguageSwitched` +### `i18n:localeSwitched` Called right after the app's locale has been switched. @@ -34,13 +34,14 @@ A typical usage would be to define those callbacks via a plugin where you can ac ```js {}[/plugins/i18n.js] export default defineNuxtPlugin(nuxtApp => { - // onBeforeLanguageSwitch called right before setting a new locale - nuxtApp.$i18n.onBeforeLanguageSwitch = (oldLocale, newLocale, isInitialSetup, nuxtApp) => { - console.log('onBeforeLanguageSwitch', oldLocale, newLocale, isInitialSetup) - } - // onLanguageSwitched called right after a new locale has been set - nuxtApp.$i18n.onLanguageSwitched = (oldLocale, newLocale) => { + // called right before setting a new locale + nuxtApp.hook('i18n:beforeLocaleSwitch', ({ oldLocale, newLocale, initialSetup, context }) => { + console.log('onBeforeLanguageSwitch', oldLocale, newLocale, initialSetup) + }) + + // called right after a new locale has been set + nuxtApp.hook('i18n:localeSwitched', ({oldLocale, newLocale}) => { console.log('onLanguageSwitched', oldLocale, newLocale) - } + }) }) ``` diff --git a/docs/content/3.options/2.routing.md b/docs/content/3.options/2.routing.md index a34740dbb..ee6777494 100644 --- a/docs/content/3.options/2.routing.md +++ b/docs/content/3.options/2.routing.md @@ -113,24 +113,6 @@ Whether [custom paths](/guide/custom-paths) are extracted from page files If `customRoutes` option is disabled with `config`, the module will look for custom routes in the `pages` option. Refer to the [Routing](/guide/routing-strategies) for usage. -## `onBeforeLanguageSwitch` - -- type: `function` -- default: `(oldLocale, newLocale, isInitialSetup, context) => {}` - -A listener called before the app's locale is changed. Can override the locale that is about to be set. - -See [callbacks](/guide/callbacks) - -## `onLanguageSwitched` - -- type: `function` -- default: `(oldLocale, newLocale) => {}` - -A listener called after app's locale has changed. - -See [callbacks](/guide/callbacks) - ## `skipSettingLocaleOnNavigate` - type: `boolean` diff --git a/docs/content/4.API/3.vue-i18n.md b/docs/content/4.API/3.vue-i18n.md index 35a322d53..34be1b467 100644 --- a/docs/content/4.API/3.vue-i18n.md +++ b/docs/content/4.API/3.vue-i18n.md @@ -102,15 +102,3 @@ Object of the current locale properties. - **Type**: `boolean` Whether `differentDomains` option is enabled. - -### onBeforeLanguageSwitch - -- **Type**: `Function` - -See [callbacks](/guide/callbacks) - -### onLanguageSwitched - -- **Type**: `Function` - -See [callbacks](/guide/callbacks) diff --git a/playground/nuxt.config.ts b/playground/nuxt.config.ts index 4c5eaa735..d49d3bcdc 100644 --- a/playground/nuxt.config.ts +++ b/playground/nuxt.config.ts @@ -78,12 +78,6 @@ export default defineNuxtConfig({ // // // cookieKey: 'my_custom_cookie_name', // // redirectOn: 'root' // }, - onBeforeLanguageSwitch: (oldLocale: string, newLocale: string, initial: boolean, nuxt: NuxtApp) => { - console.log('onBeforeLanguageSwitch', oldLocale, newLocale, initial) - }, - onLanguageSwitched: (oldLocale: string, newLocale: string) => { - console.log('onLanguageSwitched', oldLocale, newLocale) - }, // vueI18n: './vue-i18n.options.ts' vueI18n: { legacy: false, diff --git a/playground/plugins/i18n.ts b/playground/plugins/i18n.ts index 85d3cef69..8a070e260 100644 --- a/playground/plugins/i18n.ts +++ b/playground/plugins/i18n.ts @@ -1,11 +1,11 @@ import { defineNuxtPlugin } from '#imports' export default defineNuxtPlugin(nuxtApp => { - nuxtApp.$i18n.onBeforeLanguageSwitch = (oldLocale, newLocale, isInitialSetup, nuxtApp) => { - console.log('onBeforeLanguageSwitch', oldLocale, newLocale, isInitialSetup) - } - // onLanguageSwitched called right after a new locale has been set - nuxtApp.$i18n.onLanguageSwitched = (oldLocale, newLocale) => { + nuxtApp.hook('i18n:beforeLocaleSwitch', ({ oldLocale, newLocale, initialSetup }) => { + console.log('onBeforeLanguageSwitch', oldLocale, newLocale, initialSetup) + }) + + nuxtApp.hook('i18n:localeSwitched', ({ oldLocale, newLocale }) => { console.log('onLanguageSwitched', oldLocale, newLocale) - } + }) }) diff --git a/specs/fixtures/plugins/i18nHooks.ts b/specs/fixtures/plugins/i18nHooks.ts new file mode 100644 index 000000000..21222e603 --- /dev/null +++ b/specs/fixtures/plugins/i18nHooks.ts @@ -0,0 +1,15 @@ +import { defineNuxtPlugin } from '#imports' + +export default defineNuxtPlugin(nuxtApp => { + nuxtApp.hook('i18n:beforeLocaleSwitch', ({ oldLocale, newLocale, initialSetup }) => { + console.log('onBeforeLanguageSwitch', oldLocale, newLocale, initialSetup) + + if (newLocale === 'en') { + return 'fr' + } + }) + + nuxtApp.hook('i18n:localeSwitched', ({ oldLocale, newLocale }) => { + console.log('onLanguageSwitched', oldLocale, newLocale) + }) +}) diff --git a/specs/callbacks.spec.ts b/specs/runtime_hooks.spec.ts similarity index 78% rename from specs/callbacks.spec.ts rename to specs/runtime_hooks.spec.ts index 26dcd5679..f5e190207 100644 --- a/specs/callbacks.spec.ts +++ b/specs/runtime_hooks.spec.ts @@ -8,23 +8,12 @@ await setup({ browser: true, // overrides nuxtConfig: { - i18n: { - defaultLocale: 'en', - // eslint-disable-next-line @typescript-eslint/no-explicit-any - onBeforeLanguageSwitch: (oldLocale: string, newLocale: string, initialSetup: boolean, context: any) => { - console.log('onBeforeLanguageSwitch', oldLocale, newLocale, initialSetup, context) - if (newLocale === 'en') { - return 'fr' - } - }, - onLanguageSwitched: (oldLocale: string, newLocale: string) => { - console.log('onLanguageSwitched', oldLocale, newLocale) - } - } + plugins: [fileURLToPath(new URL(`./fixtures/plugins/i18nHooks.ts`, import.meta.url))], + i18n: { defaultLocale: 'en' } } }) -describe('onBeforeLanguageSwitch / onLanguageSwitched', () => { +describe('beforeLocaleSwitch / localeSwitched', () => { test('', async () => { const home = url('/') const page = await createPage() diff --git a/src/constants.ts b/src/constants.ts index 560a21747..89895deef 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -58,8 +58,6 @@ export const DEFAULT_OPTIONS = { customRoutes: 'page', pages: {}, skipSettingLocaleOnNavigate: false, - onBeforeLanguageSwitch: () => '', - onLanguageSwitched: () => null, types: undefined, debug: false } as const diff --git a/src/module.ts b/src/module.ts index c965781e8..41142c9fc 100644 --- a/src/module.ts +++ b/src/module.ts @@ -277,6 +277,9 @@ function checkOptions(options: NuxtI18nOptions) { } } +type MaybePromise = T | Promise +type LocaleSwitch = { oldLocale: T; newLocale: T } + declare module '@nuxt/schema' { interface NuxtConfig { i18n?: NuxtI18nOptions @@ -286,3 +289,15 @@ declare module '@nuxt/schema' { 'i18n:extend-messages': (messages: LocaleMessages[], localeCodes: string[]) => Promise } } + +declare module '#app' { + interface RuntimeNuxtHooks { + 'i18n:beforeLocaleSwitch': ( + params: LocaleSwitch & { + initialSetup: boolean + context: Context + } + ) => MaybePromise + 'i18n:localeSwitched': (params: LocaleSwitch) => MaybePromise + } +} diff --git a/src/options.d.ts b/src/options.d.ts index dc86c65c9..881688aed 100644 --- a/src/options.d.ts +++ b/src/options.d.ts @@ -36,8 +36,5 @@ export { NuxtI18nOptionsDefault, NuxtI18nInternalOptions, DetectBrowserLanguageOptions, - RootRedirectOptions, - LanguageSwitchedHandler, - BeforeLanguageSwitchHandler, - LanguageSwitchedHandler + RootRedirectOptions } from './types' diff --git a/src/runtime/plugins/i18n.ts b/src/runtime/plugins/i18n.ts index eb7c532f6..548dc0060 100644 --- a/src/runtime/plugins/i18n.ts +++ b/src/runtime/plugins/i18n.ts @@ -224,8 +224,12 @@ export default defineNuxtPlugin(async nuxt => { _getLocaleCookie(nuxt.ssrContext, { ...nuxtI18nOptions.detectBrowserLanguage, localeCodes }) composer.setLocaleCookie = (locale: string) => _setLocaleCookie(locale, nuxt.ssrContext, nuxtI18nOptions.detectBrowserLanguage || undefined) - composer.onBeforeLanguageSwitch = nuxtI18nOptions.onBeforeLanguageSwitch - composer.onLanguageSwitched = nuxtI18nOptions.onLanguageSwitched + + composer.onBeforeLanguageSwitch = (oldLocale, newLocale, initialSetup, context) => + nuxt.callHook('i18n:beforeLocaleSwitch', { oldLocale, newLocale, initialSetup, context }) + composer.onLanguageSwitched = (oldLocale, newLocale) => + nuxt.callHook('i18n:localeSwitched', { oldLocale, newLocale }) + composer.finalizePendingLocaleChange = async () => { if (!i18n.__pendingLocale) { return diff --git a/src/runtime/types.d.ts b/src/runtime/types.d.ts index bb26e1ab3..1fd4ef14e 100644 --- a/src/runtime/types.d.ts +++ b/src/runtime/types.d.ts @@ -26,7 +26,7 @@ type BeforeLanguageSwitchHandler = ( newLocale: string, initialSetup: boolean, context: NuxtApp -) => string | void +) => Promise /** * Called after the app's locale is switched. @@ -34,7 +34,7 @@ type BeforeLanguageSwitchHandler = ( * @param oldLocale - The app's locale before the switch * @param newLocale - The app's locale after the switch. */ -type LanguageSwitchedHandler = (oldLocale: string, newLocale: string) => void +type LanguageSwitchedHandler = (oldLocale: string, newLocale: string) => Promise export interface ComposerCustomProperties { /** diff --git a/src/runtime/utils.ts b/src/runtime/utils.ts index 37861c922..e22a183f1 100644 --- a/src/runtime/utils.ts +++ b/src/runtime/utils.ts @@ -171,7 +171,7 @@ export async function loadAndSetLocale( } // call onBeforeLanguageSwitch - const localeOverride = onBeforeLanguageSwitch(i18n, oldLocale, newLocale, initial, context) + const localeOverride = await onBeforeLanguageSwitch(i18n, oldLocale, newLocale, initial, context) const localeCodes = getLocaleCodes(i18n) if (localeOverride && localeCodes && localeCodes.includes(localeOverride)) { if (localeOverride === oldLocale) { @@ -205,7 +205,7 @@ export async function loadAndSetLocale( } setLocale(i18n, newLocale) - onLanguageSwitched(i18n, oldLocale, newLocale) + await onLanguageSwitched(i18n, oldLocale, newLocale) ret = true return [ret, oldLocale] diff --git a/src/types.ts b/src/types.ts index 0d6964393..a18fbe4bc 100644 --- a/src/types.ts +++ b/src/types.ts @@ -36,19 +36,8 @@ export type CustomRoutePages = { } } -export type BeforeLanguageSwitchHandler = ( - oldLocale: string, - newLocale: string, - initialSetup: boolean, - context: Context -) => string | void - -export type LanguageSwitchedHandler = (oldLocale: string, newLocale: string) => void - export type NuxtI18nOptions = { differentDomains?: boolean - onBeforeLanguageSwitch?: BeforeLanguageSwitchHandler - onLanguageSwitched?: LanguageSwitchedHandler detectBrowserLanguage?: DetectBrowserLanguageOptions | false langDir?: string | null lazy?: boolean | LazyOptions diff --git a/test/__snapshots__/gen.test.ts.snap b/test/__snapshots__/gen.test.ts.snap index fe1dc18a9..6e701c88b 100644 --- a/test/__snapshots__/gen.test.ts.snap +++ b/test/__snapshots__/gen.test.ts.snap @@ -24,7 +24,7 @@ export const resolveNuxtI18nOptions = async (context) => { return nuxtI18nOptions } -export const nuxtI18nOptionsDefault = Object({vueI18n: undefined,locales: [],defaultLocale: \\"\\",defaultDirection: \\"ltr\\",routesNameSeparator: \\"___\\",trailingSlash: false,defaultLocaleRouteNameSuffix: \\"default\\",strategy: \\"prefix_except_default\\",lazy: false,langDir: null,rootRedirect: null,detectBrowserLanguage: Object({\\"alwaysRedirect\\":false,\\"cookieCrossOrigin\\":false,\\"cookieDomain\\":null,\\"cookieKey\\":\\"i18n_redirected\\",\\"cookieSecure\\":false,\\"fallbackLocale\\":\\"\\",\\"redirectOn\\":\\"root\\",\\"useCookie\\":true}),differentDomains: false,baseUrl: \\"\\",dynamicRouteParams: false,customRoutes: \\"page\\",pages: Object({}),skipSettingLocaleOnNavigate: false,onBeforeLanguageSwitch: (() => \\"\\"),onLanguageSwitched: (() => null),types: undefined,debug: false}) +export const nuxtI18nOptionsDefault = Object({vueI18n: undefined,locales: [],defaultLocale: \\"\\",defaultDirection: \\"ltr\\",routesNameSeparator: \\"___\\",trailingSlash: false,defaultLocaleRouteNameSuffix: \\"default\\",strategy: \\"prefix_except_default\\",lazy: false,langDir: null,rootRedirect: null,detectBrowserLanguage: Object({\\"alwaysRedirect\\":false,\\"cookieCrossOrigin\\":false,\\"cookieDomain\\":null,\\"cookieKey\\":\\"i18n_redirected\\",\\"cookieSecure\\":false,\\"fallbackLocale\\":\\"\\",\\"redirectOn\\":\\"root\\",\\"useCookie\\":true}),differentDomains: false,baseUrl: \\"\\",dynamicRouteParams: false,customRoutes: \\"page\\",pages: Object({}),skipSettingLocaleOnNavigate: false,types: undefined,debug: false}) export const nuxtI18nInternalOptions = Object({__normalizedLocales: [Object({\\"code\\":\\"en\\"})]}) export const NUXT_I18N_MODULE_ID = \\"@nuxtjs/i18n\\" @@ -141,7 +141,7 @@ export const resolveNuxtI18nOptions = async (context) => { return nuxtI18nOptions } -export const nuxtI18nOptionsDefault = Object({vueI18n: undefined,locales: [],defaultLocale: \\"\\",defaultDirection: \\"ltr\\",routesNameSeparator: \\"___\\",trailingSlash: false,defaultLocaleRouteNameSuffix: \\"default\\",strategy: \\"prefix_except_default\\",lazy: false,langDir: null,rootRedirect: null,detectBrowserLanguage: Object({\\"alwaysRedirect\\":false,\\"cookieCrossOrigin\\":false,\\"cookieDomain\\":null,\\"cookieKey\\":\\"i18n_redirected\\",\\"cookieSecure\\":false,\\"fallbackLocale\\":\\"\\",\\"redirectOn\\":\\"root\\",\\"useCookie\\":true}),differentDomains: false,baseUrl: \\"\\",dynamicRouteParams: false,customRoutes: \\"page\\",pages: Object({}),skipSettingLocaleOnNavigate: false,onBeforeLanguageSwitch: (() => \\"\\"),onLanguageSwitched: (() => null),types: undefined,debug: false}) +export const nuxtI18nOptionsDefault = Object({vueI18n: undefined,locales: [],defaultLocale: \\"\\",defaultDirection: \\"ltr\\",routesNameSeparator: \\"___\\",trailingSlash: false,defaultLocaleRouteNameSuffix: \\"default\\",strategy: \\"prefix_except_default\\",lazy: false,langDir: null,rootRedirect: null,detectBrowserLanguage: Object({\\"alwaysRedirect\\":false,\\"cookieCrossOrigin\\":false,\\"cookieDomain\\":null,\\"cookieKey\\":\\"i18n_redirected\\",\\"cookieSecure\\":false,\\"fallbackLocale\\":\\"\\",\\"redirectOn\\":\\"root\\",\\"useCookie\\":true}),differentDomains: false,baseUrl: \\"\\",dynamicRouteParams: false,customRoutes: \\"page\\",pages: Object({}),skipSettingLocaleOnNavigate: false,types: undefined,debug: false}) export const nuxtI18nInternalOptions = Object({__normalizedLocales: [Object({\\"code\\":\\"en\\"})]}) export const NUXT_I18N_MODULE_ID = \\"@nuxtjs/i18n\\" @@ -172,7 +172,7 @@ export const resolveNuxtI18nOptions = async (context) => { return nuxtI18nOptions } -export const nuxtI18nOptionsDefault = Object({vueI18n: undefined,locales: [],defaultLocale: \\"\\",defaultDirection: \\"ltr\\",routesNameSeparator: \\"___\\",trailingSlash: false,defaultLocaleRouteNameSuffix: \\"default\\",strategy: \\"prefix_except_default\\",lazy: false,langDir: null,rootRedirect: null,detectBrowserLanguage: Object({\\"alwaysRedirect\\":false,\\"cookieCrossOrigin\\":false,\\"cookieDomain\\":null,\\"cookieKey\\":\\"i18n_redirected\\",\\"cookieSecure\\":false,\\"fallbackLocale\\":\\"\\",\\"redirectOn\\":\\"root\\",\\"useCookie\\":true}),differentDomains: false,baseUrl: \\"\\",dynamicRouteParams: false,customRoutes: \\"page\\",pages: Object({}),skipSettingLocaleOnNavigate: false,onBeforeLanguageSwitch: (() => \\"\\"),onLanguageSwitched: (() => null),types: undefined,debug: false}) +export const nuxtI18nOptionsDefault = Object({vueI18n: undefined,locales: [],defaultLocale: \\"\\",defaultDirection: \\"ltr\\",routesNameSeparator: \\"___\\",trailingSlash: false,defaultLocaleRouteNameSuffix: \\"default\\",strategy: \\"prefix_except_default\\",lazy: false,langDir: null,rootRedirect: null,detectBrowserLanguage: Object({\\"alwaysRedirect\\":false,\\"cookieCrossOrigin\\":false,\\"cookieDomain\\":null,\\"cookieKey\\":\\"i18n_redirected\\",\\"cookieSecure\\":false,\\"fallbackLocale\\":\\"\\",\\"redirectOn\\":\\"root\\",\\"useCookie\\":true}),differentDomains: false,baseUrl: \\"\\",dynamicRouteParams: false,customRoutes: \\"page\\",pages: Object({}),skipSettingLocaleOnNavigate: false,types: undefined,debug: false}) export const nuxtI18nInternalOptions = Object({__normalizedLocales: [Object({\\"code\\":\\"en\\"})]}) export const NUXT_I18N_MODULE_ID = \\"@nuxtjs/i18n\\"