From c2901e8e6588ae8f12e39f4af19eae9980f45f0a Mon Sep 17 00:00:00 2001 From: Aleksey Gallyamov Date: Sun, 5 Aug 2018 08:26:52 +0700 Subject: [PATCH] refactoring after merging, add meta data to project builds --- .../PawnHunter.Numerals.Tests.csproj | 3 +- PawnHunter.Numerals.Tests/ReadmeBasedTests.cs | 180 +++---- PawnHunter.Numerals.sln.DotSettings | 15 + PawnHunter.Numerals/Countable.cs | 75 --- PawnHunter.Numerals/CountableForm.cs | 31 ++ PawnHunter.Numerals/English.cs | 120 ----- PawnHunter.Numerals/Examples.html | 118 ----- PawnHunter.Numerals/Gender.cs | 11 + PawnHunter.Numerals/Language.cs | 45 ++ .../Languages/EnglishLanguage.cs | 101 ++++ .../Languages/RussianLanguage.cs | 121 +++++ .../Languages/UkrainianLanguage.cs | 125 +++++ PawnHunter.Numerals/Neutral.cs | 16 - PawnHunter.Numerals/NumeralsFormatter.cs | 444 ++++++++---------- .../PawnHunter.Numerals.csproj | 55 +-- .../Properties/AssemblyInfo.cs | 35 -- PawnHunter.Numerals/Russian.cs | 145 ------ PawnHunter.Numerals/Ukrainian.cs | 149 ------ 18 files changed, 763 insertions(+), 1026 deletions(-) create mode 100644 PawnHunter.Numerals.sln.DotSettings delete mode 100644 PawnHunter.Numerals/Countable.cs create mode 100644 PawnHunter.Numerals/CountableForm.cs delete mode 100644 PawnHunter.Numerals/English.cs delete mode 100644 PawnHunter.Numerals/Examples.html create mode 100644 PawnHunter.Numerals/Gender.cs create mode 100644 PawnHunter.Numerals/Language.cs create mode 100644 PawnHunter.Numerals/Languages/EnglishLanguage.cs create mode 100644 PawnHunter.Numerals/Languages/RussianLanguage.cs create mode 100644 PawnHunter.Numerals/Languages/UkrainianLanguage.cs delete mode 100644 PawnHunter.Numerals/Neutral.cs delete mode 100644 PawnHunter.Numerals/Properties/AssemblyInfo.cs delete mode 100644 PawnHunter.Numerals/Russian.cs delete mode 100644 PawnHunter.Numerals/Ukrainian.cs diff --git a/PawnHunter.Numerals.Tests/PawnHunter.Numerals.Tests.csproj b/PawnHunter.Numerals.Tests/PawnHunter.Numerals.Tests.csproj index dd2ce5e..430a707 100644 --- a/PawnHunter.Numerals.Tests/PawnHunter.Numerals.Tests.csproj +++ b/PawnHunter.Numerals.Tests/PawnHunter.Numerals.Tests.csproj @@ -1,8 +1,7 @@ - + netcoreapp2.1 - false diff --git a/PawnHunter.Numerals.Tests/ReadmeBasedTests.cs b/PawnHunter.Numerals.Tests/ReadmeBasedTests.cs index 6d8c1bc..63fc64f 100644 --- a/PawnHunter.Numerals.Tests/ReadmeBasedTests.cs +++ b/PawnHunter.Numerals.Tests/ReadmeBasedTests.cs @@ -7,119 +7,121 @@ namespace PawnHunter.Numerals.Tests public class ReadmeBasedTests { [Fact] - public void Readme_example_1() - { - var formatter = new NumeralsFormatter { CultureInfo = new CultureInfo("ru-RU") }; + public void Readme_example_1() + { + var formatter = new NumeralsFormatter {CultureInfo = new CultureInfo("ru-RU")}; - var format = "Inbox: {0} {0:W;новое,новых} {0:W;сообщение,сообщения,сообщений}"; + var format = "Inbox: {0} {0:W;новое,новых} {0:W;сообщение,сообщения,сообщений}"; - Assert.Equal("Inbox: 1 новое сообщение", string.Format(formatter, format, 1)); - Assert.Equal("Inbox: 2 новых сообщения", string.Format(formatter, format, 2)); - Assert.Equal("Inbox: 5 новых сообщений", string.Format(formatter, format, 5)); - } + Assert.Equal("Inbox: 1 новое сообщение", string.Format(formatter, format, 1)); + Assert.Equal("Inbox: 2 новых сообщения", string.Format(formatter, format, 2)); + Assert.Equal("Inbox: 5 новых сообщений", string.Format(formatter, format, 5)); + } - [Fact] - public void Readme_example_2() - { - var formatter = new NumeralsFormatter { CultureInfo = new CultureInfo("ru-RU") }; - - var format = "Inbox: {0} {0:W;нов(ое,ых)} {0:W;сообщени(е,я,й)}"; - - Assert.Equal("Inbox: 1 новое сообщение", string.Format(formatter, format, 1)); - Assert.Equal("Inbox: 2 новых сообщения", string.Format(formatter, format, 2)); - Assert.Equal("Inbox: 5 новых сообщений", string.Format(formatter, format, 5)); - } + [Fact] + public void Readme_example_10() + { + var formatter = new NumeralsFormatter {CultureInfo = new CultureInfo("en-US")}; - [Fact] - public void Readme_example_3() - { - var formatter = new NumeralsFormatter { CultureInfo = new CultureInfo("ru-RU") }; + var format = "{0:T} {0:W;hour(,s)} and {1:t} {1:W;minute(,s)}."; + var dateTime = new DateTime(2018, 8, 1, 4, 1, 0); - var format = "{0:W;Найден(а,о)} {0} {0:W;запис(ь,и,ей)}, {0:W;удовлетворяющ(ая,ие,их)} запросу."; + Assert.Equal("Four hours and one minute.", + string.Format(formatter, format, dateTime.Hour, dateTime.Minute)); + } - Assert.Equal("Найдена 1 запись, удовлетворяющая запросу.", string.Format(formatter, format, 1)); - Assert.Equal("Найдено 2 записи, удовлетворяющие запросу.", string.Format(formatter, format, 2)); - Assert.Equal("Найдено 5 записей, удовлетворяющих запросу.", string.Format(formatter, format, 5)); - } + [Fact] + public void Readme_example_2() + { + var formatter = new NumeralsFormatter {CultureInfo = new CultureInfo("ru-RU")}; - [Fact] - public void Readme_example_4() - { - var formatter = new NumeralsFormatter { CultureInfo = new CultureInfo("ru-RU") }; + var format = "Inbox: {0} {0:W;нов(ое,ых)} {0:W;сообщени(е,я,й)}"; - var format = "{0:T}"; + Assert.Equal("Inbox: 1 новое сообщение", string.Format(formatter, format, 1)); + Assert.Equal("Inbox: 2 новых сообщения", string.Format(formatter, format, 2)); + Assert.Equal("Inbox: 5 новых сообщений", string.Format(formatter, format, 5)); + } - Assert.Equal("Одно", string.Format(formatter, format, 1)); - Assert.Equal("Тринадцать", string.Format(formatter, format, 13)); - } + [Fact] + public void Readme_example_3() + { + var formatter = new NumeralsFormatter {CultureInfo = new CultureInfo("ru-RU")}; - [Fact] - public void Readme_example_5() - { - var formatter = new NumeralsFormatter { CultureInfo = new CultureInfo("ru-RU") }; + var format = "{0:W;Найден(а,о)} {0} {0:W;запис(ь,и,ей)}, {0:W;удовлетворяющ(ая,ие,их)} запросу."; - var format = "{0:t;f}"; + Assert.Equal("Найдена 1 запись, удовлетворяющая запросу.", string.Format(formatter, format, 1)); + Assert.Equal("Найдено 2 записи, удовлетворяющие запросу.", string.Format(formatter, format, 2)); + Assert.Equal("Найдено 5 записей, удовлетворяющих запросу.", string.Format(formatter, format, 5)); + } - Assert.Equal("одна", string.Format(formatter, format, 1)); - Assert.Equal("тринадцать", string.Format(formatter, format, 13)); - } + [Fact] + public void Readme_example_4() + { + var formatter = new NumeralsFormatter {CultureInfo = new CultureInfo("ru-RU")}; - [Fact] - public void Readme_example_6() - { - var formatter = new NumeralsFormatter { CultureInfo = new CultureInfo("ru-RU") }; + var format = "{0:T}"; - var format = "{0:T;M} {0:W;час(,а,ов)}."; + Assert.Equal("Одно", string.Format(formatter, format, 1)); + Assert.Equal("Тринадцать", string.Format(formatter, format, 13)); + } - Assert.Equal("Один час.", string.Format(formatter, format, 1)); - Assert.Equal("Два часа.", string.Format(formatter, format, 2)); - Assert.Equal("Десять часов.", string.Format(formatter, format, 10)); - } + [Fact] + public void Readme_example_5() + { + var formatter = new NumeralsFormatter {CultureInfo = new CultureInfo("ru-RU")}; - [Fact] - public void Readme_example_7() - { - var formatter = new NumeralsFormatter { CultureInfo = new CultureInfo("ru-RU") }; + var format = "{0:t;f}"; - var format = "{0:T;M} {0:W;час(,а,ов)} {1:t;F} {1:W;минут(а,ы,)}."; + Assert.Equal("одна", string.Format(formatter, format, 1)); + Assert.Equal("тринадцать", string.Format(formatter, format, 13)); + } - Assert.Equal("Два часа десять минут.", string.Format(formatter, format, 2, 10)); - Assert.Equal("Двадцать один час одна минута.", string.Format(formatter, format, 21, 1)); - Assert.Equal("Ноль часов ноль минут.", string.Format(formatter, format, 0, 0)); - } + [Fact] + public void Readme_example_6() + { + var formatter = new NumeralsFormatter {CultureInfo = new CultureInfo("ru-RU")}; - [Fact] - public void Readme_example_8() - { - var formatter = new NumeralsFormatter {CultureInfo = new CultureInfo("ru-RU")}; + var format = "{0:T;M} {0:W;час(,а,ов)}."; - var format = "{0:T;M} {0:W;рубл(ь,я,ей)} {1:00} коп."; + Assert.Equal("Один час.", string.Format(formatter, format, 1)); + Assert.Equal("Два часа.", string.Format(formatter, format, 2)); + Assert.Equal("Десять часов.", string.Format(formatter, format, 10)); + } - Assert.Equal("Один рубль 12 коп.", string.Format(formatter, format, 1, 12)); - Assert.Equal("Три рубля 05 коп.", string.Format(formatter, format, 3, 5)); - Assert.Equal("Минус три рубля -05 коп.", string.Format(formatter, format, -3, -5)); - } + [Fact] + public void Readme_example_7() + { + var formatter = new NumeralsFormatter {CultureInfo = new CultureInfo("ru-RU")}; - [Fact] - public void Readme_example_9() - { - var formatter = new NumeralsFormatter { CultureInfo = new CultureInfo("en-US") }; + var format = "{0:T;M} {0:W;час(,а,ов)} {1:t;F} {1:W;минут(а,ы,)}."; - var format = "{0:T} {0:W;dollar(,s)} and {1:t} {1:W;cent(,s)}."; + Assert.Equal("Два часа десять минут.", string.Format(formatter, format, 2, 10)); + Assert.Equal("Двадцать один час одна минута.", string.Format(formatter, format, 21, 1)); + Assert.Equal("Ноль часов ноль минут.", string.Format(formatter, format, 0, 0)); + } - Assert.Equal("One dollar and two cents.", string.Format(formatter, format, 1, 2)); - Assert.Equal("One hundred and twenty-three thousand, four hundred and fifty-six dollars and seven cents.", string.Format(formatter, format, 123456, 7)); - } + [Fact] + public void Readme_example_8() + { + var formatter = new NumeralsFormatter {CultureInfo = new CultureInfo("ru-RU")}; - [Fact] - public void Readme_example_10() - { - var formatter = new NumeralsFormatter { CultureInfo = new CultureInfo("en-US") }; + var format = "{0:T;M} {0:W;рубл(ь,я,ей)} {1:00} коп."; - var format = "{0:T} {0:W;hour(,s)} and {1:t} {1:W;minute(,s)}."; - var dateTime = new DateTime(2018, 8, 1, 4, 1, 0); + Assert.Equal("Один рубль 12 коп.", string.Format(formatter, format, 1, 12)); + Assert.Equal("Три рубля 05 коп.", string.Format(formatter, format, 3, 5)); + Assert.Equal("Минус три рубля -05 коп.", string.Format(formatter, format, -3, -5)); + } - Assert.Equal("Four hours and one minute.", string.Format(formatter, format, dateTime.Hour, dateTime.Minute)); - } - } -} + [Fact] + public void Readme_example_9() + { + var formatter = new NumeralsFormatter {CultureInfo = new CultureInfo("en-US")}; + + var format = "{0:T} {0:W;dollar(,s)} and {1:t} {1:W;cent(,s)}."; + + Assert.Equal("One dollar and two cents.", string.Format(formatter, format, 1, 2)); + Assert.Equal("One hundred and twenty-three thousand, four hundred and fifty-six dollars and seven cents.", + string.Format(formatter, format, 123456, 7)); + } + } +} \ No newline at end of file diff --git a/PawnHunter.Numerals.sln.DotSettings b/PawnHunter.Numerals.sln.DotSettings new file mode 100644 index 0000000..c820535 --- /dev/null +++ b/PawnHunter.Numerals.sln.DotSettings @@ -0,0 +1,15 @@ + + Inherit + True + 2 + True + NEVER + True + True + True + True + True + True + True + True + \ No newline at end of file diff --git a/PawnHunter.Numerals/Countable.cs b/PawnHunter.Numerals/Countable.cs deleted file mode 100644 index 57b00ff..0000000 --- a/PawnHunter.Numerals/Countable.cs +++ /dev/null @@ -1,75 +0,0 @@ -namespace PawnHunter.Numerals -{ - public enum Gender - { - Masculine, - Feminine, - Neutral - } - - public class Countable - { - #region Fields - - private readonly string[] _forms = new string[3]; - private readonly Gender _gender; - - #endregion Fields - - #region Properties - - public Gender Gender - { - get { return _gender; } - } - - #endregion Properties - - #region Constructors - - public Countable(string one, string two, string five) : - this(one, two, five, Gender.Neutral) - { - } - - public Countable(string one, string two, string five, Gender gender) - { - _forms[0] = one; - _forms[1] = two; - _forms[2] = five; - - _gender = gender; - } - - #endregion Constructors - - #region Indexers - - public string this[long number] - { - get - { - number %= 100; - - if (number > 20) - { - number %= 10; - } - - if (number == 1) - { - return _forms[0]; - } - - if (number > 1 && number < 5) - { - return _forms[1]; - } - - return _forms[2]; - } - } - - #endregion Indexers - } -} diff --git a/PawnHunter.Numerals/CountableForm.cs b/PawnHunter.Numerals/CountableForm.cs new file mode 100644 index 0000000..3312edc --- /dev/null +++ b/PawnHunter.Numerals/CountableForm.cs @@ -0,0 +1,31 @@ +namespace PawnHunter.Numerals +{ + public class CountableForm + { + private readonly string _one, _two, _five; + + public CountableForm(string one, string two, string five) : + this(one, two, five, Gender.Neutral) + { + } + + public CountableForm(string one, string two, string five, Gender gender) + { + _one = one; + _two = two; + _five = five; + Gender = gender; + } + + public Gender Gender { get; } + + public string ForNumber(long number) + { + number %= 100; + if (number > 20) number %= 10; + if (number == 1) return _one; + if (number > 1 && number < 5) return _two; + return _five; + } + } +} diff --git a/PawnHunter.Numerals/English.cs b/PawnHunter.Numerals/English.cs deleted file mode 100644 index 968f025..0000000 --- a/PawnHunter.Numerals/English.cs +++ /dev/null @@ -1,120 +0,0 @@ -using System; -using System.Text; - -namespace PawnHunter.Numerals -{ - public sealed class English - { - #region Fields - - private static readonly string[] Ranks = new[] - { - " quintillion", " quadrillion", " trillion", - " billion", " million", " thousand", "" - }; - - private static readonly string[] Tens = new[] - { - "", "ten", "twenty", "thirty", "forty", "fifty", - "sixty", "seventy", "eighty", "ninety" - }; - - private static readonly string[] Units = new[] - { - "zero", "one", "two", "three", "four", "five", - "six", "seven", "eight", "nine", "ten", - "eleven", "twelve", "thirteen", "fourteen", "fifteen", - "sixteen", "seventeen", "eighteen", "nineteen" - }; - - #endregion Fields - - #region Methods - - public static string ToString(long number) - { - return ToString(number, false); - } - - public static string ToString(long number, bool uppercase) - { - if (number == 0) - { - if (uppercase) - { - return Char.ToUpper(Units[0][0]) + Units[0].Remove(0, 1); - } - return Units[0]; - } - - var isNegative = false; - if (number < 0) - { - isNegative = true; - number = Math.Abs(number); - } - - var sb = new StringBuilder(); - for (var i = 0; i < Neutral.Ranks.Length; i++) - { - var rankValue = (int)(number / Neutral.Ranks[i]); - number %= Neutral.Ranks[i]; - - if (rankValue <= 0) continue; - var hundreds = rankValue / 100; - var tens = rankValue / 10 % 10; - var units = rankValue % 10; - - if (tens == 1) - { - tens = 0; - units = rankValue % 100; - } - - if (sb.Length > 0) - { - if (hundreds > 0) - sb.Append(", "); - else if (tens + units > 0) - sb.Append(" and "); - } - - if (hundreds > 0) - { - sb.Append(Units[hundreds]); - sb.Append(" hundred"); - if (tens + units > 0) - sb.Append(" and "); - } - - if (tens > 0) - { - sb.Append(Tens[tens]); - if (units > 0) - { - sb.Append("-"); - } - } - - if (units > 0) - sb.Append(Units[units]); - - sb.Append(Ranks[i]); - } - - if (isNegative) - { - sb.Insert(0, "minus "); - } - - if (uppercase) - { - sb[0] = Char.ToUpper(sb[0]); - } - - return sb.ToString(); - } - - #endregion Methods - } -} diff --git a/PawnHunter.Numerals/Examples.html b/PawnHunter.Numerals/Examples.html deleted file mode 100644 index 93e51e8..0000000 --- a/PawnHunter.Numerals/Examples.html +++ /dev/null @@ -1,118 +0,0 @@ - - - - Examples - - - -
-			Thread.CurrentThread.CurrentCulture = new CultureInfo("ru-RU");
-			//Thread.CurrentThread.CurrentCulture = new CultureInfo("uk-UA");
- 
-			NumeralsFormatter formatter = new NumeralsFormatter();
-			string format;
- 
-			format = "Inbox: {0} {0:W;новое,новых} {0:W;сообщение,сообщения,сообщений}";
-			Console.WriteLine(String.Format(formatter, format, 1));
-			Console.WriteLine(String.Format(formatter, format, 2));
-			Console.WriteLine(String.Format(formatter, format, 5));
-			Console.WriteLine();
-			
-			Inbox: 1 новое сообщение
-			Inbox: 2 новых сообщения
-			Inbox: 5 новых сообщений
-			
-			format = "Inbox: {0} {0:W;нов(ое,ых)} {0:W;сообщени(е,я,й)}";
-			Console.WriteLine(String.Format(formatter, format, 1));
-			Console.WriteLine(String.Format(formatter, format, 2));
-			Console.WriteLine(String.Format(formatter, format, 5));
-			Console.WriteLine();
-			
-			Inbox: 1 новое сообщение
-			Inbox: 2 новых сообщения
-			Inbox: 5 новых сообщений
-			
-			format = "{0:W;Найден(а,о)} {0} {0:W;запис(ь,и,ей)}, {0:W;удовлетворяющ(ая,их)} запросу.";
-			Console.WriteLine(String.Format(formatter, format, 1));
-			Console.WriteLine(String.Format(formatter, format, 5));
-			Console.WriteLine();
-			
-			Найдена 1 запись, удовлетворяющая запросу.
-			Найдено 5 записей, удовлетворяющих запросу.
-			
-			format = "{0:T}";
-			Console.WriteLine(String.Format(formatter, format, 1));
-			Console.WriteLine(String.Format(formatter, format, 13));
-			Console.WriteLine();
-			
-			Одно
-			Тринадцать
-			 
-			format = "{0:t;f}";
-			Console.WriteLine(String.Format(formatter, format, 1));
-			Console.WriteLine(String.Format(formatter, format, 13));
-			Console.WriteLine();
-			
-			одна
-			тринадцать
-			
-			format = "{0:T;M} {0:W;час(,а,ов)}.";
-			Console.WriteLine(String.Format(formatter, format, 2));
-			Console.WriteLine(String.Format(formatter, format, 10));
-			Console.WriteLine();
-			
-			Два часа.
-			Десять часов.
-			
-			format = "{0:T;M} {0:W;час(,а,ов)} {1:t;F} {1:W;минут(а,ы,)}.";
-			Console.WriteLine(String.Format(formatter, format, 2, 10));
-			Console.WriteLine(String.Format(formatter, format, 21, 1));
-			Console.WriteLine(String.Format(formatter, format, 0, 0));
-			Console.WriteLine();
-			
-			Два часа десять минут.
-			Двадцать один час одна минута.
-			Ноль часов ноль минут.
-			
-			format = "{0:T;M} {0:W;рубл(ь,я,ей)} {1:00} коп.";
-			Console.WriteLine(String.Format(formatter, format, 1, 12));
-			Console.WriteLine(String.Format(formatter, format, 3, 5));
-			Console.WriteLine(String.Format(formatter, format, -3, -5));
-			Console.WriteLine();
-			
-			Один рубль 12 коп.
-			Три рубля 05 коп.
-			Минус три рубля -05 коп.
-			
-
-			formatter.CultureInfo = new CultureInfo("en-US");
- 
-			format = "{0:T} {0:W;dollar(,s)} and {1:t} {1:W;cent(,s)}.";
-			Console.WriteLine(String.Format(formatter, format, 1, 2));
-			Console.WriteLine(String.Format(formatter, format, 123456, 7));
-			Console.WriteLine();
-			
-			One dollar and two cents.
-			One hundred and twenty-three thousand, four hundred and fifty-six dollars and seven cents.
-			
-
-			format = "{0:T} {0:W;hour(,s)} and {1:t} {1:W;minute(,s)}.";
-			string text = String.Format(formatter, format,
-				DateTime.Now.Hour, DateTime.Now.Minute);
-			
-			Four hours and one minute.
-			
-			
-			
- - diff --git a/PawnHunter.Numerals/Gender.cs b/PawnHunter.Numerals/Gender.cs new file mode 100644 index 0000000..9b061a4 --- /dev/null +++ b/PawnHunter.Numerals/Gender.cs @@ -0,0 +1,11 @@ +namespace PawnHunter.Numerals +{ + public enum Gender + { + Masculine, + + Feminine, + + Neutral + } +} diff --git a/PawnHunter.Numerals/Language.cs b/PawnHunter.Numerals/Language.cs new file mode 100644 index 0000000..4863d18 --- /dev/null +++ b/PawnHunter.Numerals/Language.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; +using System.Globalization; +using PawnHunter.Numerals.Languages; + +namespace PawnHunter.Numerals +{ + public abstract class Language + { + protected static long[] RankValues = + { + 1000000000000000000, + 1000000000000000, + 1000000000000, + 1000000000, + 1000000, + 1000, + 1 + }; + + private static readonly Language + Ru = new RussianLanguage(), + En = new EnglishLanguage(), + Ua = new UkrainianLanguage(); + + public abstract string NumberInWords(long number, Gender gender, bool uppercaseFirstLetter); + + public static Language GetLanguage(CultureInfo cultureInfo) + { + switch (cultureInfo.LCID & 0x00ff) + { + // + // Add more languages here. ;) + // + case 0x09: + return En; + case 0x19: + return Ru; + case 0x22: + return Ua; + default: + return null; + } + } + } +} diff --git a/PawnHunter.Numerals/Languages/EnglishLanguage.cs b/PawnHunter.Numerals/Languages/EnglishLanguage.cs new file mode 100644 index 0000000..a48b5ac --- /dev/null +++ b/PawnHunter.Numerals/Languages/EnglishLanguage.cs @@ -0,0 +1,101 @@ +using System; +using System.Text; + +namespace PawnHunter.Numerals.Languages +{ + public sealed class EnglishLanguage : Language + { + private static readonly string[] Ranks = + { + " quintillion", " quadrillion", " trillion", + " billion", " million", " thousand", "" + }; + + private static readonly string[] Tens = + { + "", "ten", "twenty", "thirty", "forty", "fifty", + "sixty", "seventy", "eighty", "ninety" + }; + + private static readonly string[] Units = + { + "zero", "one", "two", "three", "four", "five", + "six", "seven", "eight", "nine", "ten", + "eleven", "twelve", "thirteen", "fourteen", "fifteen", + "sixteen", "seventeen", "eighteen", "nineteen" + }; + + + #region Overrides of Language + + /// + public override string NumberInWords(long number, Gender gender, bool uppercaseFirstLetter) + { + if (number == 0) + { + if (uppercaseFirstLetter) return char.ToUpper(Units[0][0]) + Units[0].Remove(0, 1); + return Units[0]; + } + + var isNegative = false; + if (number < 0) + { + isNegative = true; + number = Math.Abs(number); + } + + var sb = new StringBuilder(); + for (var i = 0; i < RankValues.Length; i++) + { + var rankValue = (int) (number / RankValues[i]); + number %= RankValues[i]; + + if (rankValue <= 0) continue; + var hundreds = rankValue / 100; + var tens = rankValue / 10 % 10; + var units = rankValue % 10; + + if (tens == 1) + { + tens = 0; + units = rankValue % 100; + } + + if (sb.Length > 0) + { + if (hundreds > 0) + sb.Append(", "); + else if (tens + units > 0) + sb.Append(" and "); + } + + if (hundreds > 0) + { + sb.Append(Units[hundreds]); + sb.Append(" hundred"); + if (tens + units > 0) + sb.Append(" and "); + } + + if (tens > 0) + { + sb.Append(Tens[tens]); + if (units > 0) sb.Append("-"); + } + + if (units > 0) + sb.Append(Units[units]); + + sb.Append(Ranks[i]); + } + + if (isNegative) sb.Insert(0, "minus "); + + if (uppercaseFirstLetter) sb[0] = char.ToUpper(sb[0]); + + return sb.ToString(); + } + + #endregion + } +} diff --git a/PawnHunter.Numerals/Languages/RussianLanguage.cs b/PawnHunter.Numerals/Languages/RussianLanguage.cs new file mode 100644 index 0000000..3330e06 --- /dev/null +++ b/PawnHunter.Numerals/Languages/RussianLanguage.cs @@ -0,0 +1,121 @@ +using System; +using System.Text; + +namespace PawnHunter.Numerals.Languages +{ + public class RussianLanguage : Language + { + private static readonly CountableForm[] Ranks = + { + new CountableForm(" квинтильон", " квинтильона", " квинтильонов", Gender.Masculine), + new CountableForm(" квадрильон", " квадрильона", " квадрильонов", Gender.Masculine), + new CountableForm(" триллион", " триллиона", " триллионов", Gender.Masculine), + new CountableForm(" миллиард", " миллиарда", " миллиардов", Gender.Masculine), + new CountableForm(" миллион", " миллиона", " миллионов", Gender.Masculine), + new CountableForm(" тысяча", " тысячи", " тысяч", Gender.Feminine), + new CountableForm("", "", "") + }; + + private static readonly string[] Hundreds = + { + "", "сто", "двести", "триста", "четыреста", "пятьсот", + "шестьсот", "семьсот", "восемьсот", "девятьсот" + }; + + private static readonly string[] Tens = + { + "", "десять", "двадцать", "тридцать", "сорок", "пятьдесят", + "шестьдесят", "семьдесят", "восемьдесят", "девяносто" + }; + + private static readonly string[] Units = + { + "ноль", "один", "два", "три", "четыре", "пять", + "шесть", "семь", "восемь", "девять", "десять", "одиннадцать", + "двенадцать", "тринадцать", "четырнадцать", "пятнадцать", + "шестнадцать", "семнадцать", "восемнадцать", "девятнадцать" + }; + + private static readonly string[,] Gendered = + { + {"один", "одна", "одно"}, + {"два", "две", "два"} + }; + + + #region Overrides of Language + + /// + public override string NumberInWords(long number, Gender gender, bool uppercaseFirstLetter) + { + if (number == 0) + { + if (uppercaseFirstLetter) return char.ToUpper(Units[0][0]) + Units[0].Remove(0, 1); + return Units[0]; + } + + var isNegative = false; + if (number < 0) + { + isNegative = true; + number = Math.Abs(number); + } + + var sb = new StringBuilder(); + for (var i = 0; i < RankValues.Length; i++) + { + var rankValue = (int) (number / RankValues[i]); + number %= RankValues[i]; + + if (rankValue <= 0) continue; + var hundreds = rankValue / 100; + var tens = rankValue / 10 % 10; + var units = rankValue % 10; + + if (tens == 1) + { + tens = 0; + units = rankValue % 100; + } + + if (sb.Length > 0) sb.Append(" "); + + if (hundreds > 0) + { + sb.Append(Hundreds[hundreds]); + if (tens + units > 0) sb.Append(" "); + } + + if (tens > 0) + { + sb.Append(Tens[tens]); + if (units > 0) sb.Append(" "); + } + + if (units > 0) + { + // mind the gender + if (units < 3) + { + var j = i == RankValues.Length - 1 ? (int) gender : (int) Ranks[i].Gender; + sb.Append(Gendered[units - 1, j]); + } + else + { + sb.Append(Units[units]); + } + } + + sb.Append(Ranks[i].ForNumber(units)); + } + + if (isNegative) sb.Insert(0, "минус "); + + if (uppercaseFirstLetter) sb[0] = char.ToUpper(sb[0]); + + return sb.ToString(); + } + + #endregion + } +} diff --git a/PawnHunter.Numerals/Languages/UkrainianLanguage.cs b/PawnHunter.Numerals/Languages/UkrainianLanguage.cs new file mode 100644 index 0000000..9f81506 --- /dev/null +++ b/PawnHunter.Numerals/Languages/UkrainianLanguage.cs @@ -0,0 +1,125 @@ +// +// Thanks to Andre (http://www.rsdn.ru/Users/Profile.aspx?uid=6921) +// + +using System; +using System.Text; + +namespace PawnHunter.Numerals.Languages +{ + public sealed class UkrainianLanguage : Language + { + private static readonly CountableForm[] Ranks = + { + new CountableForm(" квінтильйон", " квінтильйона", " квінтильйонів", Gender.Masculine), + new CountableForm(" квадрильйон", " квадрильйона", " квадрильйонів", Gender.Masculine), + new CountableForm(" трильйон", " трильйона", " трильйонів", Gender.Masculine), + new CountableForm(" мільярд", " мільярда", " мільярдів", Gender.Masculine), + new CountableForm(" мільйон", " мільйона", " мільйонів", Gender.Masculine), + new CountableForm(" тисяча", " тисячі", " тисяч", Gender.Feminine), + new CountableForm("", "", "") + }; + + private static readonly string[] Hundreds = + { + "", "сто", "двісті", "триста", "чотириста", "п'ятсот", + "шістсот", "сімсот", "вісімсот", "дев'ятсот" + }; + + private static readonly string[] Tens = + { + "", "десять", "двадцять", "тридцять", "сорок", "п'ятдесят", + "шістдесят", "сімдесят", "вісімдесят", "дев'яносто" + }; + + private static readonly string[] Units = + { + "нуль", "один", "два", "три", "чотири", "п'ять", + "шість", "сім", "вісім", "дев'ять", "десять", "одинадцять", + "дванадцять", "тринадцять", "чотирнадцять", "п'ятнадцять", + "шістнадцять", "сімнадцять", "вісімнадцять", "дев'ятнадцять" + }; + + private static readonly string[,] Gendered = + { + {"один", "одна", "одне"}, + {"два", "дві", "двоє"} + }; + + + #region Overrides of Language + + /// + public override string NumberInWords(long number, Gender gender, bool uppercaseFirstLetter) + { + if (number == 0) + { + if (uppercaseFirstLetter) return char.ToUpper(Units[0][0]) + Units[0].Remove(0, 1); + return Units[0]; + } + + var isNegative = false; + if (number < 0) + { + isNegative = true; + number = Math.Abs(number); + } + + var sb = new StringBuilder(); + for (var i = 0; i < RankValues.Length; i++) + { + var rankValue = (int) (number / RankValues[i]); + number %= RankValues[i]; + + if (rankValue <= 0) continue; + var hundreds = rankValue / 100; + var tens = rankValue / 10 % 10; + var units = rankValue % 10; + + if (tens == 1) + { + tens = 0; + units = rankValue % 100; + } + + if (sb.Length > 0) sb.Append(" "); + + if (hundreds > 0) + { + sb.Append(Hundreds[hundreds]); + if (tens + units > 0) sb.Append(" "); + } + + if (tens > 0) + { + sb.Append(Tens[tens]); + if (units > 0) sb.Append(" "); + } + + if (units > 0) + { + // mind the gender + if (units < 3) + { + var j = i == RankValues.Length - 1 ? (int) gender : (int) Ranks[i].Gender; + sb.Append(Gendered[units - 1, j]); + } + else + { + sb.Append(Units[units]); + } + } + + sb.Append(Ranks[i].ForNumber(units)); + } + + if (isNegative) sb.Insert(0, "мінус "); + + if (uppercaseFirstLetter) sb[0] = char.ToUpper(sb[0]); + + return sb.ToString(); + } + + #endregion + } +} diff --git a/PawnHunter.Numerals/Neutral.cs b/PawnHunter.Numerals/Neutral.cs deleted file mode 100644 index 867fc49..0000000 --- a/PawnHunter.Numerals/Neutral.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace PawnHunter.Numerals -{ - internal class Neutral - { - internal static long[] Ranks = new long[] - { - 1000000000000000000, - 1000000000000000, - 1000000000000, - 1000000000, - 1000000, - 1000, - 1 - }; - } -} diff --git a/PawnHunter.Numerals/NumeralsFormatter.cs b/PawnHunter.Numerals/NumeralsFormatter.cs index 612b3b9..32c2abc 100644 --- a/PawnHunter.Numerals/NumeralsFormatter.cs +++ b/PawnHunter.Numerals/NumeralsFormatter.cs @@ -1,249 +1,207 @@ using System; +using System.Collections.Generic; using System.Globalization; namespace PawnHunter.Numerals { - /// - /// Provides formatting of numerals and countable nouns. - /// Russian and english languages are supported. - /// - /// - /// For english numerals the Thousands system of notation is used regardless of the subculture. - /// See http://www.quinion.com/words/articles/numbers.htm for more information. - /// - public sealed class NumeralsFormatter : IFormatProvider, ICustomFormatter - { - #region Fields - - private CultureInfo _cultureInfo; - - #endregion Fields - - #region Properties - - /// - /// Gets or sets the culture for the formatter. - /// - public CultureInfo CultureInfo - { - get => _cultureInfo; - set => _cultureInfo = value; - } - - #endregion Properties - - #region Constructors - - /// - /// Initializes a new instance of the NumeralsFormatter class using the current thread's culture. - /// - public NumeralsFormatter() - { - // the current thread's culture will be used - _cultureInfo = null; - } - - /// - /// Initializes a new instance of the NumeralsFormatter class using the specified culture. - /// - /// CultureInfo representing the culture to be used by the formatter. - public NumeralsFormatter(CultureInfo cultureInfo) - { - _cultureInfo = cultureInfo; - } - - #endregion Constructors - - #region Private methods - - /// - /// Delegates numeral construction to language specific class - /// - private string FormatNumeral(string[] stringFormatParts, long arg) - { - var uppercase = Char.IsUpper(stringFormatParts[0], 0); - - Gender gender; - if (stringFormatParts.Length > 1) - { - try - { - gender = GetGender(stringFormatParts[1]); - } - catch (ArgumentOutOfRangeException ex) - { - throw new FormatException("Unknown gender.", ex); - } - - } - else - { - gender = Gender.Neutral; - } - - var cultureInfo = _cultureInfo ?? CultureInfo.CurrentCulture; - - switch (cultureInfo.LCID & 0x00ff) - { - // - // Add more languages here. ;) - // - - case 0x09: - return English.ToString(arg, uppercase); - - case 0x19: - return Russian.ToString(arg, gender, uppercase); - - case 0x22: - // Thanks to Andre (http://www.rsdn.ru/Users/Profile.aspx?uid=6921) - return Ukrainian.ToString(arg, gender, uppercase); - - default: - throw new FormatException( - $"The '{cultureInfo.DisplayName}' culture is not supported.", - new NotSupportedException()); - } - } - - /// - /// Parses formatting expression, - /// extracts and builds plural forms of a given word, - /// delegates choise of the correct form to a Countable class instance. - /// - private string FormatCountable(string[] stringFormatParts, long arg) - { - var wordFormsString = stringFormatParts[stringFormatParts.Length - 1]; - var wordForms = wordFormsString.Split('('); - - string one, two, five; - - if (wordForms.Length == 2) - { - // looks like "()" format is used - - var stem = wordForms[0]; - var flectionsString = wordForms[1].Remove(wordForms[1].Length - 1, 1); - var flexions = flectionsString.Split(','); - - one = stem + flexions[0]; - two = flexions.Length > 1 ? stem + flexions[1] : one; - five = flexions.Length > 2 ? stem + flexions[2] : two; - } - else - { - // looks like ", , " format is used - - wordForms = wordFormsString.Split(','); - one = wordForms[0]; - two = wordForms.Length > 1 ? wordForms[1] : one; - five = wordForms.Length > 2 ? wordForms[2] : two; - } - - // choose correct form for arg. - var countable = new Countable(one, two, five); - return countable[Math.Abs(arg)]; - } - - /// - /// Performs default formatting. - /// - private static string FormatUnknown(string format, object arg, IFormatProvider formatProvider) - { - var formattable = arg as IFormattable; - - return formattable == null - ? arg.ToString() - : formattable.ToString(format, formatProvider); - } - - #region Helpers - - /// - /// Gets a value indicating whether the type is an integral type. - /// - private static bool IsIntegralType(Type type) - { - return type == typeof(int) - || type == typeof(uint) - || type == typeof(long) - || type == typeof(ulong) - || type == typeof(short) - || type == typeof(ushort) - || type == typeof(byte) - || type == typeof(sbyte) - || type == typeof(char); - } - - private static Gender GetGender(string genderString) - { - switch (genderString.ToUpper()) - { - case "F": - return Gender.Feminine; - - case "M": - return Gender.Masculine; - - case "N": - return Gender.Neutral; - - default: - throw new ArgumentOutOfRangeException("genderString", genderString, "Unexpected gender string."); - } - } - - #endregion Helpers - - #endregion Private methods - - #region IFormatProvider implementation - - object IFormatProvider.GetFormat(Type formatType) - { - return formatType == typeof(ICustomFormatter) - ? this - : CultureInfo.CurrentCulture.GetFormat(formatType); - } - - #endregion IFormatProvider implementation - - #region ICustomFormatter implementation - - /// - /// Formats arg using the specified format. - /// - string ICustomFormatter.Format(string format, object arg, IFormatProvider formatProvider) - { - // NumeralsFormatter processes integral types only. - if (format == null || !IsIntegralType(arg.GetType())) - { - return FormatUnknown(format, arg, formatProvider); - } - - // The base type for NumeralsFormatter is long, - // so check whether arg is within long's range - if (arg is ulong && Convert.ToUInt64(arg) > long.MaxValue) - { - throw new FormatException("Argument is too large.", - new ArgumentOutOfRangeException(nameof(arg), arg, - String.Format("arg must be within -{0} ... {0} range.", long.MaxValue))); - } - - string[] formatStringParts = format.Split(';'); - - // NumeralsFormatter understands only 'T'('t') and 'W'('w') format specifiers. - switch (formatStringParts[0].ToUpper()) - { - case "T": - return FormatNumeral(formatStringParts, Convert.ToInt64(arg)); - case "W": - return FormatCountable(formatStringParts, Convert.ToInt64(arg)); - default: - return FormatUnknown(format, arg, formatProvider); - } - } - - #endregion ICustomFormatter - } + /// + /// Provides formatting of numerals and countable nouns. + /// Russian and english languages are supported. + /// + /// + /// For english numerals the Thousands system of notation is used regardless of the subculture. + /// See http://www.quinion.com/words/articles/numbers.htm for more information. + /// + public sealed class NumeralsFormatter : IFormatProvider, ICustomFormatter + { + /// + /// Initializes a new instance of the NumeralsFormatter class using the current thread's culture. + /// + public NumeralsFormatter() + { + // the current thread's culture will be used + CultureInfo = null; + } + + /// + /// Initializes a new instance of the NumeralsFormatter class using the specified culture. + /// + /// CultureInfo representing the culture to be used by the formatter. + public NumeralsFormatter(CultureInfo cultureInfo) + { + CultureInfo = cultureInfo; + } + + /// + /// Gets or sets the culture for the formatter. + /// + public CultureInfo CultureInfo { get; set; } + + + #region ICustomFormatter implementation + + /// + /// Formats arg using the specified format. + /// + string ICustomFormatter.Format(string format, object arg, IFormatProvider formatProvider) + { + // NumeralsFormatter processes integral types only. + if (format == null || !IsIntegralType(arg.GetType())) return FormatUnknown(format, arg, formatProvider); + + // The base type for NumeralsFormatter is long, + // so check whether arg is within long's range + if (arg is ulong && Convert.ToUInt64(arg) > long.MaxValue) + throw new FormatException("Argument is too large.", + new ArgumentOutOfRangeException(nameof(arg), arg, + string.Format("arg must be within -{0} ... {0} range.", long.MaxValue))); + + var formatStringParts = format.Split(';'); + + // NumeralsFormatter understands only 'T'('t') and 'W'('w') format specifiers. + switch (formatStringParts[0].ToUpper()) + { + case "T": + return FormatNumeral(formatStringParts, Convert.ToInt64(arg)); + case "W": + return FormatCountable(formatStringParts, Convert.ToInt64(arg)); + default: + return FormatUnknown(format, arg, formatProvider); + } + } + + #endregion ICustomFormatter + + + #region IFormatProvider implementation + + object IFormatProvider.GetFormat(Type formatType) + { + return formatType == typeof(ICustomFormatter) + ? this + : CultureInfo.CurrentCulture.GetFormat(formatType); + } + + #endregion IFormatProvider implementation + + + #region Private routines + + /// + /// Delegates numeral construction to language specific class + /// + private string FormatNumeral(string[] stringFormatParts, long arg) + { + var uppercase = char.IsUpper(stringFormatParts[0], 0); + + Gender gender; + if (stringFormatParts.Length > 1) + try + { + gender = GetGender(stringFormatParts[1]); + } + catch (ArgumentOutOfRangeException ex) + { + throw new FormatException("Unknown gender.", ex); + } + else + gender = Gender.Neutral; + + var cultureInfo = CultureInfo ?? CultureInfo.CurrentCulture; + + var language = Language.GetLanguage(cultureInfo); + if (language == null) + throw new FormatException( + $"The '{cultureInfo.DisplayName}' culture is not supported.", + new NotSupportedException()); + return language.NumberInWords(arg, gender, uppercase); + } + + /// + /// Parses formatting expression, + /// extracts and builds plural forms of a given word, + /// delegates choise of the correct form to a CountableForm class instance. + /// + private static string FormatCountable(IReadOnlyList stringFormatParts, long arg) + { + var wordFormsString = stringFormatParts[stringFormatParts.Count - 1]; + var wordForms = wordFormsString.Split('('); + + string one, two, five; + + if (wordForms.Length == 2) + { + // looks like "()" format is used + + var stem = wordForms[0]; + var flectionsString = wordForms[1].Remove(wordForms[1].Length - 1, 1); + var flexions = flectionsString.Split(','); + + one = stem + flexions[0]; + two = flexions.Length > 1 ? stem + flexions[1] : one; + five = flexions.Length > 2 ? stem + flexions[2] : two; + } + else + { + // looks like ", , " format is used + + wordForms = wordFormsString.Split(','); + one = wordForms[0]; + two = wordForms.Length > 1 ? wordForms[1] : one; + five = wordForms.Length > 2 ? wordForms[2] : two; + } + + // choose correct form for arg. + var countable = new CountableForm(one, two, five); + return countable.ForNumber(Math.Abs(arg)); + } + + /// + /// Performs default formatting. + /// + private static string FormatUnknown(string format, object arg, IFormatProvider formatProvider) + { + return !(arg is IFormattable formattable) + ? arg.ToString() + : formattable.ToString(format, formatProvider); + } + + #endregion Private routines + + + #region Static helpers + + /// + /// Gets a value indicating whether the type is an integral type. + /// + private static bool IsIntegralType(Type type) + { + return type == typeof(int) + || type == typeof(uint) + || type == typeof(long) + || type == typeof(ulong) + || type == typeof(short) + || type == typeof(ushort) + || type == typeof(byte) + || type == typeof(sbyte) + || type == typeof(char); + } + + private static Gender GetGender(string genderString) + { + if (genderString == null) throw new ArgumentNullException(nameof(genderString)); + if (genderString.Length == 1) + switch (char.ToUpperInvariant(genderString[0])) + { + case 'F': + return Gender.Feminine; + case 'M': + return Gender.Masculine; + case 'N': + return Gender.Neutral; + } + throw new ArgumentOutOfRangeException(nameof(genderString), genderString, "Unexpected gender string."); + } + + #endregion Static helpers + } } diff --git a/PawnHunter.Numerals/PawnHunter.Numerals.csproj b/PawnHunter.Numerals/PawnHunter.Numerals.csproj index 89c4cbe..e8637d5 100644 --- a/PawnHunter.Numerals/PawnHunter.Numerals.csproj +++ b/PawnHunter.Numerals/PawnHunter.Numerals.csproj @@ -1,39 +1,26 @@  + - net45;net46;net47;netstandard20 - $(LibraryFrameworks) + PawnHunter.Numerals + string;format;formatting;IFormatProvider;numerals;nouns;countable;masculine;feminine;russian;ukrainian;english + Numerals String.Format provider + Provides formatting of numerals and countable nouns. Russian, ukrainian and english languages are supported. + Pawn Hunter;Aleksey Gallyamov + https://github.com/hVostt/PawnHunter.Numerals + false + https://github.com/hVostt/PawnHunter.Numerals + git + Add support .NET Standard 2.0, some refactoring + HotMinds + net45;netstandard2.0 + 1.1.0 latest - Debug;Release - false - false - false - false - false - false - false - false + true - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - + + + + - \ No newline at end of file + +
diff --git a/PawnHunter.Numerals/Properties/AssemblyInfo.cs b/PawnHunter.Numerals/Properties/AssemblyInfo.cs deleted file mode 100644 index 6b2daef..0000000 --- a/PawnHunter.Numerals/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("PawnHunter.Numerals")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("PawnHunter.Numerals")] -[assembly: AssemblyCopyright("Copyright © PawnHunter 2012")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("9663dabe-b5b8-40bf-b5df-c8c3bdb3a29e")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/PawnHunter.Numerals/Russian.cs b/PawnHunter.Numerals/Russian.cs deleted file mode 100644 index 6f48fd2..0000000 --- a/PawnHunter.Numerals/Russian.cs +++ /dev/null @@ -1,145 +0,0 @@ -using System; -using System.Text; - -namespace PawnHunter.Numerals -{ - public sealed class Russian - { - #region Fields - - private static readonly Countable[] Ranks = new[] - { - new Countable(" квинтильон", " квинтильона", " квинтильонов", Gender.Masculine), - new Countable(" квадрильон", " квадрильона", " квадрильонов", Gender.Masculine), - new Countable(" триллион", " триллиона", " триллионов", Gender.Masculine), - new Countable(" миллиард", " миллиарда", " миллиардов", Gender.Masculine), - new Countable(" миллион", " миллиона", " миллионов", Gender.Masculine), - new Countable(" тысяча", " тысячи", " тысяч", Gender.Feminine), - new Countable("", "", "") - }; - - private static readonly string[] Hundreds = new[] - { - "", "сто", "двести", "триста", "четыреста", "пятьсот", - "шестьсот", "семьсот", "восемьсот", "девятьсот" - }; - - private static readonly string[] Tens = new[] - { - "", "десять", "двадцать", "тридцать", "сорок", "пятьдесят", - "шестьдесят", "семьдесят", "восемьдесят", "девяносто" - }; - - private static readonly string[] Units = new[] - { - "ноль", "один", "два", "три", "четыре", "пять", - "шесть", "семь", "восемь", "девять", "десять", "одиннадцать", - "двенадцать", "тринадцать", "четырнадцать", "пятнадцать", - "шестнадцать", "семнадцать", "восемнадцать", "девятнадцать" - }; - - private static readonly string[,] Gendered = new[,] - { - { "один", "одна", "одно" }, - { "два", "две", "два" } - }; - - #endregion Fields - - #region Methods - - public static string ToString(long number, Gender gender) - { - return ToString(number, gender, false); - } - - public static string ToString(long number, Gender gender, bool uppercase) - { - if (number == 0) - { - if (uppercase) - { - return Char.ToUpper(Units[0][0]) + Units[0].Remove(0, 1); - } - return Units[0]; - } - - var isNegative = false; - if (number < 0) - { - isNegative = true; - number = Math.Abs(number); - } - - var sb = new StringBuilder(); - for (var i = 0; i < Neutral.Ranks.Length; i++) - { - var rankValue = (int)(number / Neutral.Ranks[i]); - number %= Neutral.Ranks[i]; - - if (rankValue <= 0) continue; - var hundreds = rankValue / 100; - var tens = rankValue / 10 % 10; - var units = rankValue % 10; - - if (tens == 1) - { - tens = 0; - units = rankValue % 100; - } - - if (sb.Length > 0) - { - sb.Append(" "); - } - - if (hundreds > 0) - { - sb.Append(Hundreds[hundreds]); - if (tens + units > 0) - { - sb.Append(" "); - } - } - - if (tens > 0) - { - sb.Append(Tens[tens]); - if (units > 0) - { - sb.Append(" "); - } - } - - if (units > 0) - { - // mind the gender - if (units < 3) - { - var j = i == Neutral.Ranks.Length - 1 ? (int)gender : (int)Ranks[i].Gender; - sb.Append(Gendered[units - 1, j]); - } - else - { - sb.Append(Units[units]); - } - } - sb.Append(Ranks[i][units]); - } - - if (isNegative) - { - sb.Insert(0, "минус "); - } - - if (uppercase) - { - sb[0] = Char.ToUpper(sb[0]); - } - - return sb.ToString(); - } - - #endregion Methods - } -} diff --git a/PawnHunter.Numerals/Ukrainian.cs b/PawnHunter.Numerals/Ukrainian.cs deleted file mode 100644 index e30f7b2..0000000 --- a/PawnHunter.Numerals/Ukrainian.cs +++ /dev/null @@ -1,149 +0,0 @@ -// -// Thanks to Andre (http://www.rsdn.ru/Users/Profile.aspx?uid=6921) -// - -using System; -using System.Text; - -namespace PawnHunter.Numerals -{ - public sealed class Ukrainian - { - #region Fields - - private static readonly Countable[] Ranks = new[] - { - new Countable(" квінтильйон", " квінтильйона", " квінтильйонів", Gender.Masculine), - new Countable(" квадрильйон", " квадрильйона", " квадрильйонів", Gender.Masculine), - new Countable(" трильйон", " трильйона", " трильйонів", Gender.Masculine), - new Countable(" мільярд", " мільярда", " мільярдів", Gender.Masculine), - new Countable(" мільйон", " мільйона", " мільйонів", Gender.Masculine), - new Countable(" тисяча", " тисячі", " тисяч", Gender.Feminine), - new Countable("", "", "") - }; - - private static readonly string[] Hundreds = new[] - { - "", "сто", "двісті", "триста", "чотириста", "п'ятсот", - "шістсот", "сімсот", "вісімсот", "дев'ятсот" - }; - - private static readonly string[] Tens = new[] - { - "", "десять", "двадцять", "тридцять", "сорок", "п'ятдесят", - "шістдесят", "сімдесят", "вісімдесят", "дев'яносто" - }; - - private static readonly string[] Units = new[] - { - "нуль", "один", "два", "три", "чотири", "п'ять", - "шість", "сім", "вісім", "дев'ять", "десять", "одинадцять", - "дванадцять", "тринадцять", "чотирнадцять", "п'ятнадцять", - "шістнадцять", "сімнадцять", "вісімнадцять", "дев'ятнадцять" - }; - - private static readonly string[,] Gendered = new[,] - { - { "один", "одна", "одне" }, - { "два", "дві", "двоє" } - }; - - #endregion Fields - - #region Methods - - public static string ToString(long number, Gender gender) - { - return ToString(number, gender, false); - } - - public static string ToString(long number, Gender gender, bool uppercase) - { - if (number == 0) - { - if (uppercase) - { - return Char.ToUpper(Units[0][0]) + Units[0].Remove(0, 1); - } - return Units[0]; - } - - var isNegative = false; - if (number < 0) - { - isNegative = true; - number = Math.Abs(number); - } - - var sb = new StringBuilder(); - for (var i = 0; i < Neutral.Ranks.Length; i++) - { - var rankValue = (int)(number / Neutral.Ranks[i]); - number %= Neutral.Ranks[i]; - - if (rankValue <= 0) continue; - var hundreds = rankValue / 100; - var tens = rankValue / 10 % 10; - var units = rankValue % 10; - - if (tens == 1) - { - tens = 0; - units = rankValue % 100; - } - - if (sb.Length > 0) - { - sb.Append(" "); - } - - if (hundreds > 0) - { - sb.Append(Hundreds[hundreds]); - if (tens + units > 0) - { - sb.Append(" "); - } - } - - if (tens > 0) - { - sb.Append(Tens[tens]); - if (units > 0) - { - sb.Append(" "); - } - } - - if (units > 0) - { - // mind the gender - if (units < 3) - { - var j = i == Neutral.Ranks.Length - 1 ? (int)gender : (int)Ranks[i].Gender; - sb.Append(Gendered[units - 1, j]); - } - else - { - sb.Append(Units[units]); - } - } - sb.Append(Ranks[i][units]); - } - - if (isNegative) - { - sb.Insert(0, "мінус "); - } - - if (uppercase) - { - sb[0] = Char.ToUpper(sb[0]); - } - - return sb.ToString(); - } - - #endregion Methods - } -}