projectfluent / fluent

Fluent — planning, spec and documentation
https://projectfluent.org
Apache License 2.0
1.4k stars 45 forks source link

Parameterized Messages #258

Closed eemeli closed 5 years ago

eemeli commented 5 years ago

Reading through the BNF, I was surprised by this: https://github.com/projectfluent/fluent/blob/aa89b9722bd779731c5015d065c6dfc07defec34/spec/fluent.ebnf#L67-L68

Having then read through some of the history (In particular #176 and this comment on #80), I think I sort of understand the reasoning here, but I believe in actual use it could still prove rather surprising.

The usage pattern I have in mind here is first implementing a parameterised term, and later having a need for using that term directly as a message. In that situation, replacing -term with term in messages won't work, as {term(foo:"bar")} is not valid syntax.

If there's a longer-term intent of differentiating the capabilities of terms and messages more, then this difference makes sense. As is it seems to be the only thing that you can do with a term but not with a message, and that's a bit odd.

stasm commented 5 years ago

Thanks for an in-depth look into the EBNF :)

Terms are meant to be private and stay private, so changing -term to term isn't something I'd recommend doing. You'd need to update all references across all messages, which would be disruptive. In Mozilla localizations, we solve the problem you described by creating a new message which simply references the term.

The primary use-case is to allow localizers to define glossary terms. Take the following Italian example:

-sync-brand-name = {$capitalization ->
   *[uppercase] Account Firefox
    [lowercase] account Firefox
}

sync-dialog-title = {-sync-brand-name}
sync-signedout-account-title =
    Connetti il tuo {-sync-brand-name(capitalization: "lowercase")}

Thanks to the term being private and impossible to reference from the outside of the file, the localizer is in total control of it. If they decide to change the default variant to the lowercase one, they are certain that no UI references the term directly. If they consequently update all references in their translations (which they control, too), everything will continue to work as expected:

-sync-brand-name = {$capitalization ->
    [uppercase] Account Firefox
   *[lowercase] account Firefox
}

sync-dialog-title = {-sync-brand-name(capitalization: "uppercase")}
sync-signedout-account-title =
    Connetti il tuo {-sync-brand-name)}

In both scenarios, calling sync-dialog-title from the code gives the uppercase variant, Account Firefox.

Let me know what you think about this. Perhaps this is more of a documentation issue? https://projectfluent.org/fluent/guide/terms.html could do a better job of explaining the reasoning behind terms being private.

eemeli commented 5 years ago

That makes sense. The particular point that I was missing (and which could be improved in the documentation) was that terms are explicitly controlled by the localizer.

One aspect of Fluent that I'm starting to like is that it's got thought put into its use from the localizer's point of view, separately from the developer, and that it's opinionated about the scope of the solutions it's covering. In particular, fixing the file format makes a lot of sense, as it's something that e.g. ICU MessageFormat leaves out of its scope.