Skip to content
Development
Skill

/web-i18n-react-intl

ICU message format internationalization for React — FormattedMessage, useIntl, defineMessages, and the FormatJS extraction workflow. Load when a project imports react-intl.

From plugin
agents-inc-skills
24200 skills
Install
$ npx -y skills add agents-inc/skills --skill web-i18n-react-intl --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/web-i18n-react-intl

Context preview

The summary Claude sees to decide when to auto-load this skill.

ICU message format internationalization for React — FormattedMessage, useIntl, defineMessages, and the FormatJS extraction workflow. Load when a project imports react-intl.

SKILL.md

web-i18n-react-intl.SKILL.md
name: web-i18n-react-intl
description: ICU message format internationalization for React — FormattedMessage, useIntl, defineMessages, and the FormatJS extraction workflow. Load when a project imports react-intl.

React-Intl (FormatJS) Internationalization Patterns

> **Quick Guide:** `FormattedMessage` renders translated text in JSX, `useIntl` returns strings for > attributes and programmatic use, and `defineMessages` produces descriptors the FormatJS CLI can > extract. `IntlProvider` supplies the context, and its `onError` is what separates a missing > translation from a real failure. Every `plural` and `select` needs an `other` branch. Version > boundary: v7.x runs on React 16.6 through 19; v8 and later require React 19.

**Detailed Resources:**

  • [examples/core.md](examples/core.md) — provider setup, FormattedMessage, useIntl, defineMessages, `createIntl`, locale switching, types, lazy loading
  • [examples/formatting.md](examples/formatting.md) — date, time, number, currency, relative time, list and display-name formatting
  • [examples/pluralization.md](examples/pluralization.md) — plural, ordinal, select, nested patterns, per-language categories, ICU escaping
  • [reference.md](reference.md) — decision trees, ICU syntax tables, API tables, anti-pattern code, checklists

---

Which path applies

  • **Text rendered inside JSX** — `FormattedMessage`, including rich text with tag values. Follow

[examples/core.md](examples/core.md).

  • **A string is needed** — an attribute, a document title, a value passed to a third-party

component, or a comparison — `useIntl().formatMessage()`. Also in [examples/core.md](examples/core.md).

  • **Outside a React tree** — a server render or a plain module — `createIntl` from

`@formatjs/intl`, optionally re-supplied through `RawIntlProvider`. In [examples/core.md](examples/core.md).

---

<critical_requirements>

Before writing react-intl code

**Wrap the tree in `IntlProvider` with `locale`, `messages` and `defaultLocale`.** Every `FormattedMessage` and `useIntl` call reads that context, and `defaultLocale` is what a missing translation falls back to instead of surfacing the raw ID.

**Give every `plural` and `select` an `other` branch.** ICU requires it, and a message without one throws when formatted rather than when authored.

**Match the major version to the React version in the project.** v7.x covers React 16.6 through 19; v8 and later dropped everything before React 19.

</critical_requirements>

---

**Auto-detection:** react-intl, FormatJS, FormattedMessage, useIntl, IntlProvider, RawIntlProvider, defineMessages, defineMessage, createIntl, formatMessage, FormattedDate, FormattedNumber, FormattedRelativeTime, ICU message format

**Applies to:**

  • Rendering messages with ICU interpolation, pluralization, select and rich text
  • Formatting dates, numbers, currency, relative time, lists and display names per locale
  • Structuring messages as descriptors so the CLI can extract and compile them
  • Typing message IDs so a typo fails at compile time
  • Loading a locale's messages on demand rather than bundling all of them

**Handled elsewhere:**

  • Where the locale value comes from and how it is persisted — this skill consumes a locale and

settles nothing about detection, routing or storage

  • A framework's own built-in i18n — a framework that resolves locale and messages per request will

do that better than a client-side provider, and this skill does not compete with it

  • Translation vendor workflow — the CLI produces and consumes JSON, and what happens to it in

between is not this skill's concern

  • Rendering and state — components receive messages through context and are otherwise ordinary

---

<philosophy>

ICU Message Format is the point: it is what translation vendors already speak, so a message written in it moves through a professional workflow without a conversion step. Everything else follows. Formatting is delegated to the browser's own `Intl` APIs rather than reimplemented, which is why locale-specific behaviour is correct for locales nobody tested. And the API is deliberately doubled — a component for JSX and a hook for strings — because a `ReactNode` cannot be put in an attribute.

</philosophy>

---

<patterns>

Core patterns

Pattern 1: IntlProvider setup

`onError` is where a missing translation is separated from a real failure, and `defaultRichTextElements` gives `<b>`, `<i>` and `<br>` one definition for the whole app.

<IntlProvider
  locale={locale}
  defaultLocale={DEFAULT_LOCALE}
  messages={messages}
  defaultRichTextElements={DEFAULT_RICH_TEXT_ELEMENTS}
  onError={(err) => {
    if (err.code === "MISSING_TRANSLATION") return;
    throw err;
  }}
>
  {children}
</IntlProvider>

Full code: [examples/core.md](examples/core.md)

Pattern 2: FormattedMessage

For text rendered directly in JSX, including messages carrying ICU syntax.

<FormattedMessage
  id="greeting.unread"
  defaultMessage="{count, plural, =0 {No messages} one {# message} other {# messages}}"
  values={{ count: unreadCount }}
/>

It returns a `ReactNode`, so an attribute — `placeholder`, `aria-label`, `title` — needs Pattern 3 instead.

Full code: [examples/core.md](examples/core.md)

Pattern 3: useIntl

For any context that needs a string: attributes, document titles, third-party props, or a value the code then compares.

const intl = useIntl();

const placeholder = intl.formatMessage({
  id: "search.placeholder",
  defaultMessage: "Search products...",
});

Full code: [examples/core.md](examples/core.md)

Pattern 4: defineMessages

Descriptors the CLI can find statically. `description` is the only channel a translator has for context.

export const productMessages = defineMessages({
  reviewCount: {
    id: "product.reviewCount",
    defaultMessage:
      "{count, plural, =0 {No reviews} one {# review} other {# reviews}}",
    description: "Number of product re
Read more
Ships withagents-inc-skills

The official skills marketplace for Agents Inc. 150+ skills covering everything from React and Prisma to Redis, ElevenLabs, and infrastructure tooling. Pick the skills that match your stack and install them via Claude Code. Need more control?

Get the whole plugin

Other skills on agents-inc-skills.