mf2react
v0.1.12
Published
MessageFormat v2 postProcessor for i18next/react-i18next
Downloads
29
Keywords
Readme
mf2react
This guide explains how to localize React applications with MessageFormat 2
(MF2), using the mf2react package.
The library builds ontop of i18next and react-i18next, popular internationalization frameworks for JavaScript and React.
The package contains a post-processor plugin for i18next that compiles MF2
messages and converts lightweight curly-tag markup into safe HTML tags, which
react-i18next can render as JSX. For example the message
{#bold}Hello{/bold},
{$name}! becomes <strong>Hello</strong>, {$name}! when
rendered.
MF2 features such as pluralization, select, and conditional logic are fully supported. For example, the following MF2 message:
.match {$count: number}
one {{You have {$count} message}}
* {{You have {$count} messages}}Can be used in a React component like this:
import { Trans } from "react-i18next";
export default function MessagesComponent({ count }: { count: number }) {
return <Trans i18nKey="messages" values={{ count }} />;
}Introduction
This guide assumes you have a basic understanding of React and i18next / react-i18next.
Installation and setup
In an existing React project, install the mf2react package, along with the
i18next, and react-i18next dependencies:
npm install mf2react i18next react-i18nextYou can also use a different package manager, such as yarn, pnpm, or deno
to install the packages.
Defining your catalogs (translations)
Create JSON files for each locale you want to support. For example, create a
locales/en/translation.json file for English translations:
{
"welcome": "Welcome to our application!",
"goodbye": "Goodbye!",
"greeting": "Hello, {$name}!",
"apples": ".input {$value :number}\n.match $value\none {{{#bold}1{/bold} apple}}\n* {{{#bold}{$value}{/bold} apples}}"
}And a locales/no/translation.json file for Norwegian translations:
{
"welcome": "Velkommen til vår applikasjon!",
"goodbye": "Ha det!",
"greeting": "Hei, {$name}!",
"apples": ".input {$value :number}\n.match $value\none {{{#bold}1{/bold} eple}}\n* {{{#bold}{$value}{/bold} epler}}"
}Setting up i18next
Create a i18n.ts file in your project to configure i18next.
// i18n.ts
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import { MF2PostProcessor, MF2ReactPreset } from "mf2react";
import en from "./locales/en/translation.json";
import no from "./locales/no/translation.json";
if (!i18n.isInitialized) {
i18n
.use(MF2PostProcessor) // Enable the post-processor
.use(MF2ReactPreset) // Enable curly-tag -> JSX conversion
.use(initReactI18next)
.init({
lng: "en",
postProcess: ["mf2"], // Apply MF2 to all translations
resources: {
// Reference the translation files
en: { translation: en },
no: { translation: no },
},
});
}
export default i18n;Instead of defining the selected locale (
lng) andresourcesdirectly in theinitfunction, you may also choose to load them dynamically, e.g. via i18next-http-backend, i18next-resources-to-backend. Additionally you may want to auto-detect the user's locale using i18next-browser-languagedetector.
Wrapping your application with I18nextProvider
To use translations in your React components, you need to wrap your application
with the I18nextProvider from react-i18next. This is typically done in your
main application file or layout component.
"use client";
import { I18nextProvider } from "react-i18next";
import { i18n } from "./i18n";
export default function AppLayout({ children }: { children: React.ReactNode }) {
return <I18nextProvider i18n={i18n}>{children}</I18nextProvider>;
}Good to know: because
I18nextProvideruses React context, it can only be used in a client component.
Choosing where to place the provider
Where you place the provider affects what becomes client-rendered. You may
either wrap your whole application in the provider, or each component that uses
translations. This is because I18nextProvider must be used in a client
component. Choose the placement based on how much of your UI should be
client-rendered.
Using translations in components
Now you can use the <Trans> component from react-i18next to render
translations in your React components. For example:
import { Trans } from "react-i18next";
export default function WelcomeComponent() {
return (
<div>
<h1>
<Trans i18nKey="welcome" />
</h1>
<p>
<Trans i18nKey="goodbye" />
</p>
</div>
);
}You can read more about the Trans component in the react-i18next documentation
Passing variables to translations
You can also pass variables to your translations using the values prop of the
<Trans> component. For example, if you have a translation that includes a
variable:
{
"greeting": "Hello, {$name}!"
}You can use it in your component like this:
import { Trans } from "react-i18next";
export default function GreetingComponent({ name }: { name: string }) {
return <Trans i18nKey="greeting" values={{ name }} />;
}This also works for MF2 messages with pluralization and formatting:
import { Trans } from "react-i18next";
export default function ApplesComponent({ count }: { count: number }) {
return <Trans i18nKey="apples" values={{ value: count }} />;
}Output when
countis 1: 1 appleOutput when
countis 5: 5 apples
Markup with curly-tags
You can use curly-tags in your translations to add formatting. For example, in your translation file:
{
"bold": "This is {#bold}bold text{/bold}."
}You can render it in your component like this:
import { Trans } from "react-i18next";
export default function BoldComponent() {
return <Trans i18nKey="bold" />;
}Output: This is bold text.
This works because the mf2react post-processor converts the curly-tags into
safe HTML tags, which react-i18next can render as JSX. The following tags are
supported:
{#bold}…{/bold}
{#strong}…{/strong}
{#i}…{/i}
{#em}…{/em}
{#u}…{/u}
{#s}…{/s}
{#small}…{/small}
{#code}…{/code}Notes and limitations
- i18next post-processors must return strings. The JSX conversion happens
inside
<Trans>. - Messages are compiled and cached per language for performance.
- The curly-tag conversion is intentionally minimal and safe. It only recognizes tags defined in the alias list.
- If you switch languages at runtime, the plugin automatically reuses or recompiles as needed.
- Unsupported MF2 syntax will fall back gracefully to raw string + curly tag conversion.
Acknowledgements
- i18next - the internalization framework
- react-i18next - React bindings for i18next
- @messageformat/core - MessageFormat2 engine used for compiling and evaluating MF2 syntax.
