Get Started

Staś Małolepszy edited this page Dec 14, 2017 · 2 revisions

This article is meant for developers looking into integrating Fluent into their codebases and workflows. For an introduction to our syntax, go to the Fluent Syntax Guide.

Fluent is a platform-agnostic localization system. Our initial target platform selection is a good starting point for both, understanding how Fluent integrates into codebases and workflows, and how it should be ported to other platforms.

In the tutorial below, we focus on three targets - a Web application, a React application and a JS application. For other platforms and use cases, consult Integrating-Fluent.

Declarative vs. Imperative

All user interface models can be divided into declarative and imperative. Declarative markup describes the user interface with a dataset, while imperative introduces it via running code.

An example of a declarative UI is HTML:

<header>
  <h1>Hello World</h1>
  <button title="Click to open menu" accesskey="C">Click me</button>
</header>

An example of an imperative UI is a JavaScript code:

console.log("Hello World");
console.log("You have 5 unread messages.");

Fluent aims to support both models, but strongly recommends describing user interfaces using the declarative approach.

HTML

In the declarative approach, Fluent introduces a binding between a UI widget and its localizations in form of a unique identifier. In HTML we chose data-l10n-id (although using namespaces has been considered), but each environment may chose their own binding scheme.

An example of such binding is:

hello-world = Hello World
click-button = Click me
    .title = Click to open a menu
    .accesskey = C
<header>
  <h1 data-l10n-id="hello-world"></h1>
  <button data-l10n-id="click-button"></button>
</header>

All other declarative markup languages can use analogous binding schemes to describe a relation between a UI widget and its translations. This model is very flexible and allows for very rich error recovery.

For more examples, see fluent-web README.

JS

In cases where the UI is built imperatively via running code, Fluent provides a low and high level translation APIs.

Low level API

The low level API is meant to be simple, yet provide all features of Fluent, while the high level adds more sophisticated features such as asynchronous language fallbacking.

Example of the Fluent API:

const ctx = new MessageContext(["de"]);
ctx.addMessages(`
hello-world = Hello World
unread-msgs = You have { $unreadCount ->
  [one] unread message
 *[other] unread messages
}
`);

console.log(ctx.formatValue("hello-world");
console.log(ctx.formatValue("unread-msg", { unreadCount: 5 }));

The core API is intentionally not opinionated and does not handle aspects such as loading .ftl files in order to keep it open to fit into any software model and allow users to select how and when they want to generate and store MessageContext objects.

For more examples of the low level API see fluent README.

High level API

Higher level API, an example of which is a Localization class in the fluent-dom package is wrapping the low level API offering hooks use for resource loading, language negotiation and lazy language fallbacking.

An example of such API use:

// Boilerplate code:
const l10n = new Localization(["fr-CA", "fr", "de", "en"]);

function async *generateMessages(resourceIds) {
  const languages = await negotiateLanguages(requested, available);

  for (const lang of languages) {
    const ctx = new MessageContext(lang);

    for (const resourceId of resourceIds) {
      const source = await loadFile(resourceId);
      ctx.addMessages(source);
    }

    yield ctx;
  }
}

// Running code:


const l10n = new Localization(document, [
  '/browser/main.ftl',
], generateMessages);

const msg = await l10n.formatValue('hello-world');
console.log(msg);

The generateMessages generator function allows for maximum flexibility when integrating the system into your workflow, but will probably be written once per application, and only the last 10 lines of the example represent what the developer will work with.

The example above is both lazy and asynchronous, but depending on your project requirements and environment you may alter it to your needs.

For more examples of Localization and DOMLocalization, see fluent-dom README.

React

fluent-react is an example of how we envision Fluent fitting into an existing UI framework workflow, and you'll find it similar to the examples above with a focus on declarative UI approach.

In case of React, we use higher-order components to create bindings between the declarative UI and localizations. An example of such binding looks like this:

export default function App() {
  return (
    <div>
      <Localized id="title">
        <h1>Hello, world!</h1>
      </Localized>
    </div>
  );
}

You may recognize the idea of the binding from the HTML example, but it's heavily adapted to the React workflow. Fluent can be fit into many different frameworks by using the low level API and building a higher level bindings on top of it.

For more examples, take a look at the examples section of the fluent-react package.

You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.