Lab: Gaining Experience with Localization/Internationalization
Instructions:
Answer the following questions one at a time. After answering each question,
check your answer (by clicking on the check-mark icon if it is available)
before proceeding to the next question.
Getting Ready:
Before going any further, you should:
-
Setup your development environment.
-
This lab is a simulation of a part of the process that is being
used to create a product
named Taxeze for the
(hypothetical) company FreeMarket.
The team is using an incremental process known as Scrum, which
divides the work up into sprints.
If you have not already done so, you must complete
the lab on serialization
before you can start this lab. If you have already completed the
executable .jar files you should start with that version of
the code instead.
-
Read the sprintable stories.
-
Read the
course help page on Internationalization/Localization
and/or the
,
lecture notes on the same topic.
-
Briefly review the documentation for
the
ResourceBundle
and PropertyResourceBundle
classes.
1. Tasks:
The sprintable stories have been decomposed
into the following tasks.
-
Create a file named
Strings_en_US.properties
using the resource bundle format in the package
taxeze.gui
. It must contain all of the String
constants in the TaxezeController
and TaxezeWindow
classes. The name of the constant must be the key and the value
of the constant must be the value. Use an =
to delimit the
key and value. The value must not contain quote characters.
CALCULATE = Calculate
DATA_FILES = Data Files
ERROR = Error
ERROR_OPENING_FILE = Error Opening File
EXIT = Exit
FILE = File
FILING_STATUS = Filing Status
INVALID_FILE_TYPE = Invalid File Type
MARGINAL_TAX_RATE = Marginal tax rate
OPEN = Open
READ = Read
TAX = Tax
TAXABLE_INCOME = Income
UTILITIES = Utilities
WRITE = Write
-
Remove the declarations/initializations of the
String
constants from both TaxezeController
and TaxezeWindow
.
-
Add a static
ResourceBundle
final attribute named
STRINGS
(with package visibility) to
the TaxezeController
class.
static final ResourceBundle STRINGS;
-
Add code that reads in the
ResourceBundle
into the attribute
named STRINGS
when the class is loaded.
static final ResourceBundle STRINGS = ResourceBundle.getBundle("taxeze.gui.Strings");
-
Replace each
String
constant
in TaxezeController
with an appropriate call
to STRINGS.getStrings()
.
taxFilter = new FileNameExtensionFilter(STRINGS.getString("DATA_FILES"), "tax");
txsFilter = new FileNameExtensionFilter(STRINGS.getString("DATA_FILES"), "txs");
// ...
if (ac.equals(STRINGS.getString("CALCULATE"))) calculate();
else if (ac.equals(STRINGS.getString("EXIT"))) System.exit(0);
else if (ac.equals(STRINGS.getString("OPEN"))) load(txsFilter, STRINGS.getString("OPEN"));
else if (ac.equals(STRINGS.getString("READ"))) load(taxFilter, STRINGS.getString("READ"));
else if (ac.equals(STRINGS.getString("WRITE"))) write();
// ...
JOptionPane.showMessageDialog(null, STRINGS.getString("ERROR_OPENING_FILE"),
STRINGS.getString("ERROR"), JOptionPane.ERROR_MESSAGE);
JOptionPane.showMessageDialog(null, STRINGS.getString("INVALID_FILE_TYPE"),
STRINGS.getString("ERROR"), JOptionPane.ERROR_MESSAGE);
// ...
fileChooser.setDialogTitle(STRINGS.getString("WRITE"));
JOptionPane.showMessageDialog(null, STRINGS.getString("ERROR_OPENING_FILE"), STRINGS.getString("ERROR"), JOptionPane.ERROR_MESSAGE);
JOptionPane.showMessageDialog(null, STRINGS.getString("INVALID_FILE_TYPE"), STRINGS.getString("ERROR"), JOptionPane.ERROR_MESSAGE);
-
Replace each
String
constant in TaxezeWindow
with an appropriate call
to STRINGS.getStrings()
. (Hint: You may want to add an
import static
statement to TaxezeWindow
.)
import static taxeze.gui.TaxezeController.*;
// ...
calculateButton = createJButton("Calculate.png", STRINGS.getString("CALCULATE"));
openButton = createJButton("Open.png", STRINGS.getString("OPEN"));
// ...
menu = new JMenu(STRINGS.getString("FILE"));
{
item = new JMenuItem(STRINGS.getString("OPEN"));
item.addActionListener(controller);
menu.add(item);
item = new JMenuItem(STRINGS.getString("EXIT"));
item.addActionListener(controller);
menu.add(item);
}
menuBar.add(menu);
menu = new JMenu(STRINGS.getString("UTILITIES"));
{
item = new JMenuItem(STRINGS.getString("READ"));
item.addActionListener(controller);
menu.add(item);
item = new JMenuItem(STRINGS.getString("WRITE"));
writeItem = item;
writeItem.setEnabled(false);
item.addActionListener(controller);
menu.add(item);
}
menuBar.add(menu);
// ...
entryPanel.add(createTitledComponent(STRINGS.getString("TAXABLE_INCOME"), incomeField), 1, 0.0);
entryPanel.add(createTitledComponent(STRINGS.getString("FILING_STATUS"), filingStatusField), 2, 100.0);
-
Conduct some system tests to make sure you haven't introduced any defects.
-
Add a
static
Locale
attribute (with
package visibility) named LOCALE
to
the TaxezeController
class and initialize it when
it is loaded using
the static
getDefault()
method in
the Locale
class.
static final Locale LOCALE = Locale.getDefault();
-
Modify each call to
String.format()
so that the first parameter
is now LOCALE
.
Currency currency = Currency.getInstance(LOCALE);
results.add(String.format(LOCALE, STRINGS.getString("MARGINAL_TAX_RATE")+": %%%5.2f", marginalRate));
results.add(String.format(LOCALE, STRINGS.getString("TAX")+": "+currency.getSymbol()+"%10.2f", taxes));
-
Conduct some system tests to make sure you haven't introduced any defects.
-
Did you miss any
String
literals?
Yes. Unfortunately we didn't use String
constants everywhere.
-
What are the implications of your answer to the previous question?
If there's any chance that a program will need to be
localized/internationalized then there shouldn't
be String
literals interspersed throughout the code.
-
Create a file for another language. (If you speak another
language, feel free to do the translation yourself. If not, a good approach
is to use Google Translate.)
-
Conduct system tests using the second language. Ensure that all of the
acceptance criteria are satisfied.