 |
Home | Changes | Index | Search | Go
Collection of issues, experiences, requirements related to localization (setLocale).
Lessons learned
An unweighted list of things that can go wrong
Lookup properties in UIManager
SwingX components (and/or their respective UIDelegate/AddOn) are encouraged to look up potentially localizable properties in the UIManager instead of hardcoding or accessing a resourceBundle directly. To be on the safe side, this should be done via one of lookup methods which take a Locale as parameter and pass-in the widgets current locale. A typical code snippet (from JXTable)
/**
* Returns a potentially localized value from the UIManager. The given key
* is prefixed by this table's <code>UIPREFIX</code> before doing the
* lookup. The lookup respects this table's current <code>locale</code>
* property. Returns the key, if no value is found.
*
* @param key the bare key to look up in the UIManager.
* @return the value mapped to UIPREFIX + key or key if no value is found.
*/
protected String getUIString(String key) {
return getUIString(key, getLocale());
}
/**
* Returns a potentially localized value from the UIManager for the
* given locale. The given key
* is prefixed by this table's <code>UIPREFIX</code> before doing the
* lookup. Returns the key, if no value is found.
*
* @param key the bare key to look up in the UIManager.
* @param locale the locale use for lookup
* @return the value mapped to UIPREFIX + key in the given locale,
* or key if no value is found.
*/
protected String getUIString(String key, Locale locale) {
String text = UIManager.getString(UIPREFIX + key, locale);
return text != null ? text : key;
}
PropertyChange Notification
Property change events are guaranteed to be fired after all internal (direct and indirectly related) state is updated to the new value. As locale is a bound property, super will do the firing - be sure to first update and then call super in overrides! Best to do the pre-update in a dedicated, protected method - this allows subclasses to do-the-correct-thing. The implication is that all locale-synching methods must carry the new Locale as parameter. A code snippet (from JXTable):
/**
* {@inheritDoc} <p>
* Overridden to update locale-dependent properties.
*
* @see #updateLocaleState(Locale)
*/
@Override
public void setLocale(Locale locale) {
updateLocaleState(locale);
super.setLocale(locale);
}
/**
* Updates locale-dependent state to the given <code>Locale</code>.
*
* Here: updates registered column actions' locale-dependent state.
* <p>
*
*
* @param locale the Locale to use for value lookup
* @see #setLocale(Locale)
* @see #updateLocaleActionState(String)
*/
protected void updateLocaleState(Locale locale) {
updateLocaleActionState(HORIZONTALSCROLL_ACTION_COMMAND, locale);
updateLocaleActionState(PACKALL_ACTION_COMMAND, locale);
updateLocaleActionState(PACKSELECTED_ACTION_COMMAND, locale);
}
/**
* Updates locale-dependent state of action registered with key in
* <code>ActionMap</code>. Does nothing if no action with key is found. <p>
*
* Here: updates the <code>Action</code>'s name property.
*
* @param key the string for lookup in this table's ActionMap
* @see #updateLocaleState()
*/
protected void updateLocaleActionState(String key, Locale locale) {
Action action = getActionMap().get(key);
if (action == null) return;
action.putValue(Action.NAME, getUIString(key, locale));
}
Code example
TBD (JW): check status of that - core bug still open?
/*
* Created on 21.02.2007
*
*/
package org.jdesktop.swingx.locale;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.util.Locale;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import org.jdesktop.swingx.JXFrame;
import org.jdesktop.swingx.JXTable;
import org.jdesktop.swingx.action.AbstractActionExt;
/**
* Locale of JFileChooser not taken.
* http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4423439
*
* @author Jeanette Winzenburg, Berlin
*/
public class ToggleXLocaleBug {
private static final Locale DEFAULT_LOCALE = Locale.US;
private static final Locale ALTERNATIVE_LOCALE = Locale.GERMAN;
private Component getContent() {
JComponent comp = new JPanel(new BorderLayout());
final JXTable table = new JXTable(10, 3);
// just to see the current local
table.getColumnExt(0).setTitle(Locale.getDefault().getLanguage());
comp.add(new JScrollPane(table));
Action toggleLocale = new AbstractActionExt("toggleLocale") {
public void actionPerformed(ActionEvent e) {
Locale old = Locale.getDefault();
Locale.setDefault(old == DEFAULT_LOCALE ? ALTERNATIVE_LOCALE : DEFAULT_LOCALE);
// make sure newly created comps get the new locale by default
// Note: this does not effect components which are already
// created
JComponent.setDefaultLocale(Locale.getDefault());
table.getColumnExt(0).setTitle(Locale.getDefault().getLanguage());
}
};
// intentional: use a shared chooser to show what's required to
// make it take the new locale.
final JFileChooser chooser = new JFileChooser();
Action openFileChooser = new AbstractActionExt("open") {
public void actionPerformed(ActionEvent e) {
// we are fine if the chooser is re-created in each call
// _and_ the JComponent.defaultLocale is kept in synch with
// with Locale.getDefault()
// JFileChooser chooser = new JFileChooser();
// otherwise we have to update the chooser's locale manually
if (!Locale.getDefault().equals(chooser.getLocale())) {
chooser.setLocale(Locale.getDefault());
// need to explicitly trigger re-install to pick up the new
// locale-dependent state
chooser.updateUI();
}
chooser.showOpenDialog(null);
}
};
JComponent buttons = new JPanel();
buttons.add(new JButton(toggleLocale));
buttons.add(new JButton(openFileChooser));
comp.add(buttons, BorderLayout.SOUTH);
return comp;
}
public static void main(String[] args) {
Locale originalDefault = Locale.getDefault();
Locale def = DEFAULT_LOCALE;
Locale alt = ALTERNATIVE_LOCALE;
// make sure the default/alternative are correct for "real" us/de
if ("de".equals(originalDefault.getLanguage())) {
def = ALTERNATIVE_LOCALE;
alt = DEFAULT_LOCALE;
}
// sequence (before creating any JComponent)
// 1. set defLocal different from system def
// 2. add custom resourceBundle
// 3. reset original defLocal
// toggling lf during later lifetime doesn't find the
// filechooser's localized resources
Locale.setDefault(alt);
UIManager.getDefaults().addResourceBundle("org.jdesktop.swingx.locale.test");
Locale.setDefault(def);
final JXFrame frame = new JXFrame("Toggle Locale and use in FileChooser", true);
// // do the same sequence as above after any comp is created - okay
// Locale.setDefault(alt);
// UIManager.getDefaults().addResourceBundle("org.jdesktop.swingx.locale.test");
// Locale.setDefault(def);
ToggleXLocaleBug toggleXLocaleBug = new ToggleXLocaleBug();
frame.add(toggleXLocaleBug.getContent());
SwingUtilities.invokeLater(new Runnable() {
public void run() {
frame.pack();
frame.setVisible(true);
}
});
}
}
Links
|