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-intlreact-intl 6 or later is a peer dependency. @transglot/runtime is installed with the loader.
Minimal setup
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>,
);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": "..." } }becomeshome.title. - Flat keys (
json_flat,laravel_json) are kept as they are. - Top-level
@-prefixed members (ARB metadata influtter_arb) are dropped. - Non-string values are converted with
String(), andnullvalues 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;
}
}Related
i18next backend
@transglot/i18next-backend: an i18next backend plugin that loads your published bundles from the CDN, so existing t() and Trans call sites keep working.
Pseudo-localization
@transglot/pseudo: generate an accented, lengthened pseudo-locale from your source JSON to find hardcoded strings and layout overflow before you translate.