Skip to content

French language support #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,16 +1,28 @@
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<Long, String> NUMBER_NAMES = new HashMap<Long, String>() {{}};
final Map<Long, String> NUMBER_NAMES_SHORT_SCALE = new HashMap<Long, String>(NUMBER_NAMES) {{}};
final Map<Long, String> NUMBER_NAMES_LONG_SCALE = new HashMap<Long, String>(NUMBER_NAMES) {{}};
final Map<Long, String> ORDINAL_NAMES = new HashMap<Long, String>() {{}};
final Map<Long, String> ORDINAL_NAMES_SHORT_SCALE = new HashMap<Long, String>(ORDINAL_NAMES) {{}};
final Map<Long, String> ORDINAL_NAMES_LONG_SCALE = new HashMap<Long, String>(ORDINAL_NAMES) {{}};

protected FrenchFormatter(String configFolder) {

protected FrenchFormatter() {
super("config/fr-fr");
}


// Copied from EnglishFormatter with no changes except for strings.
@Override
public String niceNumber(MixedFraction mixedFraction, boolean speech) {
Expand All @@ -22,7 +34,7 @@ public String niceNumber(MixedFraction mixedFraction, boolean speech) {

String denominatorString;
if (mixedFraction.denominator == 2) {
denominatorString = "moitié";
denominatorString = "moitié"; //FIXME: Should it be "demi" instead?
} else if (mixedFraction.denominator == 4) {
denominatorString = "quart";
} else {
Expand All @@ -33,7 +45,7 @@ public String niceNumber(MixedFraction mixedFraction, boolean speech) {

final String numeratorString;
if (mixedFraction.numerator == 1) {
numeratorString = "un";
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";
Expand All @@ -48,7 +60,7 @@ public String niceNumber(MixedFraction mixedFraction, boolean speech) {

} else {
return niceNumberNotSpeech(mixedFraction);
} return null;
}
}

// Copied from EnglishFormatter with no changes except for strings.
Expand All @@ -66,7 +78,7 @@ public String pronounceNumber(double number, int places, boolean shortScale, boo
// 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.ENGLISH, "%E", number);
final String scientificFormatted = String.format(Locale.FRANCE, "%E", number);
final String[] parts = scientificFormatted.split("E", 2);
final double power = Integer.parseInt(parts[1]);

Expand All @@ -90,7 +102,7 @@ public String pronounceNumber(double number, int places, boolean shortScale, boo
// do not add minus if number will be rounded to 0
//result.append(scientific ? "negative " : "minus ");

// The negative/minus disctinction dosen't exist in french in this context.
// The negative/minus distinction doesn't exist in french in this context.
result.append("moins");
}
}
Expand All @@ -106,11 +118,11 @@ public String pronounceNumber(double number, int places, boolean shortScale, boo
result.append(NUMBER_NAMES.get(numberLong / 100));
result.append(" ");
if (numberLong % 100 == 0) {
// 1900 => nineteen hundred
// 1900 => mille neuf cent (thousand nine hundred)
result.append(NUMBER_NAMES.get(100L));
} else if (numberLong % 100 < 10 && numberLong % 100 != 0) {
// 1906 => nineteen oh six
result.append("virgule ");
// 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
Expand All @@ -131,7 +143,9 @@ public String pronounceNumber(double number, int places, boolean shortScale, boo
}
result.append(NUMBER_NAMES.get(numberLong));

} else if (shortScale) {
}
// No short scale system in french.
/*else if (shortScale) {
boolean ordi = ordinal && numberIsWhole; // not ordinal if not whole
final List<Long> groups = Utils.splitByModulus(numberLong, 1000);
final List<String> groupNames = new ArrayList<>();
Expand Down Expand Up @@ -163,8 +177,8 @@ public String pronounceNumber(double number, int places, boolean shortScale, boo

appendSplitGroups(result, groupNames);

} else {
boolean ordi = ordinal && numberIsWhole; // not ordinal if not whole
}*/ else {
boolean ordi = ordinal && numberIsWhole; // not ordinal if not whole in french as well as in english
final List<Long> groups = Utils.splitByModulus(numberLong, 1000000);
final List<String> groupNames = new ArrayList<>();
for (int i = 0; i < groups.size(); ++i) {
Expand All @@ -177,14 +191,14 @@ public String pronounceNumber(double number, int places, boolean shortScale, boo
if (z < 1000) {
groupName = subThousand(z, i == 0 && ordi);
} else {
groupName = subThousand(z / 1000, false) + " thousand";
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 = "thousandth"; // remove "one" from "one thousandth"
groupName = "millième"; // remove "one" from "one thousandth"
} else {
groupName += "th";
groupName += "ième";
}
}
}
Expand Down Expand Up @@ -212,10 +226,10 @@ public String pronounceNumber(double number, int places, boolean shortScale, boo
}

if (realPlaces > 0) {
if (number < 1.0 && (result.length() == 0 || "minus ".contentEquals(result))) {
result.append("zero"); // nothing was written before
if (number < 1.0 && (result.length() == 0 || "moins ".contentEquals(result))) {
result.append("zéro"); // nothing was written before
}
result.append(" point");
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) {
Expand All @@ -227,7 +241,33 @@ public String pronounceNumber(double number, int places, boolean shortScale, boo
return result.toString();
}

@Override
/**
* @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<String> 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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,14 @@ public void testNiceDate() {
pf.niceDate(LocalDate.of(-84, 8, 13)).now(LocalDate.of(-84, 8, 23)).get());
}

/* Please note that there is two way of saying years and centuries before 2000. For exemple:
/* 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());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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());
}
}
Loading