From 1a541ae1c601c505f77215b54c042c8be2305c3b Mon Sep 17 00:00:00 2001 From: Ruben Mamo Date: Wed, 21 Nov 2018 23:53:32 +0100 Subject: [PATCH] Added support for the Maltese Language --- CONTRIBUTING.md | 2 +- NuSpecs/Humanizer.Core.mt.nuspec | 24 + .../Humanizer.Tests.Shared.projitems | 3 + .../Localisation/mt/DateHumanizeTests.cs | 124 ++++ .../Localisation/mt/NumberToWordsTests.cs | 284 ++++++++ .../Localisation/mt/TimeSpanHumanizeTests.cs | 100 +++ .../Configuration/FormatterRegistry.cs | 1 + .../NumberToWordsConverterRegistry.cs | 1 + .../Formatters/MalteseFormatter.cs | 35 + .../MalteseNumberToWordsConvertor.cs | 238 ++++++ src/Humanizer/Properties/AssemblyInfo.cs | 2 +- src/Humanizer/Properties/Resources.mt.resx | 678 ++++++++++++++++++ 12 files changed, 1490 insertions(+), 2 deletions(-) create mode 100644 NuSpecs/Humanizer.Core.mt.nuspec create mode 100644 src/Humanizer.Tests.Shared/Localisation/mt/DateHumanizeTests.cs create mode 100644 src/Humanizer.Tests.Shared/Localisation/mt/NumberToWordsTests.cs create mode 100644 src/Humanizer.Tests.Shared/Localisation/mt/TimeSpanHumanizeTests.cs create mode 100644 src/Humanizer/Localisation/Formatters/MalteseFormatter.cs create mode 100644 src/Humanizer/Localisation/NumberToWords/MalteseNumberToWordsConvertor.cs create mode 100644 src/Humanizer/Properties/Resources.mt.resx diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d6fa05aa2..f1b611dc0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -42,7 +42,7 @@ Currently Humanizer supports quite a few localisations for `DateTime.Humanize`, Humanizer could definitely do with more translations. To add a translation for `DateTime.Humanize` and `TimeSpan.Humanize`, fork the repository if you haven't done yet, duplicate the [resources.resx](https://github.com/Humanizr/Humanizer/blob/master/src/Humanizer/Properties/Resources.resx) file, add your target [locale code](http://msdn.microsoft.com/en-us/library/hh441729.aspx) -to the end (e.g. resources.ru.resx for Russian), translate the values to your language, write unit tests for the translation, commit, and send a pull request for it. Thanks. +to the end (e.g. resources.ru.resx for Russian), translate the values to your language, register your formatter in [FormatterRegistry.cs](https://github.com/Humanizr/Humanizer/blob/master/src/Humanizer/Configuration/FormatterRegistry.cs), write unit tests for the translation, commit, and send a pull request for it. Thanks. Some languages have complex rules when it comes to dealing with numbers; for example, in Romanian "5 days" is "5 zile", while "24 days" is "24 de zile" and in Arabic "2 days" is "يومان" not "2 يوم". Obviously a normal resource file doesn't cut it in these cases as a more complex mapping is required. diff --git a/NuSpecs/Humanizer.Core.mt.nuspec b/NuSpecs/Humanizer.Core.mt.nuspec new file mode 100644 index 000000000..3a5749838 --- /dev/null +++ b/NuSpecs/Humanizer.Core.mt.nuspec @@ -0,0 +1,24 @@ + + + + Humanizer.Core.mt + $version$ + Humanizer Locale (mt) + Mehdi Khalili, Oren Novotny + https://github.com/Humanizr/Humanizer + https://raw.github.com/Humanizr/Humanizer/master/logo.png + false + Humanizer Locale (mt) + Copyright (c) .NET Foundation and Contributors + https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE + + mt + + + + + + + + + \ No newline at end of file diff --git a/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems b/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems index 315e288d1..29ca4b6fb 100644 --- a/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems +++ b/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems @@ -96,6 +96,9 @@ + + + diff --git a/src/Humanizer.Tests.Shared/Localisation/mt/DateHumanizeTests.cs b/src/Humanizer.Tests.Shared/Localisation/mt/DateHumanizeTests.cs new file mode 100644 index 000000000..9511dab70 --- /dev/null +++ b/src/Humanizer.Tests.Shared/Localisation/mt/DateHumanizeTests.cs @@ -0,0 +1,124 @@ +using Humanizer.Localisation; +using Xunit; + +namespace Humanizer.Tests.Localisation.mt +{ + + [UseCulture("mt")] + public class DateHumanizeTests + { + + [Theory] + [InlineData(-3, "3 jiem ilu")] + [InlineData(-2, "jumejn ilu")] + [InlineData(-1, "il-bieraħ")] + public void DaysAgo(int days, string expected) + { + DateHumanize.Verify(expected, days, TimeUnit.Day, Tense.Past); + } + + [Theory] + [InlineData(3, "3 jiem oħra")] + [InlineData(2, "pitgħada")] + [InlineData(1, "għada")] + public void DaysFromNow(int days, string expected) + { + DateHumanize.Verify(expected, days, TimeUnit.Day, Tense.Future); + } + + [Theory] + [InlineData(-3, "3 siegħat ilu")] + [InlineData(-2, "sagħtejn ilu")] + [InlineData(-1, "siegħa ilu")] + public void HoursAgo(int hours, string expected) + { + DateHumanize.Verify(expected, hours, TimeUnit.Hour, Tense.Past); + } + + [Theory] + [InlineData(3, "3 siegħat oħra")] + [InlineData(2, "sagħtejn oħra")] + [InlineData(1, "siegħa oħra")] + public void HoursFromNow(int hours, string expected) + { + DateHumanize.Verify(expected, hours, TimeUnit.Hour, Tense.Future); + } + + [Theory] + [InlineData(-3, "3 minuti ilu")] + [InlineData(-2, "2 minuti ilu")] + [InlineData(-1, "minuta ilu")] + [InlineData(60, "siegħa ilu")] + public void MinutesAgo(int minutes, string expected) + { + DateHumanize.Verify(expected, minutes, TimeUnit.Minute, Tense.Past); + } + + [Theory] + [InlineData(2, "2 minuti oħra")] + [InlineData(1, "minuta oħra")] + public void MinutesFromNow(int minutes, string expected) + { + DateHumanize.Verify(expected, minutes, TimeUnit.Minute, Tense.Future); + } + + [Theory] + [InlineData(-3, "3 xhur ilu")] + [InlineData(-2, "xahrejn ilu")] + [InlineData(-1, "xahar ilu")] + public void MonthsAgo(int months, string expected) + { + DateHumanize.Verify(expected, months, TimeUnit.Month, Tense.Past); + } + + [Theory] + [InlineData(3, "3 xhur oħra")] + [InlineData(2, "xahrejn oħra")] + [InlineData(1, "xahar ieħor")] + public void MonthsFromNow(int months, string expected) + { + DateHumanize.Verify(expected, months, TimeUnit.Month, Tense.Future); + } + + [Theory] + [InlineData(-2, "2 sekondi ilu")] + [InlineData(-1, "sekonda ilu")] + public void SecondsAgo(int seconds, string expected) + { + DateHumanize.Verify(expected, seconds, TimeUnit.Second, Tense.Past); + } + + [Theory] + [InlineData(2, "2 sekondi oħra")] + [InlineData(1, "sekonda oħra")] + public void SecondsFromNow(int seconds, string expected) + { + DateHumanize.Verify(expected, seconds, TimeUnit.Second, Tense.Future); + } + + [Theory] + [InlineData(-3, "3 snin ilu")] + [InlineData(-2, "sentejn ilu")] + [InlineData(-1, "sena ilu")] + public void YearsAgo(int years, string expected) + { + DateHumanize.Verify(expected, years, TimeUnit.Year, Tense.Past); + } + + [Theory] + [InlineData(3, "3 snin oħra")] + [InlineData(2, "sentejn oħra")] + [InlineData(1, "sena oħra")] + public void YearsFromNow(int years, string expected) + { + DateHumanize.Verify(expected, years, TimeUnit.Year, Tense.Future); + } + + [Theory] + [InlineData(0, "issa")] + public void Now(int years, string expected) + { + DateHumanize.Verify(expected, years, TimeUnit.Year, Tense.Future); + } + } +} diff --git a/src/Humanizer.Tests.Shared/Localisation/mt/NumberToWordsTests.cs b/src/Humanizer.Tests.Shared/Localisation/mt/NumberToWordsTests.cs new file mode 100644 index 000000000..3038a0b82 --- /dev/null +++ b/src/Humanizer.Tests.Shared/Localisation/mt/NumberToWordsTests.cs @@ -0,0 +1,284 @@ +using Xunit; +namespace Humanizer.Tests.Localisation.mt +{ + [UseCulture("mt")] + public class NumberToWordsTests + { + + [Theory] + [InlineData(0, "żero")] + [InlineData(1, "wieħed")] + [InlineData(3, "tlieta")] + [InlineData(10, "għaxra")] + [InlineData(11, "ħdax")] + [InlineData(20, "għoxrin")] + [InlineData(21, "wieħed u għoxrin")] + [InlineData(38, "tmienja u tletin")] + [InlineData(43, "tlieta u erbgħin")] + [InlineData(78, "tmienja u sebgħin")] + [InlineData(99, "disgħa u disgħin")] + [InlineData(100, "mija")] + [InlineData(101, "mija u wieħed")] + [InlineData(103, "mija u tlieta")] + [InlineData(110, "mija u għaxra")] + [InlineData(111, "mija u ħdax")] + [InlineData(121, "mija u wieħed u għoxrin")] + [InlineData(122, "mija u tnejn u għoxrin")] + [InlineData(123, "mija u tlieta u għoxrin")] + [InlineData(138, "mija u tmienja u tletin")] + [InlineData(143, "mija u tlieta u erbgħin")] + [InlineData(178, "mija u tmienja u sebgħin")] + [InlineData(199, "mija u disgħa u disgħin")] + [InlineData(200, "mitejn")] + [InlineData(201, "mitejn u wieħed")] + [InlineData(203, "mitejn u tlieta")] + [InlineData(210, "mitejn u għaxra")] + [InlineData(211, "mitejn u ħdax")] + [InlineData(221, "mitejn u wieħed u għoxrin")] + [InlineData(222, "mitejn u tnejn u għoxrin")] + [InlineData(223, "mitejn u tlieta u għoxrin")] + [InlineData(238, "mitejn u tmienja u tletin")] + [InlineData(243, "mitejn u tlieta u erbgħin")] + [InlineData(278, "mitejn u tmienja u sebgħin")] + [InlineData(299, "mitejn u disgħa u disgħin")] + [InlineData(300, "tlett mija")] + [InlineData(401, "erbgħa mija u wieħed")] + [InlineData(503, "ħames mija u tlieta")] + [InlineData(610, "sitt mija u għaxra")] + [InlineData(711, "sebgħa mija u ħdax")] + [InlineData(821, "tminn mija u wieħed u għoxrin")] + [InlineData(922, "disgħa mija u tnejn u għoxrin")] + [InlineData(323, "tlett mija u tlieta u għoxrin")] + [InlineData(438, "erbgħa mija u tmienja u tletin")] + [InlineData(543, "ħames mija u tlieta u erbgħin")] + [InlineData(678, "sitt mija u tmienja u sebgħin")] + [InlineData(799, "sebgħa mija u disgħa u disgħin")] + [InlineData(1000, "elf")] + [InlineData(1001, "elf u wieħed")] + [InlineData(1111, "elf u mija u ħdax")] + [InlineData(1234, "elf u mitejn u erbgħa u tletin")] + [InlineData(1999, "elf u disgħa mija u disgħa u disgħin")] + [InlineData(2000, "elfejn")] + [InlineData(2014, "elfejn u erbatax")] + [InlineData(2048, "elfejn u tmienja u erbgħin")] + [InlineData(3000, "tlett elef")] + [InlineData(3501, "tlett elef u ħames mija u wieħed")] + [InlineData(8100, "tmint elef u mija")] + [InlineData(10000, "għaxart elef")] + [InlineData(10001, "għaxart elef u wieħed")] + [InlineData(12345, "tnax il-elf u tlett mija u ħamsa u erbgħin")] + [InlineData(18000, "tmintax il-elf")] + [InlineData(100000, "mitt elf")] + [InlineData(100001, "mitt elf u wieħed")] + [InlineData(111111, "mija u ħdax il-elf u mija u ħdax")] + [InlineData(123456, "mija u tlieta u għoxrin elf u erbgħa mija u sitta u ħamsin")] + [InlineData(1000000, "miljun")] + [InlineData(1000001, "miljun u wieħed")] + [InlineData(1111101, "miljun u mija u ħdax il-elf u mija u wieħed")] + [InlineData(1111111, "miljun u mija u ħdax il-elf u mija u ħdax")] + [InlineData(1234567, "miljun u mitejn u erbgħa u tletin elf u ħames mija u sebgħa u sittin")] + [InlineData(5000000, "ħames miljuni")] + [InlineData(10000000, "għaxar miljuni")] + [InlineData(10000001, "għaxar miljuni u wieħed")] + [InlineData(11111111, "ħdax il-miljun u mija u ħdax il-elf u mija u ħdax")] + [InlineData(12345678, "tnax il-miljun u tlett mija u ħamsa u erbgħin elf u sitt mija u tmienja u sebgħin")] + [InlineData(100000000, "mitt miljun")] + [InlineData(100000001, "mitt miljun u wieħed")] + [InlineData(111111111, "mija u ħdax il-miljun u mija u ħdax il-elf u mija u ħdax")] + [InlineData(123456789, "mija u tlieta u għoxrin miljun u erbgħa mija u sitta u ħamsin elf u sebgħa mija u disgħa u tmenin")] + [InlineData(1000000000, "biljun")] + [InlineData(1000000001, "biljun u wieħed")] + [InlineData(1101111101, "biljun u mija u miljun u mija u ħdax il-elf u mija u wieħed")] + [InlineData(2000000000, "żewġ biljuni")] + [InlineData(2147483647, "żewġ biljuni u mija u sebgħa u erbgħin miljun u erbgħa mija u tlieta u tmenin elf u sitt mija u sebgħa u erbgħin")] + public void ToWords(int number, string expected) + { + Assert.Equal(expected, number.ToWords()); + } + + [Theory] + [InlineData(1, "waħda")] + [InlineData(1001, "elf u waħda")] + [InlineData(100001, "mitt elf u waħda")] + [InlineData(1000000001, "biljun u waħda")] + public void ToFeminineWords(int number, string expected) + { + Assert.Equal(expected, number.ToWords(GrammaticalGender.Feminine)); + } + + [Theory] + [InlineData(0, "żero")] + [InlineData(1, "wieħed")] + [InlineData(3, "tlieta")] + [InlineData(10, "għaxra")] + [InlineData(11, "ħdax")] + [InlineData(20, "għoxrin")] + [InlineData(21, "wieħed u għoxrin")] + [InlineData(38, "tmienja u tletin")] + [InlineData(43, "tlieta u erbgħin")] + [InlineData(78, "tmienja u sebgħin")] + [InlineData(99, "disgħa u disgħin")] + [InlineData(100, "mija")] + [InlineData(101, "mija u wieħed")] + [InlineData(103, "mija u tlieta")] + [InlineData(110, "mija u għaxra")] + [InlineData(111, "mija u ħdax")] + [InlineData(121, "mija u wieħed u għoxrin")] + [InlineData(122, "mija u tnejn u għoxrin")] + [InlineData(123, "mija u tlieta u għoxrin")] + [InlineData(138, "mija u tmienja u tletin")] + [InlineData(143, "mija u tlieta u erbgħin")] + [InlineData(178, "mija u tmienja u sebgħin")] + [InlineData(199, "mija u disgħa u disgħin")] + [InlineData(200, "mitejn")] + [InlineData(201, "mitejn u wieħed")] + [InlineData(203, "mitejn u tlieta")] + [InlineData(210, "mitejn u għaxra")] + [InlineData(211, "mitejn u ħdax")] + [InlineData(221, "mitejn u wieħed u għoxrin")] + [InlineData(222, "mitejn u tnejn u għoxrin")] + [InlineData(223, "mitejn u tlieta u għoxrin")] + [InlineData(238, "mitejn u tmienja u tletin")] + [InlineData(243, "mitejn u tlieta u erbgħin")] + [InlineData(278, "mitejn u tmienja u sebgħin")] + [InlineData(299, "mitejn u disgħa u disgħin")] + [InlineData(300, "tlett mija")] + [InlineData(401, "erbgħa mija u wieħed")] + [InlineData(503, "ħames mija u tlieta")] + [InlineData(610, "sitt mija u għaxra")] + [InlineData(711, "sebgħa mija u ħdax")] + [InlineData(821, "tminn mija u wieħed u għoxrin")] + [InlineData(922, "disgħa mija u tnejn u għoxrin")] + [InlineData(323, "tlett mija u tlieta u għoxrin")] + [InlineData(438, "erbgħa mija u tmienja u tletin")] + [InlineData(543, "ħames mija u tlieta u erbgħin")] + [InlineData(678, "sitt mija u tmienja u sebgħin")] + [InlineData(799, "sebgħa mija u disgħa u disgħin")] + [InlineData(1000, "elf")] + [InlineData(1001, "elf u wieħed")] + [InlineData(1111, "elf u mija u ħdax")] + [InlineData(1234, "elf u mitejn u erbgħa u tletin")] + [InlineData(1999, "elf u disgħa mija u disgħa u disgħin")] + [InlineData(2000, "elfejn")] + [InlineData(2014, "elfejn u erbatax")] + [InlineData(2048, "elfejn u tmienja u erbgħin")] + [InlineData(3000, "tlett elef")] + [InlineData(3501, "tlett elef u ħames mija u wieħed")] + [InlineData(8100, "tmint elef u mija")] + [InlineData(10000, "għaxart elef")] + [InlineData(10001, "għaxart elef u wieħed")] + [InlineData(12345, "tnax il-elf u tlett mija u ħamsa u erbgħin")] + [InlineData(18000, "tmintax il-elf")] + [InlineData(100000, "mitt elf")] + [InlineData(100001, "mitt elf u wieħed")] + [InlineData(111111, "mija u ħdax il-elf u mija u ħdax")] + [InlineData(123456, "mija u tlieta u għoxrin elf u erbgħa mija u sitta u ħamsin")] + [InlineData(1000000, "miljun")] + [InlineData(1000001, "miljun u wieħed")] + [InlineData(1111101, "miljun u mija u ħdax il-elf u mija u wieħed")] + [InlineData(1111111, "miljun u mija u ħdax il-elf u mija u ħdax")] + [InlineData(1234567, "miljun u mitejn u erbgħa u tletin elf u ħames mija u sebgħa u sittin")] + [InlineData(5000000, "ħames miljuni")] + [InlineData(10000000, "għaxar miljuni")] + [InlineData(10000001, "għaxar miljuni u wieħed")] + [InlineData(11111111, "ħdax il-miljun u mija u ħdax il-elf u mija u ħdax")] + [InlineData(12345678, "tnax il-miljun u tlett mija u ħamsa u erbgħin elf u sitt mija u tmienja u sebgħin")] + [InlineData(100000000, "mitt miljun")] + [InlineData(100000001, "mitt miljun u wieħed")] + [InlineData(111111111, "mija u ħdax il-miljun u mija u ħdax il-elf u mija u ħdax")] + [InlineData(123456789, "mija u tlieta u għoxrin miljun u erbgħa mija u sitta u ħamsin elf u sebgħa mija u disgħa u tmenin")] + [InlineData(1000000000, "biljun")] + [InlineData(1000000001, "biljun u wieħed")] + [InlineData(1101111101, "biljun u mija u miljun u mija u ħdax il-elf u mija u wieħed")] + [InlineData(2000000000, "żewġ biljuni")] + [InlineData(2147483647, "żewġ biljuni u mija u sebgħa u erbgħin miljun u erbgħa mija u tlieta u tmenin elf u sitt mija u sebgħa u erbgħin")] + public void ToMasculineWords(int number, string expected) + { + Assert.Equal(expected, number.ToWords(GrammaticalGender.Masculine)); + } + + [Theory] + [InlineData(0, "0")] + [InlineData(1, "l-ewwel")] + [InlineData(2, "it-tieni")] + [InlineData(9, "id-disa'")] + [InlineData(10, "l-għaxar")] + [InlineData(11, "il-ħdax")] + [InlineData(15, "il-ħmistax")] + [InlineData(18, "it-tmintax")] + [InlineData(20, "l-għoxrin")] + [InlineData(21, "il-wieħed u għoxrin")] + [InlineData(22, "it-tnejn u għoxrin")] + [InlineData(28, "it-tmienja u għoxrin")] + [InlineData(44, "l-erbgħa u erbgħin")] + [InlineData(55, "il-ħamsa u ħamsin")] + [InlineData(60, "is-sittin")] + [InlineData(99, "id-disgħa u disgħin")] + [InlineData(100, "il-mija")] + [InlineData(101, "il-mija u wieħed")] + [InlineData(1000, "l-elf")] + [InlineData(1001, "l-elf u wieħed")] + [InlineData(2000, "l-elfejn")] + [InlineData(3000, "it-tlett elef")] + public void ToOrdinalWords(int number, string expected) + { + Assert.Equal(expected, number.ToOrdinalWords()); + } + + [Theory] + [InlineData(0, "0")] + [InlineData(1, "l-ewwel")] + [InlineData(2, "it-tieni")] + [InlineData(9, "id-disa'")] + [InlineData(10, "l-għaxar")] + [InlineData(11, "il-ħdax")] + [InlineData(15, "il-ħmistax")] + [InlineData(18, "it-tmintax")] + [InlineData(20, "l-għoxrin")] + [InlineData(21, "il-wieħed u għoxrin")] + [InlineData(22, "it-tnejn u għoxrin")] + [InlineData(28, "it-tmienja u għoxrin")] + [InlineData(44, "l-erbgħa u erbgħin")] + [InlineData(55, "il-ħamsa u ħamsin")] + [InlineData(60, "is-sittin")] + [InlineData(99, "id-disgħa u disgħin")] + [InlineData(100, "il-mija")] + [InlineData(101, "il-mija u waħda")] + [InlineData(1000, "l-elf")] + [InlineData(1001, "l-elf u waħda")] + [InlineData(2000, "l-elfejn")] + [InlineData(3000, "it-tlett elef")] + public void ToFeminineOrdinalWords(int number, string expected) + { + Assert.Equal(expected, number.ToOrdinalWords(GrammaticalGender.Feminine)); + } + + [Theory] + [InlineData(0, "0")] + [InlineData(1, "l-ewwel")] + [InlineData(2, "it-tieni")] + [InlineData(9, "id-disa'")] + [InlineData(10, "l-għaxar")] + [InlineData(11, "il-ħdax")] + [InlineData(15, "il-ħmistax")] + [InlineData(18, "it-tmintax")] + [InlineData(20, "l-għoxrin")] + [InlineData(21, "il-wieħed u għoxrin")] + [InlineData(22, "it-tnejn u għoxrin")] + [InlineData(28, "it-tmienja u għoxrin")] + [InlineData(44, "l-erbgħa u erbgħin")] + [InlineData(55, "il-ħamsa u ħamsin")] + [InlineData(60, "is-sittin")] + [InlineData(99, "id-disgħa u disgħin")] + [InlineData(100, "il-mija")] + [InlineData(101, "il-mija u wieħed")] + [InlineData(1000, "l-elf")] + [InlineData(1001, "l-elf u wieħed")] + [InlineData(2000, "l-elfejn")] + [InlineData(3000, "it-tlett elef")] + public void ToMasculineOrdinalWords(int number, string expected) + { + Assert.Equal(expected, number.ToOrdinalWords(GrammaticalGender.Masculine)); + } + } +} diff --git a/src/Humanizer.Tests.Shared/Localisation/mt/TimeSpanHumanizeTests.cs b/src/Humanizer.Tests.Shared/Localisation/mt/TimeSpanHumanizeTests.cs new file mode 100644 index 000000000..e83400f00 --- /dev/null +++ b/src/Humanizer.Tests.Shared/Localisation/mt/TimeSpanHumanizeTests.cs @@ -0,0 +1,100 @@ +using System; +using Xunit; + +namespace Humanizer.Tests.Localisation.mt +{ + + [UseCulture("mt")] + public class TimeSpanHumanizeTests + { + + [Theory] + [Trait("Translation", "Google")] + [InlineData(366, "sena")] + [InlineData(731, "sentejn")] + [InlineData(1096, "3 snin")] + [InlineData(4018, "11 snin")] + 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, "xahar")] + [InlineData(61, "xahrejn")] + [InlineData(92, "3 xhur")] + [InlineData(335, "11 xhur")] + public void Months(int days, string expected) + { + Assert.Equal(expected, TimeSpan.FromDays(days).Humanize(maxUnit: Humanizer.Localisation.TimeUnit.Year)); + } + + [Theory] + [InlineData(7, "ġimgħa")] + [InlineData(14, "ġimgħatejn")] + [InlineData(21, "3 ġimgħat")] + public void Weeks(int days, string expected) + { + Assert.Equal(expected, TimeSpan.FromDays(days).Humanize()); + } + + [Theory] + [InlineData(1, "ġurnata")] + [InlineData(2, "jumejn")] + [InlineData(3, "3 jiem")] + public void Days(int days, string expected) + { + Assert.Equal(expected, TimeSpan.FromDays(days).Humanize()); + } + + [Theory] + [InlineData(1, "siegħa")] + [InlineData(2, "sagħtejn")] + [InlineData(3, "3 siegħat")] + public void Hours(int hours, string expected) + { + Assert.Equal(expected, TimeSpan.FromHours(hours).Humanize()); + } + + [Theory] + [InlineData(1, "minuta")] + [InlineData(2, "2 minuti")] + [InlineData(3, "3 minuti")] + public void Minutes(int minutes, string expected) + { + Assert.Equal(expected, TimeSpan.FromMinutes(minutes).Humanize()); + } + + [Theory] + [InlineData(1, "sekonda")] + [InlineData(2, "2 sekondi")] + [InlineData(3, "3 sekondi")] + public void Seconds(int seconds, string expected) + { + Assert.Equal(expected, TimeSpan.FromSeconds(seconds).Humanize()); + } + + [Theory] + [InlineData(1, "millisekonda")] + [InlineData(2, "2 millisekondi")] + [InlineData(3, "3 millisekondi")] + public void Milliseconds(int milliseconds, string expected) + { + Assert.Equal(expected, TimeSpan.FromMilliseconds(milliseconds).Humanize()); + } + + [Fact] + public void NoTime() + { + Assert.Equal("0 millisekondi", TimeSpan.Zero.Humanize()); + } + + [Fact] + public void NoTimeToWords() + { + Assert.Equal("xejn", TimeSpan.Zero.Humanize(toWords: true)); + } + } +} diff --git a/src/Humanizer/Configuration/FormatterRegistry.cs b/src/Humanizer/Configuration/FormatterRegistry.cs index 52fd88c99..719b82758 100644 --- a/src/Humanizer/Configuration/FormatterRegistry.cs +++ b/src/Humanizer/Configuration/FormatterRegistry.cs @@ -36,6 +36,7 @@ public FormatterRegistry() : base(new DefaultFormatter("en-US")) RegisterDefaultFormatter("hu"); RegisterDefaultFormatter("id"); RegisterDefaultFormatter("ja"); + Register("mt", new MalteseFormatter("mt")); RegisterDefaultFormatter("nb"); RegisterDefaultFormatter("nb-NO"); RegisterDefaultFormatter("nl"); diff --git a/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs b/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs index f5f1d76ad..f0af8bd3f 100644 --- a/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs +++ b/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs @@ -29,6 +29,7 @@ public NumberToWordsConverterRegistry() Register("bn-BD", new BanglaNumberToWordsConverter()); Register("tr", new TurkishNumberToWordConverter()); Register("it", new ItalianNumberToWordsConverter()); + Register("mt", new MalteseNumberToWordsConvertor()); Register("uk", new UkrainianNumberToWordsConverter()); Register("uz-Latn-UZ", new UzbekLatnNumberToWordConverter()); Register("uz-Cyrl-UZ", new UzbekCyrlNumberToWordConverter()); diff --git a/src/Humanizer/Localisation/Formatters/MalteseFormatter.cs b/src/Humanizer/Localisation/Formatters/MalteseFormatter.cs new file mode 100644 index 000000000..331b08177 --- /dev/null +++ b/src/Humanizer/Localisation/Formatters/MalteseFormatter.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Humanizer.Localisation.Formatters +{ + internal class MalteseFormatter : DefaultFormatter + { + private const string DualPostfix = "_Dual"; + + private static readonly string[] DualResourceKeys = new[] + { + "DateHumanize_MultipleDaysAgo", "DateHumanize_MultipleDaysFromNow", "DateHumanize_MultipleHoursAgo", "DateHumanize_MultipleHoursFromNow" , + "DateHumanize_MultipleMonthsAgo", "DateHumanize_MultipleMonthsFromNow", "DateHumanize_MultipleYearsAgo", "DateHumanize_MultipleYearsFromNow", + "TimeSpanHumanize_MultipleDays", "TimeSpanHumanize_MultipleYears", "TimeSpanHumanize_MultipleMonths", "TimeSpanHumanize_MultipleHours", + "TimeSpanHumanize_MultipleWeeks" + }; + + public MalteseFormatter(string localeCode) + : base(localeCode) + { + } + + protected override string GetResourceKey(string resourceKey, int number) + { + if (number == 2 && DualResourceKeys.Contains(resourceKey)) + { + return resourceKey + DualPostfix; + } + + return resourceKey; + } + } +} diff --git a/src/Humanizer/Localisation/NumberToWords/MalteseNumberToWordsConvertor.cs b/src/Humanizer/Localisation/NumberToWords/MalteseNumberToWordsConvertor.cs new file mode 100644 index 000000000..d5ea09f69 --- /dev/null +++ b/src/Humanizer/Localisation/NumberToWords/MalteseNumberToWordsConvertor.cs @@ -0,0 +1,238 @@ +using System; +using System.Collections.Generic; + +namespace Humanizer.Localisation.NumberToWords +{ + internal class MalteseNumberToWordsConvertor : GenderedNumberToWordsConverter + { + private static readonly string[] OrdinalOverrideMap = + { + "0", "l-ewwel", "it-tieni", "it-tielet", "ir-raba'", "il-ħames", "is-sitt", "is-seba'", "it-tmien", "id-disa'", + "l-għaxar", "il-ħdax", "it-tnax", "it-tlettax", "l-erbatax", "il-ħmistax", "is-sittax", "is-sbatax", + "it-tmintax", "id-dsatax", "l-għoxrin" + }; + + private static readonly string[] UnitsMap = + { + "żero", "wieħed", "tnejn", "tlieta", "erbgħa", "ħamsa", "sitta", "sebgħa", "tmienja", "disgħa", "għaxra", + "ħdax", "tnax", "tlettax", "erbatax", "ħmistax", "sittax", "sbatax", "tmintax", "dsatax" + }; + + private static readonly string[] TensMap = + {"zero", "għaxra", "għoxrin", "tletin", "erbgħin", "ħamsin", "sittin", "sebgħin", "tmenin", "disgħin"}; + + private static readonly string[] HundredsMap = + { + string.Empty, string.Empty, string.Empty, "tlett", "erbgħa", "ħames", "sitt", "sebgħa", "tminn", "disgħa", + "għaxar" + }; + + private static readonly string[] PrefixMap = + { + string.Empty, string.Empty, string.Empty, "tlett", "erbat", "ħamest", "sitt", "sebat", "tmint", "disat", + "għaxart", "ħdax il-", "tnax il-", "tletax il-", "erbatax il-", "ħmistax il-", "sittax il-", "sbatax il-", + "tmintax il-", "dsatax il-" + }; + + public override string Convert(long input, GrammaticalGender gender) + { + if (input < 1000000000) + { + return GetMillions(input, gender); + } + + var billions = input / 1000000000; + var tensInBillions = billions % 100; + var millions = input % 1000000000; + + var billionsText = GetPrefixText(billions, tensInBillions, "biljun", "żewġ biljuni", "biljuni", false, gender); + var millionsText = GetMillions(millions, gender); + + if (millions == 0) + { + return billionsText; + } + + return $"{billionsText} u {millionsText}"; + } + + public override string ConvertToOrdinal(int number, GrammaticalGender gender) + { + if (number <= 20) + { + return OrdinalOverrideMap[number]; + } + + var ordinal = Convert(number, gender); + + if (ordinal.StartsWith("d")) + { + return $"id-{Convert(number, gender)}"; + } + if (ordinal.StartsWith("s")) + { + return $"is-{Convert(number, gender)}"; + } + if (ordinal.StartsWith("t")) + { + return $"it-{Convert(number, gender)}"; + } + if (ordinal.StartsWith("e")) + { + return $"l-{Convert(number, gender)}"; + } + return $"il-{Convert(number, gender)}"; + } + + private static string GetTens(long value, bool usePrefixMap, bool usePrefixMapForLowerDigits, GrammaticalGender gender) + { + if (value == 1 && gender == GrammaticalGender.Feminine) + { + return "waħda"; + } + + if (value < 11 && usePrefixMap && usePrefixMapForLowerDigits) + { + return PrefixMap[value]; + } + + if (value < 11 && usePrefixMap && !usePrefixMapForLowerDigits) + { + return HundredsMap[value]; + } + + if (value > 10 && value < 20 && usePrefixMap) + { + return PrefixMap[value]; + } + + if (value < 20) + { + return UnitsMap[value]; + } + + var single = value % 10; + var numberOfTens = value / 10; + if (single == 0) + { + + return TensMap[numberOfTens]; + } + + return $"{UnitsMap[single]} u {TensMap[numberOfTens]}"; + } + + private static string GetHundreds(long value, bool usePrefixMap, bool usePrefixMapForLowerValueDigits, GrammaticalGender gender) + { + if (value < 100) + { + return GetTens(value, usePrefixMap, usePrefixMapForLowerValueDigits, gender); + } + + var tens = value % 100; + var numberOfHundreds = value / 100; + + var hundredsText = string.Empty; + if (numberOfHundreds == 1) + { + hundredsText = "mija"; + } + else if (numberOfHundreds == 2) + { + hundredsText = "mitejn"; + } + else + { + hundredsText = HundredsMap[numberOfHundreds] + " mija"; + } + + if (tens == 0) + { + return hundredsText; + } + + return $"{hundredsText} u {GetTens(tens, usePrefixMap, usePrefixMapForLowerValueDigits, gender)}"; + } + + private static string GetThousands(long value, GrammaticalGender gender) + { + if (value < 1000) + { + return GetHundreds(value, false, false, gender); + } + + var thousands = value / 1000; + var tensInThousands = thousands % 100; + var hundreds = value % 1000; + + var thousandsInText = GetPrefixText(thousands, tensInThousands, "elf", "elfejn", "elef", true, gender); + + var hundredsInText = GetHundreds(hundreds, false, false, gender); + + if (hundreds == 0) + { + return thousandsInText; + } + + return $"{thousandsInText} u {hundredsInText}"; + } + + private static string GetMillions(long value, GrammaticalGender gender) + { + if (value < 1000000) + { + return GetThousands(value, gender); + } + + var millions = value / 1000000; + var tensInMillions = millions % 100; + var thousands = value % 1000000; + + var millionsText = GetPrefixText(millions, tensInMillions, "miljun", "żewġ miljuni", "miljuni", false, gender); + var thousandsText = GetThousands(thousands, gender); + + if (thousands == 0) + { + return millionsText; + } + + return $"{millionsText} u {thousandsText}"; + } + + private static string GetPrefixText(long thousands, long tensInThousands, string singular, string dual, string plural, bool usePrefixMapForLowerValueDigits, GrammaticalGender gender) + { + if (thousands == 1) + { + return singular; + } + + if (thousands == 2) + { + return dual; + } + + if (tensInThousands > 10 && tensInThousands < 20) + { + return $"{GetHundreds(thousands, true, usePrefixMapForLowerValueDigits, gender)}{singular}"; + } + + if (tensInThousands > 20) + { + return $"{GetHundreds(thousands, true, usePrefixMapForLowerValueDigits, gender)} {singular}"; + } + + if (thousands == 100) + { + return $"mitt {singular}"; + } + + if (thousands == 101) + { + return $"mija u {singular}"; + } + + return $"{GetHundreds(thousands, true, usePrefixMapForLowerValueDigits, gender)} {plural}"; + + } + } +} diff --git a/src/Humanizer/Properties/AssemblyInfo.cs b/src/Humanizer/Properties/AssemblyInfo.cs index 149d621c4..27bfb7922 100644 --- a/src/Humanizer/Properties/AssemblyInfo.cs +++ b/src/Humanizer/Properties/AssemblyInfo.cs @@ -1,3 +1,3 @@ using System.Resources; -[assembly: NeutralResourcesLanguage("en")] \ No newline at end of file +[assembly: NeutralResourcesLanguage("en")] diff --git a/src/Humanizer/Properties/Resources.mt.resx b/src/Humanizer/Properties/Resources.mt.resx new file mode 100644 index 000000000..a71b83597 --- /dev/null +++ b/src/Humanizer/Properties/Resources.mt.resx @@ -0,0 +1,678 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + sekonda ilu + + + {0} sekondi ilu + + + minuta ilu + + + {0} minuti ilu + + + siegħa ilu + + + {0} siegħat ilu + + + il-bieraħ + + + {0} jiem ilu + + + xahar ilu + + + {0} xhur ilu + + + sena ilu + + + {0} snin ilu + + + {0} jiem + + + {0} siegħat + + + {0} millisekondi + + + {0} minuti + + + {0} sekondi + + + ġurnata + + + siegħa + + + millisekonda + + + minuta + + + sekonda + + + xejn + + + {0} ġimgħat + + + ġimgħa + + + {0} jiem oħra + + + {0} siegħat oħra + + + {0} minuti oħra + + + {0} xhur oħra + + + {0} sekondi oħra + + + {0} snin oħra + + + issa + + + għada + + + siegħa oħra + + + minuta oħra + + + xahar ieħor + + + sekonda oħra + + + sena oħra + + + sagħtejn oħra + + + {0} minuti oħra + + + {0} sekondi ilu + + + {0} sekondi oħra + + + sentejn oħra + + + jumejn + + + {0} jiem + + + {0} siegħat + + + {0} millisekondi + + + {0} minuti + + + {0} minuti + + + {0} sekondi + + + {0} millisekondi + + + {0} jiem ilu + + + jumejn ilu + + + {0} jiem ilu + + + il-bieraħ + + + {0} jiem ilu + + + pitgħada + + + {0} jiem oħra + + + għada + + + {0} jiem oħra + + + {0} siegħat ilu + + + sagħtejn ilu + + + {0} siegħat ilu + + + siegħa ilu + + + {0} siegħat ilu + + + {0} siegħat oħra + + + siegħa oħra + + + {0} siegħat oħra + + + {0} minuti ilu + + + {0} minuti ilu + + + {0} minuti ilu + + + minuta ilu + + + {0} minuti ilu + + + {0} minuti oħra + + + minuta oħra + + + {0} minuti oħra + + + xahrejn ilu + + + {0} xhur ilu + + + xahar ilu + + + {0} xhur ilu + + + xahrejn oħra + + + {0} xhur oħra + + + xahar ieħor + + + {0} xhur oħra + + + {0} sekondi ilu + + + {0} sekondi ilu + + + sekonda ilu + + + {0} sekondi ilu + + + {0} sekondi oħra + + + sekonda oħra + + + {0} sekondi oħra + + + {0} snin ilu + + + sentejn ilu + + + {0} snin ilu + + + sena ilu + + + {0} snin ilu + + + {0} snin oħra + + + sena oħra + + + {0} snin oħra + + + jum + + + {0} jiem + + + sagħtejn + + + siegħa + + + {0} siegħat + + + millisekonda + + + {0} millisekondi + + + {0} minuta + + + {0} minuti + + + {0} sekondi + + + sekonda + + + {0} sekondi + + + ġimgħatejn + + + {0} ġimgħat + + + ġimgħa + + + {0} ġimgħat + + + {0} xhur + + + {0} snin + + + xahar + + + sena + + + {0} siegħat ilu + + + {0} siegħat oħra + + + {0} minuti ilu + + + {0} minuti oħra + + + {0} xhur ilu + + + {0} xhur oħra + + + {0} sekondi ilu + + + {0} sekondi oħra + + + {0} snin ilu + + + {0} snin oħra + + + {0} siegħat + + + {0} millisekondi + + + {0} minuti + + + {0} sekondi + + + {0} ġimgħat + + + qatt + + + xahrejn + + + {0} xhur + + + {0} xhur + + + xahar + + + {0} xhur + + + sentejn + + + {0} snin + + + {0} snin + + + {0} snin + + + {0} snin + + + ġurnata + + + siegħa + + + millisekonda + + + minuta + + + xahar + + + sekonda + + + ġimgħa + + + sena + + + {0} jiem + + + XL + + + grieg it-tramuntana + + + grigal + + + grieg il-lvant + + + lvant + + + east-southeast + + + xlokk + + + nofsinhar ix-xlokk + + + nofsinhar + + + nofsinhar il-lbiċ + + + lbiċ + + + punent il-lbiċ + + + punent + + + punent majjistru + + + majjistral + + + majjistral it-tramuntana + + + T + + + GT + + + G + + + GL + + + L + + + xlokk il-lvant + + + X + + + NX + + + N + + + NL + + + L + + + PL + + + P + + + PM + + + M + + + MT + + \ No newline at end of file