-
-
Notifications
You must be signed in to change notification settings - Fork 519
Description
Environment
Hi! After upgrading from @nuxtjs/i18n@9.2.5 to @nuxtjs/i18n@10.2.3, we hit a production-only SSR crash when i18n loads lazy messages via the Nitro route:
GET /_i18n/:hash/:locale/messages.json
What happens
In production SSR, Nuxt triggers a request to:
/_i18n///messages.json
and the server crashes with an error similar to:
ReferenceError: useNuxtApp is not defined
stack points to the i18n runtime messages loader/merge code executed in Nitro route context (no Nuxt app instance)
In dev mode everything works, likely because a different code path is used.
Expected
i18n should be able to serve /_i18n/:hash/:locale/messages.json in production SSR without calling Nuxt composables (e.g. useNuxtApp()) from a Nitro/h3 route context, or it should use a request-bound Nuxt context safely.
Actual
The /_i18n/.../messages.json server route (Nitro context) triggers code that calls useNuxtApp(), which is not available there → SSR crash.
Additional issue: multi-domain host lost in SSR loopback
We are a multi-tenant setup where translations are domain-specific: (domain, locale) -> messages stored in Redis (e.g. i18n:locale::).
In production SSR, the internal request to /_i18n/.../messages.json often has Host: localhost:3000 (loopback), so the real domain cannot be derived from headers.host. This makes it impossible to fetch the correct tenant messages in SSR using the current /_i18n/... request alone.
Workaround (not ideal)
We implemented a custom server handler to intercept /_i18n/.../messages.json and return cached/Redis messages. However, because SSR loopback host becomes localhost, tenant resolution is still problematic unless we rely on a primary domain fallback.
Environment
@nuxtjs/i18n: 10.2.3
Nuxt: 4.3.0
SSR: production mode (npm run build + npm run start)
Deployment: behind reverse proxy / ingress (multi-domain)
Reproduction details
Enable lazy translations (or any config that causes i18n to request /_i18n/:hash/:locale/messages.json in production SSR)
Run production SSR build
First SSR request triggers /_i18n/.../messages.json
Server crashes with useNuxtApp is not defined
If maintainers need it, I can provide a minimal reproduction repo, but the core issue seems to be calling useNuxtApp() from Nitro route context when handling the i18n messages endpoint.
Thanks!
Reproduction
No public reproduction repo (project is private). Below are minimal steps + config + stack trace.
Steps to reproduce
- Create a Nuxt SSR app and install
@nuxtjs/i18n@10.2.3. - Enable lazy loading with
strategy: 'prefix_except_default'anddetectBrowserLanguageenabled. - Build & run in production:
npm run buildnode .output/server/index.mjs
- Open any page (first SSR request).
- Server triggers a request to
GET /_i18n/<hash>/<locale>/messages.jsonand crashes.
Minimal i18n config used
i18n: {
compilation: { strictMessage: false },
locales: [
{ code: 'en', language: 'en-US' },
{ code: 'de', language: 'de-DE' },
{ code: 'ru', language: 'ru-RU' }
],
defaultLocale: 'en',
lazy: true,
strategy: 'prefix_except_default',
detectBrowserLanguage: {
useCookie: true,
cookieKey: 'i18n_locale',
redirectOn: 'all',
fallbackLocale: 'en'
}
}
### Describe the bug
### Summary
After upgrading from `@nuxtjs/i18n@9.2.5` to `@nuxtjs/i18n@10.2.3`, production SSR crashes when i18n tries to load locale messages via the server endpoint `/_i18n/:hash/:locale/messages.json`.
### Expected behavior
Production SSR should render pages normally and load/merge locale messages without calling Nuxt app composables from a Nitro route context.
### Actual behavior
On the first SSR request, Nuxt triggers `GET /_i18n/<hash>/<locale>/messages.json` and the server throws:
`H3Error: Failed to merge messages: Failed loading locale (<locale>): useNuxtApp is not defined`
→ results in HTTP 500 and the page cannot be rendered.
### Why this is a problem for us
We run a multi-tenant setup where translations are domain-specific and stored in Redis using keys like:
`i18n:locale:<domain>:<locale>`.
In production SSR, the internal loopback request for `/_i18n/...` often has `Host: localhost:3000`, so the real domain is lost and we cannot fetch the correct tenant messages for SSR even if we override the endpoint.
### Regression
Works in `@nuxtjs/i18n@9.2.5`.
In dev mode with `@nuxtjs/i18n@10.2.3` it appears to work, but production SSR crashes.
### Environment
- Nuxt: v4.3.0
- Node: v20.19.5
- @nuxtjs/i18n: 10.2.3
- SSR: production (`npm run build` + `node .output/server/index.mjs`)
### Additional context
_No response_
### Logs
```shell