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();