Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
savannidgerinel opened this issue Apr 2, 2020 · 2 comments

Comments

@savannidgerinel
Copy link

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
Copy link
Collaborator

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
Copy link
Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants