diff --git a/.gitignore b/.gitignore index 4c82a0308..771f57f1f 100644 --- a/.gitignore +++ b/.gitignore @@ -53,6 +53,9 @@ _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user +# JetBrains IDE +.idea/ + # JustCode is a .NET coding add-in .JustCode diff --git a/src/Humanizer.Tests.Shared/Localisation/vi/NumberToWordsTests.cs b/src/Humanizer.Tests.Shared/Localisation/vi/NumberToWordsTests.cs new file mode 100644 index 000000000..fce792d26 --- /dev/null +++ b/src/Humanizer.Tests.Shared/Localisation/vi/NumberToWordsTests.cs @@ -0,0 +1,78 @@ +using Xunit; + +namespace Humanizer.Tests.vi +{ + [UseCulture("vi")] + public class NumberToWordsTests + { + [Theory] + [InlineData(0, "không")] + [InlineData(1, "một")] + [InlineData(2, "hai")] + [InlineData(3, "ba")] + [InlineData(4, "bốn")] + [InlineData(5, "năm")] + [InlineData(6, "sáu")] + [InlineData(7, "bảy")] + [InlineData(8, "tám")] + [InlineData(9, "chín")] + [InlineData(10, "mười")] + [InlineData(11, "mười một")] + [InlineData(14, "mười bốn")] + [InlineData(15, "mười lăm")] + [InlineData(21, "hai mươi mốt")] + [InlineData(24, "hai mươi tư")] + [InlineData(25, "hai mươi lăm")] + [InlineData(50, "năm mươi")] + [InlineData(55, "năm mươi lăm")] + [InlineData(100, "một trăm")] + [InlineData(105, "một trăm linh năm")] + [InlineData(110, "một trăm mười")] + [InlineData(114, "một trăm mười bốn")] + [InlineData(115, "một trăm mười lăm")] + [InlineData(134, "một trăm ba mươi tư")] + [InlineData(500, "năm trăm")] + [InlineData(505, "năm trăm linh năm")] + [InlineData(555, "năm trăm năm mươi lăm")] + [InlineData(1000, "một nghìn")] + [InlineData(1005, "một nghìn linh năm")] + [InlineData(1115, "một nghìn một trăm mười lăm")] + [InlineData(10005, "mười nghìn linh năm")] + [InlineData(10115, "mười nghìn một trăm mười lăm")] + [InlineData(11115, "mười một nghìn một trăm mười lăm")] + [InlineData(30005, "ba mươi nghìn linh năm")] + [InlineData(100005, "một trăm nghìn linh năm")] + [InlineData(1000000, "một triệu")] + [InlineData(100001005, "một trăm triệu một nghìn linh năm")] + [InlineData(1000000000, "một tỉ")] + [InlineData(1111111111111111, "một triệu một trăm mười một nghìn một trăm mười một tỉ một trăm mười một triệu một trăm mười một nghìn một trăm mười một")] + [InlineData(5101101101101151101, "năm tỉ một trăm linh một triệu một trăm linh một nghìn một trăm linh một tỉ một trăm linh một triệu một trăm năm mươi mốt nghìn một trăm linh một")] + public void ToWords(long number, string expected) + { + Assert.Equal(expected, number.ToWords()); + } + + [Theory] + [InlineData(0, "thứ không")] + [InlineData(1, "thứ nhất")] + [InlineData(2, "thứ nhì")] + [InlineData(3, "thứ ba")] + [InlineData(4, "thứ tư")] + [InlineData(5, "thứ năm")] + [InlineData(6, "thứ sáu")] + [InlineData(7, "thứ bảy")] + [InlineData(8, "thứ tám")] + [InlineData(9, "thứ chín")] + [InlineData(10, "thứ mười")] + [InlineData(11, "thứ mười một")] + [InlineData(14, "thứ mười bốn")] + [InlineData(15, "thứ mười lăm")] + [InlineData(21, "thứ hai mươi mốt")] + [InlineData(24, "thứ hai mươi tư")] + [InlineData(25, "thứ hai mươi lăm")] + public void ToOrdinalWords(int number, string expected) + { + Assert.Equal(expected, number.ToOrdinalWords()); + } + } +} diff --git a/src/Humanizer.Tests.Shared/UseCultureAttribute.cs b/src/Humanizer.Tests.Shared/UseCultureAttribute.cs index 36ed1f504..2a8bcda4e 100644 --- a/src/Humanizer.Tests.Shared/UseCultureAttribute.cs +++ b/src/Humanizer.Tests.Shared/UseCultureAttribute.cs @@ -85,6 +85,4 @@ public override void After(MethodInfo methodUnderTest) CultureInfo.CurrentUICulture = originalUICulture; } } - - } diff --git a/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs b/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs index 16d6a5035..5064bdd9a 100644 --- a/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs +++ b/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs @@ -38,6 +38,7 @@ public NumberToWordsConverterRegistry() Register("sr", (culture) => new SerbianCyrlNumberToWordsConverter(culture)); Register("sr-Latn", (culture) => new SerbianNumberToWordsConverter(culture)); Register("nb", new NorwegianBokmalNumberToWordsConverter()); + Register("vi", new VietnameseNumberToWordsConverter()); Register("zh-CN", new ChineseNumberToWordsConverter()); Register("bg", new BolgarianNumberToWordsConverter()); } diff --git a/src/Humanizer/Localisation/NumberToWords/VietnameseNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/VietnameseNumberToWordsConverter.cs new file mode 100644 index 000000000..5fe6822f5 --- /dev/null +++ b/src/Humanizer/Localisation/NumberToWords/VietnameseNumberToWordsConverter.cs @@ -0,0 +1,132 @@ +using System; + +namespace Humanizer.Localisation.NumberToWords +{ + internal class VietnameseNumberToWordsConverter : GenderlessNumberToWordsConverter + { + private const int OneBillion = 1000000000; + private const int OneMillion = 1000000; + + private static readonly string[] NumberVerbalPairs = + { + "", "một", "hai", "ba", "bốn", "năm", "sáu", "bảy", "tám", "chín" + }; + + public override string Convert(long number) + { + return number == 0 + ? "không" + : ConvertImpl(number); + } + + public override string ConvertToOrdinal(int number) + { + return $"thứ {ConvertToOrdinalImpl(number)}"; + } + + private string ConvertToOrdinalImpl(int number) + { + switch (number) + { + case 1: + return "nhất"; + case 2: + return "nhì"; + case 4: + return "tư"; + default: + return Convert(number); + } + } + + private static string ConvertImpl(long number, bool hasTens = false, bool isGreaterThanOneHundred = false) + { + if (number >= OneBillion) + { + return string.Format( + "{0} tỉ {1}", + ConvertImpl(number / OneBillion), + ConvertImpl(number % OneBillion, isGreaterThanOneHundred: true) + ) + .TrimEnd(); + } + + if (number >= OneMillion) + { + return string.Format( + "{0} triệu {1}", + ConvertImpl(number / OneMillion), + ConvertImpl(number % OneMillion, isGreaterThanOneHundred: true) + ) + .TrimEnd(); + } + + if (number >= 1000) + { + return string.Format( + "{0} nghìn {1}", + ConvertImpl(number / 1000), + ConvertImpl(number % 1000, isGreaterThanOneHundred: true) + ) + .TrimEnd(); + } + + if (number >= 100) + { + return string.Format( + "{0} trăm {1}", + NumberVerbalPairs[number / 100], + ConvertImpl(number % 100, isGreaterThanOneHundred: true) + ) + .TrimEnd(); + } + + if (number >= 20) + { + return string.Format( + "{0} mươi {1}", + NumberVerbalPairs[number / 10], + ConvertImpl(number % 10, hasTens: true) + ) + .TrimEnd(); + } + + if (number == 14) + { + return "mười bốn"; + } + + if (number == 11) + { + return "mười một"; + } + + if (number >= 10) + { + return $"mười {ConvertImpl(number % 10, hasTens: true)}".TrimEnd(); + } + + if (number == 5 && hasTens) + { + return "lăm"; + } + + if (number == 4 && hasTens) + { + return "tư"; + } + + if (number == 1 && hasTens) + { + return "mốt"; + } + + if (number > 0 && isGreaterThanOneHundred && !hasTens) + { + return $"linh {NumberVerbalPairs[number]}"; + } + + return NumberVerbalPairs[number]; + } + } +}