/* * (C) 2004 - Geotechnical Software Services * * This code is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This code is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, * MA 02111-1307, USA. */ package no.geosoft.cc.locale; import java.io.PrintWriter; import java.io.StringWriter; import java.lang.ref.WeakReference; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.Locale; import java.util.MissingResourceException; import java.util.ResourceBundle; /** * Class for holding current locale information, and notify client * application about locale changes. Used for dynamic (or static) * locale updates and language translations during a program session. * <p> * For language dependent user interfaces, put translation files into * the packages where reguired. The files are named as follows: * <tt>Messages_xx_XX.properties</tt> where <em>xx</em> is the laguage * identifier and <em>XX</em> is the country identifier, for instance * * <pre> * Messages_en_US.properties // american * Messages_fr_FR.properties // french * Messages_ch_CH.properties // chineese * </pre> * * and so on. These are ordinary properties files containing language * translations, for instance (french): * * <pre> * Close = Fermer * New = Nouveau * Help = Aide * </pre> * * and (american): * * <pre> * Close = Close * New = New * Help = Help * </pre> * * <p> * A locale aware GUI will use the locale manager like this: * * <pre> * JButton helpButton = new JButton(); * : * helpButton.setText (localeManager.getText ("Help")); * </pre> * * If the latter is done inside a localeChanged callback, the GUI will * update itself dynamically if locale is changed during a session. * * @author <a href="mailto:info@geosoft.no">GeoSoft</a> */ public class LocaleManager { private static LocaleManager instance_ = null; private Locale defaultLocale_; private Locale currentLocale_; private HashMap resourceBundles_; // String -> ResourceBundle private Collection listeners_; // of WeakReference (LocaleListener) /** * Create the locale manager instance. */ private LocaleManager() { instance_ = this; currentLocale_ = null; defaultLocale_ = null; resourceBundles_ = new HashMap(); listeners_ = null; // Set locale for this JVM. This is independent of the default // locale of the application. Locale.setDefault (new Locale ("en", "US")); setDefaultLocale ("en", "US"); setLocale ("en", "US"); } /** * Return the sole instance of this class. * * @return The sole locale manager instance. */ public static LocaleManager getInstance() { if (instance_ == null) instance_ = new LocaleManager(); return instance_; } /** * Set the default locale. If a translation is not found for a key * in the current locale, the current locale is used instead. * * @param language Language of default locale. * @param country Country of default locale. */ public void setDefaultLocale (String language, String country) { defaultLocale_ = new Locale (language, country); } /** * Set current locale. * * @param locale New current locale. */ public void setLocale (Locale locale) { setLocale (locale.getLanguage(), locale.getCountry()); } /** * Return current locale. * * @return Current locale. */ public Locale getLocale() { return currentLocale_; } /** * Set the current locale, and notify listeners about the change. * * @param language Language of new locale. * @param country Country of new locale. */ public void setLocale (String language, String country) { // Leave here if no change if (currentLocale_ != null) { if (language.equals (currentLocale_.getLanguage()) && country.equals (currentLocale_.getCountry())) return; } // Create the new locale currentLocale_ = new Locale (language, country); // Clear resource bundles resourceBundles_.clear(); // Call listeners if (listeners_ != null) { for (Iterator i = listeners_.iterator(); i.hasNext(); ) { WeakReference reference = (WeakReference) i.next(); LocaleListener listener = (LocaleListener) reference.get(); if (listener == null) i.remove(); else listener.localeChanged(); } } } /** * Find resource bundle of specified name. * * @param bundleName Name of bundle to find. * @return Requested resource bundle (or null if not found). */ private ResourceBundle findResourceBundle (String bundleName) { ResourceBundle resourceBundle; if (!resourceBundles_.containsKey (bundleName)) { try { resourceBundle = ResourceBundle.getBundle (bundleName, currentLocale_); } catch (MissingResourceException exception1) { try { resourceBundle = ResourceBundle.getBundle (bundleName, defaultLocale_); } catch (MissingResourceException exception2) { resourceBundle = null; } } if (resourceBundle != null) resourceBundles_.put (bundleName, resourceBundle); } resourceBundle = (ResourceBundle) resourceBundles_.get (bundleName); return resourceBundle; } /** * Returned localized text for specified text. Search resource within * specified package. * * @param packageName Name of package to locate resource. * @param tag Text tag. * @return Localized text. */ private String getText (String packageName, String tag) { try { ResourceBundle resourceBundle = (ResourceBundle) resourceBundles_. get (packageName); if (resourceBundle == null) { String bundleName = packageName + ".Messages"; resourceBundle = ResourceBundle.getBundle (bundleName, currentLocale_); resourceBundles_.put (packageName, resourceBundle); } return resourceBundle.getString (tag); } catch (Exception exception) { // The bundle of the value was not found; return tag instead return tag; } } /** * Return localized text for a specified tag. The text is looked up * in the resource bundle of the package of the caller. * * @param tag Tag of text to locate. * @return Localized text of tag. */ public String getText (String tag) { // Find calling class StringWriter stringWriter = new StringWriter(); new Throwable().printStackTrace (new PrintWriter(stringWriter)); String callStack = stringWriter.toString(); int pos1 = callStack.indexOf ("at "); pos1 = callStack.indexOf ("at ", pos1 + 2) + 3; int pos2 = callStack.indexOf ("(", pos1); String line = callStack.substring (pos1, pos2); line = line.substring (0, line.lastIndexOf (".")); String packageName = line.substring (0, line.lastIndexOf (".")); return getText (packageName, tag); } /** * Add a locale listener. The listener is notified when the current * locale of the LocaleManager is changed. * * @param localeListener Listener to add. */ public void addLocaleListener (LocaleListener localeListener) { if (listeners_ == null) listeners_ = new ArrayList(); // Check to see if it is there already for (Iterator i = listeners_.iterator(); i.hasNext(); ) { WeakReference reference = (WeakReference) i.next(); LocaleListener listener = (LocaleListener) reference.get(); if (listener == localeListener) return; } // Add the listener listeners_.add (new WeakReference (localeListener)); } /** * Remove a locale listener. * * @param localeListener Locale listener to remove. */ public void removeLocaleListener (LocaleListener localeListener) { if (listeners_ == null) return; for (Iterator i = listeners_.iterator(); i.hasNext(); ) { WeakReference reference = (WeakReference) i.next(); LocaleListener listener = (LocaleListener) reference.get(); if (listener == localeListener) { i.remove(); break; } } } /** * Get default date format for the current locale. * * @param style One of DateFormat.SHORT, MEDIUM or LONG. * @return Date format for current locale. */ public DateFormat getDateFormat (int style) { return DateFormat.getDateInstance (style, currentLocale_); } /** * Get default time format for the current locale. * * @param style One of DateFormat.SHORT, MEDIUM or LONG. * @return Time format for current locale. */ public DateFormat getTimeFormat (int style) { DateFormat dateFormat = DateFormat.getTimeInstance (style, currentLocale_); // TODO: Check this String pattern = ((SimpleDateFormat) dateFormat).toPattern(); if (style == DateFormat.LONG){ int ind = pattern.lastIndexOf('s'); String newPattern = null; if (ind > 0){ newPattern = pattern.substring(0,ind+1)+".SSS"; if (ind+1 < pattern.length()){ newPattern += pattern.substring(ind+1,pattern.length()); } } pattern = newPattern; } if ((pattern.indexOf("h") == pattern.lastIndexOf("h")) && (pattern.indexOf("h") != -1)) { pattern = pattern.replaceAll("h","HH"); } else { pattern = pattern.replaceAll("h","H"); } pattern = pattern.replaceAll("a",""); pattern = pattern.replaceAll("z",""); pattern = pattern.trim(); ((SimpleDateFormat)dateFormat).applyPattern (pattern); return dateFormat; } /** * Get defaut date/time format for current locale. * * @param dateStyle One of DateFormat.SHORT, MEDIUM or LONG. * @param timeStyle One of DateFormat.SHORT, MEDIUM or LONG. * @return Date/time format for the currnt locale. */ public DateFormat getDateTimeFormat (int dateStyle, int timeStyle) { DateFormat dateFormat = DateFormat.getDateTimeInstance (dateStyle, timeStyle, currentLocale_); String datePattern = ((SimpleDateFormat)DateFormat.getDateInstance(dateStyle,currentLocale_)).toPattern(); DateFormat timeFormat = getTimeFormat(timeStyle); if (timeFormat instanceof SimpleDateFormat){ String timePattern = ((SimpleDateFormat)timeFormat).toPattern(); String pattern = datePattern + " " + timePattern; ((SimpleDateFormat)dateFormat).applyPattern(pattern); } return dateFormat; } /** * Return default localized text reprentation of the specified date. * * @param date Date to format. * @return String representation of specified date. */ public String getDate (Date date) { return getDate (date, DateFormat.SHORT); } /** * Return default localized text reprentation of the specified date. * * @param date Date to format. * @param style DateFormat.SHORT, MEDIUM or LONG * @return String representation of specified date. */ public String getDate (Date date, int style) { if (date == null) return ""; DateFormat dateFormat = getDateFormat (style); return dateFormat.format (date); } /** * Return default localized text representation of the specified time. * * @param date Time to format. * @param style DateFormat.SHORT, MEDIUM or LONG * @return String representation of specified time. */ public String getTime (Date date, int style) { if (date == null) return ""; DateFormat dateFormat = getTimeFormat (style); return dateFormat.format (date); } /** * Return default localized text representation of the specified time. * * @param date Time to format. * @return String representation of specified time. */ public String getTime (Date date) { return getTime (date, DateFormat.SHORT); } /** * Return default localized text representation of the specified date/time. * * @param date Date/time to format. * @param dateStyle One of DateFormat.SHORT, MEDIUM or LONG. * @param timeStyle One of DateFormat.SHORT, MEDIUM or LONG. * @return String representation of specified date/time. */ public String getDateTime (Date date, int dateStyle, int timeStyle) { if (date == null) return ""; DateFormat dateFormat = getDateTimeFormat (dateStyle, timeStyle); return dateFormat.format (date); } /** * Return default localized text representation of the specified date/time. * * @param date Date/time to format. * @return String representation of specified date/time. */ public String getDateTime (Date date) { return getDateTime (date, DateFormat.SHORT, DateFormat.SHORT); } }