/* * [InWords.java] * * Summary: displays numbers in words in various languages. * * 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-12 initial version * 1.1 1999-01-13 add Norwegian, Esperanto, British English. * 1.2 1999-01-13 correct millionoj bug in Esperanto version. * 1.3 1999-01-14 add Bahasa Indonesia * 1.4 1999-01-14 add Swedish * 1.5 1999-01-15 languages now read from com.mindprod.inwords.Inwords.properties file * rather than being hard-coded into the InWords program. * add Dutch - minor touchups to other languages. * 1.6 1999-01-16 corrections to British English and Dutch. * don't load language class until used. * 1.7 1999-01-17 add hex, octal, binary, and Martian. * 1.8 1999-01-18 add Icelandic * remove some redundant remr tests. * 1.9 1999-01-18 table driven test * add French * 2.0 1999-01-18 make refresh protected so can call from inner class without * upsetting JDK 1.2 class verifier * corrections to French * corrections to Dutch * 2.1 1999-01-18 add German * 2.2 1999-01-18 add old Dutch and Banker's Dutch * 2.3 1999-01-19 add Roman numerals * minor corrections to German * 2.4 1999-01-21 add Polish * 2.5 1999-01-22 add American ordinals * tidy classes so all language-specific Strings are constants rather * than literals. * 2.6 1999-02-04 corrections to Swedish and Icelandic * 2.7 1999-02-05 add RAM byte measure and Metric gram measure. * 2.8 1999-02-07 add Tagalog (Filipino) * 2.9 1999-08-11 correct 8 bug in Roman numerals * 3.0 1999-10-29 add Japanese * 3.1 2000-01-01 * 3.2 2001-01-01 * 3.3 2002-01-01 * 3.4 2004-07-07 add triple plurals to Polish above 1 million.- * add about box -new style gridbag * 3.5 2005-09-27 Correct bug in display of negative nine digit numbers. - * in DecimalDots and DecimalCommas * 3.6 2005-12-16 add Javadoc * 3.7 2006-10-11 add Swiss Vaudois French * 3.8 2006-10-15 reformat with IntelliJ, add Javadoc. * 3.9 2006-10-16 new French. rename DecimalSpaces * 4.0 2007-05-24 add pad, icon, IntelliJ inspector. * 4.1 2008-04-02 add build number to title * 4.2 2009-01-20 correct many mistakes in Spanish * 4.3 2009-01-21 fine tune Spanish and SpanishForCheques * 4.4 2009-01-22 add Italian, JSpinner, convert static inWords to instance toWords. * 4.5 2009-01-23 now uses font that will display Japanese * 4.6 2009-05-01 corrections to Italian. Tidy package and class comments. */ /* TODO: echo back number in comma format cleaned up. Right justify number user keys in. use exceptions for numbers classes can't handle. handle places to right of decimal. */ package com.mindprod.inwords; import com.mindprod.common18.Build; import com.mindprod.common18.CMPAboutJBox; import com.mindprod.common18.Common18; import com.mindprod.common18.FontFactory; import com.mindprod.common18.HybridJ; import com.mindprod.common18.JEButton; import com.mindprod.common18.Misc; import com.mindprod.common18.VersionCheck; import javax.swing.JApplet; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JSpinner; import javax.swing.JTextArea; import javax.swing.SpinnerNumberModel; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import java.awt.Color; import java.awt.Container; import java.awt.Dimension; import java.awt.Font; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.io.IOException; import java.io.InputStream; import static java.lang.System.*; /** * displays numbers in words in various languages. *

* Teaches you how to count in various languages. * Spells out numbers in words. * Test driver for the various InWords classes. *

* when you add a language, update the following places: * inwords.properties * inwords.use * InWords.java classcomment * inwords.xml PAD file. *

* Inwords is the GUI * ToWords is the interface the languge classes must implement * French German etc are classes for each language *

* Learn To Count shows you how to count in any of the * following languages: *

* Bahasa Indonesia * Binary * Decimal (several variants) * Dutch (modern, old and banker's) * English (British, North American, Ordinals) * Esperanto * French * German * Grams * Hexadecimal * Icelandic * Japanese * Martian * Metric Metric Prefixes (grams) * Norwegian * Octal * Polish * RAM (bytes) * Roman Numerals * Spanish * Swedish * Swiss French * Tagalog * Time Intervals *

* You can enter a number and it displays it in words: e.g. * -12345 to "minus twelve thousand three hundred forty-five" *

* The classes it uses for conversion can be easily * cannibalised for your own applications. *

* for example: *

* String numStr = BritishEnglish.inWords(-1234); *

* will generate the string: *

* "minus one thousand two hundred four" *

* handles longs up to Long.MAX_VALUE, namely: * 9,223,372,036,854,775,807 *

* Useful to learn to count in a foreign language. The InWords * package classes are also useful for cheque writing or legal * documents. * * * @author Roedy Green, Canadian Mind Products * @version 4.6 2009-05-01 corrections to Italian. Tidy package and class comments. * @since 1999-01-12 */ @SuppressWarnings( { "FieldCanBeLocal" } ) public final class InWords extends JApplet { /** * height of Applet box in pixels. Does not include surrounding frame. */ private static final int APPLET_HEIGHT = 276; /** * Width of Applet box in pixels. */ private static final int APPLET_WIDTH = 525; private static final int FIRST_COPYRIGHT_YEAR = 1999; /** * 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 RELEASE_DATE = "2009-05-01"; private static final String TITLE_STRING = "InWords Amanuensis"; private static final String VERSION_STRING = "4.6"; private static final Color BACKGROUND_FOR_BODY = Color.WHITE; // Use our own colours so Symantec won't mess with them or create dups. private static final Color FOREGROUND_FOR_DISPLAY = Color.BLACK; private static final Color FOREGROUND_FOR_INSTRUCTIONS = new Color( 0x008000 ); private static final Color FOREGROUND_FOR_LABEL = new Color( 0x0000b0 ); // --Commented out by Inspection (02/04/08 3:39 PM):private static final Color red = Color.RED; /** * for titles */ private static final Color FOREGROUND_FOR_TITLE = new Color( 0xdc143c ); /** * for for titles and About buttons */ private static final Font FONT_FOR_TITLE = FontFactory.build( "Dialog", Font.BOLD, 16 ); /** * for for title second line */ private static final Font FONT_FOR_TITLE2 = FontFactory.build( "Dialog", Font.PLAIN, 14 ); /** * Delegate object that supports the toWords method for each language. */ private static ToWords[] languageDelegate; /** * list of languages supported, as they appear in the menu. */ private static String[] languageNames; /** * about box */ private JButton about; /** * which language you want number displayed in */ private JComboBox language; /** * instructions */ private JLabel instructions; /** * label for language */ private JLabel languageLabel; /** * label for the number */ private JLabel theNumberLabel; /** * title */ private JLabel title; /** * title, second line */ private JLabel title2; /** * spinner to define the number to display */ private JSpinner spinner; /** * the number in words */ private JTextArea display; /** * tracks current value of spinner */ private SpinnerNumberModel spinnerNumberModel; /** * Finds all the languages supported in the InWords.properties file. Creates languageNames and languageDelegate * arrays. */ private static void findLanguages() { // The properties file looks like this: // //# com.mindprod.inwords.inwords.properties must live in inwords.jar. //# Describes languages supported to translate numbers to words. //# Fully qualified classname, (without .class)=name on menu (embedded blanks ok) //# Everything is case-sensitive!! //com.mindprod.inwords.Indonesian=Bahasa Indonesia //com.mindprod.inwords.Binary=Binary //com.mindprod.inwords.AmericanEnglish=English (North American) //com.mindprod.inwords.AmericanOrdinals=English (ordinals) //com.mindprod.inwords.BritishEnglish=English (British) //com.mindprod.inwords.DecimalCommas=Decimal (commas) //com.mindprod.inwords.DecimalDots=Decimal (dots) //com.mindprod.inwords.DecimalSpaces=Decimal (spaces) //com.mindprod.inwords.Dutch=Dutch (modern) //com.mindprod.inwords.BankerDutch=Dutch (banker's) //com.mindprod.inwords.OldDutch=Dutch (old) //com.mindprod.inwords.Esperanto=Esperanto //com.mindprod.inwords.French=French //com.mindprod.inwords.German=German //com.mindprod.inwords.Hex=Hexadecimal //com.mindprod.inwords.Icelandic=Icelandic //com.mindprod.inwords.Italian=Italian (beta) //com.mindprod.inwords.Japanese=Japanese (requires Kanji Unicode) //com.mindprod.inwords.Martian=Martian //com.mindprod.inwords.Grams=Metric Prefixes (grams) //com.mindprod.inwords.Norwegian=Norwegian //com.mindprod.inwords.Octal=Octal //com.mindprod.inwords.Polish=Polish (requires Unicode) //com.mindprod.inwords.RAM=RAM (bytes) //com.mindprod.inwords.Roman=Roman Numerals //com.mindprod.inwords.Spanish=Spanish //com.mindprod.inwords.SpanishForCheques=Spanish for cheques //com.mindprod.inwords.Swedish=Swedish //com.mindprod.inwords.SwissFrench=Swiss Vaudois French //com.mindprod.inwords.Tagalog=Tagalog (Filipino) //com.mindprod.inwords.TimeInterval=Time Interval in Milliseconds //#-30- String[][] result = null; try { // look in jar, and on classpath for // com.mindprod.inwords.inwords.properties InputStream fis = InWords.class .getResourceAsStream( "inwords.properties" ); if ( fis == null ) { throw new IOException( "missing resource" ); } result = Misc.loadProperties( fis ); } catch ( IOException oops ) { out.println( oops + " Problem accessing inwords.properties file." ); System.exit( 1 ); } // in pairs className=languageNames String[] languageClass = result[ 0 ]; languageNames = result[ 1 ]; languageDelegate = new ToWords[ languageClass.length ]; for ( int i = 0; i < languageClass.length; i++ ) { try { languageDelegate[ i ] = ( ToWords ) ( Class .forName( languageClass[ i ] ).newInstance() ); // could give the method a test, but that would pre-load the // class // languageDelegate[i].toWords(0); } catch ( Exception oops ) { out.println( oops + " Bug in inwords.properties or class file for " + languageNames[ i ] ); System.exit( 1 ); } } // end for } // end findLanguages /** * layout components in a GridBag * * @param contentPane where to place the components. */ private void layoutComponents( Container contentPane ) { // ---- 0 -----1-------- // 0 title --- about---- 0 // 1 title2 ---about---- 1 // 2 language number---- 2 // 3 lang-lab num-lab -- 3 // 4 ---- display words- 4 // 5 -----instructions-- 5 // ----0 ------- 1------ // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( title, new GridBagConstraints( 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets( 10, 10, 5, 5 ), 0, 0 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( title2, new GridBagConstraints( 0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets( 0, 10, 5, 5 ), 0, 0 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( about, new GridBagConstraints( 1, 0, 1, 2, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets( 10, 10, 5, 10 ), 10, 2 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( language, new GridBagConstraints( 0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets( 0, 10, 5, 10 ), 0, 0 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( spinner, new GridBagConstraints( 1, 2, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.HORIZONTAL, new Insets( 0, 10, 5, 10 ), 40, 0 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( languageLabel, new GridBagConstraints( 0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets( 0, 10, 5, 10 ), 0, 0 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( theNumberLabel, new GridBagConstraints( 1, 3, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets( 0, 10, 5, 10 ), 0, 0 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( display, new GridBagConstraints( 0, 4, 2, 1, 0.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets( 0, 10, 5, 10 ), 0, 0 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( instructions, new GridBagConstraints( 0, 5, 2, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets( 0, 10, 10, 10 ), 0, 0 ) ); } /** * Refresh screen after language or number change. */ private void refresh() { final long setting = spinnerNumberModel.getNumber().longValue(); display.setText( languageDelegate[ language.getSelectedIndex() ].toWords( setting ) ); } // end refresh /** * Allow this Applet to run as as application as well. * * @param args command line arguments ignored. */ public static void main( String args[] ) { HybridJ.fireup( new InWords(), TITLE_STRING + " " + VERSION_STRING, APPLET_WIDTH, APPLET_HEIGHT ); } // end main /** * Called by the browser or Applet viewer to inform * this Applet that it is being reclaimed and that it should destroy * any resources that it has allocated. */ public void destroy() { about = null; display = null; instructions = null; language = null; languageLabel = null; spinner = null; spinnerNumberModel = null; theNumberLabel = null; title2 = null; title = null; } /** * Called by the browser or Applet viewer to inform * this Applet that it has been loaded into the system. */ @Override public void init() { if ( !VersionCheck.isJavaVersionOK( 1, 8, 0, this ) ) { return; } Common18.setLaf(); final Container contentPane = this.getContentPane(); contentPane.setBackground( BACKGROUND_FOR_BODY ); contentPane.setLayout( new GridBagLayout() ); about = new JEButton( "About" ); about.setToolTipText( "About " + TITLE_STRING + " " + VERSION_STRING ); about.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { // open aboutbox frame new CMPAboutJBox( TITLE_STRING, VERSION_STRING, "Spells out numbers in words in many languages.", "Teaches you to count in many languages.", "freeware", RELEASE_DATE, FIRST_COPYRIGHT_YEAR, "Roedy Green", "INWORDS", "1.7" /* jdk version */ ); } } ); title = new JLabel( TITLE_STRING + " " + VERSION_STRING ); title.setFont( FONT_FOR_TITLE ); title.setForeground( FOREGROUND_FOR_TITLE ); title2 = new JLabel( "released:" + RELEASE_DATE + " build:" + Build.BUILD_NUMBER ); title2.setFont( FONT_FOR_TITLE2 ); title2.setForeground( FOREGROUND_FOR_TITLE ); // add legal choices to boxes findLanguages(); language = new JComboBox<>( languageNames ); // turn off the write-in feature language.setEditable( false ); language.setFont( FontFactory.build( "Dialog", Font.BOLD, 12 ) ); language.setSelectedItem( "English (North American)" ); language.addItemListener( new ItemListener() { /** * Notice any change to language choice * @param event details of just what the user clicked. */ public void itemStateChanged( ItemEvent event ) { if ( event.getSource() == language ) { refresh(); } // end if } // end itemStateChanged } ); languageLabel = new JLabel( "language" ); languageLabel.setFont( FontFactory.build( "Dialog", Font.BOLD, 15 ) ); languageLabel.setForeground( FOREGROUND_FOR_LABEL ); /* JSpinner with commas */ spinner = new JSpinner(); spinner.setFont( FontFactory.build( "Dialog", Font.BOLD, 15 ) ); spinner.setEnabled( true ); // how much space you want to freeze the spinner at. Dimension d = new Dimension( 200, 30 ); spinner.setMinimumSize( d ); spinner.setPreferredSize( d ); spinner.setMaximumSize( d ); // NumberModel tracks value, but not how it is formatted. spinnerNumberModel = new SpinnerNumberModel( /* initial value */ 12345L, /* min */ Long.MIN_VALUE + 1, /* -9,223,372,036,854,775,807 */ /* max */ Long.MAX_VALUE, /* 9,223,372,036,854,775,807 */ /* step */ 1 ); spinner.setModel( spinnerNumberModel ); spinner.addChangeListener( new ChangeListener() { /** * Invoked when the JSpinner changes, even one notch in * moving to another value * @param e a ChangeEvent object */ public void stateChanged( ChangeEvent e ) { // do something when user changes the value. refresh(); } } ); theNumberLabel = new JLabel( "number" ); theNumberLabel.setFont( FontFactory.build( "Dialog", Font.BOLD, 15 ) ); theNumberLabel.setForeground( FOREGROUND_FOR_LABEL ); display = new JTextArea(); display.setLineWrap( true ); display.setWrapStyleWord( true ); display.setEditable( false ); // Unicode is a CMP virtual font mapped onto a font that supports a wide set of chars display.setFont( FontFactory.build( "Unicode", Font.PLAIN, 15 ) ); display.setForeground( FOREGROUND_FOR_DISPLAY ); instructions = new JLabel( "Select language, " + "enter number (with or without commas), " + "and hit enter." ); instructions.setForeground( FOREGROUND_FOR_INSTRUCTIONS ); refresh(); layoutComponents( contentPane ); this.validate(); this.setVisible( true ); } // end init }