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

PluralRule for DualFromZeroToTwo: Does not cover value of 2 - Closes #369 #370

Merged
merged 3 commits into from
Jan 10, 2024

Conversation

axunonb
Copy link
Member

@axunonb axunonb commented Jan 8, 2024

PluralRule for DualFromZeroToTwo: Does not cover value of 2

The Dictionary<string, PluralRuleDelegate> IsoLangToDelegate is backed by a default dictionary. It can be restored with PluralRules.RestoreDefault() if one of the delegates was changed. Both has global effect.

Extract class CustomPluralRuleProvider to its own file

PluralRuleDelegate DualFromZeroToTwo:

  • with 3 words, the index is for counts of 0, > 0 and < 2, more than 2
  • with 4 words, the index is for counts of 0, > 0 and < 2, >= 2 and < 3, more than 3

Add unit tests

PluralRule for DualFromZeroToTwo: Does not cover value of 2

The `Dictionary<string, PluralRuleDelegate> IsoLangToDelegate` is backed by a default dictionary. It can be restored with `PluralRules.RestoreDefault()` if one of the delegates was changed. Both has global effect.

Extract class `CustomPluralRuleProvider` to its own file

`PluralRuleDelegate DualFromZeroToTwo`:
* with 3 words, the index is for counts of 0, > 0 and < 2, more than 2
* with 4 words, the index is for counts of 0, > 0 and < 2,  >= 2 and < 3, more than 3

Add unit tests
Copy link

codecov bot commented Jan 8, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Comparison is base (f89ccf9) 96% compared to head (6f54b1a) 96%.

Additional details and impacted files
@@         Coverage Diff         @@
##           main   #370   +/-   ##
===================================
  Coverage    96%    96%           
===================================
  Files        91     92    +1     
  Lines      3227   3233    +6     
===================================
+ Hits       3111   3117    +6     
  Misses      116    116           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

src/SmartFormat/Utilities/PluralRules.cs Outdated Show resolved Hide resolved
src/SmartFormat/Utilities/PluralRules.cs Outdated Show resolved Hide resolved
src/SmartFormat/Utilities/PluralRules.cs Show resolved Hide resolved
<=0 => 0,
> 0 and < 2 => 1,
>= 2 and < 3 => 2,
_ => 3
};
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@karljj1 While going through the code once again:
Since the very beginning, PluralRules are dealing with decimal, while comparisons are integer based. As soon as decimal is not an integer, the index for "other" is returned. See also the Between(...) method. Can you figure out, what was the intention behind?
If this is the concept, these range comparisons break the concept, don't they?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Im not sure I understand. The Between method uses decimals. Maybe Im misunderstanding you?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, Between uses decimal, but checks that there is no faction (like 1.0000001). That's what I mean: The comparison there (and in other methods as well) expects that there's no fraction. The range comparison includes fractions and is inconsistent with the other comparisons in this respect, isn't it?
Frankly I don't understand why this kind of implementation was chosen, and I wonder whether there's something I'm just missing.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which range comparison are you referring to that uses fractions?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not the author of PluralRules, and I think there is an inconsistency. I can't imagine the reason for it.

See these examples:

// Never include fractional numbers to return true
Assert.That(PluralRules.BetweenWithoutFraction(1.2M, 1M, 2M), Is.False);
Assert.That(PluralRules.BetweenWithoutFraction(1.0M, 1M, 2M), Is.True); // whole number

// Never include fractional numbers
Assert.That(PluralRules.Breton(1M, default), Is.EqualTo(1));
Assert.That(PluralRules.Breton(1.1M, default), Is.EqualTo(5)); // default, not a whole number

// Always include fractional numbers with DualFromZeroToTwo
// Why only here?
Assert.That(PluralRules.GetWordsCount4Value(1M), Is.EqualTo(1));
Assert.That(PluralRules.GetWordsCount4Value(1.1M), Is.EqualTo(1)); // same result for number with fraction

What I'm trying to say: Range comparisons for a value are only used with PluralRules.DualFromZeroToTwo. This is not consistent or must have a good reason. So what is it?

My guess is, that range comparisons would be the right way for decimal (!) values. Why should we use decimal, if we compare for equality with whole numbers? Then we could use long as well.

Copy link
Collaborator

@karljj1 karljj1 Jan 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I don't know why it's this way. There is some mention in the CLDR that the many option can also be used for fractions https://cldr.unicode.org/index/cldr-spec/plural-rules

Fractions

In some languages, fractions require a separate category. For example, Russian 'other' in the example above. In some languages, they all in a single category with some integers, and in some languages they are in multiple categories. In any case, they also need to be examined to make sure that there are sufficial minimal pairs.

Fractions are often a bit tricky to determine: languages have very different behavior for them. In some languages the fraction is ignored (when selecting the category), in some languages the final digits of the fraction are important, in some languages a number changes category just if there are visible trailing zeros. Make sure to try out a range of fractions to make sure how the numbers behave: values like 1 vs 1.0 may behave differently, as may numbers like 1.1 vs 1.2 vs 1.21, and so on.

Maybe the original used an older version of the CLDR?

Fractional support in plurals is new in CLDR 24. Because the fractions didn't work before, the changes in categories from 23 to 24 should not cause an issue for implementations.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot, Karl, with your hint to Fraction support the scales fell from my eyes! ½, 1 ½ sure!
I'll revert to v3.3.0 code, but include 2 as the minimum plural value. Then this is just a bug fix with no breaking changes.

Revert to v3.3.0 code, but include 2 as the minimum plural value
Copy link

sonarcloud bot commented Jan 10, 2024

@axunonb axunonb marked this pull request as ready for review January 10, 2024 20:40
@axunonb axunonb changed the title Closes #369 - PluralRule for DualFromZeroToTwo: Does not cover value of 2 PluralRule for DualFromZeroToTwo: Does not cover value of 2 - Closes #369 Jan 10, 2024
@axunonb axunonb merged commit b7cfc51 into main Jan 10, 2024
8 checks passed
@axunonb axunonb deleted the pr/plural-rules branch January 10, 2024 22:30
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.

None yet

2 participants