Skip to content
fluenti

@fluenti/next

Wrap your Next.js config to add the Fluenti compiler plugin. Returns a function that accepts the Next.js config:

next.config.ts
import { withFluenti } from '@fluenti/next'
export default withFluenti()({
reactStrictMode: true,
})

All options are optional. Defaults are read from fluenti.config.ts in the project root. Options passed here override the config file values.

OptionTypeDefaultDescription
localesstring[]?from config fileOverride locale list
defaultLocalestring?from config fileOverride default/fallback locale (maps to sourceLocale in config)
compiledDirstring?from config filePath to compiled message catalogs (maps to compileOutDir in config)
resolveLocalestring?reads locale cookiePath to a module that default-exports () => string | Promise<string>. Used in Server Actions and other contexts where the layout doesn’t run
serverModulestring?auto-generatedPath to a custom server module. When provided, skips auto-generation
serverModuleOutDirstring?node_modules/.fluentiDirectory where generated server module files are written
devAutoCompileboolean?trueAuto extract+compile when source files change in dev mode
devAutoCompileDelaynumber?1000Debounce delay in ms for dev auto-compile. Increase if recompilation is too frequent
buildAutoCompileboolean?trueAuto 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
  1. Generates a server module at node_modules/.fluenti/server.js containing:
    • A createServerI18n instance with setLocale(), getI18n(), and server components
    • A I18nProvider async component for layouts
    • A ClientI18nProvider with statically imported message catalogs
  2. Registers a webpack loader that uses AST scope analysis to detect Fluenti’s authoring imports and rewrites them to the correct runtime bindings
  3. Adds a resolve alias mapping @fluenti/next to the generated module

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.

ScenarioDirectiveImportNeeds async?Needs setLocale?
Server Componentimport { t } from '@fluenti/react'Auto-promotedVia I18nProvider
Client Component'use client'import { useI18n } from '@fluenti/react'NoVia I18nProvider
generateMetadataimport { t } from '@fluenti/react'Auto-promotedVia layout’s I18nProvider
Server Action'use server'import { t } from '@fluenti/react'YesresolveLocale fallback
.map() callbackSame as parentAuto (eager resolve)Via parent scope

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 compiler
export function generateMetadata() {
return {
title: t`My Page Title`,
description: t`My page description`,
}
}

withFluenti() generates modules at node_modules/.fluenti/ and aliases them as @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.

ExportTypeDescription
I18nProviderasync server componentSets server locale + wraps children in ClientI18nProvider
setLocale(locale: string) => voidSet locale for the current request (React.cache() scoped)
getI18n() => Promise<FluentiInstanceExtended & { locale: string }>Get the i18n instance for the current request
tCompileTimeTCompile-time translation API for tagged templates and descriptor calls
Transasync server componentRich text translation
Pluralasync server componentLocale-aware pluralization
Selectasync server componentLocale-aware categorical selection
DateTimeasync server componentDate formatting
NumberFormatasync server componentNumber formatting
withLocale(locale: string, fn: () => T, serverModule: ServerModule) => Promise<T>Temporarily switch request-scoped locale for a subtree render

The I18nProvider from @fluenti/next is an async server component. It does three things automatically:

  1. Calls setLocale() to initialize the request-scoped locale via React.cache
  2. Calls getI18n() to load messages on the server
  3. Renders a client-side ClientI18nProvider with 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:

app/[locale]/layout.tsx
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.

EntryComponent typeServer initMessage loading
@fluenti/nextAsync server componentAutomaticAutomatic (static imports)
@fluenti/next/providerClient componentManualManual (you pass messages prop)

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>
}

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.

Create a middleware function for locale-prefixed routing:

src/middleware.ts
import { NextResponse } from 'next/server'
import { createI18nMiddleware } from '@fluenti/next/middleware'
export default createI18nMiddleware({ NextResponse })
export const config = {
matcher: ['/((?!_next|api|favicon).*)'],
}
OptionTypeDefaultDescription
NextResponseNextResponseStaticRequired. Pass NextResponse from next/server
localesstring[]?['en']Available locales (reads from fluenti.config.ts if omitted)
sourceLocalestring?'en'Default/source locale
cookieNamestring?'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)
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)
})

Client-side navigation utilities for locale-aware routing.

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:

PropertyTypeDescription
switchLocale(locale: string) => voidNavigate to the given locale
currentLocalestringCurrent active locale
localesstring[]All available locales
sourceLocalestringSource/default locale

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'
ParameterTypeDescription
pathnamestringCurrent pathname
localestringTarget locale
options.sourceLocalestring?Source locale (default: 'en')

For setup and usage details, see the Next.js guide and Server Components.