next-static-intl
v0.1.2
Published
Small React i18n library optimized for statically exported Next.js App Router sites
Maintainers
Readme
next-static-intl
A minimal, zero-dependency internationalization helper for React, built specifically for statically exported Next.js App Router projects.
- No runtime I/O
- Fully static-friendly
- Available in both server and client components
- Supports nested keys
- Supports parameter interpolation
- Supports rich text (HTML-like) interpolation (client-only for the time being)
- Tiny bundle footprint
- Framework-agnostic — works in any React setup
Installation
npm install next-static-intlor
yarn add next-static-intlProject structure example
messages/
en.ts
it.ts
app/
[locale]/
layout.tsx
page.tsx
components/
Header.tsxMessage files can contain nested messages:
export const messages = {
home: {
title: "Welcome, {name}!",
richExample: "Click <bold>here</bold> to continue"
}
}
Quick usage
app/[locale]/layout.tsx
'use client'
import { TranslationProvider } from 'next-static-intl'
import en from '../../locales/en.ts'
import it from '../../locales/it.ts'
export const generateStaticParams = () => [{ locale: 'en' }, { locale: 'it' }]
export default function LocaleLayout({ children, params }) {
const messages = params.locale === 'en' ? en : it
return (
<TranslationProvider locale={params.locale} messages={messages}>
{children}
</TranslationProvider>
)
}or create a dedicated client-side wrapper component for <TranslationProvider /> to avoid making the layout component client-side:
// components/LocaleProvider.tsx
'use client'
import { TranslationProvider } from 'next-static-intl'
export default function LocaleProvider({ children, locale, messages }) {
return (
<TranslationProvider locale={locale} messages={messages}>
{children}
</TranslationProvider>
)
}Using translations inside client components
'use client'
import { useTranslations } from 'next-static-intl'
export default function Home() {
const { t } = useTranslations()
return <h1>{t('home.title', { name: 'Alice' })}</h1>
}Using translations inside server components
import { getTranslations } from 'next-static-intl/server'
import { messages } from '../messages/en'
export default function Home() {
const t = getTranslations(messages)
return <h1>{t('home.title', { name: 'Alice' })}</h1>
}Interpolation
Plain interpolation
Messages:
messages = {
greeting: 'Hello, {name}! Today is {day}.',
}Component:
t('greeting', { name: 'Alice', day: 'Monday' })
// → "Hello, Alice! Today is Monday."Rich text interpolation
Use the t.rich method to embed React components.
Messages:
messages = {
info: 'Click <bold>here</bold> to continue',
}Component:
t.rich('info', {
bold: (chunks) => <strong>{chunks}</strong>,
})You can support any tag name (<em>, <link>, <red>, <foo>, …).
API
<TranslationProvider />
| Prop | Type | Description |
| ---------- | --------------------- | ----------------------- |
| locale | string | Active locale code |
| messages | Record<string, any> | The loaded translations |
useTranslations()
Returns { t, locale, messages }.
const { t, locale, messages } = useTranslations()t(key, params?)
- Resolves nested keys:
home.title,auth.errors.invalid - Supports simple placeholders:
{name}
t.rich(key, renderers):
- Parses HTML-like tags:
<bold>text</bold> - Calls renderers to turn chunks into components
getTranslations()
Returns t.
const t = getTranslations()t(key, params?)
- Resolves nested keys:
home.title,auth.errors.invalid - Supports simple placeholders:
{name}
Message format limitations
- Rich tags must be properly closed
(<bold>…</bold>) - Tags must be simple alphanumeric names (e.g., bold, link, red)
- Tags cannot be nested (yet!) → coming soon?
Example of unsupported case:
bad: 'Hello <bold>very <italic>nested</italic></bold> world'Why this library?
Many i18n libraries (including next-intl) assume:
- SSR is available
- messages can be loaded dynamically
- the router is dynamic
But Next.js static export removes these capabilities. This library was created to provide the missing piece:
- translation at build time
- zero runtime overhead
- zero hydration cost
- minimal API
- no dynamic imports
- full compatibility with App Router static generation
TypeScript Support
Includes full TS definitions:
- Autocomplete for
t.rich - Strict types for
tin context - Types for messages and parameters
Example:
t('home.title') // autocomplete works if keys are typedExample messages
{
home: {
title: "Welcome, {name}!",
cta: "Click <link>here</link> to continue"
}
}License
MIT
