close
Skip to content

[Bug] Vite 8: locale JSON compiled to "const resource = ..." is fed back to vite:json, fails JSON.parse #553

@mendrinos

Description

@mendrinos

Reporting a bug?

Under Vite 8 (non-Rolldown), @intlify/unplugin-vue-i18n's configResolved patch of vite:json does not apply correctly. Locale JSON files are compiled by unplugin-vue-i18n's own transform to var resource = { ... }; export { resource as default }; and are then handed back to Vite's built-in vite:json plugin, which calls JSON.parse on the JS output and throws:

Failed loading locale (en): Unexpected token 'c', "const reso"... is not valid JSON

Affected versions: @intlify/unplugin-vue-i18n v11.0.7 (latest latest dist-tag at the time of this report). Does not reproduce under Vite 7.x.

Suspected source: https://github.com/intlify/bundle-tools/blob/v11.0.7/packages/unplugin-vue-i18n/src/core/resource.ts#L182-L193

In configResolved, the plugin does:

const isRolldownVite = !!(await getViteModule()).rolldownVersion
if (!isRolldownVite) {
  const jsonPlugin = getVitePlugin(config, 'vite:json')
  // ... patches vite:json to skip i18n locale files
}

Under Vite 8, vite:json's transform is now an ObjectHook ({ handler, filter, ... }) rather than a plain function. The current patch overwrites jsonPlugin.transform with a plain function, and on Vite 8 the ObjectHook's handler is still what gets invoked, so the patch is effectively a no-op — locale JSON files flow through vite:json, which tries to JSON.parse the already-compiled JS output from unplugin-vue-i18n's enforce: 'pre' transform.

This bug has also been reported downstream at nuxt-modules/i18n#3953, where maintainer @BobbieGoede requested a minimal upstream repro (comment). Independent reporters on the downstream issue include @floki1250 and @benlavalley, the latter confirming that the patch posted by @ysya (which preserves the ObjectHook shape) fixes it on a 77k-line locale bundle. I have also applied the same patch as a workaround on a Nuxt 4 production app (~2k lines × 23 locales) and it resolves the warning cleanly.

Expected behavior

Under Vite 8, locale JSON files matched by include should be compiled by @intlify/unplugin-vue-i18n and returned as valid i18n message objects, the same as under Vite 7. vite:json should not re-process unplugin-vue-i18n's output.

Reproduction

StackBlitz: https://stackblitz.com/~/github.com/mendrinos/mtkankzl

Steps:

  1. Open the StackBlitz link (no sign-in required to view).
  2. Wait for the WebContainer to run npm install && npm run dev.
  3. Observe the dev-server terminal — the Failed to load messages for locale "en": Unexpected token 'c', "const reso"... warning fires on the first SSR request.

Stack trace (from the StackBlitz terminal, identical to what a real-world Nuxt 4 app emits on Vite 8):

WARN  Failed to load messages for locale "en" Failed loading locale (en): Unexpected token 'c', "const reso"... is not valid JSON

    at getLocaleMessages (node_modules/@nuxtjs/i18n/dist/runtime/shared/messages.js:36:11)
    at async Object.callAsync (node_modules/unctx/dist/index.mjs:91:16)
    at async getLocaleMessagesMergedCached (node_modules/@nuxtjs/i18n/dist/runtime/shared/messages.js:52:20)
    at async loadMessagesFromClient (node_modules/@nuxtjs/i18n/dist/runtime/context.js:68:19)
    at async Object.loadMessages (node_modules/@nuxtjs/i18n/dist/runtime/context.js:139:50)
    at async loadAndSetLocale (node_modules/@nuxtjs/i18n/dist/runtime/utils.js:148:3)

The repro is intentionally minimal: one Nuxt module (@nuxtjs/i18n), one dumb index.vue calling t('common.save'), and two synthetic locale JSON files (~50 KB each). No other Nuxt modules, no middleware, no plugins, no custom server routes. Bisected down from a real production Nuxt 4 app.

Issue Package

unplugin-vue-i18n

System Info

  System:
    OS: macOS 26.4.1
    CPU: (10) arm64 Apple M1 Pro
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 23.11.0
    npm: 11.4.2
    bun: 1.3.12
  Browsers:
    Chrome: 147.0.7727.101
    Firefox: 149.0
    Safari: 26.4
  npmPackages:
    @nuxtjs/i18n: 10.2.4
    @intlify/unplugin-vue-i18n: 11.0.7 (transitive, via @nuxtjs/i18n)
    nuxt: 4.4.2
    vite: 8.0.8
    vue: 3.5.32
    vue-i18n: 11.3.2

StackBlitz reproduction runs the same versions inside a WebContainer (Node 22.22.0 + npm, no Bun) and reproduces the bug identically.

Additional context

Confirmed workaround (credit @ysya, from the downstream thread) — a pre Vite plugin that wraps vite:json's transform.handler to skip i18n/locales/*.json, leaving unplugin-vue-i18n's own transform to produce the final JS module. Adding this to nuxt.config.ts resolves the warning on a large production app:

// nuxt.config.ts — workaround until bundle-tools ships a fix
{
  name: 'i18n-json-vite8-fix',
  enforce: 'pre',
  configResolved(config) {
    const jsonPlugin = config.plugins.find(p => p.name === 'vite:json')
    if (!jsonPlugin?.transform) return
    const transform = jsonPlugin.transform
    const original = typeof transform === 'function' ? transform : transform.handler
    if (typeof original !== 'function') return
    const patched = function (this: unknown, code: string, id: string, ...args: unknown[]) {
      if (/i18n\/locales\/.*\.json$/.test(id)) return
      return (original as (...a: unknown[]) => unknown).call(this, code, id, ...args)
    }
    if (typeof transform === 'function') jsonPlugin.transform = patched as typeof jsonPlugin.transform
    else transform.handler = patched as typeof transform.handler
  },
}

Validations

  • Read the Contributing Guidelines.
  • Read the README
  • Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.
  • Check that this is a concrete bug.

Metadata

Metadata

Assignees

No one assigned

    Labels

    ❗ p4-importantPriority 4: bugs that violate documented behavior, or significantly impact perf

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions