@pico-intl-dev/next
v1.0.7
Published
Next.js App Router bindings for pico-intl - SSR + RSC support
Maintainers
Readme
@pico-intl-dev/next
Next.js App Router bindings for pico-intl.
Status: stable v1 for the Next App Router shape covered by published npm adoption and production fixture tests. The adapter intentionally avoids importing next/server internally so it stays lightweight and framework-compatible through standard Request/Response shapes.
Install
npm install @pico-intl-dev/core @pico-intl-dev/next next reactStart from scratch
Use the adapter at the App Router boundary:
app/
layout.tsx
[locale]/
layout.tsx
page.tsx
LocaleSwitcher.tsx
proxy.ts
src/
i18n/server.ts
locales/en.json
locales/es.jsonKeep app/layout.tsx for the document shell and global CSS. Put
PicoIntlNextProvider in app/[locale]/layout.tsx, after resolving the URL
locale and loading that locale's messages.
Server Components
import { createServerI18n } from '@pico-intl-dev/next';
export const { getT, resolveLocale, getLocales } = createServerI18n({
base: 'en',
supported: ['en', 'es'],
loadMessages: async (locale) => {
const mod = await import(`../locales/${locale}.json`);
return mod.default;
},
});Client Components
'use client';
import Link from 'next/link';
import { PicoIntlNextProvider, useLocale, useT } from '@pico-intl-dev/next';
function Title() {
const t = useT();
return <h1>{t('page.title')}</h1>;
}
function LocaleSwitcher() {
const { dir, locale } = useLocale();
const nextLocale = locale === 'es' ? 'en' : 'es';
return <Link dir={dir} href={`/${nextLocale}`}>Switch locale</Link>;
}
export function LocaleProvider({ locale, messages, children }) {
return (
<PicoIntlNextProvider base="en" locale={locale} messages={messages}>
{children}
</PicoIntlNextProvider>
);
}useLocale() returns an object: destructure it with const { locale } =
useLocale(). Unlike the generic React adapter, the Next adapter does not
return setLocale; switch languages with App Router navigation so Server
Components load the correct catalog.
It also exposes dir and isRTL so client components can set direction-aware
attributes without reimplementing locale detection.
For simple switches, use Link:
'use client';
import Link from 'next/link';
import { useLocale } from '@pico-intl-dev/next';
export function LocaleLink() {
const { locale } = useLocale();
const nextLocale = locale === 'es' ? 'en' : 'es';
return <Link href={`/${nextLocale}`}>{nextLocale.toUpperCase()}</Link>;
}For current-path switches, use router.push():
'use client';
import { usePathname, useRouter } from 'next/navigation';
import { useLocale } from '@pico-intl-dev/next';
export function LocaleButton() {
const pathname = usePathname();
const router = useRouter();
const { locale } = useLocale();
function switchTo(nextLocale: string) {
router.push(pathname.replace(`/${locale}`, `/${nextLocale}`));
}
return <button onClick={() => switchTo('es')}>ES</button>;
}Message formatting
The Next adapter uses the same pico-intl ICU-lite syntax as core:
| Use case | Syntax |
| --- | --- |
| Interpolation | Hello, {{name}} |
| Plural | {{count}} item \| {{count}} items |
| Select | {{role, select, admin{Admin} other{User}}} |
| Currency | {price, number, ::currency/USD} |
| Date/time | {date, date, ::yMMMd} and {time, time, ::hm} |
See the repository message formatting guide for examples and the ICU-lite support boundary.
Proxy / middleware helper
Next.js 16 renamed the request interception file convention from
middleware.ts to proxy.ts. pico-intl exposes both names so apps can follow
their Next.js version without changing runtime behavior.
// proxy.ts
import { createI18nProxy } from '@pico-intl-dev/next/proxy';
export const proxy = createI18nProxy({
base: 'en',
supported: ['en', 'es'],
});Existing middleware-based projects can continue to use:
// middleware.ts
import { createI18nMiddleware } from '@pico-intl-dev/next/middleware';
export const middleware = createI18nMiddleware({
base: 'en',
supported: ['en', 'es'],
});API
| Export | Purpose |
| --- | --- |
| createServerI18n() | Server-side translate factory for RSC and metadata |
| PicoIntlNextProvider | Client provider using server-loaded messages |
| useT(), useLocale(), useI18n() | Client hooks; useLocale() returns { locale, dir, isRTL } |
| createI18nProxy() | Next.js 16+ proxy helper for locale prefix/cookie/header routing |
| createI18nMiddleware() | Backwards-compatible middleware helper |
Production notes
- Adapt middleware to your app's routing and cookie policy.
- Validate hydration with your real layouts and route transitions.
- This package no longer installs Next as a dev dependency in the monorepo because its code does not import Next internals.
Verification
npm run build:all
npm run test:integration:next