formatjs / intl-messageformat

[MIGRATED] Format a string with placeholders, including plural and select support to create localized messages.
http://formatjs.io/
Other
530 stars 77 forks source link

How about ordinals? #84

Closed carystanley closed 9 years ago

carystanley commented 9 years ago

It would be great if intl-messageformat supported ordinal numbers:

1st, 2nd, 3rd, 4th, 10th, 11th, 12th, 13th, 52nd, 53rd, etc...

Here is a really simple version of the logic:

    function ordinal_suffix(num) {
        var s = ["th", "st", "nd", "rd"],
            v = num % 100;
        return num + (s[(v - 20) % 10] || s[v] || s[0]);
    }

I am unsure if ordinal works differently in other languages besides english

caridy commented 9 years ago

@carystanley that's exactly right. To implement ordinals we need to use CLDR to produce the logic per locale. this is not the first time we get this feature requests, we will evaluate it, but it doesn't belong to this repo for sure since ICU message format does not support ordinals, just like it doesn't support relative time, we can also push for that in that front.

ericf commented 9 years ago

I did some research and the CLDR has Rule-Based Number Format (RBNF) data, which includes spellout ("three") and ordinal ("3rd"). As of CLDR v26, these rules are defined in ICU Message syntax — which is just like how our Intl RelativeFormat's data is defined and works.

I see a few ways we could implement ordinals:

1) Add support for selectordinal to IntlMessageFormat:

This would be the easiest to implement, and not add any extra locale data to the library, but it would force the translators to add the correct ordinal suffix inside the optional parts of the messages, e.g.:

Go to the {floor, selectordinal,
  =0 {ground} 
  one {#st} 
  two {#nd}
  few {#rd}
  other {#th}
} floor.

2) Create an IntlRuleBasedNumberFormat lib, use externally:

To support automatic ordinal formatting leveraging the rules built into CLDR, we could create a new IntlRuleBasedNumberFormat, and like IntlRelativeFormat it would be used externally from the ICU Message string; i.e., you would format the value using IntlRuleBasedNumberFormat before passing it to the IntlMessageFormat instance, e.g.:

Go to the {floor} floor.
var msg  = new IntlMessageFormat('Go to the {floor} floor.', 'en');
var rbnf = new IntlRuleBasedNumberFormat('en', {style: 'ordinal'});

var output = msg.format({
    floor: rbnf.format(3)
});

console.log(output); // => "Go to the 3rd floor."

The benefit of this approach is that it doesn't add k-weight to IntlMessageFormat — just like how relative time formatting is separate. The downside is that it's not built into IntlMessageFormat, therefore deviates from the standard ICU Message. That said, at the end of the day, it would still be built into react-intl, handlebars-intl, and dust-intl :-/

Using this external approach in templates would look like this; e.g., in Handlebars:

{{formatMessage (intlGet "messages.floor")
  floor=(formatOrdinal 3)
}}

3) Create an IntlRuleBasedNumberFormat lib, use internally:

The last option would be like option 2) and create a new IntlRuleBasedNumberFormat lib, but also have IntlRelativeFormat depend on it and use it internally — just like it does for Intl.NumberFormat and Intl.DateTimeFormat.

The difference with this approach is that it would add k-weight to the intl-messageformat package both in terms of the runtime, and the locale data. This approach would allow for the ordinal argument type to be used within the ICU Message strings; e.g.:

Go to the {floor, ordinal} floor.

_Edit: The ICU Message syntax defines ordinal as an argument type; we just don't currently support it in our implementation._


What do you guys think?

juandopazo commented 9 years ago

It looks like Intl.MessageFormat needs a plugin system.

caridy commented 9 years ago

@juandopazo we don't need a plugin system, we just need to convince ICU folks that {floor, ordinal} makes sense, just like {floor, number} does it :)

@ericf my vote goes to option 2, which will pave the way to option 3, just like we did for relative, and we can engage with ICU to try the waters there.

ericf commented 9 years ago

@caridy @juandopazo I wasn't clear before, but ordinal is an argument type defined by the ICU Message syntax; we just don't currently support it in our implementation. I edited option 3 above to make this clear. So there's no need to convince the ICU folks, they already support it :smile:

If you look at the details of the ICU Message syntax, you'll see they have support for selectordinal, ordinal, and spellout argument types — all of which we don't currently support. Note that choiceStyle and ChoiceFormat have been deprecated.

caridy commented 9 years ago

awesome, lets plan for that then, we need an implementer now :)

/cc @lwelti

ericf commented 9 years ago

So we can stage this by first easily adding support for selectordinal arguments, option 1 above. @carystanley do you think that would work to unblock you?

Then we work on the automatic ordinal argument formatting via a new IntlRuleBasedNumberFormat lib, which will take a lot more time and effort for us to implement.

srl295 commented 9 years ago

By the way, concerning ICU please reach out to us if needed, not so hard to convince, and code does talk :) icu-project.org

caridy commented 9 years ago

well, they can do that today without anything new:

Go to the {floor, select,
  =0 {ground} 
  one {#st} 
  two {#nd}
  few {#rd}
  other {#th}
} floor.

not ideal, but at least this unblock them.

caridy commented 9 years ago

@srl295, lets start the conversation about relative time, e.g.: {dateValue, relative}

srl295 commented 9 years ago

@caridy relative time sounds good to me, but probably best started at http://bugs.icu-project.org/trac/newticket . Apologies if you knew this, but the select format in ICU was itself a Yahoo! contribution. Actually there's this ticket (note: follow the trail of 'xref' fields to find related tix) that might be interesting.

edit on consideration, I think a new ticket would be best, and reference any relevant ones you dig up, or even this thread. This is a great time to suggest changes in msgformat.

caridy commented 9 years ago

ICU ticket for relative time in messages: http://bugs.icu-project.org/trac/ticket/11409

srl295 commented 9 years ago

@caridy :+1: . added a backlink here and an xref

srl295 commented 9 years ago

@caridy and, the ICU ticket was moved to unscheduled. Is someone interested in contributing a C++ and Java design and implementation?

caridy commented 9 years ago

@srl295 we will try :)

@lwelti, do we have someone from your team that can work with us on this effort?

ericf commented 9 years ago

I realized that we do not have that data to support selectordinal as I mentioned above. We could get this data from the make-plural.js project which would give us basic ordinal support without needing to implement RBNF.

ericf commented 9 years ago

@caridy what you wrote in https://github.com/yahoo/intl-messageformat/issues/84#issuecomment-64905934 is not actually correct — select argument types use exact matches. We currently have the cardinal pluralization rules, but we also need the ordinal ones, and with those we can support the selectordinal argument type option #1.

ericf commented 9 years ago

Update

I feel we should add basic ordinal support to this project by adding support for selectordinal arguments, option #1 above. Here the tasks to implement this:

ericf commented 9 years ago

PR #102 Implements these selectordinal arguments, plus vast improvements to the locale data used by this package.

piuccio commented 6 years ago

@ericf sorry to dig up this old issue again but the current selectordinal only works for numbers up to 20

{rank, selectordinal,
  one {#st} 
  two {#nd}
  few {#rd}
  other {#th}
}

with {rank: 21} generates 21th which should instead be 21st. Same goes for all numbers ending with 1, 2, 3 bigger than 20.

I'm not sure if you want to track this problem somehow.

MartinDawson commented 6 years ago

@piuccio I tried numbers going up to 40. All work as expected. 21 generates 21st.

I'm using latest react-intl, don't know if you are of if that makes a difference.

Here's my code so you can try and see what's different:

export const floorMessages = (positionLowestFloor, positionHighestFloor) => {
  const ordinals = 'selectordinal, one {#st} two {#nd} few {#rd} other {#th}';

  return defineMessages({
    singleFloor: { id: 'Floor.singleFloor', defaultMessage: `{positionLowestFloor, ${ordinals} } floor`, values: { positionLowestFloor } },
  });
};
piuccio commented 6 years ago

You're right, I messed up my translation files

On Wed, 7 Feb 2018, 00:03 Martin Alex Philip Dawson, < notifications@github.com> wrote:

@piuccio https://github.com/piuccio I tried numbers going up to 40. All work as expected.

Are you using latest react-intl?

Here's my code so you can try and see different:

export const floorMessages = (positionLowestFloor, positionHighestFloor) => { const ordinals = 'selectordinal, one {#st} two {#nd} few {#rd} other {#th}';

return defineMessages({ singleFloor: { id: 'Floor.singleFloor', defaultMessage: {positionLowestFloor, ${ordinals} } floor, values: { positionLowestFloor } }, multiFloor: { id: 'Floor.multiFloor', defaultMessage: {positionLowestFloor, ${ordinals}} to {positionHighestFloor, ${ordinals}} floor, values: { positionLowestFloor, positionHighestFloor } }, }); };

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/yahoo/intl-messageformat/issues/84#issuecomment-363449263, or mute the thread https://github.com/notifications/unsubscribe-auth/AAphXAOBXAPp1M6bamLdX6h4F_odBPiVks5tSGmrgaJpZM4DBMRy .