A Complete Example


In this section, we apply the material from this chapter to localize a retirement calculator applet. The applet calculates whether or not you are saving enough money for your retirement. You enter your age, how much money you save every month, and so on (see Figure 10-4).

Figure 10-4. The retirement calculator in English


The text area and the graph show the balance of the retirement account for every year. If the numbers turn negative toward the later part of your life and the bars in the graph show up below the x-axis, you need to do something; for example, save more money, postpone your retirement, die earlier, or be younger.

The retirement calculator works in three locales (English, German, and Chinese). Here are some of the highlights of the internationalization.

  • The labels, buttons, and messages are translated into German and Chinese. You can find them in the classes RetireResources_de, RetireResources_zh. English is used as the fallbacksee the RetireResources file. To generate the Chinese messages, we first typed the file, using Notepad running in Chinese Windows, and then we used the native2ascii utility to convert the characters to Unicode.

  • Whenever the locale changes, we reset the labels and reformat the contents of the text fields.

  • The text fields handle numbers, currency amounts, and percentages in the local format.

  • The computation field uses a MessageFormat. The format string is stored in the resource bundle of each language.

  • Just to show that it can be done, we use different colors for the bar graph, depending on the language chosen by the user.

Examples 10-6 through 10-9 show the code. Examples 10-10 through 10-12 are the property files for the localized strings. Figures 10-5 and 10-6 show the outputs in German and Chinese. To see Chinese characters, install the Chinese fonts. Otherwise, all Chinese characters show up as "missing character" icons.

Figure 10-5. The retirement calculator in German


Figure 10-6. The retirement calculator in Chinese


In sum, while the localization mechanism of the Java programming language still has some rough edges, it does have one major virtue. Once you have organized your application for localization, it is extremely easy to add more localized versions. You simply provide more resource files, and they will be automatically loaded when a user wants them.

NOTE

This applet was harder to write than a typical localized application because the user can change the locale on the fly. The applet, therefore, had to be prepared to redraw itself whenever the user selects another locale. If you simply want to display your applet in the user's default locale, you will not need to work so hard. You can simply call getLocale() to find the locale of your user's system and then use it for the entire duration of the applet.


Example 10-6. Retire.java

[View full width]

   1. import java.awt.*;   2. import java.awt.event.*;   3. import java.awt.geom.*;   4. import java.applet.*;   5. import java.util.*;   6. import java.text.*;   7. import java.io.*;   8. import javax.swing.*;   9.  10. /**  11.    This applet shows a retirement calculator. The UI is displayed  12.    in English, German, and Chinese.  13. */  14. public class Retire extends JApplet  15. {  16.    public void init()  17.    {  18.       setLayout(new GridBagLayout());  19.       add(languageLabel, new GBC(0, 0).setAnchor(GBC.EAST));  20.       add(savingsLabel, new GBC(0, 1).setAnchor(GBC.EAST));  21.       add(contribLabel, new GBC(2, 1).setAnchor(GBC.EAST));  22.       add(incomeLabel, new GBC(4, 1).setAnchor(GBC.EAST));  23.       add(currentAgeLabel, new GBC(0, 2).setAnchor(GBC.EAST));  24.       add(retireAgeLabel, new GBC(2, 2).setAnchor(GBC.EAST));  25.       add(deathAgeLabel, new GBC(4, 2).setAnchor(GBC.EAST));  26.       add(inflationPercentLabel, new GBC(0, 3).setAnchor(GBC.EAST));  27.       add(investPercentLabel, new GBC(2, 3).setAnchor(GBC.EAST));  28.       add(localeCombo, new GBC(1, 0));  29.       add(savingsField, new GBC(1, 1).setWeight(100, 0).setFill(GBC.HORIZONTAL));  30.       add(contribField, new GBC(3, 1).setWeight(100, 0).setFill(GBC.HORIZONTAL));  31.       add(incomeField, new GBC(5, 1).setWeight(100, 0).setFill(GBC.HORIZONTAL));  32.       add(currentAgeField, new GBC(1, 2).setWeight(100, 0).setFill(GBC.HORIZONTAL));  33.       add(retireAgeField, new GBC(3, 2).setWeight(100, 0).setFill(GBC.HORIZONTAL));  34.       add(deathAgeField, new GBC(5, 2).setWeight(100, 0).setFill(GBC.HORIZONTAL));  35.       add(inflationPercentField, new GBC(1, 3).setWeight(100, 0).setFill(GBC .HORIZONTAL));  36.       add(investPercentField, new GBC(3, 3).setWeight(100, 0).setFill(GBC.HORIZONTAL));  37.       add(retireCanvas, new GBC(0, 4, 4, 1).setWeight(0, 100).setFill(GBC.BOTH));  38.       add(new JScrollPane(retireText), new GBC(4, 4, 2, 1).setWeight(0, 100).setFill (GBC.BOTH));  39.  40.       computeButton.setName("computeButton");  41.       computeButton.addActionListener(new  42.          ActionListener()  43.          {  44.             public void actionPerformed(ActionEvent event)  45.             {  46.                getInfo();  47.                updateData();  48.                updateGraph();  49.             }  50.          });  51.       add(computeButton, new GBC(5, 3));  52.  53.       retireText.setEditable(false);  54.       retireText.setFont(new Font("Monospaced", Font.PLAIN, 10));  55.  56.       info.setSavings(0);  57.       info.setContrib(9000);  58.       info.setIncome(60000);  59.       info.setCurrentAge(35);  60.       info.setRetireAge(65);  61.       info.setDeathAge(85);  62.       info.setInvestPercent(0.1);  63.       info.setInflationPercent(0.05);  64.  65.       int localeIndex = 0; // US locale is default selection  66.       for (int i = 0; i < locales.length; i++) // if current locale one of the  choices, select it  67.          if (getLocale().equals(locales[i])) localeIndex = i;  68.       setCurrentLocale(locales[localeIndex]);  69.  70.       localeCombo.addActionListener(new  71.          ActionListener()  72.          {  73.             public void actionPerformed(ActionEvent event)  74.             {  75.                setCurrentLocale((Locale) localeCombo.getSelectedItem());  76.             }  77.          });  78.    }  79.  80.    /**  81.       Sets the current locale.  82.       @param locale the desired locale  83.    */  84.    public void setCurrentLocale(Locale locale)  85.    {  86.       currentLocale = locale;  87.       localeCombo.setSelectedItem(currentLocale);  88.       localeCombo.setLocale(currentLocale);  89.  90.       res = ResourceBundle.getBundle("RetireResources", currentLocale);  91.       resStrings = ResourceBundle.getBundle("RetireStrings", currentLocale);  92.       currencyFmt = NumberFormat.getCurrencyInstance(currentLocale);  93.       numberFmt = NumberFormat.getNumberInstance(currentLocale);  94.       percentFmt = NumberFormat.getPercentInstance(currentLocale);  95.  96.       updateDisplay();  97.       updateInfo();  98.       updateData();  99.       updateGraph(); 100.    } 101. 102.    /** 103.       Updates all labels in the display. 104.    */ 105.    public void updateDisplay() 106.    { 107.       languageLabel.setText(resStrings.getString("language")); 108.       savingsLabel.setText(resStrings.getString("savings")); 109.       contribLabel.setText(resStrings.getString("contrib")); 110.       incomeLabel.setText(resStrings.getString("income")); 111.       currentAgeLabel.setText(resStrings.getString("currentAge")); 112.       retireAgeLabel.setText(resStrings.getString("retireAge")); 113.       deathAgeLabel.setText(resStrings.getString("deathAge")); 114.       inflationPercentLabel.setText(resStrings.getString("inflationPercent")); 115.       investPercentLabel.setText(resStrings.getString("investPercent")); 116.       computeButton.setText(resStrings.getString("computeButton")); 117.       validate(); 118.    } 119. 120.    /** 121.       Updates the information in the text fields. 122.    */ 123.    public void updateInfo() 124.    { 125.       savingsField.setText(currencyFmt.format(info.getSavings())); 126.       contribField.setText(currencyFmt.format(info.getContrib())); 127.       incomeField.setText(currencyFmt.format(info.getIncome())); 128.       currentAgeField.setText(numberFmt.format(info.getCurrentAge())); 129.       retireAgeField.setText(numberFmt.format(info.getRetireAge())); 130.       deathAgeField.setText(numberFmt.format(info.getDeathAge())); 131.       investPercentField.setText(percentFmt.format(info.getInvestPercent())); 132.       inflationPercentField.setText(percentFmt.format(info.getInflationPercent())); 133.    } 134. 135.    /** 136.       Updates the data displayed in the text area. 137.    */ 138.    public void updateData() 139.    { 140.       retireText.setText(""); 141.       MessageFormat retireMsg = new MessageFormat(""); 142.       retireMsg.setLocale(currentLocale); 143.       retireMsg.applyPattern(resStrings.getString("retire")); 144. 145.       for (int i = info.getCurrentAge(); i <= info.getDeathAge(); i++) 146.       { 147.          Object[] args = { i, info.getBalance(i) }; 148.          retireText.append(retireMsg.format(args) + "\n"); 149.       } 150.    } 151. 152.    /** 153.       Updates the graph. 154.    */ 155.    public void updateGraph() 156.    { 157.       retireCanvas.setColorPre((Color) res.getObject("colorPre")); 158.       retireCanvas.setColorGain((Color) res.getObject("colorGain")); 159.       retireCanvas.setColorLoss((Color) res.getObject("colorLoss")); 160.       retireCanvas.setInfo(info); 161.       repaint(); 162.    } 163. 164.    /** 165.       Reads the user input from the text fields. 166.    */ 167.    public void getInfo() 168.    { 169.       try 170.       { 171.          info.setSavings(currencyFmt.parse(savingsField.getText()).doubleValue()); 172.          info.setContrib(currencyFmt.parse(contribField.getText()).doubleValue()); 173.          info.setIncome(currencyFmt.parse(incomeField.getText()).doubleValue()); 174.          info.setCurrentAge(numberFmt.parse(currentAgeField.getText()).intValue()); 175.          info.setRetireAge(numberFmt.parse(retireAgeField.getText()).intValue()); 176.          info.setDeathAge(numberFmt.parse(deathAgeField.getText()).intValue()); 177.          info.setInvestPercent(percentFmt.parse(investPercentField.getText()) .doubleValue()); 178.          info.setInflationPercent(percentFmt.parse(inflationPercentField.getText()) .doubleValue()); 179.       } 180.       catch (ParseException e) 181.       { 182.       } 183.    } 184. 185.    private JTextField savingsField = new JTextField(10); 186.    private JTextField contribField = new JTextField(10); 187.    private JTextField incomeField = new JTextField(10); 188.    private JTextField currentAgeField = new JTextField(4); 189.    private JTextField retireAgeField = new JTextField(4); 190.    private JTextField deathAgeField = new JTextField(4); 191.    private JTextField inflationPercentField = new JTextField(6); 192.    private JTextField investPercentField = new JTextField(6); 193.    private JTextArea retireText = new JTextArea(10, 25); 194.    private RetireCanvas retireCanvas = new RetireCanvas(); 195.    private JButton computeButton = new JButton(); 196.    private JLabel languageLabel = new JLabel(); 197.    private JLabel savingsLabel = new JLabel(); 198.    private JLabel contribLabel = new JLabel(); 199.    private JLabel incomeLabel = new JLabel(); 200.    private JLabel currentAgeLabel = new JLabel(); 201.    private JLabel retireAgeLabel = new JLabel(); 202.    private JLabel deathAgeLabel = new JLabel(); 203.    private JLabel inflationPercentLabel = new JLabel(); 204.    private JLabel investPercentLabel = new JLabel(); 205. 206.    private RetireInfo info = new RetireInfo(); 207. 208.    private Locale[] locales = { Locale.US, Locale.CHINA, Locale.GERMANY }; 209.    private Locale currentLocale; 210.    private JComboBox localeCombo = new LocaleCombo(locales); 211.    private ResourceBundle res; 212.    private ResourceBundle resStrings; 213.    private NumberFormat currencyFmt; 214.    private NumberFormat numberFmt; 215.    private NumberFormat percentFmt; 216. } 217. 218. /** 219.    The information required to compute retirement income data. 220. */ 221. class RetireInfo 222. { 223.    /** 224.       Gets the available balance for a given year. 225.       @param year the year for which to compute the balance 226.       @return the amount of money available (or required) in 227.       that year 228.    */ 229.    public double getBalance(int year) 230.    { 231.       if (year < currentAge) return 0; 232.       else if (year == currentAge) 233.       { 234.          age = year; 235.          balance = savings; 236.          return balance; 237.       } 238.       else if (year == age) return balance; 239.       if (year != age + 1) getBalance(year - 1); 240.       age = year; 241.       if (age < retireAge) balance += contrib; 242.       else balance -= income; 243.       balance = balance * (1 + (investPercent - inflationPercent)); 244.       return balance; 245.    } 246. 247.    /** 248.       Gets the amount of prior savings. 249.       @return the savings amount 250.    */ 251.    public double getSavings() { return savings; } 252. 253.    /** 254.       Sets the amount of prior savings. 255.       @param newValue the savings amount 256.    */ 257.    public void setSavings(double newValue) { savings = newValue; } 258. 259.    /** 260.       Gets the annual contribution to the retirement account. 261.       @return the contribution amount 262.    */ 263.    public double getContrib() { return contrib; } 264. 265.    /** 266.       Sets the annual contribution to the retirement account. 267.       @param newValue the contribution amount 268.    */ 269.    public void setContrib(double newValue) { contrib = newValue; } 270. 271.    /** 272.       Gets the annual income. 273.       @return the income amount 274.    */ 275.    public double getIncome() { return income; } 276. 277.    /** 278.       Sets the annual income. 279.       @param newValue the income amount 280.    */ 281.    public void setIncome(double newValue) { income = newValue; } 282. 283.    /** 284.       Gets the current age. 285.       @return the age 286.    */ 287.    public int getCurrentAge() { return currentAge; } 288. 289.    /** 290.       Sets the current age. 291.       @param newValue the age 292.    */ 293.    public void setCurrentAge(int newValue) { currentAge = newValue; } 294. 295.    /** 296.       Gets the desired retirement age. 297.       @return the age 298.    */ 299.    public int getRetireAge() { return retireAge; } 300. 301.    /** 302.       Sets the desired retirement age. 303.       @param newValue the age 304.    */ 305.    public void setRetireAge(int newValue) { retireAge = newValue; } 306. 307.    /** 308.       Gets the expected age of death. 309.       @return the age 310.    */ 311.    public int getDeathAge() { return deathAge; } 312. 313.    /** 314.       Sets the expected age of death. 315.       @param newValue the age 316.    */ 317.    public void setDeathAge(int newValue) { deathAge = newValue; } 318. 319.    /** 320.       Gets the estimated percentage of inflation. 321.       @return the percentage 322.    */ 323.    public double getInflationPercent() { return inflationPercent; } 324. 325.    /** 326.       Sets the estimated percentage of inflation. 327.       @param newValue the percentage 328.    */ 329.    public void setInflationPercent(double newValue) { inflationPercent = newValue; } 330. 331.    /** 332.       Gets the estimated yield of the investment. 333.       @return the percentage 334.    */ 335.    public double getInvestPercent() { return investPercent; } 336. 337.    /** 338.       Sets the estimated yield of the investment. 339.       @param newValue the percentage 340.    */ 341.    public void setInvestPercent(double newValue) { investPercent = newValue; } 342. 343.    private double savings; 344.    private double contrib; 345.    private double income; 346.    private int currentAge; 347.    private int retireAge; 348.    private int deathAge; 349.    private double inflationPercent; 350.    private double investPercent; 351. 352.    private int age; 353.    private double balance; 354. } 355. 356. /** 357.    This panel draws a graph of the investment result. 358. */ 359. class RetireCanvas extends JPanel 360. { 361.    public RetireCanvas() 362.    { 363.       setSize(PANEL_WIDTH, PANEL_HEIGHT); 364.    } 365. 366.    /** 367.       Sets the retirement information to be plotted. 368.       @param newInfo the new retirement info. 369.    */ 370.    public void setInfo(RetireInfo newInfo) 371.    { 372.       info = newInfo; 373.       repaint(); 374.    } 375. 376.    public void paintComponent(Graphics g) 377.    { 378.       Graphics2D g2 = (Graphics2D) g; 379.       if (info == null) return; 380. 381.       double minValue = 0; 382.       double maxValue = 0; 383.       int i; 384.       for (i = info.getCurrentAge(); i <= info.getDeathAge(); i++) 385.       { 386.          double v = info.getBalance(i); 387.          if (minValue > v) minValue = v; 388.          if (maxValue < v) maxValue = v; 389.       } 390.       if (maxValue == minValue) return; 391. 392.       int barWidth = getWidth() / (info.getDeathAge() - info.getCurrentAge() + 1); 393.       double scale = getHeight() / (maxValue - minValue); 394. 395.       for (i = info.getCurrentAge(); i <= info.getDeathAge(); i++) 396.       { 397.          int x1 = (i - info.getCurrentAge()) * barWidth + 1; 398.          int y1; 399.          double v = info.getBalance(i); 400.          int height; 401.          int yOrigin = (int) (maxValue * scale); 402. 403.          if (v >= 0) 404.          { 405.             y1 = (int) ((maxValue - v) * scale); 406.             height = yOrigin - y1; 407.          } 408.          else 409.          { 410.             y1 = yOrigin; 411.             height = (int) (-v * scale); 412.          } 413. 414.          if (i < info.getRetireAge()) g2.setPaint(colorPre); 415.          else if (v >= 0) g2.setPaint(colorGain); 416.          else g2.setPaint(colorLoss); 417.          Rectangle2D bar = new Rectangle2D.Double(x1, y1, barWidth - 2, height); 418.          g2.fill(bar); 419.          g2.setPaint(Color.black); 420.          g2.draw(bar); 421.       } 422.    } 423. 424.    /** 425.       Sets the color to be used before retirement. 426.       @param color the desired color 427.    */ 428.    public void setColorPre(Color color) 429.    { 430.       colorPre = color; 431.       repaint(); 432.    } 433. 434.    /** 435.       Sets the color to be used after retirement while 436.       the account balance is positive. 437.       @param color the desired color 438.    */ 439.    public void setColorGain(Color color) 440.    { 441.       colorGain = color; 442.       repaint(); 443.    } 444. 445.    /** 446.       Sets the color to be used after retirement when 447.       the account balance is negative. 448.       @param color the desired color 449.    */ 450.    public void setColorLoss(Color color) 451.    { 452.       colorLoss = color; 453.       repaint(); 454.    } 455. 456.    private RetireInfo info = null; 457.    private Color colorPre; 458.    private Color colorGain; 459.    private Color colorLoss; 460.    private static final int PANEL_WIDTH = 400; 461.    private static final int PANEL_HEIGHT = 200; 462. } 

Example 10-7. RetireResources.java
  1. import java.util.*;  2. import java.awt.*;  3.  4. /**  5.    These are the English non-string resources for the retirement  6.    calculator.  7. */  8. public class RetireResources  9.    extends java.util.ListResourceBundle 10. { 11.    public Object[][] getContents() { return contents; } 12.    static final Object[][] contents = 13.    { 14.       // BEGIN LOCALIZE 15.       { "colorPre", Color.blue }, 16.       { "colorGain", Color.white }, 17.       { "colorLoss", Color.red } 18.       // END LOCALIZE 19.    }; 20. } 

Example 10-8. RetireResources_de.java
  1. import java.util.*;  2. import java.awt.*;  3.  4. /**  5.    These are the German non-string resources for the retirement  6.    calculator.  7. */  8. public class RetireResources_de  9.    extends java.util.ListResourceBundle 10. { 11.    public Object[][] getContents() { return contents; } 12.    static final Object[][] contents = 13.    { 14.       // BEGIN LOCALIZE 15.       { "colorPre", Color.yellow }, 16.       { "colorGain", Color.black }, 17.       { "colorLoss", Color.red } 18.       // END LOCALIZE 19.    }; 20. } 

Example 10-9. RetireResources_zh.java
  1. import java.util.*;  2. import java.awt.*;  3.  4. /**  5.    These are the Chinese non-string resources for the retirement  6.    calculator.  7. */  8. public class RetireResources_zh  9.    extends java.util.ListResourceBundle 10. { 11.    public Object[][] getContents() { return contents; } 12.    static final Object[][] contents = 13.    { 14.       // BEGIN LOCALIZE 15.       { "colorPre", Color.red }, 16.       { "colorGain", Color.blue }, 17.       { "colorLoss", Color.yellow } 18.       // END LOCALIZE 19.    }; 20. } 

Example 10-10. RetireStrings.properties
  1. language=Language  2. computeButton=Compute  3. savings=Prior Savings  4. contrib=Annual Contribution  5. income=Retirement Income  6. currentAge=Current Age  7. retireAge=Retirement Age  8. deathAge=Life Expectancy  9. inflationPercent=Inflation 10. investPercent=Investment Return 11. retire=Age: {0,number} Balance: {1,number,currency} 

Example 10-11. RetireStrings_de.properties
  1. language=Sprache  2. computeButton=Rechnen  3. savings=Vorherige Ersparnisse  4. contrib=J\u00e4hrliche Einzahlung  5. income=Einkommen nach Ruhestand  6. currentAge=Jetziges Alter  7. retireAge=Ruhestandsalter  8. deathAge=Lebenserwartung  9. inflationPercent=Inflation 10. investPercent=Investitionsgewinn 11. retire=Alter: {0,number} Guthaben: {1,number,currency} 

Example 10-12. RetireStrings_zh.properties
  1. language=\u8bed\u8a00  2. computeButton=\u8ba1\u7b97  3. savings=\u65e2\u5b58  4. contrib=\u6bcf\u5e74\u5b58\u91d1  5. income=\u9000\u4f11\u6536\u5165  6. currentAge=\u73b0\u9f84  7. retireAge=\u9000\u4f11\u5e74\u9f84  8. deathAge=u9884\u671f\u5bff\u547d  9. inflationPercent=\u901a\u8d27\u81a8\u6da8 10. investPercent=\u6295\u8d44\u62a5\u916c 11. retire=\u5e74\u9f84: {0,number} \u603b\u7ed3: {1,number,currency} 


 java.applet.Applet 1.0 

  • Locale getLocale() 1.1

    gets the current locale of the applet. The current locale is determined from the client computer that executes the applet.



    Core JavaT 2 Volume II - Advanced Features
    Building an On Demand Computing Environment with IBM: How to Optimize Your Current Infrastructure for Today and Tomorrow (MaxFacts Guidebook series)
    ISBN: 193164411X
    EAN: 2147483647
    Year: 2003
    Pages: 156
    Authors: Jim Hoskins

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net