/* * [French.java] * * Summary: Display a number in French words. * * Copyright: (c) 1999-2017 Roedy Green, Canadian Mind Products, http://mindprod.com * * Licence: This software may be copied and used freely for any purpose but military. * http://mindprod.com/contact/nonmil.html * * Requires: JDK 1.8+ * * Created with: JetBrains IntelliJ IDEA IDE http://www.jetbrains.com/idea/ * * Version History: * 1.0 1999-01-17 * 1.1 1999-01-18 add plurals * moins for negative * 71 is soixante-onze * accent on zéro * trillion defined at 10ˆ15, not 10ˆ18 * 1.2 1999-01-18 trillion is 10ˆ18 * quatre-vingt un instead of quatre-vingt et un. * 1.3 1999-01-21 make all language specific String literals into constants. * 1.4 2001-06-20 corrects a bug in French handling 301 trois * cent un instead of trois cents et un. * quatre-vingt with a dash. * notes on Vaudois and Romand * 1.5 2001-06-22 * 1.6 2006-10-15 10^9 un billiard * 10^18 un trillion * 10^21 un trilliard * 10^24 un quadrillion * 10^600 un centillion */ package com.mindprod.inwords; /** * Display a number in French words. *

* e.g. * -12345 -> "moins douze mille trois cent quarante-cinq". *

* Special thanks go to Jonathan Revusky <jrevusky@bigfoot.com> * for helping prod my recall of high school French, and makes correction after * correction. Then later to jpmartin@yahoo.com who found several remaining errors. *

* For rules on how French numbers work, see: * http://projets.csrs.qc.ca/goeland/envolee/francais/adjectifs2.html#num * (Official Canadian site) * http://www.olf.gouv.qc.ca/ressources/faq/492.html * (unofficial site about "Swiss French") * http://www.chez.com/languefrancaise/fllf/199910/septante.htm *

* Francais (Suisse Romand)", * is identical to French except that * 70...79 is "septante", ..., "septante-neuf" * 90...99 is "nonante", ..., "nonante-neuf" *

* "Francais (Vaudois)" you have same rules as * Francais (Suisse Romand), but also: * 80...89 "huitante", ..., "huitante-neuf" * * @author Roedy Green, Canadian Mind Products * @version 1.6 2006-10-15 10^9 un billiard * 10^18 un trillion * 10^21 un trilliard * 10^24 un quadrillion * 10^600 un centillion * @since 1999-01-17 */ public final class French implements ToWords { private static final int FIRST_COPYRIGHT_YEAR = 1999; private static final String AND = "et"; /** * undisplayed copyright notice * * @noinspection UnusedDeclaration */ private static final String EMBEDDED_COPYRIGHT = "Copyright: (c) 1999-2017 Roedy Green, Canadian Mind Products, http://mindprod.com"; private static final String MINUS = "moins"; private static final String PLURAL = "s"; private static final String ZERO = "z\u00e9ro"/* zéro zéro */; private static final String[] groupName = { /* * We only need up to a quintillion, since a long is about 9 * 10 ^ 18 */ /* * American: unit, hundred, thousand, million, billion, trillion, * quintillion */ /* 1 */ "", /* 100 */ "cent", /* 1,000 */ "mille", /* 1,000,000 */ "million", /* 1,000,000,000 */ "milliard", /* 1,000,000,000,000 */ "billion", /* 1,000,000,000,000,000 */ "billiard", /* 1,000,000,000,000,000,000 */ "trillion" }; private static final String[] lowName = { /* zero is shown as "" since it is never used in combined forms */ /* 0 .. 20 */ /* 0 */ "", /* 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" }; private static final String[] tys = { /* 0, 10, 20, 30 ... 90 */ "", /* 0 */ "", /* 10 */ "vingt" /* 20 */, "trente" /* 30 */, "quarante" /* 40 */, "cinquante" /* 50 */, "soixante" /* 60 */, "soixante-dix" /* 70 */, "quatre-vingt" /* 80 */, "quatre-vingt-dix" /* 90 */ }; private static final int[] divisor = { /* * HowToProcess many of this group is needed to form one of the succeeding group. */ /* * American: unit, hundred, thousand, million, billion, billiard, trillion, * quintillion */ 100, 10, 1000, 1000, 1000, 1000, 1000, 1000 }; /** * test harness * * @param args not used */ public static void main( String[] args ) { Test.test( new French(), new DecimalDots() ); } // end main /** * convert long integer into French words. e.g. -12345 -> "moins douze mille trois cent quarante-cinq" Handles * negative and positive integers. on range -Long.MAX_VALUE .. Long.MAX_VALUE; It cannot handle Long.MIN_VALUE; * * @param num number to convert to words * * @return words */ @SuppressWarnings( { "WeakerAccess" } ) public String toWords( long num ) { if ( num == 0 ) { return ZERO; } boolean negative = ( num < 0 ); if ( negative ) { num = -num; } String s = ""; // Work least significant digit to most, right to left. // until high order part is all 0s. // group indexes group, not power of ten. for ( int group = 0; num > 0; group++ ) { int remdr = ( int ) ( num % divisor[ group ] ); num = num / divisor[ group ]; // out.println( "group:" + group + " r:" + remdr + " n:" + num ); if ( remdr == 0 ) { continue; } String t; if ( remdr == 1 ) { // not un cent just cent, not un mille, just mille. // However, other groups do get the un if ( 1 <= group && group <= 2 ) { t = "";/* cent, mille groups, no un */ } else { t = lowName[ 1 ];/* un */ } } else if ( remdr < 20 ) { t = lowName[ remdr ]; } else if ( remdr < 100 ) { int units = remdr % 10; int tens = remdr / 10; // handle 71 as soixante-onze instead of soixante-dix et un if ( tens == 7 | tens == 9 ) { tens -= 1; units += 10; } t = tys[ tens ]; switch ( units ) { case 0: /* e.g. vingt */ break; case 1: if ( tens == 8 || tens == 0 ) { /* quatre-vingt un, trois cents un */ t += " " + lowName[ 1 ]; } else { /* e.g. vingt et un */ t += " " + AND + " " + lowName[ 1 ]; } break; default: /* e.g. vingt-trois */ t += "-" + lowName[ units ]; break; } // end switch } else { t = toWords( remdr ); } boolean leftPad = ( t.length() != 0 ); // sometimes add s to groupname to make it plural. boolean plural; switch ( group ) { case 1:// cent is only pluralised for even 100s, group.e. // nothing // to the right. plural = remdr > 1 && s.trim().equals( "" ); break; case 0:// units has no word, so does not need plural case 2:// mille not pluralized plural = false; break; default:// rest, plural if more than one group plural = remdr > 1; break; } s = t + ( leftPad ? " " : "" ) + groupName[ group ] + ( plural ? PLURAL : "" ) + " " + s; } // end for s = s.trim(); if ( negative ) { s = MINUS + " " + s; } return s; } // end toWords } // end French