Polyglot detects and translates. next-intl renders. Here's how to use them together for a complete Next.js i18n workflow — from finding untranslated strings to serving localized pages.
The most common question we get from Next.js developers: "Do I use Polyglot instead of next-intl, or with it?" The answer is with it. They solve different problems and complement each other perfectly.
next-intl is a runtime library. It provides the useTranslations() hook, locale routing middleware, and message formatting (plurals, dates, numbers). It's what your Next.js app uses at runtime to display the right translation for the current locale.
Polyglot is a CLI tool. It scans your source code to find untranslated strings, wraps hardcoded strings in next-intl calls, translates them with context-aware LLMs for you to review, and lets you preview the results. It runs in your terminal and CI pipeline, not in your app.
They work at different layers:
| Polyglot | next-intl | |
|---|---|---|
| When it runs | Development / CI | Runtime |
| What it does | Detect → Wrap → Translate → Review → Preview | Render translations |
| Where it lives | CLI / GitHub Action | Your app bundle |
| Output | JSON translation files | Localized UI |
Here's how a complete Next.js i18n workflow looks with both tools:
polyglot scan
Polyglot lists the hardcoded UI strings it finds in your TSX files as candidates to review (output trimmed):
src/app/page.tsx (1 string)
────────────────────────────────────────────────────────────
L8 [text] Welcome to our platform
src/components/nav.tsx (3 strings)
────────────────────────────────────────────────────────────
L12 [text] Dashboard
L13 [text] Settings
L14 [text] Log out
Found 47 localization candidates in 12 files (64 files scanned)
Preview the codemod first, then apply it:
polyglot wrap --dry-run
polyglot wrap
Polyglot rewrites detected strings with next-intl's useTranslations() hook, using a namespace derived from the file path and a key derived from the text:
// Before: src/app/page.tsx (Polyglot detected this)
<h1>Welcome to our platform</h1>
// After (Polyglot wraps it with next-intl)
const t = useTranslations("src.app.page");
<h1>{t('welcome_to_our_platform')}</h1>
And it adds the source string to your messages file:
// messages/en.json
{
"src": {
"app": {
"page": {
"welcome_to_our_platform": "Welcome to our platform"
}
}
}
}
Anything it can't rewrite safely is listed in polyglot-i18n-report.md with a reason, for you to handle by hand.
polyglot translate --languages fr,de,ja
Polyglot reads your messages/en.json, translates each string with LLMs (using component context, glossary terms, and translation memory), and outputs the target locale files:
// messages/fr.json
{
"src": {
"app": {
"page": {
"welcome_to_our_platform": "Bienvenue sur notre plateforme"
}
}
}
}
Generated translations also wait for review in the Polyglot dashboard — automatic approval is off. Once reviewers approve or edit them, polyglot pull --approved-only writes the reviewed values back to your catalogs.
polyglot preview --lang fr
Polyglot starts your dev server with the French catalog and opens the page. Depending on your routing, you may need to switch the locale yourself; the CLI tells you which. Preview doesn't flag problems for you — it lets you spot text overflow, layout breaks, RTL issues, and wrong-context wording before you deploy.
Add the open Polyglot Action to your PR workflow:
# .github/workflows/i18n.yml
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: polyglot-i18n/[email protected]
with:
check-mode: differential
New untranslated strings get flagged in the PR that introduces them, so hardcoded English gets caught in review. The check needs a committed polyglot.toml. See the CI/CD docs for the full set of inputs. On Team and Scale, Polyglot Automation (early access) can open a separate catalog-only translation PR after someone reviews the translations.
Polyglot auto-detects Next.js projects and reads your polyglot.toml for project-specific settings:
# polyglot.toml
[project]
name = "my-app"
framework = "nextjs"
source_language = "en"
target_languages = ["fr", "de", "ja"]
[translation]
output_dir = "messages"
output_format = "json"
The output_dir should point to where next-intl expects your message files. Polyglot reads the source messages, translates them, and writes the target locale files to the same directory.
Does Polyglot modify my source code?
polyglot scan is read-only — it only reports untranslated strings. polyglot wrap can automatically wrap strings with translation functions, but you control when and how to run it.
Does Polyglot replace next-intl? No. next-intl is your runtime library — it handles locale routing, message formatting, and rendering. Polyglot handles detection, wrapping, and translation. You need both.
What about next-intl's own tooling?
next-intl's tooling works from messages you've already written into your code with its APIs. It doesn't hunt for hardcoded strings you haven't wrapped yet. Polyglot covers the "find what I missed" step and the guarded code rewrite, with --dry-run available before it touches a file.
Can I use Polyglot with react-i18next instead of next-intl? Yes. Polyglot outputs standard JSON files. Whether your runtime library is next-intl, react-i18next, or react-intl, Polyglot's output is compatible.
What if I already have some translations? Polyglot's incremental mode only translates strings that are new or changed. Existing translations in your message files are preserved. Translation memory ensures new translations stay consistent with existing ones.
If you already have a Next.js app with next-intl:
# Install Polyglot
curl -fsSL https://getpolyglot.ai/install.sh | bash
# Initialize
polyglot init
# Find what you've missed
polyglot scan
# Wrap hardcoded strings in next-intl calls
polyglot wrap --dry-run
polyglot wrap
# Translate new strings
polyglot translate
# Preview
polyglot preview --lang fr
If you're starting from scratch, set up next-intl first (follow their getting started guide), then use Polyglot to find strings you haven't wrapped yet and translate them.
Start in your terminal
Install the CLI, run a scan, and see exactly what you're missing. Free, no account required.