feat(react): add runWithI18n for i18n in Server Functions - #2619
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
I'm not using Nextjs myself anymore, but i saw other library took an approach with a middlewares + AsyncLocalStorage, have you considered this as well? It might give a better DX overall, instead of choosing what approach to use between Server Functions and RSC you just set i18n to AsyncLocalStorage once in the middleware and it makes it availalble for the whole request run. |
I'm just starting to work with Next's server functions. Next.js middleware runs as a separate invocation before route matching -it can only touch headers/cookies/redirects. It doesn't wrap the RSC render or a Server Function call in the same call stack. So, It's interesting that your solution could work in Tanstack Start, because there the middleware and the request handler itself are one continuous call chain, not two different execution boundaries. But I think this can be added to the documentation later, outside of this PR. const i18nMiddleware = createMiddleware().server(({ next }) => {
return runWithI18n(setupI18n(...), () => next())
})docs: |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #2619 +/- ##
===========================================
+ Coverage 77.05% 90.34% +13.29%
===========================================
Files 84 126 +42
Lines 2157 3843 +1686
Branches 555 1138 +583
===========================================
+ Hits 1662 3472 +1810
+ Misses 382 337 -45
+ Partials 113 34 -79 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Ah, poor NextJs developers 😔 |
There was a problem hiding this comment.
Pull request overview
Adds a request-scoped, ambient Lingui i18n context for React 19+ Server Functions (e.g. Next.js Server Actions) while keeping the existing React Server Components (RSC) React.cache mechanism authoritative during RSC renders.
Changes:
- Introduces
runWithI18n(AsyncLocalStorage-backed) andgetI18nOrThrow, and updates server/RSC entrypoints to use the non-nullable accessor. - Adds coverage for Server Function scoping, nested/concurrent isolation, and RSC-cache behavior; adds a dedicated
TransRsctest. - Updates docs to explain when to use
setI18nvsrunWithI18n, plus bumps the React dev/test toolchain to React 19 and ignores.swc/.
Reviewed changes
Copilot reviewed 9 out of 11 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| yarn.lock | Updates React/ReactDOM and related type dependencies for React 19 testing. |
| website/docs/tutorials/react-rsc.md | Documents Server Functions usage (runWithI18n/getI18nOrThrow) and updates Next.js config/layout examples. |
| website/docs/ref/react.md | Adds/extends server API reference for I18nContext, setI18n, runWithI18n, getI18n, getI18nOrThrow. |
| packages/react/src/TransRsc.tsx | Switches RSC Trans implementation to use getI18nOrThrow. |
| packages/react/src/TransRsc.test.tsx | Adds tests verifying TransRsc works with runWithI18n and throws actionable errors without context. |
| packages/react/src/server.ts | Adds AsyncLocalStorage-backed scoping (runWithI18n) and a non-nullable accessor (getI18nOrThrow); makes setI18n warn when misused. |
| packages/react/src/server.test.ts | Adds tests for both storage paths, nested scopes, concurrency isolation, and setI18n warning behavior. |
| packages/react/src/index-rsc.ts | Switches RSC useLingui to getI18nOrThrow. |
| packages/react/src/format.tsx | Tightens typing of formatElements element map entries (children-aware). |
| packages/react/package.json | Bumps React/ReactDOM + types to React 19 for package tests/dev. |
| .gitignore | Ignores .swc/ build artifacts and normalizes .lingui/ entry. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…mjs) Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Description
This adds ambient, request-scoped i18n access for downstream helpers called by
React Server Functions.
setI18nkeeps its role — storing the instance inReact.cacheduring a Server Component render — and now warns in development ifcalled outside one, where the write would otherwise be silently lost. Server
Functions use the new
runWithI18nAPI instead.Code that already has a local
i18ninstance should continue to calli18n.t(...)directly. This feature is for call chains where the helper doingthe translation does not receive that instance.
Before this change, every function in the chain has to pass
i18nalong, evenwhen it does not use it:
After this change, a Server Function can scope the instance to a callback and
helpers can read it from the callback's async context.
setupI18ncreates theinstance;
runWithI18nmakes it available to the execution chain created insidethe callback:
The existing
React.cachepath remains authoritative inside an RSC render.runWithI18nusesAsyncLocalStorage.runoutside RSC, which keeps concurrentServer Functions isolated across
awaitcalls and restores the caller's contextafter the callback.
getI18nOrThrowprovides a non-nullable accessor for helpers,and
I18nContextis now exported from@lingui/react/server.The async-context path uses
AsyncLocalStoragefromnode:async_hooks, so itworks on Node.js, the Vercel Edge Runtime, and Cloudflare Workers (with the
nodejs_compat/nodejs_alscompatibility flag). It relies only onrunandgetStore, which all three runtimes support.The docs now include a Server Action using this API. The tests cover both
storage paths, concurrent locales, nested callbacks, and cleanup.
.swc/isalso ignored.
Types of changes
Checklist