Skip to content

perf: set up the i18n server context on first use - #4174

Open
onmax wants to merge 1 commit into
nuxt-modules:mainfrom
onmax:perf/lazy-server-context
Open

onmax wants to merge 1 commit into
nuxt-modules:mainfrom
onmax:perf/lazy-server-context

Conversation

@onmax

@onmax onmax commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

🔗 Linked issue

Resolves #4144

Repro with a before and after: https://github.com/onmax/repros/tree/repro/i18n-4144/i18n-4144 (node verify.mjs && node bench.mjs)

📚 Description

The server plugin builds the i18n context in the request hook, so every request pays for it. That includes /_nuxt assets, files from public/, API routes, and the internal $fetch behind useFetch. Most of the cost is useRuntimeConfig(event), which in Nitro 2 copies the whole config and checks env vars for every key.

Now the context is set up the first time something needs it: render:before for pages, the messages route, and the server locale detector. Pages cost the same as before, because Nuxt's renderer calls useRuntimeConfig(event) for them anyway.

From the repro: Nuxt 4.6.2, Nitro 2, i18n 10.6.0, 3 locales, production build. Server CPU per request, median of 6 alternating rounds of 2000 requests:

request before after
API route 0.448 ms 0.157 ms
public/robots.txt 0.547 ms 0.305 ms
/_nuxt asset 1.060 ms 0.821 ms
page 1.743 ms 1.557 ms (noise)

The first three dropped in every round. verify.mjs also checks that the context is no longer set up for those requests, and that pages still render their translations. On a bigger app with @nuxt/ui and Pinia, the HTML stayed byte-identical on 13 paths.

@github-actions

Copy link
Copy Markdown

We've flagged this as a potential contribution without a human behind it. We welcome the thoughtful use of AI tools when contributing to Nuxt i18n, but ask all contributors to follow two core principles:

  1. Never let an LLM speak for you - all comments, issues, and PR descriptions should be written in your own words, reflecting your own understanding.
  2. Never let an LLM think for you - only submit contributions you fully understand and can explain.

Please review our AI-assisted contribution guidelines and update this contribution if needed.

If this was flagged in error, we apologise! 😳 Just let us know. 🙏

@coderabbitai

coderabbitai Bot commented Oct 26, 2026

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: f252f1ba-0d75-45d1-9ca6-acd2cc11561b
Commits

Reviewing files that changed from the base of the PR and between 8158ed8 and e66df5f.

Files selected for processing: 6
  • specs/basic_usage.spec.ts
  • specs/fixtures/basic_usage/server/api/i18n-context.ts
  • src/runtime/server/context.ts
  • src/runtime/server/plugin.ts
  • src/runtime/server/routes/messages.ts
  • src/runtime/server/utils/locale-detector.ts
Files with no reviewable changes: 1
  • src/runtime/server/context.ts

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 6 remain after this review.

Walkthrough

The server plugin no longer initializes i18n context on every request. It reuses an existing context or initializes one in the render hook. The message route and locale detector also initialize context when it is absent. The change removes the useI18nContext and fetchMessages helpers. A test checks that an API request without prior initialization reports initialized: false.

Priority: Normal

Estimated code review effort: 2 (Simple) | ~10 minutes

Severity of issue fixed: Medium

Merge Risk: Minimal · up to e66df

No concrete current-head failure was established; the lazy initialization paths are consistent and ready for normal checks.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 2 functions across 5 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely describes the main change: initializing the i18n server context on first use instead of on every request.
Linked Issues check Passed [#4144] Requirement: avoid i18n context setup on requests that do not use i18n (for example /_nuxt assets and API routes), and leave SSR requests unaffected. The PR removes the per-request Nitro `requ…
Out of Scope Changes check Passed The changed files are plugin.ts, messages.ts, locale-detector.ts, and context.ts. Each one moves or removes context setup, which is the scope of #4144. The removed exports useI18nContext and `fetchM…
Full details: Linked Issues check

Explanation

[#4144] Requirement: avoid i18n context setup on requests that do not use i18n (for example /_nuxt assets and API routes), and leave SSR requests unaffected. The PR removes the per-request Nitro request hook that called initializeI18nContext. Context setup now runs on demand: in render:before for pages (including the prerender path), in the messages route, and in the server locale detector, each when no context exists. The new spec fixture /api/i18n-context asserts { initialized: false }, which supports that an API route does not initialize context. Pages still initialize context before rendering, so SSR behavior is preserved. The reduction in CPU cost is reported by the author as benchmark data; I did not verify those numbers. Evidence limit: file-level diffs for plugin.ts, messages.ts, and locale-detector.ts were unavailable. Their behavior is taken from the PR change summary. At head, src/runtime/server/context.ts contains initializeI18nContext and no longer contains useI18nContext or fetchMessages, which matches the summary. Asset 404 behavior is handled by Nitro before the renderer and needs no i18n change. All requirements in #4144 are coding requirements. No non-coding requirements apply.

Full details: Out of Scope Changes check

Explanation

The changed files are plugin.ts, messages.ts, locale-detector.ts, and context.ts. Each one moves or removes context setup, which is the scope of #4144. The removed exports useI18nContext and fetchMessages in context.ts have no remaining callers in the changed-file list. Their removal follows from the locale detector and messages route no longer depending on them. The spec fixture and spec assertion support the requirement that API routes do not initialize context. The PR description notes a conflict with #4088 for Nitro 3 and Nuxt 5, but this is a note, not a code change. Limit: the file-level diffs were unavailable, so the absence of other callers of the removed exports relies on the complete changed-file list, not on a repository search.

Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

initializeI18nContext runs on every request and response times at our ingress go from ~50 ms to 250–500 ms

1 participant