Currency to words mispelling problems

2019-08-18 01:05发布

问题:

This is the code below for converting numbers to words. What is the exactly problem ? Everything is great in English but in my country (Romania) there is different spelling let's make that clear in a few examples :

Ex. 1 - English, One dollar,One thousand, One hundred, Two hundred that's how you write Romanian, Un dollar, O mie, O suta, Doua Sute, Trei Sute, there is a plural changing on the words, In English you use One for everything almost in Romanian this changes and I don't know how to fix it this plural.Thank you

private static string[] _ones =
            {
                "",
                "unu",
                "doua",
                "trei",
                "patru",
                "cinci",
                "sase",
                "sapte",
                "opt",
                "noua"
            };

            private static string[] _teens =
            {
                "zece",
                "unsprezece",
                "doisprezece",
                "treisprezece",
                "paisprezece",
                "cincisprezece",
                "saisprezece",
                "saptisprezece",
                "optsprezece",
                "nouasprezece"
            };

            private static string[] _tens =
            {
                "",
                "zece",
                "douazeci",
                "treizeci",
                "patruzeci",
                "cincizeci",
                "saizeci",
                "saptezeci",
                "optzeci",
                "nouazeci"
            };

            // US Nnumbering:
            private static string[] _thousands =
            {
                "",
                "mie",
                "milion",
                "miliard",
                "trilion",
                "catralion"
            };
    string digits, temp;
                bool showThousands = false;
                bool allZeros = true;

                // Use StringBuilder to build result
                StringBuilder builder = new StringBuilder();
                // Convert integer portion of value to string
                digits = ((long)value).ToString();
                // Traverse characters in reverse order
                for (int i = digits.Length - 1; i >= 0; i--)
                {
                    int ndigit = (int)(digits[i] - '0');
                    int column = (digits.Length - (i + 1));

                    // Determine if ones, tens, or hundreds column
                    switch (column % 3)
                    {
                        case 0:        // Ones position
                            showThousands = true;
                            if (i == 0)
                            {
                                // First digit in number (last in loop)
                                temp = String.Format("{0} ", _ones[ndigit]);
                            }
                            else if (digits[i - 1] == '1')
                            {
                                // This digit is part of "teen" value
                                temp = String.Format("{0} ", _teens[ndigit]);
                                // Skip tens position
                                i--;
                            }
                            else if (ndigit != 0)
                            {
                                // Any non-zero digit
                                temp = String.Format("{0} ", _ones[ndigit]);
                            }
                            else
                            {
                                // This digit is zero. If digit in tens and hundreds
                                // column are also zero, don't show "thousands"
                                temp = String.Empty;
                                // Test for non-zero digit in this grouping
                                if (digits[i - 1] != '0' || (i > 1 && digits[i - 2] != '0'))
                                    showThousands = true;
                                else
                                    showThousands = false;
                            }

                            // Show "thousands" if non-zero in grouping
                            if (showThousands)
                            {
                                if (column > 0)
                                {
                                    temp = String.Format("{0}{1}{2}",
                                        temp,
                                        _thousands[column / 3],
                                        allZeros ? " " : ", ");
                                }
                                // Indicate non-zero digit encountered
                                allZeros = false;
                            }
                            builder.Insert(0, temp);
                            break;

                        case 1:        // Tens column
                            if (ndigit > 0)
                            {
                                temp = String.Format("{0}{1}",
                                    _tens[ndigit],
                                    (digits[i + 1] != '0') ? " si " : " ");
                                builder.Insert(0, temp);
                            }
                            break;

                        case 2:        // Hundreds column
                            if (ndigit > 0)
                            {
                                temp = String.Format("{0} sute ", _ones[ndigit]);
                                builder.Insert(0, temp);
                            }
                            break;
                    }
                }

                builder.AppendFormat("lei si {0:00} bani", (value - (long)value) * 100);


                // Capitalize first letter
                return String.Format("{0}{1}",
                    Char.ToUpper(builder[0]),
                    builder.ToString(1, builder.Length - 1));

回答1:

You've stated there are only 2 possible conditions for hundreds and thousands:

Hundreds: 1 = suta / more than 1 = sute

Thousands: 1 = mie / more than 1 = mii

In both cases, if singular, then "o" is used instead of "unu" to mean "one"

Based on this you can easily add the right conditions in the code.

I personally would refactor this code but since that's not the point here, I've included a solution for you.


Hundreds:

Instead of

case 2:        // Hundreds column
    if (ndigit > 0)
    {
        temp = String.Format("{0} sute ", _ones[ndigit]);
        builder.Insert(0, temp);
    }

Add the condition to check on digit and use sute or suta:

case 2:        // Hundreds column
    if (ndigit > 0)
    {
        temp = String.Format("{0} {1} ", ndigit == 1 ? "o" : _ones[ndigit], ndigit == 1 ? "suta" : "sute");
        builder.Insert(0, temp);
    }

Thousands

Instead of ...

// Show "thousands" if non-zero in grouping
if (showThousands)
{
    if (column > 0)
    {
        temp = String.Format("{0}{1}{2}",
            temp,
            _thousands[column / 3],
            allZeros ? " " : ", ");
    }
    // Indicate non-zero digit encountered
    allZeros = false;
}

Should be:

// Show "thousands" if non-zero in grouping
if (showThousands)
{
    if (column > 0)
    {
        bool isFirstThoussand = _thousands[column / 3] == _thousands[1] && ndigit == 1;

        temp = String.Format("{0}{1}{2}",
        isFirstThoussand ? "o " : temp,
        isFirstThoussand ? _thousands[1] : "mii",
        allZeros ? " " : ", ");
    }
    // Indicate non-zero digit encountered
    allZeros = false;
}

...

This will yield the result desired based on your current parameters.


Here's your whole code (with the updates from this answer) and some test stuff:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace RomanianNumberToWords
{
    class Program
    {
        private static string[] _ones =
        {
            "",
            "unu",
            "doua",
            "trei",
            "patru",
            "cinci",
            "sase",
            "sapte",
            "opt",
            "noua"
        };

        private static string[] _teens =
        {
            "zece",
            "unsprezece",
            "doisprezece",
            "treisprezece",
            "paisprezece",
            "cincisprezece",
            "saisprezece",
            "saptisprezece",
            "optsprezece",
            "nouasprezece"
        };

        private static string[] _tens =
        {
            "",
            "zece",
            "douazeci",
            "treizeci",
            "patruzeci",
            "cincizeci",
            "saizeci",
            "saptezeci",
            "optzeci",
            "nouazeci"
        };

        // US Nnumbering:
        private static string[] _thousands =
        {
            "",
            "mie",
            "milion",
            "miliard",
            "trilion",
            "catralion"
        };


        static string MakeWordFromNumbers(decimal value)
        {
            string digits, temp;
            bool showThousands = false;
            bool allZeros = true;

            // Use StringBuilder to build result
            StringBuilder builder = new StringBuilder();
            // Convert integer portion of value to string
            digits = ((long)value).ToString();
            // Traverse characters in reverse order
            for (int i = digits.Length - 1; i >= 0; i--)
            {
                int ndigit = (int)(digits[i] - '0');
                int column = (digits.Length - (i + 1));

                // Determine if ones, tens, or hundreds column
                switch (column % 3)
                {
                    case 0:        // Ones position
                        showThousands = true;
                        if (i == 0)
                        {
                            // First digit in number (last in loop)
                            temp = String.Format("{0} ", _ones[ndigit]);
                        }
                        else if (digits[i - 1] == '1')
                        {
                            // This digit is part of "teen" value
                            temp = String.Format("{0} ", _teens[ndigit]);
                            // Skip tens position
                            i--;
                        }
                        else if (ndigit != 0)
                        {
                            // Any non-zero digit
                            temp = String.Format("{0} ", _ones[ndigit]);
                        }
                        else
                        {
                            // This digit is zero. If digit in tens and hundreds
                            // column are also zero, don't show "thousands"
                            temp = String.Empty;
                            // Test for non-zero digit in this grouping
                            if (digits[i - 1] != '0' || (i > 1 && digits[i - 2] != '0'))
                                showThousands = true;
                            else
                                showThousands = false;
                        }

                        // Show "thousands" if non-zero in grouping
                        if (showThousands)
                        {
                            if (column > 0)
                            {
                                bool isFirstThoussand = _thousands[column / 3] == _thousands[1] && ndigit == 1;

                                temp = String.Format("{0}{1}{2}",
                                    isFirstThoussand ? "o " : temp,
                                    isFirstThoussand ? _thousands[1] : "mii",
                                    allZeros ? " " : ", ");
                            }
                            // Indicate non-zero digit encountered
                            allZeros = false;
                        }
                        builder.Insert(0, temp);
                        break;

                    case 1:        // Tens column
                        if (ndigit > 0)
                        {
                            temp = String.Format("{0}{1}",
                                _tens[ndigit],
                                (digits[i + 1] != '0') ? " si " : " ");
                            builder.Insert(0, temp);
                        }
                        break;

                    case 2:        // Hundreds column
                        if (ndigit > 0)
                        {
                            temp = String.Format("{0} {1} ", ndigit == 1 ? "o" : _ones[ndigit], ndigit == 1 ? "suta" : "sute");
                            builder.Insert(0, temp);
                        }
                        break;
                }
            }

            // You always need "lei" right?
            builder.AppendFormat("lei");

            // This code simply divides the decimal value by 1; and only adds "si NN bani" if there's a remainder
            if (Decimal.Remainder(value, 1) > 0) {
                builder.AppendFormat(" si {0:00} bani", (value - (long)value) * 100);
            }

            // Capitalize first letter
            return String.Format("{0}{1}",
                Char.ToUpper(builder[0]),
                builder.ToString(1, builder.Length - 1));
        }

        static void Main(string[] args)
        {
            Console.WriteLine(MakeWordFromNumbers(1127.00M));
            Console.WriteLine(MakeWordFromNumbers(2227.00M));

            Console.WriteLine(MakeWordFromNumbers(127.00M));
            Console.WriteLine(MakeWordFromNumbers(227.00M));

            Console.ReadKey();
        }

    }    
}


回答2:

When preparing an application for localisation (the act of translating and applying translations to an application), you will involve yourself in the process of globalisation.

Globalisation is the process of making our application play nicely with translations. There are a few guidelines to remember.

  1. DO Use resx (Resource) files
  2. DO NOT cut corners by assembling sentences from partial strings (as you are trying to do). You will always hit grammar problems! Consider German questions where the sentence is constructed nearly backwards.
  3. DO cut corners by using grammar free constructs like numerals (1, 2, 3, 14, 1,000,000) and abbreviations like (1mil, 1m, etc). GHenerally, these are universal, especially in scenarios where there is an SI abbreviation to use.


回答3:

You need to track the [various] plural forms of numbers.

Most languages has just two grammatical numbers: singular and plural and only differentiate between 1 and many when talking about things.

Some languages, Romanian apparently included, have three grammatical numbers: singular, dual and plural, and so differentiate between 1, 2 and many of something.

English is a little weird because in talking about numbers we don't generally apply grammatical number to the words: we say "one hundred" , "two hundred" and "three hundred". However, in keeping with our long tradition of inconsistent usage, there are certain contexts and scenarios in which it is permissible, and one might, say something like "7 billions dead". But I digress.

As a result, the "usual" algorithm for converting numbers to words (3,792 to "three thousand, seven hundred and ninety-two") doesn't really care about the grammatical number of the names of the digit groupings or the values.

For languages that do, you need something like a 2-dimensional matrix with the rows mapping the part's value to the word and the columns providing the proper word form for the specified grammatical number (singular, dual, plural).