Skip to content

Commit

Permalink
refactor!: functions in the config to be used through hooks (nuxt-mod…
Browse files Browse the repository at this point in the history
…ules#1919)

* chore: initial plan

* chore: progressing on it

* chore: wrapping up

* chore: minor polish
  • Loading branch information
ineshbose committed Mar 12, 2023
1 parent 3d68d35 commit 8db386b
Show file tree
Hide file tree
Showing 16 changed files with 71 additions and 95 deletions.
4 changes: 4 additions & 0 deletions docs/content/2.guide/15.migrating.md
Expand Up @@ -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 `$`:
Expand Down
@@ -1,25 +1,25 @@
# 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.

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.

Expand All @@ -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)
}
})
})
```
18 changes: 0 additions & 18 deletions docs/content/3.options/2.routing.md
Expand Up @@ -115,24 +115,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`
Expand Down
12 changes: 0 additions & 12 deletions docs/content/4.API/3.vue-i18n.md
Expand Up @@ -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)
6 changes: 0 additions & 6 deletions playground/nuxt.config.ts
Expand Up @@ -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,
Expand Down
12 changes: 6 additions & 6 deletions 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)
}
})
})
15 changes: 15 additions & 0 deletions 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)
})
})
17 changes: 3 additions & 14 deletions specs/callbacks.spec.ts → specs/runtime_hooks.spec.ts
Expand Up @@ -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('<NuxtLink>', async () => {
const home = url('/')
const page = await createPage()
Expand Down
2 changes: 0 additions & 2 deletions src/constants.ts
Expand Up @@ -58,8 +58,6 @@ export const DEFAULT_OPTIONS = {
customRoutes: 'page',
pages: {},
skipSettingLocaleOnNavigate: false,
onBeforeLanguageSwitch: () => '',
onLanguageSwitched: () => null,
types: undefined,
debug: false
} as const
Expand Down
15 changes: 15 additions & 0 deletions src/module.ts
Expand Up @@ -284,6 +284,9 @@ function checkOptions(options: NuxtI18nOptions) {
}
}

type MaybePromise<T> = T | Promise<T>
type LocaleSwitch<T extends string = string> = { oldLocale: T; newLocale: T }

declare module '@nuxt/schema' {
interface NuxtConfig {
i18n?: NuxtI18nOptions
Expand All @@ -293,3 +296,15 @@ declare module '@nuxt/schema' {
'i18n:extend-messages': (messages: LocaleMessages<DefineLocaleMessage>[], localeCodes: string[]) => Promise<void>
}
}

declare module '#app' {
interface RuntimeNuxtHooks {
'i18n:beforeLocaleSwitch': <Context = unknown>(
params: LocaleSwitch & {
initialSetup: boolean
context: Context
}
) => MaybePromise<void>
'i18n:localeSwitched': (params: LocaleSwitch) => MaybePromise<void>
}
}
5 changes: 1 addition & 4 deletions src/options.d.ts
Expand Up @@ -36,8 +36,5 @@ export {
NuxtI18nOptionsDefault,
NuxtI18nInternalOptions,
DetectBrowserLanguageOptions,
RootRedirectOptions,
LanguageSwitchedHandler,
BeforeLanguageSwitchHandler,
LanguageSwitchedHandler
RootRedirectOptions
} from './types'
8 changes: 6 additions & 2 deletions src/runtime/plugins/i18n.ts
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions src/runtime/types.d.ts
Expand Up @@ -26,15 +26,15 @@ type BeforeLanguageSwitchHandler = (
newLocale: string,
initialSetup: boolean,
context: NuxtApp
) => string | void
) => Promise<string | void>

/**
* Called after the app's locale is switched.
*
* @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<void>

export interface ComposerCustomProperties {
/**
Expand Down
4 changes: 2 additions & 2 deletions src/runtime/utils.ts
Expand Up @@ -171,7 +171,7 @@ export async function loadAndSetLocale<Context extends NuxtApp = NuxtApp>(
}

// 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) {
Expand Down Expand Up @@ -205,7 +205,7 @@ export async function loadAndSetLocale<Context extends NuxtApp = NuxtApp>(
}
setLocale(i18n, newLocale)

onLanguageSwitched(i18n, oldLocale, newLocale)
await onLanguageSwitched(i18n, oldLocale, newLocale)

ret = true
return [ret, oldLocale]
Expand Down
11 changes: 0 additions & 11 deletions src/types.ts
Expand Up @@ -36,19 +36,8 @@ export type CustomRoutePages = {
}
}

export type BeforeLanguageSwitchHandler = <Context = unknown>(
oldLocale: string,
newLocale: string,
initialSetup: boolean,
context: Context
) => string | void

export type LanguageSwitchedHandler = (oldLocale: string, newLocale: string) => void

export type NuxtI18nOptions<Context = unknown> = {
differentDomains?: boolean
onBeforeLanguageSwitch?: BeforeLanguageSwitchHandler
onLanguageSwitched?: LanguageSwitchedHandler
detectBrowserLanguage?: DetectBrowserLanguageOptions | false
langDir?: string | null
lazy?: boolean | LazyOptions
Expand Down
6 changes: 3 additions & 3 deletions test/__snapshots__/gen.test.ts.snap
Expand Up @@ -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\\"
Expand Down Expand Up @@ -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\\"
Expand Down Expand Up @@ -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\\"
Expand Down

0 comments on commit 8db386b

Please sign in to comment.