localize.dev

Docs / React

React i18n, automated

Move hardcoded copy in a React app into react-i18next t() calls, with semantic keys, translations and the i18next setup, in one command or one pull request.

How it works

Localize.dev reads every .tsx and .jsx file, finds the copy people see (text, attributes like placeholder and aria-label, strings rendered from ternaries) and rewrites it to t() calls with keys named after what the copy does.

Plurals become one message with every form each language needs, and sentences around links stay whole with <Trans>.

Before and after

The engine’s real output for this file. In a real run, the hosted model names the keys from context.

src/components/Inbox.tsx · before
export function Inbox({ user, count }) {
  return (
    <section>
      <h1>Welcome back, {user.name}</h1>
      <p>{count} {count === 1 ? 'message' : 'messages'} waiting</p>
      <input placeholder="Search messages" />
      <p>Read the <a href="/help">help center</a> first.</p>
    </section>
  );
}
src/components/Inbox.tsx · after
import { useTranslation, Trans } from 'react-i18next';

export function Inbox({ user, count }) {
  const { t } = useTranslation();
  return (
    <section>
      <h1>{t('inbox.welcome_back', { name: user.name })}</h1>
      <p>{t('inbox.messages_waiting', { count })}</p>
      <input placeholder={t('inbox.search_messages')} />
      <p><Trans i18nKey="inbox.read_the_help_center_first" components={{ a: <a href="/help" /> }} /></p>
    </section>
  );
}
locales/en.json
{
  "inbox": {
    "welcome_back": "Welcome back, {{name}}",
    "messages_waiting_one": "{{count}} message waiting",
    "messages_waiting_other": "{{count}} messages waiting",
    "search_messages": "Search messages",
    "read_the_help_center_first": "Read the <a>help center</a> first."
  }
}

What the setup adds

The first run sets up react-i18next if the project doesn’t have it yet:

  • i18next and react-i18next added to package.json (the GitHub Action updates the lockfile too)
  • src/i18n.ts, which loads every locale file and picks the language from a saved choice, then the browser
  • An import of it in your entry file, and a <LanguageSwitcher /> you can place in your header

Run it

The CLI needs Node.js 22 or later.

npm install --save-dev localize-dev
npx localize-dev login

# Preview, then write the changes
npx localize-dev run --dry-run -l es,fr,de
npx localize-dev run -l es,fr,de

# Or open a pull request instead
npx localize-dev pr -l es,fr,de

Or add the GitHub Action and get a pull request on every push. Try it on your own code first in the playground.