Runtime SDKs

react-intl

@transglot/react-intl: load a published bundle as a react-intl messages map, with full ICU MessageFormat handled by FormatJS.

@transglot/react-intl is a loader, not a provider. It fetches one locale's published bundle and returns it as the flat { id: message } map that react-intl's IntlProvider and createIntl take. FormatJS does all the formatting, so your FormattedMessage and formatMessage call sites keep working and you get full ICU MessageFormat: plural, select, selectordinal, number, date and time arguments, and rich-text tags.

It uses @transglot/runtime for the request, so it shares its authentication, rate-limit retries and error types.

Install

npm install @transglot/react-intl react-intl

react-intl 6 or later is a peer dependency. @transglot/runtime is installed with the loader.

Minimal setup

src/main.tsx
import { loadMessages } from '@transglot/react-intl';
import { createRoot } from 'react-dom/client';
import { IntlProvider } from 'react-intl';
import { App } from './App';

const { locale, messages } = await loadMessages({
  baseUrl: 'https://api.transglot.ai',
  project: 42,
  cdnKey: 'taicdn_your_key',
  locale: 'fr',
  authIn: 'query',
});

createRoot(document.getElementById('root')!).render(
  <IntlProvider locale={locale} messages={messages} defaultLocale="en">
    <App />
  </IntlProvider>,
);
src/App.tsx
import { FormattedMessage, useIntl } from 'react-intl';

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

  return (
    <main>
      <h1>{intl.formatMessage({ id: 'home.title' })}</h1>
      <FormattedMessage id="cart.items" values={{ count: 3 }} />
    </main>
  );
}

loadMessages runs anywhere fetch does: a Server Component, getServerSideProps, a route handler, or the browser. In a browser, pass authIn: 'query'; the CDN does not answer the CORS preflight that the header would trigger (see In a browser). Each call makes a fresh request and keeps no cache between calls, so it is not affected by the revalidation limit described there.

How bundles become messages

The loader reshapes the bundle; it does not touch the strings:

  • Nested objects (json_nested) are flattened to dotted ids, so { "home": { "title": "..." } } becomes home.title.
  • Flat keys (json_flat, laravel_json) are kept as they are.
  • Top-level @-prefixed members (ARB metadata in flutter_arb) are dropped.
  • Non-string values are converted with String(), and null values are skipped.

Any of the four JSON-family delivery formats works. Plural keys you manage as plurals in transglot are only published in flutter_arb, where they arrive as ICU plural messages ({count, plural, one {...} other {...}}) that FormatJS formats. ICU strings stored as ordinary values reach FormatJS unchanged in every JSON format.

If your project's Message syntax (project Settings → Checks) is ICU, a translation that is not valid ICU MessageFormat is left out of the published bundle rather than served.

Missing messages

A published bundle holds only the keys translated in that locale. For an id that is not in messages, react-intl renders the defaultMessage (or the id) and reports a missing-translation error through onError. To fall back to your source text instead, load the source locale too and merge it underneath:

const [source, target] = await Promise.all([
  loadMessages({ baseUrl, project, cdnKey, locale: 'en', authIn: 'query' }),
  loadMessages({ baseUrl, project, cdnKey, locale: 'fr', authIn: 'query' }),
]);

const messages = { ...source.messages, ...target.messages };

The source locale must be published too.

API

loadMessages(options)

loadMessages(options: LoadMessagesOptions): Promise<ReactIntlBundle>

Prop

Type

ReactIntlBundle:

Prop

Type

bundleToMessages(rawBody)

bundleToMessages(rawBody: string): IntlMessages turns a bundle body into a messages map, as loadMessages does. Use it when you fetch the bundle yourself, for example through your own cache.

Types: LoadMessagesOptions, ReactIntlBundle, IntlMessages ({ readonly [id: string]: string }).

Errors

loadMessages rejects with the runtime's RuntimeError, which the package re-exports. The slugs are listed on the JavaScript runtime page. bundleToMessages throws a RuntimeError with the slug unsupported-format when the body is not a JSON object.

import { RuntimeError, loadMessages } from '@transglot/react-intl';

try {
  bundle = await loadMessages({ baseUrl, project, cdnKey, locale, authIn: 'query' });
} catch (error) {
  if (error instanceof RuntimeError && error.slug === 'not-published') {
    bundle = { locale: 'en', messages: {} };
  } else {
    throw error;
  }
}

On this page