Next.js
The translate-projects-nextjs package has two parts: a command that translates your JSON files and utilities for using them in Next.js (App Router): language middleware, an i18next provider, Link, and hooks with autocompletion.
Requirements
- Node.js 20 or higher.
- Next.js 14 or higher and React 18 or higher. They are peer dependencies: the package uses the ones from your project.
- A Translate Projects project and an API token. See Getting started.
1. Install the package
npm i translate-projects-nextjs
i18next and react-i18next are installed with the package.
2. Write your texts
Put the base language texts in src/locales/<base language>/. Each JSON file (including in subfolders) is an i18next namespace, and the nesting is preserved:
{
"home": {
"title": "Bienvenido"
}
}
3. Configure the translation
Create translate.config.mjs in the root. The API token is read from the environment variable TRANSLATE_PROJECTS_API_KEY:
export default {
sourceLang: 'es', // Base language: the default language of the project
targetLangs: ['en', 'pt-BR', 'fr'], // Languages to generate
apiKey: process.env.TRANSLATE_PROJECTS_API_KEY,
// outputDir: './src/locales', // Default value
// scanner: true, // Optional: extract t('...') and <Trans> from ./src
};
Add the script to package.json:
{
"scripts": {
"translate": "translate-projects-nextjs translate"
}
}
4. Translate
export TRANSLATE_PROJECTS_API_KEY=tu_token
npm run translate
The command does not read .env files on its own: export the variable in the terminal or in CI, or load it yourself, for example with node --env-file=.env.local node_modules/.bin/translate-projects-nextjs translate. It exits with code 0 if all languages were translated and with 1 on any error.
Files it generates
src/locales/
├── es/translation.json ← your base language
├── en/translation.json ← generated
├── pt-BR/translation.json
└── fr/translation.json
Commit the generated files: the app imports them when building. Do not edit them by hand, they are overwritten on the next run. With the scanner enabled, <base language>/translation.json is also overwritten with the texts found in the code.
5. Use the translations in Next.js
Middleware
Redirects to /<language>/... and stores the language in the NEXT_LOCALE cookie. The routes /_next, /api, /locales, and /favicon.ico are always ignored.
import { NextRequest } from 'next/server';
import { langMiddleware } from 'translate-projects-nextjs/middleware';
export function middleware(request: NextRequest) {
return langMiddleware({
request,
defaultLocale: 'es',
locales: ['es', 'en', 'pt-BR', 'fr'],
ignorePaths: ['/images'], // Optional
});
}
Provider
import { TranslationsProvider } from 'translate-projects-nextjs/provider';
import es from '@/locales/es/translation.json';
import en from '@/locales/en/translation.json';
export default async function Layout({
children,
params,
}: {
children: React.ReactNode;
params: Promise<{ locale: string }>;
}) {
const { locale } = await params;
return (
<html lang={locale}>
<body>
<TranslationsProvider
lng={locale as any}
initialLocale={locale as any}
i18nConfig={{
fallbackLng: ['es'],
supportedLngs: ['es', 'en'],
resources: {
es: { translation: es },
en: { translation: en },
},
}}
>
{children}
</TranslationsProvider>
</body>
</html>
);
}
Texts with autocompletion
'use client';
import { useTypedTranslation } from 'translate-projects-nextjs/hooks';
import type es from '@/locales/es/translation.json';
export function Title() {
const { t } = useTypedTranslation<typeof es>();
return <h1>{t('home.title')}</h1>; // Keys are autocompleted
}
useTranslation and <Trans> from react-i18next also work.
Navigation
'use client';
import { Link } from 'translate-projects-nextjs/link';
import { useLocalizedRouter } from 'translate-projects-nextjs/hooks';
export function Nav() {
const router = useLocalizedRouter();
return (
<>
<Link href="/about">About</Link> {/* -> /<locale>/about */}
<button onClick={() => router.push('/', undefined, 'en')}>English</button>
</>
);
}
Cache and force
Unchanged files are not sent or translated again: a run with no changes makes a single request. If you delete a generated file, it is rewritten from the cache without new requests. This package has no --force: to translate everything again, delete ~/.config/translate-projects-core/cache.json (this affects all projects on the team, and each one is synced once) and run again. More details in Cache and errors.
Common errors
If the API fails or returns a partial result, existing translations are kept, the command exits with code 1, and the missing texts are retried on the next run.
- Invalid token (401): check
TRANSLATE_PROJECTS_API_KEY. - Missing AI key (
MISSING_API_KEY): configure the provider, the model, and the key in the project. In the meantime, untranslated texts are not written to the file and i18next shows the text fromfallbackLng. Missing required configuration fields: apiKey: the variableTRANSLATE_PROJECTS_API_KEYis not defined in the terminal where you run the command.translate.config.mjsnot found: the command must be run in the root of the project.- Dependency conflict when installing: check that your project uses Next.js 14 or higher and React 18 or higher.
🚀 Automate your translations for free
Translate Projects is free. You create your project, connect your AI provider's key, and only pay them what you spend.
- No plans or credit card required: All features are available from day one.
- Use your own AI: Claude, OpenAI, DeepSeek, or Gemini. Your key is stored encrypted.
- Shared translation network: If a text has already been translated, you receive it instantly without spending your key.
- Admin panel: Review and edit all your translations from one place.
We're here to help
If you have any questions, need help, or want to contribute, don't hesitate to contact me. I'm available to support you with whatever you need.
- Website: neiderruiz.com
- GitHub: @neiderruiz
- Twitter: @neiderruiz
- YouTube: Neider Ruiz
Support the Project
If this project has been helpful to you, I invite you to buy me a coffee. Your support helps me keep improving and creating valuable content. Thanks for being here! ☕