Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ public void Test_Default()
new CultureInfo("en-US"),
"There {0:plural:is|are} {0} {0:plural:item|items} remaining",
new ExpectedResults {
{ -1, "There are -1 items remaining"},
{ -1, "There is -1 item remaining"},
{ 0, "There are 0 items remaining"},
{0.5m, "There are 0.5 items remaining"},
{ 1, "There is 1 item remaining"},
Expand All @@ -116,7 +116,7 @@ public void Test_English()
new CultureInfo("en-US"),
"There {0:plural:is|are} {0} {0:plural:item|items} remaining",
new ExpectedResults {
{ -1, "There are -1 items remaining"},
{ -1, "There is -1 item remaining"},
{ 0, "There are 0 items remaining"},
{0.5m, "There are 0.5 items remaining"},
{ 1, "There is 1 item remaining"},
Expand Down Expand Up @@ -184,7 +184,7 @@ public void Test_French_3words(int count, string expected)
Assert.That(actual, Is.EqualTo(string.Format(ci, expected, count)));
}

[TestCase(-1, "-")]
[TestCase(-1, "une personne")] // -1 is treated as 1 (singular)
[TestCase(0, "pas de personne")] // 0 is singular
[TestCase(1, "une personne")] // 1 is singular
[TestCase(2, "{0} personnes")] // 2 is plural
Expand Down Expand Up @@ -214,7 +214,7 @@ public void Test_Turkish()
new CultureInfo("tr"),
"Seçili {0:plural:nesneyi|nesneleri} silmek istiyor musunuz?",
new ExpectedResults {
{ -1, "Seçili nesneleri silmek istiyor musunuz?"},
{ -1, "Seçili nesneyi silmek istiyor musunuz?"}, // -1 is treated as 1 (singular)
{ 0, "Seçili nesneleri silmek istiyor musunuz?"},
{0.5m, "Seçili nesneleri silmek istiyor musunuz?"},
{ 1, "Seçili nesneyi silmek istiyor musunuz?"},
Expand Down
190 changes: 136 additions & 54 deletions src/SmartFormat/Utilities/PluralRules.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;

namespace SmartFormat.Utilities;
#pragma warning disable S3776 // disable sonar cognitive complexity warnings
Expand Down Expand Up @@ -211,6 +210,7 @@ public static void RestoreDefault()

private static PluralRuleDelegate DualOneOther => (value, pluralWordsCount) =>
{
value = Math.Abs(value);
return pluralWordsCount switch {
2 => value == 1 ? 0 : 1,
3 => value switch {
Expand All @@ -224,10 +224,15 @@ public static void RestoreDefault()
}; // Dual: one (n == 1), other

private static PluralRuleDelegate DualWithZero =>
(value, pluralWordsCount) => value == 0 || value == 1 ? 0 : 1; // DualWithZero: one (n == 0..1), other
(value, pluralWordsCount) =>
{
value = Math.Abs(value);
return value == 0 || value == 1 ? 0 : 1;
}; // DualWithZero: one (n == 0..1), other

private static PluralRuleDelegate DualFromZeroToTwo => (value, pluralWordsCount) =>
{
value = Math.Abs(value);
if (pluralWordsCount == 2) return value is >= 0 and < 2 ? 0 : 1;

if (pluralWordsCount == 3) return GetWordsCount3Value(value);
Expand All @@ -239,6 +244,7 @@ public static void RestoreDefault()

private static int GetWordsCount3Value(decimal n)
{
n = Math.Abs(n);
return n switch
{
0 => 0,
Expand All @@ -249,6 +255,7 @@ private static int GetWordsCount3Value(decimal n)

private static int GetWordsCount4Value(decimal n)
{
n = Math.Abs(n);
return n switch
{
< 0 => 0,
Expand All @@ -258,37 +265,59 @@ private static int GetWordsCount4Value(decimal n)
};
}

private static PluralRuleDelegate TripleOneTwoOther => (value, pluralWordsCount) => value == 1 ? 0 : value == 2 ? 1 : 2; // Triple: one (n == 1), two (n == 2), other
private static PluralRuleDelegate TripleOneTwoOther => (value, pluralWordsCount) =>
{
value = Math.Abs(value);
return value == 1 ? 0 : value == 2 ? 1 : 2;
}; // Triple: one (n == 1), two (n == 2), other

private static PluralRuleDelegate RussianSerboCroatian => (value, pluralWordsCount) =>
value % 10 == 1 && value % 100 != 11 ? 0 : // one
(value % 10).BetweenWithoutFraction(2, 4) && !(value % 100).BetweenWithoutFraction(12, 14) ? 1 : // few
2; // Russian & Serbo-Croatian
{
value = Math.Abs(value);
return value % 10 == 1 && value % 100 != 11 ? 0 : // one
(value % 10).BetweenWithoutFraction(2, 4) && !(value % 100).BetweenWithoutFraction(12, 14) ? 1 : // few
2;
}; // Russian & Serbo-Croatian

private static PluralRuleDelegate Arabic => (value, pluralWordsCount) =>
value == 0 ? 0 : // zero
value == 1 ? 1 : // one
value == 2 ? 2 : // two
(value % 100).BetweenWithoutFraction(3, 10) ? 3 : // few
(value % 100).BetweenWithoutFraction(11, 99) ? 4 : // many
5; // other
{
value = Math.Abs(value);
return value == 0 ? 0 : // zero
value == 1 ? 1 : // one
value == 2 ? 2 : // two
(value % 100).BetweenWithoutFraction(3, 10) ? 3 : // few
(value % 100).BetweenWithoutFraction(11, 99) ? 4 : // many
5;
}; // other

private static PluralRuleDelegate Breton => (value, pluralWordsCount) =>
value switch
{
value = Math.Abs(value);
return value switch
{
0 => 0, // zero
1 => 1, // one
2 => 2, // two
3 => 3, // few
6 => 4, // many
_ => 5 // other
};
};
};

private static PluralRuleDelegate Czech => (value, pluralWordsCount) =>
value == 0 ? 0 : // zero
value == 1 ? 1 : // one
value.BetweenWithoutFraction(2, 4) ? 2 : // few
value % 1 == 0 ? 3 : // many
4; // other
{
value = Math.Abs(value);
return value == 0 ? 0 : // zero
value == 1 ? 1 : // one
value.BetweenWithoutFraction(2, 4) ? 2 : // few
value % 1 == 0 ? 3 : // many
4; // other
};

private static PluralRuleDelegate Welsh => (value, pluralWordsCount) =>
value switch
{
value = Math.Abs(value);
return value switch
{
0 => 0, // zero
1 => 1, // one
Expand All @@ -297,67 +326,120 @@ private static int GetWordsCount4Value(decimal n)
6 => 4, // many
_ => 5 // other
};
};
private static PluralRuleDelegate Manx => (value, pluralWordsCount) =>
(value % 10).BetweenWithoutFraction(1, 2) || value % 20 == 0
{
value = Math.Abs(value);
return (value % 10).BetweenWithoutFraction(1, 2) || value % 20 == 0
? 0
: // one
1;
};

private static PluralRuleDelegate Langi => (value, pluralWordsCount) =>
value switch
{
value = Math.Abs(value);
return value switch
{
0 => 0,
> 0 and < 2 => 1,
_ => 2
};
};

private static PluralRuleDelegate Lithuanian => (value, pluralWordsCount) =>
value % 10 == 1 && !(value % 100).BetweenWithoutFraction(11, 19) ? 0 : // one
(value % 10).BetweenWithoutFraction(2, 9) && !(value % 100).BetweenWithoutFraction(11, 19) ? 1 : // few
2;
{
value = Math.Abs(value);
return value % 10 == 1 && !(value % 100).BetweenWithoutFraction(11, 19) ? 0 : // one
(value % 10).BetweenWithoutFraction(2, 9) && !(value % 100).BetweenWithoutFraction(11, 19) ? 1 : // few
2;
};

private static PluralRuleDelegate Latvian => (value, pluralWordsCount) =>
value == 0 ? 0 : // zero
value % 10 == 1 && value % 100 != 11 ? 1 :
2;
{
value = Math.Abs(value);
return value == 0 ? 0 : // zero
value % 10 == 1 && value % 100 != 11 ? 1 :
2;
};

private static PluralRuleDelegate Macedonian => (value, pluralWordsCount) =>
value % 10 == 1 && value != 11
{
value = Math.Abs(value);
return value % 10 == 1 && value != 11
? 0
: // one
1;
};

private static PluralRuleDelegate Moldavian => (value, pluralWordsCount) =>
value == 1 ? 0 : // one
value == 0 || value != 1 && (value % 100).BetweenWithoutFraction(1, 19) ? 1 : // few
2;
{
value = Math.Abs(value);
return value == 1 ? 0 : // one
value == 0 || value != 1 && (value % 100).BetweenWithoutFraction(1, 19) ? 1 : // few
2;
};

private static PluralRuleDelegate Maltese => (value, pluralWordsCount) =>
value == 1 ? 0 : // one
value == 0 || (value % 100).BetweenWithoutFraction(2, 10) ? 1 : // few
(value % 100).BetweenWithoutFraction(11, 19) ? 2 : // many
3;
{
value = Math.Abs(value);
return value == 1 ? 0 : // one
value == 0 || (value % 100).BetweenWithoutFraction(2, 10) ? 1 : // few
(value % 100).BetweenWithoutFraction(11, 19) ? 2 : // many
3;
};

private static PluralRuleDelegate Polish => (value, pluralWordsCount) =>
value == 1 ? 0 : // one
(value % 10).BetweenWithoutFraction(2, 4) && !(value % 100).BetweenWithoutFraction(12, 14) ? 1 : // few
(value % 10).BetweenWithoutFraction(0, 1) || (value % 10).BetweenWithoutFraction(5, 9) || (value % 100).BetweenWithoutFraction(12, 14) ? 2 : // many
3;
{
value = Math.Abs(value);
return value == 1 ? 0 : // one
(value % 10).BetweenWithoutFraction(2, 4) && !(value % 100).BetweenWithoutFraction(12, 14) ? 1 : // few
(value % 10).BetweenWithoutFraction(0, 1) || (value % 10).BetweenWithoutFraction(5, 9) ||
(value % 100).BetweenWithoutFraction(12, 14) ? 2 : // many
3;
};

private static PluralRuleDelegate Romanian => (value, pluralWordsCount) =>
value == 1 ? 0 : // one
value == 0 || (value % 100).BetweenWithoutFraction(1, 19) ? 1 : // few
2;
{
value = Math.Abs(value);
return value == 1 ? 0 : // one
value == 0 || (value % 100).BetweenWithoutFraction(1, 19) ? 1 : // few
2;
};

private static PluralRuleDelegate Tachelhit => (value, pluralWordsCount) =>
value >= 0 && value <= 1 ? 0 : // one
value.BetweenWithoutFraction(2, 10) ? 1 : // few
2;
{
value = Math.Abs(value);
return value >= 0 && value <= 1 ? 0 : // one
value.BetweenWithoutFraction(2, 10) ? 1 : // few
2;
};

private static PluralRuleDelegate Slovak => (value, pluralWordsCount) =>
value == 1 ? 0 : // one
value.BetweenWithoutFraction(2, 4) ? 1 : // few
2;
{
value = Math.Abs(value);
return value == 1 ? 0 : // one
value.BetweenWithoutFraction(2, 4) ? 1 : // few
2;
};

private static PluralRuleDelegate Slovenian => (value, pluralWordsCount) =>
value % 100 == 1 ? 0 : // one
value % 100 == 2 ? 1 : // two
(value % 100).BetweenWithoutFraction(3, 4) ? 2 : // few
3;
{
value = Math.Abs(value);
return value % 100 == 1 ? 0 : // one
value % 100 == 2 ? 1 : // two
(value % 100).BetweenWithoutFraction(3, 4) ? 2 : // few
3;
};

private static PluralRuleDelegate CentralMoroccoTamazight => (value, pluralWordsCount) =>
value.BetweenWithoutFraction(0, 1) || value.BetweenWithoutFraction(11, 99)
{
value = Math.Abs(value);
return value.BetweenWithoutFraction(0, 1) || value.BetweenWithoutFraction(11, 99)
? 0
: // one
1;
};

/// <summary>
/// This delegate determines which singular or plural word should be chosen for the given quantity.
Expand Down