diff --git a/docs/content/docs/5.v9/3.options/10.misc.md b/docs/content/docs/5.v9/3.options/10.misc.md index bea0fa486..ae80b53bb 100644 --- a/docs/content/docs/5.v9/3.options/10.misc.md +++ b/docs/content/docs/5.v9/3.options/10.misc.md @@ -47,10 +47,10 @@ If it can not detect Nuxt configuration changing, you may need to run `nuxi prep ## `debug` -- type: `boolean` +- type: `boolean | 'verbose'` - default: `false` -Whether to use `@nuxtjs/i18n` debug mode. If `true`, logs will be output to the console. +Whether to use `@nuxtjs/i18n` debug mode. If `true` or `'verbose'`, logs will be output to the console, setting this to `'verbose'` will also log loaded messages objects. ::callout{icon="i-heroicons-exclamation-triangle" color="amber"} The purpose of this option is to help identify any problems with `@nuxtjs/i18n`. diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 971e2a591..5c2ac0803 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -274,78 +274,6 @@ importers: specifier: latest version: 3.12.4(@opentelemetry/api@1.9.0)(@parcel/watcher@2.4.1)(@types/node@20.14.9)(encoding@0.1.13)(eslint@9.5.0)(ioredis@5.4.1)(magicast@0.3.4)(optionator@0.9.4)(rollup@4.18.0)(terser@5.31.1)(typescript@5.5.2)(vite@5.3.5(@types/node@20.14.9)(terser@5.31.1))(vue-tsc@2.0.22(typescript@5.5.2)) - specs/fixtures/issues/1888: - devDependencies: - '@nuxtjs/i18n': - specifier: link:../../../.. - version: link:../../../.. - nuxt: - specifier: latest - version: 3.12.4(@opentelemetry/api@1.9.0)(@parcel/watcher@2.4.1)(@types/node@20.14.9)(encoding@0.1.13)(eslint@9.5.0)(ioredis@5.4.1)(magicast@0.3.4)(optionator@0.9.4)(rollup@4.18.0)(terser@5.31.1)(typescript@5.5.2)(vite@5.3.5(@types/node@20.14.9)(terser@5.31.1))(vue-tsc@2.0.22(typescript@5.5.2)) - - specs/fixtures/issues/2151: - devDependencies: - '@nuxtjs/i18n': - specifier: link:../../../.. - version: link:../../../.. - nuxt: - specifier: latest - version: 3.12.4(@opentelemetry/api@1.9.0)(@parcel/watcher@2.4.1)(@types/node@20.14.9)(encoding@0.1.13)(eslint@9.5.0)(ioredis@5.4.1)(magicast@0.3.4)(optionator@0.9.4)(rollup@4.18.0)(terser@5.31.1)(typescript@5.5.2)(vite@5.3.5(@types/node@20.14.9)(terser@5.31.1))(vue-tsc@2.0.22(typescript@5.5.2)) - - specs/fixtures/issues/2220: - devDependencies: - '@nuxtjs/i18n': - specifier: link:../../../.. - version: link:../../../.. - nuxt: - specifier: latest - version: 3.12.4(@opentelemetry/api@1.9.0)(@parcel/watcher@2.4.1)(@types/node@20.14.9)(encoding@0.1.13)(eslint@9.5.0)(ioredis@5.4.1)(magicast@0.3.4)(optionator@0.9.4)(rollup@4.18.0)(terser@5.31.1)(typescript@5.5.2)(vite@5.3.5(@types/node@20.14.9)(terser@5.31.1))(vue-tsc@2.0.22(typescript@5.5.2)) - - specs/fixtures/issues/2226: - devDependencies: - '@nuxtjs/i18n': - specifier: link:../../../.. - version: link:../../../.. - nuxt: - specifier: latest - version: 3.12.4(@opentelemetry/api@1.9.0)(@parcel/watcher@2.4.1)(@types/node@20.14.9)(encoding@0.1.13)(eslint@9.5.0)(ioredis@5.4.1)(magicast@0.3.4)(optionator@0.9.4)(rollup@4.18.0)(terser@5.31.1)(typescript@5.5.2)(vite@5.3.5(@types/node@20.14.9)(terser@5.31.1))(vue-tsc@2.0.22(typescript@5.5.2)) - - specs/fixtures/issues/2247: - devDependencies: - '@nuxtjs/i18n': - specifier: link:../../../.. - version: link:../../../.. - nuxt: - specifier: latest - version: 3.12.4(@opentelemetry/api@1.9.0)(@parcel/watcher@2.4.1)(@types/node@20.14.9)(encoding@0.1.13)(eslint@9.5.0)(ioredis@5.4.1)(magicast@0.3.4)(optionator@0.9.4)(rollup@4.18.0)(terser@5.31.1)(typescript@5.5.2)(vite@5.3.5(@types/node@20.14.9)(terser@5.31.1))(vue-tsc@2.0.22(typescript@5.5.2)) - - specs/fixtures/issues/2288: - devDependencies: - '@nuxtjs/i18n': - specifier: link:../../../.. - version: link:../../../.. - nuxt: - specifier: latest - version: 3.12.4(@opentelemetry/api@1.9.0)(@parcel/watcher@2.4.1)(@types/node@20.14.9)(encoding@0.1.13)(eslint@9.5.0)(ioredis@5.4.1)(magicast@0.3.4)(optionator@0.9.4)(rollup@4.18.0)(terser@5.31.1)(typescript@5.5.2)(vite@5.3.5(@types/node@20.14.9)(terser@5.31.1))(vue-tsc@2.0.22(typescript@5.5.2)) - - specs/fixtures/issues/2315: - devDependencies: - '@nuxtjs/i18n': - specifier: link:../../../.. - version: link:../../../.. - nuxt: - specifier: latest - version: 3.12.4(@opentelemetry/api@1.9.0)(@parcel/watcher@2.4.1)(@types/node@20.14.9)(encoding@0.1.13)(eslint@9.5.0)(ioredis@5.4.1)(magicast@0.3.4)(optionator@0.9.4)(rollup@4.18.0)(terser@5.31.1)(typescript@5.5.2)(vite@5.3.5(@types/node@20.14.9)(terser@5.31.1))(vue-tsc@2.0.22(typescript@5.5.2)) - - specs/fixtures/issues/2590: - devDependencies: - '@nuxtjs/i18n': - specifier: link:../../../.. - version: link:../../../.. - nuxt: - specifier: latest - version: 3.12.4(@opentelemetry/api@1.9.0)(@parcel/watcher@2.4.1)(@types/node@20.14.9)(encoding@0.1.13)(eslint@9.5.0)(ioredis@5.4.1)(magicast@0.3.4)(optionator@0.9.4)(rollup@4.18.0)(terser@5.31.1)(typescript@5.5.2)(vite@5.3.5(@types/node@20.14.9)(terser@5.31.1))(vue-tsc@2.0.22(typescript@5.5.2)) - specs/fixtures/lazy: devDependencies: '@nuxtjs/i18n': diff --git a/src/bundler.ts b/src/bundler.ts index c8b1f779f..600d9554d 100644 --- a/src/bundler.ts +++ b/src/bundler.ts @@ -84,7 +84,7 @@ export async function extendBundler(nuxt: Nuxt, nuxtOptions: Required { if (config.define) { - config.define['__DEBUG__'] = JSON.stringify(nuxtOptions.debug) + config.define['__DEBUG__'] = JSON.stringify(!!nuxtOptions.debug) } else { config.define = { - __DEBUG__: JSON.stringify(nuxtOptions.debug) + __DEBUG__: JSON.stringify(!!nuxtOptions.debug) } } debug('vite.config.define', config.define) diff --git a/src/logger.d.ts b/src/logger.d.ts new file mode 100644 index 000000000..37ccae036 --- /dev/null +++ b/src/logger.d.ts @@ -0,0 +1,5 @@ +declare module 'virtual:nuxt-i18n-logger' { + import type { ConsolaInstance } from 'consola' + + export function createLogger(label: string): ConsolaInstance +} diff --git a/src/module.ts b/src/module.ts index 38fb2d69a..7cf21c287 100644 --- a/src/module.ts +++ b/src/module.ts @@ -39,6 +39,7 @@ import { import { distDir, runtimeDir } from './dirs' import { applyLayerOptions, checkLayerOptions, resolveLayerVueI18nConfigInfo } from './layers' import { generateTemplateNuxtI18nOptions } from './template' +import { i18nVirtualLoggerPlugin, RESOLVED_VIRTUAL_NUXT_I18N_LOGGER, VIRTUAL_NUXT_I18N_LOGGER } from './virtual-logger' import type { HookResult } from '@nuxt/schema' import type { LocaleObject, NuxtI18nOptions } from './types' @@ -220,6 +221,7 @@ export default defineNuxtModule({ // for composables nuxt.options.alias['#i18n'] = resolve(distDir, 'runtime/composables/index.mjs') nuxt.options.build.transpile.push('#i18n') + nuxt.options.build.transpile.push(VIRTUAL_NUXT_I18N_LOGGER) const genTemplate = (isServer: boolean, lazy?: boolean) => { const nuxtI18nOptions = defu({}, options) @@ -251,6 +253,16 @@ export default defineNuxtModule({ getContents: () => genTemplate(false) }) + nuxt.options.imports.transform ??= {} + nuxt.options.imports.transform.include ??= [] + nuxt.options.imports.transform.include.push(new RegExp(`${RESOLVED_VIRTUAL_NUXT_I18N_LOGGER}$`)) + + nuxt.hook('vite:extendConfig', cfg => { + // eslint-disable-next-line @typescript-eslint/no-floating-promises + cfg.plugins ||= [] + cfg.plugins.push(i18nVirtualLoggerPlugin(options.debug)) + }) + /** * `PageMeta` augmentation to add `nuxtI18n` property * TODO: Remove in v9, `useSetI18nParams` should be used instead diff --git a/src/nitro.ts b/src/nitro.ts index 0fff01581..4f991d2bb 100644 --- a/src/nitro.ts +++ b/src/nitro.ts @@ -21,6 +21,7 @@ import { import type { Nuxt } from '@nuxt/schema' import type { NuxtI18nOptions, LocaleInfo } from './types' import { resolveI18nDir } from './layers' +import { i18nVirtualLoggerPlugin } from './virtual-logger' const debug = createDebug('@nuxtjs/i18n:nitro') @@ -52,6 +53,9 @@ export async function setupNitro( nitroConfig.rollupConfig.plugins = isArray(nitroConfig.rollupConfig.plugins) ? nitroConfig.rollupConfig.plugins : [nitroConfig.rollupConfig.plugins] + + nitroConfig.rollupConfig.plugins.push(i18nVirtualLoggerPlugin(nuxtOptions.debug)) + const yamlPaths = getResourcePaths(additionalParams.localeInfo, /\.ya?ml$/) if (yamlPaths.length > 0) { // @ts-ignore NOTE: A type error occurs due to a mismatch between the version of rollup on the nitro side (v4.x) and the version of rollup that `@rollup/plugin-yaml` depends on (v3.x). We ignore this type error because `@rollup/plugin-yaml` is rollup version compatible. @@ -120,7 +124,7 @@ export { localeDetector } } // setup debug flag - nitroConfig.replace['__DEBUG__'] = String(nuxtOptions.debug) + nitroConfig.replace['__DEBUG__'] = String(!!nuxtOptions.debug) debug('nitro.replace', nitroConfig.replace) }) diff --git a/src/runtime/internal.ts b/src/runtime/internal.ts index 5eaa7afcc..adeb2c378 100644 --- a/src/runtime/internal.ts +++ b/src/runtime/internal.ts @@ -6,7 +6,8 @@ import isHTTPS from 'is-https' import { useRequestHeaders, useRequestEvent, useCookie as useNuxtCookie, useRuntimeConfig, useNuxtApp } from '#imports' import { NUXT_I18N_MODULE_ID, DEFAULT_COOKIE_KEY, isSSG, localeCodes, normalizedLocales } from '#build/i18n.options.mjs' import { findBrowserLocale, getLocalesRegex } from './routing/utils' -import { createLogger, initCommonComposableOptions, type CommonComposableOptions } from './utils' +import { initCommonComposableOptions, type CommonComposableOptions } from './utils' +import { createLogger } from 'virtual:nuxt-i18n-logger' import type { Locale } from 'vue-i18n' import type { DetectBrowserLanguageOptions, LocaleObject } from '#build/i18n.options.mjs' @@ -49,20 +50,21 @@ export function parseAcceptLanguage(input: string): string[] { export function getBrowserLocale(): string | undefined { let ret: string | undefined + const logger = /*#__PURE__*/ createLogger('getBrowserLocale') if (import.meta.client) { if (navigator.languages) { // get browser language either from navigator if running on client side, or from the headers ret = findBrowserLocale(normalizedLocales, navigator.languages as string[]) - __DEBUG__ && console.log('getBrowserLocale (navigator.languages, ret) -', navigator.languages, ret) + __DEBUG__ && logger.log('(navigator.languages, ret) -', navigator.languages, ret) } } else if (import.meta.server) { const header = useRequestHeaders(['accept-language']) - __DEBUG__ && console.log('getBrowserLocale accept-language', header) + __DEBUG__ && logger.log('accept-language', header) const accept = header['accept-language'] if (accept) { ret = findBrowserLocale(normalizedLocales, parseAcceptLanguage(accept)) - __DEBUG__ && console.log('getBrowserLocale ret', ret) + __DEBUG__ && logger.log('ret', ret) } } @@ -92,8 +94,10 @@ export function getLocaleCookie( detect: false | DetectBrowserLanguageOptions, defaultLocale: string ): string | undefined { + const env = import.meta.client ? 'client' : 'server' + const logger = /*#__PURE__*/ createLogger(`getLocaleCookie:${env}`) __DEBUG__ && - console.log('getLocaleCookie', { + logger.log({ useCookie: detect && detect.useCookie, cookieKey: detect && detect.cookieKey, localeCodes @@ -104,27 +108,23 @@ export function getLocaleCookie( } const localeCode: string | undefined = cookieRef.value ?? undefined - const env = import.meta.client ? 'client' : 'server' if (localeCode == null) { - __DEBUG__ && console.log(`getLocaleCookie (${env}) - none`) + __DEBUG__ && logger.log(`none`) return } if (localeCodes.includes(localeCode)) { - __DEBUG__ && console.log(`getLocaleCookie (${env}) - locale from cookie: `, localeCode) + __DEBUG__ && logger.log(`locale from cookie: `, localeCode) return localeCode } if (defaultLocale) { - __DEBUG__ && - console.log( - `getLocaleCookie (${env}) - unknown locale cookie (${localeCode}), setting to defaultLocale (${defaultLocale})` - ) + __DEBUG__ && logger.log(`unknown locale cookie (${localeCode}), setting to defaultLocale (${defaultLocale})`) cookieRef.value = defaultLocale return defaultLocale } - __DEBUG__ && console.log(`getLocaleCookie (${env}) - unknown locale cookie (${localeCode}), unsetting cookie`) + __DEBUG__ && logger.log(`unknown locale cookie (${localeCode}), unsetting cookie`) cookieRef.value = undefined return } @@ -176,7 +176,7 @@ export function detectBrowserLanguage( detectLocaleContext: DetectLocaleContext, locale: Locale = '' ): DetectBrowserLanguageFromResult { - const logger = createLogger('detectBrowserLanguage') + const logger = /*#__PURE__*/ createLogger('detectBrowserLanguage') const _detect = runtimeDetectBrowserLanguage() // feature is disabled @@ -271,15 +271,13 @@ export function getLocaleDomain( strategy: string, route: string | RouteLocationNormalized | RouteLocationNormalizedLoaded ): string { + const logger = /*#__PURE__*/ createLogger(`getLocaleDomain`) let host = getHost() || '' + const routePath = isObject(route) ? route.path : isString(route) ? route : '' + if (host) { - __DEBUG__ && - console.log( - `MultiDomainsMultiLocales: locating domain for host: `, - host, - strategy, - isObject(route) ? route.path : route - ) + __DEBUG__ && logger.log(`locating domain for host`, { host, strategy, path: routePath }) + let matchingLocale: LocaleObject | undefined const matchingLocales = locales.filter(locale => { if (locale && locale.domain) { @@ -296,8 +294,7 @@ export function getLocaleDomain( if (matchingLocales.length === 1) { matchingLocale = matchingLocales[0] - __DEBUG__ && - console.log(`MultiDomainsMultiLocales: found only one matching domain: `, host, matchingLocales[0].code) + __DEBUG__ && logger.log(`found one matching domain`, { host, matchedLocale: matchingLocales[0].code }) } else if (matchingLocales.length > 1) { if (strategy === 'no_prefix') { console.warn( @@ -310,20 +307,13 @@ export function getLocaleDomain( } else { // get prefix from route if (route) { - const routePath = isObject(route) ? route.path : isString(route) ? route : '' - - __DEBUG__ && - console.log(`MultiDomainsMultiLocales: Check in matched domain for locale match in path: `, routePath, host) + __DEBUG__ && logger.log(`check matched domain for locale match`, { path: routePath, host }) if (routePath && routePath !== '') { const matches = routePath.match(getLocalesRegex(matchingLocales.map(l => l.code))) if (matches && matches.length > 1) { matchingLocale = matchingLocales.find(l => l.code === matches[1]) - __DEBUG__ && - console.log( - `MultiDomainsMultiLocales: Found matching locale from path. MatchingLocale is now`, - matchingLocale?.code - ) + __DEBUG__ && logger.log(`matched locale from path`, { matchedLocale: matchingLocale?.code }) } } } @@ -334,10 +324,7 @@ export function getLocaleDomain( Array.isArray(l.defaultForDomains) ? l.defaultForDomains.includes(host) : l.domainDefault ) __DEBUG__ && - console.log( - `MultiDomainsMultiLocales: matching locale not found - trying to get default for this domain. MatchingLocale is now`, - matchingLocale?.code - ) + logger.log(`no locale matched - using default for this domain`, { matchedLocale: matchingLocale?.code }) } } } diff --git a/src/runtime/messages.ts b/src/runtime/messages.ts index c91e448d8..e5c1cb0ca 100644 --- a/src/runtime/messages.ts +++ b/src/runtime/messages.ts @@ -1,4 +1,5 @@ import { deepCopy, isFunction, isArray, isObject, isString } from '@intlify/shared' +import { createLogger } from 'virtual:nuxt-i18n-logger' import type { I18nOptions, Locale, FallbackLocale, LocaleMessages, DefineLocaleMessage } from 'vue-i18n' import type { NuxtApp } from '#app' @@ -14,6 +15,7 @@ export type LocaleLoader> = { load: () => Promise> cache: boolean } + const cacheMessages = new Map>() export async function loadVueI18nOptions( @@ -75,19 +77,20 @@ export async function loadInitialMessages( } async function loadMessage(locale: Locale, { key, load }: LocaleLoader) { + const logger = /*#__PURE__*/ createLogger('loadMessage') let message: LocaleMessages | null = null try { - __DEBUG__ && console.log('loadMessage: (locale) -', locale) + __DEBUG__ && logger.log({ locale }) const getter = await load().then(r => ('default' in r ? r.default : r)) if (isFunction(getter)) { message = await getter(locale) - __DEBUG__ && console.log('loadMessage: dynamic load', message) + __DEBUG__ && logger.log('dynamic load', logger.level >= 999 ? message : '') } else { message = getter if (message != null && cacheMessages) { cacheMessages.set(key, message) } - __DEBUG__ && console.log('loadMessage: load', message) + __DEBUG__ && logger.log('loaded', logger.level >= 999 ? message : '') } } catch (e: unknown) { console.error('Failed locale loading: ' + (e as Error).message) @@ -100,6 +103,7 @@ export async function loadLocale( localeLoaders: Record, setter: (locale: Locale, message: LocaleMessages) => void ) { + const logger = /*#__PURE__*/ createLogger('loadLocale') const loaders = localeLoaders[locale] if (loaders == null) { @@ -112,11 +116,11 @@ export async function loadLocale( let message: LocaleMessages | undefined | null = null if (cacheMessages && cacheMessages.has(loader.key) && loader.cache) { - __DEBUG__ && console.log(loader.key + ' is already loaded') + __DEBUG__ && logger.log(loader.key + ' is already loaded') message = cacheMessages.get(loader.key) } else { - __DEBUG__ && !loader.cache && console.log(loader.key + ' bypassing cache!') - __DEBUG__ && console.log(loader.key + ' is loading ...') + __DEBUG__ && !loader.cache && logger.log(loader.key + ' bypassing cache!') + __DEBUG__ && logger.log(loader.key + ' is loading ...') message = await loadMessage(locale, loader) } diff --git a/src/runtime/plugins/i18n.ts b/src/runtime/plugins/i18n.ts index 3e0b7226b..57ca1867f 100644 --- a/src/runtime/plugins/i18n.ts +++ b/src/runtime/plugins/i18n.ts @@ -31,6 +31,7 @@ import { import { inBrowser, resolveBaseUrl } from '../routing/utils' import { extendI18n, createLocaleFromRouteGetter } from '../routing/extends' import { setLocale, getLocale, mergeLocaleMessage, setLocaleProperty } from '../compatibility' +import { createLogger } from 'virtual:nuxt-i18n-logger' import type { Locale, I18nOptions } from 'vue-i18n' import type { NuxtApp } from '#app' @@ -47,6 +48,7 @@ export default defineNuxtPlugin({ name: 'i18n:plugin', parallel: parallelPlugin, async setup(nuxt) { + const logger = /*#__PURE__*/ createLogger('plugin:i18n') const route = useRoute() const { vueApp: app } = nuxt const nuxtContext = nuxt as unknown as NuxtApp @@ -92,9 +94,9 @@ export default defineNuxtPlugin({ const _detectBrowserLanguage = runtimeDetectBrowserLanguage() - __DEBUG__ && console.log('isSSG', isSSG) - __DEBUG__ && console.log('useCookie on setup', _detectBrowserLanguage && _detectBrowserLanguage.useCookie) - __DEBUG__ && console.log('defaultLocale on setup', runtimeI18n.defaultLocale) + __DEBUG__ && logger.log('isSSG', isSSG) + __DEBUG__ && logger.log('useCookie on setup', _detectBrowserLanguage && _detectBrowserLanguage.useCookie) + __DEBUG__ && logger.log('defaultLocale on setup', runtimeI18n.defaultLocale) const vueI18nOptions: I18nOptions = await loadVueI18nOptions(vueI18nConfigs, useNuxtApp()) vueI18nOptions.messages = vueI18nOptions.messages || {} @@ -117,7 +119,7 @@ export default defineNuxtPlugin({ }, runtimeI18n ) - __DEBUG__ && console.log('first detect initial locale', initialLocale) + __DEBUG__ && logger.log('first detect initial locale', initialLocale) // load initial vue-i18n locale messages vueI18nOptions.messages = await loadInitialMessages(vueI18nOptions.messages, localeLoaders, { @@ -134,7 +136,7 @@ export default defineNuxtPlugin({ * It means a mode that works only with simple vue-i18n, without nuxtjs/i18n routing, browser detection, SEO, and other features. */ initialLocale = getDefaultLocale(initialLocale) - __DEBUG__ && console.log('final initial locale:', initialLocale) + __DEBUG__ && logger.log('final initial locale:', initialLocale) // create i18n instance const i18n = createI18n({ ...vueI18nOptions, locale: initialLocale }) @@ -151,7 +153,7 @@ export default defineNuxtPlugin({ */ if (isSSGModeInitialSetup() && runtimeI18n.strategy === 'no_prefix' && import.meta.client) { nuxt.hook('app:mounted', async () => { - __DEBUG__ && console.log('hook app:mounted') + __DEBUG__ && logger.log('hook app:mounted') const detected = detectBrowserLanguage( route, { @@ -162,7 +164,7 @@ export default defineNuxtPlugin({ }, initialLocale ) - __DEBUG__ && console.log('app:mounted: detectBrowserLanguage (locale, reason, from) -', Object.values(detected)) + __DEBUG__ && logger.log('app:mounted: detectBrowserLanguage (locale, reason, from) -', Object.values(detected)) await setLocale(i18n, detected.locale) ssgModeInitialSetup = false }) @@ -211,7 +213,7 @@ export default defineNuxtPlugin({ routeLocaleGetter: getLocaleFromRoute }) ) - __DEBUG__ && console.log('redirectPath on setLocale', redirectPath) + __DEBUG__ && logger.log('redirectPath on setLocale', redirectPath) await nuxtContext.runWithContext( async () => @@ -338,7 +340,7 @@ export default defineNuxtPlugin({ 'locale-changing', defineNuxtRouteMiddleware(async (to, from) => { - __DEBUG__ && console.log('locale-changing middleware', to, from) + __DEBUG__ && logger.log('locale-changing middleware', to, from) const locale = detectLocale( to, @@ -354,10 +356,10 @@ export default defineNuxtPlugin({ }, runtimeI18n ) - __DEBUG__ && console.log('detect locale', locale) + __DEBUG__ && logger.log('detect locale', locale) const localeSetup = isInitialLocaleSetup(locale) - __DEBUG__ && console.log('localeSetup', localeSetup) + __DEBUG__ && logger.log('localeSetup', localeSetup) const modified = await loadAndSetLocale(locale, i18n, runtimeI18n, localeSetup) @@ -373,7 +375,7 @@ export default defineNuxtPlugin({ calledWithRouting: true }) ) - __DEBUG__ && console.log('redirectPath on locale-changing middleware', redirectPath) + __DEBUG__ && logger.log('redirectPath on locale-changing middleware', redirectPath) routeChangeCount++ diff --git a/src/runtime/utils.ts b/src/runtime/utils.ts index a3a7f35e7..e1359231d 100644 --- a/src/runtime/utils.ts +++ b/src/runtime/utils.ts @@ -34,6 +34,7 @@ import { setLocaleProperty, setLocaleCookie } from './compatibility' +import { createLogger } from 'virtual:nuxt-i18n-logger' import type { I18n, Locale } from 'vue-i18n' import type { NuxtApp } from '#app' @@ -80,6 +81,7 @@ export async function loadAndSetLocale( runtimeI18n: ModulePublicRuntimeConfig['i18n'], initial: boolean = false ): Promise { + const logger = /*#__PURE__*/ createLogger('loadAndSetLocale') const { differentDomains, skipSettingLocaleOnNavigate, lazy } = runtimeI18n const opts = runtimeDetectBrowserLanguage(runtimeI18n) const nuxtApp = useNuxtApp() @@ -95,7 +97,7 @@ export async function loadAndSetLocale( setLocaleCookie(i18n, locale) } - __DEBUG__ && console.log('setLocale: new -> ', newLocale, ' old -> ', oldLocale, ' initial -> ', initial) + __DEBUG__ && logger.log({ newLocale, oldLocale, initial }) // `newLocale` is unset or empty if (!newLocale) { @@ -153,17 +155,6 @@ export async function loadAndSetLocale( type LocaleLoader = () => Locale -/** - * Used for runtime debug logs only - */ -export function createLogger(label: string) { - return { - log: console.log.bind(console, `${label}:`) - // change to this after implementing logger across runtime code - // log: console.log.bind(console, `[i18n:${label}]`) - } -} - export function detectLocale( route: string | RouteLocationNormalized | RouteLocationNormalizedLoaded, routeLocaleGetter: GetLocaleFromRouteFunction, @@ -174,7 +165,7 @@ export function detectLocale( const { strategy, defaultLocale, differentDomains, multiDomainLocales } = runtimeI18n const { localeCookie } = detectLocaleContext const _detectBrowserLanguage = runtimeDetectBrowserLanguage(runtimeI18n) - const logger = createLogger('detectLocale') + const logger = /*#__PURE__*/ createLogger('detectLocale') const initialLocale = isFunction(initialLocaleLoader) ? initialLocaleLoader() : initialLocaleLoader __DEBUG__ && logger.log({ initialLocale }) @@ -227,10 +218,10 @@ export function detectRedirect({ }): string { const nuxtApp = useNuxtApp() const common = initCommonComposableOptions() + const logger = /*#__PURE__*/ createLogger('detectRedirect') const { strategy, differentDomains } = common.runtimeConfig.public.i18n - __DEBUG__ && console.log('detectRedirect: targetLocale -> ', targetLocale) - __DEBUG__ && console.log('detectRedirect: route -> ', route) - __DEBUG__ && console.log('detectRedirect: calledWithRouting -> ', calledWithRouting, routeLocaleGetter(route.to)) + __DEBUG__ && logger.log({ route }) + __DEBUG__ && logger.log({ targetLocale, calledWithRouting, routeLocaleGetter: routeLocaleGetter(route.to) }) let redirectPath = '' const { fullPath: toFullPath } = route.to @@ -251,7 +242,7 @@ export function detectRedirect({ ) { // the current route could be 404 in which case attempt to find matching route using the full path const routePath = nuxtApp.$switchLocalePath(targetLocale) || nuxtApp.$localePath(toFullPath, targetLocale) - __DEBUG__ && console.log('detectRedirect: calculate routePath -> ', routePath, toFullPath) + __DEBUG__ && logger.log('calculate routePath', { routePath, toFullPath }) if (isString(routePath) && routePath && !isEqual(routePath, toFullPath) && !routePath.startsWith('//')) { /** * NOTE: for #1889, #2226 @@ -272,7 +263,7 @@ export function detectRedirect({ * let it be processed by the route of the router middleware. */ const routePath = switchLocalePath(common, targetLocale, route.to) - __DEBUG__ && console.log('detectRedirect: calculate domain or ssg routePath -> ', routePath) + __DEBUG__ && logger.log('calculate domain or ssg routePath', { routePath }) if (isString(routePath) && routePath && !isEqual(routePath, toFullPath) && !routePath.startsWith('//')) { redirectPath = routePath } @@ -307,18 +298,18 @@ export async function navigate( const { nuxtApp, i18n, locale, route } = args const { rootRedirect, differentDomains, multiDomainLocales, skipSettingLocaleOnNavigate, configLocales, strategy } = nuxtApp.$config.public.i18n + const logger = /*#__PURE__*/ createLogger('navigate') let { redirectPath } = args __DEBUG__ && - console.log( - 'navigate options ', + logger.log('options', { status, rootRedirect, differentDomains, skipSettingLocaleOnNavigate, - enableNavigate - ) - __DEBUG__ && console.log('navigate isSSG', isSSG) + enableNavigate, + isSSG + }) if (route.path === '/' && rootRedirect) { if (isString(rootRedirect)) { @@ -328,7 +319,7 @@ export async function navigate( status = rootRedirect.statusCode } redirectPath = nuxtApp.$localePath(redirectPath, locale) - __DEBUG__ && console.log('navigate: rootRedirect mode redirectPath -> ', redirectPath, ' status -> ', status) + __DEBUG__ && logger.log('rootRedirect mode', { redirectPath, status }) return _navigate(redirectPath, status) } @@ -377,13 +368,13 @@ export async function navigate( } } else { const state = useRedirectState() - __DEBUG__ && console.log('redirect state ->', state.value, 'redirectPath -> ', redirectPath) + __DEBUG__ && logger.log('redirect', { state: state.value, redirectPath }) if (state.value && state.value !== redirectPath) { if (import.meta.client) { state.value = '' // reset redirect path window.location.assign(redirectPath) } else if (import.meta.server) { - __DEBUG__ && console.log('differentDomains servermode ', redirectPath) + __DEBUG__ && logger.log('differentDomains servermode', { redirectPath }) state.value = redirectPath // set redirect path } } @@ -409,8 +400,9 @@ export function injectNuxtHelpers(nuxt: NuxtApp, i18n: I18n) { // override prefix for route path, support domain export function extendPrefixable(runtimeConfig = useRuntimeConfig()) { + const logger = /*#__PURE__*/ createLogger('extendPrefixable') return (opts: PrefixableOptions): boolean => { - __DEBUG__ && console.log('extendPrefixable', DefaultPrefixable(opts)) + __DEBUG__ && logger.log(DefaultPrefixable(opts)) return DefaultPrefixable(opts) && !runtimeConfig.public.i18n.differentDomains } @@ -418,10 +410,11 @@ export function extendPrefixable(runtimeConfig = useRuntimeConfig()) { // override switch locale path intercepter, support domain export function extendSwitchLocalePathIntercepter(runtimeConfig = useRuntimeConfig()): SwitchLocalePathIntercepter { + const logger = /*#__PURE__*/ createLogger('extendSwitchLocalePathIntercepter') return (path: string, locale: Locale): string => { if (runtimeConfig.public.i18n.differentDomains) { const domain = getDomainFromLocale(locale) - __DEBUG__ && console.log('extendSwitchLocalePathIntercepter: domain -> ', domain, ' path -> ', path) + __DEBUG__ && logger.log({ domain, path }) if (domain) { return joinURL(domain, path) } else { @@ -434,13 +427,14 @@ export function extendSwitchLocalePathIntercepter(runtimeConfig = useRuntimeConf } export function extendBaseUrl(): BaseUrlResolveHandler { + const logger = /*#__PURE__*/ createLogger('extendBaseUrl') return (): string => { const ctx = useNuxtApp() const { baseUrl, defaultLocale, differentDomains } = ctx.$config.public.i18n if (isFunction(baseUrl)) { const baseUrlResult = baseUrl(ctx) - __DEBUG__ && console.log('baseUrl: using localeLoader function -', baseUrlResult) + __DEBUG__ && logger.log('using localeLoader function -', { baseUrlResult }) return baseUrlResult } @@ -448,13 +442,13 @@ export function extendBaseUrl(): BaseUrlResolveHandler { if (differentDomains && localeCode) { const domain = getDomainFromLocale(localeCode) if (domain) { - __DEBUG__ && console.log('baseUrl: using differentDomains -', domain) + __DEBUG__ && logger.log('using differentDomains -', { domain }) return domain } } if (baseUrl) { - __DEBUG__ && console.log('baseUrl: using runtimeConfig -', baseUrl) + __DEBUG__ && logger.log('using runtimeConfig -', { baseUrl }) return baseUrl } diff --git a/src/types.ts b/src/types.ts index b52b76285..6413d9519 100644 --- a/src/types.ts +++ b/src/types.ts @@ -155,7 +155,7 @@ export type NuxtI18nOptions< rootRedirect?: string | RootRedirectOptions skipSettingLocaleOnNavigate?: boolean types?: 'composition' | 'legacy' - debug?: boolean + debug?: boolean | 'verbose' dynamicRouteParams?: boolean parallelPlugin?: boolean /** diff --git a/src/virtual-logger.ts b/src/virtual-logger.ts new file mode 100644 index 000000000..aa5d2c520 --- /dev/null +++ b/src/virtual-logger.ts @@ -0,0 +1,31 @@ +import type { Plugin } from 'vite' + +export const VIRTUAL_NUXT_I18N_LOGGER = 'virtual:nuxt-i18n-logger' +export const RESOLVED_VIRTUAL_NUXT_I18N_LOGGER = `\0${VIRTUAL_NUXT_I18N_LOGGER}` + +export function i18nVirtualLoggerPlugin(debug: boolean | 'verbose') { + return { + name: 'nuxtjs:i18n-logger', + enforce: 'pre', + resolveId(id) { + if (id === VIRTUAL_NUXT_I18N_LOGGER) return RESOLVED_VIRTUAL_NUXT_I18N_LOGGER + }, + load(id) { + if (id !== RESOLVED_VIRTUAL_NUXT_I18N_LOGGER) return + + // return stub if debug logging is disabled + if (!debug) { + return `export function createLogger() {}` + } + + return ` +import { createConsola } from 'consola' + +const debugLogger = createConsola({ level: ${debug === 'verbose' ? 999 : 4} }).withTag('i18n') + +export function createLogger(label) { + return debugLogger.withTag(label) +}` + } + } +}