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

MessageFormat formatters #653

Closed
wants to merge 6 commits into from

Conversation

nkovacs
Copy link
Contributor

@nkovacs nkovacs commented Nov 22, 2016

Fixes #563

@nkovacs
Copy link
Contributor Author

nkovacs commented Nov 22, 2016

About plural requiring cardinal + ordinal data... I want to avoid that. I'm wondering if {plural, ... could use a cardinal formatter, and {selectordinal, ... could use a ordinal formatter?

I've implemented this. It also doesn't output the pluralGenerator at all if it's not needed (the current version of globalize always outputs the cardinal one, even if no message needs it).
It's not perfect, because if you have a message that contains plural, a message that contains selectOrdinal, and a message that contains both, the compiled output will have all three pluralGenerators. I can modify the "both" case to use two separate plural functions, but I'll have to change the compiler's output.

@nkovacs
Copy link
Contributor Author

nkovacs commented Nov 23, 2016

I've changed the way messageformatters are compiled. Instead of this:

Globalize.b955419430 = messageFormatterFn((function(plural, fmt, en) {
    return function(d) {
        return "Hello World number( " + plural(d.x, 0, en, {
            one: "one task " + fmt[0](d.now) + " ",
            other: d.x + " tasks " + fmt[1](d.now) + " "
        });
    }
})(messageFormat.plural, [Globalize("en").dateFormatter({
    "date": "long"
}), Globalize("en").dateFormatter({
    "date": "long"
})], Globalize("en").pluralGenerator({
    type: "cardinal"
})), Globalize("en").pluralGenerator({
    "type": "cardinal"
}), Globalize("en").dateFormatter({
    "date": "long"
}), Globalize("en").dateFormatter({
    "date": "long"
}));

It now looks like this:

Globalize.b955419430 = messageFormatterFn((function(en) {
    var plural = messageFormat.plural;
    var fmt = [].slice.call(arguments, 1);
    return function(d) {
        return "Hello World number( " + plural(d.x, 0, en, {
            one: "one task " + fmt[0](d.now) + " ",
            other: d.x + " tasks " + fmt[1](d.now) + " "
        });
    }
}), "call", Globalize("en").pluralGenerator({
    "type": "cardinal"
}), Globalize("en").dateFormatter({
    "date": "long"
}), Globalize("en").dateFormatter({
    "date": "long"
}));

The formatters are not duplicated by messageFormatterRuntimeBind.

This required changing messageFormatterFn, so to make globalize-runtime compatible with older compiled bundles, I added a "call" string argument after the function. Old bundles will either have the pluralGenerator as the second argument or no second argument, so messageFormatterFn will use the old behavior.

@nkovacs nkovacs force-pushed the 563-custom-formatters branch from df1b90d to 5e72dbf Compare January 5, 2017 17:16
@nkovacs
Copy link
Contributor Author

nkovacs commented Jan 6, 2017

I've cleaned this up, and it's mostly done.

The remaining issues:

  • pluralGenerator({"type": "both"}) could be removed, since it's redundant, but that requires changing both the messageformat compiler and the runtime plural helper function. The latter expects a single plural function that can do both ordinals and cardinals. Also, maintaining compatibility with existing compiled bundles would require some extra code in that case.
  • raw and skeleton aren't supported in date. Not sure what syntax to use.
  • the way message formatters are registered could probably be done in a better way, I didn't give it much thought: https://github.com/nkovacs/globalize/blob/5e72dbf5c0a5aa5bf886de5dd8d9b1d164dc349b/src/core.js#L89
  • documentation is missing.

@rxaviers
Copy link
Member

rxaviers commented Jan 6, 2017

Ok, it's on my TODO list to review.

@rxaviers rxaviers added this to the 1.3.0 milestone Mar 17, 2017
@nkovacs nkovacs force-pushed the 563-custom-formatters branch from 5e72dbf to 06effea Compare May 15, 2017 12:22
@Strate
Copy link
Contributor

Strate commented May 26, 2017

Any news here?

@rxaviers
Copy link
Member

rxaviers commented May 26, 2017

This needs review. Basically, we need to make sure this change is backward compatible, and it works with globalize-compiler. I didn't find time to do it yet, but if someone could investigate that and post findings here, it would speed things up :) thanks

@nkovacs
Copy link
Contributor Author

nkovacs commented May 26, 2017

I also have to document and add tests. I'll try to do that soon.
I also want to add datetime skeletons, because they're much more useful now (thanks to dynamic augmentation), but we need to agree on a syntax for that (see #563 (comment)).

@rxaviers
Copy link
Member

Awesome, thanks @nkovacs

I also want to add datetime skeletons, because they're much more useful now (thanks to dynamic augmentation), but we need to agree on a syntax for that.

They are!! Thanks to @Strate for that too :)

About the syntax, it would be good to (re-)evaluate existing solutions and digest a conclusion. Useful links:

@Strate
Copy link
Contributor

Strate commented May 26, 2017

@nkovacs are you use it now in some projects? I would megre you PR in current state and test it.

@nkovacs
Copy link
Contributor Author

nkovacs commented May 26, 2017

Not yet, but please do test it. I've rebased it on master recently, but haven't had time to test it thoroughly.

@Strate
Copy link
Contributor

Strate commented May 26, 2017

@nkovacs at least {variable, number} works great. Thank you!

@rxaviers rxaviers removed this from the 1.3.0 milestone May 29, 2017
@nkovacs nkovacs force-pushed the 563-custom-formatters branch from 06effea to 6da191b Compare June 7, 2017 11:37
@nkovacs
Copy link
Contributor Author

nkovacs commented Jun 7, 2017

I've added tests, but they fail because they need globalizejs/globalize-compiler#17

I ran into some issues with messageformat-parse: messageformat/parser#1 and messageformat/messageformat#175

The second one I've already started fixing. The first one I can also fix, but that would mean that this would no longer be parsed as two separate parameters (["skeleton","yMd"]) by messageformat-parser: {var,date,skeleton,yMd}

The way ICU works is that after the second comma, everything until the } is part of argStyle, and that's the only parameter that's accepted (but you can escape } with '}')
However, it would still be possible for the formatter function to split on , so the above would work. The alternative is {var,dateskeleton,yMd}.

@nkovacs
Copy link
Contributor Author

nkovacs commented Jul 3, 2017

I've implemented date raw and skeleton support.

Raw works the same way ICU does: date raw comma: {x, date, y-M-d, HH:mm:ss zzzz } becomes date raw comma: 2010-9-15, 17:35:07 GMT+02:00 . Note that the comma doesn't split the parameter and whitespace is not trimmed.

Skeleton is {x, date, skeleton, GyMMMEdhms}, but I can change it to {x, dateskeleton, GyMMMEdhms}.

This requires messageformat/parser#3.

nkovacs added 6 commits July 3, 2017 10:33
Most globalize modules can now be used directly
from within messages.

Also fixes selectOrdinal not using the correct plural function.

The messageformat compiler and runtime are forked from
messageformat.js.

Fixes globalizejs#563
messageformatterFn now uses the runtimeArgs generated by
globalize-compiler instead of having them duplicated
by messageFormatterRuntimeBind.
The new version adds a string "call" parameter
after the formatter function.
Old bundles will not have this parameter, so messageFormatterFn
will use the old behavior.
No longer needed, since we use a forked version.
@nkovacs nkovacs force-pushed the 563-custom-formatters branch from b946b53 to c9d2460 Compare July 3, 2017 08:33
@rxaviers
Copy link
Member

rxaviers commented Jul 3, 2017

Can you also update documentation please? Somewhere we need to list this new API.

{x, date, y-M-d, HH:mm:ss zzzz }

I'd rather use skeleton by default (which is the recommended way users should customize their formats) and have a specific dateraw for the raw pattern. How can they pass presets like {date: small} please?

How does this implementation compare to https://github.com/messageformat/messageformat.js and https://github.com/yahoo/intl-messageformat/ please?

This requires messageformat/parser#3.

Please, is the update included in this PR? Or it requires any further action?

@nkovacs
Copy link
Contributor Author

nkovacs commented Jul 3, 2017

Can you also update documentation please? Somewhere we need to list this new API.

Of course. Should I add it to doc/api/message/message-formatter.md?

I'd rather use skeleton by default (which is the recommended way users should customize their formats) and have a specific dateraw for the raw pattern. How can they pass presets like {date: small} please?

I did it this way to keep it compatible with ICU. In ICU you have:

  • {x,date,short}, {x,date,medium},{x,date,long},{x,date,full},{x,date, y-M-d, HH:mm:ss zzzz }
  • {x,time,short}, {x,time,medium},{x,time,long},{x,time,full},{x,time, y-M-d, HH:mm:ss zzzz }

Both {x,date, y-M-d, HH:mm:ss zzzz } and {x,time, y-M-d, HH:mm:ss zzzz } return " 2017-7-3, 15:20:34 Central European Summer Time " (with spaces).

This PR supports that, plus:

  • {x,datetime,short}, {x,datetime,medium},{x,datetime,long},{x,datetime,full},{x,datetime, y-M-d, HH:mm:ss zzzz }
  • {x,date,skeleton,GyMMMEdhms},{x,time,skeleton,GyMMMEdhms},{x,datetime,skeleton,GyMMMEdhms} (these are all the same).

I did it this way to make it compatible with ICU. However, since it's possible to overwrite the formatter function with Globalize.addMessageFormatterFunction, I'm not against making the default different, and I agree that skeleton should be preferred. If someone needs it to be compatible (e.g. using the same messages on frontend and backend), they can overwrite the defaults.

Please, is the update included in this PR? Or it requires any further action?

No, it's not, since messageformat-parser is downloaded from npm. I made a forked version, I could use that: https://www.npmjs.com/package/icu-messageformat-parser.

@rxaviers
Copy link
Member

rxaviers commented Jul 3, 2017

Of course. Should I add it to doc/api/message/message-formatter.md?

It looks good. Thanks.

I did it this way to make it compatible with ICU. However, since it's possible to overwrite the formatter function with Globalize.addMessageFormatterFunction, I'm not against making the default different, and I agree that skeleton should be preferred. If someone needs it to be compatible (e.g. using the same messages on frontend and backend), they can overwrite the defaults.

I understand, it's always good to follow a standard, but it's unfortunate ICU is doing it that way. Does ICU allow skeleton input? If you have time, please could you also compare to what Yahoo is doing (link above)?

No, it's not, since messageformat-parser is downloaded from npm. I made a forked version, I could use that: https://www.npmjs.com/package/icu-messageformat-parser.

Would this be needed in addition to messageformat/messageformat.js or would it replace it please?

@nkovacs
Copy link
Contributor Author

nkovacs commented Jul 3, 2017

I understand, it's always good to follow a standard, but it's unfortunate ICU is doing it that way. Does ICU allow skeleton input? If you have time, please could you also compare to what Yahoo is doing (link above)?

No, ICU doesn't allow skeleton input, unfortunately.
I tried Yahoo's intl-messageformat-parser, and I found that it had the same bugs as messageformat-parser (which I fixed in my fork).

Would this be needed in addition to messageformat/messageformat.js or would it replace it please?

Messageformat.js isn't needed, the parts that were needed have been replaced by message/compiler.js and message/formatter-runtime.js (see #563 (comment))

@rxaviers
Copy link
Member

rxaviers commented Jul 3, 2017

Ok. Thanks.

and I found that it had the same bugs as messageformat-parser (which I fixed in my fork).

Did you send a PR to the origin repo? Please, if so could you provide a link for reference?

Messageformat.js isn't needed, the parts that were needed have been replaced by message/compiler.js and message/formatter-runtime.js (see #563 (comment))

Ok, that is well explained. Thanks! I see that you had reasons to drop messageformat/messageformat.js and to use a custom replacement. I also understand that the way globalize currently embeds messageformat/messageformat.js is hard to maintain over time. Having said that, there's a downside of duplicating what messageformat/messageformat.js does. We wouldn't take benefit of bug fixes and improvements done there...

@nkovacs
Copy link
Contributor Author

nkovacs commented Jul 3, 2017

Did you send a PR to the origin repo? Please, if so could you provide a link for reference?

I mean I fixed them in messageformat-parser, not intl-messageformat-parser (messageformat/parser#3). intl-messageformat-parser is based on messageformat.js's parser (before it was a standalone package), but it's quite different.

E.g. intl-messageformat-parser/intl-messageformat handles # completely differently, it's not parsed by the parser at all, instead the compiler handles it: https://github.com/yahoo/intl-messageformat/blob/master/src/compiler.js#L59
But it doesn't correctly handle '#'.

I also looked at https://www.npmjs.com/package/format-message-parse, it was slightly better with apostrophe escaping, but it also can't parse {x,date, y-M-d, HH:mm:ss zzzz }.

Then there's messageformat.js's strictNumberParsing, which is not ICU compatible, but is off by default, and turning it on would mean breaking backwards compatibility with previous versions of globalize.js.

@eemeli
Copy link

eemeli commented Jul 17, 2017

Noting also here that I've reviewed the messageformat-parser PR. Overall, it's good, but ought to be split into two as the quote-escaping will change users' outputs, and therefore requires us to make a major version update.

@rxaviers
Copy link
Member

I am closing all old (and stalled) PRs

@rxaviers rxaviers closed this Mar 16, 2020
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

Successfully merging this pull request may close these issues.

MessageFormat formatters
4 participants