diff --git a/.gitignore b/.gitignore index 6d38a7388..f734e51d8 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ obj/ src/packages/* PackageBuild/* Build/* +Release/* src/Humanizer.Tests/ApiApprover/PublicApiApprovalTest.approve_public_api.received.txt *.tss src/TestResults/* diff --git a/build.cmd b/build.cmd new file mode 100644 index 000000000..e5f3378f8 --- /dev/null +++ b/build.cmd @@ -0,0 +1 @@ +@%WINDIR%\Microsoft.Net\Framework\v4.0.30319\msbuild build.proj /m /clp:Verbosity=minimal diff --git a/build.config b/build.config new file mode 100644 index 000000000..680228da3 --- /dev/null +++ b/build.config @@ -0,0 +1,23 @@ + + + + tools\xunit + + + Release + + + + + + + + + + + + + + + diff --git a/build.proj b/build.proj new file mode 100644 index 000000000..50d2dfde8 --- /dev/null +++ b/build.proj @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/logo.png b/logo.png new file mode 100644 index 000000000..9fdf08096 Binary files /dev/null and b/logo.png differ diff --git a/readme.md b/readme.md index 658655fe9..ee97d1abd 100644 --- a/readme.md +++ b/readme.md @@ -1,3 +1,5 @@ +

Logo

+ Humanizer meets all your .NET needs for manipulating and displaying strings, enums, dates, times, timespans, numbers and quantities. ###Table of contents @@ -32,6 +34,7 @@ Humanizer meets all your .NET needs for manipulating and displaying strings, enu - [Author](#author) - [Main contributors](#main-contributors) - [License](#license) + - [Icon](#icon) ##Install You can install Humanizer as [a nuget package](https://nuget.org/packages/Humanizer): `Install-Package Humanizer` @@ -784,7 +787,7 @@ I have also flagged some of the easier issues as 'jump in' so you can start with ###Contribution guideline I use [GitHub flow](http://scottchacon.com/2011/08/31/github-flow.html) for pull requests. -So if you want to contribute, fork the repo, preferrably create a local branch to avoid conflicts with other activities, fix an issue and send a PR. +So if you want to contribute, fork the repo, preferrably create a local branch to avoid conflicts with other activities, fix an issue, run build.cmd from the root of the project, and send a PR if all is green. Pull requests are code reviewed. Here is a checklist you should tick through before submitting a pull request: @@ -844,3 +847,5 @@ Alexander I. Zaytsev ([@hazzik](https://github.com/hazzik)) ##License Humanizer is released under the MIT License. See the [bundled LICENSE](https://github.com/MehdiK/Humanizer/blob/master/LICENSE) file for details. +##Icon +Icon created by [Tyrone Rieschiek](https://twitter.com/Inkventive) \ No newline at end of file diff --git a/release_notes.md b/release_notes.md index ae0ddac56..068079dd8 100644 --- a/release_notes.md +++ b/release_notes.md @@ -1,8 +1,25 @@ ###In Development - - [#217](https://github.com/Mehdik/Humanizer/pull/217): Changed OrdinalizeExtensions to better accommodate localisations. Added pt-BR and Spanish Ordinalize localisation. + +[Commits](https://github.com/MehdiK/Humanizer/compare/v1.24.1...master) + +###v1.24.1 - 2014-04-21 + - [#232](https://github.com/Mehdik/Humanizer/pull/232): Adding code & tests to handle Arabic numbers to ordinal + - [#235](https://github.com/Mehdik/Humanizer/pull/235): Fixed the conversion for "1 millon" in SpanishNumberToWordsConverter + - [#233](https://github.com/Mehdik/Humanizer/pull/233): Added build.cmd and Verify build configuration for strict project build and analysis + +[Commits](https://github.com/MehdiK/Humanizer/compare/v1.23.1...v1.24.1) + +###v1.23.1 - 2014-04-19 + - [#217](https://github.com/Mehdik/Humanizer/pull/217): Added pt-BR and Spanish Ordinalize localisation. + - [#220](https://github.com/Mehdik/Humanizer/pull/220): Added string formatting options to ToQuantity + - [#219](https://github.com/Mehdik/Humanizer/pull/219): Added Japanese translation for date and timespan - [#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) + - [#222](https://github.com/Mehdik/Humanizer/pull/222): Updated Ordinalize and ToOrdinalWords to account for special exceptions with 1 and 3. + -[Commits](https://github.com/MehdiK/Humanizer/compare/v1.22.1...master) +[Commits](https://github.com/MehdiK/Humanizer/compare/v1.22.1...v1.23.1) ###v1.22.1 - 2014-04-14 - [#188](https://github.com/Mehdik/Humanizer/pull/188): Added Spanish ToOrdinalWords translations diff --git a/src/Humanizer.Tests/AmbientCulture.cs b/src/Humanizer.Tests/AmbientCulture.cs index d8515f329..bbd7e0584 100644 --- a/src/Humanizer.Tests/AmbientCulture.cs +++ b/src/Humanizer.Tests/AmbientCulture.cs @@ -4,6 +4,8 @@ namespace Humanizer.Tests { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly", + Justification = "This is a test only class, and doesn't need a 'proper' IDisposable implementation.")] public class AmbientCulture : IDisposable { private readonly CultureInfo _culture; @@ -20,6 +22,8 @@ public AmbientCulture(string cultureName) { } + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly", + Justification="This is a test only class, and doesn't need a 'proper' IDisposable implementation.")] public void Dispose() { Thread.CurrentThread.CurrentUICulture = _culture; diff --git a/src/Humanizer.Tests/Humanizer.Tests.csproj b/src/Humanizer.Tests/Humanizer.Tests.csproj index 211b172f1..b7a0d5106 100644 --- a/src/Humanizer.Tests/Humanizer.Tests.csproj +++ b/src/Humanizer.Tests/Humanizer.Tests.csproj @@ -49,6 +49,16 @@ true false + + bin\Verify\ + ..\Humanizer.ruleset + true + 1591 + bin\Verify\Humanizer.Tests.XML + pdbonly + true + true + ..\packages\ApprovalTests.3.0.5\lib\net40\ApprovalTests.dll @@ -98,6 +108,8 @@ + + @@ -125,6 +137,10 @@ + + + + diff --git a/src/Humanizer.Tests/Localisation/ar/DateHumanizeTests.cs b/src/Humanizer.Tests/Localisation/ar/DateHumanizeTests.cs index 00151b5cb..51ba125be 100644 --- a/src/Humanizer.Tests/Localisation/ar/DateHumanizeTests.cs +++ b/src/Humanizer.Tests/Localisation/ar/DateHumanizeTests.cs @@ -1,4 +1,4 @@ -using Humanizer.Localisation; +using Humanizer.Localisation; using Xunit.Extensions; namespace Humanizer.Tests.Localisation.ar @@ -18,6 +18,16 @@ public void DaysAgo(int days, string expected) DateHumanize.Verify(expected, days, TimeUnit.Day, Tense.Past); } + [Theory] + [InlineData(1, "في غضون يوم واحد من الآن")] + [InlineData(2, "في غضون يومين من الآن")] + [InlineData(10, "في غضون 10 أيام من الآن")] + [InlineData(17, "في غضون 17 يوم من الآن")] + public void DaysFromNow(int days, string expected) + { + DateHumanize.Verify(expected, days, TimeUnit.Day, Tense.Future); + } + [Theory] [InlineData(-2, "منذ ساعتين")] [InlineData(-1, "منذ ساعة واحدة")] @@ -28,6 +38,16 @@ public void HoursAgo(int hours, string expected) DateHumanize.Verify(expected, hours, TimeUnit.Hour, Tense.Past); } + [Theory] + [InlineData(1, "في غضون ساعة واحدة من الآن")] + [InlineData(2, "في غضون ساعتين من الآن")] + [InlineData(10, "في غضون 10 ساعات من الآن")] + [InlineData(23, "في غضون 23 ساعة من الآن")] + public void HoursFromNow(int hours, string expected) + { + DateHumanize.Verify(expected, hours, TimeUnit.Hour, Tense.Future); + } + [Theory] [InlineData(-2, "منذ دقيقتين")] [InlineData(-1, "منذ دقيقة واحدة")] @@ -38,6 +58,16 @@ public void MinutesAgo(int minutes, string expected) DateHumanize.Verify(expected, minutes, TimeUnit.Minute, Tense.Past); } + [Theory] + [InlineData(1, "في غضون دقيقة واحدة من الآن")] + [InlineData(2, "في غضون دقيقتين من الآن")] + [InlineData(10, "في غضون 10 دقائق من الآن")] + [InlineData(23, "في غضون 23 دقيقة من الآن")] + public void MinutesFromNow(int minutes, string expected) + { + DateHumanize.Verify(expected, minutes, TimeUnit.Minute, Tense.Future); + } + [Theory] [InlineData(-2, "منذ شهرين")] [InlineData(-1, "منذ شهر واحد")] @@ -48,6 +78,15 @@ public void MonthsAgo(int months, string expected) DateHumanize.Verify(expected, months, TimeUnit.Month, Tense.Past); } + [Theory] + [InlineData(1, "في غضون شهر واحد من الآن")] + [InlineData(2, "في غضون شهرين من الآن")] + [InlineData(10, "في غضون 10 أشهر من الآن")] + public void MonthsFromNow(int months, string expected) + { + DateHumanize.Verify(expected, months, TimeUnit.Month, Tense.Future); + } + [Theory] [InlineData(-2, "منذ ثانيتين")] [InlineData(-1, "منذ ثانية واحدة")] @@ -58,6 +97,17 @@ public void SecondsAgo(int seconds, string expected) DateHumanize.Verify(expected, seconds, TimeUnit.Second, Tense.Past); } + [Theory] + [InlineData(0, "الآن")] + [InlineData(1, "في غضون ثانية واحدة من الآن")] + [InlineData(2, "في غضون ثانيتين من الآن")] + [InlineData(10, "في غضون 10 ثوان من الآن")] + [InlineData(24, "في غضون 24 ثانية من الآن")] + public void SecondsFromNow(int seconds, string expected) + { + DateHumanize.Verify(expected, seconds, TimeUnit.Second, Tense.Future); + } + [Theory] [InlineData(-2, "منذ عامين")] [InlineData(-1, "العام السابق")] @@ -67,5 +117,15 @@ public void YearsAgo(int years, string expected) { DateHumanize.Verify(expected, years, TimeUnit.Year, Tense.Past); } + + [Theory] + [InlineData(1, "في غضون سنة واحدة من الآن")] + [InlineData(2, "في غضون سنتين من الآن")] + [InlineData(7, "في غضون 7 سنوات من الآن")] + [InlineData(55, "في غضون 55 سنة من الآن")] + public void YearsFromNow(int years, string expected) + { + DateHumanize.Verify(expected, years, TimeUnit.Year, Tense.Future); + } } } 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.Tests/Localisation/es/NumberToWordsTests.cs b/src/Humanizer.Tests/Localisation/es/NumberToWordsTests.cs index d8c96abab..5dcaf3a7d 100644 --- a/src/Humanizer.Tests/Localisation/es/NumberToWordsTests.cs +++ b/src/Humanizer.Tests/Localisation/es/NumberToWordsTests.cs @@ -12,33 +12,36 @@ public NumberToWordsTests() : base("es-ES") { } [InlineData(1, "uno")] [InlineData(10, "diez")] [InlineData(11, "once")] - [InlineData(122, "ciento veinte dos")] + [InlineData(122, "ciento veintidós")] [InlineData(3501, "tres mil quinientos uno")] [InlineData(100, "cien")] [InlineData(1000, "mil")] [InlineData(100000, "cien mil")] - [InlineData(1000000, "millón")] + [InlineData(1000000, "un millón")] [InlineData(10000000, "diez millones")] [InlineData(100000000, "cien millones")] [InlineData(1000000000, "mil millones")] [InlineData(111, "ciento once")] [InlineData(1111, "mil ciento once")] [InlineData(111111, "ciento once mil ciento once")] - [InlineData(1111111, "millón ciento once mil ciento once")] + [InlineData(1111111, "un millón ciento once mil ciento once")] [InlineData(11111111, "once millones ciento once mil ciento once")] [InlineData(111111111, "ciento once millones ciento once mil ciento once")] + [InlineData(1001111111, "mil millones un millón ciento once mil ciento once")] [InlineData(1111111111, "mil millones ciento once millones ciento once mil ciento once")] - [InlineData(123, "ciento veinte tres")] + [InlineData(123, "ciento veintitrés")] [InlineData(1234, "mil doscientos treinta y cuatro")] [InlineData(12345, "doce mil trescientos cuarenta y cinco")] - [InlineData(123456, "ciento veinte tres mil cuatrocientos cincuenta y seis")] - [InlineData(1234567, "millón doscientos treinta y cuatro mil quinientos sesenta y siete")] + [InlineData(123456, "ciento veintitrés mil cuatrocientos cincuenta y seis")] + [InlineData(1234567, "un millón doscientos treinta y cuatro mil quinientos sesenta y siete")] [InlineData(12345678, "doce millones trescientos cuarenta y cinco mil seiscientos setenta y ocho")] - [InlineData(123456789, "ciento veinte tres millones cuatrocientos cincuenta y seis mil setecientos ochenta y nueve")] + [InlineData(123456789, "ciento veintitrés millones cuatrocientos cincuenta y seis mil setecientos ochenta y nueve")] [InlineData(1234567890, "mil millones doscientos treinta y cuatro millones quinientos sesenta y siete mil ochocientos noventa")] [InlineData(15, "quince")] [InlineData(16, "dieciséis")] - [InlineData(25, "veinte cinco")] + [InlineData(20, "veinte")] + [InlineData(22, "veintidós")] + [InlineData(25, "veinticinco")] [InlineData(35, "treinta y cinco")] [InlineData(1999, "mil novecientos noventa y nueve")] [InlineData(2014, "dos mil catorce")] @@ -49,10 +52,15 @@ public void ToWords(int number, string expected) } [Theory] - [InlineData(1, "primero", null)] + [InlineData(1, "primer", null)] + [InlineData(1, "primer", GrammaticalGender.Masculine)] + [InlineData(1, "primera", GrammaticalGender.Feminine)] [InlineData(2, "segundo", GrammaticalGender.Masculine)] [InlineData(2, "segunda", GrammaticalGender.Feminine)] [InlineData(2, "segundo", GrammaticalGender.Neuter)] + [InlineData(3, "tercer", null)] + [InlineData(3, "tercer", GrammaticalGender.Masculine)] + [InlineData(3, "tercera", GrammaticalGender.Feminine)] [InlineData(11, "once", null)] public void ToOrdinalWords(int number, string words, GrammaticalGender gender) { diff --git a/src/Humanizer.Tests/Localisation/es/OrdinalizeTests.cs b/src/Humanizer.Tests/Localisation/es/OrdinalizeTests.cs index c2c6f56bc..cf0e9a64d 100644 --- a/src/Humanizer.Tests/Localisation/es/OrdinalizeTests.cs +++ b/src/Humanizer.Tests/Localisation/es/OrdinalizeTests.cs @@ -12,18 +12,18 @@ public OrdinalizeTests() [Theory] [InlineData("0", "0")] - [InlineData("1", "1º")] - [InlineData("2", "2º")] - [InlineData("3", "3º")] - [InlineData("4", "4º")] - [InlineData("5", "5º")] - [InlineData("6", "6º")] - [InlineData("23", "23º")] - [InlineData("100", "100º")] - [InlineData("101", "101º")] - [InlineData("102", "102º")] - [InlineData("103", "103º")] - [InlineData("1001", "1001º")] + [InlineData("1", "1.º")] + [InlineData("2", "2.º")] + [InlineData("3", "3.º")] + [InlineData("4", "4.º")] + [InlineData("5", "5.º")] + [InlineData("6", "6.º")] + [InlineData("23", "23.º")] + [InlineData("100", "100.º")] + [InlineData("101", "101.º")] + [InlineData("102", "102.º")] + [InlineData("103", "103.º")] + [InlineData("1001", "1001.º")] public void OrdinalizeString(string number, string ordinalized) { Assert.Equal(number.Ordinalize(GrammaticalGender.Masculine), ordinalized); @@ -31,18 +31,18 @@ public void OrdinalizeString(string number, string ordinalized) [Theory] [InlineData("0", "0")] - [InlineData("1", "1ª")] - [InlineData("2", "2ª")] - [InlineData("3", "3ª")] - [InlineData("4", "4ª")] - [InlineData("5", "5ª")] - [InlineData("6", "6ª")] - [InlineData("23", "23ª")] - [InlineData("100", "100ª")] - [InlineData("101", "101ª")] - [InlineData("102", "102ª")] - [InlineData("103", "103ª")] - [InlineData("1001", "1001ª")] + [InlineData("1", "1.ª")] + [InlineData("2", "2.ª")] + [InlineData("3", "3.ª")] + [InlineData("4", "4.ª")] + [InlineData("5", "5.ª")] + [InlineData("6", "6.ª")] + [InlineData("23", "23.ª")] + [InlineData("100", "100.ª")] + [InlineData("101", "101.ª")] + [InlineData("102", "102.ª")] + [InlineData("103", "103.ª")] + [InlineData("1001", "1001.ª")] public void OrdinalizeStringFeminine(string number, string ordinalized) { Assert.Equal(number.Ordinalize(GrammaticalGender.Feminine), ordinalized); @@ -50,19 +50,19 @@ public void OrdinalizeStringFeminine(string number, string ordinalized) [Theory] [InlineData(0, "0")] - [InlineData(1, "1º")] - [InlineData(2, "2º")] - [InlineData(3, "3º")] - [InlineData(4, "4º")] - [InlineData(5, "5º")] - [InlineData(6, "6º")] - [InlineData(10, "10º")] - [InlineData(23, "23º")] - [InlineData(100, "100º")] - [InlineData(101, "101º")] - [InlineData(102, "102º")] - [InlineData(103, "103º")] - [InlineData(1001, "1001º")] + [InlineData(1, "1.º")] + [InlineData(2, "2.º")] + [InlineData(3, "3.º")] + [InlineData(4, "4.º")] + [InlineData(5, "5.º")] + [InlineData(6, "6.º")] + [InlineData(10, "10.º")] + [InlineData(23, "23.º")] + [InlineData(100, "100.º")] + [InlineData(101, "101.º")] + [InlineData(102, "102.º")] + [InlineData(103, "103.º")] + [InlineData(1001, "1001.º")] public void OrdinalizeNumber(int number, string ordinalized) { Assert.Equal(number.Ordinalize(GrammaticalGender.Masculine), ordinalized); @@ -70,19 +70,19 @@ public void OrdinalizeNumber(int number, string ordinalized) [Theory] [InlineData(0, "0")] - [InlineData(1, "1ª")] - [InlineData(2, "2ª")] - [InlineData(3, "3ª")] - [InlineData(4, "4ª")] - [InlineData(5, "5ª")] - [InlineData(6, "6ª")] - [InlineData(10, "10ª")] - [InlineData(23, "23ª")] - [InlineData(100, "100ª")] - [InlineData(101, "101ª")] - [InlineData(102, "102ª")] - [InlineData(103, "103ª")] - [InlineData(1001, "1001ª")] + [InlineData(1, "1.ª")] + [InlineData(2, "2.ª")] + [InlineData(3, "3.ª")] + [InlineData(4, "4.ª")] + [InlineData(5, "5.ª")] + [InlineData(6, "6.ª")] + [InlineData(10, "10.ª")] + [InlineData(23, "23.ª")] + [InlineData(100, "100.ª")] + [InlineData(101, "101.ª")] + [InlineData(102, "102.ª")] + [InlineData(103, "103.ª")] + [InlineData(1001, "1001.ª")] public void OrdinalizeNumberFeminine(int number, string ordinalized) { Assert.Equal(number.Ordinalize(GrammaticalGender.Feminine), ordinalized); diff --git a/src/Humanizer.Tests/Localisation/ja/DateHumanizeTests.cs b/src/Humanizer.Tests/Localisation/ja/DateHumanizeTests.cs new file mode 100644 index 000000000..a36d73c17 --- /dev/null +++ b/src/Humanizer.Tests/Localisation/ja/DateHumanizeTests.cs @@ -0,0 +1,113 @@ +using Humanizer.Localisation; +using Xunit; +using Xunit.Extensions; + +namespace Humanizer.Tests.Localisation.ja +{ + public class DateHumanizeTests : AmbientCulture + { + public DateHumanizeTests() : base("ja") { } + + [Theory] + [InlineData(1, "1 秒前")] + [InlineData(2, "2 秒前")] + public void SecondsAgo(int seconds, string expected) + { + DateHumanize.Verify(expected, seconds, TimeUnit.Second, Tense.Past); + } + + [Theory] + [InlineData(1, "1 秒後")] + [InlineData(2, "2 秒後")] + public void SecondsFromNow(int seconds, string expected) + { + DateHumanize.Verify(expected, seconds, TimeUnit.Second, Tense.Future); + } + + [Theory] + [InlineData(1, "1 分前")] + [InlineData(2, "2 分前")] + public void MinutesAgo(int minutes, string expected) + { + DateHumanize.Verify(expected, minutes, TimeUnit.Minute, Tense.Past); + } + + [Theory] + [InlineData(1, "1 分後")] + [InlineData(2, "2 分後")] + public void MinutesFromNow(int minutes, string expected) + { + DateHumanize.Verify(expected, minutes, TimeUnit.Minute, Tense.Future); + } + + [Theory] + [InlineData(1, "1 時間前")] + [InlineData(2, "2 時間前")] + public void HoursAgo(int hours, string expected) + { + DateHumanize.Verify(expected, hours, TimeUnit.Hour, Tense.Past); + } + + [Theory] + [InlineData(1, "1 時間後")] + [InlineData(2, "2 時間後")] + public void HoursFromNow(int hours, string expected) + { + DateHumanize.Verify(expected, hours, TimeUnit.Hour, Tense.Future); + } + + [Theory] + [InlineData(1, "昨日")] + [InlineData(2, "2 日前")] + public void DaysAgo(int days, string expected) + { + DateHumanize.Verify(expected, days, TimeUnit.Day, Tense.Past); + } + + [Theory] + [InlineData(1, "明日")] + [InlineData(2, "2 日後")] + public void DaysFromNow(int days, string expected) + { + DateHumanize.Verify(expected, days, TimeUnit.Day, Tense.Future); + } + + [Theory] + [InlineData(1, "先月")] + [InlineData(2, "2 か月前")] + public void MonthsAgo(int months, string expected) + { + DateHumanize.Verify(expected, months, TimeUnit.Month, Tense.Past); + } + + [Theory] + [InlineData(1, "来月")] + [InlineData(2, "2 か月後")] + public void MonthsFromNow(int months, string expected) + { + DateHumanize.Verify(expected, months, TimeUnit.Month, Tense.Future); + } + + [Theory] + [InlineData(1, "去年")] + [InlineData(2, "2 年前")] + public void YearsAgo(int years, string expected) + { + DateHumanize.Verify(expected, years, TimeUnit.Year, Tense.Past); + } + + [Theory] + [InlineData(1, "来年")] + [InlineData(2, "2 年後")] + public void YearsFromNow(int years, string expected) + { + DateHumanize.Verify(expected, years, TimeUnit.Year, Tense.Future); + } + + [Fact] + public void Now() + { + DateHumanize.Verify("今", 0, TimeUnit.Day, Tense.Past); + } + } +} diff --git a/src/Humanizer.Tests/Localisation/ja/TimeSpanHumanizeTests.cs b/src/Humanizer.Tests/Localisation/ja/TimeSpanHumanizeTests.cs new file mode 100644 index 000000000..136318e5f --- /dev/null +++ b/src/Humanizer.Tests/Localisation/ja/TimeSpanHumanizeTests.cs @@ -0,0 +1,65 @@ +using System; +using Xunit; +using Xunit.Extensions; + +namespace Humanizer.Tests.Localisation.ja +{ + public class TimeSpanHumanizeTests : AmbientCulture + { + public TimeSpanHumanizeTests() : base("ja") { } + + [Theory] + [InlineData(7, "1 週間")] + [InlineData(14, "2 週間")] + public void Weeks(int days, string expected) + { + Assert.Equal(expected, TimeSpan.FromDays(days).Humanize()); + } + + [Theory] + [InlineData(1, "1 日")] + [InlineData(2, "2 日")] + public void Days(int days, string expected) + { + Assert.Equal(expected, TimeSpan.FromDays(days).Humanize()); + } + + [Theory] + [InlineData(1, "1 時間")] + [InlineData(2, "2 時間")] + public void Hours(int hours, string expected) + { + Assert.Equal(expected, TimeSpan.FromHours(hours).Humanize()); + } + + [Theory] + [InlineData(1, "1 分")] + [InlineData(2, "2 分")] + public void Minutes(int minutes, string expected) + { + Assert.Equal(expected, TimeSpan.FromMinutes(minutes).Humanize()); + } + + [Theory] + [InlineData(1, "1 秒")] + [InlineData(2, "2 秒")] + public void Seconds(int seconds, string expected) + { + Assert.Equal(expected, TimeSpan.FromSeconds(seconds).Humanize()); + } + + [Theory] + [InlineData(1, "1 ミリ秒")] + [InlineData(2, "2 ミリ秒")] + public void Milliseconds(int milliseconds, string expected) + { + Assert.Equal(expected, TimeSpan.FromMilliseconds(milliseconds).Humanize()); + } + + [Fact] + public void NoTime() + { + Assert.Equal("0 秒", TimeSpan.Zero.Humanize()); + } + } +} \ No newline at end of file diff --git a/src/Humanizer.Tests/Localisation/ru-RU/NumberToWordsTests.cs b/src/Humanizer.Tests/Localisation/ru-RU/NumberToWordsTests.cs index d5bf08d36..c0e6b16d5 100644 --- a/src/Humanizer.Tests/Localisation/ru-RU/NumberToWordsTests.cs +++ b/src/Humanizer.Tests/Localisation/ru-RU/NumberToWordsTests.cs @@ -99,6 +99,8 @@ public void ToWordsWithGender(int number, string expected, GrammaticalGender gen [InlineData(80, "восьмидесятый")] [InlineData(90, "девяностый")] [InlineData(100, "сотый")] + [InlineData(101, "сто первый")] + [InlineData(140, "сто сороковой")] [InlineData(200, "двухсотый")] [InlineData(300, "трёхсотый")] [InlineData(400, "четырёхсотый")] @@ -108,16 +110,22 @@ public void ToWordsWithGender(int number, string expected, GrammaticalGender gen [InlineData(800, "восьмисотый")] [InlineData(900, "девятисотый")] [InlineData(1000, "тысячный")] + [InlineData(1001, "одна тысяча первый")] + [InlineData(1040, "одна тысяча сороковой")] [InlineData(2000, "двухтысячный")] [InlineData(3000, "трёхтысячный")] [InlineData(4000, "четырёхтысячный")] [InlineData(5000, "пятитысячный")] [InlineData(10000, "десятитысячный")] + [InlineData(21000, "двадцатиоднатысячный")] [InlineData(100000, "стотысячный")] + [InlineData(200000, "двухсоттысячный")] [InlineData(1000000, "миллионный")] [InlineData(2000000, "двухмиллионный")] [InlineData(10000000, "десятимиллионный")] + [InlineData(21000000, "двадцатиодномиллионный")] [InlineData(100000000, "стомиллионный")] + [InlineData(230000000, "двухсоттридцатимиллионный")] [InlineData(1000000000, "миллиардный")] [InlineData(2000000000, "двухмиллиардный")] [InlineData(122, "сто двадцать второй")] @@ -125,11 +133,15 @@ public void ToWordsWithGender(int number, string expected, GrammaticalGender gen [InlineData(111, "сто одиннадцатый")] [InlineData(1112, "одна тысяча сто двенадцатый")] [InlineData(11213, "одиннадцать тысячь двести тринадцатый")] + [InlineData(101000, "стооднатысячный")] [InlineData(121314, "сто двадцать одна тысяча триста четырнадцатый")] [InlineData(2132415, "два миллиона сто тридцать две тысячи четыреста пятнадцатый")] [InlineData(12345516, "двенадцать миллионов триста сорок пять тысячь пятьсот шестнадцатый")] [InlineData(751633617, "семьсот пятьдесят один миллион шестьсот тридцать три тысячи шестьсот семнадцатый")] [InlineData(1111111118, "один миллиард сто одиннадцать миллионов сто одиннадцать тысячь сто восемнадцатый")] + [InlineData(1111111000, "один миллиард сто одиннадцать миллионов стоодиннадцатитысячный")] + [InlineData(1234567000, "один миллиард двести тридцать четыре миллиона пятисотшестидесятисемитысячный")] + [InlineData(2000000000, "двухмиллиардный")] [InlineData(-751633617, "минус семьсот пятьдесят один миллион шестьсот тридцать три тысячи шестьсот семнадцатый")] public void ToOrdinalWords(int number, string expected) { diff --git a/src/Humanizer.Tests/Localisation/sr-Latn/DateHumanizeTests.cs b/src/Humanizer.Tests/Localisation/sr-Latn/DateHumanizeTests.cs new file mode 100644 index 000000000..85c5dced5 --- /dev/null +++ b/src/Humanizer.Tests/Localisation/sr-Latn/DateHumanizeTests.cs @@ -0,0 +1,140 @@ +using Humanizer.Localisation; +using Xunit; +using Xunit.Extensions; + +namespace Humanizer.Tests.Localisation.srLatn +{ + public class DateHumanizeDefaultStrategyTests : AmbientCulture + { + public DateHumanizeDefaultStrategyTests() + : base("sr-Latn") + { + } + + [Theory] + [InlineData(1, "pre sekund")] + [InlineData(10, "pre 10 sekundi")] + [InlineData(59, "pre 59 sekundi")] + [InlineData(60, "pre minut")] + public void SecondsAgo(int seconds, string expected) + { + DateHumanize.Verify(expected, seconds, TimeUnit.Second, Tense.Past); + } + + [Theory] + [InlineData(1, "za sekund")] + [InlineData(10, "za 10 sekundi")] + [InlineData(59, "za 59 sekundi")] + [InlineData(60, "za minut")] + public void SecondsFromNow(int seconds, string expected) + { + DateHumanize.Verify(expected, seconds, TimeUnit.Second, Tense.Future); + } + + [Theory] + [InlineData(1, "pre minut")] + [InlineData(10, "pre 10 minuta")] + [InlineData(44, "pre 44 minuta")] + [InlineData(45, "pre sat vremena")] + [InlineData(119, "pre sat vremena")] + [InlineData(120, "pre 2 sata")] + public void MinutesAgo(int minutes, string expected) + { + DateHumanize.Verify(expected, minutes, TimeUnit.Minute, Tense.Past); + } + + [Theory] + [InlineData(1, "za minut")] + [InlineData(10, "za 10 minuta")] + [InlineData(44, "za 44 minuta")] + [InlineData(45, "za sat vremena")] + [InlineData(119, "za sat vremena")] + [InlineData(120, "za 2 sata")] + public void MinutesFromNow(int minutes, string expected) + { + DateHumanize.Verify(expected, minutes, TimeUnit.Minute, Tense.Future); + } + + [Theory] + [InlineData(1, "pre sat vremena")] + [InlineData(10, "pre 10 sati")] + [InlineData(23, "pre 23 sata")] + [InlineData(24, "juče")] + public void HoursAgo(int hours, string expected) + { + DateHumanize.Verify(expected, hours, TimeUnit.Hour, Tense.Past); + } + + [Theory] + [InlineData(1, "za sat vremena")] + [InlineData(10, "za 10 sati")] + [InlineData(23, "za 23 sata")] + [InlineData(24, "sutra")] + public void HoursFromNow(int hours, string expected) + { + DateHumanize.Verify(expected, hours, TimeUnit.Hour, Tense.Future); + } + + [Theory] + [InlineData(1, "juče")] + [InlineData(10, "pre 10 dana")] + [InlineData(28, "pre 28 dana")] + [InlineData(32, "pre mesec dana")] + public void DaysAgo(int days, string expected) + { + DateHumanize.Verify(expected, days, TimeUnit.Day, Tense.Past); + } + + [Theory] + [InlineData(1, "sutra")] + [InlineData(10, "za 10 dana")] + [InlineData(28, "za 28 dana")] + [InlineData(32, "za mesec dana")] + public void DaysFromNow(int days, string expected) + { + DateHumanize.Verify(expected, days, TimeUnit.Day, Tense.Future); + } + + [Theory] + [InlineData(1, "pre mesec dana")] + [InlineData(10, "pre 10 meseci")] + [InlineData(11, "pre 11 meseci")] + [InlineData(12, "pre godinu dana")] + public void MonthsAgo(int months, string expected) + { + DateHumanize.Verify(expected, months, TimeUnit.Month, Tense.Past); + } + + [Theory] + [InlineData(1, "za mesec dana")] + [InlineData(10, "za 10 meseci")] + [InlineData(11, "za 11 meseci")] + [InlineData(12, "za godinu dana")] + public void MonthsFromNow(int months, string expected) + { + DateHumanize.Verify(expected, months, TimeUnit.Month, Tense.Future); + } + + [Theory] + [InlineData(1, "pre godinu dana")] + [InlineData(2, "pre 2 godine")] + public void YearsAgo(int years, string expected) + { + DateHumanize.Verify(expected, years, TimeUnit.Year, Tense.Past); + } + + [Theory] + [InlineData(1, "za godinu dana")] + [InlineData(2, "za 2 godine")] + public void YearsFromNow(int years, string expected) + { + DateHumanize.Verify(expected, years, TimeUnit.Year, Tense.Future); + } + + [Fact] + public void Now() + { + DateHumanize.Verify("sada", 0, TimeUnit.Year, Tense.Future); + } + } +} \ No newline at end of file diff --git a/src/Humanizer.Tests/Localisation/sr-Latn/TimeSpanHumanizeTests.cs b/src/Humanizer.Tests/Localisation/sr-Latn/TimeSpanHumanizeTests.cs new file mode 100644 index 000000000..b60181297 --- /dev/null +++ b/src/Humanizer.Tests/Localisation/sr-Latn/TimeSpanHumanizeTests.cs @@ -0,0 +1,118 @@ +using System; +using Xunit; +using Xunit.Extensions; + +namespace Humanizer.Tests.Localisation.srLatn +{ + public class TimeSpanHumanizeTests : AmbientCulture + { + public TimeSpanHumanizeTests() : base("sr-Latn") { } + + [Theory] + [InlineData(35, "5 nedelja")] + [InlineData(14, "2 nedelje")] + [InlineData(7, "1 nedelja")] + public void Weeks(int days, string expected) + { + var actual = TimeSpan.FromDays(days).Humanize(); + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(6, "6 dana")] + [InlineData(2, "2 dana")] + [InlineData(1, "1 dan")] + public void Days(int days, string expected) + { + var actual = TimeSpan.FromDays(days).Humanize(); + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(5, "5 sati")] + [InlineData(2, "2 sata")] + [InlineData(1, "1 sat")] + public void Hours(int hours, string expected) + { + var actual = TimeSpan.FromHours(hours).Humanize(); + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(2, "2 minuta")] + [InlineData(1, "1 minut")] + public void Minutes(int minutes, string expected) + { + var actual = TimeSpan.FromMinutes(minutes).Humanize(); + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(135, "2 minuta")] + [InlineData(60, "1 minut")] + [InlineData(5, "5 sekundi")] + [InlineData(3, "3 sekunde")] + [InlineData(2, "2 sekunde")] + [InlineData(1, "1 sekunda")] + public void Seconds(int seconds, string expected) + { + var actual = TimeSpan.FromSeconds(seconds).Humanize(); + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(2500, "2 sekunde")] + [InlineData(1400, "1 sekunda")] + [InlineData(2, "2 milisekunde")] + [InlineData(1, "1 milisekunda")] + public void Milliseconds(int ms, string expected) + { + var actual = TimeSpan.FromMilliseconds(ms).Humanize(); + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(0, 3, "bez proteklog vremena")] + [InlineData(0, 2, "bez proteklog vremena")] + [InlineData(10, 2, "10 milisekundi")] + [InlineData(1400, 2, "1 sekunda, 400 milisekundi")] + [InlineData(2500, 2, "2 sekunde, 500 milisekundi")] + [InlineData(120000, 2, "2 minuta")] + [InlineData(62000, 2, "1 minut, 2 sekunde")] + [InlineData(62020, 2, "1 minut, 2 sekunde")] + [InlineData(62020, 3, "1 minut, 2 sekunde, 20 milisekundi")] + [InlineData(3600020, 4, "1 sat, 20 milisekundi")] + [InlineData(3600020, 3, "1 sat, 20 milisekundi")] + [InlineData(3600020, 2, "1 sat, 20 milisekundi")] + [InlineData(3600020, 1, "1 sat")] + [InlineData(3603001, 2, "1 sat, 3 sekunde")] + [InlineData(3603001, 3, "1 sat, 3 sekunde, 1 milisekunda")] + [InlineData(86400000, 3, "1 dan")] + [InlineData(86400000, 2, "1 dan")] + [InlineData(86400000, 1, "1 dan")] + [InlineData(86401000, 1, "1 dan")] + [InlineData(86401000, 2, "1 dan, 1 sekunda")] + [InlineData(86401200, 2, "1 dan, 1 sekunda")] + [InlineData(86401200, 3, "1 dan, 1 sekunda, 200 milisekundi")] + [InlineData(1296000000, 1, "2 nedelje")] + [InlineData(1296000000, 2, "2 nedelje, 1 dan")] + [InlineData(1299600000, 2, "2 nedelje, 1 dan")] + [InlineData(1299600000, 3, "2 nedelje, 1 dan, 1 sat")] + [InlineData(1299630020, 3, "2 nedelje, 1 dan, 1 sat")] + [InlineData(1299630020, 4, "2 nedelje, 1 dan, 1 sat, 30 sekundi")] + [InlineData(1299630020, 5, "2 nedelje, 1 dan, 1 sat, 30 sekundi, 20 milisekundi")] + public void TimeSpanWithPrecesion(int milliseconds, int precesion, string expected) + { + var actual = TimeSpan.FromMilliseconds(milliseconds).Humanize(precesion); + Assert.Equal(expected, actual); + } + + [Fact] + public void NoTime() + { + var noTime = TimeSpan.Zero; + var actual = noTime.Humanize(); + Assert.Equal("bez proteklog vremena", actual); + } + } +} \ No newline at end of file diff --git a/src/Humanizer.Tests/Localisation/sr/DateHumanizeTests.cs b/src/Humanizer.Tests/Localisation/sr/DateHumanizeTests.cs new file mode 100644 index 000000000..69c95c1d1 --- /dev/null +++ b/src/Humanizer.Tests/Localisation/sr/DateHumanizeTests.cs @@ -0,0 +1,140 @@ +using Humanizer.Localisation; +using Xunit; +using Xunit.Extensions; + +namespace Humanizer.Tests.Localisation.sr +{ + public class DateHumanizeDefaultStrategyTests : AmbientCulture + { + public DateHumanizeDefaultStrategyTests() + : base("sr") + { + } + + [Theory] + [InlineData(1, "пре секунд")] + [InlineData(10, "пре 10 секунди")] + [InlineData(59, "пре 59 секунди")] + [InlineData(60, "пре минут")] + public void SecondsAgo(int seconds, string expected) + { + DateHumanize.Verify(expected, seconds, TimeUnit.Second, Tense.Past); + } + + [Theory] + [InlineData(1, "за секунд")] + [InlineData(10, "за 10 секунди")] + [InlineData(59, "за 59 секунди")] + [InlineData(60, "за минут")] + public void SecondsFromNow(int seconds, string expected) + { + DateHumanize.Verify(expected, seconds, TimeUnit.Second, Tense.Future); + } + + [Theory] + [InlineData(1, "пре минут")] + [InlineData(10, "пре 10 минута")] + [InlineData(44, "пре 44 минута")] + [InlineData(45, "пре сат времена")] + [InlineData(119, "пре сат времена")] + [InlineData(120, "пре 2 сата")] + public void MinutesAgo(int minutes, string expected) + { + DateHumanize.Verify(expected, minutes, TimeUnit.Minute, Tense.Past); + } + + [Theory] + [InlineData(1, "за минут")] + [InlineData(10, "за 10 минута")] + [InlineData(44, "за 44 минута")] + [InlineData(45, "за сат времена")] + [InlineData(119, "за сат времена")] + [InlineData(120, "за 2 сата")] + public void MinutesFromNow(int minutes, string expected) + { + DateHumanize.Verify(expected, minutes, TimeUnit.Minute, Tense.Future); + } + + [Theory] + [InlineData(1, "пре сат времена")] + [InlineData(10, "пре 10 сати")] + [InlineData(23, "пре 23 сата")] + [InlineData(24, "јуче")] + public void HoursAgo(int hours, string expected) + { + DateHumanize.Verify(expected, hours, TimeUnit.Hour, Tense.Past); + } + + [Theory] + [InlineData(1, "за сат времена")] + [InlineData(10, "за 10 сати")] + [InlineData(23, "за 23 сата")] + [InlineData(24, "сутра")] + public void HoursFromNow(int hours, string expected) + { + DateHumanize.Verify(expected, hours, TimeUnit.Hour, Tense.Future); + } + + [Theory] + [InlineData(1, "јуче")] + [InlineData(10, "пре 10 дана")] + [InlineData(28, "пре 28 дана")] + [InlineData(32, "пре месец дана")] + public void DaysAgo(int days, string expected) + { + DateHumanize.Verify(expected, days, TimeUnit.Day, Tense.Past); + } + + [Theory] + [InlineData(1, "сутра")] + [InlineData(10, "за 10 дана")] + [InlineData(28, "за 28 дана")] + [InlineData(32, "за месец дана")] + public void DaysFromNow(int days, string expected) + { + DateHumanize.Verify(expected, days, TimeUnit.Day, Tense.Future); + } + + [Theory] + [InlineData(1, "пре месец дана")] + [InlineData(10, "пре 10 месеци")] + [InlineData(11, "пре 11 месеци")] + [InlineData(12, "пре годину дана")] + public void MonthsAgo(int months, string expected) + { + DateHumanize.Verify(expected, months, TimeUnit.Month, Tense.Past); + } + + [Theory] + [InlineData(1, "за месец дана")] + [InlineData(10, "за 10 месеци")] + [InlineData(11, "за 11 месеци")] + [InlineData(12, "за годину дана")] + public void MonthsFromNow(int months, string expected) + { + DateHumanize.Verify(expected, months, TimeUnit.Month, Tense.Future); + } + + [Theory] + [InlineData(1, "пре годину дана")] + [InlineData(2, "пре 2 године")] + public void YearsAgo(int years, string expected) + { + DateHumanize.Verify(expected, years, TimeUnit.Year, Tense.Past); + } + + [Theory] + [InlineData(1, "за годину дана")] + [InlineData(2, "за 2 године")] + public void YearsFromNow(int years, string expected) + { + DateHumanize.Verify(expected, years, TimeUnit.Year, Tense.Future); + } + + [Fact] + public void Now() + { + DateHumanize.Verify("сада", 0, TimeUnit.Year, Tense.Future); + } + } +} diff --git a/src/Humanizer.Tests/Localisation/sr/TimeSpanHumanizeTests.cs b/src/Humanizer.Tests/Localisation/sr/TimeSpanHumanizeTests.cs new file mode 100644 index 000000000..cd8b5fc73 --- /dev/null +++ b/src/Humanizer.Tests/Localisation/sr/TimeSpanHumanizeTests.cs @@ -0,0 +1,118 @@ +using System; +using Xunit; +using Xunit.Extensions; + +namespace Humanizer.Tests.Localisation.srCyrl +{ + public class TimeSpanHumanizeTests : AmbientCulture + { + public TimeSpanHumanizeTests() : base("sr") { } + + [Theory] + [InlineData(35, "5 недеља")] + [InlineData(14, "2 недеље")] + [InlineData(7, "1 недеља")] + public void Weeks(int days, string expected) + { + var actual = TimeSpan.FromDays(days).Humanize(); + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(6, "6 дана")] + [InlineData(2, "2 дана")] + [InlineData(1, "1 дан")] + public void Days(int days, string expected) + { + var actual = TimeSpan.FromDays(days).Humanize(); + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(5, "5 сати")] + [InlineData(2, "2 сата")] + [InlineData(1, "1 сат")] + public void Hours(int hours, string expected) + { + var actual = TimeSpan.FromHours(hours).Humanize(); + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(2, "2 минута")] + [InlineData(1, "1 минут")] + public void Minutes(int minutes, string expected) + { + var actual = TimeSpan.FromMinutes(minutes).Humanize(); + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(135, "2 минута")] + [InlineData(60, "1 минут")] + [InlineData(5, "5 секунди")] + [InlineData(3, "3 секунде")] + [InlineData(2, "2 секунде")] + [InlineData(1, "1 секунда")] + public void Seconds(int seconds, string expected) + { + var actual = TimeSpan.FromSeconds(seconds).Humanize(); + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(2500, "2 секунде")] + [InlineData(1400, "1 секунда")] + [InlineData(2, "2 милисекунде")] + [InlineData(1, "1 милисекунда")] + public void Milliseconds(int ms, string expected) + { + var actual = TimeSpan.FromMilliseconds(ms).Humanize(); + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(0, 3, "без протеклог времена")] + [InlineData(0, 2, "без протеклог времена")] + [InlineData(10, 2, "10 милисекунди")] + [InlineData(1400, 2, "1 секунда, 400 милисекунди")] + [InlineData(2500, 2, "2 секунде, 500 милисекунди")] + [InlineData(120000, 2, "2 минута")] + [InlineData(62000, 2, "1 минут, 2 секунде")] + [InlineData(62020, 2, "1 минут, 2 секунде")] + [InlineData(62020, 3, "1 минут, 2 секунде, 20 милисекунди")] + [InlineData(3600020, 4, "1 сат, 20 милисекунди")] + [InlineData(3600020, 3, "1 сат, 20 милисекунди")] + [InlineData(3600020, 2, "1 сат, 20 милисекунди")] + [InlineData(3600020, 1, "1 сат")] + [InlineData(3603001, 2, "1 сат, 3 секунде")] + [InlineData(3603001, 3, "1 сат, 3 секунде, 1 милисекунда")] + [InlineData(86400000, 3, "1 дан")] + [InlineData(86400000, 2, "1 дан")] + [InlineData(86400000, 1, "1 дан")] + [InlineData(86401000, 1, "1 дан")] + [InlineData(86401000, 2, "1 дан, 1 секунда")] + [InlineData(86401200, 2, "1 дан, 1 секунда")] + [InlineData(86401200, 3, "1 дан, 1 секунда, 200 милисекунди")] + [InlineData(1296000000, 1, "2 недеље")] + [InlineData(1296000000, 2, "2 недеље, 1 дан")] + [InlineData(1299600000, 2, "2 недеље, 1 дан")] + [InlineData(1299600000, 3, "2 недеље, 1 дан, 1 сат")] + [InlineData(1299630020, 3, "2 недеље, 1 дан, 1 сат")] + [InlineData(1299630020, 4, "2 недеље, 1 дан, 1 сат, 30 секунди")] + [InlineData(1299630020, 5, "2 недеље, 1 дан, 1 сат, 30 секунди, 20 милисекунди")] + public void TimeSpanWithPrecesion(int milliseconds, int precesion, string expected) + { + var actual = TimeSpan.FromMilliseconds(milliseconds).Humanize(precesion); + Assert.Equal(expected, actual); + } + + [Fact] + public void NoTime() + { + var noTime = TimeSpan.Zero; + var actual = noTime.Humanize(); + Assert.Equal("без протеклог времена", actual); + } + } +} diff --git a/src/Humanizer.nuspec b/src/Humanizer.nuspec index a4eaf0fd7..2bd8f217a 100644 --- a/src/Humanizer.nuspec +++ b/src/Humanizer.nuspec @@ -7,6 +7,7 @@ Mehdi Khalili Mehdi Khalili https://github.com/MehdiK/Humanizer + https://raw.github.com/MehdiK/Humanizer/master/logo.png false Humanizer meets all your .NET needs for manipulating and displaying strings, enums, dates, times, timespans, numbers and quantities Copyright 2012-2014 Mehdi Khalili diff --git a/src/Humanizer.ruleset b/src/Humanizer.ruleset new file mode 100644 index 000000000..3af068ece --- /dev/null +++ b/src/Humanizer.ruleset @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Humanizer.sln b/src/Humanizer.sln index 3428859ae..e5d98557f 100644 --- a/src/Humanizer.sln +++ b/src/Humanizer.sln @@ -16,22 +16,37 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ..\release_notes.md = ..\release_notes.md EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{97AAE24D-0488-42AE-A585-86D882F23D5F}" + ProjectSection(SolutionItems) = preProject + ..\build.cmd = ..\build.cmd + ..\build.config = ..\build.config + ..\build.proj = ..\build.proj + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU + Verify|Any CPU = Verify|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {F886A8DA-3EFC-4A89-91DD-06FAF13DA172}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F886A8DA-3EFC-4A89-91DD-06FAF13DA172}.Debug|Any CPU.Build.0 = Debug|Any CPU {F886A8DA-3EFC-4A89-91DD-06FAF13DA172}.Release|Any CPU.ActiveCfg = Release|Any CPU {F886A8DA-3EFC-4A89-91DD-06FAF13DA172}.Release|Any CPU.Build.0 = Release|Any CPU + {F886A8DA-3EFC-4A89-91DD-06FAF13DA172}.Verify|Any CPU.ActiveCfg = Verify|Any CPU + {F886A8DA-3EFC-4A89-91DD-06FAF13DA172}.Verify|Any CPU.Build.0 = Verify|Any CPU {511A7984-F455-4A6E-ADB9-9CAAC47EA079}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {511A7984-F455-4A6E-ADB9-9CAAC47EA079}.Debug|Any CPU.Build.0 = Debug|Any CPU {511A7984-F455-4A6E-ADB9-9CAAC47EA079}.Release|Any CPU.ActiveCfg = Release|Any CPU {511A7984-F455-4A6E-ADB9-9CAAC47EA079}.Release|Any CPU.Build.0 = Release|Any CPU + {511A7984-F455-4A6E-ADB9-9CAAC47EA079}.Verify|Any CPU.ActiveCfg = Verify|Any CPU + {511A7984-F455-4A6E-ADB9-9CAAC47EA079}.Verify|Any CPU.Build.0 = Verify|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {97AAE24D-0488-42AE-A585-86D882F23D5F} = {4779A7C9-9ED8-4146-A158-FBE0B1BE09D9} + EndGlobalSection EndGlobal diff --git a/src/Humanizer/Bytes/ByteSize.cs b/src/Humanizer/Bytes/ByteSize.cs index 934149054..253f749d5 100644 --- a/src/Humanizer/Bytes/ByteSize.cs +++ b/src/Humanizer/Bytes/ByteSize.cs @@ -22,12 +22,12 @@ using System; -// ReSharper disable CSharpWarnings::CS1591 namespace Humanizer.Bytes { /// /// Represents a byte size value. /// +#pragma warning disable 1591 public struct ByteSize : IComparable, IEquatable { public static readonly ByteSize MinValue = FromBits(long.MinValue); @@ -385,4 +385,4 @@ public static ByteSize Parse(string s) } } } -// ReSharper restore CSharpWarnings::CS1591 +#pragma warning restore 1591 diff --git a/src/Humanizer/Configuration/Configurator.cs b/src/Humanizer/Configuration/Configurator.cs index 4d7bfc00e..b30cd8378 100644 --- a/src/Humanizer/Configuration/Configurator.cs +++ b/src/Humanizer/Configuration/Configurator.cs @@ -20,7 +20,8 @@ public static class Configurator { "he", () => new HebrewFormatter() }, { "sk", () => new CzechSlovakPolishFormatter() }, { "cs", () => new CzechSlovakPolishFormatter() }, - { "pl", () => new CzechSlovakPolishFormatter() } + { "pl", () => new CzechSlovakPolishFormatter() }, + { "sr", () => new SerbianFormatter() } }; private static IDateTimeHumanizeStrategy _dateTimeHumanizeStrategy = new DefaultDateTimeHumanizeStrategy(); @@ -40,6 +41,9 @@ public static IFormatter Formatter } } + /// + /// The strategy to be used for DateTime.Humanize + /// public static IDateTimeHumanizeStrategy DateTimeHumanizeStrategy { get { return _dateTimeHumanizeStrategy; } diff --git a/src/Humanizer/DateTimeHumanizeStrategy/DefaultDateTimeHumanizeStrategy.cs b/src/Humanizer/DateTimeHumanizeStrategy/DefaultDateTimeHumanizeStrategy.cs index 751a4492d..3198cf9c1 100644 --- a/src/Humanizer/DateTimeHumanizeStrategy/DefaultDateTimeHumanizeStrategy.cs +++ b/src/Humanizer/DateTimeHumanizeStrategy/DefaultDateTimeHumanizeStrategy.cs @@ -5,7 +5,7 @@ namespace Humanizer.DateTimeHumanizeStrategy { /// - /// The default distance of time in works calculator + /// The default 'distance of time' -> words calculator. /// public class DefaultDateTimeHumanizeStrategy : IDateTimeHumanizeStrategy { diff --git a/src/Humanizer/DateTimeHumanizeStrategy/IDateTimeHumanizeStrategy.cs b/src/Humanizer/DateTimeHumanizeStrategy/IDateTimeHumanizeStrategy.cs index 9bff4fe4a..5aedd233b 100644 --- a/src/Humanizer/DateTimeHumanizeStrategy/IDateTimeHumanizeStrategy.cs +++ b/src/Humanizer/DateTimeHumanizeStrategy/IDateTimeHumanizeStrategy.cs @@ -2,8 +2,14 @@ namespace Humanizer.DateTimeHumanizeStrategy { + /// + /// Implement this interface to create a new strategy for DateTime.Humanize and hook it in the Configurator.DateTimeHumanizeStrategy + /// public interface IDateTimeHumanizeStrategy { + /// + /// Calculates the distance of time in words between two provided dates used for DateTime.Humanize + /// string Humanize(DateTime input, DateTime comparisonBase); } } \ No newline at end of file diff --git a/src/Humanizer/EnumDehumanizeExtensions.cs b/src/Humanizer/EnumDehumanizeExtensions.cs index 264e5da5b..c6f1a86b2 100644 --- a/src/Humanizer/EnumDehumanizeExtensions.cs +++ b/src/Humanizer/EnumDehumanizeExtensions.cs @@ -3,6 +3,9 @@ namespace Humanizer { + /// + /// Contains extension methods for dehumanizing Enum string values. + /// public static class EnumDehumanizeExtensions { /// diff --git a/src/Humanizer/EnumHumanizeExtensions.cs b/src/Humanizer/EnumHumanizeExtensions.cs index 4fd2327ce..b4891c463 100644 --- a/src/Humanizer/EnumHumanizeExtensions.cs +++ b/src/Humanizer/EnumHumanizeExtensions.cs @@ -4,6 +4,9 @@ namespace Humanizer { + /// + /// Contains extension methods for humanizing Enums + /// public static class EnumHumanizeExtensions { private static readonly Func DescriptionProperty = p => p.Name == "Description" && p.PropertyType == typeof (string); diff --git a/src/Humanizer/Humanizer.csproj b/src/Humanizer/Humanizer.csproj index 48a21ac9b..278bb1252 100644 --- a/src/Humanizer/Humanizer.csproj +++ b/src/Humanizer/Humanizer.csproj @@ -27,6 +27,7 @@ prompt 4 bin\Debug\Humanizer.xml + false 1591 @@ -37,25 +38,7 @@ prompt 4 bin\Release\Humanizer.xml - 1591 - - - bin\Release\ - TRACE - true - pdbonly - AnyCPU - bin\Release\Humanizer.dll.CodeAnalysisLog.xml - true - GlobalSuppressions.cs - prompt - MinimumRecommendedRules.ruleset - ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets - true - ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules - true - 1591 - bin\Release\Humanizer.XML + true true @@ -63,7 +46,20 @@ Humanizer.snk + + bin\Verify\ + false + bin\Verify\Humanizer.XML + true + ..\Humanizer.ruleset + true + + + pdbonly + true + + @@ -167,6 +163,7 @@ + @@ -190,6 +187,8 @@ + + diff --git a/src/Humanizer/Localisation/Formatters/DefaultFormatter.cs b/src/Humanizer/Localisation/Formatters/DefaultFormatter.cs index 727c157d4..37eca6839 100644 --- a/src/Humanizer/Localisation/Formatters/DefaultFormatter.cs +++ b/src/Humanizer/Localisation/Formatters/DefaultFormatter.cs @@ -6,28 +6,41 @@ public class DefaultFormatter : IFormatter { /// - /// Now! + /// Now /// - /// Time expressed in words + /// Returns Now public virtual string DateHumanize_Now() { return GetResourceForDate(TimeUnit.Millisecond, Tense.Past, 0); } + /// + /// Returns the string representation of the provided DateTime + /// + /// + /// + /// + /// public virtual string DateHumanize(TimeUnit timeUnit, Tense timeUnitTense, int unit) { return GetResourceForDate(timeUnit, timeUnitTense, unit); } /// - /// In NO time! + /// 0 seconds /// - /// Time expressed in words + /// Returns 0 seconds as the string representation of Zero TimeSpan public virtual string TimeSpanHumanize_Zero() { return GetResourceForTimeSpan(TimeUnit.Millisecond, 0); } + /// + /// Returns the string representation of the provided TimeSpan + /// + /// + /// + /// public virtual string TimeSpanHumanize(TimeUnit timeUnit, int unit) { return GetResourceForTimeSpan(timeUnit, unit); @@ -45,21 +58,43 @@ private string GetResourceForTimeSpan(TimeUnit unit, int count) return count == 1 ? Format(resourceKey) : Format(resourceKey, count); } + /// + /// + /// + /// + /// protected virtual string Format(string resourceKey) { return Resources.GetResource(GetResourceKey(resourceKey)); } + /// + /// + /// + /// + /// + /// protected virtual string Format(string resourceKey, int number) { return Resources.GetResource(GetResourceKey(resourceKey, number)).FormatWith(number); } + /// + /// Override this method if your locale has complex rules around multiple units; e.g. Arabic, Russian + /// + /// The resource key that's being in formatting + /// The number of the units being used in formatting + /// protected virtual string GetResourceKey(string resourceKey, int number) { return resourceKey; } + /// + /// + /// + /// + /// protected virtual string GetResourceKey(string resourceKey) { return resourceKey; diff --git a/src/Humanizer/Localisation/Formatters/IFormatter.cs b/src/Humanizer/Localisation/Formatters/IFormatter.cs index b591d6cfa..887209658 100644 --- a/src/Humanizer/Localisation/Formatters/IFormatter.cs +++ b/src/Humanizer/Localisation/Formatters/IFormatter.cs @@ -7,10 +7,33 @@ /// public interface IFormatter { + /// + /// Now + /// + /// Returns Now string DateHumanize_Now(); + + /// + /// Returns the string representation of the provided DateTime + /// + /// + /// + /// + /// string DateHumanize(TimeUnit timeUnit, Tense timeUnitTense, int unit); - + + /// + /// 0 seconds + /// + /// Returns 0 seconds as the string representation of Zero TimeSpan string TimeSpanHumanize_Zero(); + + /// + /// Returns the string representation of the provided TimeSpan + /// + /// + /// + /// string TimeSpanHumanize(TimeUnit timeUnit, int unit); } } diff --git a/src/Humanizer/Localisation/Formatters/SerbianFormatter.cs b/src/Humanizer/Localisation/Formatters/SerbianFormatter.cs new file mode 100644 index 000000000..8f8ea913e --- /dev/null +++ b/src/Humanizer/Localisation/Formatters/SerbianFormatter.cs @@ -0,0 +1,17 @@ +namespace Humanizer.Localisation.Formatters +{ + internal class SerbianFormatter : DefaultFormatter + { + private const string PaucalPostfix = "_Paucal"; + + protected override string GetResourceKey(string resourceKey, int number) + { + int mod10 = number % 10; + + if (mod10 > 1 && mod10 < 5) + return resourceKey + PaucalPostfix; + + return resourceKey; + } + } +} diff --git a/src/Humanizer/Localisation/NumberToWords/ArabicNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/ArabicNumberToWordsConverter.cs index a4104c69b..e47f7c199 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 { @@ -94,22 +96,107 @@ public override string Convert(int number) if (groupNumber >= 3 && groupNumber <= 10) result = String.Format("{0} {1}", PluralGroups[groupLevel], result); else - { result = String.Format("{0} {1}", result != String.Empty ? AppendedGroups[groupLevel] : Groups[groupLevel], result); - } } else - { result = String.Format("{0} {1}", Groups[groupLevel], result); - } } } + result = String.Format("{0} {1}", process, result); } + groupLevel++; } 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/Localisation/NumberToWords/RussianNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/RussianNumberToWordsConverter.cs index 0139061e5..f5bbef698 100644 --- a/src/Humanizer/Localisation/NumberToWords/RussianNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/RussianNumberToWordsConverter.cs @@ -66,21 +66,22 @@ public override string ConvertToOrdinal(int number, GrammaticalGender gender) if (number >= 100) { - var i = number; + var ending = GetEndingForGender(gender, number); var hundreds = number/100; number %= 100; if (number == 0) - parts.Add(GetPrefix(hundreds) + "сот" + GetEndingForGender(gender, i)); + parts.Add(UnitsOrdinalPrefixes[hundreds] + "сот" + ending); else parts.Add(HundredsMap[hundreds]); } + if (number >= 20) { - var i = number; - var tens = i/10; + var ending = GetEndingForGender(gender, number); + var tens = number/10; number %= 10; if (number == 0) - parts.Add(TensOrdinal[tens] + GetEndingForGender(gender, i)); + parts.Add(TensOrdinal[tens] + ending); else parts.Add(TensMap[tens]); } @@ -95,14 +96,14 @@ private static void CollectPartsUnderOneThousand(ICollection parts, int { if (number >= 100) { - var hundreds = number / 100; + var hundreds = number/100; number %= 100; parts.Add(HundredsMap[hundreds]); } if (number >= 20) { - int tens = number/10; + var tens = number/10; parts.Add(TensMap[tens]); number %= 10; } @@ -120,24 +121,35 @@ private static void CollectPartsUnderOneThousand(ICollection parts, int } } - private static string GetPrefix(int number) + private static string GetPrefix(int number, GrammaticalGender gender) { var parts = new List(); + if (number >= 100) { var hundreds = number/100; number %= 100; - parts.Add(HundredsMap[hundreds]); + if (hundreds != 1) + parts.Add(UnitsOrdinalPrefixes[hundreds] + "сот"); + else + parts.Add("сто"); } + if (number >= 20) { var tens = number/10; number %= 10; parts.Add(TensOrdinalPrefixes[tens]); } + if (number > 0) - parts.Add(UnitsOrdinalPrefixes[number]); - + { + if (number == 1) + parts.Add(gender == GrammaticalGender.Feminine ? "одна" : "одно"); + else + parts.Add(UnitsOrdinalPrefixes[number]); + } + return string.Join("", parts); } @@ -157,7 +169,12 @@ private static void CollectOrdinalParts(ICollection parts, ref int numbe var result = number/divisor; number %= divisor; if (number == 0) - parts.Add(GetPrefix(result) + prefixedForm); + { + if (result == 1) + parts.Add(prefixedForm); + else + parts.Add(GetPrefix(result, gender) + prefixedForm); + } else { CollectPartsUnderOneThousand(parts, result, gender); diff --git a/src/Humanizer/Localisation/NumberToWords/SpanishNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/SpanishNumberToWordsConverter.cs index b3880e6bb..a3afe02ab 100644 --- a/src/Humanizer/Localisation/NumberToWords/SpanishNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/SpanishNumberToWordsConverter.cs @@ -5,9 +5,11 @@ namespace Humanizer.Localisation.NumberToWords { internal class SpanishNumberToWordsConverter : DefaultNumberToWordsConverter { - private static readonly string[] HundredsMap = { "cero", "ciento", "doscientos", "trescientos", "cuatrocientos", "quinientos", "seiscientos", "setecientos", "ochocientos", "novecientos" }; - private static readonly string[] UnitsMap = { "cero", "uno", "dos", "tres", "cuatro", "cinco", "seis", "siete", "ocho", "nueve", "diez", "once", "doce", "trece", "catorce", "quince", "dieciséis", "diecisiete", "dieciocho", "diecinueve" }; + private static readonly string[] UnitsMap = { "cero", "uno", "dos", "tres", "cuatro", "cinco", "seis", "siete", "ocho", "nueve", "diez", "once", "doce", + "trece", "catorce", "quince", "dieciséis", "diecisiete", "dieciocho", "diecinueve", "veinte", "veintiuno", + "veintidós", "veintitrés", "veinticuatro", "veinticinco", "veintiséis", "veintisiete", "veintiocho", "veintinueve"}; private static readonly string[] TensMap = { "cero", "diez", "veinte", "treinta", "cuarenta", "cincuenta", "sesenta", "setenta", "ochenta", "noventa" }; + private static readonly string[] HundredsMap = { "cero", "ciento", "doscientos", "trescientos", "cuatrocientos", "quinientos", "seiscientos", "setecientos", "ochocientos", "novecientos" }; private static readonly Dictionary Ordinals = new Dictionary { @@ -45,7 +47,7 @@ public override string Convert(int number) if ((number / 1000000) > 0) { parts.Add(number / 1000000 == 1 - ? string.Format("millón") + ? string.Format("un millón") : string.Format("{0} millones", Convert(number / 1000000))); number %= 1000000; @@ -68,7 +70,7 @@ public override string Convert(int number) if (number > 0) { - if (number < 20) + if (number < 30) parts.Add(UnitsMap[number]); else if (number > 20 && number < 30) { var lastPart = TensMap[number / 10]; @@ -95,10 +97,13 @@ public override string ConvertToOrdinal(int number, GrammaticalGender gender = G string towords; if (!Ordinals.TryGetValue(number, out towords)) towords = Convert(number); + if (gender == GrammaticalGender.Feminine) towords = towords.TrimEnd('o') + "a"; + else if(number % 10 == 1 || number % 10 == 3) + towords = towords.TrimEnd('o'); return towords; } } -} \ No newline at end of file +} diff --git a/src/Humanizer/Localisation/Ordinalizers/SpanishOrdinalizer.cs b/src/Humanizer/Localisation/Ordinalizers/SpanishOrdinalizer.cs index 6735a126e..d34a8fbe7 100644 --- a/src/Humanizer/Localisation/Ordinalizers/SpanishOrdinalizer.cs +++ b/src/Humanizer/Localisation/Ordinalizers/SpanishOrdinalizer.cs @@ -14,9 +14,9 @@ public override string Convert(int number, string numberString, GrammaticalGende return "0"; if (gender == GrammaticalGender.Feminine) - return numberString + "ª"; - - return numberString + "º"; + return numberString + ".ª"; + else + return numberString + ".º"; } } } \ No newline at end of file diff --git a/src/Humanizer/Localisation/ResourceKeys.Common.cs b/src/Humanizer/Localisation/ResourceKeys.Common.cs index 1774b1052..5f995cb6d 100644 --- a/src/Humanizer/Localisation/ResourceKeys.Common.cs +++ b/src/Humanizer/Localisation/ResourceKeys.Common.cs @@ -2,6 +2,9 @@ namespace Humanizer.Localisation { + /// + /// + /// public partial class ResourceKeys { private const string Single = "Single"; diff --git a/src/Humanizer/Localisation/ResourceKeys.DateHumanize.cs b/src/Humanizer/Localisation/ResourceKeys.DateHumanize.cs index 3c49856fd..ee91374f9 100644 --- a/src/Humanizer/Localisation/ResourceKeys.DateHumanize.cs +++ b/src/Humanizer/Localisation/ResourceKeys.DateHumanize.cs @@ -2,6 +2,9 @@ { public partial class ResourceKeys { + /// + /// Encapsulates the logic required to get the resource keys for DateTime.Humanize + /// public static class DateHumanize { /// diff --git a/src/Humanizer/Localisation/ResourceKeys.TimeSpanHumanize.cs b/src/Humanizer/Localisation/ResourceKeys.TimeSpanHumanize.cs index 85ef1d74a..321e961a3 100644 --- a/src/Humanizer/Localisation/ResourceKeys.TimeSpanHumanize.cs +++ b/src/Humanizer/Localisation/ResourceKeys.TimeSpanHumanize.cs @@ -2,6 +2,9 @@ { public partial class ResourceKeys { + /// + /// Encapsulates the logic required to get the resource keys for TimeSpan.Humanize + /// public static class TimeSpanHumanize { /// diff --git a/src/Humanizer/Localisation/TimeUnit.cs b/src/Humanizer/Localisation/TimeUnit.cs index 86c4a56b4..e8941b3c1 100644 --- a/src/Humanizer/Localisation/TimeUnit.cs +++ b/src/Humanizer/Localisation/TimeUnit.cs @@ -3,6 +3,7 @@ /// /// Units of time. /// +#pragma warning disable 1591 public enum TimeUnit { Millisecond, @@ -14,4 +15,5 @@ public enum TimeUnit Month, Year } +#pragma warning restore 1591 } \ No newline at end of file diff --git a/src/Humanizer/NoMatchFoundException.cs b/src/Humanizer/NoMatchFoundException.cs index 19dafef78..30614308a 100644 --- a/src/Humanizer/NoMatchFoundException.cs +++ b/src/Humanizer/NoMatchFoundException.cs @@ -5,6 +5,7 @@ namespace Humanizer /// /// This is thrown on String.DehumanizeTo enum when the provided string cannot be mapped to the target enum /// +#pragma warning disable 1591 public class NoMatchFoundException : Exception { public NoMatchFoundException() @@ -21,4 +22,5 @@ public NoMatchFoundException(string message, Exception inner) { } } +#pragma warning restore 1591 } \ No newline at end of file diff --git a/src/Humanizer/Properties/Resources.ar.resx b/src/Humanizer/Properties/Resources.ar.resx index d70d8b41c..f70c4b314 100644 --- a/src/Humanizer/Properties/Resources.ar.resx +++ b/src/Humanizer/Properties/Resources.ar.resx @@ -1,4 +1,4 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + 1 秒前 + + + {0} 秒前 + + + 1 分前 + + + {0} 分前 + + + 1 時間前 + + + {0} 時間前 + + + 昨日 + + + {0} 日前 + + + 先月 + + + {0} か月前 + + + 去年 + + + {0} 年前 + + + {0} 日 + + + {0} 時間 + + + {0} ミリ秒 + + + {0} 分 + + + {0} 秒 + + + 1 日 + + + 1 時間 + + + 1 ミリ秒 + + + 1 分 + + + 1 秒 + + + 0 秒 + + + {0} 週間 + + + 1 週間 + + + {0} 日後 + + + {0} 時間後 + + + {0} 分後 + + + {0} か月後 + + + {0} 秒後 + + + {0} 年後 + + + + + + 明日 + + + 1 時間後 + + + 1 分後 + + + 来月 + + + 1 秒後 + + + 来年 + + \ No newline at end of file diff --git a/src/Humanizer/Properties/Resources.sr-Latn.resx b/src/Humanizer/Properties/Resources.sr-Latn.resx new file mode 100644 index 000000000..44974d84e --- /dev/null +++ b/src/Humanizer/Properties/Resources.sr-Latn.resx @@ -0,0 +1,288 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + +pre sekund + + +pre {0} sekundi + + +pre minut + + +pre {0} minuta + + +pre sat vremena + + +pre {0} sati + + +juče + + +pre {0} dana + + +pre mesec dana + + +pre {0} meseci + + +pre godinu dana + + +pre {0} godina + + +{0} dana + + +{0} sati + + +{0} milisekundi + + +{0} minuta + + +{0} sekundi + + +1 dan + + +1 sat + + +1 milisekunda + + +1 minut + + +1 sekunda + + +bez proteklog vremena + + +{0} nedelja + + +1 nedelja + + +za {0} dana + + +za {0} sati + + +za {0} minuta + + +za {0} meseci + + +za {0} sekundi + + +za {0} godina + + +sada + + +sutra + + +za sat vremena + + +za minut + + +za mesec dana + + +za sekund + + +za godinu dana + + +pre {0} dana + + +za {0} dana + + +pre {0} sata + + +za {0} sata + + +pre {0} minuta + + +za {0} minuta + + +pre {0} meseca + + +za {0} meseca + + +pre {0} sekunde + + +za {0} sekunde + + +pre {0} godine + + +za {0} godine + + +{0} dana + + +{0} sata + + +{0} milisekunde + + +{0} minuta + + +{0} sekunde + + +{0} nedelje + + \ No newline at end of file diff --git a/src/Humanizer/Properties/Resources.sr.resx b/src/Humanizer/Properties/Resources.sr.resx new file mode 100644 index 000000000..bd9d54e72 --- /dev/null +++ b/src/Humanizer/Properties/Resources.sr.resx @@ -0,0 +1,288 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + пре секунд + + + пре {0} секунди + + + пре минут + + + пре {0} минута + + + пре сат времена + + + пре {0} сати + + + јуче + + + пре {0} дана + + + пре месец дана + + + пре {0} месеци + + + пре годину дана + + + пре {0} година + + + {0} дана + + + {0} сати + + + {0} милисекунди + + + {0} минута + + + {0} секунди + + + 1 дан + + + 1 сат + + + 1 милисекунда + + + 1 минут + + + 1 секунда + + + без протеклог времена + + + {0} недеља + + + 1 недеља + + + за {0} дана + + + за {0} сати + + + за {0} минута + + + за {0} месеци + + + за {0} секунди + + + за {0} година + + + сада + + + сутра + + + за сат времена + + + за минут + + + за месец дана + + + за секунд + + + за годину дана + + + пре {0} дана + + + за {0} дана + + + пре {0} сата + + + за {0} сата + + + пре {0} минута + + + за {0} минута + + + пре {0} месеца + + + за {0} месеца + + + пре {0} секунде + + + за {0} секунде + + + пре {0} године + + + за {0} године + + + {0} дана + + + {0} сата + + + {0} милисекунде + + + {0} минута + + + {0} секунде + + + {0} недеље + + \ No newline at end of file diff --git a/src/Humanizer/RomanNumeralExtensions.cs b/src/Humanizer/RomanNumeralExtensions.cs index a37c85a45..d6e72b772 100644 --- a/src/Humanizer/RomanNumeralExtensions.cs +++ b/src/Humanizer/RomanNumeralExtensions.cs @@ -6,6 +6,9 @@ namespace Humanizer { + /// + /// Contains extension methods for changing a number to Roman representation (ToRoman) and from Roman representation back to the number (FromRoman) + /// public static class RomanNumeralExtensions { private const int NumberOfRomanNumeralMaps = 13; diff --git a/src/Humanizer/StringDehumanizeExtensions.cs b/src/Humanizer/StringDehumanizeExtensions.cs index 79b7b177f..64c485d6d 100644 --- a/src/Humanizer/StringDehumanizeExtensions.cs +++ b/src/Humanizer/StringDehumanizeExtensions.cs @@ -2,6 +2,9 @@ namespace Humanizer { + /// + /// Contains extension methods for dehumanizing strings. + /// public static class StringDehumanizeExtensions { /// diff --git a/src/Humanizer/StringHumanizeExtensions.cs b/src/Humanizer/StringHumanizeExtensions.cs index 17acd4d60..ed597604c 100644 --- a/src/Humanizer/StringHumanizeExtensions.cs +++ b/src/Humanizer/StringHumanizeExtensions.cs @@ -4,6 +4,9 @@ namespace Humanizer { + /// + /// Contains extension methods for humanizing string values. + /// public static class StringHumanizeExtensions { static string FromUnderscoreDashSeparatedWords (string input) diff --git a/src/Humanizer/Transformer/To.cs b/src/Humanizer/Transformer/To.cs index fb0cef5cd..ad24f88f6 100644 --- a/src/Humanizer/Transformer/To.cs +++ b/src/Humanizer/Transformer/To.cs @@ -18,6 +18,12 @@ public static string Transform(this string input, params IStringTransformer[] tr return transformers.Aggregate(input, (current, stringTransformer) => stringTransformer.Transform(current)); } + /// + /// Changes string to title case + /// + /// + /// "INvalid caSEs arE corrected" -> "Invalid Cases Are Corrected" + /// public static IStringTransformer TitleCase { get @@ -26,6 +32,12 @@ public static IStringTransformer TitleCase } } + /// + /// Changes the string to lower case + /// + /// + /// "Sentence casing" -> "sentence casing" + /// public static IStringTransformer LowerCase { get @@ -34,6 +46,12 @@ public static IStringTransformer LowerCase } } + /// + /// Changes the string to upper case + /// + /// + /// "lower case statement" -> "LOWER CASE STATEMENT" + /// public static IStringTransformer UpperCase { get @@ -42,6 +60,12 @@ public static IStringTransformer UpperCase } } + /// + /// Changes the string to sentence case + /// + /// + /// "lower case statement" -> "Lower case statement" + /// public static IStringTransformer SentenceCase { get