elixir-cldr/cldr_numbers

spellout_oridnal works not as expected

Closed this issue · 8 comments

I know that in German language, there are ordinal format from numbers like English

English German Arabic
first zuerst أول
second zweite ثاني
third dritte ثالث

but

iex(1)> MyCldr.Number.to_string 1, format: :spellout_ordinal, locale: "de"                              
{:error,
 {Cldr.Rbnf.NoRule,
  "Locale \"de\" does not define an rbnf ruleset :spellout_ordinal"}}

iex(2)> MyCldr.Number.to_string 1, format: :spellout_ordinal, locale: "en"
{:ok, "first"}  #<<<<<<<<<< works as expected

iex(4)> MyCldr.Number.to_string 1, format: :spellout_ordinal, locale: "ar"
{:error,
 {Cldr.Rbnf.NoRule,
  "Locale \"ar\" does not define an rbnf ruleset :spellout_ordinal"}}

CLDR defines different sets of rules based number formats that can and do vary between locales. In the case of locale ar the available rules based number formats for the :spellout category are:

iex> MyApp.Cldr2.Rbnf.Spellout.rule_sets("ar")
[:spellout_ordinal_masculine, :spellout_ordinal_feminine,
 :spellout_numbering_year, :spellout_numbering, :spellout_cardinal_masculine,
 :spellout_cardinal_feminine]

Which as you can see does not include :spellout_ordinal but does include :spellout_ordinal_feminine and :spellout_ordinal_masculine. So you would expect that you can use these formats, but ....

iex> MyApp.Cldr2.Number.to_string 3, locale: "ar", format: :spellout_ordinal_feminine
** (FunctionClauseError) no function clause matching in Cldr.Number.to_string/4  

Which is a limitation on my current formatting code (the limitation is allowable options, processing works fine).

The issue is similar for locale de:

iex> MyApp.Cldr2.Rbnf.Spellout.rule_sets("de")
[:spellout_ordinal_s, :spellout_ordinal_r, :spellout_ordinal_n,
 :spellout_ordinal_m, :spellout_ordinal, :spellout_numbering_year,
 :spellout_numbering, :spellout_cardinal_s, :spellout_cardinal_r,
 :spellout_cardinal_neuter, :spellout_cardinal_n, :spellout_cardinal_masculine,
 :spellout_cardinal_m, :spellout_cardinal_feminine]

But as you can see it does define :spellout_ordinal but for some reason thats also giving an error:

iex> MyApp.Cldr2.Number.to_string 3, locale: "de", format: :spellout_ordinal         
{:error,
 {Cldr.Rbnf.NoRule,
  "Locale \"de\" does not define an rbnf ruleset :spellout_ordinal"}}

What happens now

  1. Fix the bug on the de locale for :spellout_ordinal. That should definitely be ok.

  2. As you can see its quite hard to generalise the rules based number formats because the names very according to locale because, well, languages are complicated. I can easily make it so you can use any of the valid formats but then you have to know what is valid for each locale. And with en, ar and de the ordinal rule names are not consistent to this approach would be very developer unfriendly. Any thoughts on what you think the API should look like?

Arrrgggh, stupid bug on the de locale issue, fixed in master (not yet pushed the commit). Also I will make it possible to specify any valid RBNF format so at least you can move forward. I will publish a new version in the next 12 hours.

Never mind about me, I just appreciate the tremendous amount of efforts you put in all of these packages, and I just want them to be as solid as they can be. They are really useful for multilingual apps. I was reading now about "rules based number format" "offff", it is really complicated as you said due to the very big differences in languages specs. I see APIs are really logical and make sense, but just as it is mentions in the link you provided RBNF

the RBNF data is not completely fleshed out over all languages that otherwise have modern coverage. Secondly, the alternate forms are neither complete, nor useful without additional information. For example, for German there is spellout-cardinal-masculine, and spellout-cardinal-feminine. But a complete solution would have all genders (masculine/feminine/neuter), all cases (nominative, accusative, dative, genitive), plus context (with strong or weak determiner or none). Moreover, even for the alternate forms that do exist, CLDR does not supply any data for when to use one vs another (eg, when to use spellout-cardinal-masculine vs spellout-cardinal-feminine).

And Arabic is even more harder.

Thanks for the encouragement, it helps a lot with motivation. And I really don't like it when bugs this simple get through.

The good news is that CLDR does include enough rules to support what you're after, at least in en, ar and de. I'll get that into good shape first and then see where to go from there.

I appreciate you pushing, its how software gets better.

I will also contribute as much as I can, once I finish my current project. Thank you.

I have published ex_cldr_numbers version 2.18.1 to remove at least the bugs in RBNF format selection. The changelog reads:

Bug Fixes

Thanks to @alaadahmed for the collaboration to fix two bugs in rules based number formatting.

  • Fixes RBNF formatting options setting for :spellout_ordinal

  • Allows any defined RNBF rule for a locale to be invoked as the :format option to Cldr.Number.to_string/3.

Wow it works as expect. Fantaaaaaastic really. Thank you 👍🏻

For future readers, the following is how to determine what RBNF rule sets are available for a given locale:

# the Rbnf.Ordinal module has rules for ordinal number generation
# like "1st", "2nd", ...
iex> MyApp.Cldr.Rbnf.Ordinal.rule_sets "en"
[:digits_ordinal]

# The Rbnf.Spellout module has results for spelling
# numbers as words
iex> MyApp.Cldr.Rbnf.Spellout.rule_sets "fr"
[:spellout_ordinal_masculine_plural, :spellout_ordinal_masculine,
 :spellout_ordinal_feminine_plural, :spellout_ordinal_feminine,
 :spellout_numbering_year, :spellout_numbering, :spellout_cardinal_masculine,
 :spellout_cardinal_feminine]

# Note that at this time, NumberSystem rules are only on the
# "root" locale. These rule sets convert between number systems
# (scripts) and are used automatically when the `:number_system`
# option is passed to `Cldr.Number.to_string/3`.
iex> MyApp.Cldr.Rbnf.NumberSystem.rule_sets "root"
[:tamil, :roman_upper, :roman_lower, :hebrew_item, :hebrew, :greek_upper,
 :greek_lower, :georgian, :ethiopic, :cyrillic_lower, :armenian_upper,
 :armenian_lower]

Any of the available rules can be passed as the :format option to Cldr.Number.to_string/3, irrespective of which module they are hosted in.