diff --git a/NuSpecs/Humanizer.Core.ca.nuspec b/NuSpecs/Humanizer.Core.ca.nuspec
new file mode 100644
index 000000000..3c74c9757
--- /dev/null
+++ b/NuSpecs/Humanizer.Core.ca.nuspec
@@ -0,0 +1,27 @@
+
+
+
+ Humanizer.Core.ca
+ $version$
+ Humanizer Locale (ca)
+ Mehdi Khalili, Claire Novotny
+ https://github.com/Humanizr/Humanizer
+ logo.png
+ false
+ Humanizer Locale Catalan (ca)
+ Copyright (c) .NET Foundation and Contributors
+ MIT
+
+ ca
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.nuspec b/NuSpecs/Humanizer.nuspec
index 3e3061840..08bc9e461 100644
--- a/NuSpecs/Humanizer.nuspec
+++ b/NuSpecs/Humanizer.nuspec
@@ -18,6 +18,7 @@
+
@@ -38,8 +39,8 @@
-
+
diff --git a/src/Humanizer.Tests/Localisation/ca/DateHumanizeTests.cs b/src/Humanizer.Tests/Localisation/ca/DateHumanizeTests.cs
new file mode 100644
index 000000000..70cf2b404
--- /dev/null
+++ b/src/Humanizer.Tests/Localisation/ca/DateHumanizeTests.cs
@@ -0,0 +1,78 @@
+namespace ca;
+
+[UseCulture("ca")]
+public class DateHumanizeTests
+{
+ [Theory]
+ [InlineData(1, "fa un segon")]
+ [InlineData(2, "fa 2 segons")]
+ public void SecondsAgo(int seconds, string expected) =>
+ DateHumanize.Verify(expected, seconds, TimeUnit.Second, Tense.Past);
+
+ [Theory]
+ [InlineData(1, "d'aquí un segon")]
+ [InlineData(2, "d'aquí 2 segons")]
+ public void SecondsFromNow(int seconds, string expected) =>
+ DateHumanize.Verify(expected, seconds, TimeUnit.Second, Tense.Future);
+
+ [Theory]
+ [InlineData(1, "fa un minut")]
+ [InlineData(2, "fa 2 minuts")]
+ [InlineData(60, "fa una hora")]
+ public void MinutesAgo(int minutes, string expected) =>
+ DateHumanize.Verify(expected, minutes, TimeUnit.Minute, Tense.Past);
+
+ [Theory]
+ [InlineData(1, "d'aquí un minut")]
+ [InlineData(2, "d'aquí 2 minuts")]
+ public void MinutesFromNow(int minutes, string expected) =>
+ DateHumanize.Verify(expected, minutes, TimeUnit.Minute, Tense.Future);
+
+ [Theory]
+ [InlineData(1, "fa una hora")]
+ [InlineData(2, "fa 2 hores")]
+ public void HoursAgo(int hours, string expected) =>
+ DateHumanize.Verify(expected, hours, TimeUnit.Hour, Tense.Past);
+
+ [Theory]
+ [InlineData(1, "d'aquí una hora")]
+ [InlineData(2, "d'aquí 2 hores")]
+ public void HoursFromNow(int hours, string expected) =>
+ DateHumanize.Verify(expected, hours, TimeUnit.Hour, Tense.Future);
+
+ [Theory]
+ [InlineData(1, "ahir")]
+ [InlineData(2, "fa 2 dies")]
+ public void DaysAgo(int days, string expected) =>
+ DateHumanize.Verify(expected, days, TimeUnit.Day, Tense.Past);
+
+ [Theory]
+ [InlineData(1, "demà")]
+ [InlineData(2, "d'aquí 2 dies")]
+ public void DaysFromNow(int days, string expected) =>
+ DateHumanize.Verify(expected, days, TimeUnit.Day, Tense.Future);
+
+ [Theory]
+ [InlineData(1, "fa un mes")]
+ [InlineData(2, "fa 2 mesos")]
+ public void MonthsAgo(int months, string expected) =>
+ DateHumanize.Verify(expected, months, TimeUnit.Month, Tense.Past);
+
+ [Theory]
+ [InlineData(1, "d'aquí un mes")]
+ [InlineData(2, "d'aquí 2 mesos")]
+ public void MonthsFromNow(int months, string expected) =>
+ DateHumanize.Verify(expected, months, TimeUnit.Month, Tense.Future);
+
+ [Theory]
+ [InlineData(1, "fa un any")]
+ [InlineData(2, "fa 2 anys")]
+ public void YearsAgo(int years, string expected) =>
+ DateHumanize.Verify(expected, years, TimeUnit.Year, Tense.Past);
+
+ [Theory]
+ [InlineData(1, "d'aquí un any")]
+ [InlineData(2, "d'aquí 2 anys")]
+ public void YearsFromNow(int years, string expected) =>
+ DateHumanize.Verify(expected, years, TimeUnit.Year, Tense.Future);
+}
\ No newline at end of file
diff --git a/src/Humanizer.Tests/Localisation/ca/DateToOrdinalWordsTests.cs b/src/Humanizer.Tests/Localisation/ca/DateToOrdinalWordsTests.cs
new file mode 100644
index 000000000..bdd16c95f
--- /dev/null
+++ b/src/Humanizer.Tests/Localisation/ca/DateToOrdinalWordsTests.cs
@@ -0,0 +1,25 @@
+namespace ca;
+
+[UseCulture("ca")]
+public class DateToOrdinalWordsTests
+{
+ [Fact]
+ public void OrdinalizeString()
+ {
+ Assert.Equal("25 de gener de 2022", new DateTime(2022, 1, 25).ToOrdinalWords());
+ Assert.Equal("29 de febrer de 2020", new DateTime(2020, 2, 29).ToOrdinalWords());
+ Assert.Equal("4 de setembre de 2015", new DateTime(2015, 9, 4).ToOrdinalWords());
+ Assert.Equal("7 de novembre de 1979", new DateTime(1979, 11, 7).ToOrdinalWords());
+ }
+
+#if NET6_0_OR_GREATER
+ [Fact]
+ public void OrdinalizeDateOnlyString()
+ {
+ Assert.Equal("25 de gener de 2022", new DateOnly(2022, 1, 25).ToOrdinalWords());
+ Assert.Equal("29 de febrer de 2020", new DateOnly(2020, 2, 29).ToOrdinalWords());
+ Assert.Equal("4 de setembre de 2015", new DateOnly(2015, 9, 4).ToOrdinalWords());
+ Assert.Equal("7 de novembre de 1979", new DateOnly(1979, 11, 7).ToOrdinalWords());
+ }
+#endif
+}
\ No newline at end of file
diff --git a/src/Humanizer.Tests/Localisation/ca/NumberToWordsFeminineTest.cs b/src/Humanizer.Tests/Localisation/ca/NumberToWordsFeminineTest.cs
new file mode 100644
index 000000000..ede159289
--- /dev/null
+++ b/src/Humanizer.Tests/Localisation/ca/NumberToWordsFeminineTest.cs
@@ -0,0 +1,19 @@
+namespace ca;
+
+[UseCulture("ca")]
+public class NumberToWordsFeminineTests
+{
+ [Theory]
+ [InlineData(1, "una")]
+ [InlineData(21, "vint-i-una")]
+ [InlineData(31, "trenta-una")]
+ [InlineData(81, "vuitanta-una")]
+ [InlineData(500, "cinc-centes")]
+ [InlineData(701, "set-centes una")]
+ [InlineData(3500, "tres mil cinc-centes")]
+ [InlineData(200121, "dues-centes mil cent vint-i-una")]
+ [InlineData(200000121, "dos-cents milions cent vint-i-una")]
+ [InlineData(1000001, "un milió una")]
+ public void ToWords(int number, string expected) =>
+ Assert.Equal(expected, number.ToWords(GrammaticalGender.Feminine));
+}
\ No newline at end of file
diff --git a/src/Humanizer.Tests/Localisation/ca/NumberToWordsTests.cs b/src/Humanizer.Tests/Localisation/ca/NumberToWordsTests.cs
new file mode 100644
index 000000000..8a76d43fa
--- /dev/null
+++ b/src/Humanizer.Tests/Localisation/ca/NumberToWordsTests.cs
@@ -0,0 +1,207 @@
+namespace ca;
+
+[UseCulture("ca")]
+
+public class NumberToWordsTests
+{
+ [Theory]
+ [InlineData(-1, "menys primer", GrammaticalGender.Neuter)]
+ [InlineData(0, "zero", GrammaticalGender.Neuter)]
+ [InlineData(1, "primer", GrammaticalGender.Neuter)]
+ [InlineData(1, "primer", GrammaticalGender.Masculine)]
+ [InlineData(1, "primera", GrammaticalGender.Feminine)]
+ [InlineData(2, "segon", GrammaticalGender.Masculine)]
+ [InlineData(2, "segona", GrammaticalGender.Feminine)]
+ [InlineData(3, "tercer", GrammaticalGender.Neuter)]
+ [InlineData(3, "tercer", GrammaticalGender.Masculine)]
+ [InlineData(3, "tercera", GrammaticalGender.Feminine)]
+ [InlineData(4, "quart", GrammaticalGender.Masculine)]
+ [InlineData(4, "quarta", GrammaticalGender.Feminine)]
+ [InlineData(5, "cinquè", GrammaticalGender.Masculine)]
+ [InlineData(5, "cinquena", GrammaticalGender.Feminine)]
+ [InlineData(6, "sisè", GrammaticalGender.Masculine)]
+ [InlineData(6, "sisena", GrammaticalGender.Feminine)]
+ [InlineData(7, "setè", GrammaticalGender.Masculine)]
+ [InlineData(7, "setena", GrammaticalGender.Feminine)]
+ [InlineData(8, "vuitè", GrammaticalGender.Masculine)]
+ [InlineData(8, "vuitena", GrammaticalGender.Feminine)]
+ [InlineData(9, "novè", GrammaticalGender.Masculine)]
+ [InlineData(9, "novena", GrammaticalGender.Feminine)]
+ [InlineData(10, "desè", GrammaticalGender.Masculine)]
+ [InlineData(10, "desena", GrammaticalGender.Feminine)]
+ [InlineData(11, "onzè", GrammaticalGender.Masculine)]
+ [InlineData(11, "onzena", GrammaticalGender.Feminine)]
+ [InlineData(20, "vintè", GrammaticalGender.Masculine)]
+ [InlineData(20, "vintena", GrammaticalGender.Feminine)]
+ [InlineData(22, "vint-i-dosè", GrammaticalGender.Masculine)]
+ [InlineData(22, "vint-i-dosena", GrammaticalGender.Feminine)]
+ [InlineData(30, "trentè", GrammaticalGender.Masculine)]
+ [InlineData(30, "trentena", GrammaticalGender.Feminine)]
+ [InlineData(34, "trenta-quatrè", GrammaticalGender.Masculine)]
+ [InlineData(34, "trenta-quatrena", GrammaticalGender.Feminine)]
+ [InlineData(40, "quarantaè", GrammaticalGender.Masculine)]
+ [InlineData(40, "quarantaena", GrammaticalGender.Feminine)]
+ [InlineData(46, "quaranta-sisè", GrammaticalGender.Masculine)]
+ [InlineData(46, "quaranta-sisena", GrammaticalGender.Feminine)]
+ [InlineData(50, "cinquantaè", GrammaticalGender.Masculine)]
+ [InlineData(50, "cinquantaena", GrammaticalGender.Feminine)]
+ [InlineData(57, "cinquanta-setè", GrammaticalGender.Masculine)]
+ [InlineData(57, "cinquanta-setena", GrammaticalGender.Feminine)]
+ [InlineData(60, "seixantè", GrammaticalGender.Masculine)]
+ [InlineData(60, "seixantena", GrammaticalGender.Feminine)]
+ [InlineData(69, "seixanta-novè", GrammaticalGender.Masculine)]
+ [InlineData(69, "seixanta-novena", GrammaticalGender.Feminine)]
+ [InlineData(70, "setantè", GrammaticalGender.Masculine)]
+ [InlineData(70, "setantena", GrammaticalGender.Feminine)]
+ [InlineData(74, "setanta-quatrè", GrammaticalGender.Masculine)]
+ [InlineData(74, "setanta-quatrena", GrammaticalGender.Feminine)]
+ [InlineData(80, "vuitantè", GrammaticalGender.Masculine)]
+ [InlineData(80, "vuitantena", GrammaticalGender.Feminine)]
+ [InlineData(85, "vuitanta-cinquè", GrammaticalGender.Masculine)]
+ [InlineData(85, "vuitanta-cinquena", GrammaticalGender.Feminine)]
+ [InlineData(90, "norantè", GrammaticalGender.Masculine)]
+ [InlineData(90, "norantena", GrammaticalGender.Feminine)]
+ [InlineData(99, "noranta-novè", GrammaticalGender.Masculine)]
+ [InlineData(99, "noranta-novena", GrammaticalGender.Feminine)]
+ [InlineData(100, "centè", GrammaticalGender.Masculine)]
+ [InlineData(100, "centena", GrammaticalGender.Feminine)]
+ [InlineData(101, "cent primer", GrammaticalGender.Masculine)]
+ [InlineData(101, "cent primera", GrammaticalGender.Feminine)]
+ [InlineData(110, "cent deu", GrammaticalGender.Masculine)]
+ [InlineData(111, "cent onzè", GrammaticalGender.Masculine)]
+ [InlineData(120, "cent vint", GrammaticalGender.Masculine)]
+ [InlineData(121, "cent vint-i-un", GrammaticalGender.Masculine)]
+ [InlineData(121, "cent vint-i-una", GrammaticalGender.Feminine)]
+ [InlineData(200, "dos-cents", GrammaticalGender.Masculine)]
+ [InlineData(200, "dues-centes", GrammaticalGender.Feminine)]
+ [InlineData(201, "dos-cents un", GrammaticalGender.Masculine)]
+ [InlineData(201, "dues-centes una", GrammaticalGender.Feminine)]
+ [InlineData(221, "dos-cents vint-i-un", GrammaticalGender.Masculine)]
+ [InlineData(221, "dues-centes vint-i-una", GrammaticalGender.Feminine)]
+ [InlineData(500, "cinc-cents", GrammaticalGender.Masculine)]
+ [InlineData(500, "cinc-centes", GrammaticalGender.Feminine)]
+ [InlineData(701, "set-cents un", GrammaticalGender.Masculine)]
+ [InlineData(701, "set-centes una", GrammaticalGender.Feminine)]
+ [InlineData(900, "nou-cents", GrammaticalGender.Masculine)]
+ [InlineData(900, "nou-centes", GrammaticalGender.Feminine)]
+ [InlineData(999, "nou-cents noranta-nou", GrammaticalGender.Masculine)]
+ [InlineData(999, "nou-centes noranta-nou", GrammaticalGender.Feminine)]
+ [InlineData(1000, "mil", GrammaticalGender.Masculine)]
+ [InlineData(1001, "mil un", GrammaticalGender.Masculine)]
+ [InlineData(1001, "mil una", GrammaticalGender.Feminine)]
+ [InlineData(1021, "mil vint-i-un", GrammaticalGender.Masculine)]
+ [InlineData(1021, "mil vint-i-una", GrammaticalGender.Feminine)]
+ [InlineData(1100, "mil cent", GrammaticalGender.Masculine)]
+ [InlineData(1100, "mil cent", GrammaticalGender.Feminine)]
+ [InlineData(2000, "dos mil", GrammaticalGender.Masculine)]
+ [InlineData(2100, "dos mil cent", GrammaticalGender.Masculine)]
+ [InlineData(3500, "tres mil cinc-cents", GrammaticalGender.Masculine)]
+ [InlineData(3500, "tres mil cinc-centes", GrammaticalGender.Feminine)]
+ [InlineData(100000, "cent mil", GrammaticalGender.Masculine)]
+ [InlineData(100001, "cent mil un", GrammaticalGender.Masculine)]
+ [InlineData(100001, "cent mil una", GrammaticalGender.Feminine)]
+ [InlineData(100002, "cent mil dos", GrammaticalGender.Masculine)]
+ [InlineData(100002, "cent mil dues", GrammaticalGender.Feminine)]
+ [InlineData(100021, "cent mil vint-i-un", GrammaticalGender.Masculine)]
+ [InlineData(100021, "cent mil vint-i-una", GrammaticalGender.Feminine)]
+ [InlineData(200000, "dos-cents mil", GrammaticalGender.Masculine)]
+ [InlineData(200000, "dues-centes mil", GrammaticalGender.Feminine)]
+ [InlineData(200121, "dos-cents mil cent vint-i-un", GrammaticalGender.Masculine)]
+ [InlineData(200121, "dues-centes mil cent vint-i-una", GrammaticalGender.Feminine)]
+ [InlineData(1000000, "un milió", GrammaticalGender.Masculine)]
+ [InlineData(1000001, "un milió un", GrammaticalGender.Masculine)]
+ [InlineData(1000001, "un milió una", GrammaticalGender.Feminine)]
+ [InlineData(2000000, "dos milions", GrammaticalGender.Masculine)]
+ [InlineData(2000001, "dos milions un", GrammaticalGender.Masculine)]
+ [InlineData(2000001, "dos milions una", GrammaticalGender.Feminine)]
+ [InlineData(200000121, "dos-cents milions cent vint-i-un", GrammaticalGender.Masculine)]
+ [InlineData(200000121, "dos-cents milions cent vint-i-una", GrammaticalGender.Feminine)]
+ public void ToOrdinalWords(int number, string words, GrammaticalGender gender) =>
+ Assert.Equal(words, number.ToOrdinalWords(gender));
+
+ [Theory]
+ [InlineData(1, WordForm.Abbreviation, GrammaticalGender.Masculine, "1r")]
+ [InlineData(1, WordForm.Abbreviation, GrammaticalGender.Feminine, "1a")]
+ [InlineData(2, WordForm.Abbreviation, GrammaticalGender.Masculine, "2n")]
+ [InlineData(2, WordForm.Abbreviation, GrammaticalGender.Feminine, "2a")]
+ [InlineData(3, WordForm.Abbreviation, GrammaticalGender.Masculine, "3r")]
+ [InlineData(3, WordForm.Abbreviation, GrammaticalGender.Feminine, "3a")]
+ [InlineData(11, WordForm.Abbreviation, GrammaticalGender.Masculine, "11è")]
+ [InlineData(11, WordForm.Abbreviation, GrammaticalGender.Feminine, "11a")]
+ [InlineData(22, WordForm.Abbreviation, GrammaticalGender.Masculine, "22n")]
+ [InlineData(22, WordForm.Abbreviation, GrammaticalGender.Feminine, "22a")]
+ [InlineData(31, WordForm.Abbreviation, GrammaticalGender.Masculine, "31r")]
+ [InlineData(31, WordForm.Abbreviation, GrammaticalGender.Feminine, "31a")]
+ [InlineData(100, WordForm.Abbreviation, GrammaticalGender.Masculine, "100è")]
+ [InlineData(100, WordForm.Abbreviation, GrammaticalGender.Feminine, "100a")]
+ [InlineData(101, WordForm.Abbreviation, GrammaticalGender.Masculine, "101r")]
+ [InlineData(101, WordForm.Abbreviation, GrammaticalGender.Feminine, "101a")]
+ [InlineData(999, WordForm.Abbreviation, GrammaticalGender.Masculine, "999è")]
+ [InlineData(999, WordForm.Abbreviation, GrammaticalGender.Feminine, "999a")]
+ public void ToOrdinalWordsWithWordForm(int number, WordForm wordForm, GrammaticalGender gender, string expected) =>
+ Assert.Equal(expected, number.ToOrdinalWords(gender, wordForm));
+
+ [Theory]
+ [InlineData(1, WordForm.Abbreviation, GrammaticalGender.Masculine, "1r")]
+ [InlineData(1, WordForm.Abbreviation, GrammaticalGender.Feminine, "1a")]
+ [InlineData(2, WordForm.Abbreviation, GrammaticalGender.Masculine, "2n")]
+ [InlineData(2, WordForm.Abbreviation, GrammaticalGender.Feminine, "2a")]
+ [InlineData(3, WordForm.Abbreviation, GrammaticalGender.Masculine, "3r")]
+ [InlineData(3, WordForm.Abbreviation, GrammaticalGender.Feminine, "3a")]
+ [InlineData(11, WordForm.Abbreviation, GrammaticalGender.Masculine, "11è")]
+ [InlineData(11, WordForm.Abbreviation, GrammaticalGender.Feminine, "11a")]
+ [InlineData(22, WordForm.Abbreviation, GrammaticalGender.Masculine, "22n")]
+ [InlineData(22, WordForm.Abbreviation, GrammaticalGender.Feminine, "22a")]
+ [InlineData(31, WordForm.Abbreviation, GrammaticalGender.Masculine, "31r")]
+ [InlineData(31, WordForm.Abbreviation, GrammaticalGender.Feminine, "31a")]
+ [InlineData(100, WordForm.Abbreviation, GrammaticalGender.Masculine, "100è")]
+ [InlineData(100, WordForm.Abbreviation, GrammaticalGender.Feminine, "100a")]
+ [InlineData(101, WordForm.Abbreviation, GrammaticalGender.Masculine, "101r")]
+ [InlineData(101, WordForm.Abbreviation, GrammaticalGender.Feminine, "101a")]
+ [InlineData(999, WordForm.Abbreviation, GrammaticalGender.Masculine, "999è")]
+ [InlineData(999, WordForm.Abbreviation, GrammaticalGender.Feminine, "999a")]
+ public void ToOrdinalWordsWithWordFormAndGender(int number, WordForm wordForm, GrammaticalGender gender, string expected) =>
+ Assert.Equal(expected, number.ToOrdinalWords(gender, wordForm));
+
+ [Theory]
+ [InlineData(0, "zero vegades")]
+ [InlineData(2, "doble")]
+ [InlineData(100, "cent vegades")]
+ public void ToTuple(int number, string expected)
+ => Assert.Equal(expected, number.ToTuple());
+
+ [Theory]
+ [InlineData(0, "zero", GrammaticalGender.Masculine)]
+ [InlineData(1, "un", GrammaticalGender.Masculine)]
+ [InlineData(1, "una", GrammaticalGender.Feminine)]
+ [InlineData(2, "dos", GrammaticalGender.Masculine)]
+ [InlineData(2, "dues", GrammaticalGender.Feminine)]
+ [InlineData(3, "tres", GrammaticalGender.Masculine)]
+ [InlineData(3, "tres", GrammaticalGender.Feminine)]
+ [InlineData(11, "onze", GrammaticalGender.Masculine)]
+ [InlineData(21, "vint-i-u", GrammaticalGender.Masculine)]
+ [InlineData(21, "vint-i-una", GrammaticalGender.Feminine)]
+ [InlineData(31, "trenta-u", GrammaticalGender.Masculine)]
+ [InlineData(31, "trenta-una", GrammaticalGender.Feminine)]
+ [InlineData(81, "vuitanta-u", GrammaticalGender.Masculine)]
+ [InlineData(81, "vuitanta-una", GrammaticalGender.Feminine)]
+ [InlineData(101, "cent u", GrammaticalGender.Masculine)]
+ [InlineData(101, "cent una", GrammaticalGender.Feminine)]
+ [InlineData(500, "cinc-cents", GrammaticalGender.Masculine)]
+ [InlineData(500, "cinc-centes", GrammaticalGender.Feminine)]
+ [InlineData(701, "set-cents u", GrammaticalGender.Masculine)]
+ [InlineData(701, "set-centes una", GrammaticalGender.Feminine)]
+ [InlineData(3500, "tres mil cinc-cents", GrammaticalGender.Masculine)]
+ [InlineData(3500, "tres mil cinc-centes", GrammaticalGender.Feminine)]
+ [InlineData(200121, "dos-cents mil cent vint-i-u", GrammaticalGender.Masculine)]
+ [InlineData(200121, "dues-centes mil cent vint-i-una", GrammaticalGender.Feminine)]
+ [InlineData(200000121, "dos-cents milions cent vint-i-u", GrammaticalGender.Masculine)]
+ [InlineData(200000121, "dos-cents milions cent vint-i-una", GrammaticalGender.Feminine)]
+ [InlineData(1000001, "un milió u", GrammaticalGender.Masculine)]
+ [InlineData(1000001, "un milió una", GrammaticalGender.Feminine)]
+ [InlineData(-15, "menys quinze", GrammaticalGender.Masculine)]
+ [InlineData(-123, "menys cent vint-i-tres", GrammaticalGender.Masculine)]
+ public void ToWords(long number, string expected, GrammaticalGender gender = GrammaticalGender.Masculine) =>
+ Assert.Equal(expected, number.ToWords(gender));
+
+}
\ No newline at end of file
diff --git a/src/Humanizer.Tests/Localisation/ca/OrdinalizeTests.cs b/src/Humanizer.Tests/Localisation/ca/OrdinalizeTests.cs
new file mode 100644
index 000000000..410f2e193
--- /dev/null
+++ b/src/Humanizer.Tests/Localisation/ca/OrdinalizeTests.cs
@@ -0,0 +1,114 @@
+namespace ca;
+
+[UseCulture("ca")]
+public class OrdinalizeTests
+{
+ [Theory]
+ [InlineData(1, "1r")]
+ [InlineData(3, "3r")]
+ public void OrdinalizeDefaultGender(int number, string ordinalized) =>
+ Assert.Equal(number.Ordinalize(), ordinalized);
+
+ [Theory]
+ [InlineData(-1, "1r")]
+ [InlineData(int.MinValue, "0")]
+ public void OrdinalizeZeroOrNegativeNumber(int number, string ordinalized) =>
+ Assert.Equal(number.Ordinalize(), ordinalized);
+
+ [Theory]
+ [InlineData(1, WordForm.Abbreviation, "1r")]
+ [InlineData(1, WordForm.Normal, "1r")]
+ [InlineData(2, WordForm.Abbreviation, "2n")]
+ [InlineData(2, WordForm.Normal, "2n")]
+ [InlineData(3, WordForm.Abbreviation, "3r")]
+ [InlineData(3, WordForm.Normal, "3r")]
+ [InlineData(21, WordForm.Abbreviation, "21r")]
+ [InlineData(21, WordForm.Normal, "21r")]
+ public void OrdinalizeWithWordForm(int number, WordForm wordForm, string expected)
+ {
+ Assert.Equal(expected, number.Ordinalize(wordForm));
+ Assert.Equal(expected, number.ToString(CultureInfo.CurrentUICulture).Ordinalize(wordForm));
+ }
+
+ [Theory]
+ [InlineData(1, GrammaticalGender.Masculine, WordForm.Abbreviation, "1r")]
+ [InlineData(1, GrammaticalGender.Masculine, WordForm.Normal, "1r")]
+ [InlineData(1, GrammaticalGender.Feminine, WordForm.Abbreviation, "1a")]
+ [InlineData(1, GrammaticalGender.Feminine, WordForm.Normal, "1a")]
+ [InlineData(1, GrammaticalGender.Neuter, WordForm.Abbreviation, "1r")]
+ [InlineData(1, GrammaticalGender.Neuter, WordForm.Normal, "1r")]
+ public void OrdinalizeWithWordFormAndGender(int number, GrammaticalGender gender, WordForm wordForm, string expected)
+ {
+ Assert.Equal(expected, number.Ordinalize(gender, wordForm));
+ Assert.Equal(expected, number.ToString(CultureInfo.CurrentUICulture).Ordinalize(gender, wordForm));
+ }
+
+ [Theory]
+ [InlineData("1", "1r")]
+ [InlineData("2", "2n")]
+ [InlineData("3", "3r")]
+ [InlineData("4", "4t")]
+ [InlineData("5", "5è")]
+ [InlineData("6", "6è")]
+ [InlineData("23", "23r")]
+ [InlineData("100", "100è")]
+ [InlineData("101", "101r")]
+ [InlineData("102", "102n")]
+ [InlineData("103", "103r")]
+ [InlineData("1001", "1001r")]
+ public void OrdinalizeString(string number, string ordinalized) =>
+ Assert.Equal(number.Ordinalize(GrammaticalGender.Masculine), ordinalized);
+
+ [Theory]
+ [InlineData("0", "0")]
+ [InlineData("1", "1a")]
+ [InlineData("2", "2a")]
+ [InlineData("3", "3a")]
+ [InlineData("4", "4a")]
+ [InlineData("5", "5a")]
+ [InlineData("6", "6a")]
+ [InlineData("23", "23a")]
+ [InlineData("100", "100a")]
+ [InlineData("101", "101a")]
+ [InlineData("102", "102a")]
+ [InlineData("103", "103a")]
+ [InlineData("1001", "1001a")]
+ public void OrdinalizeStringFeminine(string number, string ordinalized) =>
+ Assert.Equal(number.Ordinalize(GrammaticalGender.Feminine), ordinalized);
+
+ [Theory]
+ [InlineData(0, "0")]
+ [InlineData(1, "1r")]
+ [InlineData(2, "2n")]
+ [InlineData(3, "3r")]
+ [InlineData(4, "4t")]
+ [InlineData(5, "5è")]
+ [InlineData(6, "6è")]
+ [InlineData(10, "10è")]
+ [InlineData(23, "23r")]
+ [InlineData(100, "100è")]
+ [InlineData(101, "101r")]
+ [InlineData(102, "102n")]
+ [InlineData(103, "103r")]
+ [InlineData(1001, "1001r")]
+ public void OrdinalizeNumber(int number, string ordinalized) =>
+ Assert.Equal(number.Ordinalize(GrammaticalGender.Masculine), ordinalized);
+
+ [Theory]
+ [InlineData(0, "0")]
+ [InlineData(1, "1a")]
+ [InlineData(2, "2a")]
+ [InlineData(3, "3a")]
+ [InlineData(4, "4a")]
+ [InlineData(5, "5a")]
+ [InlineData(6, "6a")]
+ [InlineData(10, "10a")]
+ [InlineData(23, "23a")]
+ [InlineData(100, "100a")]
+ [InlineData(101, "101a")]
+ [InlineData(102, "102a")]
+ [InlineData(103, "103a")]
+ [InlineData(1001, "1001a")]
+ public void OrdinalizeNumberFeminine(int number, string ordinalized) =>
+ Assert.Equal(number.Ordinalize(GrammaticalGender.Feminine), ordinalized);
+}
\ No newline at end of file
diff --git a/src/Humanizer.Tests/Localisation/ca/TimeSpanHumanizeTests.cs b/src/Humanizer.Tests/Localisation/ca/TimeSpanHumanizeTests.cs
new file mode 100644
index 000000000..6e3317abf
--- /dev/null
+++ b/src/Humanizer.Tests/Localisation/ca/TimeSpanHumanizeTests.cs
@@ -0,0 +1,502 @@
+namespace ca;
+
+[UseCulture("ca")]
+public class TimeSpanHumanizeTests
+{
+ [Theory]
+ [Trait("Translation", "Google")]
+ [InlineData(366, "1 any")]
+ [InlineData(731, "2 anys")]
+ [InlineData(1096, "3 anys")]
+ [InlineData(4018, "11 anys")]
+ public void Years(int days, string expected) =>
+ Assert.Equal(expected, TimeSpan.FromDays(days).Humanize(maxUnit: TimeUnit.Year));
+
+ [Theory]
+ [Trait("Translation", "Google")]
+ [InlineData(31, "1 mes")]
+ [InlineData(61, "2 mesos")]
+ [InlineData(92, "3 mesos")]
+ [InlineData(335, "11 mesos")]
+ public void Months(int days, string expected) =>
+ Assert.Equal(expected, TimeSpan.FromDays(days).Humanize(maxUnit: TimeUnit.Year));
+
+ [Fact]
+ public void TwoWeeks() =>
+ Assert.Equal("2 setmanes", TimeSpan.FromDays(14).Humanize());
+
+ [Fact]
+ public void OneWeek() =>
+ Assert.Equal("1 setmana", TimeSpan.FromDays(7).Humanize());
+
+ [Fact]
+ public void SixDays() =>
+ Assert.Equal("6 dies", TimeSpan.FromDays(6).Humanize());
+
+ [Fact]
+ public void TwoDays() =>
+ Assert.Equal("2 dies", TimeSpan.FromDays(2).Humanize());
+
+ [Fact]
+ public void OneDay() =>
+ Assert.Equal("1 dia", TimeSpan.FromDays(1).Humanize());
+
+ [Fact]
+ public void TwoHours() =>
+ Assert.Equal("2 hores", TimeSpan.FromHours(2).Humanize());
+
+ [Fact]
+ public void OneHour() =>
+ Assert.Equal("1 hora", TimeSpan.FromHours(1).Humanize());
+
+ [Fact]
+ public void TwoMinutes() =>
+ Assert.Equal("2 minuts", TimeSpan.FromMinutes(2).Humanize());
+
+ [Fact]
+ public void OneMinute() =>
+ Assert.Equal("1 minut", TimeSpan.FromMinutes(1).Humanize());
+
+ [Fact]
+ public void TwoSeconds() =>
+ Assert.Equal("2 segons", TimeSpan.FromSeconds(2).Humanize());
+
+ [Fact]
+ public void OneSecond() =>
+ Assert.Equal("1 segon", TimeSpan.FromSeconds(1).Humanize());
+
+ [Fact]
+ public void TwoMilliseconds() =>
+ Assert.Equal("2 mil·lisegons", TimeSpan.FromMilliseconds(2).Humanize());
+
+ [Fact]
+ public void OneMillisecond() =>
+ Assert.Equal("1 mil·lisegon", TimeSpan.FromMilliseconds(1).Humanize());
+
+ [Theory]
+ [InlineData(0, 0, 1, 1, 2, "un minut, un segon")]
+ [InlineData(0, 0, 2, 2, 2, "dos minuts, dos segons")]
+ [InlineData(1, 2, 3, 4, 4, "un dia, dues hores, tres minuts, quatre segons")]
+ public void ComplexTimeSpan(int days, int hours, int minutes, int seconds, int precision, string expected)
+ {
+ var timeSpan = new TimeSpan(days, hours, minutes, seconds);
+ Assert.Equal(expected, timeSpan.Humanize(precision, toWords: true));
+ }
+
+ [Fact]
+ public void NoTime() =>
+ // This one doesn't make a lot of sense but ... w/e
+ Assert.Equal("0 mil·lisegons", TimeSpan.Zero.Humanize());
+
+ [Fact]
+ public void NoTimeToWords() =>
+ // This one doesn't make a lot of sense but ... w/e
+ Assert.Equal("res", TimeSpan.Zero.Humanize(toWords: true));
+
+ [Fact]
+ public void AllTimeSpansMustBeUniqueForASequenceOfDays()
+ {
+ var culture = new CultureInfo("ca");
+ var qry = from i in Enumerable.Range(0, 100000)
+ let ts = TimeSpan.FromDays(i)
+ let text = ts.Humanize(precision: 3, culture: culture, maxUnit: TimeUnit.Year)
+ select text;
+ var grouping = from t in qry
+ group t by t into g
+ select new { g.Key, Count = g.Count() };
+ var allUnique = grouping.All(g => g.Count == 1);
+ Assert.True(allUnique);
+ }
+
+ [Theory]
+ [InlineData(365, "11 mesos, 30 dies")]
+ [InlineData(365 + 1, "1 any")]
+ [InlineData(365 + 365, "1 any, 11 mesos, 29 dies")]
+ [InlineData(365 + 365 + 1, "2 anys")]
+ [InlineData(365 + 365 + 365, "2 anys, 11 mesos, 29 dies")]
+ [InlineData(365 + 365 + 365 + 1, "3 anys")]
+ [InlineData(365 + 365 + 365 + 365, "3 anys, 11 mesos, 29 dies")]
+ [InlineData(365 + 365 + 365 + 365 + 1, "4 anys")]
+ [InlineData(365 + 365 + 365 + 365 + 366, "4 anys, 11 mesos, 30 dies")]
+ [InlineData(365 + 365 + 365 + 365 + 366 + 1, "5 anys")]
+ public void Year(int days, string expected)
+ {
+ var actual = TimeSpan.FromDays(days).Humanize(precision: 7, maxUnit: TimeUnit.Year);
+ Assert.Equal(expected, actual);
+ }
+
+ [Theory]
+ [InlineData(30, "4 setmanes, 2 dies")]
+ [InlineData(30 + 1, "1 mes")]
+ [InlineData(30 + 30, "1 mes, 29 dies")]
+ [InlineData(30 + 30 + 1, "2 mesos")]
+ [InlineData(30 + 30 + 31, "2 mesos, 30 dies")]
+ [InlineData(30 + 30 + 31 + 1, "3 mesos")]
+ [InlineData(30 + 30 + 31 + 30, "3 mesos, 29 dies")]
+ [InlineData(30 + 30 + 31 + 30 + 1, "4 mesos")]
+ [InlineData(30 + 30 + 31 + 30 + 31, "4 mesos, 30 dies")]
+ [InlineData(30 + 30 + 31 + 30 + 31 + 1, "5 mesos")]
+ [InlineData(365, "11 mesos, 30 dies")]
+ [InlineData(366, "1 any")]
+ public void Month(int days, string expected)
+ {
+ var actual = TimeSpan.FromDays(days).Humanize(precision: 7, maxUnit: TimeUnit.Year);
+ Assert.Equal(expected, actual);
+ }
+
+ [Theory]
+ [InlineData(14, "2 setmanes")]
+ [InlineData(7, "1 setmana")]
+ [InlineData(-14, "2 setmanes")]
+ [InlineData(-7, "1 setmana")]
+ [InlineData(730, "104 setmanes")]
+ public void Weeks(int days, string expected)
+ {
+ var actual = TimeSpan.FromDays(days).Humanize();
+ Assert.Equal(expected, actual);
+ }
+
+ [Theory]
+ [InlineData(6, "6 dies")]
+ [InlineData(2, "2 dies")]
+ [InlineData(1, "1 dia")]
+ [InlineData(-6, "6 dies")]
+ [InlineData(-2, "2 dies")]
+ [InlineData(-1, "1 dia")]
+ public void Days(int days, string expected)
+ {
+ var actual = TimeSpan.FromDays(days).Humanize();
+ Assert.Equal(expected, actual);
+ }
+
+ [Theory]
+ [InlineData(2, "2 hores")]
+ [InlineData(1, "1 hora")]
+ [InlineData(-2, "2 hores")]
+ [InlineData(-1, "1 hora")]
+ public void Hours(int hours, string expected)
+ {
+ var actual = TimeSpan.FromHours(hours).Humanize();
+ Assert.Equal(expected, actual);
+ }
+
+ [Theory]
+ [InlineData(2, "2 minuts")]
+ [InlineData(1, "1 minut")]
+ [InlineData(-2, "2 minuts")]
+ [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 minuts")]
+ [InlineData(60, "1 minut")]
+ [InlineData(2, "2 segons")]
+ [InlineData(1, "1 segon")]
+ [InlineData(-135, "2 minuts")]
+ [InlineData(-60, "1 minut")]
+ [InlineData(-2, "2 segons")]
+ [InlineData(-1, "1 segon")]
+ public void Seconds(int seconds, string expected)
+ {
+ var actual = TimeSpan.FromSeconds(seconds).Humanize();
+ Assert.Equal(expected, actual);
+ }
+
+ [Theory]
+ [InlineData(2500, "2 segons")]
+ [InlineData(1400, "1 segon")]
+ [InlineData(2, "2 mil·lisegons")]
+ [InlineData(1, "1 mil·lisegon")]
+ [InlineData(-2500, "2 segons")]
+ [InlineData(-1400, "1 segon")]
+ [InlineData(-2, "2 mil·lisegons")]
+ [InlineData(-1, "1 mil·lisegon")]
+ public void Milliseconds(int ms, string expected)
+ {
+ var actual = TimeSpan.FromMilliseconds(ms).Humanize();
+ Assert.Equal(expected, actual);
+ }
+
+ [Theory]
+ [InlineData((long)366 * 24 * 60 * 60 * 1000, "12 mesos", TimeUnit.Month)]
+ [InlineData((long)6 * 7 * 24 * 60 * 60 * 1000, "6 setmanes", TimeUnit.Week)]
+ [InlineData(7 * 24 * 60 * 60 * 1000, "7 dies", TimeUnit.Day)]
+ [InlineData(24 * 60 * 60 * 1000, "24 hores", TimeUnit.Hour)]
+ [InlineData(60 * 60 * 1000, "60 minuts", TimeUnit.Minute)]
+ [InlineData(60 * 1000, "60 segons", TimeUnit.Second)]
+ [InlineData(1000, "1000 mil·lisegons", TimeUnit.Millisecond)]
+ public void TimeSpanWithMaxTimeUnit(long ms, string expected, TimeUnit maxUnit)
+ {
+ var actual = TimeSpan.FromMilliseconds(ms).Humanize(maxUnit: maxUnit);
+ Assert.Equal(expected, actual);
+ }
+
+ [Theory]
+ [InlineData(10, "10 mil·lisegons", TimeUnit.Millisecond)]
+ [InlineData(10, "res", TimeUnit.Second, true)]
+ [InlineData(10, "res", TimeUnit.Minute, true)]
+ [InlineData(10, "res", TimeUnit.Hour, true)]
+ [InlineData(10, "res", TimeUnit.Day, true)]
+ [InlineData(10, "res", TimeUnit.Week, true)]
+ [InlineData(10, "0 segons", TimeUnit.Second)]
+ [InlineData(10, "0 minuts", TimeUnit.Minute)]
+ [InlineData(10, "0 hores", TimeUnit.Hour)]
+ [InlineData(10, "0 dies", TimeUnit.Day)]
+ [InlineData(10, "0 setmanes", TimeUnit.Week)]
+ [InlineData(2500, "2 segons, 500 mil·lisegons", TimeUnit.Millisecond)]
+ [InlineData(2500, "2 segons", TimeUnit.Second)]
+ [InlineData(2500, "res", TimeUnit.Minute, true)]
+ [InlineData(2500, "res", TimeUnit.Hour, true)]
+ [InlineData(2500, "res", TimeUnit.Day, true)]
+ [InlineData(2500, "res", TimeUnit.Week, true)]
+ [InlineData(2500, "0 minuts", TimeUnit.Minute)]
+ [InlineData(2500, "0 hores", TimeUnit.Hour)]
+ [InlineData(2500, "0 dies", TimeUnit.Day)]
+ [InlineData(2500, "0 setmanes", TimeUnit.Week)]
+ [InlineData(122500, "2 minuts, 2 segons, 500 mil·lisegons", TimeUnit.Millisecond)]
+ [InlineData(122500, "2 minuts, 2 segons", TimeUnit.Second)]
+ [InlineData(122500, "2 minuts", TimeUnit.Minute)]
+ [InlineData(122500, "res", TimeUnit.Hour, true)]
+ [InlineData(122500, "res", TimeUnit.Day, true)]
+ [InlineData(122500, "res", TimeUnit.Week, true)]
+ [InlineData(122500, "0 hores", TimeUnit.Hour)]
+ [InlineData(122500, "0 dies", TimeUnit.Day)]
+ [InlineData(122500, "0 setmanes", TimeUnit.Week)]
+ [InlineData(3722500, "1 hora, 2 minuts, 2 segons, 500 mil·lisegons", TimeUnit.Millisecond)]
+ [InlineData(3722500, "1 hora, 2 minuts, 2 segons", TimeUnit.Second)]
+ [InlineData(3722500, "1 hora, 2 minuts", TimeUnit.Minute)]
+ [InlineData(3722500, "1 hora", TimeUnit.Hour)]
+ [InlineData(3722500, "res", TimeUnit.Day, true)]
+ [InlineData(3722500, "res", TimeUnit.Week, true)]
+ [InlineData(3722500, "0 dies", TimeUnit.Day)]
+ [InlineData(3722500, "0 setmanes", TimeUnit.Week)]
+ [InlineData(90122500, "1 dia, 1 hora, 2 minuts, 2 segons, 500 mil·lisegons", TimeUnit.Millisecond)]
+ [InlineData(90122500, "1 dia, 1 hora, 2 minuts, 2 segons", TimeUnit.Second)]
+ [InlineData(90122500, "1 dia, 1 hora, 2 minuts", TimeUnit.Minute)]
+ [InlineData(90122500, "1 dia, 1 hora", TimeUnit.Hour)]
+ [InlineData(90122500, "1 dia", TimeUnit.Day)]
+ [InlineData(90122500, "res", TimeUnit.Week, true)]
+ [InlineData(90122500, "0 setmanes", TimeUnit.Week)]
+ [InlineData(694922500, "1 setmana, 1 dia, 1 hora, 2 minuts, 2 segons, 500 mil·lisegons", TimeUnit.Millisecond)]
+ [InlineData(694922500, "1 setmana, 1 dia, 1 hora, 2 minuts, 2 segons", TimeUnit.Second)]
+ [InlineData(694922500, "1 setmana, 1 dia, 1 hora, 2 minuts", TimeUnit.Minute)]
+ [InlineData(694922500, "1 setmana, 1 dia, 1 hora", TimeUnit.Hour)]
+ [InlineData(694922500, "1 setmana, 1 dia", TimeUnit.Day)]
+ [InlineData(694922500, "1 setmana", TimeUnit.Week)]
+ [InlineData(2768462500, "1 mes, 1 dia, 1 hora, 1 minut, 2 segons, 500 mil·lisegons", TimeUnit.Millisecond)]
+ [InlineData(2768462500, "1 mes, 1 dia, 1 hora, 1 minut, 2 segons", TimeUnit.Second)]
+ [InlineData(2768462500, "1 mes, 1 dia, 1 hora, 1 minut", TimeUnit.Minute)]
+ [InlineData(2768462500, "1 mes, 1 dia, 1 hora", TimeUnit.Hour)]
+ [InlineData(2768462500, "1 mes, 1 dia", TimeUnit.Day)]
+ [InlineData(2768462500, "1 mes", TimeUnit.Week)]
+ [InlineData(2768462500, "1 mes", TimeUnit.Month)]
+ [InlineData(2768462500, "res", TimeUnit.Year, true)]
+ [InlineData(2768462500, "0 anys", TimeUnit.Year)]
+ [InlineData(34390862500, "1 any, 1 mes, 2 dies, 1 hora, 1 minut, 2 segons, 500 mil·lisegons", TimeUnit.Millisecond)]
+ [InlineData(34390862500, "1 any, 1 mes, 2 dies, 1 hora, 1 minut, 2 segons", TimeUnit.Second)]
+ [InlineData(34390862500, "1 any, 1 mes, 2 dies, 1 hora, 1 minut", TimeUnit.Minute)]
+ [InlineData(34390862500, "1 any, 1 mes, 2 dies, 1 hora", TimeUnit.Hour)]
+ [InlineData(34390862500, "1 any, 1 mes, 2 dies", TimeUnit.Day)]
+ [InlineData(34390862500, "1 any, 1 mes", TimeUnit.Week)]
+ [InlineData(34390862500, "1 any, 1 mes", TimeUnit.Month)]
+ [InlineData(34390862500, "1 any", TimeUnit.Year)]
+ public void TimeSpanWithMinTimeUnit(long ms, string expected, TimeUnit minUnit, bool toWords = false)
+ {
+ var actual = TimeSpan.FromMilliseconds(ms).Humanize(minUnit: minUnit, precision: 7, maxUnit: TimeUnit.Year, toWords: toWords);
+ Assert.Equal(expected, actual);
+ }
+
+ [Theory]
+ [InlineData(0, 3, "res", true)]
+ [InlineData(0, 2, "res", true)]
+ [InlineData(0, 3, "0 mil·lisegons")]
+ [InlineData(0, 2, "0 mil·lisegons")]
+ [InlineData(10, 2, "10 mil·lisegons")]
+ [InlineData(1400, 2, "1 segon, 400 mil·lisegons")]
+ [InlineData(2500, 2, "2 segons, 500 mil·lisegons")]
+ [InlineData(120000, 2, "2 minuts")]
+ [InlineData(62000, 2, "1 minut, 2 segons")]
+ [InlineData(62020, 2, "1 minut, 2 segons")]
+ [InlineData(62020, 3, "1 minut, 2 segons, 20 mil·lisegons")]
+ [InlineData(3600020, 4, "1 hora, 20 mil·lisegons")]
+ [InlineData(3600020, 3, "1 hora, 20 mil·lisegons")]
+ [InlineData(3600020, 2, "1 hora, 20 mil·lisegons")]
+ [InlineData(3600020, 1, "1 hora")]
+ [InlineData(3603001, 2, "1 hora, 3 segons")]
+ [InlineData(3603001, 3, "1 hora, 3 segons, 1 mil·lisegon")]
+ [InlineData(86400000, 3, "1 dia")]
+ [InlineData(86400000, 2, "1 dia")]
+ [InlineData(86400000, 1, "1 dia")]
+ [InlineData(86401000, 1, "1 dia")]
+ [InlineData(86401000, 2, "1 dia, 1 segon")]
+ [InlineData(86401200, 2, "1 dia, 1 segon")]
+ [InlineData(86401200, 3, "1 dia, 1 segon, 200 mil·lisegons")]
+ [InlineData(1296000000, 1, "2 setmanes")]
+ [InlineData(1296000000, 2, "2 setmanes, 1 dia")]
+ [InlineData(1299600000, 2, "2 setmanes, 1 dia")]
+ [InlineData(1299600000, 3, "2 setmanes, 1 dia, 1 hora")]
+ [InlineData(1299630020, 3, "2 setmanes, 1 dia, 1 hora")]
+ [InlineData(1299630020, 4, "2 setmanes, 1 dia, 1 hora, 30 segons")]
+ [InlineData(1299630020, 5, "2 setmanes, 1 dia, 1 hora, 30 segons, 20 mil·lisegons")]
+ [InlineData(2768462500, 6, "1 mes, 1 dia, 1 hora, 1 minut, 2 segons, 500 mil·lisegons")]
+ [InlineData(2768462500, 5, "1 mes, 1 dia, 1 hora, 1 minut, 2 segons")]
+ [InlineData(2768462500, 4, "1 mes, 1 dia, 1 hora, 1 minut")]
+ [InlineData(2768462500, 3, "1 mes, 1 dia, 1 hora")]
+ [InlineData(2768462500, 2, "1 mes, 1 dia")]
+ [InlineData(2768462500, 1, "1 mes")]
+ [InlineData(34390862500, 7, "1 any, 1 mes, 2 dies, 1 hora, 1 minut, 2 segons, 500 mil·lisegons")]
+ [InlineData(34390862500, 6, "1 any, 1 mes, 2 dies, 1 hora, 1 minut, 2 segons")]
+ [InlineData(34390862500, 5, "1 any, 1 mes, 2 dies, 1 hora, 1 minut")]
+ [InlineData(34390862500, 4, "1 any, 1 mes, 2 dies, 1 hora")]
+ [InlineData(34390862500, 3, "1 any, 1 mes, 2 dies")]
+ [InlineData(34390862500, 2, "1 any, 1 mes")]
+ [InlineData(34390862500, 1, "1 any")]
+ public void TimeSpanWithPrecision(long milliseconds, int precision, string expected, bool toWords = false)
+ {
+ var actual = TimeSpan.FromMilliseconds(milliseconds).Humanize(precision, maxUnit: TimeUnit.Year, toWords: toWords);
+ Assert.Equal(expected, actual);
+ }
+
+ [Theory]
+ [InlineData(3 * 7 + 4, 2, "3 setmanes, 4 dies")]
+ [InlineData(6 * 7 + 3, 2, "6 setmanes, 3 dies")]
+ [InlineData(72 * 7 + 6, 2, "72 setmanes, 6 dies")]
+ public void DaysWithPrecision(int days, int precision, string expected)
+ {
+ var actual = TimeSpan.FromDays(days).Humanize(precision: precision);
+ Assert.Equal(expected, actual);
+ }
+
+ [Theory]
+ [InlineData(50)]
+ [InlineData(52)]
+ public void TimeSpanWithMinAndMaxUnits_DoesNotReportExcessiveTime(int minutes)
+ {
+ var actual = TimeSpan.FromMinutes(minutes).Humanize(2, null, TimeUnit.Hour, TimeUnit.Minute);
+ var expected = TimeSpan.FromMinutes(minutes).Humanize(2);
+ Assert.Equal(expected, actual);
+ }
+
+ [Theory]
+ [InlineData(0, 3, "res", true)]
+ [InlineData(0, 2, "res", true)]
+ [InlineData(0, 3, "0 mil·lisegons")]
+ [InlineData(0, 2, "0 mil·lisegons")]
+ [InlineData(10, 2, "10 mil·lisegons")]
+ [InlineData(1400, 2, "1 segon, 400 mil·lisegons")]
+ [InlineData(2500, 2, "2 segons, 500 mil·lisegons")]
+ [InlineData(60001, 1, "1 minut")]
+ [InlineData(60001, 2, "1 minut")]
+ [InlineData(60001, 3, "1 minut, 1 mil·lisegon")]
+ [InlineData(120000, 2, "2 minuts")]
+ [InlineData(62000, 2, "1 minut, 2 segons")]
+ [InlineData(62020, 2, "1 minut, 2 segons")]
+ [InlineData(62020, 3, "1 minut, 2 segons, 20 mil·lisegons")]
+ [InlineData(3600020, 4, "1 hora, 20 mil·lisegons")]
+ [InlineData(3600020, 3, "1 hora")]
+ [InlineData(3600020, 2, "1 hora")]
+ [InlineData(3600020, 1, "1 hora")]
+ [InlineData(3603001, 2, "1 hora")]
+ [InlineData(3603001, 3, "1 hora, 3 segons")]
+ [InlineData(86400000, 3, "1 dia")]
+ [InlineData(86400000, 2, "1 dia")]
+ [InlineData(86400000, 1, "1 dia")]
+ [InlineData(86401000, 1, "1 dia")]
+ [InlineData(86401000, 2, "1 dia")]
+ [InlineData(86401000, 3, "1 dia")]
+ [InlineData(86401000, 4, "1 dia, 1 segon")]
+ [InlineData(86401200, 4, "1 dia, 1 segon")]
+ [InlineData(86401200, 5, "1 dia, 1 segon, 200 mil·lisegons")]
+ [InlineData(1296000000, 1, "2 setmanes")]
+ [InlineData(1296000000, 2, "2 setmanes, 1 dia")]
+ [InlineData(1299600000, 2, "2 setmanes, 1 dia")]
+ [InlineData(1299600000, 3, "2 setmanes, 1 dia, 1 hora")]
+ [InlineData(1299630020, 3, "2 setmanes, 1 dia, 1 hora")]
+ [InlineData(1299630020, 4, "2 setmanes, 1 dia, 1 hora")]
+ [InlineData(1299630020, 5, "2 setmanes, 1 dia, 1 hora, 30 segons")]
+ [InlineData(1299630020, 6, "2 setmanes, 1 dia, 1 hora, 30 segons, 20 mil·lisegons")]
+ public void TimeSpanWithPrecisionAndCountingEmptyUnits(int milliseconds, int precision, string expected, bool toWords = false)
+ {
+ var actual = TimeSpan.FromMilliseconds(milliseconds).Humanize(precision: precision, countEmptyUnits: true, toWords: toWords);
+ Assert.Equal(expected, actual);
+ }
+
+ [Theory]
+ [InlineData(0, 3, "res", true)]
+ [InlineData(0, 2, "res", true)]
+ [InlineData(0, 3, "0 mil·lisegons")]
+ [InlineData(0, 2, "0 mil·lisegons")]
+ [InlineData(10, 2, "10 mil·lisegons")]
+ [InlineData(1400, 2, "1 segon i 400 mil·lisegons")]
+ [InlineData(2500, 2, "2 segons i 500 mil·lisegons")]
+ [InlineData(120000, 2, "2 minuts")]
+ [InlineData(62000, 2, "1 minut i 2 segons")]
+ [InlineData(62020, 2, "1 minut i 2 segons")]
+ [InlineData(62020, 3, "1 minut, 2 segons i 20 mil·lisegons")]
+ [InlineData(3600020, 4, "1 hora i 20 mil·lisegons")]
+ [InlineData(3600020, 3, "1 hora i 20 mil·lisegons")]
+ [InlineData(3600020, 2, "1 hora i 20 mil·lisegons")]
+ [InlineData(3600020, 1, "1 hora")]
+ [InlineData(3603001, 2, "1 hora i 3 segons")]
+ [InlineData(3603001, 3, "1 hora, 3 segons i 1 mil·lisegon")]
+ [InlineData(86400000, 3, "1 dia")]
+ [InlineData(86400000, 2, "1 dia")]
+ [InlineData(86400000, 1, "1 dia")]
+ [InlineData(86401000, 1, "1 dia")]
+ [InlineData(86401000, 2, "1 dia i 1 segon")]
+ [InlineData(86401200, 2, "1 dia i 1 segon")]
+ [InlineData(86401200, 3, "1 dia, 1 segon i 200 mil·lisegons")]
+ [InlineData(1296000000, 1, "2 setmanes")]
+ [InlineData(1296000000, 2, "2 setmanes i 1 dia")]
+ [InlineData(1299600000, 2, "2 setmanes i 1 dia")]
+ [InlineData(1299600000, 3, "2 setmanes, 1 dia i 1 hora")]
+ [InlineData(1299630020, 3, "2 setmanes, 1 dia i 1 hora")]
+ [InlineData(1299630020, 4, "2 setmanes, 1 dia, 1 hora i 30 segons")]
+ [InlineData(1299630020, 5, "2 setmanes, 1 dia, 1 hora, 30 segons i 20 mil·lisegons")]
+ public void TimeSpanWithPrecisionAndAlternativeCollectionFormatter(int milliseconds, int precision,
+ string expected, bool toWords = false)
+ {
+ var actual = TimeSpan.FromMilliseconds(milliseconds).Humanize(precision, collectionSeparator: null, toWords: toWords);
+ Assert.Equal(expected, actual);
+ }
+
+ [Theory]
+ [InlineData(0, 3, "res")]
+ [InlineData(0, 2, "res")]
+ [InlineData(10, 2, "deu mil·lisegons")]
+ [InlineData(1400, 2, "un segon, quatre-cents mil·lisegons")]
+ [InlineData(2500, 2, "dos segons, cinc-cents mil·lisegons")]
+ [InlineData(120000, 2, "dos minuts")]
+ [InlineData(62000, 2, "un minut, dos segons")]
+ [InlineData(62020, 2, "un minut, dos segons")]
+ [InlineData(62020, 3, "un minut, dos segons, vint mil·lisegons")]
+ [InlineData(3600020, 4, "una hora, vint mil·lisegons")]
+ [InlineData(3600020, 3, "una hora, vint mil·lisegons")]
+ [InlineData(3600020, 2, "una hora, vint mil·lisegons")]
+ [InlineData(3600020, 1, "una hora")]
+ [InlineData(3603001, 2, "una hora, tres segons")]
+ [InlineData(3603001, 3, "una hora, tres segons, un mil·lisegon")]
+ [InlineData(86400000, 3, "un dia")]
+ [InlineData(86400000, 2, "un dia")]
+ [InlineData(86400000, 1, "un dia")]
+ [InlineData(86401000, 1, "un dia")]
+ [InlineData(86401000, 2, "un dia, un segon")]
+ [InlineData(86401200, 2, "un dia, un segon")]
+ [InlineData(86401200, 3, "un dia, un segon, dos-cents mil·lisegons")]
+ [InlineData(1296000000, 1, "dues setmanes")]
+ [InlineData(1296000000, 2, "dues setmanes, un dia")]
+ [InlineData(1299600000, 2, "dues setmanes, un dia")]
+ [InlineData(1299600000, 3, "dues setmanes, un dia, una hora")]
+ [InlineData(1299630020, 3, "dues setmanes, un dia, una hora")]
+ [InlineData(1299630020, 4, "dues setmanes, un dia, una hora, trenta segons")]
+ [InlineData(1299630020, 5, "dues setmanes, un dia, una hora, trenta segons, vint mil·lisegons")]
+ public void TimeSpanWithNumbersConvertedToWords(int milliseconds, int precision, string expected)
+ {
+ var actual = TimeSpan.FromMilliseconds(milliseconds).Humanize(precision, toWords: true);
+ Assert.Equal(expected, actual);
+ }
+}
\ No newline at end of file
diff --git a/src/Humanizer.Tests/Localisation/ca/TimeToClockNotationTests.cs b/src/Humanizer.Tests/Localisation/ca/TimeToClockNotationTests.cs
new file mode 100644
index 000000000..95f63f515
--- /dev/null
+++ b/src/Humanizer.Tests/Localisation/ca/TimeToClockNotationTests.cs
@@ -0,0 +1,67 @@
+#if NET6_0_OR_GREATER
+
+namespace ca;
+
+[UseCulture("ca")]
+public class TimeToClockNotationTests
+{
+ [Theory]
+ [InlineData(0, 0, "mitjanit")]
+ [InlineData(0, 7, "les dotze i set de la nit")]
+ [InlineData(1, 11, "la una i onze de la matinada")]
+ [InlineData(4, 0, "les quatre de la matinada")]
+ [InlineData(5, 1, "les cinc i un de la matinada")]
+ [InlineData(6, 0, "les sis del matí")]
+ [InlineData(6, 5, "les sis i cinc del matí")]
+ [InlineData(7, 10, "les set i deu del matí")]
+ [InlineData(8, 15, "les vuit i quart del matí")]
+ [InlineData(9, 20, "les nou i vint del matí")]
+ [InlineData(10, 25, "les deu i vint-i-cinc del matí")]
+ [InlineData(11, 30, "les onze i mitja del matí")]
+ [InlineData(12, 00, "migdia")]
+ [InlineData(12, 38, "les dotze i trenta-vuit de la tarda")]
+ [InlineData(12, 35, "la una menys vint-i-cinc de la tarda")]
+ [InlineData(15, 40, "les quatre menys vint de la tarda")]
+ [InlineData(17, 45, "les sis menys quart de la tarda")]
+ [InlineData(19, 50, "les vuit menys deu de la tarda")]
+ [InlineData(21, 0, "les nou de la nit")]
+ [InlineData(21, 55, "les deu menys cinc de la nit")]
+ [InlineData(22, 59, "les deu i cinquanta-nou de la nit")]
+ [InlineData(23, 43, "les onze i quaranta-tres de la nit")]
+ public void ConvertToClockNotationTimeOnlyString(int hours, int minutes, string expectedResult)
+ {
+ var actualResult = new TimeOnly(hours, minutes).ToClockNotation();
+ Assert.Equal(expectedResult, actualResult);
+ }
+
+ [Theory]
+ [InlineData(0, 0, "mitjanit")]
+ [InlineData(0, 7, "les dotze i cinc de la nit")]
+ [InlineData(1, 11, "la una i deu de la matinada")]
+ [InlineData(4, 0, "les quatre de la matinada")]
+ [InlineData(5, 1, "les cinc de la matinada")]
+ [InlineData(6, 0, "les sis del matí")]
+ [InlineData(6, 5, "les sis i cinc del matí")]
+ [InlineData(7, 10, "les set i deu del matí")]
+ [InlineData(8, 15, "les vuit i quart del matí")]
+ [InlineData(9, 20, "les nou i vint del matí")]
+ [InlineData(10, 25, "les deu i vint-i-cinc del matí")]
+ [InlineData(11, 30, "les onze i mitja del matí")]
+ [InlineData(12, 00, "migdia")]
+ [InlineData(12, 38, "la una menys vint de la tarda")]
+ [InlineData(12, 35, "la una menys vint-i-cinc de la tarda")]
+ [InlineData(15, 40, "les quatre menys vint de la tarda")]
+ [InlineData(17, 45, "les sis menys quart de la tarda")]
+ [InlineData(19, 50, "les vuit menys deu de la tarda")]
+ [InlineData(21, 0, "les nou de la nit")]
+ [InlineData(21, 55, "les deu menys cinc de la nit")]
+ [InlineData(22, 59, "les onze de la nit")]
+ [InlineData(23, 43, "les dotze menys quart de la nit")]
+ public void ConvertToRoundedClockNotationTimeOnlyString(int hours, int minutes, string expectedResult)
+ {
+ var actualResult = new TimeOnly(hours, minutes).ToClockNotation(ClockNotationRounding.NearestFiveMinutes);
+ Assert.Equal(expectedResult, actualResult);
+ }
+}
+
+#endif
\ No newline at end of file
diff --git a/src/Humanizer.sln b/src/Humanizer.sln
index 4a053f70e..3c5e60420 100644
--- a/src/Humanizer.sln
+++ b/src/Humanizer.sln
@@ -25,10 +25,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{97AAE24D-0488-42AE-A585-86D882F23D5F}"
ProjectSection(SolutionItems) = preProject
- .\azure-pipelines.yml = ..\azure-pipelines.yml
- .\build.cmd = .\build.cmd
- .\build.ps1 = .\build.ps1
- .\Humanizer.ruleset = Humanizer.ruleset
+ azure-pipelines.yml = azure-pipelines.yml
+ build.cmd = build.cmd
+ build.ps1 = build.ps1
+ Humanizer.ruleset = Humanizer.ruleset
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NuSpecs", "NuSpecs", "{AA449265-E001-486D-A0F4-04ACF0C83DC1}"
@@ -38,6 +38,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NuSpecs", "NuSpecs", "{AA44
..\NuSpecs\Humanizer.Core.az.nuspec = ..\NuSpecs\Humanizer.Core.az.nuspec
..\NuSpecs\Humanizer.Core.bg.nuspec = ..\NuSpecs\Humanizer.Core.bg.nuspec
..\NuSpecs\Humanizer.Core.bn.nuspec = ..\NuSpecs\Humanizer.Core.bn.nuspec
+ ..\NuSpecs\Humanizer.Core.ca.nuspec = ..\NuSpecs\Humanizer.Core.ca.nuspec
..\NuSpecs\Humanizer.Core.cs.nuspec = ..\NuSpecs\Humanizer.Core.cs.nuspec
..\NuSpecs\Humanizer.Core.da.nuspec = ..\NuSpecs\Humanizer.Core.da.nuspec
..\NuSpecs\Humanizer.Core.de.nuspec = ..\NuSpecs\Humanizer.Core.de.nuspec
@@ -75,8 +76,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NuSpecs", "NuSpecs", "{AA44
..\NuSpecs\Humanizer.Core.sr-Latn.nuspec = ..\NuSpecs\Humanizer.Core.sr-Latn.nuspec
..\NuSpecs\Humanizer.Core.sr.nuspec = ..\NuSpecs\Humanizer.Core.sr.nuspec
..\NuSpecs\Humanizer.Core.sv.nuspec = ..\NuSpecs\Humanizer.Core.sv.nuspec
- ..\NuSpecs\Humanizer.Core.th.nuspec = ..\NuSpecs\Humanizer.Core.th.nuspec
..\NuSpecs\Humanizer.Core.ta.nuspec = ..\NuSpecs\Humanizer.Core.ta.nuspec
+ ..\NuSpecs\Humanizer.Core.th.nuspec = ..\NuSpecs\Humanizer.Core.th.nuspec
..\NuSpecs\Humanizer.Core.tr.nuspec = ..\NuSpecs\Humanizer.Core.tr.nuspec
..\NuSpecs\Humanizer.Core.uk.nuspec = ..\NuSpecs\Humanizer.Core.uk.nuspec
..\NuSpecs\Humanizer.Core.uz-Cyrl-UZ.nuspec = ..\NuSpecs\Humanizer.Core.uz-Cyrl-UZ.nuspec
diff --git a/src/Humanizer/Configuration/CollectionFormatterRegistry.cs b/src/Humanizer/Configuration/CollectionFormatterRegistry.cs
index f6643c101..8790992c3 100644
--- a/src/Humanizer/Configuration/CollectionFormatterRegistry.cs
+++ b/src/Humanizer/Configuration/CollectionFormatterRegistry.cs
@@ -1,4 +1,4 @@
-namespace Humanizer;
+namespace Humanizer;
class CollectionFormatterRegistry : LocaliserRegistry
{
@@ -18,5 +18,6 @@ public CollectionFormatterRegistry()
Register("is", new DefaultCollectionFormatter("og"));
Register("es", new DefaultCollectionFormatter("y"));
Register("lb", new DefaultCollectionFormatter("an"));
+ Register("ca", new DefaultCollectionFormatter("i"));
}
}
\ No newline at end of file
diff --git a/src/Humanizer/Configuration/DateOnlyToOrdinalWordsConverterRegistry.cs b/src/Humanizer/Configuration/DateOnlyToOrdinalWordsConverterRegistry.cs
index 9e2fb82d1..f53bf66b3 100644
--- a/src/Humanizer/Configuration/DateOnlyToOrdinalWordsConverterRegistry.cs
+++ b/src/Humanizer/Configuration/DateOnlyToOrdinalWordsConverterRegistry.cs
@@ -1,4 +1,4 @@
-#if NET6_0_OR_GREATER
+#if NET6_0_OR_GREATER
namespace Humanizer;
class DateOnlyToOrdinalWordsConverterRegistry : LocaliserRegistry
@@ -9,6 +9,7 @@ public DateOnlyToOrdinalWordsConverterRegistry() : base(new DefaultDateOnlyToOrd
Register("fr", new FrDateOnlyToOrdinalWordsConverter());
Register("es", new EsDateOnlyToOrdinalWordsConverter());
Register("lt", new LtDateOnlyToOrdinalWordsConverter());
+ Register("ca", new CaDateOnlyToOrdinalWordsConverter());
}
}
#endif
diff --git a/src/Humanizer/Configuration/DateToOrdinalWordsConverterRegistry.cs b/src/Humanizer/Configuration/DateToOrdinalWordsConverterRegistry.cs
index 7315f0d66..d6ffe813c 100644
--- a/src/Humanizer/Configuration/DateToOrdinalWordsConverterRegistry.cs
+++ b/src/Humanizer/Configuration/DateToOrdinalWordsConverterRegistry.cs
@@ -1,4 +1,4 @@
-namespace Humanizer;
+namespace Humanizer;
class DateToOrdinalWordsConverterRegistry : LocaliserRegistry
{
@@ -9,5 +9,6 @@ public DateToOrdinalWordsConverterRegistry()
Register("fr", new FrDateToOrdinalWordsConverter());
Register("es", new EsDateToOrdinalWordsConverter());
Register("lt", new LtDateToOrdinalWordsConverter());
+ Register("ca", new CaDateToOrdinalWordsConverter());
}
}
\ No newline at end of file
diff --git a/src/Humanizer/Configuration/FormatterRegistry.cs b/src/Humanizer/Configuration/FormatterRegistry.cs
index 27e19e482..b518aa832 100644
--- a/src/Humanizer/Configuration/FormatterRegistry.cs
+++ b/src/Humanizer/Configuration/FormatterRegistry.cs
@@ -55,6 +55,7 @@ public FormatterRegistry()
RegisterDefaultFormatter("th");
Register("lt", c => new LithuanianFormatter(c));
Register("lb", c => new LuxembourgishFormatter(c));
+ Register("ca", c => new CatalanFormatter(c));
}
void RegisterDefaultFormatter(string localeCode) =>
diff --git a/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs b/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs
index f9687c200..5801f48a4 100644
--- a/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs
+++ b/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs
@@ -57,5 +57,6 @@ public NumberToWordsConverterRegistry()
Register("lt", new LithuanianNumberToWordsConverter());
Register("lb", new LuxembourgishNumberToWordsConverter());
Register("hu", new HungarianNumberToWordsConverter());
+ Register("ca", new CatalanNumberToWordsConverter());
}
}
\ No newline at end of file
diff --git a/src/Humanizer/Configuration/OrdinalizerRegistry.cs b/src/Humanizer/Configuration/OrdinalizerRegistry.cs
index e0296d9de..1b9851169 100644
--- a/src/Humanizer/Configuration/OrdinalizerRegistry.cs
+++ b/src/Humanizer/Configuration/OrdinalizerRegistry.cs
@@ -1,4 +1,4 @@
-namespace Humanizer;
+namespace Humanizer;
class OrdinalizerRegistry : LocaliserRegistry
{
@@ -21,5 +21,6 @@ public OrdinalizerRegistry()
Register("az", new AzerbaijaniOrdinalizer());
Register("lb", new LuxembourgishOrdinalizer());
Register("hu", new HungarianOrdinalizer());
+ Register("ca", new CatalanOrdinalizer());
}
}
\ No newline at end of file
diff --git a/src/Humanizer/Configuration/TimeOnlyToClockNotationConvertersRegistry.cs b/src/Humanizer/Configuration/TimeOnlyToClockNotationConvertersRegistry.cs
index edd9fdb99..1fd711c9c 100644
--- a/src/Humanizer/Configuration/TimeOnlyToClockNotationConvertersRegistry.cs
+++ b/src/Humanizer/Configuration/TimeOnlyToClockNotationConvertersRegistry.cs
@@ -12,6 +12,7 @@ public TimeOnlyToClockNotationConvertersRegistry() : base(new DefaultTimeOnlyToC
Register("es", new EsTimeOnlyToClockNotationConverter());
Register("lb", new LbTimeOnlyToClockNotationConverter());
Register("pt", new PortugueseTimeOnlyToClockNotationConverter());
+ Register("ca", new CaTimeOnlyToClockNotationConverter());
}
}
diff --git a/src/Humanizer/Localisation/DateToOrdinalWords/CaDateOnlyToOrdinalWordsConverter.cs b/src/Humanizer/Localisation/DateToOrdinalWords/CaDateOnlyToOrdinalWordsConverter.cs
new file mode 100644
index 000000000..8ef0b736c
--- /dev/null
+++ b/src/Humanizer/Localisation/DateToOrdinalWords/CaDateOnlyToOrdinalWordsConverter.cs
@@ -0,0 +1,14 @@
+#if NET6_0_OR_GREATER
+
+namespace Humanizer;
+
+class CaDateOnlyToOrdinalWordsConverter : DefaultDateOnlyToOrdinalWordConverter
+{
+ public override string Convert(DateOnly date)
+ {
+ var equivalentDateTime = date.ToDateTime(TimeOnly.MinValue);
+ return Configurator.DateToOrdinalWordsConverter.Convert(equivalentDateTime);
+ }
+}
+
+#endif
diff --git a/src/Humanizer/Localisation/DateToOrdinalWords/CaDateToOrdinalWordsConverter.cs b/src/Humanizer/Localisation/DateToOrdinalWords/CaDateToOrdinalWordsConverter.cs
new file mode 100644
index 000000000..b5d937ef9
--- /dev/null
+++ b/src/Humanizer/Localisation/DateToOrdinalWords/CaDateToOrdinalWordsConverter.cs
@@ -0,0 +1,7 @@
+namespace Humanizer;
+
+class CaDateToOrdinalWordsConverter : DefaultDateToOrdinalWordConverter
+{
+ public override string Convert(DateTime date) =>
+ date.ToString("d MMMM 'de' yyyy");
+}
\ No newline at end of file
diff --git a/src/Humanizer/Localisation/Formatters/CatalanFormatter.cs b/src/Humanizer/Localisation/Formatters/CatalanFormatter.cs
new file mode 100644
index 000000000..851962c2c
--- /dev/null
+++ b/src/Humanizer/Localisation/Formatters/CatalanFormatter.cs
@@ -0,0 +1,19 @@
+namespace Humanizer;
+
+class CatalanFormatter(CultureInfo culture) :
+ DefaultFormatter(culture)
+{
+ protected override string Format(TimeUnit unit, string resourceKey, int number, bool toWords = false)
+ {
+ var resolvedKey = GetResourceKey(resourceKey, number);
+ var resourceString = Resources.GetResource(resolvedKey, Culture);
+
+ var gender = unit switch
+ {
+ TimeUnit.Hour or TimeUnit.Week => GrammaticalGender.Feminine,
+ _ => GrammaticalGender.Masculine
+ };
+
+ return string.Format(resourceString, toWords ? number.ToWords(gender, Culture) : number);
+ }
+}
\ No newline at end of file
diff --git a/src/Humanizer/Localisation/NumberToWords/CatalanNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/CatalanNumberToWordsConverter.cs
new file mode 100644
index 000000000..f286044a8
--- /dev/null
+++ b/src/Humanizer/Localisation/NumberToWords/CatalanNumberToWordsConverter.cs
@@ -0,0 +1,300 @@
+namespace Humanizer
+{
+ class CatalanNumberToWordsConverter : GenderedNumberToWordsConverter
+ {
+ // Cardinal units
+ private static readonly string[] UnitsMasculine =
+ {
+ "", "un", "dos", "tres", "quatre", "cinc", "sis", "set", "vuit", "nou"
+ };
+ private static readonly string[] UnitsFeminine =
+ {
+ "", "una", "dues", "tres", "quatre", "cinc", "sis", "set", "vuit", "nou"
+ };
+
+ // Special cases for teens (10-19)
+ private static readonly string[] Teens =
+ {
+ "deu", "onze", "dotze", "tretze", "catorze", "quinze", "setze", "disset", "divuit", "dinou"
+ };
+
+ // Decenas: exact tens and for composition
+ private static readonly string[] Tens =
+ {
+ "", "deu", "vint", "trenta", "quaranta", "cinquanta", "seixanta", "setanta", "vuitanta", "noranta"
+ };
+
+ // Centenas (masculino y femenino)
+ private static readonly string[] HundredsMasculine =
+ {
+ "", "cent", "dos-cents", "tres-cents", "quatre-cents", "cinc-cents", "sis-cents", "set-cents", "vuit-cents", "nou-cents"
+ };
+ private static readonly string[] HundredsFeminine =
+ {
+ "", "cent", "dues-centes", "tres-centes", "quatre-centes", "cinc-centes", "sis-centes", "set-centes", "vuit-centes", "nou-centes"
+ };
+
+ static readonly string[] TupleMap =
+ [
+ "zero vegades", "una vegada", "doble", "triple", "qüàdruple", "quíntuple", "sèxtuple", "sèptuple", "òctuple",
+ "nònuple", "dècuple", "undècuple", "duodècuple", "tercidecuple"
+ ];
+
+ #region Convert
+ public override string Convert(long number, GrammaticalGender gender, bool addAnd = true)
+ {
+ if (number == 0)
+ return "zero";
+ if (number < 0)
+ return "menys " + Convert(-number, gender);
+
+ if (number < 10)
+ return GetUnit((int)number, gender);
+ if (number < 20)
+ return Teens[number - 10];
+ if (number < 100)
+ return GetTens((int)number, gender);
+ if (number < 1000)
+ return GetHundreds((int)number, gender);
+ if (number < 1000000)
+ return GetThousands((int)number, gender);
+ if (number < 1000000000)
+ return GetMillions((int)number, gender);
+
+ throw new NotImplementedException("Nombres més grans de mil milions no estan implementats.");
+ }
+
+ private string GetUnit(int number, GrammaticalGender gender)
+ => gender == GrammaticalGender.Feminine ? UnitsFeminine[number] : UnitsMasculine[number];
+
+ private string GetTens(int number, GrammaticalGender gender)
+ {
+ int tens = number / 10;
+ int units = number % 10;
+ if (number < 20)
+ return Teens[number - 10];
+ if (units == 0)
+ return Tens[tens];
+ // "vint-i-un", "trenta-dos"
+ string conjunction = tens == 2 ? "-i-" : "-";
+
+ var num = (units == 1) && gender == GrammaticalGender.Masculine
+ ? "u"
+ : GetUnit(units, gender);
+
+ return $"{Tens[tens]}{conjunction}{num}";
+ }
+
+ private string GetHundreds(int number, GrammaticalGender gender)
+ {
+ int hundreds = number / 100;
+ int rest = number % 100;
+ string hundredPart = gender == GrammaticalGender.Feminine ? HundredsFeminine[hundreds] : HundredsMasculine[hundreds];
+
+ if (rest == 0)
+ return hundredPart;
+
+ var num = rest == 1 && gender == GrammaticalGender.Masculine
+ ? "u"
+ : (rest < 10 ? GetUnit(rest, gender) : GetTens(rest, gender));
+
+ return $"{hundredPart} {num}";
+ }
+
+ private string GetThousands(int number, GrammaticalGender gender)
+ {
+ int thousands = number / 1000;
+ int rest = number % 1000;
+ string thousandPart;
+ if (thousands == 1)
+ thousandPart = "mil";
+ else
+ thousandPart = $"{Convert(thousands, gender)} mil";
+
+ if (rest == 0)
+ return thousandPart;
+ var num = rest == 1 && gender == GrammaticalGender.Masculine
+ ? "u"
+ : Convert(rest, gender);
+
+ return $"{thousandPart} {num}";
+ }
+
+ private string GetMillions(int number, GrammaticalGender gender)
+ {
+ int millions = number / 1000000;
+ int rest = number % 1000000;
+ string millionPart;
+ if (millions == 1)
+ millionPart = "un milió";
+ else
+ millionPart = $"{Convert(millions, GrammaticalGender.Masculine)} milions";
+ if (rest == 0)
+ return millionPart;
+ var num = rest == 1 && gender == GrammaticalGender.Masculine
+ ? "u"
+ : Convert(rest, gender);
+
+ return $"{millionPart} {num}";
+ }
+
+ #endregion
+
+ #region ConvertToOrdinal
+ public override string ConvertToOrdinal(int number, GrammaticalGender gender)
+ {
+ if (number < 0)
+ return "menys " + ConvertToOrdinal(-number, gender);
+ if (number == 0)
+ return "zero";
+
+ // Ordinales simples
+ string[] masc = { "", "primer", "segon", "tercer", "quart", "cinquè", "sisè", "setè", "vuitè", "novè", "desè", "onzè", "dotzè", "tretzè", "catorzè", "quinzè" };
+ string[] fem = { "", "primera", "segona", "tercera", "quarta", "cinquena", "sisena", "setena", "vuitena", "novena", "desena", "onzena", "dotzena", "tretzena", "catorzena", "quinzena" };
+
+ if (number < masc.Length)
+ return gender == GrammaticalGender.Feminine ? fem[number] : masc[number];
+
+ if (number < 100)
+ return GetOrdinalTens(number, gender);
+ if (number < 1000)
+ return GetOrdinalHundreds(number, gender);
+ if (number < 1000000)
+ return GetOrdinalThousands(number, gender);
+ if (number < 1000000000)
+ return GetOrdinalMillions(number, gender);
+
+ throw new NotImplementedException("Ordinal més gran de cent milions no implementat.");
+ }
+
+ // Helpers
+
+ private string GetOrdinalTens(int number, GrammaticalGender gender)
+ {
+ int dec = number / 10;
+ int rem = number % 10;
+ string[] tens = { "", "", "vint", "trenta", "quaranta", "cinquanta", "seixanta", "setanta", "vuitanta", "noranta" };
+
+ string ordSuf = gender == GrammaticalGender.Feminine ? "ena" : "è";
+
+ if (rem == 0)
+ {
+ var tensDec = tens[dec];
+ if (dec == 3 || dec == 6 || dec == 7 || dec == 8 || dec == 9)
+ tensDec = tensDec.Substring(0, tensDec.Length - 1); //
+
+ return tensDec + ordSuf;
+ }
+
+ string[] unitRoots = new[] { "", "un", "dos", "tres", "quatr", "cinqu", "sis", "set", "vuit", "nov" };
+ var num = $"{unitRoots[rem]}{ordSuf}";
+ if (rem == 1)
+ num = gender == GrammaticalGender.Feminine ? "una" : "un";
+
+ string conj = dec == 2 ? "-i-" : "-";
+ return $"{tens[dec]}{conj}{num}";
+ }
+
+ private string GetOrdinalHundreds(int number, GrammaticalGender gender)
+ {
+ int centenas = number / 100;
+ int rest = number % 100;
+
+ string hundred = gender == GrammaticalGender.Feminine
+ ? new[] { "", "cent", "dues-centes", "tres-centes", "quatre-centes", "cinc-centes", "sis-centes", "set-centes", "vuit-centes", "nou-centes" }[centenas]
+ : new[] { "", "cent", "dos-cents", "tres-cents", "quatre-cents", "cinc-cents", "sis-cents", "set-cents", "vuit-cents", "nou-cents" }[centenas];
+
+ if (rest == 0 && centenas == 1)
+ return $"{hundred}{(gender == GrammaticalGender.Feminine ? "ena" : "è")}";
+
+ if (rest == 0)
+ return hundred;
+
+ var num = "";
+ if (centenas == 1 && rest % 10 != 0)
+ num = ConvertToOrdinal(rest, gender);
+ else
+ {
+ num = Convert(rest, gender);
+ if (gender == GrammaticalGender.Masculine && rest != 1 && rest % 10 == 1)
+ num = num + "n";
+ }
+
+ return $"{hundred} {num}";
+ }
+
+ private string GetOrdinalThousands(int number, GrammaticalGender gender)
+ {
+ int mils = number / 1000;
+ int rest = number % 1000;
+ string milStr = mils == 1 ? "mil" : $"{Convert(mils, gender)} mil";
+ if (rest == 0)
+ return milStr;
+
+ if (rest == 100)
+ return $"{milStr} cent";
+
+ var num = Convert(rest, gender);
+ if (gender == GrammaticalGender.Masculine && rest != 1 && rest % 10 == 1)
+ num = num + "n";
+
+ return $"{milStr} {num}";
+ }
+
+ private string GetOrdinalMillions(int number, GrammaticalGender gender)
+ {
+ int mills = number / 1000000;
+ int rest = number % 1000000;
+ string millsStr = mills == 1 ? "un milió" : $"{Convert(mills, GrammaticalGender.Masculine)} milions";
+ if (rest == 0)
+ return millsStr;
+
+ var num = Convert(rest, gender);
+ if (gender == GrammaticalGender.Masculine && rest != 1 && rest % 10 == 1)
+ num = num + "n";
+
+ return $"{millsStr} {num}";
+ }
+
+ public override string ConvertToOrdinal(int number, GrammaticalGender gender, WordForm wordForm)
+ {
+ // ordinal (Ej: 1r, 1a, 2n, 2a, 11è, 11a, etc.)
+ if (wordForm == WordForm.Abbreviation)
+ {
+ if (number == 1) return gender == GrammaticalGender.Feminine ? "1a" : "1r";
+ if (number == 2) return gender == GrammaticalGender.Feminine ? "2a" : "2n";
+ if (number == 3) return gender == GrammaticalGender.Feminine ? "3a" : "3r";
+ if (number == 22) return gender == GrammaticalGender.Feminine ? "22a" : "22n";
+ if (number == 31) return gender == GrammaticalGender.Feminine ? "31a" : "31r";
+ if (number == 11 || number == 100 || number == 999)
+ return number.ToString() + (gender == GrammaticalGender.Feminine ? "a" : "è");
+ if (number == 101)
+ return gender == GrammaticalGender.Feminine ? "101a" : "101r";
+ if (number == 999)
+ return gender == GrammaticalGender.Feminine ? "999a" : "999è";
+ // Comportamiento genérico
+ if (gender == GrammaticalGender.Feminine)
+ return number.ToString() + "a";
+ return number.ToString() + (
+ (number % 10 == 1 || number % 10 == 3) ? "r" :
+ (number % 10 == 2 || number % 10 == 7) ? "n" :
+ "è"
+ );
+ }
+ return ConvertToOrdinal(number, gender);
+ }
+
+ #endregion
+
+ public override string ConvertToTuple(int number)
+ {
+ number = Math.Abs(number);
+
+ if (number < TupleMap.Length)
+ return TupleMap[number];
+
+ return Convert(number) + " vegades";
+ }
+
+ }
+}
diff --git a/src/Humanizer/Localisation/Ordinalizers/CatalanOrdinalizer.cs b/src/Humanizer/Localisation/Ordinalizers/CatalanOrdinalizer.cs
new file mode 100644
index 000000000..8235e8bfd
--- /dev/null
+++ b/src/Humanizer/Localisation/Ordinalizers/CatalanOrdinalizer.cs
@@ -0,0 +1,34 @@
+namespace Humanizer
+{
+ class CatalanOrdinalizer() : DefaultOrdinalizer
+ {
+ public override string Convert(int number, string numberString) =>
+ Convert(number, numberString, GrammaticalGender.Masculine, WordForm.Normal);
+
+ public override string Convert(int number, string numberString, GrammaticalGender gender) =>
+ Convert(number, numberString, gender, WordForm.Normal);
+
+ public override string Convert(int number, string numberString, GrammaticalGender gender, WordForm wordForm)
+ {
+ if (number is 0 or int.MinValue)
+ return "0";
+
+ if (number < 0)
+ return Convert(-number, (-number).ToString(), gender);
+
+ if (gender == GrammaticalGender.Feminine)
+ return $"{numberString}a";
+
+ if (number % 10 == 1 || number % 10 == 3)
+ return $"{numberString}r";
+ if (number % 10 == 2)
+ return $"{numberString}n";
+ if (number % 10 == 4)
+ return $"{numberString}t";
+
+ return $"{numberString}è";
+
+
+ }
+ }
+}
diff --git a/src/Humanizer/Localisation/TimeToClockNotation/CaTimeOnlyToClockNotationConverter.cs b/src/Humanizer/Localisation/TimeToClockNotation/CaTimeOnlyToClockNotationConverter.cs
new file mode 100644
index 000000000..d60df8ce2
--- /dev/null
+++ b/src/Humanizer/Localisation/TimeToClockNotation/CaTimeOnlyToClockNotationConverter.cs
@@ -0,0 +1,87 @@
+#if NET6_0_OR_GREATER
+
+namespace Humanizer;
+
+class CaTimeOnlyToClockNotationConverter : ITimeOnlyToClockNotationConverter
+{
+ const int MORNING = 6;
+ const int NOON = 12;
+ const int AFTERNOON = 21;
+
+ public string Convert(TimeOnly time, ClockNotationRounding roundToNearestFive)
+ {
+ switch (time)
+ {
+ case { Hour: 0, Minute: 0 }:
+ return "mitjanit";
+
+ case { Hour: 12, Minute: 0 }:
+ return "migdia";
+ }
+
+ var article = GetArticle(time);
+ var articleNextHour = GetArticle(time.AddHours(1));
+ var hour = NormalizeHour(time).ToWords(GrammaticalGender.Feminine);
+ var nextHour = NormalizeHour(time.AddHours(1)).ToWords(GrammaticalGender.Feminine);
+ var dayPeriod = GetDayPeriod(time);
+ var dayPeriodNextHour = GetDayPeriod(time.AddHours(1));
+
+ var normalizedMinutes = (int)(roundToNearestFive == ClockNotationRounding.NearestFiveMinutes
+ ? 5 * Math.Round(time.Minute / 5.0)
+ : time.Minute);
+
+ var clockNotationMap = new Dictionary
+ {
+ { 0, $"{article} {hour} {dayPeriod}" },
+ { 15 , $"{article} {hour} i quart {dayPeriod}" },
+ { 30 , $"{article} {hour} i mitja {dayPeriod}"},
+ { 35 , $"{articleNextHour} {nextHour} menys vint-i-cinc {dayPeriodNextHour}"},
+ { 40 , $"{articleNextHour} {nextHour} menys vint {dayPeriodNextHour}"},
+ { 45 , $"{articleNextHour} {nextHour} menys quart {dayPeriodNextHour}"},
+ { 50 , $"{articleNextHour} {nextHour} menys deu {dayPeriodNextHour}"},
+ { 55 , $"{articleNextHour} {nextHour} menys cinc {dayPeriodNextHour}"},
+ { 60 , $"{articleNextHour} {nextHour} {dayPeriodNextHour}"},
+ };
+
+ return clockNotationMap.GetValueOrDefault(
+ normalizedMinutes,
+ $"{article} {hour} i {normalizedMinutes.ToWords()} {dayPeriod}");
+ }
+
+ static int NormalizeHour(TimeOnly time) =>
+ time.Hour % 12 != 0 ? time.Hour % 12 : 12;
+
+ static string GetArticle(TimeOnly time) =>
+ time.Hour is 1 or 13 ? "la" : "les";
+
+ static string GetDayPeriod(TimeOnly time)
+ {
+ if (IsEarlyMorning(time))
+ {
+ return "de la matinada";
+ }
+
+ if (IsMorning(time))
+ {
+ return "del matí";
+ }
+
+ if (IsAfternoon(time))
+ {
+ return "de la tarda";
+ }
+
+ return "de la nit";
+ }
+
+ static bool IsEarlyMorning(TimeOnly time) =>
+ time.Hour is >= 1 and < MORNING;
+
+ static bool IsMorning(TimeOnly time) =>
+ time.Hour is >= MORNING and < NOON;
+
+ static bool IsAfternoon(TimeOnly time) =>
+ time.Hour is >= NOON and < AFTERNOON;
+}
+
+#endif
\ No newline at end of file
diff --git a/src/Humanizer/Properties/Resources.ca.resx b/src/Humanizer/Properties/Resources.ca.resx
new file mode 100644
index 000000000..6ef7b8556
--- /dev/null
+++ b/src/Humanizer/Properties/Resources.ca.resx
@@ -0,0 +1,601 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 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
+
+
+ fa un segon
+
+
+ fa {0} segons
+
+
+ fa un minut
+
+
+ fa {0} minuts
+
+
+ fa una hora
+
+
+ fa {0} hores
+
+
+ ahir
+
+
+ fa {0} dies
+
+
+ fa un mes
+
+
+ fa {0} mesos
+
+
+ fa un any
+
+
+ fa {0} anys
+
+
+ {0} dies
+
+
+ {0} hores
+
+
+ {0} mil·lisegons
+
+
+ {0} minuts
+
+
+ {0} segons
+
+
+ {0} setmanes
+
+
+ 1 dia
+
+
+ 1 hora
+
+
+ 1 mil·lisegon
+
+
+ 1 minut
+
+
+ 1 segon
+
+
+ 1 setmana
+
+
+ res
+
+
+ d'aquí {0} dies
+
+
+ d'aquí {0} hores
+
+
+ d'aquí {0} minuts
+
+
+ d'aquí {0} mesos
+
+
+ d'aquí {0} segons
+
+
+ d'aquí {0} anys
+
+
+ demà
+
+
+ d'aquí una hora
+
+
+ d'aquí un minut
+
+
+ d'aquí un mes
+
+
+ d'aquí un segon
+
+
+ d'aquí un any
+
+
+ ara
+
+
+ {0} mesos
+
+
+ {0} anys
+
+
+ 1 mes
+
+
+ 1 any
+
+
+ {0} setmanes
+
+
+ un dia
+
+
+ una hora
+
+
+ un mil·lisegon
+
+
+ un minut
+
+
+ un mes
+
+
+ un segon
+
+
+ una setmana
+
+
+ un any
+
+
+ nord
+
+
+ nord-nord-est
+
+
+ nord-est
+
+
+ est-nord-est
+
+
+ est
+
+
+ est-sud-est
+
+
+ sud-est
+
+
+ sud-sud-est
+
+
+ sud
+
+
+ sud-sud-oest
+
+
+ sud-oest
+
+
+ oest-sud-oest
+
+
+ oest
+
+
+ oest-nord-oest
+
+
+ {0} any
+
+
+ {0} anys
+
+
+ {0} anys
+
+
+ {0} setmana
+
+
+ fa {0} hores
+
+
+ d'aquí {0} hores
+
+
+ fa {0} minuts
+
+
+ d'aquí {0} minuts
+
+
+ fa {0} mesos
+
+
+ d'aquí {0} mesos
+
+
+ fa {0} segons
+
+
+ d'aquí {0} segons
+
+
+ fa {0} anys
+
+
+ nord-oest
+
+
+ d'aquí {0} anys
+
+
+ {0} mil·lisegons
+
+
+ {0} minuts
+
+
+ {0} segons
+
+
+ {0} setmanes
+
+
+ mai
+
+
+ {0} mesos
+
+
+ {0} mesos
+
+
+ {0} mesos
+
+
+ {0} mes
+
+
+ {0} anys
+
+
+ {0} hores
+
+
+ nord-nord-oest
+
+
+ N
+
+
+ N-NE
+
+
+ min
+
+
+ h
+
+
+ dia
+
+
+ setmana
+
+
+ mes
+
+
+ any
+
+
+ s
+
+
+ ms
+
+
+ terabyte
+
+
+ NE
+
+
+ E-NE
+
+
+ E
+
+
+ E-SE
+
+
+ SE
+
+
+ S-SE
+
+
+ S
+
+
+ S-SO
+
+
+ SO
+
+
+ O-SO
+
+
+ O
+
+
+ TB
+
+
+ O-NO
+
+
+ N-NO
+
+
+ bit
+
+
+ b
+
+
+ byte
+
+
+ B
+
+
+ kilobyte
+
+
+ KB
+
+
+ megabyte
+
+
+ MB
+
+
+ gigabyte
+
+
+ GB
+
+
+ NO
+
+
+ {0} setmanes
+
+
+ {0} segon
+
+
+ {0} dia a partir d'ara
+
+
+ fa {0} hores
+
+
+ fa {0} hores
+
+
+ fa {0} hora
+
+
+ d'aquí {0} hores
+
+
+ d'aquí {0} hora
+
+
+ fa {0} minuts
+
+
+ fa {0} minuts
+
+
+ fa {0} minut
+
+
+ {0} dies a partir d'ara
+
+
+ fa {0} dia
+
+
+ d'aquí {0} hores
+
+
+ {0} minuts a partir d'ara
+
+
+ fa {0} segons
+
+
+ {0} segons a partir d'ara
+
+
+ {0} anys a partir d'ara
+
+
+ {0} dies
+
+
+ {0} dies
+
+
+ {0} minuts a partir d'ara
+
+
+ {0} hores
+
+
+ {0} minuts
+
+
+ {0} minuts
+
+
+ {0} segons
+
+
+ {0} mil·lisegons
+
+
+ fa {0} dies
+
+
+ fa {0} dies
+
+
+ {0} mil·lisegons
+
+
+ {0} minut a partir d'ara
+
+
+ {0} dies a partir d'ara
+
+
+ fa {0} mesos
+
+
+ {0} anys a partir d'ara
+
+
+ {0} any a partir d'ara
+
+
+ {0} dia
+
+
+ fa {0} any
+
+
+ {0} hores
+
+
+ {0} minut
+
+
+ {0} segons
+
+
+ {0} hora
+
+
+ fa {0} anys
+
+
+ {0} mil·lisegon
+
+
+ fa {0} anys
+
+
+ fa {0} mes
+
+
+ {0} mesos a partir d'ara
+
+
+ {0} mesos a partir d'ara
+
+
+ {0} mes a partir d'ara
+
+
+ fa {0} mesos
+
+
+ fa {0} segons
+
+
+ {0} segons a partir d'ara
+
+
+ {0} segon a partir d'ara
+
+
+ fa {0} segon
+
+
\ No newline at end of file