Repository navigation
Getting Started
📄 Mirrors
docs/getting-started.mdin the main repo — the repo version is canonical.
Status: planned / pre-release. This page documents the intended API of next-tongue. Nothing is published yet; names may change before v1.0. See ROADMAP.
Requirements (planned): Next.js 16+ (App Router), React 19.2+, TypeScript 5.5+ recommended, Node 20.9+.
npm install next-tongue # not yet published
npx tongue inittongue init asks for your locales and default locale, then creates/edits:
| File | What it does |
|---|---|
tongue.config.ts |
Single source of truth — see Configuration |
messages/en.json, messages/de.json
|
Source and target messages |
next.config.ts |
Wrapped with withTongue()
|
proxy.ts |
Locale negotiation + redirects — see Routing & proxy |
app/[locale]/layout.tsx |
Root layout with <html lang>, generateStaticParams, TongueProvider
|
tsconfig.json |
Adds .tongue/types.d.ts to include
|
.gitignore |
Adds .tongue/ (generated output) |
// tongue.config.ts
import { defineConfig } from 'next-tongue/config';
export default defineConfig({
locales: ['en', 'de'],
defaultLocale: 'en',
messages: './messages/{locale}.json',
});// next.config.ts
import type { NextConfig } from 'next';
import { withTongue } from 'next-tongue/next';
const nextConfig: NextConfig = { cacheComponents: true };
export default withTongue(nextConfig);// proxy.ts
import { createProxy } from 'next-tongue/proxy';
export const proxy = createProxy();
export const config = { matcher: ['/((?!api|_next|.*\\..*).*)'] };Matcher caveat:
.*\\..*skips every path containing a dot, so/de/blog/v1.2bypasses the proxy (no locale redirect). If you have dotted routes, exclude only real file extensions, e.g.'/((?!api|_next|.*\\.(?:ico|png|jpg|svg|txt|xml|webmanifest)$).*)'.
// app/[locale]/layout.tsx
import { notFound } from 'next/navigation';
import { localeParams, isLocale } from 'next-tongue/server';
import { TongueProvider } from 'next-tongue/client';
export const generateStaticParams = localeParams; // [{ locale: 'en' }, { locale: 'de' }]
export default async function RootLayout({ children, params }: LayoutProps<'/[locale]'>) {
const { locale } = await params;
if (!isLocale(locale)) notFound();
return (
<html lang={locale}>
<body>
<TongueProvider locale={locale}>{children}</TongueProvider>
</body>
</html>
);
}TongueProvider passes only the locale string to client components. It never serialises messages — client components import compiled message modules directly (see Architecture).
// messages/en.json
{
"home": {
"title": "Welcome, {name}!",
"inbox": "{count, plural, =0 {No messages} one {# message} other {# messages}}"
}
}// app/[locale]/page.tsx
import { getT } from 'next-tongue/server';
export default async function Page({ params }: PageProps<'/[locale]'>) {
const { locale } = await params;
const t = await getT(locale, 'home');
return <h1>{t('title', { name: 'Ada' })}</h1>;
}'use client';
import { useT } from 'next-tongue/client';
export function InboxBadge({ count }: { count: number }) {
const t = useT('home');
return <span>{t('inbox', { count })}</span>;
}npx tongue check # fails on missing, unused or mismatched keys- Routing & proxy — prefix strategies, domains, detection
- Cache Components & PPR — why locale comes from params
- Type safety — what is checked and how
Guides
- Getting Started
- Configuration
- Routing and Proxy
- Server Components
- Client Components
- Server Actions
- ICU Messages
- Type Safety
- Cache Components and PPR
- Static Export
- Metadata SEO and hreflang
- Database and CMS Messages
- Dev Tools
- Translator Workflow
- Per-user Preferred Language
- CLI
- AI Translation
- Testing
- VS Code Extension
- Migration from next intl
- Migration from next international (deferred)
Reference