projectfluent / fluent-rs

Rust implementation of Project Fluent
https://projectfluent.org
Apache License 2.0
1.07k stars 96 forks source link

External formatters #152

Closed zbraniecki closed 4 years ago

zbraniecki commented 4 years ago

As we integrate fluent into Gecko, one of the last items to be resolved is how to plug a formatter into the resolver from outside.

In JS model, we have FluentNumber and FluentDateTime [0] which is a bit arbitrary and not very extendible. In Rust we have only FluentNumber, so adding a datetime type is challenging. That inflexibility is being addressed in #146.

But the other issue is that even for types that we do have in Rust (FluentNumber) we don't have a formatter baked into the API. In JS, we use Intl.NumberFormat and expect the environment to polyfill it in case its missing. In Rust, I'm not sure how to address it.

I have an equivalent of Intl.NumberFormat, but it is provided by the Gecko, and not part of fluent-bundle crate, so I need some way to register the formatter and the resolver to know to use it for the given variant of the FluentValue.

@stasm, @Pike - any recommendations?

[0] https://github.com/projectfluent/fluent.js/blob/master/fluent-bundle/src/types.js

Pike commented 4 years ago

In a world of formatToParts, the binding level would handle the serialization.

Looking at 153, the custom type's display is what serializes, right? If we implemented FluentDateTime inside gecko, we could implement that there?

stasm commented 4 years ago

Good point about formatToParts and serialization. At the same time, we'll likely need formatters outside serialization, e.g. we'll need a plural rule formatter to run the logic in select expressions. For that reason, I expect FluentNumber to be a well-defined type in the resolver specification, with a well-defined behavior.

In Rust, could we use closures passed into FluentBundle::new to provide formatting logic from the outside?

zbraniecki commented 4 years ago

In Rust, could we use closures passed into FluentBundle::new to provide formatting logic from the outside?

Yes. What should be the API for that? In Rust, you can't pass a pointer to a variant, so we can't do:

bundle.set_formatter(FluentValue::Number, || ...);
bundle.set_formatter(FluentValue::Custom(DateTIme)), || ...);

so, I guess we'd need to do:

bundle.set_formatter("FluentNumber", || ...);
bundle.set_formatter("DateTime", || ...);

and somehow bind those strings to variants? @Manishearth - is there a better way?

Manishearth commented 4 years ago

I'd have a FluentValueKind enum instead of the string, but yeah

stasm commented 4 years ago

In the long term, I think we only need to pass in the number and plural formatter for the purpose of selection logic. Other use-cases for serialization can (and should) be solved with formatToParts. At least in theory, and we'll see if that the case once we specify and implement formatToParts. It's a bit hard to design the API with unknown requirements.

For the sake of unblocking this issue and of flexibility in the future, maybe accept a single closure which matches variants of FluentValue inside its body?

zbraniecki commented 4 years ago

I landed this as https://github.com/projectfluent/fluent-rs/commit/b69031069911a33c09663af38233fef9576e41b0