codingutf8 2023 the fairseq s microsoft research and the huggingface inc team licensed under the apache license version 2 0 the license you may not use this file except in compliance with the license you may obtain a copy of the license at http www apache orglicenseslicense2 0 unless required by applicable law or agreed to in writing software distributed under the license is distributed on an as is basis without warranties or conditions of any kind either express or implied see the license for the specific language governing permissions and limitations under the license number normalizer class for speecht5 import re class englishnumbernormalizer def initself self ones one two three four five six seven eight nine self teens eleven twelve thirteen fourteen fifteen sixteen seventeen eighteen nineteen self tens ten twenty thirty forty fifty sixty seventy eighty ninety self thousands thousand million billion trillion quadrillion quintillion sextillion septillion octillion nonillion decillion define a dictionary to map currency symbols to their names top most traded currencies according to https en wikipedia orgwikitemplate mosttradedcurrencies self currencysymbols dollars euros pounds cents japanese yen saudi riyal indian rupees russian rubles thai baht turkish liras ukrainian hryvnia swiss francs costa rican colon philippine peso israeli shekels mongolian tgrg south korean won nigerian naira vietnamese ng def spellnumberself num if num 0 return zero parts for i in range0 lenself thousands if num 1000 0 part hundreds num 1000 100 tensunits num 100 if hundreds 0 part self oneshundreds hundred if tensunits 0 part and if tensunits 10 and tensunits 20 part self teenstensunits 10 else tensdigit self tenstensunits 10 onesdigit self onestensunits 10 if tensdigit part tensdigit if onesdigit if tensdigit part part onesdigit parts appendpart num 1000 return joinreversedparts def convertself number if in number integerpart decimalpart number split else integerpart decimalpart number 00 extract currency symbol if present currencysymbol for symbol name in self currencysymbols items if integerpart startswithsymbol currencysymbol name integerpart integerpartlensymbol break if integerpart startswith if integerpart1 startswithsymbol currencysymbol name integerpart integerpartlensymbol 1 break extract minus prefix for negative numbers minusprefix if integerpart startswith minusprefix minus integerpart integerpart1 elif integerpart startswithminus minusprefix minus integerpart integerpartlenminus percentsuffix if in integerpart or in decimalpart percentsuffix percent integerpart integerpart replace decimalpart decimalpart replace integerpart integerpart zfill3 lenintegerpart 1 3 1 parts for i in range0 lenintegerpart 3 chunk intintegerparti i 3 if chunk 0 part self spellnumberchunk unit self thousandslenintegerparti 3 1 if unit part unit parts appendpart spelledinteger joinparts format the speltout number based on conditions such as if it has decimal parts currency symbol minus prefix etc if decimalpart 00 return fminusprefixspelledintegerpercentsuffixcurrencysymbol if minusprefix or currencysymbol else fspelledintegerpercentsuffix else spelleddecimal joinself spellnumberintdigit for digit in decimalpart return fminusprefixspelledinteger point spelleddecimalpercentsuffixcurrencysymbol if minusprefix or currencysymbol else fminusprefixspelledinteger point spelleddecimalpercentsuffix def callself text form part of the pattern for all currency symbols pattern r w d d1 2 w find and replace commas in numbers 15 000 15000 etc text re subrd d lambda match match group1 replace text use regex to find and replace numbers in the text convertedtext re subpattern lambda match self convertmatch group1 text convertedtext re sub convertedtext return convertedtext coding utf 8 2023 the fairseq s microsoft research and the huggingface inc team licensed under the apache license version 2 0 the license you may not use this file except in compliance with the license you may obtain a copy of the license at http www apache org licenses license 2 0 unless required by applicable law or agreed to in writing software distributed under the license is distributed on an as is basis without warranties or conditions of any kind either express or implied see the license for the specific language governing permissions and limitations under the license number normalizer class for speecht5 define a dictionary to map currency symbols to their names top most traded currencies according to https en wikipedia org wiki template most_traded_currencies converts an individual number passed in string form to spelt out form extract currency symbol if present extract minus prefix for negative numbers format the spelt out number based on conditions such as if it has decimal parts currency symbol minus prefix etc convert numbers number like quantities in a string to their spelt out counterparts form part of the pattern for all currency symbols find and replace commas in numbers 15 000 15000 etc use regex to find and replace numbers in the text
import re


class EnglishNumberNormalizer:
    def __init__(self):
        self.ones = ["", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"]
        self.teens = [
            "",
            "eleven",
            "twelve",
            "thirteen",
            "fourteen",
            "fifteen",
            "sixteen",
            "seventeen",
            "eighteen",
            "nineteen",
        ]
        self.tens = ["", "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"]
        self.thousands = [
            "",
            "thousand",
            "million",
            "billion",
            "trillion",
            "quadrillion",
            "quintillion",
            "sextillion",
            "septillion",
            "octillion",
            "nonillion",
            "decillion",
        ]

        
        
        
        self.currency_symbols = {
            "$": " dollars",
            "€": " euros",
            "£": " pounds",
            "¢": " cents",
            "¥": " japanese yen",
            "﷼": " saudi riyal",
            "₹": " indian rupees",
            "₽": " russian rubles",
            "฿": " thai baht",
            "₺": " turkish liras",
            "₴": " ukrainian hryvnia",
            "₣": " swiss francs",
            "₡": " costa rican colon",
            "₱": " philippine peso",
            "₪": " israeli shekels",
            "₮": " mongolian tögrög",
            "₩": " south korean won",
            "₦": " nigerian naira",
            "₫": " vietnamese Đồng",
        }

    def spell_number(self, num):
        if num == 0:
            return "zero"

        parts = []
        for i in range(0, len(self.thousands)):
            if num % 1000 != 0:
                part = ""
                hundreds = num % 1000 // 100
                tens_units = num % 100

                if hundreds > 0:
                    part += self.ones[hundreds] + " hundred"
                    if tens_units > 0:
                        part += " and "

                if tens_units > 10 and tens_units < 20:
                    part += self.teens[tens_units - 10]
                else:
                    tens_digit = self.tens[tens_units // 10]
                    ones_digit = self.ones[tens_units % 10]
                    if tens_digit:
                        part += tens_digit
                    if ones_digit:
                        if tens_digit:
                            part += " "
                        part += ones_digit

                parts.append(part)

            num //= 1000

        return " ".join(reversed(parts))

    def convert(self, number):
        
        if "." in number:
            integer_part, decimal_part = number.split(".")
        else:
            integer_part, decimal_part = number, "00"

        
        currency_symbol = ""
        for symbol, name in self.currency_symbols.items():
            if integer_part.startswith(symbol):
                currency_symbol = name
                integer_part = integer_part[len(symbol) :]
                break

            if integer_part.startswith("-"):
                if integer_part[1:].startswith(symbol):
                    currency_symbol = name
                    integer_part = "-" + integer_part[len(symbol) + 1 :]
                    break

        
        minus_prefix = ""
        if integer_part.startswith("-"):
            minus_prefix = "minus "
            integer_part = integer_part[1:]
        elif integer_part.startswith("minus"):
            minus_prefix = "minus "
            integer_part = integer_part[len("minus") :]

        percent_suffix = ""
        if "%" in integer_part or "%" in decimal_part:
            percent_suffix = " percent"
            integer_part = integer_part.replace("%", "")
            decimal_part = decimal_part.replace("%", "")

        integer_part = integer_part.zfill(3 * ((len(integer_part) - 1) // 3 + 1))

        parts = []
        for i in range(0, len(integer_part), 3):
            chunk = int(integer_part[i : i + 3])
            if chunk > 0:
                part = self.spell_number(chunk)
                unit = self.thousands[len(integer_part[i:]) // 3 - 1]
                if unit:
                    part += " " + unit
                parts.append(part)

        spelled_integer = " ".join(parts)

        
        
        if decimal_part == "00":
            return (
                f"{minus_prefix}{spelled_integer}{percent_suffix}{currency_symbol}"
                if minus_prefix or currency_symbol
                else f"{spelled_integer}{percent_suffix}"
            )
        else:
            spelled_decimal = " ".join([self.spell_number(int(digit)) for digit in decimal_part])
            return (
                f"{minus_prefix}{spelled_integer} point {spelled_decimal}{percent_suffix}{currency_symbol}"
                if minus_prefix or currency_symbol
                else f"{minus_prefix}{spelled_integer} point {spelled_decimal}{percent_suffix}"
            )

    def __call__(self, text):
        
        
        pattern = r"(?<!\w)(-?\$?\€?\£?\¢?\¥?\₹?\₽?\฿?\₺?\₴?\₣?\₡?\₱?\₪?\₮?\₩?\₦?\₫?\﷼?\d+(?:\.\d{1,2})?%?)(?!\w)"

        
        text = re.sub(r"(\d+,\d+)", lambda match: match.group(1).replace(",", ""), text)

        
        converted_text = re.sub(pattern, lambda match: self.convert(match.group(1)), text)
        converted_text = re.sub(" +", " ", converted_text)

        return converted_text