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