@fluenti/next
withFluenti(config?)
Section titled “withFluenti(config?)”Wrap your Next.js config to add the Fluenti compiler plugin. Returns a function that accepts the Next.js config:
import { withFluenti } from '@fluenti/next'
export default withFluenti()({ reactStrictMode: true,})Config
Section titled “Config”All options are optional. Defaults are read from fluenti.config.ts in the project root. Options passed here override the config file values.
| Option | Type | Default | Description |
|---|---|---|---|
locales | string[]? | from config file | Override locale list |
defaultLocale | string? | from config file | Override default/fallback locale (maps to sourceLocale in config) |
compiledDir | string? | from config file | Path to compiled message catalogs (maps to compileOutDir in config) |
resolveLocale | string? | reads locale cookie | Path to a module that default-exports () => string | Promise<string>. Used in Server Actions and other contexts where the layout doesn’t run |
serverModule | string? | auto-generated | Path to a custom server module. When provided, skips auto-generation |
serverModuleOutDir | string? | node_modules/.fluenti | Directory where generated server module files are written |
devAutoCompile | boolean? | true | Auto extract+compile when source files change in dev mode |
devAutoCompileDelay | number? | 1000 | Debounce delay in ms for dev auto-compile. Increase if recompilation is too frequent |
buildAutoCompile | boolean? | true | Auto extract+compile before production build |
loaderEnforce | 'pre' | 'post' | undefined | 'pre' | Webpack loader execution order. Set to undefined to use webpack’s default ordering, or 'post' to run after other loaders |
What it does
Section titled “What it does”- Generates a server module at
node_modules/.fluenti/server.jscontaining:- A
createServerI18ninstance withsetLocale(),getI18n(), and server components - A
I18nProviderasync component for layouts - A
ClientI18nProviderwith statically imported message catalogs
- A
- Registers a webpack loader that uses AST scope analysis to detect Fluenti’s authoring imports and rewrites them to the correct runtime bindings
- Adds a resolve alias mapping
@fluenti/nextto the generated module
Webpack loader
Section titled “Webpack loader”The loader does not inject globals or proxy accessors. In a withFluenti() app, the recommended authoring surface is the same on both sides:
- Client:
import { t } from '@fluenti/react' - Server:
import { t } from '@fluenti/react'
Imported t is compile-time only. It supports tagged templates and descriptor calls, but not t('message.id'). For runtime lookup, use useI18n().t() on the client or await getI18n() on the server.
On the server, the loader also reroutes Trans, Plural, Select, DateTime, and NumberFormat imported from @fluenti/react to the generated server module. useI18n() remains client-only; importing it in a server file is a compile-time error.
The loader also optimizes <Trans> components at build time by pre-computing the context-aware message ID and extraction from JSX children, injecting __id, __message, and __components props. This eliminates the runtime extractMessage() and hashMessage() fast-path cost.
Scope decision table
Section titled “Scope decision table”| Scenario | Directive | Import | Needs async? | Needs setLocale? |
|---|---|---|---|---|
| Server Component | — | import { t } from '@fluenti/react' | Auto-promoted | Via I18nProvider |
| Client Component | 'use client' | import { useI18n } from '@fluenti/react' | No | Via I18nProvider |
generateMetadata | — | import { t } from '@fluenti/react' | Auto-promoted | Via layout’s I18nProvider |
| Server Action | 'use server' | import { t } from '@fluenti/react' | Yes | resolveLocale fallback |
.map() callback | — | Same as parent | Auto (eager resolve) | Via parent scope |
generateMetadata support
Section titled “generateMetadata support”Next.js conventional exports like generateMetadata, generateStaticParams, and generateViewport are automatically recognized as server-eligible, even though they start with a lowercase letter. The compiler auto-promotes them to async when they use t:
import { t } from '@fluenti/react'
// No need for `async` — auto-promoted by the compilerexport function generateMetadata() { return { title: t`My Page Title`, description: t`My page description`, }}Generated Modules
Section titled “Generated Modules”withFluenti() generates modules at node_modules/.fluenti/ and aliases them as @fluenti/next.
@fluenti/next
Section titled “@fluenti/next”import { I18nProvider } from '@fluenti/next'This module is primarily the Next runtime/integration surface. For ordinary translation authoring in a withFluenti() app, prefer @fluenti/react.
| Export | Type | Description |
|---|---|---|
I18nProvider | async server component | Sets server locale + wraps children in ClientI18nProvider |
setLocale | (locale: string) => void | Set locale for the current request (React.cache() scoped) |
getI18n | () => Promise<FluentiInstanceExtended & { locale: string }> | Get the i18n instance for the current request |
t | CompileTimeT | Compile-time translation API for tagged templates and descriptor calls |
Trans | async server component | Rich text translation |
Plural | async server component | Locale-aware pluralization |
Select | async server component | Locale-aware categorical selection |
DateTime | async server component | Date formatting |
NumberFormat | async server component | Number formatting |
withLocale | (locale: string, fn: () => T, serverModule: ServerModule) => Promise<T> | Temporarily switch request-scoped locale for a subtree render |
I18nProvider (server)
Section titled “I18nProvider (server)”The I18nProvider from @fluenti/next is an async server component. It does three things automatically:
- Calls
setLocale()to initialize the request-scoped locale viaReact.cache - Calls
getI18n()to load messages on the server - Renders a client-side
ClientI18nProviderwith statically imported message catalogs (functions can’t cross the RSC serialization boundary, so messages are bundled into the client provider)
This is the recommended way to set up i18n in Next.js layouts. Use path-based routing (/[locale]/) so the server always knows the correct locale:
import { getDirection } from '@fluenti/core'import { I18nProvider } from '@fluenti/next'
export default async function LocaleLayout({ children, params,}: { children: React.ReactNode params: Promise<{ locale: string }>}) { const { locale } = await params return ( <div dir={getDirection(locale)}> <I18nProvider locale={locale}>{children}</I18nProvider> </div> )}I18nProvider (client) — @fluenti/next/provider
Section titled “I18nProvider (client) — @fluenti/next/provider”The package also exports a client-only I18nProvider from @fluenti/next/provider. This is the raw ClientI18nProvider without server-side initialization:
import { I18nProvider } from '@fluenti/next/provider'Use this only if you are handling server-side i18n setup yourself (e.g. custom createServerI18n wiring). In most cases, use the @fluenti/next version instead — it handles both server and client setup automatically.
| Entry | Component type | Server init | Message loading |
|---|---|---|---|
@fluenti/next | Async server component | Automatic | Automatic (static imports) |
@fluenti/next/provider | Client component | Manual | Manual (you pass messages prop) |
withLocale(locale, fn, serverModule)
Section titled “withLocale(locale, fn, serverModule)”Temporarily switch the request-scoped locale for a subtree render. Useful for rendering a component in a different locale within the same request. The third parameter serverModule is required and must be the generated server module (which provides setLocale and getI18n):
import { withLocale } from '@fluenti/next/server'import * as serverI18n from '@fluenti/next'
export default async function Page() { const jaContent = await withLocale('ja', async () => { return <Trans>Hello</Trans> // renders in Japanese }, serverI18n) return <div>{jaContent}</div>}Middleware — @fluenti/next/middleware
Section titled “Middleware — @fluenti/next/middleware”Built-in i18n middleware for Next.js App Router. Handles locale detection from URL path, cookie, and Accept-Language header, then passes the resolved locale to server components via the x-fluenti-locale request header.
createI18nMiddleware(config)
Section titled “createI18nMiddleware(config)”Create a middleware function for locale-prefixed routing:
import { NextResponse } from 'next/server'import { createI18nMiddleware } from '@fluenti/next/middleware'
export default createI18nMiddleware({ NextResponse })
export const config = { matcher: ['/((?!_next|api|favicon).*)'],}Config
Section titled “Config”| Option | Type | Default | Description |
|---|---|---|---|
NextResponse | NextResponseStatic | — | Required. Pass NextResponse from next/server |
locales | string[]? | ['en'] | Available locales (reads from fluenti.config.ts if omitted) |
sourceLocale | string? | 'en' | Default/source locale |
cookieName | string? | 'locale' | Cookie name for reading user locale preference |
localePrefix | 'always' | 'as-needed' | 'as-needed' | URL prefix strategy |
Locale prefix strategies:
'always'— all locales get a URL prefix (e.g./en/about,/fr/about)'as-needed'— source locale has no prefix, others do (e.g./about,/fr/about)
Composing with other middleware
Section titled “Composing with other middleware”import { NextResponse } from 'next/server'import { clerkMiddleware } from '@clerk/nextjs/server'import { createI18nMiddleware } from '@fluenti/next/middleware'
const i18nMiddleware = createI18nMiddleware({ NextResponse })
export default clerkMiddleware(async (auth, req) => { await auth.protect() return i18nMiddleware(req)})Navigation — @fluenti/next/navigation
Section titled “Navigation — @fluenti/next/navigation”Client-side navigation utilities for locale-aware routing.
useLocaleSwitcher(options?)
Section titled “useLocaleSwitcher(options?)”Hook for switching locales in Next.js App Router. Sets a cookie, updates React context, navigates to the new locale path, and refreshes server components:
'use client'import { useLocaleSwitcher } from '@fluenti/next/navigation'
function LanguagePicker() { const { switchLocale, currentLocale, locales } = useLocaleSwitcher() return ( <select value={currentLocale} onChange={(e) => switchLocale(e.target.value)}> {locales.map((l) => <option key={l} value={l}>{l}</option>)} </select> )}Returns:
| Property | Type | Description |
|---|---|---|
switchLocale | (locale: string) => void | Navigate to the given locale |
currentLocale | string | Current active locale |
locales | string[] | All available locales |
sourceLocale | string | Source/default locale |
getLocalePath(pathname, locale, options?)
Section titled “getLocalePath(pathname, locale, options?)”Pure function — get the locale-prefixed path for a given pathname and locale:
import { getLocalePath } from '@fluenti/next/navigation'
getLocalePath('/about', 'fr') // → '/fr/about'getLocalePath('/about', 'en') // → '/about' (source locale, no prefix)getLocalePath('/fr/about', 'ja') // → '/ja/about'| Parameter | Type | Description |
|---|---|---|
pathname | string | Current pathname |
locale | string | Target locale |
options.sourceLocale | string? | Source locale (default: 'en') |
For setup and usage details, see the Next.js guide and Server Components.