diff --git a/numbers/src/main/java/org/dicio/numbers/lang/fr/FrenchFormatter.java b/numbers/src/main/java/org/dicio/numbers/lang/fr/FrenchFormatter.java new file mode 100644 index 00000000..cdae4516 --- /dev/null +++ b/numbers/src/main/java/org/dicio/numbers/lang/fr/FrenchFormatter.java @@ -0,0 +1,276 @@ +package org.dicio.numbers.lang.fr; + +import java.time.LocalTime; +import java.util.*; + +import org.dicio.numbers.formatter.NumberFormatter; +import org.dicio.numbers.util.MixedFraction; +import org.dicio.numbers.util.Utils; +import sun.reflect.generics.reflectiveObjects.NotImplementedException; + +public class FrenchFormatter extends NumberFormatter { + //FIXME: + final Map NUMBER_NAMES = new HashMap() {{}}; + final Map NUMBER_NAMES_SHORT_SCALE = new HashMap(NUMBER_NAMES) {{}}; + final Map NUMBER_NAMES_LONG_SCALE = new HashMap(NUMBER_NAMES) {{}}; + final Map ORDINAL_NAMES = new HashMap() {{}}; + final Map ORDINAL_NAMES_SHORT_SCALE = new HashMap(ORDINAL_NAMES) {{}}; + final Map ORDINAL_NAMES_LONG_SCALE = new HashMap(ORDINAL_NAMES) {{}}; + + + protected FrenchFormatter() { + super("config/fr-fr"); + } + + + // Copied from EnglishFormatter with no changes except for strings. + @Override + public String niceNumber(MixedFraction mixedFraction, boolean speech) { + if (speech) { + final String sign = mixedFraction.negative ? "moins " : ""; + if (mixedFraction.numerator == 0) { + return sign + pronounceNumber(mixedFraction.whole, 0, true, false, false); + } + + String denominatorString; + if (mixedFraction.denominator == 2) { + denominatorString = "moitié"; //FIXME: Should it be "demi" instead? + } else if (mixedFraction.denominator == 4) { + denominatorString = "quart"; + } else { + // use ordinal: only half and quarter are exceptions + denominatorString + = pronounceNumber(mixedFraction.denominator, 0, true, false, true); + } + + final String numeratorString; + if (mixedFraction.numerator == 1) { + numeratorString = "un"; //FIXME: Should it be empty? 2/1 = 2, not 2 over one + } else { + numeratorString = pronounceNumber(mixedFraction.numerator, 0, true, false, false); + denominatorString += "s"; + } + + if (mixedFraction.whole == 0) { + return sign + numeratorString + " " + denominatorString; + } else { + return sign + pronounceNumber(mixedFraction.whole, 0, true, false, false) + + " et " + numeratorString + " " + denominatorString; + } + + } else { + return niceNumberNotSpeech(mixedFraction); + } + } + + // Copied from EnglishFormatter with no changes except for strings. + @Override + public String pronounceNumber(double number, int places, boolean shortScale, boolean scientific, boolean ordinal) { + if (number == Double.POSITIVE_INFINITY) { + return "infini"; + } else if (number == Double.NEGATIVE_INFINITY) { + return "moins l'infini"; + } else if (Double.isNaN(number)) { + return "non défini"; + } + + // also using scientific mode if the number is too big to be spoken fully. Checking against + // the biggest double smaller than 10^21 = 1000 * 10^18, which is the biggest pronounceable + // number, since e.g. 999.99 * 10^18 can be pronounced correctly. + if (scientific || Math.abs(number) > 999999999999999934463d) { + final String scientificFormatted = String.format(Locale.FRANCE, "%E", number); + final String[] parts = scientificFormatted.split("E", 2); + final double power = Integer.parseInt(parts[1]); + + if (power != 0) { + // This handles negatives of powers separately from the normal + // handling since each call disables the scientific flag + final double n = Double.parseDouble(parts[0]); + return String.format("%s%s fois dix puissance %s%s", + n < 0 ? "moins " : "", + pronounceNumber(Math.abs(n), places, shortScale, false, false), + power < 0 ? "moins " : "", + pronounceNumber(Math.abs(power), places, shortScale, false, false)); + } + } + + final StringBuilder result = new StringBuilder(); + if (number < 0) { + number = -number; + // from here on number is always positive + if (places != 0 || number >= 0.5) { + // do not add minus if number will be rounded to 0 + //result.append(scientific ? "negative " : "minus "); + + // The negative/minus distinction doesn't exist in french in this context. + result.append("moins"); + } + } + + final int realPlaces = Utils.decimalPlacesNoFinalZeros(number, places); + final boolean numberIsWhole = realPlaces == 0; + // if no decimal places to be printed, numberLong should be the rounded number + final long numberLong = (long) number + (number % 1 >= 0.5 && numberIsWhole ? 1 : 0); + + if (!ordinal && numberIsWhole && numberLong > 1000 && numberLong < 2000) { + // deal with 4 digits that can be said like a date, i.e. 1972 => nineteen seventy two + + result.append(NUMBER_NAMES.get(numberLong / 100)); + result.append(" "); + if (numberLong % 100 == 0) { + // 1900 => mille neuf cent (thousand nine hundred) + result.append(NUMBER_NAMES.get(100L)); + } else if (numberLong % 100 < 10 && numberLong % 100 != 0) { + // 1906 => mille neuf cent six (thousand nine hundred six) + // We don't have an "oh" separator + result.append(NUMBER_NAMES.get(numberLong % 10)); + } else if (numberLong % 10 == 0 || numberLong % 100 < 20) { + // 1960 => nineteen sixty; 1911 => nineteen eleven + result.append(NUMBER_NAMES.get(numberLong % 100)); + } else { + // 1961 => nineteen sixty one + result.append(NUMBER_NAMES.get(numberLong % 100 - numberLong % 10)); + result.append(" "); + result.append(NUMBER_NAMES.get(numberLong % 10)); + } + + return result.toString(); + } + + if (!ordinal && NUMBER_NAMES.containsKey(numberLong)) { + if (number > 90) { + result.append("one "); + } + result.append(NUMBER_NAMES.get(numberLong)); + + } + // No short scale system in french. + /*else if (shortScale) { + boolean ordi = ordinal && numberIsWhole; // not ordinal if not whole + final List groups = Utils.splitByModulus(numberLong, 1000); + final List groupNames = new ArrayList<>(); + for (int i = 0; i < groups.size(); ++i) { + final long z = groups.get(i); + if (z == 0) { + continue; // skip 000 groups + } + String groupName = subThousand(z, i == 0 && ordi); + + if (i != 0) { + final long magnitude = Utils.longPow(1000, i); + if (ordi) { + // ordi can be true only for the first group (i.e. at the end of the number) + if (z == 1) { + // remove "one" from first group (e.g. "one billion, millionth") + groupName = ORDINAL_NAMES_SHORT_SCALE.get(magnitude); + } else { + groupName += " " + ORDINAL_NAMES_SHORT_SCALE.get(magnitude); + } + } else { + groupName += " " + NUMBER_NAMES_SHORT_SCALE.get(magnitude); + } + } + + groupNames.add(groupName); + ordi = false; + } + + appendSplitGroups(result, groupNames); + + }*/ else { + boolean ordi = ordinal && numberIsWhole; // not ordinal if not whole in french as well as in english + final List groups = Utils.splitByModulus(numberLong, 1000000); + final List groupNames = new ArrayList<>(); + for (int i = 0; i < groups.size(); ++i) { + final long z = groups.get(i); + if (z == 0) { + continue; // skip 000000 groups + } + + String groupName; + if (z < 1000) { + groupName = subThousand(z, i == 0 && ordi); + } else { + groupName = subThousand(z / 1000, false) + " mille"; + if (z % 1000 != 0) { + groupName += (i == 0 ? ", " : " ") + subThousand(z % 1000, i == 0 && ordi); + } else if (i == 0 && ordi) { + if (z / 1000 == 1) { + groupName = "millième"; // remove "one" from "one thousandth" + } else { + groupName += "ième"; + } + } + } + + if (i != 0) { + final long magnitude = Utils.longPow(1000000, i); + if (ordi) { + // ordi can be true only for the first group (i.e. at the end of the number) + if (z == 1) { + // remove "one" from first group (e.g. "one billion, millionth") + groupName = ORDINAL_NAMES_LONG_SCALE.get(magnitude); + } else { + groupName += " " + ORDINAL_NAMES_LONG_SCALE.get(magnitude); + } + } else { + groupName += " " + NUMBER_NAMES_LONG_SCALE.get(magnitude); + } + } + + groupNames.add(groupName); + ordi = false; + } + + appendSplitGroups(result, groupNames); + } + + if (realPlaces > 0) { + if (number < 1.0 && (result.length() == 0 || "moins ".contentEquals(result))) { + result.append("zéro"); // nothing was written before + } + result.append(" virgule"); // 0,xxx english (comma decimal separator) + + final String fractionalPart = String.format("%." + realPlaces + "f", number % 1); + for (int i = 2; i < fractionalPart.length(); ++i) { + result.append(" "); + result.append(NUMBER_NAMES.get((long) (fractionalPart.charAt(i) - '0'))); + } + } + + return result.toString(); + } + + /** + * @param result the string builder to append the space-separated group names to + * @param groupNames the group names + */ + private void appendSplitGroups(final StringBuilder result, final List groupNames) { + if (!groupNames.isEmpty()) { + result.append(groupNames.get(groupNames.size() - 1)); + } + + for (int i = groupNames.size() - 2; i >= 0; --i) { + result.append(" "); // Example: 1 000 000 + result.append(groupNames.get(i)); + } + } + + /** + * @param n must be 0 <= n <= 999 + * @param ordinal whether to return an ordinal number (usually with -ième if n > 1) + * @return the string representation of a number smaller than 1000 + */ + private String subThousand(final long n, final boolean ordinal) { + //FIXME: Implement according to NiceNumberTest. + throw new NotImplementedException(); + } + + + @Override + public String niceTime(LocalTime time, boolean speech, boolean use24Hour, boolean showAmPm) { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/numbers/src/main/resources/config/fr-fr/date_time.json b/numbers/src/main/resources/config/fr-fr/date_time.json new file mode 100644 index 00000000..43449204 --- /dev/null +++ b/numbers/src/main/resources/config/fr-fr/date_time.json @@ -0,0 +1,147 @@ +{ + "decade_format": { + "1": {"match": "^\\d$", "format": "{x}"}, + "2": {"match": "^\\d0$", "format": "{x0}"}, + "3": {"match": "^[2-6]1$", "format": "{x0}-et-un"}, + "4": {"match": "^[2-6|8]\\d$", "format": "{x0}-{x}"}, + "5": {"match": "^\\d{2}$", "format": "{xx}"}, + "default": "{number}" + }, + "hundreds_format": { + "1": {"match": "^\\d{1,2}$", "format": "{formatted_decade}"}, + "2": {"match": "^100$", "format": "cent"}, + "3": {"match": "^\\d00$", "format": "{x_in_x00}-cents"}, + "4": {"match": "^1\\d{2}$", "format": "cent-{formatted_decade}"}, + "5": {"match": "^\\d{3}$", "format": "{x_in_x00}-cent-{formatted_decade}"}, + "default": "{number}" + }, + "thousand_format": { + "1": {"match": "^1000$", "format": "mille"}, + "2": {"match": "^\\d000$", "format": "{x_in_x000}-mille"}, + "3": {"match": "^1\\d{3}$", "format": "mille-{formatted_hundreds}"}, + "4": {"match": "^\\d{4}$", "format": "{x_in_x000}-mille-{formatted_hundreds}"}, + "default": "{number}" + }, + "year_format": { + "1": {"match": "^\\d\\d?$", "format": "{formatted_decade} {bc}"}, + "2": {"match": "^\\d{3}$", "format": "{formatted_hundreds} {bc}"}, + "3": {"match": "^[1-9]\\d{3}$", "format": "{formatted_thousand} {bc}"}, + "default": "{year} {bc}", + "bc": "avant Jésus Christ " + }, + "date_format": { + "date_full": "{weekday} {day} {month} {formatted_year}", + "date_full_no_year": "{weekday} {day} {month}", + "date_full_no_year_month": "{weekday} {day}", + "today": "aujourd'hui", + "tomorrow": "demain", + "yesterday": "hier" + }, + "date_time_format": { + "date_time": "{formatted_date} {formatted_time}" + }, + "weekday": { + "0": "lundi", + "1": "mardi", + "2": "mercredi", + "3": "jeudi", + "4": "vendredi", + "5": "samedi", + "6": "dimanche" + }, + "date": { + "1": "premier", + "2": "deux", + "3": "trois", + "4": "quatre", + "5": "cinq", + "6": "six", + "7": "sept", + "8": "huit", + "9": "neuf", + "10": "dix", + "11": "onze", + "12": "douze", + "13": "treize", + "14": "quatorze", + "15": "quinze", + "16": "seize", + "17": "dix-sept", + "18": "dix-huit", + "19": "dix-neuf", + "20": "vingt", + "21": "vingt-et-un", + "22": "vingt-deux", + "23": "vingt-trois", + "24": "vingt-quatre", + "25": "vingt-cinq", + "26": "vingt-six", + "27": "vingt-sept", + "28": "vingt-huit", + "29": "vingt-neuf", + "30": "trente", + "31": "trente-et-un" + }, + "month": { + "1": "janvier", + "2": "février", + "3": "mars", + "4": "avril", + "5": "mai", + "6": "juin", + "7": "juillet", + "8": "août", + "9": "septembre", + "10": "octobe", + "11": "novembre", + "12": "décembre" + }, + "number": { + "0": "zéro", + "1": "un", + "2": "deux", + "3": "trois", + "4": "quatre", + "5": "cinq", + "6": "six", + "7": "sept", + "8": "huit", + "9": "neuf", + "10": "dix", + "11": "onze", + "12": "douze", + "13": "treize", + "14": "quatorze", + "15": "quinze", + "16": "seize", + "17": "dix-sept", + "18": "dix-huit", + "19": "dix-neuf", + "20": "vingt", + "30": "trente", + "40": "quarante", + "50": "cinquante", + "60": "soixante", + "70": "soixante-dix", + "71": "soixante-et-onze", + "72": "soixante-douze", + "73": "soixante-treize", + "74": "soixante-quatorze", + "75": "soixante-quinze", + "76": "soixante-seize", + "77": "soixante-dix-sept", + "78": "soixante-dix-huit", + "79": "soixante-dix-neuf", + "80": "quatre-vingt", + "90": "quatre-vingt-dix", + "91": "quatre-vingt-onze", + "92": "quatre-vingt-douze", + "93": "quatre-vingt-treize", + "94": "quatre-vingt-quatorze", + "95": "quatre-vingt-quinze", + "96": "quatre-vingt-seize", + "97": "quatre-vingt-dix-sept", + "98": "quatre-vingt-dix-huit", + "99": "quatre-vingt-dix-neuf" + } +} diff --git a/numbers/src/main/resources/config/fr-fr/date_time_test.json b/numbers/src/main/resources/config/fr-fr/date_time_test.json new file mode 100644 index 00000000..448f2192 --- /dev/null +++ b/numbers/src/main/resources/config/fr-fr/date_time_test.json @@ -0,0 +1,43 @@ +{ + "test_nice_year": { + "1": {"datetime_param": "1, 1, 31, 13, 22, 3", "bc": "True", "assertEqual": "un avant Jésus Christ" }, + "2": {"datetime_param": "10, 1, 31, 13, 22, 3", "bc": "True", "assertEqual": "dix avant Jésus Christ" }, + "3": {"datetime_param": "92, 1, 31, 13, 22, 3", "bc": "True", "assertEqual": "quatre-vingt-douze avant Jésus Christ" }, + "4": {"datetime_param": "803, 1, 31, 13, 22, 3", "bc": "None", "assertEqual": "huit-cent-trois" }, + "5": {"datetime_param": "111, 1, 31, 13, 22, 3", "bc": "None", "assertEqual": "cent-onze" }, + "6": {"datetime_param": "454, 1, 31, 13, 22, 3", "bc": "None", "assertEqual": "quatre-cent-cinquante-quatre" }, + "7": {"datetime_param": "2005, 1, 31, 13, 22, 3", "bc": "False", "assertEqual": "deux-mille-cinq" }, + "8": {"datetime_param": "1012, 1, 31, 13, 22, 3", "bc": "False", "assertEqual": "mille-douze" }, + "9": {"datetime_param": "1046, 1, 31, 13, 22, 3", "bc": "False", "assertEqual": "mille-quarante-six" }, + "10": {"datetime_param": "1807, 1, 31, 13, 22, 3", "bc": "None", "assertEqual": "mille-huit-cent-sept" }, + "11": {"datetime_param": "1717, 1, 31, 13, 22, 3", "bc": "None", "assertEqual": "mille-sept-cent-dix-sept" }, + "12": {"datetime_param": "1988, 1, 31, 13, 22, 3", "bc": "None", "assertEqual": "mille-neuf-cent-quatre-vingt-huit"}, + "13": {"datetime_param": "2009, 1, 31, 13, 22, 3", "bc": "None", "assertEqual": "deux-mille-neuf"}, + "14": {"datetime_param": "2018, 1, 31, 13, 22, 3", "bc": "None", "assertEqual": "deux-mille-dix-huit"}, + "15": {"datetime_param": "2021, 1, 31, 13, 22, 3", "bc": "None", "assertEqual": "deux-mille-vingt-et-un"}, + "16": {"datetime_param": "2030, 1, 31, 13, 22, 3", "bc": "None", "assertEqual": "deux-mille-trente"}, + "17": {"datetime_param": "2100, 1, 31, 13, 22, 3", "bc": "False", "assertEqual": "deux-mille-cent" }, + "18": {"datetime_param": "1000, 1, 31, 13, 22, 3", "bc": "None", "assertEqual": "mille" }, + "19": {"datetime_param": "2000, 1, 31, 13, 22, 3", "bc": "None", "assertEqual": "deux-mille" }, + "20": {"datetime_param": "3120, 1, 31, 13, 22, 3", "bc": "True", "assertEqual": "trois-mille-cent-vingt avant Jésus Christ" }, + "21": {"datetime_param": "3241, 1, 31, 13, 22, 3", "bc": "True", "assertEqual": "trois-mille-deux-cent-quarante-et-un avant Jésus Christ" }, + "22": {"datetime_param": "5200, 1, 31, 13, 22, 3", "bc": "False", "assertEqual": "cinq-mille-deux-cents" }, + "23": {"datetime_param": "1100, 1, 31, 13, 22, 3", "bc": "False", "assertEqual": "mille-cent" }, + "24": {"datetime_param": "2100, 1, 31, 13, 22, 3", "bc": "False", "assertEqual": "deux-mille-cent" } + }, + "test_nice_date": { + "1": {"datetime_param": "2017, 1, 31, 0, 2, 3", "now": "None", "assertEqual": "mardi trente-et-un janvier deux-mille-dix-sept"}, + "2": {"datetime_param": "2018, 2, 4, 0, 2, 3", "now": "2017, 1, 1, 0, 2, 3", "assertEqual": "dimanche quatre février deux-mille-dix-huit"}, + "3": {"datetime_param": "2018, 2, 4, 0, 2, 3", "now": "2018, 1, 1, 0, 2, 3", "assertEqual": "dimanche quatre février"}, + "4": {"datetime_param": "2018, 2, 4, 0, 2, 3", "now": "2018, 2, 1, 0, 2, 3", "assertEqual": "dimanche quatre"}, + "5": {"datetime_param": "2018, 2, 4, 0, 2, 3", "now": "2018, 2, 3, 0, 2, 3", "assertEqual": "demain"}, + "6": {"datetime_param": "2018, 2, 4, 0, 2, 3", "now": "2018, 2, 4, 0, 2, 3", "assertEqual": "aujourd'hui"}, + "7": {"datetime_param": "2018, 2, 4, 0, 2, 3", "now": "2018, 2, 5, 0, 2, 3", "assertEqual": "hier"}, + "8": {"datetime_param": "2018, 2, 4, 0, 2, 3", "now": "2018, 2, 6, 0, 2, 3", "assertEqual": "dimanche quatre février"}, + "9": {"datetime_param": "2018, 2, 4, 0, 2, 3", "now": "2019, 2, 6, 0, 2, 3", "assertEqual": "dimanche quatre février deux-mille-dix-huit"} + }, + "test_nice_date_time": { + "1": {"datetime_param": "2017, 1, 31, 13, 22, 3", "now": "None", "use_24hour": "False", "use_ampm": "True", "assertEqual": "mardi trente-et-un janvier deux-mille-dix-sept une heure vingt-deux de l'après-midi"}, + "2": {"datetime_param": "2017, 1, 31, 13, 22, 3", "now": "None", "use_24hour": "True", "use_ampm": "False", "assertEqual": "mardi trente-et-un janvier deux-mille-dix-sept treize heures vingt-deux"} + } +} diff --git a/numbers/src/main/resources/config/fr-fr/day.word b/numbers/src/main/resources/config/fr-fr/day.word new file mode 100644 index 00000000..3e1393b8 --- /dev/null +++ b/numbers/src/main/resources/config/fr-fr/day.word @@ -0,0 +1 @@ +jour diff --git a/numbers/src/main/resources/config/fr-fr/days.word b/numbers/src/main/resources/config/fr-fr/days.word new file mode 100644 index 00000000..6a0300aa --- /dev/null +++ b/numbers/src/main/resources/config/fr-fr/days.word @@ -0,0 +1 @@ +jours diff --git a/numbers/src/main/resources/config/fr-fr/hour.word b/numbers/src/main/resources/config/fr-fr/hour.word new file mode 100644 index 00000000..cfa09b25 --- /dev/null +++ b/numbers/src/main/resources/config/fr-fr/hour.word @@ -0,0 +1 @@ +heure diff --git a/numbers/src/main/resources/config/fr-fr/hours.word b/numbers/src/main/resources/config/fr-fr/hours.word new file mode 100644 index 00000000..5afb41b1 --- /dev/null +++ b/numbers/src/main/resources/config/fr-fr/hours.word @@ -0,0 +1 @@ +heures diff --git a/numbers/src/main/resources/config/fr-fr/minute.word b/numbers/src/main/resources/config/fr-fr/minute.word new file mode 100644 index 00000000..cfcd96c9 --- /dev/null +++ b/numbers/src/main/resources/config/fr-fr/minute.word @@ -0,0 +1 @@ +minute diff --git a/numbers/src/main/resources/config/fr-fr/minutes.word b/numbers/src/main/resources/config/fr-fr/minutes.word new file mode 100644 index 00000000..5cf0e30b --- /dev/null +++ b/numbers/src/main/resources/config/fr-fr/minutes.word @@ -0,0 +1 @@ +minutes diff --git a/numbers/src/main/resources/config/fr-fr/second.word b/numbers/src/main/resources/config/fr-fr/second.word new file mode 100644 index 00000000..110f9689 --- /dev/null +++ b/numbers/src/main/resources/config/fr-fr/second.word @@ -0,0 +1 @@ +seconde diff --git a/numbers/src/main/resources/config/fr-fr/seconds.word b/numbers/src/main/resources/config/fr-fr/seconds.word new file mode 100644 index 00000000..7eac62af --- /dev/null +++ b/numbers/src/main/resources/config/fr-fr/seconds.word @@ -0,0 +1 @@ +secondes diff --git a/numbers/src/main/resources/config/fr-fr/tokenizer.json b/numbers/src/main/resources/config/fr-fr/tokenizer.json new file mode 100644 index 00000000..46b119d3 --- /dev/null +++ b/numbers/src/main/resources/config/fr-fr/tokenizer.json @@ -0,0 +1,393 @@ +{ + "spaces": " \t\n\f\r:;_!?<>|=()[]{}»«*~^`'\"", + "characters_as_word": "%‰#-+.,/", + "raw_number_categories": [ + "number", + "raw" + ], + "plural_endings": [ + "s" + ], + "word_matches": [ + { + "categories": [ + "ignore" + ], + "values": [ + "et" + ] + }, + { + "categories": [ + "ignore", + "thousand_separator" + ], + "values": [ + " ", + "-" + ] + }, + { + "categories": [ + "ordinal_suffix" + ], + "values": [ + "er", + "ier", + "ième" + ] + }, + { + "categories": [ + "point" + ], + "values": [ + "point", + "." + ] + }, + { + "categories": [ + "fraction_separator" + ], + "values": [ + "sur", + "divisé", + "/" + ] + }, + { + "categories": [ + "fraction_separator_secondary" + ], + "values": [ + "par" + ] + }, + { + "categories": [ + "sign", + "positive" + ], + "values": [ + "positif", + "plus", + "+" + ] + }, + { + "categories": [ + "sign", + "negative" + ], + "values": [ + "négatif", + "moins" + ] + }, + { + "categories": [ + "sign", + "negative", + "ignore" + ], + "values": [ + "-" + ] + }, + { + "categories": [ + "duration_separator" + ], + "values": [ + "d'" + ] + } + ], + "number_mappings": [ + { + "categories": [ + "number", + "digit", + "digit_after_point" + ], + "values": { + "zéro": 0, + "nul": 0, + "un": 1, + "deux": 2, + "trois": 3, + "quatre": 4, + "cinq": 5, + "six": 6, + "sept": 7, + "huit": 8, + "neuf": 9 + } + }, + { + "categories": [ + "number", + "digit_after_point" + ], + "values": { + "zéro": 0 + } + }, + { + "categories": [ + "number", + "teen" + ], + "values": { + "dix": 10, + "onze": 11, + "douze": 12, + "treize": 13, + "quatorze": 14, + "quinze": 15, + "seize": 16, + "dix-sept": 17, + "dix-huit": 18, + "dix-neuf": 19 + } + }, + { + "categories": [ + "number", + "tens" + ], + "values": { + "vingt": 20, + "trente": 30, + "quarante": 40, + "cinquante": 50, + "soixante": 60, + "soixante-dix": 70, + "quatre-vingts": 80, + "quatre-vingts-dix": 90 + } + }, + { + "categories": [ + "number", + "hundred" + ], + "values": { + "cent": 100 + } + }, + { + "categories": [ + "number", + "multiplier" + ], + "values": { + "mille": 1000, + "million": 1000000, + "milliard": 1000000000, + "billiard": 1000000000000, + "triliard": 1000000000000000, + "quadrilliard": 1000000000000000000 + } + }, + { + "categories": [ + "number", + "ordinal", + "digit" + ], + "values": { + "premier": 1, + "deuxième": 2, + "troisème": 3, + "quatrième": 4, + "cinqième": 5, + "sixième": 6, + "septième": 7, + "huitième": 8, + "neuvième": 9 + } + }, + { + "categories": [ + "number", + "ordinal", + "teen" + ], + "values": { + "dixième": 10, + "onzeième": 11, + "douzième": 12, + "treizième": 13, + "quatorzième": 14, + "quinzième": 15, + "seizième": 16, + "dix-septième": 17, + "dix-huitième": 18, + "dix-neuvième": 19 + } + }, + { + "categories": [ + "number", + "ordinal", + "tens" + ], + "values": { + "vingième": 20, + "trentième": 30, + "quarantième": 40, + "cinquantième": 50, + "soixantième": 60, + "soixante-dixième": 70, + "quatre-vingtième": 80, + "quatre-vingt-dixième": 90 + } + }, + { + "categories": [ + "number", + "ordinal", + "hundred" + ], + "values": { + "centième": 100 + } + }, + { + "categories": [ + "number", + "ordinal", + "multiplier" + ], + "values": { + "millième": 1000, + "millionième": 1000000, + "milliardième": 1000000000, + "billiardième": 1000000000000, + "triliardième": 1000000000000000, + "quadrilliardième": 1000000000000000000 + } + }, + { + "categories": [ + "number", + "suffix_multiplier" + ], + "values": { + "moitié": 0.5, + "moitiés": 0.5, + "quart": 0.25, + "quarts": 0.25, + "paire": 2, + "paires": 2, + "lustre": 5, + "lustres": 5, + "douzaines": 12, + "douzaine": 12, + "pourcent": 0.01, + "point": 0.01, + "%": 0.01, + "pour mille": 0.001, + "par mille": 0.001, + "par millier": 0.001, + "‰": 0.001 + } + } + ], + "duration_words": { + "1 NANOS": [ + "nanoseconde", + "nanosecondes", + "ns" + ], + "1 MICROS": [ + "microseconde", + "microsecondes", + "μs" + ], + "1 MILLIS": [ + "milliseconde", + "millisecondes", + "ms" + ], + "1 SECONDS": [ + "seconde", + "secondes", + "s", + "sec", + "secs" + ], + "1 MINUTES": [ + "minute", + "minutes", + "m", + "min", + "mins" + ], + "1 HOURS": [ + "heure", + "heures", + "h", + "hr", + "hrs" + ], + "1 DAYS": [ + "jour", + "jours", + "journée", + "journées", + "j" + ], + "1 WEEKS": [ + "semaine", + "semaines", + "S", + "w" + ], + "1 MONTHS": [ + "mois", + "M" + ], + "1 YEARS": [ + "an", + "ans", + "année", + "années", + "a", + "A" + ], + "1 DECADES": [ + "décennie", + "décennies", + "décade", + "décades" + ], + "1 CENTURIES": [ + "siècle", + "siècles" + ], + "1 MILLENNIA": [ + "millénaire", + "millénaires" + ] + }, + "duration_restrict_after_number": [ + "ns", + "μs", + "ms", + "s", + "m", + "h", + "d", + "w", + "mo", + "a", + "j", + "M", + "w", + "S", + "A" + ] +} \ No newline at end of file diff --git a/numbers/src/test/java/org/dicio/numbers/lang/fr/DateTimeConfigTest.java b/numbers/src/test/java/org/dicio/numbers/lang/fr/DateTimeConfigTest.java new file mode 100644 index 00000000..eaaf9ac0 --- /dev/null +++ b/numbers/src/test/java/org/dicio/numbers/lang/fr/DateTimeConfigTest.java @@ -0,0 +1,10 @@ +package org.dicio.numbers.lang.fr; + +import org.dicio.numbers.test.DateTimeConfigTestBase; + +public class DateTimeConfigTest extends DateTimeConfigTestBase { + @Override + public String configFolder() { + return "config/fr-fr"; + } +} diff --git a/numbers/src/test/java/org/dicio/numbers/lang/fr/DateTimeTest.java b/numbers/src/test/java/org/dicio/numbers/lang/fr/DateTimeTest.java new file mode 100644 index 00000000..7b7f8c30 --- /dev/null +++ b/numbers/src/test/java/org/dicio/numbers/lang/fr/DateTimeTest.java @@ -0,0 +1,51 @@ +package org.dicio.numbers.lang.fr; + +import org.dicio.numbers.formatter.NumberFormatter; +import org.dicio.numbers.test.DateTimeTestBase; +import org.junit.Test; + +import java.time.LocalDate; +import java.time.LocalDateTime; + +import static org.junit.Assert.assertEquals; + +public class DateTimeTest extends DateTimeTestBase { + + @Override + public String configFolder() { + return "config/fr-fr"; + } + + @Override + public NumberFormatter buildNumberFormatter() { + return new FrenchFormatter(); + } + + @Test + public void testNiceDate() { + // just check that the NumberParserFormatter functions do their job + assertEquals("mercredi vingt-huit avril deux mille un", + pf.niceDate(LocalDate.of(2021, 4, 28)).get()); + assertEquals("dimanche treize août", + pf.niceDate(LocalDate.of(-84, 8, 13)).now(LocalDate.of(-84, 8, 23)).get()); + } + + /* Please note that there is two ways of saying years and centuries before 2000. For example: + 1. mille (thousand) neuf (nine) cent (hundred) quatre-vingt (90) quatre (4) + 2. dix-neuf (nineteen) cent (hundred) quatre-vingt (90) quatre (4). (Slightly old-fashioned but common for years before 1900) + */ + + @Test + public void testNiceYear() { + // just check that the NumberParserFormatter functions do their job + assertEquals("mille neuf cent quatre-vingt quatre", pf.niceYear(LocalDate.of(1984, 4, 28)).get()); + assertEquals("mille huit cent dix av. J-C", pf.niceYear(LocalDate.of(-810, 8, 13)).get()); + } + + @Test + public void testNiceDateTime() { + // just check that the NumberParserFormatter functions do their job + assertEquals("mercredi douze septembre mille sept cent soixante quatre à midi", pf.niceDateTime(LocalDateTime.of(1764, 9, 12, 12, 0)).get()); + assertEquals("jeudi trois novembre trois cent vingt huit av. J-C à cinq heures sept", pf.niceDateTime(LocalDateTime.of(-328, 11, 3, 5, 7)).get()); + } +} diff --git a/numbers/src/test/java/org/dicio/numbers/lang/fr/NiceNumberTest.java b/numbers/src/test/java/org/dicio/numbers/lang/fr/NiceNumberTest.java new file mode 100644 index 00000000..bd9f4a56 --- /dev/null +++ b/numbers/src/test/java/org/dicio/numbers/lang/fr/NiceNumberTest.java @@ -0,0 +1,70 @@ +package org.dicio.numbers.lang.fr; + +import org.dicio.numbers.NumberParserFormatter; +import org.dicio.numbers.lang.en.EnglishFormatter; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; + +import static org.dicio.numbers.test.TestUtils.F; +import static org.junit.Assert.assertEquals; + +public class NiceNumberTest { + + private static NumberParserFormatter pf; + + @BeforeClass + public static void setup() { + pf = new NumberParserFormatter(new FrenchFormatter(), null); + } + + @Test + public void speech() { + assertEquals("trente-quatre et demi", pf.niceNumber(34.5).get()); + assertEquals("minus eighteen and trois cinquièmes", pf.niceNumber(-18.6).get()); + assertEquals("ninety eight and eighteen nineteenths", pf.niceNumber(98.947368421).get()); + assertEquals("minus five and six elevenths", pf.niceNumber(-5.5454545).get()); + assertEquals("seven ninths", pf.niceNumber(7.0 / 9).get()); + assertEquals("minus two seventeenths", pf.niceNumber(-2.0 / 17).get()); + assertEquals("four hundred and sixty five", pf.niceNumber(465).get()); + assertEquals("minus ninety one", pf.niceNumber(-91).get()); + assertEquals("zero", pf.niceNumber(0).get()); + } + + @Test + public void noSpeech() { + assertEquals("34 1/2", pf.niceNumber(34.5).speech(F).get()); + assertEquals("-18 3/5", pf.niceNumber(-18.6).speech(F).get()); + assertEquals("98 18/19", pf.niceNumber(98.947368421).speech(F).get()); + assertEquals("-5 6/11", pf.niceNumber(-5.5454545).speech(F).get()); + assertEquals("7/9", pf.niceNumber(7.0 / 9).speech(F).get()); + assertEquals("-2/17", pf.niceNumber(-2.0 / 17).speech(F).get()); + assertEquals("465", pf.niceNumber(465).speech(F).get()); + assertEquals("-91", pf.niceNumber(-91).speech(F).get()); + assertEquals("0", pf.niceNumber(0).speech(F).get()); + } + + @Test + public void customDenominators() { + assertEquals("moins quatre plus nd four tenths", pf.niceNumber(-4.4).denominators(Arrays.asList(2, 3, 4, 6, 7, 8, 9, 10, 11)).get()); + // Special case: for 1/2, we say "et demi" and not "plus un demi" + assertEquals("moins trois et demi", pf.niceNumber(-1.5).denominators(Arrays.asList(2)).get()); + + assertEquals("-64 6/12", pf.niceNumber(-64.5).speech(F).denominators(Collections.singletonList(12)).get()); + assertEquals("moins trois plus et cinq hundred thousand millionths", pf.niceNumber(-3.5).denominators(Arrays.asList(1000000, 2000000)).get()); + + assertEquals("9 1000000/2000000", pf.niceNumber(9.5).speech(F).denominators(Arrays.asList(2000000, 1000000)).get()); + assertEquals("zéro virgule huit", pf.niceNumber(4.0 / 5).denominators(Arrays.asList(2, 3, 4)).get()); + } + + @Test + public void invalidFraction() { + assertEquals("un virgule quatre-vingt quatre", pf.niceNumber(1.837).get()); + assertEquals("moins trente-huit virgule dix-neuf", pf.niceNumber(-38.192).get()); + assertEquals("3829,48", pf.niceNumber(3829.47832).speech(F).get()); + assertEquals("-7,19", pf.niceNumber(-7.1928).speech(F).get()); + assertEquals("-9322,38", pf.niceNumber(-9322 - 8.0 / 21).speech(F).get()); + } +} diff --git a/numbers/src/test/java/org/dicio/numbers/lang/fr/PronounceNumberTest.java b/numbers/src/test/java/org/dicio/numbers/lang/fr/PronounceNumberTest.java new file mode 100644 index 00000000..e54591f7 --- /dev/null +++ b/numbers/src/test/java/org/dicio/numbers/lang/fr/PronounceNumberTest.java @@ -0,0 +1,240 @@ +package org.dicio.numbers.lang.fr; + +import org.dicio.numbers.NumberParserFormatter; +import org.dicio.numbers.lang.en.EnglishFormatter; +import org.junit.BeforeClass; +import org.junit.Test; + +import static org.dicio.numbers.test.TestUtils.F; +import static org.dicio.numbers.test.TestUtils.T; +import static org.junit.Assert.assertEquals; + +public class PronounceNumberTest { + + private static NumberParserFormatter pf; + + @BeforeClass + public static void setup() { + pf = new NumberParserFormatter(new EnglishFormatter(), null); + } + + @Test + public void smallIntegers() { + assertEquals("zéro", pf.pronounceNumber(0).get()); + assertEquals("un", pf.pronounceNumber(1).get()); + assertEquals("dix", pf.pronounceNumber(10).get()); + assertEquals("quinze", pf.pronounceNumber(15).get()); + assertEquals("vingt", pf.pronounceNumber(20).get()); + assertEquals("vingt-sept", pf.pronounceNumber(27).get()); + assertEquals("trente", pf.pronounceNumber(30).get()); + assertEquals("trente trois", pf.pronounceNumber(33).get()); + } + + @Test + public void negativeSmallIntegers() { + assertEquals("moins un", pf.pronounceNumber(-1).get()); + assertEquals("moins dix", pf.pronounceNumber(-10).get()); + assertEquals("moins quinze", pf.pronounceNumber(-15).get()); + assertEquals("moins vingt", pf.pronounceNumber(-20).get()); + assertEquals("moins vingt-sept", pf.pronounceNumber(-27).get()); + assertEquals("moins trente", pf.pronounceNumber(-30).get()); + assertEquals("moins trente-trois", pf.pronounceNumber(-33).get()); + } + + @Test + public void decimals() { + assertEquals("zéro virgule zéro cinq", pf.pronounceNumber(0.05).get()); + assertEquals("moins zéro virgule zéro cinq", pf.pronounceNumber(-0.05).get()); + // In french we mostly say decimals as we would with dozens or with hundreds if there is only three places . + assertEquals("un virgule vingt-trois", pf.pronounceNumber(1.234).get()); + // vingt-et-un is also acceptable since 1990's orthographical reform. + assertEquals("vingt et un virgule deux cent soixante-quatre", pf.pronounceNumber(21.264).places(5).get()); + assertEquals("vingt et un virgule deux cent soixante-quatre", pf.pronounceNumber(21.264).places(4).get()); + assertEquals("vingt et un virgule deux cent soixante-quatre", pf.pronounceNumber(21.264).places(3).get()); + assertEquals("vingt et un virgule vingt-six", pf.pronounceNumber(21.264).places(2).get()); + assertEquals("vingt et un virgule trois", pf.pronounceNumber(21.264).places(1).get()); + assertEquals("vingt et un", pf.pronounceNumber(21.264).places(0).get()); + // Same with negative numbers + assertEquals("moins vingt et un virgule deux cent soixante-quatre", pf.pronounceNumber(-21.264).places(5).get()); + assertEquals("moins vingt et un virgule deux cent soixante-quatre", pf.pronounceNumber(-21.264).places(4).get()); + assertEquals("moins vingt et un virgule deux cent soixante-quatre", pf.pronounceNumber(-21.264).places(3).get()); + assertEquals("moins vingt et un virgule vingt-six", pf.pronounceNumber(-21.264).places(2).get()); + assertEquals("moins vingt et un virgule trois", pf.pronounceNumber(-21.264).places(1).get()); + assertEquals("moins vingt et un", pf.pronounceNumber(-21.264).places(0).get()); + } + + @Test + public void roundingDecimals() { + assertEquals("zéro", pf.pronounceNumber(0.05).places(0).get()); + assertEquals("zéro", pf.pronounceNumber(-0.4).places(0).get()); + assertEquals("moins vingt-deux", pf.pronounceNumber(-21.7).places(0).get()); + assertEquals("quatre-vingt neuf", pf.pronounceNumber(89.2).places(0).get()); + assertEquals("quatre-vingt-dix", pf.pronounceNumber(89.9).places(0).get()); + assertEquals("moins un", pf.pronounceNumber(-0.5).places(0).get()); + assertEquals("zéro", pf.pronounceNumber(-0.4).places(0).get()); + assertEquals("six virgue trois", pf.pronounceNumber(6.28).places(1).get()); + assertEquals("moins trois virgule un", pf.pronounceNumber(-3.14).places(1).get()); + // note: 3.15 does not yield "three point two" because of floating point errors + assertEquals("trois virgule deux", pf.pronounceNumber(3.150001).places(1).get()); + assertEquals("zéro virgule trois", pf.pronounceNumber(0.25).places(1).get()); + assertEquals("moins zéro virgule trois", pf.pronounceNumber(-0.25).places(1).get()); + assertEquals("dix-neuf", pf.pronounceNumber(19.004).get()); + } + + @Test + public void hundred() { + assertEquals("cent", pf.pronounceNumber(100).get()); + assertEquals("six cent soixante-dix-huit", pf.pronounceNumber(678).get()); + + assertEquals("cent trois millions, deux cent cinquante-quatre mille, six cent cinquante-quatre", + pf.pronounceNumber(103254654).get()); + assertEquals("un million, cinq cent douze mille, quatre cent cinquante-sept", + pf.pronounceNumber(1512457).get()); + assertEquals("deux cent neuf mille, neuf cent quatre-vingt-seize", + pf.pronounceNumber(209996).get()); + } + + @Test + public void year() { + assertEquals("mille quatre cent cinquante-six", pf.pronounceNumber(1456).get()); + assertEquals("mille neuf cent quatre-vingt quatre", pf.pronounceNumber(1984).get()); + assertEquals("mille huit-cent un", pf.pronounceNumber(1801).get()); + assertEquals("mille cent", pf.pronounceNumber(1100).get()); + assertEquals("mille deux cent un", pf.pronounceNumber(1201).get()); + assertEquals("mille cinq cent dix", pf.pronounceNumber(1510).get()); + assertEquals("mille six", pf.pronounceNumber(1006).get()); + assertEquals("mille", pf.pronounceNumber(1000).get()); + assertEquals("deux mille", pf.pronounceNumber(2000).get()); + assertEquals("deux mille quinze", pf.pronounceNumber(2015).get()); + assertEquals("quatre mille huit cent vingt-sept", pf.pronounceNumber(4827).get()); + } + + @Test + public void scientificNotation() { + assertEquals("zéro", pf.pronounceNumber(0.0).scientific(T).get()); + assertEquals("trois virgule trois fois dix puissance un", + pf.pronounceNumber(33).scientific(T).get()); + assertEquals("deux virgule neuf fois dix puissance huit", + pf.pronounceNumber(299492458).scientific(T).get()); + assertEquals("deux virgule quatre-vingt-dix-neuf quatre-vingt-dix-sept vingt-cinq fois dix puissance huit", + pf.pronounceNumber(299792458).scientific(T).places(6).get()); + assertEquals("un virgule six cent soixante-dix-sept fois dix puissance moins vingt-sept", + pf.pronounceNumber(1.672e-27).scientific(T).places(3).get()); + + // auto scientific notation when number is too big to be pronounced + assertEquals("deux virgule quatre-vingt-quinze fois dix puissance vingt-quatre", + pf.pronounceNumber(2.9489e24).get()); + } + + // Inexistant in french + /* + private void assertShortLongScale(final double number, + final String shortScale, + final String longScale) { + assertEquals(shortScale, pf.pronounceNumber(number).shortScale(T).get()); + assertEquals(longScale, pf.pronounceNumber(number).shortScale(F).get()); + } + + @Test + public void largeNumbers() { + assertShortLongScale(1001892, + "one million, one thousand, eight hundred and ninety two", + "one million, one thousand, eight hundred and ninety two"); + assertShortLongScale(299792458, + "two hundred and ninety nine million, seven hundred and ninety two thousand, four hundred and fifty eight", + "two hundred and ninety nine million, seven hundred and ninety two thousand, four hundred and fifty eight"); + assertShortLongScale(-100202133440.0, + "minus one hundred billion, two hundred and two million, one hundred and thirty three thousand, four hundred and forty", + "minus one hundred thousand two hundred and two million, one hundred and thirty three thousand, four hundred and forty"); + assertShortLongScale(20102000987000.0, + "twenty trillion, one hundred and two billion, nine hundred and eighty seven thousand", + "twenty billion, one hundred and two thousand million, nine hundred and eighty seven thousand"); + assertShortLongScale(-2061000560007060.0, + "minus two quadrillion, sixty one trillion, five hundred and sixty million, seven thousand, sixty", + "minus two thousand sixty one billion, five hundred and sixty million, seven thousand, sixty"); + assertShortLongScale(9111202032999999488.0, // floating point errors + "nine quintillion, one hundred and eleven quadrillion, two hundred and two trillion, thirty two billion, nine hundred and ninety nine million, nine hundred and ninety nine thousand, four hundred and eighty eight", + "nine trillion, one hundred and eleven thousand two hundred and two billion, thirty two thousand nine hundred and ninety nine million, nine hundred and ninety nine thousand, four hundred and eighty eight"); + + assertShortLongScale(29000.0, "twenty nine thousand", "twenty nine thousand"); + assertShortLongScale(301000.0, "three hundred and one thousand", "three hundred and one thousand"); + assertShortLongScale(4000000.0, "four million", "four million"); + assertShortLongScale(50000000.0, "fifty million", "fifty million"); + assertShortLongScale(630000000.0, "six hundred and thirty million", "six hundred and thirty million"); + assertShortLongScale(7000000000.0, "seven billion", "seven thousand million"); + assertShortLongScale(16000000000.0, "sixteen billion", "sixteen thousand million"); + assertShortLongScale(923000000000.0, "nine hundred and twenty three billion", "nine hundred and twenty three thousand million"); + assertShortLongScale(1000000000000.0, "one trillion", "one billion"); + assertShortLongScale(29000000000000.0, "twenty nine trillion", "twenty nine billion"); + assertShortLongScale(308000000000000.0, "three hundred and eight trillion", "three hundred and eight billion"); + assertShortLongScale(4000000000000000.0, "four quadrillion", "four thousand billion"); + assertShortLongScale(52000000000000000.0, "fifty two quadrillion", "fifty two thousand billion"); + assertShortLongScale(640000000000000000.0, "six hundred and forty quadrillion", "six hundred and forty thousand billion"); + assertShortLongScale(7000000000000000000.0, "seven quintillion", "seven trillion"); + + // TODO maybe improve this + assertShortLongScale(1000001, "one million, one", "one million, one"); + assertShortLongScale(-2000000029, "minus two billion, twenty nine", "minus two thousand million, twenty nine"); + }*/ + + @Test + public void ordinal() { + // small numbers + assertEquals("premier", pf.pronounceNumber(1).shortScale(T).ordinal(T).get()); + assertEquals("premier", pf.pronounceNumber(1).shortScale(F).ordinal(T).get()); + assertEquals("dixième", pf.pronounceNumber(10).shortScale(T).ordinal(T).get()); + assertEquals("dixième", pf.pronounceNumber(10).shortScale(F).ordinal(T).get()); + assertEquals("quinzième", pf.pronounceNumber(15).shortScale(T).ordinal(T).get()); + assertEquals("quinzième", pf.pronounceNumber(15).shortScale(F).ordinal(T).get()); + assertEquals("vingtième", pf.pronounceNumber(20).shortScale(T).ordinal(T).get()); + assertEquals("vingtième", pf.pronounceNumber(20).shortScale(F).ordinal(T).get()); + assertEquals("vingt-septième", pf.pronounceNumber(27).shortScale(T).ordinal(T).get()); + assertEquals("vingt-septième", pf.pronounceNumber(27).shortScale(F).ordinal(T).get()); + assertEquals("trentième", pf.pronounceNumber(30).shortScale(T).ordinal(T).get()); + assertEquals("trentième", pf.pronounceNumber(30).shortScale(F).ordinal(T).get()); + assertEquals("trente-troisième", pf.pronounceNumber(33).shortScale(T).ordinal(T).get()); + assertEquals("trente-troisième", pf.pronounceNumber(33).shortScale(F).ordinal(T).get()); + assertEquals("centième", pf.pronounceNumber(100).shortScale(T).ordinal(T).get()); + assertEquals("centième", pf.pronounceNumber(100).shortScale(F).ordinal(T).get()); + assertEquals("millième", pf.pronounceNumber(1000).shortScale(T).ordinal(T).get()); + assertEquals("millième", pf.pronounceNumber(1000).shortScale(F).ordinal(T).get()); + assertEquals("dix-millième", pf.pronounceNumber(10000).shortScale(T).ordinal(T).get()); + assertEquals("dix-millième", pf.pronounceNumber(10000).shortScale(F).ordinal(T).get()); + assertEquals("deux-millième", pf.pronounceNumber(200).shortScale(T).ordinal(T).get()); + assertEquals("deux-millième", pf.pronounceNumber(200).shortScale(F).ordinal(T).get()); + assertEquals("huit-mille-quatre-vingtième", pf.pronounceNumber(18691).ordinal(T).shortScale(T).get()); + assertEquals("huit-mille-quatre-vingtième", pf.pronounceNumber(18691).ordinal(T).shortScale(F).get()); + assertEquals("mille-cinq-cent-soixante-septième", pf.pronounceNumber(1567).ordinal(T).shortScale(T).get()); + assertEquals("mille-cinq-cent-soixante-septième", pf.pronounceNumber(1567).ordinal(T).shortScale(F).get()); + + // big numbers + assertEquals("dix-huit-millionième", pf.pronounceNumber(18000000).ordinal(T).get()); + assertEquals("dix-huit-mille-centième", pf.pronounceNumber(18000100).ordinal(T).get()); + assertEquals("cent-vingt-sept-milliardième", pf.pronounceNumber(127000000000.0).ordinal(T).shortScale(T).get()); + assertEquals("deux-cent-un-millionième", pf.pronounceNumber(201000000000.0).ordinal(T).shortScale(F).get()); + //TODO: Check this for french correctness as well as short/long scale issues (billion/billard) + assertEquals("neuf-cent-treize-milliard-quatre-vingt-million-six-cent-mille-soixante-mille-cent-soixante-quatrième", pf.pronounceNumber(913080600064.0).ordinal(T).shortScale(T).get()); + assertEquals("neuf-cent-treize-mille-quatre-vingt-million-six-cent-mille-soixante-mille-soixante-quatrième", pf.pronounceNumber(913080600064.0).ordinal(T).shortScale(F).get()); + assertEquals("trilliard-deux-millionième", pf.pronounceNumber(1000002000000.0).ordinal(T).shortScale(T).get()); + assertEquals("millard-deux-millionième", pf.pronounceNumber(1000002000000.0).ordinal(T).shortScale(F).get()); + assertEquals("quatre-triliard-un-millionième", pf.pronounceNumber(4000001000000.0).ordinal(T).shortScale(T).get()); + assertEquals("quatre-milliard-un-millionième", pf.pronounceNumber(4000001000000.0).ordinal(T).shortScale(F).get()); + + // decimal numbers and scientific notation: the behaviour should be the same as with ordinal=F + assertEquals("deux virgule soixante-dix-huit", pf.pronounceNumber(2.78).ordinal(T).get()); + assertEquals("tiers", pf.pronounceNumber(2.78).places(0).ordinal(T).get()); + assertEquals("dix-neuf", pf.pronounceNumber(19.004).ordinal(T).get()); + assertEquals("huit cent trente-trois millions, quatre cent vingt-huit mille, quatre-vingt-douze virgule cent quatre-vingt-trois", pf.pronounceNumber(830438092.1829).places(3).ordinal(T).get()); + assertEquals("deux virgule cinquante-quatre fois dix puissance six", pf.pronounceNumber(2.54e6).ordinal(T).scientific(T).get()); + } + + @Test + public void edgeCases() { + assertEquals("zéro", pf.pronounceNumber(0.0).get()); + assertEquals("zéro", pf.pronounceNumber(-0.0).get()); + assertEquals("infini", pf.pronounceNumber(Double.POSITIVE_INFINITY).get()); + assertEquals("moins l'infini", pf.pronounceNumber(Double.NEGATIVE_INFINITY).scientific(F).get()); + assertEquals("moins l''infini", pf.pronounceNumber(Double.NEGATIVE_INFINITY).scientific(T).get()); + assertEquals("Non défini", pf.pronounceNumber(Double.NaN).get()); + } +} diff --git a/numbers/src/test/resources/config/fr-fr/date_time_test.json b/numbers/src/test/resources/config/fr-fr/date_time_test.json new file mode 100644 index 00000000..448f2192 --- /dev/null +++ b/numbers/src/test/resources/config/fr-fr/date_time_test.json @@ -0,0 +1,43 @@ +{ + "test_nice_year": { + "1": {"datetime_param": "1, 1, 31, 13, 22, 3", "bc": "True", "assertEqual": "un avant Jésus Christ" }, + "2": {"datetime_param": "10, 1, 31, 13, 22, 3", "bc": "True", "assertEqual": "dix avant Jésus Christ" }, + "3": {"datetime_param": "92, 1, 31, 13, 22, 3", "bc": "True", "assertEqual": "quatre-vingt-douze avant Jésus Christ" }, + "4": {"datetime_param": "803, 1, 31, 13, 22, 3", "bc": "None", "assertEqual": "huit-cent-trois" }, + "5": {"datetime_param": "111, 1, 31, 13, 22, 3", "bc": "None", "assertEqual": "cent-onze" }, + "6": {"datetime_param": "454, 1, 31, 13, 22, 3", "bc": "None", "assertEqual": "quatre-cent-cinquante-quatre" }, + "7": {"datetime_param": "2005, 1, 31, 13, 22, 3", "bc": "False", "assertEqual": "deux-mille-cinq" }, + "8": {"datetime_param": "1012, 1, 31, 13, 22, 3", "bc": "False", "assertEqual": "mille-douze" }, + "9": {"datetime_param": "1046, 1, 31, 13, 22, 3", "bc": "False", "assertEqual": "mille-quarante-six" }, + "10": {"datetime_param": "1807, 1, 31, 13, 22, 3", "bc": "None", "assertEqual": "mille-huit-cent-sept" }, + "11": {"datetime_param": "1717, 1, 31, 13, 22, 3", "bc": "None", "assertEqual": "mille-sept-cent-dix-sept" }, + "12": {"datetime_param": "1988, 1, 31, 13, 22, 3", "bc": "None", "assertEqual": "mille-neuf-cent-quatre-vingt-huit"}, + "13": {"datetime_param": "2009, 1, 31, 13, 22, 3", "bc": "None", "assertEqual": "deux-mille-neuf"}, + "14": {"datetime_param": "2018, 1, 31, 13, 22, 3", "bc": "None", "assertEqual": "deux-mille-dix-huit"}, + "15": {"datetime_param": "2021, 1, 31, 13, 22, 3", "bc": "None", "assertEqual": "deux-mille-vingt-et-un"}, + "16": {"datetime_param": "2030, 1, 31, 13, 22, 3", "bc": "None", "assertEqual": "deux-mille-trente"}, + "17": {"datetime_param": "2100, 1, 31, 13, 22, 3", "bc": "False", "assertEqual": "deux-mille-cent" }, + "18": {"datetime_param": "1000, 1, 31, 13, 22, 3", "bc": "None", "assertEqual": "mille" }, + "19": {"datetime_param": "2000, 1, 31, 13, 22, 3", "bc": "None", "assertEqual": "deux-mille" }, + "20": {"datetime_param": "3120, 1, 31, 13, 22, 3", "bc": "True", "assertEqual": "trois-mille-cent-vingt avant Jésus Christ" }, + "21": {"datetime_param": "3241, 1, 31, 13, 22, 3", "bc": "True", "assertEqual": "trois-mille-deux-cent-quarante-et-un avant Jésus Christ" }, + "22": {"datetime_param": "5200, 1, 31, 13, 22, 3", "bc": "False", "assertEqual": "cinq-mille-deux-cents" }, + "23": {"datetime_param": "1100, 1, 31, 13, 22, 3", "bc": "False", "assertEqual": "mille-cent" }, + "24": {"datetime_param": "2100, 1, 31, 13, 22, 3", "bc": "False", "assertEqual": "deux-mille-cent" } + }, + "test_nice_date": { + "1": {"datetime_param": "2017, 1, 31, 0, 2, 3", "now": "None", "assertEqual": "mardi trente-et-un janvier deux-mille-dix-sept"}, + "2": {"datetime_param": "2018, 2, 4, 0, 2, 3", "now": "2017, 1, 1, 0, 2, 3", "assertEqual": "dimanche quatre février deux-mille-dix-huit"}, + "3": {"datetime_param": "2018, 2, 4, 0, 2, 3", "now": "2018, 1, 1, 0, 2, 3", "assertEqual": "dimanche quatre février"}, + "4": {"datetime_param": "2018, 2, 4, 0, 2, 3", "now": "2018, 2, 1, 0, 2, 3", "assertEqual": "dimanche quatre"}, + "5": {"datetime_param": "2018, 2, 4, 0, 2, 3", "now": "2018, 2, 3, 0, 2, 3", "assertEqual": "demain"}, + "6": {"datetime_param": "2018, 2, 4, 0, 2, 3", "now": "2018, 2, 4, 0, 2, 3", "assertEqual": "aujourd'hui"}, + "7": {"datetime_param": "2018, 2, 4, 0, 2, 3", "now": "2018, 2, 5, 0, 2, 3", "assertEqual": "hier"}, + "8": {"datetime_param": "2018, 2, 4, 0, 2, 3", "now": "2018, 2, 6, 0, 2, 3", "assertEqual": "dimanche quatre février"}, + "9": {"datetime_param": "2018, 2, 4, 0, 2, 3", "now": "2019, 2, 6, 0, 2, 3", "assertEqual": "dimanche quatre février deux-mille-dix-huit"} + }, + "test_nice_date_time": { + "1": {"datetime_param": "2017, 1, 31, 13, 22, 3", "now": "None", "use_24hour": "False", "use_ampm": "True", "assertEqual": "mardi trente-et-un janvier deux-mille-dix-sept une heure vingt-deux de l'après-midi"}, + "2": {"datetime_param": "2017, 1, 31, 13, 22, 3", "now": "None", "use_24hour": "True", "use_ampm": "False", "assertEqual": "mardi trente-et-un janvier deux-mille-dix-sept treize heures vingt-deux"} + } +}