diff --git a/NuSpecs/Humanizer.Core.az.nuspec b/NuSpecs/Humanizer.Core.az.nuspec new file mode 100644 index 000000000..dd5367439 --- /dev/null +++ b/NuSpecs/Humanizer.Core.az.nuspec @@ -0,0 +1,25 @@ + + + + Humanizer.Core.az + $version$ + Humanizer Locale (az) + Mehdi Khalili, Claire Novotny + https://github.com/Humanizr/Humanizer + logo.png + false + Humanizer Locale Azerbaijani (az) + Copyright (c) .NET Foundation and Contributors + MIT + + az + + + + + + + + + + \ No newline at end of file diff --git a/src/Humanizer.All.sln b/src/Humanizer.All.sln index 0111bf446..c887ea6f0 100644 --- a/src/Humanizer.All.sln +++ b/src/Humanizer.All.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26228.0 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30114.105 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Humanizer.Tests", "Humanizer.Tests\Humanizer.Tests.csproj", "{F886A8DA-3EFC-4A89-91DD-06FAF13DA172}" EndProject @@ -87,7 +87,6 @@ Global GlobalSection(SharedMSBuildProjectFiles) = preSolution Humanizer.Tests.Shared\Humanizer.Tests.Shared.projitems*{261367ca-00c7-4832-bd26-200cff8f575f}*SharedItemsImports = 4 Humanizer.Tests.Shared\Humanizer.Tests.Shared.projitems*{f0dc7771-ce59-4f3c-b160-56f661f5d220}*SharedItemsImports = 4 - Humanizer.Tests.Shared\Humanizer.Tests.Shared.projitems*{f886a8da-3efc-4a89-91dd-06faf13da172}*SharedItemsImports = 4 Humanizer.Tests.Shared\Humanizer.Tests.Shared.projitems*{fdec244b-f07e-4a5e-bb80-fbc6ac77a9aa}*SharedItemsImports = 13 EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -189,4 +188,7 @@ Global {F0DC7771-CE59-4F3C-B160-56F661F5D220} = {04B74BEC-A645-4D1A-BE21-F4EB4413A903} {AA449265-E001-486D-A0F4-04ACF0C83DC1} = {4779A7C9-9ED8-4146-A158-FBE0B1BE09D9} EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E4063A70-EE14-49AE-A6C4-3108B1CA70EF} + EndGlobalSection EndGlobal diff --git a/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems b/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems index c39bb923a..2ca708a9f 100644 --- a/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems +++ b/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems @@ -40,6 +40,9 @@ + + + diff --git a/src/Humanizer.Tests.Shared/Localisation/az/DateHumanizeTests.cs b/src/Humanizer.Tests.Shared/Localisation/az/DateHumanizeTests.cs new file mode 100644 index 000000000..6ad1ba80d --- /dev/null +++ b/src/Humanizer.Tests.Shared/Localisation/az/DateHumanizeTests.cs @@ -0,0 +1,113 @@ +using Humanizer.Localisation; +using Xunit; + +namespace Humanizer.Tests.Localisation.az +{ + [UseCulture("az")] + public class DateHumanizeTests + { + + [Theory] + [InlineData(1, "bir saniyə əvvəl")] + [InlineData(10, "10 saniyə əvvəl")] + public void SecondsAgo(int seconds, string expected) + { + DateHumanize.Verify(expected, seconds, TimeUnit.Second, Tense.Past); + } + + [Theory] + [InlineData(1, "bir saniyə sonra")] + [InlineData(10, "10 saniyə sonra")] + public void SecondsFromNow(int seconds, string expected) + { + DateHumanize.Verify(expected, seconds, TimeUnit.Second, Tense.Future); + } + + [Theory] + [InlineData(1, "bir dəqiqə əvvəl")] + [InlineData(10, "10 dəqiqə əvvəl")] + [InlineData(60, "bir saat əvvəl")] + public void MinutesAgo(int minutes, string expected) + { + DateHumanize.Verify(expected, minutes, TimeUnit.Minute, Tense.Past); + } + + [Theory] + [InlineData(1, "bir dəqiqə sonra")] + [InlineData(10, "10 dəqiqə sonra")] + public void MinutesFromNow(int minutes, string expected) + { + DateHumanize.Verify(expected, minutes, TimeUnit.Minute, Tense.Future); + } + + [Theory] + [InlineData(1, "bir saat əvvəl")] + [InlineData(10, "10 saat əvvəl")] + public void HoursAgo(int hours, string expected) + { + DateHumanize.Verify(expected, hours, TimeUnit.Hour, Tense.Past); + } + + [Theory] + [InlineData(1, "bir saat sonra")] + [InlineData(10, "10 saat sonra")] + public void HoursFromNow(int hours, string expected) + { + DateHumanize.Verify(expected, hours, TimeUnit.Hour, Tense.Future); + } + + [Theory] + [InlineData(1, "dünən")] + [InlineData(10, "10 gün əvvəl")] + public void DaysAgo(int days, string expected) + { + DateHumanize.Verify(expected, days, TimeUnit.Day, Tense.Past); + } + + [Theory] + [InlineData(1, "sabah")] + [InlineData(10, "10 gün sonra")] + public void DaysFromNow(int days, string expected) + { + DateHumanize.Verify(expected, days, TimeUnit.Day, Tense.Future); + } + + [Theory] + [InlineData(1, "bir ay əvvəl")] + [InlineData(10, "10 ay əvvəl")] + public void MonthsAgo(int months, string expected) + { + DateHumanize.Verify(expected, months, TimeUnit.Month, Tense.Past); + } + + [Theory] + [InlineData(1, "bir ay sonra")] + [InlineData(10, "10 ay sonra")] + public void MonthsFromNow(int months, string expected) + { + DateHumanize.Verify(expected, months, TimeUnit.Month, Tense.Future); + } + + [Theory] + [InlineData(1, "bir il əvvəl")] + [InlineData(2, "2 il əvvəl")] + public void YearsAgo(int years, string expected) + { + DateHumanize.Verify(expected, years, TimeUnit.Year, Tense.Past); + } + + [Theory] + [InlineData(1, "bir il sonra")] + [InlineData(2, "2 il sonra")] + public void YearsFromNow(int years, string expected) + { + DateHumanize.Verify(expected, years, TimeUnit.Year, Tense.Future); + } + + [Fact] + public void Now() + { + DateHumanize.Verify("indi", 0, TimeUnit.Year, Tense.Future); + } + } +} diff --git a/src/Humanizer.Tests.Shared/Localisation/az/NumberToWordsTests.cs b/src/Humanizer.Tests.Shared/Localisation/az/NumberToWordsTests.cs new file mode 100644 index 000000000..587cbd8ca --- /dev/null +++ b/src/Humanizer.Tests.Shared/Localisation/az/NumberToWordsTests.cs @@ -0,0 +1,73 @@ +using Xunit; + +namespace Humanizer.Tests.Localisation.az +{ + [UseCulture("az")] + public class NumberToWordsTests + { + [Theory] + [InlineData("sıfır", 0)] + [InlineData("bir", 1)] + [InlineData("iki", 2)] + [InlineData("on", 10)] + [InlineData("yüz on iki", 112)] + [InlineData("min dörd yüz qırx", 1440)] + [InlineData("yirmi iki", 22)] + [InlineData("on bir", 11)] + [InlineData("üç min beş yüz bir", 3501)] + [InlineData("bir milyon bir", 1000001)] + [InlineData("mənfi bir milyon üç yüz qırx altı min yeddi yüz on bir", -1346711)] + public void ToWords(string expected, int number) + { + Assert.Equal(expected, number.ToWords()); + } + + [Theory] + [InlineData(0, "sıfırıncı")] + [InlineData(1, "birinci")] + [InlineData(2, "ikinci")] + [InlineData(3, "üçüncü")] + [InlineData(4, "dördüncü")] + [InlineData(5, "beşinci")] + [InlineData(6, "altıncı")] + [InlineData(7, "yeddinci")] + [InlineData(8, "səkkizinci")] + [InlineData(9, "doqquzuncu")] + [InlineData(10, "onuncu")] + [InlineData(11, "on birinci")] + [InlineData(12, "on ikinci")] + [InlineData(13, "on üçüncü")] + [InlineData(14, "on dördüncü")] + [InlineData(15, "on beşinci")] + [InlineData(16, "on altıncı")] + [InlineData(17, "on yeddinci")] + [InlineData(18, "on səkkizinci")] + [InlineData(19, "on doqquzuncu")] + [InlineData(20, "yirminci")] + [InlineData(21, "yirmi birinci")] + [InlineData(30, "otuzuncu")] + [InlineData(40, "qırxıncı")] + [InlineData(50, "əllinci")] + [InlineData(60, "altmışıncı")] + [InlineData(70, "yetmişinci")] + [InlineData(80, "səksəninci")] + [InlineData(90, "doxsanıncı")] + [InlineData(100, "yüzüncü")] + [InlineData(120, "yüz yirminci")] + [InlineData(121, "yüz yirmi birinci")] + [InlineData(200, "iki yüzüncü")] + [InlineData(221, "iki yüz yirmi birinci")] + [InlineData(300, "üç yüzüncü")] + [InlineData(321, "üç yüz yirmi birinci")] + [InlineData(1000, "mininci")] + [InlineData(1001, "min birinci")] + [InlineData(10000, "on mininci")] + [InlineData(100000, "yüz mininci")] + [InlineData(1000000, "bir milyonuncu")] + [InlineData(1022135, "bir milyon yirmi iki min yüz otuz beşinci")] + public void ToOrdinalWords(int number, string words) + { + Assert.Equal(words, number.ToOrdinalWords()); + } + } +} diff --git a/src/Humanizer.Tests.Shared/Localisation/az/TimeSpanHumanizeTests.cs b/src/Humanizer.Tests.Shared/Localisation/az/TimeSpanHumanizeTests.cs new file mode 100644 index 000000000..87a691aca --- /dev/null +++ b/src/Humanizer.Tests.Shared/Localisation/az/TimeSpanHumanizeTests.cs @@ -0,0 +1,102 @@ +using System; +using Xunit; + +namespace Humanizer.Tests.Localisation.az +{ + [UseCulture("az")] + public class TimeSpanHumanizeTests + { + + [Theory] + [Trait("Translation", "Google")] + [InlineData(366, "1 il")] + [InlineData(731, "2 il")] + [InlineData(1096, "3 il")] + [InlineData(4018, "11 il")] + public void Years(int days, string expected) + { + Assert.Equal(expected, TimeSpan.FromDays(days).Humanize(maxUnit: Humanizer.Localisation.TimeUnit.Year)); + } + + [Theory] + [Trait("Translation", "Google")] + [InlineData(31, "1 ay")] + [InlineData(61, "2 ay")] + [InlineData(92, "3 ay")] + [InlineData(335, "11 ay")] + public void Months(int days, string expected) + { + Assert.Equal(expected, TimeSpan.FromDays(days).Humanize(maxUnit: Humanizer.Localisation.TimeUnit.Year)); + } + + [Theory] + [InlineData(14, "2 həftə")] + [InlineData(7, "1 həftə")] + public void Weeks(int days, string expected) + { + var actual = TimeSpan.FromDays(days).Humanize(); + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(6, "6 gün")] + [InlineData(2, "2 gün")] + public void Days(int days, string expected) + { + var actual = TimeSpan.FromDays(days).Humanize(); + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(2, "2 saat")] + [InlineData(1, "1 saat")] + public void Hours(int hours, string expected) + { + var actual = TimeSpan.FromHours(hours).Humanize(); + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(2, "2 dəqiqə")] + [InlineData(1, "1 dəqiqə")] + public void Minutes(int minutes, string expected) + { + var actual = TimeSpan.FromMinutes(minutes).Humanize(); + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(2, "2 saniyə")] + [InlineData(1, "1 saniyə")] + public void Seconds(int seconds, string expected) + { + var actual = TimeSpan.FromSeconds(seconds).Humanize(); + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(2, "2 millisaniyə")] + [InlineData(1, "1 millisaniyə")] + public void Milliseconds(int ms, string expected) + { + var actual = TimeSpan.FromMilliseconds(ms).Humanize(); + Assert.Equal(expected, actual); + } + + [Fact] + public void NoTime() + { + var noTime = TimeSpan.Zero; + var actual = noTime.Humanize(); + Assert.Equal("0 millisaniyə", actual); + } + + [Fact] + public void NoTimeToWords() + { + var noTime = TimeSpan.Zero; + var actual = noTime.Humanize(toWords: true); + Assert.Equal("zaman fərqi yoxdur", actual); + } + } +} diff --git a/src/Humanizer.sln b/src/Humanizer.sln index c633f5497..dccb47691 100644 --- a/src/Humanizer.sln +++ b/src/Humanizer.sln @@ -38,6 +38,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NuSpecs", "NuSpecs", "{AA44 ProjectSection(SolutionItems) = preProject ..\NuSpecs\Humanizer.Core.af.nuspec = ..\NuSpecs\Humanizer.Core.af.nuspec ..\NuSpecs\Humanizer.Core.ar.nuspec = ..\NuSpecs\Humanizer.Core.ar.nuspec + ..\NuSpecs\Humanizer.Core.az.nuspec = ..\NuSpecs\Humanizer.Core.az.nuspec ..\NuSpecs\Humanizer.Core.bg.nuspec = ..\NuSpecs\Humanizer.Core.bg.nuspec ..\NuSpecs\Humanizer.Core.bn-BD.nuspec = ..\NuSpecs\Humanizer.Core.bn-BD.nuspec ..\NuSpecs\Humanizer.Core.cs.nuspec = ..\NuSpecs\Humanizer.Core.cs.nuspec diff --git a/src/Humanizer/Configuration/FormatterRegistry.cs b/src/Humanizer/Configuration/FormatterRegistry.cs index 2b048cfdb..510e25b73 100644 --- a/src/Humanizer/Configuration/FormatterRegistry.cs +++ b/src/Humanizer/Configuration/FormatterRegistry.cs @@ -27,6 +27,7 @@ public FormatterRegistry() : base(new DefaultFormatter("en-US")) RegisterDefaultFormatter("vi"); RegisterDefaultFormatter("en-US"); RegisterDefaultFormatter("af"); + RegisterDefaultFormatter("az"); RegisterDefaultFormatter("da"); RegisterDefaultFormatter("de"); RegisterDefaultFormatter("el"); diff --git a/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs b/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs index 5e1603d76..c874f11bd 100644 --- a/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs +++ b/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs @@ -44,6 +44,7 @@ public NumberToWordsConverterRegistry() Register("zh-CN", new ChineseNumberToWordsConverter()); Register("bg", new BulgarianNumberToWordsConverter()); Register("hy", new ArmenianNumberToWordsConverter()); + Register("az", new AzerbaijaniNumberToWordsConverter()); } } } diff --git a/src/Humanizer/Configuration/OrdinalizerRegistry.cs b/src/Humanizer/Configuration/OrdinalizerRegistry.cs index ba3224c68..175ec19c7 100644 --- a/src/Humanizer/Configuration/OrdinalizerRegistry.cs +++ b/src/Humanizer/Configuration/OrdinalizerRegistry.cs @@ -18,6 +18,7 @@ public OrdinalizerRegistry() : base(new DefaultOrdinalizer()) Register("tr", new TurkishOrdinalizer()); Register("uk", new UkrainianOrdinalizer()); Register("hy", new ArmenianOrdinalizer()); + Register("az", new AzerbaijaniOrdinalizer()); } } } diff --git a/src/Humanizer/Localisation/NumberToWords/AzerbaijaniNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/AzerbaijaniNumberToWordsConverter.cs new file mode 100644 index 000000000..c6fe167e6 --- /dev/null +++ b/src/Humanizer/Localisation/NumberToWords/AzerbaijaniNumberToWordsConverter.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Generic; + +namespace Humanizer.Localisation.NumberToWords +{ + internal class AzerbaijaniNumberToWordsConverter : GenderlessNumberToWordsConverter + { + private static readonly string[] UnitsMap = { "sıfır", "bir", "iki", "üç", "dörd", "beş", "altı", "yeddi", "səkkiz", "doqquz" }; + private static readonly string[] TensMap = { "sıfır", "on", "yirmi", "otuz", "qırx", "əlli", "altmış", "yetmiş", "səksən", "doxsan" }; + + private static readonly Dictionary OrdinalSuffix = new Dictionary + { + {'ı', "ıncı"}, + {'i', "inci"}, + {'u', "uncu"}, + {'ü', "üncü"}, + {'o', "uncu"}, + {'ö', "üncü"}, + {'e', "inci"}, + {'a', "ıncı"}, + {'ə', "inci"}, + }; + + public override string Convert(long input) + { + if (input > Int32.MaxValue || input < Int32.MinValue) + { + throw new NotImplementedException(); + } + var number = (int)input; + if (number == 0) + { + return UnitsMap[0]; + } + + if (number < 0) + { + return string.Format("mənfi {0}", Convert(-number)); + } + + var parts = new List(); + + if ((number / 1000000000) > 0) + { + parts.Add(string.Format("{0} milyard", Convert(number / 1000000000))); + number %= 1000000000; + } + + if ((number / 1000000) > 0) + { + parts.Add(string.Format("{0} milyon", Convert(number / 1000000))); + number %= 1000000; + } + + var thousand = (number / 1000); + if (thousand > 0) + { + parts.Add(string.Format("{0} min", thousand > 1 ? Convert(thousand) : "").Trim()); + number %= 1000; + } + + var hundred = (number / 100); + if (hundred > 0) + { + parts.Add(string.Format("{0} yüz", hundred > 1 ? Convert(hundred) : "").Trim()); + number %= 100; + } + + if ((number / 10) > 0) + { + parts.Add(TensMap[number / 10]); + number %= 10; + } + + if (number > 0) + { + parts.Add(UnitsMap[number]); + } + + var toWords = string.Join(" ", parts.ToArray()); + + return toWords; + } + + public override string ConvertToOrdinal(int number) + { + var word = Convert(number); + var wordSuffix = string.Empty; + var suffixFoundOnLastVowel = false; + + for (var i = word.Length - 1; i >= 0; i--) + { + if (OrdinalSuffix.TryGetValue(word[i], out wordSuffix)) + { + suffixFoundOnLastVowel = i == word.Length - 1; + break; + } + } + + if (word[word.Length - 1] == 't') + { + word = word.Substring(0, word.Length - 1) + 'd'; + } + + if (suffixFoundOnLastVowel) + { + word = word.Substring(0, word.Length - 1); + } + + return string.Format("{0}{1}", word, wordSuffix); + } + } +} diff --git a/src/Humanizer/Localisation/Ordinalizers/AzerbaijaniOrdinalizer.cs b/src/Humanizer/Localisation/Ordinalizers/AzerbaijaniOrdinalizer.cs new file mode 100644 index 000000000..2e7fe6ce2 --- /dev/null +++ b/src/Humanizer/Localisation/Ordinalizers/AzerbaijaniOrdinalizer.cs @@ -0,0 +1,10 @@ +namespace Humanizer.Localisation.Ordinalizers +{ + internal class AzerbaijaniOrdinalizer : DefaultOrdinalizer + { + public override string Convert(int number, string numberString) + { + return numberString + "."; + } + } +} diff --git a/src/Humanizer/Properties/Resources.az.resx b/src/Humanizer/Properties/Resources.az.resx new file mode 100644 index 000000000..670fb9788 --- /dev/null +++ b/src/Humanizer/Properties/Resources.az.resx @@ -0,0 +1,246 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + bir saniyə əvvəl + + + {0} saniyə əvvəl + + + bir dəqiqə əvvəl + + + {0} dəqiqə əvvəl + + + bir saat əvvəl + + + {0} saat əvvəl + + + dünən + + + {0} gün əvvəl + + + bir ay əvvəl + + + {0} ay əvvəl + + + bir il əvvəl + + + {0} il əvvəl + + + {0} gün + + + {0} saat + + + {0} millisaniyə + + + {0} dəqiqə + + + {0} saniyə + + + 1 gün + + + 1 saat + + + 1 millisaniyə + + + 1 dəqiqə + + + 1 saniyə + + + zaman fərqi yoxdur + + + {0} həftə + + + 1 həftə + + + {0} gün sonra + + + {0} saat sonra + + + {0} dəqiqə sonra + + + {0} ay sonra + + + {0} saniyə sonra + + + {0} il sonra + + + indi + + + sabah + + + bir saat sonra + + + bir dəqiqə sonra + + + bir ay sonra + + + bir saniyə sonra + + + bir il sonra + + + {0} ay + + + {0} il + + + 1 ay + + + 1 il + + \ No newline at end of file