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
Add FluentMessage #366
Add FluentMessage #366
Conversation
*/ | ||
get messages() { | ||
return this._messages[Symbol.iterator](); | ||
this._dirty = new WeakSet(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what's that? It doesn't seem to be used anywhere
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's used in createScope
, and then in Pattern
to detect cyclic references.
format(id, args, errors = []) { | ||
if (!this._messages.has(id)) { | ||
errors.push(`Message not found: "${id}"`); | ||
return undefined; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why undefined
rather than null
? Is the user expected to always check if the message exists before trying to format it? I thought format("non-existing") -> null
is a reasonable API use and not an error?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I decided to used undefined
here and when formatting missing attributes in order to tell missing values from null values apart. Consider:
foo =
.attr = Attr
format("foo")
→null
,format("bar")
→undefined
.
Do you think they should both return null
instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's an interesting question. I'm torn. For me the question boils down to - should attempting to format a message that doesn't existing in the bundle be an error (which means, you should always call hasMessage
before you call format)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the bindings should always check, yes. They should also use formatValue
and formatAttribute
, rather than format
. format
is intended for more imperative approaches. The fact that it returns undefined
on missing messages was intended to be consistent with formatAttribute
returning undefined
on missing attributes.
It also works well semantically, I think. The difference between null
and undefined
maps nicely to Fluent's notions of value-less messages vs. missing messages. Attempting to format a missing message should result in an error being reported. As a developer, there's little incentive for me to intentionally format missing messages :) So it's likely an error scenario, which I'd like to be able to react to.
bundle: this, | ||
dirty: this._dirty, | ||
// TermReferences are resolved in a new scope. | ||
insideTermReference: false, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I struggled with that in Rust because of lifetimes. While the exact problem is very rust specific and doesn't warrant design changes, I kinda like what I ended up with - locale_args
in the scope - https://github.com/projectfluent/fluent-rs/blob/master/fluent-bundle/src/resolve.rs#L244
Maybe worth considering here? Not a blocker of course.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! So local_args
is both a store of arguments passed to the term and a flag meaning we're resolving a term now? I like it.
fluent/src/resource.js
Outdated
@@ -55,7 +56,7 @@ const MAX_PLACEABLES = 100; | |||
/** | |||
* Fluent Resource is a structure storing a map of parsed localization entries. | |||
*/ | |||
export default class FluentResource extends Map { | |||
export default class FluentResource extends Set { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Beyond updating the comment just a line above, can we consider Array vs. Set here? We won't likely be looking for duplicate-free set, so why not Array?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea, let me test it with make perf
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sweet, extending Array
gives a slight speed-up in addResource
. I guess iterating over the elements of an array is faster than of a set. Thanks for the suggestion!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
well, Set does more - it has to hash and look for duplicates on insertion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know, but interestingly, there's no noticeable difference on the benchmark which inserts. Instead, it's the one which iterates over the elements which sped up :)
Here's the output of
I think We're paying more for resolving and I think it's the price of the encapsulation: |
It would be nice to have a |
We're going to the go with the |
Fix #365. Obsoletes #322 and #360 (on which it's based).