Mmohebifar avatar

i18n

I18nize your codebase

mohebifar70 executions

  • transformation
  • migration

i18n-pro-codemod

Automatically extract hardcoded strings and internationalize your React codebase

This pro codemod intelligently identifies hardcoded text in your React components and transforms them into properly internationalized code using modern i18n libraries like next-intl, react-i18next, or react-intl.

🚀 Features

  • AI-powered: Uses advanced AI to generate meaningful translation keys and handle complex string patterns
  • Multi-library support: Works with next-intl, react-i18next, and react-intl
  • Smart Sharding: Handles large codebases by intelligently splitting work across teams/directories
  • Automated PR creation: Creates pull requests with proper reviews and formatting
  • Message catalog management: Automatically creates and updates translation files
  • Context-aware: Preserves code structure and handles complex JSX patterns

🔧 How It Works

The codemod scans your React/TypeScript files for hardcoded strings in JSX elements and transforms them into internationalized code:

Before

tsx
import React from "react";

export function MyComponent() {
  return (
    <div>
      <h1>Welcome to our app</h1>
      <p>Please sign in to continue</p>
      <button>Get Started</button>
    </div>
  );
}

After (next-intl)

tsx
import { useTranslations } from "next-intl";
import React from "react";

export function MyComponent() {
  const t = useTranslations();

  return (
    <div>
      <h1>{t("welcome_title")}</h1>
      <p>{t("sign_in_prompt")}</p>
      <button>{t("get_started_button")}</button>
    </div>
  );
}

After (react-intl)

tsx
import { useIntl, FormattedMessage } from "react-intl";
import React from "react";

export function MyComponent() {
  const intl = useIntl();

  return (
    <div>
      <h1>
        <FormattedMessage id="welcome_title" defaultMessage="" />
      </h1>
      <p>
        <FormattedMessage id="sign_in_prompt" defaultMessage="" />
      </p>
      <button>
        <FormattedMessage id="get_started_button" defaultMessage="" />
      </button>
    </div>
  );
}

Generated Message Catalog (messages/en.json)

json
{
  "welcome_title": "Welcome to our app",
  "sign_in_prompt": "Please sign in to continue",
  "get_started_button": "Get Started"
}

🔥 Advanced Interpolation Examples

The codemod handles complex scenarios with ICU syntax, including variables, pluralization, and rich text:

Variable Interpolation (next-intl)

Before:

tsx
export function UserProfile({ user, count }) {
  return (
    <div>
      <h1>Welcome back, {user.name}!</h1>
      <p>You have {count} new messages</p>
      <span>Last login: {formatDate(user.lastLogin)}</span>
    </div>
  );
}

After:

tsx
import { useTranslations } from "next-intl";

export function UserProfile({ user, count }) {
  const t = useTranslations();

  return (
    <div>
      <h1>{t("welcome_back", { name: user.name })}</h1>
      <p>{t("message_count", { count })}</p>
      <span>{t("last_login", { date: user.lastLogin })}</span>
    </div>
  );
}

Generated Catalog:

json
{
  "welcome_back": "Welcome back, {name}!",
  "message_count": "{count, plural, =0 {You have no new messages} =1 {You have # new message} other {You have # new messages}}",
  "last_login": "Last login: {date, date, medium}"
}

Variable Interpolation (react-intl)

tsx
import { useIntl, FormattedMessage } from "react-intl";

export function UserProfile({ user, count }) {
  const intl = useIntl();

  return (
    <div>
      <h1>
        <FormattedMessage
          id="welcome_back"
          defaultMessage=""
          values={{ name: user.name }}
        />
      </h1>
      <p>
        <FormattedMessage
          id="message_count"
          defaultMessage=""
          values={{ count }}
        />
      </p>
      <span>
        <FormattedMessage
          id="last_login"
          defaultMessage=""
          values={{ date: user.lastLogin }}
        />
      </span>
    </div>
  );
}

Complex Conditional Logic

Before:

tsx
export function OrderStatus({ status, items, total, user }) {
  return (
    <div>
      <h2>
        {status === "pending"
          ? "Your order is being processed"
          : status === "shipped"
            ? "Your order is on the way!"
            : "Order delivered"}
      </h2>
      <p>
        {user.isPremium
          ? `Hi ${user.name}, your ${items.length} premium items (${total}) will arrive tomorrow`
          : `Hello ${user.name}, your ${items.length} items totaling ${total} will arrive in 3-5 days`}
      </p>
    </div>
  );
}

After:

tsx
import { useTranslations } from "next-intl";

export function OrderStatus({ status, items, total, user }) {
  const t = useTranslations();

  return (
    <div>
      <h2>{t("order_status", { status })}</h2>
      <p>
        {t("delivery_info", {
          isPremium: user.isPremium,
          name: user.name,
          itemCount: items.length,
          total,
        })}
      </p>
    </div>
  );
}

Generated Catalog:

json
{
  "order_status": "{status, select, pending {Your order is being processed} shipped {Your order is on the way!} other {Order delivered}}",
  "delivery_info": "{isPremium, select, true {Hi {name}, your {itemCount, plural, =1 {# premium item} other {# premium items}} ({total, number, currency}) will arrive tomorrow} other {Hello {name}, your {itemCount, plural, =1 {# item} other {# items}} totaling {total, number, currency} will arrive in 3-5 days}}"
}

Rich Text with Components (next-intl)

Before:

tsx
export function PolicyNotice({ companyName }) {
  return (
    <div>
      <p>
        By clicking "Accept", you agree to our{" "}
        <Link href="/terms">Terms of Service</Link> and{" "}
        <Link href="/privacy">Privacy Policy</Link>.
      </p>
      <p>
        Questions? <strong>Contact {companyName} support</strong> for help.
      </p>
    </div>
  );
}

After:

tsx
import { useTranslations } from "next-intl";

export function PolicyNotice({ companyName }) {
  const t = useTranslations();

  return (
    <div>
      <p>
        {t.rich("policy_agreement", {
          termsLink: (chunks) => <Link href="/terms">{chunks}</Link>,
          privacyLink: (chunks) => <Link href="/privacy">{chunks}</Link>,
        })}
      </p>
      <p>
        {t.rich("support_contact", {
          companyName,
          strong: (chunks) => <strong>{chunks}</strong>,
        })}
      </p>
    </div>
  );
}

Generated Catalog:

json
{
  "policy_agreement": "By clicking \"Accept\", you agree to our <termsLink>Terms of Service</termsLink> and <privacyLink>Privacy Policy</privacyLink>.",
  "support_contact": "Questions? <strong>Contact {companyName} support</strong> for help."
}

Rich Text with Components (react-intl)

The same ICU messages above can be rendered with react-intl like this:

tsx
import { useIntl, FormattedMessage } from "react-intl";

export function PolicyNotice({ companyName }) {
  const intl = useIntl();

  return (
    <div>
      <p>
        <FormattedMessage
          id="policy_agreement"
          defaultMessage=""
          values={{
            termsLink: (chunks) => <Link href="/terms">{chunks}</Link>,
            privacyLink: (chunks) => <Link href="/privacy">{chunks}</Link>,
          }}
        />
      </p>
      <p>
        <FormattedMessage
          id="support_contact"
          defaultMessage=""
          values={{
            companyName,
            strong: (chunks) => <strong>{chunks}</strong>,
          }}
        />
      </p>
    </div>
  );
}

🎯 What Gets Transformed

  • Hardcoded text in JSX elements (<div>text</div>)
  • String literals in JSX attributes
  • Complex nested text structures
  • Preserves existing component logic and structure

⚙️ Configuration Options

  • Library: Choose between next-intl, react-i18next, or react-intl
  • Sharding Method: Process large codebases by codeowner or directory
  • Catalog File Selection: Default structure or custom Zap package organization
  • AI Review: Automated code review and quality checks
  • Custom Scripts: Pre/post-run hooks for additional processing

💼 Professional Version

This is a professional codemod that requires a license for commercial use.

To access this codemod, please contact us for:

  • Enterprise licensing
  • Custom configuration support
  • Priority support and updates
  • Team training and onboarding

Ready to evolve your codebase?

Build tailored, compiler-aware skills to assess, plan, and automate complex codebase changes.