diff --git a/playground/nuxt.config.ts b/playground/nuxt.config.ts index 4ead424f0..e13a8f2d3 100644 --- a/playground/nuxt.config.ts +++ b/playground/nuxt.config.ts @@ -105,7 +105,7 @@ export default defineNuxtConfig({ // } // } // }, - debug: false, + debug: true, i18n: { experimental: { jsTsFormatResource: true @@ -148,12 +148,12 @@ export default defineNuxtConfig({ } ], // trailingSlash: true, - debug: false, + debug: true, defaultLocale: 'en', // strategy: 'no_prefix', // strategy: 'prefix', // strategy: 'prefix_and_default', - strategy: 'prefix_except_default', + // strategy: 'prefix_except_default', // rootRedirect: '/ja/about-ja', dynamicRouteParams: true, // customRoutes: 'config', @@ -167,14 +167,14 @@ export default defineNuxtConfig({ }, // differentDomains: true, // skipSettingLocaleOnNavigate: true, - // detectBrowserLanguage: false, - detectBrowserLanguage: { - useCookie: true - // alwaysRedirect: true - // cookieKey: 'i18n_redirected', - // // cookieKey: 'my_custom_cookie_name', - // redirectOn: 'root' - }, + detectBrowserLanguage: false, + // detectBrowserLanguage: { + // // useCookie: true + // // alwaysRedirect: true + // // cookieKey: 'i18n_redirected', + // // // cookieKey: 'my_custom_cookie_name', + // // redirectOn: 'root' + // }, vueI18n: './vue-i18n.options.ts' } }) diff --git a/src/bundler.ts b/src/bundler.ts index 43172c9c8..96c3b4067 100644 --- a/src/bundler.ts +++ b/src/bundler.ts @@ -4,8 +4,7 @@ import { extendWebpackConfig, extendViteConfig, addWebpackPlugin, addVitePlugin import VueI18nWebpackPlugin from '@intlify/unplugin-vue-i18n/webpack' import VueI18nVitePlugin from '@intlify/unplugin-vue-i18n/vite' import { TransformMacroPlugin, TransformMacroPluginOptions } from './transform/macros' -import { ResourceProxyPlugin, ResourceProxyPluginOptions } from './transform/proxy' -import { ResourceDynamicPlugin, ResourceDynamicPluginOptions } from './transform/dynamic' +import { ResourcePlugin, ResourcePluginOptions } from './transform/resource' import { assign } from '@intlify/shared' import { getLayerLangPaths } from './layers' @@ -33,16 +32,12 @@ export async function extendBundler( debug('i18nModulePaths -', i18nModulePaths) const localePaths = [...langPaths, ...i18nModulePaths] - const proxyOptions: ResourceProxyPluginOptions = { - sourcemap: nuxt.options.sourcemap.server || nuxt.options.sourcemap.client - } - // extract macros from components const macroOptions: TransformMacroPluginOptions = { sourcemap: nuxt.options.sourcemap.server || nuxt.options.sourcemap.client } - const dynamicOptions: ResourceDynamicPluginOptions = { + const resourceOptions: ResourcePluginOptions = { sourcemap: nuxt.options.sourcemap.server || nuxt.options.sourcemap.client } @@ -66,10 +61,9 @@ export async function extendBundler( webpackPluginOptions.include = localePaths.map(x => resolve(x, './**')) } - addWebpackPlugin(ResourceProxyPlugin.webpack(proxyOptions)) addWebpackPlugin(VueI18nWebpackPlugin(webpackPluginOptions)) addWebpackPlugin(TransformMacroPlugin.webpack(macroOptions)) - addWebpackPlugin(ResourceDynamicPlugin.webpack(dynamicOptions)) + addWebpackPlugin(ResourcePlugin.webpack(resourceOptions)) extendWebpackConfig(config => { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- `config.plugins` is safe, so it's assigned with nuxt! @@ -98,10 +92,9 @@ export async function extendBundler( vitePluginOptions.include = localePaths.map(x => resolve(x, './**')) } - addVitePlugin(ResourceProxyPlugin.vite(proxyOptions)) addVitePlugin(VueI18nVitePlugin(vitePluginOptions)) addVitePlugin(TransformMacroPlugin.vite(macroOptions)) - addVitePlugin(ResourceDynamicPlugin.vite(dynamicOptions)) + addVitePlugin(ResourcePlugin.vite(resourceOptions)) extendViteConfig(config => { if (config.define) { diff --git a/src/constants.ts b/src/constants.ts index d4ba10b46..85855a39c 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -71,8 +71,6 @@ export const DEFAULT_OPTIONS = { debug: false } as const -export const NUXT_I18N_LOCALE_PROXY_ID = '@nuxtjs/i18n/__locale__' as const -export const NUXT_I18N_CONFIG_PROXY_ID = '@nuxtjs/i18n/__config__' as const export const NUXT_I18N_TEMPLATE_OPTIONS_KEY = 'i18n.options.mjs' as const export const NUXT_I18N_COMPOSABLE_DEFINE_ROUTE = 'defineI18nRoute' as const export const NUXT_I18N_COMPOSABLE_DEFINE_LOCALE = 'defineI18nLocale' as const diff --git a/src/gen.ts b/src/gen.ts index a50471b1a..e3383a2a9 100644 --- a/src/gen.ts +++ b/src/gen.ts @@ -3,17 +3,10 @@ import createDebug from 'debug' import { isArray, isObject } from '@intlify/shared' import { generateJSON } from '@intlify/bundle-utils' -import { - EXECUTABLE_EXTENSIONS, - NULL_HASH, - NUXT_I18N_MODULE_ID, - NUXT_I18N_CONFIG_PROXY_ID, - NUXT_I18N_LOCALE_PROXY_ID -} from './constants' +import { EXECUTABLE_EXTENSIONS, NULL_HASH, NUXT_I18N_MODULE_ID } from './constants' import { genImport, genSafeVariableName, genDynamicImport } from 'knitwork' import { parse as parsePath, normalize } from 'pathe' import { withQuery } from 'ufo' -import { asVirtualId } from './transform/utils' import { toCode } from './utils' import type { NuxtI18nOptions, NuxtI18nInternalOptions, LocaleInfo, VueI18nConfigPathInfo, LocaleType } from './types' @@ -29,6 +22,8 @@ export type LoaderOptions = { additionalMessages?: AdditionalMessages // TODO: remove `i18n:extend-messages` before v8 official release } +type ResourceType = 'locale' | 'config' + const debug = createDebug('@nuxtjs/i18n:gen') export function generateLoaderOptions( @@ -85,7 +80,7 @@ export function generateLoaderOptions( gen += `${genImport( genImportSpecifier(loadPath, ext, absolutePath, type, { hash, - virtualId: NUXT_I18N_LOCALE_PROXY_ID, + resourceType: 'locale', query: { locale: localeCode } }), variableName, @@ -179,7 +174,7 @@ export function generateLoaderOptions( ` const basicVueI18nConfigCode = generateVueI18nConfigration(vueI18nConfigPathInfo, ({ absolute: absolutePath, relative: relativePath, hash, relativeBase, type }, { dir, base, ext }) => { const configImportKey = makeImportKey(relativeBase, dir, base) - return `const vueI18n = await vueI18nConfigLoader((${genDynamicImport(genImportSpecifier(configImportKey, ext, absolutePath, type, { hash, virtualId: NUXT_I18N_CONFIG_PROXY_ID }), { comment: `webpackChunkName: "${normalizeWithUnderScore(relativePath)}_${hash}"` })}))\n` + return `const vueI18n = await vueI18nConfigLoader((${genDynamicImport(genImportSpecifier(configImportKey, ext, absolutePath, type, { hash, resourceType: 'config' }), { comment: `webpackChunkName: "${normalizeWithUnderScore(relativePath)}_${hash}"` })}))\n` }) if (basicVueI18nConfigCode != null) { genCodes += ` ${basicVueI18nConfigCode}` @@ -218,7 +213,7 @@ export function generateLoaderOptions( for (const configPath of vueI18nConfigPaths) { const additionalVueI18nConfigCode = generateVueI18nConfigration(configPath, ({ absolute: absolutePath, relative: relativePath, hash, relativeBase, type }, { dir, base, ext }) => { const configImportKey = makeImportKey(relativeBase, dir, base) - return `await mergeMessages(${rootKey}.${key}.messages, (${genDynamicImport(genImportSpecifier(configImportKey, ext, absolutePath, type, { hash, virtualId: NUXT_I18N_CONFIG_PROXY_ID }), { comment: `webpackChunkName: "${normalizeWithUnderScore(relativePath)}_${hash}"` })}))\n` + return `await mergeMessages(${rootKey}.${key}.messages, (${genDynamicImport(genImportSpecifier(configImportKey, ext, absolutePath, type, { hash, resourceType: 'config' }), { comment: `webpackChunkName: "${normalizeWithUnderScore(relativePath)}_${hash}"` })}))\n` }) if (additionalVueI18nConfigCode != null) { genCodes += ` ${additionalVueI18nConfigCode}` @@ -280,8 +275,6 @@ export function generateLoaderOptions( return genCode } -type TransformProxyType = typeof NUXT_I18N_LOCALE_PROXY_ID | typeof NUXT_I18N_CONFIG_PROXY_ID - function raiseSyntaxError(path: string) { throw new Error(`'unknown' type in '${path}'.`) } @@ -293,21 +286,21 @@ function genImportSpecifier( type: LocaleType, { hash = NULL_HASH, - virtualId = NUXT_I18N_LOCALE_PROXY_ID, + resourceType = 'locale', query = {} }: { hash?: string - virtualId?: TransformProxyType + resourceType?: ResourceType query?: Record } = {} ) { if (EXECUTABLE_EXTENSIONS.includes(ext)) { - if (virtualId === NUXT_I18N_LOCALE_PROXY_ID) { + if (resourceType === 'locale') { type === 'unknown' && raiseSyntaxError(absolutePath) - return type === 'dynamic' ? asVirtualId(withQuery(virtualId, { target: id, hash, ...query })) : id - } else if (virtualId === NUXT_I18N_CONFIG_PROXY_ID) { + return type === 'dynamic' ? withQuery(id, { hash, ...query }) : id + } else if (resourceType === 'config') { type === 'unknown' && raiseSyntaxError(absolutePath) - return asVirtualId(withQuery(virtualId, { target: id, hash, ...query })) + return withQuery(id, { hash, ...query, ...{ config: 1 } }) } else { return id } diff --git a/src/runtime/internal.ts b/src/runtime/internal.ts index 96dfbed99..19e298243 100644 --- a/src/runtime/internal.ts +++ b/src/runtime/internal.ts @@ -132,7 +132,7 @@ async function loadMessage(context: NuxtApp, loader: () => Promise, locale: const getter = await loader().then(r => r.default || r) if (isFunction(getter)) { if (i18nConfig.experimental?.jsTsFormatResource) { - message = await getter(locale).then((r: any) => r.default || r) + message = await getter(locale) __DEBUG__ && console.log('loadMessage: dynamic load', message) } else { console.warn( @@ -167,8 +167,10 @@ export async function loadLocale( const { key, load } = loaders[0] let message: LocaleMessages | undefined | null = null if (loadedMessages.has(key)) { + __DEBUG__ && console.log(key + ' is already loaded') message = loadedMessages.get(key) } else { + __DEBUG__ && console.log(key + ' is loading ...') message = await loadMessage(context, load, locale) if (message != null) { loadedMessages.set(key, message) @@ -183,8 +185,10 @@ export async function loadLocale( for (const { key, load } of loaders) { let message: LocaleMessages | undefined | null = null if (loadedMessages.has(key)) { + __DEBUG__ && console.log(key + ' is already loaded') message = loadedMessages.get(key) } else { + __DEBUG__ && console.log(key + ' is loading ...') message = await loadMessage(context, load, locale) if (message != null) { loadedMessages.set(key, message) diff --git a/src/transform/proxy.ts b/src/transform/proxy.ts deleted file mode 100644 index 4ed61a2e3..000000000 --- a/src/transform/proxy.ts +++ /dev/null @@ -1,132 +0,0 @@ -import createDebug from 'debug' -import { dirname, resolve } from 'node:path' -import { createUnplugin } from 'unplugin' -import { parseQuery, parseURL, withQuery } from 'ufo' -import { isString } from '@intlify/shared' -import MagicString from 'magic-string' -// @ts-ignore -import { transform as stripType } from '@mizchi/sucrase' -import { getVirtualId, VIRTUAL_PREFIX_HEX } from './utils' -import { toCode } from '../utils' -import { NUXT_I18N_TEMPLATE_OPTIONS_KEY, NUXT_I18N_CONFIG_PROXY_ID, NUXT_I18N_LOCALE_PROXY_ID } from '../constants' - -export interface ResourceProxyPluginOptions { - sourcemap?: boolean -} - -const debug = createDebug('@nuxtjs/i18n:transform:proxy') - -export const ResourceProxyPlugin = createUnplugin((options: ResourceProxyPluginOptions = {}, meta) => { - debug('options', options, meta) - - return { - name: 'nuxtjs:i18n-resource-proxy', - - resolveId(id, importer) { - const { pathname, search } = parseURL(decodeURIComponent(getVirtualId(id))) - const query = parseQuery(search) - - if (pathname === NUXT_I18N_LOCALE_PROXY_ID) { - debug('resolveId (locale)', id, importer) - if (importer?.endsWith(NUXT_I18N_TEMPLATE_OPTIONS_KEY)) { - return { - id: withQuery(id, { from: importer }), - moduleSideEffects: true - } - } else if (isString(query.from) && query.from.endsWith(NUXT_I18N_TEMPLATE_OPTIONS_KEY)) { - return { - id, - moduleSideEffects: true - } - } - } else if (pathname === NUXT_I18N_CONFIG_PROXY_ID) { - debug('resolveId (config)', id, importer) - if (importer?.endsWith(NUXT_I18N_TEMPLATE_OPTIONS_KEY)) { - return { - id: withQuery(id, { from: importer }), - moduleSideEffects: true - } - } else if (isString(query.from) && query.from.endsWith(NUXT_I18N_TEMPLATE_OPTIONS_KEY)) { - return { - id, - moduleSideEffects: true - } - } - } - - return null - }, - - async load(id) { - const { pathname, search } = parseURL(decodeURIComponent(getVirtualId(id))) - const query = parseQuery(search) - - if (pathname === NUXT_I18N_LOCALE_PROXY_ID) { - if (isString(query.target) && isString(query.from)) { - const baseDir = dirname(query.from) - debug('load (locale) ->', id, baseDir) - // prettier-ignore - const code = `export default async function(locale) { - __DEBUG__ && console.log('loadResource', locale) - const loader = await import(${toCode(withQuery(resolve(baseDir, query.target), { locale: query.locale }))}).then(m => m.default || m) - const message = await loader(locale) - __DEBUG__ && console.log('loaded on loadResource', message) - return message -}` - const s = new MagicString(code) - return { - code: s.toString(), - map: options.sourcemap ? s.generateMap({ hires: true }) : undefined - } - } - } else if (pathname === NUXT_I18N_CONFIG_PROXY_ID) { - if (isString(query.target) && isString(query.from)) { - const baseDir = dirname(query.from) - debug('load (config) ->', id, baseDir) - // prettier-ignore - const code = `import { isObject, isFunction } from '@intlify/shared' -export default async function() { - const loader = await import(${toCode(withQuery(resolve(baseDir, query.target), { hash: query.hash, config: 'true' }))}).then(m => m.default || m) - const config = isFunction(loader) - ? await loader() - : isObject(loader) - ? loader - : {} - __DEBUG__ && console.log('loadConfig', config) - return config -}` - const s = new MagicString(code) - return { - code: s.toString(), - map: options.sourcemap ? s.generateMap({ hires: true }) : undefined - } - } - } - }, - - transformInclude(id) { - if (id.startsWith(VIRTUAL_PREFIX_HEX) || !/\.([c|m]?ts)$/.test(id)) { - return false - } else { - debug('transformInclude', id) - return true - } - }, - - transform(code, id) { - debug('transform', id) - - const out = stripType(code, { - transforms: ['jsx'], - keepUnusedImports: true - }) - - const s = new MagicString(out.code) - - return { - code: s.toString(), - map: options.sourcemap ? s.generateMap({ hires: true }) : undefined - } - } - } -}) diff --git a/src/transform/dynamic.ts b/src/transform/resource.ts similarity index 87% rename from src/transform/dynamic.ts rename to src/transform/resource.ts index 0ce4dccf8..0fe38628e 100644 --- a/src/transform/dynamic.ts +++ b/src/transform/resource.ts @@ -6,17 +6,17 @@ import MagicString from 'magic-string' import { VIRTUAL_PREFIX_HEX } from './utils' import { NUXT_I18N_COMPOSABLE_DEFINE_LOCALE, NUXT_I18N_COMPOSABLE_DEFINE_CONFIG } from '../constants' -export interface ResourceDynamicPluginOptions { +export interface ResourcePluginOptions { sourcemap?: boolean } -const debug = createDebug('@nuxtjs/i18n:transform:dynamic') +const debug = createDebug('@nuxtjs/i18n:transform:resource') -export const ResourceDynamicPlugin = createUnplugin((options: ResourceDynamicPluginOptions) => { +export const ResourcePlugin = createUnplugin((options: ResourcePluginOptions) => { debug('options', options) return { - name: 'nuxtjs:i18n-resource-dynamic', + name: 'nuxtjs:i18n-resource', enforce: 'post', transformInclude(id) {