# General Translation Knowledge Base: General Translation vs. react-intl: which should you use?
URL: https://generaltranslation.com/en-US/kb/general-translation-vs-react-intl.mdx
Docs index: https://generaltranslation.com/llms.txt
Description: General Translation lets React teams keep ICU message syntax while adding translation generation, shared terminology, and Dashboard review. react-intl supplies formatting and FormatJS catalog tooling; General Translation produces the translations too, and gt-next supports inline translated JSX in Server Components.
Category: Internationalization
Last updated: 2026-10-16
Authors: Kevin Wu, Jackie Chen
Published: 2026-10-16

react-intl is the [React library from FormatJS](https://formatjs.github.io/docs/react-intl). You write each message inline as a `defaultMessage` in ICU MessageFormat, add a `description` for translators, collect every message into a JSON catalog with `formatjs extract`, and render translated catalogs through `IntlProvider`. It doesn't translate anything: the extracted catalog goes to you, your translators, or a translation management system (TMS), and translated files come back to be compiled. General Translation's libraries render ICU messages too, and General Translation also generates the translations.

## What react-intl does well

- **ICU MessageFormat throughout.** [Plurals with `#` and exact matches such as `=0`, `select`, and `selectordinal`](https://formatjs.github.io/docs/core-concepts/icu-syntax), built on the browser's `Intl` APIs.
- **Inline source text.** FormatJS [recommends writing `defaultMessage` and `description` inline](https://formatjs.github.io/docs/getting-started/message-declaration) and letting `babel-plugin-formatjs` or `@swc/plugin-formatjs` generate message IDs, so nobody invents keys.
- **Tooling for a TMS workflow.** [`formatjs extract`](https://formatjs.github.io/docs/tooling/cli) writes the catalog, built-in formatters produce files for Crowdin, Lokalise, Smartling, and Transifex, `formatjs compile` pre-parses messages, and `formatjs verify` checks catalogs for missing or extra keys.
- **TypeScript.** [Version 12 can type each message's ICU arguments](https://formatjs.github.io/docs/react-intl/upgrade-guide-12.x).
- **Active maintenance.** [Version 12 shipped in September 2026](https://github.com/formatjs/formatjs/blob/main/packages/react-intl/CHANGELOG.md), under the BSD-3-Clause license.

## What react-intl leaves to you

Here's a small inbox with react-intl. The English stays in the component as `defaultMessage`:

```jsx title="Inbox.jsx"
import { FormattedMessage } from 'react-intl';

export function Inbox({ count }) {
  return (
    <section>
      <h1>
        <FormattedMessage defaultMessage="Inbox" description="Heading of the inbox page" />
      </h1>
      <p>
        <FormattedMessage
          defaultMessage="You have {count, plural, one {# unread message} other {# unread messages}}."
          description="Number of unread messages in the inbox"
          values={{ count }}
        />
      </p>
    </section>
  );
}
```

`formatjs extract` turns those messages into a catalog keyed by generated IDs:

```json title="lang/en.json"
{
  "U5vJOY": {
    "defaultMessage": "You have {count, plural, one {# unread message} other {# unread messages}}.",
    "description": "Number of unread messages in the inbox"
  },
  "gBGgGs": {
    "defaultMessage": "Inbox",
    "description": "Heading of the inbox page"
  }
}
```

Each target language needs translated messages with their ICU syntax intact. `IntlProvider` accepts message strings or precompiled messages; `formatjs compile` prepares the latter. The extracted descriptors carry each message and its description. For the generated IDs in this example, configure the Babel or SWC plugin in your build. In the [Next.js App Router](https://formatjs.github.io/docs/guides/nextjs-app-router), react-intl's components run in client components while server code formats messages through `createIntl` from `react-intl/server`.

## Keep ICU messages and generate the translations

General Translation's libraries do the same job as react-intl: they render ICU messages, apply each locale's plural rules, and format numbers, currencies, and dates. Your ICU strings move almost unchanged, because [`useGT`](/docs/react/reference/hooks/use-gt) takes the same syntax:

```tsx title="Inbox.tsx"
import { useGT } from 'gt-react';

export function Inbox({ count }: { count: number }) {
  const gt = useGT();
  return (
    <section>
      <h1>{gt('Inbox', { $context: 'Heading of the inbox page' })}</h1>
      <p>
        {gt('You have {count, plural, one {# unread message} other {# unread messages}}.', {
          count,
          $context: 'Number of unread messages in the inbox',
        })}
      </p>
    </section>
  );
}
```

[`npx gt translate`](/docs/cli/reference/commands/translate) extracts these strings, generates translations using their context notes, and writes the target catalogs in one command. The example keeps both FormatJS descriptions as `$context`. Shared [Glossary terms and Custom Prompts](/docs/platform/dashboard/guides/defining-context-for-translations) guide terminology and tone, and reviewers edit results in the [Dashboard](/docs/platform/dashboard/guides/reviewing-translations).

The `#` is formatted for each locale, so a count of 12345 reads "12,345" in English and "12.345" in Spanish. For JSX with markup, [`<T>`](/docs/react/reference/components/t) translates the whole marked block, elements included. In `gt-next`, it works directly in Server Components.

Ramp's dashboard, landing page, and iOS app ship in 11+ locales with General Translation. (See [production deployments](https://generaltranslation.com/enterprise).)

## react-intl vs. General Translation at a glance

| Feature | react-intl | General Translation |
| --- | --- | --- |
| How you write copy | Inline `defaultMessage` in ICU, extracted to a JSON catalog | Inline JSX in [`<T>`](/docs/react/reference/components/t), or ICU strings in [`useGT`](/docs/react/reference/hooks/use-gt) |
| Translates your content | No | Yes, with [`npx gt translate`](/docs/cli/reference/commands/translate) |
| Catalog workflow | Extract messages, translate through your chosen workflow, optionally precompile; plugin generates IDs | [`npx gt translate`](/docs/cli/reference/commands/translate) extracts and translates before your build |
| Translation context | The message and its description | ICU strings with `$context`; marked JSX structure with inline components; shared Glossary and Custom Prompts |
| Next.js App Router | Components in client components; `createIntl` on the server | [`<T>`](/docs/react/reference/components/t) in Server and Client Components |
| License | [BSD-3-Clause](https://github.com/formatjs/formatjs/blob/main/packages/react-intl/LICENSE.md) | MIT-licensed JavaScript libraries and CLI |

Keep react-intl when your ICU catalogs and TMS formatters already work, when you rely on its typed message arguments or `formatjs verify`, or when you use FormatJS outside React. Choose General Translation to keep ICU authoring while making translation generation and review part of your development workflow.

## Moving from react-intl

Move one component or route at a time. Replace each `<FormattedMessage>` with a [`useGT`](/docs/react/reference/hooks/use-gt) call that keeps its ICU string and passes its description as `$context`, as shown above. For rich JSX, use [`<T context="…">`](/docs/react/reference/components/t#context). Keep loading existing react-intl catalogs for components you have not moved. Once the last component has moved, remove the FormatJS plugin and the extract and compile scripts. (See [Migrating i18n libraries](/docs/react/guides/migrating-i18n-libraries).)

## Key takeaways

- react-intl formats ICU messages and supplies catalog tooling; translation generation comes from a separate workflow.
- General Translation extracts and translates in one CLI command, preserving description context and giving reviewers a Dashboard editor.
- Your ICU strings move almost unchanged into [`useGT`](/docs/react/reference/hooks/use-gt), one component at a time.

## Related questions

- [General Translation vs. i18next, react-intl, and next-intl: which i18n library should you use?](https://generaltranslation.com/en-US/kb/general-translation-vs-i18n-libraries.mdx): Open-source libraries such as i18next, react-intl, and next-intl don't translate anything. They display translations you supply in a catalog file for each language, written by you, your translators, or a separate service. General Translation's libraries are open source too and do the same job, and General Translation also does the translating, with your code as context.
- [General Translation vs. i18next: which should you use?](https://generaltranslation.com/en-US/kb/general-translation-vs-i18next.mdx): Choose General Translation to keep copy inline in React components and generate translations with its JSX structure and product context. You can also keep i18next and use General Translation to translate existing catalogs. i18next supplies message rendering and plural selection; translation generation comes from a separate service.
- [General Translation vs. Lingui: which should you use?](https://generaltranslation.com/en-US/kb/general-translation-vs-lingui.mdx): Choose General Translation to keep copy inline like Lingui and generate the translations with your JSX structure as context. Lingui extracts inline messages into catalogs for your translators or a connected service to translate. General Translation's CLI updates translations as your code changes, and your team reviews them in the Dashboard.
- [General Translation vs. vue-i18n: which should you use?](https://generaltranslation.com/en-US/kb/general-translation-vs-vue-i18n.mdx): General Translation lets Vue teams write source copy directly in templates and generate translations with the CLI. Its gt-vue plugin renders that content with locale-aware plural and number formatting. Choose it to remove keyed source-file maintenance; vue-i18n remains a fit for teams using Nuxt's i18n module and an established catalog workflow.
- [How do I handle plurals in React?](https://generaltranslation.com/en-US/kb/how-to-handle-plurals-in-react.mdx): Don't hard-code plural logic with checks like n === 1. A small helper is enough for English-only apps, but multilingual apps need locale-aware plural rules, which an i18n component such as `<Plural>` from gt-react or gt-next handles for every language.

## Previous and next questions

- Previous question: [General Translation vs. Rails I18n: do you need both?](https://generaltranslation.com/en-US/kb/general-translation-vs-rails-i18n.mdx)
- Next question: [General Translation vs. Smartling: which should you use?](https://generaltranslation.com/en-US/kb/general-translation-vs-smartling.mdx)

## Sitemap

See the [Knowledge Base sitemap](https://generaltranslation.com/kb/sitemap.md) for every article.
