-
Notifications
You must be signed in to change notification settings - Fork 77
React Bindings
@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.
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.
@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:
- negotiates the best language for the user; you can use
@fluent/langneg
for this, - fetches the translation files; you can bundle some translations with the app for sync access or load them asynchronously,
- creates an iterable of
FluentBundle
instances;FluentBundle
is a class responsible for parsing and formatting translations; it is provided by the@fluent/bundle
package. This iterable then needs to be used to instantiate aReactLocalization
, which is passed as thel10n
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/bundle @fluent/langneg @fluent/react intl-pluralrules
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.
All <Localized>
components need access to a translation store to which they subscribe. The LocalizationProvider component implements the provider pattern to make translations available to all localized elements in the app without passing them explicitly. It expects a single prop, l10n
, which should be an instance of ReactLocalization
holding the sequence of FluentBundles
in order of the user's language preference.
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).
import 'intl-polyfill';
import { negotiateLanguages } from '@fluent/langneg';
import { FluentBundle, FluentResource } from '@fluent/bundle';
import { LocalizationProvider, ReactLocalization } from '@fluent/react';
// Store all translations as a simple object which is available
// synchronously and bundled with the rest of the code.
const RESOURCES = {
'fr': new FluentResource('hello = Salut le monde !'),
'en-US': new FluentResource('hello = Hello, world!'),
'pl': new FluentResource('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.addResource(RESOURCES[locale]);
yield bundle;
}
}
// The ReactLocalization instance stores and caches the sequence of generated
// bundles. You can store it in your app's state.
let l10n = new ReactLocalization(generateBundles(navigator.languages));
ReactDOM.render(
<LocalizationProvider l10n={l10n}>
<div>
<Localized id="hello">
<h1>Hello, world!</h1>
</Localized>
</div>
</LocalizationProvider>,
document.getElementById('root')
);
Consult the fluent-react/example
directory for a working example of a React app using `@fluent/react.