projectfluent / fluent-rs

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

Pattern matches on optional values always seem to go with the default branch #168

Closed savannidgerinel closed 4 years ago

savannidgerinel commented 4 years ago

I have a template that relies upon choosing a format based on the presence or the absence of a value:

simplified-td-summary = {$distance ->
    *[zero] some activity
    [other] {$distance} miles of activity
}

This works in this fluent playground page, in that when I give distance a value (such as 15), the template prints out "15⁩ miles of activity", and when I remove distance from the variables or set it to null, I get "some activity".

However, I have pasted this template into my translations file, and fluent-rs gives me different results. The following output is from my test script, in which I print the value of the arguments immediately before I pass them to format_pattern, and on the next line print the results of format_pattern.

Some({"distance": String("10.00")})
some activity

Some({})
some activity

What I expect is:

Some({"distance": String("10.00")})
10.00 miles of activity

Some({})
some activity

It appears that the matcher is always going with the default case. I have done additional experiments in which I have changed the default case, and in those, again, the matcher chooses that new default case.

A more complex example, which exhibits the same behavior, appears in this unit test and this translation string.

Running Fluent 0.11.0 with rustc 1.39.0.

zbraniecki commented 4 years ago

Hi! Thank you for filing the issue!

The reason you're encountering it is because you pass the argument as a String in the rust case, but as a number in the playground case.

If you passed 5.into() in Rust, it'd also work the same way.

The reason behind this is that we employ an implicit pluralization selector when your select expression is a number. We assume that if you are selecting a localization string based on a number, you probably really want its plural category.

So what happens is that, behind the scenes, we de-sugar your message to:

simplified-td-summary = { PLURAL($distance) ->
    *[zero] some activity
    [other] {$distance} miles of activity
}

or, almost like that (because we actually do a bit more and allow you to match against a number as well).

When you pass a string, we just do a simple comparison - is the variant other equal to the passed string? Is the variant zero equal to the passed string? If none is true, take the default variant.

In the particular example you provided, we're also discouraging users from using in-message variant logic for that use case: https://github.com/projectfluent/fluent/wiki/Good-Practices-for-Developers#prefer-separate-messages-over-variants-for-ui-logic

The reason is that message variants are meant to be optional and per-locale. They should be used when a message may depend on a grammatical feature of some locales.

In your case, you really want two messages - one for unknown distance and one for a known one. The same pair of messages should be available for all locales and does not depend on any grammatical features - it just depend on the availability of data.

Therefore, I'd recommend:

simplified-td-summary-unknown = some activity
simplified-td-summary = { $distance ->
    [one] { $distance } mile of activity
   *[other] { $distance } miles of activity
}

and then in your code, pick the right message id depending on the availability of the data.

As you can see, I did use variants here, because the sentence in English depends on the plural category of the $distance.

Let me know if that answers your question!

savannidgerinel commented 4 years ago

Oh, so it's not actually a bug!

As soon as I ensure that I'm sending a number, it works.

Ironically, what you recommend as best practice is... well... what I'd switched to as a workaround. I hadn't questioned the fact that I was going to need the same kind of structure in all of my language files.

Thank you for this really extensive response. I really appreciate it. I'm going to close this, as it seems to be not a bug.