diff --git a/release_notes.md b/release_notes.md index 44f8f340c..6bdf0cd55 100644 --- a/release_notes.md +++ b/release_notes.md @@ -4,6 +4,8 @@ - [#221](https://github.com/Mehdik/Humanizer/pull/221): Added Russian ordinalizer - [#228](https://github.com/Mehdik/Humanizer/pull/228): Fixed the "twenties" in SpanishNumberToWordsConverter - [#231](https://github.com/Mehdik/Humanizer/pull/231): Added more settings for FromNow, Dual and Plural (Arabic) + - [#232](https://github.com/Mehdik/Humanizer/pull/232): Adding code & tests to handle Arabic numbers to ordinal + [Commits](https://github.com/MehdiK/Humanizer/compare/v1.22.1...master) diff --git a/src/Humanizer.Tests/Localisation/ar/DateHumanizeTests.cs b/src/Humanizer.Tests/Localisation/ar/DateHumanizeTests.cs index dcacec886..51ba125be 100644 --- a/src/Humanizer.Tests/Localisation/ar/DateHumanizeTests.cs +++ b/src/Humanizer.Tests/Localisation/ar/DateHumanizeTests.cs @@ -98,6 +98,7 @@ public void SecondsAgo(int seconds, string expected) } [Theory] + [InlineData(0, "الآن")] [InlineData(1, "في غضون ثانية واحدة من الآن")] [InlineData(2, "في غضون ثانيتين من الآن")] [InlineData(10, "في غضون 10 ثوان من الآن")] diff --git a/src/Humanizer.Tests/Localisation/ar/NumberToWordsTests.cs b/src/Humanizer.Tests/Localisation/ar/NumberToWordsTests.cs index 33e82ae3e..d318ecf2b 100644 --- a/src/Humanizer.Tests/Localisation/ar/NumberToWordsTests.cs +++ b/src/Humanizer.Tests/Localisation/ar/NumberToWordsTests.cs @@ -19,5 +19,59 @@ public void ToWordsArabic(string expected, int number) { Assert.Equal(expected, number.ToWords()); } + + [Theory] + [InlineData(0, "الصفر")] + [InlineData(1, "الأول")] + [InlineData(2, "الثاني")] + [InlineData(3, "الثالث")] + [InlineData(4, "الرابع")] + [InlineData(5, "الخامس")] + [InlineData(6, "السادس")] + [InlineData(7, "السابع")] + [InlineData(8, "الثامن")] + [InlineData(9, "التاسع")] + [InlineData(10, "العاشر")] + [InlineData(11, "الحادي عشر")] + [InlineData(12, "الثاني عشر")] + [InlineData(13, "الثالث عشر")] + [InlineData(14, "الرابع عشر")] + [InlineData(15, "الخامس عشر")] + [InlineData(16, "السادس عشر")] + [InlineData(17, "السابع عشر")] + [InlineData(18, "الثامن عشر")] + [InlineData(19, "التاسع عشر")] + [InlineData(20, "العشرون")] + [InlineData(21, "الحادي و العشرون")] + [InlineData(22, "الثاني و العشرون")] + [InlineData(30, "الثلاثون")] + [InlineData(40, "الأربعون")] + [InlineData(50, "الخمسون")] + [InlineData(60, "الستون")] + [InlineData(70, "السبعون")] + [InlineData(80, "الثمانون")] + [InlineData(90, "التسعون")] + [InlineData(95, "الخامس و التسعون")] + [InlineData(96, "السادس و التسعون")] + [InlineData(100, "المئة")] + [InlineData(120, "العشرون بعد المئة")] + [InlineData(121, "الحادي و العشرون بعد المئة")] + [InlineData(200, "المئتان")] + [InlineData(221, "الحادي و العشرون بعد المئتان")] + [InlineData(300, "الثلاث مئة")] + [InlineData(321, "الحادي و العشرون بعد الثلاث مئة")] + [InlineData(327, "السابع و العشرون بعد الثلاث مئة")] + [InlineData(1000, "الألف")] + [InlineData(1001, "الأول بعد الألف")] + [InlineData(1021, "الحادي و العشرون بعد الألف")] + [InlineData(10000, "العشرة آلاف")] + [InlineData(10121, "الحادي و العشرون بعد العشرة آلاف و مئة")] + [InlineData(100000, "المئة ألف")] + [InlineData(1000000, "المليون")] + [InlineData(1020135, "الخامس و الثلاثون بعد المليون و عشرون ألفاً و مئة")] + public void ToOrdinalWords(int number, string words) + { + Assert.Equal(words, number.ToOrdinalWords()); + } } } diff --git a/src/Humanizer/Localisation/NumberToWords/ArabicNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/ArabicNumberToWordsConverter.cs index a4104c69b..651e25a7b 100644 --- a/src/Humanizer/Localisation/NumberToWords/ArabicNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/ArabicNumberToWordsConverter.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; namespace Humanizer.Localisation.NumberToWords { @@ -111,5 +113,89 @@ public override string Convert(int number) return result.Trim(); } + + private static readonly Dictionary OrdinalExceptions = new Dictionary + { + {"واحد", "الحادي"}, + {"أحد", "الحادي"}, + {"اثنان", "الثاني"}, + {"اثنا", "الثاني"}, + {"ثلاثة", "الثالث"}, + {"أربعة", "الرابع"}, + {"خمسة", "الخامس"}, + {"ستة", "السادس"}, + {"سبعة", "السابع"}, + {"ثمانية", "الثامن"}, + {"تسعة", "التاسع"}, + {"عشرة", "العاشر"}, + }; + + public override string ConvertToOrdinal(int number) + { + if (number == 0) return "الصفر"; + var beforeOneHundredNumber = number%100; + var overTensPart = number/100*100; + var beforeOneHundredWord = string.Empty; + var overTensWord = string.Empty; + + if (beforeOneHundredNumber > 0) + { + beforeOneHundredWord = Convert(beforeOneHundredNumber); + beforeOneHundredWord = ParseNumber(beforeOneHundredWord, beforeOneHundredNumber); + } + + if (overTensPart > 0) + { + overTensWord = Convert(overTensPart); + overTensWord = ParseNumber(overTensWord, overTensPart); + } + + var word = + beforeOneHundredWord + (overTensPart > 0 + ? (string.IsNullOrWhiteSpace(beforeOneHundredWord) ? string.Empty : " بعد ") + overTensWord + : string.Empty + ); + return word.Trim(); + } + + private static string ParseNumber(string word, int number) + { + if (number == 1) return "الأول"; + + if (number <= 10) + { + foreach (var kv in OrdinalExceptions.Where(kv => word.EndsWith(kv.Key))) + { + // replace word with exception + return word.Substring(0, word.Length - kv.Key.Length) + kv.Value; + } + } + else if (number > 10 && number < 100) + { + var parts = word.Split(' '); + var newParts = new string[parts.Length]; + int count = 0; + foreach (var part in parts) + { + var newPart = part; + var oldPart = part; + foreach (var kv in OrdinalExceptions.Where(kv => oldPart.EndsWith(kv.Key))) + { + // replace word with exception + newPart = oldPart.Substring(0, oldPart.Length - kv.Key.Length) + kv.Value; + } + if (number > 19 && newPart == oldPart && oldPart.Length > 1) + newPart = "ال" + oldPart; + newParts[count++] = newPart; + } + word = string.Join(" ", newParts); + } + else + { + word = "ال"+word; + } + + return word; + } } } \ No newline at end of file diff --git a/src/Humanizer/Properties/Resources.ar.resx b/src/Humanizer/Properties/Resources.ar.resx index b20210703..f70c4b314 100644 --- a/src/Humanizer/Properties/Resources.ar.resx +++ b/src/Humanizer/Properties/Resources.ar.resx @@ -397,4 +397,8 @@ في غضون سنة واحدة من الآن - + + الآن + now + + \ No newline at end of file