8

Normally I would use string.Format() to get a formatted string.

But I got the requirement that all amounts should be printed as text and not digits.

i.e. something like:

Format(3000, "us"); // => Resulting text: "three thousand dollars"

Are there a .NET library which can handle that (russian is mandatory)?

jgauffin
  • 99,844
  • 45
  • 235
  • 372
  • There is nothing built into the .net Framework for that. I'm pretty sure there are third party libraries, I just don't know any right now. – Michael Stum Nov 01 '12 at 08:27
  • 2
    A similar question, sadly without any multilanguage-answers - if you know the rules of the language (some are not as straight forward as english, e.g. French has some special cases) you can built your own from that. http://stackoverflow.com/questions/794663/ – Michael Stum Nov 01 '12 at 08:29
  • Maybe `CultureInfo.CurrentCulture.NumberFormat.CurrencySymbol` – L.B Nov 01 '12 at 08:29
  • check this question, it will use for your requirement... http://stackoverflow.com/questions/2093423/best-way-to-convert-decimal-or-string-to-currency-in-c – Manikandan Sethuraju Nov 01 '12 at 09:08
  • I do not think there is such open-source library for russian language somewhere. You need a russian freelancer, which will implement this for you. – SergeyS Jan 04 '13 at 08:27
  • Check this out - http://robertgreiner.com/2011/08/numbertext-converting-numbers-into-words-in-csharp/ It's in english, but maybe you can switch this into Russian? – Asunez Oct 05 '15 at 08:05

5 Answers5

2

Are there a .NET library which can handle that (russian is mandatory)?

Short answer:No.

You have to write your own, I would recommend you to have a look at the following though: converting numbers in to words C# , How can I convert an integer into its verbal representation? and as one of these says, have a look at project Euler problem number 17 and use it for wider google searches.

On top of that you have the issue of currency names, should 'us' denote the currency or the language or even both? Is the canadian dollar different from the american dollar?

For example this is built in:

// Gives USD
var ISOCode = System.Globalization.RegionInfo.RegionInfo("US").ISOCurrencySymbol
// Gives $
var symbol = System.Globalization.RegionInfo.RegionInfo("US").CurrencySymbol
// Gives USD Dollar (i believe :))
var nameUSDENG = System.Globalization.RegionInfo.RegionInfo("US").CurrencyEnglishName
// Gives Svensk Krona
var nameSEKSWE = System.Globalization.RegionInfo.RegionInfo("SE").CurrencyNativeName
// Gives Swedish Krona
var nameSEKENG = System.Globalization.RegionInfo.RegionInfo("SE").CurrencyEnglishName

I would suggest you to start there and see where the path takes you.

Community
  • 1
  • 1
flindeberg
  • 4,887
  • 1
  • 24
  • 37
  • With .NET library I do not refer to the .NET framework but any external library (commercial or open source). I've found one that it didn't support russian – jgauffin Nov 01 '12 at 14:24
  • It should be pretty easy to roll your own with the info provided above. That way you have more flexibility. – Steve Oct 05 '15 at 08:10
  • There are libraries that can do it, as you can see in my answer. – JotaBe Oct 05 '15 at 08:44
2

There is a mature library for doing with you needd. It's called humanizer.

You can see what it is about here: NuGet Package of the Week: Humanizer makes .NET data types more human

The open source project is on GitHub

As you can see it does what you need and many more things. And it has a lot of localizations.

Some examples:

1.ToWords() => "one"
10.ToWords() => "ten"
11.ToWords() => "eleven"
122.ToWords() => "one hundred and twenty-two"
3501.ToWords() => "three thousand five hundred and one"

You can also pass a second argument, GrammaticalGender, to ToWords to specify which gender the number should be outputted in.

1.ToWords(GrammaticalGender.Masculine) => "один"
1.ToWords(GrammaticalGender.Feminine) => "одна"
1.ToWords(GrammaticalGender.Neuter) => "одно"
JotaBe
  • 38,030
  • 8
  • 98
  • 117
1

I've had a look around but I couldn't find a proper version to convert to a English currency, so I combined a few into this solution:

public static class NumberExtensions
{
    public static String toCurrency(this double number, String major, String minor, String endStr, int minorLength)
    {
        return ((decimal)number).toCurrency(major, minor, endStr, minorLength);
    }

    public static String toCurrency(this decimal number, String major, String minor, String endStr, int minorLength)
    {

        if (String.IsNullOrWhiteSpace(major)) throw new Exception("Must specify major currency");
        if (String.IsNullOrWhiteSpace(minor)) throw new Exception("Must specify minor currency");

        String numb = number.ToString();
        String val = "", wholeNo = numb, points = "", andStr = major, pointStr = "";
        int decimalPlace = numb.IndexOf(".");
        if (decimalPlace > 0)
        {
            wholeNo = numb.Substring(0, decimalPlace);
            points = numb.Substring(decimalPlace + 1);
            if (points.Length > minorLength) throw new Exception("Incorrect format");
            if (Convert.ToInt32(points) > 0)
            {
                andStr = major + " and";
                endStr = minor + " " + endStr;
                pointStr = Int32.Parse(points.Length == 1 ? points + "0" : points).toWords();
            }
        }
        if (String.IsNullOrWhiteSpace(pointStr))
        {
            if (endStr == minor + " " || endStr == null)
            {
                val = String.Format("{0} {1}", Int32.Parse(wholeNo).toWords().Trim(), andStr.Trim());
            }
            else
            {
                val = String.Format("{0} {1} {2}", Int32.Parse(wholeNo).toWords().Trim(), andStr.Trim(), endStr.Trim());
            }
        }
        else
        {
            val = String.Format("{0} {1} {2} {3}", Int32.Parse(wholeNo).toWords().Trim(), andStr.Trim(), pointStr.Trim(), endStr.Trim());
        }
        return val;
    }

    public static string toWords(this int number)
    {
        if (number == 0)
            return "Zero";

        if (number < 0)
            return "minus " + Math.Abs(number).toWords();

        string words = "";

        if ((number / 1000000) > 0)
        {
            words += (number / 1000000).toWords().TrimEnd() + " Million ";
            number %= 1000000;
        }

        if ((number / 1000) > 0)
        {
            words += (number / 1000).toWords().TrimEnd() + " Thousand ";
            number %= 1000;
        }

        if ((number / 100) > 0)
        {
            words += (number / 100).toWords().TrimEnd() + " Hundred ";
            number %= 100;
        }

        if (number > 0)
        {
            if (words != "")
                words += "and ";

            var unitsMap = new[] { "Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen" };
            var tensMap = new[] { "Zero", "Ten", "Twenty", "Thirty", "Forty", "Fifty", "Sixty", "Seventy", "Eighty", "Ninety" };

            if (number < 20)
                words += unitsMap[number];
            else
            {
                words += tensMap[number / 10];
                if ((number % 10) > 0)
                    words += "-" + unitsMap[number % 10];
            }
        }

        return words;
    }
}

And here is the unit test:

[TestFixture]
public class NumbericExtensionMethodsTests
    : TestBase
{
    [Test]
    [TestCase(0.0, null, null, null, 2, ExpectedException = typeof(Exception))]
    [TestCase(0.0, "", null, null, 2, ExpectedException = typeof(Exception))]
    [TestCase(0.0, null, "", null, 2, ExpectedException = typeof(Exception))]
    [TestCase(0.0, "", "", null, 2, ExpectedException = typeof(Exception))]
    [TestCase(0.001, "Rand", "Cent", null, 2, ExpectedException = typeof(Exception))]
    [TestCase(0.001, "Rand", "Cent", null, 3, Result = "Zero Rand and One Cent")]
    [TestCase(0, "Rand", "Cent", null, 2, Result = "Zero Rand")]
    [TestCase(0.0, "Rand", "Cent", null, 2, Result = "Zero Rand")]
    [TestCase(0.01, "Rand", "Cent", null, 2, Result = "Zero Rand and One Cent")]
    [TestCase(0.1, "Rand", "Cent", null, 2, Result = "Zero Rand and Ten Cent")]
    [TestCase(0.10, "Rand", "Cent", null, 2, Result = "Zero Rand and Ten Cent")]
    [TestCase(0.10, "Rand", "Cent", "only", 2, Result = "Zero Rand and Ten Cent only")]
    [TestCase(1.10, "Rand", "Cent", "only", 2, Result = "One Rand and Ten Cent only")]
    [TestCase(1.0, "Rand", "Cent", "only", 2, Result = "One Rand only")]
    [TestCase(123312.32, "Rand", "Cent", null, 2, Result = "One Hundred and Twenty-Three Thousand Three Hundred and Twelve Rand and Thirty-Two Cent")]
    public string NumberToCurrency(decimal number, string major, string minor, string endStr, int minorLength)
    {
        return number.toCurrency(major, minor, endStr, minorLength);
    }
    
    [Test]
    [TestCase(0, Result = "Zero")]
    [TestCase(1, Result = "One")]
    [TestCase(2, Result = "Two")]
    [TestCase(3, Result = "Three")]
    [TestCase(4, Result = "Four")]
    [TestCase(5, Result = "Five")]
    [TestCase(6, Result = "Six")]
    [TestCase(7, Result = "Seven")]
    [TestCase(8, Result = "Eight")]
    [TestCase(9, Result = "Nine")]

    [TestCase(10, Result = "Ten")]
    [TestCase(11, Result = "Eleven")]
    [TestCase(12, Result = "Twelve")]
    [TestCase(13, Result = "Thirteen")]
    [TestCase(14, Result = "Fourteen")]
    [TestCase(15, Result = "Fifteen")]
    [TestCase(16, Result = "Sixteen")]
    [TestCase(17, Result = "Seventeen")]
    [TestCase(18, Result = "Eighteen")]
    [TestCase(19, Result = "Nineteen")]
    [TestCase(20, Result = "Twenty")]
    [TestCase(30, Result = "Thirty")]
    [TestCase(40, Result = "Forty")]
    [TestCase(50, Result = "Fifty")]
    [TestCase(60, Result = "Sixty")]
    [TestCase(70, Result = "Seventy")]
    [TestCase(80, Result = "Eighty")]
    [TestCase(90, Result = "Ninety")]
    [TestCase(99, Result = "Ninety-Nine")]

    [TestCase(100, Result = "One Hundred")]
    [TestCase(101, Result = "One Hundred and One")]
    [TestCase(111, Result = "One Hundred and Eleven")]
    [TestCase(121, Result = "One Hundred and Twenty-One")]

    [TestCase(1000, Result = "One Thousand")]
    [TestCase(1001, Result = "One Thousand and One")]
    [TestCase(1011, Result = "One Thousand and Eleven")]
    [TestCase(1021, Result = "One Thousand and Twenty-One")]
    [TestCase(1100, Result = "One Thousand One Hundred")]
    [TestCase(1101, Result = "One Thousand One Hundred and One")]
    [TestCase(1111, Result = "One Thousand One Hundred and Eleven")]
    [TestCase(1121, Result = "One Thousand One Hundred and Twenty-One")]

    [TestCase(12000, Result = "Twelve Thousand")]
    [TestCase(12001, Result = "Twelve Thousand and One")]
    [TestCase(12011, Result = "Twelve Thousand and Eleven")]
    [TestCase(12021, Result = "Twelve Thousand and Twenty-One")]
    [TestCase(12100, Result = "Twelve Thousand One Hundred")]
    [TestCase(12101, Result = "Twelve Thousand One Hundred and One")]
    [TestCase(12111, Result = "Twelve Thousand One Hundred and Eleven")]
    [TestCase(12121, Result = "Twelve Thousand One Hundred and Twenty-One")]
    [TestCase(60000, Result = "Sixty Thousand")]
    [TestCase(60001, Result = "Sixty Thousand and One")]
    [TestCase(60011, Result = "Sixty Thousand and Eleven")]
    [TestCase(60021, Result = "Sixty Thousand and Twenty-One")]
    [TestCase(60100, Result = "Sixty Thousand One Hundred")]
    [TestCase(60101, Result = "Sixty Thousand One Hundred and One")]
    [TestCase(60111, Result = "Sixty Thousand One Hundred and Eleven")]
    [TestCase(60121, Result = "Sixty Thousand One Hundred and Twenty-One")]
    [TestCase(61000, Result = "Sixty-One Thousand")]
    [TestCase(61001, Result = "Sixty-One Thousand and One")]
    [TestCase(61011, Result = "Sixty-One Thousand and Eleven")]
    [TestCase(61021, Result = "Sixty-One Thousand and Twenty-One")]
    [TestCase(61100, Result = "Sixty-One Thousand One Hundred")]
    [TestCase(61101, Result = "Sixty-One Thousand One Hundred and One")]
    [TestCase(61111, Result = "Sixty-One Thousand One Hundred and Eleven")]
    [TestCase(61121, Result = "Sixty-One Thousand One Hundred and Twenty-One")]
    
    [TestCase(100000, Result = "One Hundred Thousand")]
    [TestCase(100001, Result = "One Hundred Thousand and One")]
    [TestCase(100011, Result = "One Hundred Thousand and Eleven")]
    [TestCase(100021, Result = "One Hundred Thousand and Twenty-One")]
    [TestCase(100100, Result = "One Hundred Thousand One Hundred")]
    [TestCase(100101, Result = "One Hundred Thousand One Hundred and One")]
    [TestCase(100111, Result = "One Hundred Thousand One Hundred and Eleven")]
    [TestCase(100121, Result = "One Hundred Thousand One Hundred and Twenty-One")]
    [TestCase(101000, Result = "One Hundred and One Thousand")]
    [TestCase(101001, Result = "One Hundred and One Thousand and One")]
    [TestCase(101011, Result = "One Hundred and One Thousand and Eleven")]
    [TestCase(101021, Result = "One Hundred and One Thousand and Twenty-One")]
    [TestCase(101100, Result = "One Hundred and One Thousand One Hundred")]
    [TestCase(101101, Result = "One Hundred and One Thousand One Hundred and One")]
    [TestCase(101111, Result = "One Hundred and One Thousand One Hundred and Eleven")]
    [TestCase(101121, Result = "One Hundred and One Thousand One Hundred and Twenty-One")]
    [TestCase(112000, Result = "One Hundred and Twelve Thousand")]
    [TestCase(112001, Result = "One Hundred and Twelve Thousand and One")]
    [TestCase(112011, Result = "One Hundred and Twelve Thousand and Eleven")]
    [TestCase(112021, Result = "One Hundred and Twelve Thousand and Twenty-One")]
    [TestCase(112100, Result = "One Hundred and Twelve Thousand One Hundred")]
    [TestCase(112101, Result = "One Hundred and Twelve Thousand One Hundred and One")]
    [TestCase(112111, Result = "One Hundred and Twelve Thousand One Hundred and Eleven")]
    [TestCase(112121, Result = "One Hundred and Twelve Thousand One Hundred and Twenty-One")]
    [TestCase(160000, Result = "One Hundred and Sixty Thousand")]
    [TestCase(160001, Result = "One Hundred and Sixty Thousand and One")]
    [TestCase(160011, Result = "One Hundred and Sixty Thousand and Eleven")]
    [TestCase(160021, Result = "One Hundred and Sixty Thousand and Twenty-One")]
    [TestCase(160100, Result = "One Hundred and Sixty Thousand One Hundred")]
    [TestCase(160101, Result = "One Hundred and Sixty Thousand One Hundred and One")]
    [TestCase(160111, Result = "One Hundred and Sixty Thousand One Hundred and Eleven")]
    [TestCase(160121, Result = "One Hundred and Sixty Thousand One Hundred and Twenty-One")]
    [TestCase(161000, Result = "One Hundred and Sixty-One Thousand")]
    [TestCase(161001, Result = "One Hundred and Sixty-One Thousand and One")]
    [TestCase(161011, Result = "One Hundred and Sixty-One Thousand and Eleven")]
    [TestCase(161021, Result = "One Hundred and Sixty-One Thousand and Twenty-One")]
    [TestCase(161100, Result = "One Hundred and Sixty-One Thousand One Hundred")]
    [TestCase(161101, Result = "One Hundred and Sixty-One Thousand One Hundred and One")]
    [TestCase(161111, Result = "One Hundred and Sixty-One Thousand One Hundred and Eleven")]
    [TestCase(161121, Result = "One Hundred and Sixty-One Thousand One Hundred and Twenty-One")]
    [TestCase(1000000, Result = "One Million")]
    [TestCase(Int32.MaxValue, Result = "Two Thousand One Hundred and Forty-Seven Million Four Hundred and Eighty-Three Thousand Six Hundred and Forty-Seven")]
    /*Assuming Millions will work if Thousands worked*/
    public string IntToWords(int number)
    {
        return number.toWords().Trim();
    }
}

Enjoy this works perfectly :D

Credits

Community
  • 1
  • 1
Aldracor
  • 2,133
  • 1
  • 19
  • 27
1

I haven't tested it out, but maybe this will fix your problem?

Nuget Package

It says it supports Russian language too.

From the package description:

Number To Text Converter. Supported Languages English Russian Spanish Turkish

Usage:

var number = 123456.78
var moneyText = number.ToText("usd", "en");

var number = 123456.78;
var moneyText = number.ToText(Nut.Currency.USD, Nut.Language.English);

var number = 123456.78;
var options = new Nut.Options {
    MainUnitNotConvertedToText = true,
    SubUnitNotConvertedToText = true,
    MainUnitFirstCharUpper = true,
    SubUnitFirstCharUpper = true,
    CurrencyFirstCharUpper = true,
    SubUnitZeroNotDisplayed = true
}
var moneyText = number.ToText(Nut.Currency.USD, Nut.Language.English, options);
Asunez
  • 2,327
  • 1
  • 23
  • 46
0

For Russian language you can use my library: https://github.com/nick-buhro/NumToWords
It is also available on nuget: https://www.nuget.org/packages/NickBuhro.NumToWords

Example:

var unit = new UnitOfMeasure(Gender.Masculine, "доллар", "доллара", "долларов");
var text = RussianConverter.Format(3000, unit);
Console.WriteLine(text);
// Output: три тысячи долларов
Nick Buhro
  • 71
  • 1
  • 4