Skip to content

Store messages as FluentMessage in the runtime #365

Closed
@stasm

Description

@stasm

This is a proposal for a new API for formatting messages from bundles. It's an alternative to #208. It solves the same problems that #208 does, but in a way which doesn't regress the ability to retrieve a message representation without formatting it and cache it for subsequent formatting operations. See also #358.

The current API (fluent 0.12)

bundle.addMessages(`
foo = Foo
    .bar = Bar
`);
let message = bundle.getMessage("foo");
bundle.format(message, args, errs);
if (message.attrs && message.attrs["bar"]) {
    bundle.format(message.attrs["bar"], args, errs]);
}

This API exposes the internal representation of th e message to the consumer. The message might be a primitive string, an array, or an object of {value,attrs}. In the last case, value maybe undefined, a string, or an array; attrs may be undefined or an object of attributes whose values may again be strings or arrays.

This complexity comes from the days of Firefox OS where it was imperative to minimize the size of the JSON representing translations because the storage on low-end devices was limited.

The compound API proposed in #208

bundle.addMessages(`
foo = Foo
    .bar = Bar
`);

// The "pull" API.
bundle.format("foo", args, errs);
bundle.format("foo.bar", args, errs);

// The "push" API; returns a `{value, attributes}` shape.
bundle.compound("foo", args, errs);

In this API design, there are two entry points. The format method allows to specify the attribute to format after a dot. Each attribute formatted in this manner would cause the message to be re-retrieved by the bundle internally, which isn't optimal performance-wise.

The compound entry-point returns an object of formatted values and attributes. I'm calling it "push" because all attributes would be formatted at all times (which is probably what we want anyways for the most part). However, if a need to implement performant "pull" API arises, this method could evolve to return a lazy attributes field which would format attributes only when queried. This idea was the gist of my comment in #208 (comment).

This design hides the details of how messages are stored internally by the bundle from consumers, but it doesn't allow to retrieve a message once for later re-use when formatting the same UI widget multiple times.

The FluentMessage API (this proposal)

In this proposal, I'd like to formalize the interface of FluentMessage, returned by getMessage. FluentMessages would provide methods for resolving their value and attributes, and a iterable over their attributes' names. Obviously, in current JS we can't really hide the fields used for storing the parsed value and attributes, but we can hint at their being private with the _ prefix, and later on switch to proper private fields when they're standardized.

bundle.addMessages(`
foo = Foo
    .bar = Bar
`);

// An atomic retrieve-and-format API for when attributes are not defined on the message.
// Good for quickly getting started with the bundle and for imperative callsites.
bundle.format("foo", args, errs);

// `message` can be cached for re-use. Attributes can 
let message = bundle.getMessage("foo");
bundle.formatValue(message, args, errs);

// The "pull" API. `name` is known up front.
bundle.formatAttribute(message, name, args, errs);

// The "push" API. `name` comes from the message itself.
for (let name of message.attributeNames) {
    bundle.formatAttribute(message, name, args, errs);
}

This API provides better encapsulation of the runtime AST inside of the FluentMessage class. It supports both the "pull" and the "push" API, and allows caching of the message for multiple formatting operations.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions