Skip to content

yossTheDev/i18nizer

Repository files navigation

i18nizer 🌍

banner

Node.js Badge oclif Badge TypeScript Badge npm downloads license test status

i18nizer automates the boring parts of i18n.

If your project already uses i18next or next-intl, i18nizer:

  • extracts hardcoded strings from JSX/TSX
  • generates i18n JSON files using AI-assisted translations
  • creates readable English keys (AI-powered & cached)
  • rewrites your components to use t("key")

No runtime, no lock-in, no SaaS.
Just a CLI that fits into your existing development workflow.

🤖 How translations work

i18nizer uses AI providers (OpenAI, Gemini, Hugging Face) to generate translations at development time.

  • Translations are generated once and stored as plain JSON files
  • Output is fully editable and version-controlled
  • No AI is required at runtime
  • Cached translations avoid repeated AI requests

You own the result — i18nizer only automates the process.

Who is this for?

  • React / Next.js developers already using i18next or next-intl
  • Teams tired of manually extracting strings and managing i18n JSONs
  • Projects that want automation without changing runtime behavior

Supported AI Providers

  • OpenAI

  • Google Gemini

  • Hugging Face (DeepSeek)

    Used for development-time translations and key generation.
    

🚀 Installation

npm install -g i18nizer

Requires Node.js 18+


🔑 API Keys Configuration

i18nizer keys --setOpenAI <YOUR_OPENAI_API_KEY>
i18nizer keys --setGemini <YOUR_GEMINI_API_KEY>
i18nizer keys --setHF <YOUR_HUGGING_FACE_API_KEY>

Keys are stored inside:

[HOME]/.i18nizer/api-keys.json

⚡ Quick Start

Initialize Your Project

Interactive Mode (Recommended)

i18nizer start

This launches an interactive setup that will:

  • 🔍 Auto-detect your framework (Next.js or React)
  • 🔍 Auto-detect your i18n library (next-intl, react-i18next, i18next)
  • ❓ Ask you to confirm or change the detected settings
  • ✅ Create i18nizer.config.yml with optimal defaults
  • 📁 Set up .i18nizer/ directory for caching and project data
  • 📂 Create messages/ directory for translation files

Non-Interactive Mode (CI/Automation)

i18nizer start --yes

Auto-detects and uses default values without prompts.

Manual Configuration

Specify framework and i18n library explicitly:

# Next.js with next-intl
i18nizer start --framework nextjs --i18n next-intl

# React with react-i18next
i18nizer start --framework react --i18n react-i18next

# Custom setup
i18nizer start --framework custom --i18n custom

Available options:

  • --framework: nextjs, react, custom
  • --i18n: next-intl, react-i18next, i18next, custom
  • --yes, -y: Skip interactive prompts
  • --force, -f: Re-initialize existing project

Translate Your Components

Translate a single file:

i18nizer translate src/components/Login.tsx --locales en,es,fr

Translate all components in your project:

i18nizer translate --all --locales en,es,fr

Preview changes without modifying files:

i18nizer translate <file> --dry-run

Show generated JSON output:

i18nizer translate <file> --show-json

Regenerate Aggregator

Rebuild the auto-generated aggregator file:

i18nizer regenerate

This command regenerates the i18n/messages.generated.ts file by scanning all JSON files in your messages directory. Use this when:

  • You manually add or remove translation JSON files
  • You rename JSON files
  • The aggregator becomes out of sync with your messages

The aggregator automatically uses valid TypeScript identifiers for imports, converting hyphenated filenames (e.g., notification-item.json) to PascalCase identifiers (e.g., NotificationItem_en).

Legacy Command (Still Supported)

i18nizer extract <file-path> --locales en,es,fr --provider openai

Flags:

  • --locales → Languages to generate (default: en,es)
  • --provideropenai | gemini | huggingface (optional)

🧩 Example

Input Component

export function Login() {
  return (
    <div>
      <h1>Welcome back</h1>
      <p>Please sign in to continue</p>
      <button>Sign in</button>
    </div>
  );
}

Output Component (Automatically Rewritten)

import { useTranslations } from "next-intl";

export function Login() {
  const t = useTranslations("Login");

  return (
    <div>
      <h1>{t("welcomeBack")}</h1>
      <p>{t("pleaseSignInToContinue")}</p>
      <button>{t("signIn")}</button>
    </div>
  );
}

Generated JSON (messages/en/login.json)

{
  "Login": {
    "welcomeBack": "Welcome back",
    "pleaseSignInToContinue": "Please sign in to continue",
    "signIn": "Sign in"
  }
}

Note: The filename uses lowercase-hyphen format (login.json), while the namespace inside uses PascalCase (Login).


📂 Project Structure

When initialized with i18nizer start:

your-project/
├─ i18nizer.config.yml       # Configuration file
├─ .i18nizer/
│  ├─ cache/
│  │  └─ translations.json   # Translation cache
│  └─ ...
├─ i18n/
│  └─ messages.generated.ts  # Auto-generated aggregator (imports all JSON files)
└─ messages/                 # Translation files (configurable path)
   ├─ en/
   │  └─ login.json          # Lowercase-hyphen format for filenames
   ├─ es/
   │  └─ login.json
   └─ fr/
      └─ login.json

Note: JSON filenames use lowercase-hyphen format (e.g., notification-item.json, user-profile.json), while the namespace inside the JSON file uses PascalCase (e.g., NotificationItem, UserProfile). The auto-generated aggregator converts filenames to valid TypeScript identifiers.

Legacy standalone mode (without i18nizer start):

[HOME]/.i18nizer/
├─ api-keys.json
├─ tsconfig.json
├─ i18n/
│  └─ messages.generated.ts  # Auto-generated aggregator
└─ messages/
   ├─ en/
   │  └─ login.json
   ├─ es/
   │  └─ login.json
   └─ fr/
      └─ login.json

⚙️ Configuration

The i18nizer.config.yml file controls all aspects of how i18nizer processes your project. Here's a complete reference:

Complete Configuration Reference

# Framework and i18n library settings
framework: react                    # nextjs | react | custom
i18nLibrary: react-i18next          # next-intl | react-i18next | i18next | custom

# AI provider configuration
ai:
  provider: openai                  # openai | gemini | huggingface
  model: gpt-4                      # AI model to use for translations

# Directory paths
paths:
  src: src                          # Source code directory
  i18n: i18n                        # i18n output directory

# i18n function configuration
i18n:
  function: t                       # Translation function name
  import:
    source: react-i18next           # Package to import from
    named: useTranslation           # Named import

# Translation file settings
messages:
  path: messages                    # Where to store translation JSON files
  defaultLocale: en                 # Default locale
  locales:                          # List of supported locales
    - en
    - es
  format: json                      # Output format (currently only json)

# Behavior settings
behavior:
  detectDuplicates: true            # Reuse keys for duplicate strings across components
  opinionatedStructure: true        # Use opinionated file structure
  autoInjectT: true                 # Auto-inject translation hooks (disable for Next.js Server Components)
  useAiForKeys: true                # Use AI to generate English keys
  allowedFunctions:                 # Functions whose string arguments should be translated
    - alert
    - confirm
    - prompt
  allowedMemberFunctions:           # Member functions whose string arguments should be translated
    - toast.error
    - toast.info
    - toast.success
    - toast.warn
  allowedProps:                     # JSX props that should be translated
    - alt
    - aria-label
    - aria-placeholder
    - helperText
    - label
    - placeholder
    - text
    - title
    - tooltip

Translation Function Injection (autoInjectT)

i18nizer can automatically inject translation hooks into your components:

const t = useTranslations("ComponentName");

This behavior is controlled by behavior.autoInjectT in i18nizer.config.yml:

Next.js Projects (disabled by default):

behavior:
  autoInjectT: false  # Disabled to avoid breaking Server Components
  • Server Components cannot use hooks like useTranslations
  • i18nizer will replace strings with t("key") but won't inject the hook
  • You manually add the translation hook where appropriate

React Projects (enabled by default):

behavior:
  autoInjectT: true  # Safe for Client Components
  • Full automation: injects hooks and replaces strings
  • Works seamlessly with React components

Why disabled for Next.js?

  • Automatically detecting Server vs Client Components is ambiguous
  • Injecting hooks in Server Components causes runtime errors
  • User has full control over translation function placement

You can override this setting in your i18nizer.config.yml if you know your setup.

AI-Powered English Key Generation (useAiForKeys)

i18nizer uses AI to generate English camelCase keys regardless of your source language:

behavior:
  useAiForKeys: true  # Default: enabled

Benefits:

  • Consistent keys: Keys are always in English, even if your source text is in Spanish, French, German, etc.
  • Readable: welcomeBack instead of bienvenidoDeNuevo
  • Stable: Keys are cached per source text for deterministic behavior across runs
  • Minimal diffs: Same source text always produces the same key

How it works:

  1. First run: AI generates an English key for each source text
  2. Key is cached with the source text hash
  3. Subsequent runs: Cached key is reused (no AI call needed)
  4. Fallback: If AI is unavailable, uses deterministic camelCase generation

Example:

Source text (Spanish):

<h1>Bienvenido de nuevo</h1>
<button>Iniciar sesión</button>

Generated keys (English):

{
  "welcomeBack": "Bienvenido de nuevo",
  "signIn": "Iniciar sesión"
}

Disabling AI key generation:

If you prefer deterministic key generation based on source text:

behavior:
  useAiForKeys: false

Note: Keys will be in the source language (e.g., bienvenidoDeNuevo for Spanish text).

AI Provider and Model Configuration

i18nizer supports multiple AI providers for translations. You can configure the provider and model in i18nizer.config.yml:

ai:
  provider: openai        # openai | gemini | huggingface
  model: gpt-4           # AI model name

Supported Providers:

  • OpenAI (default): Uses OpenAI API with models like gpt-4, gpt-4o-mini, etc.
  • Google Gemini: Uses Google's Gemini API with models like gemini-2.5-flash, gemini-pro
  • Hugging Face: Uses Hugging Face Inference API with models like deepseek-ai/DeepSeek-V3.2

Default Configuration:

ai:
  provider: openai
  model: gpt-4

Example Configurations:

For Google Gemini:

ai:
  provider: gemini
  model: gemini-2.5-flash

For Hugging Face:

ai:
  provider: huggingface
  model: deepseek-ai/DeepSeek-V3.2

Note: You still need to set up API keys using:

i18nizer keys --setOpenAI <YOUR_OPENAI_API_KEY>
i18nizer keys --setGemini <YOUR_GEMINI_API_KEY>
i18nizer keys --setHF <YOUR_HUGGING_FACE_API_KEY>

The provider setting in config file will be used by default, but you can override it on a per-command basis using the --provider flag:

i18nizer translate <file> --provider gemini

Paths Configuration

Configure default paths for your source code and i18n files in i18nizer.config.yml:

paths:
  src: src               # Source directory
  i18n: i18n             # i18n output directory

Default Configuration:

paths:
  src: src
  i18n: i18n

Custom Example:

paths:
  src: source
  i18n: locales

These paths serve as defaults and can help organize your project structure. The messages.path setting (which specifies where translation JSON files are stored) is separate from paths.i18n.


✨ Features

Phase 1 (Current)

  • Project-level integration with i18nizer start and i18nizer translate
  • Configuration system with i18nizer.config.yml
  • Framework presets (Next.js + next-intl, React + react-i18next)
  • Intelligent caching to avoid redundant AI translation requests
  • String deduplication with deterministic key reuse
  • AI-powered English key generation for consistent, readable keys regardless of source language
  • Configurable behavior (allowed functions, props, member functions)
  • Dry-run mode to preview changes
  • JSON output preview with --show-json
  • Pluralization documentation - comprehensive guide for ICU message format
  • Rich text formatting documentation - patterns for JSX within translations
  • Project-wide or single-file translation
  • Works with JSX & TSX
  • Rewrites components automatically (t("key"))
  • Generates English camelCase keys (AI-assisted with deterministic fallback)
  • Supports any number of locales
  • Isolated TypeScript parsing (no project tsconfig required)
  • Friendly logs with colors and spinners

Supported Extraction Cases

  • JSX text children: <div>Hello</div>
  • JSX attributes: placeholder, title, alt, aria-label, aria-placeholder, label, text, tooltip, helperText
  • String literals: placeholder="Enter name"
  • Curly-braced strings: placeholder={"Enter name"}
  • Template literals: placeholder={`Enter name`}
  • Template literals with placeholders: <p>{`Hello ${name}`}</p>
  • Ternary operators: placeholder={condition ? "Text A" : "Text B"}
  • Logical AND: {condition && "Visible text"}
  • Logical OR: {condition || "Fallback text"}
  • Nested expressions: Complex combinations of the above

Filtering & Quality

  • Skips non-translatable content: Single symbols (*, |), punctuation-only strings (...), whitespace
  • Deterministic key generation: Same input always produces the same key
  • Stable JSON output: Alphabetically sorted keys, consistent formatting
  • 2-space indentation: Clean and diff-friendly JSON files

🎨 Advanced i18n Patterns

i18nizer extracts translatable strings and generates standard i18n JSON files. For advanced patterns like pluralization and rich text formatting, you can leverage the built-in features of i18next and next-intl.

Pluralization Support

Both i18next and next-intl support ICU message format for handling plurals. After i18nizer extracts your strings, you can enhance them with plural rules.

Before i18nizer:

<p>{count} {count === 1 ? 'item' : 'items'} in cart</p>

After i18nizer extraction:

<p>{t('itemCount', { count })} {t('itemsLabel', { count })}</p>

Manual enhancement to use pluralization:

<p>{t('itemsInCart', { count })}</p>

Translation with ICU plural format:

{
  "itemsInCart": "{count, plural, =0 {No items} one {# item} other {# items}} in cart"
}

How plurals work:

  • =0 - Exact match for zero
  • one - Singular form (1 item in English)
  • other - Plural form (2+ items)
  • # - Placeholder for the count number

Additional plural categories (language-dependent):

  • zero, one, two, few, many, other

Rich Text Formatting

When you need JSX elements within translated text (like links or bold text), both i18next and next-intl provide rich text formatting capabilities.

Before i18nizer:

<p>By clicking Sign Up, you agree to our <a href="/terms">Terms of Service</a></p>

After i18nizer extraction:

<p>{t('byClickingSignUpYouAgree')} <a href="/terms">{t('termsOfService')}</a></p>

Manual enhancement with rich text (next-intl):

<p>{t.rich('signUpAgreement', {
  terms: (chunks) => <a href="/terms">{chunks}</a>
})}</p>

Translation:

{
  "signUpAgreement": "By clicking Sign Up, you agree to our <terms>Terms of Service</terms>"
}

Using rich text with i18next:

import { Trans } from 'react-i18next';

<p>
  <Trans i18nKey="signUpAgreement">
    By clicking Sign Up, you agree to our <a href="/terms">Terms of Service</a>
  </Trans>
</p>

Translation:

{
  "signUpAgreement": "By clicking Sign Up, you agree to our <1>Terms of Service</1>"
}

More Examples

Date and time formatting:

// Using next-intl
<p>{t('lastUpdated', { date: new Date() })}</p>

// Translation
{
  "lastUpdated": "Last updated: {date, date, medium}"
}

Number formatting:

// Using next-intl
<p>{t('price', { amount: 99.99 })}</p>

// Translation
{
  "price": "{amount, number, currency}"
}

Nested plurals:

<p>{t('cartSummary', { itemCount: 5, totalPrice: 149.99 })}</p>

// Translation
{
  "cartSummary": "{itemCount, plural, one {# item} other {# items}} • Total: ${totalPrice}"
}

🔮 Roadmap

✅ Phase 0: Foundation & Reliability (Complete)

  • Stable extraction and replacement
  • Deterministic key generation
  • Comprehensive test coverage
  • JSON output quality

✅ Phase 1: Project Integration (Complete)

  • i18nizer start command for project initialization
  • i18nizer translate command with --all flag
  • Configuration system with YAML
  • Framework detection and presets
  • Intelligent caching system
  • Cross-file string deduplication
  • Configurable behavior (allowed props, functions, etc.)
  • Dry-run and JSON preview modes

🚧 Phase 2: Advanced Features (Planned)

  • Automatic pluralization detection and conversion
  • Automatic rich text formatting detection
  • Watch mode for continuous translation
  • Non-AI fallback mode
  • Framework support (Vue, Svelte)
  • Additional i18n library presets
  • Context-aware translations
  • Translation memory and glossary

⚠️ Current Limitations

  • AI-generated keys may vary between runs (deterministic fallback available)
  • Cached keys minimize diffs across runs.
  • Only supports React JSX/TSX (no Vue, Svelte yet)
  • Does not handle runtime-only string generation

⚠️ Notes

  • API keys are never committed
  • JSON files are stored in /{HOME}/.i18nizer/
  • Designed for incremental adoption

Made with ❤️ by Yoannis Sánchez Soto

About

Developer-first CLI that extracts translatable strings from JSX/TSX files, automatically generates i18n JSON files, and rewrites your components to use t().

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages