diff --git a/release_notes.md b/release_notes.md index 314c35d10..fbf856b45 100644 --- a/release_notes.md +++ b/release_notes.md @@ -1,5 +1,7 @@ ###In Development - [#186](https://github.com/Mehdik/Humanizer/pull/186): Refactored 'ToOrdinalWords` to use existing `NumberToWordsExtension` to prepare for Ordinal localization. + - [#194](https://github.com/MehdiK/Humanizer/pull/194): Added pt-BR NumberToWords localisation + [Commits](https://github.com/MehdiK/Humanizer/compare/v1.20.2...master) ###v1.20.2 - 2014-04-11 diff --git a/src/Humanizer.Tests/Humanizer.Tests.csproj b/src/Humanizer.Tests/Humanizer.Tests.csproj index b36032eae..0f2ef0d8e 100644 --- a/src/Humanizer.Tests/Humanizer.Tests.csproj +++ b/src/Humanizer.Tests/Humanizer.Tests.csproj @@ -88,9 +88,11 @@ + + diff --git a/src/Humanizer.Tests/Localisation/NumberToWordsFactoryTests.cs b/src/Humanizer.Tests/Localisation/NumberToWordsFactoryTests.cs new file mode 100644 index 000000000..f97b7b668 --- /dev/null +++ b/src/Humanizer.Tests/Localisation/NumberToWordsFactoryTests.cs @@ -0,0 +1,46 @@ +using Xunit; + +namespace Humanizer.Tests.Localisation +{ + public class NumberToWordsFactoryTests + { + [Fact] + public void CanGetTwoLetterIsoLanguageSpecificFactory() + { + using (new AmbientCulture("ar")) + { + string result = 1000000000.ToWords(); + Assert.NotEqual("1000000000", result); + } + } + + [Fact] + public void CanGetRfcStandardLanguageSpecificFactory() + { + using (new AmbientCulture("pt-BR")) + { + string result = 1000000000.ToWords(); + Assert.NotEqual("1000000000", result); + } + } + + [Fact] + public void CanGetCorrectRfcStandardLanguageSpecificFactory() + { + string resultPtBr; + const int number = 1000000000; + using (new AmbientCulture("pt-BR")) + { + resultPtBr = number.ToWords(); + } + + string resultPtPt; + using (new AmbientCulture("pt-PT")) + { + resultPtPt = number.ToWords(); + } + + Assert.NotEqual(resultPtBr, resultPtPt); + } + } +} diff --git a/src/Humanizer.Tests/Localisation/pt-BR/NumberToWordsTests.cs b/src/Humanizer.Tests/Localisation/pt-BR/NumberToWordsTests.cs new file mode 100644 index 000000000..4ede90565 --- /dev/null +++ b/src/Humanizer.Tests/Localisation/pt-BR/NumberToWordsTests.cs @@ -0,0 +1,47 @@ +using Xunit; +using Xunit.Extensions; + +namespace Humanizer.Tests.Localisation.ptBR +{ + public class NumberToWordsTests : AmbientCulture + { + public NumberToWordsTests() : base("pt-BR") { } + + [Theory] + [InlineData(1, "um")] + [InlineData(10, "dez")] + [InlineData(11, "onze")] + [InlineData(122, "cento e vinte e dois")] + [InlineData(3501, "três mil quinhentos e um")] + [InlineData(100, "cem")] + [InlineData(1000, "mil")] + [InlineData(100000, "cem mil")] + [InlineData(1000000, "um milhão")] + [InlineData(10000000, "dez milhões")] + [InlineData(100000000, "cem milhões")] + [InlineData(1000000000, "um bilhão")] + [InlineData(111, "cento e onze")] + [InlineData(1111, "mil cento e onze")] + [InlineData(111111, "cento e onze mil cento e onze")] + [InlineData(1111111, "um milhão cento e onze mil cento e onze")] + [InlineData(11111111, "onze milhões cento e onze mil cento e onze")] + [InlineData(111111111, "cento e onze milhões cento e onze mil cento e onze")] + [InlineData(1111111111, "um bilhão cento e onze milhões cento e onze mil cento e onze")] + [InlineData(123, "cento e vinte e três")] + [InlineData(1234, "mil duzentos e trinta e quatro")] + [InlineData(8100, "oito mil e cem")] + [InlineData(12345, "doze mil trezentos e quarenta e cinco")] + [InlineData(123456, "cento e vinte e três mil quatrocentos e cinquenta e seis")] + [InlineData(1234567, "um milhão duzentos e trinta e quatro mil quinhentos e sessenta e sete")] + [InlineData(12345678, "doze milhões trezentos e quarenta e cinco mil seiscentos e setenta e oito")] + [InlineData(123456789, "cento e vinte e três milhões quatrocentos e cinquenta e seis mil setecentos e oitenta e nove")] + [InlineData(1234567890, "um bilhão duzentos e trinta e quatro milhões quinhentos e sessenta e sete mil oitocentos e noventa")] + [InlineData(1999, "mil novecentos e noventa e nove")] + [InlineData(2014, "dois mil e quatorze")] + [InlineData(2048, "dois mil e quarenta e oito")] + public void ToWordsPortuguese(int number, string expected) + { + Assert.Equal(expected, number.ToWords()); + } + } +} diff --git a/src/Humanizer/Humanizer.csproj b/src/Humanizer/Humanizer.csproj index 42f8ae754..8bfdef92a 100644 --- a/src/Humanizer/Humanizer.csproj +++ b/src/Humanizer/Humanizer.csproj @@ -65,6 +65,7 @@ Humanizer.snk + diff --git a/src/Humanizer/Localisation/NumberToWords/BrazilianPortugueseNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/BrazilianPortugueseNumberToWordsConverter.cs new file mode 100644 index 000000000..b3567f40c --- /dev/null +++ b/src/Humanizer/Localisation/NumberToWords/BrazilianPortugueseNumberToWordsConverter.cs @@ -0,0 +1,100 @@ +using Humanizer.Localisation.NumberToWords; +using System; +using System.Collections.Generic; + +namespace Humanizer.Localisation.NumberToWords +{ + internal class BrazilianPortugueseNumberToWordsConverter : DefaultNumberToWordsConverter + { + private static readonly string[] portugueseUnitsMap = { "zero", "um", "dois", "três", "quatro", "cinco", "seis", "sete", "oito", "nove", "dez", "onze", "doze", "treze", "quatorze", "quinze", "dezesseis", "dezessete", "dezoito", "dezenove" }; + private static readonly string[] portugueseTensMap = { "zero", "dez", "vinte", "trinta", "quarenta", "cinquenta", "sessenta", "setenta", "oitenta", "noventa" }; + private static readonly string[] portugueseHundredsMap = { "zero", "cento", "duzentos", "trezentos", "quatrocentos", "quinhentos", "seiscentos", "setecentos", "oitocentos", "novecentos" }; + + public override string Convert(int number) + { + if (number == 0) + return "zero"; + + if (number < 0) + return string.Format("menos {0}", Convert(Math.Abs(number))); + + var parts = new List(); + + if ((number / 1000000000) > 0) + { + if (number / 1000000000 > 2) + { + parts.Add(string.Format("{0} bilhões", Convert(number / 1000000000))); + } + else + { + parts.Add(string.Format("{0} bilhão", Convert(number / 1000000000))); + } + number %= 1000000000; + } + + if ((number / 1000000) > 0) + { + if (number / 1000000 > 2) + { + parts.Add(string.Format("{0} milhões", Convert(number / 1000000))); + } + else + { + parts.Add(string.Format("{0} milhão", Convert(number / 1000000))); + } + number %= 1000000; + } + + if ((number / 1000) > 0) + { + if (number / 1000 == 1) + parts.Add("mil"); + else + parts.Add(string.Format("{0} mil", Convert(number / 1000))); + + number %= 1000; + } + + if ((number / 100) > 0) + { + if (number == 100) + { + if (parts.Count > 0) + { + parts.Add("e cem"); + } + else + { + parts.Add("cem"); + } + } + else + { + parts.Add(portugueseHundredsMap[(number / 100)]); + } + + number %= 100; + } + + if (number > 0) + { + if (parts.Count != 0) + parts.Add("e"); + + if (number < 20) + parts.Add(portugueseUnitsMap[number]); + else + { + var lastPart = portugueseTensMap[number / 10]; + if ((number % 10) > 0) + lastPart += string.Format(" e {0}", portugueseUnitsMap[number % 10]); + + parts.Add(lastPart); + } + } + + return string.Join(" ", parts.ToArray()); + } + } +} \ No newline at end of file diff --git a/src/Humanizer/NumberToWordsExtension.cs b/src/Humanizer/NumberToWordsExtension.cs index 3c980fdf4..371e33d6d 100644 --- a/src/Humanizer/NumberToWordsExtension.cs +++ b/src/Humanizer/NumberToWordsExtension.cs @@ -1,9 +1,10 @@ -using Humanizer.Localisation.NumberToWords; using System; using System.Collections.Generic; using System.Globalization; +using Humanizer.Localisation.NumberToWords; -namespace Humanizer { +namespace Humanizer +{ /// /// Transform a number into words; e.g. 1 => one /// @@ -15,7 +16,8 @@ public static class NumberToWordsExtension { { "ar", () => new ArabicNumberToWordsConverter() }, { "fa", () => new FarsiNumberToWordsConverter() }, { "es", () => new SpanishNumberToWordsConverter() }, - { "pl", () => new PolishNumberToWordsConverter() } + { "pl", () => new PolishNumberToWordsConverter() }, + { "pt-BR", () => new BrazilianPortugueseNumberToWordsConverter() } }; /// @@ -43,6 +45,10 @@ private static INumberToWordsConverter Converter get { Func converterFactory; + + if (ConverterFactories.TryGetValue(CultureInfo.CurrentUICulture.Name, out converterFactory)) + return converterFactory(); + if (ConverterFactories.TryGetValue(CultureInfo.CurrentUICulture.TwoLetterISOLanguageName, out converterFactory)) return converterFactory();