001    // $Id: //open/mondrian/src/main/mondrian/gui/I18n.java#5 $
002    // This software is subject to the terms of the Common Public License
003    // Agreement, available at the following URL:
004    // http://www.opensource.org/licenses/cpl.html.
005    // Copyright (C) 2007 JasperSoft
006    // Copyright (C) 2008-2008 Julian Hyde
007    // All Rights Reserved.
008    // You must accept the terms of that agreement to use this software.
009    
010    package mondrian.gui;
011    
012    import java.text.MessageFormat;
013    import java.util.*;
014    import java.util.jar.*;
015    import java.net.*;
016    import java.io.*;
017    
018    import org.apache.log4j.Logger;
019    
020    public class I18n {
021        private static final Logger LOGGER = Logger.getLogger(I18n.class);
022    
023        // Default to english
024        private Locale currentLocale = Locale.ENGLISH;
025    
026        private ResourceBundle guiBundle = null;
027        private ResourceBundle languageBundle = null;
028    
029        private static String defaultIcon = "nopic";
030    
031        public static Vector<LanguageChangedListener> languageChangedListeners = null;
032    
033        static {
034            languageChangedListeners = new Vector<LanguageChangedListener>();
035        }
036    
037        public static void addOnLanguageChangedListener(LanguageChangedListener listener) {
038            languageChangedListeners.add(listener);
039        }
040    
041        public I18n(ResourceBundle guiBundle, ResourceBundle languageBundle) {
042            this.guiBundle = guiBundle;
043            this.languageBundle = languageBundle;
044        }
045    
046    
047        public static List getListOfAvailableLanguages(Class cl) {
048            java.util.List<Locale> supportedLocales = new ArrayList<Locale>();
049    
050            try {
051                Set names = getResourcesInPackage(cl, cl.getName());
052                Iterator it = names.iterator();
053    
054                while (it.hasNext()) {
055                    String n = (String) it.next();
056    
057                    // From
058                    //    '../../<application>_en.properties'
059                    //   or
060                    //    '../../<application>_en_UK.properties'
061                    // To
062                    // 'en' OR 'en_UK_' OR even en_UK_Brighton dialect
063    
064                    String lang = n.substring(n.lastIndexOf('/') + 1);
065    
066                    // only accept resources with extension '.properties'
067                    if (lang.indexOf(".properties") < 0) {
068                        continue;
069                    }
070    
071                    lang = lang.substring(0, lang.indexOf(".properties"));
072    
073                    StringTokenizer tokenizer = new StringTokenizer(lang, "_");
074                    if (tokenizer.countTokens() <=  1) {
075                        continue;
076                    }
077    
078                    String language = "";
079                    String country  = "";
080                    String variant  = "";
081    
082                    int i = 0;
083                    while (tokenizer.hasMoreTokens()) {
084                        String token = tokenizer.nextToken();
085    
086                        switch (i) {
087                        case 0:
088                            //the word <application>
089                            break;
090                        case 1:
091                            language = token;
092                            break;
093                        case 2:
094                            country = token;
095                            break;
096                        case 3:
097                            variant = token;
098                            break;
099                        default:
100                            //
101                        }
102                        i++;
103                    }
104    
105                    Locale model = new Locale(language, country, variant);
106                    supportedLocales.add(model);
107    
108                }
109            } catch (Exception e) {
110                LOGGER.error("getListOfAvailableLanguages", e);
111            }
112    
113            // Sort the list. Probably should use the current locale when getting the
114            // DisplayLanguage so the sort order is correct for the user.
115    
116            Collections.sort(
117                supportedLocales,
118                new Comparator<Object>() {
119                    public int compare(Object lhs, Object rhs) {
120                        String ls = ((Locale)lhs).getDisplayLanguage();
121                        String rs = ((Locale)rhs).getDisplayLanguage();
122    
123                        // TODO this is not very nice - We should introduce a MyLocale
124                        if (ls.equals("pap")) {
125                            ls = "Papiamentu";
126                        }
127                        if (rs.equals("pap")) {
128                            rs = "Papiamentu";
129                        }
130    
131                        return ls.compareTo(rs);
132                    }
133                }
134            );
135    
136            return supportedLocales;
137        }
138    
139        /**
140         * Enumerates the resouces in a give package name.
141         * This works even if the resources are loaded from a jar file!
142         *
143         * Adapted from code by mikewse
144         * on the java.sun.com message boards.
145         * http://forum.java.sun.com/thread.jsp?forum=22&thread=30984
146         *
147         * @param coreClass Class for class loader to find the resources
148         * @param packageName The package to enumerate
149         * @return A Set of Strings for each resouce in the package.
150         */
151        public static Set getResourcesInPackage(Class coreClass, String packageName) throws IOException {
152            String localPackageName;
153            if (packageName.endsWith("/")) {
154                localPackageName = packageName;
155            } else {
156                localPackageName = packageName + '/';
157            }
158    
159            ClassLoader cl = coreClass.getClassLoader();
160    
161            Enumeration dirEnum = cl.getResources(localPackageName);
162    
163            Set<String> names = new HashSet<String>();
164    
165            // Loop CLASSPATH directories
166            while (dirEnum.hasMoreElements()) {
167                URL resUrl = (URL) dirEnum.nextElement();
168    
169                // Pointing to filesystem directory
170                if (resUrl.getProtocol().equals("file")) {
171                    try {
172                        File dir = new File(resUrl.getFile());
173                        File[] files = dir.listFiles();
174                        if (files != null) {
175                            for (int i = 0; i < files.length; i++) {
176                                File file = files[i];
177                                if (file.isDirectory()) {
178                                    continue;
179                                }
180                                names.add(localPackageName + file.getName());
181                            }
182                        }
183                    } catch (Exception ex) {
184                        ex.printStackTrace();
185                    }
186    
187                    // Pointing to Jar file
188                } else if (resUrl.getProtocol().equals("jar")) {
189                    JarURLConnection jconn = (JarURLConnection) resUrl.openConnection();
190                    JarFile jfile = jconn.getJarFile();
191                    Enumeration entryEnum = jfile.entries();
192                    while (entryEnum.hasMoreElements()) {
193                        JarEntry entry = (JarEntry) entryEnum.nextElement();
194                        String entryName = entry.getName();
195                        // Exclude our own directory
196                        if (entryName.equals(localPackageName)) {
197                            continue;
198                        }
199                        String parentDirName = entryName.substring(0, entryName.lastIndexOf('/') + 1);
200                        if (! parentDirName.equals(localPackageName)) {
201                            continue;
202                        }
203                        names.add(entryName);
204                    }
205                } else {
206                    // Invalid classpath entry
207                }
208            }
209    
210            return names;
211        }
212    
213    
214        public void setCurrentLocale(String language) {
215            setCurrentLocale(language, null);
216        }
217    
218        public void setCurrentLocale(String language, String country) {
219            if (language != null && !language.equals("")) {
220                if (country != null && !country.equals("")) {
221                    setCurrentLocale(new Locale(language, country));
222                } else {
223                    setCurrentLocale(new Locale(language));
224                }
225            } else {
226                setCurrentLocale(Locale.getDefault());
227            }
228        }
229    
230        public void setCurrentLocale(Locale locale) {
231            currentLocale = locale;
232            languageBundle = null;
233    
234            Enumeration enum_listeners = languageChangedListeners.elements();
235            while (enum_listeners.hasMoreElements()) {
236                try {
237                    ((LanguageChangedListener)(enum_listeners.nextElement())).languageChanged(new LanguageChangedEvent(locale));
238                } catch (Exception ex) {
239                    LOGGER.error("setCurrentLocale", ex);
240                }
241            }
242        }
243    
244        public Locale getCurrentLocale() {
245            if (currentLocale == null) {
246                currentLocale = Locale.getDefault();
247            }
248            return currentLocale;
249        }
250    
251        public String getGUIReference(String reference) {
252            try {
253                if (guiBundle == null) {
254                    throw new Exception("No GUI bundle");
255                }
256                return guiBundle.getString(reference);
257            } catch (MissingResourceException ex) {
258                LOGGER.error("Can't find the translation for key = " + reference, ex);
259                throw ex;
260            } catch (Exception ex) {
261                LOGGER.error("Exception loading reference = " + reference, ex);
262                return guiBundle.getString(defaultIcon);
263            }
264        }
265    
266        /**
267         * Retreive a resource string using the current locale.
268         * @param stringID The resource string identifier
269         * @return The locale specific string
270         */
271        public String getString(String stringID) {
272            return getString(stringID, getCurrentLocale());
273        }
274    
275        /**
276         * Retreive a resource string using the current locale, with a default.
277         * @param stringID The resource string identifier
278         * @param defaultValue if no resource for the stringID is specified, use this default value
279         * @return The locale specific string
280         */
281        public String getString(String stringID, String defaultValue) {
282            return getString(stringID, getCurrentLocale(), defaultValue);
283        }
284    
285        /**
286         * Retreive a resource string using the current locale.
287         * @param stringID The resource string identifier
288         * @param defaultValue The default value for the resource string
289         * @param args arguments to be inserted into the resource string
290         * @return The locale specific string
291         */
292        public String getFormattedString(String stringID, String defaultValue, Object[] args)
293        {
294            String pattern = getString(stringID, getCurrentLocale(), defaultValue);
295            MessageFormat mf = new java.text.MessageFormat(pattern, getCurrentLocale());
296            return mf.format(args);
297        }
298    
299    
300        /**
301         * Retreive a resource string using the given locale. The stringID is the default.
302         * @param stringID The resource string identifier
303         * @param currentLocale required Locale for resource
304         * @return The locale specific string
305         */
306        private String getString(String stringID, Locale currentLocale) {
307            return getString(stringID, currentLocale, stringID);
308        }
309    
310        /**
311         * Retreive a resource string using the given locale. Use the default if there is nothing for the given Locale.
312         * @param stringID The resource string identifier
313         * @param currentLocale required Locale for resource
314         * @param defaultValue The default value for the resource string
315         * @return The locale specific string
316         */
317        public String getString(String stringID, Locale currentLocale, String defaultValue) {
318            try {
319                if (languageBundle == null) {
320                    throw new Exception("No language bundle");
321                }
322                return languageBundle.getString(stringID);
323            } catch (MissingResourceException ex) {
324                LOGGER.error("Can't find the translation for key = " + stringID + ": using default (" + defaultValue + ")", ex);
325            } catch (Exception ex) {
326                LOGGER.error("Exception loading stringID = " + stringID, ex);
327            }
328            return defaultValue;
329        }
330    
331        public static String getCurrentLocaleID() {
332            return "";
333        }
334    }
335    
336    // End I18n.java