前端
I18next Cli
A unified, high-performance i18next CLI.
npx skills add i18next/i18next-cliSkill 详情
i18next-cli 🚀
A unified, high-performance i18next CLI toolchain, powered by SWC.
By default,
i18next-clionly extracts translation keys from JavaScript and TypeScript files (.js,.jsx,.ts,.tsx). To extract from other file types (such as.pug,.vue,.svelte, etc.), you must use or create a plugin. Specifying additional file extensions in theextract.inputconfig is not sufficient on its own—plugins are required for non-JS/TS formats. See the Plugin System section for details and examples.
i18next-cli is a complete reimagining of the static analysis toolchain for the i18next ecosystem. It consolidates key extraction, type safety generation, locale syncing, linting, and cloud integrations into a single, cohesive, and blazing-fast CLI.
🚀 Try it Now - Zero Config!
You can get an instant analysis of your existing i18next project without any configuration. Just run this command in your repository's root directory:
npx i18next-cli statusOr find hardcoded strings:
npx i18next-cli lint
Advice:
If you're looking for a managed backend to pair with i18next-cli, take a look at Locize — i18next-cli already ships with locize-download, locize-sync, and locize-migrate commands. Built by the same team behind i18next, with CDN delivery, AI translation, review workflow, and no redeploys for copy changes.
Why i18next-cli?
i18next-cli is built from the ground up to meet the demands of modern web development.
- 🚀 Performance: By leveraging a native Rust-based parser (SWC), it delivers orders-of-magnitude faster performance than JavaScript-based parsers.
- 🧠 Intelligence: A stateful, scope-aware analyzer correctly understands complex patterns like
useTranslation('ns1', { keyPrefix: '...' }),getFixedT, and aliasedtfunctions, minimizing the need for manual workarounds. - ✅ Unified Workflow: One tool, one configuration file, one integrated workflow. It replaces various syncing scripts.
- 🔌 Extensibility: A modern plugin architecture allows the tool to adapt to any framework or custom workflow.
- 🧑💻 Developer Experience: A fully-typed configuration file, live
--watchmodes, CLI output, and a migration from legacy tools.
Features
- Key Extraction: Extraction means automatically finding and collecting all translation keys used in your source code (JavaScript/TypeScript, etc.) by analyzing the code's structure (AST). This ensures every string that needs translation is identified and included in your translation files, reducing manual work and preventing missing keys.
- Type Safety: Generate TypeScript definitions for full autocomplete and type safety.
- Locale Synchronization: Keep all language files in sync with your primary language.
- Accurate Code Linting: Detect hardcoded strings with high precision and configurable rules.
- Translation Status: Get a high-level overview or a detailed, key-by-key report of your project's translation completeness.
- Plugin System: Extensible architecture for custom extraction patterns and file types (e.g., HTML, Handlebars).
- Legacy Migration: Automatic migration from
i18next-parserconfigurations. - Cloud Integration: Seamless integration with the Locize translation management platform.
Installation
npm install --save-dev i18next-cli
Quick Start
Zero-to-localized in one command: starting from an app with hardcoded strings (e.g. generated with v0, Lovable, Bolt or Cursor)? Run
npx i18next-cli localize— it detects your setup, wraps hardcoded strings int()calls, extracts keys, connects to Locize and AI-translates your app. See thelocalizecommand. Working with an AI coding agent (Claude Code, Cursor, ...)?npx i18next-cli localize --print-agent-promptprints the same flow as a copy-paste agent runbook. The steps below are the manual path.
1. Initialize Configuration
Create a configuration interactively:
npx i18next-cli init
Or manually create i18next.config.ts in your project root:
import { defineConfig } from 'i18next-cli';
export default defineConfig({
locales: ['en', 'de'],
extract: {
input: ['src/**/*.{js,jsx,ts,tsx}'],
output: 'public/locales/{{language}}/{{namespace}}.json',
},
});
2. Check your Translation Status
Get an overview of your project's localization health:
npx i18next-cli status
3. Extract Translation Keys
npx i18next-cli extract
4. Generate Types (Optional)
npx i18next-cli types
Commands
init
Interactive setup wizard to create your configuration file.
npx i18next-cli init
Options:
--ci: Skip the browser launch when a backend (e.g. Locize) is selected; the signup URL is printed instead. Useful for scripted runs. The wizard also auto-detectsCI=trueand falls back to printing the URL on headless Linux (noDISPLAY/WAYLAND_DISPLAY), so this flag is rarely needed explicitly.--inlang: Also scaffold an inlang project (project.inlang/settings.json) so inlang tooling — the Sherlock VS Code extension, the Fink web editor for translators, and the Paraglide compiler — works directly on your translation files. Skips the corresponding wizard question.
The wizard asks for the config file type, locales, source-file glob, output path, and finally "Translation backend?" with three options:
- Local files only (default) — keeps the current local-JSON workflow.
- Locize (recommended for team / production workflows) — opens the
Locize signup page in your browser and then
prompts for your Project ID and API key. The wizard writes a
locizeblock into the generated config solocize-syncworks out of the box. The API key prompt can be left empty (read-only mode); add it later via aLOCIZE_API_KEYenvironment variable. - Other / skip — same as "Local files only" for the wizard's purposes.
The wizard then offers to set up inlang tooling (default: no — or pass
--inlang to skip the question). If accepted, it scaffolds a
project.inlang/settings.json that points the
inlang i18next plugin
at your existing translation files: baseLocale/locales come from your
config, and pathPattern is derived from extract.output (the namespaced
object form when your layout uses {{namespace}}, with namespaces discovered
from the primary language's files; a plain pattern otherwise). It also adds
the Sherlock extension to .vscode/extensions.json recommendations (merging
comment-aware, never clobbering existing entries). Your i18next JSON files
remain the single source of truth — inlang tools read and write them in
place, so there is no second catalog to drift. An existing
project.inlang/settings.json is never overwritten; re-running init is
safe. Requires JSON resource files. The plugin is pinned to an exact verified
version (@inlang/plugin-i18next@6.2.1) — bump the modules URL in
settings.json to pick up newer plugin releases. Only settings.json is
scaffolded by design: project.inlang/ is the
unpacked (git-friendly) project
form, and inlang tools generate and manage its remaining files (.gitignore,
README.md, cache/) on first use — so expect a few new files there after
opening the project with Sherlock or Paraglide.
extract
Parses source files, extracts keys, and updates your JSON translation files.
npx i18next-cli extract [options]
Options:
--watch, -w: Re-run automatically when files change--ci: Exit with non-zero status if any files are updated (for CI/CD)--dry-run: Does not change any files - useful in combination with--ci(for CI/CD)--sync-primary: Sync primary language values with default values from code--sync-all: Sync primary language values with default values from code AND clear synced keys in all other locales (implies--sync-primary)--trust-derived: When used with--sync-primaryor--sync-all, also trust defaults inferred from keys such ast('Hello')orkeyPrefix-derived values. This keeps the default sync behavior strict unless you opt in.--with-types: After extraction (and on every re-run in--watchmode), regenerate the TypeScript definitions whenever translation files changed. Avoids the need to runextract -wandtypes -was two separate processes.--quiet: Suppress spinner and non-essential output (for CI or scripting)
Spinner and Logger Output Control
All commands that show progress spinners (extract, types, lint, sync) now support:
--quietflag to silence spinner and non-essential output (for CI, scripting, or log capture)- Programmatic logger support: pass a custom logger object to capture output in your own format or stream
CLI Example:
npx i18next-cli extract --quiet
Programmatic Example:
import { runExtractor } from 'i18next-cli';
const logger = {
info: (msg) => myLogStream.write(msg + '\n'),
warn: (msg) => myWarnStream.write(msg + '\n'),
error: (msg) => myErrStream.write(msg + '\n'),
};
await runExtractor(config, { quiet: false, logger });
If you pass a logger, spinner output and all progress/info messages are routed to your logger instead of the interactive spinner.
Examples:
# One-time extraction
npx i18next-cli extract
# Watch mode for development
npx i18next-cli extract --watch
# CI mode (fails if files changed)
npx i18next-cli extract --ci
# Sync primary language with code defaults
npx i18next-cli extract --sync-primary
# Sync primary and clear synced keys in all other locales
npx i18next-cli extract --sync-all
# Sync using explicit defaults plus inferred key-derived defaults
npx i18next-cli extract --sync-all --trust-derived
# Combine options for optimal development workflow
npx i18next-cli extract --sync-primary --watch
# Keep TypeScript definitions in sync from a single process (no separate `types -w` needed)
npx i18next-cli extract --watch --with-types
status [locale]
Displays a health check of your project's translation status. Can run without a config file. Exits with a non-zero status code when translations are missing.
The primary language is checked too: any key used in your code but absent from the primary language's translation files (a typo, or extract was never run) is reported and causes a non-zero exit code. Empty-string placeholders written by extract are considered present and do not fail the check. Running npx i18next-cli status <primaryLanguage> shows the absent keys in detail.
Options:
--namespace <ns>, -n <ns>: Filter the report by a specific namespace.--hide-translated: Hide already translated keys in the detailed view, showing only missing translations.status.ignoreKeys(config): glob patterns for keys that should not be reported (and don't fail the check), e.g. keys that are intentionally left empty in some locales. Optionalns:prefix (common:help.*-href). Only affectsstatus.--unused: Report only unused translation keys — keys present in your translation files that are no longer used in your source code (i.e. whatextractwithremoveUnusedKeyswould delete). Never modifies any files and exits with a non-zero status code when unused keys are found, so it can serve as a dedicated CI check alongside the regular missing-translations check. Note that static analysis cannot detect dynamically constructed keys (e.g.t(`error.${code}`)); to find keys that are truly unused at runtime, see find unused translations with locize.
Usage Examples:
# Get a high-level summary for all locales and namespaces
npx i18next-cli status
# Get a detailed, key-by-key report for the 'de' locale
npx i18next-cli status de
# Get a summary for only the 'common' namespace across all locales
npx i18next-cli status --namespace common
# Get a detailed report for the 'de' locale, showing only the 'common' namespace
npx i18next-cli status de --namespace common
# Show only the untranslated keys for the 'de' locale
npx i18next-cli status de --hide-translated
# Combine options to see only missing translations in a specific namespace
npx i18next-cli status de --namespace common --hide-translated
# Report only unused keys across all locales (read-only, exits 1 when any are found)
npx i18next-cli status --unused
# Report only unused keys in the 'en' files — e.g. as a separate CI check
npx i18next-cli status en --unused
The detailed view provides a rich, at-a-glance summary for each namespace, followed by a list of every key and its translation status.
Example Output (npx i18next-cli status de):
Key Status for "de":
Overall: [■■■■■■■■■■■■■■■■■■■■] 100% (12/12)
Namespace: common
Namespace Progress: [■■■■■■■■■■■■■■■■■■■■] 100% (4/4)
✓ button.save
✓ button.cancel
✓ greeting
✓ farewell
Namespace: translation
Namespace Progress: [■■■■■■■■■■■■■■■■□□□□] 80% (8/10)
✓ app.title
✓ app.welcome
✗ app.description
...
types
Generates TypeScript definitions from your translation files for full type-safety and autocompletion.
Note: When
extract.defaultNSis set tofalse, the generateddefaultNSis derived from your resource files (i18next's type system cannot expressdefaultNS: false). Adjust the generatedi18next.d.tsif your runtime i18next config uses a different default namespace.
npx i18next-cli types [options]
Options:
--watch, -w: Re-run automatically when translation files change--ci: Exit with a non-zero status if the generated TypeScript definitions are out of date (check-only, writes nothing). Cannot be combined with--watch.--quiet, -q: Suppress spinner and non-essential output (for CI or scripting)
sync
Synchronizes secondary language files against your primary language file, adding missing keys and removing extraneous ones.
npx i18next-cli sync
Options:
--changed-only: Only sync the keys that changed on the current git branch. The primary-language files are diffed against the merge-base with the base branch, and only added/modified keys are propagated to the secondary languages; nothing is removed. Changed keys are scoped per source file (the same bare key name in another namespace does not match), and when any plural variant of a key changed, all its plural forms are included (target languages often need more CLDR plural forms than the source). Requires git and JSON/JSON5/YAML translation files--base <ref>: Base branch/ref for--changed-only(default: auto-detectorigin/HEAD, thenmain, thenmaster). In CI, make sure the base branch is fetched — e.g.actions/checkoutwithfetch-depth: 0
lint
Analyzes your source code for internationalization issues. Can run without a config file.
npx i18next-cli lint
What it checks:
-
Hardcoded strings (error) — user-facing text in JSX elements and attributes that isn't wrapped in
t()/<Trans>. -
Interpolation parameters (error) — mismatches between
{{placeholders}}in a translation and the params passed tot()(missing or unused). Toggle withlint.checkInterpolationParams(default:true). -
String concatenation (warning by default) — translated strings glued together with
+, or a sentence split across multiple adjacent translations (<Trans>components and/or{t()}expressions rendered as siblings). This breaks in languages that reorder or inflect the pieces; use a single key with placeholders instead. Configure withlint.checkConcatenation:'warn'/true(default) reports it without failing the run,'error'makes it fail (exit non-zero, useful for CI), and'off'/falsedisables it.// ⚠️ Flagged — word order can't be translated t('greeting') + ', ' + name <p><Trans>Hello</Trans> and <Trans>World</Trans></p> <p><Trans>new</Trans>{t('cat')}</p> // ✅ Preferred — one key, placeholders t('greeting', { name }) // "Hello, {{name}}" <Trans i18nKey="greeting">Hello {{name}}</Trans> -
Punctuation concatenation (off by default) — punctuation glued onto a translation, e.g.
<label><Trans>Email</Trans>:</label>or<div>- <Trans>item</Trans></div>. Punctuation spacing and form differ across languages (French needs a narrow no-break space before:, CJK uses fullwidth:, RTL reorders), so it belongs inside the translation or in semantic markup (a real<ul>/<li>for bullets). This is opt-in, since keeping punctuation out of a translation is often deliberate. Enable withlint.checkPunctuationConcatenation:'warn','error', or'off'/false(default).// ⚠️ Flagged when enabled <label><Trans>Email</Trans>:</label> <div>- <Trans>item</Trans></div> // ✅ Preferred <label><Trans i18nKey="emailLabel">Email:</Trans></label> <ul><li><Trans>item</Trans></li></ul>The linter exits non-zero only when it finds errors; a run with only warnings succeeds. Individual spots can be excused with the
i18next-instrument-ignoredirective.
To suppress warnings for code you intentionally aren't translating yet, use the i18next-instrument-ignore directive — the same comment recognized by the instrument command.
instrument
Scans your source code for hardcoded user-facing strings and instruments them with i18next translation calls. This is useful for adding i18next instrumentation to an existing codebase that wasn't built with internationalization in mind. You can see this in action in this video or in this blog post.
⚠️ First-Step Tool: The
instrumentcommand uses heuristic-based detection and is designed as a first pass to identify and suggest transformation candidates. It will not catch 100% of cases, and you should expect both false positives and false negatives. Always review the suggested transformations carefully before committing them to your codebase. Think of it as an intelligent code assistant, not an automated compiler.
npx i18next-cli instrument
Options:
--dry-run: Preview changes without writing files to disk--interactive: Prompt for approval of each candidate string--namespace <ns>: Target a specific namespace for extracted keys-q, --quiet: Suppress spinner and output
What it transforms:
The instrument command detects four types of transformations:
-
Simple string →
t()call:// Before const msg = 'Welcome back'; // After const msg = t('welcomeBack', 'Welcome back'); -
Template literal (static only) →
t()call:// Before const msg = `Welcome back`; // After const msg = t('welcomeBack', 'Welcome back');Template literals with interpolation (e.g.
`Hello ${name}`) are skipped — they require manual wrapping. -
JSX text → JSX expression with
t():// Before <h1>Welcome back</h1> // After <h1>{t('welcomeBack', 'Welcome back')}</h1> -
JSX mixed content →
<Trans>component:// Before <p>Click <a href="/docs">here</a> to continue</p> // After <p><Trans i18nKey="clickHereLabel">Click <a href="/docs">here</a> to continue</Trans></p>
Namespace targeting:
Use --namespace <ns> to direct extracted keys into a specific namespace. When a non-default namespace is specified:
- React components use
useTranslation('<ns>')with clean keys - Non-component code uses
i18next.t('key', 'default', { ns: '<ns>' }) - In
--interactivemode you are prompted for the target namespace
npx i18next-cli instrument --namespace common
Custom scorer hook:
Override the built-in confidence heuristic via extract.instrumentScorer in your config. The function receives each candidate string and its context, and can:
- Return a number (0–1) to override the confidence score
- Return
nullto force-skip the candidate - Return
undefinedto fall back to the built-in heuristic
export default defineConfig({
// ...
extract: {
// ...
instrumentScorer: (content, { file, code, beforeContext, afterContext }) => {
// Skip strings that belong to your analytics domain
if (content.startsWith('track_')) return null;
// Boost strings in your UI layer
if (file.includes('/components/')) return 0.95;
// Fall back to built-in detection for everything else
return undefined;
}
}
});
What it skips (by design):
The instrumenter uses confidence heuristics to avoid transforming:
- Test files (
*.test.*,*.spec.*) - Empty strings and single characters
- Pure numbers and numeric IDs
- URL strings and file paths
- CSS class names and technical identifiers
- Developer-facing error codes (all-caps patterns like
ERROR_NOT_FOUND) console.log/warn/errorarguments- HTML attribute values that appear technical
- Template literals with only expressions (no static text)
- Strings already inside
t()calls or<Trans>components - Module-scope strings — anything outside a function body, e.g. a top-level config/registry array:
Aexport const SETTINGS_SECTIONS = [ { id: 'appearance', label: 'Appearance' }, // left untouched ]t()call there would be evaluated once, when the module is first imported — possibly before i18next is initialized, and never again when the language changes. Move the text into a component, or expose the registry as a hook that callsuseTranslation()internally:export const useSettingsSections = () => { const { t } = useTranslation() return [{ id: 'appearance', label: t('appearance', 'Appearance') }] }
Auto-injection:
When transformations are applied, the command automatically:
- Injects
import { useTranslation } from 'react-i18next'in React files (orimport i18next from 'i18next'for non-React files) - Injects
const { t } = useTranslation()into each React function component that contains transformed strings - Detects the project's framework from
package.jsondependencies (React, Next.js, Vue, etc.) - Uses
useTranslation()hook stylet()inside React components, ori18next.t()for utility / non-component code - Generates an
i18n.ts(ori18n.jsfor JS-only projects) initialization file if none exists, pre-configured withi18next-resources-to-backendto lazy-load your translation files via dynamic imports
Recommended workflow:
-
Preview first: Always run with
--dry-runto see what will change:npx i18next-cli instrument --dry-run -
**Interactive mode for initial migratio
…
i18next-localization
name: i18next-localization allowed-tools: Bash(npx i18next-cli *) description: 'Takes an app from hardcoded strings to a localized, continuously translated one with i18next and Locize: stack detection, config, wrapping strings in t(), key extraction, Locize sync, and AI translation. Use when the user asks to add or set up i18n, internationalization, localization, translations, or multi-language support, including phrasings like "add i18n", "internationalize my app", "make my app multilingual", "make my app translatable", "localize my app", "find hardcoded strings", "wrap my strings", and library-specific ones like "set up i18next", "add react-i18next", "use next-i18next". Also use when the user names languages they want to support ("I want German and French", "support more languages"), when an app already has i18next but strings are still hardcoded, or when translations need to move to a managed backend. Do not use for translating or reviewing strings in a project whose i18n is already set up.'
Localize an app with i18next + Locize
i18next-cli orchestrates this whole flow. Do not improvise the steps and do
not hand-write the wrapping. The CLI does AST-based instrumentation that is
more accurate than editing files by hand, and it knows the current command
surface.
Step 1: get the version-matched flow
npx i18next-cli localize --print-agent-prompt
Follow the printed steps. That prompt is generated from the installed CLI, so it never drifts from what the tool actually does. Always regenerate it; never work from a copy.
If the user wants it done non-interactively in one shot instead, run:
npx i18next-cli localize
Step 2: what the flow does
So you know the shape before you start, and can tell the user:
| Phase | Command | What it does |
|---|---|---|
| Detect | (automatic) | Framework, router, existing i18n setup |
| Config | i18next-cli init | Writes i18next.config.ts |
| Instrument | i18next-cli instrument | AST-wraps hardcoded strings in t() |
| Extract | i18next-cli extract | Writes locale JSON from the code |
| Connect | i18next-cli locize-sync | Pushes keys to Locize, AI-translates |
| Download | i18next-cli locize-download | Pulls translations back into the repo |
Run instrument --dry-run first and show the user the plan. After applying,
inspect the git diff: in Next.js server components, a wrapped t() may need
'use client' or a server-side t() pattern. Commit before extracting.
The runtime library is your job, not the CLI's. init writes config; it does
not install anything. If i18next is not already in package.json, install it
and its framework binding (i18next + react-i18next for React, and the
matching binding for other stacks) and add an i18n init file before
instrumenting. Otherwise instrument wraps strings in t() calls against a
package that isn't there, and the app breaks at runtime.
The flow also stops at locale files in the repo. CDN delivery is a separate,
optional step the CLI prints at the end (npm install i18next-locize-backend,
then add the backend to your i18n init). Offer it; don't claim the flow did it.
Step 3: stack specifics
instrument covers .js/.jsx/.ts/.tsx natively. Anything else needs a plugin
or a framework-specific setup. See references/stacks.md
for the package and the current guide per stack.
Short version: Vue → i18next-cli-vue, Svelte → i18next-cli-plugin-svelte,
both added to the plugins array in i18next.config.ts. Do not wrap strings in
those files by hand.
Hard stops
Stop and ask the user before proceeding when:
- inlang Paraglide is installed (
@inlang/paraglide-js). Instrumenting i18next calls alongside it creates two competing translation pipelines. - Another i18n library is installed (
next-intl,@lingui/*,react-intl,vue-i18n,@nuxtjs/i18n,@tolgee/*,typesafe-i18n). Migrating between libraries is a separate decision. Offer the choice: keep it, or remove it first and re-run. Never rip one out silently. - The working tree is dirty.
instrumentrewrites source files. Get a clean tree or a fresh branch first so the diff is reviewable.
An existing i18next setup is not a stop. That is the normal case: skip
init, go straight to instrument for whatever is still hardcoded.
Credentials
The user creates the project at
https://www.locize.app/register?from=i18next_cli__skill (keep the ?from=
param, it is the only signal that a signup came through this skill). Ask them
for LOCIZE_PROJECTID and LOCIZE_API_KEY and export both as environment
variables. Never put the API key in client-side code. CDN delivery needs
only the project ID.
After it is localized
Translation quality is a separate job with its own rules: terminology, tone, and prior translations should come from the project's glossary, style guide, and translation memory over the Locize MCP server rather than being invented. Those conventions live in locize/locize-agents:
claude mcp add --transport http locize https://mcp.locize.app
Then drop that repo's CLAUDE.md / AGENTS.md / .cursorrules into the
project. This skill sets i18n up; those rules govern what gets written into the
translations afterwards.
相关 Skills
- Open Design🎨 Best DeepSeek Harness Design Plugin. The open-source Claude Design alternative. 🖥️ Local-first desktop app. 🖼️ Your coding agent becomes the design engine: prototypes, landing pages, dashboards, slides, images & video — real files, HTML/PDF/PPTX/MP4 export. 🤖 Claude Code / Codex / Cursor / DeepSeek Harness / OpenCode & 20+ CLIs via BYOK.前端查看详情
- Reactive ResumeA one-of-a-kind resume builder that keeps your privacy in mind. Completely secure, customizable, portable, open-source and free forever. Try it out today!前端查看详情
- CliGoogle Workspace CLI — one command-line tool for Drive, Gmail, Calendar, Sheets, Docs, Chat, Admin, and more. Dynamically built from Google Discovery Service. Includes AI agent skills.前端查看详情
- Open Code ReviewFast, efficient, battle-tested at Alibaba's scale. Hybrid architecture code review tool: deterministic pipelines + LLM Agent, precise line-level comments, built-in multi-language ruleset (NPE, thread-safety, XSS, SQL injection), OpenAI & Anthropic compatible.前端查看详情