Issue-1411 - Leave all nice_number formatting to language
parent
9b5f4dd09c
commit
c45d228d91
|
@ -16,7 +16,10 @@
|
|||
from mycroft.util.lang.format_en import *
|
||||
from mycroft.util.lang.format_pt import *
|
||||
from mycroft.util.lang.format_it import *
|
||||
from mycroft.util.lang.format_fr import *
|
||||
|
||||
from mycroft.util.lang.format_fr import nice_number_fr
|
||||
from mycroft.util.lang.format_fr import nice_time_fr
|
||||
from mycroft.util.lang.format_fr import pronounce_number_fr
|
||||
|
||||
|
||||
def nice_number(number, lang="en-us", speech=True, denominators=None):
|
||||
|
@ -32,29 +35,16 @@ def nice_number(number, lang="en-us", speech=True, denominators=None):
|
|||
Returns:
|
||||
(str): The formatted string.
|
||||
"""
|
||||
result = _convert_to_mixed_fraction(number, denominators)
|
||||
if not result:
|
||||
# Give up, just represent as a 3 decimal number
|
||||
return str(round(number, 3))
|
||||
|
||||
if not speech:
|
||||
whole, num, den = result
|
||||
if num == 0:
|
||||
# TODO: Number grouping? E.g. "1,000,000"
|
||||
return str(whole)
|
||||
else:
|
||||
return '{} {}/{}'.format(whole, num, den)
|
||||
|
||||
# Convert to spoken representation in appropriate language
|
||||
lang_lower = str(lang).lower()
|
||||
if lang_lower.startswith("en"):
|
||||
return nice_number_en(result)
|
||||
return nice_number_en(number, speech, denominators)
|
||||
elif lang_lower.startswith("pt"):
|
||||
return nice_number_pt(result)
|
||||
return nice_number_pt(number, speech, denominators)
|
||||
elif lang_lower.startswith("it"):
|
||||
return nice_number_it(result)
|
||||
return nice_number_it(number, speech, denominators)
|
||||
elif lang_lower.startswith("fr"):
|
||||
return nice_number_fr(result)
|
||||
return nice_number_fr(number, speech, denominators)
|
||||
|
||||
# Default to the raw number for unsupported languages,
|
||||
# hopefully the STT engine will pronounce understandably.
|
||||
|
@ -111,35 +101,3 @@ def pronounce_number(number, lang="en-us", places=2):
|
|||
|
||||
# Default to just returning the numeric value
|
||||
return str(number)
|
||||
|
||||
|
||||
def _convert_to_mixed_fraction(number, denominators):
|
||||
"""
|
||||
Convert floats to components of a mixed fraction representation
|
||||
|
||||
Returns the closest fractional representation using the
|
||||
provided denominators. For example, 4.500002 would become
|
||||
the whole number 4, the numerator 1 and the denominator 2
|
||||
|
||||
Args:
|
||||
number (float): number for convert
|
||||
denominators (iter of ints): denominators to use, default [1 .. 20]
|
||||
Returns:
|
||||
whole, numerator, denominator (int): Integers of the mixed fraction
|
||||
"""
|
||||
int_number = int(number)
|
||||
if int_number == number:
|
||||
return int_number, 0, 1 # whole number, no fraction
|
||||
|
||||
frac_number = abs(number - int_number)
|
||||
if not denominators:
|
||||
denominators = range(1, 21)
|
||||
|
||||
for denominator in denominators:
|
||||
numerator = abs(frac_number) * denominator
|
||||
if (abs(numerator - round(numerator)) < 0.01): # 0.01 accuracy
|
||||
break
|
||||
else:
|
||||
return None
|
||||
|
||||
return int_number, int(round(numerator)), denominator
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2017 Mycroft AI Inc.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
|
||||
def convert_to_mixed_fraction(number, denominators):
|
||||
"""
|
||||
Convert floats to components of a mixed fraction representation
|
||||
|
||||
Returns the closest fractional representation using the
|
||||
provided denominators. For example, 4.500002 would become
|
||||
the whole number 4, the numerator 1 and the denominator 2
|
||||
|
||||
Args:
|
||||
number (float): number for convert
|
||||
denominators (iter of ints): denominators to use, default [1 .. 20]
|
||||
Returns:
|
||||
whole, numerator, denominator (int): Integers of the mixed fraction
|
||||
"""
|
||||
int_number = int(number)
|
||||
if int_number == number:
|
||||
return int_number, 0, 1 # whole number, no fraction
|
||||
|
||||
frac_number = abs(number - int_number)
|
||||
if not denominators:
|
||||
denominators = range(1, 21)
|
||||
|
||||
for denominator in denominators:
|
||||
numerator = abs(frac_number) * denominator
|
||||
if (abs(numerator - round(numerator)) < 0.01): # 0.01 accuracy
|
||||
break
|
||||
else:
|
||||
return None
|
||||
|
||||
return int_number, int(round(numerator)), denominator
|
|
@ -15,6 +15,7 @@
|
|||
# limitations under the License.
|
||||
#
|
||||
|
||||
from mycroft.util.lang.format_common import convert_to_mixed_fraction
|
||||
|
||||
NUM_STRING_EN = {
|
||||
0: 'zero',
|
||||
|
@ -71,18 +72,34 @@ FRACTION_STRING_EN = {
|
|||
}
|
||||
|
||||
|
||||
def nice_number_en(mixed):
|
||||
def nice_number_en(number, speech, denominators):
|
||||
"""
|
||||
Helper for for nice_number
|
||||
|
||||
Convert (1 1 3) to spoken value like "1 and 1 third"
|
||||
|
||||
Args:
|
||||
mixed (int,int,int): the mixed number; whole, numerator, denominator
|
||||
Return:
|
||||
(str): spoken version of the number
|
||||
number (int or float): the float to format
|
||||
speech (bool): format for speech (True) or display (False)
|
||||
denominators (iter of ints): denominators to use, default [1 .. 20]
|
||||
Returns:
|
||||
(str): The formatted string.
|
||||
"""
|
||||
whole, num, den = mixed
|
||||
|
||||
result = convert_to_mixed_fraction(number, denominators)
|
||||
if not result:
|
||||
# Give up, just represent as a 3 decimal number
|
||||
return str(round(number, 3))
|
||||
|
||||
whole, num, den = result
|
||||
|
||||
if not speech:
|
||||
if num == 0:
|
||||
# TODO: Number grouping? E.g. "1,000,000"
|
||||
return str(whole)
|
||||
else:
|
||||
return '{} {}/{}'.format(whole, num, den)
|
||||
|
||||
if num == 0:
|
||||
return str(whole)
|
||||
den_str = FRACTION_STRING_EN[den]
|
||||
|
|
|
@ -16,10 +16,10 @@
|
|||
#
|
||||
""" Format functions for french (fr)
|
||||
|
||||
Todo:
|
||||
* nice_number should leave number formatting to nice_number_fr
|
||||
"""
|
||||
|
||||
from mycroft.util.lang.format_common import convert_to_mixed_fraction
|
||||
|
||||
NUM_STRING_FR = {
|
||||
0: 'zéro',
|
||||
1: 'un',
|
||||
|
@ -71,48 +71,73 @@ FRACTION_STRING_FR = {
|
|||
}
|
||||
|
||||
|
||||
def nice_number_fr(result):
|
||||
def nice_number_fr(number, speech, denominators):
|
||||
"""
|
||||
Helper for nice_number
|
||||
|
||||
Convert (1 1/3) to spoken value like "1 et 1 tiers"
|
||||
|
||||
Args:
|
||||
mixed (int,int,int): the mixed number; whole, numerator, denominator
|
||||
Return:
|
||||
(str): spoken version of the number
|
||||
number (int or float): the float to format
|
||||
speech (bool): format for speech (True) or display (False)
|
||||
denominators (iter of ints): denominators to use, default [1 .. 20]
|
||||
Returns:
|
||||
(str): The formatted string.
|
||||
"""
|
||||
whole, num, den = result
|
||||
if num == 0:
|
||||
# if the number is an integer, nothing to do
|
||||
return str(whole)
|
||||
den_str = FRACTION_STRING_FR[den]
|
||||
# if it is not an integer
|
||||
if whole == 0:
|
||||
# if there is no whole number
|
||||
if num == 1:
|
||||
# if numerator is 1, return "un demi", for example
|
||||
return_string = 'un {}'.format(den_str)
|
||||
else:
|
||||
# else return "quatre tiers", for example
|
||||
return_string = '{} {}'.format(num, den_str)
|
||||
elif num == 1:
|
||||
# if there is a whole number and numerator is 1
|
||||
if den == 2:
|
||||
# if denominator is 2, return "1 et demi", for example
|
||||
return_string = '{} et {}'.format(whole, den_str)
|
||||
else:
|
||||
# else return "1 et 1 tiers", for example
|
||||
return_string = '{} et 1 {}'.format(whole, den_str)
|
||||
else:
|
||||
# else return "2 et 3 quart", for example
|
||||
return_string = '{} et {} {}'.format(whole, num, den_str)
|
||||
if num > 1 and den != 3:
|
||||
# if the numerator is greater than 1 and the denominator
|
||||
# is not 3 ("tiers"), add an s for plural
|
||||
return_string += 's'
|
||||
strNumber = ""
|
||||
whole = 0
|
||||
num = 0
|
||||
den = 0
|
||||
|
||||
return return_string
|
||||
result = convert_to_mixed_fraction(number, denominators)
|
||||
|
||||
if not result:
|
||||
# Give up, just represent as a 3 decimal number
|
||||
whole = round(number, 3)
|
||||
else:
|
||||
whole, num, den = result
|
||||
|
||||
if not speech:
|
||||
if num == 0:
|
||||
strNumber = '{:,}'.format(whole)
|
||||
strNumber = strNumber.replace(",", " ")
|
||||
strNumber = strNumber.replace(".", ",")
|
||||
return strNumber
|
||||
else:
|
||||
return '{} {}/{}'.format(whole, num, den)
|
||||
else:
|
||||
if num == 0:
|
||||
# if the number is not a fraction, nothing to do
|
||||
strNumber = str(whole)
|
||||
strNumber = strNumber.replace(".", ",")
|
||||
return strNumber
|
||||
den_str = FRACTION_STRING_FR[den]
|
||||
# if it is not an integer
|
||||
if whole == 0:
|
||||
# if there is no whole number
|
||||
if num == 1:
|
||||
# if numerator is 1, return "un demi", for example
|
||||
strNumber = 'un {}'.format(den_str)
|
||||
else:
|
||||
# else return "quatre tiers", for example
|
||||
strNumber = '{} {}'.format(num, den_str)
|
||||
elif num == 1:
|
||||
# if there is a whole number and numerator is 1
|
||||
if den == 2:
|
||||
# if denominator is 2, return "1 et demi", for example
|
||||
strNumber = '{} et {}'.format(whole, den_str)
|
||||
else:
|
||||
# else return "1 et 1 tiers", for example
|
||||
strNumber = '{} et 1 {}'.format(whole, den_str)
|
||||
else:
|
||||
# else return "2 et 3 quart", for example
|
||||
strNumber = '{} et {} {}'.format(whole, num, den_str)
|
||||
if num > 1 and den != 3:
|
||||
# if the numerator is greater than 1 and the denominator
|
||||
# is not 3 ("tiers"), add an s for plural
|
||||
strNumber += 's'
|
||||
|
||||
return strNumber
|
||||
|
||||
|
||||
def pronounce_number_fr(num, places=2):
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
# limitations under the License.
|
||||
#
|
||||
|
||||
from mycroft.util.lang.format_common import convert_to_mixed_fraction
|
||||
|
||||
NUM_STRING_IT = {
|
||||
0: 'zero',
|
||||
|
@ -70,7 +71,7 @@ FRACTION_STRING_IT = {
|
|||
}
|
||||
|
||||
|
||||
def nice_number_it(mixed):
|
||||
def nice_number_it(number, speech, denominators):
|
||||
"""
|
||||
Helper for for nice_number adapted to italian
|
||||
adapted to italian fron en version
|
||||
|
@ -78,12 +79,27 @@ def nice_number_it(mixed):
|
|||
Convert (1 1 3) to spoken value like "1 e un terzo"
|
||||
|
||||
Args:
|
||||
mixed (int,int,int): the mixed number; whole, numerator, denominator
|
||||
Return:
|
||||
(str): spoken version of the number
|
||||
number (int or float): the float to format
|
||||
speech (bool): format for speech (True) or display (False)
|
||||
denominators (iter of ints): denominators to use, default [1 .. 20]
|
||||
Returns:
|
||||
(str): The formatted string.
|
||||
"""
|
||||
|
||||
whole, num, den = mixed
|
||||
result = convert_to_mixed_fraction(number, denominators)
|
||||
if not result:
|
||||
# Give up, just represent as a 3 decimal number
|
||||
return str(round(number, 3))
|
||||
|
||||
whole, num, den = result
|
||||
|
||||
if not speech:
|
||||
if num == 0:
|
||||
# TODO: Number grouping? E.g. "1,000,000"
|
||||
return str(whole)
|
||||
else:
|
||||
return '{} {}/{}'.format(whole, num, den)
|
||||
|
||||
if num == 0:
|
||||
return str(whole)
|
||||
# denominatore
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
# limitations under the License.
|
||||
#
|
||||
|
||||
from mycroft.util.lang.format_common import convert_to_mixed_fraction
|
||||
|
||||
FRACTION_STRING_PT = {
|
||||
2: 'meio',
|
||||
|
@ -42,9 +43,23 @@ FRACTION_STRING_PT = {
|
|||
}
|
||||
|
||||
|
||||
def nice_number_pt(result):
|
||||
def nice_number_pt(number, speech, denominators):
|
||||
""" Portuguese conversion for nice_number """
|
||||
|
||||
result = convert_to_mixed_fraction(number, denominators)
|
||||
if not result:
|
||||
# Give up, just represent as a 3 decimal number
|
||||
return str(round(number, 3))
|
||||
|
||||
whole, num, den = result
|
||||
|
||||
if not speech:
|
||||
if num == 0:
|
||||
# TODO: Number grouping? E.g. "1,000,000"
|
||||
return str(whole)
|
||||
else:
|
||||
return '{} {}/{}'.format(whole, num, den)
|
||||
|
||||
if num == 0:
|
||||
return str(whole)
|
||||
# denominador
|
||||
|
|
|
@ -23,10 +23,13 @@ from mycroft.util.format import pronounce_number
|
|||
|
||||
|
||||
NUMBERS_FIXTURE_FR = {
|
||||
1.435634: '1.436',
|
||||
1.435634: '1,436',
|
||||
2: '2',
|
||||
5.0: '5',
|
||||
0.027: '0.027',
|
||||
0.027: '0,027',
|
||||
1234567890: '1234567890',
|
||||
12345.67890: '12345,679',
|
||||
0.027: '0,027',
|
||||
0.5: 'un demi',
|
||||
1.333: '1 et 1 tiers',
|
||||
2.666: '2 et 2 tiers',
|
||||
|
@ -65,24 +68,37 @@ class TestNiceNumberFormat_fr(unittest.TestCase):
|
|||
self.assertEqual(nice_number(5.5, lang="fr-fr",
|
||||
denominators=[1, 2, 3]),
|
||||
'5 et demi',
|
||||
'should format 5.5 as 5 and a half not {}'.format(
|
||||
'should format 5.5 as 5 et demi not {}'.format(
|
||||
nice_number(5.5, lang="fr-fr",
|
||||
denominators=[1, 2, 3])))
|
||||
self.assertEqual(nice_number(2.333, denominators=[1, 2]),
|
||||
'2.333',
|
||||
'should format 2.333 as 2.333 not {}'.format(
|
||||
self.assertEqual(nice_number(2.333, lang="fr-fr",
|
||||
denominators=[1, 2]),
|
||||
'2,333',
|
||||
'should format 2.333 as 2,333 not {}'.format(
|
||||
nice_number(2.333, lang="fr-fr",
|
||||
denominators=[1, 2])))
|
||||
|
||||
def test_no_speech_fr(self):
|
||||
self.assertEqual(nice_number(6.777, speech=False),
|
||||
self.assertEqual(nice_number(6.777, lang="fr-fr", speech=False),
|
||||
'6 7/9',
|
||||
'should format 6.777 as 6 7/9 not {}'.format(
|
||||
nice_number(6.777, lang="fr-fr", speech=False)))
|
||||
self.assertEqual(nice_number(6.0, speech=False),
|
||||
self.assertEqual(nice_number(6.0, lang="fr-fr", speech=False),
|
||||
'6',
|
||||
'should format 6.0 as 6 not {}'.format(
|
||||
nice_number(6.0, lang="fr-fr", speech=False)))
|
||||
self.assertEqual(nice_number(1234567890, lang="fr-fr", speech=False),
|
||||
'1 234 567 890',
|
||||
'should format 1234567890 as'
|
||||
'1 234 567 890 not {}'.format(
|
||||
nice_number(1234567890, lang="fr-fr",
|
||||
speech=False)))
|
||||
self.assertEqual(nice_number(12345.6789, lang="fr-fr", speech=False),
|
||||
'12 345,679',
|
||||
'should format 12345.6789 as'
|
||||
'12 345,679 not {}'.format(
|
||||
nice_number(12345.6789, lang="fr-fr",
|
||||
speech=False)))
|
||||
|
||||
|
||||
# def pronounce_number(number, lang="en-us", places=2):
|
||||
|
|
Loading…
Reference in New Issue