SwingXLocalization < Javadesktop < TWiki

TWiki . Javadesktop . SwingXLocalization

Home | Changes | Index | Search | Go

SwingXLocalization

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

----- Revision r2 - 06 Nov 2007 - 13:11:48 - KleopatraX