Skip to content

React Bindings

Adrian Gaudebert edited this page Oct 17, 2018 · 8 revisions

fluent-react provides React bindings for Project Fluent, a localization framework designed to unleash the expressive power of the natural language.

Together with other fluent-* packages and a little bit of glue code, fluent-react is meant to be a complete yet flexible solution to localizing React apps. fluent-react takes advantage of React's Components system and the virtual DOM. Translations are defined using the Fluent syntax (FTL) and exposed to Localized components via LocalizationProvider.

The Fluent Syntax

FTL is a localization file format used for describing translation resources. FTL stands for Fluent Translation List. FTL is designed to be simple to read, but at the same time allows to represent complex concepts from natural languages like gender, plurals, conjugations, and others. An example FTL file might look like the following:

hello-world = Hello, world!
welcome = Welcome, { $username }.

Learn more about the syntax by reading the Fluent Syntax Guide. An online editor is also available at the Fluent Playground.

Getting Started

fluent-react alone is only responsible for displaying translated components in your React app. For a complete solution, you'll need to write code which:

  1. negotiates the best language for the user; you can use fluent-langneg for this,
  2. fetches the translation files; you can bundle some translations with the app for sync access or load them asynchronously,
  3. creates an iterable of MessageContext instances; MessageContext is a class responsible for parsing and formatting translations; it is provided by the fluent package. This iterable then needs to be passed as the messages prop to the LocalizationProvider.

This setup allows the most flexibility when it comes to storing and loading user language preferences and translations on runtime.

In most of the cases you'll want to install the following packages:

npm install fluent fluent-langneg fluent-intl-polyfill fluent-react

Wrapped components

In principle, fluent-react works by wrapping localizable elements in Localized, a special element which subscribes to a localization store.

<Localized id="hello">
    <p>Hello, world!</p>
</Localized>

This allows the developer to use any DOM elements and other React Components. The id prop should be the unique identifier of the translation. You can also pass arguments to the translation (strings, numbers, dates and React elements). See the documentation of the Localized component for more information and examples.

LocalizationProvider

All <Localized> components need access to a translation store to which they subscribe. The LocalizationProvider component implements the provider pattern known from Redux to make translations available to all localized elements in the app without passing them explicitly. It expects a single prop, messages, which should be an iterable of MessageContext instance in the order of user's language preferences.

Sometimes it's useful to imperatively retrieve a translation rather than rely on the declarative <Localized /> API. Good examples of this are the window.alert and window.confirm functions. It's possible to connect any component to its enclosing LocalizationProvider using the withLocalization higher-order component (HOC).

A complete example

import 'fluent-intl-polyfill';
import negotiateLanguages from 'fluent-langneg';
import { FluentBundle } from 'fluent';
import { LocalizationProvider } from 'fluent-react';

// Store all translations as a simple object which is available 
// synchronously and bundled with the rest of the code.
const MESSAGES_ALL = {
    'fr': 'hello = Salut le monde !',
    'en-US': 'hello = Hello, world!',
    'pl': 'hello = Witaj świecie!'
};

// A generator function responsible for building the sequence 
// of FluentBundle instances in the order of user's language
// preferences.
function* generateBundles(userLocales) {
    // Choose locales that are best for the user.
    const currentLocales = negotiateLanguages(
        userLocales,
        ['fr', 'en-US', 'pl'],
        { defaultLocale: 'en-US' }
    );

    for (const locale of currentLocales) {
        const bundle = new FluentBundle(locale);
        bundle.addMessages(MESSAGES_ALL[locale]);
        yield bundle;
    }
}

ReactDOM.render(
    <LocalizationProvider bundles={generateBundles(navigator.languages)}>
        <div>
            <Localized id="hello">
                <h1>Hello, world!</h1>
            </Localized>
        </div>
    </LocalizationProvider>,
    document.getElementById('root')
);

Consult the fluent-react/examples directory for examples of how to fetch translations asynchronously, change languages or integrate with Redux.