diff --git a/specs/basic_usage.spec.ts b/specs/basic_usage.spec.ts index e2ab9a241..a47cd5e66 100644 --- a/specs/basic_usage.spec.ts +++ b/specs/basic_usage.spec.ts @@ -583,6 +583,10 @@ describe('basic usage', async () => { expect(jaRes?.['server-key']).toMatch('こんにちは!') }) + test('(#4144) server routes do not set up the i18n server context', async () => { + expect(await $fetch('/api/i18n-context')).toEqual({ initialized: false }) + }) + test('(#2220) vue-i18n message compiler works in server routes', async () => { const res = await $fetch('/api/vue-i18n-compile') expect(res).toMatchObject({ message: 'Hello, nitro!' }) diff --git a/specs/fixtures/basic_usage/server/api/i18n-context.ts b/specs/fixtures/basic_usage/server/api/i18n-context.ts new file mode 100644 index 000000000..6a7d66bc6 --- /dev/null +++ b/specs/fixtures/basic_usage/server/api/i18n-context.ts @@ -0,0 +1,3 @@ +export default defineEventHandler(event => { + return { initialized: event.context.nuxtI18n != null } +}) diff --git a/src/runtime/server/context.ts b/src/runtime/server/context.ts index 461432cc6..1d858ab85 100644 --- a/src/runtime/server/context.ts +++ b/src/runtime/server/context.ts @@ -8,13 +8,6 @@ import { createLocaleConfigs, resolveDefaultLocale } from '../shared/locales' import { cloneDeep } from '../shared/messages' import { getMergedMessages } from './utils/messages' -export function useI18nContext(event: H3Event) { - if (event.context.nuxtI18n == null) { - throw new Error('Nuxt I18n server context has not been set up yet.') - } - return event.context.nuxtI18n -} - export function tryUseI18nContext(event: H3Event) { return event.context.nuxtI18n } @@ -35,20 +28,6 @@ export async function initializeI18nContext(event: H3Event) { return ctx } -/** - * Fetches the messages for the specified locale. - * @internal - */ -export const fetchMessages = async (locale: string) => { - const headers = new Headers({ 'x-nuxt-i18n': 'internal' }) - if (import.meta.dev) { - headers.set('Cache-Control', 'no-cache') - } - return await $fetch>(`${__I18N_SERVER_ROUTE__}/${__I18N_LOCALE_HASHES__[locale]}/${locale}/messages.json`, { - headers, - }) -} - export function createI18nContext(): NonNullable { return { messages: {}, diff --git a/src/runtime/server/plugin.ts b/src/runtime/server/plugin.ts index 7b43286d6..4e8af1d7e 100644 --- a/src/runtime/server/plugin.ts +++ b/src/runtime/server/plugin.ts @@ -1,7 +1,7 @@ import { stringify } from 'devalue' import { defineI18nMiddleware } from '@intlify/h3' import { defineNitroPlugin, useRuntimeConfig, useStorage } from 'nitropack/runtime' -import { initializeI18nContext, tryUseI18nContext, useI18nContext } from './context' +import { initializeI18nContext, tryUseI18nContext } from './context' import { createUserLocaleDetector } from './utils/locale-detector' import { pickNested } from './utils/messages-utils' import { isSupportedLocale, resolveDefaultLocale } from '../shared/locales' @@ -98,15 +98,12 @@ export default defineNitroPlugin(async (nitro) => { return { path: relocated, code: runtimeI18n.redirectStatusCode ?? 302, locale, origin } } - nitro.hooks.hook('request', async (event: H3Event) => { - await initializeI18nContext(event) - }) - nitro.hooks.hook('render:before', async (context) => { - if (!__I18N_SERVER_REDIRECT__) { return } const { event } = context + // set up on first use, so requests that render no page (assets, server routes) skip it (#4144) + const ctx = tryUseI18nContext(event) || await initializeI18nContext(event) + if (!__I18N_SERVER_REDIRECT__) { return } - const ctx = import.meta.prerender && !event.context.nuxtI18n ? await initializeI18nContext(event) : useI18nContext(event) const url = getRequestURL(event) const detector = useDetectors(event, detection) const localeSegment = detector.route(event.path) diff --git a/src/runtime/server/routes/messages.ts b/src/runtime/server/routes/messages.ts index ad2b5464a..05cd870ba 100644 --- a/src/runtime/server/routes/messages.ts +++ b/src/runtime/server/routes/messages.ts @@ -1,6 +1,6 @@ import { defineCachedEventHandler, defineCachedFunction } from 'nitropack/runtime' import { createError, defineEventHandler, getRouterParam } from 'h3' -import { initializeI18nContext, tryUseI18nContext, useI18nContext } from '../context' +import { initializeI18nContext, tryUseI18nContext } from '../context' import { warnMissedMessageFunctions } from '../../shared/messages' import type { H3Event } from 'h3' @@ -15,7 +15,7 @@ const _messagesHandler = defineEventHandler(async (event: H3Event) => { throw createError({ status: 400, message: 'Locale not specified.' }) } - const ctx = useI18nContext(event) + const ctx = tryUseI18nContext(event) || await initializeI18nContext(event) if (ctx.localeConfigs && locale in ctx.localeConfigs === false) { throw createError({ status: 404, message: `Locale '${locale}' not found.` }) } @@ -36,7 +36,6 @@ const getCacheKey = (event: H3Event) => async function shouldBypassCache(event: H3Event) { const locale = getRouterParam(event, 'locale') if (locale == null) { return false } - // prerendering may require initializing context const ctx = tryUseI18nContext(event) || await initializeI18nContext(event) return !ctx.localeConfigs?.[locale]?.cacheable } diff --git a/src/runtime/server/utils/locale-detector.ts b/src/runtime/server/utils/locale-detector.ts index e90338e4b..d50da5534 100644 --- a/src/runtime/server/utils/locale-detector.ts +++ b/src/runtime/server/utils/locale-detector.ts @@ -1,4 +1,4 @@ -import { fetchMessages, tryUseI18nContext } from '../context' +import { initializeI18nContext, tryUseI18nContext } from '../context' import { deepCopy } from '@intlify/shared' import { localeDetector } from '#internal/i18n-locale-detector.mjs' @@ -11,7 +11,8 @@ export function createUserLocaleDetector(defaultLocale: string, fallbackLocale: // Merge messages into i18n context which contains unserializable messages from vue-i18n // configurations - read in-process, `$fetch` would serialize message functions away (#3880) - const messages = await (tryUseI18nContext(event)?.loadMessages(locale) ?? fetchMessages(locale)) + const ctx = tryUseI18nContext(event) || await initializeI18nContext(event) + const messages = await ctx.loadMessages(locale) for (const locale of Object.keys(messages)) { i18nCtx.messages![locale] ??= {} deepCopy(messages[locale], i18nCtx.messages![locale])